[User32]
[reactos.git] / reactos / win32ss / user / user32 / controls / appswitch.c
index 229ee84..cd13841 100644 (file)
@@ -5,13 +5,37 @@
  * PURPOSE:         app switching functionality
  * PROGRAMMERS:     Johannes Anderwald (johannes.anderwald@reactos.org)
  *                  David Quintana (gigaherz@gmail.com)
+ *                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
  */
 
+//
+// TODO:
+//       Move to Win32k.
+//       Add registry support.
+//
+//
+
+
 #include <user32.h>
 
 #include <wine/debug.h>
 WINE_DEFAULT_DEBUG_CHANNEL(user32);
 
+#define DIALOG_MARGIN   8       // margin of dialog contents
+
+#define CX_ICON         32      // width of icon
+#define CY_ICON         32      // height of icon
+#define ICON_MARGIN     4       // margin width around an icon
+
+#define CX_ITEM         (CX_ICON + 2 * ICON_MARGIN)
+#define CY_ITEM         (CY_ICON + 2 * ICON_MARGIN)
+#define ITEM_MARGIN     4       // margin width around an item
+
+#define CX_ITEM_SPACE   (CX_ITEM + 2 * ITEM_MARGIN)
+#define CY_ITEM_SPACE   (CY_ITEM + 2 * ITEM_MARGIN)
+
+#define CY_TEXT_MARGIN  4       // margin height around text
+
 // limit the number of windows shown in the alt-tab window
 // 120 windows results in (12*40) by (10*40) pixels worth of icons.
 #define MAX_WINDOWS 120
@@ -35,28 +59,74 @@ int nItems, nCols, nRows;
 int itemsW, itemsH;
 int totalW, totalH;
 int xOffset, yOffset;
-POINT pt;
+POINT ptStart;
+
+int nShift = 0;
+
+BOOL Esc = FALSE;
+
+BOOL CoolSwitch = TRUE;
+int CoolSwitchRows = 3;
+int CoolSwitchColumns = 7;
+
+// window style
+const DWORD Style = WS_POPUP | WS_BORDER | WS_DISABLED;
+const DWORD ExStyle = WS_EX_TOPMOST | WS_EX_DLGMODALFRAME | WS_EX_TOOLWINDOW;
+
+DWORD wtodw(const WCHAR *psz)
+{
+   const WCHAR *pch = psz;
+   DWORD Value = 0;
+   while ('0' <= *pch && *pch <= '9')
+   {
+      Value *= 10;
+      Value += *pch - L'0';
+   }
+   return Value;
+}
+
+BOOL LoadCoolSwitchSettings(void)
+{
+   CoolSwitch = TRUE;
+   CoolSwitchRows = 3;
+   CoolSwitchColumns = 7;
+
+   // FIXME: load the settings from registry
+
+   TRACE("CoolSwitch: %d\n", CoolSwitch);
+   TRACE("CoolSwitchRows: %d\n", CoolSwitchRows);
+   TRACE("CoolSwitchColumns: %d\n", CoolSwitchColumns);
+
+   return TRUE;
+}
 
 void ResizeAndCenter(HWND hwnd, int width, int height)
 {
+   int x, y;
+   RECT Rect;
+
    int screenwidth = GetSystemMetrics(SM_CXSCREEN);
    int screenheight = GetSystemMetrics(SM_CYSCREEN);
 
-   pt.x = (screenwidth - width) / 2;
-   pt.y = (screenheight - height) / 2;
+   x = (screenwidth - width) / 2;
+   y = (screenheight - height) / 2;
+
+   SetRect(&Rect, x, y, x + width, y + height);
+   AdjustWindowRectEx(&Rect, Style, FALSE, ExStyle);
+
+   x = Rect.left;
+   y = Rect.top;
+   width = Rect.right - Rect.left;
+   height = Rect.bottom - Rect.top;
+   MoveWindow(hwnd, x, y, width, height, FALSE);
 
-   MoveWindow(hwnd, pt.x, pt.y, width, height, FALSE);
+   ptStart.x = x;
+   ptStart.y = y;
 }
 
 void MakeWindowActive(HWND hwnd)
 {
-   WINDOWPLACEMENT wpl;
-
-   wpl.length = sizeof(WINDOWPLACEMENT);
-   GetWindowPlacement(hwnd, &wpl);
-
-   TRACE("GetWindowPlacement wpl.showCmd %d\n",wpl.showCmd);
-   if (wpl.showCmd == SW_SHOWMINIMIZED)
+   if (IsIconic(hwnd))
       ShowWindowAsync(hwnd, SW_RESTORE);
 
    BringWindowToTop(hwnd);  // same as: SetWindowPos(hwnd,HWND_TOP,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); ?
@@ -139,7 +209,7 @@ BOOL CALLBACK EnumerateCallback(HWND window, LPARAM lParam)
 
    // If we got to the max number of windows,
    // we won't be able to add any more
-   if(windowCount == MAX_WINDOWS)
+   if(windowCount >= MAX_WINDOWS)
       return FALSE;
 
    return TRUE;
@@ -164,17 +234,14 @@ void ProcessMouseMessage(UINT message, LPARAM lParam)
    int xPos = LOWORD(lParam);
    int yPos = HIWORD(lParam);
 
-   int xIndex = (xPos - xOffset)/40;
-   int xOff   = (xPos - xOffset)%40;
+   int xIndex = (xPos - DIALOG_MARGIN) / CX_ITEM_SPACE;
+   int yIndex = (yPos - DIALOG_MARGIN) / CY_ITEM_SPACE;
 
-   int yIndex = (yPos - yOffset)/40;
-   int yOff   = (yPos - yOffset)%40;
-
-   if(xOff > 32 || xIndex > nItems)
-      return;
-
-   if(yOff > 32 || yIndex > nRows)
-      return;
+   if (xIndex < 0 || nCols <= xIndex ||
+       yIndex < 0 || nRows <= yIndex)
+   {
+        return;
+   }
 
    selectedWindow = (yIndex*nCols) + xIndex;
    if (message == WM_MOUSEMOVE)
@@ -191,63 +258,95 @@ void ProcessMouseMessage(UINT message, LPARAM lParam)
 
 void OnPaint(HWND hWnd)
 {
-   HDC dialogDC;
-   PAINTSTRUCT paint;
-   RECT cRC, textRC;
-   int i;
-   HBRUSH hBrush;
-   HPEN hPen;
-   HFONT dcFont;
-   COLORREF cr;
-   int nch = GetWindowTextW(windowList[selectedWindow], windowText, _countof(windowText));
-
-   dialogDC = BeginPaint(hWnd, &paint);
-   {
-      GetClientRect(hWnd, &cRC);
-      FillRect(dialogDC, &cRC, GetSysColorBrush(COLOR_MENU));
+    HDC dialogDC;
+    PAINTSTRUCT paint;
+    RECT cRC, textRC;
+    int i, xPos, yPos, CharCount;
+    HFONT dcFont;
+    HICON hIcon;
+    HPEN hPen;
+    COLORREF Color;
+
+    // check
+    if (nCols == 0 || nItems == 0)
+        return;
+
+    // begin painting
+    dialogDC = BeginPaint(hWnd, &paint);
+    if (dialogDC == NULL)
+        return;
+
+    // fill the client area
+    GetClientRect(hWnd, &cRC);
+    FillRect(dialogDC, &cRC, (HBRUSH)(COLOR_3DFACE + 1));
+
+    // if the selection index exceeded the display items, then
+    // do display item shifting
+    if (selectedWindow >= nItems)
+        nShift = selectedWindow - nItems + 1;
+    else
+        nShift = 0;
+
+    for (i = 0; i < nItems; ++i)
+    {
+        // get the icon to display
+        hIcon = iconList[i + nShift];
 
-      for(i=0; i< windowCount; i++)
-      {
-         HICON hIcon = iconList[i];
+        // calculate the position where we start drawing
+        xPos = DIALOG_MARGIN + CX_ITEM_SPACE * (i % nCols) + ITEM_MARGIN;
+        yPos = DIALOG_MARGIN + CY_ITEM_SPACE * (i / nCols) + ITEM_MARGIN;
 
-         int xpos = xOffset + 40 * (i % nCols);
-         int ypos = yOffset + 40 * (i / nCols);
+        // centering
+        if (nItems < CoolSwitchColumns)
+        {
+            xPos += (itemsW - nItems * CX_ITEM_SPACE) / 2;
+        }
 
-         if (selectedWindow == i)
-         {
-            hBrush = GetSysColorBrush(COLOR_HIGHLIGHT);
-         }
-         else
-         {
-            hBrush = GetSysColorBrush(COLOR_MENU);
-         }
-#if TRUE
-         cr = GetSysColor(COLOR_BTNTEXT); // doesn't look right! >_<
-         hPen = CreatePen(PS_DOT, 1, cr);
-         SelectObject(dialogDC, hPen);
-         SelectObject(dialogDC, hBrush);
-         Rectangle(dialogDC, xpos-2, ypos-2, xpos+32+2, ypos+32+2);
-         DeleteObject(hPen);
-         // Must NOT destroy the system brush!
-#else
-         RECT rc = { xpos-2, ypos-2, xpos+32+2, ypos+32+2 };
-         FillRect(dialogDC, &rc, hBrush);
-#endif
-         DrawIcon(dialogDC, xpos, ypos, hIcon);
-      }
+        // if this position is selected,
+        if (selectedWindow == i + nShift)
+        {
+            // create a solid pen
+            Color = GetSysColor(COLOR_HIGHLIGHT);
+            hPen = CreatePen(PS_SOLID, 1, Color);
+
+            // draw a rectangle with using the pen
+            SelectObject(dialogDC, hPen);
+            SelectObject(dialogDC, GetStockObject(NULL_BRUSH));
+            Rectangle(dialogDC, xPos, yPos, xPos + CX_ITEM, yPos + CY_ITEM);
+            Rectangle(dialogDC, xPos + 1, yPos + 1,
+                                xPos + CX_ITEM - 1, yPos + CY_ITEM - 1);
+
+            // delete the pen
+            DeleteObject(hPen);
+        }
+
+        // draw icon
+        DrawIconEx(dialogDC, xPos + ICON_MARGIN, yPos + ICON_MARGIN,
+                   hIcon, CX_ICON, CY_ICON, 0, NULL, DI_NORMAL);
+    }
 
-      dcFont = SelectObject(dialogDC, dialogFont);
-      SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT));
-      SetBkMode(dialogDC, TRANSPARENT);
+    // set the text rectangle
+    SetRect(&textRC, DIALOG_MARGIN, DIALOG_MARGIN + itemsH,
+            totalW - DIALOG_MARGIN, totalH - DIALOG_MARGIN);
 
-      textRC.top = itemsH;
-      textRC.left = 8;
-      textRC.right = totalW - 8;
-      textRC.bottom = totalH - 8;
-      DrawTextW(dialogDC, windowText, nch, &textRC, DT_CENTER|DT_END_ELLIPSIS);
-      SelectObject(dialogDC, dcFont);
-   }
-   EndPaint(hWnd, &paint);
+    // draw the sunken button around text
+    DrawFrameControl(dialogDC, &textRC, DFC_BUTTON,
+                     DFCS_BUTTONPUSH | DFCS_PUSHED);
+
+    // get text
+    CharCount = GetWindowTextW(windowList[selectedWindow], windowText,
+                               _countof(windowText));
+
+    // draw text
+    dcFont = SelectObject(dialogDC, dialogFont);
+    SetTextColor(dialogDC, GetSysColor(COLOR_BTNTEXT));
+    SetBkMode(dialogDC, TRANSPARENT);
+    DrawTextW(dialogDC, windowText, CharCount, &textRC,
+              DT_CENTER | DT_VCENTER | DT_END_ELLIPSIS | DT_SINGLELINE);
+    SelectObject(dialogDC, dcFont);
+
+    // end painting
+    EndPaint(hWnd, &paint);
 }
 
 DWORD CreateSwitcherWindow(HINSTANCE hInstance)
@@ -288,27 +387,23 @@ DWORD GetDialogFont(VOID)
 
 void PrepareWindow(VOID)
 {
-   cxBorder = GetSystemMetrics(SM_CXBORDER);
-   cyBorder = GetSystemMetrics(SM_CYBORDER);
-
    nItems = windowCount;
-   nCols = min(max(nItems,8),12);
-   nRows = (nItems+nCols-1)/nCols;
 
-   itemsW = nCols*32 + (nCols+1)*8;
-   itemsH = nRows*32 + (nRows+1)*8;
+   nCols = CoolSwitchColumns;
+   nRows = (nItems + CoolSwitchColumns - 1) / CoolSwitchColumns;
+   if (nRows > CoolSwitchRows)
+   {
+      nRows = CoolSwitchRows;
+      nItems = nRows * nCols;
+   }
 
-   totalW = itemsW + 2*cxBorder + 4;
-   totalH = itemsH + 2*cyBorder + fontHeight + 8; // give extra pixels for the window title
+   itemsW = nCols * CX_ITEM_SPACE;
+   itemsH = nRows * CY_ITEM_SPACE;
 
-   xOffset = 8;
-   yOffset = 8;
+   totalW = itemsW + 2 * DIALOG_MARGIN;
+   totalH = itemsH + 2 * DIALOG_MARGIN;
+   totalH += fontHeight + 2 * CY_TEXT_MARGIN;
 
-   if (nItems < nCols)
-   {
-      int w2 = nItems*32 + (nItems-1)*8;
-      xOffset = (itemsW-w2)/2;
-   }
    ResizeAndCenter(switchdialog, totalW, totalH);
 }
 
@@ -338,20 +433,95 @@ BOOL ProcessHotKey(VOID)
    return TRUE;
 }
 
+void RotateTasks(BOOL bShift)
+{
+    HWND hwndFirst, hwndLast;
+    DWORD Size;
+
+    if (windowCount < 2 || !Esc)
+        return;
+
+    hwndFirst = windowList[0];
+    hwndLast = windowList[windowCount - 1];
+
+    if (bShift)
+    {
+        SetWindowPos(hwndLast, HWND_TOP, 0, 0, 0, 0,
+                     SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
+                     SWP_NOOWNERZORDER | SWP_NOREPOSITION);
+
+        MakeWindowActive(hwndLast);
+
+        Size = (windowCount - 1) * sizeof(HWND);
+        MoveMemory(&windowList[1], &windowList[0], Size);
+        windowList[0] = hwndLast;
+    }
+    else
+    {
+        SetWindowPos(hwndFirst, hwndLast, 0, 0, 0, 0,
+                     SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE |
+                     SWP_NOOWNERZORDER | SWP_NOREPOSITION);
+
+        MakeWindowActive(windowList[1]);
+
+        Size = (windowCount - 1) * sizeof(HWND);
+        MoveMemory(&windowList[0], &windowList[1], Size);
+        windowList[windowCount - 1] = hwndFirst;
+    }
+}
+
+VOID
+DestroyAppWindows(VOID)
+{
+    // for every item of the icon list:
+    INT i;
+    for (i = 0; i < windowCount; ++i)
+    {
+        // destroy the icon
+        DestroyIcon(iconList[i]);
+        iconList[i] = NULL;
+    }
+}
+
 LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
 {
-   HWND hwnd, hwndActive;
+   HWND hwndActive;
    MSG msg;
-   BOOL Esc = FALSE;
-   INT Count = 0;
-   WCHAR Text[1024];
+
+   // FIXME: Is loading timing OK?
+   LoadCoolSwitchSettings();
+
+   if (!CoolSwitch)
+      return 0;
 
    // Already in the loop.
-   if (switchdialog) return 0;
+   if (switchdialog || Esc) return 0;
 
    hwndActive = GetActiveWindow();
    // Nothing is active so exit.
    if (!hwndActive) return 0;
+
+   if (lParam == VK_ESCAPE)
+   {
+      Esc = TRUE;
+
+      windowCount = 0;
+      EnumWindowsZOrder(EnumerateCallback, 0);
+
+      if (windowCount < 2)
+          return 0;
+
+      RotateTasks(GetAsyncKeyState(VK_SHIFT) < 0);
+
+      hwndActive = GetActiveWindow();
+
+      if (hwndActive == NULL)
+      {
+          Esc = FALSE;
+          return 0;
+      }
+   }
+
    // Capture current active window.
    SetCapture( hwndActive );
 
@@ -364,25 +534,6 @@ LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
          break;
 
       case VK_ESCAPE:
-         windowCount = 0;
-         Count = 0;
-         EnumWindowsZOrder(EnumerateCallback, 0);
-         if (windowCount < 2) goto Exit;
-         if (wParam == SC_NEXTWINDOW)
-            Count = 1;
-         else
-         {
-            if (windowCount == 2)
-               Count = 0;
-            else
-               Count = windowCount - 1;
-         }
-         TRACE("DoAppSwitch VK_ESCAPE 1 Count %d windowCount %d\n",Count,windowCount);
-         hwnd = windowList[Count];
-         GetWindowTextW(hwnd, Text, _countof(Text));
-         TRACE("[ATbot] Switching to 0x%08x (%ls)\n", hwnd, Text);
-         MakeWindowActive(hwnd);
-         Esc = TRUE;
          break;
 
       default:
@@ -429,41 +580,25 @@ LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
           PeekMessageW( &msg, 0, msg.message, msg.message, PM_REMOVE );
           if (HIWORD(msg.lParam) & KF_ALTDOWN)
           {
-             INT Shift;
              if ( msg.wParam == VK_TAB )
              {
                 if (Esc) break;
-                Shift = GetKeyState(VK_SHIFT) & 0x8000 ? SC_PREVWINDOW : SC_NEXTWINDOW;
-                if (Shift == SC_NEXTWINDOW)
-                {
-                   selectedWindow = (selectedWindow + 1)%windowCount;
-                }
-                else
+                if (GetKeyState(VK_SHIFT) < 0)
                 {
                    selectedWindow = selectedWindow - 1;
                    if (selectedWindow < 0)
                       selectedWindow = windowCount - 1;
                 }
+                else
+                {
+                   selectedWindow = (selectedWindow + 1)%windowCount;
+                }
                 InvalidateRect(switchdialog, NULL, TRUE);
              }
              else if ( msg.wParam == VK_ESCAPE )
              {
                 if (!Esc) break;
-                if (windowCount < 2)
-                   goto Exit;
-                if (wParam == SC_NEXTWINDOW)
-                {
-                   Count = (Count + 1)%windowCount;
-                }
-                else
-                {
-                   Count--;
-                   if (Count < 0)
-                      Count = windowCount - 1;
-                }
-                hwnd = windowList[Count];
-                GetWindowTextW(hwnd, Text, _countof(Text));
-                MakeWindowActive(hwnd);
+                RotateTasks(GetKeyState(VK_SHIFT) < 0);
              }
           }
           break;
@@ -486,23 +621,14 @@ LRESULT WINAPI DoAppSwitch( WPARAM wParam, LPARAM lParam )
 Exit:
    ReleaseCapture();
    if (switchdialog) DestroyWindow(switchdialog);
+   if (Esc) DestroyAppWindows();
    switchdialog = NULL;
    selectedWindow = 0;
    windowCount = 0;
+   Esc = FALSE;
    return 0;
 }
 
-VOID
-DestroyAppWindows(VOID)
-{
-   INT i;
-   for (i=0; i< windowCount; i++)
-   {
-      HICON hIcon = iconList[i];
-      DestroyIcon(hIcon);
-   }
-}
-
 //
 // Switch System Class Window Proc.
 //
@@ -532,10 +658,23 @@ LRESULT WINAPI SwitchWndProc_common(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM
          {
             PrepareWindow();
             ati = (PALTTABINFO)GetWindowLongPtrW(hWnd, 0);
+            ati->cbSize = sizeof(ALTTABINFO);
             ati->cItems = nItems;
-            ati->cxItem = ati->cyItem = 43;
-            ati->cRows = nRows;
             ati->cColumns = nCols;
+            ati->cRows = nRows;
+            if (nCols)
+            {
+               ati->iColFocus = (selectedWindow - nShift) % nCols;
+               ati->iRowFocus = (selectedWindow - nShift) / nCols;
+            }
+            else
+            {
+               ati->iColFocus = 0;
+               ati->iRowFocus = 0;
+            }
+            ati->cxItem = CX_ITEM_SPACE;
+            ati->cyItem = CY_ITEM_SPACE;
+            ati->ptStart = ptStart;
          }
          return 0;