reactos/dll/cpl/desk/monslctl.c

1660 lines
44 KiB
C

#include "desk.h"
static const TCHAR szMonitorSelWndClass[] = TEXT("MONITORSELWNDCLASS");
typedef struct _MONSL_MON
{
RECT rc;
HFONT hFont;
TCHAR szCaption[12];
} MONSL_MON, *PMONSL_MON;
typedef struct _MONITORSELWND
{
HWND hSelf;
HWND hNotify;
HFONT hFont;
SIZE ClientSize;
DWORD UIState;
union
{
DWORD dwInternalFlags;
struct
{
UINT Enabled : 1;
UINT HasFocus : 1;
UINT CanDisplay : 1;
UINT LeftBtnDown : 1;
UINT IsDraggingMonitor : 1;
};
};
DWORD ControlExStyle;
DWORD MonitorsCount;
INT SelectedMonitor;
INT DraggingMonitor;
RECT rcDragging;
POINT ptDrag, ptDragBegin;
SIZE DraggingMargin;
PMONSL_MONINFO MonitorInfo;
PMONSL_MON Monitors;
RECT rcExtent;
RECT rcMonitors;
RECT rcOldMonitors;
POINT ScrollPos;
SIZE Margin;
SIZE SelectionFrame;
HBITMAP hbmDisabledPattern;
HBRUSH hbrDisabled;
} MONITORSELWND, *PMONITORSELWND;
static LRESULT
MonSelNotify(IN PMONITORSELWND infoPtr,
IN UINT code,
IN OUT PVOID data)
{
LRESULT Ret = 0;
if (infoPtr->hNotify != NULL)
{
LPNMHDR pnmh = (LPNMHDR)data;
pnmh->hwndFrom = infoPtr->hSelf;
pnmh->idFrom = GetWindowLongPtr(infoPtr->hSelf,
GWLP_ID);
pnmh->code = code;
Ret = SendMessage(infoPtr->hNotify,
WM_NOTIFY,
(WPARAM)pnmh->idFrom,
(LPARAM)pnmh);
}
return Ret;
}
static LRESULT
MonSelNotifyMonitor(IN PMONITORSELWND infoPtr,
IN UINT code,
IN INT Index,
IN OUT PMONSL_MONNMHDR pmonnmh)
{
pmonnmh->Index = Index;
if (Index >= 0)
{
pmonnmh->MonitorInfo = infoPtr->MonitorInfo[Index];
}
else
{
ZeroMemory(&pmonnmh->MonitorInfo,
sizeof(pmonnmh->MonitorInfo));
}
return MonSelNotify(infoPtr,
code,
pmonnmh);
}
static HFONT
MonSelChangeFont(IN OUT PMONITORSELWND infoPtr,
IN HFONT hFont,
IN BOOL Redraw)
{
HFONT hOldFont = infoPtr->hFont;
infoPtr->hFont = hFont;
if (Redraw)
{
InvalidateRect(infoPtr->hSelf,
NULL,
TRUE);
}
return hOldFont;
}
static VOID
MonSelRectToScreen(IN PMONITORSELWND infoPtr,
IN const RECT *prc,
OUT PRECT prcOnScreen)
{
*prcOnScreen = *prc;
OffsetRect(prcOnScreen,
-infoPtr->ScrollPos.x,
-infoPtr->ScrollPos.y);
}
static VOID
MonSelScreenToPt(IN PMONITORSELWND infoPtr,
IN const POINT *pptOnScreen,
OUT PPOINT ppt)
{
ppt->x = pptOnScreen->x + infoPtr->ScrollPos.x;
ppt->y = pptOnScreen->y + infoPtr->ScrollPos.y;
}
static VOID
MonSelMonInfoToRect(IN const MONSL_MONINFO *pMonInfo,
OUT PRECT prc)
{
prc->left = pMonInfo->Position.x;
prc->top = pMonInfo->Position.y;
prc->right = pMonInfo->Position.x + pMonInfo->Size.cx;
prc->bottom = pMonInfo->Position.y + pMonInfo->Size.cy;
}
static INT
MonSelHitTest(IN PMONITORSELWND infoPtr,
IN const POINT *ppt)
{
POINT pt;
INT Index, Ret = -1;
if (infoPtr->CanDisplay)
{
MonSelScreenToPt(infoPtr,
ppt,
&pt);
for (Index = 0; Index < (INT)infoPtr->MonitorsCount; Index++)
{
if (PtInRect(&infoPtr->Monitors[Index].rc,
pt))
{
Ret = Index;
break;
}
}
}
return Ret;
}
static VOID
MonSelUpdateExtent(IN OUT PMONITORSELWND infoPtr)
{
DWORD Index;
RECT rcMonitor;
/* NOTE: This routine calculates the extent of all monitor coordinates.
These are not control coordinates! */
if (infoPtr->MonitorsCount > 0)
{
MonSelMonInfoToRect(&infoPtr->MonitorInfo[0],
&infoPtr->rcExtent);
for (Index = 1; Index < infoPtr->MonitorsCount; Index++)
{
MonSelMonInfoToRect(&infoPtr->MonitorInfo[Index],
&rcMonitor);
UnionRect(&infoPtr->rcExtent,
&infoPtr->rcExtent,
&rcMonitor);
}
}
else
{
ZeroMemory(&infoPtr->rcExtent,
sizeof(infoPtr->rcExtent));
}
}
static VOID
MonSelScaleRectRelative(IN const RECT *prcBaseFrom,
IN const RECT *prcFrom,
IN const RECT *prcBaseTo,
OUT PRECT prcTo)
{
SIZE BaseFrom, BaseTo, From;
BaseFrom.cx = prcBaseFrom->right - prcBaseFrom->left;
BaseFrom.cy = prcBaseFrom->bottom - prcBaseFrom->top;
BaseTo.cx = prcBaseTo->right - prcBaseTo->left;
BaseTo.cy = prcBaseTo->bottom - prcBaseTo->top;
From.cx = prcFrom->right - prcFrom->left;
From.cy = prcFrom->bottom - prcFrom->top;
prcTo->left = prcBaseTo->left + (((prcFrom->left - prcBaseFrom->left) * BaseTo.cx) / BaseFrom.cx);
prcTo->top = prcBaseTo->top + (((prcFrom->top - prcBaseFrom->top) * BaseTo.cy) / BaseFrom.cy);
prcTo->right = prcTo->left + ((From.cx * BaseTo.cx) / BaseFrom.cx);
prcTo->bottom = prcTo->top + ((From.cy * BaseTo.cy) / BaseFrom.cy);
}
static VOID
ScaleRectSizeFit(IN const RECT *prcContainerRect,
IN OUT PRECT prcRectToScale)
{
SIZE ContainerSize, RectSize;
ContainerSize.cx = prcContainerRect->right - prcContainerRect->left;
ContainerSize.cy = prcContainerRect->bottom - prcContainerRect->top;
RectSize.cx = prcRectToScale->right - prcRectToScale->left;
RectSize.cy = prcRectToScale->bottom - prcRectToScale->top;
if (((RectSize.cx * 0xFFF) / RectSize.cy) < ((ContainerSize.cx * 0xFFF) / ContainerSize.cy))
{
RectSize.cx = (RectSize.cx * ((ContainerSize.cy * 0xFFF) / RectSize.cy)) / 0xFFF;
RectSize.cy = ContainerSize.cy;
}
else
{
RectSize.cy = (RectSize.cy * ((ContainerSize.cx * 0xFFF) / RectSize.cx)) / 0xFFF;
RectSize.cx = ContainerSize.cx;
}
prcRectToScale->right = prcRectToScale->left + RectSize.cx;
prcRectToScale->bottom = prcRectToScale->top + RectSize.cy;
OffsetRect(prcRectToScale,
prcContainerRect->left + ((ContainerSize.cx - RectSize.cx) / 2),
prcContainerRect->top + ((ContainerSize.cy - RectSize.cy) / 2));
}
static VOID
MonSelRepaint(IN PMONITORSELWND infoPtr)
{
RECT rc;
MonSelRectToScreen(infoPtr,
&infoPtr->rcMonitors,
&rc);
InvalidateRect(infoPtr->hSelf,
&rc,
TRUE);
if (!EqualRect(&infoPtr->rcMonitors, &infoPtr->rcOldMonitors) &&
infoPtr->rcOldMonitors.right != infoPtr->rcOldMonitors.left)
{
MonSelRectToScreen(infoPtr, &infoPtr->rcOldMonitors, &rc);
InvalidateRect(infoPtr->hSelf, &rc, TRUE);
infoPtr->rcOldMonitors = infoPtr->rcMonitors;
}
}
static VOID
MonSelRepaintMonitor(IN PMONITORSELWND infoPtr,
IN DWORD Index)
{
RECT rc;
BOOL NoRepaint = FALSE;
if (Index < infoPtr->MonitorsCount)
{
if (Index == (DWORD)infoPtr->DraggingMonitor)
{
if (infoPtr->IsDraggingMonitor)
{
MonSelRectToScreen(infoPtr,
&infoPtr->rcDragging,
&rc);
}
else
NoRepaint = TRUE;
}
else
{
MonSelRectToScreen(infoPtr,
&infoPtr->Monitors[Index].rc,
&rc);
}
if (!NoRepaint)
{
InvalidateRect(infoPtr->hSelf,
&rc,
TRUE);
}
}
}
static VOID
MonSelRepaintSelected(IN PMONITORSELWND infoPtr)
{
if (infoPtr->SelectedMonitor >= 0)
{
MonSelRepaintMonitor(infoPtr,
(DWORD)infoPtr->SelectedMonitor);
}
}
static VOID
MonSelResetMonitors(IN OUT PMONITORSELWND infoPtr)
{
DWORD Index;
for (Index = 0; Index < infoPtr->MonitorsCount; Index++)
{
if (infoPtr->Monitors[Index].hFont != NULL)
{
DeleteObject(infoPtr->Monitors[Index].hFont);
infoPtr->Monitors[Index].hFont = NULL;
}
}
}
static VOID
MonSelUpdateMonitorsInfo(IN OUT PMONITORSELWND infoPtr,
IN BOOL bRepaint)
{
RECT rcExtSurface, rcExtDisplay;
DWORD Index;
/* Recalculate rcExtent */
MonSelUpdateExtent(infoPtr);
infoPtr-> CanDisplay = infoPtr->MonitorsCount != 0 &&
(infoPtr->ClientSize.cx > (2 * (infoPtr->Margin.cx + infoPtr->SelectionFrame.cx))) &&
(infoPtr->ClientSize.cy > (2 * (infoPtr->Margin.cy + infoPtr->SelectionFrame.cy)));
if (infoPtr->CanDisplay)
{
/* Calculate the rectangle on the control in which may be painted */
rcExtSurface.left = infoPtr->Margin.cx;
rcExtSurface.top = infoPtr->Margin.cy;
rcExtSurface.right = rcExtSurface.left + infoPtr->ClientSize.cx - (2 * infoPtr->Margin.cx);
rcExtSurface.bottom = rcExtSurface.top + infoPtr->ClientSize.cy - (2 * infoPtr->Margin.cy);
/* Calculate the rectangle on the control that is actually painted on */
rcExtDisplay.left = rcExtDisplay.top = 0;
rcExtDisplay.right = infoPtr->rcExtent.right - infoPtr->rcExtent.left;
rcExtDisplay.bottom = infoPtr->rcExtent.bottom - infoPtr->rcExtent.top;
ScaleRectSizeFit(&rcExtSurface,
&rcExtDisplay);
infoPtr->rcOldMonitors = infoPtr->rcMonitors;
infoPtr->rcMonitors = rcExtDisplay;
/* Now that we know in which area all monitors are located,
calculate the monitors selection rectangles on the screen */
for (Index = 0; Index < infoPtr->MonitorsCount; Index++)
{
MonSelMonInfoToRect(&infoPtr->MonitorInfo[Index],
&rcExtDisplay);
MonSelScaleRectRelative(&infoPtr->rcExtent,
&rcExtDisplay,
&infoPtr->rcMonitors,
&infoPtr->Monitors[Index].rc);
}
MonSelResetMonitors(infoPtr);
if (bRepaint)
MonSelRepaint(infoPtr);
}
else if (bRepaint)
{
InvalidateRect(infoPtr->hSelf,
NULL,
TRUE);
}
}
static BOOL
MonSelSetMonitorsInfo(IN OUT PMONITORSELWND infoPtr,
IN DWORD dwMonitors,
IN const MONSL_MONINFO *MonitorsInfo)
{
DWORD Index;
BOOL Ret = TRUE;
if (infoPtr->DraggingMonitor >= 0)
return FALSE;
if (infoPtr->MonitorInfo != NULL)
{
LocalFree((HLOCAL)infoPtr->MonitorInfo);
infoPtr->MonitorInfo = NULL;
MonSelResetMonitors(infoPtr);
LocalFree((HLOCAL)infoPtr->Monitors);
infoPtr->Monitors = NULL;
infoPtr->MonitorsCount = 0;
}
if (dwMonitors != 0)
{
infoPtr->MonitorInfo = (PMONSL_MONINFO)LocalAlloc(LMEM_FIXED,
dwMonitors * sizeof(MONSL_MONINFO));
if (infoPtr->MonitorInfo != NULL)
{
infoPtr->Monitors = (PMONSL_MON)LocalAlloc(LMEM_FIXED,
dwMonitors * sizeof(MONSL_MON));
if (infoPtr->Monitors != NULL)
{
CopyMemory(infoPtr->MonitorInfo,
MonitorsInfo,
dwMonitors * sizeof(MONSL_MONINFO));
ZeroMemory(infoPtr->Monitors,
dwMonitors * sizeof(MONSL_MON));
for (Index = 0; Index < dwMonitors; Index++)
{
_stprintf(infoPtr->Monitors[Index].szCaption,
_T("%u"),
Index + 1);
}
infoPtr->MonitorsCount = dwMonitors;
if (infoPtr->SelectedMonitor >= (INT)infoPtr->MonitorsCount)
infoPtr->SelectedMonitor = -1;
if (!(infoPtr->ControlExStyle & MSLM_EX_ALLOWSELECTNONE) && infoPtr->SelectedMonitor < 0)
infoPtr->SelectedMonitor = 0;
MonSelUpdateMonitorsInfo(infoPtr,
TRUE);
}
else
{
LocalFree((HLOCAL)infoPtr->MonitorInfo);
infoPtr->MonitorInfo = NULL;
Ret = FALSE;
}
}
else
Ret = FALSE;
}
if (!Ret)
infoPtr->SelectedMonitor = -1;
if (!Ret || dwMonitors == 0)
{
InvalidateRect(infoPtr->hSelf,
NULL,
TRUE);
}
return Ret;
}
static DWORD
MonSelGetMonitorsInfo(IN PMONITORSELWND infoPtr,
IN DWORD dwMonitors,
IN OUT PMONSL_MONINFO MonitorsInfo)
{
if (dwMonitors != 0)
{
if (dwMonitors > infoPtr->MonitorsCount)
dwMonitors = infoPtr->MonitorsCount;
CopyMemory(MonitorsInfo,
infoPtr->MonitorInfo,
dwMonitors * sizeof(MONSL_MONINFO));
return dwMonitors;
}
else
return infoPtr->MonitorsCount;
}
static BOOL
MonSelSetMonitorInfo(IN OUT PMONITORSELWND infoPtr,
IN INT Index,
IN const MONSL_MONINFO *MonitorsInfo)
{
if (infoPtr->DraggingMonitor < 0 &&
Index >= 0 && Index < (INT)infoPtr->MonitorsCount)
{
CopyMemory(&infoPtr->MonitorInfo[Index],
MonitorsInfo,
sizeof(MONSL_MONINFO));
MonSelUpdateMonitorsInfo(infoPtr,
TRUE);
return TRUE;
}
return FALSE;
}
static BOOL
MonSelGetMonitorInfo(IN PMONITORSELWND infoPtr,
IN INT Index,
IN OUT PMONSL_MONINFO MonitorsInfo)
{
if (Index >= 0 && Index < (INT)infoPtr->MonitorsCount)
{
CopyMemory(MonitorsInfo,
&infoPtr->MonitorInfo[Index],
sizeof(MONSL_MONINFO));
return TRUE;
}
return FALSE;
}
static INT
MonSelGetMonitorRect(IN OUT PMONITORSELWND infoPtr,
IN INT Index,
OUT PRECT prc)
{
RECT rc, rcClient;
if (Index < 0 || (UINT)Index >= infoPtr->MonitorsCount)
return -1;
if (!infoPtr->CanDisplay)
return 0;
MonSelRectToScreen(infoPtr,
&infoPtr->Monitors[Index].rc,
prc);
rcClient.left = rcClient.top = 0;
rcClient.right = infoPtr->ClientSize.cx;
rcClient.bottom = infoPtr->ClientSize.cy;
return IntersectRect(&rc,
&rcClient,
prc) != FALSE;
}
static BOOL
MonSelSetCurSelMonitor(IN OUT PMONITORSELWND infoPtr,
IN INT Index,
IN BOOL bNotify)
{
INT PrevSel;
BOOL PreventSelect = FALSE;
BOOL Ret = FALSE;
if (infoPtr->DraggingMonitor < 0 &&
(Index == -1 || Index < (INT)infoPtr->MonitorsCount))
{
if (Index != infoPtr->SelectedMonitor)
{
if ((infoPtr->MonitorInfo[Index].Flags & MSL_MIF_DISABLED) &&
!(infoPtr->ControlExStyle & MSLM_EX_ALLOWSELECTDISABLED))
{
PreventSelect = TRUE;
}
if (!PreventSelect && bNotify)
{
MONSL_MONNMMONITORCHANGING nmi;
nmi.PreviousSelected = infoPtr->SelectedMonitor;
nmi.AllowChanging = TRUE;
MonSelNotifyMonitor(infoPtr,
MSLN_MONITORCHANGING,
Index,
&nmi.hdr);
PreventSelect = (nmi.AllowChanging == FALSE);
}
if (!PreventSelect)
{
PrevSel = infoPtr->SelectedMonitor;
infoPtr->SelectedMonitor = Index;
if (PrevSel >= 0)
{
MonSelRepaintMonitor(infoPtr,
PrevSel);
}
if (infoPtr->SelectedMonitor >= 0)
MonSelRepaintSelected(infoPtr);
if (bNotify)
{
MONSL_MONNMHDR nm;
MonSelNotifyMonitor(infoPtr,
MSLN_MONITORCHANGED,
Index,
&nm);
}
}
}
Ret = TRUE;
}
return Ret;
}
static VOID
MonSelCreate(IN OUT PMONITORSELWND infoPtr)
{
infoPtr->SelectionFrame.cx = infoPtr->SelectionFrame.cy = 4;
infoPtr->Margin.cx = infoPtr->Margin.cy = 20;
infoPtr->SelectedMonitor = -1;
infoPtr->DraggingMonitor = -1;
infoPtr->ControlExStyle = MSLM_EX_ALLOWSELECTDISABLED | MSLM_EX_HIDENUMBERONSINGLE |
MSLM_EX_SELECTONRIGHTCLICK | MSLM_EX_SELECTBYARROWKEY;
return;
}
static VOID
MonSelDestroy(IN OUT PMONITORSELWND infoPtr)
{
/* Free all monitors */
MonSelSetMonitorsInfo(infoPtr,
0,
NULL);
if (infoPtr->hbrDisabled != NULL)
{
DeleteObject(infoPtr->hbrDisabled);
infoPtr->hbrDisabled = NULL;
}
if (infoPtr->hbmDisabledPattern != NULL)
{
DeleteObject(infoPtr->hbmDisabledPattern);
infoPtr->hbmDisabledPattern = NULL;
}
}
static BOOL
MonSelSetExtendedStyle(IN OUT PMONITORSELWND infoPtr,
IN DWORD dwExtendedStyle)
{
if (infoPtr->DraggingMonitor >= 0)
return FALSE;
if (dwExtendedStyle != infoPtr->ControlExStyle)
{
infoPtr->ControlExStyle = dwExtendedStyle;
/* Repaint the control */
InvalidateRect(infoPtr->hSelf,
NULL,
TRUE);
}
return TRUE;
}
static DWORD
MonSelGetExtendedStyle(IN PMONITORSELWND infoPtr)
{
return infoPtr->ControlExStyle;
}
static HFONT
MonSelGetMonitorFont(IN OUT PMONITORSELWND infoPtr,
IN HDC hDC,
IN INT Index)
{
TEXTMETRIC tm;
SIZE rcsize;
LOGFONT lf;
HFONT hPrevFont, hFont;
//INT len;
hFont = infoPtr->Monitors[Index].hFont;
if (hFont == NULL &&
GetObject(infoPtr->hFont,
sizeof(lf),
&lf) != 0)
{
rcsize.cx = infoPtr->Monitors[Index].rc.right - infoPtr->Monitors[Index].rc.left -
(2 * infoPtr->SelectionFrame.cx) - 2;
rcsize.cy = infoPtr->Monitors[Index].rc.bottom - infoPtr->Monitors[Index].rc.top -
(2 * infoPtr->SelectionFrame.cy) - 2;
rcsize.cy = (rcsize.cy * 60) / 100;
//len = _tcslen(infoPtr->Monitors[Index].szCaption);
hPrevFont = SelectObject(hDC,
infoPtr->hFont);
if (GetTextMetrics(hDC,
&tm))
{
lf.lfWeight = FW_SEMIBOLD;
lf.lfHeight = -MulDiv(rcsize.cy - tm.tmExternalLeading,
GetDeviceCaps(hDC,
LOGPIXELSY),
72);
hFont = CreateFontIndirect(&lf);
if (hFont != NULL)
infoPtr->Monitors[Index].hFont = hFont;
}
SelectObject(hDC,
hPrevFont);
}
return hFont;
}
static BOOL
MonSelDrawDisabledRect(IN OUT PMONITORSELWND infoPtr,
IN HDC hDC,
IN const RECT *prc)
{
BOOL Ret = FALSE;
if (infoPtr->hbrDisabled == NULL)
{
static const DWORD Pattern[4] = {0x5555AAAA, 0x5555AAAA, 0x5555AAAA, 0x5555AAAA};
if (infoPtr->hbmDisabledPattern == NULL)
{
infoPtr->hbmDisabledPattern = CreateBitmap(8,
8,
1,
1,
Pattern);
}
if (infoPtr->hbmDisabledPattern != NULL)
infoPtr->hbrDisabled = CreatePatternBrush(infoPtr->hbmDisabledPattern);
}
if (infoPtr->hbrDisabled != NULL)
{
/* FIXME: Implement */
}
return Ret;
}
static VOID
MonSelPaintMonitor(IN OUT PMONITORSELWND infoPtr,
IN HDC hDC,
IN DWORD Index,
IN OUT PRECT prc,
IN COLORREF crDefFontColor,
IN BOOL bHideNumber)
{
HFONT hFont, hPrevFont;
COLORREF crPrevText;
if ((INT)Index == infoPtr->SelectedMonitor)
{
FillRect(hDC,
prc,
(HBRUSH)(COLOR_HIGHLIGHT + 1));
if (infoPtr->HasFocus && !(infoPtr->UIState & UISF_HIDEFOCUS))
{
/* NOTE: We need to switch the text color to the default, because
DrawFocusRect draws a solid line if the text is white! */
crPrevText = SetTextColor(hDC,
crDefFontColor);
DrawFocusRect(hDC,
prc);
SetTextColor(hDC,
crPrevText);
}
}
InflateRect(prc,
-infoPtr->SelectionFrame.cx,
-infoPtr->SelectionFrame.cy);
Rectangle(hDC,
prc->left,
prc->top,
prc->right,
prc->bottom);
InflateRect(prc,
-1,
-1);
if (!bHideNumber)
{
hFont = MonSelGetMonitorFont(infoPtr,
hDC,
Index);
if (hFont != NULL)
{
hPrevFont = SelectObject(hDC,
hFont);
DrawText(hDC,
infoPtr->Monitors[Index].szCaption,
-1,
prc,
DT_VCENTER | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE);
SelectObject(hDC,
hPrevFont);
}
}
if (infoPtr->MonitorInfo[Index].Flags & MSL_MIF_DISABLED)
{
InflateRect(prc,
1,
1);
MonSelDrawDisabledRect(infoPtr,
hDC,
prc);
}
}
static VOID
MonSelPaint(IN OUT PMONITORSELWND infoPtr,
IN HDC hDC,
IN const RECT *prcUpdate)
{
COLORREF crPrevText;
HBRUSH hbBk, hbOldBk;
HPEN hpFg, hpOldFg;
DWORD Index;
RECT rc, rctmp;
INT iPrevBkMode;
BOOL bHideNumber;
bHideNumber = (infoPtr->ControlExStyle & MSLM_EX_HIDENUMBERS) ||
((infoPtr->MonitorsCount == 1) && (infoPtr->ControlExStyle & MSLM_EX_HIDENUMBERONSINGLE));
hbBk = GetSysColorBrush(COLOR_BACKGROUND);
hpFg = CreatePen(PS_SOLID,
0,
GetSysColor(COLOR_HIGHLIGHTTEXT));
hbOldBk = SelectObject(hDC,
hbBk);
hpOldFg = SelectObject(hDC,
hpFg);
iPrevBkMode = SetBkMode(hDC,
TRANSPARENT);
crPrevText = SetTextColor(hDC,
GetSysColor(COLOR_HIGHLIGHTTEXT));
for (Index = 0; Index < infoPtr->MonitorsCount; Index++)
{
if (infoPtr->IsDraggingMonitor &&
(DWORD)infoPtr->DraggingMonitor == Index)
{
continue;
}
MonSelRectToScreen(infoPtr,
&infoPtr->Monitors[Index].rc,
&rc);
if (IntersectRect(&rctmp,
&rc,
prcUpdate))
{
MonSelPaintMonitor(infoPtr,
hDC,
Index,
&rc,
crPrevText,
bHideNumber);
}
}
/* Paint the dragging monitor last */
if (infoPtr->IsDraggingMonitor &&
infoPtr->DraggingMonitor >= 0)
{
MonSelRectToScreen(infoPtr,
&infoPtr->rcDragging,
&rc);
if (IntersectRect(&rctmp,
&rc,
prcUpdate))
{
MonSelPaintMonitor(infoPtr,
hDC,
(DWORD)infoPtr->DraggingMonitor,
&rc,
crPrevText,
bHideNumber);
}
}
SetTextColor(hDC,
crPrevText);
SetBkMode(hDC,
iPrevBkMode);
SelectObject(hDC,
hpOldFg);
SelectObject(hDC,
hbOldBk);
DeleteObject(hpFg);
}
static VOID
MonSelContextMenu(IN OUT PMONITORSELWND infoPtr,
IN SHORT x,
IN SHORT y)
{
MONSL_MONNMBUTTONCLICKED nm;
INT Index;
if (!infoPtr->HasFocus)
SetFocus(infoPtr->hSelf);
nm.pt.x = x;
nm.pt.y = y;
Index = MonSelHitTest(infoPtr,
&nm.pt);
MonSelNotifyMonitor(infoPtr,
MSLN_RBUTTONUP,
Index,
(PMONSL_MONNMHDR)&nm);
/* Send a WM_CONTEXTMENU notification */
MapWindowPoints(infoPtr->hSelf,
NULL,
&nm.pt,
1);
SendMessage(infoPtr->hSelf,
WM_CONTEXTMENU,
(WPARAM)infoPtr->hSelf,
MAKELPARAM(nm.pt.x,
nm.pt.y));
}
static VOID
MonSelApplyCursorClipping(IN PMONITORSELWND infoPtr,
IN BOOL bClip)
{
RECT rc;
if (bClip)
{
rc.left = rc.top = 0;
rc.right = infoPtr->ClientSize.cx;
rc.bottom = infoPtr->ClientSize.cy;
if (MapWindowPoints(infoPtr->hSelf,
NULL,
(LPPOINT)&rc,
2))
{
ClipCursor(&rc);
}
}
else
{
ClipCursor(NULL);
}
}
static VOID
MonSelMoveDragRect(IN OUT PMONITORSELWND infoPtr,
IN PPOINT ppt)
{
RECT rcPrev, rcUpdate, *prc;
HRGN hRgnPrev;
HDC hDC;
if (infoPtr->CanDisplay)
{
hDC = GetDC(infoPtr->hSelf);
if (hDC != NULL)
{
if (infoPtr->ptDrag.x != ppt->x ||
infoPtr->ptDrag.y != ppt->y)
{
infoPtr->ptDrag = *ppt;
rcPrev = infoPtr->rcDragging;
/* Calculate updated dragging rectangle */
prc = &infoPtr->Monitors[infoPtr->DraggingMonitor].rc;
infoPtr->rcDragging.left = ppt->x - infoPtr->DraggingMargin.cx;
infoPtr->rcDragging.top = ppt->y - infoPtr->DraggingMargin.cy;
infoPtr->rcDragging.right = infoPtr->rcDragging.left + (prc->right - prc->left);
infoPtr->rcDragging.bottom = infoPtr->rcDragging.top + (prc->bottom - prc->top);
hRgnPrev = CreateRectRgn(rcPrev.left,
rcPrev.top,
rcPrev.right,
rcPrev.bottom);
if (hRgnPrev != NULL)
{
if (!ScrollDC(hDC,
infoPtr->rcDragging.left - rcPrev.left,
infoPtr->rcDragging.top - rcPrev.top,
&rcPrev,
NULL,
hRgnPrev,
&rcUpdate) ||
!InvalidateRgn(infoPtr->hSelf,
hRgnPrev,
TRUE))
{
DeleteObject(hRgnPrev);
goto InvRects;
}
DeleteObject(hRgnPrev);
}
else
{
InvRects:
InvalidateRect(infoPtr->hSelf,
&rcPrev,
TRUE);
InvalidateRect(infoPtr->hSelf,
&infoPtr->rcDragging,
TRUE);
}
}
ReleaseDC(infoPtr->hSelf,
hDC);
}
}
}
static VOID
MonSelCancelDragging(IN OUT PMONITORSELWND infoPtr)
{
DWORD Index;
if (infoPtr->DraggingMonitor >= 0)
{
MonSelMoveDragRect(infoPtr,
&infoPtr->ptDragBegin);
Index = (DWORD)infoPtr->DraggingMonitor;
infoPtr->DraggingMonitor = -1;
if (infoPtr->CanDisplay)
{
/* Repaint the area where the monitor was last dragged */
MonSelRepaintMonitor(infoPtr,
Index);
infoPtr->IsDraggingMonitor = FALSE;
/* Repaint the area where the monitor is located */
MonSelRepaintMonitor(infoPtr,
Index);
}
else
infoPtr->IsDraggingMonitor = FALSE;
ReleaseCapture();
MonSelApplyCursorClipping(infoPtr,
FALSE);
}
}
static VOID
MonSelInitDragging(IN OUT PMONITORSELWND infoPtr,
IN DWORD Index,
IN PPOINT ppt)
{
POINT pt;
MonSelCancelDragging(infoPtr);
infoPtr->IsDraggingMonitor = FALSE;
MonSelScreenToPt(infoPtr,
ppt,
&pt);
infoPtr->ptDrag = infoPtr->ptDragBegin = pt;
infoPtr->DraggingMonitor = (INT)Index;
infoPtr->DraggingMargin.cx = ppt->x - infoPtr->Monitors[Index].rc.left;
infoPtr->DraggingMargin.cy = ppt->y - infoPtr->Monitors[Index].rc.top;
infoPtr->rcDragging = infoPtr->Monitors[Index].rc;
MonSelApplyCursorClipping(infoPtr,
TRUE);
}
static VOID
MonSelDrag(IN OUT PMONITORSELWND infoPtr,
IN PPOINT ppt)
{
SIZE szDrag;
POINT pt;
RECT rcDrag;
if (infoPtr->DraggingMonitor >= 0)
{
MonSelScreenToPt(infoPtr,
ppt,
&pt);
if (!infoPtr->IsDraggingMonitor)
{
szDrag.cx = GetSystemMetrics(SM_CXDRAG);
szDrag.cy = GetSystemMetrics(SM_CYDRAG);
rcDrag.left = infoPtr->Monitors[infoPtr->DraggingMonitor].rc.left + infoPtr->DraggingMargin.cx - (szDrag.cx / 2);
rcDrag.top = infoPtr->Monitors[infoPtr->DraggingMonitor].rc.top + infoPtr->DraggingMargin.cy - (szDrag.cy / 2);
rcDrag.right = rcDrag.left + szDrag.cx;
rcDrag.bottom = rcDrag.top + szDrag.cy;
if (!PtInRect(&rcDrag,
pt))
{
/* The user started moving around the mouse: Begin dragging */
infoPtr->IsDraggingMonitor = TRUE;
MonSelMoveDragRect(infoPtr,
&pt);
}
}
else
{
MonSelMoveDragRect(infoPtr,
&pt);
}
}
}
static LRESULT CALLBACK
MonitorSelWndProc(IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
PMONITORSELWND infoPtr;
LRESULT Ret = 0;
infoPtr = (PMONITORSELWND)GetWindowLongPtrW(hwnd,
0);
if (infoPtr == NULL && uMsg != WM_CREATE)
{
goto HandleDefaultMessage;
}
switch (uMsg)
{
case WM_PAINT:
case WM_PRINTCLIENT:
{
PAINTSTRUCT ps;
HDC hDC;
if (wParam != 0)
{
if (!GetUpdateRect(hwnd,
&ps.rcPaint,
TRUE))
{
break;
}
hDC = (HDC)wParam;
}
else
{
hDC = BeginPaint(hwnd,
&ps);
if (hDC == NULL)
{
break;
}
}
if (infoPtr->CanDisplay)
{
MonSelPaint(infoPtr,
hDC,
&ps.rcPaint);
}
if (wParam == 0)
{
EndPaint(hwnd,
&ps);
}
break;
}
case WM_MOUSEMOVE:
{
POINT pt;
if (!(wParam & MK_LBUTTON))
{
MonSelCancelDragging(infoPtr);
break;
}
if (infoPtr->LeftBtnDown)
{
pt.x = (LONG)LOWORD(lParam);
pt.y = (LONG)HIWORD(lParam);
MonSelDrag(infoPtr,
&pt);
}
break;
}
case WM_RBUTTONDOWN:
{
if (!(infoPtr->ControlExStyle & MSLM_EX_SELECTONRIGHTCLICK))
break;
/* Fall through */
}
case WM_LBUTTONDBLCLK:
case WM_LBUTTONDOWN:
{
INT Index;
POINT pt;
if (!infoPtr->HasFocus)
SetFocus(infoPtr->hSelf);
pt.x = (LONG)LOWORD(lParam);
pt.y = (LONG)HIWORD(lParam);
Index = MonSelHitTest(infoPtr,
&pt);
if (Index >= 0 || (infoPtr->ControlExStyle & MSLM_EX_ALLOWSELECTNONE))
{
MonSelSetCurSelMonitor(infoPtr,
Index,
TRUE);
}
if (Index >= 0 && (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK))
{
infoPtr->LeftBtnDown = TRUE;
MonSelInitDragging(infoPtr,
(DWORD)Index,
&pt);
}
/* Fall through */
}
case WM_MBUTTONDOWN:
{
if (!infoPtr->HasFocus)
SetFocus(hwnd);
break;
}
case WM_RBUTTONUP:
{
MonSelContextMenu(infoPtr,
(SHORT)LOWORD(lParam),
(SHORT)HIWORD(lParam));
break;
}
case WM_LBUTTONUP:
{
MonSelCancelDragging(infoPtr);
infoPtr->LeftBtnDown = FALSE;
break;
}
case WM_GETDLGCODE:
{
INT virtKey;
virtKey = (lParam != 0 ? (INT)((LPMSG)lParam)->wParam : 0);
switch (virtKey)
{
case VK_TAB:
{
/* Change the UI status */
SendMessage(GetAncestor(hwnd,
GA_PARENT),
WM_CHANGEUISTATE,
MAKEWPARAM(UIS_INITIALIZE,
0),
0);
break;
}
}
Ret |= DLGC_WANTARROWS;
if (infoPtr->ControlExStyle & MSLM_EX_SELECTBYNUMKEY)
Ret |= DLGC_WANTCHARS;
break;
}
case WM_SETFOCUS:
{
infoPtr->HasFocus = TRUE;
MonSelRepaintSelected(infoPtr);
break;
}
case WM_KILLFOCUS:
{
infoPtr->HasFocus = FALSE;
MonSelCancelDragging(infoPtr);
MonSelRepaintSelected(infoPtr);
break;
}
case WM_UPDATEUISTATE:
{
DWORD OldUIState;
Ret = DefWindowProcW(hwnd,
uMsg,
wParam,
lParam);
OldUIState = infoPtr->UIState;
switch (LOWORD(wParam))
{
case UIS_SET:
infoPtr->UIState |= HIWORD(wParam);
break;
case UIS_CLEAR:
infoPtr->UIState &= ~HIWORD(wParam);
break;
}
if (infoPtr->UIState != OldUIState)
MonSelRepaintSelected(infoPtr);
break;
}
case WM_SETFONT:
{
Ret = (LRESULT)MonSelChangeFont(infoPtr,
(HFONT)wParam,
(BOOL)LOWORD(lParam));
break;
}
case WM_SIZE:
{
infoPtr->ClientSize.cx = LOWORD(lParam);
infoPtr->ClientSize.cy = HIWORD(lParam);
/* Don't let MonSelUpdateMonitorsInfo repaint the control
because this won't work properly in case the control
was sized down! */
MonSelUpdateMonitorsInfo(infoPtr,
FALSE);
InvalidateRect(infoPtr->hSelf,
NULL,
TRUE);
break;
}
case WM_GETFONT:
{
Ret = (LRESULT)infoPtr->hFont;
break;
}
case WM_ENABLE:
{
infoPtr->Enabled = ((BOOL)wParam != FALSE);
MonSelRepaint(infoPtr);
break;
}
case WM_STYLECHANGED:
{
if (wParam == GWL_STYLE)
{
unsigned int OldEnabled = infoPtr->Enabled;
infoPtr->Enabled = !(((LPSTYLESTRUCT)lParam)->styleNew & WS_DISABLED);
if (OldEnabled != infoPtr->Enabled)
MonSelRepaint(infoPtr);
}
break;
}
case WM_KEYDOWN:
{
INT Index;
if (infoPtr->ControlExStyle & MSLM_EX_SELECTBYARROWKEY)
{
switch (wParam)
{
case VK_UP:
case VK_LEFT:
{
Index = infoPtr->SelectedMonitor;
if (infoPtr->MonitorsCount != 0)
{
if (Index < 0)
Index = 0;
else if (Index > 0)
Index--;
}
if (Index >= 0)
{
MonSelSetCurSelMonitor(infoPtr,
Index,
TRUE);
}
break;
}
case VK_DOWN:
case VK_RIGHT:
{
Index = infoPtr->SelectedMonitor;
if (infoPtr->MonitorsCount != 0)
{
if (Index < 0)
Index = (INT)infoPtr->MonitorsCount - 1;
else if (Index < (INT)infoPtr->MonitorsCount - 1)
Index++;
}
if (infoPtr->SelectedMonitor < (INT)infoPtr->MonitorsCount)
{
MonSelSetCurSelMonitor(infoPtr,
Index,
TRUE);
}
break;
}
}
}
break;
}
case WM_CHAR:
{
if ((infoPtr->ControlExStyle & MSLM_EX_SELECTBYNUMKEY) &&
wParam >= '1' && wParam <= '9')
{
INT Index = (INT)(wParam - '1');
if (Index < (INT)infoPtr->MonitorsCount)
{
MonSelSetCurSelMonitor(infoPtr,
Index,
TRUE);
}
}
break;
}
case MSLM_SETMONITORSINFO:
{
Ret = MonSelSetMonitorsInfo(infoPtr,
(DWORD)wParam,
(const MONSL_MONINFO *)lParam);
break;
}
case MSLM_GETMONITORSINFO:
{
Ret = MonSelGetMonitorsInfo(infoPtr,
(DWORD)wParam,
(PMONSL_MONINFO)lParam);
break;
}
case MSLM_GETMONITORINFOCOUNT:
{
Ret = infoPtr->MonitorsCount;
break;
}
case MSLM_HITTEST:
{
Ret = MonSelHitTest(infoPtr,
(const POINT *)wParam);
break;
}
case MSLM_SETCURSEL:
{
Ret = MonSelSetCurSelMonitor(infoPtr,
(INT)wParam,
FALSE);
break;
}
case MSLM_GETCURSEL:
{
Ret = infoPtr->SelectedMonitor;
break;
}
case MSLM_SETMONITORINFO:
{
Ret = MonSelSetMonitorInfo(infoPtr,
(INT)wParam,
(const MONSL_MONINFO *)lParam);
break;
}
case MSLM_GETMONITORINFO:
{
Ret = MonSelGetMonitorInfo(infoPtr,
(INT)wParam,
(PMONSL_MONINFO)lParam);
break;
}
case MSLM_SETEXSTYLE:
{
Ret = MonSelSetExtendedStyle(infoPtr,
(DWORD)lParam);
break;
}
case MSLM_GETEXSTYLE:
{
Ret = MonSelGetExtendedStyle(infoPtr);
break;
}
case MSLM_GETMONITORRECT:
{
Ret = (LRESULT)MonSelGetMonitorRect(infoPtr,
(INT)wParam,
(PRECT)lParam);
break;
}
case WM_CREATE:
{
infoPtr = (PMONITORSELWND)HeapAlloc(GetProcessHeap(),
0,
sizeof(MONITORSELWND));
if (infoPtr == NULL)
{
Ret = (LRESULT)-1;
break;
}
ZeroMemory(infoPtr,
sizeof(MONITORSELWND));
infoPtr->hSelf = hwnd;
infoPtr->hNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
infoPtr->Enabled = !(((LPCREATESTRUCTW)lParam)->style & WS_DISABLED);
infoPtr->UIState = SendMessage(hwnd,
WM_QUERYUISTATE,
0,
0);
SetWindowLongPtrW(hwnd,
0,
(LONG_PTR)infoPtr);
MonSelCreate(infoPtr);
break;
}
case WM_DESTROY:
{
MonSelDestroy(infoPtr);
HeapFree(GetProcessHeap(),
0,
infoPtr);
SetWindowLongPtrW(hwnd,
0,
(DWORD_PTR)NULL);
break;
}
default:
{
HandleDefaultMessage:
Ret = DefWindowProcW(hwnd,
uMsg,
wParam,
lParam);
break;
}
}
return Ret;
}
BOOL
RegisterMonitorSelectionControl(IN HINSTANCE hInstance)
{
WNDCLASS wc = {0};
wc.style = CS_DBLCLKS;
wc.lpfnWndProc = MonitorSelWndProc;
wc.cbWndExtra = sizeof(PMONITORSELWND);
wc.hInstance = hInstance;
wc.hCursor = LoadCursorW(NULL,
(LPWSTR)IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
wc.lpszClassName = szMonitorSelWndClass;
return RegisterClass(&wc) != 0;
}
VOID
UnregisterMonitorSelectionControl(IN HINSTANCE hInstance)
{
UnregisterClassW(szMonitorSelWndClass,
hInstance);
}