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

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

First attempt at new container contol.

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