Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenappswitch.c
Go to the documentation of this file.
00001 /* 00002 * COPYRIGHT: See COPYING in the top level directory 00003 * PROJECT: ReactOS system libraries 00004 * FILE: dll/win32/user32/controls/appswitch.c 00005 * PURPOSE: app switching functionality 00006 * PROGRAMMERS: Johannes Anderwald (janderwald@reactos.org) 00007 * David Quintana (gigaherz@gmail.com) 00008 */ 00009 00010 #include <user32.h> 00011 00012 #include <wine/debug.h> 00013 WINE_DEFAULT_DEBUG_CHANNEL(user32); 00014 00015 // limit the number of windows shown in the alt-tab window 00016 // 120 windows results in (12*40) by (10*40) pixels worth of icons. 00017 #define MAX_WINDOWS 120 00018 00019 // Global variables 00020 HWND switchdialog; 00021 HFONT dialogFont; 00022 int selectedWindow = 0; 00023 BOOL isOpen = FALSE; 00024 00025 int fontHeight=0; 00026 00027 WCHAR windowText[1024]; 00028 00029 HWND windowList[MAX_WINDOWS]; 00030 HICON iconList[MAX_WINDOWS]; 00031 int windowCount = 0; 00032 00033 int cxBorder, cyBorder; 00034 int nItems, nCols, nRows; 00035 int itemsW, itemsH; 00036 int totalW, totalH; 00037 int xOffset, yOffset; 00038 POINT pt; 00039 00040 void ResizeAndCenter(HWND hwnd, int width, int height) 00041 { 00042 int screenwidth = GetSystemMetrics(SM_CXSCREEN); 00043 int screenheight = GetSystemMetrics(SM_CYSCREEN); 00044 00045 pt.x = (screenwidth - width) / 2; 00046 pt.y = (screenheight - height) / 2; 00047 00048 MoveWindow(hwnd, pt.x, pt.y, width, height, FALSE); 00049 } 00050 00051 void MakeWindowActive(HWND hwnd) 00052 { 00053 WINDOWPLACEMENT wpl; 00054 00055 wpl.length = sizeof(WINDOWPLACEMENT); 00056 GetWindowPlacement(hwnd, &wpl); 00057 00058 TRACE("GetWindowPlacement wpl.showCmd %d\n",wpl.showCmd); 00059 if (wpl.showCmd == SW_SHOWMINIMIZED) 00060 ShowWindowAsync(hwnd, SW_RESTORE); 00061 00062 BringWindowToTop(hwnd); // same as: SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); ? 00063 SetForegroundWindow(hwnd); 00064 } 00065 00066 void CompleteSwitch(BOOL doSwitch) 00067 { 00068 if (!isOpen) 00069 return; 00070 00071 isOpen = FALSE; 00072 00073 TRACE("[ATbot] CompleteSwitch Hiding Window.\n"); 00074 ShowWindow(switchdialog, SW_HIDE); 00075 00076 if(doSwitch) 00077 { 00078 if(selectedWindow >= windowCount) 00079 return; 00080 00081 // FIXME: workaround because reactos fails to activate the previous window correctly. 00082 //if(selectedWindow != 0) 00083 { 00084 HWND hwnd = windowList[selectedWindow]; 00085 00086 GetWindowTextW(hwnd, windowText, 1023); 00087 00088 TRACE("[ATbot] CompleteSwitch Switching to 0x%08x (%ls)\n", hwnd, windowText); 00089 00090 MakeWindowActive(hwnd); 00091 } 00092 } 00093 00094 windowCount = 0; 00095 } 00096 00097 BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam) 00098 { 00099 HICON hIcon; 00100 00101 UNREFERENCED_PARAMETER(lParam); 00102 00103 if (!IsWindowVisible(window)) 00104 return TRUE; 00105 00106 GetClassNameW(window,windowText,4095); 00107 if ((wcscmp(L"Shell_TrayWnd",windowText)==0) || 00108 (wcscmp(L"Progman",windowText)==0) ) 00109 return TRUE; 00110 00111 // First try to get the big icon assigned to the window 00112 hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_BIG, 0); 00113 if (!hIcon) 00114 { 00115 // If no icon is assigned, try to get the icon assigned to the windows' class 00116 hIcon = (HICON)GetClassLongPtrW(window, GCL_HICON); 00117 if (!hIcon) 00118 { 00119 // If we still don't have an icon, see if we can do with the small icon, 00120 // or a default application icon 00121 hIcon = (HICON)SendMessageW(window, WM_GETICON, ICON_SMALL2, 0); 00122 if (!hIcon) 00123 { 00124 // If all fails, give up and continue with the next window 00125 return TRUE; 00126 } 00127 } 00128 } 00129 00130 windowList[windowCount] = window; 00131 iconList[windowCount] = CopyIcon(hIcon); 00132 00133 windowCount++; 00134 00135 // If we got to the max number of windows, 00136 // we won't be able to add any more 00137 if(windowCount == MAX_WINDOWS) 00138 return FALSE; 00139 00140 return TRUE; 00141 } 00142 00143 // Function mostly compatible with the normal EnumWindows, 00144 // except it lists in Z-Order and it doesn't ensure consistency 00145 // if a window is removed while enumerating 00146 void EnumWindowsZOrder(WNDENUMPROC callback, LPARAM lParam) 00147 { 00148 HWND next = GetTopWindow(NULL); 00149 while (next != NULL) 00150 { 00151 if(!callback(next, lParam)) 00152 break; 00153 next = GetWindow(next, GW_HWNDNEXT); 00154 } 00155 } 00156 00157 void ProcessMouseMessage(UINT message, LPARAM lParam) 00158 { 00159 int xPos = LOWORD(lParam); 00160 int yPos = HIWORD(lParam); 00161 00162 int xIndex = (xPos - xOffset)/40; 00163 int xOff = (xPos - xOffset)%40; 00164 00165 int yIndex = (yPos - yOffset)/40; 00166 int yOff = (yPos - yOffset)%40; 00167 00168 if(xOff > 32 || xIndex > nItems) 00169 return; 00170 00171 if(yOff > 32 || yIndex > nRows) 00172 return; 00173 00174 selectedWindow = (yIndex*nCols) + xIndex; 00175 if (message == WM_MOUSEMOVE) 00176 { 00177 InvalidateRect(switchdialog, NULL, TRUE); 00178 //RedrawWindow(switchdialog, NULL, NULL, 0); 00179 } 00180 else 00181 { 00182 selectedWindow = (yIndex*nCols) + xIndex; 00183 CompleteSwitch(TRUE); 00184 } 00185 } 00186 00187 void OnPaint(HWND hWnd) 00188 { 00189 HDC dialogDC; 00190 PAINTSTRUCT paint; 00191 RECT cRC, textRC; 00192 int i; 00193 HBRUSH hBrush; 00194 HPEN hPen; 00195 HFONT dcFont; 00196 COLORREF cr; 00197 int nch = GetWindowTextW(windowList[selectedWindow], windowText, 1023); 00198 00199 dialogDC = BeginPaint(hWnd, &paint); 00200 { 00201 GetClientRect(hWnd, &cRC); 00202 FillRect(dialogDC, &cRC, GetSysColorBrush(COLOR_MENU)); 00203 00204 for(i=0; i< windowCount; i++) 00205 { 00206 HICON hIcon = iconList[i]; 00207 00208 int xpos = xOffset + 40 * (i % nCols); 00209 int ypos = yOffset + 40 * (i / nCols); 00210 00211 if (selectedWindow == i) 00212 { 00213 hBrush = GetSysColorBrush(COLOR_HIGHLIGHT); 00214 } 00215 else 00216 { 00217 hBrush = GetSysColorBrush(COLOR_MENU); 00218 } 00219 #if TRUE 00220 cr = GetSysColor(COLOR_BTNTEXT); // doesn't look right! >_< 00221 hPen = CreatePen(PS_DOT, 1, cr); 00222 SelectObject(dialogDC, hPen); 00223 SelectObject(dialogDC, hBrush); 00224 Rectangle(dialogDC, xpos-2, ypos-2, xpos+32+2, ypos+32+2); 00225 DeleteObject(hPen); 00226 // Must NOT destroy the system brush! 00227 #else 00228 RECT rc = { xpos-2, ypos-2, xpos+32+2, ypos+32+2 }; 00229 FillRect(dialogDC, &rc, hBrush); 00230 #endif 00231 DrawIcon(dialogDC, xpos, ypos, hIcon); 00232 } 00233 00234 dcFont = SelectObject(dialogDC, dialogFont); 00235 SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT)); 00236 SetBkColor(dialogDC, GetSysColor(COLOR_BTNFACE)); 00237 00238 textRC.top = itemsH; 00239 textRC.left = 8; 00240 textRC.right = totalW - 8; 00241 textRC.bottom = totalH - 8; 00242 DrawTextW(dialogDC, windowText, nch, &textRC, DT_CENTER|DT_END_ELLIPSIS); 00243 SelectObject(dialogDC, dcFont); 00244 } 00245 EndPaint(hWnd, &paint); 00246 } 00247 00248 DWORD CreateSwitcherWindow(HINSTANCE hInstance) 00249 { 00250 switchdialog = CreateWindowExW( WS_EX_TOPMOST|WS_EX_DLGMODALFRAME|WS_EX_TOOLWINDOW, 00251 WC_SWITCH, 00252 L"", 00253 WS_POPUP|WS_BORDER|WS_DISABLED, 00254 CW_USEDEFAULT, 00255 CW_USEDEFAULT, 00256 400, 150, 00257 NULL, NULL, 00258 hInstance, NULL); 00259 if (!switchdialog) 00260 { 00261 TRACE("[ATbot] Task Switcher Window failed to create.\n"); 00262 return 0; 00263 } 00264 00265 isOpen = FALSE; 00266 return 1; 00267 } 00268 00269 DWORD GetDialogFont() 00270 { 00271 HDC tDC; 00272 TEXTMETRIC tm; 00273 00274 dialogFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT); 00275 00276 tDC = GetDC(0); 00277 GetTextMetrics(tDC, &tm); 00278 fontHeight = tm.tmHeight; 00279 ReleaseDC(0, tDC); 00280 00281 return 1; 00282 } 00283 00284 void PrepareWindow() 00285 { 00286 cxBorder = GetSystemMetrics(SM_CXBORDER); 00287 cyBorder = GetSystemMetrics(SM_CYBORDER); 00288 00289 nItems = windowCount; 00290 nCols = min(max(nItems,8),12); 00291 nRows = (nItems+nCols-1)/nCols; 00292 00293 itemsW = nCols*32 + (nCols+1)*8; 00294 itemsH = nRows*32 + (nRows+1)*8; 00295 00296 totalW = itemsW + 2*cxBorder + 4; 00297 totalH = itemsH + 2*cyBorder + fontHeight + 8; // give extra pixels for the window title 00298 00299 xOffset = 8; 00300 yOffset = 8; 00301 00302 if (nItems < nCols) 00303 { 00304 int w2 = nItems*32 + (nItems-1)*8; 00305 xOffset = (itemsW-w2)/2; 00306 } 00307 ResizeAndCenter(switchdialog, totalW, totalH); 00308 } 00309 00310 void ProcessHotKey() 00311 { 00312 if (!isOpen) 00313 { 00314 windowCount=0; 00315 EnumWindowsZOrder(EnumerateCallback, 0); 00316 00317 if (windowCount < 2) 00318 return; 00319 00320 selectedWindow = 1; 00321 00322 TRACE("[ATbot] HotKey Received. Opening window.\n"); 00323 ShowWindow(switchdialog, SW_SHOWNORMAL); 00324 MakeWindowActive(switchdialog); 00325 isOpen = TRUE; 00326 } 00327 else 00328 { 00329 TRACE("[ATbot] HotKey Received Rotating.\n"); 00330 selectedWindow = (selectedWindow + 1)%windowCount; 00331 InvalidateRect(switchdialog, NULL, TRUE); 00332 } 00333 } 00334 00335 LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam ) 00336 { 00337 HWND hwnd; 00338 MSG msg; 00339 BOOL Esc = FALSE; 00340 INT Count = 0; 00341 WCHAR Text[1024]; 00342 00343 switchdialog = NULL; 00344 00345 switch (lParam) 00346 { 00347 case VK_TAB: 00348 if( !CreateSwitcherWindow(User32Instance) ) return 0; 00349 if( !GetDialogFont() ) return 0; 00350 ProcessHotKey(); 00351 break; 00352 00353 case VK_ESCAPE: 00354 windowCount = 0; 00355 Count = 0; 00356 EnumWindowsZOrder(EnumerateCallback, 0); 00357 if (windowCount < 2) return 0; 00358 if (wParam == SC_NEXTWINDOW) 00359 Count = 1; 00360 else 00361 { 00362 if (windowCount == 2) 00363 Count = 0; 00364 else 00365 Count = windowCount - 1; 00366 } 00367 TRACE("DoAppSwitch VK_ESCAPE 1 Count %d windowCount %d\n",Count,windowCount); 00368 hwnd = windowList[Count]; 00369 GetWindowTextW(hwnd, Text, 1023); 00370 TRACE("[ATbot] Switching to 0x%08x (%ls)\n", hwnd, Text); 00371 MakeWindowActive(hwnd); 00372 Esc = TRUE; 00373 break; 00374 00375 default: 00376 return 0; 00377 } 00378 // Main message loop: 00379 while (1) 00380 { 00381 for (;;) 00382 { 00383 if (PeekMessageW( &msg, 0, 0, 0, PM_NOREMOVE )) 00384 { 00385 if (!CallMsgFilterW( &msg, MSGF_NEXTWINDOW )) break; 00386 /* remove the message from the queue */ 00387 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 00388 } 00389 else 00390 WaitMessage(); 00391 } 00392 00393 switch (msg.message) 00394 { 00395 case WM_KEYUP: 00396 { 00397 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 00398 if (msg.wParam == VK_MENU) 00399 { 00400 CompleteSwitch(TRUE); 00401 } 00402 else if (msg.wParam == VK_RETURN) 00403 { 00404 CompleteSwitch(TRUE); 00405 } 00406 else if (msg.wParam == VK_ESCAPE) 00407 { 00408 TRACE("DoAppSwitch VK_ESCAPE 2\n"); 00409 CompleteSwitch(FALSE); 00410 } 00411 goto Exit; //break; 00412 } 00413 00414 case WM_SYSKEYDOWN: 00415 { 00416 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 00417 if (HIWORD(msg.lParam) & KF_ALTDOWN) 00418 { 00419 INT Shift; 00420 if ( msg.wParam == VK_TAB ) 00421 { 00422 if (Esc) break; 00423 Shift = GetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW; 00424 if (Shift == SC_NEXTWINDOW) 00425 { 00426 selectedWindow = (selectedWindow + 1)%windowCount; 00427 } 00428 else 00429 { 00430 selectedWindow = selectedWindow - 1; 00431 if (selectedWindow < 0) 00432 selectedWindow = windowCount - 1; 00433 } 00434 InvalidateRect(switchdialog, NULL, TRUE); 00435 } 00436 else if ( msg.wParam == VK_ESCAPE ) 00437 { 00438 if (!Esc) break; 00439 if (windowCount < 2) 00440 goto Exit; 00441 if (wParam == SC_NEXTWINDOW) 00442 { 00443 Count = (Count + 1)%windowCount; 00444 } 00445 else 00446 { 00447 Count--; 00448 if (Count < 0) 00449 Count = windowCount - 1; 00450 } 00451 hwnd = windowList[Count]; 00452 GetWindowTextW(hwnd, Text, 1023); 00453 MakeWindowActive(hwnd); 00454 } 00455 } 00456 break; 00457 } 00458 00459 case WM_LBUTTONUP: 00460 PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE ); 00461 ProcessMouseMessage(msg.message, msg.lParam); 00462 goto Exit; 00463 00464 default: 00465 if (PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE )) 00466 { 00467 TranslateMessage(&msg); 00468 DispatchMessageW(&msg); 00469 } 00470 break; 00471 } 00472 } 00473 Exit: 00474 if (switchdialog) DestroyWindow(switchdialog); 00475 switchdialog = NULL; 00476 selectedWindow = 0; 00477 windowCount = 0; 00478 return 0; 00479 } 00480 00481 VOID 00482 DestroyAppWindows() 00483 { 00484 INT i; 00485 for (i=0; i< windowCount; i++) 00486 { 00487 HICON hIcon = iconList[i]; 00488 DestroyIcon(hIcon); 00489 } 00490 } 00491 00492 // 00493 // Switch System Class Window Proc. 00494 // 00495 LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL unicode ) 00496 { 00497 PWND pWnd; 00498 PALTTABINFO ati; 00499 pWnd = ValidateHwnd(hWnd); 00500 if (pWnd) 00501 { 00502 if (!pWnd->fnid) 00503 { 00504 NtUserSetWindowFNID(hWnd, FNID_SWITCH); 00505 } 00506 } 00507 00508 switch (uMsg) 00509 { 00510 case WM_NCCREATE: 00511 if (!(ati = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*ati)))) 00512 return 0; 00513 SetWindowLongPtrW( hWnd, 0, (LONG_PTR)ati ); 00514 return TRUE; 00515 00516 case WM_SHOWWINDOW: 00517 if (wParam == TRUE) 00518 { 00519 PrepareWindow(); 00520 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0); 00521 ati->cItems = nItems; 00522 ati->cxItem = ati->cyItem = 43; 00523 ati->cRows = nRows; 00524 ati->cColumns = nCols; 00525 } 00526 return 0; 00527 00528 case WM_MOUSEMOVE: 00529 ProcessMouseMessage(uMsg, lParam); 00530 return 0; 00531 00532 case WM_ACTIVATE: 00533 if (wParam == WA_INACTIVE) 00534 { 00535 CompleteSwitch(FALSE); 00536 } 00537 return 0; 00538 00539 case WM_PAINT: 00540 OnPaint(hWnd); 00541 return 0; 00542 00543 case WM_DESTROY: 00544 isOpen = FALSE; 00545 ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0); 00546 HeapFree( GetProcessHeap(), 0, ati ); 00547 SetWindowLongPtrW( hWnd, 0, 0 ); 00548 DestroyAppWindows(); 00549 NtUserSetWindowFNID(hWnd, FNID_DESTROY); 00550 return 0; 00551 } 00552 return DefWindowProcW(hWnd, uMsg, wParam, lParam); 00553 } 00554 00555 LRESULT WINAPI SwitchWndProcA(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 00556 { 00557 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, FALSE); 00558 } 00559 00560 LRESULT WINAPI SwitchWndProcW(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) 00561 { 00562 return SwitchWndProc_common(hWnd, uMsg, wParam, lParam, TRUE); 00563 } Generated on Sun May 27 2012 04:38:37 for ReactOS by
1.7.6.1
|