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.

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