merge ROS Shell without integrated explorer part into trunk
[reactos.git] / reactos / subsys / system / explorer / desktop / desktop.cpp
index 4e716a1..21ecd83 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2003, 2004 Martin Fuchs
+ * Copyright 2003, 2004, 2005 Martin Fuchs
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -18,7 +18,7 @@
 
 
  //
- // Explorer clone, lean version
+ // Explorer clone
  //
  // desktop.cpp
  //
 
 #include "precomp.h"
 
+#include "../explorer_intres.h"
+
 #include "../taskbar/desktopbar.h"
 #include "../taskbar/taskbar.h"        // for PM_GET_LAST_ACTIVE
 
-#include "../explorer_intres.h"
-
 
 static BOOL (WINAPI*SetShellWindow)(HWND);
 static BOOL (WINAPI*SetShellWindowEx)(HWND, HWND);
 
 
+#ifdef _USE_HDESK
+
+Desktop::Desktop(HDESK hdesktop/*, HWINSTA hwinsta*/)
+ :     _hdesktop(hdesktop)
+//     _hwinsta(hwinsta)
+{
+}
+
+Desktop::~Desktop()
+{
+       if (_hdesktop)
+               CloseDesktop(_hdesktop);
+
+//     if (_hwinsta)
+//             CloseWindowStation(_hwinsta);
+
+       if (_pThread.get()) {
+               _pThread->Stop();
+               _pThread.release();
+       }
+}
+
+#endif
+
+
 Desktops::Desktops()
  :     _current_desktop(0)
 {
@@ -54,14 +79,84 @@ Desktops::~Desktops()
 void Desktops::init()
 {
        resize(DESKTOP_COUNT);
+
+#ifdef _USE_HDESK
+       DesktopPtr& desktop = (*this)[0];
+
+       desktop = DesktopPtr(new Desktop(OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP)));
+#endif
 }
 
+#ifdef _USE_HDESK
+
+void Desktops::SwitchToDesktop(int idx)
+{
+       if (_current_desktop == idx)
+               return;
+
+       DesktopPtr& desktop = (*this)[idx];
+
+       DesktopThread* pThread = NULL;
+
+       if (desktop.get()) {
+               if (desktop->_hdesktop)
+                       if (!SwitchDesktop(desktop->_hdesktop))
+                               return;
+       } else {
+               FmtString desktop_name(TEXT("Desktop %d"), idx);
+
+               SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
+/*
+               HWINSTA hwinsta = CreateWindowStation(TEXT("ExplorerWinStation"), 0, GENERIC_ALL, &saAttr);
+
+               if (!SetProcessWindowStation(hwinsta))
+                       return;
+*/
+               HDESK hdesktop = CreateDesktop(desktop_name, NULL, NULL, 0, GENERIC_ALL, &saAttr);
+               if (!hdesktop)
+                       return;
+
+               desktop = DesktopPtr(new Desktop(hdesktop/*, hwinsta*/));
+
+               pThread = new DesktopThread(*desktop);
+       }
+
+       _current_desktop = idx;
+
+       if (pThread) {
+               desktop->_pThread = DesktopThreadPtr(pThread);
+               pThread->Start();
+       }
+}
+
+int DesktopThread::Run()
+{
+       if (!SetThreadDesktop(_desktop._hdesktop))
+               return -1;
+
+       HDESK hDesk_old = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
+
+       if (!SwitchDesktop(_desktop._hdesktop))
+               return -1;
+
+       if (!_desktop._hwndDesktop)
+               _desktop._hwndDesktop = DesktopWindow::Create();
+
+       int ret = Window::MessageLoop();
+
+       SwitchDesktop(hDesk_old);
+
+       return ret;
+}
+
+#else // _USE_HDESK
+
 static BOOL CALLBACK SwitchDesktopEnumFct(HWND hwnd, LPARAM lparam)
 {
        WindowSet& windows = *(WindowSet*)lparam;
 
-       if (IsWindowVisible(hwnd))
-               if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop)
+       if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop)
+               if (IsWindowVisible(hwnd))
                        windows.insert(hwnd);
 
        return TRUE;
@@ -110,17 +205,24 @@ void Desktops::SwitchToDesktop(int idx)
        _current_desktop = idx;
 }
 
+#endif // _USE_HDESK
+
 
 static BOOL CALLBACK MinimizeDesktopEnumFct(HWND hwnd, LPARAM lparam)
 {
        list<MinimizeStruct>& minimized = *(list<MinimizeStruct>*)lparam;
 
-       if (IsWindowVisible(hwnd))
-               if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop)
-                       if (!IsIconic(hwnd)) {
+       if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop)
+               if (IsWindowVisible(hwnd) && !IsIconic(hwnd)) {
+                       RECT rect;
+
+                       if (GetWindowRect(hwnd,&rect))
+                               if (rect.right>0 && rect.bottom>0 &&
+                                       rect.right>rect.left && rect.bottom>rect.top) {
                                minimized.push_back(MinimizeStruct(hwnd, GetWindowStyle(hwnd)));
                                ShowWindowAsync(hwnd, SW_MINIMIZE);
                        }
+               }
 
        return TRUE;
 }
@@ -170,7 +272,9 @@ LRESULT BackgroundWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
                return TRUE;
 
          case WM_MBUTTONDBLCLK:
-               explorer_show_frame(SW_SHOWNORMAL);
+               /* Imagelist icons are missing if MainFrame::Create() is called directly from here!
+               explorer_show_frame(SW_SHOWNORMAL); */
+               PostMessage(g_Globals._hwndDesktop, nmsg, wparam, lparam);
                break;
 
          case PM_DISPLAY_VERSION:
@@ -179,6 +283,8 @@ LRESULT BackgroundWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
                        DWORD reset_mask = LOWORD(lparam);
                        DWORD xor_mask = HIWORD(lparam);
                        _display_version = ((_display_version&~reset_mask) | or_mask) ^ xor_mask;
+                       RegSetDWORDValue(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), TEXT("PaintDesktopVersion"), _display_version);
+                       ///@todo Changing the PaintDesktopVersion-Flag needs a restart of the shell -> display a message box
                        InvalidateRect(_hwnd, NULL, TRUE);
                }
                return _display_version;
@@ -199,27 +305,6 @@ void BackgroundWindow::DrawDesktopBkgnd(HDC hdc)
        FillRect(hdc, &rect, bkgndBrush);
        DeleteBrush(bkgndBrush);
 */
-       if (_display_version) {
-               ClientRect rect(_hwnd);
-
-               rect.left = rect.right - 280;
-               rect.top = rect.bottom - 56 - DESKTOPBARBAR_HEIGHT;
-               rect.right = rect.left + 250;
-               rect.bottom = rect.top + 40;
-
-       #include "../buildno.h"
-               static const LPCTSTR BkgndText = TEXT("ReactOS ")TEXT(KERNEL_VERSION_STR)TEXT(" Explorer\nby Martin Fuchs");
-
-               BkMode bkMode(hdc, TRANSPARENT);
-
-               TextColor textColor(hdc, RGB(128,128,192));
-               DrawText(hdc, BkgndText, -1, &rect, DT_RIGHT);
-
-               SetTextColor(hdc, RGB(255,255,255));
-               --rect.right;
-               ++rect.top;
-               DrawText(hdc, BkgndText, -1, &rect, DT_RIGHT);
-       }
 }
 
 
@@ -246,7 +331,7 @@ HWND DesktopWindow::Create()
        int height = GetSystemMetrics(SM_CYSCREEN);
 
        HWND hwndDesktop = Window::Create(WINDOW_CREATOR(DesktopWindow),
-                                       WS_EX_TOOLWINDOW, wcDesktop, TEXT("Program Manager"), WS_POPUP|WS_VISIBLE|WS_CLIPCHILDREN,
+                                       WS_EX_TOOLWINDOW, wcDesktop, TEXT("Program Manager"), WS_POPUP|WS_VISIBLE,      //|WS_CLIPCHILDREN for SDI frames
                                        0, 0, width, height, 0);
 
         // work around to display desktop bar in Wine
@@ -356,7 +441,7 @@ LRESULT DesktopWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
          case WM_DESTROY:
 
                ///@todo use IShellBrowser::GetViewStateStream() and _pShellView->SaveViewState() to store view state
-               
+
                if (SetShellWindow)
                        SetShellWindow(0);
                break;
@@ -382,8 +467,10 @@ LRESULT DesktopWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
 
 HRESULT DesktopWindow::OnDefaultCommand(LPIDA pida)
 {
-       if (MainFrame::OpenShellFolders(pida, 0))
+#ifndef ROSSHELL       // in shell-only-mode fall through and let shell32 handle the command
+       if (MainFrameBase::OpenShellFolders(pida, 0))
                return S_OK;
+#endif
 
        return E_NOTIMPL;
 }
@@ -405,6 +492,9 @@ DesktopShellView::DesktopShellView(HWND hwnd, IShellView* pShellView)
         // subclass background window
        new BackgroundWindow(_hwndListView);
 
+       _icon_algo = 1; // default icon arrangement
+
+       PositionIcons();
        InitDragDrop();
 }
 
@@ -439,7 +529,7 @@ bool DesktopShellView::InitDragDrop()
        return true;
 }
 
-LRESULT        DesktopShellView::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
+LRESULT DesktopShellView::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
 {
        switch(nmsg) {
          case WM_CONTEXTMENU:
@@ -447,6 +537,14 @@ LRESULT    DesktopShellView::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
                        DoDesktopContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
                break;
 
+         case PM_SET_ICON_ALGORITHM:
+               _icon_algo = wparam;
+               PositionIcons();
+               break;
+
+         case PM_GET_ICON_ALGORITHM:
+               return _icon_algo;
+
          case PM_DISPLAY_VERSION:
                return SendMessage(_hwndListView, nmsg, wparam, lparam);
 
@@ -497,7 +595,7 @@ bool DesktopShellView::DoContextMenu(int x, int y)
        for(int i=pida->cidl; i>0; --i)
                apidl[i-1] = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[i]);
 
-       hr = ShellFolderContextMenu(ShellFolder(parent_pidl), _hwnd, pida->cidl, apidl, x, y);
+       hr = ShellFolderContextMenu(ShellFolder(parent_pidl), _hwnd, pida->cidl, apidl, x, y, _cm_ifs);
 
        selection->Release();
 
@@ -513,6 +611,8 @@ HRESULT DesktopShellView::DoDesktopContextMenu(int x, int y)
        HRESULT hr = DesktopFolder()->GetUIObjectOf(_hwnd, 0, NULL, IID_IContextMenu, NULL, (LPVOID*)&pcm);
 
        if (SUCCEEDED(hr)) {
+               pcm = _cm_ifs.query_interfaces(pcm);
+
                HMENU hmenu = CreatePopupMenu();
 
                if (hmenu) {
@@ -524,6 +624,8 @@ HRESULT DesktopShellView::DoDesktopContextMenu(int x, int y)
 
                                UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, _hwnd, NULL);
 
+                               _cm_ifs.reset();
+
                                if (idCmd == FCIDM_SHVIEWLAST-1) {
                                        explorer_about(_hwnd);
                                } else if (idCmd) {
@@ -549,3 +651,177 @@ HRESULT DesktopShellView::DoDesktopContextMenu(int x, int y)
 
        return hr;
 }
+
+
+#define        ARRANGE_BORDER_DOWN      8
+#define        ARRANGE_BORDER_HV        9
+#define        ARRANGE_ROUNDABOUT      10
+
+static const POINTS s_align_start[] = {
+       {0, 0}, // left/top
+       {0, 0},
+       {1, 0}, // right/top
+       {1, 0},
+       {0, 1}, // left/bottom
+       {0, 1},
+       {1, 1}, // right/bottom
+       {1, 1},
+
+       {0, 0}, // left/top
+       {0, 0},
+       {0, 0}
+};
+
+static const POINTS s_align_dir1[] = {
+       { 0, +1},       // down
+       {+1,  0},       // right
+       {-1,  0},       // left
+       { 0, +1},       // down
+       { 0, -1},       // up
+       {+1,  0},       // right
+       {-1,  0},       // left
+       { 0, -1},       // up
+
+       { 0, +1},       // down
+       {+1,  0},       // right
+       {+1,  0}        // right
+};
+
+static const POINTS s_align_dir2[] = {
+       {+1,  0},       // right
+       { 0, +1},       // down
+       { 0, +1},       // down
+       {-1,  0},       // left
+       {+1,  0},       // right
+       { 0, -1},       // up
+       { 0, -1},       // up
+       {-1,  0},       // left
+
+       {+1,  0},       // right
+       { 0, +1},       // down
+       { 0, +1}        // down
+};
+
+typedef pair<int,int> IconPos;
+typedef map<IconPos, int> IconMap;
+
+void DesktopShellView::PositionIcons(int dir)
+{
+       DWORD spacing = ListView_GetItemSpacing(_hwndListView, FALSE);
+
+       RECT work_area;
+       SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0);
+
+       const POINTS& dir1 = s_align_dir1[_icon_algo];
+       const POINTS& dir2 = s_align_dir2[_icon_algo];
+       const POINTS& start_pos = s_align_start[_icon_algo];
+
+       int dir_x1 = dir1.x;
+       int dir_y1 = dir1.y;
+       int dir_x2 = dir2.x;
+       int dir_y2 = dir2.y;
+
+       int cx = LOWORD(spacing);
+       int cy = HIWORD(spacing);
+
+       int dx1 = dir_x1 * cx;
+       int dy1 = dir_y1 * cy;
+       int dx2 = dir_x2 * cx;
+       int dy2 = dir_y2 * cy;
+
+       int start_x = (start_pos.x * work_area.right)/cx*cx + (cx-32)/2;
+       int start_y = (start_pos.y * work_area.bottom)/cy*cy + 4/*(cy-32)/2*/;
+
+       if (start_x >= work_area.right)
+               start_x -= cx;
+
+       if (start_y >= work_area.bottom)
+               start_y -= cy;
+
+       int x = start_x;
+       int y = start_y;
+
+       int all = ListView_GetItemCount(_hwndListView);
+       int i1, i2;
+
+       if (dir > 0) {
+               i1 = 0;
+               i2 = all;
+       } else {
+               i1 = all-1;
+               i2 = -1;
+       }
+
+       IconMap pos_idx;
+       int cnt = 0;
+
+       for(int idx=i1; idx!=i2; idx+=dir) {
+               pos_idx[IconPos(y, x)] = idx;
+
+               if (_icon_algo == ARRANGE_BORDER_DOWN) {
+                       if (++cnt & 1)
+                               x = work_area.right - x;
+                       else {
+                               y += dy1;
+
+                               if (y >= work_area.bottom) {
+                                       y = start_y;
+                                       x += dx2;
+                               }
+                       }
+
+                       continue;
+               }
+               else if (_icon_algo == ARRANGE_BORDER_HV) {
+                       if (++cnt & 1)
+                               x = work_area.right - x;
+                       else if (cnt & 2) {
+                               y += dy1;
+
+                               if (y >= work_area.bottom) {
+                                       y = start_y;
+                                       x += dx2;
+                               }
+                       } else {
+                               x += dx1;
+
+                               if (x >= work_area.right) {
+                                       x = start_x;
+                                       y += dy2;
+                               }
+                       }
+
+                       continue;
+               }
+               else if (_icon_algo == ARRANGE_ROUNDABOUT) {
+
+                       ///@todo
+
+               }
+
+               x += dx1;
+               y += dy1;
+
+               if (x<0 || x>=work_area.right) {
+                       x = start_x;
+                       y += dy2;
+               } else if (y<0 || y>=work_area.bottom) {
+                       y = start_y;
+                       x += dx2;
+               }
+       }
+
+        // use a little trick to get the icons where we want them to be...
+
+       for(IconMap::const_iterator it=pos_idx.end(); --it!=pos_idx.begin(); ) {
+               const IconPos& pos = it->first;
+
+               ListView_SetItemPosition32(_hwndListView, it->second, pos.second, pos.first);
+       }
+
+       for(IconMap::const_iterator it=pos_idx.begin(); it!=pos_idx.end(); ++it) {
+               const IconPos& pos = it->first;
+
+               ListView_SetItemPosition32(_hwndListView, it->second, pos.second, pos.first);
+       }
+}