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