2 * Copyright 2003, 2004, 2005 Martin Fuchs
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 // Martin Fuchs, 09.08.2003
31 #include "../taskbar/desktopbar.h"
32 #include "../taskbar/taskbar.h" // for PM_GET_LAST_ACTIVE
35 static BOOL (WINAPI
*SetShellWindow
)(HWND
);
36 static BOOL (WINAPI
*SetShellWindowEx
)(HWND
, HWND
);
41 Desktop::Desktop(HDESK hdesktop
/*, HWINSTA hwinsta*/)
50 CloseDesktop(_hdesktop
);
53 // CloseWindowStation(_hwinsta);
71 // show all hidden windows
72 for(iterator it_dsk
=begin(); it_dsk
!=end(); ++it_dsk
)
73 for(WindowSet::iterator it
=it_dsk
->_windows
.begin(); it
!=it_dsk
->_windows
.end(); ++it
)
74 ShowWindowAsync(*it
, SW_SHOW
);
79 resize(DESKTOP_COUNT
);
82 DesktopPtr
& desktop
= (*this)[0];
84 desktop
= DesktopPtr(new Desktop(OpenInputDesktop(0, FALSE
, DESKTOP_SWITCHDESKTOP
)));
90 void Desktops::SwitchToDesktop(int idx
)
92 if (_current_desktop
== idx
)
95 DesktopPtr
& desktop
= (*this)[idx
];
97 DesktopThread
* pThread
= NULL
;
100 if (desktop
->_hdesktop
)
101 if (!SwitchDesktop(desktop
->_hdesktop
))
104 FmtString
desktop_name(TEXT("Desktop %d"), idx
);
106 SECURITY_ATTRIBUTES saAttr
= {sizeof(SECURITY_ATTRIBUTES
), 0, TRUE
};
108 HWINSTA hwinsta = CreateWindowStation(TEXT("ExplorerWinStation"), 0, GENERIC_ALL, &saAttr);
110 if (!SetProcessWindowStation(hwinsta))
113 HDESK hdesktop
= CreateDesktop(desktop_name
, NULL
, NULL
, 0, GENERIC_ALL
, &saAttr
);
117 desktop
= DesktopPtr(new Desktop(hdesktop
/*, hwinsta*/));
119 pThread
= new DesktopThread(*desktop
);
122 _current_desktop
= idx
;
125 desktop
->_pThread
= DesktopThreadPtr(pThread
);
130 int DesktopThread::Run()
132 if (!SetThreadDesktop(_desktop
._hdesktop
))
135 HDESK hDesk_old
= OpenInputDesktop(0, FALSE
, DESKTOP_SWITCHDESKTOP
);
137 if (!SwitchDesktop(_desktop
._hdesktop
))
140 if (!_desktop
._hwndDesktop
)
141 _desktop
._hwndDesktop
= DesktopWindow::Create();
143 int ret
= Window::MessageLoop();
145 SwitchDesktop(hDesk_old
);
152 static BOOL CALLBACK
SwitchDesktopEnumFct(HWND hwnd
, LPARAM lparam
)
154 WindowSet
& windows
= *(WindowSet
*)lparam
;
156 if (hwnd
!=g_Globals
._hwndDesktopBar
&& hwnd
!=g_Globals
._hwndDesktop
)
157 if (IsWindowVisible(hwnd
))
158 windows
.insert(hwnd
);
163 void Desktops::SwitchToDesktop(int idx
)
165 if (_current_desktop
== idx
)
168 Desktop
& old_desktop
= (*this)[_current_desktop
];
169 WindowSet
& windows
= old_desktop
._windows
;
170 Desktop
& desktop
= (*this)[idx
];
174 // collect window handles of all other desktops
175 WindowSet other_wnds
;
176 for(const_iterator it1
=begin(); it1
!=end(); ++it1
)
177 for(WindowSet::const_iterator it2
=it1
->_windows
.begin(); it2
!=it1
->_windows
.end(); ++it2
)
178 other_wnds
.insert(*it2
);
180 // save currently visible application windows
181 EnumWindows(SwitchDesktopEnumFct
, (LPARAM
)&windows
);
183 old_desktop
._hwndForeground
= (HWND
)SendMessage(g_Globals
._hwndDesktopBar
, PM_GET_LAST_ACTIVE
, 0, 0);
185 // hide all windows of the previous desktop
186 for(WindowSet::iterator it
=windows
.begin(); it
!=windows
.end(); ++it
)
187 ShowWindowAsync(*it
, SW_HIDE
);
189 // show all windows of the new desktop
190 for(WindowSet::iterator it
=desktop
._windows
.begin(); it
!=desktop
._windows
.end(); ++it
)
191 ShowWindowAsync(*it
, SW_SHOW
);
193 if (desktop
._hwndForeground
)
194 SetForegroundWindow(desktop
._hwndForeground
);
196 // remove the window handles of the other desktops from what we found on the previous desktop
197 for(WindowSet::const_iterator it
=other_wnds
.begin(); it
!=other_wnds
.end(); ++it
)
200 // We don't need to store the window handles of what's now visible the now current desktop.
201 desktop
._windows
.clear();
203 _current_desktop
= idx
;
209 static BOOL CALLBACK
MinimizeDesktopEnumFct(HWND hwnd
, LPARAM lparam
)
211 list
<MinimizeStruct
>& minimized
= *(list
<MinimizeStruct
>*)lparam
;
213 if (hwnd
!=g_Globals
._hwndDesktopBar
&& hwnd
!=g_Globals
._hwndDesktop
)
214 if (IsWindowVisible(hwnd
) && !IsIconic(hwnd
)) {
217 if (GetWindowRect(hwnd
,&rect
))
218 if (rect
.right
>0 && rect
.bottom
>0 &&
219 rect
.right
>rect
.left
&& rect
.bottom
>rect
.top
) {
220 minimized
.push_back(MinimizeStruct(hwnd
, GetWindowStyle(hwnd
)));
221 ShowWindowAsync(hwnd
, SW_MINIMIZE
);
228 /// minimize/restore all windows on the desktop
229 void Desktops::ToggleMinimize()
231 list
<MinimizeStruct
>& minimized
= (*this)[_current_desktop
]._minimized
;
233 if (minimized
.empty()) {
234 EnumWindows(MinimizeDesktopEnumFct
, (LPARAM
)&minimized
);
236 const list
<MinimizeStruct
>& cminimized
= minimized
;
237 for(list
<MinimizeStruct
>::const_reverse_iterator it
=cminimized
.rbegin();
238 it
!=cminimized
.rend(); ++it
) {
239 ShowWindowAsync(it
->first
, it
->second
&WS_MAXIMIZE
? SW_MAXIMIZE
: SW_RESTORE
);
248 BOOL
IsAnyDesktopRunning()
250 HINSTANCE hUser32
= GetModuleHandle(TEXT("user32"));
252 SetShellWindow
= (BOOL(WINAPI
*)(HWND
)) GetProcAddress(hUser32
, "SetShellWindow");
253 SetShellWindowEx
= (BOOL(WINAPI
*)(HWND
,HWND
)) GetProcAddress(hUser32
, "SetShellWindowEx");
255 return GetShellWindow() != 0;
259 BackgroundWindow::BackgroundWindow(HWND hwnd
)
262 // set background brush for the short moment of displaying the
263 // background color while moving foreground windows
264 SetClassLongPtr(hwnd
, GCL_HBRBACKGROUND
, COLOR_BACKGROUND
+1);
266 _display_version
= RegGetDWORDValue(HKEY_CURRENT_USER
, TEXT("Control Panel\\Desktop"), TEXT("PaintDesktopVersion"), 1);
269 LRESULT
BackgroundWindow::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
273 DrawDesktopBkgnd((HDC
)wparam
);
276 case WM_MBUTTONDBLCLK
:
277 /* Imagelist icons are missing if MainFrame::Create() is called directly from here!
278 explorer_show_frame(SW_SHOWNORMAL); */
279 PostMessage(g_Globals
._hwndDesktop
, nmsg
, wparam
, lparam
);
282 case PM_DISPLAY_VERSION
:
283 if (lparam
|| wparam
) {
284 DWORD or_mask
= wparam
;
285 DWORD reset_mask
= LOWORD(lparam
);
286 DWORD xor_mask
= HIWORD(lparam
);
287 _display_version
= ((_display_version
&~reset_mask
) | or_mask
) ^ xor_mask
;
288 RegSetDWORDValue(HKEY_CURRENT_USER
, TEXT("Control Panel\\Desktop"), TEXT("PaintDesktopVersion"), _display_version
);
289 ///@todo Changing the PaintDesktopVersion-Flag needs a restart of the shell -> display a message box
290 InvalidateRect(_hwnd
, NULL
, TRUE
);
292 return _display_version
;
295 return super::WndProc(nmsg
, wparam
, lparam
);
301 void BackgroundWindow::DrawDesktopBkgnd(HDC hdc
)
305 /* special solid background
306 HBRUSH bkgndBrush = CreateSolidBrush(RGB(0,32,160)); // dark blue
307 FillRect(hdc, &rect, bkgndBrush);
308 DeleteBrush(bkgndBrush);
313 DesktopWindow::DesktopWindow(HWND hwnd
)
319 DesktopWindow::~DesktopWindow()
322 _pShellView
->Release();
326 HWND
DesktopWindow::Create()
328 static IconWindowClass
wcDesktop(TEXT("Progman"), IDI_REACTOS
, CS_DBLCLKS
);
329 /* (disabled because of small ugly temporary artefacts when hiding start menu)
330 wcDesktop.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1); */
332 int width
= GetSystemMetrics(SM_CXSCREEN
);
333 int height
= GetSystemMetrics(SM_CYSCREEN
);
335 HWND hwndDesktop
= Window::Create(WINDOW_CREATOR(DesktopWindow
),
336 WS_EX_TOOLWINDOW
, wcDesktop
, TEXT("Program Manager"), WS_POPUP
|WS_VISIBLE
, //|WS_CLIPCHILDREN for SDI frames
337 0, 0, width
, height
, 0);
339 // work around to display desktop bar in Wine
340 ShowWindow(GET_WINDOW(DesktopWindow
, hwndDesktop
)->_desktopBar
, SW_SHOW
);
342 // work around for Windows NT, Win 98, ...
343 // Without this the desktop has mysteriously only a size of 800x600 pixels.
344 MoveWindow(hwndDesktop
, 0, 0, width
, height
, TRUE
);
350 LRESULT
DesktopWindow::Init(LPCREATESTRUCT pcs
)
352 if (super::Init(pcs
))
355 HRESULT hr
= GetDesktopFolder()->CreateViewObject(_hwnd
, IID_IShellView
, (void**)&_pShellView
);
357 SFV_CREATE sfv_create;
359 sfv_create.cbSize = sizeof(SFV_CREATE);
360 sfv_create.pshf = GetDesktopFolder();
361 sfv_create.psvOuter = NULL;
362 sfv_create.psfvcb = NULL;
364 HRESULT hr = SHCreateShellFolderView(&sfv_create, &_pShellView);
371 fs
.ViewMode
= FVM_ICON
;
372 fs
.fFlags
= FWF_DESKTOP
|FWF_NOCLIENTEDGE
|FWF_NOSCROLL
|FWF_BESTFITWINDOW
|FWF_SNAPTOGRID
; //|FWF_AUTOARRANGE;
374 ClientRect
rect(_hwnd
);
376 hr
= _pShellView
->CreateViewWindow(NULL
, &fs
, this, &rect
, &hWndView
);
378 ///@todo use IShellBrowser::GetViewStateStream() to restore previous view state -> see SHOpenRegStream()
381 g_Globals
._hwndShellView
= hWndView
;
383 // subclass shellview window
384 new DesktopShellView(hWndView
, _pShellView
);
386 _pShellView
->UIActivate(SVUIA_ACTIVATE_FOCUS
);
389 IShellView2* pShellView2;
391 hr = _pShellView->QueryInterface(IID_IShellView2, (void**)&pShellView2);
393 SV2CVW2_PARAMS params;
394 params.cbSize = sizeof(SV2CVW2_PARAMS);
395 params.psvPrev = _pShellView;
397 params.psbOwner = this;
398 params.prcView = ▭
399 params.pvid = params.pvid;//@@
401 hr = pShellView2->CreateViewWindow2(¶ms);
406 IFolderView* pFolderView;
408 hr = _pShellView->QueryInterface(IID_IFolderView, (void**)&pFolderView);
411 hr = pFolderView->GetAutoArrange();
412 hr = pFolderView->SetCurrentViewMode(FVM_DETAILS);
418 if (hWndView
&& SetShellWindowEx
)
419 SetShellWindowEx(_hwnd
, hWndView
);
420 else if (SetShellWindow
)
421 SetShellWindow(_hwnd
);
423 // create the explorer bar
424 _desktopBar
= DesktopBar::Create();
425 g_Globals
._hwndDesktopBar
= _desktopBar
;
431 LRESULT
DesktopWindow::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
434 case WM_LBUTTONDBLCLK
:
435 case WM_RBUTTONDBLCLK
:
436 case WM_MBUTTONDBLCLK
:
437 explorer_show_frame(SW_SHOWNORMAL
);
440 case WM_DISPLAYCHANGE
:
441 MoveWindow(_hwnd
, 0, 0, LOWORD(lparam
), HIWORD(lparam
), TRUE
);
442 MoveWindow(g_Globals
._hwndShellView
, 0, 0, LOWORD(lparam
), HIWORD(lparam
), TRUE
);
443 MoveWindow(_desktopBar
, 0, HIWORD(lparam
) - DESKTOPBARBAR_HEIGHT
, LOWORD(lparam
), DESKTOPBARBAR_HEIGHT
, TRUE
);
446 case WM_GETISHELLBROWSER
:
447 return (LRESULT
)static_cast<IShellBrowser
*>(this);
451 ///@todo use IShellBrowser::GetViewStateStream() and _pShellView->SaveViewState() to store view state
458 ShowExitWindowsDialog(_hwnd
);
462 if (wparam
== SC_TASKLIST
) {
464 SendMessage(_desktopBar
, nmsg
, wparam
, lparam
);
468 case WM_SYSCOLORCHANGE
:
469 // redraw background window - it's done by system
470 //InvalidateRect(g_Globals._hwndShellView, NULL, TRUE);
472 // forward message to common controls
473 SendMessage(g_Globals
._hwndShellView
, WM_SYSCOLORCHANGE
, 0, 0);
474 SendMessage(_desktopBar
, WM_SYSCOLORCHANGE
, 0, 0);
477 case WM_SETTINGCHANGE
:
478 SendMessage(g_Globals
._hwndShellView
, nmsg
, wparam
, lparam
);
481 case PM_TRANSLATE_MSG
:
483 /* TranslateAccelerator is called for all explorer windows that are open
484 so we have to decide if this is the correct recipient */
485 LPMSG lpmsg
= (LPMSG
)lparam
;
486 HWND hwnd
= lpmsg
->hwnd
;
493 hwnd
= GetParent(hwnd
);
497 return _pShellView
->TranslateAccelerator(lpmsg
) == S_OK
;
502 return super::WndProc(nmsg
, wparam
, lparam
);
509 HRESULT
DesktopWindow::OnDefaultCommand(LPIDA pida
)
511 #ifndef ROSSHELL // in shell-only-mode fall through and let shell32 handle the command
512 if (MainFrameBase::OpenShellFolders(pida
, 0))
520 DesktopShellView::DesktopShellView(HWND hwnd
, IShellView
* pShellView
)
522 _pShellView(pShellView
)
524 _hwndListView
= GetNextWindow(hwnd
, GW_CHILD
);
526 // work around for Windows NT, Win 98, ...
527 // Without this the desktop has mysteriously only a size of 800x600 pixels.
528 ClientRect
rect(hwnd
);
529 MoveWindow(_hwndListView
, 0, 0, rect
.right
, rect
.bottom
, TRUE
);
531 // subclass background window
532 new BackgroundWindow(_hwndListView
);
534 _icon_algo
= 0; // default icon arrangement
540 DesktopShellView::~DesktopShellView()
542 if (FAILED(RevokeDragDrop(_hwnd
)))
547 bool DesktopShellView::InitDragDrop()
549 CONTEXT("DesktopShellView::InitDragDrop()");
551 DesktopDropTarget
* pDropTarget
= new DesktopDropTarget(_hwnd
);
556 pDropTarget
->AddRef();
558 if (FAILED(RegisterDragDrop(_hwnd
, pDropTarget
))) {
559 pDropTarget
->Release();
565 ftetc
.dwAspect
= DVASPECT_CONTENT
;
567 ftetc
.tymed
= TYMED_HGLOBAL
;
568 ftetc
.cfFormat
= CF_HDROP
;
570 pDropTarget
->AddSuportedFormat(ftetc
);
571 pDropTarget
->Release();
576 LRESULT
DesktopShellView::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
580 if (!DoContextMenu(GET_X_LPARAM(lparam
), GET_Y_LPARAM(lparam
)))
581 DoDesktopContextMenu(GET_X_LPARAM(lparam
), GET_Y_LPARAM(lparam
));
584 case PM_SET_ICON_ALGORITHM
:
589 case PM_GET_ICON_ALGORITHM
:
592 case PM_DISPLAY_VERSION
:
593 return SendMessage(_hwndListView
, nmsg
, wparam
, lparam
);
596 return super::WndProc(nmsg
, wparam
, lparam
);
602 int DesktopShellView::Command(int id
, int code
)
604 return super::Command(id
, code
);
607 int DesktopShellView::Notify(int id
, NMHDR
* pnmh
)
609 return super::Notify(id
, pnmh
);
612 bool DesktopShellView::DoContextMenu(int x
, int y
)
614 IDataObject
* selection
;
616 HRESULT hr
= _pShellView
->GetItemObject(SVGIO_SELECTION
, IID_IDataObject
, (void**)&selection
);
622 hr
= pidList
.GetData(selection
);
624 selection
->Release();
629 LPIDA pida
= pidList
;
631 selection
->Release();
635 LPCITEMIDLIST parent_pidl
= (LPCITEMIDLIST
) ((LPBYTE
)pida
+pida
->aoffset
[0]);
637 LPCITEMIDLIST
* apidl
= (LPCITEMIDLIST
*) alloca(pida
->cidl
*sizeof(LPCITEMIDLIST
));
639 for(int i
=pida
->cidl
; i
>0; --i
)
640 apidl
[i
-1] = (LPCITEMIDLIST
) ((LPBYTE
)pida
+pida
->aoffset
[i
]);
642 hr
= ShellFolderContextMenu(ShellFolder(parent_pidl
), _hwnd
, pida
->cidl
, apidl
, x
, y
, _cm_ifs
);
644 selection
->Release();
654 HRESULT
DesktopShellView::DoDesktopContextMenu(int x
, int y
)
658 HRESULT hr
= DesktopFolder()->GetUIObjectOf(_hwnd
, 0, NULL
, IID_IContextMenu
, NULL
, (LPVOID
*)&pcm
);
661 pcm
= _cm_ifs
.query_interfaces(pcm
);
663 HMENU hmenu
= CreatePopupMenu();
666 hr
= pcm
->QueryContextMenu(hmenu
, 0, FCIDM_SHVIEWFIRST
, FCIDM_SHVIEWLAST
-1, CMF_NORMAL
|CMF_EXPLORE
);
669 AppendMenu(hmenu
, MF_SEPARATOR
, 0, NULL
);
670 AppendMenu(hmenu
, 0, FCIDM_SHVIEWLAST
-1, ResString(IDS_ABOUT_EXPLORER
));
672 UINT idCmd
= TrackPopupMenu(hmenu
, TPM_LEFTALIGN
|TPM_RETURNCMD
|TPM_RIGHTBUTTON
, x
, y
, 0, _hwnd
, NULL
);
676 if (idCmd
== FCIDM_SHVIEWLAST
-1) {
677 explorer_about(_hwnd
);
679 CMINVOKECOMMANDINFO cmi
;
681 cmi
.cbSize
= sizeof(CMINVOKECOMMANDINFO
);
684 cmi
.lpVerb
= (LPCSTR
)(INT_PTR
)(idCmd
- FCIDM_SHVIEWFIRST
);
685 cmi
.lpParameters
= NULL
;
686 cmi
.lpDirectory
= NULL
;
687 cmi
.nShow
= SW_SHOWNORMAL
;
691 hr
= pcm
->InvokeCommand(&cmi
);
705 #define ARRANGE_BORDER_DOWN 8
706 #define ARRANGE_BORDER_HV 9
707 #define ARRANGE_ROUNDABOUT 10
709 static const POINTS s_align_start
[] = {
714 {0, 1}, // left/bottom
716 {1, 1}, // right/bottom
724 static const POINTS s_align_dir1
[] = {
739 static const POINTS s_align_dir2
[] = {
754 typedef pair
<int,int> IconPos
;
755 typedef map
<IconPos
, int> IconMap
;
757 void DesktopShellView::PositionIcons(int dir
)
759 DWORD spacing
= ListView_GetItemSpacing(_hwndListView
, FALSE
);
762 SystemParametersInfo(SPI_GETWORKAREA
, 0, &work_area
, 0);
764 /* disable default allignment */
765 SetWindowStyle(_hwndListView
, GetWindowStyle(_hwndListView
)&~LVS_ALIGNMASK
);//|LVS_ALIGNTOP|LVS_AUTOARRANGE);
767 const POINTS
& dir1
= s_align_dir1
[_icon_algo
];
768 const POINTS
& dir2
= s_align_dir2
[_icon_algo
];
769 const POINTS
& start_pos
= s_align_start
[_icon_algo
];
776 int cx
= LOWORD(spacing
);
777 int cy
= HIWORD(spacing
);
779 int dx1
= dir_x1
* cx
;
780 int dy1
= dir_y1
* cy
;
781 int dx2
= dir_x2
* cx
;
782 int dy2
= dir_y2
* cy
;
784 int xoffset
= (cx
-32)/2;
785 int yoffset
= 4/*(cy-32)/2*/;
787 int start_x
= start_pos
.x
* (work_area
.right
- cx
) + xoffset
;
788 int start_y
= start_pos
.y
* (work_area
.bottom
- cy
) + yoffset
;
793 int all
= ListView_GetItemCount(_hwndListView
);
809 for(int idx
=i1
; idx
!=i2
; idx
+=dir
) {
810 pos_idx
[IconPos(y
, x
)] = idx
;
812 if (_icon_algo
== ARRANGE_BORDER_DOWN
) {
814 x
= work_area
.right
- x
- cx
+ 2*xoffset
;
818 if (y
+ cy
- 2 * yoffset
> work_area
.bottom
) {
827 else if (_icon_algo
== ARRANGE_BORDER_HV
) {
829 x
= work_area
.right
- x
- cx
+ 2*xoffset
;
835 if (y
+ cy
- 2 * yoffset
> work_area
.bottom
) {
848 if (x
+ cx
- 2 * xoffset
> work_area
.right
) {
860 else if (_icon_algo
== ARRANGE_ROUNDABOUT
) {
869 if (x
<0 || x
+cx
-2*xoffset
>work_area
.right
) {
872 } else if (y
<0 || y
+cy
-2*yoffset
>work_area
.bottom
) {
878 // use a little trick to get the icons where we want them to be...
880 //for(IconMap::const_iterator it=pos_idx.end(); --it!=pos_idx.begin(); ) {
881 // const IconPos& pos = it->first;
883 // ListView_SetItemPosition32(_hwndListView, it->second, pos.second, pos.first);
886 for(IconMap::const_iterator it
=pos_idx
.begin(); it
!=pos_idx
.end(); ++it
) {
887 const IconPos
& pos
= it
->first
;
889 ListView_SetItemPosition32(_hwndListView
, it
->second
, pos
.second
, pos
.first
);
894 void DesktopShellView::refresh()