recact.c
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 91k
Category:

Windows Kernel

Development Platform:

Visual C++

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