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

Windows Kernel

Development Platform:

Visual C++

  1. // large icon view stuff
  2. #include "ctlspriv.h"
  3. #include "listview.h"
  4. #ifdef FE_IME
  5. #include <imm.h>
  6. static char const szIMECompPos[]="IMECompPos";
  7. #endif
  8. #define ICONCXLABEL(pl, pi) ((pl->style & LVS_NOLABELWRAP) ? pi->cxSingleLabel : pi->cxMultiLabel)
  9. void NEAR PASCAL ListView_IDrawItem(LV* plv, int i, HDC hdc, LPPOINT lpptOrg, RECT FAR* prcClip, UINT fDraw)
  10. {
  11.     RECT rcIcon;
  12.     RECT rcLabel;
  13.     RECT rcBounds;
  14.     RECT rcT;
  15.     ListView_GetRects(plv, i, &rcIcon, &rcLabel, &rcBounds, NULL);
  16.     if (!prcClip || IntersectRect(&rcT, prcClip, &rcBounds))
  17.     {
  18.         LV_ITEM item;
  19.         char ach[CCHLABELMAX];
  20.         UINT fText;
  21. if (lpptOrg)
  22. {
  23.     OffsetRect(&rcIcon, lpptOrg->x - rcBounds.left,
  24.      lpptOrg->y - rcBounds.top);
  25.     OffsetRect(&rcLabel, lpptOrg->x - rcBounds.left,
  26.      lpptOrg->y - rcBounds.top);
  27. }
  28.         item.iItem = i;
  29.         item.iSubItem = 0;
  30.         item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
  31.         item.stateMask = LVIS_ALL;
  32.         item.pszText = ach;
  33.         item.cchTextMax = sizeof(ach);
  34.         ListView_OnGetItem(plv, &item);
  35.         fText = ListView_DrawImage(plv, &item, hdc,
  36.             rcIcon.left + g_cxIconMargin, rcIcon.top + g_cyIconMargin, fDraw);
  37.         // Don't draw label if it's being edited...
  38.         //
  39.         if (plv->iEdit != i)
  40.         {
  41.             if (rcLabel.bottom - rcLabel.top > plv->cyLabelChar)
  42.                 fText |= SHDT_DRAWTEXT;
  43.             else
  44.                 fText |= SHDT_ELLIPSES;
  45.     if (fDraw & LVDI_TRANSTEXT)
  46.                 fText |= SHDT_TRANSPARENT;
  47.             if (item.pszText)
  48.             {
  49.                 // yow!  this eats stack.  256 from this proc and 256 from shell_drawtext
  50.                 SHDrawText(hdc, item.pszText, &rcLabel, LVCFMT_LEFT, fText,
  51.                         plv->cyLabelChar, plv->cxEllipses, plv->clrText,
  52.                            (plv->style & WS_DISABLED ? plv->clrBk : plv->clrTextBk));
  53.             }
  54.             if ((fDraw & LVDI_FOCUS) && (item.state & LVIS_FOCUSED))
  55.                 DrawFocusRect(hdc, &rcLabel);
  56.         }
  57.     }
  58. }
  59. int NEAR ListView_IItemHitTest(LV* plv, int x, int y, UINT FAR* pflags)
  60. {
  61.     int iHit;
  62.     UINT flags;
  63.     POINT pt;
  64.     // Map window-relative coordinates to view-relative coords...
  65.     //
  66.     pt.x = x + plv->ptOrigin.x;
  67.     pt.y = y + plv->ptOrigin.y;
  68.     // If there are any uncomputed items, recompute them now.
  69.     //
  70.     if (plv->rcView.left == RECOMPUTE)
  71.         ListView_Recompute(plv);
  72.     flags = 0;
  73.     for (iHit = 0; iHit < ListView_Count(plv); iHit++)
  74.     {
  75.         LISTITEM FAR* pitem = ListView_FastGetZItemPtr(plv, iHit);
  76.         POINT ptItem;
  77.         RECT rcLabel;
  78.         RECT rcIcon;
  79.         ptItem.x = pitem->pt.x;
  80.         ptItem.y = pitem->pt.y;
  81.         rcIcon.top    = ptItem.y - g_cyIconMargin;
  82.         rcLabel.top    = ptItem.y + plv->cyIcon + g_cyLabelSpace;
  83.         rcLabel.bottom = rcLabel.top + pitem->cyMultiLabel;
  84.         // Quick, easy rejection test...
  85.         //
  86.         if (pt.y < rcIcon.top || pt.y >= rcLabel.bottom)
  87.             continue;
  88.         rcIcon.left   = ptItem.x - g_cxIconMargin;
  89.         rcIcon.right  = ptItem.x + plv->cxIcon + g_cxIconMargin;
  90.         // We need to make sure there is no gap between the icon and label
  91.         rcIcon.bottom = rcLabel.top;
  92.         rcLabel.left   = ptItem.x  + (plv->cxIcon / 2) - (ICONCXLABEL(plv, pitem) / 2);
  93.         rcLabel.right  = rcLabel.left + ICONCXLABEL(plv, pitem);
  94.         if (PtInRect(&rcIcon, pt))
  95.         {
  96.             flags = LVHT_ONITEMICON;
  97.             break;
  98.         }
  99.         if (PtInRect(&rcLabel, pt))
  100.         {
  101.             flags = LVHT_ONITEMLABEL;
  102.             break;
  103.         }
  104.     }
  105.     if (flags == 0)
  106.     {
  107.         flags = LVHT_NOWHERE;
  108.         iHit = -1;
  109.     }
  110.     else
  111.     {
  112.         iHit = DPA_GetPtrIndex(plv->hdpa, ListView_FastGetZItemPtr(plv, iHit));
  113.     }
  114.     *pflags = flags;
  115.     return iHit;
  116. }
  117. // out:
  118. //      prcIcon         icon bounds including icon margin area
  119. void NEAR ListView_IGetRects(LV* plv, LISTITEM FAR* pitem, RECT FAR* prcIcon, RECT FAR* prcLabel, LPRECT prcBounds)
  120. {
  121.     prcIcon->left   = pitem->pt.x - g_cxIconMargin - plv->ptOrigin.x;
  122.     prcIcon->right  = prcIcon->left + plv->cxIcon + 2 * g_cxIconMargin;
  123.     prcIcon->top    = pitem->pt.y - g_cyIconMargin - plv->ptOrigin.y;
  124.     prcIcon->bottom = prcIcon->top + plv->cyIcon + 2 * g_cyIconMargin;
  125.     prcLabel->left   = pitem->pt.x  + (plv->cxIcon / 2) - (ICONCXLABEL(plv, pitem) / 2) - plv->ptOrigin.x;
  126.     prcLabel->right  = prcLabel->left + ICONCXLABEL(plv, pitem);
  127.     prcLabel->top    = pitem->pt.y  + plv->cyIcon + g_cyLabelSpace - plv->ptOrigin.y;
  128.     prcLabel->bottom = prcLabel->top  + pitem->cyMultiLabel;
  129. }
  130. int NEAR ListView_GetSlotCount(LV* plv, BOOL fWithoutScrollbars)
  131. {
  132.     int cxScreen;
  133.     int cyScreen;
  134.     int dxItem;
  135.     int dyItem;
  136.     int iSlots = 1;
  137.     BOOL fCheckWithScroll = FALSE;
  138.     DWORD style;
  139.     // Always use the current client window size to determine
  140.     //
  141.     // REVIEW: Should we exclude any vertical scroll bar that may
  142.     // exist when computing this?  progman.exe does not.
  143.     //
  144.     cxScreen = plv->sizeClient.cx;
  145.     cyScreen = plv->sizeClient.cy;
  146.     if (fWithoutScrollbars) {
  147.         style = GetWindowStyle(plv->hwnd);
  148.         if (style & WS_VSCROLL)
  149.             cxScreen += g_cxScrollbar;
  150.         if (style & WS_HSCROLL)
  151.             cyScreen += g_cyScrollbar;
  152.     }
  153.     if (ListView_IsSmallView(plv))
  154.         dxItem = plv->cxItem;
  155.     else
  156.         dxItem = lv_cxIconSpacing;
  157.     if (ListView_IsSmallView(plv))
  158.         dyItem = plv->cyItem;
  159.     else
  160.         dyItem = lv_cyIconSpacing;
  161.     // Lets see which direction the view states
  162.     switch (plv->style & LVS_ALIGNMASK)
  163.     {
  164.     case LVS_ALIGNBOTTOM:
  165.     case LVS_ALIGNTOP:
  166.         iSlots = max(1, (cxScreen) / dxItem);
  167.         fCheckWithScroll = (BOOL)(style & WS_VSCROLL);
  168.         break;
  169.     case LVS_ALIGNRIGHT:
  170.     case LVS_ALIGNLEFT:
  171.         iSlots = max(1, (cyScreen) / dyItem);
  172.         fCheckWithScroll = (BOOL)(style & WS_HSCROLL);
  173.         break;
  174.     default:
  175.         Assert(0);
  176.         return 1;
  177.     }
  178.     // if we don't have enough slots total on the screen, we're going to have
  179.     // a scrollbar, so recompute with the scrollbars on
  180.     if (fWithoutScrollbars && fCheckWithScroll) {
  181.         int iTotalSlots = (dxItem * dyItem);
  182.         if (iTotalSlots < ListView_Count(plv)) {
  183.             iSlots = ListView_GetSlotCount(plv, FALSE);
  184.         }
  185.     }
  186.     return iSlots;
  187. }
  188. // Go through and recompute any icon positions and optionally
  189. // icon label dimensions.
  190. //
  191. // This function also recomputes the view bounds rectangle.
  192. //
  193. // The algorithm is to simply search the list for any items needing
  194. // recomputation.  For icon positions, we scan possible icon slots
  195. // and check to see if any already-positioned icon intersects the slot.
  196. // If not, the slot is free.  As an optimization, we start scanning
  197. // icon slots from the previous slot we found.
  198. //
  199. void NEAR ListView_Recompute(LV* plv)
  200. {
  201.     int i;
  202.     int cSlots;
  203.     BOOL fUpdateSB;
  204.     BOOL fAppendAtEnd = FALSE;
  205.     BOOL fLargeIconView;
  206.     int iFree;
  207.     HDC hdc;
  208.     if (!(ListView_IsIconView(plv) || ListView_IsSmallView(plv)))
  209.         return;
  210.     if (plv->flags & LVF_INRECOMPUTE)
  211.     {
  212.         return;
  213.     }
  214.     plv->flags |= LVF_INRECOMPUTE;
  215.     fLargeIconView = ListView_IsIconView(plv);
  216.     hdc = NULL;
  217.     cSlots = ListView_GetSlotCount(plv, FALSE);
  218.     // Scan all items for RECOMPUTE, and recompute slot if needed.
  219.     //
  220.     fUpdateSB = (plv->rcView.left == RECOMPUTE);
  221.     iFree = -1;
  222.     for (i = 0; i < ListView_Count(plv); i++)
  223.     {
  224.         LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, i);
  225.         BOOL fRedraw = FALSE;
  226.         if (pitem->cyMultiLabel == SRECOMPUTE)
  227.         {
  228.             hdc = ListView_RecomputeLabelSize(plv, pitem, i, hdc);
  229.             fRedraw = TRUE;
  230.         }
  231.         if (pitem->pt.y == RECOMPUTE)
  232.         {
  233.             iFree = ListView_FindFreeSlot(plv, i, iFree + 1, cSlots,
  234.                     &fUpdateSB, &fAppendAtEnd, &hdc);
  235.             Assert(iFree != -1);
  236.             ListView_SetIconPos(plv, pitem, iFree, cSlots);
  237.             fRedraw = TRUE;
  238.         }
  239.         if (fRedraw)
  240.         {
  241.             ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
  242.             fUpdateSB = TRUE;
  243.         }
  244.     }
  245.     if (hdc)
  246.         ReleaseDC(HWND_DESKTOP, hdc);
  247.     // If we changed something, recompute the view rectangle
  248.     // and then update the scroll bars.
  249.     //
  250.     if (fUpdateSB)
  251.     {
  252.         // NOTE: No infinite recursion results because we're setting
  253.         // plv->rcView.left != RECOMPUTE
  254.         //
  255.         SetRectEmpty(&plv->rcView);
  256.         for (i = 0; i < ListView_Count(plv); i++)
  257.         {
  258.             RECT rcItem;
  259.             ListView_GetRects(plv, i, NULL, NULL, &rcItem, NULL);
  260.             UnionRect(&plv->rcView, &plv->rcView, &rcItem);
  261.         }
  262.         // add a little space at the edges so that we don't bump text
  263.         // completely to the end of the window
  264.         plv->rcView.bottom += g_cyEdge;
  265.         plv->rcView.right += g_cxEdge;
  266.         OffsetRect(&plv->rcView, plv->ptOrigin.x, plv->ptOrigin.y);
  267.         //DebugMsg(DM_TRACE, "RECOMPUTE: rcView %x %x %x %x", plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
  268.         //DebugMsg(DM_TRACE, "Origin %x %x", plv->ptOrigin.x, plv->ptOrigin.y);
  269.         ListView_UpdateScrollBars(plv);
  270.     }
  271.     // Now state we are out of the recompute...
  272.     plv->flags &= ~LVF_INRECOMPUTE;
  273. }
  274. void NEAR PASCAL NearestSlot(int FAR *x, int FAR *y, int cxItem, int cyItem)
  275. {
  276.     *x += cxItem/2;
  277.     *y += cyItem/2;
  278.     *x = *x - (*x % cxItem);
  279.     *y = *y - (*y % cyItem);
  280. }
  281. void NEAR PASCAL NextSlot(LV* plv, LPRECT lprc)
  282. {
  283.     int cxItem;
  284.     if (ListView_IsSmallView(plv))
  285.     {
  286.         cxItem = plv->cxItem;
  287.     }
  288.     else
  289.     {
  290.         cxItem = lv_cxIconSpacing;
  291.     }
  292.     lprc->left += cxItem;
  293.     lprc->right += cxItem;
  294. }
  295. BOOL NEAR _CalcSlotRect(LV* plv, int iSlot, int cSlot, BOOL fBias, LPRECT lprc)
  296. {
  297.     int cxItem, cyItem;
  298.     BOOL fSmallIcon;
  299.     Assert(plv);
  300.     if (cSlot < 1)
  301.         cSlot = 1;
  302.     if (fSmallIcon = ListView_IsSmallView(plv))
  303.     {
  304.         cxItem = plv->cxItem;
  305.         cyItem = plv->cyItem;
  306.     }
  307.     else
  308.     {
  309.         cxItem = lv_cxIconSpacing;
  310.         cyItem = lv_cyIconSpacing;
  311.     }
  312.     // Lets see which direction the view states
  313.     switch (plv->style & LVS_ALIGNMASK)
  314.     {
  315.     case LVS_ALIGNBOTTOM:
  316.         // Assert False (Change default in shell2d.. to ALIGN_TOP)
  317.     case LVS_ALIGNTOP:
  318.         lprc->left = (iSlot % cSlot) * cxItem;
  319.         lprc->top = (iSlot / cSlot) * cyItem;
  320.         break;
  321.     case LVS_ALIGNLEFT:
  322.         lprc->top = (iSlot % cSlot) * cyItem;
  323.         lprc->left = (iSlot / cSlot) * cxItem;
  324.         break;
  325.     case LVS_ALIGNRIGHT:
  326.         Assert(FALSE);      // Not implemented yet...
  327.         break;
  328.     }
  329.     if (fBias)
  330.     {
  331.         lprc->left -= plv->ptOrigin.x;
  332.         lprc->top -= plv->ptOrigin.y;
  333.     }
  334.     lprc->bottom = lprc->top + cyItem;
  335.     lprc->right = lprc->left + cxItem;
  336.     return(fSmallIcon);
  337. }
  338. // Find an icon slot that doesn't intersect an icon.
  339. // Start search for free slot from slot i.
  340. //
  341. int NEAR ListView_FindFreeSlot(LV* plv, int iItem, int i, int cSlot, BOOL FAR* pfUpdate,
  342.         BOOL FAR *pfAppend, HDC FAR* phdc)
  343. {
  344.     int j;
  345.     HDC hdc;
  346.     RECT rcSlot;
  347.     RECT rcItem;
  348.     RECT rc;
  349.     int xMax = -1;
  350.     int yMax = -1;
  351.     int cItems;
  352.     // Horrible N-squared algorithm:
  353.     // enumerate each slot and see if any items intersect it.
  354.     //
  355.     // REVIEW: This is really slow with long lists (e.g., 1000)
  356.     //
  357.     hdc = NULL;
  358.     cItems = ListView_Count(plv);
  359.     //
  360.     // If the Append at end is set, we should be able to simply get the
  361.     // rectangle of the i-1 element and check against it instead of
  362.     // looking at every other item...
  363.     //
  364.     if (*pfAppend)
  365.     {
  366. Assert(iItem > 0);
  367. // Be carefull about going of the end of the list. (i is a slot
  368. // number not an item index).
  369.         ListView_GetRects(plv, iItem-1, NULL, NULL, &rcItem, NULL);
  370.     }
  371.     for ( ; ; i++)
  372.     {
  373.         // Compute view-relative slot rectangle...
  374.         //
  375.         _CalcSlotRect(plv, i, cSlot, TRUE, &rcSlot);
  376.         if (*pfAppend)
  377.         {
  378.             if (!IntersectRect(&rc, &rcItem, &rcSlot))
  379.                 return i;       // Found a free slot...
  380.         }
  381.         else
  382.         {
  383.             for (j = cItems; j-- > 0; )
  384.             {
  385.                 LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, j);
  386.                 if (pitem->pt.y != RECOMPUTE)
  387.                 {
  388.                     // If the dimensions aren't computed, then do it now.
  389.                     //
  390.                     if (pitem->cyMultiLabel == SRECOMPUTE)
  391.                     {
  392.                         *phdc = ListView_RecomputeLabelSize(plv, pitem, i, *phdc);
  393.                         // Ensure that the item gets redrawn...
  394.                         //
  395.                         ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
  396.                         // Set flag indicating that scroll bars need to be
  397.                         // adjusted.
  398.                         //
  399.                         *pfUpdate = TRUE;
  400.                     }
  401.                     ListView_GetRects(plv, j, NULL, NULL, &rc, NULL);
  402.                     if (IntersectRect(&rc, &rc, &rcSlot))
  403.                         break;
  404.                 }
  405.             }
  406.             if (j < 0)
  407.                 break;
  408.         }
  409.     }
  410.     if ( (rcSlot.bottom > yMax) ||
  411.           ((rcSlot.bottom == yMax) && (rcSlot.right > xMax)))
  412.         *pfAppend = TRUE;
  413.     return i;
  414. }
  415. // Recompute an item's label size (cxLabel/cyLabel).  For speed, this function
  416. // is passed a DC to use for text measurement.
  417. //
  418. // If hdc is NULL, then this function will get a DC and return it.  Otherwise,
  419. // the returned hdc is the same as the one passed in.  It's the caller's
  420. // responsibility to eventually release the DC.
  421. //
  422. HDC NEAR ListView_RecomputeLabelSize(LV* plv, LISTITEM FAR* pitem, int i, HDC hdc)
  423. {
  424.     char szLabel[CCHLABELMAX + 4];
  425.     int cchLabel;
  426.     RECT rcSingle, rcMulti;
  427.     LV_ITEM item;
  428.     Assert(plv);
  429.     Assert(pitem);
  430.     // Get the DC and select the font only once for entire loop.
  431.     //
  432.     if (!hdc)
  433.     {
  434.         // we return this DC and have the calller release it
  435.         hdc = GetDC(HWND_DESKTOP);
  436.         SelectFont(hdc, plv->hfontLabel);
  437.     }
  438.     item.mask = LVIF_TEXT;
  439.     item.iItem = i;
  440.     item.iSubItem = 0;
  441.     item.pszText = szLabel;
  442.     item.cchTextMax = sizeof(szLabel);
  443.     item.stateMask = 0;
  444.     ListView_OnGetItem(plv, &item);
  445.     if (!item.pszText)
  446.     {
  447.         SetRectEmpty(&rcSingle);
  448. rcMulti = rcSingle;
  449.         goto Exit;
  450.     }
  451.     if (item.pszText != szLabel)
  452.         lstrcpy(szLabel, item.pszText);
  453.     cchLabel = lstrlen(szLabel);
  454.     rcMulti.left = rcMulti.top = rcMulti.bottom = 0;
  455.     rcMulti.right = lv_cxIconSpacing - g_cxLabelMargin * 2;
  456.     rcSingle = rcMulti;
  457.     if (cchLabel > 0)
  458.     {
  459.         // Strip off spaces so they're not included in format
  460.         // REVIEW: Is this is a DrawText bug?
  461.         //
  462.         while (cchLabel > 1 && szLabel[cchLabel - 1] == ' ')
  463.             szLabel[--cchLabel] = 0;
  464.         DrawText(hdc, szLabel, cchLabel, &rcSingle, (DT_LV | DT_CALCRECT));
  465. #ifdef WIN32
  466.         DrawText(hdc, szLabel, cchLabel, &rcMulti, (DT_LVWRAP | DT_WORD_ELLIPSIS | DT_CALCRECT));
  467. #else
  468.         DrawText(hdc, szLabel, cchLabel, &rcMulti, (DT_LVWRAP | DT_CALCRECT));
  469. #endif
  470.     }
  471.     else
  472.     {
  473.         rcMulti.bottom = rcMulti.top + plv->cyLabelChar;
  474.     }
  475. Exit:
  476.     pitem->cxSingleLabel = (rcSingle.right - rcSingle.left) + 2 * g_cxLabelMargin;
  477.     pitem->cxMultiLabel = (rcMulti.right - rcMulti.left) + 2 * g_cxLabelMargin;
  478.     pitem->cyMultiLabel = (short)(rcMulti.bottom - rcMulti.top);
  479.     return hdc;
  480. }
  481. // Set up an icon slot position.  Returns FALSE if position didn't change.
  482. //
  483. BOOL NEAR ListView_SetIconPos(LV* plv, LISTITEM FAR* pitem, int iSlot, int cSlot)
  484. {
  485.     RECT rc;
  486.     Assert(plv);
  487.     //
  488.     // Sort of a hack, this internal function return TRUE if small icon.
  489.     if (!_CalcSlotRect(plv, iSlot, cSlot, FALSE, &rc))
  490.     {
  491.         rc.left += g_cxIconOffset;
  492.         rc.top += g_cyIconOffset;
  493.     }
  494.     if (rc.left != pitem->pt.x || rc.top != pitem->pt.y)
  495.     {
  496.         pitem->pt.x = rc.left;
  497.         pitem->pt.y = rc.top;
  498.         // REVIEW: Performance idea:
  499.         //
  500.         // Invalidate rcView only if this icon's old or new position
  501.         // touches or is outside of the current rcView.
  502.         // If we do this, then we must change the various tests
  503.         // of rcView.left == RECOMPUTE to more specific tests of pitem->...
  504.         //
  505.         plv->rcView.left = RECOMPUTE;
  506.         return TRUE;
  507.     }
  508.     return FALSE;
  509. }
  510. void NEAR ListView_GetViewRect2(LV* plv, RECT FAR* prcView, int cx, int cy)
  511. {
  512.     if (plv->rcView.left == RECOMPUTE)
  513.         ListView_Recompute(plv);
  514.     *prcView = plv->rcView;
  515.     OffsetRect(prcView, -plv->ptOrigin.x, -plv->ptOrigin.y);
  516.     if (ListView_IsIconView(plv) || ListView_IsSmallView(plv)) {
  517.         //  don't do that funky half-re-origining thing.
  518.         RECT rc;
  519.         rc.left = 0;
  520.         rc.top = 0;
  521.         rc.bottom = rc.top + 1;
  522.         rc.right = rc.left + 1;
  523.         UnionRect(prcView, prcView, &rc);
  524.         rc.right = cx;
  525.         rc.bottom = cy;
  526.         rc.left = rc.right - 1;
  527.         rc.top = rc.bottom - 1;
  528.         UnionRect(prcView, prcView, &rc);
  529. #if 0
  530.         // if we're scrolled way in the positive area (plv->ptOrigin > 0), make sure we
  531.         // include our true origin
  532.         if ((prcView->left > -plv->ptOrigin.x))
  533.             prcView->left = -plv->ptOrigin.x;
  534.         if ((prcView->top > -plv->ptOrigin.y))
  535.             prcView->top = -plv->ptOrigin.y;
  536.         // if we're scrolled way in the positive area (plv->ptOrigin > 0),
  537.         // make sure our scrollbars include our current position
  538.         if ((prcView->right < (plv->sizeClient.cx)))
  539.             prcView->right = plv->sizeClient.cx;
  540.         if ((prcView->bottom < (plv->sizeClient.cy)))
  541.             prcView->bottom = plv->sizeClient.cy;
  542. #endif
  543.     }
  544. }
  545. // prcViewRect used only if fSubScroll is TRUE
  546. DWORD NEAR ListView_GetClientRect(LV* plv, RECT FAR* prcClient, BOOL fSubScroll, RECT FAR *prcViewRect)
  547. {
  548.     RECT rcClient;
  549.     RECT rcView;
  550.     DWORD style;
  551. #if 1
  552.     // do this instead of the #else below because
  553.     // in new versus old apps, you may need to add in g_c?Border because of
  554.     // the one pixel overlap...
  555.     GetWindowRect(plv->hwnd, &rcClient);
  556.     if (GetWindowLong(plv->hwnd, GWL_EXSTYLE) & (WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE)) {
  557.         rcClient.right -= 2 * g_cxEdge;
  558.         rcClient.bottom -= 2 * g_cyEdge;
  559.     }
  560.     rcClient.right -= rcClient.left;
  561.     rcClient.bottom -= rcClient.top;
  562.     if (rcClient.right < 0)
  563.         rcClient.right = 0;
  564.     if (rcClient.bottom < 0)
  565.         rcClient.bottom = 0;
  566.     rcClient.top = rcClient.left = 0;
  567. #else
  568.     style = GetWindowStyle(plv->hwnd);
  569.     GetClientRect(plv->hwnd, &rcClient);
  570.     if (style & WS_VSCROLL)
  571.         rcClient.right += g_cxScrollbar;
  572.     if (style & WS_HSCROLL)
  573.         rcClient.bottom += g_cyScrollbar;
  574. #endif
  575.     style = 0L;
  576.     if (fSubScroll)
  577.     {
  578.         ListView_GetViewRect2(plv, &rcView,
  579.                               rcClient.right - g_cxScrollbar,
  580.                               rcClient.bottom - g_cyScrollbar);
  581.         if ((rcClient.left < rcClient.right) && (rcClient.top < rcClient.bottom))
  582.         {
  583.             do
  584.             {
  585.                 if (rcView.left < rcClient.left || rcView.right > rcClient.right)
  586.                 {
  587.                     style |= WS_HSCROLL;
  588.                     rcClient.bottom -= g_cyScrollbar;
  589.                 }
  590.                 if (rcView.top < rcClient.top || rcView.bottom > rcClient.bottom)
  591.                 {
  592.                     style |= WS_VSCROLL;
  593.                     rcClient.right -= g_cxScrollbar;
  594.                 }
  595.             }
  596.             while (!(style & WS_HSCROLL) && rcView.right > rcClient.right);
  597.         }
  598.         if (prcViewRect)
  599.             *prcViewRect = rcView;
  600.     }
  601.     *prcClient = rcClient;
  602.     return style;
  603. }
  604. int CALLBACK ArrangeIconCompare(LISTITEM FAR* pitem1, LISTITEM FAR* pitem2, LPARAM lParam)
  605. {
  606.     int v1, v2;
  607.     if (HIWORD(lParam))
  608.     {
  609.         // Vertical arrange
  610.         v1 = pitem1->pt.x / (int)LOWORD(lParam);
  611.         v2 = pitem2->pt.x / (int)LOWORD(lParam);
  612.         if (v1 > v2)
  613.             return 1;
  614.         else if (v1 < v2)
  615.             return -1;
  616.         else
  617.         {
  618.             int y1 = pitem1->pt.y;
  619.             int y2 = pitem2->pt.y;
  620.             if (y1 > y2)
  621.                 return 1;
  622.             else if (y1 < y2)
  623.                 return -1;
  624.         }
  625.     }
  626.     else
  627.     {
  628.         v1 = pitem1->pt.y / (int)lParam;
  629.         v2 = pitem2->pt.y / (int)lParam;
  630.         if (v1 > v2)
  631.             return 1;
  632.         else if (v1 < v2)
  633.             return -1;
  634.         else
  635.         {
  636.             int x1 = pitem1->pt.x;
  637.             int x2 = pitem2->pt.x;
  638.             if (x1 > x2)
  639.                 return 1;
  640.             else if (x1 < x2)
  641.                 return -1;
  642.         }
  643.     }
  644.     return 0;
  645. }
  646. void NEAR PASCAL _ListView_GetRectsFromItem(LV* plv, BOOL bSmallIconView,
  647.                                             LISTITEM FAR *pitem,
  648.                                             LPRECT prcIcon, LPRECT prcLabel, LPRECT prcBounds, LPRECT prcSelectBounds)
  649. {
  650.     RECT rcIcon;
  651.     RECT rcLabel;
  652.     if (!prcIcon)
  653.         prcIcon = &rcIcon;
  654.     if (!prcLabel)
  655.         prcLabel = &rcLabel;
  656.     // Test for NULL item passed in
  657.     if (pitem)
  658.     {
  659.         // This routine is called during ListView_Recompute(), while
  660.         // plv->rcView.left may still be == RECOMPUTE.  So, we can't
  661.         // test that to see if recomputation is needed.
  662.         //
  663.         if (pitem->pt.y == RECOMPUTE || pitem->cyMultiLabel == SRECOMPUTE)
  664.             ListView_Recompute(plv);
  665.         if (bSmallIconView)
  666.             ListView_SGetRects(plv, pitem, prcIcon, prcLabel, prcBounds);
  667.         else
  668.             ListView_IGetRects(plv, pitem, prcIcon, prcLabel, prcBounds);
  669.         if (prcBounds) {
  670.             UnionRect(prcBounds, prcIcon, prcLabel);
  671.             if (plv->himlState && (LV_StateImageValue(pitem))) {
  672.                 prcBounds->left -= plv->cxState;
  673.             }
  674.         }
  675.     } else {
  676.         SetRectEmpty(prcIcon);
  677.         *prcLabel = *prcIcon;
  678.         if (prcBounds)
  679.             *prcBounds = *prcIcon;
  680.     }
  681.     if (prcSelectBounds)
  682.         UnionRect(prcSelectBounds, prcIcon, prcLabel);
  683. }
  684. void NEAR _ListView_InvalidateItemPtr(LV* plv, BOOL bSmallIcon, LISTITEM FAR *pitem, UINT fRedraw)
  685. {
  686.     RECT rcBounds;
  687.     _ListView_GetRectsFromItem(plv, bSmallIcon, pitem, NULL, NULL, &rcBounds, NULL);
  688.     RedrawWindow(plv->hwnd, &rcBounds, NULL, fRedraw);
  689. }
  690. // return TRUE if things still overlap
  691. // this only happens if we tried to unstack things, and there was NOSCROLL set and
  692. // items tried to go off the deep end
  693. BOOL NEAR PASCAL ListView_IUnstackOverlaps(LV* plv, HDPA hdpaSort, int iDirection)
  694. {
  695.     BOOL fRet = FALSE;
  696.     int i;
  697.     int iCount;
  698.     BOOL bSmallIconView;
  699.     RECT rcItem, rcItem2, rcTemp;
  700.     int cxItem, cyItem;
  701.     LISTITEM FAR* pitem;
  702.     LISTITEM FAR* pitem2;
  703.     if (bSmallIconView = ListView_IsSmallView(plv))
  704.     {
  705.         cxItem = plv->cxItem;
  706.         cyItem = plv->cyItem;
  707.     }
  708.     else
  709.     {
  710.         cxItem = lv_cxIconSpacing;
  711.         cyItem = lv_cyIconSpacing;
  712.     }
  713.     iCount = ListView_Count(plv);
  714.     // finally, unstack any overlaps
  715.     for (i = 0 ; i < iCount ; i++) {
  716.         int j;
  717.         pitem = DPA_GetPtr(hdpaSort, i);
  718.         if (bSmallIconView) {
  719.             _ListView_GetRectsFromItem(plv, bSmallIconView, pitem, NULL, NULL, &rcItem, NULL);
  720.         }
  721.         // move all the items that overlap with us
  722.         for (j = i+1 ; j < iCount; j++) {
  723.             POINT ptOldPos;
  724.             pitem2 = DPA_GetPtr(hdpaSort, j);
  725.             ptOldPos = pitem2->pt;
  726.             if (bSmallIconView) {
  727.                 // for small icons, we need to do an intersect rect
  728.                 _ListView_GetRectsFromItem(plv, bSmallIconView, pitem2, NULL, NULL, &rcItem2, NULL);
  729.                 if (IntersectRect(&rcTemp, &rcItem, &rcItem2)) {
  730.                     // yes, it intersects.  move it out
  731.                     _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
  732.                     do {
  733.                         pitem2->pt.x += (cxItem * iDirection);
  734.                     } while (PtInRect(&rcItem, pitem2->pt));
  735.                 } else {
  736.                     // no more intersect!
  737.                     break;
  738.                 }
  739.             } else {
  740.                 // for large icons, just find the ones that share the x,y;
  741.                 if (pitem2->pt.x == pitem->pt.x && pitem2->pt.y == pitem->pt.y) {
  742.                     _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
  743.                     pitem2->pt.x += (cxItem * iDirection);
  744.                 } else {
  745.                     // no more intersect!
  746.                     break;
  747.                 }
  748.             }
  749.             if (plv->style & LVS_NOSCROLL) {
  750.                 if (pitem2->pt.x < 0 || pitem2->pt.y < 0 ||
  751.                     pitem2->pt.x > (plv->sizeClient.cx - (cxItem/2))||
  752.                     pitem2->pt.y > (plv->sizeClient.cy - (cyItem/2))) {
  753.                     pitem2->pt = ptOldPos;
  754.                     fRet = TRUE;
  755.                 }
  756.             }
  757.             // invalidate the new position as well
  758.             _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
  759.         }
  760.     }
  761.     return fRet;
  762. }
  763. BOOL NEAR PASCAL ListView_SnapToGrid(LV* plv, HDPA hdpaSort)
  764. {
  765.     // this algorithm can't fit in the structure of the other
  766.     // arrange loop without becoming n^2 or worse.
  767.     // this algorithm is order n.
  768.     // iterate through and snap to the nearest grid.
  769.     // iterate through and push aside overlaps.
  770.     int i;
  771.     int iCount;
  772.     LPARAM  xySpacing;
  773.     int x,y;
  774.     LISTITEM FAR* pitem;
  775.     BOOL bSmallIconView;
  776.     int cxItem, cyItem;
  777.     if (bSmallIconView = ListView_IsSmallView(plv))
  778.     {
  779.         cxItem = plv->cxItem;
  780.         cyItem = plv->cyItem;
  781.     }
  782.     else
  783.     {
  784.         cxItem = lv_cxIconSpacing;
  785.         cyItem = lv_cyIconSpacing;
  786.     }
  787.     iCount = ListView_Count(plv);
  788.     // first snap to nearest grid
  789.     for (i = 0; i < iCount; i++) {
  790.         pitem = DPA_GetPtr(hdpaSort, i);
  791.         x = pitem->pt.x;
  792.         y = pitem->pt.y;
  793.         if (!bSmallIconView) {
  794.             x -= g_cxIconOffset;
  795.             y -= g_cyIconOffset;
  796.         }
  797.         NearestSlot(&x,&y, cxItem, cyItem);
  798.         if (!bSmallIconView) {
  799.             x += g_cxIconOffset;
  800.             y += g_cyIconOffset;
  801.         }
  802.         if (x != pitem->pt.x || y != pitem->pt.y) {
  803.             _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem, RDW_INVALIDATE| RDW_ERASE);
  804.             if (plv->style & LVS_NOSCROLL) {
  805.                 // if it's marked noscroll, make sure it's still on the client region
  806.                 while (x >= (plv->sizeClient.cx - (cxItem/2)))
  807.                     x -= cxItem;
  808.                 while (x < 0)
  809.                     x += cxItem;
  810.                 while (y >= (plv->sizeClient.cy - (cyItem/2)))
  811.                     y -= cyItem;
  812.                 while (y < 0)
  813.                     y += cyItem;
  814.             }
  815.             pitem->pt.x = x;
  816.             pitem->pt.y = y;
  817.             _ListView_InvalidateItemPtr(plv, bSmallIconView, pitem, RDW_INVALIDATE| RDW_ERASE);
  818.         }
  819.     }
  820.     // now resort the dpa
  821.     switch (plv->style & LVS_ALIGNMASK)
  822.     {
  823.         case LVS_ALIGNLEFT:
  824.         case LVS_ALIGNRIGHT:
  825.             xySpacing = MAKELONG(bSmallIconView ? plv->cxItem : lv_cxIconSpacing, TRUE);
  826.             break;
  827.         default:
  828.             xySpacing = MAKELONG(bSmallIconView ? plv->cyItem : lv_cyIconSpacing, FALSE);
  829.     }
  830.     if (!DPA_Sort(hdpaSort, ArrangeIconCompare, xySpacing))
  831.         return FALSE;
  832.     // go in one direction, if there are still overlaps, go in the other
  833.     // direction as well
  834.     if (ListView_IUnstackOverlaps(plv, hdpaSort, 1))
  835.         ListView_IUnstackOverlaps(plv, hdpaSort, -1);
  836.     return FALSE;
  837. }
  838. BOOL NEAR ListView_OnArrange(LV* plv, UINT style)
  839. {
  840.     BOOL bSmallIconView;
  841.     LPARAM  xySpacing;
  842.     HDPA hdpaSort;
  843.     bSmallIconView = ListView_IsSmallView(plv);
  844.     if (!bSmallIconView && !ListView_IsIconView(plv)) {
  845.         return FALSE;
  846.     }
  847.     // Make sure our items have positions and their text rectangles
  848.     // caluculated
  849.     if (plv->rcView.left == RECOMPUTE)
  850.         ListView_Recompute(plv);
  851.     // we clone plv->hdpa so we don't blow away indices that
  852.     // apps have saved away.
  853.     // we sort here to make the nested for loop below more bearable.
  854.     hdpaSort = DPA_Clone(plv->hdpa, NULL);
  855.     if (!hdpaSort)
  856.         return FALSE;
  857.     switch (plv->style & LVS_ALIGNMASK)
  858.     {
  859.         case LVS_ALIGNLEFT:
  860.         case LVS_ALIGNRIGHT:
  861.             xySpacing = MAKELONG(bSmallIconView ? plv->cxItem : lv_cxIconSpacing, TRUE);
  862.             break;
  863.         default:
  864.             xySpacing = MAKELONG(bSmallIconView ? plv->cyItem : lv_cyIconSpacing, FALSE);
  865.     }
  866.     if (!DPA_Sort(hdpaSort, ArrangeIconCompare, xySpacing))
  867.         return FALSE;
  868.     ListView_CommonArrange(plv, style, hdpaSort);
  869.     DPA_Destroy(hdpaSort);
  870. }
  871. // this arranges the icon given a sorted hdpa.
  872. BOOL NEAR ListView_CommonArrange(LV* plv, UINT style, HDPA hdpaSort)
  873. {
  874.     int iSlot;
  875.     int iItem;
  876.     int cSlots;
  877.     BOOL fItemMoved;
  878.     RECT rcLastItem;
  879.     RECT rcSlot;
  880.     RECT rcT;
  881.     BOOL bSmallIconView;
  882.     int  xMin = 0;
  883.     bSmallIconView = ListView_IsSmallView(plv);
  884.     // REVIEW, this causes a repaint if we are scrollled
  885.     // we can probably avoid this some how
  886.     fItemMoved = (plv->ptOrigin.x != 0) || (plv->ptOrigin.y != 0);
  887.     plv->ptOrigin.x = 0;
  888.     plv->ptOrigin.y = 0;
  889.     if (style == LVA_SNAPTOGRID) {
  890.         fItemMoved |= ListView_SnapToGrid(plv, hdpaSort);
  891.     } else {
  892.         cSlots = ListView_GetSlotCount(plv, TRUE);
  893.         SetRectEmpty(&rcLastItem);
  894.         // manipulate only the sorted version of the item list below!
  895.         iSlot = 0;
  896.         for (iItem = 0; iItem < ListView_Count(plv); iItem++)
  897.         {
  898.             LISTITEM FAR* pitem = DPA_GetPtr(hdpaSort, iItem);
  899.             if (bSmallIconView)
  900.             {
  901.                 // BUGBUG:: Only check for Small view...  See if this gets the
  902.                 // results people expect?
  903.                 for ( ; ; )
  904.                 {
  905.                     _CalcSlotRect(plv, iSlot, cSlots, FALSE, &rcSlot);
  906.                     if (!IntersectRect(&rcT, &rcSlot, &rcLastItem))
  907.                         break;
  908.                     iSlot++;
  909.                 }
  910.             }
  911.             fItemMoved |= ListView_SetIconPos(plv, pitem, iSlot++, cSlots);
  912.             // do this instead of ListView_GetRects() because we need
  913.             // to use the pitem from the sorted hdpa, not the ones in *plv
  914.             _ListView_GetRectsFromItem(plv, bSmallIconView, pitem, NULL, NULL, &rcLastItem, NULL);
  915.             //
  916.             // Keep track of the minimum x as we don't want negative values
  917.             // when we finish.
  918.             if (rcLastItem.left < xMin)
  919.                 xMin = rcLastItem.left;
  920.         }
  921.         //
  922.         // See if we need to scroll the items over to make sure that all of the
  923.         // no items are hanging off the left hand side.
  924.         //
  925.         if (xMin < 0)
  926.         {
  927.             for (iItem = 0; iItem < ListView_Count(plv); iItem++)
  928.             {
  929.                 LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, iItem);
  930.                 pitem->pt.x -= xMin;        // scroll them over
  931.             }
  932.             plv->rcView.left = RECOMPUTE;   // need to recompute.
  933.             fItemMoved = TRUE;
  934.         }
  935.     }
  936.     //
  937.     // We might as well invalidate the entire window to make sure...
  938.     if (fItemMoved) {
  939.         if (ListView_RedrawEnabled(plv))
  940.             RedrawWindow(plv->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  941.         else {
  942.             ListView_DeleteHrgnInval(plv);
  943.             plv->hrgnInval = (HRGN)ENTIRE_REGION;
  944.             plv->flags |= LVF_ERASE;
  945.         }
  946.         // ensure important items are visible
  947.         iItem = (plv->iFocus >= 0) ? plv->iFocus : ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
  948.         if (iItem >= 0)
  949.             ListView_OnEnsureVisible(plv, iItem, FALSE);
  950.         if (ListView_RedrawEnabled(plv))
  951.             ListView_UpdateScrollBars(plv);
  952.     }
  953.     return TRUE;
  954. }
  955. void NEAR ListView_IUpdateScrollBars(LV* plv)
  956. {
  957.     RECT rcClient;
  958.     RECT rcView;
  959.     DWORD style;
  960.     DWORD styleOld;
  961.     SCROLLINFO si;
  962.     styleOld = GetWindowStyle(plv->hwnd);
  963.     style = ListView_GetClientRect(plv, &rcClient, TRUE, &rcView);
  964.     //DebugMsg(DM_TRACE, "ListView_GetClientRect %x %x %x %x", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
  965.     //DebugMsg(DM_TRACE, "ListView_GetViewRect2 %x %x %x %x", rcView.left, rcView.top, rcView.right, rcView.bottom);
  966.     //DebugMsg(DM_TRACE, "rcView %x %x %x %x", plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
  967.     //DebugMsg(DM_TRACE, "Origin %x %x", plv->ptOrigin.x, plv->ptOrigin.y);
  968.     si.cbSize = sizeof(SCROLLINFO);
  969.     if (style & WS_HSCROLL)
  970.     {
  971.         si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  972.         si.nMin = 0;
  973.         si.nMax = rcView.right - rcView.left - 1;
  974.         //DebugMsg(DM_TRACE, "si.nMax rcView.right - rcView.left - 1 %x", si.nMax);
  975.         si.nPage = min(rcClient.right, rcView.right) - rcClient.left;
  976.         //DebugMsg(DM_TRACE, "si.nPage %x", si.nPage);
  977.         si.nPos = 0;
  978.         if (rcView.left < rcClient.left)
  979.             si.nPos = rcClient.left - rcView.left;
  980.         //DebugMsg(DM_TRACE, "si.nPos %x", si.nPos);
  981. #ifdef IEWIN31_25
  982.         plv->cxScrollPage = (int)si.nPage;
  983.         SetScrollRange( plv->hwnd, SB_HORZ, (int) si.nMin,
  984.                            max( (int)(si.nMax-si.nPage+1), 0 ), FALSE );
  985.         SetScrollPos( plv->hwnd, SB_HORZ, (int) si.nPos, TRUE );
  986. #else
  987.         SetScrollInfo(plv->hwnd, SB_HORZ, &si, TRUE);
  988. #endif //IEWIN31_25
  989.     }
  990.     else if (styleOld & WS_HSCROLL)
  991.     {
  992.         SetScrollRange(plv->hwnd, SB_HORZ, 0, 0, TRUE);
  993.     }
  994.     if (style & WS_VSCROLL)
  995.     {
  996.         si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  997.         si.nMin = 0;
  998.         si.nMax = rcView.bottom - rcView.top - 1;
  999.         si.nPage = min(rcClient.bottom, rcView.bottom) - rcClient.top;
  1000.         si.nPos = 0;
  1001.         if (rcView.top < rcClient.top)
  1002.             si.nPos = rcClient.top - rcView.top;
  1003. #ifdef IEWIN31_25
  1004.         plv->cyScrollPage = (int)si.nPage;
  1005.         SetScrollRange( plv->hwnd, SB_VERT, (int) si.nMin,
  1006.                            max( (int)(si.nMax-si.nPage+1), 0 ), FALSE );
  1007.         SetScrollPos( plv->hwnd, SB_VERT, (int) si.nPos, TRUE );
  1008. #else
  1009.         SetScrollInfo(plv->hwnd, SB_VERT, &si, TRUE);
  1010. #endif //IEWIN31_25
  1011.     }
  1012.     else if (styleOld & WS_VSCROLL)
  1013.     {
  1014.         SetScrollRange(plv->hwnd, SB_VERT, 0, 0, TRUE);
  1015.     }
  1016. }
  1017. void FAR PASCAL ListView_ComOnScroll(LV* plv, UINT code, int posNew, int sb,
  1018.                                      int cLine, int cPage,
  1019.                                      SCROLLPROC lpfnScroll2)
  1020. {
  1021.     int pos;
  1022.     SCROLLINFO si;
  1023.     BOOL fVert = (sb == SB_VERT);
  1024. #ifdef IEWIN31_25
  1025.     {
  1026.         int   n1, n2;
  1027.         GetScrollRange( plv->hwnd, sb, &n1, &n2 );
  1028.         si.nMin = n1;
  1029.         si.nMax = n2;
  1030.         si.nPos = GetScrollPos( plv->hwnd, sb );
  1031.         si.nPage = fVert ? plv->cyScrollPage : plv->cxScrollPage;
  1032.     }
  1033. #else
  1034.     si.cbSize = sizeof(SCROLLINFO);
  1035.     si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
  1036.     if (!GetScrollInfo(plv->hwnd, sb, &si)) {
  1037.         return;
  1038.     }
  1039. #endif //IEWIN31_25
  1040.     if (cPage != -1)
  1041.         si.nPage = cPage;
  1042.     si.nMax -= (si.nPage - 1);
  1043.     if (si.nMax < si.nMin)
  1044.         si.nMax = si.nMin;
  1045.     pos = (int)si.nPos; // current position
  1046.     switch (code)
  1047.     {
  1048.     case SB_LEFT:
  1049.         si.nPos = si.nMin;
  1050.         break;
  1051.     case SB_RIGHT:
  1052.         si.nPos = si.nMax;
  1053.         break;
  1054.     case SB_PAGELEFT:
  1055.          si.nPos -= si.nPage;
  1056.         break;
  1057.     case SB_LINELEFT:
  1058.         si.nPos -= cLine;
  1059.         break;
  1060.     case SB_PAGERIGHT:
  1061.         si.nPos += si.nPage;
  1062.         break;
  1063.     case SB_LINERIGHT:
  1064.         si.nPos += cLine;
  1065.         break;
  1066.     case SB_THUMBTRACK:
  1067.         si.nPos = posNew;
  1068.         break;
  1069.     case SB_ENDSCROLL:
  1070.         // When scroll bar tracking is over, ensure scroll bars
  1071.         // are properly updated...
  1072.         //
  1073.         ListView_UpdateScrollBars(plv);
  1074.         return;
  1075.     default:
  1076.         return;
  1077.     }
  1078.     si.fMask = SIF_POS;
  1079. #ifdef IEWIN31_25
  1080.     SetScrollPos( plv->hwnd, sb, (int) si.nPos, TRUE );
  1081.     si.nPos = GetScrollPos( plv->hwnd, sb );
  1082. #else
  1083.     si.nPos = SetScrollInfo(plv->hwnd, sb, &si, TRUE);
  1084. #endif
  1085.     if (pos != si.nPos)
  1086.     {
  1087.         int delta = (int)si.nPos - pos;
  1088.         int dx = 0, dy = 0;
  1089.         if (fVert)
  1090.             dy = delta;
  1091.         else
  1092.             dx = delta;
  1093.         lpfnScroll2(plv, dx, dy);
  1094.         UpdateWindow(plv->hwnd);
  1095.     }
  1096. }
  1097. void FAR PASCAL ListView_IScroll2(LV* plv, int dx, int dy)
  1098. {
  1099.     if (dx | dy)
  1100.     {
  1101.         plv->ptOrigin.x += dx;
  1102.         plv->ptOrigin.y += dy;
  1103.         ScrollWindowEx(plv->hwnd, -dx, -dy, NULL, NULL, NULL, NULL,
  1104.                 SW_INVALIDATE | SW_ERASE);
  1105.     }
  1106. }
  1107. void NEAR ListView_IOnScroll(LV* plv, UINT code, int posNew, int sb)
  1108. {
  1109.     int cLine;
  1110.     if (sb == SB_VERT)
  1111.     {
  1112.         cLine = lv_cyIconSpacing / 2;
  1113.     }
  1114.     else
  1115.     {
  1116.         cLine = lv_cxIconSpacing / 2;
  1117.     }
  1118.     ListView_ComOnScroll(plv, code,  posNew,  sb,
  1119.                          cLine, -1,
  1120.                          ListView_IScroll2);
  1121. }
  1122. // NOTE: there is very similar code in the treeview
  1123. //
  1124. // Totally disgusting hack in order to catch VK_RETURN
  1125. // before edit control gets it.
  1126. //
  1127. LRESULT CALLBACK ListView_EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1128. {
  1129.     LV* plv = ListView_GetPtr(GetParent(hwnd));
  1130.     Assert(plv);
  1131. #ifdef FE_IME
  1132.     if ( LOWORD(GetKeyboardLayout(0L)) == 0x0411 )
  1133.     {
  1134.         // The following code adds IME awareness to the
  1135.         // listview's label editing. Currently just for Japanese.
  1136.         // 
  1137.         DWORD dwGcs;
  1138.         if (msg==WM_SIZE)
  1139.         {
  1140.             // If it's given the size, tell it to an IME.
  1141.  
  1142.              ListView_SizeIME(hwnd);
  1143.         }
  1144.         else if (msg == EM_SETLIMITTEXT )
  1145.         {
  1146.            if (wParam < 13)
  1147.                plv->flags |= LVF_DONTDRAWCOMP; 
  1148.            else
  1149.                plv->flags &= ~LVF_DONTDRAWCOMP;
  1150.         }
  1151.         // Give up to draw IME composition by ourselves in case
  1152.         // we're working on SFN. Win95d-5709
  1153.         else if (!(plv->flags & LVF_DONTDRAWCOMP ))
  1154.         { 
  1155.             switch (msg)
  1156.             {
  1157.              case WM_IME_STARTCOMPOSITION:
  1158.              case WM_IME_ENDCOMPOSITION:
  1159.                  return 0L;
  1160.              case WM_IME_COMPOSITION:
  1161.              // If lParam has no data available bit, it implies 
  1162.              // canceling composition.
  1163.              // ListView_InsertComposition() tries to get composition
  1164.              // string w/ GCS_COMPSTR then remove it from edit control if
  1165.              // nothing is available.
  1166.              //
  1167.                  if ( !lParam )
  1168.                      dwGcs = GCS_COMPSTR;
  1169.                  else
  1170.                      dwGcs = lParam;
  1171.                  ListView_InsertComposition(hwnd, wParam, dwGcs, plv);
  1172.                  ListView_PaintComposition(hwnd,plv);
  1173.                  return 0L;
  1174.              case WM_IME_SETCONTEXT:
  1175.              // We draw composition string.
  1176.              //
  1177.                  lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
  1178.                  break;
  1179.              default:
  1180.                  // the other messages should simply be processed
  1181.                  // in this subclass procedure.
  1182.                  break;
  1183.             }
  1184.         }
  1185.     }
  1186. #endif
  1187.     switch (msg)
  1188.     {
  1189.     case WM_SETTEXT:
  1190.         SetWindowID(hwnd, 1);
  1191.         break;
  1192.     case WM_KEYDOWN:
  1193.         switch (wParam)
  1194.         {
  1195.         case VK_RETURN:
  1196.             ListView_DismissEdit(plv, FALSE);
  1197.             return 0L;
  1198.         case VK_ESCAPE:
  1199.             ListView_DismissEdit(plv, TRUE);
  1200.             return 0L;
  1201.         }
  1202.         break;
  1203.     case WM_CHAR:
  1204.         switch (wParam)
  1205.         {
  1206.         case VK_RETURN:
  1207.             // Eat the character, so edit control wont beep!
  1208.             return 0L;
  1209.         }
  1210. break;
  1211. case WM_GETDLGCODE:
  1212. return DLGC_WANTALLKEYS; /* editing name, no dialog handling right now */
  1213.     }
  1214.     return CallWindowProc(plv->pfnEditWndProc, hwnd, msg, wParam, lParam);
  1215. }
  1216. // BUGBUG: very similar routine in treeview
  1217. void NEAR ListView_SetEditSize(LV* plv)
  1218. {
  1219.     RECT rcLabel;
  1220.     LISTITEM FAR* pitem;
  1221.     pitem = ListView_GetItemPtr(plv, plv->iEdit);
  1222.     if (!pitem)
  1223.     {
  1224.         ListView_DismissEdit(plv, TRUE);    // cancel edits
  1225.         return;
  1226.     }
  1227.     ListView_GetRects(plv, plv->iEdit, NULL, &rcLabel, NULL, NULL);
  1228.     // OffsetRect(&rc, rcLabel.left + g_cxLabelMargin + g_cxBorder,
  1229.     //         (rcLabel.bottom + rcLabel.top - rc.bottom) / 2 + g_cyBorder);
  1230.     // OffsetRect(&rc, rcLabel.left + g_cxLabelMargin , rcLabel.top);
  1231.     // get the text bounding rect
  1232.     if (ListView_IsIconView(plv))
  1233.     {
  1234. // We should not adjust y-positoin in case of the icon view.
  1235. InflateRect(&rcLabel, -g_cxLabelMargin, -g_cyBorder);
  1236.     }
  1237.     else
  1238.     {
  1239. // Special case for single-line & centered
  1240. InflateRect(&rcLabel, -g_cxLabelMargin - g_cxBorder, (-(rcLabel.bottom - rcLabel.top - plv->cyLabelChar) / 2) - g_cyBorder);
  1241.     }
  1242.     SetEditInPlaceSize(plv->hwndEdit, &rcLabel, plv->hfontLabel, ListView_IsIconView(plv) && !(plv->style & LVS_NOLABELWRAP));
  1243. }
  1244. // to avoid eating too much stack
  1245. void NEAR ListView_DoOnEditLabel(LV *plv, int i, LPSTR pszInitial)
  1246. {
  1247.     char szLabel[CCHLABELMAX];
  1248.     LV_ITEM item;
  1249.     item.mask = LVIF_TEXT;
  1250.     item.iItem = i;
  1251.     item.iSubItem = 0;
  1252.     item.pszText = szLabel;
  1253.     item.cchTextMax = sizeof(szLabel);
  1254.     ListView_OnGetItem(plv, &item);
  1255.     if (!item.pszText)
  1256.         return;
  1257.     // Make sure the edited item has the focus.
  1258.     if (plv->iFocus != i)
  1259.         ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
  1260.     // Make sure the item is fully visible
  1261.     ListView_OnEnsureVisible(plv, i, FALSE);        // fPartialOK == FALSE
  1262.     plv->hwndEdit = CreateEditInPlaceWindow(plv->hwnd,
  1263.             pszInitial? pszInitial : item.pszText, sizeof(szLabel),
  1264.         ListView_IsIconView(plv) ?
  1265.             (WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD | ES_CENTER | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL) :
  1266.             (WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD | ES_LEFT | ES_AUTOHSCROLL), plv->hfontLabel);
  1267.     if (plv->hwndEdit)
  1268.     {
  1269.         LISTITEM FAR* pitem;
  1270.         LV_DISPINFO nm;
  1271.         // We create the edit window but have not shown it.  Ask the owner
  1272.         // if they are interested or not.
  1273.         // If we passed in initial text set the ID to be dirty...
  1274.         if (pszInitial)
  1275.             SetWindowID(plv->hwndEdit, 1);
  1276.         if (!(pitem = ListView_GetItemPtr(plv, i)))
  1277.         {
  1278.             DestroyWindow(plv->hwndEdit);
  1279.             plv->hwndEdit = NULL;
  1280.             return;
  1281.         }
  1282.         nm.item.iItem = i;
  1283.         nm.item.iSubItem = 0;
  1284.         nm.item.lParam = pitem->lParam;
  1285.         // if they have LVS_EDITLABELS but return non-FALSE here, stop!
  1286.         if ((BOOL)SendNotify(plv->hwndParent, plv->hwnd, LVN_BEGINLABELEDIT, &nm.hdr))
  1287.         {
  1288.             DestroyWindow(plv->hwndEdit);
  1289.             plv->hwndEdit = NULL;
  1290.         }
  1291.         else
  1292.         {
  1293.             // Ok To continue - so Show the window and set focus to it.
  1294.             SetFocus(plv->hwndEdit);
  1295.             ShowWindow(plv->hwndEdit, SW_SHOW);
  1296.         }
  1297.     }
  1298. }
  1299. void FAR PASCAL RescrollEditWindow(HWND hwndEdit)
  1300. {
  1301.     Edit_SetSel(hwndEdit, 32000, 32000); // move to the end
  1302.     Edit_SetSel(hwndEdit, 0, 32000); // select all text
  1303. }
  1304. // BUGBUG: very similar code in treeview.c
  1305. HWND NEAR ListView_OnEditLabel(LV* plv, int i, LPSTR pszInitialText)
  1306. {
  1307.     // this eats stack
  1308.     ListView_DismissEdit(plv, FALSE);
  1309.     if (!(plv->style & LVS_EDITLABELS) || (GetFocus() != plv->hwnd) ||
  1310.         (i == -1))
  1311.         return(NULL);   // Does not support this.
  1312.     ListView_DoOnEditLabel(plv, i, pszInitialText);
  1313.     if (plv->hwndEdit) {
  1314.         plv->iEdit = i;
  1315.         plv->pfnEditWndProc = SubclassWindow(plv->hwndEdit, ListView_EditWndProc);
  1316. #ifdef FE_IME
  1317.         if (SendMessage(plv->hwndEdit, EM_GETLIMITTEXT, (WPARAM)0, (LPARAM)0)<13)
  1318.         {
  1319.             plv->flags |= LVF_DONTDRAWCOMP; 
  1320.         }
  1321. #endif
  1322.         ListView_SetEditSize(plv);
  1323.         RescrollEditWindow(plv->hwndEdit);
  1324.     }
  1325.     return plv->hwndEdit;
  1326. }
  1327. // BUGBUG: very similar code in treeview.c
  1328. BOOL NEAR ListView_DismissEdit(LV* plv, BOOL fCancel)
  1329. {
  1330.     LISTITEM FAR* pitem;
  1331.     BOOL fOkToContinue = TRUE;
  1332.     HWND hwndEdit = plv->hwndEdit;
  1333.     HWND hwnd = plv->hwnd;
  1334.     int iEdit;
  1335. #ifdef FE_IME
  1336.     HIMC himc;
  1337. #endif
  1338.     if (plv->fNoDismissEdit)
  1339.         return FALSE;
  1340.     if (!hwndEdit) {
  1341.         // Also make sure there are no pending edits...
  1342.         ListView_CancelPendingEdit(plv);
  1343.         return TRUE;    // It is OK to process as normal...
  1344.     }
  1345.     // If the window is not visible, we are probably in the process
  1346.     // of being destroyed, so assume that we are being destroyed
  1347.     if (!IsWindowVisible(plv->hwnd))
  1348.         fCancel = TRUE;
  1349.     //
  1350.     // We are using the Window ID of the control as a BOOL to
  1351.     // state if it is dirty or not.
  1352.     switch (GetWindowID(hwndEdit)) {
  1353.     case 0:
  1354.         // The edit control is not dirty so act like cancel.
  1355.         fCancel = TRUE;
  1356.         // Fall through to set window so we will not recurse!
  1357.     case 1:
  1358.         // The edit control is dirty so continue.
  1359.         SetWindowID(hwndEdit, 2);    // Don't recurse
  1360.         break;
  1361.     case 2:
  1362.         // We are in the process of processing an update now, bail out
  1363.         return TRUE;
  1364.     }
  1365.     // BUGBUG: this will fail if the program deleted the items out
  1366.     // from underneath us (while we are waiting for the edit timer).
  1367.     // make delete item invalidate our edit item
  1368.     // We uncouple the edit control and hwnd out from under this as
  1369.     // to allow code that process the LVN_ENDLABELEDIT to reenter
  1370.     // editing mode if an error happens.
  1371.     iEdit = plv->iEdit;
  1372.     pitem = ListView_GetItemPtr(plv, iEdit);
  1373.     Assert(pitem);
  1374.     if (pitem != NULL)
  1375.     {
  1376.         LV_DISPINFO nm;
  1377.         char szLabel[CCHLABELMAX];
  1378.         nm.item.iItem = iEdit;
  1379.         nm.item.lParam = pitem->lParam;
  1380.         nm.item.iSubItem = 0;
  1381.         nm.item.cchTextMax = 0;
  1382. nm.item.mask = 0;
  1383.         if (fCancel)
  1384.             nm.item.pszText = NULL;
  1385.         else {
  1386.             Edit_GetText(hwndEdit, szLabel, sizeof(szLabel));
  1387.             nm.item.pszText = szLabel;
  1388.         }
  1389.         //
  1390.         // Notify the parent that we the label editing has completed.
  1391.         // We will use the LV_DISPINFO structure to return the new
  1392.         // label in.  The parent still has the old text available by
  1393.         // calling the GetItemText function.
  1394.         //
  1395.         fOkToContinue = (BOOL)SendNotify(plv->hwndParent, plv->hwnd, LVN_ENDLABELEDIT, &nm.hdr);
  1396.         if (!IsWindow(hwnd)) {
  1397.             return FALSE;
  1398.         }
  1399.         if (fOkToContinue && !fCancel)
  1400.         {
  1401.             //
  1402.             // If the item has the text set as CALLBACK, we will let the
  1403.             // ower know that they are supposed to set the item text in
  1404.             // their own data structures.  Else we will simply update the
  1405.             // text in the actual view.
  1406.             //
  1407.             if (pitem->pszText != LPSTR_TEXTCALLBACK)
  1408.             {
  1409.                 // Set the item text (everything's set up in nm.item)
  1410.                 //
  1411. nm.item.mask = LVIF_TEXT;
  1412.                 ListView_OnSetItem(plv, &nm.item);
  1413.             }
  1414.             else
  1415.             {
  1416.                 SendNotify(plv->hwndParent, plv->hwnd, LVN_SETDISPINFO, &nm.hdr);
  1417.                 // Also we will assume that our cached size is invalid...
  1418.                 plv->rcView.left = RECOMPUTE;
  1419.                 pitem->cyMultiLabel = pitem->cxSingleLabel = pitem->cxMultiLabel = SRECOMPUTE;
  1420.             }
  1421.         }
  1422. #ifdef FE_IME
  1423.         if (LOWORD(GetKeyboardLayout(0L)) == 0x0411 && (himc = ImmGetContext(hwndEdit)))
  1424.         {
  1425.             ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0L);
  1426.             ImmReleaseContext(hwndEdit, himc);
  1427.         }
  1428. #endif
  1429.         // redraw
  1430.         ListView_InvalidateItem(plv, iEdit, FALSE, RDW_INVALIDATE | RDW_ERASE);
  1431.     }
  1432.     // If the hwnedit is still us clear out the variables
  1433.     if (hwndEdit == plv->hwndEdit)
  1434.     {
  1435.         plv->iEdit = -1;
  1436.         plv->hwndEdit = NULL; // avoid being reentered
  1437.     }
  1438.     DestroyWindow(hwndEdit);
  1439.     return fOkToContinue;
  1440. }
  1441. //
  1442. // This function will scall the icon positions that are stored in the
  1443. // item structures between large and small icon view.
  1444. //
  1445. void NEAR ListView_ScaleIconPositions(LV* plv, BOOL fSmallIconView)
  1446. {
  1447.     int cxItem, cyItem;
  1448.     HWND hwnd;
  1449.     int i;
  1450.     cxItem = plv->cxItem;
  1451.     cyItem = plv->cyItem;
  1452.     hwnd = plv->hwnd;
  1453.     if (fSmallIconView)
  1454.     {
  1455.         if (plv->flags & LVF_ICONPOSSML)
  1456.             return;     // Already done
  1457.     }
  1458.     else
  1459.     {
  1460.         if ((plv->flags & LVF_ICONPOSSML) == 0)
  1461.             return;     // dito
  1462.     }
  1463.     // Last but not least update our bit!
  1464.     plv->flags ^= LVF_ICONPOSSML;
  1465.     // We will now loop through all of the items and update their coordinats
  1466.     // We will update th position directly into the view instead of calling
  1467.     // SetItemPosition as to not do 5000 invalidates and messages...
  1468.     for (i = 0; i < ListView_Count(plv); i++)
  1469.     {
  1470.         LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, i);
  1471.         if (fSmallIconView)
  1472.         {
  1473.             if (pitem->pt.y != RECOMPUTE) {
  1474.                 pitem->pt.x = MulDiv(pitem->pt.x - g_cxIconOffset, cxItem, lv_cxIconSpacing);
  1475.                 pitem->pt.y = MulDiv(pitem->pt.y - g_cyIconOffset, cyItem, lv_cyIconSpacing);
  1476.             }
  1477.         }
  1478.         else
  1479.         {
  1480.             pitem->pt.x = MulDiv(pitem->pt.x, lv_cxIconSpacing, cxItem) + g_cxIconOffset;
  1481.             pitem->pt.y = MulDiv(pitem->pt.y, lv_cyIconSpacing, cyItem) + g_cyIconOffset;
  1482.         }
  1483.     }
  1484.     if (plv->style & LVS_AUTOARRANGE)
  1485.     {
  1486.         // If autoarrange is turned on, the arrange function will do
  1487.         // everything that is needed.
  1488.         ListView_OnArrange(plv, LVA_DEFAULT);
  1489.         return;
  1490.     }
  1491.     plv->rcView.left = RECOMPUTE;
  1492.     //
  1493.     // Also scale the origin
  1494.     //
  1495.     if (fSmallIconView)
  1496.     {
  1497.         plv->ptOrigin.x = MulDiv(plv->ptOrigin.x, cxItem, lv_cxIconSpacing);
  1498.         plv->ptOrigin.y = MulDiv(plv->ptOrigin.y, cyItem, lv_cyIconSpacing);
  1499.     }
  1500.     else
  1501.     {
  1502.         plv->ptOrigin.x = MulDiv(plv->ptOrigin.x, lv_cxIconSpacing, cxItem);
  1503.         plv->ptOrigin.y = MulDiv(plv->ptOrigin.y, lv_cyIconSpacing, cyItem);
  1504.     }
  1505.     // Make sure it fully redraws correctly
  1506.     RedrawWindow(plv->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
  1507. }
  1508. HWND FAR PASCAL CreateEditInPlaceWindow(HWND hwnd, LPCSTR lpText, int cbText, LONG style, HFONT hFont)
  1509. {
  1510.     HWND hwndEdit;
  1511.     hwndEdit = CreateWindowEx(0 /* WS_EX_CLIENTEDGE */, "EDIT", lpText, style,
  1512.             0, 0, 0, 0, hwnd, NULL, HINST_THISDLL, NULL);
  1513.     if (hwndEdit) {
  1514.         Edit_LimitText(hwndEdit, cbText);
  1515.         Edit_SetSel(hwndEdit, 0, 0); // move to the beginning
  1516.         FORWARD_WM_SETFONT(hwndEdit, hFont, FALSE, SendMessage);
  1517.     }
  1518.     return hwndEdit;
  1519. }
  1520. // BUGBUG: very similar routine in treeview
  1521. // in:
  1522. //      hwndEdit        edit control to position in client coords of parent window
  1523. //      prc             bonding rect of the text, used to position everthing
  1524. //      hFont           font being used
  1525. //      fWrap           if this is a wrapped type edit
  1526. //
  1527. // Notes:
  1528. //       The top-left corner of the bouding rectangle must be the position
  1529. //      the client uses to draw text. We adjust the edit field rectangle
  1530. //      appropriately.
  1531. //
  1532. void FAR PASCAL SetEditInPlaceSize(HWND hwndEdit, RECT FAR *prc, HFONT hFont, BOOL fWrap)
  1533. {
  1534.     RECT rc, rcClient, rcFormat;
  1535.     char szLabel[CCHLABELMAX + 1];
  1536.     int cchLabel, cxIconTextWidth;
  1537.     HDC hdc;
  1538.     HWND hwndParent = GetParent(hwndEdit);
  1539. #ifdef DBCS
  1540.     short wRightMgn;
  1541. #endif
  1542.     cchLabel = Edit_GetText(hwndEdit, szLabel, sizeof(szLabel));
  1543.     if (szLabel[0] == 0)
  1544.     {
  1545.         lstrcpy(szLabel, c_szSpace);
  1546.         cchLabel = 1;
  1547.     }
  1548.     hdc = GetDC(hwndParent);
  1549. #ifdef DEBUG
  1550.     //DrawFocusRect(hdc, prc);       // this is the rect they are passing in
  1551. #endif
  1552.     SelectFont(hdc, hFont);
  1553.     cxIconTextWidth = g_cxIconSpacing - g_cxLabelMargin * 2;
  1554.     rc.left = rc.top = rc.bottom = 0;
  1555.     rc.right = cxIconTextWidth;      // for DT_LVWRAP
  1556.     // REVIEW: we might want to include DT_EDITCONTROL in our DT_LVWRAP
  1557.     // If the string is NULL display a rectangle that is visible.
  1558.     DrawText(hdc, szLabel, cchLabel, &rc, fWrap ? (DT_LVWRAP | DT_CALCRECT) : (DT_LV | DT_CALCRECT));
  1559.     // Minimum text box size is 1/4 icon spacing size
  1560.     if (rc.right < g_cxIconSpacing / 4)
  1561.         rc.right = g_cxIconSpacing / 4;
  1562.     // position the text rect based on the text rect passed in
  1563.     // if wrapping, center the edit control around the text mid point
  1564.     OffsetRect(&rc,
  1565.         fWrap ? prc->left + ((prc->right - prc->left) - (rc.right - rc.left)) / 2 : prc->left,
  1566.         fWrap ? prc->top : prc->top +  ((prc->bottom - prc->top) - (rc.bottom - rc.top)) / 2 );
  1567.     // give a little space to ease the editing of this thing
  1568.     if (!fWrap)
  1569.         rc.right += g_cxLabelMargin * 4;
  1570. #ifdef DEBUG
  1571.     //DrawFocusRect(hdc, &rc);
  1572. #endif
  1573.     ReleaseDC(hwndParent, hdc);
  1574.     //
  1575.     // #5688: We need to make it sure that the whole edit window is
  1576.     //  always visible. We should not extend it to the outside of
  1577.     //  the parent window.
  1578.     //
  1579.     {
  1580.         BOOL fSuccess;
  1581.         GetClientRect(hwndParent, &rcClient);
  1582.         fSuccess = IntersectRect(&rc, &rc, &rcClient);
  1583.         Assert(fSuccess || IsRectEmpty(&rcClient));
  1584.     }
  1585.     //
  1586.     // Inflate it after the clipping, because it's ok to hide border.
  1587.     //
  1588.     SendMessage(hwndEdit, EM_GETRECT, 0, (LPARAM)(LPRECT)&rcFormat);
  1589.     // account for the border style, REVIEW: there might be a better way!
  1590. #ifdef DBCS
  1591.     // some FE fonts have suprisingly big negative C width
  1592. #ifndef IEWIN31_25
  1593.     wRightMgn=HIWORD(SendMessage(hwndEdit, EM_GETMARGINS, 0, 0));
  1594. #else
  1595.     wRightMgn=0;
  1596. #endif
  1597.     InflateRect(&rc, rcFormat.left + wRightMgn + g_cxEdge, rcFormat.top + g_cyEdge);
  1598. #else
  1599.     InflateRect(&rc, rcFormat.left + g_cxEdge, rcFormat.top + g_cyEdge);
  1600. #endif
  1601.     rc.right += g_cyEdge;   // try to leave a little more for dual blanks
  1602.     HideCaret(hwndEdit);
  1603.     SetWindowPos(hwndEdit, NULL, rc.left, rc.top,
  1604.             rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
  1605.     ShowCaret(hwndEdit);
  1606. }
  1607. UINT NEAR PASCAL ListView_DrawImage(LV* plv, LV_ITEM FAR* pitem, HDC hdc, int x, int y, UINT fDraw)
  1608. {
  1609.     UINT fText = SHDT_DESELECTED;
  1610.     UINT fImage = ILD_NORMAL;
  1611.     COLORREF clr;
  1612.     HIMAGELIST himl;
  1613.     fImage = (pitem->state & LVIS_OVERLAYMASK);
  1614.     fText = SHDT_DESELECTED;
  1615.     himl = ListView_IsIconView(plv) ? plv->himl : plv->himlSmall;
  1616.     // the item can have one of 4 states, for 3 looks:
  1617.     //    normal                    simple drawing
  1618.     //    selected, no focus        light image highlight, no text hi
  1619.     //    selected w/ focus         highlight image & text
  1620.     //    drop highlighting         highlight image & text
  1621.     if ((pitem->state & LVIS_DROPHILITED) ||
  1622.         ((fDraw & LVDI_SELECTED) && (pitem->state & LVIS_SELECTED)))
  1623.     {
  1624.         fText = SHDT_SELECTED;
  1625.         fImage |= ILD_BLEND50;
  1626.         clr = CLR_HILIGHT;
  1627.     }
  1628.     if (pitem->state & LVIS_CUT)
  1629.     {
  1630.         fImage |= ILD_BLEND50;
  1631.         clr = plv->clrBk;
  1632.     }
  1633. #if 0   // dont do a selected but dont have the focus vis.
  1634.     else if (item.state & LVIS_SELECTED)
  1635.     {
  1636.         fImage |= ILD_BLEND25;
  1637.         clr = CLR_HILIGHT;
  1638.     }
  1639. #endif
  1640.     if (!(fDraw & LVDI_NOIMAGE))
  1641.     {
  1642. if (himl) {
  1643.             ImageList_DrawEx(himl, pitem->iImage, hdc, x, y, 0, 0, plv->clrBk, clr, fImage);
  1644. }
  1645.         if (plv->himlState) {
  1646.     if (LV_StateImageValue(pitem)) {
  1647.                 int iState = LV_StateImageIndex(pitem);
  1648.                 int dyImage =
  1649.                     (himl) ?
  1650.                         ( (ListView_IsIconView(plv) ? plv->cyIcon : plv->cySmIcon) - plv->cyState)
  1651.                             : 0;
  1652. ImageList_Draw(plv->himlState, iState, hdc, x-plv->cxState,
  1653.        y + dyImage,
  1654.        ILD_NORMAL);
  1655.             }
  1656.         }
  1657.     }
  1658.     return fText;
  1659. }
  1660. #ifdef FE_IME
  1661. void NEAR PASCAL ListView_SizeIME(HWND hwnd)
  1662. {
  1663.     HIMC himc;
  1664. #ifdef _WIN32
  1665.     CANDIDATEFORM   candf;
  1666. #else
  1667.     CANDIDATEFORM16   candf;
  1668. #endif
  1669.     RECT rc;
  1670.     // If this subclass procedure is being called with WM_SIZE,
  1671.     // This routine sets the rectangle to an IME.
  1672.     GetClientRect(hwnd, &rc); 
  1673.     // Candidate stuff
  1674.     candf.dwIndex = 0; // Bogus assumption for Japanese IME.
  1675.     candf.dwStyle = CFS_EXCLUDE;
  1676.     candf.ptCurrentPos.x = rc.left;
  1677.     candf.ptCurrentPos.y = rc.bottom;
  1678.     candf.rcArea = rc;
  1679.     if (himc=ImmGetContext(hwnd))
  1680.     {
  1681.         ImmSetCandidateWindow(himc, &candf);
  1682.         ImmReleaseContext(hwnd, himc);
  1683.     }
  1684. }
  1685. LPSTR NEAR PASCAL DoDBCSBoundary(LPSTR lpsz, int FAR *lpcchMax)
  1686. {
  1687.     int i = 0;
  1688.     while (i < *lpcchMax && *lpsz)
  1689.     {
  1690.         i++;
  1691.         if (IsDBCSLeadByte(*lpsz))
  1692.         {
  1693.             if (i >= *lpcchMax)
  1694.             {
  1695.                 --i; // Wrap up without the last leadbyte.
  1696.                 break;
  1697.             }
  1698.             i++;
  1699.             lpsz+= 2;
  1700.         }
  1701.         else
  1702.             lpsz++; 
  1703.    }
  1704.    *lpcchMax = i;
  1705.    return lpsz;
  1706. }
  1707. void NEAR PASCAL DrawCompositionLine(HWND hwnd, HDC hdc, HFONT hfont, LPCSTR lpszComp, LPCSTR lpszAttr, int ichCompStart, int ichCompEnd, int ichStart)
  1708. {
  1709.     PSTR pszCompStr;
  1710.     int ichSt,ichEnd;
  1711.     DWORD dwPos;
  1712.     BYTE bAttr; 
  1713.     HFONT hfontOld;    
  1714.     COLORREF crFore = GetSysColor(COLOR_WINDOWTEXT);
  1715.     COLORREF crBack = GetSysColor(COLOR_WINDOW);
  1716.     COLORREF crForeH = GetSysColor(COLOR_HIGHLIGHTTEXT);
  1717.     COLORREF crBackH = GetSysColor(COLOR_HIGHLIGHT);
  1718.     int  fnPen;
  1719.     HPEN hPen;
  1720.     COLORREF crDrawText;
  1721.     COLORREF crDrawBack;
  1722.     COLORREF crOldText;
  1723.     COLORREF crOldBk;
  1724.     while (ichCompStart < ichCompEnd)
  1725.     {
  1726.     
  1727.         // Get the fragment to draw
  1728.         //
  1729.         // ichCompStart,ichCompEnd -- index at Edit Control
  1730.         // ichSt,ichEnd            -- index at lpszComp
  1731.     
  1732.         ichEnd = ichSt  = ichCompStart - ichStart;
  1733.         bAttr = lpszAttr[ichSt];
  1734.     
  1735.         while (ichEnd < ichCompEnd - ichStart)
  1736.         {
  1737.             if (bAttr == lpszAttr[ichEnd])
  1738.                 ichEnd++;
  1739.             else
  1740.                 break;
  1741.         }
  1742.     
  1743.         pszCompStr = (PSTR)LocalAlloc(LPTR, ichEnd - ichSt + 1 + 1 ); // 1 for NULL.
  1744.     
  1745.         if (pszCompStr)
  1746.         {
  1747.             lstrcpyn(pszCompStr, &lpszComp[ichSt], ichEnd-ichSt+1);
  1748.             pszCompStr[ichEnd-ichSt] = '';
  1749.         }
  1750.     
  1751.     
  1752.         // Attribute stuff
  1753.         switch (bAttr)
  1754.         {
  1755.             case ATTR_INPUT:
  1756.                 fnPen = PS_DOT;
  1757.                 crDrawText = crFore;                
  1758.                 crDrawBack = crBack;
  1759.                 break;
  1760.             case ATTR_TARGET_CONVERTED:
  1761.             case ATTR_TARGET_NOTCONVERTED:
  1762.                 fnPen = PS_DOT;
  1763.                 crDrawText = crForeH;                
  1764.                 crDrawBack = crBackH;
  1765.                 break;
  1766.             case ATTR_CONVERTED:
  1767.                 fnPen = PS_SOLID;
  1768.                 crDrawText = crFore;                
  1769.                 crDrawBack = crBack;
  1770.                 break;
  1771.         }
  1772.         crOldText = SetTextColor(hdc, crDrawText);
  1773.         crOldBk = SetBkColor(hdc, crDrawBack);
  1774.         hfontOld= SelectObject(hdc, hfont);
  1775.     
  1776.         // Get the start position of composition
  1777.         //
  1778.         dwPos = SendMessage(hwnd, EM_POSFROMCHAR, ichCompStart, 0);
  1779.     
  1780.         // Draw it.
  1781.         TextOut(hdc, LOWORD(dwPos), HIWORD(dwPos), pszCompStr, ichEnd-ichSt);
  1782.     
  1783.     
  1784.         // Underline
  1785.         hPen = CreatePen(fnPen, 1, crDrawText);
  1786.         if( hPen ) {
  1787.             HPEN hpenOld = SelectObject( hdc, hPen );
  1788.             int iOldBk = SetBkMode( hdc, TRANSPARENT );
  1789.             SIZE size;
  1790.     
  1791.             GetTextExtentPoint(hdc, pszCompStr, ichEnd-ichSt, &size);
  1792.     
  1793.             MoveToEx( hdc, LOWORD(dwPos), size.cy + HIWORD(dwPos)-1, NULL);
  1794.     
  1795.             LineTo( hdc, size.cx + LOWORD(dwPos),  size.cy + HIWORD(dwPos)-1 );
  1796.     
  1797.             SetBkMode( hdc, iOldBk );
  1798.     
  1799.             if( hpenOld ) SelectObject( hdc, hpenOld );
  1800.     
  1801.             DeleteObject( hPen );
  1802.         }
  1803.     
  1804.         if (hfontOld)
  1805.             SelectObject(hdc, hfontOld);
  1806.         
  1807.         SetTextColor(hdc, crOldText);
  1808.         SetBkColor(hdc, crOldBk);
  1809.     
  1810.         LocalFree((HLOCAL)pszCompStr);
  1811.         //Next fragment
  1812.         //
  1813.         ichCompStart += ichEnd-ichSt; 
  1814.     }
  1815. }
  1816. void NEAR PASCAL ListView_InsertComposition(HWND hwnd, WPARAM wParam, LPARAM lParam, LV *plv)
  1817. {
  1818.     char *pszCompStr;
  1819.     int  cchComp = 0;
  1820.     int  cchCompNew;
  1821.     int  cchMax;
  1822.     int  cchText;
  1823.     DWORD dwSel;
  1824.     HIMC himc = (HIMC)0;
  1825.     // To prevent recursion..
  1826.     if (plv->flags & LVF_INSERTINGCOMP)
  1827.     {
  1828.         return;
  1829.     }
  1830.     plv->flags |= LVF_INSERTINGCOMP;
  1831.     // Don't want to redraw edit during inserting.
  1832.     //
  1833.     SendMessage(hwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
  1834.     // If we have RESULT STR, put it to EC first.
  1835.     if (himc = ImmGetContext(hwnd))
  1836.     {
  1837. #ifdef WIN32
  1838.         if (!(dwSel = (DWORD)GetProp(hwnd, szIMECompPos)))
  1839.             dwSel = Edit_GetSel(hwnd);
  1840.         // Becaues we don't setsel after inserting composition
  1841.         // in win32 case.
  1842.         Edit_SetSel(hwnd, LOWORD(dwSel), HIWORD(dwSel));
  1843. #endif
  1844.         if (lParam&GCS_RESULTSTR)
  1845.         {
  1846.             pszCompStr = (PSTR)LocalAlloc(LPTR, 1 );
  1847.             if(cchComp = (int)ImmGetCompositionString(himc, GCS_RESULTSTR, NULL, 0))
  1848.             {
  1849.                 if(pszCompStr = (PSTR)LocalReAlloc(pszCompStr, cchComp+1,LMEM_MOVEABLE ))
  1850.                 {
  1851.                     ImmGetCompositionString(himc, GCS_RESULTSTR, pszCompStr, cchComp+1);
  1852.                 }
  1853.             }
  1854.             pszCompStr[cchComp] = '';
  1855.             Edit_ReplaceSel(hwnd, pszCompStr);
  1856.             LocalFree((HLOCAL)pszCompStr);
  1857. #ifdef WIN32
  1858.             // There's no longer selection
  1859.             //
  1860.             RemoveProp(hwnd, szIMECompPos);
  1861.             
  1862.             // Get current cursor pos so that the subsequent composition
  1863.             // handling will do the right thing.
  1864.             //
  1865.             dwSel = Edit_GetSel(hwnd);
  1866. #endif
  1867.         }
  1868.         if (lParam & GCS_COMPSTR)
  1869.         {
  1870.             pszCompStr = (PSTR)LocalAlloc(LPTR, 1 );
  1871.             if(cchComp = (int)ImmGetCompositionString(himc, GCS_COMPSTR, NULL, 0))
  1872.             {
  1873.                 
  1874.                 pszCompStr = (PSTR)LocalReAlloc(pszCompStr, cchComp+1,LMEM_MOVEABLE );
  1875.                 if (!pszCompStr)
  1876.                     goto ReleaseContext;
  1877.                 ImmGetCompositionString(himc, GCS_COMPSTR, pszCompStr, cchComp+1);
  1878.         
  1879.                 // Get position of the current selection 
  1880.                 //
  1881. #ifndef WIN32
  1882.                 dwSel  = Edit_GetSel(hwnd);
  1883. #endif
  1884.                 cchMax = (int)SendMessage(hwnd, EM_GETLIMITTEXT, 0, 0);
  1885.                 cchText = Edit_GetTextLength(hwnd);
  1886.                 // Cut the composition string if it exceeds limit.
  1887.                 //
  1888.                 cchCompNew = min(cchComp, 
  1889.                               cchMax-(cchText-(HIWORD(dwSel)-LOWORD(dwSel))));
  1890.         
  1891.                 // wrap up the DBCS at the end of string
  1892.                 //
  1893.                 if (cchCompNew < cchComp)
  1894.                 {
  1895.                     DoDBCSBoundary((LPSTR)pszCompStr, (int FAR *)&cchCompNew);
  1896.                     pszCompStr[cchCompNew] = '';
  1897.                     // Reset composition string if we cut it.
  1898.                     ImmSetCompositionString(himc, SCS_SETSTR, pszCompStr, cchCompNew, NULL, 0);
  1899.                     cchComp = cchCompNew;
  1900.                 }
  1901.            } 
  1902.            pszCompStr[cchComp] = '';
  1903.         
  1904.            // Replace the current selection with composition string.
  1905.            //
  1906.            Edit_ReplaceSel(hwnd, pszCompStr);
  1907.         
  1908.            LocalFree((HLOCAL)pszCompStr);
  1909.            // Mark the composition string so that we can replace it again 
  1910.            // for the next time. 
  1911.            //
  1912. #ifdef WIN32
  1913.            // Don't setsel to avoid flicking
  1914.            if (cchComp)
  1915.            {
  1916.                dwSel = MAKELONG(LOWORD(dwSel),LOWORD(dwSel)+cchComp);
  1917.                SetProp(hwnd, szIMECompPos, (HANDLE)dwSel);
  1918.            }
  1919.            else
  1920.                RemoveProp(hwnd, szIMECompPos);
  1921. #else
  1922.            // Still use SETSEL for 16bit.
  1923.            if (cchComp)
  1924.                Edit_SetSel(hwnd, LOWORD(dwSel), LOWORD(dwSel)+cchComp);
  1925. #endif
  1926.         
  1927.         }
  1928. ReleaseContext:
  1929.         ImmReleaseContext(hwnd, himc);
  1930.     }
  1931.     SendMessage(hwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
  1932.     //
  1933.     // We want to update the size of label edit just once at 
  1934.     // each WM_IME_COMPOSITION processing. ReplaceSel causes several EN_UPDATE
  1935.     // and it causes ugly flicking too.
  1936.     //
  1937.     SetWindowID(plv->hwndEdit, 1);
  1938.     ListView_SetEditSize(plv);
  1939.     RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT|RDW_INVALIDATE); 
  1940.     UpdateWindow(hwnd);
  1941.     plv->flags &= ~LVF_INSERTINGCOMP;
  1942. }
  1943. void NEAR PASCAL ListView_PaintComposition(HWND hwnd, LV * plv)
  1944. {
  1945.     char szCompStr[CCHLABELMAX + 1];
  1946.     char szCompAttr[CCHLABELMAX + 1];
  1947.     int  cchLine, ichLineStart;
  1948.     int  cchComp = 0;
  1949.     int  nLine;
  1950.     int  ichCompStart, ichCompEnd;
  1951.     DWORD dwSel;
  1952.     int  cchMax, cchText;
  1953.     HIMC himc = (HIMC)0;
  1954.     HDC  hdc;
  1955.     if (plv->flags & LVF_INSERTINGCOMP)
  1956.     {
  1957.         // This is the case that ImmSetCompositionString() generates
  1958.         // WM_IME_COMPOSITION. We're not ready to paint composition here.
  1959.         return;
  1960.     }
  1961.     if (himc = ImmGetContext(hwnd))
  1962.     {
  1963.     
  1964.         cchComp=(UINT)ImmGetCompositionString(himc, GCS_COMPSTR, szCompStr, sizeof(szCompStr));
  1965.         
  1966.         ImmGetCompositionString(himc, GCS_COMPATTR, szCompAttr, sizeof(szCompStr));
  1967.         ImmReleaseContext(hwnd, himc);
  1968.     }
  1969.     if (cchComp)
  1970.     {
  1971.         // Get the position of current selection 
  1972.         //
  1973. #ifdef WIN32
  1974.         if (!(dwSel = (DWORD)GetProp(hwnd, szIMECompPos)))
  1975.             dwSel = 0L;
  1976. #else
  1977.         dwSel  = Edit_GetSel(hwnd);
  1978. #endif
  1979.         cchMax = (int)SendMessage(hwnd, EM_GETLIMITTEXT, 0, 0);
  1980.         cchText = Edit_GetTextLength(hwnd);
  1981.         cchComp = min(cchComp, cchMax-(cchText-(HIWORD(dwSel)-LOWORD(dwSel))));
  1982.         DoDBCSBoundary((LPSTR)szCompStr, (int FAR *)&cchComp);
  1983.         szCompStr[cchComp] = ''; 
  1984.         /////////////////////////////////////////////////
  1985.         //                                             //
  1986.         // Draw composition string over the sel string.//
  1987.         //                                             //
  1988.         /////////////////////////////////////////////////
  1989.         hdc = GetDC(hwnd);
  1990.         ichCompStart = LOWORD(dwSel);
  1991.         while (ichCompStart < (int)LOWORD(dwSel) + cchComp)
  1992.         {
  1993.             // Get line from each start pos.
  1994.             //
  1995.             nLine = Edit_LineFromChar(hwnd, ichCompStart); 
  1996.             ichLineStart = Edit_LineIndex(hwnd, nLine);
  1997.             cchLine= Edit_LineLength(hwnd, ichLineStart);
  1998.             // See if composition string is longer than this line.
  1999.             //
  2000.             if(ichLineStart+cchLine > (int)LOWORD(dwSel)+cchComp)
  2001.                 ichCompEnd = LOWORD(dwSel)+cchComp;
  2002.             else
  2003.             {
  2004.                 // Yes, the composition string is longer.
  2005.                 // Take the begining of the next line as next start.
  2006.                 //
  2007.                 if (ichLineStart+cchLine > ichCompStart)
  2008.                     ichCompEnd = ichLineStart+cchLine;
  2009.                 else
  2010.                 {
  2011.                     // If the starting position is not proceeding,
  2012.                     // let's get out of here.
  2013.                     break;
  2014.                 }
  2015.             }
  2016.             
  2017.             // Draw the line
  2018.             //
  2019.             DrawCompositionLine(hwnd, hdc, plv->hfontLabel, szCompStr, szCompAttr, ichCompStart, ichCompEnd, LOWORD(dwSel));
  2020.             ichCompStart = ichCompEnd;
  2021.         }
  2022.         ReleaseDC(hwnd, hdc);
  2023.     }
  2024.     // We don't want to repaint the window.
  2025.     ValidateRect(hwnd, NULL);
  2026. }
  2027. #endif