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.

3539 lines
92 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1993-1994
  4. //
  5. // File: recact.c
  6. //
  7. // This file contains the reconciliation-action control class code
  8. //
  9. //
  10. // History:
  11. // 08-12-93 ScottH Created.
  12. //
  13. //---------------------------------------------------------------------------
  14. ///////////////////////////////////////////////////// INCLUDES
  15. #include "brfprv.h" // common headers
  16. #include <help.h>
  17. #include "res.h"
  18. #include "recact.h"
  19. #include "dobj.h"
  20. ///////////////////////////////////////////////////// CONTROLLING DEFINES
  21. ///////////////////////////////////////////////////// DEFINES
  22. // Manifest constants
  23. #define SIDE_INSIDE 0
  24. #define SIDE_OUTSIDE 1
  25. // These should be changed if the bitmap sizes change!!
  26. #define CX_ACTIONBMP 26
  27. #define CY_ACTIONBMP 26
  28. #define RECOMPUTE (-1)
  29. #define X_INCOLUMN (g_cxIcon*2)
  30. // Image indexes
  31. #define II_RIGHT 0
  32. #define II_LEFT 1
  33. #define II_CONFLICT 2
  34. #define II_SKIP 3
  35. #define II_MERGE 4
  36. #define II_SOMETHING 5
  37. #define II_UPTODATE 6
  38. #define II_DELETE 7
  39. // Menu items
  40. //
  41. #define IDM_ACTIONFIRST 100
  42. #define IDM_TOOUT 100
  43. #define IDM_TOIN 101
  44. #define IDM_SKIP 102
  45. #define IDM_MERGE 103
  46. #define IDM_DELETEOUT 104
  47. #define IDM_DELETEIN 105
  48. #define IDM_DONTDELETE 106
  49. #define IDM_ACTIONLAST 106
  50. #define IDM_WHATSTHIS 107
  51. ///////////////////////////////////////////////////// TYPEDEFS
  52. typedef struct tagRECACT
  53. {
  54. HWND hwnd;
  55. HWND hwndLB;
  56. HWND hwndTip;
  57. HDC hdcOwn; // Own DC
  58. HMENU hmenu; // Action and help context menu
  59. HFONT hfont;
  60. WNDPROC lpfnLBProc; // Default LB proc
  61. HIMAGELIST himlAction; // imagelist for actions
  62. HIMAGELIST himlCache; // control imagelist cache
  63. HBITMAP hbmpBullet;
  64. HDSA hdsa;
  65. HBRUSH hbrBkgnd;
  66. COLORREF clrBkgnd;
  67. LONG lStyle; // Window style flags
  68. UINT cTipID; // Tip IDs are handed out 2 per item
  69. // Metrics
  70. int cxItem; // Generic width of an item
  71. int cxMenuCheck;
  72. int cyMenuCheck;
  73. int cyText;
  74. int cxEllipses;
  75. } RECACT, * LPRECACT;
  76. #define RecAct_IsNoIcon(this) IsFlagSet((this)->lStyle, RAS_SINGLEITEM)
  77. // Internal item data struct
  78. //
  79. typedef struct tagRA_PRIV
  80. {
  81. UINT uStyle; // One of RAIS_
  82. UINT uAction; // One of RAIA_
  83. FileInfo * pfi;
  84. SIDEITEM siInside;
  85. SIDEITEM siOutside;
  86. LPARAM lParam;
  87. DOBJ rgdobj[4]; // Array of Draw object info
  88. int cx; // Bounding width and height
  89. int cy;
  90. } RA_PRIV, * LPRA_PRIV;
  91. #define IDOBJ_FILE 0
  92. #define IDOBJ_ACTION 1
  93. #define IDOBJ_INSIDE 2
  94. #define IDOBJ_OUTSIDE 3
  95. // RecAction menu item definition structure. Used to define the
  96. // context menu brought up in this control.
  97. //
  98. typedef struct tagRAMID
  99. {
  100. UINT idm; // Menu ID (for MENUITEMINFO struct)
  101. UINT uAction; // One of RAIA_* flags
  102. UINT ids; // Resource string ID
  103. int iImage; // Index into himlAction
  104. RECT rcExtent; // Extent rect of string
  105. } RAMID, * LPRAMID; // RecAction Menu Item Definition
  106. // Help menu item definition structure. Used to define the help
  107. // items in the context menu.
  108. //
  109. typedef struct tagHMID
  110. {
  111. UINT idm;
  112. UINT ids;
  113. } HMID;
  114. ///////////////////////////////////////////////////// MACROS
  115. #define RecAct_DefProc DefWindowProc
  116. #define RecActLB_DefProc CallWindowProc
  117. // Instance data pointer macros
  118. //
  119. #define RecAct_GetPtr(hwnd) (LPRECACT)GetWindowLongPtr(hwnd, 0)
  120. #define RecAct_SetPtr(hwnd, lp) (LPRECACT)SetWindowLongPtr(hwnd, 0, (LRESULT)(lp))
  121. #define RecAct_GetCount(this) ListBox_GetCount((this)->hwndLB)
  122. LPCTSTR PRIVATE SkipDisplayJunkHack(LPSIDEITEM psi);
  123. ///////////////////////////////////////////////////// MODULE DATA
  124. #ifdef SAVE_FOR_RESIZE
  125. static TCHAR const c_szDateDummy[] = TEXT("99/99/99 99:99PM");
  126. #endif
  127. // Map RAIA_* values to image indexes
  128. //
  129. static UINT const c_mpraiaiImage[] =
  130. { II_RIGHT,
  131. II_LEFT,
  132. II_SKIP,
  133. II_CONFLICT,
  134. II_MERGE,
  135. II_SOMETHING,
  136. II_UPTODATE,
  137. 0,
  138. #ifdef NEW_REC
  139. II_DELETE,
  140. II_DELETE,
  141. II_SKIP
  142. #endif
  143. };
  144. // Map RAIA_* values to menu command positions
  145. //
  146. static UINT const c_mpraiaidmMenu[] =
  147. { IDM_TOOUT,
  148. IDM_TOIN,
  149. IDM_SKIP,
  150. IDM_SKIP,
  151. IDM_MERGE,
  152. 0, 0, 0,
  153. #ifdef NEW_REC
  154. IDM_DELETEOUT,
  155. IDM_DELETEIN,
  156. IDM_DONTDELETE
  157. #endif
  158. };
  159. // Define the context menu layout
  160. //
  161. static RAMID const c_rgramid[] = {
  162. { IDM_TOOUT, RAIA_TOOUT, IDS_MENU_REPLACE, II_RIGHT, 0 },
  163. { IDM_TOIN, RAIA_TOIN, IDS_MENU_REPLACE, II_LEFT, 0 },
  164. { IDM_SKIP, RAIA_SKIP, IDS_MENU_SKIP, II_SKIP, 0 },
  165. // Merge must be the last item!
  166. { IDM_MERGE, RAIA_MERGE, IDS_MENU_MERGE, II_MERGE, 0 },
  167. };
  168. static RAMID const c_rgramidCreates[] = {
  169. { IDM_TOOUT, RAIA_TOOUT, IDS_MENU_CREATE, II_RIGHT, 0 },
  170. { IDM_TOIN, RAIA_TOIN, IDS_MENU_CREATE, II_LEFT, 0 },
  171. };
  172. #ifdef NEW_REC
  173. static RAMID const c_rgramidDeletes[] = {
  174. { IDM_DELETEOUT, RAIA_DELETEOUT, IDS_MENU_DELETE, II_DELETE, 0 },
  175. { IDM_DELETEIN, RAIA_DELETEIN, IDS_MENU_DELETE, II_DELETE, 0 },
  176. { IDM_DONTDELETE, RAIA_DONTDELETE,IDS_MENU_DONTDELETE,II_SKIP, 0 },
  177. };
  178. #endif
  179. // Indexes into c_rgramidCreates
  180. //
  181. #define IRAMID_CREATEOUT 0
  182. #define IRAMID_CREATEIN 1
  183. // Indexes into c_rgramidDeletes
  184. //
  185. #define IRAMID_DELETEOUT 0
  186. #define IRAMID_DELETEIN 1
  187. #define IRAMID_DONTDELETE 2
  188. static HMID const c_rghmid[] = {
  189. { IDM_WHATSTHIS, IDS_MENU_WHATSTHIS },
  190. };
  191. ///////////////////////////////////////////////////// LOCAL PROCEDURES
  192. LRESULT _export CALLBACK RecActLB_LBProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
  193. ///////////////////////////////////////////////////// PRIVATE FUNCTIONS
  194. #ifdef DEBUG
  195. LPCTSTR PRIVATE DumpRecAction(
  196. UINT uAction) // RAIA_
  197. {
  198. switch (uAction)
  199. {
  200. DEBUG_CASE_STRING( RAIA_TOOUT );
  201. DEBUG_CASE_STRING( RAIA_TOIN );
  202. DEBUG_CASE_STRING( RAIA_SKIP );
  203. DEBUG_CASE_STRING( RAIA_CONFLICT );
  204. DEBUG_CASE_STRING( RAIA_MERGE );
  205. DEBUG_CASE_STRING( RAIA_SOMETHING );
  206. DEBUG_CASE_STRING( RAIA_NOTHING );
  207. DEBUG_CASE_STRING( RAIA_ORPHAN );
  208. DEBUG_CASE_STRING( RAIA_DELETEOUT );
  209. DEBUG_CASE_STRING( RAIA_DELETEIN );
  210. DEBUG_CASE_STRING( RAIA_DONTDELETE );
  211. default: return TEXT("Unknown");
  212. }
  213. }
  214. LPCTSTR PRIVATE DumpSideItemState(
  215. UINT uState) // SI_
  216. {
  217. switch (uState)
  218. {
  219. DEBUG_CASE_STRING( SI_UNCHANGED );
  220. DEBUG_CASE_STRING( SI_CHANGED );
  221. DEBUG_CASE_STRING( SI_NEW );
  222. DEBUG_CASE_STRING( SI_NOEXIST );
  223. DEBUG_CASE_STRING( SI_UNAVAILABLE );
  224. DEBUG_CASE_STRING( SI_DELETED );
  225. default: return TEXT("Unknown");
  226. }
  227. }
  228. /*----------------------------------------------------------
  229. Purpose: Dumps a twin pair
  230. Returns: --
  231. Cond: --
  232. */
  233. void PUBLIC DumpTwinPair(
  234. LPRA_ITEM pitem)
  235. {
  236. if (pitem)
  237. {
  238. TCHAR szBuf[MAXMSGLEN];
  239. #define szDump TEXT("Dump TWINPAIR: ")
  240. #define szBlank TEXT(" ")
  241. if (IsFlagClear(g_uDumpFlags, DF_TWINPAIR))
  242. {
  243. return;
  244. }
  245. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%s.pszName = %s\r\n"), (LPTSTR)szDump, Dbg_SafeStr(pitem->pszName));
  246. OutputDebugString(szBuf);
  247. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%s.uStyle = %lx\r\n"), (LPTSTR)szBlank, pitem->uStyle);
  248. OutputDebugString(szBuf);
  249. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%s.uAction = %s\r\n"), (LPTSTR)szBlank, DumpRecAction(pitem->uAction));
  250. OutputDebugString(szBuf);
  251. #undef szDump
  252. #define szDump TEXT(" Inside: ")
  253. wnsprintf(szBuf, TEXT("%s.pszDir = %s\r\n"), (LPTSTR)szDump, Dbg_SafeStr(pitem->siInside.pszDir));
  254. OutputDebugString(szBuf);
  255. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%s.uState = %s\r\n"), (LPTSTR)szBlank, DumpSideItemState(pitem->siInside.uState));
  256. OutputDebugString(szBuf);
  257. #undef szDump
  258. #define szDump TEXT(" Outside: ")
  259. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%s.pszDir = %s\r\n"), (LPTSTR)szDump, Dbg_SafeStr(pitem->siOutside.pszDir));
  260. OutputDebugString(szBuf);
  261. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT("%s.uState = %s\r\n"), (LPTSTR)szBlank, DumpSideItemState(pitem->siOutside.uState));
  262. OutputDebugString(szBuf);
  263. #undef szDump
  264. #undef szBlank
  265. }
  266. }
  267. #endif
  268. /*----------------------------------------------------------
  269. Purpose: Create a monochrome bitmap of the bullet, so we can
  270. play with the colors later.
  271. Returns: handle to bitmap
  272. Cond: Caller must delete bitmap
  273. */
  274. HBITMAP PRIVATE CreateBulletBitmap(
  275. LPSIZE psize)
  276. {
  277. HDC hdcMem;
  278. HBITMAP hbmp = NULL;
  279. hdcMem = CreateCompatibleDC(NULL);
  280. if (hdcMem)
  281. {
  282. hbmp = CreateCompatibleBitmap(hdcMem, psize->cx, psize->cy);
  283. if (hbmp)
  284. {
  285. HBITMAP hbmpOld;
  286. RECT rc;
  287. // hbmp is monochrome
  288. hbmpOld = SelectBitmap(hdcMem, hbmp);
  289. rc.left = 0;
  290. rc.top = 0;
  291. rc.right = psize->cx;
  292. rc.bottom = psize->cy;
  293. DrawFrameControl(hdcMem, &rc, DFC_MENU, DFCS_MENUBULLET);
  294. SelectBitmap(hdcMem, hbmpOld);
  295. }
  296. DeleteDC(hdcMem);
  297. }
  298. return hbmp;
  299. }
  300. /*----------------------------------------------------------
  301. Purpose: Returns the top and bottom indexes of the visible
  302. entries in the listbox
  303. Returns: --
  304. Cond: --
  305. */
  306. void PRIVATE GetVisibleRange(
  307. HWND hwndLB,
  308. int * piTop,
  309. int * piBottom)
  310. {
  311. int i;
  312. int cel;
  313. int cyMac;
  314. RECT rc;
  315. *piTop = ListBox_GetTopIndex(hwndLB);
  316. cel = ListBox_GetCount(hwndLB);
  317. GetClientRect(hwndLB, &rc);
  318. cyMac = 0;
  319. for (i = *piTop; i < cel; i++)
  320. {
  321. if (cyMac > rc.bottom)
  322. break;
  323. cyMac += ListBox_GetItemHeight(hwndLB, i);
  324. }
  325. *piBottom = i-1;;
  326. }
  327. /*----------------------------------------------------------
  328. Purpose: Returns the top and bottom indexes of the visible
  329. entries in the listbox
  330. Returns: --
  331. Cond: --
  332. */
  333. int PRIVATE GetHitIndex(
  334. HWND hwndLB,
  335. POINT pt)
  336. {
  337. int i;
  338. int iTop;
  339. int cel;
  340. int cyMac;
  341. int cy;
  342. RECT rc;
  343. iTop = ListBox_GetTopIndex(hwndLB);
  344. cel = ListBox_GetCount(hwndLB);
  345. GetClientRect(hwndLB, &rc);
  346. cyMac = 0;
  347. for (i = iTop; i < cel; i++)
  348. {
  349. cy = ListBox_GetItemHeight(hwndLB, i);
  350. if (InRange(pt.y, cyMac, cyMac + cy))
  351. break;
  352. cyMac += cy;
  353. }
  354. if (i == cel)
  355. return LB_ERR;
  356. return i;
  357. }
  358. /*----------------------------------------------------------
  359. Purpose: Returns the resource ID string given the action
  360. flag.
  361. Returns: IDS_ value
  362. Cond: --
  363. */
  364. UINT PRIVATE GetActionText(
  365. LPRA_PRIV ppriv)
  366. {
  367. UINT ids;
  368. ASSERT(ppriv);
  369. switch (ppriv->uAction)
  370. {
  371. case RAIA_TOOUT:
  372. if (SI_NEW == ppriv->siInside.uState ||
  373. SI_DELETED == ppriv->siOutside.uState)
  374. {
  375. ids = IDS_STATE_Creates;
  376. }
  377. else
  378. {
  379. ids = IDS_STATE_Replaces;
  380. }
  381. break;
  382. case RAIA_TOIN:
  383. if (SI_NEW == ppriv->siOutside.uState ||
  384. SI_DELETED == ppriv->siInside.uState)
  385. {
  386. ids = IDS_STATE_Creates;
  387. }
  388. else
  389. {
  390. ids = IDS_STATE_Replaces;
  391. }
  392. break;
  393. #ifdef NEW_REC
  394. case RAIA_DONTDELETE:
  395. ASSERT(SI_DELETED == ppriv->siInside.uState ||
  396. SI_DELETED == ppriv->siOutside.uState);
  397. ids = IDS_STATE_DontDelete;
  398. break;
  399. #endif
  400. case RAIA_SKIP:
  401. // Can occur if the user explicitly wants to skip, or if
  402. // one side is unavailable.
  403. ids = IDS_STATE_Skip;
  404. break;
  405. case RAIA_CONFLICT: ids = IDS_STATE_Conflict; break;
  406. case RAIA_MERGE: ids = IDS_STATE_Merge; break;
  407. case RAIA_NOTHING: ids = IDS_STATE_Uptodate; break;
  408. case RAIA_SOMETHING: ids = IDS_STATE_NeedToUpdate; break;
  409. #ifdef NEW_REC
  410. case RAIA_DELETEOUT: ids = IDS_STATE_Delete; break;
  411. case RAIA_DELETEIN: ids = IDS_STATE_Delete; break;
  412. #endif
  413. default: ids = 0; break;
  414. }
  415. return ids;
  416. }
  417. /*----------------------------------------------------------
  418. Purpose: Repaint an item in the listbox
  419. Returns: --
  420. Cond: --
  421. */
  422. void PRIVATE ListBox_RepaintItemNow(
  423. HWND hwnd,
  424. int iItem,
  425. LPRECT prc, // Relative to individual entry rect. May be NULL
  426. BOOL bEraseBk)
  427. {
  428. RECT rc;
  429. RECT rcItem;
  430. ListBox_GetItemRect(hwnd, iItem, &rcItem);
  431. if (prc)
  432. {
  433. OffsetRect(prc, rcItem.left, rcItem.top);
  434. IntersectRect(&rc, &rcItem, prc);
  435. }
  436. else
  437. rc = rcItem;
  438. InvalidateRect(hwnd, &rc, bEraseBk);
  439. UpdateWindow(hwnd);
  440. }
  441. /*----------------------------------------------------------
  442. Purpose: Determine which DOBJ of the item is going to get the caret.
  443. Returns: pointer to DOBJ
  444. Cond: --
  445. */
  446. LPDOBJ PRIVATE RecAct_ChooseCaretDobj(
  447. LPRECACT this,
  448. LPRA_PRIV ppriv)
  449. {
  450. // Focus rect on file icon?
  451. if (!RecAct_IsNoIcon(this))
  452. return ppriv->rgdobj; // Yes
  453. else
  454. return &ppriv->rgdobj[IDOBJ_ACTION]; // No
  455. }
  456. /*----------------------------------------------------------
  457. Purpose: Returns the tool tip ID for the visible rectangle
  458. that the given item is currently occupying.
  459. Returns: see above
  460. Cond: --
  461. */
  462. UINT PRIVATE RecAct_GetTipIDFromItemID(
  463. LPRECACT this,
  464. int itemID)
  465. {
  466. int iTop;
  467. int iBottom;
  468. int idsa;
  469. UINT uID;
  470. GetVisibleRange(this->hwndLB, &iTop, &iBottom);
  471. ASSERT(iTop <= itemID);
  472. ASSERT(itemID <= iBottom);
  473. idsa = itemID - iTop;
  474. if ( !DSA_GetItem(this->hdsa, idsa, &uID) )
  475. {
  476. // This region has not been added yet
  477. uID = this->cTipID;
  478. if (-1 != DSA_SetItem(this->hdsa, idsa, &uID))
  479. {
  480. TOOLINFO ti;
  481. ti.cbSize = sizeof(ti);
  482. ti.uFlags = 0;
  483. ti.hwnd = this->hwndLB;
  484. ti.uId = uID;
  485. ti.lpszText = LPSTR_TEXTCALLBACK;
  486. ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0;
  487. SendMessage(this->hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
  488. ti.uId++;
  489. SendMessage(this->hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
  490. this->cTipID += 2;
  491. }
  492. }
  493. return uID;
  494. }
  495. /*----------------------------------------------------------
  496. Purpose: Finds a listbox item given the tip ID.
  497. Returns: item index
  498. Cond: --
  499. */
  500. int PRIVATE RecAct_FindItemFromTipID(
  501. LPRECACT this,
  502. UINT uTipID,
  503. BOOL * pbInside)
  504. {
  505. int iTop;
  506. int iBottom;
  507. int iVisibleItem = uTipID / 2;
  508. int iItem;
  509. ASSERT(0 <= iVisibleItem);
  510. GetVisibleRange(this->hwndLB, &iTop, &iBottom);
  511. if (iVisibleItem <= iBottom - iTop)
  512. {
  513. iItem = iTop + iVisibleItem;
  514. if (uTipID % 2)
  515. *pbInside = FALSE;
  516. else
  517. *pbInside = TRUE;
  518. }
  519. else
  520. iItem = LB_ERR;
  521. return iItem;
  522. }
  523. /*----------------------------------------------------------
  524. Purpose: Send selection change notification
  525. Returns:
  526. Cond: --
  527. */
  528. BOOL PRIVATE RecAct_SendSelChange(
  529. LPRECACT this,
  530. int isel)
  531. {
  532. NM_RECACT nm;
  533. nm.iItem = isel;
  534. nm.mask = 0;
  535. if (isel != -1)
  536. {
  537. LPRA_ITEM pitem;
  538. ListBox_GetText(this->hwndLB, isel, &pitem);
  539. if (!pitem)
  540. return FALSE;
  541. nm.lParam = pitem->lParam;
  542. nm.mask |= RAIF_LPARAM;
  543. }
  544. return !SendNotify(GetParent(this->hwnd), this->hwnd, RN_SELCHANGED, &nm.hdr);
  545. }
  546. /*----------------------------------------------------------
  547. Purpose: Send an action change notification
  548. Returns:
  549. Cond: --
  550. */
  551. BOOL PRIVATE RecAct_SendItemChange(
  552. LPRECACT this,
  553. int iEntry,
  554. UINT uActionOld)
  555. {
  556. NM_RECACT nm;
  557. nm.iItem = iEntry;
  558. nm.mask = 0;
  559. if (iEntry != -1)
  560. {
  561. LPRA_PRIV ppriv;
  562. ListBox_GetText(this->hwndLB, iEntry, &ppriv);
  563. if (!ppriv)
  564. return FALSE;
  565. nm.mask |= RAIF_LPARAM | RAIF_ACTION;
  566. nm.lParam = ppriv->lParam;
  567. nm.uAction = ppriv->uAction;
  568. nm.uActionOld = uActionOld;
  569. }
  570. return !SendNotify(GetParent(this->hwnd), this->hwnd, RN_ITEMCHANGED, &nm.hdr);
  571. }
  572. /*----------------------------------------------------------
  573. Purpose: Create the action context menu
  574. Returns: TRUE on success
  575. Cond: --
  576. */
  577. BOOL PRIVATE RecAct_CreateMenu(
  578. LPRECACT this)
  579. {
  580. HMENU hmenu;
  581. hmenu = CreatePopupMenu();
  582. if (hmenu)
  583. {
  584. TCHAR sz[MAXSHORTLEN];
  585. MENUITEMINFO mii;
  586. int i;
  587. // Add the help menu items now, since these will be standard
  588. //
  589. mii.cbSize = sizeof(MENUITEMINFO);
  590. mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID;
  591. mii.fType = MFT_STRING;
  592. mii.fState = MFS_ENABLED;
  593. for (i = 0; i < ARRAYSIZE(c_rghmid); i++)
  594. {
  595. mii.wID = c_rghmid[i].idm;
  596. mii.dwTypeData = SzFromIDS(c_rghmid[i].ids, sz, ARRAYSIZE(sz));
  597. InsertMenuItem(hmenu, i, TRUE, &mii);
  598. }
  599. this->hmenu = hmenu;
  600. }
  601. return hmenu != NULL;
  602. }
  603. /*----------------------------------------------------------
  604. Purpose: Add the action menu items to the context menu
  605. Returns: --
  606. Cond: --
  607. */
  608. void PRIVATE AddActionsToContextMenu(
  609. HMENU hmenu,
  610. UINT idmCheck, // menu item to checkmark
  611. LPRA_PRIV ppriv)
  612. {
  613. MENUITEMINFO mii;
  614. int i;
  615. int cItems = ARRAYSIZE(c_rgramid);
  616. mii.cbSize = sizeof(MENUITEMINFO);
  617. mii.fMask = MIIM_TYPE | MIIM_STATE | MIIM_ID | MIIM_DATA;
  618. mii.fType = MFT_OWNERDRAW;
  619. mii.fState = MFS_ENABLED;
  620. // Is merge supported?
  621. if (IsFlagClear(ppriv->uStyle, RAIS_CANMERGE))
  622. {
  623. // No
  624. --cItems;
  625. }
  626. for (i = 0; i < cItems; i++)
  627. {
  628. mii.wID = c_rgramid[i].idm;
  629. mii.dwItemData = (DWORD_PTR)&c_rgramid[i];
  630. InsertMenuItem(hmenu, i, TRUE, &mii);
  631. }
  632. // Add the separator
  633. mii.fMask = MIIM_TYPE;
  634. mii.fType = MFT_SEPARATOR;
  635. InsertMenuItem(hmenu, i, TRUE, &mii);
  636. // Set the initial checkmark.
  637. CheckMenuRadioItem(hmenu, IDM_ACTIONFIRST, IDM_ACTIONLAST, idmCheck,
  638. MF_BYCOMMAND | MF_CHECKED);
  639. // Is the file or its sync copy unavailable?
  640. if (SI_UNAVAILABLE == ppriv->siInside.uState ||
  641. SI_UNAVAILABLE == ppriv->siOutside.uState)
  642. {
  643. // Yes
  644. mii.fMask = MIIM_STATE;
  645. mii.fState = MFS_GRAYED | MFS_DISABLED;
  646. SetMenuItemInfo(hmenu, IDM_TOIN, FALSE, &mii);
  647. SetMenuItemInfo(hmenu, IDM_TOOUT, FALSE, &mii);
  648. SetMenuItemInfo(hmenu, IDM_MERGE, FALSE, &mii);
  649. }
  650. // Is the file being created?
  651. else if (ppriv->siInside.uState == SI_NEW ||
  652. ppriv->siOutside.uState == SI_NEW)
  653. {
  654. // Yes; disable the replace-in-opposite direction
  655. UINT idmDisable;
  656. UINT idmChangeVerb;
  657. if (ppriv->siInside.uState == SI_NEW)
  658. {
  659. idmDisable = IDM_TOIN;
  660. idmChangeVerb = IDM_TOOUT;
  661. i = IRAMID_CREATEOUT;
  662. }
  663. else
  664. {
  665. idmDisable = IDM_TOOUT;
  666. idmChangeVerb = IDM_TOIN;
  667. i = IRAMID_CREATEIN;
  668. }
  669. // Disable one of the directions
  670. mii.fMask = MIIM_STATE;
  671. mii.fState = MFS_GRAYED | MFS_DISABLED;
  672. SetMenuItemInfo(hmenu, idmDisable, FALSE, &mii);
  673. // Change the verb of the other direction
  674. mii.fMask = MIIM_DATA;
  675. mii.dwItemData = (DWORD_PTR)&c_rgramidCreates[i];
  676. SetMenuItemInfo(hmenu, idmChangeVerb, FALSE, &mii);
  677. }
  678. #ifdef NEW_REC
  679. // Is the file being deleted?
  680. else if (SI_DELETED == ppriv->siInside.uState ||
  681. SI_DELETED == ppriv->siOutside.uState)
  682. {
  683. // Yes;
  684. UINT idmCreate;
  685. UINT idmChangeVerb;
  686. UINT iCreate;
  687. if (SI_DELETED == ppriv->siInside.uState)
  688. {
  689. idmCreate = IDM_TOIN;
  690. iCreate = IRAMID_CREATEIN;
  691. idmChangeVerb = IDM_TOOUT;
  692. i = IRAMID_DELETEOUT;
  693. }
  694. else
  695. {
  696. ASSERT(SI_DELETED == ppriv->siOutside.uState);
  697. idmCreate = IDM_TOOUT;
  698. iCreate = IRAMID_CREATEOUT;
  699. idmChangeVerb = IDM_TOIN;
  700. i = IRAMID_DELETEIN;
  701. }
  702. // Change one of the directions to be create
  703. mii.fMask = MIIM_DATA;
  704. mii.dwItemData = (DWORD_PTR)&c_rgramidCreates[iCreate];
  705. SetMenuItemInfo(hmenu, idmCreate, FALSE, &mii);
  706. // Change the verb of the other direction
  707. mii.fMask = MIIM_DATA | MIIM_ID;
  708. mii.wID = c_rgramidDeletes[i].idm;
  709. mii.dwItemData = (DWORD_PTR)&c_rgramidDeletes[i];
  710. SetMenuItemInfo(hmenu, idmChangeVerb, FALSE, &mii);
  711. // Change the skip verb to be "Don't Delete"
  712. mii.fMask = MIIM_DATA | MIIM_ID;
  713. mii.wID = c_rgramidDeletes[IRAMID_DONTDELETE].idm;
  714. mii.dwItemData = (DWORD_PTR)&c_rgramidDeletes[IRAMID_DONTDELETE];
  715. SetMenuItemInfo(hmenu, IDM_SKIP, FALSE, &mii);
  716. }
  717. #endif
  718. }
  719. /*----------------------------------------------------------
  720. Purpose: Clear out the context menu
  721. Returns: --
  722. Cond: --
  723. */
  724. void PRIVATE ResetContextMenu(
  725. HMENU hmenu)
  726. {
  727. int cnt;
  728. // If there is more than just the help items, remove them
  729. // (but leave the help items)
  730. //
  731. cnt = GetMenuItemCount(hmenu);
  732. if (cnt > ARRAYSIZE(c_rghmid))
  733. {
  734. int i;
  735. cnt -= ARRAYSIZE(c_rghmid);
  736. for (i = 0; i < cnt; i++)
  737. {
  738. DeleteMenu(hmenu, 0, MF_BYPOSITION);
  739. }
  740. }
  741. }
  742. /*----------------------------------------------------------
  743. Purpose: Do the context menu
  744. Returns: --
  745. Cond: --
  746. */
  747. void PRIVATE RecAct_DoContextMenu(
  748. LPRECACT this,
  749. int x, // in screen coords
  750. int y,
  751. int iEntry,
  752. BOOL bHelpOnly) // TRUE: only show the help items
  753. {
  754. UINT idCmd;
  755. if (this->hmenu)
  756. {
  757. LPRA_PRIV ppriv;
  758. RECT rc;
  759. int idmCheck;
  760. UINT uActionOld;
  761. // Only show help-portion of context menu?
  762. if (bHelpOnly)
  763. {
  764. // Yes
  765. ppriv = NULL;
  766. }
  767. else
  768. {
  769. // No
  770. ListBox_GetText(this->hwndLB, iEntry, &ppriv);
  771. // Determine if this is a help-context menu only.
  772. // It is if this is a folder-item or if there is no action
  773. // to take.
  774. //
  775. ASSERT(ppriv->uAction < ARRAYSIZE(c_mpraiaidmMenu));
  776. idmCheck = c_mpraiaidmMenu[ppriv->uAction];
  777. // Build the context menu
  778. //
  779. if (IsFlagClear(ppriv->uStyle, RAIS_FOLDER) && idmCheck != 0)
  780. {
  781. AddActionsToContextMenu(this->hmenu, idmCheck, ppriv);
  782. }
  783. }
  784. // Show context menu
  785. //
  786. SendMessage(this->hwndTip, TTM_ACTIVATE, FALSE, 0L);
  787. idCmd = TrackPopupMenu(this->hmenu,
  788. TPM_RETURNCMD | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  789. x, y, 0, this->hwnd, NULL);
  790. SendMessage(this->hwndTip, TTM_ACTIVATE, TRUE, 0L);
  791. // Clear menu
  792. //
  793. ResetContextMenu(this->hmenu);
  794. if (ppriv)
  795. {
  796. // Save the old action
  797. uActionOld = ppriv->uAction;
  798. }
  799. // Act on whatever the user chose
  800. switch (idCmd)
  801. {
  802. case IDM_TOOUT:
  803. ppriv->uAction = RAIA_TOOUT;
  804. break;
  805. case IDM_TOIN:
  806. ppriv->uAction = RAIA_TOIN;
  807. break;
  808. case IDM_SKIP:
  809. ppriv->uAction = RAIA_SKIP;
  810. break;
  811. case IDM_MERGE:
  812. ppriv->uAction = RAIA_MERGE;
  813. break;
  814. #ifdef NEW_REC
  815. case IDM_DELETEOUT:
  816. ppriv->uAction = RAIA_DELETEOUT;
  817. break;
  818. case IDM_DELETEIN:
  819. ppriv->uAction = RAIA_DELETEIN;
  820. break;
  821. case IDM_DONTDELETE:
  822. ppriv->uAction = RAIA_DONTDELETE;
  823. break;
  824. #endif
  825. case IDM_WHATSTHIS:
  826. WinHelp(this->hwnd, c_szWinHelpFile, HELP_CONTEXTPOPUP, IDH_BFC_UPDATE_SCREEN);
  827. return; // Return now
  828. default:
  829. return; // Return now
  830. }
  831. // Repaint action portion of entry
  832. ppriv->cx = RECOMPUTE;
  833. rc = ppriv->rgdobj[IDOBJ_ACTION].rcBounding;
  834. ListBox_RepaintItemNow(this->hwndLB, iEntry, &rc, TRUE);
  835. // Send a notify message
  836. ASSERT(NULL != ppriv); // uActionOld should be valid
  837. RecAct_SendItemChange(this, iEntry, uActionOld);
  838. }
  839. }
  840. /*----------------------------------------------------------
  841. Purpose: Create the windows for this control
  842. Returns: TRUE on success
  843. Cond: --
  844. */
  845. BOOL PRIVATE RecAct_CreateWindows(
  846. LPRECACT this,
  847. CREATESTRUCT * lpcs)
  848. {
  849. HWND hwnd = this->hwnd;
  850. HWND hwndLB;
  851. RECT rc;
  852. int cxEdge = GetSystemMetrics(SM_CXEDGE);
  853. int cyEdge = GetSystemMetrics(SM_CYEDGE);
  854. TOOLINFO ti;
  855. // Create listbox
  856. hwndLB = CreateWindowEx(
  857. 0,
  858. TEXT("listbox"),
  859. TEXT(""),
  860. WS_CHILD | WS_CLIPSIBLINGS | LBS_SORT | LBS_OWNERDRAWVARIABLE |
  861. WS_VSCROLL | WS_TABSTOP | WS_VISIBLE | LBS_NOINTEGRALHEIGHT |
  862. LBS_NOTIFY,
  863. 0, 0, lpcs->cx, lpcs->cy,
  864. hwnd,
  865. NULL,
  866. lpcs->hInstance,
  867. 0L);
  868. if (!hwndLB)
  869. return FALSE;
  870. SetWindowFont(hwndLB, this->hfont, FALSE);
  871. this->hwndLB = hwndLB;
  872. // Determine layout of window
  873. GetClientRect(hwnd, &rc);
  874. InflateRect(&rc, -cxEdge, -cyEdge);
  875. SetWindowPos(hwndLB, NULL, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top,
  876. SWP_NOACTIVATE | SWP_SHOWWINDOW | SWP_NOZORDER);
  877. GetClientRect(hwndLB, &rc);
  878. this->cxItem = rc.right - rc.left;
  879. this->hwndTip = CreateWindow(
  880. TOOLTIPS_CLASS,
  881. c_szNULL,
  882. WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
  883. CW_USEDEFAULT, CW_USEDEFAULT,
  884. CW_USEDEFAULT, CW_USEDEFAULT,
  885. hwnd,
  886. NULL,
  887. lpcs->hInstance,
  888. 0L);
  889. // Add a dummy tool so the delay is shorter between other tools
  890. ti.cbSize = sizeof(ti);
  891. ti.uFlags = TTF_IDISHWND;
  892. ti.hwnd = this->hwndLB;
  893. ti.uId = (UINT_PTR)this->hwndLB;
  894. ti.lpszText = (LPTSTR)c_szNULL;
  895. ti.rect.left = ti.rect.top = ti.rect.bottom = ti.rect.right = 0;
  896. SendMessage(this->hwndTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
  897. return TRUE;
  898. }
  899. /*----------------------------------------------------------
  900. Purpose: Set the colors of the control
  901. Returns: --
  902. Cond: --
  903. */
  904. void PRIVATE RecAct_SetColors(
  905. LPRECACT this)
  906. {
  907. int cr;
  908. if (IsFlagClear(this->lStyle, RAS_SINGLEITEM))
  909. {
  910. cr = COLOR_WINDOW;
  911. }
  912. else
  913. {
  914. cr = COLOR_3DFACE;
  915. }
  916. this->clrBkgnd = GetSysColor(cr);
  917. if (this->hbrBkgnd)
  918. DeleteBrush(this->hbrBkgnd);
  919. this->hbrBkgnd = CreateSolidBrush(this->clrBkgnd);
  920. }
  921. /*----------------------------------------------------------
  922. Purpose: Creates an imagelist of the action images
  923. Returns: TRUE on success
  924. Cond: --
  925. */
  926. BOOL PRIVATE CreateImageList(
  927. HIMAGELIST * phiml,
  928. HDC hdc,
  929. UINT idb,
  930. int cxBmp,
  931. int cyBmp,
  932. int cImage,
  933. UINT flags
  934. )
  935. {
  936. BOOL bRet;
  937. HIMAGELIST himl;
  938. himl = ImageList_Create(cxBmp, cyBmp, flags, cImage, 1);
  939. if (himl)
  940. {
  941. COLORREF clrMask;
  942. HBITMAP hbm;
  943. hbm = LoadBitmap(g_hinst, MAKEINTRESOURCE(idb));
  944. ASSERT(hbm);
  945. if (hbm)
  946. {
  947. HDC hdcMem = CreateCompatibleDC(hdc);
  948. if (hdcMem)
  949. {
  950. HBITMAP hbmSav = SelectBitmap(hdcMem, hbm);
  951. clrMask = GetPixel(hdcMem, 0, 0);
  952. SelectBitmap(hdcMem, hbmSav);
  953. bRet = (0 == ImageList_AddMasked(himl, hbm, clrMask));
  954. DeleteDC(hdcMem);
  955. }
  956. else
  957. bRet = FALSE;
  958. DeleteBitmap(hbm);
  959. }
  960. else
  961. bRet = FALSE;
  962. }
  963. else
  964. bRet = FALSE;
  965. *phiml = himl;
  966. return bRet;
  967. }
  968. /*----------------------------------------------------------
  969. Purpose: WM_CREATE handler
  970. Returns: TRUE on success
  971. Cond: --
  972. */
  973. BOOL PRIVATE RecAct_OnCreate(
  974. LPRECACT this,
  975. CREATESTRUCT * lpcs)
  976. {
  977. BOOL bRet = FALSE;
  978. HWND hwnd = this->hwnd;
  979. HDC hdc;
  980. TEXTMETRIC tm;
  981. RECT rcT;
  982. LOGFONT lf;
  983. UINT flags = ILC_MASK;
  984. this->lStyle = GetWindowLong(hwnd, GWL_STYLE);
  985. RecAct_SetColors(this);
  986. // Determine some font things
  987. SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(lf), &lf, FALSE);
  988. this->hfont = CreateFontIndirect(&lf);
  989. // This window is registered with the CS_OWNDC flag
  990. this->hdcOwn = GetDC(hwnd);
  991. ASSERT(this->hdcOwn);
  992. hdc = this->hdcOwn;
  993. SelectFont(hdc, this->hfont);
  994. GetTextMetrics(hdc, &tm);
  995. this->cyText = tm.tmHeight;
  996. // Calculate text extent for sideitems (use the listbox font)
  997. //
  998. SetRectFromExtent(hdc, &rcT, c_szEllipses);
  999. this->cxEllipses = rcT.right - rcT.left;
  1000. // Create windows used by control
  1001. if (RecAct_CreateWindows(this, lpcs))
  1002. {
  1003. this->lpfnLBProc = SubclassWindow(this->hwndLB, RecActLB_LBProc);
  1004. this->hdsa = DSA_Create(sizeof(int), 16);
  1005. if (this->hdsa)
  1006. {
  1007. // Get the system imagelist cache
  1008. this->himlCache = ImageList_Create(g_cxIcon, g_cyIcon, TRUE, 8, 8);
  1009. if (this->himlCache)
  1010. {
  1011. if(IS_WINDOW_RTL_MIRRORED(hwnd))
  1012. {
  1013. flags |= ILC_MIRROR;
  1014. }
  1015. if (CreateImageList(&this->himlAction, hdc, IDB_ACTIONS,
  1016. CX_ACTIONBMP, CY_ACTIONBMP, 8, flags))
  1017. {
  1018. SIZE size;
  1019. // Get some metrics
  1020. this->cxMenuCheck = GetSystemMetrics(SM_CXMENUCHECK);
  1021. this->cyMenuCheck = GetSystemMetrics(SM_CYMENUCHECK);
  1022. size.cx = this->cxMenuCheck;
  1023. size.cy = this->cyMenuCheck;
  1024. this->hbmpBullet = CreateBulletBitmap(&size);
  1025. if (this->hbmpBullet)
  1026. {
  1027. bRet = RecAct_CreateMenu(this);
  1028. }
  1029. }
  1030. }
  1031. }
  1032. }
  1033. return bRet;
  1034. }
  1035. /*----------------------------------------------------------
  1036. Purpose: WM_DESTROY Handler
  1037. Returns: --
  1038. Cond: --
  1039. */
  1040. void PRIVATE RecAct_OnDestroy(
  1041. LPRECACT this)
  1042. {
  1043. if (this->himlCache)
  1044. {
  1045. ImageList_Destroy(this->himlCache);
  1046. this->himlCache = NULL;
  1047. }
  1048. if (this->himlAction)
  1049. {
  1050. ImageList_Destroy(this->himlAction);
  1051. this->himlAction = NULL;
  1052. }
  1053. if (this->hbmpBullet)
  1054. {
  1055. DeleteBitmap(this->hbmpBullet);
  1056. this->hbmpBullet = NULL;
  1057. }
  1058. if (this->hmenu)
  1059. {
  1060. DestroyMenu(this->hmenu);
  1061. this->hmenu = NULL;
  1062. }
  1063. if (this->hbrBkgnd)
  1064. DeleteBrush(this->hbrBkgnd);
  1065. if (this->hfont)
  1066. DeleteFont(this->hfont);
  1067. if (this->hdsa)
  1068. DSA_Destroy(this->hdsa);
  1069. }
  1070. /*----------------------------------------------------------
  1071. Purpose: WM_COMMAND Handler
  1072. Returns: --
  1073. Cond: --
  1074. */
  1075. VOID PRIVATE RecAct_OnCommand(
  1076. LPRECACT this,
  1077. int id,
  1078. HWND hwndCtl,
  1079. UINT uNotifyCode)
  1080. {
  1081. if (hwndCtl == this->hwndLB)
  1082. {
  1083. switch (uNotifyCode)
  1084. {
  1085. case LBN_SELCHANGE:
  1086. break;
  1087. }
  1088. }
  1089. }
  1090. /*----------------------------------------------------------
  1091. Purpose: Handles WM_SYSKEYDOWN
  1092. Returns: 0 if we processed it
  1093. Cond: --
  1094. */
  1095. int PRIVATE RecAct_OnSysKeyDown(
  1096. LPRECACT this,
  1097. UINT vkey,
  1098. LPARAM lKeyData)
  1099. {
  1100. int nRet = -1;
  1101. // Context menu invoked by the keyboard?
  1102. if (VK_F10 == vkey && 0 > GetKeyState(VK_SHIFT))
  1103. {
  1104. // Yes; forward the message
  1105. HWND hwndLB = this->hwndLB;
  1106. int iCaret = ListBox_GetCurSel(hwndLB);
  1107. // Is this in a property page?
  1108. if (RecAct_IsNoIcon(this) && 0 > iCaret)
  1109. {
  1110. // Yes; don't require the item to be selected
  1111. iCaret = 0;
  1112. }
  1113. if (0 <= iCaret)
  1114. {
  1115. LPRA_PRIV ppriv;
  1116. LPDOBJ pdobj;
  1117. POINT pt;
  1118. RECT rc;
  1119. // Determine where to show the context menu
  1120. ListBox_GetText(hwndLB, iCaret, &ppriv);
  1121. pdobj = RecAct_ChooseCaretDobj(this, ppriv);
  1122. ListBox_GetItemRect(hwndLB, iCaret, &rc);
  1123. pt.x = pdobj->x + (g_cxIcon / 2) + rc.left;
  1124. pt.y = pdobj->y + (g_cyIcon / 2) + rc.top;
  1125. ClientToScreen(hwndLB, &pt);
  1126. PostMessage(this->hwnd, WM_CONTEXTMENU, (WPARAM)hwndLB, MAKELPARAM(pt.x, pt.y));
  1127. }
  1128. nRet = 0;
  1129. }
  1130. return nRet;
  1131. }
  1132. // ( (4+1) is for ellipses )
  1133. #define MAX_HALF (ARRAYSIZE(pttt->szText)/2 - (4+1))
  1134. /*----------------------------------------------------------
  1135. Purpose: Handles TTN_NEEDTEXT
  1136. Returns: --
  1137. Cond: --
  1138. */
  1139. void PRIVATE RecAct_OnNeedTipText(
  1140. LPRECACT this,
  1141. LPTOOLTIPTEXT pttt)
  1142. {
  1143. // Find the visible listbox item associated with this tip ID.
  1144. HWND hwndLB = this->hwndLB;
  1145. LPRA_PRIV ppriv;
  1146. int iItem;
  1147. BOOL bInside;
  1148. SIDEITEM * psi;
  1149. iItem = RecAct_FindItemFromTipID(this, (UINT)pttt->hdr.idFrom, &bInside);
  1150. if (LB_ERR != iItem)
  1151. {
  1152. int cb;
  1153. ListBox_GetText(hwndLB, iItem, &ppriv);
  1154. if (bInside)
  1155. psi = &ppriv->siInside;
  1156. else
  1157. psi = &ppriv->siOutside;
  1158. // Need ellipses?
  1159. cb = CbFromCch(lstrlen(psi->pszDir));
  1160. if (cb >= sizeof(pttt->szText))
  1161. {
  1162. // Yes
  1163. LPTSTR pszLastHalf;
  1164. LPTSTR psz;
  1165. LPTSTR pszStart = psi->pszDir;
  1166. LPTSTR pszEnd = &psi->pszDir[lstrlen(psi->pszDir)];
  1167. for (psz = pszEnd;
  1168. psz != pszStart && (pszEnd - psz) < MAX_HALF;
  1169. psz = CharPrev(pszStart, psz))
  1170. ;
  1171. pszLastHalf = CharNext(psz);
  1172. lstrcpyn(pttt->szText, psi->pszDir, MAX_HALF);
  1173. StrCatBuff(pttt->szText, c_szEllipses, ARRAYSIZE(pttt->szText));
  1174. StrCatBuff(pttt->szText, pszLastHalf, ARRAYSIZE(pttt->szText));
  1175. }
  1176. else
  1177. lstrcpyn(pttt->szText, psi->pszDir, ARRAYSIZE(pttt->szText));
  1178. }
  1179. else
  1180. *pttt->szText = 0;
  1181. }
  1182. /*----------------------------------------------------------
  1183. Purpose: WM_NOTIFY handler
  1184. Returns: varies
  1185. Cond: --
  1186. */
  1187. LRESULT PRIVATE RecAct_OnNotify(
  1188. LPRECACT this,
  1189. int idFrom,
  1190. NMHDR * lpnmhdr)
  1191. {
  1192. LRESULT lRet = 0;
  1193. switch (lpnmhdr->code)
  1194. {
  1195. case HDN_BEGINTRACK:
  1196. lRet = TRUE; // prevent tracking
  1197. break;
  1198. default:
  1199. break;
  1200. }
  1201. return lRet;
  1202. }
  1203. /*----------------------------------------------------------
  1204. Purpose: WM_CONTEXTMENU handler
  1205. Returns: --
  1206. Cond: --
  1207. */
  1208. void PRIVATE RecAct_OnContextMenu(
  1209. LPRECACT this,
  1210. HWND hwnd,
  1211. int x,
  1212. int y)
  1213. {
  1214. if (hwnd == this->hwndLB)
  1215. {
  1216. POINT pt;
  1217. int iHitEntry;
  1218. BOOL bHelpOnly;
  1219. pt.x = x;
  1220. pt.y = y;
  1221. ScreenToClient(hwnd, &pt);
  1222. iHitEntry = GetHitIndex(hwnd, pt);
  1223. if (LB_ERR != iHitEntry)
  1224. {
  1225. ASSERT(iHitEntry < ListBox_GetCount(hwnd));
  1226. ListBox_SetCurSel(hwnd, iHitEntry);
  1227. ListBox_RepaintItemNow(hwnd, iHitEntry, NULL, FALSE);
  1228. bHelpOnly = FALSE;
  1229. }
  1230. else
  1231. bHelpOnly = TRUE;
  1232. // Bring up the context menu for the listbox
  1233. RecAct_DoContextMenu(this, x, y, iHitEntry, bHelpOnly);
  1234. }
  1235. }
  1236. /*----------------------------------------------------------
  1237. Purpose: Calculate the rectangle boundary of a sideitem
  1238. Returns: calculated rect
  1239. Cond: --
  1240. */
  1241. void PRIVATE RecAct_CalcSideItemRect(
  1242. LPRECACT this,
  1243. int nSide, // SIDE_INSIDE or SIDE_OUTSIDE
  1244. int cxFile,
  1245. int cxAction,
  1246. LPRECT prcOut)
  1247. {
  1248. int x;
  1249. int y = g_cyIconMargin*2;
  1250. int cx = ((this->cxItem - cxFile - cxAction) / 2);
  1251. switch (nSide)
  1252. {
  1253. case SIDE_INSIDE:
  1254. if (RecAct_IsNoIcon(this))
  1255. x = 0;
  1256. else
  1257. x = cxFile;
  1258. break;
  1259. case SIDE_OUTSIDE:
  1260. if (RecAct_IsNoIcon(this))
  1261. x = cx + cxAction;
  1262. else
  1263. x = cxFile + cx + cxAction;
  1264. break;
  1265. default:
  1266. ASSERT(0);
  1267. break;
  1268. }
  1269. x += g_cxMargin;
  1270. prcOut->left = x + g_cxMargin;
  1271. prcOut->top = y;
  1272. prcOut->right = prcOut->left + (cx - 2*g_cxMargin);
  1273. prcOut->bottom = y + (this->cyText * 3);
  1274. }
  1275. /*----------------------------------------------------------
  1276. Purpose: Draw a reconciliation listbox entry
  1277. Returns: --
  1278. Cond: --
  1279. */
  1280. void PRIVATE RecAct_RecomputeItemMetrics(
  1281. LPRECACT this,
  1282. LPRA_PRIV ppriv)
  1283. {
  1284. HDC hdc = this->hdcOwn;
  1285. LPDOBJ pdobj = ppriv->rgdobj;
  1286. RECT rcT;
  1287. RECT rcUnion;
  1288. TCHAR szIDS[MAXBUFLEN];
  1289. UINT ids;
  1290. int cyText = this->cyText;
  1291. int dx;
  1292. int cxFile;
  1293. int cxAction;
  1294. POINT pt;
  1295. // Compute the metrics and dimensions of each of the draw objects
  1296. // and store back into the item.
  1297. // File icon and label
  1298. pt.x = 0;
  1299. pt.y = 0;
  1300. ComputeImageRects(FIGetDisplayName(ppriv->pfi), hdc, &pt, &rcT,
  1301. &pdobj->rcLabel, g_cxIcon, g_cyIcon, g_cxIconSpacing, cyText);
  1302. pdobj->uKind = DOK_IMAGE;
  1303. pdobj->lpvObject = FIGetDisplayName(ppriv->pfi);
  1304. pdobj->uFlags = DOF_DIFFER | DOF_CENTER;
  1305. if (RecAct_IsNoIcon(this))
  1306. {
  1307. SetFlag(pdobj->uFlags, DOF_NODRAW);
  1308. cxFile = 0;
  1309. }
  1310. else
  1311. {
  1312. cxFile = rcT.right - rcT.left;
  1313. }
  1314. pdobj->x = pt.x;
  1315. pdobj->y = pt.y;
  1316. pdobj->himl = this->himlCache;
  1317. pdobj->iImage = (UINT)ppriv->pfi->lParam;
  1318. pdobj->rcBounding = rcT;
  1319. rcUnion = pdobj->rcBounding;
  1320. // Action image
  1321. ASSERT(ppriv->uAction <= ARRAYSIZE(c_mpraiaiImage));
  1322. pdobj++;
  1323. ids = GetActionText(ppriv);
  1324. pt.x = 0; // (we'll adjust this after the call)
  1325. pt.y = 0;
  1326. ComputeImageRects(SzFromIDS(ids, szIDS, ARRAYSIZE(szIDS)), hdc, &pt,
  1327. &rcT, &pdobj->rcLabel, CX_ACTIONBMP, CY_ACTIONBMP,
  1328. g_cxIconSpacing, cyText);
  1329. // (Adjust pt and the two rects to be centered in the remaining space)
  1330. cxAction = rcT.right - rcT.left;
  1331. dx = cxFile + (((this->cxItem - cxFile) / 2) - (cxAction / 2));
  1332. pt.x += dx;
  1333. OffsetRect(&rcT, dx, 0);
  1334. OffsetRect(&pdobj->rcLabel, dx, 0);
  1335. pdobj->uKind = DOK_IMAGE;
  1336. pdobj->lpvObject = IntToPtr(ids);
  1337. pdobj->uFlags = DOF_CENTER | DOF_USEIDS;
  1338. if (!RecAct_IsNoIcon(this))
  1339. SetFlag(pdobj->uFlags, DOF_IGNORESEL);
  1340. pdobj->x = pt.x;
  1341. pdobj->y = pt.y;
  1342. pdobj->himl = this->himlAction;
  1343. pdobj->iImage = c_mpraiaiImage[ppriv->uAction];
  1344. pdobj->rcBounding = rcT;
  1345. UnionRect(&rcUnion, &rcUnion, &pdobj->rcBounding);
  1346. // Sideitem Info (Inside Briefcase)
  1347. RecAct_CalcSideItemRect(this, SIDE_INSIDE, cxFile, cxAction, &rcT);
  1348. pdobj++;
  1349. pdobj->uKind = DOK_SIDEITEM;
  1350. pdobj->lpvObject = &ppriv->siInside;
  1351. pdobj->uFlags = DOF_LEFT;
  1352. pdobj->x = rcT.left;
  1353. pdobj->y = rcT.top;
  1354. pdobj->rcClip = rcT;
  1355. pdobj->rcBounding = rcT;
  1356. // Sideitem Info (Outside Briefcase)
  1357. RecAct_CalcSideItemRect(this, SIDE_OUTSIDE, cxFile, cxAction, &rcT);
  1358. pdobj++;
  1359. pdobj->uKind = DOK_SIDEITEM;
  1360. pdobj->lpvObject = &ppriv->siOutside;
  1361. pdobj->uFlags = DOF_LEFT;
  1362. pdobj->x = rcT.left;
  1363. pdobj->y = rcT.top;
  1364. pdobj->rcClip = rcT;
  1365. pdobj->rcBounding = rcT;
  1366. UnionRect(&rcUnion, &rcUnion, &rcT);
  1367. // Set the bounding rect of this item.
  1368. ppriv->cx = rcUnion.right - rcUnion.left;
  1369. ppriv->cy = max((rcUnion.bottom - rcUnion.top), g_cyIconSpacing);
  1370. }
  1371. /*----------------------------------------------------------
  1372. Purpose: WM_MEASUREITEM handler
  1373. Returns: --
  1374. Cond: --
  1375. */
  1376. BOOL PRIVATE RecAct_OnMeasureItem(
  1377. LPRECACT this,
  1378. LPMEASUREITEMSTRUCT lpmis)
  1379. {
  1380. HDC hdc = this->hdcOwn;
  1381. switch (lpmis->CtlType)
  1382. {
  1383. case ODT_LISTBOX: {
  1384. LPRA_PRIV ppriv = (LPRA_PRIV)lpmis->itemData;
  1385. // Recompute item metrics?
  1386. if (RECOMPUTE == ppriv->cx)
  1387. {
  1388. RecAct_RecomputeItemMetrics(this, ppriv); // Yes
  1389. }
  1390. lpmis->itemHeight = ppriv->cy;
  1391. }
  1392. return TRUE;
  1393. case ODT_MENU:
  1394. {
  1395. int i;
  1396. int cxMac = 0;
  1397. RECT rc;
  1398. TCHAR sz[MAXBUFLEN];
  1399. // Calculate based on font and image dimensions.
  1400. //
  1401. SelectFont(hdc, this->hfont);
  1402. cxMac = 0;
  1403. for (i = 0; i < ARRAYSIZE(c_rgramid); i++)
  1404. {
  1405. SzFromIDS(c_rgramid[i].ids, sz, ARRAYSIZE(sz));
  1406. SetRectFromExtent(hdc, &rc, sz);
  1407. cxMac = max(cxMac,
  1408. g_cxMargin + CX_ACTIONBMP + g_cxMargin +
  1409. (rc.right-rc.left) + g_cxMargin);
  1410. }
  1411. lpmis->itemHeight = max(this->cyText, CY_ACTIONBMP);
  1412. lpmis->itemWidth = cxMac;
  1413. }
  1414. return TRUE;
  1415. }
  1416. return FALSE;
  1417. }
  1418. /*----------------------------------------------------------
  1419. Purpose: Draw a reconciliation listbox entry
  1420. Returns: --
  1421. Cond: --
  1422. */
  1423. void PRIVATE RecAct_DrawLBItem(
  1424. LPRECACT this,
  1425. const DRAWITEMSTRUCT * lpcdis)
  1426. {
  1427. LPRA_PRIV ppriv = (LPRA_PRIV)lpcdis->itemData;
  1428. HDC hdc = lpcdis->hDC;
  1429. RECT rc = lpcdis->rcItem;
  1430. POINT ptSav;
  1431. LPDOBJ pdobj;
  1432. UINT cdobjs;
  1433. if (!ppriv)
  1434. {
  1435. // Empty listbox and we're getting the focus
  1436. return;
  1437. }
  1438. SetBkMode(hdc, TRANSPARENT); // required for Shell_DrawText
  1439. SetViewportOrgEx(hdc, rc.left, rc.top, &ptSav);
  1440. // The Chicago-look mandates that icon and filename are selected,
  1441. // the rest of the entry is normal.
  1442. // Recompute item metrics?
  1443. if (RECOMPUTE == ppriv->cx)
  1444. {
  1445. RecAct_RecomputeItemMetrics(this, ppriv); // Yes
  1446. }
  1447. // Do we need to redraw everything?
  1448. if (IsFlagSet(lpcdis->itemAction, ODA_DRAWENTIRE))
  1449. {
  1450. // Yes
  1451. TOOLINFO ti;
  1452. cdobjs = ARRAYSIZE(ppriv->rgdobj);
  1453. pdobj = ppriv->rgdobj;
  1454. // Get the tooltip ID given this ith visible entry
  1455. ti.cbSize = sizeof(ti);
  1456. ti.uFlags = 0;
  1457. ti.hwnd = this->hwndLB;
  1458. ti.lpszText = LPSTR_TEXTCALLBACK;
  1459. ti.uId = RecAct_GetTipIDFromItemID(this, lpcdis->itemID);
  1460. ti.rect = ppriv->rgdobj[IDOBJ_INSIDE].rcBounding;
  1461. OffsetRect(&ti.rect, lpcdis->rcItem.left, lpcdis->rcItem.top);
  1462. SendMessage(this->hwndTip, TTM_NEWTOOLRECT, 0, (LPARAM)&ti);
  1463. ti.uId++;
  1464. ti.rect = ppriv->rgdobj[IDOBJ_OUTSIDE].rcBounding;
  1465. OffsetRect(&ti.rect, lpcdis->rcItem.left, lpcdis->rcItem.top);
  1466. SendMessage(this->hwndTip, TTM_NEWTOOLRECT, 0, (LPARAM)&ti);
  1467. }
  1468. else
  1469. {
  1470. // No; should we even draw the file icon or action icon?
  1471. if (lpcdis->itemAction & (ODA_FOCUS | ODA_SELECT))
  1472. {
  1473. cdobjs = 1; // Yes
  1474. pdobj = RecAct_ChooseCaretDobj(this, ppriv);
  1475. }
  1476. else
  1477. {
  1478. cdobjs = 0; // No
  1479. pdobj = ppriv->rgdobj;
  1480. }
  1481. }
  1482. Dobj_Draw(hdc, pdobj, cdobjs, lpcdis->itemState, this->cxEllipses, this->cyText,
  1483. this->clrBkgnd);
  1484. // Clean up
  1485. //
  1486. SetViewportOrgEx(hdc, ptSav.x, ptSav.y, NULL);
  1487. }
  1488. /*----------------------------------------------------------
  1489. Purpose: Draw an action menu item
  1490. Returns: --
  1491. Cond: --
  1492. */
  1493. void PRIVATE RecAct_DrawMenuItem(
  1494. LPRECACT this,
  1495. const DRAWITEMSTRUCT * lpcdis)
  1496. {
  1497. LPRAMID pramid = (LPRAMID)lpcdis->itemData;
  1498. HDC hdc = lpcdis->hDC;
  1499. RECT rc = lpcdis->rcItem;
  1500. DOBJ dobj;
  1501. LPDOBJ pdobj;
  1502. POINT ptSav;
  1503. MENUITEMINFO mii;
  1504. int cx;
  1505. int cy;
  1506. UINT uFlags;
  1507. UINT uFlagsChecked;
  1508. ASSERT(pramid);
  1509. if (lpcdis->itemID == -1)
  1510. return;
  1511. SetViewportOrgEx(hdc, rc.left, rc.top, &ptSav);
  1512. OffsetRect(&rc, -rc.left, -rc.top);
  1513. cx = rc.right - rc.left;
  1514. cy = rc.bottom - rc.top;
  1515. // Get the menu state
  1516. mii.cbSize = sizeof(mii);
  1517. mii.fMask = MIIM_STATE | MIIM_CHECKMARKS;
  1518. GetMenuItemInfo(this->hmenu, lpcdis->itemID, FALSE, &mii);
  1519. uFlagsChecked = IsFlagClear(mii.fState, MFS_CHECKED) ? DOF_NODRAW : 0;
  1520. uFlags = DOF_DIFFER | DOF_MENU | DOF_USEIDS;
  1521. if (IsFlagSet(mii.fState, MFS_GRAYED))
  1522. SetFlag(uFlags, DOF_DISABLED);
  1523. // Build the array of DObjs that we want to draw.
  1524. // Action image
  1525. pdobj = &dobj;
  1526. pdobj->uKind = DOK_IMAGE;
  1527. pdobj->lpvObject = IntToPtr(pramid->ids);
  1528. pdobj->himl = this->himlAction;
  1529. pdobj->iImage = pramid->iImage;
  1530. pdobj->uFlags = uFlags;
  1531. pdobj->x = g_cxMargin;
  1532. pdobj->y = (cy - CY_ACTIONBMP) / 2;
  1533. pdobj->rcLabel.left = 0;
  1534. pdobj->rcLabel.right = cx;
  1535. pdobj->rcLabel.top = 0;
  1536. pdobj->rcLabel.bottom = cy;
  1537. // Draw the entry...
  1538. //
  1539. Dobj_Draw(hdc, &dobj, 1, lpcdis->itemState, 0, this->cyText, this->clrBkgnd);
  1540. // Clean up
  1541. //
  1542. SetViewportOrgEx(hdc, ptSav.x, ptSav.y, NULL);
  1543. }
  1544. /*----------------------------------------------------------
  1545. Purpose: WM_DRAWITEM handler
  1546. Returns: --
  1547. Cond: --
  1548. */
  1549. BOOL PRIVATE RecAct_OnDrawItem(
  1550. LPRECACT this,
  1551. const DRAWITEMSTRUCT * lpcdis)
  1552. {
  1553. switch (lpcdis->CtlType)
  1554. {
  1555. case ODT_LISTBOX:
  1556. RecAct_DrawLBItem(this, lpcdis);
  1557. return TRUE;
  1558. case ODT_MENU:
  1559. RecAct_DrawMenuItem(this, lpcdis);
  1560. return TRUE;
  1561. }
  1562. return FALSE;
  1563. }
  1564. /*----------------------------------------------------------
  1565. Purpose: WM_COMPAREITEM handler
  1566. Returns: -1 (item 1 precedes item 2), 0 (equal), 1 (item 2 precedes item 1)
  1567. Cond: --
  1568. */
  1569. int PRIVATE RecAct_OnCompareItem(
  1570. LPRECACT this,
  1571. const COMPAREITEMSTRUCT * lpcis)
  1572. {
  1573. LPRA_PRIV ppriv1 = (LPRA_PRIV)lpcis->itemData1;
  1574. LPRA_PRIV ppriv2 = (LPRA_PRIV)lpcis->itemData2;
  1575. // We sort based on name of file
  1576. //
  1577. return lstrcmpi(FIGetPath(ppriv1->pfi), FIGetPath(ppriv2->pfi));
  1578. }
  1579. /*----------------------------------------------------------
  1580. Purpose: WM_DELETEITEM handler
  1581. Returns: --
  1582. Cond: --
  1583. */
  1584. void RecAct_OnDeleteLBItem(
  1585. LPRECACT this,
  1586. const DELETEITEMSTRUCT * lpcdis)
  1587. {
  1588. switch (lpcdis->CtlType)
  1589. {
  1590. case ODT_LISTBOX:
  1591. {
  1592. LPRA_PRIV ppriv = (LPRA_PRIV)lpcdis->itemData;
  1593. ASSERT(ppriv);
  1594. if (ppriv)
  1595. {
  1596. FIFree(ppriv->pfi);
  1597. GFree(ppriv->siInside.pszDir);
  1598. GFree(ppriv->siOutside.pszDir);
  1599. GFree(ppriv);
  1600. }
  1601. }
  1602. break;
  1603. }
  1604. }
  1605. /*----------------------------------------------------------
  1606. Purpose: WM_CTLCOLORLISTBOX handler
  1607. Returns: --
  1608. Cond: --
  1609. */
  1610. HBRUSH PRIVATE RecAct_OnCtlColorListBox(
  1611. LPRECACT this,
  1612. HDC hdc,
  1613. HWND hwndLB,
  1614. int nType)
  1615. {
  1616. return this->hbrBkgnd;
  1617. }
  1618. /*----------------------------------------------------------
  1619. Purpose: WM_PAINT handler
  1620. Returns: --
  1621. Cond: --
  1622. */
  1623. void RecAct_OnPaint(
  1624. LPRECACT this)
  1625. {
  1626. HWND hwnd = this->hwnd;
  1627. PAINTSTRUCT ps;
  1628. RECT rc;
  1629. HDC hdc;
  1630. hdc = BeginPaint(hwnd, &ps);
  1631. GetClientRect(hwnd, &rc);
  1632. if (IsFlagSet(this->lStyle, RAS_SINGLEITEM))
  1633. {
  1634. DrawEdge(hdc, &rc, BDR_SUNKENINNER, BF_TOPLEFT);
  1635. DrawEdge(hdc, &rc, BDR_SUNKENOUTER, BF_BOTTOMRIGHT);
  1636. }
  1637. else
  1638. {
  1639. DrawEdge(hdc, &rc, EDGE_SUNKEN, BF_RECT);
  1640. }
  1641. EndPaint(hwnd, &ps);
  1642. }
  1643. /*----------------------------------------------------------
  1644. Purpose: WM_SETFONT handler
  1645. Returns: --
  1646. Cond: --
  1647. */
  1648. void RecAct_OnSetFont(
  1649. LPRECACT this,
  1650. HFONT hfont,
  1651. BOOL bRedraw)
  1652. {
  1653. this->hfont = hfont;
  1654. FORWARD_WM_SETFONT(this->hwnd, hfont, bRedraw, RecAct_DefProc);
  1655. }
  1656. /*----------------------------------------------------------
  1657. Purpose: WM_SETFOCUS handler
  1658. Returns: --
  1659. Cond: --
  1660. */
  1661. void RecAct_OnSetFocus(
  1662. LPRECACT this,
  1663. HWND hwndOldFocus)
  1664. {
  1665. SetFocus(this->hwndLB);
  1666. }
  1667. /*----------------------------------------------------------
  1668. Purpose: WM_SYSCOLORCHANGE handler
  1669. Returns: --
  1670. Cond: --
  1671. */
  1672. void RecAct_OnSysColorChange(
  1673. LPRECACT this)
  1674. {
  1675. RecAct_SetColors(this);
  1676. InvalidateRect(this->hwnd, NULL, TRUE);
  1677. }
  1678. /*----------------------------------------------------------
  1679. Purpose: Insert item
  1680. Returns: index
  1681. Cond: --
  1682. */
  1683. int PRIVATE RecAct_OnInsertItem(
  1684. LPRECACT this,
  1685. const LPRA_ITEM pitem)
  1686. {
  1687. HWND hwndLB = this->hwndLB;
  1688. LPRA_PRIV pprivNew;
  1689. TCHAR szPath[MAXPATHLEN];
  1690. int iRet = -1;
  1691. int iItem = LB_ERR;
  1692. ASSERT(pitem);
  1693. ASSERT(pitem->siInside.pszDir);
  1694. ASSERT(pitem->siOutside.pszDir);
  1695. ASSERT(pitem->pszName);
  1696. pprivNew = GAlloc(sizeof(*pprivNew));
  1697. if (pprivNew)
  1698. {
  1699. SetWindowRedraw(hwndLB, FALSE);
  1700. // Fill the prerequisite fields first
  1701. //
  1702. pprivNew->uStyle = pitem->uStyle;
  1703. pprivNew->uAction = pitem->uAction;
  1704. // Set the fileinfo stuff and large icon system-cache index.
  1705. // If we can't get the fileinfo of the inside file, get the outside
  1706. // file. If neither can be found, then we fail
  1707. //
  1708. lstrcpyn(szPath, SkipDisplayJunkHack(&pitem->siInside), ARRAYSIZE(szPath));
  1709. if (IsFlagClear(pitem->uStyle, RAIS_FOLDER))
  1710. PathAppend(szPath, pitem->pszName);
  1711. if (FAILED(FICreate(szPath, &pprivNew->pfi, FIF_ICON)))
  1712. {
  1713. // Try the outside file
  1714. //
  1715. lstrcpyn(szPath, SkipDisplayJunkHack(&pitem->siOutside), ARRAYSIZE(szPath));
  1716. if (IsFlagClear(pitem->uStyle, RAIS_FOLDER))
  1717. PathAppend(szPath, pitem->pszName);
  1718. if (FAILED(FICreate(szPath, &pprivNew->pfi, FIF_ICON)))
  1719. {
  1720. // Don't try to touch the file
  1721. if (FAILED(FICreate(szPath, &pprivNew->pfi, FIF_ICON | FIF_DONTTOUCH)))
  1722. goto Insert_Cleanup;
  1723. }
  1724. }
  1725. ASSERT(pprivNew->pfi);
  1726. pprivNew->pfi->lParam = (LPARAM)ImageList_AddIcon(this->himlCache, pprivNew->pfi->hicon);
  1727. // Fill in the rest of the fields
  1728. //
  1729. lstrcpyn(szPath, pitem->siInside.pszDir, ARRAYSIZE(szPath));
  1730. if (IsFlagSet(pitem->uStyle, RAIS_FOLDER))
  1731. PathRemoveFileSpec(szPath);
  1732. if (!GSetString(&pprivNew->siInside.pszDir, szPath))
  1733. goto Insert_Cleanup;
  1734. pprivNew->siInside.uState = pitem->siInside.uState;
  1735. pprivNew->siInside.fs = pitem->siInside.fs;
  1736. pprivNew->siInside.ichRealPath = pitem->siInside.ichRealPath;
  1737. lstrcpyn(szPath, pitem->siOutside.pszDir, ARRAYSIZE(szPath));
  1738. if (IsFlagSet(pitem->uStyle, RAIS_FOLDER))
  1739. PathRemoveFileSpec(szPath);
  1740. if (!GSetString(&pprivNew->siOutside.pszDir, szPath))
  1741. goto Insert_Cleanup;
  1742. pprivNew->siOutside.uState = pitem->siOutside.uState;
  1743. pprivNew->siOutside.fs = pitem->siOutside.fs;
  1744. pprivNew->siOutside.ichRealPath = pitem->siOutside.ichRealPath;
  1745. pprivNew->lParam = pitem->lParam;
  1746. pprivNew->cx = RECOMPUTE;
  1747. // We know we're doing a redundant sorted add if the element
  1748. // needs to be inserted at the end of the list, but who cares.
  1749. //
  1750. if (pitem->iItem >= RecAct_GetCount(this))
  1751. iItem = ListBox_AddString(hwndLB, pprivNew);
  1752. else
  1753. iItem = ListBox_InsertString(hwndLB, pitem->iItem, pprivNew);
  1754. if (iItem == LB_ERR)
  1755. goto Insert_Cleanup;
  1756. SetWindowRedraw(hwndLB, TRUE);
  1757. iRet = iItem;
  1758. }
  1759. goto Insert_End;
  1760. Insert_Cleanup:
  1761. // Have DeleteString handler clean up field allocations
  1762. // of pitem.
  1763. //
  1764. if (iItem != LB_ERR)
  1765. ListBox_DeleteString(hwndLB, iItem);
  1766. else
  1767. {
  1768. FIFree(pprivNew->pfi);
  1769. GFree(pprivNew);
  1770. }
  1771. SetWindowRedraw(hwndLB, TRUE);
  1772. Insert_End:
  1773. return iRet;
  1774. }
  1775. /*----------------------------------------------------------
  1776. Purpose: Delete item
  1777. Returns: count of items left
  1778. Cond: --
  1779. */
  1780. int PRIVATE RecAct_OnDeleteItem(
  1781. LPRECACT this,
  1782. int i)
  1783. {
  1784. HWND hwndLB = this->hwndLB;
  1785. return ListBox_DeleteString(hwndLB, i);
  1786. }
  1787. /*----------------------------------------------------------
  1788. Purpose: Delete all items
  1789. Returns: TRUE
  1790. Cond: --
  1791. */
  1792. BOOL PRIVATE RecAct_OnDeleteAllItems(
  1793. LPRECACT this)
  1794. {
  1795. ListBox_ResetContent(this->hwndLB);
  1796. return TRUE;
  1797. }
  1798. /*----------------------------------------------------------
  1799. Purpose: Get item
  1800. Returns: TRUE on success
  1801. Cond: --
  1802. */
  1803. BOOL PRIVATE RecAct_OnGetItem(
  1804. LPRECACT this,
  1805. LPRA_ITEM pitem)
  1806. {
  1807. LPRA_PRIV ppriv;
  1808. HWND hwndLB = this->hwndLB;
  1809. UINT uMask;
  1810. int iItem;
  1811. if (!pitem)
  1812. return FALSE;
  1813. iItem = pitem->iItem;
  1814. uMask = pitem->mask;
  1815. ListBox_GetText(hwndLB, iItem, &ppriv);
  1816. if (uMask & RAIF_ACTION)
  1817. pitem->uAction = ppriv->uAction;
  1818. if (uMask & RAIF_NAME)
  1819. pitem->pszName = FIGetPath(ppriv->pfi);
  1820. if (uMask & RAIF_STYLE)
  1821. pitem->uStyle = ppriv->uStyle;
  1822. if (uMask & RAIF_INSIDE)
  1823. pitem->siInside = ppriv->siInside;
  1824. if (uMask & RAIF_OUTSIDE)
  1825. pitem->siOutside = ppriv->siOutside;
  1826. if (uMask & RAIF_LPARAM)
  1827. pitem->lParam = ppriv->lParam;
  1828. return TRUE;
  1829. }
  1830. /*----------------------------------------------------------
  1831. Purpose: Set item
  1832. Returns: TRUE on success
  1833. Cond: --
  1834. */
  1835. BOOL PRIVATE RecAct_OnSetItem(
  1836. LPRECACT this,
  1837. LPRA_ITEM pitem)
  1838. {
  1839. LPRA_PRIV ppriv;
  1840. HWND hwndLB = this->hwndLB;
  1841. UINT uMask;
  1842. int iItem;
  1843. if (!pitem)
  1844. return FALSE;
  1845. uMask = pitem->mask;
  1846. iItem = pitem->iItem;
  1847. ListBox_GetText(hwndLB, iItem, &ppriv);
  1848. if (uMask & RAIF_ACTION)
  1849. ppriv->uAction = pitem->uAction;
  1850. if (uMask & RAIF_STYLE)
  1851. ppriv->uStyle = pitem->uStyle;
  1852. if (uMask & RAIF_NAME)
  1853. {
  1854. if (!FISetPath(&ppriv->pfi, pitem->pszName, FIF_ICON))
  1855. return FALSE;
  1856. ppriv->pfi->lParam = (LPARAM)ImageList_AddIcon(this->himlCache, ppriv->pfi->hicon);
  1857. }
  1858. if (uMask & RAIF_INSIDE)
  1859. {
  1860. if (!GSetString(&ppriv->siInside.pszDir, pitem->siInside.pszDir))
  1861. return FALSE;
  1862. ppriv->siInside.uState = pitem->siInside.uState;
  1863. ppriv->siInside.fs = pitem->siInside.fs;
  1864. ppriv->siInside.ichRealPath = pitem->siInside.ichRealPath;
  1865. }
  1866. if (uMask & RAIF_OUTSIDE)
  1867. {
  1868. if (!GSetString(&ppriv->siOutside.pszDir, pitem->siOutside.pszDir))
  1869. return FALSE;
  1870. ppriv->siOutside.uState = pitem->siOutside.uState;
  1871. ppriv->siOutside.fs = pitem->siOutside.fs;
  1872. ppriv->siOutside.ichRealPath = pitem->siOutside.ichRealPath;
  1873. }
  1874. if (uMask & RAIF_LPARAM)
  1875. ppriv->lParam = pitem->lParam;
  1876. return TRUE;
  1877. }
  1878. /*----------------------------------------------------------
  1879. Purpose: Get the current selection
  1880. Returns: index
  1881. Cond: --
  1882. */
  1883. int PRIVATE RecAct_OnGetCurSel(
  1884. LPRECACT this)
  1885. {
  1886. return ListBox_GetCurSel(this->hwndLB);
  1887. }
  1888. /*----------------------------------------------------------
  1889. Purpose: Set the current selection
  1890. Returns: --
  1891. Cond: --
  1892. */
  1893. int PRIVATE RecAct_OnSetCurSel(
  1894. LPRECACT this,
  1895. int i)
  1896. {
  1897. int iRet = ListBox_SetCurSel(this->hwndLB, i);
  1898. if (iRet != LB_ERR)
  1899. RecAct_SendSelChange(this, i);
  1900. return iRet;
  1901. }
  1902. /*----------------------------------------------------------
  1903. Purpose: Find an item
  1904. Returns: TRUE on success
  1905. Cond: --
  1906. */
  1907. int PRIVATE RecAct_OnFindItem(
  1908. LPRECACT this,
  1909. int iStart,
  1910. const RA_FINDITEM * prafi)
  1911. {
  1912. HWND hwndLB = this->hwndLB;
  1913. UINT uMask = prafi->flags;
  1914. LPRA_PRIV ppriv;
  1915. BOOL bPass;
  1916. int i;
  1917. int cItems = ListBox_GetCount(hwndLB);
  1918. for (i = iStart+1; i < cItems; i++)
  1919. {
  1920. bPass = TRUE; // assume we pass
  1921. ListBox_GetText(hwndLB, i, &ppriv);
  1922. if (uMask & RAFI_NAME &&
  1923. !IsSzEqual(FIGetPath(ppriv->pfi), prafi->psz))
  1924. bPass = FALSE;
  1925. if (uMask & RAFI_ACTION && ppriv->uAction != prafi->uAction)
  1926. bPass = FALSE;
  1927. if (uMask & RAFI_LPARAM && ppriv->lParam != prafi->lParam)
  1928. bPass = FALSE;
  1929. if (bPass)
  1930. break; // found it
  1931. }
  1932. return i == cItems ? -1 : i;
  1933. }
  1934. ///////////////////////////////////////////////////// EXPORTED FUNCTIONS
  1935. /*----------------------------------------------------------
  1936. Purpose: RecAct window proc
  1937. Returns: varies
  1938. Cond: --
  1939. */
  1940. LRESULT CALLBACK RecAct_WndProc(
  1941. HWND hwnd,
  1942. UINT msg,
  1943. WPARAM wParam,
  1944. LPARAM lParam)
  1945. {
  1946. LPRECACT this = RecAct_GetPtr(hwnd);
  1947. if (this == NULL)
  1948. {
  1949. if (msg == WM_NCCREATE)
  1950. {
  1951. this = GAlloc(sizeof(*this));
  1952. ASSERT(this);
  1953. if (!this)
  1954. return 0L; // OOM failure
  1955. this->hwnd = hwnd;
  1956. RecAct_SetPtr(hwnd, this);
  1957. }
  1958. else
  1959. {
  1960. return RecAct_DefProc(hwnd, msg, wParam, lParam);
  1961. }
  1962. }
  1963. if (msg == WM_NCDESTROY)
  1964. {
  1965. GFree(this);
  1966. RecAct_SetPtr(hwnd, NULL);
  1967. }
  1968. switch (msg)
  1969. {
  1970. HANDLE_MSG(this, WM_CREATE, RecAct_OnCreate);
  1971. HANDLE_MSG(this, WM_DESTROY, RecAct_OnDestroy);
  1972. HANDLE_MSG(this, WM_SETFONT, RecAct_OnSetFont);
  1973. HANDLE_MSG(this, WM_COMMAND, RecAct_OnCommand);
  1974. HANDLE_MSG(this, WM_NOTIFY, RecAct_OnNotify);
  1975. HANDLE_MSG(this, WM_MEASUREITEM, RecAct_OnMeasureItem);
  1976. HANDLE_MSG(this, WM_DRAWITEM, RecAct_OnDrawItem);
  1977. HANDLE_MSG(this, WM_COMPAREITEM, RecAct_OnCompareItem);
  1978. HANDLE_MSG(this, WM_DELETEITEM, RecAct_OnDeleteLBItem);
  1979. HANDLE_MSG(this, WM_CONTEXTMENU, RecAct_OnContextMenu);
  1980. HANDLE_MSG(this, WM_SETFOCUS, RecAct_OnSetFocus);
  1981. HANDLE_MSG(this, WM_CTLCOLORLISTBOX, RecAct_OnCtlColorListBox);
  1982. HANDLE_MSG(this, WM_PAINT, RecAct_OnPaint);
  1983. HANDLE_MSG(this, WM_SYSCOLORCHANGE, RecAct_OnSysColorChange);
  1984. case WM_HELP:
  1985. WinHelp(this->hwnd, c_szWinHelpFile, HELP_CONTEXTPOPUP, IDH_BFC_UPDATE_SCREEN);
  1986. return 0;
  1987. case RAM_GETITEMCOUNT:
  1988. return (LRESULT)RecAct_GetCount(this);
  1989. case RAM_GETITEM:
  1990. return (LRESULT)RecAct_OnGetItem(this, (LPRA_ITEM)lParam);
  1991. case RAM_SETITEM:
  1992. return (LRESULT)RecAct_OnSetItem(this, (const LPRA_ITEM)lParam);
  1993. case RAM_INSERTITEM:
  1994. return (LRESULT)RecAct_OnInsertItem(this, (const LPRA_ITEM)lParam);
  1995. case RAM_DELETEITEM:
  1996. return (LRESULT)RecAct_OnDeleteItem(this, (int)wParam);
  1997. case RAM_DELETEALLITEMS:
  1998. return (LRESULT)RecAct_OnDeleteAllItems(this);
  1999. case RAM_GETCURSEL:
  2000. return (LRESULT)RecAct_OnGetCurSel(this);
  2001. case RAM_SETCURSEL:
  2002. return (LRESULT)RecAct_OnSetCurSel(this, (int)wParam);
  2003. case RAM_FINDITEM:
  2004. return (LRESULT)RecAct_OnFindItem(this, (int)wParam, (const RA_FINDITEM *)lParam);
  2005. case RAM_REFRESH:
  2006. RedrawWindow(this->hwndLB, NULL, NULL, RDW_ERASE | RDW_INVALIDATE);
  2007. default:
  2008. return RecAct_DefProc(hwnd, msg, wParam, lParam);
  2009. }
  2010. }
  2011. ///////////////////////////////////////////////////// PUBLIC FUNCTIONS
  2012. /*----------------------------------------------------------
  2013. Purpose: Initialize the reconciliation-action window class
  2014. Returns: TRUE on success
  2015. Cond: --
  2016. */
  2017. BOOL PUBLIC RecAct_Init(
  2018. HINSTANCE hinst)
  2019. {
  2020. WNDCLASSEX wc;
  2021. wc.cbSize = sizeof(WNDCLASSEX);
  2022. wc.style = CS_DBLCLKS | CS_OWNDC;
  2023. wc.lpfnWndProc = RecAct_WndProc;
  2024. wc.cbClsExtra = 0;
  2025. wc.cbWndExtra = sizeof(LPRECACT);
  2026. wc.hInstance = hinst;
  2027. wc.hIcon = NULL;
  2028. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  2029. wc.hbrBackground= NULL;
  2030. wc.lpszMenuName = NULL;
  2031. wc.lpszClassName= WC_RECACT;
  2032. wc.hIconSm = NULL;
  2033. return RegisterClassEx(&wc) != 0;
  2034. }
  2035. /*----------------------------------------------------------
  2036. Purpose: Clean up RecAct window class
  2037. Returns: --
  2038. Cond: --
  2039. */
  2040. void PUBLIC RecAct_Term(
  2041. HINSTANCE hinst)
  2042. {
  2043. UnregisterClass(WC_RECACT, hinst);
  2044. }
  2045. /*----------------------------------------------------------
  2046. Purpose: Special sub-class listbox proc
  2047. Returns: varies
  2048. Cond: --
  2049. */
  2050. LRESULT _export CALLBACK RecActLB_LBProc(
  2051. HWND hwnd, // window handle
  2052. UINT uMsg, // window message
  2053. WPARAM wparam, // varies
  2054. LPARAM lparam) // varies
  2055. {
  2056. LRESULT lRet;
  2057. LPRECACT lpra = NULL;
  2058. // Get the instance data for the control
  2059. lpra = RecAct_GetPtr(GetParent(hwnd));
  2060. ASSERT(lpra);
  2061. switch (uMsg)
  2062. {
  2063. case WM_NOTIFY: {
  2064. NMHDR * pnmhdr = (NMHDR *)lparam;
  2065. if (TTN_NEEDTEXT == pnmhdr->code)
  2066. {
  2067. RecAct_OnNeedTipText(lpra, (LPTOOLTIPTEXT)pnmhdr);
  2068. }
  2069. }
  2070. break;
  2071. case WM_SYSKEYDOWN: {
  2072. lRet = RecAct_OnSysKeyDown(lpra, (UINT)wparam, lparam);
  2073. if (0 != lRet)
  2074. lRet = RecActLB_DefProc(lpra->lpfnLBProc, hwnd, uMsg, wparam, lparam);
  2075. }
  2076. break;
  2077. case WM_MOUSEMOVE: {
  2078. MSG msg;
  2079. ASSERT(hwnd == lpra->hwndLB);
  2080. msg.lParam = lparam;
  2081. msg.wParam = wparam;
  2082. msg.message = uMsg;
  2083. msg.hwnd = hwnd;
  2084. SendMessage(lpra->hwndTip, TTM_RELAYEVENT, 0, (LPARAM)&msg);
  2085. lRet = RecActLB_DefProc(lpra->lpfnLBProc, hwnd, uMsg, wparam, lparam);
  2086. }
  2087. break;
  2088. default:
  2089. lRet = RecActLB_DefProc(lpra->lpfnLBProc, hwnd, uMsg, wparam, lparam);
  2090. break;
  2091. }
  2092. return lRet;
  2093. }
  2094. //---------------------------------------------------------------------------
  2095. //
  2096. //---------------------------------------------------------------------------
  2097. /*----------------------------------------------------------
  2098. Purpose: Converts a recnode state to a sideitem state
  2099. Returns: see above
  2100. Cond: --
  2101. */
  2102. UINT PRIVATE SiFromRns(
  2103. RECNODESTATE rnstate)
  2104. {
  2105. switch (rnstate)
  2106. {
  2107. #ifdef NEW_REC
  2108. case RNS_NEVER_RECONCILED: return SI_CHANGED;
  2109. #endif
  2110. case RNS_UNAVAILABLE: return SI_UNAVAILABLE;
  2111. case RNS_DOES_NOT_EXIST: return SI_NOEXIST;
  2112. case RNS_DELETED: return SI_DELETED;
  2113. case RNS_NOT_RECONCILED: return SI_UNCHANGED;
  2114. case RNS_UP_TO_DATE: return SI_UNCHANGED;
  2115. case RNS_CHANGED: return SI_CHANGED;
  2116. default:
  2117. ASSERT(0);
  2118. return SI_UNCHANGED;
  2119. }
  2120. }
  2121. /*----------------------------------------------------------
  2122. Purpose: Hack to skip potential volume name.
  2123. Returns: pointer to beginning of pathname in sideitem
  2124. Cond: --
  2125. */
  2126. LPCTSTR PRIVATE SkipDisplayJunkHack(
  2127. LPSIDEITEM psi)
  2128. {
  2129. UINT ich;
  2130. ASSERT(psi);
  2131. ASSERT(psi->pszDir);
  2132. ASSERT(TEXT('(') == *psi->pszDir && 0 < psi->ichRealPath ||
  2133. 0 == psi->ichRealPath);
  2134. ASSERT(psi->ichRealPath <= (UINT)lstrlen(psi->pszDir));
  2135. // Paranoid checking here. This function is being added close
  2136. // to RTM, so as an added safety net, we're adding this min()
  2137. // check. For Nashville, after we're sure that there is no
  2138. // problem with ichRealPath, we can remove the min() function.
  2139. ich = min(psi->ichRealPath, (UINT)lstrlen(psi->pszDir));
  2140. return &psi->pszDir[ich];
  2141. }
  2142. /*----------------------------------------------------------
  2143. Purpose: Returns a path that uses the share name of the hvid,
  2144. or the machine name if that is not available.
  2145. Returns: Pointer to buffer
  2146. Cond: --
  2147. */
  2148. LPTSTR PRIVATE GetAlternativePath(
  2149. LPTSTR pszBuf, // Must be MAX_PATH in length
  2150. int cchMax, // Must be equal to MAX_PATH
  2151. LPCTSTR pszPath,
  2152. HVOLUMEID hvid,
  2153. LPUINT pichRealPath)
  2154. {
  2155. TWINRESULT tr;
  2156. VOLUMEDESC vd;
  2157. ASSERT(pichRealPath);
  2158. *pichRealPath = 0;
  2159. vd.ulSize = sizeof(vd);
  2160. tr = Sync_GetVolumeDescription(hvid, &vd);
  2161. if (TR_SUCCESS == tr)
  2162. {
  2163. // Is a share name available?
  2164. if (IsFlagSet(vd.dwFlags, VD_FL_NET_RESOURCE_VALID))
  2165. {
  2166. // Yes; use that
  2167. lstrcpyn(pszBuf, vd.rgchNetResource, cchMax);
  2168. PathAppend(pszBuf, PathFindEndOfRoot(pszPath));
  2169. PathMakePresentable(pszBuf);
  2170. }
  2171. else if (IsFlagSet(vd.dwFlags, VD_FL_VOLUME_LABEL_VALID))
  2172. {
  2173. // No; use volume label
  2174. LPTSTR pszMsg;
  2175. PathMakePresentable(vd.rgchVolumeLabel);
  2176. if (ConstructMessage(&pszMsg, g_hinst, MAKEINTRESOURCE(IDS_ALTNAME),
  2177. vd.rgchVolumeLabel, pszPath))
  2178. {
  2179. lstrcpyn(pszBuf, pszMsg, cchMax);
  2180. GFree(pszMsg);
  2181. }
  2182. else
  2183. lstrcpyn(pszBuf, pszPath, cchMax);
  2184. *pichRealPath = 3 + lstrlen(vd.rgchVolumeLabel);
  2185. PathMakePresentable(&pszBuf[*pichRealPath]);
  2186. }
  2187. else
  2188. {
  2189. lstrcpyn(pszBuf, pszPath, cchMax);
  2190. PathMakePresentable(pszBuf);
  2191. }
  2192. }
  2193. else
  2194. {
  2195. lstrcpyn(pszBuf, pszPath, cchMax);
  2196. PathMakePresentable(pszBuf);
  2197. }
  2198. return pszBuf;
  2199. }
  2200. /*----------------------------------------------------------
  2201. Purpose: Constructs a path that would be appropriate for
  2202. the sideitem structure. The path is placed in the
  2203. provided buffer.
  2204. Typically the path will simply be the folder path in
  2205. the recnode. In cases when the recnode is unavailable,
  2206. this function prepends the machine name (or share name)
  2207. to the path.
  2208. Returns: --
  2209. Cond: --
  2210. */
  2211. void PRIVATE PathForSideItem(
  2212. LPTSTR pszBuf, // Must be MAX_PATH in length
  2213. int cchMax, // Must be equal to MAX_PATH
  2214. HVOLUMEID hvid,
  2215. LPCTSTR pszFolder,
  2216. RECNODESTATE rns,
  2217. LPUINT pichRealPath)
  2218. {
  2219. ASSERT(pszBuf);
  2220. ASSERT(pszFolder);
  2221. ASSERT(pichRealPath);
  2222. if (RNS_UNAVAILABLE == rns)
  2223. GetAlternativePath(pszBuf, cchMax, pszFolder, hvid, pichRealPath);
  2224. else
  2225. {
  2226. lstrcpyn(pszBuf, pszFolder, cchMax);
  2227. PathMakePresentable(pszBuf);
  2228. *pichRealPath = 0;
  2229. }
  2230. MyPathRemoveBackslash(pszBuf);
  2231. }
  2232. /*----------------------------------------------------------
  2233. Purpose: Determines the recact action based on the combination
  2234. of the inside and outside recnode actions
  2235. Returns: FALSE if this pair seems like an unlikely match.
  2236. (This can occur if there are two recnodes inside the
  2237. briefcase and we choose the wrong one such that the
  2238. pair consists of two destinations but no source.)
  2239. Cond: --
  2240. */
  2241. BOOL PRIVATE DeriveFileAction(
  2242. RA_ITEM * pitem,
  2243. RECNODEACTION rnaInside,
  2244. RECNODEACTION rnaOutside)
  2245. {
  2246. BOOL bRet = TRUE;
  2247. if (RNA_COPY_FROM_ME == rnaInside &&
  2248. RNA_COPY_TO_ME == rnaOutside)
  2249. {
  2250. pitem->uAction = RAIA_TOOUT;
  2251. }
  2252. else if (RNA_COPY_TO_ME == rnaInside &&
  2253. RNA_COPY_FROM_ME == rnaOutside)
  2254. {
  2255. pitem->uAction = RAIA_TOIN;
  2256. }
  2257. #ifdef NEW_REC
  2258. else if (RNA_DELETE_ME == rnaInside)
  2259. {
  2260. pitem->uAction = RAIA_DELETEIN;
  2261. }
  2262. else if (RNA_DELETE_ME == rnaOutside)
  2263. {
  2264. pitem->uAction = RAIA_DELETEOUT;
  2265. }
  2266. #endif
  2267. else if (RNA_MERGE_ME == rnaInside &&
  2268. RNA_MERGE_ME == rnaOutside)
  2269. {
  2270. pitem->uAction = RAIA_MERGE;
  2271. }
  2272. else if (RNA_COPY_TO_ME == rnaInside &&
  2273. RNA_MERGE_ME == rnaOutside)
  2274. {
  2275. // (This is the merge-first-then-copy to third
  2276. // file case. We sorta punt because we're not
  2277. // showing the implicit merge.)
  2278. pitem->uAction = RAIA_TOIN;
  2279. }
  2280. else if (RNA_MERGE_ME == rnaInside &&
  2281. RNA_COPY_TO_ME == rnaOutside)
  2282. {
  2283. // (This is the merge-first-then-copy to third
  2284. // file case. We sorta punt because we're not
  2285. // showing the implicit merge.)
  2286. pitem->uAction = RAIA_TOOUT;
  2287. }
  2288. else if (RNA_NOTHING == rnaInside)
  2289. {
  2290. // Is one side unavailable?
  2291. if (SI_UNAVAILABLE == pitem->siInside.uState ||
  2292. SI_UNAVAILABLE == pitem->siOutside.uState)
  2293. {
  2294. // Yes; force a skip
  2295. pitem->uAction = RAIA_SKIP;
  2296. }
  2297. else if (SI_DELETED == pitem->siOutside.uState)
  2298. {
  2299. // No; the outside was deleted and the user had previously
  2300. // said don't delete, so it is an orphan now.
  2301. pitem->uAction = RAIA_ORPHAN;
  2302. }
  2303. else
  2304. {
  2305. // No; it is up-to-date or both sides don't exist
  2306. pitem->uAction = RAIA_NOTHING;
  2307. }
  2308. }
  2309. else
  2310. {
  2311. pitem->uAction = RAIA_TOIN;
  2312. bRet = FALSE;
  2313. }
  2314. return bRet;
  2315. }
  2316. /*----------------------------------------------------------
  2317. Purpose: Determines the action and possibly a better inside
  2318. path if there are multiple nodes to pick from.
  2319. Returns: better (or same) inside path
  2320. Cond: --
  2321. */
  2322. PCHOOSESIDE PRIVATE DeriveFileActionAndSide(
  2323. RA_ITEM * pitem,
  2324. HDSA hdsa,
  2325. PCHOOSESIDE pchsideInside,
  2326. PCHOOSESIDE pchsideOutside, // May be NULL
  2327. BOOL bKeepFirstChoice)
  2328. {
  2329. ASSERT(pchsideInside);
  2330. if (pchsideOutside)
  2331. {
  2332. PRECNODE prnInside = pchsideInside->prn;
  2333. PRECNODE prnOutside = pchsideOutside->prn;
  2334. PRECITEM pri = prnInside->priParent;
  2335. #ifndef NEW_REC
  2336. // Was the original deleted?
  2337. if (RNS_DELETED == prnOutside->rnstate)
  2338. {
  2339. // Yes; make this an orphan
  2340. TRACE_MSG(TF_GENERAL, TEXT("Found outside path to be deleted"));
  2341. pitem->uAction = RAIA_ORPHAN;
  2342. }
  2343. else
  2344. #endif
  2345. {
  2346. // No
  2347. BOOL bDoAgain;
  2348. PCHOOSESIDE pchside = pchsideInside;
  2349. // Determine the action based on the currently
  2350. // chosen inside and outside pair. If DeriveFileAction
  2351. // determines that the current inside selection is
  2352. // unlikely, we get the next best choice and try
  2353. // again.
  2354. do
  2355. {
  2356. BOOL bGetNextBest = !DeriveFileAction(pitem,
  2357. pchside->prn->rnaction,
  2358. prnOutside->rnaction);
  2359. bDoAgain = FALSE;
  2360. if (!bKeepFirstChoice)
  2361. {
  2362. if (bGetNextBest &&
  2363. 2 < pri->ulcNodes)
  2364. {
  2365. TRACE_MSG(TF_GENERAL, TEXT("Getting next best node"));
  2366. if (!ChooseSide_GetNextBest(hdsa, &pchside))
  2367. break;
  2368. bDoAgain = TRUE;
  2369. }
  2370. else if (!bGetNextBest)
  2371. pchsideInside = pchside;
  2372. else
  2373. ASSERT(0);
  2374. }
  2375. } while (bDoAgain);
  2376. // Is this a broken merge?
  2377. if (RIA_BROKEN_MERGE == pri->riaction)
  2378. {
  2379. // Yes; override and say it is a conflict
  2380. pitem->uAction = RAIA_CONFLICT;
  2381. }
  2382. }
  2383. }
  2384. else
  2385. {
  2386. TRACE_MSG(TF_GENERAL, TEXT("Outside path doesn't exist in recitem"));
  2387. pitem->uAction = RAIA_ORPHAN;
  2388. }
  2389. return pchsideInside;
  2390. }
  2391. /*----------------------------------------------------------
  2392. Purpose: Updates *prns and *prna based on given pchside, or
  2393. leaves them alone.
  2394. Returns: --
  2395. Cond: --
  2396. */
  2397. void PRIVATE DeriveFolderStateAndAction(
  2398. PCHOOSESIDE pchside,
  2399. RECNODESTATE * prns,
  2400. UINT * puAction)
  2401. {
  2402. PRECNODE prn;
  2403. ASSERT(pchside);
  2404. ASSERT(prns);
  2405. ASSERT(puAction);
  2406. ASSERT(RAIA_SOMETHING == *puAction || RAIA_NOTHING == *puAction ||
  2407. RAIA_SKIP == *puAction);
  2408. prn = pchside->prn;
  2409. ASSERT(prn);
  2410. switch (prn->rnstate)
  2411. {
  2412. case RNS_UNAVAILABLE:
  2413. *prns = RNS_UNAVAILABLE;
  2414. *puAction = RAIA_SKIP; // (Always takes precedence)
  2415. break;
  2416. #ifdef NEW_REC
  2417. case RNS_NEVER_RECONCILED:
  2418. #endif
  2419. case RNS_CHANGED:
  2420. *prns = RNS_CHANGED;
  2421. if (RAIA_NOTHING == *puAction)
  2422. *puAction = RAIA_SOMETHING;
  2423. break;
  2424. case RNS_DELETED:
  2425. #ifdef NEW_REC
  2426. if (RNA_DELETE_ME == prn->rnaction)
  2427. {
  2428. *prns = RNS_CHANGED;
  2429. if (RAIA_NOTHING == *puAction)
  2430. *puAction = RAIA_SOMETHING;
  2431. }
  2432. #else
  2433. // Leave the state as it is
  2434. #endif
  2435. break;
  2436. case RNS_DOES_NOT_EXIST:
  2437. case RNS_UP_TO_DATE:
  2438. case RNS_NOT_RECONCILED:
  2439. switch (prn->rnaction)
  2440. {
  2441. case RNA_COPY_TO_ME:
  2442. #ifdef NEW_REC
  2443. if (RAIA_NOTHING == *puAction)
  2444. *puAction = RAIA_SOMETHING;
  2445. #else
  2446. // Poor man's tombstoning. Don't say the folder
  2447. // needs updating if files have been deleted or
  2448. // the whole folder has been deleted.
  2449. //
  2450. if (!PathExists(prn->pcszFolder))
  2451. {
  2452. // Folder is gone. Say this is an orphan now.
  2453. *prns = RNS_DELETED;
  2454. }
  2455. else if (RAIA_NOTHING == *puAction)
  2456. {
  2457. *puAction = RAIA_SOMETHING;
  2458. }
  2459. #endif
  2460. break;
  2461. #ifdef NEW_REC
  2462. case RNA_DELETE_ME:
  2463. #endif
  2464. case RNA_MERGE_ME:
  2465. if (RAIA_NOTHING == *puAction)
  2466. *puAction = RAIA_SOMETHING;
  2467. break;
  2468. }
  2469. break;
  2470. default:
  2471. ASSERT(0);
  2472. break;
  2473. }
  2474. }
  2475. /*----------------------------------------------------------
  2476. Purpose: Determine the recnode state of a folder that has
  2477. no intersecting recnodes.
  2478. Returns: recnode state
  2479. Cond: --
  2480. */
  2481. RECNODESTATE PRIVATE DeriveFolderState(
  2482. PCHOOSESIDE pchside)
  2483. {
  2484. FOLDERTWINSTATUS uStatus;
  2485. RECNODESTATE rns;
  2486. Sync_GetFolderTwinStatus((HFOLDERTWIN)pchside->htwin, NULL, 0, &uStatus);
  2487. if (FTS_UNAVAILABLE == uStatus)
  2488. rns = RNS_UNAVAILABLE;
  2489. else
  2490. rns = RNS_UP_TO_DATE;
  2491. return rns;
  2492. }
  2493. /*----------------------------------------------------------
  2494. Purpose: Initialize a paired-twin structure assuming pszPath
  2495. is a file.
  2496. Returns: standard result
  2497. Cond: --
  2498. */
  2499. HRESULT PRIVATE RAI_InitAsRecItem(
  2500. LPRA_ITEM pitem,
  2501. LPCTSTR pszBrfPath,
  2502. LPCTSTR pszPath, // May be NULL
  2503. PRECITEM pri,
  2504. BOOL bKeepFirstChoice)
  2505. {
  2506. HRESULT hres;
  2507. HDSA hdsa;
  2508. ASSERT(pitem);
  2509. ASSERT(pszBrfPath);
  2510. ASSERT(pri);
  2511. hres = ChooseSide_CreateAsFile(&hdsa, pri);
  2512. if (SUCCEEDED(hres))
  2513. {
  2514. TCHAR sz[MAX_PATH];
  2515. PCHOOSESIDE pchside;
  2516. PCHOOSESIDE pchsideOutside;
  2517. UINT ichRealPath;
  2518. DEBUG_CODE( Sync_DumpRecItem(TR_SUCCESS, pri, TEXT("RAI_InitAsFile")); )
  2519. pitem->mask = RAIF_ALL & ~RAIF_LPARAM;
  2520. if (!GSetString(&pitem->pszName, pri->pcszName))
  2521. goto Error;
  2522. PathMakePresentable(pitem->pszName);
  2523. // Default style
  2524. if (RIA_MERGE == pri->riaction)
  2525. pitem->uStyle = RAIS_CANMERGE;
  2526. else
  2527. pitem->uStyle = 0;
  2528. // Is there an outside file?
  2529. if (ChooseSide_GetBest(hdsa, pszBrfPath, NULL, &pchside))
  2530. {
  2531. // Yes
  2532. RECNODESTATE rns = pchside->prn->rnstate;
  2533. DEBUG_CODE( ChooseSide_DumpList(hdsa); )
  2534. pitem->siOutside.uState = SiFromRns(rns);
  2535. PathForSideItem(sz, ARRAYSIZE(sz), pchside->hvid, pchside->pszFolder, rns, &ichRealPath);
  2536. if (!GSetString(&pitem->siOutside.pszDir, sz))
  2537. goto Error;
  2538. pitem->siOutside.fs = pchside->prn->fsCurrent;
  2539. pitem->siOutside.ichRealPath = ichRealPath;
  2540. }
  2541. else
  2542. {
  2543. // No; this is an orphan
  2544. DEBUG_CODE( ChooseSide_DumpList(hdsa); )
  2545. if (!GSetString(&pitem->siOutside.pszDir, c_szNULL))
  2546. goto Error;
  2547. pitem->siOutside.uState = SI_NOEXIST;
  2548. pitem->siOutside.ichRealPath = 0;
  2549. }
  2550. pchsideOutside = pchside;
  2551. // Make sure we have some fully qualified folder on which
  2552. // to base our decision for an inside path
  2553. if (pszPath)
  2554. {
  2555. lstrcpyn(sz, pszPath, ARRAYSIZE(sz));
  2556. PathRemoveFileSpec(sz);
  2557. }
  2558. else
  2559. lstrcpyn(sz, pszBrfPath, ARRAYSIZE(sz)); // (best we can do...)
  2560. // Get the inside folder
  2561. if (ChooseSide_GetBest(hdsa, pszBrfPath, sz, &pchside))
  2562. {
  2563. RECNODESTATE rns;
  2564. DEBUG_CODE( ChooseSide_DumpList(hdsa); )
  2565. pchside = DeriveFileActionAndSide(pitem, hdsa, pchside, pchsideOutside, bKeepFirstChoice);
  2566. // Determine status of inside file
  2567. rns = pchside->prn->rnstate;
  2568. pitem->siInside.uState = SiFromRns(rns);
  2569. PathForSideItem(sz, ARRAYSIZE(sz), pchside->hvid, pchside->pszFolder, rns, &ichRealPath);
  2570. GSetString(&pitem->siInside.pszDir, sz);
  2571. pitem->siInside.fs = pchside->prn->fsCurrent;
  2572. pitem->siInside.ichRealPath = ichRealPath;
  2573. // Is there a node for the outside?
  2574. if (pchsideOutside)
  2575. {
  2576. // Yes; special case. If a single side does not exist
  2577. // then say the existing side is new.
  2578. if (SI_NOEXIST == pitem->siInside.uState &&
  2579. SI_NOEXIST == pitem->siOutside.uState)
  2580. ; // Do nothing special
  2581. else if (SI_NOEXIST == pitem->siInside.uState)
  2582. {
  2583. ASSERT(SI_NOEXIST != pitem->siOutside.uState);
  2584. pitem->siOutside.uState = SI_NEW;
  2585. }
  2586. else if (SI_NOEXIST == pitem->siOutside.uState)
  2587. {
  2588. ASSERT(SI_NOEXIST != pitem->siInside.uState);
  2589. pitem->siInside.uState = SI_NEW;
  2590. }
  2591. }
  2592. // Save away twin handle. Use the inside htwin because
  2593. // we want to always delete from inside the briefcase
  2594. // (it's all in your perspective...)
  2595. pitem->htwin = (HTWIN)pchside->prn->hObjectTwin;
  2596. }
  2597. else
  2598. {
  2599. // It is relatively bad to be here
  2600. DEBUG_CODE( ChooseSide_DumpList(hdsa); )
  2601. ASSERT(0);
  2602. hres = E_FAIL;
  2603. }
  2604. DEBUG_CODE( DumpTwinPair(pitem); )
  2605. ChooseSide_Free(hdsa);
  2606. hdsa = NULL;
  2607. }
  2608. else
  2609. {
  2610. hdsa = NULL;
  2611. Error:
  2612. hres = E_OUTOFMEMORY;
  2613. }
  2614. if (FAILED(hres))
  2615. {
  2616. ChooseSide_Free(hdsa);
  2617. }
  2618. return hres;
  2619. }
  2620. /*----------------------------------------------------------
  2621. Purpose: Choose a recitem whose name matches the given name.
  2622. Returns: A pointer to the recitem in the given reclist
  2623. NULL if filespec is not found
  2624. Cond: --
  2625. */
  2626. PRECITEM PRIVATE ChooseRecItem(
  2627. PRECLIST prl,
  2628. LPCTSTR pszName)
  2629. {
  2630. PRECITEM pri;
  2631. for (pri = prl->priFirst; pri; pri = pri->priNext)
  2632. {
  2633. if (IsSzEqual(pri->pcszName, pszName))
  2634. return pri;
  2635. }
  2636. return NULL;
  2637. }
  2638. /*----------------------------------------------------------
  2639. Purpose: Initialize a paired-twin structure assuming pszPath
  2640. is a file.
  2641. Returns: standard result
  2642. Cond: --
  2643. */
  2644. HRESULT PRIVATE RAI_InitAsFile(
  2645. LPRA_ITEM pitem,
  2646. LPCTSTR pszBrfPath,
  2647. LPCTSTR pszPath,
  2648. PRECLIST prl)
  2649. {
  2650. HRESULT hres;
  2651. PRECITEM pri;
  2652. ASSERT(pitem);
  2653. ASSERT(pszBrfPath);
  2654. ASSERT(pszPath);
  2655. ASSERT(prl);
  2656. if (pszPath)
  2657. {
  2658. LPCTSTR pszFile;
  2659. pszFile = PathFindFileName(pszPath);
  2660. pri = ChooseRecItem(prl, pszFile);
  2661. ASSERT(pri);
  2662. }
  2663. else
  2664. {
  2665. pri = NULL;
  2666. }
  2667. if (pri)
  2668. {
  2669. hres = RAI_InitAsRecItem(pitem, pszBrfPath, pszPath, pri, TRUE);
  2670. }
  2671. else
  2672. {
  2673. hres = E_OUTOFMEMORY;
  2674. }
  2675. return hres;
  2676. }
  2677. /*----------------------------------------------------------
  2678. Purpose: Initialize a paired-twin structure assuming pszPath
  2679. is a file.
  2680. Returns: standard result
  2681. Cond: --
  2682. */
  2683. HRESULT PRIVATE RAI_InitAsFolder(
  2684. LPRA_ITEM pitem,
  2685. LPCTSTR pszBrfPath,
  2686. LPCTSTR pszPath, // Should be inside the briefcase
  2687. PRECLIST prl,
  2688. PFOLDERTWINLIST pftl)
  2689. {
  2690. HRESULT hres;
  2691. HDSA hdsa;
  2692. ASSERT(pitem);
  2693. ASSERT(pszBrfPath);
  2694. ASSERT(pszPath);
  2695. ASSERT(prl);
  2696. ASSERT(pftl);
  2697. ASSERT(0 < pftl->ulcItems);
  2698. pitem->mask = RAIF_ALL & ~RAIF_LPARAM;
  2699. DEBUG_CODE( Sync_DumpRecList(TR_SUCCESS, prl, TEXT("RAI_InitAsFolder")); )
  2700. DEBUG_CODE( Sync_DumpFolderTwinList(pftl, NULL); )
  2701. // We only need to flag RAIS_FOLDER for the folder case.
  2702. // (Context menu isn't available for folders, so RAIS_CANMERGE is
  2703. // unnecessary.)
  2704. //
  2705. pitem->uStyle = RAIS_FOLDER;
  2706. hres = ChooseSide_CreateEmpty(&hdsa);
  2707. if (SUCCEEDED(hres))
  2708. {
  2709. PRECITEM pri;
  2710. RECNODESTATE rnsInside;
  2711. RECNODESTATE rnsOutside;
  2712. PCHOOSESIDE pchside;
  2713. // Set starting defaults
  2714. pitem->uAction = RAIA_NOTHING;
  2715. rnsInside = RNS_UP_TO_DATE;
  2716. rnsOutside = RNS_UP_TO_DATE;
  2717. // Iterate thru reclist, choosing recnode pairs and dynamically
  2718. // updating rnsInside, rnsOutside and pitem->uAction.
  2719. for (pri = prl->priFirst; pri; pri = pri->priNext)
  2720. {
  2721. ChooseSide_InitAsFile(hdsa, pri);
  2722. // Get the inside item
  2723. if (ChooseSide_GetBest(hdsa, pszBrfPath, pszPath, &pchside))
  2724. {
  2725. DeriveFolderStateAndAction(pchside, &rnsInside, &pitem->uAction);
  2726. }
  2727. else
  2728. ASSERT(0);
  2729. // Get the outside item
  2730. if (ChooseSide_GetBest(hdsa, pszBrfPath, NULL, &pchside))
  2731. {
  2732. DeriveFolderStateAndAction(pchside, &rnsOutside, &pitem->uAction);
  2733. }
  2734. else
  2735. ASSERT(0);
  2736. }
  2737. ChooseSide_Free(hdsa);
  2738. // Finish up
  2739. hres = ChooseSide_CreateAsFolder(&hdsa, pftl);
  2740. if (SUCCEEDED(hres))
  2741. {
  2742. TCHAR sz[MAX_PATH];
  2743. UINT ichRealPath;
  2744. // Name
  2745. if (!GSetString(&pitem->pszName, PathFindFileName(pszPath)))
  2746. goto Error;
  2747. PathMakePresentable(pitem->pszName);
  2748. // Get the inside folder
  2749. if (ChooseSide_GetBest(hdsa, pszBrfPath, pszPath, &pchside))
  2750. {
  2751. DEBUG_CODE( ChooseSide_DumpList(hdsa); )
  2752. // Are there any intersecting files in this folder twin?
  2753. if (0 == prl->ulcItems)
  2754. rnsInside = DeriveFolderState(pchside); // No
  2755. pitem->siInside.uState = SiFromRns(rnsInside);
  2756. PathForSideItem(sz, ARRAYSIZE(sz), pchside->hvid, pchside->pszFolder, rnsInside, &ichRealPath);
  2757. if (!GSetString(&pitem->siInside.pszDir, sz))
  2758. goto Error;
  2759. // (Hack to avoid printing bogus time/date)
  2760. pitem->siInside.fs.fscond = FS_COND_UNAVAILABLE;
  2761. pitem->siInside.ichRealPath = ichRealPath;
  2762. }
  2763. else
  2764. {
  2765. DEBUG_CODE( ChooseSide_DumpList(hdsa); )
  2766. ASSERT(0);
  2767. }
  2768. // Get the outside folder
  2769. if (ChooseSide_GetBest(hdsa, pszBrfPath, NULL, &pchside))
  2770. {
  2771. DEBUG_CODE( ChooseSide_DumpList(hdsa); )
  2772. // Are there any intersecting files in this folder twin?
  2773. if (0 == prl->ulcItems)
  2774. rnsOutside = DeriveFolderState(pchside); // No
  2775. pitem->siOutside.uState = SiFromRns(rnsOutside);
  2776. PathForSideItem(sz, ARRAYSIZE(sz), pchside->hvid, pchside->pszFolder, rnsOutside, &ichRealPath);
  2777. if (!GSetString(&pitem->siOutside.pszDir, sz))
  2778. goto Error;
  2779. // (Hack to avoid printing bogus time/date)
  2780. pitem->siOutside.fs.fscond = FS_COND_UNAVAILABLE;
  2781. pitem->siOutside.ichRealPath = ichRealPath;
  2782. // Save away twin handle. Use the outside handle
  2783. // for folders.
  2784. pitem->htwin = pchside->htwin;
  2785. }
  2786. else
  2787. {
  2788. DEBUG_CODE( ChooseSide_DumpList(hdsa); )
  2789. ASSERT(0);
  2790. }
  2791. DEBUG_CODE( DumpTwinPair(pitem); )
  2792. ChooseSide_Free(hdsa);
  2793. }
  2794. }
  2795. if (FAILED(hres))
  2796. {
  2797. Error:
  2798. if (SUCCEEDED(hres))
  2799. hres = E_OUTOFMEMORY;
  2800. ChooseSide_Free(hdsa);
  2801. }
  2802. return hres;
  2803. }
  2804. /*----------------------------------------------------------
  2805. Purpose: Create a paired-twin structure given a path name.
  2806. Returns: standard result
  2807. Cond: --
  2808. */
  2809. HRESULT PUBLIC RAI_Create(
  2810. LPRA_ITEM * ppitem,
  2811. LPCTSTR pszBrfPath,
  2812. LPCTSTR pszPath, // Should be inside the briefcase
  2813. PRECLIST prl,
  2814. PFOLDERTWINLIST pftl) // NULL if pszPath is a file
  2815. {
  2816. HRESULT hres;
  2817. LPRA_ITEM pitem;
  2818. ASSERT(ppitem);
  2819. ASSERT(pszPath);
  2820. ASSERT(pszBrfPath);
  2821. ASSERT(prl);
  2822. DBG_ENTER_SZ(TEXT("RAI_Create"), pszPath);
  2823. if (PathExists(pszPath))
  2824. {
  2825. pitem = GAlloc(sizeof(*pitem));
  2826. if (pitem)
  2827. {
  2828. if (PathIsDirectory(pszPath))
  2829. hres = RAI_InitAsFolder(pitem, pszBrfPath, pszPath, prl, pftl);
  2830. else
  2831. hres = RAI_InitAsFile(pitem, pszBrfPath, pszPath, prl);
  2832. if (FAILED(hres))
  2833. {
  2834. // Cleanup
  2835. RAI_Free(pitem);
  2836. pitem = NULL;
  2837. }
  2838. }
  2839. else
  2840. hres = E_OUTOFMEMORY;
  2841. }
  2842. else
  2843. {
  2844. pitem = NULL;
  2845. hres = E_FAIL;
  2846. }
  2847. *ppitem = pitem;
  2848. DBG_EXIT_HRES(TEXT("RAI_Create"), hres);
  2849. return hres;
  2850. }
  2851. /*----------------------------------------------------------
  2852. Purpose: Create a paired-twin structure given a recitem.
  2853. Returns: standard result
  2854. Cond: --
  2855. */
  2856. HRESULT PUBLIC RAI_CreateFromRecItem(LPRA_ITEM * ppitem, LPCTSTR pszBrfPath, PRECITEM pri)
  2857. {
  2858. HRESULT hr = E_INVALIDARG;
  2859. LPRA_ITEM pitem;
  2860. DBG_ENTER(TEXT("RAI_CreateFromRecItem"));
  2861. if (ppitem && pszBrfPath && pri)
  2862. {
  2863. pitem = GAlloc(sizeof(*pitem));
  2864. if (pitem)
  2865. {
  2866. hr = RAI_InitAsRecItem(pitem, pszBrfPath, NULL, pri, FALSE);
  2867. if (FAILED(hr))
  2868. {
  2869. // Cleanup
  2870. RAI_Free(pitem);
  2871. pitem = NULL;
  2872. }
  2873. }
  2874. else
  2875. {
  2876. hr = E_OUTOFMEMORY;
  2877. }
  2878. *ppitem = pitem;
  2879. }
  2880. DBG_EXIT_HRES(TEXT("RAI_CreateFromRecItem"), hr);
  2881. return hr;
  2882. }
  2883. /*----------------------------------------------------------
  2884. Purpose: Free a paired item structure
  2885. Returns: standard result
  2886. Cond: --
  2887. */
  2888. HRESULT PUBLIC RAI_Free(
  2889. LPRA_ITEM pitem)
  2890. {
  2891. HRESULT hres;
  2892. if (pitem)
  2893. {
  2894. GFree(pitem->pszName);
  2895. GFree(pitem->siInside.pszDir);
  2896. GFree(pitem->siOutside.pszDir);
  2897. GFree(pitem);
  2898. hres = NOERROR;
  2899. }
  2900. else
  2901. hres = E_FAIL;
  2902. return hres;
  2903. }