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)
12 #include <wine/debug.h>
13 WINE_DEFAULT_DEBUG_CHANNEL(user32
);
15 // limit the number of windows shown in the alt-tab window
16 // 120 windows results in (12*40) by (10*40) pixels worth of icons.
17 #define MAX_WINDOWS 120
20 HWND switchdialog
= NULL
;
22 int selectedWindow
= 0;
27 WCHAR windowText
[1024];
29 HWND windowList
[MAX_WINDOWS
];
30 HICON iconList
[MAX_WINDOWS
];
33 int cxBorder
, cyBorder
;
34 int nItems
, nCols
, nRows
;
40 void ResizeAndCenter(HWND hwnd
, int width
, int height
)
42 int screenwidth
= GetSystemMetrics(SM_CXSCREEN
);
43 int screenheight
= GetSystemMetrics(SM_CYSCREEN
);
45 pt
.x
= (screenwidth
- width
) / 2;
46 pt
.y
= (screenheight
- height
) / 2;
48 MoveWindow(hwnd
, pt
.x
, pt
.y
, width
, height
, FALSE
);
51 void MakeWindowActive(HWND hwnd
)
55 wpl
.length
= sizeof(WINDOWPLACEMENT
);
56 GetWindowPlacement(hwnd
, &wpl
);
58 TRACE("GetWindowPlacement wpl.showCmd %d\n",wpl
.showCmd
);
59 if (wpl
.showCmd
== SW_SHOWMINIMIZED
)
60 ShowWindowAsync(hwnd
, SW_RESTORE
);
62 BringWindowToTop(hwnd
); // same as: SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); ?
63 SetForegroundWindow(hwnd
);
66 void CompleteSwitch(BOOL doSwitch
)
73 TRACE("[ATbot] CompleteSwitch Hiding Window.\n");
74 ShowWindow(switchdialog
, SW_HIDE
);
78 if(selectedWindow
>= windowCount
)
81 // FIXME: workaround because reactos fails to activate the previous window correctly.
82 //if(selectedWindow != 0)
84 HWND hwnd
= windowList
[selectedWindow
];
86 GetWindowTextW(hwnd
, windowText
, _countof(windowText
));
88 TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd
, windowText
);
90 MakeWindowActive(hwnd
);
97 BOOL CALLBACK
EnumerateCallback(HWND window
, LPARAM lParam
)
101 UNREFERENCED_PARAMETER(lParam
);
103 if (!IsWindowVisible(window
))
106 GetClassNameW(window
, windowText
, _countof(windowText
));
107 if ((wcscmp(L
"Shell_TrayWnd", windowText
)==0) ||
108 (wcscmp(L
"Progman", windowText
)==0) )
111 // First try to get the big icon assigned to the window
112 hIcon
= (HICON
)SendMessageW(window
, WM_GETICON
, ICON_BIG
, 0);
115 // If no icon is assigned, try to get the icon assigned to the windows' class
116 hIcon
= (HICON
)GetClassLongPtrW(window
, GCL_HICON
);
119 // If we still don't have an icon, see if we can do with the small icon,
120 // or a default application icon
121 hIcon
= (HICON
)SendMessageW(window
, WM_GETICON
, ICON_SMALL2
, 0);
124 // using windows logo icon as default
125 hIcon
= gpsi
->hIconWindows
;
128 //if all attempts to get icon fails go to the next window
135 windowList
[windowCount
] = window
;
136 iconList
[windowCount
] = CopyIcon(hIcon
);
140 // If we got to the max number of windows,
141 // we won't be able to add any more
142 if(windowCount
== MAX_WINDOWS
)
148 // Function mostly compatible with the normal EnumWindows,
149 // except it lists in Z-Order and it doesn't ensure consistency
150 // if a window is removed while enumerating
151 void EnumWindowsZOrder(WNDENUMPROC callback
, LPARAM lParam
)
153 HWND next
= GetTopWindow(NULL
);
156 if(!callback(next
, lParam
))
158 next
= GetWindow(next
, GW_HWNDNEXT
);
162 void ProcessMouseMessage(UINT message
, LPARAM lParam
)
164 int xPos
= LOWORD(lParam
);
165 int yPos
= HIWORD(lParam
);
167 int xIndex
= (xPos
- xOffset
)/40;
168 int xOff
= (xPos
- xOffset
)%40;
170 int yIndex
= (yPos
- yOffset
)/40;
171 int yOff
= (yPos
- yOffset
)%40;
173 if(xOff
> 32 || xIndex
> nItems
)
176 if(yOff
> 32 || yIndex
> nRows
)
179 selectedWindow
= (yIndex
*nCols
) + xIndex
;
180 if (message
== WM_MOUSEMOVE
)
182 InvalidateRect(switchdialog
, NULL
, TRUE
);
183 //RedrawWindow(switchdialog, NULL, NULL, 0);
187 selectedWindow
= (yIndex
*nCols
) + xIndex
;
188 CompleteSwitch(TRUE
);
192 void OnPaint(HWND hWnd
)
202 int nch
= GetWindowTextW(windowList
[selectedWindow
], windowText
, _countof(windowText
));
204 dialogDC
= BeginPaint(hWnd
, &paint
);
206 GetClientRect(hWnd
, &cRC
);
207 FillRect(dialogDC
, &cRC
, GetSysColorBrush(COLOR_MENU
));
209 for(i
=0; i
< windowCount
; i
++)
211 HICON hIcon
= iconList
[i
];
213 int xpos
= xOffset
+ 40 * (i
% nCols
);
214 int ypos
= yOffset
+ 40 * (i
/ nCols
);
216 if (selectedWindow
== i
)
218 hBrush
= GetSysColorBrush(COLOR_HIGHLIGHT
);
222 hBrush
= GetSysColorBrush(COLOR_MENU
);
225 cr
= GetSysColor(COLOR_BTNTEXT
); // doesn't look right! >_<
226 hPen
= CreatePen(PS_DOT
, 1, cr
);
227 SelectObject(dialogDC
, hPen
);
228 SelectObject(dialogDC
, hBrush
);
229 Rectangle(dialogDC
, xpos
-2, ypos
-2, xpos
+32+2, ypos
+32+2);
231 // Must NOT destroy the system brush!
233 RECT rc
= { xpos
-2, ypos
-2, xpos
+32+2, ypos
+32+2 };
234 FillRect(dialogDC
, &rc
, hBrush
);
236 DrawIcon(dialogDC
, xpos
, ypos
, hIcon
);
239 dcFont
= SelectObject(dialogDC
, dialogFont
);
240 SetTextColor(dialogDC
, GetSysColor(COLOR_BTNTEXT
));
241 SetBkMode(dialogDC
, TRANSPARENT
);
245 textRC
.right
= totalW
- 8;
246 textRC
.bottom
= totalH
- 8;
247 DrawTextW(dialogDC
, windowText
, nch
, &textRC
, DT_CENTER
|DT_END_ELLIPSIS
);
248 SelectObject(dialogDC
, dcFont
);
250 EndPaint(hWnd
, &paint
);
253 DWORD
CreateSwitcherWindow(HINSTANCE hInstance
)
255 switchdialog
= CreateWindowExW( WS_EX_TOPMOST
|WS_EX_DLGMODALFRAME
|WS_EX_TOOLWINDOW
,
258 WS_POPUP
|WS_BORDER
|WS_DISABLED
,
266 TRACE("[ATbot] Task Switcher Window failed to create.\n");
274 DWORD
GetDialogFont(VOID
)
279 dialogFont
= (HFONT
)GetStockObject(DEFAULT_GUI_FONT
);
282 GetTextMetrics(tDC
, &tm
);
283 fontHeight
= tm
.tmHeight
;
289 void PrepareWindow(VOID
)
291 cxBorder
= GetSystemMetrics(SM_CXBORDER
);
292 cyBorder
= GetSystemMetrics(SM_CYBORDER
);
294 nItems
= windowCount
;
295 nCols
= min(max(nItems
,8),12);
296 nRows
= (nItems
+nCols
-1)/nCols
;
298 itemsW
= nCols
*32 + (nCols
+1)*8;
299 itemsH
= nRows
*32 + (nRows
+1)*8;
301 totalW
= itemsW
+ 2*cxBorder
+ 4;
302 totalH
= itemsH
+ 2*cyBorder
+ fontHeight
+ 8; // give extra pixels for the window title
309 int w2
= nItems
*32 + (nItems
-1)*8;
310 xOffset
= (itemsW
-w2
)/2;
312 ResizeAndCenter(switchdialog
, totalW
, totalH
);
315 BOOL
ProcessHotKey(VOID
)
320 EnumWindowsZOrder(EnumerateCallback
, 0);
327 TRACE("[ATbot] HotKey Received. Opening window.\n");
328 ShowWindow(switchdialog
, SW_SHOWNORMAL
);
329 MakeWindowActive(switchdialog
);
334 TRACE("[ATbot] HotKey Received Rotating.\n");
335 selectedWindow
= (selectedWindow
+ 1)%windowCount
;
336 InvalidateRect(switchdialog
, NULL
, TRUE
);
341 LRESULT WINAPI
DoAppSwitch( WPARAM wParam
, LPARAM lParam
)
343 HWND hwnd
, hwndActive
;
349 // Already in the loop.
350 if (switchdialog
) return 0;
352 hwndActive
= GetActiveWindow();
353 // Nothing is active so exit.
354 if (!hwndActive
) return 0;
355 // Capture current active window.
356 SetCapture( hwndActive
);
361 if( !CreateSwitcherWindow(User32Instance
) ) goto Exit
;
362 if( !GetDialogFont() ) goto Exit
;
363 if( !ProcessHotKey() ) goto Exit
;
369 EnumWindowsZOrder(EnumerateCallback
, 0);
370 if (windowCount
< 2) goto Exit
;
371 if (wParam
== SC_NEXTWINDOW
)
375 if (windowCount
== 2)
378 Count
= windowCount
- 1;
380 TRACE("DoAppSwitch VK_ESCAPE 1 Count %d windowCount %d\n",Count
,windowCount
);
381 hwnd
= windowList
[Count
];
382 GetWindowTextW(hwnd
, Text
, _countof(Text
));
383 TRACE("[ATbot] Switching to 0x%08x (%ls)\n", hwnd
, Text
);
384 MakeWindowActive(hwnd
);
391 // Main message loop:
396 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
398 if (!CallMsgFilterW( &msg
, MSGF_NEXTWINDOW
)) break;
399 /* remove the message from the queue */
400 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
410 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
411 if (msg
.wParam
== VK_MENU
)
413 CompleteSwitch(TRUE
);
415 else if (msg
.wParam
== VK_RETURN
)
417 CompleteSwitch(TRUE
);
419 else if (msg
.wParam
== VK_ESCAPE
)
421 TRACE("DoAppSwitch VK_ESCAPE 2\n");
422 CompleteSwitch(FALSE
);
429 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
430 if (HIWORD(msg
.lParam
) & KF_ALTDOWN
)
433 if ( msg
.wParam
== VK_TAB
)
436 Shift
= GetKeyState(VK_SHIFT
) & 0x8000 ? SC_PREVWINDOW
: SC_NEXTWINDOW
;
437 if (Shift
== SC_NEXTWINDOW
)
439 selectedWindow
= (selectedWindow
+ 1)%windowCount
;
443 selectedWindow
= selectedWindow
- 1;
444 if (selectedWindow
< 0)
445 selectedWindow
= windowCount
- 1;
447 InvalidateRect(switchdialog
, NULL
, TRUE
);
449 else if ( msg
.wParam
== VK_ESCAPE
)
454 if (wParam
== SC_NEXTWINDOW
)
456 Count
= (Count
+ 1)%windowCount
;
462 Count
= windowCount
- 1;
464 hwnd
= windowList
[Count
];
465 GetWindowTextW(hwnd
, Text
, _countof(Text
));
466 MakeWindowActive(hwnd
);
473 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
474 ProcessMouseMessage(msg
.message
, msg
.lParam
);
478 if (PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
))
480 TranslateMessage(&msg
);
481 DispatchMessageW(&msg
);
488 if (switchdialog
) DestroyWindow(switchdialog
);
496 DestroyAppWindows(VOID
)
499 for (i
=0; i
< windowCount
; i
++)
501 HICON hIcon
= iconList
[i
];
507 // Switch System Class Window Proc.
509 LRESULT WINAPI
SwitchWndProc_common(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
513 pWnd
= ValidateHwnd(hWnd
);
518 NtUserSetWindowFNID(hWnd
, FNID_SWITCH
);
525 if (!(ati
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*ati
))))
527 SetWindowLongPtrW( hWnd
, 0, (LONG_PTR
)ati
);
534 ati
= (PALTTABINFO
)GetWindowLongPtrW(hWnd
, 0);
535 ati
->cItems
= nItems
;
536 ati
->cxItem
= ati
->cyItem
= 43;
538 ati
->cColumns
= nCols
;
543 ProcessMouseMessage(uMsg
, lParam
);
547 if (wParam
== WA_INACTIVE
)
549 CompleteSwitch(FALSE
);
559 ati
= (PALTTABINFO
)GetWindowLongPtrW(hWnd
, 0);
560 HeapFree( GetProcessHeap(), 0, ati
);
561 SetWindowLongPtrW( hWnd
, 0, 0 );
563 NtUserSetWindowFNID(hWnd
, FNID_DESTROY
);
566 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
569 LRESULT WINAPI
SwitchWndProcA(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
571 return SwitchWndProc_common(hWnd
, uMsg
, wParam
, lParam
, FALSE
);
574 LRESULT WINAPI
SwitchWndProcW(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
576 return SwitchWndProc_common(hWnd
, uMsg
, wParam
, lParam
, TRUE
);