ee9c10677cc705680946861817b549e90b37749b
[reactos.git] / win32ss / user / user32 / controls / appswitch.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: win32ss/user/user32/controls/appswitch.c
5 * PURPOSE: app switching functionality
6 * PROGRAMMERS: Johannes Anderwald (johannes.anderwald@reactos.org)
7 * David Quintana (gigaherz@gmail.com)
8 * Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
9 */
10
11 //
12 // TODO:
13 // Move to Win32k.
14 // Add registry support.
15 //
16 //
17
18 #include <user32.h>
19
20 WINE_DEFAULT_DEBUG_CHANNEL(user32);
21
22 #define DIALOG_MARGIN 8 // margin of dialog contents
23
24 #define CX_ICON 32 // width of icon
25 #define CY_ICON 32 // height of icon
26 #define ICON_MARGIN 4 // margin width around an icon
27
28 #define CX_ITEM (CX_ICON + 2 * ICON_MARGIN)
29 #define CY_ITEM (CY_ICON + 2 * ICON_MARGIN)
30 #define ITEM_MARGIN 4 // margin width around an item
31
32 #define CX_ITEM_SPACE (CX_ITEM + 2 * ITEM_MARGIN)
33 #define CY_ITEM_SPACE (CY_ITEM + 2 * ITEM_MARGIN)
34
35 #define CY_TEXT_MARGIN 4 // margin height around text
36
37 // limit the number of windows shown in the alt-tab window
38 // 120 windows results in (12*40) by (10*40) pixels worth of icons.
39 #define MAX_WINDOWS 120
40
41 // Global variables
42 HWND switchdialog = NULL;
43 HFONT dialogFont;
44 int selectedWindow = 0;
45 BOOL isOpen = FALSE;
46
47 int fontHeight=0;
48
49 WCHAR windowText[1024];
50
51 HWND windowList[MAX_WINDOWS];
52 HICON iconList[MAX_WINDOWS];
53 int windowCount = 0;
54
55 int cxBorder, cyBorder;
56 int nItems, nCols, nRows;
57 int itemsW, itemsH;
58 int totalW, totalH;
59 int xOffset, yOffset;
60 POINT ptStart;
61
62 int nShift = 0;
63
64 BOOL Esc = FALSE;
65
66 BOOL CoolSwitch = TRUE;
67 int CoolSwitchRows = 3;
68 int CoolSwitchColumns = 7;
69
70 // window style
71 const DWORD Style = WS_POPUP | WS_BORDER | WS_DISABLED;
72 const DWORD ExStyle = WS_EX_TOPMOST | WS_EX_DLGMODALFRAME | WS_EX_TOOLWINDOW;
73
74 DWORD wtodw(const WCHAR *psz)
75 {
76 const WCHAR *pch = psz;
77 DWORD Value = 0;
78 while ('0' <= *pch && *pch <= '9')
79 {
80 Value *= 10;
81 Value += *pch - L'0';
82 }
83 return Value;
84 }
85
86 BOOL LoadCoolSwitchSettings(void)
87 {
88 CoolSwitch = TRUE;
89 CoolSwitchRows = 3;
90 CoolSwitchColumns = 7;
91
92 // FIXME: load the settings from registry
93
94 TRACE("CoolSwitch: %d\n", CoolSwitch);
95 TRACE("CoolSwitchRows: %d\n", CoolSwitchRows);
96 TRACE("CoolSwitchColumns: %d\n", CoolSwitchColumns);
97
98 return TRUE;
99 }
100
101 void ResizeAndCenter(HWND hwnd, int width, int height)
102 {
103 int x, y;
104 RECT Rect;
105
106 int screenwidth = GetSystemMetrics(SM_CXSCREEN);
107 int screenheight = GetSystemMetrics(SM_CYSCREEN);
108
109 x = (screenwidth - width) / 2;
110 y = (screenheight - height) / 2;
111
112 SetRect(&Rect, x, y, x + width, y + height);
113 AdjustWindowRectEx(&Rect, Style, FALSE, ExStyle);
114
115 x = Rect.left;
116 y = Rect.top;
117 width = Rect.right - Rect.left;
118 height = Rect.bottom - Rect.top;
119 MoveWindow(hwnd, x, y, width, height, FALSE);
120
121 ptStart.x = x;
122 ptStart.y = y;
123 }
124
125 void MakeWindowActive(HWND hwnd)
126 {
127 if (IsIconic(hwnd))
128 PostMessageW(hwnd, WM_SYSCOMMAND, SC_RESTORE, 0);
129
130 BringWindowToTop(hwnd); // same as: SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); ?
131 SetForegroundWindow(hwnd);
132 }
133
134 void CompleteSwitch(BOOL doSwitch)
135 {
136 if (!isOpen)
137 return;
138
139 isOpen = FALSE;
140
141 TRACE("[ATbot] CompleteSwitch Hiding Window.\n");
142 ShowWindow(switchdialog, SW_HIDE);
143
144 if(doSwitch)
145 {
146 if(selectedWindow >= windowCount)
147 return;
148
149 // FIXME: workaround because reactos fails to activate the previous window correctly.
150 //if(selectedWindow != 0)
151 {
152 HWND hwnd = windowList[selectedWindow];
153
154 GetWindowTextW(hwnd, windowText, _countof(windowText));
155
156 TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd, windowText);
157
158 MakeWindowActive(hwnd);
159 }
160 }
161
162 windowCount = 0;
163 }
164
165 BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam)
166 {
167 HICON hIcon;
168 HWND hwndIcon, hwndOwner;
169
170 UNREFERENCED_PARAMETER(lParam);
171
172 hwndOwner = GetWindow(window, GW_OWNER);
173 hwndIcon = (hwndOwner ? hwndOwner : window);
174
175 // First try to get the big icon assigned to the window
176 hIcon = (HICON)SendMessageW(hwndIcon, WM_GETICON, ICON_BIG, 0);
177 if (!hIcon)
178 {
179 // If no icon is assigned, try to get the icon assigned to the windows' class
180 hIcon = (HICON)GetClassLongPtrW(hwndIcon, GCL_HICON);
181 if (!hIcon)
182 {
183 // If we still don't have an icon, see if we can do with the small icon,
184 // or a default application icon
185 hIcon = (HICON)SendMessageW(hwndIcon, WM_GETICON, ICON_SMALL2, 0);
186 if (!hIcon)
187 {
188 // using windows logo icon as default
189 hIcon = gpsi->hIconWindows;
190 if (!hIcon)
191 {
192 //if all attempts to get icon fails go to the next window
193 return TRUE;
194 }
195 }
196 }
197 }
198
199 windowList[windowCount] = window;
200 iconList[windowCount] = CopyIcon(hIcon);
201
202 windowCount++;
203
204 // If we got to the max number of windows,
205 // we won't be able to add any more
206 if(windowCount >= MAX_WINDOWS)
207 return FALSE;
208
209 return TRUE;
210 }
211
212 static BOOL CALLBACK
213 EnumWindowsProc(HWND hwnd, LPARAM lParam)
214 {
215 HWND hwndOwner;
216 WCHAR szClass[64];
217 DWORD ExStyle;
218
219 if (!IsWindowVisible(hwnd))
220 return TRUE;
221
222 // check special windows
223 if (!GetClassNameW(hwnd, szClass, _countof(szClass)) ||
224 wcscmp(szClass, L"Shell_TrayWnd") == 0 ||
225 wcscmp(szClass, L"Progman") == 0)
226 {
227 return TRUE;
228 }
229
230 ExStyle = GetWindowLongPtrW(hwnd, GWL_EXSTYLE);
231 if (ExStyle & WS_EX_TOOLWINDOW)
232 return TRUE;
233
234 hwndOwner = GetWindow(hwnd, GW_OWNER);
235 if (!IsWindowVisible(hwndOwner) || (ExStyle & WS_EX_APPWINDOW))
236 {
237 if (!EnumerateCallback(hwnd, lParam))
238 return FALSE;
239 }
240
241 return TRUE;
242 }
243
244 void ProcessMouseMessage(UINT message, LPARAM lParam)
245 {
246 int xPos = LOWORD(lParam);
247 int yPos = HIWORD(lParam);
248
249 int xIndex = (xPos - DIALOG_MARGIN) / CX_ITEM_SPACE;
250 int yIndex = (yPos - DIALOG_MARGIN) / CY_ITEM_SPACE;
251
252 if (xIndex < 0 || nCols <= xIndex ||
253 yIndex < 0 || nRows <= yIndex)
254 {
255 return;
256 }
257
258 selectedWindow = (yIndex*nCols) + xIndex;
259 if (message == WM_MOUSEMOVE)
260 {
261 InvalidateRect(switchdialog, NULL, TRUE);
262 //RedrawWindow(switchdialog, NULL, NULL, 0);
263 }
264 else
265 {
266 selectedWindow = (yIndex*nCols) + xIndex;
267 CompleteSwitch(TRUE);
268 }
269 }
270
271 void OnPaint(HWND hWnd)
272 {
273 HDC dialogDC;
274 PAINTSTRUCT paint;
275 RECT cRC, textRC;
276 int i, xPos, yPos, CharCount;
277 HFONT dcFont;
278 HICON hIcon;
279 HPEN hPen;
280 COLORREF Color;
281
282 // check
283 if (nCols == 0 || nItems == 0)
284 return;
285
286 // begin painting
287 dialogDC = BeginPaint(hWnd, &paint);
288 if (dialogDC == NULL)
289 return;
290
291 // fill the client area
292 GetClientRect(hWnd, &cRC);
293 FillRect(dialogDC, &cRC, (HBRUSH)(COLOR_3DFACE + 1));
294
295 // if the selection index exceeded the display items, then
296 // do display item shifting
297 if (selectedWindow >= nItems)
298 nShift = selectedWindow - nItems + 1;
299 else
300 nShift = 0;
301
302 for (i = 0; i < nItems; ++i)
303 {
304 // get the icon to display
305 hIcon = iconList[i + nShift];
306
307 // calculate the position where we start drawing
308 xPos = DIALOG_MARGIN + CX_ITEM_SPACE * (i % nCols) + ITEM_MARGIN;
309 yPos = DIALOG_MARGIN + CY_ITEM_SPACE * (i / nCols) + ITEM_MARGIN;
310
311 // centering
312 if (nItems < CoolSwitchColumns)
313 {
314 xPos += (itemsW - nItems * CX_ITEM_SPACE) / 2;
315 }
316
317 // if this position is selected,
318 if (selectedWindow == i + nShift)
319 {
320 // create a solid pen
321 Color = GetSysColor(COLOR_HIGHLIGHT);
322 hPen = CreatePen(PS_SOLID, 1, Color);
323
324 // draw a rectangle with using the pen
325 SelectObject(dialogDC, hPen);
326 SelectObject(dialogDC, GetStockObject(NULL_BRUSH));
327 Rectangle(dialogDC, xPos, yPos, xPos + CX_ITEM, yPos + CY_ITEM);
328 Rectangle(dialogDC, xPos + 1, yPos + 1,
329 xPos + CX_ITEM - 1, yPos + CY_ITEM - 1);
330
331 // delete the pen
332 DeleteObject(hPen);
333 }
334
335 // draw icon
336 DrawIconEx(dialogDC, xPos + ICON_MARGIN, yPos + ICON_MARGIN,
337 hIcon, CX_ICON, CY_ICON, 0, NULL, DI_NORMAL);
338 }
339
340 // set the text rectangle
341 SetRect(&textRC, DIALOG_MARGIN, DIALOG_MARGIN + itemsH,
342 totalW - DIALOG_MARGIN, totalH - DIALOG_MARGIN);
343
344 // draw the sunken button around text
345 DrawFrameControl(dialogDC, &textRC, DFC_BUTTON,
346 DFCS_BUTTONPUSH | DFCS_PUSHED);
347
348 // get text
349 CharCount = GetWindowTextW(windowList[selectedWindow], windowText,
350 _countof(windowText));
351
352 // draw text
353 dcFont = SelectObject(dialogDC, dialogFont);
354 SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT));
355 SetBkMode(dialogDC, TRANSPARENT);
356 DrawTextW(dialogDC, windowText, CharCount, &textRC,
357 DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE);
358 SelectObject(dialogDC, dcFont);
359
360 // end painting
361 EndPaint(hWnd, &paint);
362 }
363
364 DWORD CreateSwitcherWindow(HINSTANCE hInstance)
365 {
366 switchdialog = CreateWindowExW( WS_EX_TOPMOST|WS_EX_DLGMODALFRAME|WS_EX_TOOLWINDOW,
367 WC_SWITCH,
368 L"",
369 WS_POPUP|WS_BORDER|WS_DISABLED,
370 CW_USEDEFAULT,
371 CW_USEDEFAULT,
372 400, 150,
373 NULL, NULL,
374 hInstance, NULL);
375 if (!switchdialog)
376 {
377 TRACE("[ATbot] Task Switcher Window failed to create.\n");
378 return 0;
379 }
380
381 isOpen = FALSE;
382 return 1;
383 }
384
385 DWORD GetDialogFont(VOID)
386 {
387 HDC tDC;
388 TEXTMETRIC tm;
389
390 dialogFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
391
392 tDC = GetDC(0);
393 GetTextMetrics(tDC, &tm);
394 fontHeight = tm.tmHeight;
395 ReleaseDC(0, tDC);
396
397 return 1;
398 }
399
400 void PrepareWindow(VOID)
401 {
402 nItems = windowCount;
403
404 nCols = CoolSwitchColumns;
405 nRows = (nItems + CoolSwitchColumns - 1) / CoolSwitchColumns;
406 if (nRows > CoolSwitchRows)
407 {
408 nRows = CoolSwitchRows;
409 nItems = nRows * nCols;
410 }
411
412 itemsW = nCols * CX_ITEM_SPACE;
413 itemsH = nRows * CY_ITEM_SPACE;
414
415 totalW = itemsW + 2 * DIALOG_MARGIN;
416 totalH = itemsH + 2 * DIALOG_MARGIN;
417 totalH += fontHeight + 2 * CY_TEXT_MARGIN;
418
419 ResizeAndCenter(switchdialog, totalW, totalH);
420 }
421
422 BOOL ProcessHotKey(VOID)
423 {
424 if (!isOpen)
425 {
426 windowCount = 0;
427 EnumWindows(EnumWindowsProc, 0);
428
429 if (windowCount == 0)
430 return FALSE;
431
432 if (windowCount == 1)
433 {
434 selectedWindow = 0;
435 CompleteSwitch(TRUE);
436 return TRUE;
437 }
438
439 selectedWindow = 1;
440
441 TRACE("[ATbot] HotKey Received. Opening window.\n");
442 ShowWindow(switchdialog, SW_SHOWNORMAL);
443 MakeWindowActive(switchdialog);
444 isOpen = TRUE;
445 }
446 else
447 {
448 TRACE("[ATbot] HotKey Received Rotating.\n");
449 selectedWindow = (selectedWindow + 1)%windowCount;
450 InvalidateRect(switchdialog, NULL, TRUE);
451 }
452 return TRUE;
453 }
454
455 void RotateTasks(BOOL bShift)
456 {
457 HWND hwndFirst, hwndLast;
458 DWORD Size;
459
460 if (windowCount < 2 || !Esc)
461 return;
462
463 hwndFirst = windowList[0];
464 hwndLast = windowList[windowCount - 1];
465
466 if (bShift)
467 {
468 SetWindowPos(hwndLast, HWND_TOP, 0, 0, 0, 0,
469 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
470 SWP_NOOWNERZORDER | SWP_NOREPOSITION);
471
472 MakeWindowActive(hwndLast);
473
474 Size = (windowCount - 1) * sizeof(HWND);
475 MoveMemory(&windowList[1], &windowList[0], Size);
476 windowList[0] = hwndLast;
477 }
478 else
479 {
480 SetWindowPos(hwndFirst, hwndLast, 0, 0, 0, 0,
481 SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
482 SWP_NOOWNERZORDER | SWP_NOREPOSITION);
483
484 MakeWindowActive(windowList[1]);
485
486 Size = (windowCount - 1) * sizeof(HWND);
487 MoveMemory(&windowList[0], &windowList[1], Size);
488 windowList[windowCount - 1] = hwndFirst;
489 }
490 }
491
492 static void MoveLeft(void)
493 {
494 selectedWindow = selectedWindow - 1;
495 if (selectedWindow < 0)
496 selectedWindow = windowCount - 1;
497 InvalidateRect(switchdialog, NULL, TRUE);
498 }
499
500 static void MoveRight(void)
501 {
502 selectedWindow = (selectedWindow + 1) % windowCount;
503 InvalidateRect(switchdialog, NULL, TRUE);
504 }
505
506 static void MoveUp(void)
507 {
508 INT iRow = selectedWindow / nCols;
509 INT iCol = selectedWindow % nCols;
510
511 --iRow;
512 if (iRow < 0)
513 iRow = nRows - 1;
514
515 selectedWindow = iRow * nCols + iCol;
516 if (selectedWindow >= windowCount)
517 selectedWindow = windowCount - 1;
518 InvalidateRect(switchdialog, NULL, TRUE);
519 }
520
521 static void MoveDown(void)
522 {
523 INT iRow = selectedWindow / nCols;
524 INT iCol = selectedWindow % nCols;
525
526 ++iRow;
527 if (iRow >= nRows)
528 iRow = 0;
529
530 selectedWindow = iRow * nCols + iCol;
531 if (selectedWindow >= windowCount)
532 selectedWindow = windowCount - 1;
533 InvalidateRect(switchdialog, NULL, TRUE);
534 }
535
536 VOID
537 DestroyAppWindows(VOID)
538 {
539 // for every item of the icon list:
540 INT i;
541 for (i = 0; i < windowCount; ++i)
542 {
543 // destroy the icon
544 DestroyIcon(iconList[i]);
545 iconList[i] = NULL;
546 }
547 }
548
549 LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
550 {
551 HWND hwndActive;
552 MSG msg;
553
554 // FIXME: Is loading timing OK?
555 LoadCoolSwitchSettings();
556
557 if (!CoolSwitch)
558 return 0;
559
560 // Already in the loop.
561 if (switchdialog || Esc) return 0;
562
563 hwndActive = GetActiveWindow();
564 // Nothing is active so exit.
565 if (!hwndActive) return 0;
566
567 if (lParam == VK_ESCAPE)
568 {
569 Esc = TRUE;
570
571 windowCount = 0;
572 EnumWindows(EnumWindowsProc, 0);
573
574 if (windowCount < 2)
575 return 0;
576
577 RotateTasks(GetAsyncKeyState(VK_SHIFT) < 0);
578
579 hwndActive = GetActiveWindow();
580
581 if (hwndActive == NULL)
582 {
583 Esc = FALSE;
584 return 0;
585 }
586 }
587
588 // Capture current active window.
589 SetCapture( hwndActive );
590
591 switch (lParam)
592 {
593 case VK_TAB:
594 if( !CreateSwitcherWindow(User32Instance) ) goto Exit;
595 if( !GetDialogFont() ) goto Exit;
596 if( !ProcessHotKey() ) goto Exit;
597 break;
598
599 case VK_ESCAPE:
600 break;
601
602 default:
603 goto Exit;
604 }
605 // Main message loop:
606 while (1)
607 {
608 for (;;)
609 {
610 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE ))
611 {
612 if (!CallMsgFilterW( &msg, MSGF_NEXTWINDOW )) break;
613 /* remove the message from the queue */
614 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
615 }
616 else
617 WaitMessage();
618 }
619
620 switch (msg.message)
621 {
622 case WM_KEYUP:
623 {
624 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
625 if (msg.wParam == VK_MENU)
626 {
627 CompleteSwitch(TRUE);
628 }
629 else if (msg.wParam == VK_RETURN)
630 {
631 CompleteSwitch(TRUE);
632 }
633 else if (msg.wParam == VK_ESCAPE)
634 {
635 TRACE("DoAppSwitch VK_ESCAPE 2\n");
636 CompleteSwitch(FALSE);
637 }
638 goto Exit; //break;
639 }
640
641 case WM_SYSKEYDOWN:
642 {
643 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
644 if (HIWORD(msg.lParam) & KF_ALTDOWN)
645 {
646 if ( msg.wParam == VK_TAB )
647 {
648 if (Esc) break;
649 if (GetKeyState(VK_SHIFT) < 0)
650 {
651 MoveLeft();
652 }
653 else
654 {
655 MoveRight();
656 }
657 }
658 else if ( msg.wParam == VK_ESCAPE )
659 {
660 if (!Esc) break;
661 RotateTasks(GetKeyState(VK_SHIFT) < 0);
662 }
663 else if ( msg.wParam == VK_LEFT )
664 {
665 MoveLeft();
666 }
667 else if ( msg.wParam == VK_RIGHT )
668 {
669 MoveRight();
670 }
671 else if ( msg.wParam == VK_UP )
672 {
673 MoveUp();
674 }
675 else if ( msg.wParam == VK_DOWN )
676 {
677 MoveDown();
678 }
679 }
680 break;
681 }
682
683 case WM_LBUTTONUP:
684 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
685 ProcessMouseMessage(msg.message, msg.lParam);
686 goto Exit;
687
688 default:
689 if (PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ))
690 {
691 TranslateMessage(&msg);
692 DispatchMessageW(&msg);
693 }
694 break;
695 }
696 }
697 Exit:
698 ReleaseCapture();
699 if (switchdialog) DestroyWindow(switchdialog);
700 if (Esc) DestroyAppWindows();
701 switchdialog = NULL;
702 selectedWindow = 0;
703 windowCount = 0;
704 Esc = FALSE;
705 return 0;
706 }
707
708 //
709 // Switch System Class Window Proc.
710 //
711 LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL unicode )
712 {
713 PWND pWnd;
714 PALTTABINFO ati;
715 pWnd = ValidateHwnd(hWnd);
716 if (pWnd)
717 {
718 if (!pWnd->fnid)
719 {
720 NtUserSetWindowFNID(hWnd, FNID_SWITCH);
721 }
722 }
723
724 switch (uMsg)
725 {
726 case WM_NCCREATE:
727 if (!(ati = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ati))))
728 return 0;
729 SetWindowLongPtrW( hWnd, 0, (LONG_PTR)ati );
730 return TRUE;
731
732 case WM_SHOWWINDOW:
733 if (wParam)
734 {
735 PrepareWindow();
736 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
737 ati->cbSize = sizeof(ALTTABINFO);
738 ati->cItems = nItems;
739 ati->cColumns = nCols;
740 ati->cRows = nRows;
741 if (nCols)
742 {
743 ati->iColFocus = (selectedWindow - nShift) % nCols;
744 ati->iRowFocus = (selectedWindow - nShift) / nCols;
745 }
746 else
747 {
748 ati->iColFocus = 0;
749 ati->iRowFocus = 0;
750 }
751 ati->cxItem = CX_ITEM_SPACE;
752 ati->cyItem = CY_ITEM_SPACE;
753 ati->ptStart = ptStart;
754 }
755 return 0;
756
757 case WM_MOUSEMOVE:
758 ProcessMouseMessage(uMsg, lParam);
759 return 0;
760
761 case WM_ACTIVATE:
762 if (wParam == WA_INACTIVE)
763 {
764 CompleteSwitch(FALSE);
765 }
766 return 0;
767
768 case WM_PAINT:
769 OnPaint(hWnd);
770 return 0;
771
772 case WM_DESTROY:
773 isOpen = FALSE;
774 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
775 HeapFree( GetProcessHeap(), 0, ati );
776 SetWindowLongPtrW( hWnd, 0, 0 );
777 DestroyAppWindows();
778 NtUserSetWindowFNID(hWnd, FNID_DESTROY);
779 return 0;
780 }
781 return DefWindowProcW(hWnd, uMsg, wParam, lParam);
782 }
783
784 LRESULT WINAPI SwitchWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
785 {
786 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, FALSE);
787 }
788
789 LRESULT WINAPI SwitchWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
790 {
791 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, TRUE);
792 }