[MAGNIFY] Fix a string buffer misuage that led to division by zero exception. (#1655)
[reactos.git] / base / applications / magnify / magnifier.c
index 3af6236..13a0a3c 100644 (file)
  * LICENSE:     GPL - See COPYING in the top level directory
  * FILE:        base/applications/magnify/magnifier.c
  * PURPOSE:     Magnification of parts of the screen.
- * COPYRIGHT:   Copyright 2007 Marc Piulachs <marc.piulachs@codexchange.net>
- *
+ * AUTHORS:
+ *     Marc Piulachs <marc.piulachs@codexchange.net>
+ *     David Quintana <gigaherz@gmail.com>
  */
 
-/* TODO: AppBar */
+/* TODO: Support AppBar types other than ABE_TOP */
+
 #include "magnifier.h"
 
 #include <winbase.h>
 #include <winuser.h>
 #include <wingdi.h>
 #include <winnls.h>
+#include <commctrl.h>
+#include <shellapi.h>
+#include <windowsx.h>
+#include <stdlib.h>
+#include <tchar.h>
 
 #include "resource.h"
 
-const TCHAR szWindowClass[] = TEXT("MAGNIFIER");
+#define APPMSG_NOTIFYICON (WM_APP+1)
+#define APPMSG_APPBAR     (WM_APP+2)
 
-#define MAX_LOADSTRING 100
+const TCHAR szWindowClass[] = TEXT("MAGNIFIER");
 
-// Global Variables:
-HINSTANCE hInst;                                                               // current instance
+/* Global Variables */
+HINSTANCE hInst;
 HWND hMainWnd;
 
-TCHAR szTitle[MAX_LOADSTRING];                                 // The title bar text
+#define MAX_LOADSTRING 100
+TCHAR szTitle[MAX_LOADSTRING];
+
+#define TIMER_SPEED   1
+#define REPAINT_SPEED 100
 
-#define REPAINT_SPEED  100
+DWORD lastTicks = 0;
 
 HWND hDesktopWindow = NULL;
 
-//Current magnified area
+NOTIFYICONDATA nid;
+HICON notifyIcon;
+HMENU notifyMenu;
+HWND  hOptionsDialog;
+BOOL  bOptionsDialog = FALSE;
+BOOL  bRecreateOffscreenDC = TRUE;
+LONG  sourceWidth = 0;
+LONG  sourceHeight = 0;
+HDC     hdcOffscreen = NULL;
+HBITMAP hbmpOffscreen = NULL;
+HANDLE  hbmpOld;
+POINT ptDragOffset;
+INT nearEdge;
+
+/* Current magnified area */
 POINT cp;
 
-//Last positions
+/* Last positions */
 POINT pMouse;
 POINT pCaret;
 POINT pFocus;
+HWND pCaretWnd;
+HWND pFocusWnd;
 
-// Forward declarations of functions included in this code module:
-ATOM                           MyRegisterClass(HINSTANCE hInstance);
-BOOL                           InitInstance(HINSTANCE, int);
-LRESULT CALLBACK       WndProc(HWND, UINT, WPARAM, LPARAM);
-INT_PTR CALLBACK       AboutProc(HWND, UINT, WPARAM, LPARAM);
-INT_PTR CALLBACK       OptionsProc(HWND, UINT, WPARAM, LPARAM);
-INT_PTR CALLBACK       WarningProc(HWND, UINT, WPARAM, LPARAM);
+ATOM                MyRegisterClass(HINSTANCE hInstance);
+BOOL                InitInstance(HINSTANCE, int);
+LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
+INT_PTR CALLBACK    AboutProc(HWND, UINT, WPARAM, LPARAM);
+INT_PTR CALLBACK    OptionsProc(HWND, UINT, WPARAM, LPARAM);
+INT_PTR CALLBACK    WarningProc(HWND, UINT, WPARAM, LPARAM);
 
-int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
 {
-    // TODO: Place code here.
     MSG msg;
     HACCEL hAccelTable;
+    INITCOMMONCONTROLSEX iccex;
 
     UNREFERENCED_PARAMETER(hPrevInstance);
     UNREFERENCED_PARAMETER(lpCmdLine);
 
     switch (GetUserDefaultUILanguage())
-  {
-    case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
-      SetProcessDefaultLayout(LAYOUT_RTL);
-      break;
+    {
+        case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
+          SetProcessDefaultLayout(LAYOUT_RTL);
+          break;
 
-    default:
-      break;
-  }
+        default:
+          break;
+    }
 
-    // Initialize global strings
+    /* Initialize global strings */
     LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
     MyRegisterClass(hInstance);
 
-    // Perform application initialization:
-    if (!InitInstance (hInstance, nCmdShow))
-    {
+    /* Perform application initialization */
+    if (!InitInstance(hInstance, nCmdShow))
         return FALSE;
-    }
 
     hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MAGNIFIER));
 
-    // Main message loop:
+    /* Main message loop */
     while (GetMessage(&msg, NULL, 0, 0))
     {
-        if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
+        if (!TranslateAccelerator(hMainWnd, hAccelTable, &msg))
         {
             TranslateMessage(&msg);
             DispatchMessage(&msg);
         }
     }
 
-    return (int) msg.wParam;
-}
+    /* Load the common controls */
+    iccex.dwSize = sizeof(INITCOMMONCONTROLSEX);
+    iccex.dwICC = ICC_STANDARD_CLASSES | ICC_WIN95_CLASSES;
+    InitCommonControlsEx(&iccex);
 
+    SelectObject(hdcOffscreen, hbmpOld);
+    DeleteObject (hbmpOffscreen);
+    DeleteDC(hdcOffscreen);
 
+    return (int) msg.wParam;
+}
 
-/*
- *  FUNCTION: MyRegisterClass()
- *
- *  PURPOSE: Registers the window class.
- *
- *  COMMENTS:
- *
- *    This function and its usage are only necessary if you want this code
- *    to be compatible with Win32 systems prior to the 'RegisterClassEx'
- *    function that was added to Windows 95. It is important to call this function
- *    so that the application will get 'well formed' small icons associated
- *    with it.
- */
 ATOM MyRegisterClass(HINSTANCE hInstance)
 {
     WNDCLASS wc;
@@ -118,200 +137,360 @@ ATOM MyRegisterClass(HINSTANCE hInstance)
     wc.hIcon            = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
     wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
     wc.hbrBackground    = (HBRUSH)(COLOR_WINDOW+1);
-    wc.lpszMenuName     = MAKEINTRESOURCE(IDC_MAGNIFIER);
+    wc.lpszMenuName     = NULL; //MAKEINTRESOURCE(IDC_MAGNIFIER);
     wc.lpszClassName    = szWindowClass;
 
     return RegisterClass(&wc);
 }
 
-/*
- *  FUNCTION: InitInstance(HINSTANCE, int)
- *
- *  PURPOSE: Saves instance handle and creates main window
- *
- *   COMMENTS:
- *
- *        In this function, we save the instance handle in a global variable and
- *        create and display the main program window.
- */
+void DoAppBarStuff(DWORD mode)
+{
+    UINT uState;
+    APPBARDATA data = {0};
+    data.cbSize = sizeof(data);
+    data.hWnd = hMainWnd;
+    data.uCallbackMessage = APPMSG_APPBAR;
+
+    if (mode == ABM_NEW || mode == ABM_SETPOS)
+    {
+        HWND hWndOrder = HWND_BOTTOM;
+        int rcw, rch;
+        RECT rcWorkArea;
+        SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
+
+        if(mode == ABM_NEW)
+        {
+            SHAppBarMessage(ABM_NEW, &data);
+
+            switch(AppBarConfig.uEdge)
+            {
+                case ABE_LEFT:
+                    data.rc.top = rcWorkArea.top;
+                    data.rc.bottom = rcWorkArea.bottom;
+                    data.rc.left = rcWorkArea.left;
+                    data.rc.right = data.rc.left + AppBarConfig.appBarSizes.left;
+                    break;
+                case ABE_TOP:
+                    data.rc.left = rcWorkArea.left;
+                    data.rc.right = rcWorkArea.right;
+                    data.rc.top = rcWorkArea.top;
+                    data.rc.bottom = data.rc.top + AppBarConfig.appBarSizes.top;
+                    break;
+                case ABE_RIGHT:
+                    data.rc.top = rcWorkArea.top;
+                    data.rc.bottom = rcWorkArea.bottom;
+                    data.rc.right = rcWorkArea.left;
+                    data.rc.left = data.rc.right - AppBarConfig.appBarSizes.right;
+                    break;
+                case ABE_BOTTOM:
+                    data.rc.left = rcWorkArea.left;
+                    data.rc.right = rcWorkArea.right;
+                    data.rc.bottom = rcWorkArea.bottom;
+                    data.rc.top = data.rc.bottom - AppBarConfig.appBarSizes.bottom;
+                    break;
+            }
+        }
+        else
+        {
+            GetWindowRect(hMainWnd, &data.rc);
+        }
+
+        data.uEdge = AppBarConfig.uEdge;
+        uState = SHAppBarMessage(ABM_QUERYPOS, &data);
+
+        uState = SHAppBarMessage(ABM_SETPOS, &data);
+
+        rcw = data.rc.right-data.rc.left;
+        rch = data.rc.bottom-data.rc.top;
+
+        uState = SHAppBarMessage(ABM_GETSTATE, &data);
+        if(uState & ABS_ALWAYSONTOP)
+            hWndOrder = HWND_TOPMOST;
+
+        SetWindowPos(hMainWnd, hWndOrder, data.rc.left, data.rc.top, rcw, rch, SWP_SHOWWINDOW|SWP_NOCOPYBITS);
+
+    }
+    else if(mode == ABM_GETSTATE)
+    {
+        HWND hWndOrder = HWND_BOTTOM;
+        uState = SHAppBarMessage(ABM_GETSTATE, &data);
+        if(uState & ABS_ALWAYSONTOP)
+            hWndOrder = HWND_TOPMOST;
+        SetWindowPos(hMainWnd, hWndOrder, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+    }
+    else if(mode == ABM_ACTIVATE)
+    {
+        SHAppBarMessage(ABM_ACTIVATE, &data);
+    }
+    else if(mode == ABM_WINDOWPOSCHANGED)
+    {
+        SHAppBarMessage(ABM_WINDOWPOSCHANGED, &data);
+    }
+    else if(mode == ABM_REMOVE)
+    {
+        SHAppBarMessage(ABM_REMOVE, &data);
+    }
+}
+
+void AttachAppBar(INT uEdge)
+{
+    if (AppBarConfig.uEdge == uEdge)
+        return;
+
+    if(AppBarConfig.uEdge < 0 && uEdge >= 0)
+    {
+        SetWindowLongPtr(hMainWnd, GWL_STYLE, GetWindowLongPtr(hMainWnd, GWL_STYLE) & (~WS_CAPTION));
+    }
+    else if(uEdge < 0 && AppBarConfig.uEdge>=0)
+    {
+        SetWindowLongPtr(hMainWnd, GWL_STYLE, GetWindowLongPtr(hMainWnd, GWL_STYLE) | WS_CAPTION);
+        SetWindowPos(hMainWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED);
+    }
+
+    if(AppBarConfig.uEdge >= 0)
+    {
+        DoAppBarStuff(ABM_REMOVE);
+    }
+
+    if (uEdge >=0)
+    {
+        AppBarConfig.uEdge = uEdge;
+        DoAppBarStuff(ABM_NEW);
+    }
+    else
+    {
+        RECT rc = AppBarConfig.rcFloating;
+        SetWindowPos(hMainWnd, HWND_TOPMOST, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, 0);
+    }
+
+    AppBarConfig.uEdge = uEdge;
+}
+
 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
 {
-   RECT rcWorkArea;
-   hInst = hInstance; // Store instance handle in our global variable
+    RECT rc;
+    DWORD exStyles = WS_EX_TOOLWINDOW | WS_EX_CONTROLPARENT;
+    DWORD dwStyles = WS_SIZEBOX | WS_SYSMENU | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | WS_POPUP;
 
-   SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
+    /* Load settings from registry */
+    LoadSettings();
+
+    rc = AppBarConfig.rcFloating;
+
+    hInst = hInstance; // Store instance handle in our global variable
+
+    if (AppBarConfig.uEdge<0)
+    {
+        dwStyles |= WS_CAPTION;
+        exStyles |= WS_EX_TOPMOST;
+    }
 
-   /* Create the Window */
-   hMainWnd = CreateWindowEx(
-        WS_EX_TOPMOST | WS_EX_PALETTEWINDOW,
+    /* Create the Window */
+    hMainWnd = CreateWindowEx(
+        exStyles,
         szWindowClass,
         szTitle,
-        WS_OVERLAPPEDWINDOW,
-        CW_USEDEFAULT,
-        CW_USEDEFAULT,
-        (rcWorkArea.right - rcWorkArea.left) * 2 / 3,
-        200,
+        dwStyles,
+        rc.left,
+        rc.top,
+        rc.right-rc.left,
+        rc.bottom-rc.top,
         NULL,
         NULL,
         hInstance,
         NULL);
 
-   if (!hMainWnd)
-   {
-      return FALSE;
-   }
+    if (!hMainWnd)
+        return FALSE;
+
+    if (AppBarConfig.uEdge>=0) DoAppBarStuff(ABM_NEW);
+    else SetWindowPos(hMainWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_SHOWWINDOW);
 
-   ShowWindow(hMainWnd, (bStartMinimized) ? SW_MINIMIZE : nCmdShow);
-   UpdateWindow(hMainWnd);
+    // In Windows 2003's Magnifier, the "Start Minimized" setting
+    // refers exclusively to the options dialog, not the main window itself.
+    hOptionsDialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOGOPTIONS), hMainWnd, OptionsProc);
+    if (bStartMinimized)
+        ShowWindow(hOptionsDialog, SW_HIDE);
+    else
+        ShowWindow(hOptionsDialog, SW_SHOW);
 
-   if (bShowWarning)
-   {
-      DialogBox (hInstance, MAKEINTRESOURCE(IDD_WARNINGDIALOG), hMainWnd, WarningProc);
-   }
+    if (bShowWarning)
+        DialogBox(hInstance, MAKEINTRESOURCE(IDD_WARNINGDIALOG), hMainWnd, WarningProc);
 
-   return TRUE;
+    return TRUE;
 }
 
-void Refresh ()
+void Refresh()
 {
     if (!IsIconic(hMainWnd))
     {
-        // Invalidate the client area forcing a WM_PAINT message
+        /* Invalidate the client area forcing a WM_PAINT message */
         InvalidateRgn(hMainWnd, NULL, TRUE);
     }
 }
 
+void GetBestOverlapWithMonitors(LPRECT rect)
+{
+    int rcLeft, rcTop;
+    int rcWidth, rcHeight;
+    RECT rcMon;
+    HMONITOR hMon = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST);
+    MONITORINFO info;
+    info.cbSize = sizeof(info);
+
+    GetMonitorInfo(hMon, &info);
+
+    rcMon = info.rcMonitor;
+
+    rcLeft = rect->left;
+    rcTop = rect->top;
+    rcWidth  = (rect->right - rect->left);
+    rcHeight = (rect->bottom - rect->top);
+
+    if (rcLeft < rcMon.left)
+       rcLeft = rcMon.left;
+
+    if (rcTop < rcMon.top)
+       rcTop = rcMon.top;
+
+    if (rcLeft > (rcMon.right - rcWidth))
+        rcLeft = (rcMon.right - rcWidth);
+
+    if (rcTop > (rcMon.bottom - rcHeight))
+        rcTop = (rcMon.bottom - rcHeight);
+
+    OffsetRect(rect, (rcLeft-rect->left), (rcTop-rect->top));
+}
+
 void Draw(HDC aDc)
 {
     HDC desktopHdc = NULL;
-    HDC HdcStrech;
-    HANDLE hOld;
-    HBITMAP HbmpStrech;
-    
-    RECT R;
-    RECT appRect;
+
+    RECT sourceRect, intersectedRect;
+    RECT targetRect, appRect;
     DWORD rop = SRCCOPY;
     CURSORINFO cinfo;
     ICONINFO iinfo;
 
-    int Width, Height, AppWidth, AppHeight;
-    LONG blitAreaWidth, blitAreaHeight, blitAreaX, blitAreaY;
+    int AppWidth, AppHeight;
+
+    if (bInvertColors)
+        rop = NOTSRCCOPY;
 
-    desktopHdc = GetWindowDC (hDesktopWindow);
+    desktopHdc = GetDC(0);
 
     GetClientRect(hMainWnd, &appRect);
-    GetWindowRect(hDesktopWindow, &R);
+    AppWidth  = (appRect.right - appRect.left);
+    AppHeight = (appRect.bottom - appRect.top);
 
-    ZeroMemory(&cinfo, sizeof(CURSORINFO));
-    ZeroMemory(&iinfo, sizeof(ICONINFO));
+    ZeroMemory(&cinfo, sizeof(cinfo));
+    ZeroMemory(&iinfo, sizeof(iinfo));
     cinfo.cbSize = sizeof(cinfo);
     GetCursorInfo(&cinfo);
     GetIconInfo(cinfo.hCursor, &iinfo);
 
-     /* Create a memory DC compatible with client area DC.*/
-    HdcStrech = CreateCompatibleDC(desktopHdc);
-
-    /* Create a bitmap compatible with the client area DC.*/
-    HbmpStrech = CreateCompatibleBitmap(
-        desktopHdc,
-        R.right,
-        R.bottom);
+    targetRect = appRect;
+    ClientToScreen(hMainWnd, (POINT*)&targetRect.left);
+    ClientToScreen(hMainWnd, (POINT*)&targetRect.right);
 
-    /* Select our bitmap in memory DC and save the old one.*/
-    hOld = SelectObject (HdcStrech , HbmpStrech);
-    
-    /* Paint the screen bitmap to our in memory DC */
-    BitBlt(
-        HdcStrech,
-        0,
-        0,
-        R.right,
-        R.bottom,
-        desktopHdc,
-        0,
-        0,
-        SRCCOPY);
-        
-    /* Draw the mouse pointer in the right position */
-    DrawIcon(
-        HdcStrech ,
-        pMouse.x - iinfo.xHotspot, // - 10,
-        pMouse.y - iinfo.yHotspot, // - 10,
-        cinfo.hCursor);
+    if (bRecreateOffscreenDC || !hdcOffscreen)
+    {
+        bRecreateOffscreenDC = FALSE;
 
-    Width = (R.right - R.left);
-    Height = (R.bottom - R.top);
+        if(hdcOffscreen)
+        {
+            SelectObject(hdcOffscreen, hbmpOld);
+            DeleteObject (hbmpOffscreen);
+            DeleteDC(hdcOffscreen);
+        }
 
-    AppWidth = (appRect.right - appRect.left);
-    AppHeight = (appRect.bottom - appRect.top);
+        sourceWidth  = AppWidth / iZoom;
+        sourceHeight = AppHeight / iZoom;
 
-    blitAreaWidth = AppWidth / iZoom;
-    blitAreaHeight = AppHeight / iZoom;
+         /* Create a memory DC compatible with client area DC */
+        hdcOffscreen = CreateCompatibleDC(desktopHdc);
 
-    blitAreaX = (cp.x) - (blitAreaWidth /2);
-    blitAreaY = (cp.y) - (blitAreaHeight /2);
+        /* Create a bitmap compatible with the client area DC */
+        hbmpOffscreen = CreateCompatibleBitmap(
+            desktopHdc,
+            sourceWidth,
+            sourceHeight);
 
-    if (blitAreaX < 0)
-    {
-       blitAreaX = 0;
+        /* Select our bitmap in memory DC and save the old one */
+        hbmpOld = SelectObject(hdcOffscreen , hbmpOffscreen);
     }
 
-    if (blitAreaY < 0)
-    {
-       blitAreaY = 0;
-    }
+    GetWindowRect(hDesktopWindow, &sourceRect);
+    sourceRect.left = (cp.x) - (sourceWidth /2);
+    sourceRect.top = (cp.y) - (sourceHeight /2);
+    sourceRect.right = sourceRect.left + sourceWidth;
+    sourceRect.bottom = sourceRect.top + sourceHeight;
 
-    if (blitAreaX > (Width - blitAreaWidth))
-    {
-        blitAreaX = (Width - blitAreaWidth);
-    }
+    GetBestOverlapWithMonitors(&sourceRect);
 
-    if (blitAreaY > (Height - blitAreaHeight))
-    {
-        blitAreaY = (Height - blitAreaHeight);
-    }
+    /* Paint the screen bitmap to our in memory DC */
+    BitBlt(
+        hdcOffscreen,
+        0,
+        0,
+        sourceWidth,
+        sourceHeight,
+        desktopHdc,
+        sourceRect.left,
+        sourceRect.top,
+        rop);
 
-    if (bInvertColors)
+    if (IntersectRect(&intersectedRect, &sourceRect, &targetRect))
     {
-        rop = NOTSRCCOPY;
+        OffsetRect(&intersectedRect, -sourceRect.left, -sourceRect.top);
+        FillRect(hdcOffscreen, &intersectedRect, GetStockObject(DC_BRUSH));
     }
 
-    /* Blast the stretched image from memory DC to window DC.*/
+    /* Draw the mouse pointer in the right position */
+    DrawIcon(
+        hdcOffscreen ,
+        pMouse.x - iinfo.xHotspot - sourceRect.left, // - 10,
+        pMouse.y - iinfo.yHotspot - sourceRect.top, // - 10,
+        cinfo.hCursor);
+
+    /* Blast the stretched image from memory DC to window DC */
     StretchBlt(
         aDc,
         0,
         0,
         AppWidth,
         AppHeight,
-        HdcStrech,
-        blitAreaX,
-        blitAreaY,
-        blitAreaWidth,
-        blitAreaHeight,
-        rop | NOMIRRORBITMAP);
-        
-        
-    /* Cleanup.*/
+        hdcOffscreen,
+        0,
+        0,
+        sourceWidth,
+        sourceHeight,
+        SRCCOPY | NOMIRRORBITMAP);
+
+    /* Cleanup */
     if (iinfo.hbmMask)
         DeleteObject(iinfo.hbmMask);
     if (iinfo.hbmColor)
         DeleteObject(iinfo.hbmColor);
-    SelectObject (HdcStrech, hOld);
-    DeleteObject (HbmpStrech);
-    DeleteDC (HdcStrech);
     ReleaseDC(hDesktopWindow, desktopHdc);
 }
 
-/*
- *  FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
- *
- *  PURPOSE:  Processes messages for the main window.
- *
- *  WM_COMMAND - process the application menu
- *  WM_PAINT   - Paint the main window
- *  WM_DESTROY - post a quit message and return
- *
- */
+void HandleNotifyIconMessage(HWND hWnd, WPARAM wParam, LPARAM lParam)
+{
+    POINT pt;
+
+    switch(lParam)
+    {
+        case WM_LBUTTONUP:
+            PostMessage(hMainWnd, WM_COMMAND, IDM_OPTIONS, 0);
+            break;
+        case WM_RBUTTONUP:
+            GetCursorPos (&pt);
+            TrackPopupMenu(notifyMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
+            break;
+    }
+}
+
 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
 {
     int wmId;
@@ -320,136 +499,390 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
     {
         case WM_TIMER:
         {
-            POINT pNewMouse;
-            POINT pNewCaret;
-            POINT pNewFocus;
-            HWND hwnd1, hwnd2, hwnd3;
-            DWORD a, b;
-            RECT controlRect;
-
-            //Get current mouse position
-            GetCursorPos (&pNewMouse);
-
-            //Get caret position
-            hwnd1 = GetForegroundWindow ();
-            a = GetWindowThreadProcessId(hwnd1, NULL);
-            b = GetCurrentThreadId();
-            AttachThreadInput (a, b, TRUE);
-            hwnd2 = GetFocus();
-
-            GetCaretPos( &pNewCaret);
-            ClientToScreen (hwnd2, (LPPOINT) &pNewCaret);
-            AttachThreadInput (a, b, FALSE);
-
-            //Get current control focus
-            hwnd3 = GetFocus ();
-            GetWindowRect (hwnd3 , &controlRect);
-            pNewFocus.x = controlRect.left;
-            pNewFocus.y = controlRect.top;
-
-            //If mouse has moved ....
-            if (((pMouse.x != pNewMouse.x) || (pMouse.y != pNewMouse.y)) && bFollowMouse)
+            BOOL hasMoved = FALSE;
+
+            GUITHREADINFO guiInfo;
+            guiInfo.cbSize = sizeof(guiInfo);
+
+            GetGUIThreadInfo(0, &guiInfo);
+
+            if (bFollowMouse)
             {
-                //Update to new position
-                pMouse = pNewMouse;
-                cp = pNewMouse;
+                POINT pNewMouse;
+
+                //Get current mouse position
+                GetCursorPos (&pNewMouse);
+
+#define PointsAreEqual(pt1, pt2) (((pt1).x == (pt2).x) && ((pt1).y == (pt2).y))
+
+                //If mouse has moved ...
+                if (!PointsAreEqual(pMouse, pNewMouse))
+                {
+                    //Update to new position
+                    pMouse = pNewMouse;
+                    cp = pNewMouse;
+                    hasMoved = TRUE;
+                }
             }
-            else if (((pCaret.x != pNewCaret.x) || (pCaret.y != pNewCaret.y)) && bFollowCaret)
+
+            if(guiInfo.hwndActive != hMainWnd)
             {
-                //Update to new position
-                pCaret = pNewCaret;
-                cp = pNewCaret;
+                if (bFollowCaret)
+                {
+                    if (guiInfo.hwndCaret)
+                    {
+                        POINT ptCaret;
+                        ptCaret.x = (guiInfo.rcCaret.left + guiInfo.rcCaret.right) / 2;
+                        ptCaret.y = (guiInfo.rcCaret.top + guiInfo.rcCaret.bottom) / 2;
+
+                        if ((pCaretWnd != guiInfo.hwndCaret) || !PointsAreEqual(pCaret, ptCaret))
+                        {
+                            //Update to new position
+                            pCaret = ptCaret;
+                            pCaretWnd = guiInfo.hwndCaret;
+                            if(!hasMoved)
+                            {
+                                ClientToScreen (guiInfo.hwndCaret, (LPPOINT) &ptCaret);
+                                cp = ptCaret;
+                                hasMoved = TRUE;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        pCaretWnd = NULL;
+                    }
+                }
+
+                if (bFollowFocus)
+                {
+                    if(guiInfo.hwndFocus && !guiInfo.hwndCaret)
+                    {
+                        POINT ptFocus;
+                        RECT activeRect;
+
+                        //Get current control focus
+                        GetWindowRect(guiInfo.hwndFocus, &activeRect);
+                        ptFocus.x = (activeRect.left + activeRect.right) / 2;
+                        ptFocus.y = (activeRect.top + activeRect.bottom) / 2;
+
+                        if((guiInfo.hwndFocus != pFocusWnd) || !PointsAreEqual(pFocus, ptFocus))
+                        {
+                            //Update to new position
+                            pFocus = ptFocus;
+                            pFocusWnd = guiInfo.hwndFocus;
+                            if(!hasMoved)
+                            {
+                                cp = ptFocus;
+                                hasMoved = TRUE;
+                            }
+                        }
+                    }
+                    else
+                    {
+                        pFocusWnd = NULL;
+                    }
+                }
             }
-            else if (((pFocus.x != pNewFocus.x) || (pFocus.y != pNewFocus.y)) && bFollowFocus)
+
+            if(!hasMoved)
             {
-                //Update to new position
-                pFocus = pNewFocus;
-                cp = pNewFocus;
+                DWORD newTicks = GetTickCount();
+                DWORD elapsed = (newTicks - lastTicks);
+                if(elapsed > REPAINT_SPEED)
+                {
+                    hasMoved = TRUE;
+                }
             }
-            Refresh();
+
+            if(hasMoved)
+            {
+                lastTicks = GetTickCount();
+                Refresh();
+            }
+
+            return 0;
         }
-        break;
-    case WM_COMMAND:
-        wmId    = LOWORD(wParam);
-        // Parse the menu selections:
-        switch (wmId)
+
+        case WM_COMMAND:
         {
-            case IDM_OPTIONS:
-                DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOGOPTIONS), hWnd, OptionsProc);
-                break;
-            case IDM_ABOUT:
-                DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutProc);
-                break;
-            case IDM_EXIT:
-                DestroyWindow(hWnd);
-                break;
-            default:
-                return DefWindowProc(hWnd, message, wParam, lParam);
+            wmId = LOWORD(wParam);
+            /* Parse the menu selections */
+            switch (wmId)
+            {
+                case IDM_OPTIONS:
+                    if(bOptionsDialog)
+                    {
+                        ShowWindow(hOptionsDialog, SW_HIDE);
+                    }
+                    else
+                    {
+                        ShowWindow(hOptionsDialog, SW_SHOW);
+                    }
+                    break;
+                case IDM_ABOUT:
+                    DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutProc);
+                    break;
+                case IDM_EXIT:
+                    DestroyWindow(hWnd);
+                    break;
+                default:
+                    return DefWindowProc(hWnd, message, wParam, lParam);
+            }
+            return 0;
         }
-        break;
-   case WM_PAINT:
+
+        case WM_PAINT:
         {
             PAINTSTRUCT PaintStruct;
             HDC dc;
             dc = BeginPaint(hWnd, &PaintStruct);
             Draw(dc);
             EndPaint(hWnd, &PaintStruct);
+            return 0;
+        }
+
+        case WM_CONTEXTMENU:
+            TrackPopupMenu(notifyMenu, 0, GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), 0, hWnd, NULL);
+            return 0;
+
+        case WM_LBUTTONDOWN:
+        {
+            RECT rc;
+            POINT pt;
+            SetCapture(hWnd);
+
+            GetCursorPos(&pt);
+            GetWindowRect(hWnd, &rc);
+            ptDragOffset.x = pt.x - rc.left;
+            ptDragOffset.y = pt.y - rc.top;
+
+            nearEdge = AppBarConfig.uEdge;
+
+            break;
+        }
+        case WM_MOUSEMOVE:
+            if(GetCapture() == hWnd)
+            {
+                RECT rc;
+                POINT pt;
+                RECT rcWorkArea;
+                SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
+                GetCursorPos(&pt);
+                GetWindowRect(hWnd, &rc);
+
+                if(AppBarConfig.uEdge>=0)
+                {
+                    if (pt.x >= rcWorkArea.left && pt.x <= rcWorkArea.right &&
+                        pt.y >= rcWorkArea.top && pt.y <= rcWorkArea.bottom)
+                    {
+                        AttachAppBar(-2);
+
+                        // Fixup offset
+                        GetWindowRect(hWnd, &rc);
+                        ptDragOffset.x = (rc.right-rc.left)/2;
+                        ptDragOffset.y = 2;
+
+                        rc.left = pt.x - ptDragOffset.x;
+                        rc.top = pt.y - ptDragOffset.y;
+
+                        SetWindowPos(hWnd, HWND_TOPMOST, rc.left, rc.top, 0, 0, SWP_NOSIZE);
+                    }
+                }
+                else
+                {
+                    if(pt.x <= rcWorkArea.left+8 && nearEdge != ABE_LEFT)
+                    {
+                        AttachAppBar(ABE_LEFT);
+                        nearEdge = ABE_LEFT;
+                    }
+                    else if(pt.y <= rcWorkArea.top+8 && nearEdge != ABE_TOP)
+                    {
+                        AttachAppBar(ABE_TOP);
+                        nearEdge = ABE_TOP;
+                    }
+                    else if(pt.x >= rcWorkArea.right-8 && nearEdge != ABE_RIGHT)
+                    {
+                        AttachAppBar(ABE_RIGHT);
+                        nearEdge = ABE_RIGHT;
+                    }
+                    else if(pt.y >= rcWorkArea.bottom-8 && nearEdge != ABE_BOTTOM)
+                    {
+                        AttachAppBar(ABE_BOTTOM);
+                        nearEdge = ABE_BOTTOM;
+                    }
+                    else
+                    {
+                        rc.left = pt.x - ptDragOffset.x;
+                        rc.top = pt.y - ptDragOffset.y;
+
+                        SetWindowPos(hWnd, HWND_TOPMOST, rc.left, rc.top, 0, 0, SWP_NOSIZE);
+                        nearEdge = -1;
+                    }
+                }
+
+                pMouse = pt;
+                Refresh();
+            }
+            break;
+        case WM_LBUTTONUP:
+            if(GetCapture() == hWnd)
+            {
+                if (AppBarConfig.uEdge>=0)
+                    DoAppBarStuff(ABM_GETSTATE);
+                else
+                    SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+                ReleaseCapture();
+            }
+            break;
+
+        case WM_SIZE:
+            if(AppBarConfig.uEdge>=0) DoAppBarStuff(ABM_SETPOS);
+            /* fallthrough */
+        case WM_DISPLAYCHANGE:
+            bRecreateOffscreenDC = TRUE;
+            Refresh();
+            break;
+
+        case WM_ERASEBKGND:
+            // handle WM_ERASEBKGND by simply returning non-zero because we did all the drawing in WM_PAINT.
+            return 0;
+
+        case WM_DESTROY:
+        {
+            if (AppBarConfig.uEdge >= 0)
+                DoAppBarStuff(ABM_REMOVE);
+
+            KillTimer(hWnd, 1);
+
+            /* Save settings to registry */
+            SaveSettings();
+
+            /* Cleanup notification icon */
+            ZeroMemory(&nid, sizeof(nid));
+            nid.cbSize = sizeof(nid);
+            nid.uFlags = NIF_MESSAGE;
+            nid.hWnd = hWnd;
+            nid.uCallbackMessage = APPMSG_NOTIFYICON;
+            Shell_NotifyIcon(NIM_DELETE, &nid);
+            DestroyIcon(notifyIcon);
+
+            DestroyWindow(hOptionsDialog);
+
+            PostQuitMessage(0);
+            return 0;
         }
-      break;
-   case WM_ERASEBKGND:
-        //handle WM_ERASEBKGND by simply returning non-zero because we did all the drawing in WM_PAINT.
-       break;
-    case WM_DESTROY:
-        //Save settings to registry
-        SaveSettings ();
-        KillTimer (hWnd , 1);
-        PostQuitMessage(0);
-        break;
-    case WM_CREATE:
-            //Load settings from registry
-            LoadSettings ();
-
-            //Get the desktop window
+
+        case WM_CREATE:
+        {
+            HMENU tempMenu;
+
+            /* Get the desktop window */
             hDesktopWindow = GetDesktopWindow();
 
-            //Set the timer
-            SetTimer (hWnd , 1, REPAINT_SPEED , NULL);
+            /* Set the timer */
+            SetTimer(hWnd, 1, TIMER_SPEED, NULL);
+
+            /* Notification icon */
+            notifyIcon = (HICON)LoadImage(hInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0);
+
+            ZeroMemory(&nid, sizeof(nid));
+            nid.cbSize = sizeof(nid);
+            nid.uFlags = NIF_ICON | NIF_MESSAGE;
+            nid.hWnd = hWnd;
+            nid.uCallbackMessage = APPMSG_NOTIFYICON;
+            nid.hIcon = notifyIcon;
+            Shell_NotifyIcon(NIM_ADD, &nid);
+
+            tempMenu = LoadMenu(hInst, MAKEINTRESOURCE(IDC_MAGNIFIER));
+            notifyMenu = GetSubMenu(tempMenu, 0);
+            RemoveMenu(tempMenu, 0, MF_BYPOSITION);
+            DestroyMenu(tempMenu);
+            return 0;
+        }
+
+        case APPMSG_APPBAR:
+        {
+            switch (wParam)
+            {
+                case ABN_STATECHANGE:
+                    DoAppBarStuff(ABM_GETSTATE);
+                    break;
+                case ABN_POSCHANGED:
+                    DoAppBarStuff(ABM_SETPOS);
+                    break;
+                case ABN_FULLSCREENAPP:
+                {
+                    if(!lParam)
+                    {
+                        DoAppBarStuff(ABM_GETSTATE);
+                        break;
+                    }
+
+                    SetWindowPos(hMainWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE);
+                    break;
+                }
+               case ABN_WINDOWARRANGE:
+                    if(lParam)
+                        ShowWindow(hMainWnd, SW_HIDE);
+                    else
+                        ShowWindow(hMainWnd, SW_SHOW);
+                    break;
+            }
+            return 0;
+        }
+
+        case APPMSG_NOTIFYICON:
+            HandleNotifyIconMessage(hWnd, wParam, lParam);
+            return 0;
+
+        case WM_ACTIVATE:
+            if(AppBarConfig.uEdge>=0) DoAppBarStuff(ABM_ACTIVATE);
+            break;
+
+        case WM_WINDOWPOSCHANGED:
+            if(AppBarConfig.uEdge>=0) DoAppBarStuff(ABM_WINDOWPOSCHANGED);
+            Refresh();
+            break;
+
+        default:
             break;
-    default:
-        return DefWindowProc(hWnd, message, wParam, lParam);
     }
-    return 0;
+
+    return DefWindowProc(hWnd, message, wParam, lParam);
 }
 
-// Message handler for about box.
 INT_PTR CALLBACK AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
 {
     UNREFERENCED_PARAMETER(lParam);
     switch (message)
     {
-    case WM_INITDIALOG:
-        return (INT_PTR)TRUE;
-
-    case WM_COMMAND:
-        if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
-        {
-            EndDialog(hDlg, LOWORD(wParam));
+        case WM_INITDIALOG:
             return (INT_PTR)TRUE;
-        }
-        break;
+
+        case WM_COMMAND:
+            if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
+            {
+                EndDialog(hDlg, LOWORD(wParam));
+                return (INT_PTR)TRUE;
+            }
+            break;
     }
+
     return (INT_PTR)FALSE;
 }
 
-// Message handler for options box.
 INT_PTR CALLBACK OptionsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
 {
     UNREFERENCED_PARAMETER(lParam);
     switch (message)
     {
+        case WM_SHOWWINDOW:
+            bOptionsDialog = wParam;
+            break;
         case WM_INITDIALOG:
         {
-            //Add the zoom items....
+            // Add the zoom items...
             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("1"));
             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("2"));
             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("3"));
@@ -459,7 +892,6 @@ INT_PTR CALLBACK OptionsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPar
             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("7"));
             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("8"));
 
-            //
             SendDlgItemMessage(hDlg, IDC_ZOOM, CB_SETCURSEL, iZoom - 1, 0);
 
             if (bFollowMouse)
@@ -482,60 +914,66 @@ INT_PTR CALLBACK OptionsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPar
 
             return (INT_PTR)TRUE;
         }
+
     case WM_COMMAND:
-        switch(LOWORD(wParam))
+        switch (LOWORD(wParam))
         {
             case IDOK:
             case IDCANCEL:
-                EndDialog(hDlg, LOWORD(wParam));
+                ShowWindow(hDlg, SW_HIDE);
                 return (INT_PTR)TRUE;
 
             case IDC_BUTTON_HELP:
-                /* unimplemented */
+                /* Unimplemented */
                 MessageBox(hDlg , TEXT("Magnifier help not available yet!") , TEXT("Help") , MB_OK);
                 break;
             case IDC_ZOOM:
-                if(HIWORD(wParam) == CBN_SELCHANGE)
+                if (HIWORD(wParam) == CBN_SELCHANGE)
                 {
                     HWND hCombo = GetDlgItem(hDlg,IDC_ZOOM);
+                    TCHAR currentZoomValue[2] = TEXT("");
 
-                    /* Get index of current selection and the text of that selection. */
-                    iZoom = SendMessage( hCombo, CB_GETCURSEL, (WPARAM) wParam, (LPARAM) lParam ) + 1;
+                    /* Get index of current selection and the text of that selection */
+                    int currentSelectionIndex = ComboBox_GetCurSel(hCombo);  
+                    ComboBox_GetLBText(hCombo, currentSelectionIndex, currentZoomValue);
+                    iZoom = _ttoi(currentZoomValue);
 
-                    //Update the magnigier UI
-                    Refresh ();
+                    /* Trigger the Draw function to rezoom (which will be set false automatically after rezooming) */
+                    bRecreateOffscreenDC = TRUE;
+
+                    /* Update the magnifier UI */
+                    Refresh();
                 }
                 break;
             case IDC_INVERTCOLORSCHECK:
-                bInvertColors = IsDlgButtonChecked (hDlg, IDC_INVERTCOLORSCHECK);
-                Refresh ();
+                bInvertColors = IsDlgButtonChecked(hDlg, IDC_INVERTCOLORSCHECK);
+                Refresh();
                 break;
             case IDC_FOLLOWMOUSECHECK:
-                bFollowMouse = IsDlgButtonChecked (hDlg, IDC_FOLLOWMOUSECHECK);
+                bFollowMouse = IsDlgButtonChecked(hDlg, IDC_FOLLOWMOUSECHECK);
                 break;
             case IDC_FOLLOWKEYBOARDCHECK:
-                bFollowFocus = IsDlgButtonChecked (hDlg, IDC_FOLLOWKEYBOARDCHECK);
+                bFollowFocus = IsDlgButtonChecked(hDlg, IDC_FOLLOWKEYBOARDCHECK);
                 break;
             case IDC_FOLLOWTEXTEDITINGCHECK:
-                bFollowCaret = IsDlgButtonChecked (hDlg, IDC_FOLLOWTEXTEDITINGCHECK);
+                bFollowCaret = IsDlgButtonChecked(hDlg, IDC_FOLLOWTEXTEDITINGCHECK);
                 break;
             case IDC_STARTMINIMIZEDCHECK:
-                bStartMinimized = IsDlgButtonChecked (hDlg, IDC_STARTMINIMIZEDCHECK);
+                bStartMinimized = IsDlgButtonChecked(hDlg, IDC_STARTMINIMIZEDCHECK);
                 break;
             case IDC_SHOWMAGNIFIER:
-                bShowMagnifier = IsDlgButtonChecked (hDlg, IDC_SHOWMAGNIFIERCHECK);
-                if (bShowMagnifier){
+                bShowMagnifier = IsDlgButtonChecked(hDlg, IDC_SHOWMAGNIFIERCHECK);
+                if (bShowMagnifier)
                     ShowWindow (hMainWnd , SW_SHOW);
-                }else{
+                else
                     ShowWindow (hMainWnd , SW_HIDE);
-                }
                 break;
         }
     }
+
     return (INT_PTR)FALSE;
 }
 
-// Message handler for warning box.
 INT_PTR CALLBACK WarningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
 {
     UNREFERENCED_PARAMETER(lParam);
@@ -544,11 +982,12 @@ INT_PTR CALLBACK WarningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPar
     {
         case WM_INITDIALOG:
             return (INT_PTR)TRUE;
+
         case WM_COMMAND:
-            switch(LOWORD(wParam))
+            switch (LOWORD(wParam))
             {
                 case IDC_SHOWWARNINGCHECK:
-                    bShowWarning = !IsDlgButtonChecked (hDlg, IDC_SHOWWARNINGCHECK);
+                    bShowWarning = !IsDlgButtonChecked(hDlg, IDC_SHOWWARNINGCHECK);
                     break;
             }
             if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
@@ -558,5 +997,6 @@ INT_PTR CALLBACK WarningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lPar
             }
             break;
     }
+
     return (INT_PTR)FALSE;
 }