xref: /reactos/dll/cpl/desk/monslctl.c (revision 1734f297)
1 #include "desk.h"
2 
3 static const TCHAR szMonitorSelWndClass[] = TEXT("MONITORSELWNDCLASS");
4 
5 typedef struct _MONSL_MON
6 {
7     RECT rc;
8     HFONT hFont;
9     TCHAR szCaption[12];
10 } MONSL_MON, *PMONSL_MON;
11 
12 typedef struct _MONITORSELWND
13 {
14     HWND hSelf;
15     HWND hNotify;
16     HFONT hFont;
17     SIZE ClientSize;
18     DWORD UIState;
19     union
20     {
21         DWORD dwInternalFlags;
22         struct
23         {
24             UINT Enabled : 1;
25             UINT HasFocus : 1;
26             UINT CanDisplay : 1;
27             UINT LeftBtnDown : 1;
28             UINT IsDraggingMonitor : 1;
29         };
30     };
31     DWORD ControlExStyle;
32     DWORD MonitorsCount;
33     INT SelectedMonitor;
34     INT DraggingMonitor;
35     RECT rcDragging;
36     POINT ptDrag, ptDragBegin;
37     SIZE DraggingMargin;
38     PMONSL_MONINFO MonitorInfo;
39     PMONSL_MON Monitors;
40     RECT rcExtent;
41     RECT rcMonitors;
42     RECT rcOldMonitors;
43     POINT ScrollPos;
44     SIZE Margin;
45     SIZE SelectionFrame;
46     HBITMAP hbmDisabledPattern;
47     HBRUSH hbrDisabled;
48 } MONITORSELWND, *PMONITORSELWND;
49 
50 static LRESULT
51 MonSelNotify(IN PMONITORSELWND infoPtr,
52              IN UINT code,
53              IN OUT PVOID data)
54 {
55     LRESULT Ret = 0;
56 
57     if (infoPtr->hNotify != NULL)
58     {
59         LPNMHDR pnmh = (LPNMHDR)data;
60 
61         pnmh->hwndFrom = infoPtr->hSelf;
62         pnmh->idFrom = GetWindowLongPtr(infoPtr->hSelf,
63                                         GWLP_ID);
64         pnmh->code = code;
65 
66         Ret = SendMessage(infoPtr->hNotify,
67                           WM_NOTIFY,
68                           (WPARAM)pnmh->idFrom,
69                           (LPARAM)pnmh);
70     }
71 
72     return Ret;
73 }
74 
75 static LRESULT
76 MonSelNotifyMonitor(IN PMONITORSELWND infoPtr,
77                     IN UINT code,
78                     IN INT Index,
79                     IN OUT PMONSL_MONNMHDR pmonnmh)
80 {
81     pmonnmh->Index = Index;
82 
83     if (Index >= 0)
84     {
85         pmonnmh->MonitorInfo = infoPtr->MonitorInfo[Index];
86     }
87     else
88     {
89         ZeroMemory(&pmonnmh->MonitorInfo,
90                    sizeof(pmonnmh->MonitorInfo));
91     }
92 
93     return MonSelNotify(infoPtr,
94                         code,
95                         pmonnmh);
96 }
97 
98 static HFONT
99 MonSelChangeFont(IN OUT PMONITORSELWND infoPtr,
100                  IN HFONT hFont,
101                  IN BOOL Redraw)
102 {
103     HFONT hOldFont = infoPtr->hFont;
104     infoPtr->hFont = hFont;
105 
106     if (Redraw)
107     {
108         InvalidateRect(infoPtr->hSelf,
109                        NULL,
110                        TRUE);
111     }
112 
113     return hOldFont;
114 }
115 
116 static VOID
117 MonSelRectToScreen(IN PMONITORSELWND infoPtr,
118                    IN const RECT *prc,
119                    OUT PRECT prcOnScreen)
120 {
121     *prcOnScreen = *prc;
122     OffsetRect(prcOnScreen,
123                -infoPtr->ScrollPos.x,
124                -infoPtr->ScrollPos.y);
125 }
126 
127 static VOID
128 MonSelScreenToPt(IN PMONITORSELWND infoPtr,
129                  IN const POINT *pptOnScreen,
130                  OUT PPOINT ppt)
131 {
132     ppt->x = pptOnScreen->x + infoPtr->ScrollPos.x;
133     ppt->y = pptOnScreen->y + infoPtr->ScrollPos.y;
134 }
135 
136 static VOID
137 MonSelMonInfoToRect(IN const MONSL_MONINFO *pMonInfo,
138                     OUT PRECT prc)
139 {
140     prc->left = pMonInfo->Position.x;
141     prc->top = pMonInfo->Position.y;
142     prc->right = pMonInfo->Position.x + pMonInfo->Size.cx;
143     prc->bottom = pMonInfo->Position.y + pMonInfo->Size.cy;
144 }
145 
146 static INT
147 MonSelHitTest(IN PMONITORSELWND infoPtr,
148               IN const POINT *ppt)
149 {
150     POINT pt;
151     INT Index, Ret = -1;
152 
153     if (infoPtr->CanDisplay)
154     {
155         MonSelScreenToPt(infoPtr,
156                          ppt,
157                          &pt);
158 
159         for (Index = 0; Index < (INT)infoPtr->MonitorsCount; Index++)
160         {
161             if (PtInRect(&infoPtr->Monitors[Index].rc,
162                          pt))
163             {
164                 Ret = Index;
165                 break;
166             }
167         }
168     }
169 
170     return Ret;
171 }
172 
173 static VOID
174 MonSelUpdateExtent(IN OUT PMONITORSELWND infoPtr)
175 {
176     DWORD Index;
177     RECT rcMonitor;
178 
179     /* NOTE: This routine calculates the extent of all monitor coordinates.
180              These are not control coordinates! */
181     if (infoPtr->MonitorsCount > 0)
182     {
183         MonSelMonInfoToRect(&infoPtr->MonitorInfo[0],
184                             &infoPtr->rcExtent);
185 
186         for (Index = 1; Index < infoPtr->MonitorsCount; Index++)
187         {
188             MonSelMonInfoToRect(&infoPtr->MonitorInfo[Index],
189                                 &rcMonitor);
190 
191             UnionRect(&infoPtr->rcExtent,
192                       &infoPtr->rcExtent,
193                       &rcMonitor);
194         }
195     }
196     else
197     {
198         ZeroMemory(&infoPtr->rcExtent,
199                    sizeof(infoPtr->rcExtent));
200     }
201 }
202 
203 static VOID
204 MonSelScaleRectRelative(IN const RECT *prcBaseFrom,
205                         IN const RECT *prcFrom,
206                         IN const RECT *prcBaseTo,
207                         OUT PRECT prcTo)
208 {
209     SIZE BaseFrom, BaseTo, From;
210 
211     BaseFrom.cx = prcBaseFrom->right - prcBaseFrom->left;
212     BaseFrom.cy = prcBaseFrom->bottom - prcBaseFrom->top;
213     BaseTo.cx = prcBaseTo->right - prcBaseTo->left;
214     BaseTo.cy = prcBaseTo->bottom - prcBaseTo->top;
215     From.cx = prcFrom->right - prcFrom->left;
216     From.cy = prcFrom->bottom - prcFrom->top;
217 
218     prcTo->left = prcBaseTo->left + (((prcFrom->left - prcBaseFrom->left) * BaseTo.cx) / BaseFrom.cx);
219     prcTo->top = prcBaseTo->top + (((prcFrom->top - prcBaseFrom->top) * BaseTo.cy) / BaseFrom.cy);
220     prcTo->right = prcTo->left + ((From.cx * BaseTo.cx) / BaseFrom.cx);
221     prcTo->bottom = prcTo->top + ((From.cy * BaseTo.cy) / BaseFrom.cy);
222 }
223 
224 static VOID
225 ScaleRectSizeFit(IN const RECT *prcContainerRect,
226                  IN OUT PRECT prcRectToScale)
227 {
228     SIZE ContainerSize, RectSize;
229 
230     ContainerSize.cx = prcContainerRect->right - prcContainerRect->left;
231     ContainerSize.cy = prcContainerRect->bottom - prcContainerRect->top;
232     RectSize.cx = prcRectToScale->right - prcRectToScale->left;
233     RectSize.cy = prcRectToScale->bottom - prcRectToScale->top;
234 
235     if (((RectSize.cx * 0xFFF) / RectSize.cy) < ((ContainerSize.cx * 0xFFF) / ContainerSize.cy))
236     {
237         RectSize.cx = (RectSize.cx * ((ContainerSize.cy * 0xFFF) / RectSize.cy)) / 0xFFF;
238         RectSize.cy = ContainerSize.cy;
239     }
240     else
241     {
242         RectSize.cy = (RectSize.cy * ((ContainerSize.cx * 0xFFF) / RectSize.cx)) / 0xFFF;
243         RectSize.cx = ContainerSize.cx;
244     }
245 
246     prcRectToScale->right = prcRectToScale->left + RectSize.cx;
247     prcRectToScale->bottom = prcRectToScale->top + RectSize.cy;
248 
249     OffsetRect(prcRectToScale,
250                prcContainerRect->left + ((ContainerSize.cx - RectSize.cx) / 2),
251                prcContainerRect->top + ((ContainerSize.cy - RectSize.cy) / 2));
252 }
253 
254 static VOID
255 MonSelRepaint(IN PMONITORSELWND infoPtr)
256 {
257     RECT rc;
258 
259     MonSelRectToScreen(infoPtr,
260                        &infoPtr->rcMonitors,
261                        &rc);
262     InvalidateRect(infoPtr->hSelf,
263                    &rc,
264                    TRUE);
265 
266     if (!EqualRect(&infoPtr->rcMonitors, &infoPtr->rcOldMonitors) &&
267         infoPtr->rcOldMonitors.right != infoPtr->rcOldMonitors.left)
268     {
269         MonSelRectToScreen(infoPtr, &infoPtr->rcOldMonitors, &rc);
270         InvalidateRect(infoPtr->hSelf, &rc, TRUE);
271         infoPtr->rcOldMonitors = infoPtr->rcMonitors;
272     }
273 }
274 
275 static VOID
276 MonSelRepaintMonitor(IN PMONITORSELWND infoPtr,
277                      IN DWORD Index)
278 {
279     RECT rc;
280     BOOL NoRepaint = FALSE;
281 
282     if (Index < infoPtr->MonitorsCount)
283     {
284         if (Index == (DWORD)infoPtr->DraggingMonitor)
285         {
286             if (infoPtr->IsDraggingMonitor)
287             {
288                 MonSelRectToScreen(infoPtr,
289                                    &infoPtr->rcDragging,
290                                    &rc);
291             }
292             else
293                 NoRepaint = TRUE;
294         }
295         else
296         {
297             MonSelRectToScreen(infoPtr,
298                                &infoPtr->Monitors[Index].rc,
299                                &rc);
300         }
301 
302         if (!NoRepaint)
303         {
304             InvalidateRect(infoPtr->hSelf,
305                            &rc,
306                            TRUE);
307         }
308     }
309 }
310 
311 static VOID
312 MonSelRepaintSelected(IN PMONITORSELWND infoPtr)
313 {
314     if (infoPtr->SelectedMonitor >= 0)
315     {
316         MonSelRepaintMonitor(infoPtr,
317                              (DWORD)infoPtr->SelectedMonitor);
318     }
319 }
320 
321 static VOID
322 MonSelResetMonitors(IN OUT PMONITORSELWND infoPtr)
323 {
324     DWORD Index;
325 
326     for (Index = 0; Index < infoPtr->MonitorsCount; Index++)
327     {
328         if (infoPtr->Monitors[Index].hFont != NULL)
329         {
330             DeleteObject(infoPtr->Monitors[Index].hFont);
331             infoPtr->Monitors[Index].hFont = NULL;
332         }
333     }
334 }
335 
336 
337 static VOID
338 MonSelUpdateMonitorsInfo(IN OUT PMONITORSELWND infoPtr,
339                          IN BOOL bRepaint)
340 {
341     RECT rcExtSurface, rcExtDisplay;
342     DWORD Index;
343 
344     /* Recalculate rcExtent */
345     MonSelUpdateExtent(infoPtr);
346 
347     infoPtr-> CanDisplay = infoPtr->MonitorsCount != 0 &&
348                            (infoPtr->ClientSize.cx > (2 * (infoPtr->Margin.cx + infoPtr->SelectionFrame.cx))) &&
349                            (infoPtr->ClientSize.cy > (2 * (infoPtr->Margin.cy + infoPtr->SelectionFrame.cy)));
350 
351     if (infoPtr->CanDisplay)
352     {
353         /* Calculate the rectangle on the control in which may be painted */
354         rcExtSurface.left = infoPtr->Margin.cx;
355         rcExtSurface.top = infoPtr->Margin.cy;
356         rcExtSurface.right = rcExtSurface.left + infoPtr->ClientSize.cx - (2 * infoPtr->Margin.cx);
357         rcExtSurface.bottom = rcExtSurface.top + infoPtr->ClientSize.cy - (2 * infoPtr->Margin.cy);
358 
359         /* Calculate the rectangle on the control that is actually painted on */
360         rcExtDisplay.left = rcExtDisplay.top = 0;
361         rcExtDisplay.right = infoPtr->rcExtent.right - infoPtr->rcExtent.left;
362         rcExtDisplay.bottom = infoPtr->rcExtent.bottom - infoPtr->rcExtent.top;
363 
364         ScaleRectSizeFit(&rcExtSurface,
365                          &rcExtDisplay);
366 
367         infoPtr->rcOldMonitors = infoPtr->rcMonitors;
368         infoPtr->rcMonitors = rcExtDisplay;
369 
370         /* Now that we know in which area all monitors are located,
371            calculate the monitors selection rectangles on the screen */
372 
373         for (Index = 0; Index < infoPtr->MonitorsCount; Index++)
374         {
375             MonSelMonInfoToRect(&infoPtr->MonitorInfo[Index],
376                                 &rcExtDisplay);
377 
378             MonSelScaleRectRelative(&infoPtr->rcExtent,
379                                     &rcExtDisplay,
380                                     &infoPtr->rcMonitors,
381                                     &infoPtr->Monitors[Index].rc);
382         }
383 
384         MonSelResetMonitors(infoPtr);
385 
386         if (bRepaint)
387             MonSelRepaint(infoPtr);
388     }
389     else if (bRepaint)
390     {
391         InvalidateRect(infoPtr->hSelf,
392                        NULL,
393                        TRUE);
394     }
395 }
396 
397 static BOOL
398 MonSelSetMonitorsInfo(IN OUT PMONITORSELWND infoPtr,
399                       IN DWORD dwMonitors,
400                       IN const MONSL_MONINFO *MonitorsInfo)
401 {
402     DWORD Index;
403     BOOL Ret = TRUE;
404 
405     if (infoPtr->DraggingMonitor >= 0)
406         return FALSE;
407 
408     if (infoPtr->MonitorInfo != NULL)
409     {
410         LocalFree((HLOCAL)infoPtr->MonitorInfo);
411         infoPtr->MonitorInfo = NULL;
412 
413         MonSelResetMonitors(infoPtr);
414 
415         LocalFree((HLOCAL)infoPtr->Monitors);
416         infoPtr->Monitors = NULL;
417 
418         infoPtr->MonitorsCount = 0;
419     }
420 
421     if (dwMonitors != 0)
422     {
423         infoPtr->MonitorInfo = (PMONSL_MONINFO)LocalAlloc(LMEM_FIXED,
424                                                           dwMonitors * sizeof(MONSL_MONINFO));
425         if (infoPtr->MonitorInfo != NULL)
426         {
427             infoPtr->Monitors = (PMONSL_MON)LocalAlloc(LMEM_FIXED,
428                                                        dwMonitors * sizeof(MONSL_MON));
429             if (infoPtr->Monitors != NULL)
430             {
431                 CopyMemory(infoPtr->MonitorInfo,
432                            MonitorsInfo,
433                            dwMonitors * sizeof(MONSL_MONINFO));
434                 ZeroMemory(infoPtr->Monitors,
435                            dwMonitors * sizeof(MONSL_MON));
436 
437                 for (Index = 0; Index < dwMonitors; Index++)
438                 {
439                     _stprintf(infoPtr->Monitors[Index].szCaption,
440                               _T("%u"),
441                               Index + 1);
442                 }
443 
444                 infoPtr->MonitorsCount = dwMonitors;
445 
446                 if (infoPtr->SelectedMonitor >= (INT)infoPtr->MonitorsCount)
447                     infoPtr->SelectedMonitor = -1;
448 
449                 if (!(infoPtr->ControlExStyle & MSLM_EX_ALLOWSELECTNONE) && infoPtr->SelectedMonitor < 0)
450                     infoPtr->SelectedMonitor = 0;
451 
452                 MonSelUpdateMonitorsInfo(infoPtr,
453                                          TRUE);
454             }
455             else
456             {
457                 LocalFree((HLOCAL)infoPtr->MonitorInfo);
458                 infoPtr->MonitorInfo = NULL;
459 
460                 Ret = FALSE;
461             }
462         }
463         else
464             Ret = FALSE;
465     }
466 
467     if (!Ret)
468         infoPtr->SelectedMonitor = -1;
469 
470     if (!Ret || dwMonitors == 0)
471     {
472         InvalidateRect(infoPtr->hSelf,
473                        NULL,
474                        TRUE);
475     }
476 
477     return Ret;
478 }
479 
480 static DWORD
481 MonSelGetMonitorsInfo(IN PMONITORSELWND infoPtr,
482                       IN DWORD dwMonitors,
483                       IN OUT PMONSL_MONINFO MonitorsInfo)
484 {
485     if (dwMonitors != 0)
486     {
487         if (dwMonitors > infoPtr->MonitorsCount)
488             dwMonitors = infoPtr->MonitorsCount;
489 
490         CopyMemory(MonitorsInfo,
491                    infoPtr->MonitorInfo,
492                    dwMonitors * sizeof(MONSL_MONINFO));
493         return dwMonitors;
494     }
495     else
496         return infoPtr->MonitorsCount;
497 }
498 
499 static BOOL
500 MonSelSetMonitorInfo(IN OUT PMONITORSELWND infoPtr,
501                      IN INT Index,
502                      IN const MONSL_MONINFO *MonitorsInfo)
503 {
504     if (infoPtr->DraggingMonitor < 0 &&
505         Index >= 0 && Index < (INT)infoPtr->MonitorsCount)
506     {
507         CopyMemory(&infoPtr->MonitorInfo[Index],
508                    MonitorsInfo,
509                    sizeof(MONSL_MONINFO));
510 
511         MonSelUpdateMonitorsInfo(infoPtr,
512                                  TRUE);
513         return TRUE;
514     }
515 
516     return FALSE;
517 }
518 
519 static BOOL
520 MonSelGetMonitorInfo(IN PMONITORSELWND infoPtr,
521                      IN INT Index,
522                      IN OUT PMONSL_MONINFO MonitorsInfo)
523 {
524     if (Index >= 0 && Index < (INT)infoPtr->MonitorsCount)
525     {
526         CopyMemory(MonitorsInfo,
527                    &infoPtr->MonitorInfo[Index],
528                    sizeof(MONSL_MONINFO));
529         return TRUE;
530     }
531 
532     return FALSE;
533 }
534 
535 static INT
536 MonSelGetMonitorRect(IN OUT PMONITORSELWND infoPtr,
537                      IN INT Index,
538                      OUT PRECT prc)
539 {
540     RECT rc, rcClient;
541 
542     if (Index < 0 || (UINT)Index >= infoPtr->MonitorsCount)
543         return -1;
544 
545     if (!infoPtr->CanDisplay)
546         return 0;
547 
548     MonSelRectToScreen(infoPtr,
549                        &infoPtr->Monitors[Index].rc,
550                        prc);
551 
552     rcClient.left = rcClient.top = 0;
553     rcClient.right = infoPtr->ClientSize.cx;
554     rcClient.bottom = infoPtr->ClientSize.cy;
555 
556     return IntersectRect(&rc,
557                          &rcClient,
558                          prc) != FALSE;
559 }
560 
561 static BOOL
562 MonSelSetCurSelMonitor(IN OUT PMONITORSELWND infoPtr,
563                        IN INT Index,
564                        IN BOOL bNotify)
565 {
566     INT PrevSel;
567     BOOL PreventSelect = FALSE;
568     BOOL Ret = FALSE;
569 
570     if (infoPtr->DraggingMonitor < 0 &&
571         (Index == -1 || Index < (INT)infoPtr->MonitorsCount))
572     {
573         if (Index != infoPtr->SelectedMonitor)
574         {
575             if ((infoPtr->MonitorInfo[Index].Flags & MSL_MIF_DISABLED) &&
576                 !(infoPtr->ControlExStyle & MSLM_EX_ALLOWSELECTDISABLED))
577             {
578                 PreventSelect = TRUE;
579             }
580 
581             if (!PreventSelect && bNotify)
582             {
583                 MONSL_MONNMMONITORCHANGING nmi;
584 
585                 nmi.PreviousSelected = infoPtr->SelectedMonitor;
586                 nmi.AllowChanging = TRUE;
587 
588                 MonSelNotifyMonitor(infoPtr,
589                                     MSLN_MONITORCHANGING,
590                                     Index,
591                                     &nmi.hdr);
592 
593                 PreventSelect = (nmi.AllowChanging == FALSE);
594             }
595 
596             if (!PreventSelect)
597             {
598                 PrevSel = infoPtr->SelectedMonitor;
599                 infoPtr->SelectedMonitor = Index;
600 
601                 if (PrevSel >= 0)
602                 {
603                     MonSelRepaintMonitor(infoPtr,
604                                          PrevSel);
605                 }
606 
607                 if (infoPtr->SelectedMonitor >= 0)
608                     MonSelRepaintSelected(infoPtr);
609 
610                 if (bNotify)
611                 {
612                     MONSL_MONNMHDR nm;
613 
614                     MonSelNotifyMonitor(infoPtr,
615                                         MSLN_MONITORCHANGED,
616                                         Index,
617                                         &nm);
618                 }
619             }
620         }
621 
622         Ret = TRUE;
623     }
624 
625     return Ret;
626 }
627 
628 static VOID
629 MonSelCreate(IN OUT PMONITORSELWND infoPtr)
630 {
631     infoPtr->SelectionFrame.cx = infoPtr->SelectionFrame.cy = 4;
632     infoPtr->Margin.cx = infoPtr->Margin.cy = 20;
633     infoPtr->SelectedMonitor = -1;
634     infoPtr->DraggingMonitor = -1;
635     infoPtr->ControlExStyle = MSLM_EX_ALLOWSELECTDISABLED | MSLM_EX_HIDENUMBERONSINGLE |
636         MSLM_EX_SELECTONRIGHTCLICK | MSLM_EX_SELECTBYARROWKEY;
637     return;
638 }
639 
640 static VOID
641 MonSelDestroy(IN OUT PMONITORSELWND infoPtr)
642 {
643     /* Free all monitors */
644     MonSelSetMonitorsInfo(infoPtr,
645                           0,
646                           NULL);
647 
648     if (infoPtr->hbrDisabled != NULL)
649     {
650         DeleteObject(infoPtr->hbrDisabled);
651         infoPtr->hbrDisabled = NULL;
652     }
653 
654     if (infoPtr->hbmDisabledPattern != NULL)
655     {
656         DeleteObject(infoPtr->hbmDisabledPattern);
657         infoPtr->hbmDisabledPattern = NULL;
658     }
659 }
660 
661 static BOOL
662 MonSelSetExtendedStyle(IN OUT PMONITORSELWND infoPtr,
663                        IN DWORD dwExtendedStyle)
664 {
665     if (infoPtr->DraggingMonitor >= 0)
666         return FALSE;
667 
668     if (dwExtendedStyle != infoPtr->ControlExStyle)
669     {
670         infoPtr->ControlExStyle = dwExtendedStyle;
671 
672         /* Repaint the control */
673         InvalidateRect(infoPtr->hSelf,
674                        NULL,
675                        TRUE);
676     }
677 
678     return TRUE;
679 }
680 
681 static DWORD
682 MonSelGetExtendedStyle(IN PMONITORSELWND infoPtr)
683 {
684     return infoPtr->ControlExStyle;
685 }
686 
687 static HFONT
688 MonSelGetMonitorFont(IN OUT PMONITORSELWND infoPtr,
689                      IN HDC hDC,
690                      IN INT Index)
691 {
692     TEXTMETRIC tm;
693     SIZE rcsize;
694     LOGFONT lf;
695     HFONT hPrevFont, hFont;
696     //INT len;
697 
698     hFont = infoPtr->Monitors[Index].hFont;
699     if (hFont == NULL &&
700         GetObject(infoPtr->hFont,
701                   sizeof(lf),
702                   &lf) != 0)
703     {
704         rcsize.cx = infoPtr->Monitors[Index].rc.right - infoPtr->Monitors[Index].rc.left -
705                     (2 * infoPtr->SelectionFrame.cx) - 2;
706         rcsize.cy = infoPtr->Monitors[Index].rc.bottom - infoPtr->Monitors[Index].rc.top -
707                     (2 * infoPtr->SelectionFrame.cy) - 2;
708         rcsize.cy = (rcsize.cy * 60) / 100;
709 
710         //len = _tcslen(infoPtr->Monitors[Index].szCaption);
711 
712         hPrevFont = SelectObject(hDC,
713                                  infoPtr->hFont);
714 
715         if (GetTextMetrics(hDC,
716                            &tm))
717         {
718             lf.lfWeight = FW_SEMIBOLD;
719             lf.lfHeight = -MulDiv(rcsize.cy - tm.tmExternalLeading,
720                                   GetDeviceCaps(hDC,
721                                                 LOGPIXELSY),
722                                   72);
723 
724             hFont = CreateFontIndirect(&lf);
725             if (hFont != NULL)
726                 infoPtr->Monitors[Index].hFont = hFont;
727         }
728 
729         SelectObject(hDC,
730                      hPrevFont);
731     }
732 
733     return hFont;
734 }
735 
736 static BOOL
737 MonSelDrawDisabledRect(IN OUT PMONITORSELWND infoPtr,
738                        IN HDC hDC,
739                        IN const RECT *prc)
740 {
741     BOOL Ret = FALSE;
742 
743     if (infoPtr->hbrDisabled == NULL)
744     {
745         static const DWORD Pattern[4] = {0x5555AAAA, 0x5555AAAA, 0x5555AAAA, 0x5555AAAA};
746 
747         if (infoPtr->hbmDisabledPattern == NULL)
748         {
749             infoPtr->hbmDisabledPattern = CreateBitmap(8,
750                                                        8,
751                                                        1,
752                                                        1,
753                                                        Pattern);
754         }
755 
756         if (infoPtr->hbmDisabledPattern != NULL)
757             infoPtr->hbrDisabled = CreatePatternBrush(infoPtr->hbmDisabledPattern);
758     }
759 
760     if (infoPtr->hbrDisabled != NULL)
761     {
762         /* FIXME: Implement */
763     }
764 
765     return Ret;
766 }
767 
768 static VOID
769 MonSelPaintMonitor(IN OUT PMONITORSELWND infoPtr,
770                    IN HDC hDC,
771                    IN DWORD Index,
772                    IN OUT PRECT prc,
773                    IN COLORREF crDefFontColor,
774                    IN BOOL bHideNumber)
775 {
776     HFONT hFont, hPrevFont;
777     COLORREF crPrevText;
778 
779     if ((INT)Index == infoPtr->SelectedMonitor)
780     {
781         FillRect(hDC,
782                  prc,
783                  (HBRUSH)(COLOR_HIGHLIGHT + 1));
784 
785         if (infoPtr->HasFocus && !(infoPtr->UIState & UISF_HIDEFOCUS))
786         {
787             /* NOTE: We need to switch the text color to the default, because
788                      DrawFocusRect draws a solid line if the text is white! */
789 
790             crPrevText = SetTextColor(hDC,
791                                       crDefFontColor);
792 
793             DrawFocusRect(hDC,
794                           prc);
795 
796             SetTextColor(hDC,
797                          crPrevText);
798         }
799     }
800 
801     InflateRect(prc,
802                 -infoPtr->SelectionFrame.cx,
803                 -infoPtr->SelectionFrame.cy);
804 
805     Rectangle(hDC,
806               prc->left,
807               prc->top,
808               prc->right,
809               prc->bottom);
810 
811     InflateRect(prc,
812                 -1,
813                 -1);
814 
815     if (!bHideNumber)
816     {
817         hFont = MonSelGetMonitorFont(infoPtr,
818                                      hDC,
819                                      Index);
820         if (hFont != NULL)
821         {
822             hPrevFont = SelectObject(hDC,
823                                      hFont);
824 
825             DrawText(hDC,
826                      infoPtr->Monitors[Index].szCaption,
827                      -1,
828                      prc,
829                      DT_VCENTER | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE);
830 
831             SelectObject(hDC,
832                          hPrevFont);
833         }
834     }
835 
836     if (infoPtr->MonitorInfo[Index].Flags & MSL_MIF_DISABLED)
837     {
838         InflateRect(prc,
839                     1,
840                     1);
841 
842         MonSelDrawDisabledRect(infoPtr,
843                                hDC,
844                                prc);
845     }
846 }
847 
848 static VOID
849 MonSelPaint(IN OUT PMONITORSELWND infoPtr,
850             IN HDC hDC,
851             IN const RECT *prcUpdate)
852 {
853     COLORREF crPrevText;
854     HBRUSH hbBk, hbOldBk;
855     HPEN hpFg, hpOldFg;
856     DWORD Index;
857     RECT rc, rctmp;
858     INT iPrevBkMode;
859     BOOL bHideNumber;
860 
861     bHideNumber = (infoPtr->ControlExStyle & MSLM_EX_HIDENUMBERS) ||
862         ((infoPtr->MonitorsCount == 1) && (infoPtr->ControlExStyle & MSLM_EX_HIDENUMBERONSINGLE));
863 
864     hbBk = GetSysColorBrush(COLOR_BACKGROUND);
865     hpFg = CreatePen(PS_SOLID,
866                      0,
867                      GetSysColor(COLOR_HIGHLIGHTTEXT));
868 
869     hbOldBk = SelectObject(hDC,
870                            hbBk);
871     hpOldFg = SelectObject(hDC,
872                            hpFg);
873     iPrevBkMode = SetBkMode(hDC,
874                             TRANSPARENT);
875     crPrevText = SetTextColor(hDC,
876                               GetSysColor(COLOR_HIGHLIGHTTEXT));
877 
878     for (Index = 0; Index < infoPtr->MonitorsCount; Index++)
879     {
880         if (infoPtr->IsDraggingMonitor &&
881             (DWORD)infoPtr->DraggingMonitor == Index)
882         {
883             continue;
884         }
885 
886         MonSelRectToScreen(infoPtr,
887                            &infoPtr->Monitors[Index].rc,
888                            &rc);
889 
890         if (IntersectRect(&rctmp,
891                           &rc,
892                           prcUpdate))
893         {
894             MonSelPaintMonitor(infoPtr,
895                                hDC,
896                                Index,
897                                &rc,
898                                crPrevText,
899                                bHideNumber);
900         }
901     }
902 
903     /* Paint the dragging monitor last */
904     if (infoPtr->IsDraggingMonitor &&
905         infoPtr->DraggingMonitor >= 0)
906     {
907         MonSelRectToScreen(infoPtr,
908                            &infoPtr->rcDragging,
909                            &rc);
910 
911         if (IntersectRect(&rctmp,
912                           &rc,
913                           prcUpdate))
914         {
915             MonSelPaintMonitor(infoPtr,
916                                hDC,
917                                (DWORD)infoPtr->DraggingMonitor,
918                                &rc,
919                                crPrevText,
920                                bHideNumber);
921         }
922     }
923 
924     SetTextColor(hDC,
925                  crPrevText);
926     SetBkMode(hDC,
927               iPrevBkMode);
928     SelectObject(hDC,
929                  hpOldFg);
930     SelectObject(hDC,
931                  hbOldBk);
932 
933     DeleteObject(hpFg);
934 }
935 
936 static VOID
937 MonSelContextMenu(IN OUT PMONITORSELWND infoPtr,
938                   IN SHORT x,
939                   IN SHORT y)
940 {
941     MONSL_MONNMBUTTONCLICKED nm;
942     INT Index;
943 
944     if (!infoPtr->HasFocus)
945         SetFocus(infoPtr->hSelf);
946 
947     nm.pt.x = x;
948     nm.pt.y = y;
949 
950     Index = MonSelHitTest(infoPtr,
951                           &nm.pt);
952 
953     MonSelNotifyMonitor(infoPtr,
954                         MSLN_RBUTTONUP,
955                         Index,
956                         (PMONSL_MONNMHDR)&nm);
957 
958     /* Send a WM_CONTEXTMENU notification */
959     MapWindowPoints(infoPtr->hSelf,
960                     NULL,
961                     &nm.pt,
962                     1);
963 
964     SendMessage(infoPtr->hSelf,
965                 WM_CONTEXTMENU,
966                 (WPARAM)infoPtr->hSelf,
967                 MAKELPARAM(nm.pt.x,
968                            nm.pt.y));
969 }
970 
971 static VOID
972 MonSelApplyCursorClipping(IN PMONITORSELWND infoPtr,
973                           IN BOOL bClip)
974 {
975     RECT rc;
976 
977     if (bClip)
978     {
979         rc.left = rc.top = 0;
980         rc.right = infoPtr->ClientSize.cx;
981         rc.bottom = infoPtr->ClientSize.cy;
982 
983         if (MapWindowPoints(infoPtr->hSelf,
984                             NULL,
985                             (LPPOINT)&rc,
986                             2))
987         {
988             ClipCursor(&rc);
989         }
990     }
991     else
992     {
993         ClipCursor(NULL);
994     }
995 }
996 
997 static VOID
998 MonSelMoveDragRect(IN OUT PMONITORSELWND infoPtr,
999                    IN PPOINT ppt)
1000 {
1001     RECT rcPrev, rcUpdate, *prc;
1002     HRGN hRgnPrev;
1003     HDC hDC;
1004 
1005     if (infoPtr->CanDisplay)
1006     {
1007         hDC = GetDC(infoPtr->hSelf);
1008         if (hDC != NULL)
1009         {
1010             if (infoPtr->ptDrag.x != ppt->x ||
1011                 infoPtr->ptDrag.y != ppt->y)
1012             {
1013                 infoPtr->ptDrag = *ppt;
1014 
1015                 rcPrev = infoPtr->rcDragging;
1016 
1017                 /* Calculate updated dragging rectangle */
1018                 prc = &infoPtr->Monitors[infoPtr->DraggingMonitor].rc;
1019                 infoPtr->rcDragging.left = ppt->x - infoPtr->DraggingMargin.cx;
1020                 infoPtr->rcDragging.top = ppt->y - infoPtr->DraggingMargin.cy;
1021                 infoPtr->rcDragging.right = infoPtr->rcDragging.left + (prc->right - prc->left);
1022                 infoPtr->rcDragging.bottom = infoPtr->rcDragging.top + (prc->bottom - prc->top);
1023 
1024                 hRgnPrev = CreateRectRgn(rcPrev.left,
1025                                          rcPrev.top,
1026                                          rcPrev.right,
1027                                          rcPrev.bottom);
1028 
1029                 if (hRgnPrev != NULL)
1030                 {
1031                     if (!ScrollDC(hDC,
1032                                   infoPtr->rcDragging.left - rcPrev.left,
1033                                   infoPtr->rcDragging.top - rcPrev.top,
1034                                   &rcPrev,
1035                                   NULL,
1036                                   hRgnPrev,
1037                                   &rcUpdate) ||
1038                         !InvalidateRgn(infoPtr->hSelf,
1039                                        hRgnPrev,
1040                                        TRUE))
1041                     {
1042                         DeleteObject(hRgnPrev);
1043                         goto InvRects;
1044                     }
1045 
1046                     DeleteObject(hRgnPrev);
1047                 }
1048                 else
1049                 {
1050 InvRects:
1051                     InvalidateRect(infoPtr->hSelf,
1052                                    &rcPrev,
1053                                    TRUE);
1054                     InvalidateRect(infoPtr->hSelf,
1055                                    &infoPtr->rcDragging,
1056                                    TRUE);
1057                 }
1058             }
1059 
1060             ReleaseDC(infoPtr->hSelf,
1061                       hDC);
1062         }
1063     }
1064 }
1065 
1066 static VOID
1067 MonSelCancelDragging(IN OUT PMONITORSELWND infoPtr)
1068 {
1069     DWORD Index;
1070 
1071     if (infoPtr->DraggingMonitor >= 0)
1072     {
1073         MonSelMoveDragRect(infoPtr,
1074                            &infoPtr->ptDragBegin);
1075 
1076         Index = (DWORD)infoPtr->DraggingMonitor;
1077         infoPtr->DraggingMonitor = -1;
1078 
1079         if (infoPtr->CanDisplay)
1080         {
1081             /* Repaint the area where the monitor was last dragged */
1082             MonSelRepaintMonitor(infoPtr,
1083                                  Index);
1084 
1085             infoPtr->IsDraggingMonitor = FALSE;
1086 
1087             /* Repaint the area where the monitor is located */
1088             MonSelRepaintMonitor(infoPtr,
1089                                  Index);
1090         }
1091         else
1092             infoPtr->IsDraggingMonitor = FALSE;
1093 
1094         ReleaseCapture();
1095 
1096         MonSelApplyCursorClipping(infoPtr,
1097                                   FALSE);
1098     }
1099 }
1100 
1101 static VOID
1102 MonSelInitDragging(IN OUT PMONITORSELWND infoPtr,
1103                    IN DWORD Index,
1104                    IN PPOINT ppt)
1105 {
1106     POINT pt;
1107 
1108     MonSelCancelDragging(infoPtr);
1109     infoPtr->IsDraggingMonitor = FALSE;
1110 
1111     MonSelScreenToPt(infoPtr,
1112                      ppt,
1113                      &pt);
1114 
1115     infoPtr->ptDrag = infoPtr->ptDragBegin = pt;
1116     infoPtr->DraggingMonitor = (INT)Index;
1117 
1118     infoPtr->DraggingMargin.cx = ppt->x - infoPtr->Monitors[Index].rc.left;
1119     infoPtr->DraggingMargin.cy = ppt->y - infoPtr->Monitors[Index].rc.top;
1120     infoPtr->rcDragging = infoPtr->Monitors[Index].rc;
1121 
1122     MonSelApplyCursorClipping(infoPtr,
1123                               TRUE);
1124 }
1125 
1126 static VOID
1127 MonSelDrag(IN OUT PMONITORSELWND infoPtr,
1128            IN PPOINT ppt)
1129 {
1130     SIZE szDrag;
1131     POINT pt;
1132     RECT rcDrag;
1133 
1134     if (infoPtr->DraggingMonitor >= 0)
1135     {
1136         MonSelScreenToPt(infoPtr,
1137                          ppt,
1138                          &pt);
1139 
1140         if (!infoPtr->IsDraggingMonitor)
1141         {
1142             szDrag.cx = GetSystemMetrics(SM_CXDRAG);
1143             szDrag.cy = GetSystemMetrics(SM_CYDRAG);
1144 
1145             rcDrag.left = infoPtr->Monitors[infoPtr->DraggingMonitor].rc.left + infoPtr->DraggingMargin.cx - (szDrag.cx / 2);
1146             rcDrag.top = infoPtr->Monitors[infoPtr->DraggingMonitor].rc.top + infoPtr->DraggingMargin.cy - (szDrag.cy / 2);
1147             rcDrag.right = rcDrag.left + szDrag.cx;
1148             rcDrag.bottom = rcDrag.top + szDrag.cy;
1149 
1150             if (!PtInRect(&rcDrag,
1151                           pt))
1152             {
1153                 /* The user started moving around the mouse: Begin dragging */
1154                 infoPtr->IsDraggingMonitor = TRUE;
1155                 MonSelMoveDragRect(infoPtr,
1156                                    &pt);
1157             }
1158         }
1159         else
1160         {
1161             MonSelMoveDragRect(infoPtr,
1162                                &pt);
1163         }
1164     }
1165 }
1166 
1167 static LRESULT CALLBACK
1168 MonitorSelWndProc(IN HWND hwnd,
1169                   IN UINT uMsg,
1170                   IN WPARAM wParam,
1171                   IN LPARAM lParam)
1172 {
1173     PMONITORSELWND infoPtr;
1174     LRESULT Ret = 0;
1175 
1176     infoPtr = (PMONITORSELWND)GetWindowLongPtrW(hwnd,
1177                                                 0);
1178 
1179     if (infoPtr == NULL && uMsg != WM_CREATE)
1180     {
1181         goto HandleDefaultMessage;
1182     }
1183 
1184     switch (uMsg)
1185     {
1186         case WM_PAINT:
1187         case WM_PRINTCLIENT:
1188         {
1189             PAINTSTRUCT ps;
1190             HDC hDC;
1191 
1192             if (wParam != 0)
1193             {
1194                 if (!GetUpdateRect(hwnd,
1195                                    &ps.rcPaint,
1196                                    TRUE))
1197                 {
1198                     break;
1199                 }
1200                 hDC = (HDC)wParam;
1201             }
1202             else
1203             {
1204                 hDC = BeginPaint(hwnd,
1205                                  &ps);
1206                 if (hDC == NULL)
1207                 {
1208                     break;
1209                 }
1210             }
1211 
1212             if (infoPtr->CanDisplay)
1213             {
1214                 MonSelPaint(infoPtr,
1215                             hDC,
1216                             &ps.rcPaint);
1217             }
1218 
1219             if (wParam == 0)
1220             {
1221                 EndPaint(hwnd,
1222                          &ps);
1223             }
1224             break;
1225         }
1226 
1227         case WM_MOUSEMOVE:
1228         {
1229             POINT pt;
1230 
1231             if (!(wParam & MK_LBUTTON))
1232             {
1233                 MonSelCancelDragging(infoPtr);
1234                 break;
1235             }
1236 
1237             if (infoPtr->LeftBtnDown)
1238             {
1239                 pt.x = (LONG)LOWORD(lParam);
1240                 pt.y = (LONG)HIWORD(lParam);
1241 
1242                 MonSelDrag(infoPtr,
1243                            &pt);
1244             }
1245 
1246             break;
1247         }
1248 
1249         case WM_RBUTTONDOWN:
1250         {
1251             if (!(infoPtr->ControlExStyle & MSLM_EX_SELECTONRIGHTCLICK))
1252                 break;
1253 
1254             /* Fall through */
1255         }
1256 
1257         case WM_LBUTTONDBLCLK:
1258         case WM_LBUTTONDOWN:
1259         {
1260             INT Index;
1261             POINT pt;
1262 
1263             if (!infoPtr->HasFocus)
1264                 SetFocus(infoPtr->hSelf);
1265 
1266             pt.x = (LONG)LOWORD(lParam);
1267             pt.y = (LONG)HIWORD(lParam);
1268 
1269             Index = MonSelHitTest(infoPtr,
1270                                   &pt);
1271             if (Index >= 0 || (infoPtr->ControlExStyle & MSLM_EX_ALLOWSELECTNONE))
1272             {
1273                 MonSelSetCurSelMonitor(infoPtr,
1274                                        Index,
1275                                        TRUE);
1276             }
1277 
1278             if (Index >= 0 && (uMsg == WM_LBUTTONDOWN || uMsg == WM_LBUTTONDBLCLK))
1279             {
1280                 infoPtr->LeftBtnDown = TRUE;
1281                 MonSelInitDragging(infoPtr,
1282                                    (DWORD)Index,
1283                                    &pt);
1284             }
1285 
1286             /* Fall through */
1287         }
1288 
1289         case WM_MBUTTONDOWN:
1290         {
1291             if (!infoPtr->HasFocus)
1292                 SetFocus(hwnd);
1293             break;
1294         }
1295 
1296         case WM_RBUTTONUP:
1297         {
1298             MonSelContextMenu(infoPtr,
1299                               (SHORT)LOWORD(lParam),
1300                               (SHORT)HIWORD(lParam));
1301             break;
1302         }
1303 
1304         case WM_LBUTTONUP:
1305         {
1306             MonSelCancelDragging(infoPtr);
1307             infoPtr->LeftBtnDown = FALSE;
1308             break;
1309         }
1310 
1311         case WM_GETDLGCODE:
1312         {
1313             INT virtKey;
1314 
1315             virtKey = (lParam != 0 ? (INT)((LPMSG)lParam)->wParam : 0);
1316             switch (virtKey)
1317             {
1318                 case VK_TAB:
1319                 {
1320                     /* Change the UI status */
1321                     SendMessage(GetAncestor(hwnd,
1322                                             GA_PARENT),
1323                                 WM_CHANGEUISTATE,
1324                                 MAKEWPARAM(UIS_INITIALIZE,
1325                                            0),
1326                                 0);
1327                     break;
1328                 }
1329             }
1330 
1331             Ret |= DLGC_WANTARROWS;
1332 
1333             if (infoPtr->ControlExStyle & MSLM_EX_SELECTBYNUMKEY)
1334                 Ret |= DLGC_WANTCHARS;
1335             break;
1336         }
1337 
1338         case WM_SETFOCUS:
1339         {
1340             infoPtr->HasFocus = TRUE;
1341             MonSelRepaintSelected(infoPtr);
1342             break;
1343         }
1344 
1345         case WM_KILLFOCUS:
1346         {
1347             infoPtr->HasFocus = FALSE;
1348             MonSelCancelDragging(infoPtr);
1349             MonSelRepaintSelected(infoPtr);
1350             break;
1351         }
1352 
1353         case WM_UPDATEUISTATE:
1354         {
1355             DWORD OldUIState;
1356 
1357             Ret = DefWindowProcW(hwnd,
1358                                  uMsg,
1359                                  wParam,
1360                                  lParam);
1361 
1362             OldUIState = infoPtr->UIState;
1363             switch (LOWORD(wParam))
1364             {
1365                 case UIS_SET:
1366                     infoPtr->UIState |= HIWORD(wParam);
1367                     break;
1368 
1369                 case UIS_CLEAR:
1370                     infoPtr->UIState &= ~HIWORD(wParam);
1371                     break;
1372             }
1373 
1374             if (infoPtr->UIState != OldUIState)
1375                 MonSelRepaintSelected(infoPtr);
1376             break;
1377         }
1378 
1379         case WM_SETFONT:
1380         {
1381             Ret = (LRESULT)MonSelChangeFont(infoPtr,
1382                                             (HFONT)wParam,
1383                                             (BOOL)LOWORD(lParam));
1384             break;
1385         }
1386 
1387         case WM_SIZE:
1388         {
1389             infoPtr->ClientSize.cx = LOWORD(lParam);
1390             infoPtr->ClientSize.cy = HIWORD(lParam);
1391 
1392             /* Don't let MonSelUpdateMonitorsInfo repaint the control
1393                because this won't work properly in case the control
1394                was sized down! */
1395             MonSelUpdateMonitorsInfo(infoPtr,
1396                                      FALSE);
1397             InvalidateRect(infoPtr->hSelf,
1398                            NULL,
1399                            TRUE);
1400             break;
1401         }
1402 
1403         case WM_GETFONT:
1404         {
1405             Ret = (LRESULT)infoPtr->hFont;
1406             break;
1407         }
1408 
1409         case WM_ENABLE:
1410         {
1411             infoPtr->Enabled = ((BOOL)wParam != FALSE);
1412             MonSelRepaint(infoPtr);
1413             break;
1414         }
1415 
1416         case WM_STYLECHANGED:
1417         {
1418             if (wParam == GWL_STYLE)
1419             {
1420                 unsigned int OldEnabled = infoPtr->Enabled;
1421                 infoPtr->Enabled = !(((LPSTYLESTRUCT)lParam)->styleNew & WS_DISABLED);
1422 
1423                 if (OldEnabled != infoPtr->Enabled)
1424                     MonSelRepaint(infoPtr);
1425             }
1426             break;
1427         }
1428 
1429         case WM_KEYDOWN:
1430         {
1431             INT Index;
1432 
1433             if (infoPtr->ControlExStyle & MSLM_EX_SELECTBYARROWKEY)
1434             {
1435                 switch (wParam)
1436                 {
1437                     case VK_UP:
1438                     case VK_LEFT:
1439                     {
1440                         Index = infoPtr->SelectedMonitor;
1441 
1442                         if (infoPtr->MonitorsCount != 0)
1443                         {
1444                             if (Index < 0)
1445                                 Index = 0;
1446                             else if (Index > 0)
1447                                 Index--;
1448                         }
1449 
1450                         if (Index >= 0)
1451                         {
1452                             MonSelSetCurSelMonitor(infoPtr,
1453                                                    Index,
1454                                                    TRUE);
1455                         }
1456                         break;
1457                     }
1458 
1459                     case VK_DOWN:
1460                     case VK_RIGHT:
1461                     {
1462                         Index = infoPtr->SelectedMonitor;
1463 
1464                         if (infoPtr->MonitorsCount != 0)
1465                         {
1466                             if (Index < 0)
1467                                 Index = (INT)infoPtr->MonitorsCount - 1;
1468                             else if (Index < (INT)infoPtr->MonitorsCount - 1)
1469                                 Index++;
1470                         }
1471 
1472                         if (infoPtr->SelectedMonitor < (INT)infoPtr->MonitorsCount)
1473                         {
1474                             MonSelSetCurSelMonitor(infoPtr,
1475                                                    Index,
1476                                                    TRUE);
1477                         }
1478                         break;
1479                     }
1480                 }
1481             }
1482             break;
1483         }
1484 
1485         case WM_CHAR:
1486         {
1487             if ((infoPtr->ControlExStyle & MSLM_EX_SELECTBYNUMKEY) &&
1488                 wParam >= '1' && wParam <= '9')
1489             {
1490                 INT Index = (INT)(wParam - '1');
1491                 if (Index < (INT)infoPtr->MonitorsCount)
1492                 {
1493                     MonSelSetCurSelMonitor(infoPtr,
1494                                            Index,
1495                                            TRUE);
1496                 }
1497             }
1498             break;
1499         }
1500 
1501         case MSLM_SETMONITORSINFO:
1502         {
1503             Ret = MonSelSetMonitorsInfo(infoPtr,
1504                                         (DWORD)wParam,
1505                                         (const MONSL_MONINFO *)lParam);
1506             break;
1507         }
1508 
1509         case MSLM_GETMONITORSINFO:
1510         {
1511             Ret = MonSelGetMonitorsInfo(infoPtr,
1512                                         (DWORD)wParam,
1513                                         (PMONSL_MONINFO)lParam);
1514             break;
1515         }
1516 
1517         case MSLM_GETMONITORINFOCOUNT:
1518         {
1519             Ret = infoPtr->MonitorsCount;
1520             break;
1521         }
1522 
1523         case MSLM_HITTEST:
1524         {
1525             Ret = MonSelHitTest(infoPtr,
1526                                 (const POINT *)wParam);
1527             break;
1528         }
1529 
1530         case MSLM_SETCURSEL:
1531         {
1532             Ret = MonSelSetCurSelMonitor(infoPtr,
1533                                          (INT)wParam,
1534                                          FALSE);
1535             break;
1536         }
1537 
1538         case MSLM_GETCURSEL:
1539         {
1540             Ret = infoPtr->SelectedMonitor;
1541             break;
1542         }
1543 
1544         case MSLM_SETMONITORINFO:
1545         {
1546             Ret = MonSelSetMonitorInfo(infoPtr,
1547                                        (INT)wParam,
1548                                        (const MONSL_MONINFO *)lParam);
1549             break;
1550         }
1551 
1552         case MSLM_GETMONITORINFO:
1553         {
1554             Ret = MonSelGetMonitorInfo(infoPtr,
1555                                        (INT)wParam,
1556                                        (PMONSL_MONINFO)lParam);
1557             break;
1558         }
1559 
1560         case MSLM_SETEXSTYLE:
1561         {
1562             Ret = MonSelSetExtendedStyle(infoPtr,
1563                                          (DWORD)lParam);
1564             break;
1565         }
1566 
1567         case MSLM_GETEXSTYLE:
1568         {
1569             Ret = MonSelGetExtendedStyle(infoPtr);
1570             break;
1571         }
1572 
1573         case MSLM_GETMONITORRECT:
1574         {
1575             Ret = (LRESULT)MonSelGetMonitorRect(infoPtr,
1576                                                 (INT)wParam,
1577                                                 (PRECT)lParam);
1578             break;
1579         }
1580 
1581         case WM_CREATE:
1582         {
1583             infoPtr = (PMONITORSELWND)HeapAlloc(GetProcessHeap(),
1584                                                 0,
1585                                                 sizeof(MONITORSELWND));
1586             if (infoPtr == NULL)
1587             {
1588                 Ret = (LRESULT)-1;
1589                 break;
1590             }
1591 
1592             ZeroMemory(infoPtr,
1593                        sizeof(MONITORSELWND));
1594             infoPtr->hSelf = hwnd;
1595             infoPtr->hNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1596             infoPtr->Enabled = !(((LPCREATESTRUCTW)lParam)->style & WS_DISABLED);
1597             infoPtr->UIState = SendMessage(hwnd,
1598                                            WM_QUERYUISTATE,
1599                                            0,
1600                                            0);
1601 
1602             SetWindowLongPtrW(hwnd,
1603                               0,
1604                               (LONG_PTR)infoPtr);
1605 
1606             MonSelCreate(infoPtr);
1607             break;
1608         }
1609 
1610         case WM_DESTROY:
1611         {
1612             MonSelDestroy(infoPtr);
1613 
1614             HeapFree(GetProcessHeap(),
1615                      0,
1616                      infoPtr);
1617             SetWindowLongPtrW(hwnd,
1618                               0,
1619                               (DWORD_PTR)NULL);
1620             break;
1621         }
1622 
1623         default:
1624         {
1625 HandleDefaultMessage:
1626             Ret = DefWindowProcW(hwnd,
1627                                  uMsg,
1628                                  wParam,
1629                                  lParam);
1630             break;
1631         }
1632     }
1633 
1634     return Ret;
1635 }
1636 
1637 BOOL
1638 RegisterMonitorSelectionControl(IN HINSTANCE hInstance)
1639 {
1640     WNDCLASS wc = {0};
1641 
1642     wc.style = CS_DBLCLKS;
1643     wc.lpfnWndProc = MonitorSelWndProc;
1644     wc.cbWndExtra = sizeof(PMONITORSELWND);
1645     wc.hInstance = hInstance;
1646     wc.hCursor = LoadCursorW(NULL,
1647                              (LPWSTR)IDC_ARROW);
1648     wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
1649     wc.lpszClassName = szMonitorSelWndClass;
1650 
1651     return RegisterClass(&wc) != 0;
1652 }
1653 
1654 VOID
1655 UnregisterMonitorSelectionControl(IN HINSTANCE hInstance)
1656 {
1657     UnregisterClassW(szMonitorSelWndClass,
1658                      hInstance);
1659 }
1660