2 * Copyright 2003, 2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 // Martin Fuchs, 09.08.2003
29 #include "../utility/utility.h"
30 #include "../utility/shellclasses.h"
31 #include "../utility/shellbrowserimpl.h"
32 #include "../utility/dragdropimpl.h"
33 #include "../utility/window.h"
35 #include "../globals.h"
36 #include "../externals.h"
37 #include "../explorer_intres.h"
40 #include "../taskbar/desktopbar.h"
41 #include "../taskbar/taskbar.h" // for PM_GET_LAST_ACTIVE
42 #include "../shell/mainframe.h" // for MainFrame::OpenShellFolders()
45 static BOOL (WINAPI
*SetShellWindow
)(HWND
);
46 static BOOL (WINAPI
*SetShellWindowEx
)(HWND
, HWND
);
51 Desktop::Desktop(HDESK hdesktop
/*, HWINSTA hwinsta*/)
60 CloseDesktop(_hdesktop
);
63 // CloseWindowStation(_hwinsta);
81 // show all hidden windows
82 for(iterator it_dsk
=begin(); it_dsk
!=end(); ++it_dsk
)
83 for(WindowSet::iterator it
=it_dsk
->_windows
.begin(); it
!=it_dsk
->_windows
.end(); ++it
)
84 ShowWindowAsync(*it
, SW_SHOW
);
89 resize(DESKTOP_COUNT
);
92 DesktopPtr
& desktop
= (*this)[0];
94 desktop
= DesktopPtr(new Desktop(OpenInputDesktop(0, FALSE
, DESKTOP_SWITCHDESKTOP
)));
100 void Desktops::SwitchToDesktop(int idx
)
102 if (_current_desktop
== idx
)
105 DesktopPtr
& desktop
= (*this)[idx
];
107 DesktopThread
* pThread
= NULL
;
110 if (desktop
->_hdesktop
)
111 if (!SwitchDesktop(desktop
->_hdesktop
))
114 FmtString
desktop_name(TEXT("Desktop %d"), idx
);
116 SECURITY_ATTRIBUTES saAttr
= {sizeof(SECURITY_ATTRIBUTES
), 0, TRUE
};
118 HWINSTA hwinsta = CreateWindowStation(TEXT("ExplorerWinStation"), 0, GENERIC_ALL, &saAttr);
120 if (!SetProcessWindowStation(hwinsta))
123 HDESK hdesktop
= CreateDesktop(desktop_name
, NULL
, NULL
, 0, GENERIC_ALL
, &saAttr
);
127 desktop
= DesktopPtr(new Desktop(hdesktop
/*, hwinsta*/));
129 pThread
= new DesktopThread(*desktop
);
132 _current_desktop
= idx
;
135 desktop
->_pThread
= DesktopThreadPtr(pThread
);
140 int DesktopThread::Run()
142 if (!SetThreadDesktop(_desktop
._hdesktop
))
145 HDESK hDesk_old
= OpenInputDesktop(0, FALSE
, DESKTOP_SWITCHDESKTOP
);
147 if (!SwitchDesktop(_desktop
._hdesktop
))
150 if (!_desktop
._hwndDesktop
)
151 _desktop
._hwndDesktop
= DesktopWindow::Create();
153 int ret
= Window::MessageLoop();
155 SwitchDesktop(hDesk_old
);
162 static BOOL CALLBACK
SwitchDesktopEnumFct(HWND hwnd
, LPARAM lparam
)
164 WindowSet
& windows
= *(WindowSet
*)lparam
;
166 if (IsWindowVisible(hwnd
))
167 if (hwnd
!=g_Globals
._hwndDesktopBar
&& hwnd
!=g_Globals
._hwndDesktop
)
168 windows
.insert(hwnd
);
173 void Desktops::SwitchToDesktop(int idx
)
175 if (_current_desktop
== idx
)
178 Desktop
& old_desktop
= (*this)[_current_desktop
];
179 WindowSet
& windows
= old_desktop
._windows
;
180 Desktop
& desktop
= (*this)[idx
];
184 // collect window handles of all other desktops
185 WindowSet other_wnds
;
186 for(const_iterator it1
=begin(); it1
!=end(); ++it1
)
187 for(WindowSet::const_iterator it2
=it1
->_windows
.begin(); it2
!=it1
->_windows
.end(); ++it2
)
188 other_wnds
.insert(*it2
);
190 // save currently visible application windows
191 EnumWindows(SwitchDesktopEnumFct
, (LPARAM
)&windows
);
193 old_desktop
._hwndForeground
= (HWND
)SendMessage(g_Globals
._hwndDesktopBar
, PM_GET_LAST_ACTIVE
, 0, 0);
195 // hide all windows of the previous desktop
196 for(WindowSet::iterator it
=windows
.begin(); it
!=windows
.end(); ++it
)
197 ShowWindowAsync(*it
, SW_HIDE
);
199 // show all windows of the new desktop
200 for(WindowSet::iterator it
=desktop
._windows
.begin(); it
!=desktop
._windows
.end(); ++it
)
201 ShowWindowAsync(*it
, SW_SHOW
);
203 if (desktop
._hwndForeground
)
204 SetForegroundWindow(desktop
._hwndForeground
);
206 // remove the window handles of the other desktops from what we found on the previous desktop
207 for(WindowSet::const_iterator it
=other_wnds
.begin(); it
!=other_wnds
.end(); ++it
)
210 // We don't need to store the window handles of what's now visible the now current desktop.
211 desktop
._windows
.clear();
213 _current_desktop
= idx
;
219 static BOOL CALLBACK
MinimizeDesktopEnumFct(HWND hwnd
, LPARAM lparam
)
221 list
<MinimizeStruct
>& minimized
= *(list
<MinimizeStruct
>*)lparam
;
223 if (IsWindowVisible(hwnd
))
224 if (hwnd
!=g_Globals
._hwndDesktopBar
&& hwnd
!=g_Globals
._hwndDesktop
)
225 if (!IsIconic(hwnd
)) {
226 minimized
.push_back(MinimizeStruct(hwnd
, GetWindowStyle(hwnd
)));
227 ShowWindowAsync(hwnd
, SW_MINIMIZE
);
233 /// minimize/restore all windows on the desktop
234 void Desktops::ToggleMinimize()
236 list
<MinimizeStruct
>& minimized
= (*this)[_current_desktop
]._minimized
;
238 if (minimized
.empty()) {
239 EnumWindows(MinimizeDesktopEnumFct
, (LPARAM
)&minimized
);
241 for(list
<MinimizeStruct
>::const_iterator it
=minimized
.begin(); it
!=minimized
.end(); ++it
)
242 ShowWindowAsync(it
->first
, it
->second
&WS_MAXIMIZE
? SW_MAXIMIZE
: SW_RESTORE
);
249 BOOL
IsAnyDesktopRunning()
251 HINSTANCE hUser32
= GetModuleHandle(TEXT("user32"));
253 SetShellWindow
= (BOOL(WINAPI
*)(HWND
)) GetProcAddress(hUser32
, "SetShellWindow");
254 SetShellWindowEx
= (BOOL(WINAPI
*)(HWND
,HWND
)) GetProcAddress(hUser32
, "SetShellWindowEx");
256 return GetShellWindow() != 0;
260 static void draw_desktop_background(HWND hwnd
, HDC hdc
)
262 ClientRect
rect(hwnd
);
266 HBRUSH bkgndBrush = CreateSolidBrush(RGB(0,32,160)); // dark blue
267 FillRect(hdc, &rect, bkgndBrush);
268 DeleteBrush(bkgndBrush);
271 rect
.left
= rect
.right
- 280;
272 rect
.top
= rect
.bottom
- 56 - DESKTOPBARBAR_HEIGHT
;
273 rect
.right
= rect
.left
+ 250;
274 rect
.bottom
= rect
.top
+ 40;
276 #include "../buildno.h"
277 static const LPCTSTR BkgndText
= TEXT("ReactOS ")TEXT(KERNEL_VERSION_STR
)TEXT(" Explorer\nby Martin Fuchs");
279 BkMode
bkMode(hdc
, TRANSPARENT
);
281 TextColor
textColor(hdc
, RGB(128,128,192));
282 DrawText(hdc
, BkgndText
, -1, &rect
, DT_RIGHT
);
284 SetTextColor(hdc
, RGB(255,255,255));
287 DrawText(hdc
, BkgndText
, -1, &rect
, DT_RIGHT
);
291 BackgroundWindow::BackgroundWindow(HWND hwnd
)
294 // set background brush for the short moment of displaying the
295 // background color while moving foreground windows
296 SetClassLong(hwnd
, GCL_HBRBACKGROUND
, COLOR_BACKGROUND
+1);
299 LRESULT
BackgroundWindow::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
303 PaintDesktop((HDC
)wparam
);
306 case WM_MBUTTONDBLCLK
:
307 explorer_show_frame(SW_SHOWNORMAL
);
311 return super::WndProc(nmsg
, wparam
, lparam
);
318 DesktopWindow::DesktopWindow(HWND hwnd
)
324 DesktopWindow::~DesktopWindow()
327 _pShellView
->Release();
331 HWND
DesktopWindow::Create()
333 static IconWindowClass
wcDesktop(TEXT("Progman"), IDI_REACTOS
, CS_DBLCLKS
);
334 /* (disabled because of small ugly temporary artefacts when hiding start menu)
335 wcDesktop.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1); */
337 int width
= GetSystemMetrics(SM_CXSCREEN
);
338 int height
= GetSystemMetrics(SM_CYSCREEN
);
340 HWND hwndDesktop
= Window::Create(WINDOW_CREATOR(DesktopWindow
),
341 WS_EX_TOOLWINDOW
, wcDesktop
, TEXT("Program Manager"), WS_POPUP
|WS_VISIBLE
, //|WS_CLIPCHILDREN for SDI frames
342 0, 0, width
, height
, 0);
344 // work around to display desktop bar in Wine
345 ShowWindow(GET_WINDOW(DesktopWindow
, hwndDesktop
)->_desktopBar
, SW_SHOW
);
347 // work around for Windows NT, Win 98, ...
348 // Without this the desktop has mysteriously only a size of 800x600 pixels.
349 MoveWindow(hwndDesktop
, 0, 0, width
, height
, TRUE
);
355 LRESULT
DesktopWindow::Init(LPCREATESTRUCT pcs
)
357 if (super::Init(pcs
))
360 HRESULT hr
= GetDesktopFolder()->CreateViewObject(_hwnd
, IID_IShellView
, (void**)&_pShellView
);
362 SFV_CREATE sfv_create;
364 sfv_create.cbSize = sizeof(SFV_CREATE);
365 sfv_create.pshf = GetDesktopFolder();
366 sfv_create.psvOuter = NULL;
367 sfv_create.psfvcb = NULL;
369 HRESULT hr = SHCreateShellFolderView(&sfv_create, &_pShellView);
376 fs
.ViewMode
= FVM_ICON
;
377 fs
.fFlags
= FWF_DESKTOP
|FWF_NOCLIENTEDGE
|FWF_NOSCROLL
|FWF_BESTFITWINDOW
|FWF_SNAPTOGRID
; //|FWF_AUTOARRANGE;
379 ClientRect
rect(_hwnd
);
381 hr
= _pShellView
->CreateViewWindow(NULL
, &fs
, this, &rect
, &hWndView
);
383 ///@todo use IShellBrowser::GetViewStateStream() to restore previous view state -> see SHOpenRegStream()
386 g_Globals
._hwndShellView
= hWndView
;
388 // subclass shellview window
389 new DesktopShellView(hWndView
, _pShellView
);
391 _pShellView
->UIActivate(SVUIA_ACTIVATE_FOCUS
);
394 IShellView2* pShellView2;
396 hr = _pShellView->QueryInterface(IID_IShellView2, (void**)&pShellView2);
398 SV2CVW2_PARAMS params;
399 params.cbSize = sizeof(SV2CVW2_PARAMS);
400 params.psvPrev = _pShellView;
402 params.psbOwner = this;
403 params.prcView = ▭
404 params.pvid = params.pvid;//@@
406 hr = pShellView2->CreateViewWindow2(¶ms);
411 IFolderView* pFolderView;
413 hr = _pShellView->QueryInterface(IID_IFolderView, (void**)&pFolderView);
416 hr = pFolderView->GetAutoArrange();
417 hr = pFolderView->SetCurrentViewMode(FVM_DETAILS);
423 if (hWndView
&& SetShellWindowEx
)
424 SetShellWindowEx(_hwnd
, hWndView
);
425 else if (SetShellWindow
)
426 SetShellWindow(_hwnd
);
428 // create the explorer bar
429 _desktopBar
= DesktopBar::Create();
430 g_Globals
._hwndDesktopBar
= _desktopBar
;
436 LRESULT
DesktopWindow::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
440 draw_desktop_background(_hwnd
, PaintCanvas(_hwnd
));
443 case WM_LBUTTONDBLCLK
:
444 case WM_RBUTTONDBLCLK
:
445 case WM_MBUTTONDBLCLK
:
446 explorer_show_frame(SW_SHOWNORMAL
);
449 case WM_GETISHELLBROWSER
:
450 return (LRESULT
)static_cast<IShellBrowser
*>(this);
454 ///@todo use IShellBrowser::GetViewStateStream() and _pShellView->SaveViewState() to store view state
461 ShowExitWindowsDialog(_hwnd
);
465 if (wparam
== SC_TASKLIST
) {
467 SendMessage(_desktopBar
, nmsg
, wparam
, lparam
);
472 return super::WndProc(nmsg
, wparam
, lparam
);
479 HRESULT
DesktopWindow::OnDefaultCommand(LPIDA pida
)
481 if (MainFrame::OpenShellFolders(pida
, 0))
488 DesktopShellView::DesktopShellView(HWND hwnd
, IShellView
* pShellView
)
490 _pShellView(pShellView
)
492 _hwndListView
= ::GetNextWindow(hwnd
, GW_CHILD
);
494 SetWindowStyle(_hwndListView
, GetWindowStyle(_hwndListView
)&~LVS_ALIGNMASK
);//|LVS_ALIGNTOP|LVS_AUTOARRANGE);
496 // work around for Windows NT, Win 98, ...
497 // Without this the desktop has mysteriously only a size of 800x600 pixels.
498 ClientRect
rect(hwnd
);
499 MoveWindow(_hwndListView
, 0, 0, rect
.right
, rect
.bottom
, TRUE
);
501 // subclass background window
502 new BackgroundWindow(_hwndListView
);
504 _icon_algo
= 1; // default icon arrangement
510 bool DesktopShellView::InitDragDrop()
512 CONTEXT("DesktopShellView::InitDragDrop()");
514 _pDropTarget
= new DesktopDropTarget(_hwnd
);
519 _pDropTarget
->AddRef();
521 if (FAILED(RegisterDragDrop(_hwnd
, _pDropTarget
))) {
522 _pDropTarget
->Release();
527 _pDropTarget
->Release();
531 ftetc
.dwAspect
= DVASPECT_CONTENT
;
533 ftetc
.tymed
= TYMED_HGLOBAL
;
534 ftetc
.cfFormat
= CF_HDROP
;
536 _pDropTarget
->AddSuportedFormat(ftetc
);
541 LRESULT
DesktopShellView::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
545 if (!DoContextMenu(GET_X_LPARAM(lparam
), GET_Y_LPARAM(lparam
)))
546 DoDesktopContextMenu(GET_X_LPARAM(lparam
), GET_Y_LPARAM(lparam
));
549 case PM_SET_ICON_ALGORITHM
:
554 case PM_GET_ICON_ALGORITHM
:
558 return super::WndProc(nmsg
, wparam
, lparam
);
564 int DesktopShellView::Command(int id
, int code
)
566 return super::Command(id
, code
);
569 int DesktopShellView::Notify(int id
, NMHDR
* pnmh
)
571 return super::Notify(id
, pnmh
);
574 bool DesktopShellView::DoContextMenu(int x
, int y
)
576 IDataObject
* selection
;
578 HRESULT hr
= _pShellView
->GetItemObject(SVGIO_SELECTION
, IID_IDataObject
, (void**)&selection
);
584 hr
= pidList
.GetData(selection
);
586 selection
->Release();
591 LPIDA pida
= pidList
;
593 selection
->Release();
597 LPCITEMIDLIST parent_pidl
= (LPCITEMIDLIST
) ((LPBYTE
)pida
+pida
->aoffset
[0]);
599 LPCITEMIDLIST
* apidl
= (LPCITEMIDLIST
*) alloca(pida
->cidl
*sizeof(LPCITEMIDLIST
));
601 for(int i
=pida
->cidl
; i
>0; --i
)
602 apidl
[i
-1] = (LPCITEMIDLIST
) ((LPBYTE
)pida
+pida
->aoffset
[i
]);
604 hr
= ShellFolderContextMenu(ShellFolder(parent_pidl
), _hwnd
, pida
->cidl
, apidl
, x
, y
);
606 selection
->Release();
613 HRESULT
DesktopShellView::DoDesktopContextMenu(int x
, int y
)
617 HRESULT hr
= DesktopFolder()->GetUIObjectOf(_hwnd
, 0, NULL
, IID_IContextMenu
, NULL
, (LPVOID
*)&pcm
);
620 HMENU hmenu
= CreatePopupMenu();
623 hr
= pcm
->QueryContextMenu(hmenu
, 0, FCIDM_SHVIEWFIRST
, FCIDM_SHVIEWLAST
-1, CMF_NORMAL
|CMF_EXPLORE
);
626 AppendMenu(hmenu
, MF_SEPARATOR
, 0, NULL
);
627 AppendMenu(hmenu
, 0, FCIDM_SHVIEWLAST
-1, ResString(IDS_ABOUT_EXPLORER
));
629 UINT idCmd
= TrackPopupMenu(hmenu
, TPM_LEFTALIGN
|TPM_RETURNCMD
|TPM_RIGHTBUTTON
, x
, y
, 0, _hwnd
, NULL
);
631 if (idCmd
== FCIDM_SHVIEWLAST
-1) {
632 explorer_about(_hwnd
);
634 CMINVOKECOMMANDINFO cmi
;
636 cmi
.cbSize
= sizeof(CMINVOKECOMMANDINFO
);
639 cmi
.lpVerb
= (LPCSTR
)(INT_PTR
)(idCmd
- FCIDM_SHVIEWFIRST
);
640 cmi
.lpParameters
= NULL
;
641 cmi
.lpDirectory
= NULL
;
642 cmi
.nShow
= SW_SHOWNORMAL
;
646 hr
= pcm
->InvokeCommand(&cmi
);
658 #define ARRANGE_BORDER_DOWN 8
659 #define ARRANGE_BORDER_HV 9
660 #define ARRANGE_ROUNDABOUT 10
662 static const POINTS s_align_start
[] = {
667 {0, 1}, // left/bottom
669 {1, 1}, // right/bottom
677 static const POINTS s_align_dir1
[] = {
692 static const POINTS s_align_dir2
[] = {
707 typedef pair
<int,int> IconPos
;
708 typedef map
<IconPos
, int> IconMap
;
710 void DesktopShellView::PositionIcons(int dir
)
712 DWORD spacing
= ListView_GetItemSpacing(_hwndListView
, FALSE
);
715 SystemParametersInfo(SPI_GETWORKAREA
, 0, &work_area
, 0);
717 const POINTS
& dir1
= s_align_dir1
[_icon_algo
];
718 const POINTS
& dir2
= s_align_dir2
[_icon_algo
];
719 const POINTS
& start_pos
= s_align_start
[_icon_algo
];
726 int cx
= LOWORD(spacing
);
727 int cy
= HIWORD(spacing
);
729 int dx1
= dir_x1
* cx
;
730 int dy1
= dir_y1
* cy
;
731 int dx2
= dir_x2
* cx
;
732 int dy2
= dir_y2
* cy
;
734 int start_x
= (start_pos
.x
* work_area
.right
)/cx
*cx
+ (cx
-32)/2;
735 int start_y
= (start_pos
.y
* work_area
.bottom
)/cy
*cy
+ 4/*(cy-32)/2*/;
737 if (start_x
>= work_area
.right
)
740 if (start_y
>= work_area
.bottom
)
746 int all
= ListView_GetItemCount(_hwndListView
);
760 for(int idx
=i1
; idx
!=i2
; idx
+=dir
) {
761 pos_idx
[IconPos(y
, x
)] = idx
;
763 if (_icon_algo
== ARRANGE_BORDER_DOWN
) {
765 x
= work_area
.right
- x
;
769 if (y
>= work_area
.bottom
) {
777 else if (_icon_algo
== ARRANGE_BORDER_HV
) {
779 x
= work_area
.right
- x
;
783 if (y
>= work_area
.bottom
) {
790 if (x
>= work_area
.right
) {
798 else if (_icon_algo
== ARRANGE_ROUNDABOUT
) {
807 if (x
<0 || x
>=work_area
.right
) {
810 } else if (y
<0 || y
>=work_area
.bottom
) {
816 // use a little trick to get the icons where we want them to be...
818 for(IconMap::const_iterator it
=pos_idx
.end(); --it
!=pos_idx
.begin(); ) {
819 const IconPos
& pos
= it
->first
;
821 ListView_SetItemPosition32(_hwndListView
, it
->second
, pos
.second
, pos
.first
);
824 for(IconMap::const_iterator it
=pos_idx
.begin(); it
!=pos_idx
.end(); ++it
) {
825 const IconPos
& pos
= it
->first
;
827 ListView_SetItemPosition32(_hwndListView
, it
->second
, pos
.second
, pos
.first
);