737663a17db573fba584d682f2a435a633c49f2a
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)
14 // Add registry support.
20 WINE_DEFAULT_DEBUG_CHANNEL(user32
);
22 #define DIALOG_MARGIN 8 // margin of dialog contents
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
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
32 #define CX_ITEM_SPACE (CX_ITEM + 2 * ITEM_MARGIN)
33 #define CY_ITEM_SPACE (CY_ITEM + 2 * ITEM_MARGIN)
35 #define CY_TEXT_MARGIN 4 // margin height around text
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
42 HWND switchdialog
= NULL
;
44 int selectedWindow
= 0;
49 WCHAR windowText
[1024];
51 HWND windowList
[MAX_WINDOWS
];
52 HICON iconList
[MAX_WINDOWS
];
55 int cxBorder
, cyBorder
;
56 int nItems
, nCols
, nRows
;
66 BOOL CoolSwitch
= TRUE
;
67 int CoolSwitchRows
= 3;
68 int CoolSwitchColumns
= 7;
71 const DWORD Style
= WS_POPUP
| WS_BORDER
| WS_DISABLED
;
72 const DWORD ExStyle
= WS_EX_TOPMOST
| WS_EX_DLGMODALFRAME
| WS_EX_TOOLWINDOW
;
74 DWORD
wtodw(const WCHAR
*psz
)
76 const WCHAR
*pch
= psz
;
78 while ('0' <= *pch
&& *pch
<= '9')
86 BOOL
LoadCoolSwitchSettings(void)
90 CoolSwitchColumns
= 7;
92 // FIXME: load the settings from registry
94 TRACE("CoolSwitch: %d\n", CoolSwitch
);
95 TRACE("CoolSwitchRows: %d\n", CoolSwitchRows
);
96 TRACE("CoolSwitchColumns: %d\n", CoolSwitchColumns
);
101 void ResizeAndCenter(HWND hwnd
, int width
, int height
)
106 int screenwidth
= GetSystemMetrics(SM_CXSCREEN
);
107 int screenheight
= GetSystemMetrics(SM_CYSCREEN
);
109 x
= (screenwidth
- width
) / 2;
110 y
= (screenheight
- height
) / 2;
112 SetRect(&Rect
, x
, y
, x
+ width
, y
+ height
);
113 AdjustWindowRectEx(&Rect
, Style
, FALSE
, ExStyle
);
117 width
= Rect
.right
- Rect
.left
;
118 height
= Rect
.bottom
- Rect
.top
;
119 MoveWindow(hwnd
, x
, y
, width
, height
, FALSE
);
125 void MakeWindowActive(HWND hwnd
)
128 PostMessageW(hwnd
, WM_SYSCOMMAND
, SC_RESTORE
, 0);
130 BringWindowToTop(hwnd
); // same as: SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); ?
131 SetForegroundWindow(hwnd
);
134 void CompleteSwitch(BOOL doSwitch
)
141 TRACE("[ATbot] CompleteSwitch Hiding Window.\n");
142 ShowWindow(switchdialog
, SW_HIDE
);
146 if(selectedWindow
>= windowCount
)
149 // FIXME: workaround because reactos fails to activate the previous window correctly.
150 //if(selectedWindow != 0)
152 HWND hwnd
= windowList
[selectedWindow
];
154 GetWindowTextW(hwnd
, windowText
, _countof(windowText
));
156 TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd
, windowText
);
158 MakeWindowActive(hwnd
);
165 BOOL CALLBACK
EnumerateCallback(HWND window
, LPARAM lParam
)
169 UNREFERENCED_PARAMETER(lParam
);
171 // First try to get the big icon assigned to the window
172 hIcon
= (HICON
)SendMessageW(window
, WM_GETICON
, ICON_BIG
, 0);
175 // If no icon is assigned, try to get the icon assigned to the windows' class
176 hIcon
= (HICON
)GetClassLongPtrW(window
, GCL_HICON
);
179 // If we still don't have an icon, see if we can do with the small icon,
180 // or a default application icon
181 hIcon
= (HICON
)SendMessageW(window
, WM_GETICON
, ICON_SMALL2
, 0);
184 // using windows logo icon as default
185 hIcon
= gpsi
->hIconWindows
;
188 //if all attempts to get icon fails go to the next window
195 windowList
[windowCount
] = window
;
196 iconList
[windowCount
] = CopyIcon(hIcon
);
200 // If we got to the max number of windows,
201 // we won't be able to add any more
202 if(windowCount
>= MAX_WINDOWS
)
208 static HWND
GetNiceRootOwner(HWND hwnd
)
211 DWORD ExStyle
, OwnerExStyle
;
215 // A window with WS_EX_APPWINDOW is treated as if it has no owner
216 ExStyle
= GetWindowLong(hwnd
, GWL_EXSTYLE
);
217 if (ExStyle
& WS_EX_APPWINDOW
)
220 // Is the owner visible?
221 // An window with WS_EX_TOOLWINDOW is treated as if it weren't visible
222 hwndOwner
= GetWindow(hwnd
, GW_OWNER
);
223 OwnerExStyle
= GetWindowLong(hwndOwner
, GWL_EXSTYLE
);
224 if (!IsWindowVisible(hwndOwner
) || (OwnerExStyle
& WS_EX_TOOLWINDOW
))
233 // c.f. http://blogs.msdn.com/b/oldnewthing/archive/2007/10/08/5351207.aspx
234 BOOL
IsAltTabWindow(HWND hwnd
)
238 HWND hwndTry
, hwndWalk
;
242 if (!IsWindowVisible(hwnd
))
245 // must not be WS_EX_TOOLWINDOW
246 ExStyle
= GetWindowLong(hwnd
, GWL_EXSTYLE
);
247 if (ExStyle
& WS_EX_TOOLWINDOW
)
250 // must be not empty rect
251 GetWindowRect(hwnd
, &rc
);
252 if (IsRectEmpty(&rc
))
255 // check special windows
256 if (!GetClassNameW(hwnd
, szClass
, _countof(szClass
)) ||
257 wcscmp(szClass
, L
"Shell_TrayWnd") == 0 ||
258 wcscmp(szClass
, L
"Progman") == 0)
263 // get 'nice' root owner
264 hwndWalk
= GetNiceRootOwner(hwnd
);
266 // walk back from hwndWalk toward hwnd
269 hwndTry
= GetLastActivePopup(hwndWalk
);
270 if (hwndTry
== hwndWalk
)
273 ExStyle
= GetWindowLong(hwndTry
, GWL_EXSTYLE
);
274 if (IsWindowVisible(hwndTry
) && !(ExStyle
& WS_EX_TOOLWINDOW
))
280 return hwnd
== hwndTry
; // Reached?
284 EnumWindowsProc(HWND hwnd
, LPARAM lParam
)
286 if (IsAltTabWindow(hwnd
))
288 if (!EnumerateCallback(hwnd
, lParam
))
294 void ProcessMouseMessage(UINT message
, LPARAM lParam
)
296 int xPos
= LOWORD(lParam
);
297 int yPos
= HIWORD(lParam
);
299 int xIndex
= (xPos
- DIALOG_MARGIN
) / CX_ITEM_SPACE
;
300 int yIndex
= (yPos
- DIALOG_MARGIN
) / CY_ITEM_SPACE
;
302 if (xIndex
< 0 || nCols
<= xIndex
||
303 yIndex
< 0 || nRows
<= yIndex
)
308 selectedWindow
= (yIndex
*nCols
) + xIndex
;
309 if (message
== WM_MOUSEMOVE
)
311 InvalidateRect(switchdialog
, NULL
, TRUE
);
312 //RedrawWindow(switchdialog, NULL, NULL, 0);
316 selectedWindow
= (yIndex
*nCols
) + xIndex
;
317 CompleteSwitch(TRUE
);
321 void OnPaint(HWND hWnd
)
326 int i
, xPos
, yPos
, CharCount
;
333 if (nCols
== 0 || nItems
== 0)
337 dialogDC
= BeginPaint(hWnd
, &paint
);
338 if (dialogDC
== NULL
)
341 // fill the client area
342 GetClientRect(hWnd
, &cRC
);
343 FillRect(dialogDC
, &cRC
, (HBRUSH
)(COLOR_3DFACE
+ 1));
345 // if the selection index exceeded the display items, then
346 // do display item shifting
347 if (selectedWindow
>= nItems
)
348 nShift
= selectedWindow
- nItems
+ 1;
352 for (i
= 0; i
< nItems
; ++i
)
354 // get the icon to display
355 hIcon
= iconList
[i
+ nShift
];
357 // calculate the position where we start drawing
358 xPos
= DIALOG_MARGIN
+ CX_ITEM_SPACE
* (i
% nCols
) + ITEM_MARGIN
;
359 yPos
= DIALOG_MARGIN
+ CY_ITEM_SPACE
* (i
/ nCols
) + ITEM_MARGIN
;
362 if (nItems
< CoolSwitchColumns
)
364 xPos
+= (itemsW
- nItems
* CX_ITEM_SPACE
) / 2;
367 // if this position is selected,
368 if (selectedWindow
== i
+ nShift
)
370 // create a solid pen
371 Color
= GetSysColor(COLOR_HIGHLIGHT
);
372 hPen
= CreatePen(PS_SOLID
, 1, Color
);
374 // draw a rectangle with using the pen
375 SelectObject(dialogDC
, hPen
);
376 SelectObject(dialogDC
, GetStockObject(NULL_BRUSH
));
377 Rectangle(dialogDC
, xPos
, yPos
, xPos
+ CX_ITEM
, yPos
+ CY_ITEM
);
378 Rectangle(dialogDC
, xPos
+ 1, yPos
+ 1,
379 xPos
+ CX_ITEM
- 1, yPos
+ CY_ITEM
- 1);
386 DrawIconEx(dialogDC
, xPos
+ ICON_MARGIN
, yPos
+ ICON_MARGIN
,
387 hIcon
, CX_ICON
, CY_ICON
, 0, NULL
, DI_NORMAL
);
390 // set the text rectangle
391 SetRect(&textRC
, DIALOG_MARGIN
, DIALOG_MARGIN
+ itemsH
,
392 totalW
- DIALOG_MARGIN
, totalH
- DIALOG_MARGIN
);
394 // draw the sunken button around text
395 DrawFrameControl(dialogDC
, &textRC
, DFC_BUTTON
,
396 DFCS_BUTTONPUSH
| DFCS_PUSHED
);
399 CharCount
= GetWindowTextW(windowList
[selectedWindow
], windowText
,
400 _countof(windowText
));
403 dcFont
= SelectObject(dialogDC
, dialogFont
);
404 SetTextColor(dialogDC
, GetSysColor(COLOR_BTNTEXT
));
405 SetBkMode(dialogDC
, TRANSPARENT
);
406 DrawTextW(dialogDC
, windowText
, CharCount
, &textRC
,
407 DT_CENTER
| DT_VCENTER
| DT_END_ELLIPSIS
| DT_SINGLELINE
);
408 SelectObject(dialogDC
, dcFont
);
411 EndPaint(hWnd
, &paint
);
414 DWORD
CreateSwitcherWindow(HINSTANCE hInstance
)
416 switchdialog
= CreateWindowExW( WS_EX_TOPMOST
|WS_EX_DLGMODALFRAME
|WS_EX_TOOLWINDOW
,
419 WS_POPUP
|WS_BORDER
|WS_DISABLED
,
427 TRACE("[ATbot] Task Switcher Window failed to create.\n");
435 DWORD
GetDialogFont(VOID
)
440 dialogFont
= (HFONT
)GetStockObject(DEFAULT_GUI_FONT
);
443 GetTextMetrics(tDC
, &tm
);
444 fontHeight
= tm
.tmHeight
;
450 void PrepareWindow(VOID
)
452 nItems
= windowCount
;
454 nCols
= CoolSwitchColumns
;
455 nRows
= (nItems
+ CoolSwitchColumns
- 1) / CoolSwitchColumns
;
456 if (nRows
> CoolSwitchRows
)
458 nRows
= CoolSwitchRows
;
459 nItems
= nRows
* nCols
;
462 itemsW
= nCols
* CX_ITEM_SPACE
;
463 itemsH
= nRows
* CY_ITEM_SPACE
;
465 totalW
= itemsW
+ 2 * DIALOG_MARGIN
;
466 totalH
= itemsH
+ 2 * DIALOG_MARGIN
;
467 totalH
+= fontHeight
+ 2 * CY_TEXT_MARGIN
;
469 ResizeAndCenter(switchdialog
, totalW
, totalH
);
472 BOOL
ProcessHotKey(VOID
)
477 EnumWindows(EnumWindowsProc
, 0);
479 if (windowCount
== 0)
482 if (windowCount
== 1)
485 CompleteSwitch(TRUE
);
491 TRACE("[ATbot] HotKey Received. Opening window.\n");
492 ShowWindow(switchdialog
, SW_SHOWNORMAL
);
493 MakeWindowActive(switchdialog
);
498 TRACE("[ATbot] HotKey Received Rotating.\n");
499 selectedWindow
= (selectedWindow
+ 1)%windowCount
;
500 InvalidateRect(switchdialog
, NULL
, TRUE
);
505 void RotateTasks(BOOL bShift
)
507 HWND hwndFirst
, hwndLast
;
510 if (windowCount
< 2 || !Esc
)
513 hwndFirst
= windowList
[0];
514 hwndLast
= windowList
[windowCount
- 1];
518 SetWindowPos(hwndLast
, HWND_TOP
, 0, 0, 0, 0,
519 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
|
520 SWP_NOOWNERZORDER
| SWP_NOREPOSITION
);
522 MakeWindowActive(hwndLast
);
524 Size
= (windowCount
- 1) * sizeof(HWND
);
525 MoveMemory(&windowList
[1], &windowList
[0], Size
);
526 windowList
[0] = hwndLast
;
530 SetWindowPos(hwndFirst
, hwndLast
, 0, 0, 0, 0,
531 SWP_NOMOVE
| SWP_NOSIZE
| SWP_NOACTIVATE
|
532 SWP_NOOWNERZORDER
| SWP_NOREPOSITION
);
534 MakeWindowActive(windowList
[1]);
536 Size
= (windowCount
- 1) * sizeof(HWND
);
537 MoveMemory(&windowList
[0], &windowList
[1], Size
);
538 windowList
[windowCount
- 1] = hwndFirst
;
542 static void MoveLeft(void)
544 selectedWindow
= selectedWindow
- 1;
545 if (selectedWindow
< 0)
546 selectedWindow
= windowCount
- 1;
547 InvalidateRect(switchdialog
, NULL
, TRUE
);
550 static void MoveRight(void)
552 selectedWindow
= (selectedWindow
+ 1) % windowCount
;
553 InvalidateRect(switchdialog
, NULL
, TRUE
);
556 static void MoveUp(void)
558 INT iRow
= selectedWindow
/ nCols
;
559 INT iCol
= selectedWindow
% nCols
;
565 selectedWindow
= iRow
* nCols
+ iCol
;
566 if (selectedWindow
>= windowCount
)
567 selectedWindow
= windowCount
- 1;
568 InvalidateRect(switchdialog
, NULL
, TRUE
);
571 static void MoveDown(void)
573 INT iRow
= selectedWindow
/ nCols
;
574 INT iCol
= selectedWindow
% nCols
;
580 selectedWindow
= iRow
* nCols
+ iCol
;
581 if (selectedWindow
>= windowCount
)
582 selectedWindow
= windowCount
- 1;
583 InvalidateRect(switchdialog
, NULL
, TRUE
);
587 DestroyAppWindows(VOID
)
589 // for every item of the icon list:
591 for (i
= 0; i
< windowCount
; ++i
)
594 DestroyIcon(iconList
[i
]);
599 LRESULT WINAPI
DoAppSwitch( WPARAM wParam
, LPARAM lParam
)
604 // FIXME: Is loading timing OK?
605 LoadCoolSwitchSettings();
610 // Already in the loop.
611 if (switchdialog
|| Esc
) return 0;
613 hwndActive
= GetActiveWindow();
614 // Nothing is active so exit.
615 if (!hwndActive
) return 0;
617 if (lParam
== VK_ESCAPE
)
622 EnumWindows(EnumWindowsProc
, 0);
627 RotateTasks(GetAsyncKeyState(VK_SHIFT
) < 0);
629 hwndActive
= GetActiveWindow();
631 if (hwndActive
== NULL
)
638 // Capture current active window.
639 SetCapture( hwndActive
);
644 if( !CreateSwitcherWindow(User32Instance
) ) goto Exit
;
645 if( !GetDialogFont() ) goto Exit
;
646 if( !ProcessHotKey() ) goto Exit
;
655 // Main message loop:
660 if (PeekMessageW( &msg
, 0, 0, 0, PM_NOREMOVE
))
662 if (!CallMsgFilterW( &msg
, MSGF_NEXTWINDOW
)) break;
663 /* remove the message from the queue */
664 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
674 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
675 if (msg
.wParam
== VK_MENU
)
677 CompleteSwitch(TRUE
);
679 else if (msg
.wParam
== VK_RETURN
)
681 CompleteSwitch(TRUE
);
683 else if (msg
.wParam
== VK_ESCAPE
)
685 TRACE("DoAppSwitch VK_ESCAPE 2\n");
686 CompleteSwitch(FALSE
);
693 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
694 if (HIWORD(msg
.lParam
) & KF_ALTDOWN
)
696 if ( msg
.wParam
== VK_TAB
)
699 if (GetKeyState(VK_SHIFT
) < 0)
708 else if ( msg
.wParam
== VK_ESCAPE
)
711 RotateTasks(GetKeyState(VK_SHIFT
) < 0);
713 else if ( msg
.wParam
== VK_LEFT
)
717 else if ( msg
.wParam
== VK_RIGHT
)
721 else if ( msg
.wParam
== VK_UP
)
725 else if ( msg
.wParam
== VK_DOWN
)
734 PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
);
735 ProcessMouseMessage(msg
.message
, msg
.lParam
);
739 if (PeekMessageW( &msg
, 0, msg
.message
, msg
.message
, PM_REMOVE
))
741 TranslateMessage(&msg
);
742 DispatchMessageW(&msg
);
749 if (switchdialog
) DestroyWindow(switchdialog
);
750 if (Esc
) DestroyAppWindows();
759 // Switch System Class Window Proc.
761 LRESULT WINAPI
SwitchWndProc_common(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
, BOOL unicode
)
765 pWnd
= ValidateHwnd(hWnd
);
770 NtUserSetWindowFNID(hWnd
, FNID_SWITCH
);
777 if (!(ati
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(*ati
))))
779 SetWindowLongPtrW( hWnd
, 0, (LONG_PTR
)ati
);
786 ati
= (PALTTABINFO
)GetWindowLongPtrW(hWnd
, 0);
787 ati
->cbSize
= sizeof(ALTTABINFO
);
788 ati
->cItems
= nItems
;
789 ati
->cColumns
= nCols
;
793 ati
->iColFocus
= (selectedWindow
- nShift
) % nCols
;
794 ati
->iRowFocus
= (selectedWindow
- nShift
) / nCols
;
801 ati
->cxItem
= CX_ITEM_SPACE
;
802 ati
->cyItem
= CY_ITEM_SPACE
;
803 ati
->ptStart
= ptStart
;
808 ProcessMouseMessage(uMsg
, lParam
);
812 if (wParam
== WA_INACTIVE
)
814 CompleteSwitch(FALSE
);
824 ati
= (PALTTABINFO
)GetWindowLongPtrW(hWnd
, 0);
825 HeapFree( GetProcessHeap(), 0, ati
);
826 SetWindowLongPtrW( hWnd
, 0, 0 );
828 NtUserSetWindowFNID(hWnd
, FNID_DESTROY
);
831 return DefWindowProcW(hWnd
, uMsg
, wParam
, lParam
);
834 LRESULT WINAPI
SwitchWndProcA(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
836 return SwitchWndProc_common(hWnd
, uMsg
, wParam
, lParam
, FALSE
);
839 LRESULT WINAPI
SwitchWndProcW(HWND hWnd
, UINT uMsg
, WPARAM wParam
, LPARAM lParam
)
841 return SwitchWndProc_common(hWnd
, uMsg
, wParam
, lParam
, TRUE
);