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

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

New build system, multimedia, other misc fixes.

  • 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 = ctlQueryColor(&pbd->dwd, CTLCOL_BGND); // 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,
347 ctlQueryColor(&pbd->dwd, CTLCOL_FGND)); // pbd->dwd.lcolForeground);
348 rclTemp.yTop -= 2 * TBB_TEXTSPACING + lOfs;
349 rclTemp.xRight += 2 * lOfs; // twice the offset because we center horizontally
350 winhDrawFormattedText(hps,
351 &rclTemp,
352 pbd->dwd.pszText,
353 DT_CENTER | DT_TOP | DT_MNEMONIC | DT_WORDBREAK);
354 }
355}
356
357/* static const SYSCOLORSET G_scsToolbarButton =
358 {
359 TRUE, // inherit presparams
360
361 SYSCLR_BUTTONMIDDLE,
362 SYSCLR_MENUTEXT
363 };
364*/
365
366static const CCTLCOLOR G_scsToolbarButton[] =
367 {
368 TRUE, PP_BACKGROUNDCOLOR, SYSCLR_BUTTONMIDDLE,
369 TRUE, PP_FOREGROUNDCOLOR, SYSCLR_MENUTEXT,
370 };
371
372/*
373 *@@ BtnAutoSize:
374 *
375 */
376
377STATIC VOID BtnAutoSize(ULONG flStyle,
378 PTBBUTTONDATA pData)
379{
380 if (flStyle & TBBS_AUTORESIZE)
381 {
382 HPS hps;
383 if (hps = WinGetPS(pData->bd.dwd.hwnd))
384 {
385 ULONG ulBorder = 2;
386 if (!(flStyle & TBBS_FLAT))
387 ulBorder += TBB_BORDER;
388
389 pData->bd.dwd.szlWin.cx
390 = pData->bd.dwd.szlWin.cy
391 = 2 * ulBorder;
392
393 if (flStyle & (TBBS_BIGICON | TBBS_MINIICON | TBBS_BITMAP))
394 {
395 pData->bd.dwd.szlWin.cx += pData->bd.szlIconOrBitmap.cx;
396 pData->bd.dwd.szlWin.cy += pData->bd.szlIconOrBitmap.cy;
397
398 if (flStyle & TBBS_TEXT)
399 pData->bd.dwd.szlWin.cx += 2 * TBB_TEXTSPACING;
400 }
401
402 if (flStyle & TBBS_TEXT)
403 {
404 RECTL rcl = { 0, 0, 100, 100 };
405 LONG lMinX;
406 winhDrawFormattedText(hps,
407 &rcl,
408 pData->bd.dwd.pszText,
409 DT_TOP | DT_LEFT | DT_QUERYEXTENT);
410
411 lMinX = (rcl.xRight - rcl.xLeft) + 2 * TBB_TEXTSPACING;
412 STOREIFMAX(lMinX, pData->bd.dwd.szlWin.cx);
413 // pData->bd.dwd.rcl.xRight += (rcl.xRight - rcl.xLeft) + 2 * TBB_TEXTSPACING;
414 pData->bd.dwd.szlWin.cy += (rcl.yTop - rcl.yBottom) + 2 * TBB_TEXTSPACING;
415 }
416
417 WinSetWindowPos(pData->bd.dwd.hwnd,
418 0,
419 0,
420 0,
421 pData->bd.dwd.szlWin.cx,
422 pData->bd.dwd.szlWin.cy,
423 SWP_SIZE | SWP_NOADJUST);
424
425 WinReleasePS(hps);
426 }
427 }
428}
429
430/*
431 *@@ BtnCreate:
432 * implementation for WM_CREATE in ctl_fnwpToolbarButton.
433 */
434
435STATIC MRESULT BtnCreate(HWND hwndButton, MPARAM mp1, MPARAM mp2)
436{
437 PTBBUTTONDATA pData;
438 MRESULT mrc = 0;
439
440 if (!(pData = NEW(TBBUTTONDATA)))
441 mrc = (MRESULT)TRUE; // stop window creation
442 else
443 {
444 PCSZ pcszText = ((PCREATESTRUCT)mp2)->pszText;
445 PSZ p;
446
447 WinSetWindowPtr(hwndButton, QWL_USER + 1, pData);
448 ZERO(pData);
449
450 // initialize DEFWINDOWDATA
451 ctlInitDWD(hwndButton,
452 mp2,
453 &pData->bd.dwd,
454 WinDefWindowProc,
455 0,
456 G_scsToolbarButton,
457 ARRAYITEMCOUNT(G_scsToolbarButton));
458
459 if ( (pcszText)
460 && (*pcszText == '#')
461 )
462 {
463 pData->bd.hptr = atoi(pcszText + 1);
464 if (p = strchr(pcszText + 1, '#'))
465 pcszText = p + 1;
466 else
467 pcszText = NULL;
468 }
469
470 ctlInitXButtonData(&pData->bd,
471 ((PCREATESTRUCT)mp2)->flStyle);
472
473 if (pcszText)
474 {
475 pData->bd.dwd.pszText = strdup(pcszText);
476 if ( (((PCREATESTRUCT)mp2)->flStyle & TBBS_DROPMNEMONIC)
477 && (p = strchr(pData->bd.dwd.pszText, '~'))
478 )
479 memmove(p, p + 1, strlen(p));
480 }
481
482 BtnAutoSize(((PCREATESTRUCT)mp2)->flStyle,
483 pData);
484
485 if ( (((PCREATESTRUCT)mp2)->flStyle & TBBS_CHECKINITIAL)
486 && (((PCREATESTRUCT)mp2)->flStyle & (TBBS_CHECK | TBBS_RADIO))
487 )
488 pData->bs.fPaintButtonSunk = TRUE;
489 }
490
491 return mrc;
492}
493
494/*
495 *@@ BtnButton1Down:
496 * implementation for WM_BUTTON1DOWN in ctl_fnwpToolbarButton.
497 */
498
499STATIC MRESULT BtnButton1Down(HWND hwndButton)
500{
501 PTBBUTTONDATA pData;
502 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
503 {
504 ULONG flStyle = winhQueryWindowStyle(hwndButton);
505
506 if (flStyle & WS_DISABLED)
507 WinAlarm(HWND_DESKTOP, WA_WARNING);
508 else
509 {
510 WinSetFocus(HWND_DESKTOP, hwndButton);
511
512 if (!pData->bs.fMouseCaptured)
513 {
514 // capture mouse events while the
515 // mouse button is down
516 WinSetCapture(HWND_DESKTOP, hwndButton);
517 pData->bs.fMouseCaptured = TRUE;
518 }
519
520 if (!pData->bs.fPaintButtonSunk)
521 {
522 // toggle state is still UP (i.e. button pressed
523 // for the first time): create menu
524 pData->bs.fPaintButtonSunk = TRUE;
525 WinInvalidateRect(hwndButton, NULL, FALSE);
526
527 if (flStyle & TBBS_CHECK)
528 // ignore the next button 1 up
529 pData->bs.fIgnoreMB1Up = TRUE;
530
531 } // end if (!pData->fButtonSunk)
532 }
533 }
534
535 return (MRESULT)TRUE; // processed
536}
537
538/*
539 *@@ ClickNotifyOwner:
540 *
541 */
542
543STATIC VOID ClickNotifyOwner(HWND hwndButton)
544{
545 HWND hwndOwner;
546
547 if (hwndOwner = WinQueryWindow(hwndButton, QW_OWNER))
548 {
549 ULONG flStyle = winhQueryWindowStyle(hwndButton);
550
551 if (flStyle & (TBBS_COMMAND | TBBS_SYSCOMMAND))
552 WinPostMsg(hwndOwner,
553 (flStyle & TBBS_SYSCOMMAND)
554 ? WM_SYSCOMMAND
555 : WM_COMMAND,
556 (MPARAM)WinQueryWindowUShort(hwndButton, QWS_ID),
557 MPFROM2SHORT(CMDSRC_PUSHBUTTON,
558 TRUE)); // pointer, not keyboard
559 else
560 ctlSendWmControl(hwndButton,
561 BN_CLICKED,
562 (MPARAM)hwndButton);
563 }
564}
565
566/*
567 *@@ BtnButton1Up:
568 * implementation for WM_BUTTON1UP in ctl_fnwpToolbarButton.
569 */
570
571STATIC MRESULT BtnButton1Up(HWND hwndButton)
572{
573 PTBBUTTONDATA pData;
574 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
575 {
576 ULONG flStyle = winhQueryWindowStyle(hwndButton);
577
578 if (pData->bs.fMouseCaptured)
579 {
580 WinSetCapture(HWND_DESKTOP, NULLHANDLE);
581 pData->bs.fMouseCaptured = FALSE;
582 }
583
584 if (!(flStyle & WS_DISABLED))
585 {
586 pData->bs.fMB1Pressed = FALSE;
587
588 if (flStyle & TBBS_CHECK)
589 {
590 if (pData->bs.fIgnoreMB1Up)
591 pData->bs.fIgnoreMB1Up = FALSE;
592 else
593 pData->bs.fPaintButtonSunk = FALSE;
594
595 WinInvalidateRect(hwndButton, NULL, FALSE);
596
597 ClickNotifyOwner(hwndButton);
598 }
599 else if (flStyle & TBBS_RADIO)
600 {
601 WinSendMsg(hwndButton,
602 TBBM_CHECK,
603 (MPARAM)1,
604 0);
605
606 ClickNotifyOwner(hwndButton);
607 }
608 else
609 {
610 pData->bs.fPaintButtonSunk = FALSE;
611
612 ClickNotifyOwner(hwndButton);
613
614 WinInvalidateRect(hwndButton, NULL, FALSE);
615 }
616 }
617 }
618
619 return (MRESULT)TRUE; // processed
620}
621
622#define IGNORE_CHECK_MAGIC 0x87678a1d
623
624/*
625 *@@ UncheckOthers:
626 * gets called twice from BtnCheck for radio buttons
627 * to uncheck the others in the group.
628 */
629
630STATIC VOID BtnUncheckOthers(HWND hwndButton,
631 ULONG ulQW) // in: QW_PREV or QW_NEXT
632{
633 HWND hwnd = hwndButton;
634 CHAR szClass[50];
635
636 while (hwnd = WinQueryWindow(hwnd, ulQW))
637 {
638 if ( (!WinQueryClassName(hwnd, sizeof(szClass), szClass)
639 || (strcmp(szClass, WC_CCTL_TBBUTTON))
640 || (!(winhQueryWindowStyle(hwnd) & TBBS_RADIO)))
641 )
642 break;
643
644 WinSendMsg(hwnd,
645 TBBM_CHECK,
646 (MPARAM)FALSE,
647 (MPARAM)IGNORE_CHECK_MAGIC); // force uncheck without resending
648 }
649}
650
651/*
652 *@@ BtnCheck:
653 * implementation for TBBM_CHECK in ctl_fnwpToolbarButton.
654 */
655
656STATIC VOID BtnCheck(HWND hwndButton,
657 BOOL fCheck,
658 ULONG ulMagic)
659{
660 PTBBUTTONDATA pData;
661
662 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
663 {
664 ULONG flStyle = winhQueryWindowStyle(hwndButton);
665
666 if ( (flStyle & TBBS_CHECK)
667 || (ulMagic == IGNORE_CHECK_MAGIC)
668 // magic code sent to radio tool bar buttons to
669 // force an uncheck without resending
670 )
671 {
672 pData->bs.fPaintButtonSunk = fCheck;
673 }
674 else if (flStyle & TBBS_RADIO)
675 {
676 BtnUncheckOthers(hwndButton, QW_PREV);
677 BtnUncheckOthers(hwndButton, QW_NEXT);
678
679 pData->bs.fPaintButtonSunk = TRUE;
680 }
681
682 WinInvalidateRect(hwndButton, NULL, FALSE);
683 }
684}
685
686/*
687 *@@ ctl_fnwpToolbarButton:
688 * window proc for the tool bar button control.
689 *
690 * This control is not at all based on the ugly OS/2 button
691 * control, but a complete rewrite. This supports a large
692 * variety of TBBS_* style flags which are useful in the
693 * context of a tool bar.
694 *
695 * The following styles are supported:
696 *
697 * -- WS_DISABLED @@todo halftone the display
698 *
699 * -- optionally one of TBBS_BIGICON, TBBS_MINIICON, or
700 * TBBS_BITMAP to paint a picture in the control
701 *
702 * -- optionally TBBS_TEXT; you can use this alone or
703 * together with one of the picture styles
704 *
705 * -- TBBS_CHECK: if set, button toggles between pressed
706 * and released on every click ("checkbox" style,
707 * even though it still looks as a button).
708 *
709 * -- TBBS_RADIO: if set, the button assumes it is part of
710 * a group and behaves like a radio button, that is, it
711 * automatically unchecks its sibling buttons which have
712 * this style too.
713 *
714 * -- TBBS_AUTORESIZE: if set, the button automatically
715 * resizes itself to the space it needs when its style
716 * or text changes.
717 *
718 * -- TBBS_HILITE: if set, the button hilites when mouse
719 * moves over it.
720 *
721 * -- TBBS_FLAT: if set, the button paints a border only
722 * if the mouse is moving over it; if not set, it
723 * always has a (thicker) border.
724 *
725 * -- TBBS_COMMAND, TBBS_SYSCOMMAND: if none of these are
726 * set, the button _sends_ WM_CONTROL with the standard
727 * button code BN_CLICKED to its owner when it is
728 * pressed. If TBBS_COMMAND is set, the button _posts_
729 * WM_COMMAND instead; if TBBS_SYSCOMMAND is set, the
730 * button posts WM_SYSCOMMAND instead.
731 *
732 * Note that this is different from the standard button
733 * behavior: even a tool bar button that does not have
734 * the TBBS_CHECK or TBBS_RADIO styles will only post
735 * WM_COMMAND if the TBBS_COMMAND style is set.
736 *
737 * There are two ways to set the icon or bitmap to be
738 * displayed with the control:
739 *
740 * -- Pass it with the window title on tool bar creation in
741 * the form "#handle#text", where "handle" is the decimal
742 * HPOINTER or HBITMAP and "text" is the actual button
743 * text. Note that this only works on creation, not with
744 * WinSetWindowText after creation.
745 *
746 * -- Send a XBBM_SETHANDLE message after button creation.
747 */
748
749MRESULT EXPENTRY ctl_fnwpToolbarButton(HWND hwndButton, ULONG msg, MPARAM mp1, MPARAM mp2)
750{
751 MRESULT mrc = 0;
752 PTBBUTTONDATA pData;
753
754 switch (msg)
755 {
756 case WM_CREATE:
757 mrc = BtnCreate(hwndButton, mp1, mp2);
758 break;
759
760 case WM_BUTTON1DOWN:
761 mrc = BtnButton1Down(hwndButton);
762 break;
763
764 case WM_BUTTON1UP:
765 mrc = BtnButton1Up(hwndButton);
766 break;
767
768 case WM_MOUSEENTER:
769 case WM_MOUSELEAVE:
770 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
771 {
772 BOOL fMouseOver = (msg == WM_MOUSEENTER);
773 if (fMouseOver != pData->bs.fMouseOver)
774 {
775 pData->bs.fMouseOver = fMouseOver;
776
777 if (winhQueryWindowStyle(hwndButton) & TBBS_HILITE)
778 WinInvalidateRect(hwndButton, NULL, FALSE);
779 }
780 }
781 break;
782
783 case WM_PAINT:
784 {
785 HPS hps;
786 RECTL rcl;
787 POINTL ptl;
788 if (hps = WinBeginPaint(hwndButton, NULLHANDLE, &rcl))
789 {
790 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
791 {
792 gpihSwitchToRGB(hps);
793 ctlPaintTBButton(hps,
794 winhQueryWindowStyle(hwndButton)
795 | TBBS_BACKGROUND,
796 &pData->bd,
797 &pData->bs);
798 }
799
800 WinEndPaint(hps);
801 }
802 }
803 break;
804
805 /*
806 *@@ TBBM_CHECK:
807 * checks the given button. Effect depends on the
808 * button style:
809 *
810 * -- With TBBS_CHECK, this sets the button check
811 * state to (BOOL)mp1.
812 *
813 * -- With TBBS_CHECKGROUP, this sets the current
814 * button check state and unchecks neighboring
815 * buttons (siblings) that have the same style.
816 * mp1 is ignored.
817 *
818 * As opposed to a check in response to a mouse
819 * event, this does _not_ send out the BN_CLICKED
820 * notification.
821 */
822
823 case TBBM_CHECK:
824 BtnCheck(hwndButton, (BOOL)mp1, (ULONG)mp2);
825 break;
826
827 /*
828 *@@ TBBM_QUERYCHECK:
829 * returns the current check status of a button
830 * with TBBS_CHECK or TBBS_CHECKGROUP style
831 * as TRUE or FALSE.
832 *
833 * No parameters.
834 */
835
836 case TBBM_QUERYCHECK:
837 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
838 mrc = (MRESULT)pData->bs.fPaintButtonSunk;
839 break;
840
841 case WM_DESTROY:
842 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
843 {
844 FREE(pData->bd.dwd.pszText);
845 free(pData);
846 }
847 break;
848
849 default:
850 if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1))
851 mrc = ctlDefWindowProc(&pData->bd.dwd, msg, mp1, mp2);
852 }
853
854 return mrc;
855}
856
857/* ******************************************************************
858 *
859 * "Toolbar" control
860 *
861 ********************************************************************/
862
863static const CCTLCOLOR G_scsToolbar[] =
864 {
865 TRUE, PP_BACKGROUNDCOLOR, SYSCLR_MENU,
866 TRUE, PP_FOREGROUNDCOLOR, SYSCLR_MENUTEXT
867 };
868
869/*
870 *@@ CreateToolbarControl:
871 *
872 */
873
874STATIC HWND CreateToolbarControl(PTOOLBARDATA pData,
875 PTOOLBARCONTROL pControl,
876 PLONG px,
877 PPRESPARAMS ppp)
878{
879 HWND hwndControl;
880
881 if (hwndControl = WinCreateWindow(pData->dwd.hwnd,
882 (PSZ)pControl->pcszClass,
883 (PSZ)pControl->pcszTitle,
884 pControl->flStyle,
885 *px,
886 TB_BOTTOM_SPACING,
887 pControl->cx,
888 pControl->cy,
889 pData->hwndControlsOwner,
890 HWND_TOP,
891 pControl->id,
892 NULL,
893 ppp))
894 {
895 *px += pControl->cx + pData->lSpacing;
896
897 if (pData->hwndToolTip)
898 {
899 TOOLINFO ti = {0};
900 ti.ulFlags = TTF_CENTER_X_ON_TOOL | TTF_POS_Y_BELOW_TOOL | TTF_SUBCLASS;
901 ti.hwndToolOwner = pData->dwd.hwnd;
902 ti.pszText = PSZ_TEXTCALLBACK;
903 ti.hwndTool = hwndControl;
904 WinSendMsg(pData->hwndToolTip,
905 TTM_ADDTOOL,
906 (MPARAM)0,
907 &ti);
908 }
909 }
910
911 return hwndControl;
912}
913
914/*
915 *@@ ReformatControls:
916 *
917 */
918
919STATIC VOID ReformatControls(HWND hwndToolBar)
920{
921 PTOOLBARDATA pData;
922 if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
923 {
924 LONG x = TB_LEFT_SPACING;
925 PLISTNODE pNode;
926 LONG cControls;
927 PSWP paswp,
928 pswpThis;
929
930 if ( (cControls = lstCountItems(&pData->llControls))
931 && (paswp = (PSWP)malloc(sizeof(SWP) * cControls))
932 )
933 {
934 BOOL rc;
935
936 pswpThis = paswp;
937
938 FOR_ALL_NODES(&pData->llControls, pNode)
939 {
940 HWND hwnd = (HWND)pNode->pItemData;
941 SWP swp;
942 CHAR szClass[50];
943
944 WinQueryWindowPos(hwnd, &swp);
945
946 if ( (WinQueryClassName(hwnd, sizeof(szClass), szClass))
947 && (!strcmp(szClass, WC_CCTL_SEPARATOR))
948 )
949 {
950 pswpThis->cy = pData->lMaxControlCY;
951 pswpThis->fl = SWP_MOVE | SWP_SIZE;
952 }
953 else
954 {
955 pswpThis->cy = swp.cy;
956 pswpThis->fl = SWP_MOVE;
957 }
958
959 pswpThis->cx = swp.cx;
960 pswpThis->y = TB_BOTTOM_SPACING;
961 pswpThis->x = x;
962 pswpThis->hwndInsertBehind = HWND_BOTTOM;
963 pswpThis->hwnd = hwnd;
964
965 x += swp.cx + pData->lSpacing;
966
967 if (swp.cy > pData->lMaxControlCY)
968 {
969 pData->lMaxControlCY = swp.cy;
970 pData->flReformat = RFFL_HEIGHT;
971 }
972
973 pswpThis++;
974 }
975
976 rc = WinSetMultWindowPos(pData->dwd.hab,
977 paswp,
978 cControls);
979
980 free(paswp);
981 }
982 }
983}
984
985/*
986 *@@ TbAddControls:
987 *
988 */
989
990STATIC ULONG TbAddControls(PTOOLBARDATA pData,
991 ULONG cControls,
992 PTOOLBARCONTROL paControls,
993 LONG lIndex) // in: index before which to add entries; -1 means rightmost
994{
995 ULONG ul,
996 cCreated = 0;
997
998 LONG x = 0;
999 HWND hwndBefore;
1000 SWP swp;
1001
1002 if (!lIndex)
1003 x = TB_LEFT_SPACING;
1004 else
1005 {
1006 if ( (lIndex > 0)
1007 && (hwndBefore = (HWND)lstItemFromIndex(&pData->llControls,
1008 lIndex))
1009 && (WinQueryWindowPos(hwndBefore, &swp))
1010 )
1011 {
1012 x = swp.x + swp.cx + pData->lSpacing;
1013 }
1014 }
1015
1016 if (x)
1017 {
1018 PPRESPARAMS ppp = NULL;
1019
1020 PCSZ pcszFont = winhQueryDefaultFont();
1021 LONG lColor;
1022
1023 winhStorePresParam(&ppp,
1024 PP_FONTNAMESIZE,
1025 strlen(pcszFont) + 1,
1026 (PVOID)pcszFont);
1027
1028 lColor = ctlQueryColor(&pData->dwd, CTLCOL_BGND);
1029 winhStorePresParam(&ppp,
1030 PP_BACKGROUNDCOLOR,
1031 sizeof(lColor),
1032 &lColor);
1033
1034 lColor = ctlQueryColor(&pData->dwd, CTLCOL_FGND);
1035 winhStorePresParam(&ppp,
1036 PP_FOREGROUNDCOLOR,
1037 sizeof(lColor),
1038 &lColor);
1039
1040 // create controls
1041 for (ul = 0;
1042 ul < cControls;
1043 ++ul)
1044 {
1045 HWND hwndControl;
1046
1047 if (hwndControl = CreateToolbarControl(pData,
1048 &paControls[ul],
1049 &x,
1050 ppp))
1051 {
1052 lstInsertItemBefore(&pData->llControls,
1053 (PVOID)hwndControl,
1054 lIndex++);
1055 ++cCreated;
1056 }
1057 else
1058 break;
1059 }
1060
1061 if (ppp)
1062 free(ppp);
1063 }
1064
1065 pData->lMaxControlCY = 0;
1066 ReformatControls(pData->dwd.hwnd);
1067
1068 if (pData->flReformat & RFFL_HEIGHT)
1069 {
1070 if (WinQueryWindowULong(pData->dwd.hwnd, QWL_STYLE) & TBS_AUTORESIZE)
1071 {
1072 WinQueryWindowPos(pData->dwd.hwnd, &swp);
1073 WinSetWindowPos(pData->dwd.hwnd,
1074 0,
1075 0,
1076 0,
1077 swp.cx,
1078 pData->lMaxControlCY + 2 * TB_BOTTOM_SPACING,
1079 SWP_SIZE);
1080
1081 ReformatControls(pData->dwd.hwnd);
1082
1083 ctlPostWmControl(pData->dwd.hwnd,
1084 TBN_RESIZED,
1085 0);
1086
1087 pData->flReformat &= ~RFFL_HEIGHT;
1088 }
1089 }
1090
1091 return cCreated;
1092}
1093
1094/*
1095 *@@ TbCreate:
1096 *
1097 */
1098
1099STATIC MRESULT TbCreate(HWND hwndToolBar, MPARAM mp1, MPARAM mp2)
1100{
1101 PTOOLBARDATA pData;
1102 PTOOLBARCDATA ptbcd = (PTOOLBARCDATA)mp1;
1103
1104 if (!(pData = NEW(TOOLBARDATA)))
1105 return (MRESULT)TRUE; // stop window creation
1106
1107 WinSetWindowPtr(hwndToolBar, QWL_USER + 1, pData);
1108 ZERO(pData);
1109
1110 // initialize DEFWINDOWDATA
1111 ctlInitDWD(hwndToolBar,
1112 mp2,
1113 &pData->dwd,
1114 WinDefWindowProc,
1115 0,
1116 G_scsToolbar,
1117 ARRAYITEMCOUNT(G_scsToolbar));
1118
1119 pData->hwndControlsOwner = ptbcd->hwndControlsOwner;
1120 pData->lSpacing = 5;
1121 lstInit(&pData->llControls, FALSE);
1122
1123 xstrInit(&pData->strToolTipBuf, 0);
1124
1125 if (((PCREATESTRUCT)mp2)->flStyle & TBS_TOOLTIPS)
1126 {
1127 pData->hwndToolTip = WinCreateWindow(HWND_DESKTOP,
1128 WC_CCTL_TOOLTIP,
1129 NULL,
1130 TTS_ALWAYSTIP,
1131 0, 0, 0, 0, // window pos and size, ignored
1132 hwndToolBar,
1133 NULLHANDLE, // hwndInsertBehind, ignored
1134 0,
1135 NULL, // control data
1136 NULL); // presparams
1137 }
1138
1139 if ( (ptbcd->cControls)
1140 && (ptbcd->patbc)
1141 )
1142 {
1143 TbAddControls(pData,
1144 ptbcd->cControls,
1145 ptbcd->patbc,
1146 0);
1147 }
1148
1149 return (MRESULT)FALSE;
1150}
1151
1152/*
1153 *@@ TbDestroy:
1154 *
1155 */
1156
1157STATIC VOID TbDestroy(HWND hwndToolBar)
1158{
1159 PTOOLBARDATA pData;
1160 if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
1161 {
1162 PLISTNODE pNode;
1163
1164 if (pData->hwndToolTip)
1165 WinDestroyWindow(pData->hwndToolTip);
1166
1167 FOR_ALL_NODES(&pData->llControls, pNode)
1168 {
1169 WinDestroyWindow((HWND)pNode->pItemData);
1170 }
1171 lstClear(&pData->llControls);
1172
1173 xstrClear(&pData->strToolTipBuf);
1174
1175 free(pData);
1176 }
1177}
1178
1179/*
1180 *@@ ctl_fnwpToolbar:
1181 * window proc for the tool bar class.
1182 *
1183 * The tool bar understands the following messages:
1184 *
1185 * -- TBM_ADDCONTROLS
1186 */
1187
1188MRESULT EXPENTRY ctl_fnwpToolbar(HWND hwndToolBar, ULONG msg, MPARAM mp1, MPARAM mp2)
1189{
1190 MRESULT mrc = 0;
1191 PTOOLBARDATA pData;
1192
1193 switch (msg)
1194 {
1195 case WM_CREATE:
1196 TbCreate(hwndToolBar, mp1, mp2);
1197 break;
1198
1199 /*
1200 *@@ TBM_ADDCONTROLS:
1201 * adds new controls to the tool bar.
1202 *
1203 * Parameters:
1204 *
1205 * -- PTOOLBARCONTROL mp1: array of TOOLBARCONTROL structs
1206 * which specify the windows to add to the tool bar.
1207 *
1208 * -- SHORT1FROMMP(mp2): number of items in that array
1209 * (not array size).
1210 *
1211 * -- SHORT2FROMMP(mp2): index where to add the new controls.
1212 * 0 means leftmost, 1 before the second item, and so on.
1213 * -1 means add rightmost.
1214 *
1215 * The tool bar will automatically repaint itself. If it
1216 * also has the TBS_AUTORESIZE window style, it will even
1217 * automatically resize itself and post its owner a
1218 * WM_CONTROL with TBN_RESIZED message so it can adjust
1219 * itself.
1220 */
1221
1222 case TBM_ADDCONTROLS:
1223 if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
1224 {
1225 TbAddControls(pData,
1226 SHORT1FROMMP(mp2),
1227 (PTOOLBARCONTROL)mp1,
1228 SHORT2FROMMP(mp2));
1229 }
1230 break;
1231
1232 case WM_CONTROL:
1233 if ( (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
1234 && (pData->hwndToolTip)
1235 && (SHORT2FROMMP(mp1) == TTN_NEEDTEXT)
1236 )
1237 {
1238 PTOOLTIPTEXT pttt = (PTOOLTIPTEXT)mp2;
1239 PSZ psz;
1240 xstrClear(&pData->strToolTipBuf);
1241
1242 if (psz = winhQueryWindowText(pttt->hwndTool))
1243 xstrset(&pData->strToolTipBuf, psz);
1244
1245 pttt->ulFormat = TTFMT_PSZ;
1246 pttt->pszText = pData->strToolTipBuf.psz;
1247 }
1248 break;
1249
1250 case WM_PAINT:
1251 {
1252 HPS hps;
1253 RECTL rcl;
1254 POINTL ptl;
1255 if (hps = WinBeginPaint(hwndToolBar, NULLHANDLE, &rcl))
1256 {
1257 gpihSwitchToRGB(hps);
1258 rcl.yBottom += 2;
1259 WinFillRect(hps,
1260 &rcl,
1261 winhQueryPresColor2(hwndToolBar,
1262 PP_BACKGROUNDCOLOR,
1263 PP_BACKGROUNDCOLORINDEX,
1264 FALSE,
1265 SYSCLR_MENU));
1266 ptl.x = 0;
1267 ptl.y = 0;
1268 GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONLIGHT, 0));
1269 GpiMove(hps, &ptl);
1270 ptl.x = rcl.xRight;
1271 GpiLine(hps, &ptl);
1272 ptl.x = 0;
1273 ptl.y = 1;
1274 GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONDARK, 0));
1275 GpiMove(hps, &ptl);
1276 ptl.x = rcl.xRight;
1277 GpiLine(hps, &ptl);
1278
1279 WinEndPaint(hps);
1280 }
1281 }
1282 break;
1283
1284 case WM_DESTROY:
1285 TbDestroy(hwndToolBar);
1286 break;
1287
1288 default:
1289 if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1))
1290 mrc = ctlDefWindowProc(&pData->dwd, msg, mp1, mp2);
1291 }
1292
1293 return mrc;
1294}
1295
1296/*
1297 *@@ ctlRegisterToolbar:
1298 * this registers the tool bar window class (ctl_fnwpToolbar)
1299 * _and_ the tool bar button control (ctl_fnwpToolbarButton)
1300 * for an application. This is required before the tool bar
1301 * control can be used.
1302 */
1303
1304BOOL ctlRegisterToolbar(HAB hab)
1305{
1306 return ( WinRegisterClass(hab,
1307 WC_CCTL_TOOLBAR,
1308 ctl_fnwpToolbar,
1309 CS_SYNCPAINT | /* CS_CLIPSIBLINGS | */ CS_CLIPCHILDREN,
1310 sizeof(PVOID) * 2) // addt'l bytes to reserve:
1311 // one pointer for QWL_USER,
1312 // one more for instance data
1313 && WinRegisterClass(hab,
1314 WC_CCTL_TBBUTTON,
1315 ctl_fnwpToolbarButton,
1316 CS_SYNCPAINT | CS_CLIPSIBLINGS | CS_CLIPCHILDREN,
1317 sizeof(PVOID) * 2) // addt'l bytes to reserve:
1318 // one pointer for QWL_USER,
1319 // one more for instance data
1320 );
1321}
1322
1323/*
1324 *@@ ctlCreateToolBar:
1325 * type-safe wrapper around WinCreateWindow to create a tool bar.
1326 *
1327 * The easiest way to create a tool bar completely with the tools
1328 * is to pass them as an array of TOOLBARCONTROL structs here,
1329 * which simply specify the window classes to create. In most
1330 * cases, you will want to add tools of the WC_CCTL_TBBUTTON
1331 * and WC_SEPARATORLINE classes.
1332 *
1333 * Keep in mind to call ctlRegisterToolbar and ctlRegisterSeparatorLine
1334 * first, or window creation will fail.
1335 */
1336
1337HWND ctlCreateToolBar(HWND hwndParent, // in: parent of tool bar (e.g. frame)
1338 HWND hwndOwner, // in: owner of tool bar itself (e.g. frame)
1339 ULONG flStyle, // in: window style (WS_VISIBLE | TBS_* flags)
1340 HWND hwndControlsOwner, // in: owner for tool bar controls (e.g. frame client)
1341 ULONG cControls,
1342 PTOOLBARCONTROL patbc)
1343{
1344 TOOLBARCDATA tbcd;
1345 memset(&tbcd, 0, sizeof(tbcd));
1346 tbcd.cb = sizeof(tbcd);
1347 tbcd.hwndControlsOwner = hwndControlsOwner;
1348 tbcd.cControls = cControls;
1349 tbcd.patbc = patbc;
1350
1351 return WinCreateWindow(hwndParent,
1352 WC_CCTL_TOOLBAR,
1353 NULL,
1354 flStyle,
1355 0,
1356 0,
1357 0,
1358 0,
1359 hwndOwner,
1360 HWND_BOTTOM,
1361 FID_TOOLBAR,
1362 &tbcd,
1363 NULL);
1364}
1365
Note: See TracBrowser for help on using the repository browser.