[MAGNIFY]
[reactos.git] / reactos / base / applications / magnify / magnifier.c
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 * COPYRIGHT: Copyright 2007 Marc Piulachs <marc.piulachs@codexchange.net>
7 *
8 */
9
10 /* TODO: AppBar */
11 #include "magnifier.h"
12
13 #include <winbase.h>
14 #include <winuser.h>
15 #include <wingdi.h>
16 #include <winnls.h>
17
18 #include "resource.h"
19
20 const TCHAR szWindowClass[] = TEXT("MAGNIFIER");
21
22 #define MAX_LOADSTRING 100
23
24 /* Global Variables */
25 HINSTANCE hInst;
26 HWND hMainWnd;
27
28 TCHAR szTitle[MAX_LOADSTRING];
29
30 #define TIMER_SPEED 1
31 #define REPAINT_SPEED 100
32
33 DWORD lastTicks = 0;
34
35 HWND hDesktopWindow = NULL;
36
37 /* Current magnified area */
38 POINT cp;
39
40 /* Last positions */
41 POINT pMouse;
42 POINT pCaret;
43 POINT pFocus;
44
45 ATOM MyRegisterClass(HINSTANCE hInstance);
46 BOOL InitInstance(HINSTANCE, int);
47 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
48 INT_PTR CALLBACK AboutProc(HWND, UINT, WPARAM, LPARAM);
49 INT_PTR CALLBACK OptionsProc(HWND, UINT, WPARAM, LPARAM);
50 INT_PTR CALLBACK WarningProc(HWND, UINT, WPARAM, LPARAM);
51
52 int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
53 {
54 MSG msg;
55 HACCEL hAccelTable;
56
57 UNREFERENCED_PARAMETER(hPrevInstance);
58 UNREFERENCED_PARAMETER(lpCmdLine);
59
60 switch (GetUserDefaultUILanguage())
61 {
62 case MAKELANGID(LANG_HEBREW, SUBLANG_DEFAULT):
63 SetProcessDefaultLayout(LAYOUT_RTL);
64 break;
65
66 default:
67 break;
68 }
69
70 /* Initialize global strings */
71 LoadString(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
72 MyRegisterClass(hInstance);
73
74 /* Perform application initialization */
75 if (!InitInstance(hInstance, nCmdShow))
76 return FALSE;
77
78 hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_MAGNIFIER));
79
80 /* Main message loop */
81 while (GetMessage(&msg, NULL, 0, 0))
82 {
83 if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
84 {
85 TranslateMessage(&msg);
86 DispatchMessage(&msg);
87 }
88 }
89
90 return (int) msg.wParam;
91 }
92
93 ATOM MyRegisterClass(HINSTANCE hInstance)
94 {
95 WNDCLASS wc;
96
97 wc.style = CS_HREDRAW | CS_VREDRAW;
98 wc.lpfnWndProc = WndProc;
99 wc.cbClsExtra = 0;
100 wc.cbWndExtra = 0;
101 wc.hInstance = hInstance;
102 wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
103 wc.hCursor = LoadCursor(NULL, IDC_ARROW);
104 wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
105 wc.lpszMenuName = MAKEINTRESOURCE(IDC_MAGNIFIER);
106 wc.lpszClassName = szWindowClass;
107
108 return RegisterClass(&wc);
109 }
110
111 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
112 {
113 RECT rcWorkArea;
114 hInst = hInstance; // Store instance handle in our global variable
115
116 SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
117
118 /* Create the Window */
119 hMainWnd = CreateWindowEx(
120 WS_EX_TOPMOST | WS_EX_PALETTEWINDOW,
121 szWindowClass,
122 szTitle,
123 WS_OVERLAPPEDWINDOW,
124 CW_USEDEFAULT,
125 CW_USEDEFAULT,
126 (rcWorkArea.right - rcWorkArea.left) * 2 / 3,
127 200,
128 NULL,
129 NULL,
130 hInstance,
131 NULL);
132
133 if (!hMainWnd)
134 return FALSE;
135
136 ShowWindow(hMainWnd, bStartMinimized ? SW_MINIMIZE : nCmdShow);
137 UpdateWindow(hMainWnd);
138
139 if (bShowWarning)
140 DialogBox(hInstance, MAKEINTRESOURCE(IDD_WARNINGDIALOG), hMainWnd, WarningProc);
141
142 return TRUE;
143 }
144
145 void Refresh()
146 {
147 if (!IsIconic(hMainWnd))
148 {
149 /* Invalidate the client area forcing a WM_PAINT message */
150 InvalidateRgn(hMainWnd, NULL, TRUE);
151 }
152 }
153
154 void GetBestOverlapWithMonitors(LPRECT rect)
155 {
156 int rcLeft, rcTop;
157 int rcWidth, rcHeight;
158 RECT rcMon;
159 HMONITOR hMon = MonitorFromRect(rect, MONITOR_DEFAULTTONEAREST);
160 MONITORINFO info;
161 info.cbSize = sizeof(info);
162
163 GetMonitorInfo(hMon, &info);
164
165 rcMon = info.rcMonitor;
166
167 rcLeft = rect->left;
168 rcTop = rect->top;
169 rcWidth = (rect->right - rect->left);
170 rcHeight = (rect->bottom - rect->top);
171
172 if (rcLeft < rcMon.left)
173 rcLeft = rcMon.left;
174
175 if (rcTop < rcMon.top)
176 rcTop = rcMon.top;
177
178 if (rcLeft > (rcMon.right - rcWidth))
179 rcLeft = (rcMon.right - rcWidth);
180
181 if (rcTop > (rcMon.bottom - rcHeight))
182 rcTop = (rcMon.bottom - rcHeight);
183
184 OffsetRect(rect, (rcLeft-rect->left), (rcTop-rect->top));
185 }
186
187 void Draw(HDC aDc)
188 {
189 HDC desktopHdc = NULL;
190 HDC HdcStretch;
191 HANDLE hOld;
192 HBITMAP HbmpStrech;
193
194 RECT sourceRect, intersectedRect;
195 RECT targetRect, appRect;
196 DWORD rop = SRCCOPY;
197 CURSORINFO cinfo;
198 ICONINFO iinfo;
199
200 int AppWidth, AppHeight;
201 LONG blitAreaWidth, blitAreaHeight;
202
203 if (bInvertColors)
204 rop = NOTSRCCOPY;
205
206 desktopHdc = GetDC(0);
207
208 GetClientRect(hMainWnd, &appRect);
209 GetWindowRect(hDesktopWindow, &sourceRect);
210
211 ZeroMemory(&cinfo, sizeof(cinfo));
212 ZeroMemory(&iinfo, sizeof(iinfo));
213 cinfo.cbSize = sizeof(cinfo);
214 GetCursorInfo(&cinfo);
215 GetIconInfo(cinfo.hCursor, &iinfo);
216
217 targetRect = appRect;
218 ClientToScreen(hMainWnd, (POINT*)&targetRect.left);
219 ClientToScreen(hMainWnd, (POINT*)&targetRect.right);
220
221 AppWidth = (appRect.right - appRect.left);
222 AppHeight = (appRect.bottom - appRect.top);
223
224 blitAreaWidth = AppWidth / iZoom;
225 blitAreaHeight = AppHeight / iZoom;
226
227 sourceRect.left = (cp.x) - (blitAreaWidth /2);
228 sourceRect.top = (cp.y) - (blitAreaHeight /2);
229 sourceRect.right = sourceRect.left + blitAreaWidth;
230 sourceRect.bottom = sourceRect.top + blitAreaHeight;
231
232 GetBestOverlapWithMonitors(&sourceRect);
233
234 /* Create a memory DC compatible with client area DC */
235 HdcStretch = CreateCompatibleDC(desktopHdc);
236
237 /* Create a bitmap compatible with the client area DC */
238 HbmpStrech = CreateCompatibleBitmap(
239 desktopHdc,
240 blitAreaWidth,
241 blitAreaHeight);
242
243 /* Select our bitmap in memory DC and save the old one */
244 hOld = SelectObject(HdcStretch , HbmpStrech);
245
246 /* Paint the screen bitmap to our in memory DC */
247 BitBlt(
248 HdcStretch,
249 0,
250 0,
251 blitAreaWidth,
252 blitAreaHeight,
253 desktopHdc,
254 sourceRect.left,
255 sourceRect.top,
256 rop);
257
258 if (IntersectRect(&intersectedRect, &sourceRect, &targetRect))
259 {
260 OffsetRect(&intersectedRect, -sourceRect.left, -sourceRect.top);
261 FillRect(HdcStretch, &intersectedRect, GetStockObject(DC_BRUSH));
262 }
263
264 /* Draw the mouse pointer in the right position */
265 DrawIcon(
266 HdcStretch ,
267 pMouse.x - iinfo.xHotspot - sourceRect.left, // - 10,
268 pMouse.y - iinfo.yHotspot - sourceRect.top, // - 10,
269 cinfo.hCursor);
270
271 /* Blast the stretched image from memory DC to window DC */
272 StretchBlt(
273 aDc,
274 0,
275 0,
276 AppWidth,
277 AppHeight,
278 HdcStretch,
279 0,
280 0,
281 blitAreaWidth,
282 blitAreaHeight,
283 SRCCOPY | NOMIRRORBITMAP);
284
285 /* Cleanup */
286 if (iinfo.hbmMask)
287 DeleteObject(iinfo.hbmMask);
288 if (iinfo.hbmColor)
289 DeleteObject(iinfo.hbmColor);
290 SelectObject(HdcStretch, hOld);
291 DeleteObject (HbmpStrech);
292 DeleteDC(HdcStretch);
293 ReleaseDC(hDesktopWindow, desktopHdc);
294 }
295
296 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
297 {
298 int wmId;
299
300 switch (message)
301 {
302 case WM_TIMER:
303 {
304 BOOL hasMoved = FALSE;
305
306 if (bFollowMouse)
307 {
308 POINT pNewMouse;
309
310 //Get current mouse position
311 GetCursorPos (&pNewMouse);
312
313 //If mouse has moved ...
314 if (((pMouse.x != pNewMouse.x) || (pMouse.y != pNewMouse.y)))
315 {
316 //Update to new position
317 pMouse = pNewMouse;
318 cp = pNewMouse;
319 hasMoved = TRUE;
320 }
321 }
322
323 if (bFollowCaret && !hasMoved)
324 {
325 POINT pNewCaret;
326 HWND hwnd2;
327
328 //Get caret position
329 HWND hwnd1 = GetForegroundWindow ();
330 DWORD a = GetWindowThreadProcessId(hwnd1, NULL);
331 DWORD b = GetCurrentThreadId();
332 AttachThreadInput (a, b, TRUE);
333 hwnd2 = GetFocus();
334
335 GetCaretPos( &pNewCaret);
336 ClientToScreen (hwnd2, (LPPOINT) &pNewCaret);
337 AttachThreadInput (a, b, FALSE);
338
339 if (((pCaret.x != pNewCaret.x) || (pCaret.y != pNewCaret.y)))
340 {
341 //Update to new position
342 pCaret = pNewCaret;
343 cp = pNewCaret;
344 hasMoved = TRUE;
345 }
346 }
347
348 if (bFollowFocus && !hasMoved)
349 {
350 POINT pNewFocus;
351 RECT controlRect;
352
353 //Get current control focus
354 HWND hwnd3 = GetFocus ();
355 GetWindowRect (hwnd3 , &controlRect);
356 pNewFocus.x = controlRect.left;
357 pNewFocus.y = controlRect.top;
358
359 if(((pFocus.x != pNewFocus.x) || (pFocus.y != pNewFocus.y)))
360 {
361 //Update to new position
362 pFocus = pNewFocus;
363 cp = pNewFocus;
364 hasMoved = TRUE;
365 }
366 }
367
368 if(!hasMoved)
369 {
370 DWORD newTicks = GetTickCount();
371 DWORD elapsed = (newTicks - lastTicks);
372 if(elapsed > REPAINT_SPEED)
373 {
374 hasMoved = TRUE;
375 }
376 }
377
378 if(hasMoved)
379 {
380 lastTicks = GetTickCount();
381 Refresh();
382 }
383 }
384 break;
385
386 case WM_COMMAND:
387 {
388 wmId = LOWORD(wParam);
389 /* Parse the menu selections */
390 switch (wmId)
391 {
392 case IDM_OPTIONS:
393 DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOGOPTIONS), hWnd, OptionsProc);
394 break;
395 case IDM_ABOUT:
396 DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, AboutProc);
397 break;
398 case IDM_EXIT:
399 DestroyWindow(hWnd);
400 break;
401 default:
402 return DefWindowProc(hWnd, message, wParam, lParam);
403 }
404 break;
405 }
406
407 case WM_PAINT:
408 {
409 PAINTSTRUCT PaintStruct;
410 HDC dc;
411 dc = BeginPaint(hWnd, &PaintStruct);
412 Draw(dc);
413 EndPaint(hWnd, &PaintStruct);
414 break;
415 }
416
417 case WM_ERASEBKGND:
418 // handle WM_ERASEBKGND by simply returning non-zero because we did all the drawing in WM_PAINT.
419 break;
420
421 case WM_DESTROY:
422 /* Save settings to registry */
423 SaveSettings();
424 KillTimer(hWnd , 1);
425 PostQuitMessage(0);
426 break;
427
428 case WM_CREATE:
429 /* Load settings from registry */
430 LoadSettings();
431
432 /* Get the desktop window */
433 hDesktopWindow = GetDesktopWindow();
434
435 /* Set the timer */
436 SetTimer (hWnd , 1, TIMER_SPEED , NULL);
437 break;
438
439 default:
440 return DefWindowProc(hWnd, message, wParam, lParam);
441 }
442
443 return 0;
444 }
445
446 INT_PTR CALLBACK AboutProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
447 {
448 UNREFERENCED_PARAMETER(lParam);
449 switch (message)
450 {
451 case WM_INITDIALOG:
452 return (INT_PTR)TRUE;
453
454 case WM_COMMAND:
455 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
456 {
457 EndDialog(hDlg, LOWORD(wParam));
458 return (INT_PTR)TRUE;
459 }
460 break;
461 }
462
463 return (INT_PTR)FALSE;
464 }
465
466 INT_PTR CALLBACK OptionsProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
467 {
468 UNREFERENCED_PARAMETER(lParam);
469 switch (message)
470 {
471 case WM_INITDIALOG:
472 {
473 // Add the zoom items...
474 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("1"));
475 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("2"));
476 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("3"));
477 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("4"));
478 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("5"));
479 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("6"));
480 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("7"));
481 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_ADDSTRING, 0, (LPARAM)("8"));
482
483 SendDlgItemMessage(hDlg, IDC_ZOOM, CB_SETCURSEL, iZoom - 1, 0);
484
485 if (bFollowMouse)
486 SendDlgItemMessage(hDlg,IDC_FOLLOWMOUSECHECK,BM_SETCHECK , wParam ,0);
487
488 if (bFollowFocus)
489 SendDlgItemMessage(hDlg,IDC_FOLLOWKEYBOARDCHECK,BM_SETCHECK , wParam ,0);
490
491 if (bFollowCaret)
492 SendDlgItemMessage(hDlg,IDC_FOLLOWTEXTEDITINGCHECK,BM_SETCHECK , wParam ,0);
493
494 if (bInvertColors)
495 SendDlgItemMessage(hDlg,IDC_INVERTCOLORSCHECK,BM_SETCHECK , wParam ,0);
496
497 if (bStartMinimized)
498 SendDlgItemMessage(hDlg,IDC_STARTMINIMIZEDCHECK,BM_SETCHECK , wParam ,0);
499
500 if (bShowMagnifier)
501 SendDlgItemMessage(hDlg,IDC_SHOWMAGNIFIERCHECK,BM_SETCHECK , wParam ,0);
502
503 return (INT_PTR)TRUE;
504 }
505
506 case WM_COMMAND:
507 switch (LOWORD(wParam))
508 {
509 case IDOK:
510 case IDCANCEL:
511 EndDialog(hDlg, LOWORD(wParam));
512 return (INT_PTR)TRUE;
513
514 case IDC_BUTTON_HELP:
515 /* Unimplemented */
516 MessageBox(hDlg , TEXT("Magnifier help not available yet!") , TEXT("Help") , MB_OK);
517 break;
518 case IDC_ZOOM:
519 if (HIWORD(wParam) == CBN_SELCHANGE)
520 {
521 HWND hCombo = GetDlgItem(hDlg,IDC_ZOOM);
522
523 /* Get index of current selection and the text of that selection */
524 iZoom = SendMessage( hCombo, CB_GETCURSEL, (WPARAM) wParam, (LPARAM) lParam ) + 1;
525
526 /* Update the magnifier UI */
527 Refresh();
528 }
529 break;
530 case IDC_INVERTCOLORSCHECK:
531 bInvertColors = IsDlgButtonChecked(hDlg, IDC_INVERTCOLORSCHECK);
532 Refresh();
533 break;
534 case IDC_FOLLOWMOUSECHECK:
535 bFollowMouse = IsDlgButtonChecked(hDlg, IDC_FOLLOWMOUSECHECK);
536 break;
537 case IDC_FOLLOWKEYBOARDCHECK:
538 bFollowFocus = IsDlgButtonChecked(hDlg, IDC_FOLLOWKEYBOARDCHECK);
539 break;
540 case IDC_FOLLOWTEXTEDITINGCHECK:
541 bFollowCaret = IsDlgButtonChecked(hDlg, IDC_FOLLOWTEXTEDITINGCHECK);
542 break;
543 case IDC_STARTMINIMIZEDCHECK:
544 bStartMinimized = IsDlgButtonChecked(hDlg, IDC_STARTMINIMIZEDCHECK);
545 break;
546 case IDC_SHOWMAGNIFIER:
547 bShowMagnifier = IsDlgButtonChecked(hDlg, IDC_SHOWMAGNIFIERCHECK);
548 if (bShowMagnifier)
549 ShowWindow (hMainWnd , SW_SHOW);
550 else
551 ShowWindow (hMainWnd , SW_HIDE);
552 break;
553 }
554 }
555
556 return (INT_PTR)FALSE;
557 }
558
559 INT_PTR CALLBACK WarningProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
560 {
561 UNREFERENCED_PARAMETER(lParam);
562
563 switch (message)
564 {
565 case WM_INITDIALOG:
566 return (INT_PTR)TRUE;
567
568 case WM_COMMAND:
569 switch (LOWORD(wParam))
570 {
571 case IDC_SHOWWARNINGCHECK:
572 bShowWarning = !IsDlgButtonChecked(hDlg, IDC_SHOWWARNINGCHECK);
573 break;
574 }
575 if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
576 {
577 EndDialog(hDlg, LOWORD(wParam));
578 return (INT_PTR)TRUE;
579 }
580 break;
581 }
582
583 return (INT_PTR)FALSE;
584 }