/* *@@sourcefile cctl_toolbar.c: * implementation for the "tool bar" common control. * See ctlCreateToolBar and ctl_fnwpToolbar. * * See comctl.c for an overview of the common controls. * * This file implements two window classes: a tool bar * control (which is most conveniently used with * ctlCreateStdWindow) and a tool button control as * a replacement for the dull OS/2 pushbutton controls. * * This is new with V1.0.1 (2002-11-30) [umoeller]. * * Note: Version numbering in this file relates to XWorkplace version * numbering. * *@@header "helpers\comctl.h" *@@added V1.0.1 (2002-11-30) [umoeller] */ /* * Copyright (C) 1997-2002 Ulrich M”ller. * This file is part of the "XWorkplace helpers" source package. * This is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, in version 2 as it comes in the * "COPYING" file of the XWorkplace main distribution. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #define OS2EMX_PLAIN_CHAR // this is needed for "os2emx.h"; if this is defined, // emx will define PSZ as _signed_ char, otherwise // as unsigned char #define INCL_WINWINDOWMGR #define INCL_WINFRAMEMGR #define INCL_WINSYS #define INCL_WINPOINTERS #define INCL_WININPUT #define INCL_WINBUTTONS #define INCL_GPIPRIMITIVES #define INCL_GPILOGCOLORTABLE #define INCL_GPIBITMAPS #define INCL_GPIPATHS #define INCL_GPIREGIONS #define INCL_GPIBITMAPS #include #include #include #include #include // needed for except.h #include // needed for except.h #include "setup.h" // code generation and debugging options #include "helpers\gpih.h" #include "helpers\linklist.h" #include "helpers\standards.h" #include "helpers\stringh.h" #include "helpers\winh.h" #include "helpers\xstring.h" #include "helpers\comctl.h" #pragma hdrstop /* *@@category: Helpers\PM helpers\Window classes\Toolbars * See cctl_toolbar.c. */ /* ****************************************************************** * * Private definitions * ********************************************************************/ /* *@@ TBBUTTONDATA: * */ typedef struct _TBBUTTONDATA { XBUTTONDATA bd; XBUTTONSTATE bs; } TBBUTTONDATA, *PTBBUTTONDATA; /* *@@ TOOLBARDATA: * */ typedef struct _TOOLBARDATA { DEFWINDATA dwd; HWND hwndControlsOwner, hwndToolTip; // != NULLHANDLE if TBS_TOOLTIPS is set LONG lSpacing, lMaxControlCY; LINKLIST llControls; // linked list of HWNDs for controls in toolbar ULONG flReformat; #define RFFL_HEIGHT 0x0001 // height changed, needs complete resize XSTRING strToolTipBuf; } TOOLBARDATA, *PTOOLBARDATA; #define TB_LEFT_SPACING 5 #define TB_BOTTOM_SPACING 5 #define TBB_BORDER 2 #define TBB_TEXTSPACING 2 /* ****************************************************************** * * "Toolbar button" control * ********************************************************************/ /* *@@ ctlInitXButtonData: * */ VOID ctlInitXButtonData(PXBUTTONDATA pbd, // in: button data ULONG fl) // in: TBBS_* style flags { if (fl & TBBS_BIGICON) { pbd->szlIconOrBitmap.cx = G_cxIcon; pbd->szlIconOrBitmap.cy = G_cyIcon; } else if (fl & TBBS_MINIICON) { pbd->szlIconOrBitmap.cx = G_cxIcon / 2; pbd->szlIconOrBitmap.cy = G_cyIcon / 2; } else if ( (fl & TBBS_BITMAP) && (pbd->hptr) ) { BITMAPINFOHEADER2 bmih2; bmih2.cbFix = sizeof(BITMAPINFOHEADER2); GpiQueryBitmapInfoHeader(pbd->hptr, &bmih2); pbd->szlIconOrBitmap.cx = bmih2.cx; pbd->szlIconOrBitmap.cy = bmih2.cy; } } /* *@@ ctlPaintTBButton: * paints a tool bar button control. Can be called externally * for just painting a button even if this is not really * a window. * *@@added V0.9.13 (2001-06-21) [umoeller] *@@changed V0.9.16 (2001-10-24) [umoeller]: fixed wrong hatch color and paint offset *@@changed V0.9.16 (2001-10-28) [umoeller]: added bitmap support, fixed bad clip rectangle *@@changed V0.9.20 (2002-08-04) [umoeller]: fixed button offset, depressed color *@@changed V1.0.1 (2002-11-30) [umoeller]: moved this here from comctl.c, renamed *@@changed V1.0.1 (2002-12-08) [umoeller]: added support for WS_DISABLED */ VOID ctlPaintTBButton(HPS hps, // in: presentation space (RGB mode) ULONG fl, // in: TBBS_* flags PXBUTTONDATA pbd, // in: button data PXBUTTONSTATE pbs) // in: button state { LONG lBorder = 0, lOfs = 0; LONG lLeft, lRight, lColorMiddle = ctlQueryColor(&pbd->dwd, CTLCOL_BGND); // pbd->dwd.lcolBackground; RECTL rclWin, rclTemp; POINTL ptl; rclWin.xLeft = 0; rclWin.yBottom = 0; rclWin.xRight = pbd->dwd.szlWin.cx; rclWin.yTop = pbd->dwd.szlWin.cy; // make backup for later memcpy(&rclTemp, &rclWin, sizeof(RECTL)); if (!(fl & TBBS_FLAT)) lBorder = TBB_BORDER; gpihSwitchToRGB(hps); if (pbs->fPaintButtonSunk) { // paint button "down": lLeft = G_lcol3DDark; lRight = G_lcol3DLight; // add offset for icon painting at the bottom lOfs += 1; if (!lBorder) lBorder = 1; } else { lLeft = G_lcol3DLight; lRight = G_lcol3DDark; if ( (!lBorder) && (pbs->fMouseOver) && (fl & TBBS_HILITE) ) lBorder = 1; } if (lBorder) { // paint button border // make rcl inclusive rclWin.xRight--; rclWin.yTop--; gpihDraw3DFrame2(hps, &rclWin, // inclusive lBorder, lLeft, lRight); rclWin.xRight++; rclWin.yTop++; } if (fl & TBBS_BACKGROUND) { // now paint button middle if (pbs->fPaintButtonSunk) // make the depressed color darker gpihManipulateRGB(&lColorMiddle, .95); else if ((fl & TBBS_HILITE) && (pbs->fMouseOver)) // make the mouse over color lighter gpihManipulateRGB(&lColorMiddle, 1.05); WinFillRect(hps, &rclWin, // exclusive lColorMiddle); } // calc x and y so that icon is centered in rectangle ptl.x = ((pbd->dwd.szlWin.cx - pbd->szlIconOrBitmap.cx) / 2); // center vertically only if we have no text if (fl & TBBS_TEXT) ptl.y = pbd->dwd.szlWin.cy - pbd->szlIconOrBitmap.cy - 2; else ptl.y = ((pbd->dwd.szlWin.cy - pbd->szlIconOrBitmap.cy) / 2); if (fl & TBBS_INUSE) { // caller wants in-use (hatched) emphasis: // draw a box then POINTL ptl2; ptl2.x = ptl.x - 2; ptl2.y = ptl.y - 2; GpiMove(hps, &ptl2); // &ptl // duh, typo V0.9.16 (2001-10-24) [umoeller] GpiSetPattern(hps, PATSYM_DIAG1); GpiSetColor(hps, RGBCOL_BLACK); // V0.9.16 (2001-10-24) [umoeller] ptl2.x = ptl.x + pbd->szlIconOrBitmap.cx + 1; // inclusive! ptl2.y = ptl.y + pbd->szlIconOrBitmap.cy + 1; // inclusive! GpiBox(hps, DRO_FILL, &ptl2, 0, 0); } // make rcl inclusive rclWin.xRight--; rclWin.yTop--; GpiIntersectClipRectangle(hps, &rclWin); // inclusive! if ( (pbd->hptr) && (fl & (TBBS_BIGICON | TBBS_MINIICON | TBBS_BITMAP)) ) { // RECTL rcl3; // now paint icon ptl.x += lOfs; ptl.y -= lOfs; /* rcl3.xLeft = ptl.x; rcl3.yBottom = ptl.y; rcl3.xRight = ptl.x + pbd->szlIconOrBitmap.cx; rcl3.yTop = ptl.y + pbd->szlIconOrBitmap.cy; WinFillRect(hps, &rcl3, RGBCOL_GREEN); */ if (fl & TBBS_BITMAP) // V0.9.16 (2001-10-28) [umoeller] WinDrawBitmap(hps, pbd->hptr, // a bitmap really NULL, // entire bitmap &ptl, 0, 0, (fl & WS_DISABLED) ? DBM_HALFTONE : DBM_NORMAL); else { ULONG fl2 = DP_NORMAL; // 0x0000 if (!(fl & TBBS_BIGICON)) fl2 = DP_MINI; if (fl & WS_DISABLED) // V1.0.1 (2002-12-08) [umoeller] fl2 |= DP_HALFTONED; // I love this... DBM_HALFTONE, but DP_HALFTONED! // PM is just so half-toned itself... WinDrawPointer(hps, ptl.x, ptl.y, pbd->hptr, fl2); } rclTemp.yTop -= pbd->szlIconOrBitmap.cy; } if ( (pbd->dwd.pszText) && (fl & TBBS_TEXT) ) { GpiSetColor(hps, ctlQueryColor(&pbd->dwd, CTLCOL_FGND)); // pbd->dwd.lcolForeground); rclTemp.yTop -= 2 * TBB_TEXTSPACING + lOfs; rclTemp.xRight += 2 * lOfs; // twice the offset because we center horizontally winhDrawFormattedText(hps, &rclTemp, pbd->dwd.pszText, DT_CENTER | DT_TOP | DT_MNEMONIC | DT_WORDBREAK); } } /* static const SYSCOLORSET G_scsToolbarButton = { TRUE, // inherit presparams SYSCLR_BUTTONMIDDLE, SYSCLR_MENUTEXT }; */ static const CCTLCOLOR G_scsToolbarButton[] = { TRUE, PP_BACKGROUNDCOLOR, SYSCLR_BUTTONMIDDLE, TRUE, PP_FOREGROUNDCOLOR, SYSCLR_MENUTEXT, }; /* *@@ BtnAutoSize: * */ STATIC VOID BtnAutoSize(ULONG flStyle, PTBBUTTONDATA pData) { if (flStyle & TBBS_AUTORESIZE) { HPS hps; if (hps = WinGetPS(pData->bd.dwd.hwnd)) { ULONG ulBorder = 2; if (!(flStyle & TBBS_FLAT)) ulBorder += TBB_BORDER; pData->bd.dwd.szlWin.cx = pData->bd.dwd.szlWin.cy = 2 * ulBorder; if (flStyle & (TBBS_BIGICON | TBBS_MINIICON | TBBS_BITMAP)) { pData->bd.dwd.szlWin.cx += pData->bd.szlIconOrBitmap.cx; pData->bd.dwd.szlWin.cy += pData->bd.szlIconOrBitmap.cy; if (flStyle & TBBS_TEXT) pData->bd.dwd.szlWin.cx += 2 * TBB_TEXTSPACING; } if (flStyle & TBBS_TEXT) { RECTL rcl = { 0, 0, 100, 100 }; LONG lMinX; winhDrawFormattedText(hps, &rcl, pData->bd.dwd.pszText, DT_TOP | DT_LEFT | DT_QUERYEXTENT); lMinX = (rcl.xRight - rcl.xLeft) + 2 * TBB_TEXTSPACING; STOREIFMAX(lMinX, pData->bd.dwd.szlWin.cx); // pData->bd.dwd.rcl.xRight += (rcl.xRight - rcl.xLeft) + 2 * TBB_TEXTSPACING; pData->bd.dwd.szlWin.cy += (rcl.yTop - rcl.yBottom) + 2 * TBB_TEXTSPACING; } WinSetWindowPos(pData->bd.dwd.hwnd, 0, 0, 0, pData->bd.dwd.szlWin.cx, pData->bd.dwd.szlWin.cy, SWP_SIZE | SWP_NOADJUST); WinReleasePS(hps); } } } /* *@@ BtnCreate: * implementation for WM_CREATE in ctl_fnwpToolbarButton. */ STATIC MRESULT BtnCreate(HWND hwndButton, MPARAM mp1, MPARAM mp2) { PTBBUTTONDATA pData; MRESULT mrc = 0; if (!(pData = NEW(TBBUTTONDATA))) mrc = (MRESULT)TRUE; // stop window creation else { PCSZ pcszText = ((PCREATESTRUCT)mp2)->pszText; PSZ p; WinSetWindowPtr(hwndButton, QWL_USER + 1, pData); ZERO(pData); // initialize DEFWINDOWDATA ctlInitDWD(hwndButton, mp2, &pData->bd.dwd, WinDefWindowProc, 0, G_scsToolbarButton, ARRAYITEMCOUNT(G_scsToolbarButton)); if ( (pcszText) && (*pcszText == '#') ) { pData->bd.hptr = atoi(pcszText + 1); if (p = strchr(pcszText + 1, '#')) pcszText = p + 1; else pcszText = NULL; } ctlInitXButtonData(&pData->bd, ((PCREATESTRUCT)mp2)->flStyle); if (pcszText) { pData->bd.dwd.pszText = strdup(pcszText); if ( (((PCREATESTRUCT)mp2)->flStyle & TBBS_DROPMNEMONIC) && (p = strchr(pData->bd.dwd.pszText, '~')) ) memmove(p, p + 1, strlen(p)); } BtnAutoSize(((PCREATESTRUCT)mp2)->flStyle, pData); if ( (((PCREATESTRUCT)mp2)->flStyle & TBBS_CHECKINITIAL) && (((PCREATESTRUCT)mp2)->flStyle & (TBBS_CHECK | TBBS_RADIO)) ) pData->bs.fPaintButtonSunk = TRUE; } return mrc; } /* *@@ BtnButton1Down: * implementation for WM_BUTTON1DOWN in ctl_fnwpToolbarButton. */ STATIC MRESULT BtnButton1Down(HWND hwndButton) { PTBBUTTONDATA pData; if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1)) { ULONG flStyle = winhQueryWindowStyle(hwndButton); if (flStyle & WS_DISABLED) WinAlarm(HWND_DESKTOP, WA_WARNING); else { WinSetFocus(HWND_DESKTOP, hwndButton); if (!pData->bs.fMouseCaptured) { // capture mouse events while the // mouse button is down WinSetCapture(HWND_DESKTOP, hwndButton); pData->bs.fMouseCaptured = TRUE; } if (!pData->bs.fPaintButtonSunk) { // toggle state is still UP (i.e. button pressed // for the first time): create menu pData->bs.fPaintButtonSunk = TRUE; WinInvalidateRect(hwndButton, NULL, FALSE); if (flStyle & TBBS_CHECK) // ignore the next button 1 up pData->bs.fIgnoreMB1Up = TRUE; } // end if (!pData->fButtonSunk) } } return (MRESULT)TRUE; // processed } /* *@@ ClickNotifyOwner: * */ STATIC VOID ClickNotifyOwner(HWND hwndButton) { HWND hwndOwner; if (hwndOwner = WinQueryWindow(hwndButton, QW_OWNER)) { ULONG flStyle = winhQueryWindowStyle(hwndButton); if (flStyle & (TBBS_COMMAND | TBBS_SYSCOMMAND)) WinPostMsg(hwndOwner, (flStyle & TBBS_SYSCOMMAND) ? WM_SYSCOMMAND : WM_COMMAND, (MPARAM)WinQueryWindowUShort(hwndButton, QWS_ID), MPFROM2SHORT(CMDSRC_PUSHBUTTON, TRUE)); // pointer, not keyboard else ctlSendWmControl(hwndButton, BN_CLICKED, (MPARAM)hwndButton); } } /* *@@ BtnButton1Up: * implementation for WM_BUTTON1UP in ctl_fnwpToolbarButton. */ STATIC MRESULT BtnButton1Up(HWND hwndButton) { PTBBUTTONDATA pData; if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1)) { ULONG flStyle = winhQueryWindowStyle(hwndButton); if (pData->bs.fMouseCaptured) { WinSetCapture(HWND_DESKTOP, NULLHANDLE); pData->bs.fMouseCaptured = FALSE; } if (!(flStyle & WS_DISABLED)) { pData->bs.fMB1Pressed = FALSE; if (flStyle & TBBS_CHECK) { if (pData->bs.fIgnoreMB1Up) pData->bs.fIgnoreMB1Up = FALSE; else pData->bs.fPaintButtonSunk = FALSE; WinInvalidateRect(hwndButton, NULL, FALSE); ClickNotifyOwner(hwndButton); } else if (flStyle & TBBS_RADIO) { WinSendMsg(hwndButton, TBBM_CHECK, (MPARAM)1, 0); ClickNotifyOwner(hwndButton); } else { pData->bs.fPaintButtonSunk = FALSE; ClickNotifyOwner(hwndButton); WinInvalidateRect(hwndButton, NULL, FALSE); } } } return (MRESULT)TRUE; // processed } #define IGNORE_CHECK_MAGIC 0x87678a1d /* *@@ UncheckOthers: * gets called twice from BtnCheck for radio buttons * to uncheck the others in the group. */ STATIC VOID BtnUncheckOthers(HWND hwndButton, ULONG ulQW) // in: QW_PREV or QW_NEXT { HWND hwnd = hwndButton; CHAR szClass[50]; while (hwnd = WinQueryWindow(hwnd, ulQW)) { if ( (!WinQueryClassName(hwnd, sizeof(szClass), szClass) || (strcmp(szClass, WC_CCTL_TBBUTTON)) || (!(winhQueryWindowStyle(hwnd) & TBBS_RADIO))) ) break; WinSendMsg(hwnd, TBBM_CHECK, (MPARAM)FALSE, (MPARAM)IGNORE_CHECK_MAGIC); // force uncheck without resending } } /* *@@ BtnCheck: * implementation for TBBM_CHECK in ctl_fnwpToolbarButton. */ STATIC VOID BtnCheck(HWND hwndButton, BOOL fCheck, ULONG ulMagic) { PTBBUTTONDATA pData; if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1)) { ULONG flStyle = winhQueryWindowStyle(hwndButton); if ( (flStyle & TBBS_CHECK) || (ulMagic == IGNORE_CHECK_MAGIC) // magic code sent to radio tool bar buttons to // force an uncheck without resending ) { pData->bs.fPaintButtonSunk = fCheck; } else if (flStyle & TBBS_RADIO) { BtnUncheckOthers(hwndButton, QW_PREV); BtnUncheckOthers(hwndButton, QW_NEXT); pData->bs.fPaintButtonSunk = TRUE; } WinInvalidateRect(hwndButton, NULL, FALSE); } } /* *@@ ctl_fnwpToolbarButton: * window proc for the tool bar button control. * * This control is not at all based on the ugly OS/2 button * control, but a complete rewrite. This supports a large * variety of TBBS_* style flags which are useful in the * context of a tool bar. * * The following styles are supported: * * -- WS_DISABLED @@todo halftone the display * * -- optionally one of TBBS_BIGICON, TBBS_MINIICON, or * TBBS_BITMAP to paint a picture in the control * * -- optionally TBBS_TEXT; you can use this alone or * together with one of the picture styles * * -- TBBS_CHECK: if set, button toggles between pressed * and released on every click ("checkbox" style, * even though it still looks as a button). * * -- TBBS_RADIO: if set, the button assumes it is part of * a group and behaves like a radio button, that is, it * automatically unchecks its sibling buttons which have * this style too. * * -- TBBS_AUTORESIZE: if set, the button automatically * resizes itself to the space it needs when its style * or text changes. * * -- TBBS_HILITE: if set, the button hilites when mouse * moves over it. * * -- TBBS_FLAT: if set, the button paints a border only * if the mouse is moving over it; if not set, it * always has a (thicker) border. * * -- TBBS_COMMAND, TBBS_SYSCOMMAND: if none of these are * set, the button _sends_ WM_CONTROL with the standard * button code BN_CLICKED to its owner when it is * pressed. If TBBS_COMMAND is set, the button _posts_ * WM_COMMAND instead; if TBBS_SYSCOMMAND is set, the * button posts WM_SYSCOMMAND instead. * * Note that this is different from the standard button * behavior: even a tool bar button that does not have * the TBBS_CHECK or TBBS_RADIO styles will only post * WM_COMMAND if the TBBS_COMMAND style is set. * * There are two ways to set the icon or bitmap to be * displayed with the control: * * -- Pass it with the window title on tool bar creation in * the form "#handle#text", where "handle" is the decimal * HPOINTER or HBITMAP and "text" is the actual button * text. Note that this only works on creation, not with * WinSetWindowText after creation. * * -- Send a XBBM_SETHANDLE message after button creation. */ MRESULT EXPENTRY ctl_fnwpToolbarButton(HWND hwndButton, ULONG msg, MPARAM mp1, MPARAM mp2) { MRESULT mrc = 0; PTBBUTTONDATA pData; switch (msg) { case WM_CREATE: mrc = BtnCreate(hwndButton, mp1, mp2); break; case WM_BUTTON1DOWN: mrc = BtnButton1Down(hwndButton); break; case WM_BUTTON1UP: mrc = BtnButton1Up(hwndButton); break; case WM_MOUSEENTER: case WM_MOUSELEAVE: if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1)) { BOOL fMouseOver = (msg == WM_MOUSEENTER); if (fMouseOver != pData->bs.fMouseOver) { pData->bs.fMouseOver = fMouseOver; if (winhQueryWindowStyle(hwndButton) & TBBS_HILITE) WinInvalidateRect(hwndButton, NULL, FALSE); } } break; case WM_PAINT: { HPS hps; RECTL rcl; POINTL ptl; if (hps = WinBeginPaint(hwndButton, NULLHANDLE, &rcl)) { if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1)) { gpihSwitchToRGB(hps); ctlPaintTBButton(hps, winhQueryWindowStyle(hwndButton) | TBBS_BACKGROUND, &pData->bd, &pData->bs); } WinEndPaint(hps); } } break; /* *@@ TBBM_CHECK: * checks the given button. Effect depends on the * button style: * * -- With TBBS_CHECK, this sets the button check * state to (BOOL)mp1. * * -- With TBBS_CHECKGROUP, this sets the current * button check state and unchecks neighboring * buttons (siblings) that have the same style. * mp1 is ignored. * * As opposed to a check in response to a mouse * event, this does _not_ send out the BN_CLICKED * notification. */ case TBBM_CHECK: BtnCheck(hwndButton, (BOOL)mp1, (ULONG)mp2); break; /* *@@ TBBM_QUERYCHECK: * returns the current check status of a button * with TBBS_CHECK or TBBS_CHECKGROUP style * as TRUE or FALSE. * * No parameters. */ case TBBM_QUERYCHECK: if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1)) mrc = (MRESULT)pData->bs.fPaintButtonSunk; break; case WM_DESTROY: if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1)) { FREE(pData->bd.dwd.pszText); free(pData); } break; default: if (pData = (PTBBUTTONDATA)WinQueryWindowPtr(hwndButton, QWL_USER + 1)) mrc = ctlDefWindowProc(&pData->bd.dwd, msg, mp1, mp2); } return mrc; } /* ****************************************************************** * * "Toolbar" control * ********************************************************************/ static const CCTLCOLOR G_scsToolbar[] = { TRUE, PP_BACKGROUNDCOLOR, SYSCLR_MENU, TRUE, PP_FOREGROUNDCOLOR, SYSCLR_MENUTEXT }; /* *@@ CreateToolbarControl: * */ STATIC HWND CreateToolbarControl(PTOOLBARDATA pData, PTOOLBARCONTROL pControl, PLONG px, PPRESPARAMS ppp) { HWND hwndControl; if (hwndControl = WinCreateWindow(pData->dwd.hwnd, (PSZ)pControl->pcszClass, (PSZ)pControl->pcszTitle, pControl->flStyle, *px, TB_BOTTOM_SPACING, pControl->cx, pControl->cy, pData->hwndControlsOwner, HWND_TOP, pControl->id, NULL, ppp)) { *px += pControl->cx + pData->lSpacing; if (pData->hwndToolTip) { TOOLINFO ti = {0}; ti.ulFlags = TTF_CENTER_X_ON_TOOL | TTF_POS_Y_BELOW_TOOL | TTF_SUBCLASS; ti.hwndToolOwner = pData->dwd.hwnd; ti.pszText = PSZ_TEXTCALLBACK; ti.hwndTool = hwndControl; WinSendMsg(pData->hwndToolTip, TTM_ADDTOOL, (MPARAM)0, &ti); } } return hwndControl; } /* *@@ ReformatControls: * */ STATIC VOID ReformatControls(HWND hwndToolBar) { PTOOLBARDATA pData; if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1)) { LONG x = TB_LEFT_SPACING; PLISTNODE pNode; LONG cControls; PSWP paswp, pswpThis; if ( (cControls = lstCountItems(&pData->llControls)) && (paswp = (PSWP)malloc(sizeof(SWP) * cControls)) ) { BOOL rc; pswpThis = paswp; FOR_ALL_NODES(&pData->llControls, pNode) { HWND hwnd = (HWND)pNode->pItemData; SWP swp; CHAR szClass[50]; WinQueryWindowPos(hwnd, &swp); if ( (WinQueryClassName(hwnd, sizeof(szClass), szClass)) && (!strcmp(szClass, WC_CCTL_SEPARATOR)) ) { pswpThis->cy = pData->lMaxControlCY; pswpThis->fl = SWP_MOVE | SWP_SIZE; } else { pswpThis->cy = swp.cy; pswpThis->fl = SWP_MOVE; } pswpThis->cx = swp.cx; pswpThis->y = TB_BOTTOM_SPACING; pswpThis->x = x; pswpThis->hwndInsertBehind = HWND_BOTTOM; pswpThis->hwnd = hwnd; x += swp.cx + pData->lSpacing; if (swp.cy > pData->lMaxControlCY) { pData->lMaxControlCY = swp.cy; pData->flReformat = RFFL_HEIGHT; } pswpThis++; } rc = WinSetMultWindowPos(pData->dwd.hab, paswp, cControls); free(paswp); } } } /* *@@ TbAddControls: * */ STATIC ULONG TbAddControls(PTOOLBARDATA pData, ULONG cControls, PTOOLBARCONTROL paControls, LONG lIndex) // in: index before which to add entries; -1 means rightmost { ULONG ul, cCreated = 0; LONG x = 0; HWND hwndBefore; SWP swp; if (!lIndex) x = TB_LEFT_SPACING; else { if ( (lIndex > 0) && (hwndBefore = (HWND)lstItemFromIndex(&pData->llControls, lIndex)) && (WinQueryWindowPos(hwndBefore, &swp)) ) { x = swp.x + swp.cx + pData->lSpacing; } } if (x) { PPRESPARAMS ppp = NULL; PCSZ pcszFont = winhQueryDefaultFont(); LONG lColor; winhStorePresParam(&ppp, PP_FONTNAMESIZE, strlen(pcszFont) + 1, (PVOID)pcszFont); lColor = ctlQueryColor(&pData->dwd, CTLCOL_BGND); winhStorePresParam(&ppp, PP_BACKGROUNDCOLOR, sizeof(lColor), &lColor); lColor = ctlQueryColor(&pData->dwd, CTLCOL_FGND); winhStorePresParam(&ppp, PP_FOREGROUNDCOLOR, sizeof(lColor), &lColor); // create controls for (ul = 0; ul < cControls; ++ul) { HWND hwndControl; if (hwndControl = CreateToolbarControl(pData, &paControls[ul], &x, ppp)) { lstInsertItemBefore(&pData->llControls, (PVOID)hwndControl, lIndex++); ++cCreated; } else break; } if (ppp) free(ppp); } pData->lMaxControlCY = 0; ReformatControls(pData->dwd.hwnd); if (pData->flReformat & RFFL_HEIGHT) { if (WinQueryWindowULong(pData->dwd.hwnd, QWL_STYLE) & TBS_AUTORESIZE) { WinQueryWindowPos(pData->dwd.hwnd, &swp); WinSetWindowPos(pData->dwd.hwnd, 0, 0, 0, swp.cx, pData->lMaxControlCY + 2 * TB_BOTTOM_SPACING, SWP_SIZE); ReformatControls(pData->dwd.hwnd); ctlPostWmControl(pData->dwd.hwnd, TBN_RESIZED, 0); pData->flReformat &= ~RFFL_HEIGHT; } } return cCreated; } /* *@@ TbCreate: * */ STATIC MRESULT TbCreate(HWND hwndToolBar, MPARAM mp1, MPARAM mp2) { PTOOLBARDATA pData; PTOOLBARCDATA ptbcd = (PTOOLBARCDATA)mp1; if (!(pData = NEW(TOOLBARDATA))) return (MRESULT)TRUE; // stop window creation WinSetWindowPtr(hwndToolBar, QWL_USER + 1, pData); ZERO(pData); // initialize DEFWINDOWDATA ctlInitDWD(hwndToolBar, mp2, &pData->dwd, WinDefWindowProc, 0, G_scsToolbar, ARRAYITEMCOUNT(G_scsToolbar)); pData->hwndControlsOwner = ptbcd->hwndControlsOwner; pData->lSpacing = 5; lstInit(&pData->llControls, FALSE); xstrInit(&pData->strToolTipBuf, 0); if (((PCREATESTRUCT)mp2)->flStyle & TBS_TOOLTIPS) { pData->hwndToolTip = WinCreateWindow(HWND_DESKTOP, WC_CCTL_TOOLTIP, NULL, TTS_ALWAYSTIP, 0, 0, 0, 0, // window pos and size, ignored hwndToolBar, NULLHANDLE, // hwndInsertBehind, ignored 0, NULL, // control data NULL); // presparams } if ( (ptbcd->cControls) && (ptbcd->patbc) ) { TbAddControls(pData, ptbcd->cControls, ptbcd->patbc, 0); } return (MRESULT)FALSE; } /* *@@ TbDestroy: * */ STATIC VOID TbDestroy(HWND hwndToolBar) { PTOOLBARDATA pData; if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1)) { PLISTNODE pNode; if (pData->hwndToolTip) WinDestroyWindow(pData->hwndToolTip); FOR_ALL_NODES(&pData->llControls, pNode) { WinDestroyWindow((HWND)pNode->pItemData); } lstClear(&pData->llControls); xstrClear(&pData->strToolTipBuf); free(pData); } } /* *@@ ctl_fnwpToolbar: * window proc for the tool bar class. * * The tool bar understands the following messages: * * -- TBM_ADDCONTROLS */ MRESULT EXPENTRY ctl_fnwpToolbar(HWND hwndToolBar, ULONG msg, MPARAM mp1, MPARAM mp2) { MRESULT mrc = 0; PTOOLBARDATA pData; switch (msg) { case WM_CREATE: TbCreate(hwndToolBar, mp1, mp2); break; /* *@@ TBM_ADDCONTROLS: * adds new controls to the tool bar. * * Parameters: * * -- PTOOLBARCONTROL mp1: array of TOOLBARCONTROL structs * which specify the windows to add to the tool bar. * * -- SHORT1FROMMP(mp2): number of items in that array * (not array size). * * -- SHORT2FROMMP(mp2): index where to add the new controls. * 0 means leftmost, 1 before the second item, and so on. * -1 means add rightmost. * * The tool bar will automatically repaint itself. If it * also has the TBS_AUTORESIZE window style, it will even * automatically resize itself and post its owner a * WM_CONTROL with TBN_RESIZED message so it can adjust * itself. */ case TBM_ADDCONTROLS: if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1)) { TbAddControls(pData, SHORT1FROMMP(mp2), (PTOOLBARCONTROL)mp1, SHORT2FROMMP(mp2)); } break; case WM_CONTROL: if ( (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1)) && (pData->hwndToolTip) && (SHORT2FROMMP(mp1) == TTN_NEEDTEXT) ) { PTOOLTIPTEXT pttt = (PTOOLTIPTEXT)mp2; PSZ psz; xstrClear(&pData->strToolTipBuf); if (psz = winhQueryWindowText(pttt->hwndTool)) xstrset(&pData->strToolTipBuf, psz); pttt->ulFormat = TTFMT_PSZ; pttt->pszText = pData->strToolTipBuf.psz; } break; case WM_PAINT: { HPS hps; RECTL rcl; POINTL ptl; if (hps = WinBeginPaint(hwndToolBar, NULLHANDLE, &rcl)) { gpihSwitchToRGB(hps); rcl.yBottom += 2; WinFillRect(hps, &rcl, winhQueryPresColor2(hwndToolBar, PP_BACKGROUNDCOLOR, PP_BACKGROUNDCOLORINDEX, FALSE, SYSCLR_MENU)); ptl.x = 0; ptl.y = 0; GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONLIGHT, 0)); GpiMove(hps, &ptl); ptl.x = rcl.xRight; GpiLine(hps, &ptl); ptl.x = 0; ptl.y = 1; GpiSetColor(hps, WinQuerySysColor(HWND_DESKTOP, SYSCLR_BUTTONDARK, 0)); GpiMove(hps, &ptl); ptl.x = rcl.xRight; GpiLine(hps, &ptl); WinEndPaint(hps); } } break; case WM_DESTROY: TbDestroy(hwndToolBar); break; default: if (pData = (PTOOLBARDATA)WinQueryWindowPtr(hwndToolBar, QWL_USER + 1)) mrc = ctlDefWindowProc(&pData->dwd, msg, mp1, mp2); } return mrc; } /* *@@ ctlRegisterToolbar: * this registers the tool bar window class (ctl_fnwpToolbar) * _and_ the tool bar button control (ctl_fnwpToolbarButton) * for an application. This is required before the tool bar * control can be used. */ BOOL ctlRegisterToolbar(HAB hab) { return ( WinRegisterClass(hab, WC_CCTL_TOOLBAR, ctl_fnwpToolbar, CS_SYNCPAINT | /* CS_CLIPSIBLINGS | */ CS_CLIPCHILDREN, sizeof(PVOID) * 2) // addt'l bytes to reserve: // one pointer for QWL_USER, // one more for instance data && WinRegisterClass(hab, WC_CCTL_TBBUTTON, ctl_fnwpToolbarButton, CS_SYNCPAINT | CS_CLIPSIBLINGS | CS_CLIPCHILDREN, sizeof(PVOID) * 2) // addt'l bytes to reserve: // one pointer for QWL_USER, // one more for instance data ); } /* *@@ ctlCreateToolBar: * type-safe wrapper around WinCreateWindow to create a tool bar. * * The easiest way to create a tool bar completely with the tools * is to pass them as an array of TOOLBARCONTROL structs here, * which simply specify the window classes to create. In most * cases, you will want to add tools of the WC_CCTL_TBBUTTON * and WC_SEPARATORLINE classes. * * Keep in mind to call ctlRegisterToolbar and ctlRegisterSeparatorLine * first, or window creation will fail. */ HWND ctlCreateToolBar(HWND hwndParent, // in: parent of tool bar (e.g. frame) HWND hwndOwner, // in: owner of tool bar itself (e.g. frame) ULONG flStyle, // in: window style (WS_VISIBLE | TBS_* flags) HWND hwndControlsOwner, // in: owner for tool bar controls (e.g. frame client) ULONG cControls, PTOOLBARCONTROL patbc) { TOOLBARCDATA tbcd; memset(&tbcd, 0, sizeof(tbcd)); tbcd.cb = sizeof(tbcd); tbcd.hwndControlsOwner = hwndControlsOwner; tbcd.cControls = cControls; tbcd.patbc = patbc; return WinCreateWindow(hwndParent, WC_CCTL_TOOLBAR, NULL, flStyle, 0, 0, 0, 0, hwndOwner, HWND_BOTTOM, FID_TOOLBAR, &tbcd, NULL); }