Leaked source code of windows server 2003
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.

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