support for owner drawn context menus on the desktop
[reactos.git] / reactos / subsys / system / explorer / desktop / desktop.cpp
1 /*
2 * Copyright 2003, 2004, 2005 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 "precomp.h"
30
31 #include "../explorer_intres.h"
32
33 #include "../taskbar/desktopbar.h"
34 #include "../taskbar/taskbar.h" // for PM_GET_LAST_ACTIVE
35
36
37 static BOOL (WINAPI*SetShellWindow)(HWND);
38 static BOOL (WINAPI*SetShellWindowEx)(HWND, HWND);
39
40
41 #ifdef _USE_HDESK
42
43 Desktop::Desktop(HDESK hdesktop/*, HWINSTA hwinsta*/)
44 : _hdesktop(hdesktop)
45 // _hwinsta(hwinsta)
46 {
47 }
48
49 Desktop::~Desktop()
50 {
51 if (_hdesktop)
52 CloseDesktop(_hdesktop);
53
54 // if (_hwinsta)
55 // CloseWindowStation(_hwinsta);
56
57 if (_pThread.get()) {
58 _pThread->Stop();
59 _pThread.release();
60 }
61 }
62
63 #endif
64
65
66 Desktops::Desktops()
67 : _current_desktop(0)
68 {
69 }
70
71 Desktops::~Desktops()
72 {
73 // show all hidden windows
74 for(iterator it_dsk=begin(); it_dsk!=end(); ++it_dsk)
75 for(WindowSet::iterator it=it_dsk->_windows.begin(); it!=it_dsk->_windows.end(); ++it)
76 ShowWindowAsync(*it, SW_SHOW);
77 }
78
79 void Desktops::init()
80 {
81 resize(DESKTOP_COUNT);
82
83 #ifdef _USE_HDESK
84 DesktopPtr& desktop = (*this)[0];
85
86 desktop = DesktopPtr(new Desktop(OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP)));
87 #endif
88 }
89
90 #ifdef _USE_HDESK
91
92 void Desktops::SwitchToDesktop(int idx)
93 {
94 if (_current_desktop == idx)
95 return;
96
97 DesktopPtr& desktop = (*this)[idx];
98
99 DesktopThread* pThread = NULL;
100
101 if (desktop.get()) {
102 if (desktop->_hdesktop)
103 if (!SwitchDesktop(desktop->_hdesktop))
104 return;
105 } else {
106 FmtString desktop_name(TEXT("Desktop %d"), idx);
107
108 SECURITY_ATTRIBUTES saAttr = {sizeof(SECURITY_ATTRIBUTES), 0, TRUE};
109 /*
110 HWINSTA hwinsta = CreateWindowStation(TEXT("ExplorerWinStation"), 0, GENERIC_ALL, &saAttr);
111
112 if (!SetProcessWindowStation(hwinsta))
113 return;
114 */
115 HDESK hdesktop = CreateDesktop(desktop_name, NULL, NULL, 0, GENERIC_ALL, &saAttr);
116 if (!hdesktop)
117 return;
118
119 desktop = DesktopPtr(new Desktop(hdesktop/*, hwinsta*/));
120
121 pThread = new DesktopThread(*desktop);
122 }
123
124 _current_desktop = idx;
125
126 if (pThread) {
127 desktop->_pThread = DesktopThreadPtr(pThread);
128 pThread->Start();
129 }
130 }
131
132 int DesktopThread::Run()
133 {
134 if (!SetThreadDesktop(_desktop._hdesktop))
135 return -1;
136
137 HDESK hDesk_old = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP);
138
139 if (!SwitchDesktop(_desktop._hdesktop))
140 return -1;
141
142 if (!_desktop._hwndDesktop)
143 _desktop._hwndDesktop = DesktopWindow::Create();
144
145 int ret = Window::MessageLoop();
146
147 SwitchDesktop(hDesk_old);
148
149 return ret;
150 }
151
152 #else // _USE_HDESK
153
154 static BOOL CALLBACK SwitchDesktopEnumFct(HWND hwnd, LPARAM lparam)
155 {
156 WindowSet& windows = *(WindowSet*)lparam;
157
158 if (IsWindowVisible(hwnd))
159 if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop)
160 windows.insert(hwnd);
161
162 return TRUE;
163 }
164
165 void Desktops::SwitchToDesktop(int idx)
166 {
167 if (_current_desktop == idx)
168 return;
169
170 Desktop& old_desktop = (*this)[_current_desktop];
171 WindowSet& windows = old_desktop._windows;
172 Desktop& desktop = (*this)[idx];
173
174 windows.clear();
175
176 // collect window handles of all other desktops
177 WindowSet other_wnds;
178 for(const_iterator it1=begin(); it1!=end(); ++it1)
179 for(WindowSet::const_iterator it2=it1->_windows.begin(); it2!=it1->_windows.end(); ++it2)
180 other_wnds.insert(*it2);
181
182 // save currently visible application windows
183 EnumWindows(SwitchDesktopEnumFct, (LPARAM)&windows);
184
185 old_desktop._hwndForeground = (HWND)SendMessage(g_Globals._hwndDesktopBar, PM_GET_LAST_ACTIVE, 0, 0);
186
187 // hide all windows of the previous desktop
188 for(WindowSet::iterator it=windows.begin(); it!=windows.end(); ++it)
189 ShowWindowAsync(*it, SW_HIDE);
190
191 // show all windows of the new desktop
192 for(WindowSet::iterator it=desktop._windows.begin(); it!=desktop._windows.end(); ++it)
193 ShowWindowAsync(*it, SW_SHOW);
194
195 if (desktop._hwndForeground)
196 SetForegroundWindow(desktop._hwndForeground);
197
198 // remove the window handles of the other desktops from what we found on the previous desktop
199 for(WindowSet::const_iterator it=other_wnds.begin(); it!=other_wnds.end(); ++it)
200 windows.erase(*it);
201
202 // We don't need to store the window handles of what's now visible the now current desktop.
203 desktop._windows.clear();
204
205 _current_desktop = idx;
206 }
207
208 #endif // _USE_HDESK
209
210
211 static BOOL CALLBACK MinimizeDesktopEnumFct(HWND hwnd, LPARAM lparam)
212 {
213 list<MinimizeStruct>& minimized = *(list<MinimizeStruct>*)lparam;
214
215 if (IsWindowVisible(hwnd))
216 if (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop)
217 if (!IsIconic(hwnd)) {
218 minimized.push_back(MinimizeStruct(hwnd, GetWindowStyle(hwnd)));
219 ShowWindowAsync(hwnd, SW_MINIMIZE);
220 }
221
222 return TRUE;
223 }
224
225 /// minimize/restore all windows on the desktop
226 void Desktops::ToggleMinimize()
227 {
228 list<MinimizeStruct>& minimized = (*this)[_current_desktop]._minimized;
229
230 if (minimized.empty()) {
231 EnumWindows(MinimizeDesktopEnumFct, (LPARAM)&minimized);
232 } else {
233 for(list<MinimizeStruct>::const_iterator it=minimized.begin(); it!=minimized.end(); ++it)
234 ShowWindowAsync(it->first, it->second&WS_MAXIMIZE? SW_MAXIMIZE: SW_RESTORE);
235
236 minimized.clear();
237 }
238 }
239
240
241 BOOL IsAnyDesktopRunning()
242 {
243 HINSTANCE hUser32 = GetModuleHandle(TEXT("user32"));
244
245 SetShellWindow = (BOOL(WINAPI*)(HWND)) GetProcAddress(hUser32, "SetShellWindow");
246 SetShellWindowEx = (BOOL(WINAPI*)(HWND,HWND)) GetProcAddress(hUser32, "SetShellWindowEx");
247
248 return GetShellWindow() != 0;
249 }
250
251
252 BackgroundWindow::BackgroundWindow(HWND hwnd)
253 : super(hwnd)
254 {
255 // set background brush for the short moment of displaying the
256 // background color while moving foreground windows
257 SetClassLong(hwnd, GCL_HBRBACKGROUND, COLOR_BACKGROUND+1);
258
259 _display_version = RegGetDWORDValue(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), TEXT("PaintDesktopVersion"), 1);
260 }
261
262 LRESULT BackgroundWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
263 {
264 switch(nmsg) {
265 case WM_ERASEBKGND:
266 DrawDesktopBkgnd((HDC)wparam);
267 return TRUE;
268
269 case WM_MBUTTONDBLCLK:
270 /* Imagelist icons are missing if MainFrame::Create() is called directly from here!
271 explorer_show_frame(SW_SHOWNORMAL); */
272 PostMessage(g_Globals._hwndDesktop, nmsg, wparam, lparam);
273 break;
274
275 case PM_DISPLAY_VERSION:
276 if (lparam || wparam) {
277 DWORD or_mask = wparam;
278 DWORD reset_mask = LOWORD(lparam);
279 DWORD xor_mask = HIWORD(lparam);
280 _display_version = ((_display_version&~reset_mask) | or_mask) ^ xor_mask;
281 RegSetDWORDValue(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), TEXT("PaintDesktopVersion"), _display_version);
282 ///@todo Changing the PaintDesktopVersion-Flag needs a restart of the shell -> display a message box
283 InvalidateRect(_hwnd, NULL, TRUE);
284 }
285 return _display_version;
286
287 default:
288 return super::WndProc(nmsg, wparam, lparam);
289 }
290
291 return 0;
292 }
293
294 void BackgroundWindow::DrawDesktopBkgnd(HDC hdc)
295 {
296 PaintDesktop(hdc);
297
298 /* special solid background
299 HBRUSH bkgndBrush = CreateSolidBrush(RGB(0,32,160)); // dark blue
300 FillRect(hdc, &rect, bkgndBrush);
301 DeleteBrush(bkgndBrush);
302 */
303 }
304
305
306 DesktopWindow::DesktopWindow(HWND hwnd)
307 : super(hwnd)
308 {
309 _pShellView = NULL;
310 }
311
312 DesktopWindow::~DesktopWindow()
313 {
314 if (_pShellView)
315 _pShellView->Release();
316 }
317
318
319 HWND DesktopWindow::Create()
320 {
321 static IconWindowClass wcDesktop(TEXT("Progman"), IDI_REACTOS, CS_DBLCLKS);
322 /* (disabled because of small ugly temporary artefacts when hiding start menu)
323 wcDesktop.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1); */
324
325 int width = GetSystemMetrics(SM_CXSCREEN);
326 int height = GetSystemMetrics(SM_CYSCREEN);
327
328 HWND hwndDesktop = Window::Create(WINDOW_CREATOR(DesktopWindow),
329 WS_EX_TOOLWINDOW, wcDesktop, TEXT("Program Manager"), WS_POPUP|WS_VISIBLE, //|WS_CLIPCHILDREN for SDI frames
330 0, 0, width, height, 0);
331
332 // work around to display desktop bar in Wine
333 ShowWindow(GET_WINDOW(DesktopWindow, hwndDesktop)->_desktopBar, SW_SHOW);
334
335 // work around for Windows NT, Win 98, ...
336 // Without this the desktop has mysteriously only a size of 800x600 pixels.
337 MoveWindow(hwndDesktop, 0, 0, width, height, TRUE);
338
339 return hwndDesktop;
340 }
341
342
343 LRESULT DesktopWindow::Init(LPCREATESTRUCT pcs)
344 {
345 if (super::Init(pcs))
346 return 1;
347
348 HRESULT hr = GetDesktopFolder()->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
349 /* also possible:
350 SFV_CREATE sfv_create;
351
352 sfv_create.cbSize = sizeof(SFV_CREATE);
353 sfv_create.pshf = GetDesktopFolder();
354 sfv_create.psvOuter = NULL;
355 sfv_create.psfvcb = NULL;
356
357 HRESULT hr = SHCreateShellFolderView(&sfv_create, &_pShellView);
358 */
359 HWND hWndView = 0;
360
361 if (SUCCEEDED(hr)) {
362 FOLDERSETTINGS fs;
363
364 fs.ViewMode = FVM_ICON;
365 fs.fFlags = FWF_DESKTOP|FWF_NOCLIENTEDGE|FWF_NOSCROLL|FWF_BESTFITWINDOW|FWF_SNAPTOGRID; //|FWF_AUTOARRANGE;
366
367 ClientRect rect(_hwnd);
368
369 hr = _pShellView->CreateViewWindow(NULL, &fs, this, &rect, &hWndView);
370
371 ///@todo use IShellBrowser::GetViewStateStream() to restore previous view state -> see SHOpenRegStream()
372
373 if (SUCCEEDED(hr)) {
374 g_Globals._hwndShellView = hWndView;
375
376 // subclass shellview window
377 new DesktopShellView(hWndView, _pShellView);
378
379 _pShellView->UIActivate(SVUIA_ACTIVATE_FOCUS);
380
381 /*
382 IShellView2* pShellView2;
383
384 hr = _pShellView->QueryInterface(IID_IShellView2, (void**)&pShellView2);
385
386 SV2CVW2_PARAMS params;
387 params.cbSize = sizeof(SV2CVW2_PARAMS);
388 params.psvPrev = _pShellView;
389 params.pfs = &fs;
390 params.psbOwner = this;
391 params.prcView = &rect;
392 params.pvid = params.pvid;//@@
393
394 hr = pShellView2->CreateViewWindow2(&params);
395 params.pvid;
396 */
397
398 /*
399 IFolderView* pFolderView;
400
401 hr = _pShellView->QueryInterface(IID_IFolderView, (void**)&pFolderView);
402
403 if (SUCCEEDED(hr)) {
404 hr = pFolderView->GetAutoArrange();
405 hr = pFolderView->SetCurrentViewMode(FVM_DETAILS);
406 }
407 */
408 }
409 }
410
411 if (hWndView && SetShellWindowEx)
412 SetShellWindowEx(_hwnd, hWndView);
413 else if (SetShellWindow)
414 SetShellWindow(_hwnd);
415
416 // create the explorer bar
417 _desktopBar = DesktopBar::Create();
418 g_Globals._hwndDesktopBar = _desktopBar;
419
420 return 0;
421 }
422
423
424 LRESULT DesktopWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
425 {
426 switch(nmsg) {
427 case WM_LBUTTONDBLCLK:
428 case WM_RBUTTONDBLCLK:
429 case WM_MBUTTONDBLCLK:
430 explorer_show_frame(SW_SHOWNORMAL);
431 break;
432
433 case WM_GETISHELLBROWSER:
434 return (LRESULT)static_cast<IShellBrowser*>(this);
435
436 case WM_DESTROY:
437
438 ///@todo use IShellBrowser::GetViewStateStream() and _pShellView->SaveViewState() to store view state
439
440 if (SetShellWindow)
441 SetShellWindow(0);
442 break;
443
444 case WM_CLOSE:
445 ShowExitWindowsDialog(_hwnd);
446 break;
447
448 case WM_SYSCOMMAND:
449 if (wparam == SC_TASKLIST) {
450 if (_desktopBar)
451 SendMessage(_desktopBar, nmsg, wparam, lparam);
452 }
453 goto def;
454
455 default: def:
456 return super::WndProc(nmsg, wparam, lparam);
457 }
458
459 return 0;
460 }
461
462
463 HRESULT DesktopWindow::OnDefaultCommand(LPIDA pida)
464 {
465 if (MainFrameBase::OpenShellFolders(pida, 0))
466 return S_OK;
467
468 return E_NOTIMPL;
469 }
470
471
472 DesktopShellView::DesktopShellView(HWND hwnd, IShellView* pShellView)
473 : super(hwnd),
474 _pShellView(pShellView)
475 {
476 _pctxmenu2 = NULL;
477 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
478 _pctxmenu3 = NULL;
479 #endif
480
481 _hwndListView = ::GetNextWindow(hwnd, GW_CHILD);
482
483 SetWindowStyle(_hwndListView, GetWindowStyle(_hwndListView)&~LVS_ALIGNMASK);//|LVS_ALIGNTOP|LVS_AUTOARRANGE);
484
485 // work around for Windows NT, Win 98, ...
486 // Without this the desktop has mysteriously only a size of 800x600 pixels.
487 ClientRect rect(hwnd);
488 MoveWindow(_hwndListView, 0, 0, rect.right, rect.bottom, TRUE);
489
490 // subclass background window
491 new BackgroundWindow(_hwndListView);
492
493 _icon_algo = 1; // default icon arrangement
494
495 PositionIcons();
496 InitDragDrop();
497 }
498
499 bool DesktopShellView::InitDragDrop()
500 {
501 CONTEXT("DesktopShellView::InitDragDrop()");
502
503 _pDropTarget = new DesktopDropTarget(_hwnd);
504
505 if (!_pDropTarget)
506 return false;
507
508 _pDropTarget->AddRef();
509
510 if (FAILED(RegisterDragDrop(_hwnd, _pDropTarget))) {
511 _pDropTarget->Release();
512 _pDropTarget = NULL;
513 return false;
514 }
515 else
516 _pDropTarget->Release();
517
518 FORMATETC ftetc;
519
520 ftetc.dwAspect = DVASPECT_CONTENT;
521 ftetc.lindex = -1;
522 ftetc.tymed = TYMED_HGLOBAL;
523 ftetc.cfFormat = CF_HDROP;
524
525 _pDropTarget->AddSuportedFormat(ftetc);
526
527 return true;
528 }
529
530 LRESULT DesktopShellView::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
531 {
532 switch(nmsg) {
533 case WM_CONTEXTMENU:
534 if (!DoContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)))
535 DoDesktopContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
536 break;
537
538 case PM_SET_ICON_ALGORITHM:
539 _icon_algo = wparam;
540 PositionIcons();
541 break;
542
543 case PM_GET_ICON_ALGORITHM:
544 return _icon_algo;
545
546 case PM_DISPLAY_VERSION:
547 return SendMessage(_hwndListView, nmsg, wparam, lparam);
548
549 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
550 case WM_MENUCHAR: // only supported by IContextMenu3
551 if (_pctxmenu3) {
552 LRESULT lResult = 0;
553
554 _pctxmenu3->HandleMenuMsg2(nmsg, wparam, lparam, &lResult);
555
556 return lResult;
557 }
558 break;
559 #endif
560
561 case WM_DRAWITEM:
562 case WM_MEASUREITEM:
563 if (wparam)
564 break; // If wParam != 0 then the message is not menu-related.
565
566 // fall through
567
568 case WM_INITMENUPOPUP:
569 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
570 if (_pctxmenu3)
571 _pctxmenu3->HandleMenuMsg(nmsg, wparam, lparam);
572 else
573 #endif
574 if (_pctxmenu2)
575 _pctxmenu2->HandleMenuMsg(nmsg, wparam, lparam);
576
577 return nmsg==WM_INITMENUPOPUP? 0: TRUE; // Inform caller that we handled WM_INITPOPUPMENU by ourself.
578
579 default:
580 return super::WndProc(nmsg, wparam, lparam);
581 }
582
583 return 0;
584 }
585
586 int DesktopShellView::Command(int id, int code)
587 {
588 return super::Command(id, code);
589 }
590
591 int DesktopShellView::Notify(int id, NMHDR* pnmh)
592 {
593 return super::Notify(id, pnmh);
594 }
595
596 bool DesktopShellView::DoContextMenu(int x, int y)
597 {
598 IDataObject* selection;
599
600 HRESULT hr = _pShellView->GetItemObject(SVGIO_SELECTION, IID_IDataObject, (void**)&selection);
601 if (FAILED(hr))
602 return false;
603
604 PIDList pidList;
605
606 hr = pidList.GetData(selection);
607 if (FAILED(hr)) {
608 selection->Release();
609 //CHECKERROR(hr);
610 return false;
611 }
612
613 LPIDA pida = pidList;
614 if (!pida->cidl) {
615 selection->Release();
616 return false;
617 }
618
619 LPCITEMIDLIST parent_pidl = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]);
620
621 LPCITEMIDLIST* apidl = (LPCITEMIDLIST*) alloca(pida->cidl*sizeof(LPCITEMIDLIST));
622
623 for(int i=pida->cidl; i>0; --i)
624 apidl[i-1] = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[i]);
625
626 hr = ShellFolderContextMenu(ShellFolder(parent_pidl), _hwnd, pida->cidl, apidl, x, y);
627
628 selection->Release();
629
630 CHECKERROR(hr);
631
632 return true;
633 }
634
635 HRESULT DesktopShellView::DoDesktopContextMenu(int x, int y)
636 {
637 IContextMenu* pcm1;
638 IContextMenu* pcm;
639
640 HRESULT hr = DesktopFolder()->GetUIObjectOf(_hwnd, 0, NULL, IID_IContextMenu, NULL, (LPVOID*)&pcm1);
641
642 if (SUCCEEDED(hr)) {
643 // Get the higher version context menu interfaces.
644 _pctxmenu2 = NULL;
645 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
646 _pctxmenu3 = NULL;
647
648 if (pcm1->QueryInterface(IID_IContextMenu3, (void**)&pcm) == NOERROR)
649 _pctxmenu3 = (LPCONTEXTMENU3)pcm;
650 else
651 #endif
652 if (pcm1->QueryInterface (IID_IContextMenu2, (void**)&pcm) == NOERROR)
653 _pctxmenu2 = (LPCONTEXTMENU2)pcm;
654
655 if (pcm)
656 pcm1->Release();
657 else
658 pcm = pcm1;
659
660 HMENU hmenu = CreatePopupMenu();
661
662 if (hmenu) {
663 hr = pcm->QueryContextMenu(hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST-1, CMF_NORMAL|CMF_EXPLORE);
664
665 if (SUCCEEDED(hr)) {
666 AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
667 AppendMenu(hmenu, 0, FCIDM_SHVIEWLAST-1, ResString(IDS_ABOUT_EXPLORER));
668
669 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, _hwnd, NULL);
670
671 _pctxmenu2 = NULL;
672 #ifndef __MINGW32__ // IContextMenu3 missing in MinGW (as of 6.2.2005)
673 _pctxmenu3 = NULL;
674 #endif
675
676 if (idCmd == FCIDM_SHVIEWLAST-1) {
677 explorer_about(_hwnd);
678 } else if (idCmd) {
679 CMINVOKECOMMANDINFO cmi;
680
681 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
682 cmi.fMask = 0;
683 cmi.hwnd = _hwnd;
684 cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST);
685 cmi.lpParameters = NULL;
686 cmi.lpDirectory = NULL;
687 cmi.nShow = SW_SHOWNORMAL;
688 cmi.dwHotKey = 0;
689 cmi.hIcon = 0;
690
691 hr = pcm->InvokeCommand(&cmi);
692 }
693 }
694 }
695
696 pcm->Release();
697 }
698
699 return hr;
700 }
701
702
703 #define ARRANGE_BORDER_DOWN 8
704 #define ARRANGE_BORDER_HV 9
705 #define ARRANGE_ROUNDABOUT 10
706
707 static const POINTS s_align_start[] = {
708 {0, 0}, // left/top
709 {0, 0},
710 {1, 0}, // right/top
711 {1, 0},
712 {0, 1}, // left/bottom
713 {0, 1},
714 {1, 1}, // right/bottom
715 {1, 1},
716
717 {0, 0}, // left/top
718 {0, 0},
719 {0, 0}
720 };
721
722 static const POINTS s_align_dir1[] = {
723 { 0, +1}, // down
724 {+1, 0}, // right
725 {-1, 0}, // left
726 { 0, +1}, // down
727 { 0, -1}, // up
728 {+1, 0}, // right
729 {-1, 0}, // left
730 { 0, -1}, // up
731
732 { 0, +1}, // down
733 {+1, 0}, // right
734 {+1, 0} // right
735 };
736
737 static const POINTS s_align_dir2[] = {
738 {+1, 0}, // right
739 { 0, +1}, // down
740 { 0, +1}, // down
741 {-1, 0}, // left
742 {+1, 0}, // right
743 { 0, -1}, // up
744 { 0, -1}, // up
745 {-1, 0}, // left
746
747 {+1, 0}, // right
748 { 0, +1}, // down
749 { 0, +1} // down
750 };
751
752 typedef pair<int,int> IconPos;
753 typedef map<IconPos, int> IconMap;
754
755 void DesktopShellView::PositionIcons(int dir)
756 {
757 DWORD spacing = ListView_GetItemSpacing(_hwndListView, FALSE);
758
759 RECT work_area;
760 SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0);
761
762 const POINTS& dir1 = s_align_dir1[_icon_algo];
763 const POINTS& dir2 = s_align_dir2[_icon_algo];
764 const POINTS& start_pos = s_align_start[_icon_algo];
765
766 int dir_x1 = dir1.x;
767 int dir_y1 = dir1.y;
768 int dir_x2 = dir2.x;
769 int dir_y2 = dir2.y;
770
771 int cx = LOWORD(spacing);
772 int cy = HIWORD(spacing);
773
774 int dx1 = dir_x1 * cx;
775 int dy1 = dir_y1 * cy;
776 int dx2 = dir_x2 * cx;
777 int dy2 = dir_y2 * cy;
778
779 int start_x = (start_pos.x * work_area.right)/cx*cx + (cx-32)/2;
780 int start_y = (start_pos.y * work_area.bottom)/cy*cy + 4/*(cy-32)/2*/;
781
782 if (start_x >= work_area.right)
783 start_x -= cx;
784
785 if (start_y >= work_area.bottom)
786 start_y -= cy;
787
788 int x = start_x;
789 int y = start_y;
790
791 int all = ListView_GetItemCount(_hwndListView);
792 int i1, i2;
793
794 if (dir > 0) {
795 i1 = 0;
796 i2 = all;
797 } else {
798 i1 = all-1;
799 i2 = -1;
800 }
801
802 IconMap pos_idx;
803 int cnt = 0;
804
805 for(int idx=i1; idx!=i2; idx+=dir) {
806 pos_idx[IconPos(y, x)] = idx;
807
808 if (_icon_algo == ARRANGE_BORDER_DOWN) {
809 if (++cnt & 1)
810 x = work_area.right - x;
811 else {
812 y += dy1;
813
814 if (y >= work_area.bottom) {
815 y = start_y;
816 x += dx2;
817 }
818 }
819
820 continue;
821 }
822 else if (_icon_algo == ARRANGE_BORDER_HV) {
823 if (++cnt & 1)
824 x = work_area.right - x;
825 else if (cnt & 2) {
826 y += dy1;
827
828 if (y >= work_area.bottom) {
829 y = start_y;
830 x += dx2;
831 }
832 } else {
833 x += dx1;
834
835 if (x >= work_area.right) {
836 x = start_x;
837 y += dy2;
838 }
839 }
840
841 continue;
842 }
843 else if (_icon_algo == ARRANGE_ROUNDABOUT) {
844
845 ///@todo
846
847 }
848
849 x += dx1;
850 y += dy1;
851
852 if (x<0 || x>=work_area.right) {
853 x = start_x;
854 y += dy2;
855 } else if (y<0 || y>=work_area.bottom) {
856 y = start_y;
857 x += dx2;
858 }
859 }
860
861 // use a little trick to get the icons where we want them to be...
862
863 for(IconMap::const_iterator it=pos_idx.end(); --it!=pos_idx.begin(); ) {
864 const IconPos& pos = it->first;
865
866 ListView_SetItemPosition32(_hwndListView, it->second, pos.second, pos.first);
867 }
868
869 for(IconMap::const_iterator it=pos_idx.begin(); it!=pos_idx.end(); ++it) {
870 const IconPos& pos = it->first;
871
872 ListView_SetItemPosition32(_hwndListView, it->second, pos.second, pos.first);
873 }
874 }