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.

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