Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1499 lines
50 KiB

  1. #include "ctlspriv.h"
  2. #include "toolbar.h"
  3. #include "help.h" // Help IDs
  4. #define SEND_WM_COMMAND(hwnd, id, hwndCtl, codeNotify) \
  5. (void)SendMessage((hwnd), WM_COMMAND, MAKEWPARAM((UINT)(id),(UINT)(codeNotify)), (LPARAM)(HWND)(hwndCtl))
  6. #define SPACESTRLEN 20
  7. #define FLAG_NODEL 0x8000
  8. #define FLAG_HIDDEN 0x4000
  9. #define FLAG_SEP 0x2000
  10. #define FLAG_ALLFLAGS (FLAG_NODEL|FLAG_HIDDEN|FLAG_SEP)
  11. typedef struct { /* instance data for toolbar edit dialog */
  12. HWND hDlg; /* dialog hwnd */
  13. PTBSTATE ptb; // current toolbar state
  14. int iPos; /* position to insert into */
  15. } ADJUSTDLGDATA, *LPADJUSTDLGDATA;
  16. int g_dyButtonHack = 0; // to pass on before WM_INITDIALOG
  17. LPTSTR TB_StrForButton(PTBSTATE ptb, LPTBBUTTONDATA pTBButton);
  18. int GetPrevButton(PTBSTATE ptb, int iPos)
  19. {
  20. /* This means to delete the preceding space
  21. */
  22. for (--iPos; ; --iPos)
  23. {
  24. if (iPos < 0)
  25. break;
  26. if (!(ptb->Buttons[iPos].fsState & TBSTATE_HIDDEN))
  27. break;;
  28. }
  29. return(iPos);
  30. }
  31. BOOL GetAdjustInfo(PTBSTATE ptb, int iItem, LPTBBUTTONDATA ptbButton, LPTSTR lpString, int cbString)
  32. {
  33. TBNOTIFY tbn;
  34. tbn.pszText = lpString;
  35. tbn.cchText = cbString;
  36. tbn.iItem = iItem;
  37. if (lpString)
  38. *lpString = 0;
  39. if ((BOOL)CCSendNotify(&ptb->ci, TBN_GETBUTTONINFO, &tbn.hdr))
  40. {
  41. TBInputStruct(ptb, ptbButton, &tbn.tbButton);
  42. return TRUE;
  43. }
  44. return FALSE;
  45. }
  46. LRESULT SendItemNotify(PTBSTATE ptb, int iItem, int code)
  47. {
  48. TBNOTIFY tbn = {0};
  49. tbn.iItem = iItem;
  50. switch (code)
  51. {
  52. case TBN_QUERYDELETE:
  53. case TBN_QUERYINSERT:
  54. // The following is to provide the parent app with information
  55. // about the button that information is being requested for...
  56. // Otherwise it's really awful trying to have control over
  57. // certain aspects of toolbar customization... [t-mkim]
  58. // IE4.0's toolbar wants this information.
  59. // Should ONLY be done for TBN_QUERY* notifications BECAUSE
  60. // this can be either a zero-based index _or_ Command ID depending
  61. // on the particular notification code.
  62. if (iItem < ptb->iNumButtons)
  63. CopyMemory (&tbn.tbButton, &ptb->Buttons[iItem], sizeof (TBBUTTON));
  64. break;
  65. case TBN_DROPDOWN:
  66. TB_GetItemRect(ptb, PositionFromID(ptb, iItem), &tbn.rcButton);
  67. break;
  68. }
  69. // default return from SendNotify is false
  70. // this actually shouldnt return a bool, TBN_DROPDOWN needs to return 0, 1, or 2.
  71. return CCSendNotify(&ptb->ci, code, &tbn.hdr);
  72. }
  73. #define SendCmdNotify(ptb, code) CCSendNotify(&ptb->ci, code, NULL)
  74. // this is used to deal with the case where the ptb structure is re-alloced
  75. // after a TBInsertButtons()
  76. PTBSTATE FixPTB(HWND hwnd)
  77. {
  78. PTBSTATE ptb = (PTBSTATE)GetWindowInt(hwnd, 0);
  79. if (ptb->hdlgCust)
  80. {
  81. LPADJUSTDLGDATA lpad = (LPADJUSTDLGDATA)GetWindowPtr(ptb->hdlgCust, DWLP_USER);
  82. #ifdef DEBUG
  83. if (lpad->ptb != ptb)
  84. DebugMsg(DM_TRACE, TEXT("Fixing busted ptb pointer"));
  85. #endif
  86. lpad->ptb = ptb;
  87. }
  88. return ptb;
  89. }
  90. void MoveButton(PTBSTATE ptb, int nSource)
  91. {
  92. int nDest;
  93. RECT rc;
  94. HCURSOR hCursor;
  95. MSG32 msg32;
  96. /* You can't move separators like this
  97. */
  98. if (nSource < 0)
  99. return;
  100. // Make sure it is all right to "delete" the selected button
  101. if (!SendItemNotify(ptb, nSource, TBN_QUERYDELETE))
  102. return;
  103. hCursor = SetCursor(LoadCursor(HINST_THISDLL, MAKEINTRESOURCE(IDC_MOVEBUTTON)));
  104. SetCapture(ptb->ci.hwnd);
  105. // Get the dimension of the window.
  106. GetClientRect(ptb->ci.hwnd, &rc);
  107. for ( ; ; )
  108. {
  109. while (!PeekMessage32(&msg32, NULL, 0, 0, PM_REMOVE, TRUE))
  110. ;
  111. if (GetCapture() != ptb->ci.hwnd)
  112. goto AbortMove;
  113. // See if the application wants to process the message...
  114. if (CallMsgFilter32(&msg32, MSGF_COMMCTRL_TOOLBARCUST, TRUE) != 0)
  115. continue;
  116. switch (msg32.message)
  117. {
  118. case WM_KEYDOWN:
  119. case WM_KEYUP:
  120. case WM_CHAR:
  121. //notify of navigation key usage
  122. CCNotifyNavigationKeyUsage(&(ptb->ci), UISF_HIDEFOCUS);
  123. break;
  124. case WM_LBUTTONUP:
  125. RelayToToolTips(ptb->hwndToolTips, ptb->ci.hwnd, msg32.message, msg32.wParam, msg32.lParam);
  126. if ((GET_Y_LPARAM(msg32.lParam) > (short)(rc.bottom+ptb->iButWidth)) ||
  127. (GET_X_LPARAM(msg32.lParam) > (short)(rc.right+ptb->iButWidth)) ||
  128. (GET_Y_LPARAM(msg32.lParam) < -ptb->iButWidth) ||
  129. (GET_X_LPARAM(msg32.lParam) < -ptb->iButWidth))
  130. {
  131. /* If the button was dragged off the toolbar, delete it.
  132. */
  133. DeleteSrcButton:
  134. DeleteButton(ptb, nSource);
  135. SendCmdNotify(ptb, TBN_TOOLBARCHANGE);
  136. TBInvalidateItemRects(ptb);
  137. }
  138. else
  139. {
  140. TBBUTTONDATA tbbAdd;
  141. /* Add half a button to X so that it looks like it is centered
  142. * over the target button, iff we have a horizontal layout.
  143. * Add half a button to Y otherwise.
  144. */
  145. if (rc.right!=ptb->iButWidth)
  146. nDest = TBHitTest(ptb,
  147. GET_X_LPARAM(msg32.lParam) + ptb->iButWidth / 2,
  148. GET_Y_LPARAM(msg32.lParam));
  149. else
  150. nDest = TBHitTest(ptb,
  151. GET_X_LPARAM(msg32.lParam),
  152. GET_Y_LPARAM(msg32.lParam) + ptb->iButHeight / 2);
  153. if (nDest < 0)
  154. nDest = -1 - nDest;
  155. if (nDest>0 &&
  156. (ptb->Buttons[nDest-1].fsState & TBSTATE_WRAP) &&
  157. GET_X_LPARAM(msg32.lParam)>ptb->iButWidth &&
  158. SendItemNotify(ptb, --nDest, TBN_QUERYINSERT))
  159. {
  160. tbbAdd = ptb->Buttons[nSource];
  161. DeleteButton(ptb, nSource);
  162. if (nDest>nSource)
  163. --nDest;
  164. /* Insert before spaces, but after buttons. */
  165. if (!(ptb->Buttons[nDest].fsStyle & TBSTYLE_SEP))
  166. nDest++;
  167. goto InsertSrcButton;
  168. }
  169. else if (nDest == nSource)
  170. {
  171. /* This means to delete the preceding space, or to move a
  172. button to the previous row.
  173. */
  174. nSource = GetPrevButton(ptb, nSource);
  175. if (nSource < 0)
  176. goto AbortMove;
  177. // If the preceding item is a space with no ID, and
  178. // the app says it's OK, then delete it.
  179. if ((ptb->Buttons[nSource].fsStyle & TBSTYLE_SEP)
  180. && !ptb->Buttons[nSource].idCommand
  181. && SendItemNotify(ptb, nSource, TBN_QUERYDELETE))
  182. goto DeleteSrcButton;
  183. }
  184. else if (nDest == nSource+1)
  185. {
  186. // This means to add a preceding space
  187. --nDest;
  188. if (SendItemNotify(ptb, nDest, TBN_QUERYINSERT))
  189. {
  190. tbbAdd.DUMMYUNION_MEMBER(iBitmap) = 0;
  191. tbbAdd.idCommand = 0;
  192. tbbAdd.iString = -1;
  193. tbbAdd.fsState = 0;
  194. tbbAdd.fsStyle = TBSTYLE_SEP;
  195. goto InsertSrcButton;
  196. }
  197. }
  198. else if (SendItemNotify(ptb, nDest, TBN_QUERYINSERT))
  199. {
  200. HWND hwndT;
  201. TBBUTTON tbbAddExt;
  202. /* This is a normal move operation
  203. */
  204. tbbAdd = ptb->Buttons[nSource];
  205. ptb->Buttons[nSource].iString = -1;
  206. DeleteButton(ptb, nSource);
  207. if (nDest > nSource)
  208. --nDest;
  209. InsertSrcButton:
  210. hwndT = ptb->ci.hwnd;
  211. TBOutputStruct(ptb, &tbbAdd, &tbbAddExt);
  212. TBInsertButtons(ptb, nDest, 1, &tbbAddExt, TRUE);
  213. ptb = FixPTB(hwndT);
  214. SendCmdNotify(ptb, TBN_TOOLBARCHANGE);
  215. TBInvalidateItemRects(ptb);
  216. }
  217. else
  218. {
  219. AbortMove:
  220. ;
  221. }
  222. }
  223. goto AllDone;
  224. case WM_RBUTTONDOWN:
  225. goto AbortMove;
  226. default:
  227. TranslateMessage32(&msg32, TRUE);
  228. DispatchMessage32(&msg32, TRUE);
  229. break;
  230. }
  231. }
  232. AllDone:
  233. SetCursor(hCursor);
  234. CCReleaseCapture(&ptb->ci);
  235. }
  236. #define GNI_HIGH 0x0001
  237. #define GNI_LOW 0x0002
  238. int GetNearestInsert(PTBSTATE ptb, int iPos, int iNumButtons, UINT uFlags)
  239. {
  240. int i;
  241. BOOL bKeepTrying;
  242. // Find the nearest index where we can actually insert items
  243. for (i = iPos; ; ++i, --iPos)
  244. {
  245. bKeepTrying = FALSE;
  246. // Notice we favor going high if both flags are set
  247. if ((uFlags & GNI_HIGH) && i <= iNumButtons)
  248. {
  249. bKeepTrying = TRUE;
  250. if (SendItemNotify(ptb, i, TBN_QUERYINSERT))
  251. return i;
  252. }
  253. if ((uFlags & GNI_LOW) && iPos >= 0)
  254. {
  255. bKeepTrying = TRUE;
  256. if (SendItemNotify(ptb, iPos, TBN_QUERYINSERT))
  257. return iPos;
  258. }
  259. if (!bKeepTrying)
  260. return -1; // There was no place to add buttons
  261. }
  262. }
  263. BOOL InitAdjustDlg(HWND hDlg, LPADJUSTDLGDATA lpad)
  264. {
  265. HDC hDC;
  266. HFONT hFont;
  267. HWND hwndCurrent, hwndNew;
  268. LPTBBUTTONDATA ptbButton;
  269. int i, iPos, nItem, nWid, nMaxWid;
  270. TBBUTTONDATA tbAdjust;
  271. TCHAR szDesc[128];
  272. NMTBCUSTOMIZEDLG nm;
  273. TCHAR szSeparator[MAX_PATH];
  274. szSeparator[0] = 0;
  275. LocalizedLoadString(IDS_SPACE, szSeparator, ARRAYSIZE(szSeparator));
  276. lpad->hDlg = hDlg;
  277. lpad->ptb->hdlgCust = hDlg;
  278. /* Determine the item nearest the desired item that will allow
  279. * insertion.
  280. */
  281. iPos = GetNearestInsert(lpad->ptb, lpad->iPos, lpad->ptb->iNumButtons,
  282. GNI_HIGH | GNI_LOW);
  283. if (iPos < 0)
  284. /* No item allowed insertion, so leave the dialog */
  285. {
  286. return(FALSE);
  287. }
  288. /* Reset the lists of used and available items.
  289. */
  290. hwndCurrent = GetDlgItem(hDlg, IDC_CURRENT);
  291. SendMessage(hwndCurrent, LB_RESETCONTENT, 0, 0L);
  292. hwndNew = GetDlgItem(hDlg, IDC_BUTTONLIST);
  293. SendMessage(hwndNew, LB_RESETCONTENT, 0, 0L);
  294. nm.hDlg = hDlg;
  295. if (CCSendNotify(&lpad->ptb->ci, TBN_INITCUSTOMIZE, &nm.hdr) == TBNRF_HIDEHELP)
  296. {
  297. ShowWindow(GetDlgItem(hDlg, IDC_APPHELP), SW_HIDE);
  298. }
  299. for (i=0, ptbButton = lpad->ptb->Buttons; i < lpad->ptb->iNumButtons; ++i, ++ptbButton)
  300. {
  301. UINT uFlags;
  302. int iBitmap;
  303. LPTSTR pszStr = NULL;
  304. uFlags = 0;
  305. // Non-deletable and hidden items show up grayed.
  306. if (!SendItemNotify(lpad->ptb, i, TBN_QUERYDELETE))
  307. {
  308. uFlags |= FLAG_NODEL;
  309. }
  310. if (ptbButton->fsState & TBSTATE_HIDDEN)
  311. {
  312. uFlags |= FLAG_HIDDEN;
  313. }
  314. /* Separators have no bitmaps (even ones with IDs). Only set
  315. * the separator flag if there is no ID (it is a "real"
  316. * separator rather than an owner item).
  317. */
  318. if (ptbButton->fsStyle&TBSTYLE_SEP)
  319. {
  320. if (!(ptbButton->idCommand))
  321. {
  322. uFlags |= FLAG_SEP;
  323. }
  324. iBitmap = -1;
  325. pszStr = szSeparator;
  326. }
  327. else
  328. {
  329. iBitmap = ptbButton->DUMMYUNION_MEMBER(iBitmap);
  330. // this specifies an imagelist.
  331. // pack this into the loword of the ibitmap.
  332. // this causes a restriction of max 16 imagelists, and 4096 images in any imagelist
  333. iBitmap = LOWORD(iBitmap) | (HIWORD(iBitmap) << 12);
  334. /* Add the item and the data
  335. * Note: A negative number in the LOWORD indicates no bitmap;
  336. * otherwise it is the bitmap index.
  337. */
  338. pszStr = TB_StrForButton(lpad->ptb, ptbButton);
  339. }
  340. if ((int)SendMessage(hwndCurrent, LB_ADDSTRING, 0, (LPARAM)(LPTSTR)(pszStr ? pszStr : (LPTSTR)c_szNULL)) != i)
  341. {
  342. return(FALSE);
  343. }
  344. SendMessage(hwndCurrent, LB_SETITEMDATA, i, MAKELPARAM(iBitmap, uFlags));
  345. }
  346. /* Add a dummy "nodel" space at the end so things can be inserted at the end.
  347. */
  348. if ((int)SendMessage(hwndCurrent, LB_ADDSTRING, 0,(LPARAM)(LPTSTR)szSeparator) == i)
  349. {
  350. SendMessage(hwndCurrent, LB_SETITEMDATA, i, MAKELPARAM(-1, FLAG_NODEL|FLAG_SEP));
  351. }
  352. /* Now add a space at the beginning of the "new" list.
  353. */
  354. if (SendMessage(hwndNew, LB_ADDSTRING, 0, (LPARAM)(LPTSTR)szSeparator) == LB_ERR)
  355. return(FALSE);
  356. SendMessage(hwndNew, LB_SETITEMDATA, 0, MAKELPARAM(-1, FLAG_SEP));
  357. /* We need this to determine the widest (in pixels) item string.
  358. */
  359. hDC = GetDC(hwndCurrent);
  360. hFont = (HFONT)(INT_PTR)SendMessage(hwndCurrent, WM_GETFONT, 0, 0L);
  361. if (hFont)
  362. {
  363. hFont = SelectObject(hDC, hFont);
  364. }
  365. nMaxWid = 0;
  366. for (i=0; ; ++i)
  367. {
  368. // Get the info about the i'th item from the app.
  369. if (!GetAdjustInfo(lpad->ptb, i, &tbAdjust, szDesc, ARRAYSIZE(szDesc)))
  370. break;
  371. if (!szDesc[0])
  372. {
  373. LPTSTR psz = TB_StrForButton(lpad->ptb, &tbAdjust);
  374. if (psz)
  375. {
  376. lstrcpyn(szDesc, psz, ARRAYSIZE(szDesc));
  377. }
  378. }
  379. /* Don't show separators that don't have commands
  380. */
  381. if (!(tbAdjust.fsStyle & TBSTYLE_SEP) || tbAdjust.idCommand)
  382. {
  383. /* Get the maximum width of a string.
  384. */
  385. MGetTextExtent(hDC, szDesc, lstrlen(szDesc), &nWid, NULL);
  386. if (nMaxWid < nWid)
  387. {
  388. nMaxWid = nWid;
  389. }
  390. nItem = PositionFromID(lpad->ptb, tbAdjust.idCommand);
  391. if (nItem < 0)
  392. /* If the item is not on the toolbar already */
  393. {
  394. #ifdef UNIX
  395. if (!lstrcmp(szDesc, TEXT("Folders")) || !lstrcmp(szDesc, TEXT("Edit")))
  396. continue;
  397. #endif
  398. /* Don't show hidden buttons
  399. */
  400. if (!(tbAdjust.fsState & TBSTATE_HIDDEN))
  401. {
  402. nItem = (int)SendMessage(hwndNew, LB_ADDSTRING, 0,
  403. (LPARAM)(LPTSTR)szDesc);
  404. if (nItem != LB_ERR)
  405. {
  406. if (tbAdjust.fsStyle & TBSTYLE_SEP)
  407. SendMessage(hwndNew, LB_SETITEMDATA, nItem,
  408. MAKELPARAM(-1, i));
  409. else
  410. {
  411. int iBitmap = tbAdjust.DUMMYUNION_MEMBER(iBitmap);
  412. iBitmap = LOWORD(iBitmap) | (HIWORD(iBitmap) << 12);
  413. SendMessage(hwndNew, LB_SETITEMDATA, nItem,
  414. MAKELPARAM(iBitmap, i));
  415. }
  416. }
  417. }
  418. }
  419. else
  420. /* The item is on the toolbar already */
  421. {
  422. /* Preserve the flags and bitmap.
  423. */
  424. DWORD dwTemp = (DWORD)SendMessage(hwndCurrent, LB_GETITEMDATA, nItem, 0L);
  425. if (szDesc[0])
  426. {
  427. SendMessage(hwndCurrent, LB_DELETESTRING, nItem, 0L);
  428. if ((int)SendMessage(hwndCurrent, LB_INSERTSTRING, nItem,
  429. (LPARAM)(LPTSTR)szDesc) != nItem)
  430. {
  431. ReleaseDC(hwndCurrent, hDC);
  432. return(FALSE);
  433. }
  434. }
  435. SendMessage(hwndCurrent, LB_SETITEMDATA, nItem,
  436. MAKELPARAM(LOWORD(dwTemp), HIWORD(dwTemp)|i));
  437. }
  438. }
  439. }
  440. if (hFont)
  441. {
  442. SelectObject(hDC, hFont);
  443. }
  444. ReleaseDC(hwndCurrent, hDC);
  445. /* Add on some extra and set the extents for both lists.
  446. */
  447. nMaxWid += lpad->ptb->iButWidth + 2 + 1;
  448. SendMessage(hwndNew, LB_SETHORIZONTALEXTENT, nMaxWid, 0L);
  449. SendMessage(hwndCurrent, LB_SETHORIZONTALEXTENT, nMaxWid, 0L);
  450. /* Set the sels and return.
  451. */
  452. SendMessage(hwndNew, LB_SETCURSEL, 0, 0L);
  453. SendMessage(hwndCurrent, LB_SETCURSEL, iPos, 0L);
  454. SEND_WM_COMMAND(hDlg, IDC_CURRENT, hwndCurrent, LBN_SELCHANGE);
  455. return(TRUE);
  456. }
  457. #define IsSeparator(x) (HIWORD(x) & FLAG_SEP)
  458. void PaintAdjustLine(PTBSTATE ptb, DRAWITEMSTRUCT *lpdis)
  459. {
  460. HDC hdc = lpdis->hDC;
  461. HWND hwndList = lpdis->hwndItem;
  462. PTSTR pszText;
  463. RECT rc = lpdis->rcItem;
  464. int nBitmap, nLen, nItem = lpdis->itemID;
  465. COLORREF oldBkColor, oldTextColor;
  466. BOOL bSelected, bHasFocus;
  467. int wHeight;
  468. int x;
  469. if (lpdis->CtlID != IDC_BUTTONLIST && lpdis->CtlID != IDC_CURRENT)
  470. return;
  471. nBitmap = LOWORD(lpdis->itemData);
  472. // unpack the nBitmap. we stored the imagelist spec in the hi char of loword
  473. if (nBitmap != 0xFFFF)
  474. nBitmap = (nBitmap & 0x0FFF) | ((nBitmap & 0xF000) << 4);
  475. nLen = (int)SendMessage(hwndList, LB_GETTEXTLEN, nItem, 0L);
  476. if (nLen < 0)
  477. return;
  478. pszText = (PTSTR)LocalAlloc(LPTR, (nLen+1)*sizeof(TCHAR));
  479. if (!pszText)
  480. return;
  481. // This needs to work for separators also or ActiveAccessibility
  482. // won't work.
  483. SendMessage(hwndList, LB_GETTEXT, nItem, (LPARAM)(LPTSTR)pszText);
  484. if (lpdis->itemAction != ODA_FOCUS)
  485. {
  486. COLORREF clr;
  487. TCHAR szSample[2];
  488. /* We don't care about focus if the item is not selected.
  489. */
  490. bSelected = lpdis->itemState & ODS_SELECTED;
  491. bHasFocus = bSelected && (GetFocus() == hwndList);
  492. if (HIWORD(lpdis->itemData) & (FLAG_NODEL | FLAG_HIDDEN))
  493. clr = g_clrGrayText;
  494. else if (bHasFocus)
  495. clr = g_clrHighlightText;
  496. else
  497. clr = g_clrWindowText;
  498. oldTextColor = SetTextColor(hdc, clr);
  499. oldBkColor = SetBkColor(hdc, bHasFocus ? g_clrHighlight : g_clrWindow);
  500. szSample[0] = TEXT('W');
  501. szSample[1] = TEXT('\0');
  502. MGetTextExtent(hdc, szSample, 1, NULL, &wHeight);
  503. x = rc.left + 2;
  504. x += (ptb->ci.style & TBSTYLE_FLAT) ? (ptb->iDxBitmap + g_cxEdge) : ptb->iButWidth;
  505. ExtTextOut(hdc, x,
  506. (rc.top + rc.bottom-wHeight) / 2,
  507. ETO_CLIPPED | ETO_OPAQUE, &rc, pszText, nLen, NULL);
  508. /* We really care about the bitmap value here; this is not just an
  509. * indicator for the separator.
  510. */
  511. if (nBitmap >= 0)
  512. {
  513. TBBUTTONDATA tbbAdd = {0};
  514. TBDRAWITEM tbdraw = {0};
  515. tbbAdd.DUMMYUNION_MEMBER(iBitmap) = nBitmap;
  516. tbbAdd.iString = -1;
  517. tbbAdd.fsStyle = TBSTYLE_BUTTON;
  518. tbbAdd.fsState = (BYTE)((HIWORD(lpdis->itemData) & FLAG_HIDDEN) ? 0 : TBSTATE_ENABLED);
  519. InitTBDrawItem(&tbdraw, ptb, &tbbAdd, tbbAdd.fsState, 0, 0, 0);
  520. if (ptb->ci.style & TBSTYLE_FLAT)
  521. {
  522. RECT rcFace = rc;
  523. rcFace.right = rcFace.left + ptb->iDxBitmap + g_cxEdge;
  524. DrawFace(hdc, &rcFace, rc.left + 1, rc.top + 1, 0, 0, 0, 0, &tbdraw, 0, 0);
  525. }
  526. else
  527. DrawButton(hdc, rc.left + 1, rc.top + 1, ptb, &tbbAdd, TRUE);
  528. ReleaseMonoDC(ptb);
  529. }
  530. SetBkColor(hdc, oldBkColor);
  531. SetTextColor(hdc, oldTextColor);
  532. /* Frame the item if it is selected but does not have the focus.
  533. */
  534. if (bSelected && !bHasFocus)
  535. {
  536. nLen = rc.left + (int)SendMessage(hwndList,
  537. LB_GETHORIZONTALEXTENT, 0, 0L);
  538. if (rc.right < nLen)
  539. rc.right = nLen;
  540. FrameRect(hdc, &rc, g_hbrHighlight);
  541. }
  542. }
  543. if ((lpdis->itemAction == ODA_FOCUS || (lpdis->itemState & ODS_FOCUS)) &&
  544. !(CCGetUIState(&(ptb->ci)) & UISF_HIDEFOCUS))
  545. {
  546. DrawFocusRect(hdc, &rc);
  547. }
  548. LocalFree((HLOCAL)pszText);
  549. }
  550. void LBMoveButton(LPADJUSTDLGDATA lpad, UINT wIDSrc, int iPosSrc,
  551. UINT wIDDst, int iPosDst, int iSelOffset)
  552. {
  553. HWND hwndSrc, hwndDst;
  554. DWORD dwDataSrc;
  555. PTSTR pStr;
  556. TBBUTTONDATA tbAdjust = {0};
  557. TBBUTTON tbbAddExt;
  558. int iTopDst;
  559. TCHAR szDesc[128];
  560. hwndSrc = GetDlgItem(lpad->hDlg, wIDSrc);
  561. hwndDst = GetDlgItem(lpad->hDlg, wIDDst);
  562. // Make sure we can delete the source and insert at the dest
  563. //
  564. dwDataSrc = (DWORD)SendMessage(hwndSrc, LB_GETITEMDATA, iPosSrc, 0L);
  565. if (iPosSrc < 0 || (HIWORD(dwDataSrc) & FLAG_NODEL))
  566. return;
  567. if (wIDDst == IDC_CURRENT &&
  568. !SendItemNotify(lpad->ptb, iPosDst, TBN_QUERYINSERT))
  569. return;
  570. // Get the string for the source
  571. //
  572. pStr = (PTSTR)LocalAlloc(LPTR,
  573. ((int)(SendMessage(hwndSrc, LB_GETTEXTLEN, iPosSrc, 0L))+1)*sizeof(TCHAR));
  574. if (!pStr)
  575. return;
  576. SendMessage(hwndSrc, LB_GETTEXT, iPosSrc, (LPARAM)(LPTSTR)pStr);
  577. SendMessage(hwndSrc, WM_SETREDRAW, 0, 0L);
  578. SendMessage(hwndDst, WM_SETREDRAW, 0, 0L);
  579. iTopDst = (int)SendMessage(hwndDst, LB_GETTOPINDEX, 0, 0L);
  580. // If we are inserting into the available button list, we need to determine
  581. // the insertion point
  582. //
  583. if (wIDDst == IDC_BUTTONLIST)
  584. {
  585. // Insert this back in the available list if this is not a space or a
  586. // hidden button.
  587. //
  588. if (HIWORD(dwDataSrc)&(FLAG_SEP|FLAG_HIDDEN))
  589. {
  590. iPosDst = 0;
  591. goto DelTheSrc;
  592. }
  593. else
  594. {
  595. UINT uCmdSrc = HIWORD(dwDataSrc) & ~(FLAG_ALLFLAGS);
  596. // This just does a linear search for where to put the
  597. // item. Slow, but this only happens when the user clicks
  598. // the "Remove" button.
  599. //
  600. iPosDst = 1;
  601. for ( ; ; ++iPosDst)
  602. {
  603. // Notice that this will break out when iPosDst is
  604. // past the number of items, since -1 will be returned
  605. //
  606. if ((UINT)HIWORD(SendMessage(hwndDst, LB_GETITEMDATA,
  607. iPosDst, 0L)) >= uCmdSrc)
  608. break;
  609. }
  610. }
  611. }
  612. else if (iPosDst < 0)
  613. goto CleanUp;
  614. // Attempt to insert the new string
  615. //
  616. if ((int)SendMessage(hwndDst, LB_INSERTSTRING, iPosDst, (LPARAM)(LPTSTR)pStr)
  617. == iPosDst)
  618. {
  619. // Attempt to sync up the actual toolbar.
  620. //
  621. if (wIDDst == IDC_CURRENT)
  622. {
  623. HWND hwndT;
  624. if (IsSeparator(dwDataSrc))
  625. {
  626. // Make up a dummy lpInfo if this is a space
  627. //
  628. tbAdjust.DUMMYUNION_MEMBER(iBitmap) = 0;
  629. tbAdjust.idCommand = 0;
  630. tbAdjust.fsState = 0;
  631. tbAdjust.fsStyle = TBSTYLE_SEP;
  632. }
  633. else
  634. {
  635. // Call back to client to get the source button info
  636. //
  637. int iCmdSrc = HIWORD(dwDataSrc) & ~FLAG_ALLFLAGS;
  638. if (!GetAdjustInfo(lpad->ptb, iCmdSrc, &tbAdjust, szDesc, ARRAYSIZE(szDesc)))
  639. goto DelTheDst;
  640. }
  641. hwndT = lpad->ptb->ci.hwnd;
  642. TBOutputStruct(lpad->ptb, &tbAdjust, &tbbAddExt);
  643. if (!TBInsertButtons(lpad->ptb, iPosDst, 1, &tbbAddExt, TRUE))
  644. {
  645. DelTheDst:
  646. SendMessage(hwndDst, LB_DELETESTRING, iPosDst, 0L);
  647. goto CleanUp;
  648. }
  649. else
  650. {
  651. lpad->ptb = FixPTB(hwndT);
  652. }
  653. if (wIDSrc == IDC_CURRENT && iPosSrc >= iPosDst)
  654. ++iPosSrc;
  655. }
  656. SendMessage(hwndDst, LB_SETITEMDATA, iPosDst, dwDataSrc);
  657. DelTheSrc:
  658. // Don't delete the "Separator" in the new list
  659. //
  660. if ((wIDSrc != IDC_BUTTONLIST) || (iPosSrc != 0))
  661. {
  662. SendMessage(hwndSrc, LB_DELETESTRING, iPosSrc, 0L);
  663. if (wIDSrc == wIDDst)
  664. {
  665. if (iPosSrc < iPosDst)
  666. --iPosDst;
  667. if (iPosSrc < iTopDst)
  668. --iTopDst;
  669. }
  670. }
  671. // Delete the corresponding button
  672. //
  673. if (wIDSrc == IDC_CURRENT)
  674. DeleteButton(lpad->ptb, iPosSrc);
  675. // Only set the src index if the two windows are different
  676. //
  677. if (wIDSrc != wIDDst)
  678. {
  679. if (iPosSrc >= SendMessage(hwndSrc, LB_GETCOUNT, 0, 0L))
  680. {
  681. // HACKHACK: workaround for funkdified listbox scrolling behavior.
  682. // Select the first item (to force scroll back to top of list),
  683. // then select the item we really want selected.
  684. SendMessage(hwndSrc, LB_SETCURSEL, 0, 0L);
  685. }
  686. if (SendMessage(hwndSrc, LB_SETCURSEL, iPosSrc, 0L) == LB_ERR)
  687. SendMessage(hwndSrc, LB_SETCURSEL, iPosSrc-1, 0L);
  688. SEND_WM_COMMAND(lpad->hDlg, wIDSrc, hwndSrc, LBN_SELCHANGE);
  689. }
  690. // Send the final SELCHANGE message after everything else is done
  691. //
  692. SendMessage(hwndDst, LB_SETCURSEL, iPosDst+iSelOffset, 0L);
  693. SEND_WM_COMMAND(lpad->hDlg, wIDDst, hwndDst, LBN_SELCHANGE);
  694. }
  695. CleanUp:
  696. LocalFree((HLOCAL)pStr);
  697. if (wIDSrc == wIDDst)
  698. {
  699. SendMessage(hwndDst, LB_SETTOPINDEX, iTopDst, 0L);
  700. //make sure that the selected item is still visible
  701. SendMessage(hwndDst, LB_SETCURSEL, (int)SendMessage(hwndDst, LB_GETCURSEL, 0, 0L), 0);
  702. }
  703. SendMessage(hwndSrc, WM_SETREDRAW, 1, 0L);
  704. SendMessage(hwndDst, WM_SETREDRAW, 1, 0L);
  705. InvalidateRect(hwndDst, NULL, TRUE);
  706. SendCmdNotify(lpad->ptb, TBN_TOOLBARCHANGE);
  707. }
  708. void SafeEnableWindow(HWND hDlg, UINT wID, HWND hwndDef, BOOL bEnable)
  709. {
  710. HWND hwndEnable;
  711. hwndEnable = GetDlgItem(hDlg, wID);
  712. if (!bEnable && GetFocus()==hwndEnable)
  713. SendMessage(hDlg, WM_NEXTDLGCTL, (WPARAM)hwndDef, 1L);
  714. EnableWindow(hwndEnable, bEnable);
  715. }
  716. int InsertIndex(LPADJUSTDLGDATA lpad, POINT pt, BOOL bDragging)
  717. {
  718. HWND hwndCurrent = GetDlgItem(lpad->hDlg, IDC_CURRENT);
  719. int nItem = LBItemFromPt(hwndCurrent, pt, bDragging);
  720. if (nItem >= 0)
  721. {
  722. if (!SendItemNotify(lpad->ptb, nItem, TBN_QUERYINSERT))
  723. nItem = -1;
  724. }
  725. DrawInsert(lpad->hDlg, hwndCurrent, bDragging ? nItem : -1);
  726. return(nItem);
  727. }
  728. BOOL IsInButtonList(HWND hDlg, POINT pt)
  729. {
  730. ScreenToClient(hDlg, &pt);
  731. return(ChildWindowFromPoint(hDlg, pt) == GetDlgItem(hDlg, IDC_BUTTONLIST));
  732. }
  733. BOOL HandleDragMsg(LPADJUSTDLGDATA lpad, HWND hDlg, WPARAM wID, LPDRAGLISTINFO lpns)
  734. {
  735. switch (wID)
  736. {
  737. case IDC_CURRENT:
  738. switch (lpns->uNotification)
  739. {
  740. case DL_BEGINDRAG:
  741. {
  742. int nItem = (int)SendMessage(lpns->hWnd, LB_GETCURSEL, 0, 0L);
  743. if (HIWORD(SendMessage(lpns->hWnd, LB_GETITEMDATA, nItem, 0L)) & FLAG_NODEL)
  744. return SetDlgMsgResult(hDlg, WM_COMMAND, FALSE);
  745. return SetDlgMsgResult(hDlg, WM_COMMAND, TRUE);
  746. }
  747. case DL_DRAGGING:
  748. {
  749. int nDropIndex;
  750. DraggingSomething:
  751. nDropIndex = InsertIndex(lpad, lpns->ptCursor, TRUE);
  752. if (nDropIndex>=0 || IsInButtonList(hDlg, lpns->ptCursor))
  753. {
  754. SetCursor(LoadCursor(HINST_THISDLL,
  755. MAKEINTRESOURCE(IDC_MOVEBUTTON)));
  756. return SetDlgMsgResult(hDlg, WM_COMMAND, 0);
  757. }
  758. return SetDlgMsgResult(hDlg, WM_COMMAND, DL_STOPCURSOR);
  759. }
  760. case DL_DROPPED:
  761. {
  762. int nDropIndex, nSrcIndex;
  763. nDropIndex = InsertIndex(lpad, lpns->ptCursor, FALSE);
  764. nSrcIndex = (int)SendMessage(lpns->hWnd, LB_GETCURSEL, 0, 0L);
  765. if (nDropIndex >= 0)
  766. {
  767. if ((UINT)(nDropIndex-nSrcIndex) > 1)
  768. LBMoveButton(lpad, IDC_CURRENT, nSrcIndex,
  769. IDC_CURRENT, nDropIndex, 0);
  770. }
  771. else if (IsInButtonList(hDlg, lpns->ptCursor))
  772. {
  773. LBMoveButton(lpad, IDC_CURRENT, nSrcIndex, IDC_BUTTONLIST, 0, 0);
  774. }
  775. break;
  776. }
  777. case DL_CANCELDRAG:
  778. CancelDrag:
  779. /* This erases the insert icon if it exists.
  780. */
  781. InsertIndex(lpad, lpns->ptCursor, FALSE);
  782. break;
  783. default:
  784. break;
  785. }
  786. break;
  787. case IDC_BUTTONLIST:
  788. switch (lpns->uNotification)
  789. {
  790. case DL_BEGINDRAG:
  791. return SetDlgMsgResult(hDlg, WM_COMMAND, TRUE);
  792. case DL_DRAGGING:
  793. goto DraggingSomething;
  794. case DL_DROPPED:
  795. {
  796. int nDropIndex;
  797. nDropIndex = InsertIndex(lpad, lpns->ptCursor, FALSE);
  798. if (nDropIndex >= 0)
  799. LBMoveButton(lpad, IDC_BUTTONLIST,
  800. (int)SendMessage(lpns->hWnd,LB_GETCURSEL,0,0L),
  801. IDC_CURRENT, nDropIndex, 0);
  802. break;
  803. }
  804. case DL_CANCELDRAG:
  805. goto CancelDrag;
  806. default:
  807. break;
  808. }
  809. break;
  810. default:
  811. break;
  812. }
  813. return(0);
  814. }
  815. // Context Help IDs
  816. const static DWORD aAdjustHelpIDs[] =
  817. {
  818. IDC_RESET, IDH_COMCTL_RESET,
  819. IDC_APPHELP, IDH_HELP,
  820. IDC_MOVEUP, IDH_COMCTL_MOVEUP,
  821. IDC_MOVEDOWN, IDH_COMCTL_MOVEDOWN,
  822. IDC_BUTTONLIST, IDH_COMCTL_AVAIL_BUTTONS,
  823. IDOK, IDH_COMCTL_ADD,
  824. IDC_REMOVE, IDH_COMCTL_REMOVE,
  825. IDC_CURRENT, IDH_COMCTL_BUTTON_LIST,
  826. IDCANCEL, IDH_COMCTL_CLOSE,
  827. 0, 0
  828. };
  829. BOOL_PTR CALLBACK AdjustDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  830. {
  831. LPADJUSTDLGDATA lpad = (LPADJUSTDLGDATA)GetWindowPtr(hDlg, DWLP_USER);
  832. switch (uMsg)
  833. {
  834. case WM_INITDIALOG:
  835. SetWindowLongPtr(hDlg, DWLP_USER, lParam); /* LPADJUSTDLGDATA pointer */
  836. if (!InitAdjustDlg(hDlg, (LPADJUSTDLGDATA)lParam))
  837. EndDialog(hDlg, FALSE);
  838. ShowWindow(hDlg, SW_SHOW);
  839. UpdateWindow(hDlg);
  840. SetFocus(GetDlgItem(hDlg, IDC_CURRENT));
  841. MakeDragList(GetDlgItem(hDlg, IDC_CURRENT));
  842. MakeDragList(GetDlgItem(hDlg, IDC_BUTTONLIST));
  843. return FALSE;
  844. case WM_MEASUREITEM:
  845. #define lpmis ((MEASUREITEMSTRUCT *)lParam)
  846. if (lpmis->CtlID == IDC_BUTTONLIST || lpmis->CtlID == IDC_CURRENT)
  847. {
  848. int nHeight;
  849. HWND hwndList = GetDlgItem(hDlg, lpmis->CtlID);
  850. HDC hDC = GetDC(hwndList);
  851. TCHAR szSample[2];
  852. szSample[0] = TEXT('W');
  853. szSample[1] = TEXT('\0');
  854. MGetTextExtent(hDC, szSample, 1, NULL, &nHeight);
  855. // note, we use this hack because we get WM_MEASUREITEMS
  856. // before our WM_INITDIALOG where we get the lpad setup
  857. if (nHeight < g_dyButtonHack + 2)
  858. nHeight = g_dyButtonHack + 2;
  859. lpmis->itemHeight = nHeight;
  860. ReleaseDC(hwndList, hDC);
  861. }
  862. break;
  863. case WM_DRAWITEM:
  864. PaintAdjustLine(lpad->ptb, (DRAWITEMSTRUCT *)lParam);
  865. break;
  866. case WM_HELP:
  867. WinHelp((HWND)((LPHELPINFO) lParam)->hItemHandle, NULL,
  868. HELP_WM_HELP, (ULONG_PTR)(LPTSTR) aAdjustHelpIDs);
  869. break;
  870. case WM_CONTEXTMENU:
  871. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
  872. (ULONG_PTR)(LPVOID) aAdjustHelpIDs);
  873. break;
  874. case WM_COMMAND:
  875. switch (GET_WM_COMMAND_ID(wParam, lParam))
  876. {
  877. case IDC_APPHELP:
  878. SendCmdNotify(lpad->ptb, TBN_CUSTHELP);
  879. break;
  880. case IDOK:
  881. {
  882. int iPos, nItem;
  883. nItem = (int)SendDlgItemMessage(hDlg, IDC_BUTTONLIST,
  884. LB_GETCURSEL, 0, 0L);
  885. iPos = (int)SendDlgItemMessage(hDlg, IDC_CURRENT,
  886. LB_GETCURSEL, 0, 0L);
  887. if (iPos == -1)
  888. iPos = 0;
  889. LBMoveButton(lpad, IDC_BUTTONLIST, nItem, IDC_CURRENT, iPos, 1);
  890. break;
  891. }
  892. case IDC_BUTTONLIST:
  893. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  894. {
  895. case LBN_DBLCLK:
  896. SendMessage(hDlg, WM_COMMAND, IDOK, 0L);
  897. break;
  898. case LBN_SETFOCUS:
  899. case LBN_KILLFOCUS:
  900. {
  901. RECT rc;
  902. if (SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETITEMRECT,
  903. (int)SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETCURSEL,
  904. 0, 0L), (LPARAM)(LPRECT)&rc) != LB_ERR)
  905. InvalidateRect(GET_WM_COMMAND_HWND(wParam, lParam), &rc, FALSE);
  906. }
  907. default:
  908. break;
  909. }
  910. break;
  911. case IDC_CURRENT:
  912. switch (GET_WM_COMMAND_CMD(wParam, lParam))
  913. {
  914. case LBN_SELCHANGE:
  915. {
  916. BOOL bDelOK;
  917. HWND hwndList = GET_WM_COMMAND_HWND(wParam, lParam);
  918. int iPos = (int)SendMessage(hwndList, LB_GETCURSEL, 0, 0L);
  919. SafeEnableWindow(hDlg, IDOK, hwndList, BOOLFROMPTR(SendItemNotify(lpad->ptb, iPos, TBN_QUERYINSERT)));
  920. bDelOK = !(HIWORD(SendMessage(hwndList, LB_GETITEMDATA, iPos, 0L)) & FLAG_NODEL);
  921. SafeEnableWindow(hDlg, IDC_REMOVE, hwndList, bDelOK);
  922. SafeEnableWindow(hDlg, IDC_MOVEUP, hwndList, bDelOK &&
  923. GetNearestInsert(lpad->ptb, iPos - 1, 0, GNI_LOW) >= 0);
  924. SafeEnableWindow(hDlg, IDC_MOVEDOWN, hwndList, bDelOK &&
  925. GetNearestInsert(lpad->ptb, iPos + 2,
  926. lpad->ptb->iNumButtons, GNI_HIGH) >=0 );
  927. break;
  928. }
  929. case LBN_DBLCLK:
  930. SendMessage(hDlg, WM_COMMAND, IDC_REMOVE, 0L);
  931. break;
  932. case LBN_SETFOCUS:
  933. case LBN_KILLFOCUS:
  934. {
  935. RECT rc;
  936. if (SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETITEMRECT,
  937. (int)SendMessage(GET_WM_COMMAND_HWND(wParam, lParam), LB_GETCURSEL,
  938. 0, 0L), (LPARAM)(LPRECT)&rc) != LB_ERR)
  939. InvalidateRect(GET_WM_COMMAND_HWND(wParam, lParam), &rc, FALSE);
  940. }
  941. default:
  942. break;
  943. }
  944. break;
  945. case IDC_REMOVE:
  946. {
  947. int iPos = (int)SendDlgItemMessage(hDlg, IDC_CURRENT, LB_GETCURSEL, 0, 0);
  948. LBMoveButton(lpad, IDC_CURRENT, iPos, IDC_BUTTONLIST, 0, 0);
  949. break;
  950. }
  951. case IDC_MOVEUP:
  952. case IDC_MOVEDOWN:
  953. {
  954. int iPosSrc, iPosDst;
  955. iPosSrc = (int)SendDlgItemMessage(hDlg, IDC_CURRENT, LB_GETCURSEL, 0, 0L);
  956. if (wParam == IDC_MOVEUP)
  957. iPosDst = GetNearestInsert(lpad->ptb, iPosSrc - 1, 0, GNI_LOW);
  958. else
  959. iPosDst = GetNearestInsert(lpad->ptb, iPosSrc + 2, lpad->ptb->iNumButtons, GNI_HIGH);
  960. LBMoveButton(lpad, IDC_CURRENT, iPosSrc, IDC_CURRENT,iPosDst,0);
  961. break;
  962. }
  963. case IDC_RESET:
  964. {
  965. // ptb will change across call below
  966. HWND hwndT = lpad->ptb->ci.hwnd;
  967. BOOL fClose = FALSE;
  968. NMTBCUSTOMIZEDLG nm;
  969. nm.hDlg = hDlg;
  970. if (CCSendNotify(&lpad->ptb->ci, TBN_RESET, &nm.hdr) == TBNRF_ENDCUSTOMIZE)
  971. fClose = TRUE;
  972. // ptb probably changed across above call
  973. lpad->ptb = FixPTB(hwndT);
  974. /* Reset the dialog, but exit if something goes wrong. */
  975. lpad->iPos = 0;
  976. if (!fClose && InitAdjustDlg(hDlg, lpad))
  977. break;
  978. }
  979. /* We have to fall through because we won't know where to insert
  980. * buttons after resetting.
  981. */
  982. case IDCANCEL:
  983. EndDialog(hDlg, TRUE);
  984. break;
  985. default:
  986. return(FALSE);
  987. }
  988. break;
  989. default:
  990. if (uMsg == uDragListMsg)
  991. return HandleDragMsg(lpad, hDlg, wParam, (LPDRAGLISTINFO)lParam);
  992. return(FALSE);
  993. }
  994. return(TRUE);
  995. }
  996. // FEATURE: this should support saving to an IStream
  997. /* This saves the state of the toolbar. Spaces are saved as -1 (-2 if hidden)
  998. * and other buttons are just saved as the command ID. When restoring, all
  999. * ID's are filled in, and the app is queried for all buttons so that the
  1000. * bitmap and state information may be filled in. Button ID's that are not
  1001. * returned from the app are removed.
  1002. */
  1003. BOOL SaveRestoreFromReg(PTBSTATE ptb, BOOL bWrite, HKEY hkr, LPCTSTR pszSubKey, LPCTSTR pszValueName)
  1004. {
  1005. BOOL bRet = FALSE;
  1006. TCHAR szDesc[128];
  1007. if (bWrite)
  1008. {
  1009. UINT uSize = ptb->iNumButtons * sizeof(DWORD);
  1010. NMTBSAVE nmtbs;
  1011. BOOL fAlloced = FALSE;
  1012. nmtbs.pData = NULL;
  1013. nmtbs.cbData = uSize;
  1014. nmtbs.pCurrent = NULL;
  1015. nmtbs.iItem = -1; // signal pre saving
  1016. nmtbs.cButtons = ptb->iNumButtons;
  1017. CCSendNotify(&ptb->ci, TBN_SAVE, &nmtbs.hdr);
  1018. if (!nmtbs.pData)
  1019. {
  1020. nmtbs.pData = (DWORD *)LocalAlloc(LPTR, nmtbs.cbData);
  1021. fAlloced = TRUE;
  1022. }
  1023. // Bug#94345 -- Somebody could've changed ptb->iNumButtons
  1024. // during the CCSendNotify
  1025. if (!nmtbs.pCurrent)
  1026. nmtbs.pCurrent = nmtbs.pData;
  1027. if (nmtbs.pData)
  1028. {
  1029. HKEY hkeySave;
  1030. if (RegCreateKey(hkr, pszSubKey, &hkeySave) == ERROR_SUCCESS)
  1031. {
  1032. int i;
  1033. for (i = 0; i < ptb->iNumButtons; i++)
  1034. {
  1035. if (ptb->Buttons[i].idCommand)
  1036. *nmtbs.pCurrent = ptb->Buttons[i].idCommand;
  1037. else
  1038. {
  1039. // If the separator has an ID, then it is an "owner" item.
  1040. if (ptb->Buttons[i].fsState & TBSTATE_HIDDEN)
  1041. *nmtbs.pCurrent = (DWORD)-2; // hidden
  1042. else
  1043. *nmtbs.pCurrent = (DWORD)-1; // normal seperator
  1044. }
  1045. nmtbs.pCurrent++;
  1046. nmtbs.iItem = i;
  1047. TBOutputStruct(ptb, &ptb->Buttons[i], &nmtbs.tbButton);
  1048. CCSendNotify(&ptb->ci, TBN_SAVE, &nmtbs.hdr);
  1049. }
  1050. if (RegSetValueEx(hkeySave, (LPTSTR)pszValueName, 0, REG_BINARY, (LPVOID)nmtbs.pData, nmtbs.cbData) == ERROR_SUCCESS)
  1051. bRet = TRUE;
  1052. RegCloseKey(hkeySave);
  1053. }
  1054. if (fAlloced)
  1055. LocalFree((HLOCAL)nmtbs.pData);
  1056. }
  1057. }
  1058. else
  1059. {
  1060. HKEY hkey;
  1061. if (RegOpenKey(hkr, pszSubKey, &hkey) == ERROR_SUCCESS)
  1062. {
  1063. DWORD cbSize = 0;
  1064. if ((RegQueryValueEx(hkey, (LPTSTR)pszValueName, 0, NULL, NULL, &cbSize) == ERROR_SUCCESS) &&
  1065. (cbSize > sizeof(DWORD)))
  1066. {
  1067. UINT uSize = (UINT)cbSize;
  1068. DWORD *pData = (DWORD *)LocalAlloc(LPTR, uSize);
  1069. if (pData)
  1070. {
  1071. DWORD dwType;
  1072. DWORD cbSize = (DWORD)uSize;
  1073. if ((RegQueryValueEx(hkey, (LPTSTR)pszValueName, 0, &dwType, (LPVOID)pData, &cbSize) == ERROR_SUCCESS) &&
  1074. (dwType == REG_BINARY) &&
  1075. (cbSize == (DWORD)uSize))
  1076. {
  1077. int iButtonIndex;
  1078. NMTBRESTORE nmtbs;
  1079. BOOL fAlloced = FALSE;
  1080. nmtbs.pData = pData;
  1081. nmtbs.pCurrent = pData;
  1082. nmtbs.iItem = -1; // signal pre saving
  1083. nmtbs.cButtons = (int)uSize / SIZEOF(DWORD);
  1084. nmtbs.cbBytesPerRecord = SIZEOF(DWORD);
  1085. nmtbs.cbData = uSize;
  1086. // since we don't know the cButtons if they've added on extra data to pData,
  1087. // we'll use whatever they fill for cButtons
  1088. if (!CCSendNotify(&ptb->ci, TBN_RESTORE, &nmtbs.hdr))
  1089. {
  1090. //
  1091. // Before reloading the buttons, delete the tooltips
  1092. // of the previous buttons (if they exist).
  1093. //
  1094. if (ptb && ptb->hwndToolTips)
  1095. {
  1096. TOOLINFO ti;
  1097. ti.cbSize = sizeof(ti);
  1098. ti.hwnd = ptb->ci.hwnd;
  1099. for (iButtonIndex = 0;
  1100. iButtonIndex < ptb->iNumButtons; iButtonIndex++)
  1101. {
  1102. if (!(ptb->Buttons[iButtonIndex].fsStyle & TBSTYLE_SEP))
  1103. {
  1104. ti.uId = ptb->Buttons[iButtonIndex].idCommand;
  1105. SendMessage(ptb->hwndToolTips, TTM_DELTOOL,
  1106. 0, (LPARAM)(LPTOOLINFO)&ti);
  1107. }
  1108. }
  1109. }
  1110. // grow (or maybe shrink) pbt to hold new buttons
  1111. if (TBReallocButtons(ptb, nmtbs.cButtons))
  1112. {
  1113. int i;
  1114. if (ptb->iNumButtons < nmtbs.cButtons)
  1115. ZeroMemory(&ptb->Buttons[ptb->iNumButtons], (nmtbs.cButtons - ptb->iNumButtons) * sizeof(TBBUTTON));
  1116. ptb->iNumButtons = nmtbs.cButtons;
  1117. for (i = 0; i < ptb->iNumButtons; i++)
  1118. {
  1119. nmtbs.iItem = i;
  1120. if ((long)*nmtbs.pCurrent < 0)
  1121. {
  1122. ptb->Buttons[i].fsStyle = TBSTYLE_SEP;
  1123. ptb->Buttons[i].DUMMYUNION_MEMBER(iBitmap) = g_dxButtonSep;
  1124. ptb->Buttons[i].idCommand = 0;
  1125. if (*nmtbs.pCurrent == (DWORD)-1)
  1126. ptb->Buttons[i].fsState = 0;
  1127. else
  1128. {
  1129. ASSERT(*nmtbs.pCurrent == (DWORD)-2);
  1130. ptb->Buttons[i].fsState = TBSTATE_HIDDEN;
  1131. }
  1132. }
  1133. else
  1134. {
  1135. ptb->Buttons[i].fsStyle = 0;
  1136. ptb->Buttons[i].idCommand = *nmtbs.pCurrent;
  1137. ptb->Buttons[i].DUMMYUNION_MEMBER(iBitmap) = -1;
  1138. }
  1139. nmtbs.pCurrent++;
  1140. TBOutputStruct(ptb, &ptb->Buttons[i], &nmtbs.tbButton);
  1141. CCSendNotify(&ptb->ci, TBN_RESTORE, &nmtbs.hdr);
  1142. ASSERT(nmtbs.tbButton.iString == -1 || !HIWORD(nmtbs.tbButton.iString));
  1143. // we don't thunk. only allow string index in string pool here
  1144. if (HIWORD(nmtbs.tbButton.iString))
  1145. nmtbs.tbButton.iString = 0;
  1146. TBInputStruct(ptb, &ptb->Buttons[i], &nmtbs.tbButton);
  1147. }
  1148. // Now query for all buttons, and fill in the rest of the info
  1149. // For backward compatibility, ignore return value of TBN_BEGINADJUST
  1150. // if client is older than version 5 (NT5 #185499).
  1151. if (!SendCmdNotify(ptb, TBN_BEGINADJUST) || (ptb->ci.iVersion < 5))
  1152. {
  1153. for (i = 0; ; i++)
  1154. {
  1155. TBBUTTONDATA tbAdjust;
  1156. tbAdjust.idCommand = 0;
  1157. if (!GetAdjustInfo(ptb, i, &tbAdjust, szDesc, ARRAYSIZE(szDesc)))
  1158. break;
  1159. if (!(tbAdjust.fsStyle & TBSTYLE_SEP) || tbAdjust.idCommand)
  1160. {
  1161. int iPos = PositionFromID(ptb, tbAdjust.idCommand);
  1162. if (iPos >= 0)
  1163. {
  1164. ptb->Buttons[iPos] = tbAdjust;
  1165. }
  1166. }
  1167. }
  1168. SendCmdNotify(ptb, TBN_ENDADJUST);
  1169. }
  1170. // cleanup all the buttons that were not recognized
  1171. // do this backwards to minimize data movement (and nmtbs.cButtons changes)
  1172. for (i = ptb->iNumButtons - 1; i >= 0; i--)
  1173. {
  1174. // DeleteButton does no realloc, so ptb will not move
  1175. if (ptb->Buttons[i].DUMMYUNION_MEMBER(iBitmap) < 0)
  1176. DeleteButton(ptb, (UINT)i);
  1177. else
  1178. {
  1179. // the rest, add to tooltips
  1180. if(ptb->hwndToolTips &&
  1181. (!(ptb->Buttons[i].fsStyle & TBSTYLE_SEP || !ptb->Buttons[i].idCommand)))
  1182. {
  1183. TOOLINFO ti;
  1184. // don't bother setting the rect because we'll do it below
  1185. // in TBInvalidateItemRects;
  1186. ti.cbSize = sizeof(ti);
  1187. ti.uFlags = 0;
  1188. ti.hwnd = ptb->ci.hwnd;
  1189. ti.uId = ptb->Buttons[i].idCommand;
  1190. ti.lpszText = LPSTR_TEXTCALLBACK;
  1191. SendMessage(ptb->hwndToolTips, TTM_ADDTOOL, 0, (LPARAM)(LPTOOLINFO)&ti);
  1192. }
  1193. }
  1194. }
  1195. bRet = (ptb->iNumButtons != 0); // success
  1196. // Bug#94368: break autosize to a function and call it
  1197. SendMessage(ptb->ci.hwnd, TB_AUTOSIZE, 0, 0);
  1198. InvalidateRect(ptb->ci.hwnd, NULL, TRUE);
  1199. TBInvalidateItemRects(ptb);
  1200. }
  1201. }
  1202. }
  1203. LocalFree((HLOCAL)pData);
  1204. }
  1205. }
  1206. RegCloseKey(hkey);
  1207. }
  1208. }
  1209. return bRet;
  1210. }
  1211. void CustomizeTB(PTBSTATE ptb, int iPos)
  1212. {
  1213. ADJUSTDLGDATA ad;
  1214. HWND hwndT = ptb->ci.hwnd; // ptb will change across call below
  1215. HRSRC hrsrc;
  1216. LANGID wLang;
  1217. LPVOID pTemplate;
  1218. if (ptb->hdlgCust) // We are already customizing this toolbar
  1219. return;
  1220. ad.ptb = ptb;
  1221. ad.iPos = iPos;
  1222. // REVIEW: really should be per thread data, but not likely to cause a problem
  1223. // see note in WM_MEASUREITEM code
  1224. g_dyButtonHack = (ptb->ci.style & TBSTYLE_FLAT) ? ptb->iDyBitmap : ptb->iButHeight;
  1225. SendCmdNotify(ptb, TBN_BEGINADJUST);
  1226. //
  1227. // Do locale-specific futzing.
  1228. //
  1229. wLang = LANGIDFROMLCID(CCGetProperThreadLocale(NULL));
  1230. hrsrc = FindResourceExRetry(HINST_THISDLL, RT_DIALOG, MAKEINTRESOURCE(ADJUSTDLG), wLang);
  1231. if (hrsrc &&
  1232. (pTemplate = (LPVOID)LoadResource(HINST_THISDLL, hrsrc)))
  1233. {
  1234. DialogBoxIndirectParam(HINST_THISDLL, pTemplate,
  1235. ptb->ci.hwndParent, AdjustDlgProc, (LPARAM)(LPADJUSTDLGDATA)&ad);
  1236. }
  1237. // ptb probably changed across above call
  1238. ptb = (PTBSTATE)GetWindowInt(hwndT, 0);
  1239. ptb->hdlgCust = NULL;
  1240. SendCmdNotify(ptb, TBN_ENDADJUST);
  1241. }