source: trunk/src/helpers/cctl_toolbar.c@ 233

Last change on this file since 233 was 233, checked in by umoeller, 23 years ago

Minor changes.

  • Property svn:eol-style set to CRLF
  • Property svn:keywords set to Author Date Id Revision
File size: 41.5 KB
Line 
1
2/*
3 *@@sourcefile cctl_toolbar.c:
4 * implementation for the "tool bar" common control.
5 * See ctlCreateToolBar and ctl_fnwpToolbar.
6 *
7 * See comctl.c for an overview of the common controls.
8 *
9 * This file implements two window classes: a tool bar
10 * control (which is most conveniently used with
11 * ctlCreateStdWindow) and a tool button control as
12 * a replacement for the dull OS/2 pushbutton controls.
13 *
14 * This is new with V1.0.1 (2002-11-30) [umoeller].
15 *
16 * Note: Version numbering in this file relates to XWorkplace version
17 * numbering.
18 *
19 *@@header "helpers\comctl.h"
20 *@@added V1.0.1 (2002-11-30) [umoeller]
21 */
22
23/*
24 * Copyright (C) 1997-2002 Ulrich M”ller.
25 * This file is part of the "XWorkplace helpers" source package.
26 * This is free software; you can redistribute it and/or modify
27 * it under the terms of the GNU General Public License as published
28 * by the Free Software Foundation, in version 2 as it comes in the
29 * "COPYING" file of the XWorkplace main distribution.
30 * This program is distributed in the hope that it will be useful,
31 * but WITHOUT ANY WARRANTY; without even the implied warranty of
32 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
33 * GNU General Public License for more details.
34 */
35
36#define OS2EMX_PLAIN_CHAR
37 // this is needed for "os2emx.h"; if this is defined,
38 // emx will define PSZ as _signed_ char, otherwise
39 // as unsigned char
40
41#define INCL_WINWINDOWMGR
42#define INCL_WINFRAMEMGR
43#define INCL_WINSYS
44#define INCL_WINPOINTERS
45#define INCL_WININPUT
46#define INCL_WINBUTTONS
47
48#define INCL_GPIPRIMITIVES
49#define INCL_GPILOGCOLORTABLE
50#define INCL_GPIBITMAPS
51#define INCL_GPIPATHS
52#define INCL_GPIREGIONS
53#define INCL_GPIBITMAPS
54#include <os2.h>
55
56#include <stdlib.h>
57#include <stdio.h>
58#include <string.h>
59#include <setjmp.h> // needed for except.h
60#include <assert.h> // needed for except.h
61
62#include "setup.h" // code generation and debugging options
63
64#include "helpers\gpih.h"
65#include "helpers\linklist.h"
66#include "helpers\standards.h"
67#include "helpers\stringh.h"
68#include "helpers\winh.h"
69#include "helpers\xstring.h"
70
71#include "helpers\comctl.h"
72
73#pragma hdrstop
74
75/*
76 *@@category: Helpers\PM helpers\Window classes\Toolbars
77 * See cctl_toolbar.c.
78 */
79
80/* ******************************************************************
81 *
82 * Private definitions
83 *
84 ********************************************************************/
85
86/*
87 *@@ TBBUTTONDATA:
88 *
89 */
90
91typedef struct _TBBUTTONDATA
92{
93 XBUTTONDATA bd;
94 XBUTTONSTATE bs;
95} TBBUTTONDATA, *PTBBUTTONDATA;
96
97/*
98 *@@ TOOLBARDATA:
99 *
100 */
101
102typedef struct _TOOLBARDATA
103{
104 DEFWINDATA dwd;
105
106 HWND hwndControlsOwner,
107 hwndToolTip; // != NULLHANDLE if TBS_TOOLTIPS is set
108
109 LONG lSpacing,
110 lMaxControlCY;
111
112 LINKLIST llControls; // linked list of HWNDs for controls in toolbar
113
114 ULONG flReformat;
115 #define RFFL_HEIGHT 0x0001 // height changed, needs complete resize
116
117 XSTRING strToolTipBuf;
118
119} TOOLBARDATA, *PTOOLBARDATA;
120
121#define TB_LEFT_SPACING 5
122#define TB_BOTTOM_SPACING 5
123
124#define TBB_BORDER 2
125
126#define TBB_TEXTSPACING 2
127
128/* ******************************************************************
129 *
130 * "Toolbar button" control
131 *
132 ********************************************************************/
133
134/*
135 *@@ ctlInitXButtonData:
136 *
137 */
138
139VOID ctlInitXButtonData(PXBUTTONDATA pbd, // in: button data
140 ULONG fl) // in: TBBS_* style flags
141{
142 if (fl & TBBS_BIGICON)
143 {
144 pbd->szlIconOrBitmap.cx = G_cxIcon;
145 pbd->szlIconOrBitmap.cy = G_cyIcon;
146 }
147 else if (fl & TBBS_MINIICON)
148 {
149 pbd->szlIconOrBitmap.cx = G_cxIcon / 2;
150 pbd->szlIconOrBitmap.cy = G_cyIcon / 2;
151 }
152 else if ( (fl & TBBS_BITMAP)
153 && (pbd->hptr)
154 )
155 {
156 BITMAPINFOHEADER2 bmih2;
157 bmih2.cbFix = sizeof(BITMAPINFOHEADER2);
158 GpiQueryBitmapInfoHeader(pbd->hptr,
159 &bmih2);
160 pbd->szlIconOrBitmap.cx = bmih2.cx;
161 pbd->szlIconOrBitmap.cy = bmih2.cy;
162 }
163}
164
165/*
166 *@@ ctlPaintTBButton:
167 * paints a tool bar button control. Can be called externally
168 * for just painting a button even if this is not really
169 * a window.
170 *
171 *@@added V0.9.13 (2001-06-21) [umoeller]
172 *@@changed V0.9.16 (2001-10-24) [umoeller]: fixed wrong hatch color and paint offset
173 *@@changed V0.9.16 (2001-10-28) [umoeller]: added bitmap support, fixed bad clip rectangle
174 *@@changed V0.9.20 (2002-08-04) [umoeller]: fixed button offset, depressed color
175 *@@changed V1.0.1 (2002-11-30) [umoeller]: moved this here from comctl.c, renamed
176 *@@changed V1.0.1 (2002-12-08) [umoeller]: added support for WS_DISABLED
177 */
178
179VOID ctlPaintTBButton(HPS hps, // in: presentation space (RGB mode)
180 ULONG fl, // in: TBBS_* flags
181 PXBUTTONDATA pbd, // in: button data
182 PXBUTTONSTATE pbs) // in: button state
183{
184 LONG lBorder = 0,
185 lOfs = 0;
186 LONG lLeft,
187 lRight,
188 lColorMiddle = pbd->dwd.lcolBackground;
189 RECTL rclWin,
190 rclTemp;
191 POINTL ptl;
192
193 rclWin.xLeft = 0;
194 rclWin.yBottom = 0;
195 rclWin.xRight = pbd->dwd.szlWin.cx;
196 rclWin.yTop = pbd->dwd.szlWin.cy;
197 // make backup for later
198 memcpy(&rclTemp, &rclWin, sizeof(RECTL));
199
200 if (!(fl & TBBS_FLAT))
201 lBorder = TBB_BORDER;
202
203 gpihSwitchToRGB(hps);
204
205 if (pbs->fPaintButtonSunk)
206 {
207 // paint button "down":
208 lLeft = G_lcol3DDark;
209 lRight = G_lcol3DLight;
210 // add offset for icon painting at the bottom
211 lOfs += 1;
212 if (!lBorder)
213 lBorder = 1;
214 }
215 else
216 {
217 lLeft = G_lcol3DLight;
218 lRight = G_lcol3DDark;
219
220 if ( (!lBorder)
221 && (pbs->fMouseOver)
222 && (fl & TBBS_HILITE)
223 )
224 lBorder = 1;
225 }
226
227 if (lBorder)
228 {
229 // paint button border
230 // make rcl inclusive
231 rclWin.xRight--;
232 rclWin.yTop--;
233 gpihDraw3DFrame2(hps,
234 &rclWin, // inclusive
235 lBorder,
236 lLeft,
237 lRight);
238 rclWin.xRight++;
239 rclWin.yTop++;
240 }
241
242 if (fl & TBBS_BACKGROUND)
243 {
244 // now paint button middle
245 if (pbs->fPaintButtonSunk)
246 // make the depressed color darker
247 gpihManipulateRGB(&lColorMiddle,
248 .95);
249 else if ((fl & TBBS_HILITE) && (pbs->fMouseOver))
250 // make the mouse over color lighter
251 gpihManipulateRGB(&lColorMiddle,
252 1.05);
253
254 WinFillRect(hps,
255 &rclWin, // exclusive
256 lColorMiddle);
257 }
258
259 // calc x and y so that icon is centered in rectangle
260 ptl.x = ((pbd->dwd.szlWin.cx - pbd->szlIconOrBitmap.cx) / 2);
261 // center vertically only if we have no text
262 if (fl & TBBS_TEXT)
263 ptl.y = pbd->dwd.szlWin.cy - pbd->szlIconOrBitmap.cy - 2;
264 else
265 ptl.y = ((pbd->dwd.szlWin.cy - pbd->szlIconOrBitmap.cy) / 2);
266
267 if (fl & TBBS_INUSE)
268 {
269 // caller wants in-use (hatched) emphasis:
270 // draw a box then
271 POINTL ptl2;
272 ptl2.x = ptl.x - 2;
273 ptl2.y = ptl.y - 2;
274 GpiMove(hps,
275 &ptl2); // &ptl
276 // duh, typo V0.9.16 (2001-10-24) [umoeller]
277 GpiSetPattern(hps, PATSYM_DIAG1);
278 GpiSetColor(hps, RGBCOL_BLACK); // V0.9.16 (2001-10-24) [umoeller]
279 ptl2.x = ptl.x + pbd->szlIconOrBitmap.cx + 1; // inclusive!
280 ptl2.y = ptl.y + pbd->szlIconOrBitmap.cy + 1; // inclusive!
281 GpiBox(hps,
282 DRO_FILL,
283 &ptl2,
284 0,
285 0);
286 }
287
288 // make rcl inclusive
289 rclWin.xRight--;
290 rclWin.yTop--;
291 GpiIntersectClipRectangle(hps,
292 &rclWin); // inclusive!
293
294 if ( (pbd->hptr)
295 && (fl & (TBBS_BIGICON | TBBS_MINIICON | TBBS_BITMAP))
296 )
297 {
298 // RECTL rcl3;
299
300 // now paint icon
301 ptl.x += lOfs;
302 ptl.y -= lOfs;
303
304 /*
305 rcl3.xLeft = ptl.x;
306 rcl3.yBottom = ptl.y;
307 rcl3.xRight = ptl.x + pbd->szlIconOrBitmap.cx;
308 rcl3.yTop = ptl.y + pbd->szlIconOrBitmap.cy;
309 WinFillRect(hps, &rcl3, RGBCOL_GREEN);
310 */
311
312 if (fl & TBBS_BITMAP)
313 // V0.9.16 (2001-10-28) [umoeller]
314 WinDrawBitmap(hps,
315 pbd->hptr, // a bitmap really
316 NULL, // entire bitmap
317 &ptl,
318 0,
319 0,
320 (fl & WS_DISABLED)
321 ? DBM_HALFTONE
322 : DBM_NORMAL);
323 else
324 {
325 ULONG fl2 = DP_NORMAL; // 0x0000
326
327 if (!(fl & TBBS_BIGICON))
328 fl2 = DP_MINI;
329 if (fl & WS_DISABLED) // V1.0.1 (2002-12-08) [umoeller]
330 fl2 |= DP_HALFTONED; // I love this... DBM_HALFTONE, but DP_HALFTONED!
331 // PM is just so half-toned itself...
332 WinDrawPointer(hps,
333 ptl.x,
334 ptl.y,
335 pbd->hptr,
336 fl2);
337 }
338
339 rclTemp.yTop -= pbd->szlIconOrBitmap.cy;
340 }
341
342 if ( (pbd->dwd.pszText)
343 && (fl & TBBS_TEXT)
344 )
345 {
346 GpiSetColor(hps, pbd->dwd.lcolForeground);
347 rclTemp.yTop -= 2 * TBB_TEXTSPACING + lOfs;
348 rclTemp.xRight += 2 * lOfs; // twice the offset because we center horizontally
349 winhDrawFormattedText(hps,
350 &rclTemp,
351 pbd->dwd.pszText,
352 DT_CENTER | DT_TOP | DT_MNEMONIC | DT_WORDBREAK);
353 }
354}
355
356static const SYSCOLORSET scsToolbarButton =
357 {
358 TRUE, // inherit presparams
359 SYSCLR_BUTTONMIDDLE,
360 SYSCLR_MENUTEXT
361 };
362
363/*
364 *@@ BtnAutoSize:
365 *
366 */
367
368STATIC VOID BtnAutoSize(ULONG flStyle,
369 PTBBUTTONDATA pData)
370{
371 if (flStyle & TBBS_AUTORESIZE)
372 {
373 HPS hps;
374 if (hps = WinGetPS(pData->bd.dwd.hwnd))
375 {
376 ULONG ulBorder = 2;
377 if (!(flStyle & TBBS_FLAT))
378 ulBorder += TBB_BORDER;
379
380 pData->bd.dwd.szlWin.cx
381 = pData->bd.dwd.szlWin.cy
382 = 2 * ulBorder;
383
384 if (flStyle & (TBBS_BIGICON | TBBS_MINIICON | TBBS_BITMAP))
385 {
386 pData->bd.dwd.szlWin.cx += pData->bd.szlIconOrBitmap.cx;
387 pData->bd.dwd.szlWin.cy += pData->bd.szlIconOrBitmap.cy;
388
389 if (flStyle & TBBS_TEXT)
390 pData->bd.dwd.szlWin.cx += 2 * TBB_TEXTSPACING;
391 }
392
393 if (flStyle & TBBS_TEXT)
394 {
395 RECTL rcl = { 0, 0, 100, 100 };
396 LONG lMinX;
397 winhDrawFormattedText(hps,
398 &rcl,
399 pData->bd.dwd.pszText,
400 DT_TOP | DT_LEFT | DT_QUERYEXTENT);
401
402 lMinX = (rcl.xRight - rcl.xLeft) + 2 * TBB_TEXTSPACING;
403 STOREIFMAX(lMinX, pData->bd.dwd.szlWin.cx);
404 // pData->bd.dwd.rcl.xRight += (rcl.xRight - rcl.xLeft) + 2 * TBB_TEXTSPACING;
405 pData->bd.dwd.szlWin.cy += (rcl.yTop - rcl.yBottom) + 2 * TBB_TEXTSPACING;
406 }
407
408 WinSetWindowPos(pData->bd.dwd.hwnd,
409 0,
410 0,
411 0,
412 pData->bd.dwd.szlWin.cx,
413 pData->bd.dwd.szlWin.cy,
414 SWP_SIZE | SWP_NOADJUST);
415
416 WinReleasePS(hps);
417 }
418 }
419}
420
421/*
422 *@@ BtnCreate:
423 * implementation for WM_CREATE in ctl_fnwpToolbarButton.
424 */
425
426STATIC MRESULT BtnCreate(HWND hwndButton, MPARAM mp1, MPARAM mp2)
427{
428 PTBBUTTONDATA pData;
429 MRESULT mrc = 0;
430
431 if (!(pData = NEW(TBBUTTONDATA)))
432 mrc = (MRESULT)TRUE; // stop window creation
433 else
434 {
435 PCSZ pcszText = ((PCREATESTRUCT)mp2)->pszText;
436 PSZ p;
437
438 WinSetWindowPtr(hwndButton, QWL_USER + 1, pData);
439 ZERO(pData);
440
441 // initialize DEFWINDOWDATA
442 ctlInitDWD(hwndButton,
443 mp2,
444 &pData->bd.dwd,
445 WinDefWindowProc,
446 &scsToolbarButton);
447
448 if ( (pcszText)
449 && (*pcszText == '#')
450 )
451 {
452 pData->bd.hptr = atoi(pcszText + 1);
453 if (p = strchr(pcszText + 1, '#'))
454 pcszText = p + 1;
455 else
456 pcszText = NULL;
457 }
458
459 ctlInitXButtonData(&pData->bd,
460 ((PCREATESTRUCT)mp2)->flStyle);
461
462 if (pcszText)
463 {
464 pData->bd.dwd.pszText = strdup(pcszText);
465 if ( (((PCREATESTRUCT)mp2)->flStyle & TBBS_DROPMNEMONIC)
466 && (p = strchr(pData->bd.dwd.pszText, '~'))
467 )
468 memmove(p, p + 1, strlen(p));
469 }
470
471 BtnAutoSize(((PCREATESTRUCT)mp2)->flStyle,
472 pData);
473
474 if ( (((PCREATESTRUCT)mp2)->flStyle & TBBS_CHECKINITIAL)
475 && (((PCREATESTRUCT)mp2)->flStyle & (TBBS_CHECK | TBBS_RADIO))
476 )
477 pData->bs.fPaintButtonSunk = TRUE;
478 }
479
480 return mrc;
481}
482
483/*
484 *@@ BtnButton1Down:
485 * implementation for WM_BUTTON1DOWN in ctl_fnwpToolbarButton.
486 */
487
488STATIC MRESULT BtnButton1Down(HWND hwndButton)
489{
490 PTBBUTTONDATA pData;
491 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
492 {
493 ULONG flStyle = winhQueryWindowStyle(hwndButton);
494
495 if (flStyle & WS_DISABLED)
496 WinAlarm(HWND_DESKTOP, WA_WARNING);
497 else
498 {
499 WinSetFocus(HWND_DESKTOP, hwndButton);
500
501 if (!pData->bs.fMouseCaptured)
502 {
503 // capture mouse events while the
504 // mouse button is down
505 WinSetCapture(HWND_DESKTOP, hwndButton);
506 pData->bs.fMouseCaptured = TRUE;
507 }
508
509 if (!pData->bs.fPaintButtonSunk)
510 {
511 // toggle state is still UP (i.e. button pressed
512 // for the first time): create menu
513 pData->bs.fPaintButtonSunk = TRUE;
514 WinInvalidateRect(hwndButton, NULL, FALSE);
515
516 if (flStyle & TBBS_CHECK)
517 // ignore the next button 1 up
518 pData->bs.fIgnoreMB1Up = TRUE;
519
520 } // end if (!pData->fButtonSunk)
521 }
522 }
523
524 return (MRESULT)TRUE; // processed
525}
526
527/*
528 *@@ ClickNotifyOwner:
529 *
530 */
531
532STATIC VOID ClickNotifyOwner(HWND hwndButton)
533{
534 HWND hwndOwner;
535
536 if (hwndOwner = WinQueryWindow(hwndButton, QW_OWNER))
537 {
538 ULONG flStyle = winhQueryWindowStyle(hwndButton);
539
540 if (flStyle & (TBBS_COMMAND | TBBS_SYSCOMMAND))
541 WinPostMsg(hwndOwner,
542 (flStyle & TBBS_SYSCOMMAND)
543 ? WM_SYSCOMMAND
544 : WM_COMMAND,
545 (MPARAM)WinQueryWindowUShort(hwndButton, QWS_ID),
546 MPFROM2SHORT(CMDSRC_PUSHBUTTON,
547 TRUE)); // pointer, not keyboard
548 else
549 ctlSendWmControl(hwndButton,
550 BN_CLICKED,
551 (MPARAM)hwndButton);
552 }
553}
554
555/*
556 *@@ BtnButton1Up:
557 * implementation for WM_BUTTON1UP in ctl_fnwpToolbarButton.
558 */
559
560STATIC MRESULT BtnButton1Up(HWND hwndButton)
561{
562 PTBBUTTONDATA pData;
563 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
564 {
565 ULONG flStyle = winhQueryWindowStyle(hwndButton);
566
567 if (pData->bs.fMouseCaptured)
568 {
569 WinSetCapture(HWND_DESKTOP, NULLHANDLE);
570 pData->bs.fMouseCaptured = FALSE;
571 }
572
573 if (!(flStyle & WS_DISABLED))
574 {
575 pData->bs.fMB1Pressed = FALSE;
576
577 if (flStyle & TBBS_CHECK)
578 {
579 if (pData->bs.fIgnoreMB1Up)
580 pData->bs.fIgnoreMB1Up = FALSE;
581 else
582 pData->bs.fPaintButtonSunk = FALSE;
583
584 WinInvalidateRect(hwndButton, NULL, FALSE);
585
586 ClickNotifyOwner(hwndButton);
587 }
588 else if (flStyle & TBBS_RADIO)
589 {
590 WinSendMsg(hwndButton,
591 TBBM_CHECK,
592 (MPARAM)1,
593 0);
594
595 ClickNotifyOwner(hwndButton);
596 }
597 else
598 {
599 pData->bs.fPaintButtonSunk = FALSE;
600
601 ClickNotifyOwner(hwndButton);
602
603 WinInvalidateRect(hwndButton, NULL, FALSE);
604 }
605 }
606 }
607
608 return (MRESULT)TRUE; // processed
609}
610
611#define IGNORE_CHECK_MAGIC 0x87678a1d
612
613/*
614 *@@ UncheckOthers:
615 * gets called twice from BtnCheck for radio buttons
616 * to uncheck the others in the group.
617 */
618
619STATIC VOID BtnUncheckOthers(HWND hwndButton,
620 ULONG ulQW) // in: QW_PREV or QW_NEXT
621{
622 HWND hwnd = hwndButton;
623 CHAR szClass[50];
624
625 while (hwnd = WinQueryWindow(hwnd, ulQW))
626 {
627 if ( (!WinQueryClassName(hwnd, sizeof(szClass), szClass)
628 || (strcmp(szClass, WC_CCTL_TBBUTTON))
629 || (!(winhQueryWindowStyle(hwnd) & TBBS_RADIO)))
630 )
631 break;
632
633 WinSendMsg(hwnd,
634 TBBM_CHECK,
635 (MPARAM)FALSE,
636 (MPARAM)IGNORE_CHECK_MAGIC); // force uncheck without resending
637 }
638}
639
640/*
641 *@@ BtnCheck:
642 * implementation for TBBM_CHECK in ctl_fnwpToolbarButton.
643 */
644
645STATIC VOID BtnCheck(HWND hwndButton,
646 BOOL fCheck,
647 ULONG ulMagic)
648{
649 PTBBUTTONDATA pData;
650
651 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
652 {
653 ULONG flStyle = winhQueryWindowStyle(hwndButton);
654
655 if ( (flStyle & TBBS_CHECK)
656 || (ulMagic == IGNORE_CHECK_MAGIC)
657 // magic code sent to radio tool bar buttons to
658 // force an uncheck without resending
659 )
660 {
661 pData->bs.fPaintButtonSunk = fCheck;
662 }
663 else if (flStyle & TBBS_RADIO)
664 {
665 BtnUncheckOthers(hwndButton, QW_PREV);
666 BtnUncheckOthers(hwndButton, QW_NEXT);
667
668 pData->bs.fPaintButtonSunk = TRUE;
669 }
670
671 WinInvalidateRect(hwndButton, NULL, FALSE);
672 }
673}
674
675/*
676 *@@ ctl_fnwpToolbarButton:
677 * window proc for the tool bar button control.
678 *
679 * This control is not at all based on the ugly OS/2 button
680 * control, but a complete rewrite. This supports a large
681 * variety of TBBS_* style flags which are useful in the
682 * context of a tool bar.
683 *
684 * The following styles are supported:
685 *
686 * -- WS_DISABLED @@todo halftone the display
687 *
688 * -- optionally one of TBBS_BIGICON, TBBS_MINIICON, or
689 * TBBS_BITMAP to paint a picture in the control
690 *
691 * -- optionally TBBS_TEXT; you can use this alone or
692 * together with one of the picture styles
693 *
694 * -- TBBS_CHECK: if set, button toggles between pressed
695 * and released on every click ("checkbox" style,
696 * even though it still looks as a button).
697 *
698 * -- TBBS_RADIO: if set, the button assumes it is part of
699 * a group and behaves like a radio button, that is, it
700 * automatically unchecks its sibling buttons which have
701 * this style too.
702 *
703 * -- TBBS_AUTORESIZE: if set, the button automatically
704 * resizes itself to the space it needs when its style
705 * or text changes.
706 *
707 * -- TBBS_HILITE: if set, the button hilites when mouse
708 * moves over it.
709 *
710 * -- TBBS_FLAT: if set, the button paints a border only
711 * if the mouse is moving over it; if not set, it
712 * always has a (thicker) border.
713 *
714 * -- TBBS_COMMAND, TBBS_SYSCOMMAND: if none of these are
715 * set, the button _sends_ WM_CONTROL with the standard
716 * button code BN_CLICKED to its owner when it is
717 * pressed. If TBBS_COMMAND is set, the button _posts_
718 * WM_COMMAND instead; if TBBS_SYSCOMMAND is set, the
719 * button posts WM_SYSCOMMAND instead.
720 *
721 * Note that this is different from the standard button
722 * behavior: even a tool bar button that does not have
723 * the TBBS_CHECK or TBBS_RADIO styles will only post
724 * WM_COMMAND if the TBBS_COMMAND style is set.
725 *
726 * There are two ways to set the icon or bitmap to be
727 * displayed with the control:
728 *
729 * -- Pass it with the window title on tool bar creation in
730 * the form "#handle#text", where "handle" is the decimal
731 * HPOINTER or HBITMAP and "text" is the actual button
732 * text. Note that this only works on creation, not with
733 * WinSetWindowText after creation.
734 *
735 * -- Send a XBBM_SETHANDLE message after button creation.
736 */
737
738MRESULT EXPENTRY ctl_fnwpToolbarButton(HWND hwndButton, ULONG msg, MPARAM mp1, MPARAM mp2)
739{
740 MRESULT mrc = 0;
741 PTBBUTTONDATA pData;
742
743 switch (msg)
744 {
745 case WM_CREATE:
746 mrc = BtnCreate(hwndButton, mp1, mp2);
747 break;
748
749 case WM_BUTTON1DOWN:
750 mrc = BtnButton1Down(hwndButton);
751 break;
752
753 case WM_BUTTON1UP:
754 mrc = BtnButton1Up(hwndButton);
755 break;
756
757 case WM_MOUSEENTER:
758 case WM_MOUSELEAVE:
759 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
760 {
761 BOOL fMouseOver = (msg == WM_MOUSEENTER);
762 if (fMouseOver != pData->bs.fMouseOver)
763 {
764 pData->bs.fMouseOver = fMouseOver;
765
766 if (winhQueryWindowStyle(hwndButton) & TBBS_HILITE)
767 WinInvalidateRect(hwndButton, NULL, FALSE);
768 }
769 }
770 break;
771
772 case WM_PAINT:
773 {
774 HPS hps;
775 RECTL rcl;
776 POINTL ptl;
777 if (hps = WinBeginPaint(hwndButton, NULLHANDLE, &rcl))
778 {
779 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
780 {
781 gpihSwitchToRGB(hps);
782 ctlPaintTBButton(hps,
783 winhQueryWindowStyle(hwndButton)
784 | TBBS_BACKGROUND,
785 &pData->bd,
786 &pData->bs);
787 }
788
789 WinEndPaint(hps);
790 }
791 }
792 break;
793
794 /*
795 *@@ TBBM_CHECK:
796 * checks the given button. Effect depends on the
797 * button style:
798 *
799 * -- With TBBS_CHECK, this sets the button check
800 * state to (BOOL)mp1.
801 *
802 * -- With TBBS_CHECKGROUP, this sets the current
803 * button check state and unchecks neighboring
804 * buttons (siblings) that have the same style.
805 * mp1 is ignored.
806 *
807 * As opposed to a check in response to a mouse
808 * event, this does _not_ send out the BN_CLICKED
809 * notification.
810 */
811
812 case TBBM_CHECK:
813 BtnCheck(hwndButton, (BOOL)mp1, (ULONG)mp2);
814 break;
815
816 /*
817 *@@ TBBM_QUERYCHECK:
818 * returns the current check status of a button
819 * with TBBS_CHECK or TBBS_CHECKGROUP style
820 * as TRUE or FALSE.
821 *
822 * No parameters.
823 */
824
825 case TBBM_QUERYCHECK:
826 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
827 mrc = (MRESULT)pData->bs.fPaintButtonSunk;
828 break;
829
830 case WM_DESTROY:
831 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
832 {
833 FREE(pData->bd.dwd.pszText);
834 free(pData);
835 }
836 break;
837
838 default:
839 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
840 mrc = ctlDefWindowProc(&pData->bd.dwd, msg, mp1, mp2);
841 }
842
843 return mrc;
844}
845
846/* ******************************************************************
847 *
848 * "Toolbar" control
849 *
850 ********************************************************************/
851
852static const SYSCOLORSET scsToolbar =
853 {
854 TRUE, // inherit presparams
855 SYSCLR_MENU,
856 SYSCLR_MENUTEXT
857 };
858
859/*
860 *@@ CreateToolbarControl:
861 *
862 */
863
864STATIC HWND CreateToolbarControl(PTOOLBARDATA pData,
865 PTOOLBARCONTROL pControl,
866 PLONG px,
867 PPRESPARAMS ppp)
868{
869 HWND hwndControl;
870
871 if (hwndControl = WinCreateWindow(pData->dwd.hwnd,
872 (PSZ)pControl->pcszClass,
873 (PSZ)pControl->pcszTitle,
874 pControl->flStyle,
875 *px,
876 TB_BOTTOM_SPACING,
877 pControl->cx,
878 pControl->cy,
879 pData->hwndControlsOwner,
880 HWND_TOP,
881 pControl->id,
882 NULL,
883 ppp))
884 {
885 *px += pControl->cx + pData->lSpacing;
886
887 if (pData->hwndToolTip)
888 {
889 TOOLINFO ti = {0};
890 ti.ulFlags = TTF_CENTER_X_ON_TOOL | TTF_POS_Y_BELOW_TOOL | TTF_SUBCLASS;
891 ti.hwndToolOwner = pData->dwd.hwnd;
892 ti.pszText = PSZ_TEXTCALLBACK;
893 ti.hwndTool = hwndControl;
894 WinSendMsg(pData->hwndToolTip,
895 TTM_ADDTOOL,
896 (MPARAM)0,
897 &ti);
898 }
899 }
900
901 return hwndControl;
902}
903
904/*
905 *@@ ReformatControls:
906 *
907 */
908
909STATIC VOID ReformatControls(HWND hwndToolBar)
910{
911 PTOOLBARDATA pData;
912 if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
913 {
914 LONG x = TB_LEFT_SPACING;
915 PLISTNODE pNode;
916 LONG cControls;
917 PSWP paswp,
918 pswpThis;
919
920 if ( (cControls = lstCountItems(&pData->llControls))
921 && (paswp = (PSWP)malloc(sizeof(SWP) * cControls))
922 )
923 {
924 BOOL rc;
925
926 pswpThis = paswp;
927
928 FOR_ALL_NODES(&pData->llControls, pNode)
929 {
930 HWND hwnd = (HWND)pNode->pItemData;
931 SWP swp;
932 CHAR szClass[50];
933
934 WinQueryWindowPos(hwnd, &swp);
935
936 if ( (WinQueryClassName(hwnd, sizeof(szClass), szClass))
937 && (!strcmp(szClass, WC_CCTL_SEPARATOR))
938 )
939 {
940 pswpThis->cy = pData->lMaxControlCY;
941 pswpThis->fl = SWP_MOVE | SWP_SIZE;
942 }
943 else
944 {
945 pswpThis->cy = swp.cy;
946 pswpThis->fl = SWP_MOVE;
947 }
948
949 pswpThis->cx = swp.cx;
950 pswpThis->y = TB_BOTTOM_SPACING;
951 pswpThis->x = x;
952 pswpThis->hwndInsertBehind = HWND_BOTTOM;
953 pswpThis->hwnd = hwnd;
954
955 x += swp.cx + pData->lSpacing;
956
957 if (swp.cy > pData->lMaxControlCY)
958 {
959 pData->lMaxControlCY = swp.cy;
960 pData->flReformat = RFFL_HEIGHT;
961 }
962
963 pswpThis++;
964 }
965
966 rc = WinSetMultWindowPos(pData->dwd.hab,
967 paswp,
968 cControls);
969
970 free(paswp);
971 }
972 }
973}
974
975/*
976 *@@ TbAddControls:
977 *
978 */
979
980STATIC ULONG TbAddControls(PTOOLBARDATA pData,
981 ULONG cControls,
982 PTOOLBARCONTROL paControls,
983 LONG lIndex) // in: index before which to add entries; -1 means rightmost
984{
985 ULONG ul,
986 cCreated = 0;
987
988 LONG x = 0;
989 HWND hwndBefore;
990 SWP swp;
991
992 if (!lIndex)
993 x = TB_LEFT_SPACING;
994 else
995 {
996 if ( (lIndex > 0)
997 && (hwndBefore = (HWND)lstItemFromIndex(&pData->llControls,
998 lIndex))
999 && (WinQueryWindowPos(hwndBefore, &swp))
1000 )
1001 {
1002 x = swp.x + swp.cx + pData->lSpacing;
1003 }
1004 }
1005
1006 if (x)
1007 {
1008 PPRESPARAMS ppp = NULL;
1009
1010 PCSZ pcszFont = winhQueryDefaultFont();
1011 LONG lColor;
1012
1013 winhStorePresParam(&ppp,
1014 PP_FONTNAMESIZE,
1015 strlen(pcszFont) + 1,
1016 (PVOID)pcszFont);
1017
1018 lColor = winhQueryPresColor2(pData->dwd.hwnd,
1019 PP_BACKGROUNDCOLOR,
1020 PP_BACKGROUNDCOLORINDEX,
1021 FALSE,
1022 scsToolbar.lBackIndex);
1023 winhStorePresParam(&ppp,
1024 PP_BACKGROUNDCOLOR,
1025 sizeof(lColor),
1026 &lColor);
1027
1028 lColor = winhQueryPresColor2(pData->dwd.hwnd,
1029 PP_FOREGROUNDCOLOR,
1030 PP_FOREGROUNDCOLORINDEX,
1031 FALSE,
1032 scsToolbar.lForeIndex);
1033 winhStorePresParam(&ppp,
1034 PP_FOREGROUNDCOLOR,
1035 sizeof(lColor),
1036 &lColor);
1037
1038 // create controls
1039 for (ul = 0;
1040 ul < cControls;
1041 ++ul)
1042 {
1043 HWND hwndControl;
1044
1045 if (hwndControl = CreateToolbarControl(pData,
1046 &paControls[ul],
1047 &x,
1048 ppp))
1049 {
1050 lstInsertItemBefore(&pData->llControls,
1051 (PVOID)hwndControl,
1052 lIndex++);
1053 ++cCreated;
1054 }
1055 else
1056 break;
1057 }
1058
1059 if (ppp)
1060 free(ppp);
1061 }
1062
1063 pData->lMaxControlCY = 0;
1064 ReformatControls(pData->dwd.hwnd);
1065
1066 if (pData->flReformat & RFFL_HEIGHT)
1067 {
1068 if (WinQueryWindowULong(pData->dwd.hwnd, QWL_STYLE) & TBS_AUTORESIZE)
1069 {
1070 WinQueryWindowPos(pData->dwd.hwnd, &swp);
1071 WinSetWindowPos(pData->dwd.hwnd,
1072 0,
1073 0,
1074 0,
1075 swp.cx,
1076 pData->lMaxControlCY + 2 * TB_BOTTOM_SPACING,
1077 SWP_SIZE);
1078
1079 ReformatControls(pData->dwd.hwnd);
1080
1081 ctlPostWmControl(pData->dwd.hwnd,
1082 TBN_RESIZED,
1083 0);
1084
1085 pData->flReformat &= ~RFFL_HEIGHT;
1086 }
1087 }
1088
1089 return cCreated;
1090}
1091
1092/*
1093 *@@ TbCreate:
1094 *
1095 */
1096
1097STATIC MRESULT TbCreate(HWND hwndToolBar, MPARAM mp1, MPARAM mp2)
1098{
1099 PTOOLBARDATA pData;
1100 PTOOLBARCDATA ptbcd = (PTOOLBARCDATA)mp1;
1101
1102 if (!(pData = NEW(TOOLBARDATA)))
1103 return (MRESULT)TRUE; // stop window creation
1104
1105 WinSetWindowPtr(hwndToolBar, QWL_USER + 1, pData);
1106 ZERO(pData);
1107
1108 // initialize DEFWINDOWDATA
1109 ctlInitDWD(hwndToolBar,
1110 mp2,
1111 &pData->dwd,
1112 WinDefWindowProc,
1113 &scsToolbar);
1114
1115 pData->hwndControlsOwner = ptbcd->hwndControlsOwner;
1116 pData->lSpacing = 5;
1117 lstInit(&pData->llControls, FALSE);
1118
1119 xstrInit(&pData->strToolTipBuf, 0);
1120
1121 if (((PCREATESTRUCT)mp2)->flStyle & TBS_TOOLTIPS)
1122 {
1123 pData->hwndToolTip = WinCreateWindow(HWND_DESKTOP,
1124 WC_CCTL_TOOLTIP,
1125 NULL,
1126 TTS_ALWAYSTIP,
1127 0, 0, 0, 0, // window pos and size, ignored
1128 hwndToolBar,
1129 NULLHANDLE, // hwndInsertBehind, ignored
1130 0,
1131 NULL, // control data
1132 NULL); // presparams
1133 }
1134
1135 if ( (ptbcd->cControls)
1136 && (ptbcd->patbc)
1137 )
1138 {
1139 TbAddControls(pData,
1140 ptbcd->cControls,
1141 ptbcd->patbc,
1142 0);
1143 }
1144
1145 return (MRESULT)FALSE;
1146}
1147
1148/*
1149 *@@ TbDestroy:
1150 *
1151 */
1152
1153STATIC VOID TbDestroy(HWND hwndToolBar)
1154{
1155 PTOOLBARDATA pData;
1156 if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
1157 {
1158 PLISTNODE pNode;
1159
1160 if (pData->hwndToolTip)
1161 WinDestroyWindow(pData->hwndToolTip);
1162
1163 FOR_ALL_NODES(&pData->llControls, pNode)
1164 {
1165 WinDestroyWindow((HWND)pNode->pItemData);
1166 }
1167 lstClear(&pData->llControls);
1168
1169 xstrClear(&pData->strToolTipBuf);
1170
1171 free(pData);
1172 }
1173}
1174
1175/*
1176 *@@ ctl_fnwpToolbar:
1177 * window proc for the tool bar class.
1178 *
1179 * The tool bar understands the following messages:
1180 *
1181 * -- TBM_ADDCONTROLS
1182 */
1183
1184MRESULT EXPENTRY ctl_fnwpToolbar(HWND hwndToolBar, ULONG msg, MPARAM mp1, MPARAM mp2)
1185{
1186 MRESULT mrc = 0;
1187 PTOOLBARDATA pData;
1188
1189 switch (msg)
1190 {
1191 case WM_CREATE:
1192 TbCreate(hwndToolBar, mp1, mp2);
1193 break;
1194
1195 /*
1196 *@@ TBM_ADDCONTROLS:
1197 * adds new controls to the tool bar.
1198 *
1199 * Parameters:
1200 *
1201 * -- PTOOLBARCONTROL mp1: array of TOOLBARCONTROL structs
1202 * which specify the windows to add to the tool bar.
1203 *
1204 * -- SHORT1FROMMP(mp2): number of items in that array
1205 * (not array size).
1206 *
1207 * -- SHORT2FROMMP(mp2): index where to add the new controls.
1208 * 0 means leftmost, 1 before the second item, and so on.
1209 * -1 means add rightmost.
1210 *
1211 * The tool bar will automatically repaint itself. If it
1212 * also has the TBS_AUTORESIZE window style, it will even
1213 * automatically resize itself and post its owner a
1214 * WM_CONTROL with TBN_RESIZED message so it can adjust
1215 * itself.
1216 */
1217
1218 case TBM_ADDCONTROLS:
1219 if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
1220 {
1221 TbAddControls(pData,
1222 SHORT1FROMMP(mp2),
1223 (PTOOLBARCONTROL)mp1,
1224 SHORT2FROMMP(mp2));
1225 }
1226 break;
1227
1228 case WM_CONTROL:
1229 if ( (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
1230 && (pData->hwndToolTip)
1231 && (SHORT2FROMMP(mp1) == TTN_NEEDTEXT)
1232 )
1233 {
1234 PTOOLTIPTEXT pttt = (PTOOLTIPTEXT)mp2;
1235 PSZ psz;
1236 xstrClear(&pData->strToolTipBuf);
1237
1238 if (psz = winhQueryWindowText(pttt->hwndTool))
1239 xstrset(&pData->strToolTipBuf, psz);
1240
1241 pttt->ulFormat = TTFMT_PSZ;
1242 pttt->pszText = pData->strToolTipBuf.psz;
1243 }
1244 break;
1245
1246 case WM_PAINT:
1247 {
1248 HPS hps;
1249 RECTL rcl;
1250 POINTL ptl;
1251 if (hps = WinBeginPaint(hwndToolBar, NULLHANDLE, &rcl))
1252 {
1253 gpihSwitchToRGB(hps);
1254 rcl.yBottom += 2;
1255 WinFillRect(hps,
1256 &rcl,
1257 winhQueryPresColor2(hwndToolBar,
1258 PP_BACKGROUNDCOLOR,
1259 PP_BACKGROUNDCOLORINDEX,
1260 FALSE,
1261 SYSCLR_MENU));
1262 ptl.x = 0;
1263 ptl.y = 0;
1264 GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONLIGHT, 0));
1265 GpiMove(hps, &ptl);
1266 ptl.x = rcl.xRight;
1267 GpiLine(hps, &ptl);
1268 ptl.x = 0;
1269 ptl.y = 1;
1270 GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONDARK, 0));
1271 GpiMove(hps, &ptl);
1272 ptl.x = rcl.xRight;
1273 GpiLine(hps, &ptl);
1274
1275 WinEndPaint(hps);
1276 }
1277 }
1278 break;
1279
1280 case WM_DESTROY:
1281 TbDestroy(hwndToolBar);
1282 break;
1283
1284 default:
1285 if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
1286 mrc = ctlDefWindowProc(&pData->dwd, msg, mp1, mp2);
1287 }
1288
1289 return mrc;
1290}
1291
1292/*
1293 *@@ ctlRegisterToolbar:
1294 * this registers the tool bar window class (ctl_fnwpToolbar)
1295 * _and_ the tool bar button control (ctl_fnwpToolbarButton)
1296 * for an application. This is required before the tool bar
1297 * control can be used.
1298 */
1299
1300BOOL ctlRegisterToolbar(HAB hab)
1301{
1302 return ( WinRegisterClass(hab,
1303 WC_CCTL_TOOLBAR,
1304 ctl_fnwpToolbar,
1305 CS_SYNCPAINT | /* CS_CLIPSIBLINGS | */ CS_CLIPCHILDREN,
1306 sizeof(PVOID) * 2) // addt'l bytes to reserve:
1307 // one pointer for QWL_USER,
1308 // one more for instance data
1309 && WinRegisterClass(hab,
1310 WC_CCTL_TBBUTTON,
1311 ctl_fnwpToolbarButton,
1312 CS_SYNCPAINT | CS_CLIPSIBLINGS | CS_CLIPCHILDREN,
1313 sizeof(PVOID) * 2) // addt'l bytes to reserve:
1314 // one pointer for QWL_USER,
1315 // one more for instance data
1316 );
1317}
1318
1319/*
1320 *@@ ctlCreateToolBar:
1321 * type-safe wrapper around WinCreateWindow to create a tool bar.
1322 *
1323 * The easiest way to create a tool bar completely with the tools
1324 * is to pass them as an array of TOOLBARCONTROL structs here,
1325 * which simply specify the window classes to create. In most
1326 * cases, you will want to add tools of the WC_CCTL_TBBUTTON
1327 * and WC_SEPARATORLINE classes.
1328 *
1329 * Keep in mind to call ctlRegisterToolbar and ctlRegisterSeparatorLine
1330 * first, or window creation will fail.
1331 */
1332
1333HWND ctlCreateToolBar(HWND hwndParent, // in: parent of tool bar (e.g. frame)
1334 HWND hwndOwner, // in: owner of tool bar itself (e.g. frame)
1335 ULONG flStyle, // in: window style (WS_VISIBLE | TBS_* flags)
1336 HWND hwndControlsOwner, // in: owner for tool bar controls (e.g. frame client)
1337 ULONG cControls,
1338 PTOOLBARCONTROL patbc)
1339{
1340 TOOLBARCDATA tbcd;
1341 memset(&tbcd, 0, sizeof(tbcd));
1342 tbcd.cb = sizeof(tbcd);
1343 tbcd.hwndControlsOwner = hwndControlsOwner;
1344 tbcd.cControls = cControls;
1345 tbcd.patbc = patbc;
1346
1347 return WinCreateWindow(hwndParent,
1348 WC_CCTL_TOOLBAR,
1349 NULL,
1350 flStyle,
1351 0,
1352 0,
1353 0,
1354 0,
1355 hwndOwner,
1356 HWND_BOTTOM,
1357 FID_TOOLBAR,
1358 &tbcd,
1359 NULL);
1360}
1361
Note: See TracBrowser for help on using the repository browser.