1 /*
2  * PROJECT:     ReactOS Magnify
3  * LICENSE:     GPL - See COPYING in the top level directory
4  * FILE:        base/applications/magnify/magnifier.c
5  * PURPOSE:     Magnification of parts of the screen.
6  * AUTHORS:
7  *     Marc Piulachs <marc.piulachs@codexchange.net>
8  *     David Quintana <gigaherz@gmail.com>
9  */
10 
11 /* TODO: Support AppBar types other than ABE_TOP */
12 
13 #include "magnifier.h"
14 
15 #include <winbase.h>
16 #include <winuser.h>
17 #include <wingdi.h>
18 #include <winnls.h>
19 #include <commctrl.h>
20 #include <shellapi.h>
21 #include <windowsx.h>
22 #include <stdlib.h>
23 #include <tchar.h>
24 
25 #include "resource.h"
26 
27 #define APPMSG_NOTIFYICON (WM_APP+1)
28 #define APPMSG_APPBAR     (WM_APP+2)
29 
30 const TCHAR szWindowClass[] = TEXT("MAGNIFIER");
31 
32 /* Global Variables */
33 HINSTANCE hInst;
34 HWND hMainWnd;
35 
36 #define MAX_LOADSTRING 100
37 TCHAR szTitle[MAX_LOADSTRING];
38 
39 #define TIMER_SPEED   1
40 #define REPAINT_SPEED 100
41 
42 DWORD lastTicks = 0;
43 
44 HWND hDesktopWindow = NULL;
45 
46 NOTIFYICONDATA nid;
47 HICON notifyIcon;
48 HMENU notifyMenu;
49 HWND  hOptionsDialog;
50 BOOL  bOptionsDialog = FALSE;
51 BOOL  bRecreateOffscreenDC = TRUE;
52 LONG  sourceWidth = 0;
53 LONG  sourceHeight = 0;
54 HDC     hdcOffscreen = NULL;
55 HBITMAP hbmpOffscreen = NULL;
56 HANDLE  hbmpOld;
57 POINT ptDragOffset;
58 INT nearEdge;
59 
60 /* Current magnified area */
61 POINT cp;
62 
63 /* Last positions */
64 POINT pMouse;
65 POINT pCaret;
66 POINT pFocus;
67 HWND pCaretWnd;
68 HWND pFocusWnd;
69 
70 ATOM                MyRegisterClass(HINSTANCE hInstance);
71 BOOL                InitInstance(HINSTANCE, int);
72 LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
73 INT_PTR CALLBACK    AboutProc(HWND, UINT, WPARAM, LPARAM);
74 INT_PTR CALLBACK    OptionsProc(HWND, UINT, WPARAM, LPARAM);
75 INT_PTR CALLBACK    WarningProc(HWND, UINT, WPARAM, LPARAM);
76 
77 int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
78 {
79     MSG msg;
80     HACCEL hAccelTable;
81     INITCOMMONCONTROLSEX iccex;
82 
83     UNREFERENCED_PARAMETER(hPrevInstance);
84     UNREFERENCED_PARAMETER(lpCmdLine);
85 
86     switch (GetUserDefaultUILanguage())
87     {
88         case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
89           SetProcessDefaultLayout(LAYOUT_RTL);
90           break;
91 
92         default:
93           break;
94     }
95 
96     /* Initialize global strings */
97     LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
98     MyRegisterClass(hInstance);
99 
100     /* Perform application initialization */
101     if (!InitInstance(hInstance, nCmdShow))
102         return FALSE;
103 
104     hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MAGNIFIER));
105 
106     /* Main message loop */
107     while (GetMessage(&msg, NULL, 0, 0))
108     {
109         if (!TranslateAccelerator(hMainWnd, hAccelTable, &msg))
110         {
111             TranslateMessage(&msg);
112             DispatchMessage(&msg);
113         }
114     }
115 
116     /* Load the common controls */
117     iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
118     iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES;
119     InitCommonControlsEx(&iccex);
120 
121     SelectObject(hdcOffscreen, hbmpOld);
122     DeleteObject (hbmpOffscreen);
123     DeleteDC(hdcOffscreen);
124 
125     return (int) msg.wParam;
126 }
127 
128 ATOM MyRegisterClass(HINSTANCE hInstance)
129 {
130     WNDCLASS wc;
131 
132     wc.style            = CS_HREDRAW | CS_VREDRAW;
133     wc.lpfnWndProc      = WndProc;
134     wc.cbClsExtra       = 0;
135     wc.cbWndExtra       = 0;
136     wc.hInstance        = hInstance;
137     wc.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
138     wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
139     wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
140     wc.lpszMenuName     = NULL; //MAKEINTRESOURCE(IDC_MAGNIFIER);
141     wc.lpszClassName    = szWindowClass;
142 
143     return RegisterClass(&wc);
144 }
145 
146 void DoAppBarStuff(DWORD mode)
147 {
148     UINT uState;
149     APPBARDATA data = {0};
150     data.cbSize = sizeof(data);
151     data.hWnd = hMainWnd;
152     data.uCallbackMessage = APPMSG_APPBAR;
153 
154     if (mode == ABM_NEW || mode == ABM_SETPOS)
155     {
156         HWND hWndOrder = HWND_BOTTOM;
157         int rcw, rch;
158         RECT rcWorkArea;
159         SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
160 
161         if(mode == ABM_NEW)
162         {
163             SHAppBarMessage(ABM_NEW, &data);
164 
165             switch(AppBarConfig.uEdge)
166             {
167                 case ABE_LEFT:
168                     data.rc.top = rcWorkArea.top;
169                     data.rc.bottom = rcWorkArea.bottom;
170                     data.rc.left = rcWorkArea.left;
171                     data.rc.right = data.rc.left + AppBarConfig.appBarSizes.left;
172                     break;
173                 case ABE_TOP:
174                     data.rc.left = rcWorkArea.left;
175                     data.rc.right = rcWorkArea.right;
176                     data.rc.top = rcWorkArea.top;
177                     data.rc.bottom = data.rc.top + AppBarConfig.appBarSizes.top;
178                     break;
179                 case ABE_RIGHT:
180                     data.rc.top = rcWorkArea.top;
181                     data.rc.bottom = rcWorkArea.bottom;
182                     data.rc.right = rcWorkArea.left;
183                     data.rc.left = data.rc.right - AppBarConfig.appBarSizes.right;
184                     break;
185                 case ABE_BOTTOM:
186                     data.rc.left = rcWorkArea.left;
187                     data.rc.right = rcWorkArea.right;
188                     data.rc.bottom = rcWorkArea.bottom;
189                     data.rc.top = data.rc.bottom - AppBarConfig.appBarSizes.bottom;
190                     break;
191             }
192         }
193         else
194         {
195             GetWindowRect(hMainWnd, &data.rc);
196         }
197 
198         data.uEdge = AppBarConfig.uEdge;
199         uState = SHAppBarMessage(ABM_QUERYPOS, &data);
200 
201         uState = SHAppBarMessage(ABM_SETPOS, &data);
202 
203         rcw = data.rc.right-data.rc.left;
204         rch = data.rc.bottom-data.rc.top;
205 
206         uState = SHAppBarMessage(ABM_GETSTATE, &data);
207         if(uState & ABS_ALWAYSONTOP)
208             hWndOrder = HWND_TOPMOST;
209 
210         SetWindowPos(hMainWnd, hWndOrder, data.rc.left, data.rc.top, rcw, rch, SWP_SHOWWINDOW|SWP_NOCOPYBITS);
211 
212     }
213     else if(mode == ABM_GETSTATE)
214     {
215         HWND hWndOrder = HWND_BOTTOM;
216         uState = SHAppBarMessage(ABM_GETSTATE, &data);
217         if(uState & ABS_ALWAYSONTOP)
218             hWndOrder = HWND_TOPMOST;
219         SetWindowPos(hMainWnd, hWndOrder, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
220     }
221     else if(mode == ABM_ACTIVATE)
222     {
223         SHAppBarMessage(ABM_ACTIVATE, &data);
224     }
225     else if(mode == ABM_WINDOWPOSCHANGED)
226     {
227         SHAppBarMessage(ABM_WINDOWPOSCHANGED, &data);
228     }
229     else if(mode == ABM_REMOVE)
230     {
231         SHAppBarMessage(ABM_REMOVE, &data);
232     }
233 }
234 
235 void AttachAppBar(INT uEdge)
236 {
237     if (AppBarConfig.uEdge == uEdge)
238         return;
239 
240     if(AppBarConfig.uEdge < 0 && uEdge >= 0)
241     {
242         SetWindowLongPtr(hMainWnd, GWL_STYLE, GetWindowLongPtr(hMainWnd, GWL_STYLE) & (~WS_CAPTION));
243     }
244     else if(uEdge < 0 && AppBarConfig.uEdge>=0)
245     {
246         SetWindowLongPtr(hMainWnd, GWL_STYLE, GetWindowLongPtr(hMainWnd, GWL_STYLE) | WS_CAPTION);
247         SetWindowPos(hMainWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
248     }
249 
250     if(AppBarConfig.uEdge >= 0)
251     {
252         DoAppBarStuff(ABM_REMOVE);
253     }
254 
255     if (uEdge >=0)
256     {
257         AppBarConfig.uEdge = uEdge;
258         DoAppBarStuff(ABM_NEW);
259     }
260     else
261     {
262         RECT rc = AppBarConfig.rcFloating;
263         SetWindowPos(hMainWnd, HWND_TOPMOST, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0);
264     }
265 
266     AppBarConfig.uEdge = uEdge;
267 }
268 
269 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
270 {
271     RECT rc;
272     DWORD exStyles = WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT;
273     DWORD dwStyles = WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
274 
275     /* Load settings from registry */
276     LoadSettings();
277 
278     rc = AppBarConfig.rcFloating;
279 
280     hInst = hInstance; // Store instance handle in our global variable
281 
282     if (AppBarConfig.uEdge<0)
283     {
284         dwStyles |= WS_CAPTION;
285         exStyles |= WS_EX_TOPMOST;
286     }
287 
288     /* Create the Window */
289     hMainWnd = CreateWindowEx(
290         exStyles,
291         szWindowClass,
292         szTitle,
293         dwStyles,
294         rc.left,
295         rc.top,
296         rc.right-rc.left,
297         rc.bottom-rc.top,
298         NULL,
299         NULL,
300         hInstance,
301         NULL);
302 
303     if (!hMainWnd)
304         return FALSE;
305 
306     if (AppBarConfig.uEdge>=0) DoAppBarStuff(ABM_NEW);
307     else SetWindowPos(hMainWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
308 
309     // In Windows 2003's Magnifier, the "Start Minimized" setting
310     // refers exclusively to the options dialog, not the main window itself.
311     hOptionsDialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOGOPTIONS), hMainWnd, OptionsProc);
312     if (bStartMinimized)
313         ShowWindow(hOptionsDialog, SW_HIDE);
314     else
315         ShowWindow(hOptionsDialog, SW_SHOW);
316 
317     if (bShowWarning)
318         DialogBox(hInstance, MAKEINTRESOURCE(IDD_WARNINGDIALOG), hMainWnd, WarningProc);
319 
320     return TRUE;
321 }
322 
323 void Refresh()
324 {
325     if (!IsIconic(hMainWnd))
326     {
327         /* Invalidate the client area forcing a WM_PAINT message */
328         InvalidateRgn(hMainWnd, NULL, TRUE);
329     }
330 }
331 
332 void GetBestOverlapWithMonitors(LPRECT rect)
333 {
334     int rcLeft, rcTop;
335     int rcWidth, rcHeight;
336     RECT rcMon;
337     HMONITOR hMon = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST);
338     MONITORINFO info;
339     info.cbSize = sizeof(info);
340 
341     GetMonitorInfo(hMon, &info);
342 
343     rcMon = info.rcMonitor;
344 
345     rcLeft = rect->left;
346     rcTop = rect->top;
347     rcWidth  = (rect->right - rect->left);
348     rcHeight = (rect->bottom - rect->top);
349 
350     if (rcLeft < rcMon.left)
351        rcLeft = rcMon.left;
352 
353     if (rcTop < rcMon.top)
354        rcTop = rcMon.top;
355 
356     if (rcLeft > (rcMon.right - rcWidth))
357         rcLeft = (rcMon.right - rcWidth);
358 
359     if (rcTop > (rcMon.bottom - rcHeight))
360         rcTop = (rcMon.bottom - rcHeight);
361 
362     OffsetRect(rect, (rcLeft-rect->left), (rcTop-rect->top));
363 }
364 
365 void Draw(HDC aDc)
366 {
367     HDC desktopHdc = NULL;
368 
369     RECT sourceRect, intersectedRect;
370     RECT targetRect, appRect;
371     DWORD rop = SRCCOPY;
372     CURSORINFO cinfo;
373     ICONINFO iinfo;
374 
375     int AppWidth, AppHeight;
376 
377     if (bInvertColors)
378         rop = NOTSRCCOPY;
379 
380     desktopHdc = GetDC(0);
381 
382     GetClientRect(hMainWnd, &appRect);
383     AppWidth  = (appRect.right - appRect.left);
384     AppHeight = (appRect.bottom - appRect.top);
385 
386     ZeroMemory(&cinfo, sizeof(cinfo));
387     ZeroMemory(&iinfo, sizeof(iinfo));
388     cinfo.cbSize = sizeof(cinfo);
389     GetCursorInfo(&cinfo);
390     GetIconInfo(cinfo.hCursor, &iinfo);
391 
392     targetRect = appRect;
393     ClientToScreen(hMainWnd, (POINT*)&targetRect.left);
394     ClientToScreen(hMainWnd, (POINT*)&targetRect.right);
395 
396     if (bRecreateOffscreenDC || !hdcOffscreen)
397     {
398         bRecreateOffscreenDC = FALSE;
399 
400         if(hdcOffscreen)
401         {
402             SelectObject(hdcOffscreen, hbmpOld);
403             DeleteObject (hbmpOffscreen);
404             DeleteDC(hdcOffscreen);
405         }
406 
407         sourceWidth  = AppWidth / iZoom;
408         sourceHeight = AppHeight / iZoom;
409 
410          /* Create a memory DC compatible with client area DC */
411         hdcOffscreen = CreateCompatibleDC(desktopHdc);
412 
413         /* Create a bitmap compatible with the client area DC */
414         hbmpOffscreen = CreateCompatibleBitmap(
415             desktopHdc,
416             sourceWidth,
417             sourceHeight);
418 
419         /* Select our bitmap in memory DC and save the old one */
420         hbmpOld = SelectObject(hdcOffscreen , hbmpOffscreen);
421     }
422 
423     GetWindowRect(hDesktopWindow, &sourceRect);
424     sourceRect.left = (cp.x) - (sourceWidth /2);
425     sourceRect.top = (cp.y) - (sourceHeight /2);
426     sourceRect.right = sourceRect.left + sourceWidth;
427     sourceRect.bottom = sourceRect.top + sourceHeight;
428 
429     GetBestOverlapWithMonitors(&sourceRect);
430 
431     /* Paint the screen bitmap to our in memory DC */
432     BitBlt(
433         hdcOffscreen,
434         0,
435         0,
436         sourceWidth,
437         sourceHeight,
438         desktopHdc,
439         sourceRect.left,
440         sourceRect.top,
441         rop);
442 
443     if (IntersectRect(&intersectedRect, &sourceRect, &targetRect))
444     {
445         OffsetRect(&intersectedRect, -sourceRect.left, -sourceRect.top);
446         FillRect(hdcOffscreen, &intersectedRect, GetStockObject(DC_BRUSH));
447     }
448 
449     /* Draw the mouse pointer in the right position */
450     DrawIcon(
451         hdcOffscreen ,
452         pMouse.x - iinfo.xHotspot - sourceRect.left, // - 10,
453         pMouse.y - iinfo.yHotspot - sourceRect.top, // - 10,
454         cinfo.hCursor);
455 
456     /* Blast the stretched image from memory DC to window DC */
457     StretchBlt(
458         aDc,
459         0,
460         0,
461         AppWidth,
462         AppHeight,
463         hdcOffscreen,
464         0,
465         0,
466         sourceWidth,
467         sourceHeight,
468         SRCCOPY | NOMIRRORBITMAP);
469 
470     /* Cleanup */
471     if (iinfo.hbmMask)
472         DeleteObject(iinfo.hbmMask);
473     if (iinfo.hbmColor)
474         DeleteObject(iinfo.hbmColor);
475     ReleaseDC(hDesktopWindow, desktopHdc);
476 }
477 
478 void HandleNotifyIconMessage(HWND hWnd, WPARAM wParam, LPARAM lParam)
479 {
480     POINT pt;
481 
482     switch(lParam)
483     {
484         case WM_LBUTTONUP:
485             PostMessage(hMainWnd, WM_COMMAND, IDM_OPTIONS, 0);
486             break;
487         case WM_RBUTTONUP:
488             GetCursorPos (&pt);
489             TrackPopupMenu(notifyMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
490             break;
491     }
492 }
493 
494 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
495 {
496     int wmId;
497 
498     switch (message)
499     {
500         case WM_TIMER:
501         {
502             BOOL hasMoved = FALSE;
503 
504             GUITHREADINFO guiInfo;
505             guiInfo.cbSize = sizeof(guiInfo);
506 
507             GetGUIThreadInfo(0, &guiInfo);
508 
509             if (bFollowMouse)
510             {
511                 POINT pNewMouse;
512 
513                 //Get current mouse position
514                 GetCursorPos (&pNewMouse);
515 
516 #define PointsAreEqual(pt1, pt2) (((pt1).x == (pt2).x) && ((pt1).y == (pt2).y))
517 
518                 //If mouse has moved ...
519                 if (!PointsAreEqual(pMouse, pNewMouse))
520                 {
521                     //Update to new position
522                     pMouse = pNewMouse;
523                     cp = pNewMouse;
524                     hasMoved = TRUE;
525                 }
526             }
527 
528             if(guiInfo.hwndActive != hMainWnd)
529             {
530                 if (bFollowCaret)
531                 {
532                     if (guiInfo.hwndCaret)
533                     {
534                         POINT ptCaret;
535                         ptCaret.x = (guiInfo.rcCaret.left + guiInfo.rcCaret.right) / 2;
536                         ptCaret.y = (guiInfo.rcCaret.top + guiInfo.rcCaret.bottom) / 2;
537 
538                         if ((pCaretWnd != guiInfo.hwndCaret) || !PointsAreEqual(pCaret, ptCaret))
539                         {
540                             //Update to new position
541                             pCaret = ptCaret;
542                             pCaretWnd = guiInfo.hwndCaret;
543                             if(!hasMoved)
544                             {
545                                 ClientToScreen (guiInfo.hwndCaret, (LPPOINT) &ptCaret);
546                                 cp = ptCaret;
547                                 hasMoved = TRUE;
548                             }
549                         }
550                     }
551                     else
552                     {
553                         pCaretWnd = NULL;
554                     }
555                 }
556 
557                 if (bFollowFocus)
558                 {
559                     if(guiInfo.hwndFocus && !guiInfo.hwndCaret)
560                     {
561                         POINT ptFocus;
562                         RECT activeRect;
563 
564                         //Get current control focus
565                         GetWindowRect(guiInfo.hwndFocus, &activeRect);
566                         ptFocus.x = (activeRect.left + activeRect.right) / 2;
567                         ptFocus.y = (activeRect.top + activeRect.bottom) / 2;
568 
569                         if((guiInfo.hwndFocus != pFocusWnd) || !PointsAreEqual(pFocus, ptFocus))
570                         {
571                             //Update to new position
572                             pFocus = ptFocus;
573                             pFocusWnd = guiInfo.hwndFocus;
574                             if(!hasMoved)
575                             {
576                                 cp = ptFocus;
577                                 hasMoved = TRUE;
578                             }
579                         }
580                     }
581                     else
582                     {
583                         pFocusWnd = NULL;
584                     }
585                 }
586             }
587 
588             if(!hasMoved)
589             {
590                 DWORD newTicks = GetTickCount();
591                 DWORD elapsed = (newTicks - lastTicks);
592                 if(elapsed > REPAINT_SPEED)
593                 {
594                     hasMoved = TRUE;
595                 }
596             }
597 
598             if(hasMoved)
599             {
600                 lastTicks = GetTickCount();
601                 Refresh();
602             }
603 
604             return 0;
605         }
606 
607         case WM_COMMAND:
608         {
609             wmId = LOWORD(wParam);
610             /* Parse the menu selections */
611             switch (wmId)
612             {
613                 case IDM_OPTIONS:
614                     if(bOptionsDialog)
615                     {
616                         ShowWindow(hOptionsDialog, SW_HIDE);
617                     }
618                     else
619                     {
620                         ShowWindow(hOptionsDialog, SW_SHOW);
621                     }
622                     break;
623                 case IDM_ABOUT:
624                     DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutProc);
625                     break;
626                 case IDM_EXIT:
627                     DestroyWindow(hWnd);
628                     break;
629                 default:
630                     return DefWindowProc(hWnd, message, wParam, lParam);
631             }
632             return 0;
633         }
634 
635         case WM_PAINT:
636         {
637             PAINTSTRUCT PaintStruct;
638             HDC dc;
639             dc = BeginPaint(hWnd, &PaintStruct);
640             Draw(dc);
641             EndPaint(hWnd, &PaintStruct);
642             return 0;
643         }
644 
645         case WM_CONTEXTMENU:
646             TrackPopupMenu(notifyMenu, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hWnd, NULL);
647             return 0;
648 
649         case WM_LBUTTONDOWN:
650         {
651             RECT rc;
652             POINT pt;
653             SetCapture(hWnd);
654 
655             GetCursorPos(&pt);
656             GetWindowRect(hWnd, &rc);
657             ptDragOffset.x = pt.x - rc.left;
658             ptDragOffset.y = pt.y - rc.top;
659 
660             nearEdge = AppBarConfig.uEdge;
661 
662             break;
663         }
664         case WM_MOUSEMOVE:
665             if(GetCapture() == hWnd)
666             {
667                 RECT rc;
668                 POINT pt;
669                 RECT rcWorkArea;
670                 SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
671                 GetCursorPos(&pt);
672                 GetWindowRect(hWnd, &rc);
673 
674                 if(AppBarConfig.uEdge>=0)
675                 {
676                     if (pt.x >= rcWorkArea.left && pt.x <= rcWorkArea.right &&
677                         pt.y >= rcWorkArea.top && pt.y <= rcWorkArea.bottom)
678                     {
679                         AttachAppBar(-2);
680 
681                         // Fixup offset
682                         GetWindowRect(hWnd, &rc);
683                         ptDragOffset.x = (rc.right-rc.left)/2;
684                         ptDragOffset.y = 2;
685 
686                         rc.left = pt.x - ptDragOffset.x;
687                         rc.top = pt.y - ptDragOffset.y;
688 
689                         SetWindowPos(hWnd, HWND_TOPMOST, rc.left, rc.top, 0, 0, SWP_NOSIZE);
690                     }
691                 }
692                 else
693                 {
694                     if(pt.x <= rcWorkArea.left+8 && nearEdge != ABE_LEFT)
695                     {
696                         AttachAppBar(ABE_LEFT);
697                         nearEdge = ABE_LEFT;
698                     }
699                     else if(pt.y <= rcWorkArea.top+8 && nearEdge != ABE_TOP)
700                     {
701                         AttachAppBar(ABE_TOP);
702                         nearEdge = ABE_TOP;
703                     }
704                     else if(pt.x >= rcWorkArea.right-8 && nearEdge != ABE_RIGHT)
705                     {
706                         AttachAppBar(ABE_RIGHT);
707                         nearEdge = ABE_RIGHT;
708                     }
709                     else if(pt.y >= rcWorkArea.bottom-8 && nearEdge != ABE_BOTTOM)
710                     {
711                         AttachAppBar(ABE_BOTTOM);
712                         nearEdge = ABE_BOTTOM;
713                     }
714                     else
715                     {
716                         rc.left = pt.x - ptDragOffset.x;
717                         rc.top = pt.y - ptDragOffset.y;
718 
719                         SetWindowPos(hWnd, HWND_TOPMOST, rc.left, rc.top, 0, 0, SWP_NOSIZE);
720                         nearEdge = -1;
721                     }
722                 }
723 
724                 pMouse = pt;
725                 Refresh();
726             }
727             break;
728         case WM_LBUTTONUP:
729             if(GetCapture() == hWnd)
730             {
731                 if (AppBarConfig.uEdge>=0)
732                     DoAppBarStuff(ABM_GETSTATE);
733                 else
734                     SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
735                 ReleaseCapture();
736             }
737             break;
738 
739         case WM_SIZE:
740             if(AppBarConfig.uEdge>=0) DoAppBarStuff(ABM_SETPOS);
741             /* fallthrough */
742         case WM_DISPLAYCHANGE:
743             bRecreateOffscreenDC = TRUE;
744             Refresh();
745             break;
746 
747         case WM_ERASEBKGND:
748             // handle WM_ERASEBKGND by simply returning non-zero because we did all the drawing in WM_PAINT.
749             return 0;
750 
751         case WM_DESTROY:
752         {
753             if (AppBarConfig.uEdge >= 0)
754                 DoAppBarStuff(ABM_REMOVE);
755 
756             KillTimer(hWnd, 1);
757 
758             /* Save settings to registry */
759             SaveSettings();
760 
761             /* Cleanup notification icon */
762             ZeroMemory(&nid, sizeof(nid));
763             nid.cbSize = sizeof(nid);
764             nid.uFlags = NIF_MESSAGE;
765             nid.hWnd = hWnd;
766             nid.uCallbackMessage = APPMSG_NOTIFYICON;
767             Shell_NotifyIcon(NIM_DELETE, &nid);
768             DestroyIcon(notifyIcon);
769 
770             DestroyWindow(hOptionsDialog);
771 
772             PostQuitMessage(0);
773             return 0;
774         }
775 
776         case WM_CREATE:
777         {
778             HMENU tempMenu;
779 
780             /* Get the desktop window */
781             hDesktopWindow = GetDesktopWindow();
782 
783             /* Set the timer */
784             SetTimer(hWnd, 1, TIMER_SPEED, NULL);
785 
786             /* Notification icon */
787             notifyIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0);
788 
789             ZeroMemory(&nid, sizeof(nid));
790             nid.cbSize = sizeof(nid);
791             nid.uFlags = NIF_ICON | NIF_MESSAGE;
792             nid.hWnd = hWnd;
793             nid.uCallbackMessage = APPMSG_NOTIFYICON;
794             nid.hIcon = notifyIcon;
795             Shell_NotifyIcon(NIM_ADD, &nid);
796 
797             tempMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDC_MAGNIFIER));
798             notifyMenu = GetSubMenu(tempMenu, 0);
799             RemoveMenu(tempMenu, 0, MF_BYPOSITION);
800             DestroyMenu(tempMenu);
801             return 0;
802         }
803 
804         case APPMSG_APPBAR:
805         {
806             switch (wParam)
807             {
808                 case ABN_STATECHANGE:
809                     DoAppBarStuff(ABM_GETSTATE);
810                     break;
811                 case ABN_POSCHANGED:
812                     DoAppBarStuff(ABM_SETPOS);
813                     break;
814                 case ABN_FULLSCREENAPP:
815                 {
816                     if(!lParam)
817                     {
818                         DoAppBarStuff(ABM_GETSTATE);
819                         break;
820                     }
821 
822                     SetWindowPos(hMainWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
823                     break;
824                 }
825                case ABN_WINDOWARRANGE:
826                     if(lParam)
827                         ShowWindow(hMainWnd, SW_HIDE);
828                     else
829                         ShowWindow(hMainWnd, SW_SHOW);
830                     break;
831             }
832             return 0;
833         }
834 
835         case APPMSG_NOTIFYICON:
836             HandleNotifyIconMessage(hWnd, wParam, lParam);
837             return 0;
838 
839         case WM_ACTIVATE:
840             if(AppBarConfig.uEdge>=0) DoAppBarStuff(ABM_ACTIVATE);
841             break;
842 
843         case WM_WINDOWPOSCHANGED:
844             if(AppBarConfig.uEdge>=0) DoAppBarStuff(ABM_WINDOWPOSCHANGED);
845             Refresh();
846             break;
847 
848         default:
849             break;
850     }
851 
852     return DefWindowProc(hWnd, message, wParam, lParam);
853 }
854 
855 INT_PTR CALLBACK AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
856 {
857     UNREFERENCED_PARAMETER(lParam);
858     switch (message)
859     {
860         case WM_INITDIALOG:
861             return (INT_PTR)TRUE;
862 
863         case WM_COMMAND:
864             if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
865             {
866                 EndDialog(hDlg, LOWORD(wParam));
867                 return (INT_PTR)TRUE;
868             }
869             break;
870     }
871 
872     return (INT_PTR)FALSE;
873 }
874 
875 INT_PTR CALLBACK OptionsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
876 {
877     UNREFERENCED_PARAMETER(lParam);
878     switch (message)
879     {
880         case WM_SHOWWINDOW:
881             bOptionsDialog = wParam;
882             break;
883         case WM_INITDIALOG:
884         {
885             // Add the zoom items...
886             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("1"));
887             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("2"));
888             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("3"));
889             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("4"));
890             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("5"));
891             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("6"));
892             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("7"));
893             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("8"));
894 
895             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_SETCURSEL, iZoom - 1, 0);
896 
897             if (bFollowMouse)
898                 SendDlgItemMessage(hDlg,IDC_FOLLOWMOUSECHECK,BM_SETCHECK , wParam ,0);
899 
900             if (bFollowFocus)
901                 SendDlgItemMessage(hDlg,IDC_FOLLOWKEYBOARDCHECK,BM_SETCHECK , wParam ,0);
902 
903             if (bFollowCaret)
904                 SendDlgItemMessage(hDlg,IDC_FOLLOWTEXTEDITINGCHECK,BM_SETCHECK , wParam ,0);
905 
906             if (bInvertColors)
907                 SendDlgItemMessage(hDlg,IDC_INVERTCOLORSCHECK,BM_SETCHECK , wParam ,0);
908 
909             if (bStartMinimized)
910                 SendDlgItemMessage(hDlg,IDC_STARTMINIMIZEDCHECK,BM_SETCHECK , wParam ,0);
911 
912             if (bShowMagnifier)
913                 SendDlgItemMessage(hDlg,IDC_SHOWMAGNIFIERCHECK,BM_SETCHECK , wParam ,0);
914 
915             return (INT_PTR)TRUE;
916         }
917 
918     case WM_COMMAND:
919         switch (LOWORD(wParam))
920         {
921             case IDOK:
922             case IDCANCEL:
923                 ShowWindow(hDlg, SW_HIDE);
924                 return (INT_PTR)TRUE;
925 
926             case IDC_BUTTON_HELP:
927                 /* Unimplemented */
928                 MessageBox(hDlg , TEXT("Magnifier help not available yet!") , TEXT("Help") , MB_OK);
929                 break;
930             case IDC_ZOOM:
931                 if (HIWORD(wParam) == CBN_SELCHANGE)
932                 {
933                     HWND hCombo = GetDlgItem(hDlg,IDC_ZOOM);
934                     LPCTSTR currentZoomValue = TEXT("");
935 
936                     /* Get index of current selection and the text of that selection */
937                     int currentSelectionIndex = ComboBox_GetCurSel(hCombo);
938                     ComboBox_GetLBText(hCombo, currentSelectionIndex, currentZoomValue);
939                     iZoom = _ttoi(currentZoomValue);
940 
941                     /* Trigger the Draw function to rezoom (which will be set false automatically after rezooming) */
942                     bRecreateOffscreenDC = TRUE;
943 
944                     /* Update the magnifier UI */
945                     Refresh();
946                 }
947                 break;
948             case IDC_INVERTCOLORSCHECK:
949                 bInvertColors = IsDlgButtonChecked(hDlg, IDC_INVERTCOLORSCHECK);
950                 Refresh();
951                 break;
952             case IDC_FOLLOWMOUSECHECK:
953                 bFollowMouse = IsDlgButtonChecked(hDlg, IDC_FOLLOWMOUSECHECK);
954                 break;
955             case IDC_FOLLOWKEYBOARDCHECK:
956                 bFollowFocus = IsDlgButtonChecked(hDlg, IDC_FOLLOWKEYBOARDCHECK);
957                 break;
958             case IDC_FOLLOWTEXTEDITINGCHECK:
959                 bFollowCaret = IsDlgButtonChecked(hDlg, IDC_FOLLOWTEXTEDITINGCHECK);
960                 break;
961             case IDC_STARTMINIMIZEDCHECK:
962                 bStartMinimized = IsDlgButtonChecked(hDlg, IDC_STARTMINIMIZEDCHECK);
963                 break;
964             case IDC_SHOWMAGNIFIER:
965                 bShowMagnifier = IsDlgButtonChecked(hDlg, IDC_SHOWMAGNIFIERCHECK);
966                 if (bShowMagnifier)
967                     ShowWindow (hMainWnd , SW_SHOW);
968                 else
969                     ShowWindow (hMainWnd , SW_HIDE);
970                 break;
971         }
972     }
973 
974     return (INT_PTR)FALSE;
975 }
976 
977 INT_PTR CALLBACK WarningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
978 {
979     UNREFERENCED_PARAMETER(lParam);
980 
981     switch (message)
982     {
983         case WM_INITDIALOG:
984             return (INT_PTR)TRUE;
985 
986         case WM_COMMAND:
987             switch (LOWORD(wParam))
988             {
989                 case IDC_SHOWWARNINGCHECK:
990                     bShowWarning = !IsDlgButtonChecked(hDlg, IDC_SHOWWARNINGCHECK);
991                     break;
992             }
993             if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
994             {
995                 EndDialog(hDlg, LOWORD(wParam));
996                 return (INT_PTR)TRUE;
997             }
998             break;
999     }
1000 
1001     return (INT_PTR)FALSE;
1002 }
1003