2468 lines
66 KiB
C
2468 lines
66 KiB
C
|
// large icon view stuff
|
||
|
|
||
|
#include "ctlspriv.h"
|
||
|
#include "listview.h"
|
||
|
|
||
|
#ifdef FE_IME
|
||
|
#include <imm.h>
|
||
|
|
||
|
static char const szIMECompPos[]="IMECompPos";
|
||
|
#endif
|
||
|
|
||
|
#define ICONCXLABEL(pl, pi) ((pl->style & LVS_NOLABELWRAP) ? pi->cxSingleLabel : pi->cxMultiLabel)
|
||
|
|
||
|
void NEAR PASCAL ListView_IDrawItem(LV* plv, int i, HDC hdc, LPPOINT lpptOrg, RECT FAR* prcClip, UINT fDraw)
|
||
|
{
|
||
|
RECT rcIcon;
|
||
|
RECT rcLabel;
|
||
|
RECT rcBounds;
|
||
|
RECT rcT;
|
||
|
|
||
|
ListView_GetRects(plv, i, &rcIcon, &rcLabel, &rcBounds, NULL);
|
||
|
|
||
|
if (!prcClip || IntersectRect(&rcT, prcClip, &rcBounds))
|
||
|
{
|
||
|
LV_ITEM item;
|
||
|
char ach[CCHLABELMAX];
|
||
|
UINT fText;
|
||
|
|
||
|
if (lpptOrg)
|
||
|
{
|
||
|
OffsetRect(&rcIcon, lpptOrg->x - rcBounds.left,
|
||
|
lpptOrg->y - rcBounds.top);
|
||
|
OffsetRect(&rcLabel, lpptOrg->x - rcBounds.left,
|
||
|
lpptOrg->y - rcBounds.top);
|
||
|
}
|
||
|
|
||
|
item.iItem = i;
|
||
|
item.iSubItem = 0;
|
||
|
item.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_STATE;
|
||
|
item.stateMask = LVIS_ALL;
|
||
|
item.pszText = ach;
|
||
|
item.cchTextMax = sizeof(ach);
|
||
|
|
||
|
ListView_OnGetItem(plv, &item);
|
||
|
|
||
|
fText = ListView_DrawImage(plv, &item, hdc,
|
||
|
rcIcon.left + g_cxIconMargin, rcIcon.top + g_cyIconMargin, fDraw);
|
||
|
|
||
|
// Don't draw label if it's being edited...
|
||
|
|
||
|
if (plv->iEdit != i)
|
||
|
{
|
||
|
if (rcLabel.bottom - rcLabel.top > plv->cyLabelChar)
|
||
|
fText |= SHDT_DRAWTEXT;
|
||
|
else
|
||
|
fText |= SHDT_ELLIPSES;
|
||
|
|
||
|
if (fDraw & LVDI_TRANSTEXT)
|
||
|
fText |= SHDT_TRANSPARENT;
|
||
|
|
||
|
if (item.pszText)
|
||
|
{
|
||
|
// yow! this eats stack. 256 from this proc and 256 from shell_drawtext
|
||
|
SHDrawText(hdc, item.pszText, &rcLabel, LVCFMT_LEFT, fText,
|
||
|
plv->cyLabelChar, plv->cxEllipses, plv->clrText,
|
||
|
(plv->style & WS_DISABLED ? plv->clrBk : plv->clrTextBk));
|
||
|
}
|
||
|
|
||
|
if ((fDraw & LVDI_FOCUS) && (item.state & LVIS_FOCUSED))
|
||
|
DrawFocusRect(hdc, &rcLabel);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
int NEAR ListView_IItemHitTest(LV* plv, int x, int y, UINT FAR* pflags)
|
||
|
{
|
||
|
int iHit;
|
||
|
UINT flags;
|
||
|
POINT pt;
|
||
|
|
||
|
// Map window-relative coordinates to view-relative coords...
|
||
|
|
||
|
pt.x = x + plv->ptOrigin.x;
|
||
|
pt.y = y + plv->ptOrigin.y;
|
||
|
|
||
|
// If there are any uncomputed items, recompute them now.
|
||
|
|
||
|
if (plv->rcView.left == RECOMPUTE)
|
||
|
ListView_Recompute(plv);
|
||
|
|
||
|
flags = 0;
|
||
|
for (iHit = 0; iHit < ListView_Count(plv); iHit++)
|
||
|
{
|
||
|
LISTITEM FAR* pitem = ListView_FastGetZItemPtr(plv, iHit);
|
||
|
POINT ptItem;
|
||
|
RECT rcLabel;
|
||
|
RECT rcIcon;
|
||
|
|
||
|
ptItem.x = pitem->pt.x;
|
||
|
ptItem.y = pitem->pt.y;
|
||
|
|
||
|
rcIcon.top = ptItem.y - g_cyIconMargin;
|
||
|
|
||
|
rcLabel.top = ptItem.y + plv->cyIcon + g_cyLabelSpace;
|
||
|
rcLabel.bottom = rcLabel.top + pitem->cyMultiLabel;
|
||
|
|
||
|
// Quick, easy rejection test...
|
||
|
|
||
|
if (pt.y < rcIcon.top || pt.y >= rcLabel.bottom)
|
||
|
continue;
|
||
|
|
||
|
rcIcon.left = ptItem.x - g_cxIconMargin;
|
||
|
rcIcon.right = ptItem.x + plv->cxIcon + g_cxIconMargin;
|
||
|
// We need to make sure there is no gap between the icon and label
|
||
|
rcIcon.bottom = rcLabel.top;
|
||
|
|
||
|
rcLabel.left = ptItem.x + (plv->cxIcon / 2) - (ICONCXLABEL(plv, pitem) / 2);
|
||
|
rcLabel.right = rcLabel.left + ICONCXLABEL(plv, pitem);
|
||
|
|
||
|
if (PtInRect(&rcIcon, pt))
|
||
|
{
|
||
|
flags = LVHT_ONITEMICON;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (PtInRect(&rcLabel, pt))
|
||
|
{
|
||
|
flags = LVHT_ONITEMLABEL;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
if (flags == 0)
|
||
|
{
|
||
|
flags = LVHT_NOWHERE;
|
||
|
iHit = -1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
iHit = DPA_GetPtrIndex(plv->hdpa, ListView_FastGetZItemPtr(plv, iHit));
|
||
|
}
|
||
|
|
||
|
*pflags = flags;
|
||
|
return iHit;
|
||
|
}
|
||
|
|
||
|
// out:
|
||
|
// prcIcon icon bounds including icon margin area
|
||
|
|
||
|
void NEAR ListView_IGetRects(LV* plv, LISTITEM FAR* pitem, RECT FAR* prcIcon, RECT FAR* prcLabel, LPRECT prcBounds)
|
||
|
{
|
||
|
|
||
|
prcIcon->left = pitem->pt.x - g_cxIconMargin - plv->ptOrigin.x;
|
||
|
prcIcon->right = prcIcon->left + plv->cxIcon + 2 * g_cxIconMargin;
|
||
|
prcIcon->top = pitem->pt.y - g_cyIconMargin - plv->ptOrigin.y;
|
||
|
prcIcon->bottom = prcIcon->top + plv->cyIcon + 2 * g_cyIconMargin;
|
||
|
|
||
|
prcLabel->left = pitem->pt.x + (plv->cxIcon / 2) - (ICONCXLABEL(plv, pitem) / 2) - plv->ptOrigin.x;
|
||
|
prcLabel->right = prcLabel->left + ICONCXLABEL(plv, pitem);
|
||
|
prcLabel->top = pitem->pt.y + plv->cyIcon + g_cyLabelSpace - plv->ptOrigin.y;
|
||
|
prcLabel->bottom = prcLabel->top + pitem->cyMultiLabel;
|
||
|
|
||
|
}
|
||
|
|
||
|
int NEAR ListView_GetSlotCount(LV* plv, BOOL fWithoutScrollbars)
|
||
|
{
|
||
|
int cxScreen;
|
||
|
int cyScreen;
|
||
|
int dxItem;
|
||
|
int dyItem;
|
||
|
int iSlots = 1;
|
||
|
BOOL fCheckWithScroll = FALSE;
|
||
|
DWORD style;
|
||
|
|
||
|
// Always use the current client window size to determine
|
||
|
|
||
|
// REVIEW: Should we exclude any vertical scroll bar that may
|
||
|
// exist when computing this? progman.exe does not.
|
||
|
|
||
|
cxScreen = plv->sizeClient.cx;
|
||
|
cyScreen = plv->sizeClient.cy;
|
||
|
|
||
|
if (fWithoutScrollbars) {
|
||
|
style = GetWindowStyle(plv->hwnd);
|
||
|
if (style & WS_VSCROLL)
|
||
|
cxScreen += g_cxScrollbar;
|
||
|
if (style & WS_HSCROLL)
|
||
|
cyScreen += g_cyScrollbar;
|
||
|
|
||
|
}
|
||
|
|
||
|
if (ListView_IsSmallView(plv))
|
||
|
dxItem = plv->cxItem;
|
||
|
else
|
||
|
dxItem = lv_cxIconSpacing;
|
||
|
|
||
|
if (ListView_IsSmallView(plv))
|
||
|
dyItem = plv->cyItem;
|
||
|
else
|
||
|
dyItem = lv_cyIconSpacing;
|
||
|
|
||
|
|
||
|
// Lets see which direction the view states
|
||
|
switch (plv->style & LVS_ALIGNMASK)
|
||
|
{
|
||
|
case LVS_ALIGNBOTTOM:
|
||
|
case LVS_ALIGNTOP:
|
||
|
iSlots = max(1, (cxScreen) / dxItem);
|
||
|
fCheckWithScroll = (BOOL)(style & WS_VSCROLL);
|
||
|
break;
|
||
|
|
||
|
case LVS_ALIGNRIGHT:
|
||
|
case LVS_ALIGNLEFT:
|
||
|
iSlots = max(1, (cyScreen) / dyItem);
|
||
|
fCheckWithScroll = (BOOL)(style & WS_HSCROLL);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
Assert(0);
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
// if we don't have enough slots total on the screen, we're going to have
|
||
|
// a scrollbar, so recompute with the scrollbars on
|
||
|
if (fWithoutScrollbars && fCheckWithScroll) {
|
||
|
int iTotalSlots = (dxItem * dyItem);
|
||
|
if (iTotalSlots < ListView_Count(plv)) {
|
||
|
iSlots = ListView_GetSlotCount(plv, FALSE);
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return iSlots;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Go through and recompute any icon positions and optionally
|
||
|
// icon label dimensions.
|
||
|
|
||
|
// This function also recomputes the view bounds rectangle.
|
||
|
|
||
|
// The algorithm is to simply search the list for any items needing
|
||
|
// recomputation. For icon positions, we scan possible icon slots
|
||
|
// and check to see if any already-positioned icon intersects the slot.
|
||
|
// If not, the slot is free. As an optimization, we start scanning
|
||
|
// icon slots from the previous slot we found.
|
||
|
|
||
|
void NEAR ListView_Recompute(LV* plv)
|
||
|
{
|
||
|
int i;
|
||
|
int cSlots;
|
||
|
BOOL fUpdateSB;
|
||
|
BOOL fAppendAtEnd = FALSE;
|
||
|
BOOL fLargeIconView;
|
||
|
int iFree;
|
||
|
HDC hdc;
|
||
|
|
||
|
if (!(ListView_IsIconView(plv) || ListView_IsSmallView(plv)))
|
||
|
return;
|
||
|
|
||
|
if (plv->flags & LVF_INRECOMPUTE)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
plv->flags |= LVF_INRECOMPUTE;
|
||
|
|
||
|
fLargeIconView = ListView_IsIconView(plv);
|
||
|
|
||
|
hdc = NULL;
|
||
|
|
||
|
cSlots = ListView_GetSlotCount(plv, FALSE);
|
||
|
|
||
|
// Scan all items for RECOMPUTE, and recompute slot if needed.
|
||
|
|
||
|
fUpdateSB = (plv->rcView.left == RECOMPUTE);
|
||
|
iFree = -1;
|
||
|
for (i = 0; i < ListView_Count(plv); i++)
|
||
|
{
|
||
|
LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, i);
|
||
|
BOOL fRedraw = FALSE;
|
||
|
|
||
|
if (pitem->cyMultiLabel == SRECOMPUTE)
|
||
|
{
|
||
|
hdc = ListView_RecomputeLabelSize(plv, pitem, i, hdc);
|
||
|
fRedraw = TRUE;
|
||
|
}
|
||
|
|
||
|
if (pitem->pt.y == RECOMPUTE)
|
||
|
{
|
||
|
|
||
|
iFree = ListView_FindFreeSlot(plv, i, iFree + 1, cSlots,
|
||
|
&fUpdateSB, &fAppendAtEnd, &hdc);
|
||
|
Assert(iFree != -1);
|
||
|
|
||
|
ListView_SetIconPos(plv, pitem, iFree, cSlots);
|
||
|
fRedraw = TRUE;
|
||
|
}
|
||
|
|
||
|
if (fRedraw)
|
||
|
{
|
||
|
ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
|
||
|
fUpdateSB = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (hdc)
|
||
|
ReleaseDC(HWND_DESKTOP, hdc);
|
||
|
|
||
|
// If we changed something, recompute the view rectangle
|
||
|
// and then update the scroll bars.
|
||
|
|
||
|
if (fUpdateSB)
|
||
|
{
|
||
|
// NOTE: No infinite recursion results because we're setting
|
||
|
// plv->rcView.left != RECOMPUTE
|
||
|
|
||
|
SetRectEmpty(&plv->rcView);
|
||
|
|
||
|
for (i = 0; i < ListView_Count(plv); i++)
|
||
|
{
|
||
|
RECT rcItem;
|
||
|
|
||
|
ListView_GetRects(plv, i, NULL, NULL, &rcItem, NULL);
|
||
|
UnionRect(&plv->rcView, &plv->rcView, &rcItem);
|
||
|
}
|
||
|
|
||
|
// add a little space at the edges so that we don't bump text
|
||
|
// completely to the end of the window
|
||
|
plv->rcView.bottom += g_cyEdge;
|
||
|
plv->rcView.right += g_cxEdge;
|
||
|
|
||
|
OffsetRect(&plv->rcView, plv->ptOrigin.x, plv->ptOrigin.y);
|
||
|
//DebugMsg(DM_TRACE, "RECOMPUTE: rcView %x %x %x %x", plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
|
||
|
//DebugMsg(DM_TRACE, "Origin %x %x", plv->ptOrigin.x, plv->ptOrigin.y);
|
||
|
|
||
|
ListView_UpdateScrollBars(plv);
|
||
|
}
|
||
|
|
||
|
// Now state we are out of the recompute...
|
||
|
plv->flags &= ~LVF_INRECOMPUTE;
|
||
|
}
|
||
|
|
||
|
void NEAR PASCAL NearestSlot(int FAR *x, int FAR *y, int cxItem, int cyItem)
|
||
|
{
|
||
|
*x += cxItem/2;
|
||
|
*y += cyItem/2;
|
||
|
*x = *x - (*x % cxItem);
|
||
|
*y = *y - (*y % cyItem);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
void NEAR PASCAL NextSlot(LV* plv, LPRECT lprc)
|
||
|
{
|
||
|
int cxItem;
|
||
|
|
||
|
if (ListView_IsSmallView(plv))
|
||
|
{
|
||
|
cxItem = plv->cxItem;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cxItem = lv_cxIconSpacing;
|
||
|
}
|
||
|
lprc->left += cxItem;
|
||
|
lprc->right += cxItem;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL NEAR _CalcSlotRect(LV* plv, int iSlot, int cSlot, BOOL fBias, LPRECT lprc)
|
||
|
{
|
||
|
int cxItem, cyItem;
|
||
|
BOOL fSmallIcon;
|
||
|
|
||
|
Assert(plv);
|
||
|
|
||
|
if (cSlot < 1)
|
||
|
cSlot = 1;
|
||
|
|
||
|
if (fSmallIcon = ListView_IsSmallView(plv))
|
||
|
{
|
||
|
cxItem = plv->cxItem;
|
||
|
cyItem = plv->cyItem;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cxItem = lv_cxIconSpacing;
|
||
|
cyItem = lv_cyIconSpacing;
|
||
|
}
|
||
|
|
||
|
// Lets see which direction the view states
|
||
|
switch (plv->style & LVS_ALIGNMASK)
|
||
|
{
|
||
|
case LVS_ALIGNBOTTOM:
|
||
|
// Assert False (Change default in shell2d.. to ALIGN_TOP)
|
||
|
|
||
|
case LVS_ALIGNTOP:
|
||
|
lprc->left = (iSlot % cSlot) * cxItem;
|
||
|
lprc->top = (iSlot / cSlot) * cyItem;
|
||
|
break;
|
||
|
|
||
|
case LVS_ALIGNLEFT:
|
||
|
lprc->top = (iSlot % cSlot) * cyItem;
|
||
|
lprc->left = (iSlot / cSlot) * cxItem;
|
||
|
break;
|
||
|
|
||
|
case LVS_ALIGNRIGHT:
|
||
|
Assert(FALSE); // Not implemented yet...
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (fBias)
|
||
|
{
|
||
|
lprc->left -= plv->ptOrigin.x;
|
||
|
lprc->top -= plv->ptOrigin.y;
|
||
|
}
|
||
|
lprc->bottom = lprc->top + cyItem;
|
||
|
lprc->right = lprc->left + cxItem;
|
||
|
|
||
|
return(fSmallIcon);
|
||
|
}
|
||
|
|
||
|
// Find an icon slot that doesn't intersect an icon.
|
||
|
// Start search for free slot from slot i.
|
||
|
|
||
|
int NEAR ListView_FindFreeSlot(LV* plv, int iItem, int i, int cSlot, BOOL FAR* pfUpdate,
|
||
|
BOOL FAR *pfAppend, HDC FAR* phdc)
|
||
|
{
|
||
|
int j;
|
||
|
HDC hdc;
|
||
|
RECT rcSlot;
|
||
|
RECT rcItem;
|
||
|
RECT rc;
|
||
|
int xMax = -1;
|
||
|
int yMax = -1;
|
||
|
int cItems;
|
||
|
|
||
|
// Horrible N-squared algorithm:
|
||
|
// enumerate each slot and see if any items intersect it.
|
||
|
|
||
|
// REVIEW: This is really slow with long lists (e.g., 1000)
|
||
|
|
||
|
hdc = NULL;
|
||
|
cItems = ListView_Count(plv);
|
||
|
|
||
|
|
||
|
// If the Append at end is set, we should be able to simply get the
|
||
|
// rectangle of the i-1 element and check against it instead of
|
||
|
// looking at every other item...
|
||
|
|
||
|
if (*pfAppend)
|
||
|
{
|
||
|
Assert(iItem > 0);
|
||
|
// Be carefull about going of the end of the list. (i is a slot
|
||
|
// number not an item index).
|
||
|
ListView_GetRects(plv, iItem-1, NULL, NULL, &rcItem, NULL);
|
||
|
}
|
||
|
|
||
|
for ( ; ; i++)
|
||
|
{
|
||
|
|
||
|
// Compute view-relative slot rectangle...
|
||
|
|
||
|
_CalcSlotRect(plv, i, cSlot, TRUE, &rcSlot);
|
||
|
|
||
|
if (*pfAppend)
|
||
|
{
|
||
|
if (!IntersectRect(&rc, &rcItem, &rcSlot))
|
||
|
return i; // Found a free slot...
|
||
|
}
|
||
|
|
||
|
else
|
||
|
{
|
||
|
for (j = cItems; j-- > 0; )
|
||
|
{
|
||
|
LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, j);
|
||
|
if (pitem->pt.y != RECOMPUTE)
|
||
|
{
|
||
|
// If the dimensions aren't computed, then do it now.
|
||
|
|
||
|
if (pitem->cyMultiLabel == SRECOMPUTE)
|
||
|
{
|
||
|
*phdc = ListView_RecomputeLabelSize(plv, pitem, i, *phdc);
|
||
|
|
||
|
// Ensure that the item gets redrawn...
|
||
|
|
||
|
ListView_InvalidateItem(plv, i, FALSE, RDW_INVALIDATE | RDW_ERASE);
|
||
|
|
||
|
// Set flag indicating that scroll bars need to be
|
||
|
// adjusted.
|
||
|
|
||
|
*pfUpdate = TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
ListView_GetRects(plv, j, NULL, NULL, &rc, NULL);
|
||
|
|
||
|
if (IntersectRect(&rc, &rc, &rcSlot))
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (j < 0)
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ( (rcSlot.bottom > yMax) ||
|
||
|
((rcSlot.bottom == yMax) && (rcSlot.right > xMax)))
|
||
|
*pfAppend = TRUE;
|
||
|
|
||
|
return i;
|
||
|
}
|
||
|
|
||
|
// Recompute an item's label size (cxLabel/cyLabel). For speed, this function
|
||
|
// is passed a DC to use for text measurement.
|
||
|
|
||
|
// If hdc is NULL, then this function will get a DC and return it. Otherwise,
|
||
|
// the returned hdc is the same as the one passed in. It's the caller's
|
||
|
// responsibility to eventually release the DC.
|
||
|
|
||
|
HDC NEAR ListView_RecomputeLabelSize(LV* plv, LISTITEM FAR* pitem, int i, HDC hdc)
|
||
|
{
|
||
|
char szLabel[CCHLABELMAX + 4];
|
||
|
int cchLabel;
|
||
|
RECT rcSingle, rcMulti;
|
||
|
LV_ITEM item;
|
||
|
|
||
|
Assert(plv);
|
||
|
Assert(pitem);
|
||
|
|
||
|
// Get the DC and select the font only once for entire loop.
|
||
|
|
||
|
if (!hdc)
|
||
|
{
|
||
|
// we return this DC and have the calller release it
|
||
|
hdc = GetDC(HWND_DESKTOP);
|
||
|
SelectFont(hdc, plv->hfontLabel);
|
||
|
}
|
||
|
|
||
|
item.mask = LVIF_TEXT;
|
||
|
item.iItem = i;
|
||
|
item.iSubItem = 0;
|
||
|
item.pszText = szLabel;
|
||
|
item.cchTextMax = sizeof(szLabel);
|
||
|
item.stateMask = 0;
|
||
|
ListView_OnGetItem(plv, &item);
|
||
|
|
||
|
if (!item.pszText)
|
||
|
{
|
||
|
SetRectEmpty(&rcSingle);
|
||
|
rcMulti = rcSingle;
|
||
|
goto Exit;
|
||
|
}
|
||
|
|
||
|
if (item.pszText != szLabel)
|
||
|
lstrcpy(szLabel, item.pszText);
|
||
|
|
||
|
cchLabel = lstrlen(szLabel);
|
||
|
|
||
|
rcMulti.left = rcMulti.top = rcMulti.bottom = 0;
|
||
|
rcMulti.right = lv_cxIconSpacing - g_cxLabelMargin * 2;
|
||
|
rcSingle = rcMulti;
|
||
|
|
||
|
if (cchLabel > 0)
|
||
|
{
|
||
|
// Strip off spaces so they're not included in format
|
||
|
// REVIEW: Is this is a DrawText bug?
|
||
|
|
||
|
while (cchLabel > 1 && szLabel[cchLabel - 1] == ' ')
|
||
|
szLabel[--cchLabel] = 0;
|
||
|
|
||
|
DrawText(hdc, szLabel, cchLabel, &rcSingle, (DT_LV | DT_CALCRECT));
|
||
|
#ifdef WIN32
|
||
|
DrawText(hdc, szLabel, cchLabel, &rcMulti, (DT_LVWRAP | DT_WORD_ELLIPSIS | DT_CALCRECT));
|
||
|
#else
|
||
|
DrawText(hdc, szLabel, cchLabel, &rcMulti, (DT_LVWRAP | DT_CALCRECT));
|
||
|
#endif
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
rcMulti.bottom = rcMulti.top + plv->cyLabelChar;
|
||
|
}
|
||
|
Exit:
|
||
|
pitem->cxSingleLabel = (rcSingle.right - rcSingle.left) + 2 * g_cxLabelMargin;
|
||
|
pitem->cxMultiLabel = (rcMulti.right - rcMulti.left) + 2 * g_cxLabelMargin;
|
||
|
pitem->cyMultiLabel = (short)(rcMulti.bottom - rcMulti.top);
|
||
|
|
||
|
return hdc;
|
||
|
}
|
||
|
|
||
|
// Set up an icon slot position. Returns FALSE if position didn't change.
|
||
|
|
||
|
BOOL NEAR ListView_SetIconPos(LV* plv, LISTITEM FAR* pitem, int iSlot, int cSlot)
|
||
|
{
|
||
|
RECT rc;
|
||
|
|
||
|
Assert(plv);
|
||
|
|
||
|
|
||
|
// Sort of a hack, this internal function return TRUE if small icon.
|
||
|
|
||
|
if (!_CalcSlotRect(plv, iSlot, cSlot, FALSE, &rc))
|
||
|
{
|
||
|
rc.left += g_cxIconOffset;
|
||
|
rc.top += g_cyIconOffset;
|
||
|
}
|
||
|
|
||
|
if (rc.left != pitem->pt.x || rc.top != pitem->pt.y)
|
||
|
{
|
||
|
pitem->pt.x = rc.left;
|
||
|
pitem->pt.y = rc.top;
|
||
|
|
||
|
// REVIEW: Performance idea:
|
||
|
|
||
|
// Invalidate rcView only if this icon's old or new position
|
||
|
// touches or is outside of the current rcView.
|
||
|
// If we do this, then we must change the various tests
|
||
|
// of rcView.left == RECOMPUTE to more specific tests of pitem->...
|
||
|
|
||
|
plv->rcView.left = RECOMPUTE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
void NEAR ListView_GetViewRect2(LV* plv, RECT FAR* prcView, int cx, int cy)
|
||
|
{
|
||
|
|
||
|
if (plv->rcView.left == RECOMPUTE)
|
||
|
ListView_Recompute(plv);
|
||
|
|
||
|
*prcView = plv->rcView;
|
||
|
OffsetRect(prcView, -plv->ptOrigin.x, -plv->ptOrigin.y);
|
||
|
if (ListView_IsIconView(plv) || ListView_IsSmallView(plv)) {
|
||
|
// don't do that funky half-re-origining thing.
|
||
|
|
||
|
RECT rc;
|
||
|
|
||
|
rc.left = 0;
|
||
|
rc.top = 0;
|
||
|
rc.bottom = rc.top + 1;
|
||
|
rc.right = rc.left + 1;
|
||
|
UnionRect(prcView, prcView, &rc);
|
||
|
|
||
|
rc.right = cx;
|
||
|
rc.bottom = cy;
|
||
|
rc.left = rc.right - 1;
|
||
|
rc.top = rc.bottom - 1;
|
||
|
UnionRect(prcView, prcView, &rc);
|
||
|
|
||
|
|
||
|
#if 0
|
||
|
// if we're scrolled way in the positive area (plv->ptOrigin > 0), make sure we
|
||
|
// include our true origin
|
||
|
if ((prcView->left > -plv->ptOrigin.x))
|
||
|
prcView->left = -plv->ptOrigin.x;
|
||
|
if ((prcView->top > -plv->ptOrigin.y))
|
||
|
prcView->top = -plv->ptOrigin.y;
|
||
|
|
||
|
// if we're scrolled way in the positive area (plv->ptOrigin > 0),
|
||
|
// make sure our scrollbars include our current position
|
||
|
if ((prcView->right < (plv->sizeClient.cx)))
|
||
|
prcView->right = plv->sizeClient.cx;
|
||
|
if ((prcView->bottom < (plv->sizeClient.cy)))
|
||
|
prcView->bottom = plv->sizeClient.cy;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// prcViewRect used only if fSubScroll is TRUE
|
||
|
DWORD NEAR ListView_GetClientRect(LV* plv, RECT FAR* prcClient, BOOL fSubScroll, RECT FAR *prcViewRect)
|
||
|
{
|
||
|
RECT rcClient;
|
||
|
RECT rcView;
|
||
|
DWORD style;
|
||
|
|
||
|
#if 1
|
||
|
// do this instead of the #else below because
|
||
|
// in new versus old apps, you may need to add in g_c?Border because of
|
||
|
// the one pixel overlap...
|
||
|
GetWindowRect(plv->hwnd, &rcClient);
|
||
|
if (GetWindowLong(plv->hwnd, GWL_EXSTYLE) & (WS_EX_CLIENTEDGE | WS_EX_STATICEDGE | WS_EX_WINDOWEDGE)) {
|
||
|
rcClient.right -= 2 * g_cxEdge;
|
||
|
rcClient.bottom -= 2 * g_cyEdge;
|
||
|
}
|
||
|
rcClient.right -= rcClient.left;
|
||
|
rcClient.bottom -= rcClient.top;
|
||
|
if (rcClient.right < 0)
|
||
|
rcClient.right = 0;
|
||
|
if (rcClient.bottom < 0)
|
||
|
rcClient.bottom = 0;
|
||
|
rcClient.top = rcClient.left = 0;
|
||
|
#else
|
||
|
style = GetWindowStyle(plv->hwnd);
|
||
|
GetClientRect(plv->hwnd, &rcClient);
|
||
|
if (style & WS_VSCROLL)
|
||
|
rcClient.right += g_cxScrollbar;
|
||
|
if (style & WS_HSCROLL)
|
||
|
rcClient.bottom += g_cyScrollbar;
|
||
|
#endif
|
||
|
|
||
|
style = 0L;
|
||
|
if (fSubScroll)
|
||
|
{
|
||
|
ListView_GetViewRect2(plv, &rcView,
|
||
|
rcClient.right - g_cxScrollbar,
|
||
|
rcClient.bottom - g_cyScrollbar);
|
||
|
if ((rcClient.left < rcClient.right) && (rcClient.top < rcClient.bottom))
|
||
|
{
|
||
|
do
|
||
|
{
|
||
|
if (rcView.left < rcClient.left || rcView.right > rcClient.right)
|
||
|
{
|
||
|
style |= WS_HSCROLL;
|
||
|
rcClient.bottom -= g_cyScrollbar;
|
||
|
}
|
||
|
if (rcView.top < rcClient.top || rcView.bottom > rcClient.bottom)
|
||
|
{
|
||
|
style |= WS_VSCROLL;
|
||
|
rcClient.right -= g_cxScrollbar;
|
||
|
}
|
||
|
}
|
||
|
while (!(style & WS_HSCROLL) && rcView.right > rcClient.right);
|
||
|
}
|
||
|
if (prcViewRect)
|
||
|
*prcViewRect = rcView;
|
||
|
}
|
||
|
*prcClient = rcClient;
|
||
|
return style;
|
||
|
}
|
||
|
|
||
|
int CALLBACK ArrangeIconCompare(LISTITEM FAR* pitem1, LISTITEM FAR* pitem2, LPARAM lParam)
|
||
|
{
|
||
|
int v1, v2;
|
||
|
|
||
|
if (HIWORD(lParam))
|
||
|
{
|
||
|
// Vertical arrange
|
||
|
v1 = pitem1->pt.x / (int)LOWORD(lParam);
|
||
|
v2 = pitem2->pt.x / (int)LOWORD(lParam);
|
||
|
|
||
|
if (v1 > v2)
|
||
|
return 1;
|
||
|
else if (v1 < v2)
|
||
|
return -1;
|
||
|
else
|
||
|
{
|
||
|
int y1 = pitem1->pt.y;
|
||
|
int y2 = pitem2->pt.y;
|
||
|
|
||
|
if (y1 > y2)
|
||
|
return 1;
|
||
|
else if (y1 < y2)
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
v1 = pitem1->pt.y / (int)lParam;
|
||
|
v2 = pitem2->pt.y / (int)lParam;
|
||
|
|
||
|
if (v1 > v2)
|
||
|
return 1;
|
||
|
else if (v1 < v2)
|
||
|
return -1;
|
||
|
else
|
||
|
{
|
||
|
int x1 = pitem1->pt.x;
|
||
|
int x2 = pitem2->pt.x;
|
||
|
|
||
|
if (x1 > x2)
|
||
|
return 1;
|
||
|
else if (x1 < x2)
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void NEAR PASCAL _ListView_GetRectsFromItem(LV* plv, BOOL bSmallIconView,
|
||
|
LISTITEM FAR *pitem,
|
||
|
LPRECT prcIcon, LPRECT prcLabel, LPRECT prcBounds, LPRECT prcSelectBounds)
|
||
|
{
|
||
|
RECT rcIcon;
|
||
|
RECT rcLabel;
|
||
|
|
||
|
if (!prcIcon)
|
||
|
prcIcon = &rcIcon;
|
||
|
if (!prcLabel)
|
||
|
prcLabel = &rcLabel;
|
||
|
|
||
|
// Test for NULL item passed in
|
||
|
if (pitem)
|
||
|
{
|
||
|
|
||
|
// This routine is called during ListView_Recompute(), while
|
||
|
// plv->rcView.left may still be == RECOMPUTE. So, we can't
|
||
|
// test that to see if recomputation is needed.
|
||
|
|
||
|
if (pitem->pt.y == RECOMPUTE || pitem->cyMultiLabel == SRECOMPUTE)
|
||
|
ListView_Recompute(plv);
|
||
|
|
||
|
if (bSmallIconView)
|
||
|
ListView_SGetRects(plv, pitem, prcIcon, prcLabel, prcBounds);
|
||
|
else
|
||
|
ListView_IGetRects(plv, pitem, prcIcon, prcLabel, prcBounds);
|
||
|
|
||
|
if (prcBounds) {
|
||
|
UnionRect(prcBounds, prcIcon, prcLabel);
|
||
|
if (plv->himlState && (LV_StateImageValue(pitem))) {
|
||
|
prcBounds->left -= plv->cxState;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
SetRectEmpty(prcIcon);
|
||
|
*prcLabel = *prcIcon;
|
||
|
if (prcBounds)
|
||
|
*prcBounds = *prcIcon;
|
||
|
}
|
||
|
|
||
|
if (prcSelectBounds)
|
||
|
UnionRect(prcSelectBounds, prcIcon, prcLabel);
|
||
|
}
|
||
|
|
||
|
void NEAR _ListView_InvalidateItemPtr(LV* plv, BOOL bSmallIcon, LISTITEM FAR *pitem, UINT fRedraw)
|
||
|
{
|
||
|
RECT rcBounds;
|
||
|
|
||
|
_ListView_GetRectsFromItem(plv, bSmallIcon, pitem, NULL, NULL, &rcBounds, NULL);
|
||
|
RedrawWindow(plv->hwnd, &rcBounds, NULL, fRedraw);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
// return TRUE if things still overlap
|
||
|
// this only happens if we tried to unstack things, and there was NOSCROLL set and
|
||
|
// items tried to go off the deep end
|
||
|
BOOL NEAR PASCAL ListView_IUnstackOverlaps(LV* plv, HDPA hdpaSort, int iDirection)
|
||
|
{
|
||
|
BOOL fRet = FALSE;
|
||
|
int i;
|
||
|
int iCount;
|
||
|
BOOL bSmallIconView;
|
||
|
RECT rcItem, rcItem2, rcTemp;
|
||
|
int cxItem, cyItem;
|
||
|
LISTITEM FAR* pitem;
|
||
|
LISTITEM FAR* pitem2;
|
||
|
|
||
|
|
||
|
if (bSmallIconView = ListView_IsSmallView(plv))
|
||
|
{
|
||
|
cxItem = plv->cxItem;
|
||
|
cyItem = plv->cyItem;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cxItem = lv_cxIconSpacing;
|
||
|
cyItem = lv_cyIconSpacing;
|
||
|
}
|
||
|
iCount = ListView_Count(plv);
|
||
|
|
||
|
// finally, unstack any overlaps
|
||
|
for (i = 0 ; i < iCount ; i++) {
|
||
|
int j;
|
||
|
pitem = DPA_GetPtr(hdpaSort, i);
|
||
|
|
||
|
if (bSmallIconView) {
|
||
|
_ListView_GetRectsFromItem(plv, bSmallIconView, pitem, NULL, NULL, &rcItem, NULL);
|
||
|
}
|
||
|
|
||
|
// move all the items that overlap with us
|
||
|
for (j = i+1 ; j < iCount; j++) {
|
||
|
POINT ptOldPos;
|
||
|
|
||
|
pitem2 = DPA_GetPtr(hdpaSort, j);
|
||
|
ptOldPos = pitem2->pt;
|
||
|
|
||
|
if (bSmallIconView) {
|
||
|
|
||
|
// for small icons, we need to do an intersect rect
|
||
|
_ListView_GetRectsFromItem(plv, bSmallIconView, pitem2, NULL, NULL, &rcItem2, NULL);
|
||
|
|
||
|
if (IntersectRect(&rcTemp, &rcItem, &rcItem2)) {
|
||
|
// yes, it intersects. move it out
|
||
|
_ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
|
||
|
do {
|
||
|
pitem2->pt.x += (cxItem * iDirection);
|
||
|
} while (PtInRect(&rcItem, pitem2->pt));
|
||
|
} else {
|
||
|
// no more intersect!
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
} else {
|
||
|
// for large icons, just find the ones that share the x,y;
|
||
|
if (pitem2->pt.x == pitem->pt.x && pitem2->pt.y == pitem->pt.y) {
|
||
|
|
||
|
_ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
|
||
|
pitem2->pt.x += (cxItem * iDirection);
|
||
|
} else {
|
||
|
// no more intersect!
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (plv->style & LVS_NOSCROLL) {
|
||
|
if (pitem2->pt.x < 0 || pitem2->pt.y < 0 ||
|
||
|
pitem2->pt.x > (plv->sizeClient.cx - (cxItem/2))||
|
||
|
pitem2->pt.y > (plv->sizeClient.cy - (cyItem/2))) {
|
||
|
pitem2->pt = ptOldPos;
|
||
|
fRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// invalidate the new position as well
|
||
|
_ListView_InvalidateItemPtr(plv, bSmallIconView, pitem2, RDW_INVALIDATE| RDW_ERASE);
|
||
|
}
|
||
|
}
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL NEAR PASCAL ListView_SnapToGrid(LV* plv, HDPA hdpaSort)
|
||
|
{
|
||
|
// this algorithm can't fit in the structure of the other
|
||
|
// arrange loop without becoming n^2 or worse.
|
||
|
// this algorithm is order n.
|
||
|
|
||
|
// iterate through and snap to the nearest grid.
|
||
|
// iterate through and push aside overlaps.
|
||
|
|
||
|
int i;
|
||
|
int iCount;
|
||
|
LPARAM xySpacing;
|
||
|
int x,y;
|
||
|
LISTITEM FAR* pitem;
|
||
|
BOOL bSmallIconView;
|
||
|
int cxItem, cyItem;
|
||
|
|
||
|
|
||
|
if (bSmallIconView = ListView_IsSmallView(plv))
|
||
|
{
|
||
|
cxItem = plv->cxItem;
|
||
|
cyItem = plv->cyItem;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cxItem = lv_cxIconSpacing;
|
||
|
cyItem = lv_cyIconSpacing;
|
||
|
}
|
||
|
|
||
|
|
||
|
iCount = ListView_Count(plv);
|
||
|
|
||
|
// first snap to nearest grid
|
||
|
for (i = 0; i < iCount; i++) {
|
||
|
pitem = DPA_GetPtr(hdpaSort, i);
|
||
|
|
||
|
x = pitem->pt.x;
|
||
|
y = pitem->pt.y;
|
||
|
|
||
|
if (!bSmallIconView) {
|
||
|
x -= g_cxIconOffset;
|
||
|
y -= g_cyIconOffset;
|
||
|
}
|
||
|
|
||
|
NearestSlot(&x,&y, cxItem, cyItem);
|
||
|
if (!bSmallIconView) {
|
||
|
x += g_cxIconOffset;
|
||
|
y += g_cyIconOffset;
|
||
|
}
|
||
|
|
||
|
if (x != pitem->pt.x || y != pitem->pt.y) {
|
||
|
_ListView_InvalidateItemPtr(plv, bSmallIconView, pitem, RDW_INVALIDATE| RDW_ERASE);
|
||
|
if (plv->style & LVS_NOSCROLL) {
|
||
|
|
||
|
// if it's marked noscroll, make sure it's still on the client region
|
||
|
while (x >= (plv->sizeClient.cx - (cxItem/2)))
|
||
|
x -= cxItem;
|
||
|
|
||
|
while (x < 0)
|
||
|
x += cxItem;
|
||
|
|
||
|
while (y >= (plv->sizeClient.cy - (cyItem/2)))
|
||
|
y -= cyItem;
|
||
|
|
||
|
while (y < 0)
|
||
|
y += cyItem;
|
||
|
}
|
||
|
pitem->pt.x = x;
|
||
|
pitem->pt.y = y;
|
||
|
_ListView_InvalidateItemPtr(plv, bSmallIconView, pitem, RDW_INVALIDATE| RDW_ERASE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now resort the dpa
|
||
|
switch (plv->style & LVS_ALIGNMASK)
|
||
|
{
|
||
|
case LVS_ALIGNLEFT:
|
||
|
case LVS_ALIGNRIGHT:
|
||
|
xySpacing = MAKELONG(bSmallIconView ? plv->cxItem : lv_cxIconSpacing, TRUE);
|
||
|
break;
|
||
|
default:
|
||
|
xySpacing = MAKELONG(bSmallIconView ? plv->cyItem : lv_cyIconSpacing, FALSE);
|
||
|
}
|
||
|
|
||
|
if (!DPA_Sort(hdpaSort, ArrangeIconCompare, xySpacing))
|
||
|
return FALSE;
|
||
|
|
||
|
|
||
|
// go in one direction, if there are still overlaps, go in the other
|
||
|
// direction as well
|
||
|
if (ListView_IUnstackOverlaps(plv, hdpaSort, 1))
|
||
|
ListView_IUnstackOverlaps(plv, hdpaSort, -1);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL NEAR ListView_OnArrange(LV* plv, UINT style)
|
||
|
{
|
||
|
BOOL bSmallIconView;
|
||
|
LPARAM xySpacing;
|
||
|
HDPA hdpaSort;
|
||
|
|
||
|
bSmallIconView = ListView_IsSmallView(plv);
|
||
|
|
||
|
if (!bSmallIconView && !ListView_IsIconView(plv)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
// Make sure our items have positions and their text rectangles
|
||
|
// caluculated
|
||
|
if (plv->rcView.left == RECOMPUTE)
|
||
|
ListView_Recompute(plv);
|
||
|
|
||
|
// we clone plv->hdpa so we don't blow away indices that
|
||
|
// apps have saved away.
|
||
|
// we sort here to make the nested for loop below more bearable.
|
||
|
hdpaSort = DPA_Clone(plv->hdpa, NULL);
|
||
|
|
||
|
if (!hdpaSort)
|
||
|
return FALSE;
|
||
|
|
||
|
switch (plv->style & LVS_ALIGNMASK)
|
||
|
{
|
||
|
case LVS_ALIGNLEFT:
|
||
|
case LVS_ALIGNRIGHT:
|
||
|
xySpacing = MAKELONG(bSmallIconView ? plv->cxItem : lv_cxIconSpacing, TRUE);
|
||
|
break;
|
||
|
default:
|
||
|
xySpacing = MAKELONG(bSmallIconView ? plv->cyItem : lv_cyIconSpacing, FALSE);
|
||
|
}
|
||
|
|
||
|
if (!DPA_Sort(hdpaSort, ArrangeIconCompare, xySpacing))
|
||
|
return FALSE;
|
||
|
|
||
|
ListView_CommonArrange(plv, style, hdpaSort);
|
||
|
|
||
|
DPA_Destroy(hdpaSort);
|
||
|
}
|
||
|
|
||
|
// this arranges the icon given a sorted hdpa.
|
||
|
BOOL NEAR ListView_CommonArrange(LV* plv, UINT style, HDPA hdpaSort)
|
||
|
{
|
||
|
int iSlot;
|
||
|
int iItem;
|
||
|
int cSlots;
|
||
|
BOOL fItemMoved;
|
||
|
RECT rcLastItem;
|
||
|
RECT rcSlot;
|
||
|
RECT rcT;
|
||
|
BOOL bSmallIconView;
|
||
|
int xMin = 0;
|
||
|
|
||
|
bSmallIconView = ListView_IsSmallView(plv);
|
||
|
|
||
|
// REVIEW, this causes a repaint if we are scrollled
|
||
|
// we can probably avoid this some how
|
||
|
|
||
|
fItemMoved = (plv->ptOrigin.x != 0) || (plv->ptOrigin.y != 0);
|
||
|
plv->ptOrigin.x = 0;
|
||
|
plv->ptOrigin.y = 0;
|
||
|
|
||
|
if (style == LVA_SNAPTOGRID) {
|
||
|
|
||
|
fItemMoved |= ListView_SnapToGrid(plv, hdpaSort);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
cSlots = ListView_GetSlotCount(plv, TRUE);
|
||
|
|
||
|
SetRectEmpty(&rcLastItem);
|
||
|
|
||
|
// manipulate only the sorted version of the item list below!
|
||
|
|
||
|
iSlot = 0;
|
||
|
for (iItem = 0; iItem < ListView_Count(plv); iItem++)
|
||
|
{
|
||
|
LISTITEM FAR* pitem = DPA_GetPtr(hdpaSort, iItem);
|
||
|
|
||
|
if (bSmallIconView)
|
||
|
{
|
||
|
// BUGBUG:: Only check for Small view... See if this gets the
|
||
|
// results people expect?
|
||
|
for ( ; ; )
|
||
|
{
|
||
|
_CalcSlotRect(plv, iSlot, cSlots, FALSE, &rcSlot);
|
||
|
if (!IntersectRect(&rcT, &rcSlot, &rcLastItem))
|
||
|
break;
|
||
|
iSlot++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
fItemMoved |= ListView_SetIconPos(plv, pitem, iSlot++, cSlots);
|
||
|
|
||
|
// do this instead of ListView_GetRects() because we need
|
||
|
// to use the pitem from the sorted hdpa, not the ones in *plv
|
||
|
_ListView_GetRectsFromItem(plv, bSmallIconView, pitem, NULL, NULL, &rcLastItem, NULL);
|
||
|
|
||
|
|
||
|
// Keep track of the minimum x as we don't want negative values
|
||
|
// when we finish.
|
||
|
if (rcLastItem.left < xMin)
|
||
|
xMin = rcLastItem.left;
|
||
|
}
|
||
|
|
||
|
|
||
|
// See if we need to scroll the items over to make sure that all of the
|
||
|
// no items are hanging off the left hand side.
|
||
|
|
||
|
if (xMin < 0)
|
||
|
{
|
||
|
for (iItem = 0; iItem < ListView_Count(plv); iItem++)
|
||
|
{
|
||
|
LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, iItem);
|
||
|
pitem->pt.x -= xMin; // scroll them over
|
||
|
}
|
||
|
plv->rcView.left = RECOMPUTE; // need to recompute.
|
||
|
fItemMoved = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
// We might as well invalidate the entire window to make sure...
|
||
|
if (fItemMoved) {
|
||
|
if (ListView_RedrawEnabled(plv))
|
||
|
RedrawWindow(plv->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
|
||
|
else {
|
||
|
ListView_DeleteHrgnInval(plv);
|
||
|
plv->hrgnInval = (HRGN)ENTIRE_REGION;
|
||
|
plv->flags |= LVF_ERASE;
|
||
|
}
|
||
|
|
||
|
// ensure important items are visible
|
||
|
iItem = (plv->iFocus >= 0) ? plv->iFocus : ListView_OnGetNextItem(plv, -1, LVNI_SELECTED);
|
||
|
|
||
|
if (iItem >= 0)
|
||
|
ListView_OnEnsureVisible(plv, iItem, FALSE);
|
||
|
|
||
|
if (ListView_RedrawEnabled(plv))
|
||
|
ListView_UpdateScrollBars(plv);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
void NEAR ListView_IUpdateScrollBars(LV* plv)
|
||
|
{
|
||
|
RECT rcClient;
|
||
|
RECT rcView;
|
||
|
DWORD style;
|
||
|
DWORD styleOld;
|
||
|
SCROLLINFO si;
|
||
|
|
||
|
styleOld = GetWindowStyle(plv->hwnd);
|
||
|
style = ListView_GetClientRect(plv, &rcClient, TRUE, &rcView);
|
||
|
|
||
|
//DebugMsg(DM_TRACE, "ListView_GetClientRect %x %x %x %x", rcClient.left, rcClient.top, rcClient.right, rcClient.bottom);
|
||
|
//DebugMsg(DM_TRACE, "ListView_GetViewRect2 %x %x %x %x", rcView.left, rcView.top, rcView.right, rcView.bottom);
|
||
|
//DebugMsg(DM_TRACE, "rcView %x %x %x %x", plv->rcView.left, plv->rcView.top, plv->rcView.right, plv->rcView.bottom);
|
||
|
//DebugMsg(DM_TRACE, "Origin %x %x", plv->ptOrigin.x, plv->ptOrigin.y);
|
||
|
|
||
|
si.cbSize = sizeof(SCROLLINFO);
|
||
|
|
||
|
if (style & WS_HSCROLL)
|
||
|
{
|
||
|
|
||
|
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
|
||
|
si.nMin = 0;
|
||
|
si.nMax = rcView.right - rcView.left - 1;
|
||
|
//DebugMsg(DM_TRACE, "si.nMax rcView.right - rcView.left - 1 %x", si.nMax);
|
||
|
|
||
|
si.nPage = min(rcClient.right, rcView.right) - rcClient.left;
|
||
|
//DebugMsg(DM_TRACE, "si.nPage %x", si.nPage);
|
||
|
|
||
|
si.nPos = 0;
|
||
|
if (rcView.left < rcClient.left)
|
||
|
si.nPos = rcClient.left - rcView.left;
|
||
|
//DebugMsg(DM_TRACE, "si.nPos %x", si.nPos);
|
||
|
|
||
|
#ifdef IEWIN31_25
|
||
|
plv->cxScrollPage = (int)si.nPage;
|
||
|
SetScrollRange( plv->hwnd, SB_HORZ, (int) si.nMin,
|
||
|
max( (int)(si.nMax-si.nPage+1), 0 ), FALSE );
|
||
|
SetScrollPos( plv->hwnd, SB_HORZ, (int) si.nPos, TRUE );
|
||
|
#else
|
||
|
SetScrollInfo(plv->hwnd, SB_HORZ, &si, TRUE);
|
||
|
#endif //IEWIN31_25
|
||
|
|
||
|
}
|
||
|
else if (styleOld & WS_HSCROLL)
|
||
|
{
|
||
|
SetScrollRange(plv->hwnd, SB_HORZ, 0, 0, TRUE);
|
||
|
}
|
||
|
|
||
|
if (style & WS_VSCROLL)
|
||
|
{
|
||
|
|
||
|
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
|
||
|
si.nMin = 0;
|
||
|
si.nMax = rcView.bottom - rcView.top - 1;
|
||
|
si.nPage = min(rcClient.bottom, rcView.bottom) - rcClient.top;
|
||
|
si.nPos = 0;
|
||
|
if (rcView.top < rcClient.top)
|
||
|
si.nPos = rcClient.top - rcView.top;
|
||
|
|
||
|
#ifdef IEWIN31_25
|
||
|
plv->cyScrollPage = (int)si.nPage;
|
||
|
SetScrollRange( plv->hwnd, SB_VERT, (int) si.nMin,
|
||
|
max( (int)(si.nMax-si.nPage+1), 0 ), FALSE );
|
||
|
SetScrollPos( plv->hwnd, SB_VERT, (int) si.nPos, TRUE );
|
||
|
#else
|
||
|
SetScrollInfo(plv->hwnd, SB_VERT, &si, TRUE);
|
||
|
#endif //IEWIN31_25
|
||
|
|
||
|
}
|
||
|
else if (styleOld & WS_VSCROLL)
|
||
|
{
|
||
|
SetScrollRange(plv->hwnd, SB_VERT, 0, 0, TRUE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void FAR PASCAL ListView_ComOnScroll(LV* plv, UINT code, int posNew, int sb,
|
||
|
int cLine, int cPage,
|
||
|
SCROLLPROC lpfnScroll2)
|
||
|
{
|
||
|
int pos;
|
||
|
SCROLLINFO si;
|
||
|
BOOL fVert = (sb == SB_VERT);
|
||
|
|
||
|
#ifdef IEWIN31_25
|
||
|
{
|
||
|
int n1, n2;
|
||
|
GetScrollRange( plv->hwnd, sb, &n1, &n2 );
|
||
|
si.nMin = n1;
|
||
|
si.nMax = n2;
|
||
|
si.nPos = GetScrollPos( plv->hwnd, sb );
|
||
|
si.nPage = fVert ? plv->cyScrollPage : plv->cxScrollPage;
|
||
|
}
|
||
|
#else
|
||
|
si.cbSize = sizeof(SCROLLINFO);
|
||
|
si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
|
||
|
|
||
|
if (!GetScrollInfo(plv->hwnd, sb, &si)) {
|
||
|
return;
|
||
|
}
|
||
|
#endif //IEWIN31_25
|
||
|
|
||
|
if (cPage != -1)
|
||
|
si.nPage = cPage;
|
||
|
|
||
|
si.nMax -= (si.nPage - 1);
|
||
|
|
||
|
if (si.nMax < si.nMin)
|
||
|
si.nMax = si.nMin;
|
||
|
|
||
|
pos = (int)si.nPos; // current position
|
||
|
|
||
|
switch (code)
|
||
|
{
|
||
|
case SB_LEFT:
|
||
|
si.nPos = si.nMin;
|
||
|
break;
|
||
|
case SB_RIGHT:
|
||
|
si.nPos = si.nMax;
|
||
|
break;
|
||
|
case SB_PAGELEFT:
|
||
|
si.nPos -= si.nPage;
|
||
|
break;
|
||
|
case SB_LINELEFT:
|
||
|
si.nPos -= cLine;
|
||
|
break;
|
||
|
case SB_PAGERIGHT:
|
||
|
si.nPos += si.nPage;
|
||
|
break;
|
||
|
case SB_LINERIGHT:
|
||
|
si.nPos += cLine;
|
||
|
break;
|
||
|
|
||
|
case SB_THUMBTRACK:
|
||
|
si.nPos = posNew;
|
||
|
break;
|
||
|
|
||
|
case SB_ENDSCROLL:
|
||
|
// When scroll bar tracking is over, ensure scroll bars
|
||
|
// are properly updated...
|
||
|
|
||
|
ListView_UpdateScrollBars(plv);
|
||
|
return;
|
||
|
|
||
|
default:
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
si.fMask = SIF_POS;
|
||
|
#ifdef IEWIN31_25
|
||
|
SetScrollPos( plv->hwnd, sb, (int) si.nPos, TRUE );
|
||
|
si.nPos = GetScrollPos( plv->hwnd, sb );
|
||
|
#else
|
||
|
si.nPos = SetScrollInfo(plv->hwnd, sb, &si, TRUE);
|
||
|
#endif
|
||
|
|
||
|
if (pos != si.nPos)
|
||
|
{
|
||
|
int delta = (int)si.nPos - pos;
|
||
|
int dx = 0, dy = 0;
|
||
|
if (fVert)
|
||
|
dy = delta;
|
||
|
else
|
||
|
dx = delta;
|
||
|
lpfnScroll2(plv, dx, dy);
|
||
|
UpdateWindow(plv->hwnd);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void FAR PASCAL ListView_IScroll2(LV* plv, int dx, int dy)
|
||
|
{
|
||
|
if (dx | dy)
|
||
|
{
|
||
|
plv->ptOrigin.x += dx;
|
||
|
plv->ptOrigin.y += dy;
|
||
|
|
||
|
ScrollWindowEx(plv->hwnd, -dx, -dy, NULL, NULL, NULL, NULL,
|
||
|
SW_INVALIDATE | SW_ERASE);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NEAR ListView_IOnScroll(LV* plv, UINT code, int posNew, int sb)
|
||
|
{
|
||
|
int cLine;
|
||
|
|
||
|
if (sb == SB_VERT)
|
||
|
{
|
||
|
cLine = lv_cyIconSpacing / 2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
cLine = lv_cxIconSpacing / 2;
|
||
|
}
|
||
|
|
||
|
ListView_ComOnScroll(plv, code, posNew, sb,
|
||
|
cLine, -1,
|
||
|
ListView_IScroll2);
|
||
|
|
||
|
}
|
||
|
|
||
|
// NOTE: there is very similar code in the treeview
|
||
|
|
||
|
// Totally disgusting hack in order to catch VK_RETURN
|
||
|
// before edit control gets it.
|
||
|
|
||
|
LRESULT CALLBACK ListView_EditWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
LV* plv = ListView_GetPtr(GetParent(hwnd));
|
||
|
|
||
|
Assert(plv);
|
||
|
|
||
|
#ifdef FE_IME
|
||
|
if ( LOWORD(GetKeyboardLayout(0L)) == 0x0411 )
|
||
|
{
|
||
|
// The following code adds IME awareness to the
|
||
|
// listview's label editing. Currently just for Japanese.
|
||
|
|
||
|
DWORD dwGcs;
|
||
|
|
||
|
if (msg==WM_SIZE)
|
||
|
{
|
||
|
// If it's given the size, tell it to an IME.
|
||
|
|
||
|
ListView_SizeIME(hwnd);
|
||
|
}
|
||
|
else if (msg == EM_SETLIMITTEXT )
|
||
|
{
|
||
|
if (wParam < 13)
|
||
|
plv->flags |= LVF_DONTDRAWCOMP;
|
||
|
else
|
||
|
plv->flags &= ~LVF_DONTDRAWCOMP;
|
||
|
}
|
||
|
// Give up to draw IME composition by ourselves in case
|
||
|
// we're working on SFN. Win95d-5709
|
||
|
else if (!(plv->flags & LVF_DONTDRAWCOMP ))
|
||
|
{
|
||
|
switch (msg)
|
||
|
{
|
||
|
|
||
|
case WM_IME_STARTCOMPOSITION:
|
||
|
case WM_IME_ENDCOMPOSITION:
|
||
|
return 0L;
|
||
|
|
||
|
|
||
|
case WM_IME_COMPOSITION:
|
||
|
|
||
|
// If lParam has no data available bit, it implies
|
||
|
// canceling composition.
|
||
|
// ListView_InsertComposition() tries to get composition
|
||
|
// string w/ GCS_COMPSTR then remove it from edit control if
|
||
|
// nothing is available.
|
||
|
|
||
|
if ( !lParam )
|
||
|
dwGcs = GCS_COMPSTR;
|
||
|
else
|
||
|
dwGcs = lParam;
|
||
|
|
||
|
ListView_InsertComposition(hwnd, wParam, dwGcs, plv);
|
||
|
ListView_PaintComposition(hwnd,plv);
|
||
|
return 0L;
|
||
|
|
||
|
case WM_IME_SETCONTEXT:
|
||
|
|
||
|
// We draw composition string.
|
||
|
|
||
|
lParam &= ~ISC_SHOWUICOMPOSITIONWINDOW;
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
// the other messages should simply be processed
|
||
|
// in this subclass procedure.
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
switch (msg)
|
||
|
{
|
||
|
case WM_SETTEXT:
|
||
|
SetWindowID(hwnd, 1);
|
||
|
break;
|
||
|
|
||
|
|
||
|
case WM_KEYDOWN:
|
||
|
switch (wParam)
|
||
|
{
|
||
|
case VK_RETURN:
|
||
|
ListView_DismissEdit(plv, FALSE);
|
||
|
return 0L;
|
||
|
|
||
|
case VK_ESCAPE:
|
||
|
ListView_DismissEdit(plv, TRUE);
|
||
|
return 0L;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_CHAR:
|
||
|
switch (wParam)
|
||
|
{
|
||
|
case VK_RETURN:
|
||
|
// Eat the character, so edit control wont beep!
|
||
|
return 0L;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case WM_GETDLGCODE:
|
||
|
return DLGC_WANTALLKEYS; /* editing name, no dialog handling right now */
|
||
|
}
|
||
|
|
||
|
return CallWindowProc(plv->pfnEditWndProc, hwnd, msg, wParam, lParam);
|
||
|
}
|
||
|
|
||
|
// BUGBUG: very similar routine in treeview
|
||
|
|
||
|
void NEAR ListView_SetEditSize(LV* plv)
|
||
|
{
|
||
|
RECT rcLabel;
|
||
|
LISTITEM FAR* pitem;
|
||
|
|
||
|
pitem = ListView_GetItemPtr(plv, plv->iEdit);
|
||
|
if (!pitem)
|
||
|
{
|
||
|
ListView_DismissEdit(plv, TRUE); // cancel edits
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
ListView_GetRects(plv, plv->iEdit, NULL, &rcLabel, NULL, NULL);
|
||
|
|
||
|
// OffsetRect(&rc, rcLabel.left + g_cxLabelMargin + g_cxBorder,
|
||
|
// (rcLabel.bottom + rcLabel.top - rc.bottom) / 2 + g_cyBorder);
|
||
|
// OffsetRect(&rc, rcLabel.left + g_cxLabelMargin , rcLabel.top);
|
||
|
|
||
|
// get the text bounding rect
|
||
|
|
||
|
if (ListView_IsIconView(plv))
|
||
|
{
|
||
|
// We should not adjust y-positoin in case of the icon view.
|
||
|
InflateRect(&rcLabel, -g_cxLabelMargin, -g_cyBorder);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Special case for single-line & centered
|
||
|
InflateRect(&rcLabel, -g_cxLabelMargin - g_cxBorder, (-(rcLabel.bottom - rcLabel.top - plv->cyLabelChar) / 2) - g_cyBorder);
|
||
|
}
|
||
|
|
||
|
SetEditInPlaceSize(plv->hwndEdit, &rcLabel, plv->hfontLabel, ListView_IsIconView(plv) && !(plv->style & LVS_NOLABELWRAP));
|
||
|
}
|
||
|
|
||
|
// to avoid eating too much stack
|
||
|
void NEAR ListView_DoOnEditLabel(LV *plv, int i, LPSTR pszInitial)
|
||
|
{
|
||
|
char szLabel[CCHLABELMAX];
|
||
|
LV_ITEM item;
|
||
|
|
||
|
item.mask = LVIF_TEXT;
|
||
|
item.iItem = i;
|
||
|
item.iSubItem = 0;
|
||
|
item.pszText = szLabel;
|
||
|
item.cchTextMax = sizeof(szLabel);
|
||
|
ListView_OnGetItem(plv, &item);
|
||
|
|
||
|
if (!item.pszText)
|
||
|
return;
|
||
|
|
||
|
// Make sure the edited item has the focus.
|
||
|
if (plv->iFocus != i)
|
||
|
ListView_SetFocusSel(plv, i, TRUE, TRUE, FALSE);
|
||
|
|
||
|
// Make sure the item is fully visible
|
||
|
ListView_OnEnsureVisible(plv, i, FALSE); // fPartialOK == FALSE
|
||
|
|
||
|
plv->hwndEdit = CreateEditInPlaceWindow(plv->hwnd,
|
||
|
pszInitial? pszInitial : item.pszText, sizeof(szLabel),
|
||
|
ListView_IsIconView(plv) ?
|
||
|
(WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD | ES_CENTER | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL) :
|
||
|
(WS_BORDER | WS_CLIPSIBLINGS | WS_CHILD | ES_LEFT | ES_AUTOHSCROLL), plv->hfontLabel);
|
||
|
if (plv->hwndEdit)
|
||
|
{
|
||
|
LISTITEM FAR* pitem;
|
||
|
LV_DISPINFO nm;
|
||
|
|
||
|
// We create the edit window but have not shown it. Ask the owner
|
||
|
// if they are interested or not.
|
||
|
// If we passed in initial text set the ID to be dirty...
|
||
|
if (pszInitial)
|
||
|
SetWindowID(plv->hwndEdit, 1);
|
||
|
|
||
|
if (!(pitem = ListView_GetItemPtr(plv, i)))
|
||
|
{
|
||
|
DestroyWindow(plv->hwndEdit);
|
||
|
plv->hwndEdit = NULL;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
nm.item.iItem = i;
|
||
|
nm.item.iSubItem = 0;
|
||
|
nm.item.lParam = pitem->lParam;
|
||
|
|
||
|
// if they have LVS_EDITLABELS but return non-FALSE here, stop!
|
||
|
if ((BOOL)SendNotify(plv->hwndParent, plv->hwnd, LVN_BEGINLABELEDIT, &nm.hdr))
|
||
|
{
|
||
|
DestroyWindow(plv->hwndEdit);
|
||
|
plv->hwndEdit = NULL;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Ok To continue - so Show the window and set focus to it.
|
||
|
SetFocus(plv->hwndEdit);
|
||
|
ShowWindow(plv->hwndEdit, SW_SHOW);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
void FAR PASCAL RescrollEditWindow(HWND hwndEdit)
|
||
|
{
|
||
|
Edit_SetSel(hwndEdit, 32000, 32000); // move to the end
|
||
|
Edit_SetSel(hwndEdit, 0, 32000); // select all text
|
||
|
}
|
||
|
// BUGBUG: very similar code in treeview.c
|
||
|
|
||
|
HWND NEAR ListView_OnEditLabel(LV* plv, int i, LPSTR pszInitialText)
|
||
|
{
|
||
|
|
||
|
// this eats stack
|
||
|
ListView_DismissEdit(plv, FALSE);
|
||
|
|
||
|
if (!(plv->style & LVS_EDITLABELS) || (GetFocus() != plv->hwnd) ||
|
||
|
(i == -1))
|
||
|
return(NULL); // Does not support this.
|
||
|
|
||
|
ListView_DoOnEditLabel(plv, i, pszInitialText);
|
||
|
|
||
|
if (plv->hwndEdit) {
|
||
|
|
||
|
plv->iEdit = i;
|
||
|
|
||
|
plv->pfnEditWndProc = SubclassWindow(plv->hwndEdit, ListView_EditWndProc);
|
||
|
|
||
|
#ifdef FE_IME
|
||
|
if (SendMessage(plv->hwndEdit, EM_GETLIMITTEXT, (WPARAM)0, (LPARAM)0)<13)
|
||
|
{
|
||
|
plv->flags |= LVF_DONTDRAWCOMP;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
ListView_SetEditSize(plv);
|
||
|
RescrollEditWindow(plv->hwndEdit);
|
||
|
}
|
||
|
|
||
|
return plv->hwndEdit;
|
||
|
}
|
||
|
|
||
|
|
||
|
// BUGBUG: very similar code in treeview.c
|
||
|
|
||
|
BOOL NEAR ListView_DismissEdit(LV* plv, BOOL fCancel)
|
||
|
{
|
||
|
LISTITEM FAR* pitem;
|
||
|
BOOL fOkToContinue = TRUE;
|
||
|
HWND hwndEdit = plv->hwndEdit;
|
||
|
HWND hwnd = plv->hwnd;
|
||
|
int iEdit;
|
||
|
#ifdef FE_IME
|
||
|
HIMC himc;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
if (plv->fNoDismissEdit)
|
||
|
return FALSE;
|
||
|
|
||
|
if (!hwndEdit) {
|
||
|
// Also make sure there are no pending edits...
|
||
|
ListView_CancelPendingEdit(plv);
|
||
|
return TRUE; // It is OK to process as normal...
|
||
|
}
|
||
|
|
||
|
// If the window is not visible, we are probably in the process
|
||
|
// of being destroyed, so assume that we are being destroyed
|
||
|
if (!IsWindowVisible(plv->hwnd))
|
||
|
fCancel = TRUE;
|
||
|
|
||
|
|
||
|
// We are using the Window ID of the control as a BOOL to
|
||
|
// state if it is dirty or not.
|
||
|
switch (GetWindowID(hwndEdit)) {
|
||
|
case 0:
|
||
|
// The edit control is not dirty so act like cancel.
|
||
|
fCancel = TRUE;
|
||
|
// Fall through to set window so we will not recurse!
|
||
|
case 1:
|
||
|
// The edit control is dirty so continue.
|
||
|
SetWindowID(hwndEdit, 2); // Don't recurse
|
||
|
break;
|
||
|
case 2:
|
||
|
// We are in the process of processing an update now, bail out
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// BUGBUG: this will fail if the program deleted the items out
|
||
|
// from underneath us (while we are waiting for the edit timer).
|
||
|
// make delete item invalidate our edit item
|
||
|
// We uncouple the edit control and hwnd out from under this as
|
||
|
// to allow code that process the LVN_ENDLABELEDIT to reenter
|
||
|
// editing mode if an error happens.
|
||
|
iEdit = plv->iEdit;
|
||
|
|
||
|
|
||
|
pitem = ListView_GetItemPtr(plv, iEdit);
|
||
|
Assert(pitem);
|
||
|
|
||
|
if (pitem != NULL)
|
||
|
{
|
||
|
LV_DISPINFO nm;
|
||
|
char szLabel[CCHLABELMAX];
|
||
|
|
||
|
nm.item.iItem = iEdit;
|
||
|
nm.item.lParam = pitem->lParam;
|
||
|
nm.item.iSubItem = 0;
|
||
|
nm.item.cchTextMax = 0;
|
||
|
nm.item.mask = 0;
|
||
|
|
||
|
if (fCancel)
|
||
|
nm.item.pszText = NULL;
|
||
|
else {
|
||
|
Edit_GetText(hwndEdit, szLabel, sizeof(szLabel));
|
||
|
nm.item.pszText = szLabel;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Notify the parent that we the label editing has completed.
|
||
|
// We will use the LV_DISPINFO structure to return the new
|
||
|
// label in. The parent still has the old text available by
|
||
|
// calling the GetItemText function.
|
||
|
|
||
|
|
||
|
fOkToContinue = (BOOL)SendNotify(plv->hwndParent, plv->hwnd, LVN_ENDLABELEDIT, &nm.hdr);
|
||
|
if (!IsWindow(hwnd)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
if (fOkToContinue && !fCancel)
|
||
|
{
|
||
|
|
||
|
// If the item has the text set as CALLBACK, we will let the
|
||
|
// ower know that they are supposed to set the item text in
|
||
|
// their own data structures. Else we will simply update the
|
||
|
// text in the actual view.
|
||
|
|
||
|
if (pitem->pszText != LPSTR_TEXTCALLBACK)
|
||
|
{
|
||
|
// Set the item text (everything's set up in nm.item)
|
||
|
|
||
|
nm.item.mask = LVIF_TEXT;
|
||
|
ListView_OnSetItem(plv, &nm.item);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SendNotify(plv->hwndParent, plv->hwnd, LVN_SETDISPINFO, &nm.hdr);
|
||
|
|
||
|
// Also we will assume that our cached size is invalid...
|
||
|
plv->rcView.left = RECOMPUTE;
|
||
|
pitem->cyMultiLabel = pitem->cxSingleLabel = pitem->cxMultiLabel = SRECOMPUTE;
|
||
|
}
|
||
|
}
|
||
|
#ifdef FE_IME
|
||
|
if (LOWORD(GetKeyboardLayout(0L)) == 0x0411 && (himc = ImmGetContext(hwndEdit)))
|
||
|
{
|
||
|
ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_COMPLETE, 0L);
|
||
|
ImmReleaseContext(hwndEdit, himc);
|
||
|
}
|
||
|
#endif
|
||
|
// redraw
|
||
|
ListView_InvalidateItem(plv, iEdit, FALSE, RDW_INVALIDATE | RDW_ERASE);
|
||
|
}
|
||
|
|
||
|
// If the hwnedit is still us clear out the variables
|
||
|
if (hwndEdit == plv->hwndEdit)
|
||
|
{
|
||
|
plv->iEdit = -1;
|
||
|
plv->hwndEdit = NULL; // avoid being reentered
|
||
|
}
|
||
|
DestroyWindow(hwndEdit);
|
||
|
|
||
|
return fOkToContinue;
|
||
|
}
|
||
|
|
||
|
|
||
|
// This function will scall the icon positions that are stored in the
|
||
|
// item structures between large and small icon view.
|
||
|
|
||
|
void NEAR ListView_ScaleIconPositions(LV* plv, BOOL fSmallIconView)
|
||
|
{
|
||
|
int cxItem, cyItem;
|
||
|
HWND hwnd;
|
||
|
int i;
|
||
|
|
||
|
cxItem = plv->cxItem;
|
||
|
cyItem = plv->cyItem;
|
||
|
hwnd = plv->hwnd;
|
||
|
|
||
|
if (fSmallIconView)
|
||
|
{
|
||
|
if (plv->flags & LVF_ICONPOSSML)
|
||
|
return; // Already done
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if ((plv->flags & LVF_ICONPOSSML) == 0)
|
||
|
return; // dito
|
||
|
}
|
||
|
|
||
|
// Last but not least update our bit!
|
||
|
plv->flags ^= LVF_ICONPOSSML;
|
||
|
|
||
|
// We will now loop through all of the items and update their coordinats
|
||
|
// We will update th position directly into the view instead of calling
|
||
|
// SetItemPosition as to not do 5000 invalidates and messages...
|
||
|
|
||
|
for (i = 0; i < ListView_Count(plv); i++)
|
||
|
{
|
||
|
LISTITEM FAR* pitem = ListView_FastGetItemPtr(plv, i);
|
||
|
|
||
|
if (fSmallIconView)
|
||
|
{
|
||
|
if (pitem->pt.y != RECOMPUTE) {
|
||
|
pitem->pt.x = MulDiv(pitem->pt.x - g_cxIconOffset, cxItem, lv_cxIconSpacing);
|
||
|
pitem->pt.y = MulDiv(pitem->pt.y - g_cyIconOffset, cyItem, lv_cyIconSpacing);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
pitem->pt.x = MulDiv(pitem->pt.x, lv_cxIconSpacing, cxItem) + g_cxIconOffset;
|
||
|
pitem->pt.y = MulDiv(pitem->pt.y, lv_cyIconSpacing, cyItem) + g_cyIconOffset;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (plv->style & LVS_AUTOARRANGE)
|
||
|
{
|
||
|
// If autoarrange is turned on, the arrange function will do
|
||
|
// everything that is needed.
|
||
|
ListView_OnArrange(plv, LVA_DEFAULT);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
plv->rcView.left = RECOMPUTE;
|
||
|
|
||
|
|
||
|
// Also scale the origin
|
||
|
|
||
|
if (fSmallIconView)
|
||
|
{
|
||
|
plv->ptOrigin.x = MulDiv(plv->ptOrigin.x, cxItem, lv_cxIconSpacing);
|
||
|
plv->ptOrigin.y = MulDiv(plv->ptOrigin.y, cyItem, lv_cyIconSpacing);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
plv->ptOrigin.x = MulDiv(plv->ptOrigin.x, lv_cxIconSpacing, cxItem);
|
||
|
plv->ptOrigin.y = MulDiv(plv->ptOrigin.y, lv_cyIconSpacing, cyItem);
|
||
|
}
|
||
|
|
||
|
|
||
|
// Make sure it fully redraws correctly
|
||
|
RedrawWindow(plv->hwnd, NULL, NULL, RDW_INVALIDATE | RDW_ERASE);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
HWND FAR PASCAL CreateEditInPlaceWindow(HWND hwnd, LPCSTR lpText, int cbText, LONG style, HFONT hFont)
|
||
|
{
|
||
|
HWND hwndEdit;
|
||
|
|
||
|
hwndEdit = CreateWindowEx(0 /* WS_EX_CLIENTEDGE */, "EDIT", lpText, style,
|
||
|
0, 0, 0, 0, hwnd, NULL, HINST_THISDLL, NULL);
|
||
|
|
||
|
if (hwndEdit) {
|
||
|
|
||
|
Edit_LimitText(hwndEdit, cbText);
|
||
|
|
||
|
Edit_SetSel(hwndEdit, 0, 0); // move to the beginning
|
||
|
|
||
|
FORWARD_WM_SETFONT(hwndEdit, hFont, FALSE, SendMessage);
|
||
|
|
||
|
}
|
||
|
|
||
|
return hwndEdit;
|
||
|
}
|
||
|
|
||
|
|
||
|
// BUGBUG: very similar routine in treeview
|
||
|
|
||
|
// in:
|
||
|
// hwndEdit edit control to position in client coords of parent window
|
||
|
// prc bonding rect of the text, used to position everthing
|
||
|
// hFont font being used
|
||
|
// fWrap if this is a wrapped type edit
|
||
|
|
||
|
// Notes:
|
||
|
// The top-left corner of the bouding rectangle must be the position
|
||
|
// the client uses to draw text. We adjust the edit field rectangle
|
||
|
// appropriately.
|
||
|
|
||
|
|
||
|
void FAR PASCAL SetEditInPlaceSize(HWND hwndEdit, RECT FAR *prc, HFONT hFont, BOOL fWrap)
|
||
|
{
|
||
|
RECT rc, rcClient, rcFormat;
|
||
|
char szLabel[CCHLABELMAX + 1];
|
||
|
int cchLabel, cxIconTextWidth;
|
||
|
HDC hdc;
|
||
|
HWND hwndParent = GetParent(hwndEdit);
|
||
|
#ifdef DBCS
|
||
|
short wRightMgn;
|
||
|
#endif
|
||
|
|
||
|
|
||
|
cchLabel = Edit_GetText(hwndEdit, szLabel, sizeof(szLabel));
|
||
|
|
||
|
|
||
|
if (szLabel[0] == 0)
|
||
|
{
|
||
|
lstrcpy(szLabel, c_szSpace);
|
||
|
cchLabel = 1;
|
||
|
}
|
||
|
|
||
|
hdc = GetDC(hwndParent);
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
//DrawFocusRect(hdc, prc); // this is the rect they are passing in
|
||
|
#endif
|
||
|
|
||
|
SelectFont(hdc, hFont);
|
||
|
|
||
|
cxIconTextWidth = g_cxIconSpacing - g_cxLabelMargin * 2;
|
||
|
rc.left = rc.top = rc.bottom = 0;
|
||
|
rc.right = cxIconTextWidth; // for DT_LVWRAP
|
||
|
|
||
|
// REVIEW: we might want to include DT_EDITCONTROL in our DT_LVWRAP
|
||
|
|
||
|
// If the string is NULL display a rectangle that is visible.
|
||
|
DrawText(hdc, szLabel, cchLabel, &rc, fWrap ? (DT_LVWRAP | DT_CALCRECT) : (DT_LV | DT_CALCRECT));
|
||
|
|
||
|
// Minimum text box size is 1/4 icon spacing size
|
||
|
if (rc.right < g_cxIconSpacing / 4)
|
||
|
rc.right = g_cxIconSpacing / 4;
|
||
|
|
||
|
// position the text rect based on the text rect passed in
|
||
|
// if wrapping, center the edit control around the text mid point
|
||
|
|
||
|
OffsetRect(&rc,
|
||
|
fWrap ? prc->left + ((prc->right - prc->left) - (rc.right - rc.left)) / 2 : prc->left,
|
||
|
fWrap ? prc->top : prc->top + ((prc->bottom - prc->top) - (rc.bottom - rc.top)) / 2 );
|
||
|
|
||
|
// give a little space to ease the editing of this thing
|
||
|
if (!fWrap)
|
||
|
rc.right += g_cxLabelMargin * 4;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
//DrawFocusRect(hdc, &rc);
|
||
|
#endif
|
||
|
|
||
|
ReleaseDC(hwndParent, hdc);
|
||
|
|
||
|
|
||
|
// #5688: We need to make it sure that the whole edit window is
|
||
|
// always visible. We should not extend it to the outside of
|
||
|
// the parent window.
|
||
|
|
||
|
{
|
||
|
BOOL fSuccess;
|
||
|
GetClientRect(hwndParent, &rcClient);
|
||
|
fSuccess = IntersectRect(&rc, &rc, &rcClient);
|
||
|
Assert(fSuccess || IsRectEmpty(&rcClient));
|
||
|
}
|
||
|
|
||
|
|
||
|
// Inflate it after the clipping, because it's ok to hide border.
|
||
|
|
||
|
SendMessage(hwndEdit, EM_GETRECT, 0, (LPARAM)(LPRECT)&rcFormat);
|
||
|
// account for the border style, REVIEW: there might be a better way!
|
||
|
#ifdef DBCS
|
||
|
// some FE fonts have suprisingly big negative C width
|
||
|
#ifndef IEWIN31_25
|
||
|
wRightMgn=HIWORD(SendMessage(hwndEdit, EM_GETMARGINS, 0, 0));
|
||
|
#else
|
||
|
wRightMgn=0;
|
||
|
#endif
|
||
|
InflateRect(&rc, rcFormat.left + wRightMgn + g_cxEdge, rcFormat.top + g_cyEdge);
|
||
|
#else
|
||
|
InflateRect(&rc, rcFormat.left + g_cxEdge, rcFormat.top + g_cyEdge);
|
||
|
#endif
|
||
|
rc.right += g_cyEdge; // try to leave a little more for dual blanks
|
||
|
|
||
|
HideCaret(hwndEdit);
|
||
|
|
||
|
SetWindowPos(hwndEdit, NULL, rc.left, rc.top,
|
||
|
rc.right - rc.left, rc.bottom - rc.top, SWP_NOZORDER | SWP_NOACTIVATE);
|
||
|
|
||
|
ShowCaret(hwndEdit);
|
||
|
}
|
||
|
|
||
|
|
||
|
UINT NEAR PASCAL ListView_DrawImage(LV* plv, LV_ITEM FAR* pitem, HDC hdc, int x, int y, UINT fDraw)
|
||
|
{
|
||
|
UINT fText = SHDT_DESELECTED;
|
||
|
UINT fImage = ILD_NORMAL;
|
||
|
COLORREF clr;
|
||
|
HIMAGELIST himl;
|
||
|
|
||
|
fImage = (pitem->state & LVIS_OVERLAYMASK);
|
||
|
fText = SHDT_DESELECTED;
|
||
|
|
||
|
himl = ListView_IsIconView(plv) ? plv->himl : plv->himlSmall;
|
||
|
|
||
|
// the item can have one of 4 states, for 3 looks:
|
||
|
// normal simple drawing
|
||
|
// selected, no focus light image highlight, no text hi
|
||
|
// selected w/ focus highlight image & text
|
||
|
// drop highlighting highlight image & text
|
||
|
|
||
|
if ((pitem->state & LVIS_DROPHILITED) ||
|
||
|
((fDraw & LVDI_SELECTED) && (pitem->state & LVIS_SELECTED)))
|
||
|
{
|
||
|
fText = SHDT_SELECTED;
|
||
|
fImage |= ILD_BLEND50;
|
||
|
clr = CLR_HILIGHT;
|
||
|
}
|
||
|
|
||
|
if (pitem->state & LVIS_CUT)
|
||
|
{
|
||
|
fImage |= ILD_BLEND50;
|
||
|
clr = plv->clrBk;
|
||
|
}
|
||
|
|
||
|
#if 0 // dont do a selected but dont have the focus vis.
|
||
|
else if (item.state & LVIS_SELECTED)
|
||
|
{
|
||
|
fImage |= ILD_BLEND25;
|
||
|
clr = CLR_HILIGHT;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (!(fDraw & LVDI_NOIMAGE))
|
||
|
{
|
||
|
if (himl) {
|
||
|
ImageList_DrawEx(himl, pitem->iImage, hdc, x, y, 0, 0, plv->clrBk, clr, fImage);
|
||
|
}
|
||
|
|
||
|
if (plv->himlState) {
|
||
|
if (LV_StateImageValue(pitem)) {
|
||
|
int iState = LV_StateImageIndex(pitem);
|
||
|
int dyImage =
|
||
|
(himl) ?
|
||
|
( (ListView_IsIconView(plv) ? plv->cyIcon : plv->cySmIcon) - plv->cyState)
|
||
|
: 0;
|
||
|
ImageList_Draw(plv->himlState, iState, hdc, x-plv->cxState,
|
||
|
y + dyImage,
|
||
|
ILD_NORMAL);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return fText;
|
||
|
}
|
||
|
|
||
|
#ifdef FE_IME
|
||
|
void NEAR PASCAL ListView_SizeIME(HWND hwnd)
|
||
|
{
|
||
|
HIMC himc;
|
||
|
#ifdef _WIN32
|
||
|
CANDIDATEFORM candf;
|
||
|
#else
|
||
|
CANDIDATEFORM16 candf;
|
||
|
#endif
|
||
|
RECT rc;
|
||
|
|
||
|
// If this subclass procedure is being called with WM_SIZE,
|
||
|
// This routine sets the rectangle to an IME.
|
||
|
|
||
|
GetClientRect(hwnd, &rc);
|
||
|
|
||
|
|
||
|
// Candidate stuff
|
||
|
candf.dwIndex = 0; // Bogus assumption for Japanese IME.
|
||
|
candf.dwStyle = CFS_EXCLUDE;
|
||
|
candf.ptCurrentPos.x = rc.left;
|
||
|
candf.ptCurrentPos.y = rc.bottom;
|
||
|
candf.rcArea = rc;
|
||
|
|
||
|
if (himc=ImmGetContext(hwnd))
|
||
|
{
|
||
|
ImmSetCandidateWindow(himc, &candf);
|
||
|
ImmReleaseContext(hwnd, himc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
LPSTR NEAR PASCAL DoDBCSBoundary(LPSTR lpsz, int FAR *lpcchMax)
|
||
|
{
|
||
|
int i = 0;
|
||
|
|
||
|
while (i < *lpcchMax && *lpsz)
|
||
|
{
|
||
|
i++;
|
||
|
|
||
|
if (IsDBCSLeadByte(*lpsz))
|
||
|
{
|
||
|
|
||
|
if (i >= *lpcchMax)
|
||
|
{
|
||
|
--i; // Wrap up without the last leadbyte.
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
i++;
|
||
|
lpsz+= 2;
|
||
|
}
|
||
|
else
|
||
|
lpsz++;
|
||
|
}
|
||
|
|
||
|
*lpcchMax = i;
|
||
|
|
||
|
return lpsz;
|
||
|
}
|
||
|
|
||
|
void NEAR PASCAL DrawCompositionLine(HWND hwnd, HDC hdc, HFONT hfont, LPCSTR lpszComp, LPCSTR lpszAttr, int ichCompStart, int ichCompEnd, int ichStart)
|
||
|
{
|
||
|
PSTR pszCompStr;
|
||
|
int ichSt,ichEnd;
|
||
|
DWORD dwPos;
|
||
|
BYTE bAttr;
|
||
|
HFONT hfontOld;
|
||
|
|
||
|
COLORREF crFore = GetSysColor(COLOR_WINDOWTEXT);
|
||
|
COLORREF crBack = GetSysColor(COLOR_WINDOW);
|
||
|
COLORREF crForeH = GetSysColor(COLOR_HIGHLIGHTTEXT);
|
||
|
COLORREF crBackH = GetSysColor(COLOR_HIGHLIGHT);
|
||
|
|
||
|
int fnPen;
|
||
|
HPEN hPen;
|
||
|
COLORREF crDrawText;
|
||
|
COLORREF crDrawBack;
|
||
|
COLORREF crOldText;
|
||
|
COLORREF crOldBk;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
while (ichCompStart < ichCompEnd)
|
||
|
{
|
||
|
|
||
|
|
||
|
// Get the fragment to draw
|
||
|
|
||
|
// ichCompStart,ichCompEnd -- index at Edit Control
|
||
|
// ichSt,ichEnd -- index at lpszComp
|
||
|
|
||
|
ichEnd = ichSt = ichCompStart - ichStart;
|
||
|
bAttr = lpszAttr[ichSt];
|
||
|
|
||
|
while (ichEnd < ichCompEnd - ichStart)
|
||
|
{
|
||
|
if (bAttr == lpszAttr[ichEnd])
|
||
|
ichEnd++;
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
pszCompStr = (PSTR)LocalAlloc(LPTR, ichEnd - ichSt + 1 + 1 ); // 1 for NULL.
|
||
|
|
||
|
if (pszCompStr)
|
||
|
{
|
||
|
lstrcpyn(pszCompStr, &lpszComp[ichSt], ichEnd-ichSt+1);
|
||
|
pszCompStr[ichEnd-ichSt] = '\0';
|
||
|
}
|
||
|
|
||
|
|
||
|
// Attribute stuff
|
||
|
switch (bAttr)
|
||
|
{
|
||
|
case ATTR_INPUT:
|
||
|
fnPen = PS_DOT;
|
||
|
crDrawText = crFore;
|
||
|
crDrawBack = crBack;
|
||
|
break;
|
||
|
case ATTR_TARGET_CONVERTED:
|
||
|
case ATTR_TARGET_NOTCONVERTED:
|
||
|
fnPen = PS_DOT;
|
||
|
crDrawText = crForeH;
|
||
|
crDrawBack = crBackH;
|
||
|
break;
|
||
|
case ATTR_CONVERTED:
|
||
|
fnPen = PS_SOLID;
|
||
|
crDrawText = crFore;
|
||
|
crDrawBack = crBack;
|
||
|
break;
|
||
|
}
|
||
|
crOldText = SetTextColor(hdc, crDrawText);
|
||
|
crOldBk = SetBkColor(hdc, crDrawBack);
|
||
|
|
||
|
hfontOld= SelectObject(hdc, hfont);
|
||
|
|
||
|
// Get the start position of composition
|
||
|
|
||
|
dwPos = SendMessage(hwnd, EM_POSFROMCHAR, ichCompStart, 0);
|
||
|
|
||
|
// Draw it.
|
||
|
TextOut(hdc, LOWORD(dwPos), HIWORD(dwPos), pszCompStr, ichEnd-ichSt);
|
||
|
|
||
|
|
||
|
// Underline
|
||
|
hPen = CreatePen(fnPen, 1, crDrawText);
|
||
|
if( hPen ) {
|
||
|
|
||
|
HPEN hpenOld = SelectObject( hdc, hPen );
|
||
|
int iOldBk = SetBkMode( hdc, TRANSPARENT );
|
||
|
SIZE size;
|
||
|
|
||
|
GetTextExtentPoint(hdc, pszCompStr, ichEnd-ichSt, &size);
|
||
|
|
||
|
MoveToEx( hdc, LOWORD(dwPos), size.cy + HIWORD(dwPos)-1, NULL);
|
||
|
|
||
|
LineTo( hdc, size.cx + LOWORD(dwPos), size.cy + HIWORD(dwPos)-1 );
|
||
|
|
||
|
SetBkMode( hdc, iOldBk );
|
||
|
|
||
|
if( hpenOld ) SelectObject( hdc, hpenOld );
|
||
|
|
||
|
DeleteObject( hPen );
|
||
|
}
|
||
|
|
||
|
if (hfontOld)
|
||
|
SelectObject(hdc, hfontOld);
|
||
|
|
||
|
SetTextColor(hdc, crOldText);
|
||
|
SetBkColor(hdc, crOldBk);
|
||
|
|
||
|
LocalFree((HLOCAL)pszCompStr);
|
||
|
|
||
|
//Next fragment
|
||
|
|
||
|
ichCompStart += ichEnd-ichSt;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void NEAR PASCAL ListView_InsertComposition(HWND hwnd, WPARAM wParam, LPARAM lParam, LV *plv)
|
||
|
{
|
||
|
char *pszCompStr;
|
||
|
|
||
|
int cchComp = 0;
|
||
|
int cchCompNew;
|
||
|
int cchMax;
|
||
|
int cchText;
|
||
|
DWORD dwSel;
|
||
|
HIMC himc = (HIMC)0;
|
||
|
|
||
|
|
||
|
// To prevent recursion..
|
||
|
|
||
|
if (plv->flags & LVF_INSERTINGCOMP)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
plv->flags |= LVF_INSERTINGCOMP;
|
||
|
|
||
|
// Don't want to redraw edit during inserting.
|
||
|
|
||
|
SendMessage(hwnd, WM_SETREDRAW, (WPARAM)FALSE, 0);
|
||
|
|
||
|
// If we have RESULT STR, put it to EC first.
|
||
|
|
||
|
if (himc = ImmGetContext(hwnd))
|
||
|
{
|
||
|
#ifdef WIN32
|
||
|
if (!(dwSel = (DWORD)GetProp(hwnd, szIMECompPos)))
|
||
|
dwSel = Edit_GetSel(hwnd);
|
||
|
|
||
|
// Becaues we don't setsel after inserting composition
|
||
|
// in win32 case.
|
||
|
Edit_SetSel(hwnd, LOWORD(dwSel), HIWORD(dwSel));
|
||
|
#endif
|
||
|
if (lParam&GCS_RESULTSTR)
|
||
|
{
|
||
|
pszCompStr = (PSTR)LocalAlloc(LPTR, 1 );
|
||
|
if(cchComp = (int)ImmGetCompositionString(himc, GCS_RESULTSTR, NULL, 0))
|
||
|
{
|
||
|
if(pszCompStr = (PSTR)LocalReAlloc(pszCompStr, cchComp+1,LMEM_MOVEABLE ))
|
||
|
{
|
||
|
ImmGetCompositionString(himc, GCS_RESULTSTR, pszCompStr, cchComp+1);
|
||
|
}
|
||
|
}
|
||
|
pszCompStr[cchComp] = '\0';
|
||
|
Edit_ReplaceSel(hwnd, pszCompStr);
|
||
|
LocalFree((HLOCAL)pszCompStr);
|
||
|
#ifdef WIN32
|
||
|
// There's no longer selection
|
||
|
|
||
|
RemoveProp(hwnd, szIMECompPos);
|
||
|
|
||
|
// Get current cursor pos so that the subsequent composition
|
||
|
// handling will do the right thing.
|
||
|
|
||
|
dwSel = Edit_GetSel(hwnd);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (lParam & GCS_COMPSTR)
|
||
|
{
|
||
|
pszCompStr = (PSTR)LocalAlloc(LPTR, 1 );
|
||
|
if(cchComp = (int)ImmGetCompositionString(himc, GCS_COMPSTR, NULL, 0))
|
||
|
{
|
||
|
|
||
|
pszCompStr = (PSTR)LocalReAlloc(pszCompStr, cchComp+1,LMEM_MOVEABLE );
|
||
|
|
||
|
if (!pszCompStr)
|
||
|
goto ReleaseContext;
|
||
|
|
||
|
ImmGetCompositionString(himc, GCS_COMPSTR, pszCompStr, cchComp+1);
|
||
|
|
||
|
// Get position of the current selection
|
||
|
|
||
|
#ifndef WIN32
|
||
|
dwSel = Edit_GetSel(hwnd);
|
||
|
#endif
|
||
|
cchMax = (int)SendMessage(hwnd, EM_GETLIMITTEXT, 0, 0);
|
||
|
cchText = Edit_GetTextLength(hwnd);
|
||
|
|
||
|
// Cut the composition string if it exceeds limit.
|
||
|
|
||
|
cchCompNew = min(cchComp,
|
||
|
cchMax-(cchText-(HIWORD(dwSel)-LOWORD(dwSel))));
|
||
|
|
||
|
// wrap up the DBCS at the end of string
|
||
|
|
||
|
if (cchCompNew < cchComp)
|
||
|
{
|
||
|
DoDBCSBoundary((LPSTR)pszCompStr, (int FAR *)&cchCompNew);
|
||
|
|
||
|
pszCompStr[cchCompNew] = '\0';
|
||
|
|
||
|
// Reset composition string if we cut it.
|
||
|
ImmSetCompositionString(himc, SCS_SETSTR, pszCompStr, cchCompNew, NULL, 0);
|
||
|
cchComp = cchCompNew;
|
||
|
}
|
||
|
}
|
||
|
pszCompStr[cchComp] = '\0';
|
||
|
|
||
|
// Replace the current selection with composition string.
|
||
|
|
||
|
Edit_ReplaceSel(hwnd, pszCompStr);
|
||
|
|
||
|
LocalFree((HLOCAL)pszCompStr);
|
||
|
|
||
|
// Mark the composition string so that we can replace it again
|
||
|
// for the next time.
|
||
|
|
||
|
|
||
|
#ifdef WIN32
|
||
|
// Don't setsel to avoid flicking
|
||
|
if (cchComp)
|
||
|
{
|
||
|
dwSel = MAKELONG(LOWORD(dwSel),LOWORD(dwSel)+cchComp);
|
||
|
SetProp(hwnd, szIMECompPos, (HANDLE)dwSel);
|
||
|
}
|
||
|
else
|
||
|
RemoveProp(hwnd, szIMECompPos);
|
||
|
#else
|
||
|
// Still use SETSEL for 16bit.
|
||
|
if (cchComp)
|
||
|
Edit_SetSel(hwnd, LOWORD(dwSel), LOWORD(dwSel)+cchComp);
|
||
|
#endif
|
||
|
|
||
|
}
|
||
|
ReleaseContext:
|
||
|
ImmReleaseContext(hwnd, himc);
|
||
|
}
|
||
|
|
||
|
SendMessage(hwnd, WM_SETREDRAW, (WPARAM)TRUE, 0);
|
||
|
|
||
|
// We want to update the size of label edit just once at
|
||
|
// each WM_IME_COMPOSITION processing. ReplaceSel causes several EN_UPDATE
|
||
|
// and it causes ugly flicking too.
|
||
|
|
||
|
SetWindowID(plv->hwndEdit, 1);
|
||
|
ListView_SetEditSize(plv);
|
||
|
|
||
|
RedrawWindow(hwnd, NULL, NULL, RDW_INTERNALPAINT|RDW_INVALIDATE);
|
||
|
UpdateWindow(hwnd);
|
||
|
|
||
|
plv->flags &= ~LVF_INSERTINGCOMP;
|
||
|
}
|
||
|
|
||
|
void NEAR PASCAL ListView_PaintComposition(HWND hwnd, LV * plv)
|
||
|
{
|
||
|
char szCompStr[CCHLABELMAX + 1];
|
||
|
char szCompAttr[CCHLABELMAX + 1];
|
||
|
|
||
|
int cchLine, ichLineStart;
|
||
|
int cchComp = 0;
|
||
|
int nLine;
|
||
|
int ichCompStart, ichCompEnd;
|
||
|
DWORD dwSel;
|
||
|
int cchMax, cchText;
|
||
|
HIMC himc = (HIMC)0;
|
||
|
HDC hdc;
|
||
|
|
||
|
|
||
|
if (plv->flags & LVF_INSERTINGCOMP)
|
||
|
{
|
||
|
// This is the case that ImmSetCompositionString() generates
|
||
|
// WM_IME_COMPOSITION. We're not ready to paint composition here.
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (himc = ImmGetContext(hwnd))
|
||
|
{
|
||
|
|
||
|
cchComp=(UINT)ImmGetCompositionString(himc, GCS_COMPSTR, szCompStr, sizeof(szCompStr));
|
||
|
|
||
|
ImmGetCompositionString(himc, GCS_COMPATTR, szCompAttr, sizeof(szCompStr));
|
||
|
ImmReleaseContext(hwnd, himc);
|
||
|
}
|
||
|
|
||
|
if (cchComp)
|
||
|
{
|
||
|
|
||
|
// Get the position of current selection
|
||
|
|
||
|
#ifdef WIN32
|
||
|
|
||
|
if (!(dwSel = (DWORD)GetProp(hwnd, szIMECompPos)))
|
||
|
dwSel = 0L;
|
||
|
#else
|
||
|
dwSel = Edit_GetSel(hwnd);
|
||
|
#endif
|
||
|
cchMax = (int)SendMessage(hwnd, EM_GETLIMITTEXT, 0, 0);
|
||
|
cchText = Edit_GetTextLength(hwnd);
|
||
|
cchComp = min(cchComp, cchMax-(cchText-(HIWORD(dwSel)-LOWORD(dwSel))));
|
||
|
DoDBCSBoundary((LPSTR)szCompStr, (int FAR *)&cchComp);
|
||
|
szCompStr[cchComp] = '\0';
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
// //
|
||
|
// Draw composition string over the sel string.//
|
||
|
// //
|
||
|
|
||
|
|
||
|
|
||
|
hdc = GetDC(hwnd);
|
||
|
|
||
|
|
||
|
ichCompStart = LOWORD(dwSel);
|
||
|
|
||
|
while (ichCompStart < (int)LOWORD(dwSel) + cchComp)
|
||
|
{
|
||
|
// Get line from each start pos.
|
||
|
|
||
|
nLine = Edit_LineFromChar(hwnd, ichCompStart);
|
||
|
ichLineStart = Edit_LineIndex(hwnd, nLine);
|
||
|
cchLine= Edit_LineLength(hwnd, ichLineStart);
|
||
|
|
||
|
// See if composition string is longer than this line.
|
||
|
|
||
|
if(ichLineStart+cchLine > (int)LOWORD(dwSel)+cchComp)
|
||
|
ichCompEnd = LOWORD(dwSel)+cchComp;
|
||
|
else
|
||
|
{
|
||
|
// Yes, the composition string is longer.
|
||
|
// Take the begining of the next line as next start.
|
||
|
|
||
|
if (ichLineStart+cchLine > ichCompStart)
|
||
|
ichCompEnd = ichLineStart+cchLine;
|
||
|
else
|
||
|
{
|
||
|
// If the starting position is not proceeding,
|
||
|
// let's get out of here.
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Draw the line
|
||
|
|
||
|
DrawCompositionLine(hwnd, hdc, plv->hfontLabel, szCompStr, szCompAttr, ichCompStart, ichCompEnd, LOWORD(dwSel));
|
||
|
|
||
|
ichCompStart = ichCompEnd;
|
||
|
}
|
||
|
|
||
|
ReleaseDC(hwnd, hdc);
|
||
|
}
|
||
|
// We don't want to repaint the window.
|
||
|
ValidateRect(hwnd, NULL);
|
||
|
}
|
||
|
|
||
|
#endif
|