Display a context menu for the selected monitor
[reactos.git] / reactos / dll / cpl / desk / monslctl.c
1 #include <windows.h>
2 #include <tchar.h>
3 #include <stdio.h>
4 #include "monslctl.h"
5
6 static const TCHAR szMonitorSelWndClass[] = TEXT("MONITORSELWNDCLASS");
7
8 typedef struct _MONSL_MON
9 {
10 RECT rc;
11 HFONT hFont;
12 TCHAR szCaption[12];
13 } MONSL_MON, *PMONSL_MON;
14
15 typedef struct _MONITORSELWND
16 {
17 HWND hSelf;
18 HWND hNotify;
19 HFONT hFont;
20 SIZE ClientSize;
21 DWORD UIState;
22 union
23 {
24 DWORD dwInternalFlags;
25 struct
26 {
27 UINT Enabled : 1;
28 UINT HasFocus : 1;
29 UINT CanDisplay : 1;
30 };
31 };
32 DWORD ControlExStyle;
33 DWORD MonitorsCount;
34 INT SelectedMonitor;
35 PMONSL_MONINFO MonitorInfo;
36 PMONSL_MON Monitors;
37 RECT rcExtent;
38 RECT rcMonitors;
39 POINT ScrollPos;
40 SIZE Margin;
41 SIZE SelectionFrame;
42 HBITMAP hbmDisabledPattern;
43 HBRUSH hbrDisabled;
44 } MONITORSELWND, *PMONITORSELWND;
45
46 static LRESULT
47 MonSelNotify(IN PMONITORSELWND infoPtr,
48 IN UINT code,
49 IN OUT PVOID data)
50 {
51 LRESULT Ret = 0;
52
53 if (infoPtr->hNotify != NULL)
54 {
55 LPNMHDR pnmh = (LPNMHDR)data;
56
57 pnmh->hwndFrom = infoPtr->hSelf;
58 pnmh->idFrom = GetWindowLongPtr(infoPtr->hSelf,
59 GWLP_ID);
60 pnmh->code = code;
61
62 Ret = SendMessage(infoPtr->hNotify,
63 WM_NOTIFY,
64 (WPARAM)pnmh->idFrom,
65 (LPARAM)pnmh);
66 }
67
68 return Ret;
69 }
70
71 static LRESULT
72 MonSelNotifyMonitor(IN PMONITORSELWND infoPtr,
73 IN UINT code,
74 IN INT Index,
75 IN OUT PMONSL_MONNMHDR pmonnmh)
76 {
77 pmonnmh->Index = Index;
78
79 if (Index >= 0)
80 {
81 pmonnmh->MonitorInfo = infoPtr->MonitorInfo[Index];
82 }
83 else
84 {
85 ZeroMemory(&pmonnmh->MonitorInfo,
86 sizeof(pmonnmh->MonitorInfo));
87 }
88
89 return MonSelNotify(infoPtr,
90 code,
91 pmonnmh);
92 }
93
94 static HFONT
95 MonSelChangeFont(IN OUT PMONITORSELWND infoPtr,
96 IN HFONT hFont,
97 IN BOOL Redraw)
98 {
99 HFONT hOldFont = infoPtr->hFont;
100 infoPtr->hFont = hFont;
101
102 if (Redraw)
103 {
104 InvalidateRect(infoPtr->hSelf,
105 NULL,
106 TRUE);
107 }
108
109 return hOldFont;
110 }
111
112 static VOID
113 MonSelRectToScreen(IN PMONITORSELWND infoPtr,
114 IN const RECT *prc,
115 OUT PRECT prcOnScreen)
116 {
117 *prcOnScreen = *prc;
118 OffsetRect(prcOnScreen,
119 -infoPtr->ScrollPos.x,
120 -infoPtr->ScrollPos.y);
121 }
122
123 static VOID
124 MonSelScreenToPt(IN PMONITORSELWND infoPtr,
125 IN const POINT *pptOnScreen,
126 OUT PPOINT ppt)
127 {
128 ppt->x = pptOnScreen->x + infoPtr->ScrollPos.x;
129 ppt->y = pptOnScreen->y + infoPtr->ScrollPos.y;
130 }
131
132 static VOID
133 MonSelMonInfoToRect(IN const MONSL_MONINFO *pMonInfo,
134 OUT PRECT prc)
135 {
136 prc->left = pMonInfo->Position.x;
137 prc->top = pMonInfo->Position.y;
138 prc->right = pMonInfo->Position.x + pMonInfo->Size.cx;
139 prc->bottom = pMonInfo->Position.y + pMonInfo->Size.cy;
140 }
141
142 static INT
143 MonSelHitTest(IN PMONITORSELWND infoPtr,
144 IN const POINT *ppt)
145 {
146 POINT pt;
147 INT Index, Ret = -1;
148
149 if (infoPtr->CanDisplay)
150 {
151 MonSelScreenToPt(infoPtr,
152 ppt,
153 &pt);
154
155 for (Index = 0; Index < (INT)infoPtr->MonitorsCount; Index++)
156 {
157 if (PtInRect(&infoPtr->Monitors[Index].rc,
158 pt))
159 {
160 Ret = Index;
161 break;
162 }
163 }
164 }
165
166 return Ret;
167 }
168
169 static VOID
170 MonSelUpdateExtent(IN OUT PMONITORSELWND infoPtr)
171 {
172 DWORD Index;
173 RECT rcMonitor;
174
175 /* NOTE: This routine calculates the extent of all monitor coordinates.
176 These are not control coordinates! */
177 if (infoPtr->MonitorsCount > 0)
178 {
179 MonSelMonInfoToRect(&infoPtr->MonitorInfo[0],
180 &infoPtr->rcExtent);
181
182 for (Index = 1; Index < infoPtr->MonitorsCount; Index++)
183 {
184 MonSelMonInfoToRect(&infoPtr->MonitorInfo[Index],
185 &rcMonitor);
186
187 UnionRect(&infoPtr->rcExtent,
188 &infoPtr->rcExtent,
189 &rcMonitor);
190 }
191 }
192 else
193 {
194 ZeroMemory(&infoPtr->rcExtent,
195 sizeof(infoPtr->rcExtent));
196 }
197 }
198
199 static VOID
200 MonSelScaleRectRelative(IN const RECT *prcBaseFrom,
201 IN const RECT *prcFrom,
202 IN const RECT *prcBaseTo,
203 OUT PRECT prcTo)
204 {
205 SIZE BaseFrom, BaseTo, From;
206
207 BaseFrom.cx = prcBaseFrom->right - prcBaseFrom->left;
208 BaseFrom.cy = prcBaseFrom->bottom - prcBaseFrom->top;
209 BaseTo.cx = prcBaseTo->right - prcBaseTo->left;
210 BaseTo.cy = prcBaseTo->bottom - prcBaseTo->top;
211 From.cx = prcFrom->right - prcFrom->left;
212 From.cy = prcFrom->bottom - prcFrom->top;
213
214 prcTo->left = prcBaseTo->left + (((prcFrom->left - prcBaseFrom->left) * BaseTo.cx) / BaseFrom.cx);
215 prcTo->top = prcBaseTo->top + (((prcFrom->top - prcBaseFrom->top) * BaseTo.cy) / BaseFrom.cy);
216 prcTo->right = prcTo->left + ((From.cx * BaseTo.cx) / BaseFrom.cx);
217 prcTo->bottom = prcTo->top + ((From.cy * BaseTo.cy) / BaseFrom.cy);
218 }
219
220 static VOID
221 ScaleRectSizeFit(IN const RECT *prcContainerRect,
222 IN OUT PRECT prcRectToScale)
223 {
224 SIZE ContainerSize, RectSize;
225
226 ContainerSize.cx = prcContainerRect->right - prcContainerRect->left;
227 ContainerSize.cy = prcContainerRect->bottom - prcContainerRect->top;
228 RectSize.cx = prcRectToScale->right - prcRectToScale->left;
229 RectSize.cy = prcRectToScale->bottom - prcRectToScale->top;
230
231 if (((RectSize.cx * 0xFFF) / RectSize.cy) < ((ContainerSize.cx * 0xFFF) / ContainerSize.cy))
232 {
233 RectSize.cx = (RectSize.cx * ((ContainerSize.cy * 0xFFF) / RectSize.cy)) / 0xFFF;
234 RectSize.cy = ContainerSize.cy;
235 }
236 else
237 {
238 RectSize.cy = (RectSize.cy * ((ContainerSize.cx * 0xFFF) / RectSize.cx)) / 0xFFF;
239 RectSize.cx = ContainerSize.cx;
240 }
241
242 prcRectToScale->right = prcRectToScale->left + RectSize.cx;
243 prcRectToScale->bottom = prcRectToScale->top + RectSize.cy;
244
245 OffsetRect(prcRectToScale,
246 prcContainerRect->left + ((ContainerSize.cx - RectSize.cx) / 2),
247 prcContainerRect->top + ((ContainerSize.cy - RectSize.cy) / 2));
248 }
249
250 static VOID
251 MonSelRepaint(IN PMONITORSELWND infoPtr)
252 {
253 RECT rc;
254
255 MonSelRectToScreen(infoPtr,
256 &infoPtr->rcMonitors,
257 &rc);
258 InvalidateRect(infoPtr->hSelf,
259 &rc,
260 TRUE);
261 }
262
263 static VOID
264 MonSelRepaintMonitor(IN PMONITORSELWND infoPtr,
265 IN DWORD Index)
266 {
267 RECT rc;
268
269 if (Index < infoPtr->MonitorsCount)
270 {
271 MonSelRectToScreen(infoPtr,
272 &infoPtr->Monitors[Index].rc,
273 &rc);
274 InvalidateRect(infoPtr->hSelf,
275 &rc,
276 TRUE);
277 }
278 }
279
280 static VOID
281 MonSelRepaintSelected(IN PMONITORSELWND infoPtr)
282 {
283 if (infoPtr->SelectedMonitor >= 0)
284 {
285 MonSelRepaintMonitor(infoPtr,
286 (DWORD)infoPtr->SelectedMonitor);
287 }
288 }
289
290 static VOID
291 MonSelResetMonitors(IN OUT PMONITORSELWND infoPtr)
292 {
293 DWORD Index;
294
295 for (Index = 0; Index < infoPtr->MonitorsCount; Index++)
296 {
297 if (infoPtr->Monitors[Index].hFont != NULL)
298 {
299 DeleteObject(infoPtr->Monitors[Index].hFont);
300 infoPtr->Monitors[Index].hFont = NULL;
301 }
302 }
303 }
304
305
306 static VOID
307 MonSelUpdateMonitorsInfo(IN OUT PMONITORSELWND infoPtr,
308 IN BOOL bRepaint)
309 {
310 RECT rcExtSurface, rcExtDisplay;
311 DWORD Index;
312
313 /* Recalculate rcExtent */
314 MonSelUpdateExtent(infoPtr);
315
316 infoPtr-> CanDisplay = infoPtr->MonitorsCount != 0 &&
317 (infoPtr->ClientSize.cx > (2 * (infoPtr->Margin.cx + infoPtr->SelectionFrame.cx))) &&
318 (infoPtr->ClientSize.cy > (2 * (infoPtr->Margin.cy + infoPtr->SelectionFrame.cy)));
319
320 if (infoPtr->CanDisplay)
321 {
322 /* Calculate the rectangle on the control in which may be painted */
323 rcExtSurface.left = infoPtr->Margin.cx;
324 rcExtSurface.top = infoPtr->Margin.cy;
325 rcExtSurface.right = rcExtSurface.left + infoPtr->ClientSize.cx - (2 * infoPtr->Margin.cx);
326 rcExtSurface.bottom = rcExtSurface.top + infoPtr->ClientSize.cy - (2 * infoPtr->Margin.cy);
327
328 /* Calculate the rectangle on the control that is actually painted on */
329 rcExtDisplay.left = rcExtDisplay.top = 0;
330 rcExtDisplay.right = infoPtr->rcExtent.right - infoPtr->rcExtent.left;
331 rcExtDisplay.bottom = infoPtr->rcExtent.bottom - infoPtr->rcExtent.top;
332
333 ScaleRectSizeFit(&rcExtSurface,
334 &rcExtDisplay);
335
336 infoPtr->rcMonitors = rcExtDisplay;
337
338 /* Now that we know in which area all monitors are located,
339 calculate the monitors selection rectangles on the screen */
340
341 for (Index = 0; Index < infoPtr->MonitorsCount; Index++)
342 {
343 MonSelMonInfoToRect(&infoPtr->MonitorInfo[Index],
344 &rcExtDisplay);
345
346 MonSelScaleRectRelative(&infoPtr->rcExtent,
347 &rcExtDisplay,
348 &infoPtr->rcMonitors,
349 &infoPtr->Monitors[Index].rc);
350 }
351
352 MonSelResetMonitors(infoPtr);
353
354 if (bRepaint)
355 MonSelRepaint(infoPtr);
356 }
357 else if (bRepaint)
358 {
359 InvalidateRect(infoPtr->hSelf,
360 NULL,
361 TRUE);
362 }
363 }
364
365 static BOOL
366 MonSelSetMonitorsInfo(IN OUT PMONITORSELWND infoPtr,
367 IN DWORD dwMonitors,
368 IN const MONSL_MONINFO *MonitorsInfo)
369 {
370 DWORD Index;
371 BOOL Ret = TRUE;
372
373 if (infoPtr->MonitorInfo != NULL)
374 {
375 LocalFree((HLOCAL)infoPtr->MonitorInfo);
376 infoPtr->MonitorInfo = NULL;
377
378 MonSelResetMonitors(infoPtr);
379
380 LocalFree((HLOCAL)infoPtr->Monitors);
381 infoPtr->Monitors = NULL;
382
383 infoPtr->MonitorsCount = 0;
384 }
385
386 if (dwMonitors != 0)
387 {
388 infoPtr->MonitorInfo = (PMONSL_MONINFO)LocalAlloc(LMEM_FIXED,
389 dwMonitors * sizeof(MONSL_MONINFO));
390 if (infoPtr->MonitorInfo != NULL)
391 {
392 infoPtr->Monitors = (PMONSL_MON)LocalAlloc(LMEM_FIXED,
393 dwMonitors * sizeof(MONSL_MON));
394 if (infoPtr->Monitors != NULL)
395 {
396 CopyMemory(infoPtr->MonitorInfo,
397 MonitorsInfo,
398 dwMonitors * sizeof(MONSL_MONINFO));
399 ZeroMemory(infoPtr->Monitors,
400 dwMonitors * sizeof(MONSL_MON));
401
402 for (Index = 0; Index < dwMonitors; Index++)
403 {
404 _stprintf(infoPtr->Monitors[Index].szCaption,
405 _T("%u"),
406 Index + 1);
407 }
408
409 infoPtr->MonitorsCount = dwMonitors;
410
411 if (infoPtr->SelectedMonitor >= (INT)infoPtr->MonitorsCount)
412 infoPtr->SelectedMonitor = -1;
413
414 if (!(infoPtr->ControlExStyle & MSLM_EX_ALLOWSELECTNONE) && infoPtr->SelectedMonitor < 0)
415 infoPtr->SelectedMonitor = 0;
416
417 MonSelUpdateMonitorsInfo(infoPtr,
418 TRUE);
419 }
420 else
421 {
422 LocalFree((HLOCAL)infoPtr->MonitorInfo);
423 infoPtr->MonitorInfo = NULL;
424
425 Ret = FALSE;
426 }
427 }
428 else
429 Ret = FALSE;
430 }
431
432 if (!Ret)
433 infoPtr->SelectedMonitor = -1;
434
435 if (!Ret || dwMonitors == 0)
436 {
437 InvalidateRect(infoPtr->hSelf,
438 NULL,
439 TRUE);
440 }
441
442 return Ret;
443 }
444
445 static DWORD
446 MonSelGetMonitorsInfo(IN PMONITORSELWND infoPtr,
447 IN DWORD dwMonitors,
448 IN OUT PMONSL_MONINFO MonitorsInfo)
449 {
450 if (dwMonitors != 0)
451 {
452 if (dwMonitors > infoPtr->MonitorsCount)
453 dwMonitors = infoPtr->MonitorsCount;
454
455 CopyMemory(MonitorsInfo,
456 infoPtr->MonitorInfo,
457 dwMonitors * sizeof(MONSL_MONINFO));
458 return dwMonitors;
459 }
460 else
461 return infoPtr->MonitorsCount;
462 }
463
464 static BOOL
465 MonSelSetMonitorInfo(IN OUT PMONITORSELWND infoPtr,
466 IN INT Index,
467 IN const MONSL_MONINFO *MonitorsInfo)
468 {
469 if (Index >= 0 && Index < (INT)infoPtr->MonitorsCount)
470 {
471 CopyMemory(&infoPtr->MonitorInfo[Index],
472 MonitorsInfo,
473 sizeof(MONSL_MONINFO));
474
475 MonSelUpdateMonitorsInfo(infoPtr,
476 TRUE);
477 return TRUE;
478 }
479
480 return FALSE;
481 }
482
483 static BOOL
484 MonSelGetMonitorInfo(IN PMONITORSELWND infoPtr,
485 IN INT Index,
486 IN OUT PMONSL_MONINFO MonitorsInfo)
487 {
488 if (Index >= 0 && Index < (INT)infoPtr->MonitorsCount)
489 {
490 CopyMemory(MonitorsInfo,
491 &infoPtr->MonitorInfo[Index],
492 sizeof(MONSL_MONINFO));
493 return TRUE;
494 }
495
496 return FALSE;
497 }
498
499 static INT
500 MonSelGetMonitorRect(IN OUT PMONITORSELWND infoPtr,
501 IN INT Index,
502 OUT PRECT prc)
503 {
504 RECT rc, rcClient;
505
506 if (Index < 0 || Index >= infoPtr->MonitorsCount)
507 return -1;
508
509 if (!infoPtr->CanDisplay)
510 return 0;
511
512 MonSelRectToScreen(infoPtr,
513 &infoPtr->Monitors[Index].rc,
514 prc);
515
516 rcClient.left = rcClient.top = 0;
517 rcClient.right = infoPtr->ClientSize.cx;
518 rcClient.bottom = infoPtr->ClientSize.cy;
519
520 return IntersectRect(&rc,
521 &rcClient,
522 prc) != FALSE;
523 }
524
525 static BOOL
526 MonSelSetCurSelMonitor(IN OUT PMONITORSELWND infoPtr,
527 IN INT Index,
528 IN BOOL bNotify)
529 {
530 INT PrevSel;
531 BOOL PreventSelect = FALSE;
532 BOOL Ret = FALSE;
533
534 if (Index == -1 || Index < (INT)infoPtr->MonitorsCount)
535 {
536 if (Index != infoPtr->SelectedMonitor)
537 {
538 if ((infoPtr->MonitorInfo[Index].Flags & MSL_MIF_DISABLED) &&
539 !(infoPtr->ControlExStyle & MSLM_EX_ALLOWSELECTDISABLED))
540 {
541 PreventSelect = TRUE;
542 }
543
544 if (!PreventSelect && bNotify)
545 {
546 MONSL_MONNMMONITORCHANGING nmi;
547
548 nmi.PreviousSelected = infoPtr->SelectedMonitor;
549 nmi.AllowChanging = TRUE;
550
551 MonSelNotifyMonitor(infoPtr,
552 MSLN_MONITORCHANGING,
553 Index,
554 &nmi.hdr);
555
556 PreventSelect = (nmi.AllowChanging == FALSE);
557 }
558
559 if (!PreventSelect)
560 {
561 PrevSel = infoPtr->SelectedMonitor;
562 infoPtr->SelectedMonitor = Index;
563
564 if (PrevSel >= 0)
565 {
566 MonSelRepaintMonitor(infoPtr,
567 PrevSel);
568 }
569
570 if (infoPtr->SelectedMonitor >= 0)
571 MonSelRepaintSelected(infoPtr);
572
573 if (bNotify)
574 {
575 MONSL_MONNMHDR nm;
576
577 MonSelNotifyMonitor(infoPtr,
578 MSLN_MONITORCHANGED,
579 Index,
580 &nm);
581 }
582 }
583 }
584
585 Ret = TRUE;
586 }
587
588 return Ret;
589 }
590
591 static VOID
592 MonSelCreate(IN OUT PMONITORSELWND infoPtr)
593 {
594 infoPtr->SelectionFrame.cx = infoPtr->SelectionFrame.cy = 4;
595 infoPtr->Margin.cx = infoPtr->Margin.cy = 20;
596 infoPtr->SelectedMonitor = -1;
597 infoPtr->ControlExStyle = MSLM_EX_ALLOWSELECTDISABLED | MSLM_EX_HIDENUMBERONSINGLE |
598 MSLM_EX_SELECTONRIGHTCLICK;
599 return;
600 }
601
602 static VOID
603 MonSelDestroy(IN OUT PMONITORSELWND infoPtr)
604 {
605 /* Free all monitors */
606 MonSelSetMonitorsInfo(infoPtr,
607 0,
608 NULL);
609
610 if (infoPtr->hbrDisabled != NULL)
611 {
612 DeleteObject(infoPtr->hbrDisabled);
613 infoPtr->hbrDisabled = NULL;
614 }
615
616 if (infoPtr->hbmDisabledPattern != NULL)
617 {
618 DeleteObject(infoPtr->hbmDisabledPattern);
619 infoPtr->hbmDisabledPattern = NULL;
620 }
621 }
622
623 static BOOL
624 MonSelSetExtendedStyle(IN OUT PMONITORSELWND infoPtr,
625 IN DWORD dwExtendedStyle)
626 {
627 if (dwExtendedStyle != infoPtr->ControlExStyle)
628 {
629 infoPtr->ControlExStyle = dwExtendedStyle;
630
631 /* Repaint the control */
632 InvalidateRect(infoPtr->hSelf,
633 NULL,
634 TRUE);
635 }
636
637 return TRUE;
638 }
639
640 static DWORD
641 MonSelGetExtendedStyle(IN PMONITORSELWND infoPtr)
642 {
643 return infoPtr->ControlExStyle;
644 }
645
646 static HFONT
647 MonSelGetMonitorFont(IN OUT PMONITORSELWND infoPtr,
648 IN HDC hDC,
649 IN INT Index)
650 {
651 TEXTMETRIC tm;
652 SIZE rcsize;
653 LOGFONT lf;
654 HFONT hPrevFont, hFont;
655 INT len;
656
657 hFont = infoPtr->Monitors[Index].hFont;
658 if (hFont == NULL &&
659 GetObject(infoPtr->hFont,
660 sizeof(LOGFONT),
661 &lf) != 0)
662 {
663 rcsize.cx = infoPtr->Monitors[Index].rc.right - infoPtr->Monitors[Index].rc.left -
664 (2 * infoPtr->SelectionFrame.cx) - 2;
665 rcsize.cy = infoPtr->Monitors[Index].rc.bottom - infoPtr->Monitors[Index].rc.top -
666 (2 * infoPtr->SelectionFrame.cy) - 2;
667 rcsize.cy = (rcsize.cy * 60) / 100;
668
669 len = _tcslen(infoPtr->Monitors[Index].szCaption);
670
671 hPrevFont = SelectObject(hDC,
672 infoPtr->hFont);
673
674 if (GetTextMetrics(hDC,
675 &tm))
676 {
677 lf.lfWeight = FW_SEMIBOLD;
678 lf.lfHeight = -MulDiv(rcsize.cy - tm.tmExternalLeading,
679 GetDeviceCaps(hDC,
680 LOGPIXELSY),
681 72);
682
683 hFont = CreateFontIndirect(&lf);
684 if (hFont != NULL)
685 infoPtr->Monitors[Index].hFont = hFont;
686 }
687
688 SelectObject(hDC,
689 hPrevFont);
690 }
691
692 return hFont;
693 }
694
695 static BOOL
696 MonSelDrawDisabledRect(IN OUT PMONITORSELWND infoPtr,
697 IN HDC hDC,
698 IN const RECT *prc)
699 {
700 BOOL Ret = FALSE;
701
702 if (infoPtr->hbrDisabled == NULL)
703 {
704 static const DWORD Pattern[4] = {0x5555AAAA, 0x5555AAAA, 0x5555AAAA, 0x5555AAAA};
705
706 if (infoPtr->hbmDisabledPattern == NULL)
707 {
708 infoPtr->hbmDisabledPattern = CreateBitmap(8,
709 8,
710 1,
711 1,
712 Pattern);
713 }
714
715 if (infoPtr->hbmDisabledPattern != NULL)
716 infoPtr->hbrDisabled = CreatePatternBrush(infoPtr->hbmDisabledPattern);
717 }
718
719 if (infoPtr->hbrDisabled != NULL)
720 {
721 /* FIXME - implement */
722 }
723
724 return Ret;
725 }
726
727 static VOID
728 MonSelPaint(IN OUT PMONITORSELWND infoPtr,
729 IN HDC hDC,
730 IN const RECT *prcUpdate)
731 {
732 COLORREF crPrevText, crPrevText2;
733 HFONT hFont, hPrevFont;
734 HBRUSH hbBk, hbOldBk;
735 HPEN hpFg, hpOldFg;
736 DWORD Index;
737 RECT rc, rctmp;
738 INT iPrevBkMode;
739 BOOL bHideNumber;
740
741 bHideNumber = (infoPtr->ControlExStyle & MSLM_EX_HIDENUMBERS) ||
742 ((infoPtr->MonitorsCount == 1) && (infoPtr->ControlExStyle & MSLM_EX_HIDENUMBERONSINGLE));
743
744 hbBk = GetSysColorBrush(COLOR_BACKGROUND);
745 hpFg = CreatePen(PS_SOLID,
746 0,
747 GetSysColor(COLOR_HIGHLIGHTTEXT));
748
749 hbOldBk = SelectObject(hDC,
750 hbBk);
751 hpOldFg = SelectObject(hDC,
752 hpFg);
753 iPrevBkMode = SetBkMode(hDC,
754 TRANSPARENT);
755 crPrevText = SetTextColor(hDC,
756 GetSysColor(COLOR_HIGHLIGHTTEXT));
757
758 for (Index = 0; Index < infoPtr->MonitorsCount; Index++)
759 {
760 MonSelRectToScreen(infoPtr,
761 &infoPtr->Monitors[Index].rc,
762 &rc);
763
764 if (!IntersectRect(&rctmp,
765 &rc,
766 prcUpdate))
767 {
768 continue;
769 }
770
771 if ((INT)Index == infoPtr->SelectedMonitor)
772 {
773 FillRect(hDC,
774 &rc,
775 (HBRUSH)(COLOR_HIGHLIGHT + 1));
776
777 if (infoPtr->HasFocus && !(infoPtr->UIState & UISF_HIDEFOCUS))
778 {
779 /* NOTE: We need to switch the text color to the default, because
780 DrawFocusRect draws a solid line if the text is white! */
781
782 crPrevText2 = SetTextColor(hDC,
783 crPrevText);
784
785 DrawFocusRect(hDC,
786 &rc);
787
788 SetTextColor(hDC,
789 crPrevText2);
790 }
791 }
792
793 InflateRect(&rc,
794 -infoPtr->SelectionFrame.cx,
795 -infoPtr->SelectionFrame.cy);
796
797 Rectangle(hDC,
798 rc.left,
799 rc.top,
800 rc.right,
801 rc.bottom);
802
803 InflateRect(&rc,
804 -1,
805 -1);
806
807 if (!bHideNumber)
808 {
809 hFont = MonSelGetMonitorFont(infoPtr,
810 hDC,
811 Index);
812 if (hFont != NULL)
813 {
814 hPrevFont = SelectObject(hDC,
815 hFont);
816
817 DrawText(hDC,
818 infoPtr->Monitors[Index].szCaption,
819 -1,
820 &rc,
821 DT_VCENTER | DT_CENTER | DT_NOPREFIX | DT_SINGLELINE);
822
823 SelectObject(hDC,
824 hPrevFont);
825 }
826 }
827
828 if (infoPtr->MonitorInfo[Index].Flags & MSL_MIF_DISABLED)
829 {
830 InflateRect(&rc,
831 1,
832 1);
833
834 MonSelDrawDisabledRect(infoPtr,
835 hDC,
836 &rc);
837 }
838 }
839
840 SetTextColor(hDC,
841 crPrevText);
842 SetBkMode(hDC,
843 iPrevBkMode);
844 SelectObject(hDC,
845 hpOldFg);
846 SelectObject(hDC,
847 hbOldBk);
848 }
849
850 static VOID
851 MonSelContextMenu(IN OUT PMONITORSELWND infoPtr,
852 IN SHORT x,
853 IN SHORT y)
854 {
855 MONSL_MONNMBUTTONCLICKED nm;
856 INT Index;
857
858 if (!infoPtr->HasFocus)
859 SetFocus(infoPtr->hSelf);
860
861 nm.pt.x = x;
862 nm.pt.y = y;
863
864 Index = MonSelHitTest(infoPtr,
865 &nm.pt);
866
867 MonSelNotifyMonitor(infoPtr,
868 MSLN_RBUTTONUP,
869 Index,
870 (PMONSL_MONNMHDR)&nm);
871
872 /* Send a WM_CONTEXTMENU notification */
873 MapWindowPoints(infoPtr->hSelf,
874 NULL,
875 &nm.pt,
876 1);
877
878 SendMessage(infoPtr->hSelf,
879 WM_CONTEXTMENU,
880 (WPARAM)infoPtr->hSelf,
881 MAKELPARAM(nm.pt.x,
882 nm.pt.y));
883 }
884
885 static LRESULT CALLBACK
886 MonitorSelWndProc(IN HWND hwnd,
887 IN UINT uMsg,
888 IN WPARAM wParam,
889 IN LPARAM lParam)
890 {
891 PMONITORSELWND infoPtr;
892 LRESULT Ret = 0;
893
894 infoPtr = (PMONITORSELWND)GetWindowLongPtrW(hwnd,
895 0);
896
897 if (infoPtr == NULL && uMsg != WM_CREATE)
898 {
899 goto HandleDefaultMessage;
900 }
901
902 switch (uMsg)
903 {
904 case WM_PAINT:
905 case WM_PRINTCLIENT:
906 {
907 PAINTSTRUCT ps;
908 HDC hDC;
909
910 if (wParam != 0)
911 {
912 if (!GetUpdateRect(hwnd,
913 &ps.rcPaint,
914 TRUE))
915 {
916 break;
917 }
918 hDC = (HDC)wParam;
919 }
920 else
921 {
922 hDC = BeginPaint(hwnd,
923 &ps);
924 if (hDC == NULL)
925 {
926 break;
927 }
928 }
929
930 if (infoPtr->CanDisplay)
931 {
932 MonSelPaint(infoPtr,
933 hDC,
934 &ps.rcPaint);
935 }
936
937 if (wParam == 0)
938 {
939 EndPaint(hwnd,
940 &ps);
941 }
942 break;
943 }
944
945 case WM_RBUTTONDOWN:
946 {
947 if (!(infoPtr->ControlExStyle & MSLM_EX_SELECTONRIGHTCLICK))
948 break;
949
950 /* fall through */
951 }
952
953 case WM_LBUTTONDBLCLK:
954 case WM_LBUTTONDOWN:
955 {
956 INT Index;
957 POINT pt;
958
959 pt.x = (LONG)LOWORD(lParam);
960 pt.y = (LONG)HIWORD(lParam);
961
962 Index = MonSelHitTest(infoPtr,
963 &pt);
964 if (Index >= 0 || (infoPtr->ControlExStyle & MSLM_EX_ALLOWSELECTNONE))
965 {
966 MonSelSetCurSelMonitor(infoPtr,
967 Index,
968 TRUE);
969 }
970
971 /* fall through */
972 }
973
974 case WM_MBUTTONDOWN:
975 {
976 if (!infoPtr->HasFocus)
977 SetFocus(hwnd);
978 break;
979 }
980
981 case WM_RBUTTONUP:
982 {
983 MonSelContextMenu(infoPtr,
984 (SHORT)LOWORD(lParam),
985 (SHORT)HIWORD(lParam));
986 break;
987 }
988
989 case WM_GETDLGCODE:
990 {
991 INT virtKey;
992
993 virtKey = (lParam != 0 ? (INT)((LPMSG)lParam)->wParam : 0);
994 switch (virtKey)
995 {
996 case VK_TAB:
997 {
998 /* change the UI status */
999 SendMessage(GetAncestor(hwnd,
1000 GA_PARENT),
1001 WM_CHANGEUISTATE,
1002 MAKEWPARAM(UIS_INITIALIZE,
1003 0),
1004 0);
1005 break;
1006 }
1007 }
1008
1009 Ret |= DLGC_WANTARROWS;
1010 break;
1011 }
1012
1013 case WM_SETFOCUS:
1014 {
1015 infoPtr->HasFocus = TRUE;
1016 MonSelRepaintSelected(infoPtr);
1017 break;
1018 }
1019
1020 case WM_KILLFOCUS:
1021 {
1022 infoPtr->HasFocus = FALSE;
1023 MonSelRepaintSelected(infoPtr);
1024 break;
1025 }
1026
1027 case WM_UPDATEUISTATE:
1028 {
1029 DWORD OldUIState = infoPtr->UIState;
1030 switch (LOWORD(wParam))
1031 {
1032 case UIS_SET:
1033 infoPtr->UIState |= HIWORD(wParam);
1034 break;
1035
1036 case UIS_CLEAR:
1037 infoPtr->UIState &= ~HIWORD(wParam);
1038 break;
1039 }
1040
1041 if (infoPtr->UIState != OldUIState)
1042 MonSelRepaintSelected(infoPtr);
1043 break;
1044 }
1045
1046 case WM_SETFONT:
1047 {
1048 Ret = (LRESULT)MonSelChangeFont(infoPtr,
1049 (HFONT)wParam,
1050 (BOOL)LOWORD(lParam));
1051 break;
1052 }
1053
1054 case WM_SIZE:
1055 {
1056 infoPtr->ClientSize.cx = LOWORD(lParam);
1057 infoPtr->ClientSize.cy = HIWORD(lParam);
1058
1059 /* Don't let MonSelUpdateMonitorsInfo repaint the control
1060 because this won't work properly in case the control
1061 was sized down! */
1062 MonSelUpdateMonitorsInfo(infoPtr,
1063 FALSE);
1064 InvalidateRect(infoPtr->hSelf,
1065 NULL,
1066 TRUE);
1067 break;
1068 }
1069
1070 case WM_GETFONT:
1071 {
1072 Ret = (LRESULT)infoPtr->hFont;
1073 break;
1074 }
1075
1076 case WM_ENABLE:
1077 {
1078 infoPtr->Enabled = ((BOOL)wParam != FALSE);
1079 MonSelRepaint(infoPtr);
1080 break;
1081 }
1082
1083 case WM_STYLECHANGED:
1084 {
1085 if (wParam == GWL_STYLE)
1086 {
1087 unsigned int OldEnabled = infoPtr->Enabled;
1088 infoPtr->Enabled = !(((LPSTYLESTRUCT)lParam)->styleNew & WS_DISABLED);
1089
1090 if (OldEnabled != infoPtr->Enabled)
1091 MonSelRepaint(infoPtr);
1092 }
1093 break;
1094 }
1095
1096 case MSLM_SETMONITORSINFO:
1097 {
1098 Ret = MonSelSetMonitorsInfo(infoPtr,
1099 (DWORD)wParam,
1100 (const MONSL_MONINFO *)lParam);
1101 break;
1102 }
1103
1104 case MSLM_GETMONITORSINFO:
1105 {
1106 Ret = MonSelGetMonitorsInfo(infoPtr,
1107 (DWORD)wParam,
1108 (PMONSL_MONINFO)lParam);
1109 break;
1110 }
1111
1112 case MSLM_GETMONITORINFOCOUNT:
1113 {
1114 Ret = infoPtr->MonitorsCount;
1115 break;
1116 }
1117
1118 case MSLM_HITTEST:
1119 {
1120 Ret = MonSelHitTest(infoPtr,
1121 (const POINT *)wParam);
1122 break;
1123 }
1124
1125 case MSLM_SETCURSEL:
1126 {
1127 Ret = MonSelSetCurSelMonitor(infoPtr,
1128 (INT)wParam,
1129 FALSE);
1130 break;
1131 }
1132
1133 case MSLM_GETCURSEL:
1134 {
1135 Ret = infoPtr->SelectedMonitor;
1136 break;
1137 }
1138
1139 case MSLM_SETMONITORINFO:
1140 {
1141 Ret = MonSelSetMonitorInfo(infoPtr,
1142 (INT)wParam,
1143 (const MONSL_MONINFO *)lParam);
1144 break;
1145 }
1146
1147 case MSLM_GETMONITORINFO:
1148 {
1149 Ret = MonSelGetMonitorInfo(infoPtr,
1150 (INT)wParam,
1151 (PMONSL_MONINFO)lParam);
1152 break;
1153 }
1154
1155 case MSLM_SETEXSTYLE:
1156 {
1157 Ret = MonSelSetExtendedStyle(infoPtr,
1158 (DWORD)lParam);
1159 break;
1160 }
1161
1162 case MSLM_GETEXSTYLE:
1163 {
1164 Ret = MonSelGetExtendedStyle(infoPtr);
1165 break;
1166 }
1167
1168 case MSLM_GETMONITORRECT:
1169 {
1170 Ret = (LRESULT)MonSelGetMonitorRect(infoPtr,
1171 (INT)wParam,
1172 (PRECT)lParam);
1173 break;
1174 }
1175
1176 case WM_CREATE:
1177 {
1178 infoPtr = (PMONITORSELWND) HeapAlloc(GetProcessHeap(),
1179 0,
1180 sizeof(MONITORSELWND));
1181 if (infoPtr == NULL)
1182 {
1183 Ret = (LRESULT)-1;
1184 break;
1185 }
1186
1187 ZeroMemory(infoPtr,
1188 sizeof(MONITORSELWND));
1189 infoPtr->hSelf = hwnd;
1190 infoPtr->hNotify = ((LPCREATESTRUCTW)lParam)->hwndParent;
1191 infoPtr->Enabled = !(((LPCREATESTRUCTW)lParam)->style & WS_DISABLED);
1192 infoPtr->UIState = SendMessage(hwnd,
1193 WM_QUERYUISTATE,
1194 0,
1195 0);
1196
1197 SetWindowLongPtrW(hwnd,
1198 0,
1199 (LONG_PTR)infoPtr);
1200
1201 MonSelCreate(infoPtr);
1202 break;
1203 }
1204
1205 case WM_DESTROY:
1206 {
1207 MonSelDestroy(infoPtr);
1208
1209 HeapFree(GetProcessHeap(),
1210 0,
1211 infoPtr);
1212 SetWindowLongPtrW(hwnd,
1213 0,
1214 (DWORD_PTR)NULL);
1215 break;
1216 }
1217
1218 default:
1219 {
1220 HandleDefaultMessage:
1221 Ret = DefWindowProcW(hwnd,
1222 uMsg,
1223 wParam,
1224 lParam);
1225 break;
1226 }
1227 }
1228
1229 return Ret;
1230 }
1231
1232 BOOL
1233 RegisterMonitorSelectionControl(IN HINSTANCE hInstance)
1234 {
1235 WNDCLASS wc = {0};
1236
1237 wc.style = CS_DBLCLKS;
1238 wc.lpfnWndProc = MonitorSelWndProc;
1239 wc.cbWndExtra = sizeof(PMONITORSELWND);
1240 wc.hInstance = hInstance;
1241 wc.hCursor = LoadCursorW(NULL,
1242 (LPWSTR)IDC_ARROW);
1243 wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);
1244 wc.lpszClassName = szMonitorSelWndClass;
1245
1246 return RegisterClass(&wc) != 0;
1247 }
1248
1249 VOID
1250 UnregisterMonitorSelectionControl(IN HINSTANCE hInstance)
1251 {
1252 UnregisterClassW(szMonitorSelWndClass,
1253 hInstance);
1254 }