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