|
|
#include "ctlspriv.h"
#include "toolbar.h"
#include "help.h" // Help IDs
#define SEND_WM_COMMAND(hwnd, id, hwndCtl, codeNotify) \
(void)SendMessage((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))
#define SPACESTRLEN 20
#define FLAG_NODEL 0x8000
#define FLAG_HIDDEN 0x4000
#define FLAG_SEP 0x2000
#define FLAG_ALLFLAGS (FLAG_NODEL|FLAG_HIDDEN|FLAG_SEP)
typedef struct { /* instance data for toolbar edit dialog */ HWND hDlg; /* dialog hwnd */ PTBSTATE ptb; // current toolbar state
int iPos; /* position to insert into */ } ADJUSTDLGDATA, *LPADJUSTDLGDATA;
int g_dyButtonHack = 0; // to pass on before WM_INITDIALOG
LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTONDATA pTBButton);
int GetPrevButton(PTBSTATE ptb, int iPos) { /* This means to delete the preceding space
*/ for (--iPos; ; --iPos) { if (iPos < 0) break;
if (!(ptb->Buttons[iPos].fsState & TBSTATE_HIDDEN)) break;; }
return(iPos); }
BOOL GetAdjustInfo(PTBSTATE ptb, int iItem, LPTBBUTTONDATA ptbButton, LPTSTR lpString, int cbString) { TBNOTIFY tbn; tbn.pszText = lpString; tbn.cchText = cbString; tbn.iItem = iItem;
if (lpString) *lpString = 0;
if ((BOOL)CCSendNotify(&ptb->ci, TBN_GETBUTTONINFO, &tbn.hdr)) { TBInputStruct(ptb, ptbButton, &tbn.tbButton); return TRUE; } return FALSE; }
LRESULT SendItemNotify(PTBSTATE ptb, int iItem, int code) { TBNOTIFY tbn = {0}; tbn.iItem = iItem;
switch (code) { case TBN_QUERYDELETE: case TBN_QUERYINSERT: // The following is to provide the parent app with information
// about the button that information is being requested for...
// Otherwise it's really awful trying to have control over
// certain aspects of toolbar customization... [t-mkim]
// IE4.0's toolbar wants this information.
// Should ONLY be done for TBN_QUERY* notifications BECAUSE
// this can be either a zero-based index _or_ Command ID depending
// on the particular notification code.
if (iItem < ptb->iNumButtons) { CopyMemory(&tbn.tbButton, &ptb->Buttons[iItem], sizeof (TBBUTTON)); } break;
case TBN_DROPDOWN: TB_GetItemRect(ptb, PositionFromID(ptb, iItem), &tbn.rcButton); break; }
// default return from SendNotify is false
// this actually shouldnt return a bool, TBN_DROPDOWN needs to return 0, 1, or 2.
return CCSendNotify(&ptb->ci, code, &tbn.hdr); }
#define SendCmdNotify(ptb, code) CCSendNotify(&ptb->ci, code, NULL)
// this is used to deal with the case where the ptb structure is re-alloced
// after a TBInsertButtons()
PTBSTATE FixPTB(HWND hwnd) { PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwnd, 0);
if (ptb->hdlgCust) { LPADJUSTDLGDATA lpad = (LPADJUSTDLGDATA)GetWindowPtr(ptb->hdlgCust, DWLP_USER); #ifdef DEBUG
if (lpad->ptb != ptb) DebugMsg(DM_TRACE, TEXT("Fixing busted ptb pointer")); #endif
lpad->ptb = ptb; } return ptb; }
void MoveButton(PTBSTATE ptb, int nSource) { int nDest; RECT rc; HCURSOR hCursor; MSG32 msg32;
/* You can't move separators like this
*/ if (nSource < 0) return;
// Make sure it is all right to "delete" the selected button
if (!SendItemNotify(ptb, nSource, TBN_QUERYDELETE)) return;
hCursor = SetCursor(LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_MOVEBUTTON))); SetCapture(ptb->ci.hwnd);
// Get the dimension of the window.
GetClientRect(ptb->ci.hwnd, &rc); for ( ; ; ) { while (!PeekMessage32(&msg32, NULL, 0, 0, PM_REMOVE, TRUE)) ;
if (GetCapture() != ptb->ci.hwnd) goto AbortMove;
// See if the application wants to process the message...
if (CallMsgFilter32(&msg32, MSGF_COMMCTRL_TOOLBARCUST, TRUE) != 0) continue;
switch (msg32.message) { case WM_KEYDOWN: case WM_KEYUP: case WM_CHAR:
//notify of navigation key usage
CCNotifyNavigationKeyUsage(&(ptb->ci), UISF_HIDEFOCUS);
break;
case WM_LBUTTONUP: RelayToToolTips(ptb->hwndToolTips, ptb->ci.hwnd, msg32.message, msg32.wParam, msg32.lParam); if ((GET_Y_LPARAM(msg32.lParam) > (short)(rc.bottom+ptb->iButWidth)) || (GET_X_LPARAM(msg32.lParam) > (short)(rc.right+ptb->iButWidth)) || (GET_Y_LPARAM(msg32.lParam) < -ptb->iButWidth) || (GET_X_LPARAM(msg32.lParam) < -ptb->iButWidth))
{ /* If the button was dragged off the toolbar, delete it.
*/ DeleteSrcButton: DeleteButton(ptb, nSource); SendCmdNotify(ptb, TBN_TOOLBARCHANGE); TBInvalidateItemRects(ptb); } else { TBBUTTONDATA tbbAdd;
/* Add half a button to X so that it looks like it is centered
* over the target button, iff we have a horizontal layout. * Add half a button to Y otherwise. */ if (rc.right!=ptb->iButWidth) nDest = TBHitTest(ptb, GET_X_LPARAM(msg32.lParam) + ptb->iButWidth / 2, GET_Y_LPARAM(msg32.lParam)); else nDest = TBHitTest(ptb, GET_X_LPARAM(msg32.lParam), GET_Y_LPARAM(msg32.lParam) + ptb->iButHeight / 2);
if (nDest < 0) nDest = -1 - nDest;
if (nDest>0 && (ptb->Buttons[nDest-1].fsState & TBSTATE_WRAP) && GET_X_LPARAM(msg32.lParam)>ptb->iButWidth && SendItemNotify(ptb, --nDest, TBN_QUERYINSERT)) { tbbAdd = ptb->Buttons[nSource]; DeleteButton(ptb, nSource); if (nDest>nSource) --nDest;
/* Insert before spaces, but after buttons. */ if (!(ptb->Buttons[nDest].fsStyle & TBSTYLE_SEP)) nDest++;
goto InsertSrcButton; } else if (nDest == nSource) { /* This means to delete the preceding space, or to move a
button to the previous row. */ nSource = GetPrevButton(ptb, nSource); if (nSource < 0) goto AbortMove;
// If the preceding item is a space with no ID, and
// the app says it's OK, then delete it.
if ((ptb->Buttons[nSource].fsStyle & TBSTYLE_SEP) && !ptb->Buttons[nSource].idCommand && SendItemNotify(ptb, nSource, TBN_QUERYDELETE)) goto DeleteSrcButton; } else if (nDest == nSource+1) { // This means to add a preceding space
--nDest; if (SendItemNotify(ptb, nDest, TBN_QUERYINSERT)) { tbbAdd.DUMMYUNION_MEMBER(iBitmap) = 0; tbbAdd.idCommand = 0; tbbAdd.iString = -1; tbbAdd.fsState = 0; tbbAdd.fsStyle = TBSTYLE_SEP; goto InsertSrcButton; } } else if (SendItemNotify(ptb, nDest, TBN_QUERYINSERT)) { HWND hwndT; TBBUTTON tbbAddExt;
/* This is a normal move operation
*/ tbbAdd = ptb->Buttons[nSource];
ptb->Buttons[nSource].iString = -1; DeleteButton(ptb, nSource); if (nDest > nSource) --nDest; InsertSrcButton: hwndT = ptb->ci.hwnd;
TBOutputStruct(ptb, &tbbAdd, &tbbAddExt); TBInsertButtons(ptb, nDest, 1, &tbbAddExt, TRUE);
ptb = FixPTB(hwndT);
SendCmdNotify(ptb, TBN_TOOLBARCHANGE); TBInvalidateItemRects(ptb); } else { AbortMove: ; } } goto AllDone;
case WM_RBUTTONDOWN: goto AbortMove;
default: TranslateMessage32(&msg32, TRUE); DispatchMessage32(&msg32, TRUE); break; } } AllDone:
SetCursor(hCursor); CCReleaseCapture(&ptb->ci); }
#define GNI_HIGH 0x0001
#define GNI_LOW 0x0002
int GetNearestInsert(PTBSTATE ptb, int iPos, int iNumButtons, UINT uFlags) { int i; BOOL bKeepTrying;
// Find the nearest index where we can actually insert items
for (i = iPos; ; ++i, --iPos) { bKeepTrying = FALSE;
// Notice we favor going high if both flags are set
if ((uFlags & GNI_HIGH) && i <= iNumButtons) { bKeepTrying = TRUE;
if (SendItemNotify(ptb, i, TBN_QUERYINSERT)) return i; }
if ((uFlags & GNI_LOW) && iPos >= 0) { bKeepTrying = TRUE;
if (SendItemNotify(ptb, iPos, TBN_QUERYINSERT)) return iPos; }
if (!bKeepTrying) return -1; // There was no place to add buttons
} }
BOOL InitAdjustDlg(HWND hDlg, LPADJUSTDLGDATA lpad) { HDC hDC; HFONT hFont; HWND hwndCurrent, hwndNew; LPTBBUTTONDATA ptbButton; int i, iPos, nItem, nWid, nMaxWid; TBBUTTONDATA tbAdjust; TCHAR szDesc[128]; NMTBCUSTOMIZEDLG nm; TCHAR szSeparator[MAX_PATH];
szSeparator[0] = 0; LocalizedLoadString(IDS_SPACE, szSeparator, ARRAYSIZE(szSeparator));
lpad->hDlg = hDlg; lpad->ptb->hdlgCust = hDlg;
/* Determine the item nearest the desired item that will allow
* insertion. */ iPos = GetNearestInsert(lpad->ptb, lpad->iPos, lpad->ptb->iNumButtons, GNI_HIGH | GNI_LOW); if (iPos < 0) /* No item allowed insertion, so leave the dialog */ { return(FALSE); }
/* Reset the lists of used and available items.
*/ hwndCurrent = GetDlgItem(hDlg, IDC_CURRENT); SendMessage(hwndCurrent, LB_RESETCONTENT, 0, 0L);
hwndNew = GetDlgItem(hDlg, IDC_BUTTONLIST); SendMessage(hwndNew, LB_RESETCONTENT, 0, 0L);
nm.hDlg = hDlg; if (CCSendNotify(&lpad->ptb->ci, TBN_INITCUSTOMIZE, &nm.hdr) == TBNRF_HIDEHELP) { ShowWindow(GetDlgItem(hDlg, IDC_APPHELP), SW_HIDE); }
for (i=0, ptbButton = lpad->ptb->Buttons; i < lpad->ptb->iNumButtons; ++i, ++ptbButton) { UINT uFlags; int iBitmap; LPTSTR pszStr = NULL;
uFlags = 0;
// Non-deletable and hidden items show up grayed.
if (!SendItemNotify(lpad->ptb, i, TBN_QUERYDELETE)) { uFlags |= FLAG_NODEL; } if (ptbButton->fsState & TBSTATE_HIDDEN) { uFlags |= FLAG_HIDDEN; }
/* Separators have no bitmaps (even ones with IDs). Only set
* the separator flag if there is no ID (it is a "real" * separator rather than an owner item). */ if (ptbButton->fsStyle&TBSTYLE_SEP) { if (!(ptbButton->idCommand)) { uFlags |= FLAG_SEP; } iBitmap = -1;
pszStr = szSeparator; } else { iBitmap = ptbButton->DUMMYUNION_MEMBER(iBitmap); // this specifies an imagelist.
// pack this into the loword of the ibitmap.
// this causes a restriction of max 16 imagelists, and 4096 images in any imagelist
iBitmap = LOWORD(iBitmap) | (HIWORD(iBitmap) << 12);
/* Add the item and the data
* Note: A negative number in the LOWORD indicates no bitmap; * otherwise it is the bitmap index. */ pszStr = TB_StrForButton(lpad->ptb, ptbButton); }
if ((int)SendMessage(hwndCurrent, LB_ADDSTRING, 0, (LPARAM)(LPTSTR)(pszStr ? pszStr : (LPTSTR)c_szNULL)) != i) { return(FALSE); } SendMessage(hwndCurrent, LB_SETITEMDATA, i, MAKELPARAM(iBitmap, uFlags)); }
/* Add a dummy "nodel" space at the end so things can be inserted at the end.
*/ if ((int)SendMessage(hwndCurrent, LB_ADDSTRING, 0,(LPARAM)(LPTSTR)szSeparator) == i) { SendMessage(hwndCurrent, LB_SETITEMDATA, i, MAKELPARAM(-1, FLAG_NODEL|FLAG_SEP)); }
/* Now add a space at the beginning of the "new" list.
*/ if (SendMessage(hwndNew, LB_ADDSTRING, 0, (LPARAM)(LPTSTR)szSeparator) == LB_ERR) return(FALSE); SendMessage(hwndNew, LB_SETITEMDATA, 0, MAKELPARAM(-1, FLAG_SEP));
/* We need this to determine the widest (in pixels) item string.
*/ hDC = GetDC(hwndCurrent); hFont = (HFONT)(INT_PTR)SendMessage(hwndCurrent, WM_GETFONT, 0, 0L); if (hFont) { hFont = SelectObject(hDC, hFont); } nMaxWid = 0;
for (i=0; ; ++i) { // Get the info about the i'th item from the app.
if (!GetAdjustInfo(lpad->ptb, i, &tbAdjust, szDesc, ARRAYSIZE(szDesc))) break; if (!szDesc[0]) { LPTSTR psz = TB_StrForButton(lpad->ptb, &tbAdjust); if (psz) { StringCchCopy(szDesc, ARRAYSIZE(szDesc), psz); } }
/* Don't show separators that don't have commands
*/ if (!(tbAdjust.fsStyle & TBSTYLE_SEP) || tbAdjust.idCommand) { /* Get the maximum width of a string.
*/ MGetTextExtent(hDC, szDesc, lstrlen(szDesc), &nWid, NULL);
if (nMaxWid < nWid) { nMaxWid = nWid; }
nItem = PositionFromID(lpad->ptb, tbAdjust.idCommand); if (nItem < 0) /* If the item is not on the toolbar already */ { /* Don't show hidden buttons
*/ if (!(tbAdjust.fsState & TBSTATE_HIDDEN)) { nItem = (int)SendMessage(hwndNew, LB_ADDSTRING, 0, (LPARAM)(LPTSTR)szDesc); if (nItem != LB_ERR) { if (tbAdjust.fsStyle & TBSTYLE_SEP) SendMessage(hwndNew, LB_SETITEMDATA, nItem, MAKELPARAM(-1, i)); else { int iBitmap = tbAdjust.DUMMYUNION_MEMBER(iBitmap); iBitmap = LOWORD(iBitmap) | (HIWORD(iBitmap) << 12); SendMessage(hwndNew, LB_SETITEMDATA, nItem, MAKELPARAM(iBitmap, i)); } } } } else /* The item is on the toolbar already */ { /* Preserve the flags and bitmap.
*/ DWORD dwTemp = (DWORD)SendMessage(hwndCurrent, LB_GETITEMDATA, nItem, 0L);
if (szDesc[0]) { SendMessage(hwndCurrent, LB_DELETESTRING, nItem, 0L);
if ((int)SendMessage(hwndCurrent, LB_INSERTSTRING, nItem, (LPARAM)(LPTSTR)szDesc) != nItem) { ReleaseDC(hwndCurrent, hDC); return(FALSE); } } SendMessage(hwndCurrent, LB_SETITEMDATA, nItem, MAKELPARAM(LOWORD(dwTemp), HIWORD(dwTemp)|i)); } } }
if (hFont) { SelectObject(hDC, hFont); } ReleaseDC(hwndCurrent, hDC);
/* Add on some extra and set the extents for both lists.
*/ nMaxWid += lpad->ptb->iButWidth + 2 + 1; SendMessage(hwndNew, LB_SETHORIZONTALEXTENT, nMaxWid, 0L); SendMessage(hwndCurrent, LB_SETHORIZONTALEXTENT, nMaxWid, 0L);
/* Set the sels and return.
*/ SendMessage(hwndNew, LB_SETCURSEL, 0, 0L); SendMessage(hwndCurrent, LB_SETCURSEL, iPos, 0L); SEND_WM_COMMAND(hDlg, IDC_CURRENT, hwndCurrent, LBN_SELCHANGE);
return(TRUE); }
#define IsSeparator(x) (HIWORD(x) & FLAG_SEP)
void PaintAdjustLine(PTBSTATE ptb, DRAWITEMSTRUCT *lpdis) { HDC hdc = lpdis->hDC; HWND hwndList = lpdis->hwndItem; PTSTR pszText; RECT rc = lpdis->rcItem; int nBitmap, nLen, nItem = lpdis->itemID; COLORREF oldBkColor, oldTextColor; BOOL bSelected, bHasFocus; int wHeight; int x;
if (lpdis->CtlID != IDC_BUTTONLIST && lpdis->CtlID != IDC_CURRENT) return;
nBitmap = LOWORD(lpdis->itemData); // unpack the nBitmap. we stored the imagelist spec in the hi char of loword
if (nBitmap != 0xFFFF) nBitmap = (nBitmap & 0x0FFF) | ((nBitmap & 0xF000) << 4);
nLen = (int)SendMessage(hwndList, LB_GETTEXTLEN, nItem, 0L); if (nLen < 0) return;
pszText = (PTSTR)LocalAlloc(LPTR, (nLen+1)*sizeof(TCHAR)); if (!pszText) return;
// This needs to work for separators also or ActiveAccessibility
// won't work.
SendMessage(hwndList, LB_GETTEXT, nItem, (LPARAM)(LPTSTR)pszText); if (lpdis->itemAction != ODA_FOCUS) { COLORREF clr; TCHAR szSample[2];
/* We don't care about focus if the item is not selected.
*/ bSelected = lpdis->itemState & ODS_SELECTED; bHasFocus = bSelected && (GetFocus() == hwndList);
if (HIWORD(lpdis->itemData) & (FLAG_NODEL | FLAG_HIDDEN)) clr = g_clrGrayText; else if (bHasFocus) clr = g_clrHighlightText; else clr = g_clrWindowText;
oldTextColor = SetTextColor(hdc, clr); oldBkColor = SetBkColor(hdc, bHasFocus ? g_clrHighlight : g_clrWindow);
szSample[0] = TEXT('W'); szSample[1] = TEXT('\0');
MGetTextExtent(hdc, szSample, 1, NULL, &wHeight);
x = rc.left + 2; x += (ptb->ci.style & TBSTYLE_FLAT) ? (ptb->iDxBitmap + g_cxEdge) : ptb->iButWidth; ExtTextOut(hdc, x, (rc.top + rc.bottom-wHeight) / 2, ETO_CLIPPED | ETO_OPAQUE, &rc, pszText, nLen, NULL);
/* We really care about the bitmap value here; this is not just an
* indicator for the separator. */ if (nBitmap >= 0) { TBBUTTONDATA tbbAdd = {0}; TBDRAWITEM tbdraw = {0};
tbbAdd.DUMMYUNION_MEMBER(iBitmap) = nBitmap; tbbAdd.iString = -1; tbbAdd.fsStyle = TBSTYLE_BUTTON; tbbAdd.fsState = (BYTE)((HIWORD(lpdis->itemData) & FLAG_HIDDEN) ? 0 : TBSTATE_ENABLED);
InitTBDrawItem(&tbdraw, ptb, &tbbAdd, tbbAdd.fsState, 0, 0, 0);
if (ptb->ci.style & TBSTYLE_FLAT) { RECT rcFace = rc; rcFace.right = rcFace.left + ptb->iDxBitmap + g_cxEdge; DrawFace(hdc, &rcFace, rc.left + 1, rc.top + 1, 0, 0, 0, 0, &tbdraw, 0, 0); } else DrawButton(hdc, rc.left + 1, rc.top + 1, ptb, &tbbAdd, TRUE); ReleaseMonoDC(ptb); }
SetBkColor(hdc, oldBkColor); SetTextColor(hdc, oldTextColor);
/* Frame the item if it is selected but does not have the focus.
*/ if (bSelected && !bHasFocus) { nLen = rc.left + (int)SendMessage(hwndList, LB_GETHORIZONTALEXTENT, 0, 0L); if (rc.right < nLen) rc.right = nLen;
FrameRect(hdc, &rc, g_hbrHighlight); } }
if ((lpdis->itemAction == ODA_FOCUS || (lpdis->itemState & ODS_FOCUS)) && !(CCGetUIState(&(ptb->ci)) & UISF_HIDEFOCUS)) { DrawFocusRect(hdc, &rc); }
LocalFree((HLOCAL)pszText); }
void LBMoveButton(LPADJUSTDLGDATA lpad, UINT wIDSrc, int iPosSrc, UINT wIDDst, int iPosDst, int iSelOffset) { HWND hwndSrc, hwndDst; DWORD dwDataSrc; PTSTR pStr; TBBUTTONDATA tbAdjust = {0}; TBBUTTON tbbAddExt; int iTopDst; TCHAR szDesc[128];
hwndSrc = GetDlgItem(lpad->hDlg, wIDSrc); hwndDst = GetDlgItem(lpad->hDlg, wIDDst);
// Make sure we can delete the source and insert at the dest
//
dwDataSrc = (DWORD)SendMessage(hwndSrc, LB_GETITEMDATA, iPosSrc, 0L); if (iPosSrc < 0 || (HIWORD(dwDataSrc) & FLAG_NODEL)) return; if (wIDDst == IDC_CURRENT && !SendItemNotify(lpad->ptb, iPosDst, TBN_QUERYINSERT)) return;
// Get the string for the source
//
pStr = (PTSTR)LocalAlloc(LPTR, ((int)(SendMessage(hwndSrc, LB_GETTEXTLEN, iPosSrc, 0L))+1)*sizeof(TCHAR)); if (!pStr) return; SendMessage(hwndSrc, LB_GETTEXT, iPosSrc, (LPARAM)(LPTSTR)pStr);
SendMessage(hwndSrc, WM_SETREDRAW, 0, 0L); SendMessage(hwndDst, WM_SETREDRAW, 0, 0L); iTopDst = (int)SendMessage(hwndDst, LB_GETTOPINDEX, 0, 0L);
// If we are inserting into the available button list, we need to determine
// the insertion point
//
if (wIDDst == IDC_BUTTONLIST) { // Insert this back in the available list if this is not a space or a
// hidden button.
//
if (HIWORD(dwDataSrc)&(FLAG_SEP|FLAG_HIDDEN)) { iPosDst = 0; goto DelTheSrc; } else { UINT uCmdSrc = HIWORD(dwDataSrc) & ~(FLAG_ALLFLAGS);
// This just does a linear search for where to put the
// item. Slow, but this only happens when the user clicks
// the "Remove" button.
//
iPosDst = 1; for ( ; ; ++iPosDst) { // Notice that this will break out when iPosDst is
// past the number of items, since -1 will be returned
//
if ((UINT)HIWORD(SendMessage(hwndDst, LB_GETITEMDATA, iPosDst, 0L)) >= uCmdSrc) break; } } } else if (iPosDst < 0) goto CleanUp;
// Attempt to insert the new string
//
if ((int)SendMessage(hwndDst, LB_INSERTSTRING, iPosDst, (LPARAM)(LPTSTR)pStr) == iPosDst) { // Attempt to sync up the actual toolbar.
//
if (wIDDst == IDC_CURRENT) { HWND hwndT;
if (IsSeparator(dwDataSrc)) { // Make up a dummy lpInfo if this is a space
//
tbAdjust.DUMMYUNION_MEMBER(iBitmap) = 0; tbAdjust.idCommand = 0; tbAdjust.fsState = 0; tbAdjust.fsStyle = TBSTYLE_SEP; } else { // Call back to client to get the source button info
//
int iCmdSrc = HIWORD(dwDataSrc) & ~FLAG_ALLFLAGS; if (!GetAdjustInfo(lpad->ptb, iCmdSrc, &tbAdjust, szDesc, ARRAYSIZE(szDesc))) goto DelTheDst; }
hwndT = lpad->ptb->ci.hwnd;
TBOutputStruct(lpad->ptb, &tbAdjust, &tbbAddExt); if (!TBInsertButtons(lpad->ptb, iPosDst, 1, &tbbAddExt, TRUE)) { DelTheDst: SendMessage(hwndDst, LB_DELETESTRING, iPosDst, 0L); goto CleanUp; } else { lpad->ptb = FixPTB(hwndT); }
if (wIDSrc == IDC_CURRENT && iPosSrc >= iPosDst) ++iPosSrc; }
SendMessage(hwndDst, LB_SETITEMDATA, iPosDst, dwDataSrc);
DelTheSrc: // Don't delete the "Separator" in the new list
//
if ((wIDSrc != IDC_BUTTONLIST) || (iPosSrc != 0)) { SendMessage(hwndSrc, LB_DELETESTRING, iPosSrc, 0L); if (wIDSrc == wIDDst) { if (iPosSrc < iPosDst) --iPosDst; if (iPosSrc < iTopDst) --iTopDst; } }
// Delete the corresponding button
//
if (wIDSrc == IDC_CURRENT) DeleteButton(lpad->ptb, iPosSrc);
// Only set the src index if the two windows are different
//
if (wIDSrc != wIDDst) { if (iPosSrc >= SendMessage(hwndSrc, LB_GETCOUNT, 0, 0L)) { // HACKHACK: workaround for funkdified listbox scrolling behavior.
// Select the first item (to force scroll back to top of list),
// then select the item we really want selected.
SendMessage(hwndSrc, LB_SETCURSEL, 0, 0L); }
if (SendMessage(hwndSrc, LB_SETCURSEL, iPosSrc, 0L) == LB_ERR) SendMessage(hwndSrc, LB_SETCURSEL, iPosSrc-1, 0L); SEND_WM_COMMAND(lpad->hDlg, wIDSrc, hwndSrc, LBN_SELCHANGE); }
// Send the final SELCHANGE message after everything else is done
//
SendMessage(hwndDst, LB_SETCURSEL, iPosDst+iSelOffset, 0L); SEND_WM_COMMAND(lpad->hDlg, wIDDst, hwndDst, LBN_SELCHANGE); }
CleanUp:
LocalFree((HLOCAL)pStr);
if (wIDSrc == wIDDst) { SendMessage(hwndDst, LB_SETTOPINDEX, iTopDst, 0L); //make sure that the selected item is still visible
SendMessage(hwndDst, LB_SETCURSEL, (int)SendMessage(hwndDst, LB_GETCURSEL, 0, 0L), 0); } SendMessage(hwndSrc, WM_SETREDRAW, 1, 0L); SendMessage(hwndDst, WM_SETREDRAW, 1, 0L);
InvalidateRect(hwndDst, NULL, TRUE);
SendCmdNotify(lpad->ptb, TBN_TOOLBARCHANGE); }
void SafeEnableWindow(HWND hDlg, UINT wID, HWND hwndDef, BOOL bEnable) { HWND hwndEnable;
hwndEnable = GetDlgItem(hDlg, wID);
if (!bEnable && GetFocus()==hwndEnable) SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)hwndDef, 1L); EnableWindow(hwndEnable, bEnable); }
int InsertIndex(LPADJUSTDLGDATA lpad, POINT pt, BOOL bDragging) { HWND hwndCurrent = GetDlgItem(lpad->hDlg, IDC_CURRENT); int nItem = LBItemFromPt(hwndCurrent, pt, bDragging); if (nItem >= 0) { if (!SendItemNotify(lpad->ptb, nItem, TBN_QUERYINSERT)) nItem = -1; }
DrawInsert(lpad->hDlg, hwndCurrent, bDragging ? nItem : -1);
return(nItem); }
BOOL IsInButtonList(HWND hDlg, POINT pt) { ScreenToClient(hDlg, &pt);
return(ChildWindowFromPoint(hDlg, pt) == GetDlgItem(hDlg, IDC_BUTTONLIST)); }
BOOL HandleDragMsg(LPADJUSTDLGDATA lpad, HWND hDlg, WPARAM wID, LPDRAGLISTINFO lpns) { switch (wID) { case IDC_CURRENT: switch (lpns->uNotification) { case DL_BEGINDRAG: { int nItem = (int)SendMessage(lpns->hWnd, LB_GETCURSEL, 0, 0L); if (HIWORD(SendMessage(lpns->hWnd, LB_GETITEMDATA, nItem, 0L)) & FLAG_NODEL) return SetDlgMsgResult(hDlg, WM_COMMAND, FALSE); return SetDlgMsgResult(hDlg, WM_COMMAND, TRUE); } case DL_DRAGGING: { int nDropIndex;
DraggingSomething: nDropIndex = InsertIndex(lpad, lpns->ptCursor, TRUE); if (nDropIndex>=0 || IsInButtonList(hDlg, lpns->ptCursor)) { SetCursor(LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_MOVEBUTTON))); return SetDlgMsgResult(hDlg, WM_COMMAND, 0); } return SetDlgMsgResult(hDlg, WM_COMMAND, DL_STOPCURSOR); } case DL_DROPPED: { int nDropIndex, nSrcIndex; nDropIndex = InsertIndex(lpad, lpns->ptCursor, FALSE); nSrcIndex = (int)SendMessage(lpns->hWnd, LB_GETCURSEL, 0, 0L); if (nDropIndex >= 0) { if ((UINT)(nDropIndex-nSrcIndex) > 1) LBMoveButton(lpad, IDC_CURRENT, nSrcIndex, IDC_CURRENT, nDropIndex, 0); } else if (IsInButtonList(hDlg, lpns->ptCursor)) { LBMoveButton(lpad, IDC_CURRENT, nSrcIndex, IDC_BUTTONLIST, 0, 0); } break; } case DL_CANCELDRAG: CancelDrag: /* This erases the insert icon if it exists.
*/ InsertIndex(lpad, lpns->ptCursor, FALSE); break; default: break; } break; case IDC_BUTTONLIST: switch (lpns->uNotification) { case DL_BEGINDRAG: return SetDlgMsgResult(hDlg, WM_COMMAND, TRUE); case DL_DRAGGING: goto DraggingSomething; case DL_DROPPED: { int nDropIndex; nDropIndex = InsertIndex(lpad, lpns->ptCursor, FALSE); if (nDropIndex >= 0) LBMoveButton(lpad, IDC_BUTTONLIST, (int)SendMessage(lpns->hWnd,LB_GETCURSEL,0,0L), IDC_CURRENT, nDropIndex, 0); break; } case DL_CANCELDRAG: goto CancelDrag; default: break; } break; default: break; } return(0); }
// Context Help IDs
const static DWORD aAdjustHelpIDs[] = { IDC_RESET, IDH_COMCTL_RESET, IDC_APPHELP, IDH_HELP, IDC_MOVEUP, IDH_COMCTL_MOVEUP, IDC_MOVEDOWN, IDH_COMCTL_MOVEDOWN, IDC_BUTTONLIST, IDH_COMCTL_AVAIL_BUTTONS, IDOK, IDH_COMCTL_ADD, IDC_REMOVE, IDH_COMCTL_REMOVE, IDC_CURRENT, IDH_COMCTL_BUTTON_LIST, IDCANCEL, IDH_COMCTL_CLOSE, 0, 0 };
BOOL_PTR CALLBACK AdjustDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { LPADJUSTDLGDATA lpad = (LPADJUSTDLGDATA)GetWindowPtr(hDlg, DWLP_USER); switch (uMsg) { case WM_INITDIALOG: SetWindowLongPtr(hDlg, DWLP_USER, lParam); /* LPADJUSTDLGDATA pointer */ if (!InitAdjustDlg(hDlg, (LPADJUSTDLGDATA)lParam)) EndDialog(hDlg, FALSE); ShowWindow(hDlg, SW_SHOW); UpdateWindow(hDlg); SetFocus(GetDlgItem(hDlg, IDC_CURRENT)); MakeDragList(GetDlgItem(hDlg, IDC_CURRENT)); MakeDragList(GetDlgItem(hDlg, IDC_BUTTONLIST)); return FALSE; case WM_MEASUREITEM: #define lpmis ((MEASUREITEMSTRUCT *)lParam)
if (lpmis->CtlID == IDC_BUTTONLIST || lpmis->CtlID == IDC_CURRENT) { int nHeight; HWND hwndList = GetDlgItem(hDlg, lpmis->CtlID); HDC hDC = GetDC(hwndList); TCHAR szSample[2]; szSample[0] = TEXT('W'); szSample[1] = TEXT('\0'); MGetTextExtent(hDC, szSample, 1, NULL, &nHeight); // note, we use this hack because we get WM_MEASUREITEMS
// before our WM_INITDIALOG where we get the lpad setup
if (nHeight < g_dyButtonHack + 2) nHeight = g_dyButtonHack + 2; lpmis->itemHeight = nHeight; ReleaseDC(hwndList, hDC); } break; case WM_DRAWITEM: PaintAdjustLine(lpad->ptb, (DRAWITEMSTRUCT *)lParam); break; case WM_HELP: WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aAdjustHelpIDs); break; case WM_CONTEXTMENU: WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU, (ULONG_PTR)(LPVOID) aAdjustHelpIDs); break; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { case IDC_APPHELP: SendCmdNotify(lpad->ptb, TBN_CUSTHELP); break; case IDOK: { int iPos, nItem; nItem = (int)SendDlgItemMessage(hDlg, IDC_BUTTONLIST, LB_GETCURSEL, 0, 0L); iPos = (int)SendDlgItemMessage(hDlg, IDC_CURRENT, LB_GETCURSEL, 0, 0L); if (iPos == -1) iPos = 0; LBMoveButton(lpad, IDC_BUTTONLIST, nItem, IDC_CURRENT, iPos, 1); break; } case IDC_BUTTONLIST: switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case LBN_DBLCLK: SendMessage(hDlg, WM_COMMAND, IDOK, 0L); break; case LBN_SETFOCUS: case LBN_KILLFOCUS: { RECT rc; if (SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETITEMRECT, (int)SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETCURSEL, 0, 0L), (LPARAM)(LPRECT)&rc) != LB_ERR) InvalidateRect(GET_WM_COMMAND_HWND(wParam, lParam), &rc, FALSE); } default: break; } break; case IDC_CURRENT: switch (GET_WM_COMMAND_CMD(wParam, lParam)) { case LBN_SELCHANGE: { BOOL bDelOK; HWND hwndList = GET_WM_COMMAND_HWND(wParam, lParam); int iPos = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0L); SafeEnableWindow(hDlg, IDOK, hwndList, BOOLFROMPTR(SendItemNotify(lpad->ptb, iPos, TBN_QUERYINSERT))); bDelOK = !(HIWORD(SendMessage(hwndList, LB_GETITEMDATA, iPos, 0L)) & FLAG_NODEL); SafeEnableWindow(hDlg, IDC_REMOVE, hwndList, bDelOK); SafeEnableWindow(hDlg, IDC_MOVEUP, hwndList, bDelOK && GetNearestInsert(lpad->ptb, iPos - 1, 0, GNI_LOW) >= 0); SafeEnableWindow(hDlg, IDC_MOVEDOWN, hwndList, bDelOK && GetNearestInsert(lpad->ptb, iPos + 2, lpad->ptb->iNumButtons, GNI_HIGH) >=0 ); break; } case LBN_DBLCLK: SendMessage(hDlg, WM_COMMAND, IDC_REMOVE, 0L); break; case LBN_SETFOCUS: case LBN_KILLFOCUS: { RECT rc;
if (SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETITEMRECT, (int)SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETCURSEL, 0, 0L), (LPARAM)(LPRECT)&rc) != LB_ERR) InvalidateRect(GET_WM_COMMAND_HWND(wParam, lParam), &rc, FALSE); } default: break; } break; case IDC_REMOVE: { int iPos = (int)SendDlgItemMessage(hDlg, IDC_CURRENT, LB_GETCURSEL, 0, 0); LBMoveButton(lpad, IDC_CURRENT, iPos, IDC_BUTTONLIST, 0, 0); break; } case IDC_MOVEUP: case IDC_MOVEDOWN: { int iPosSrc, iPosDst; iPosSrc = (int)SendDlgItemMessage(hDlg, IDC_CURRENT, LB_GETCURSEL, 0, 0L); if (wParam == IDC_MOVEUP) iPosDst = GetNearestInsert(lpad->ptb, iPosSrc - 1, 0, GNI_LOW); else iPosDst = GetNearestInsert(lpad->ptb, iPosSrc + 2, lpad->ptb->iNumButtons, GNI_HIGH); LBMoveButton(lpad, IDC_CURRENT, iPosSrc, IDC_CURRENT,iPosDst,0); break; } case IDC_RESET: { // ptb will change across call below
HWND hwndT = lpad->ptb->ci.hwnd; BOOL fClose = FALSE; NMTBCUSTOMIZEDLG nm; nm.hDlg = hDlg; if (CCSendNotify(&lpad->ptb->ci, TBN_RESET, &nm.hdr) == TBNRF_ENDCUSTOMIZE) fClose = TRUE; // ptb probably changed across above call
lpad->ptb = FixPTB(hwndT); /* Reset the dialog, but exit if something goes wrong. */ lpad->iPos = 0; if (!fClose && InitAdjustDlg(hDlg, lpad)) break; } /* We have to fall through because we won't know where to insert
* buttons after resetting. */ case IDCANCEL: EndDialog(hDlg, TRUE); break; default: return(FALSE); } break; default: if (uMsg == uDragListMsg) return HandleDragMsg(lpad, hDlg, wParam, (LPDRAGLISTINFO)lParam); return(FALSE); } return(TRUE); }
// FEATURE: this should support saving to an IStream
/* This saves the state of the toolbar. Spaces are saved as -1 (-2 if hidden)
* and other buttons are just saved as the command ID. When restoring, all * ID's are filled in, and the app is queried for all buttons so that the * bitmap and state information may be filled in. Button ID's that are not * returned from the app are removed. */
BOOL SaveRestoreFromReg(PTBSTATE ptb, BOOL bWrite, HKEY hkr, LPCTSTR pszSubKey, LPCTSTR pszValueName) { BOOL bRet = FALSE; TCHAR szDesc[128]; if (bWrite) { UINT uSize = ptb->iNumButtons * sizeof(DWORD); NMTBSAVE nmtbs; BOOL fAlloced = FALSE; nmtbs.pData = NULL; nmtbs.cbData = uSize; nmtbs.pCurrent = NULL; nmtbs.iItem = -1; // signal pre saving
nmtbs.cButtons = ptb->iNumButtons; CCSendNotify(&ptb->ci, TBN_SAVE, &nmtbs.hdr); if (!nmtbs.pData) { nmtbs.pData = (DWORD *)LocalAlloc(LPTR, nmtbs.cbData); fAlloced = TRUE; }
// Bug#94345 -- Somebody could've changed ptb->iNumButtons
// during the CCSendNotify
if (!nmtbs.pCurrent) nmtbs.pCurrent = nmtbs.pData; if (nmtbs.pData) { HKEY hkeySave; if (RegCreateKey(hkr, pszSubKey, &hkeySave) == ERROR_SUCCESS) { int i; for (i = 0; i < ptb->iNumButtons; i++) { if (ptb->Buttons[i].idCommand) *nmtbs.pCurrent = ptb->Buttons[i].idCommand; else { // If the separator has an ID, then it is an "owner" item.
if (ptb->Buttons[i].fsState & TBSTATE_HIDDEN) *nmtbs.pCurrent = (DWORD)-2; // hidden
else *nmtbs.pCurrent = (DWORD)-1; // normal seperator
} nmtbs.pCurrent++; nmtbs.iItem = i; TBOutputStruct(ptb, &ptb->Buttons[i], &nmtbs.tbButton); CCSendNotify(&ptb->ci, TBN_SAVE, &nmtbs.hdr); } if (RegSetValueEx(hkeySave, (LPTSTR)pszValueName, 0, REG_BINARY, (LPVOID)nmtbs.pData, nmtbs.cbData) == ERROR_SUCCESS) bRet = TRUE; RegCloseKey(hkeySave); } if (fAlloced) LocalFree((HLOCAL)nmtbs.pData); } } else { HKEY hkey; if (RegOpenKeyEx(hkr, pszSubKey, 0, KEY_READ, &hkey) == ERROR_SUCCESS) { DWORD cbSize = 0; if ((RegQueryValueEx(hkey, (LPTSTR)pszValueName, 0, NULL, NULL, &cbSize) == ERROR_SUCCESS) && (cbSize > sizeof(DWORD))) { UINT uSize = (UINT)cbSize; DWORD *pData = (DWORD *)LocalAlloc(LPTR, uSize); if (pData) { DWORD dwType; DWORD cbSize = (DWORD)uSize; if ((RegQueryValueEx(hkey, (LPTSTR)pszValueName, 0, &dwType, (LPVOID)pData, &cbSize) == ERROR_SUCCESS) && (dwType == REG_BINARY) && (cbSize == (DWORD)uSize)) { int iButtonIndex;
NMTBRESTORE nmtbs; BOOL fAlloced = FALSE; nmtbs.pData = pData; nmtbs.pCurrent = pData; nmtbs.iItem = -1; // signal pre saving
nmtbs.cButtons = (int)uSize / SIZEOF(DWORD); nmtbs.cbBytesPerRecord = SIZEOF(DWORD); nmtbs.cbData = uSize; // since we don't know the cButtons if they've added on extra data to pData,
// we'll use whatever they fill for cButtons
if (!CCSendNotify(&ptb->ci, TBN_RESTORE, &nmtbs.hdr)) { //
// Before reloading the buttons, delete the tooltips
// of the previous buttons (if they exist).
//
if (ptb && ptb->hwndToolTips) { TOOLINFO ti;
ti.cbSize = sizeof(ti); ti.hwnd = ptb->ci.hwnd;
for (iButtonIndex = 0; iButtonIndex < ptb->iNumButtons; iButtonIndex++) { if (!(ptb->Buttons[iButtonIndex].fsStyle & TBSTYLE_SEP)) { ti.uId = ptb->Buttons[iButtonIndex].idCommand; SendMessage(ptb->hwndToolTips, TTM_DELTOOL, 0, (LPARAM)(LPTOOLINFO)&ti); } } }
// grow (or maybe shrink) pbt to hold new buttons
if (TBReallocButtons(ptb, nmtbs.cButtons)) { int i; if (ptb->iNumButtons < nmtbs.cButtons) { ZeroMemory(&ptb->Buttons[ptb->iNumButtons], (nmtbs.cButtons - ptb->iNumButtons) * SIZEOF(TBBUTTON)); } ptb->iNumButtons = nmtbs.cButtons;
for (i = 0; i < ptb->iNumButtons; i++) { nmtbs.iItem = i;
if ((long)*nmtbs.pCurrent < 0) { ptb->Buttons[i].fsStyle = TBSTYLE_SEP; ptb->Buttons[i].DUMMYUNION_MEMBER(iBitmap) = g_dxButtonSep; ptb->Buttons[i].idCommand = 0; if (*nmtbs.pCurrent == (DWORD)-1) ptb->Buttons[i].fsState = 0; else { ASSERT(*nmtbs.pCurrent == (DWORD)-2); ptb->Buttons[i].fsState = TBSTATE_HIDDEN; } } else { ptb->Buttons[i].fsStyle = 0; ptb->Buttons[i].idCommand = *nmtbs.pCurrent; ptb->Buttons[i].DUMMYUNION_MEMBER(iBitmap) = -1; } nmtbs.pCurrent++; TBOutputStruct(ptb, &ptb->Buttons[i], &nmtbs.tbButton); CCSendNotify(&ptb->ci, TBN_RESTORE, &nmtbs.hdr); ASSERT(nmtbs.tbButton.iString == -1 || !HIWORD(nmtbs.tbButton.iString)); // we don't thunk. only allow string index in string pool here
if (HIWORD(nmtbs.tbButton.iString)) nmtbs.tbButton.iString = 0; TBInputStruct(ptb, &ptb->Buttons[i], &nmtbs.tbButton); }
// Now query for all buttons, and fill in the rest of the info
// For backward compatibility, ignore return value of TBN_BEGINADJUST
// if client is older than version 5 (NT5 #185499).
if (!SendCmdNotify(ptb, TBN_BEGINADJUST) || (ptb->ci.iVersion < 5)) { for (i = 0; ; i++) { TBBUTTONDATA tbAdjust;
tbAdjust.idCommand = 0;
if (!GetAdjustInfo(ptb, i, &tbAdjust, szDesc, ARRAYSIZE(szDesc))) break;
if (!(tbAdjust.fsStyle & TBSTYLE_SEP) || tbAdjust.idCommand) { int iPos = PositionFromID(ptb, tbAdjust.idCommand); if (iPos >= 0) { ptb->Buttons[iPos] = tbAdjust; } }
} SendCmdNotify(ptb, TBN_ENDADJUST); }
// cleanup all the buttons that were not recognized
// do this backwards to minimize data movement (and nmtbs.cButtons changes)
for (i = ptb->iNumButtons - 1; i >= 0; i--) { // DeleteButton does no realloc, so ptb will not move
if (ptb->Buttons[i].DUMMYUNION_MEMBER(iBitmap) < 0) DeleteButton(ptb, (UINT)i); else { // the rest, add to tooltips
if(ptb->hwndToolTips && (!(ptb->Buttons[i].fsStyle & TBSTYLE_SEP || !ptb->Buttons[i].idCommand))) { TOOLINFO ti; // don't bother setting the rect because we'll do it below
// in TBInvalidateItemRects;
ti.cbSize = sizeof(ti); ti.uFlags = 0; ti.hwnd = ptb->ci.hwnd; ti.uId = ptb->Buttons[i].idCommand; ti.lpszText = LPSTR_TEXTCALLBACK;
SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti); } }
} bRet = (ptb->iNumButtons != 0); // success
// Bug#94368: break autosize to a function and call it
SendMessage(ptb->ci.hwnd, TB_AUTOSIZE, 0, 0); InvalidateRect(ptb->ci.hwnd, NULL, TRUE); TBInvalidateItemRects(ptb); } } } LocalFree((HLOCAL)pData); } } RegCloseKey(hkey); } } return bRet; }
void CustomizeTB(PTBSTATE ptb, int iPos) { ADJUSTDLGDATA ad; HWND hwndT = ptb->ci.hwnd; // ptb will change across call below
HRSRC hrsrc; LANGID wLang; LPVOID pTemplate;
if (ptb->hdlgCust) // We are already customizing this toolbar
return; ad.ptb = ptb; ad.iPos = iPos; // REVIEW: really should be per thread data, but not likely to cause a problem
// see note in WM_MEASUREITEM code
g_dyButtonHack = (ptb->ci.style & TBSTYLE_FLAT) ? ptb->iDyBitmap : ptb->iButHeight; SendCmdNotify(ptb, TBN_BEGINADJUST);
//
// Do locale-specific futzing.
//
wLang = LANGIDFROMLCID(CCGetProperThreadLocale(NULL)); hrsrc = FindResourceExRetry(HINST_THISDLL, RT_DIALOG, MAKEINTRESOURCE(ADJUSTDLG), wLang); if (hrsrc && (pTemplate = (LPVOID)LoadResource(HINST_THISDLL, hrsrc))) { DialogBoxIndirectParam(HINST_THISDLL, pTemplate, ptb->ci.hwndParent, AdjustDlgProc, (LPARAM)(LPADJUSTDLGDATA)&ad); }
// ptb probably changed across above call
ptb = (PTBSTATE)GetWindowInt(hwndT, 0); ptb->hdlgCust = NULL; SendCmdNotify(ptb, TBN_ENDADJUST); }
|