include explorer windows in desktop switching
[reactos.git] / reactos / subsys / system / explorer / desktop / desktop.cpp
1 /*
2 * Copyright 2003 Martin Fuchs
3 *
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.
8 *
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.
13 *
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
17 */
18
19
20 //
21 // Explorer clone
22 //
23 // desktop.cpp
24 //
25 // Martin Fuchs, 09.08.2003
26 //
27
28
29 #include "../utility/utility.h"
30
31 #include "../explorer.h" // for MainFrame::OpenShellFolders()
32 #include "desktop.h"
33
34 #include "../globals.h"
35 #include "../externals.h"
36
37 #include "../taskbar/desktopbar.h"
38
39 #include "../explorer_intres.h"
40
41
42 static BOOL (WINAPI*SetShellWindow)(HWND);
43 static BOOL (WINAPI*SetShellWindowEx)(HWND, HWND);
44
45
46 Desktops::Desktops()
47 : _current_desktop(0)
48 {
49 }
50
51 Desktops::~Desktops()
52 {
53 // show all hidden windows
54 for(iterator it_dsk=begin(); it_dsk!=end(); ++it_dsk)
55 for(WindowSet::iterator it=it_dsk->_windows.begin(); it!=it_dsk->_windows.end(); ++it)
56 ShowWindowAsync(*it, SW_SHOW);
57 }
58
59 void Desktops::init()
60 {
61 resize(DESKTOP_COUNT);
62 }
63
64 static BOOL CALLBACK DesktopEnumFct(HWND hwnd, LPARAM lparam)
65 {
66 WindowSet& windows = *(WindowSet*)lparam;
67
68 if (IsWindowVisible(hwnd))
69 if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop)
70 windows.insert(hwnd);
71
72 return TRUE;
73 }
74
75 void Desktops::SwitchToDesktop(int idx)
76 {
77 if (_current_desktop == idx)
78 return;
79
80 Desktop& desktop = (*this)[idx];
81
82 // save currently visible application windows
83 Desktop& old_desktop = (*this)[_current_desktop];
84 WindowSet& windows = old_desktop._windows;
85
86 windows.clear();
87 EnumWindows(DesktopEnumFct, (LPARAM)&windows);
88
89 // hide all windows we found
90 for(WindowSet::iterator it=windows.begin(); it!=windows.end(); ++it)
91 ShowWindowAsync(*it, SW_HIDE);
92
93 // show all windows of the new desktop
94 for(WindowSet::iterator it=desktop._windows.begin(); it!=desktop._windows.end(); ++it)
95 ShowWindowAsync(*it, SW_SHOW);
96
97 desktop._windows.clear();
98
99 _current_desktop = idx;
100 }
101
102
103 BOOL IsAnyDesktopRunning()
104 {
105 HINSTANCE hUser32 = GetModuleHandle(TEXT("user32"));
106
107 SetShellWindow = (BOOL(WINAPI*)(HWND)) GetProcAddress(hUser32, "SetShellWindow");
108 SetShellWindowEx = (BOOL(WINAPI*)(HWND,HWND)) GetProcAddress(hUser32, "SetShellWindowEx");
109
110 return GetShellWindow() != 0;
111 }
112
113
114 static void draw_desktop_background(HWND hwnd, HDC hdc)
115 {
116 ClientRect rect(hwnd);
117
118 PaintDesktop(hdc);
119 /*
120 HBRUSH bkgndBrush = CreateSolidBrush(RGB(0,32,160)); // dark blue
121 FillRect(hdc, &rect, bkgndBrush);
122 DeleteBrush(bkgndBrush);
123 */
124
125 rect.left = rect.right - 280;
126 rect.top = rect.bottom - 56 - DESKTOPBARBAR_HEIGHT;
127 rect.right = rect.left + 250;
128 rect.bottom = rect.top + 40;
129
130 #include "../buildno.h"
131 static const LPCTSTR BkgndText = TEXT("ReactOS ")TEXT(KERNEL_VERSION_STR)TEXT(" Explorer\nby Martin Fuchs");
132
133 BkMode bkMode(hdc, TRANSPARENT);
134
135 TextColor textColor(hdc, RGB(128,128,192));
136 DrawText(hdc, BkgndText, -1, &rect, DT_RIGHT);
137
138 SetTextColor(hdc, RGB(255,255,255));
139 --rect.right;
140 ++rect.top;
141 DrawText(hdc, BkgndText, -1, &rect, DT_RIGHT);
142 }
143
144
145 BackgroundWindow::BackgroundWindow(HWND hwnd)
146 : super(hwnd)
147 {
148 // set background brush for the short moment of displaying the
149 // background color while moving foreground windows
150 SetClassLong(hwnd, GCL_HBRBACKGROUND, COLOR_BACKGROUND+1);
151 }
152
153 LRESULT BackgroundWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
154 {
155 switch(nmsg) {
156 case WM_ERASEBKGND:
157 PaintDesktop((HDC)wparam);
158 return TRUE;
159
160 case WM_MBUTTONDBLCLK:
161 explorer_show_frame(SW_SHOWNORMAL);
162 break;
163
164 default:
165 return super::WndProc(nmsg, wparam, lparam);
166 }
167
168 return 0;
169 }
170
171
172 DesktopWindow::DesktopWindow(HWND hwnd)
173 : super(hwnd)
174 {
175 _pShellView = NULL;
176 }
177
178 DesktopWindow::~DesktopWindow()
179 {
180 if (_pShellView)
181 _pShellView->Release();
182 }
183
184
185 HWND DesktopWindow::Create()
186 {
187 static IconWindowClass wcDesktop(TEXT("Progman"), IDI_REACTOS, CS_DBLCLKS);
188 wcDesktop.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1);
189
190 int width = GetSystemMetrics(SM_CXSCREEN);
191 int height = GetSystemMetrics(SM_CYSCREEN);
192
193 HWND hwndDesktop = Window::Create(WINDOW_CREATOR(DesktopWindow),
194 WS_EX_TOOLWINDOW, wcDesktop, TEXT("Program Manager"), WS_POPUP|WS_VISIBLE|WS_CLIPCHILDREN,
195 0, 0, width, height, 0);
196
197 // work around to display desktop bar in Wine
198 ShowWindow(GET_WINDOW(DesktopWindow, hwndDesktop)->_desktopBar, SW_SHOW);
199
200 // work around for Windows NT, Win 98, ...
201 // Without this the desktop has mysteriously only a size of 800x600 pixels.
202 MoveWindow(hwndDesktop, 0, 0, width, height, TRUE);
203
204 return hwndDesktop;
205 }
206
207
208 LRESULT DesktopWindow::Init(LPCREATESTRUCT pcs)
209 {
210 if (super::Init(pcs))
211 return 1;
212
213 HRESULT hr = GetDesktopFolder()->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
214 /* also possible:
215 SFV_CREATE sfv_create;
216
217 sfv_create.cbSize = sizeof(SFV_CREATE);
218 sfv_create.pshf = GetDesktopFolder();
219 sfv_create.psvOuter = NULL;
220 sfv_create.psfvcb = NULL;
221
222 HRESULT hr = SHCreateShellFolderView(&sfv_create, &_pShellView);
223 */
224 HWND hWndView = 0;
225
226 if (SUCCEEDED(hr)) {
227 FOLDERSETTINGS fs;
228
229 fs.ViewMode = FVM_ICON;
230 fs.fFlags = FWF_DESKTOP|FWF_NOCLIENTEDGE|FWF_NOSCROLL|FWF_BESTFITWINDOW|FWF_SNAPTOGRID; //|FWF_AUTOARRANGE;
231
232 ClientRect rect(_hwnd);
233
234 hr = _pShellView->CreateViewWindow(NULL, &fs, this, &rect, &hWndView);
235
236 ///@todo use IShellBrowser::GetViewStateStream() to restore previous view state -> see SHOpenRegStream()
237
238 if (SUCCEEDED(hr)) {
239 g_Globals._hwndShellView = hWndView;
240
241 // subclass shellview window
242 new DesktopShellView(hWndView, _pShellView);
243
244 _pShellView->UIActivate(SVUIA_ACTIVATE_FOCUS);
245
246 /*
247 IShellView2* pShellView2;
248
249 hr = _pShellView->QueryInterface(IID_IShellView2, (void**)&pShellView2);
250
251 SV2CVW2_PARAMS params;
252 params.cbSize = sizeof(SV2CVW2_PARAMS);
253 params.psvPrev = _pShellView;
254 params.pfs = &fs;
255 params.psbOwner = this;
256 params.prcView = ▭
257 params.pvid = params.pvid;//@@
258
259 hr = pShellView2->CreateViewWindow2(&params);
260 params.pvid;
261 */
262
263 /*
264 IFolderView* pFolderView;
265
266 hr = _pShellView->QueryInterface(IID_IFolderView, (void**)&pFolderView);
267
268 if (SUCCEEDED(hr)) {
269 hr = pFolderView->GetAutoArrange();
270 hr = pFolderView->SetCurrentViewMode(FVM_DETAILS);
271 }
272 */
273 }
274 }
275
276 if (hWndView && SetShellWindowEx)
277 SetShellWindowEx(_hwnd, hWndView);
278 else if (SetShellWindow)
279 SetShellWindow(_hwnd);
280
281 // create the explorer bar
282 _desktopBar = DesktopBar::Create();
283 g_Globals._hwndDesktopBar = _desktopBar;
284
285 return 0;
286 }
287
288
289 LRESULT DesktopWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
290 {
291 switch(nmsg) {
292 case WM_PAINT:
293 draw_desktop_background(_hwnd, PaintCanvas(_hwnd));
294 break;
295
296 case WM_LBUTTONDBLCLK:
297 case WM_RBUTTONDBLCLK:
298 case WM_MBUTTONDBLCLK:
299 explorer_show_frame(SW_SHOWNORMAL);
300 break;
301
302 case WM_GETISHELLBROWSER:
303 return (LRESULT)static_cast<IShellBrowser*>(this);
304
305 case WM_DESTROY:
306
307 ///@todo use IShellBrowser::GetViewStateStream() and _pShellView->SaveViewState() to store view state
308
309 if (SetShellWindow)
310 SetShellWindow(0);
311 break;
312
313 case WM_CLOSE:
314 ShowExitWindowsDialog(_hwnd);
315 break;
316
317 case WM_SYSCOMMAND:
318 if (wparam == SC_TASKLIST) {
319 if (_desktopBar)
320 SendMessage(_desktopBar, nmsg, wparam, lparam);
321 }
322 goto def;
323
324 default: def:
325 return super::WndProc(nmsg, wparam, lparam);
326 }
327
328 return 0;
329 }
330
331
332 HRESULT DesktopWindow::OnDefaultCommand(LPIDA pida)
333 {
334 if (MainFrame::OpenShellFolders(pida, 0))
335 return S_OK;
336
337 return E_NOTIMPL;
338 }
339
340
341 DesktopShellView::DesktopShellView(HWND hwnd, IShellView* pShellView)
342 : super(hwnd),
343 _pShellView(pShellView)
344 {
345 _hwndListView = ::GetNextWindow(hwnd, GW_CHILD);
346
347 SetWindowStyle(_hwndListView, GetWindowStyle(_hwndListView)&~LVS_ALIGNMASK);//|LVS_ALIGNTOP|LVS_AUTOARRANGE);
348
349 // work around for Windows NT, Win 98, ...
350 // Without this the desktop has mysteriously only a size of 800x600 pixels.
351 ClientRect rect(hwnd);
352 MoveWindow(_hwndListView, 0, 0, rect.right, rect.bottom, TRUE);
353
354 // subclass background window
355 new BackgroundWindow(_hwndListView);
356
357 InitDragDrop();
358 }
359
360 bool DesktopShellView::InitDragDrop()
361 {
362 CONTEXT("DesktopShellView::InitDragDrop()");
363
364 _pDropTarget = new DesktopDropTarget(_hwnd);
365
366 if (!_pDropTarget)
367 return false;
368
369 _pDropTarget->AddRef();
370
371 if (FAILED(RegisterDragDrop(_hwnd, _pDropTarget))) {
372 _pDropTarget->Release();
373 _pDropTarget = NULL;
374 return false;
375 }
376 else
377 _pDropTarget->Release();
378
379 FORMATETC ftetc;
380
381 ftetc.dwAspect = DVASPECT_CONTENT;
382 ftetc.lindex = -1;
383 ftetc.tymed = TYMED_HGLOBAL;
384 ftetc.cfFormat = CF_HDROP;
385
386 _pDropTarget->AddSuportedFormat(ftetc);
387
388 return true;
389 }
390
391 LRESULT DesktopShellView::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
392 {
393 switch(nmsg) {
394 case WM_CONTEXTMENU:
395 if (!DoContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)))
396 DoDesktopContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
397 break;
398
399 default:
400 return super::WndProc(nmsg, wparam, lparam);
401 }
402
403 return 0;
404 }
405
406 int DesktopShellView::Command(int id, int code)
407 {
408 return super::Command(id, code);
409 }
410
411 int DesktopShellView::Notify(int id, NMHDR* pnmh)
412 {
413 return super::Notify(id, pnmh);
414 }
415
416 bool DesktopShellView::DoContextMenu(int x, int y)
417 {
418 IDataObject* selection;
419
420 HRESULT hr = _pShellView->GetItemObject(SVGIO_SELECTION, IID_IDataObject, (void**)&selection);
421 if (FAILED(hr))
422 return false;
423
424 PIDList pidList;
425
426 hr = pidList.GetData(selection);
427 if (FAILED(hr)) {
428 selection->Release();
429 //CHECKERROR(hr);
430 return false;
431 }
432
433 LPIDA pida = pidList;
434 if (!pida->cidl) {
435 selection->Release();
436 return false;
437 }
438
439 LPCITEMIDLIST parent_pidl = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]);
440
441 LPCITEMIDLIST* apidl = (LPCITEMIDLIST*) alloca(pida->cidl*sizeof(LPCITEMIDLIST));
442
443 for(int i=pida->cidl; i>0; --i)
444 apidl[i-1] = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[i]);
445
446 hr = ShellFolderContextMenu(ShellFolder(parent_pidl), _hwnd, pida->cidl, apidl, x, y);
447
448 selection->Release();
449
450 CHECKERROR(hr);
451
452 return true;
453 }
454
455 HRESULT DesktopShellView::DoDesktopContextMenu(int x, int y)
456 {
457 IContextMenu* pcm;
458
459 HRESULT hr = DesktopFolder()->GetUIObjectOf(_hwnd, 0, NULL, IID_IContextMenu, NULL, (LPVOID*)&pcm);
460
461 if (SUCCEEDED(hr)) {
462 HMENU hmenu = CreatePopupMenu();
463
464 if (hmenu) {
465 hr = pcm->QueryContextMenu(hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST-1, CMF_NORMAL|CMF_EXPLORE);
466
467 if (SUCCEEDED(hr)) {
468 AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
469 AppendMenu(hmenu, 0, FCIDM_SHVIEWLAST-1, ResString(IDS_ABOUT_EXPLORER));
470
471 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, _hwnd, NULL);
472
473 if (idCmd == FCIDM_SHVIEWLAST-1) {
474 explorer_about(_hwnd);
475 } else if (idCmd) {
476 CMINVOKECOMMANDINFO cmi;
477
478 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
479 cmi.fMask = 0;
480 cmi.hwnd = _hwnd;
481 cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST);
482 cmi.lpParameters = NULL;
483 cmi.lpDirectory = NULL;
484 cmi.nShow = SW_SHOWNORMAL;
485 cmi.dwHotKey = 0;
486 cmi.hIcon = 0;
487
488 hr = pcm->InvokeCommand(&cmi);
489 }
490 }
491 }
492
493 pcm->Release();
494 }
495
496 return hr;
497 }