Synchronize up to trunk's revision r57689.
[reactos.git] / base / shell / 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 "../resource.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 (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop)
159 if (IsWindowVisible(hwnd))
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 (hwnd!=g_Globals._hwndDesktopBar && hwnd!=g_Globals._hwndDesktop)
216 if (IsWindowVisible(hwnd) && !IsIconic(hwnd)) {
217 RECT rect;
218
219 if (GetWindowRect(hwnd,&rect))
220 if (rect.right>0 && rect.bottom>0 &&
221 rect.right>rect.left && rect.bottom>rect.top) {
222 minimized.push_back(MinimizeStruct(hwnd, GetWindowStyle(hwnd)));
223 ShowWindowAsync(hwnd, SW_MINIMIZE);
224 }
225 }
226
227 return TRUE;
228 }
229
230 /// minimize/restore all windows on the desktop
231 void Desktops::ToggleMinimize()
232 {
233 list<MinimizeStruct>& minimized = (*this)[_current_desktop]._minimized;
234
235 if (minimized.empty()) {
236 EnumWindows(MinimizeDesktopEnumFct, (LPARAM)&minimized);
237 } else {
238 for(list<MinimizeStruct>::const_reverse_iterator it=minimized.rbegin();
239 it!=minimized.rend(); ++it) {
240 ShowWindowAsync(it->first, it->second&WS_MAXIMIZE? SW_MAXIMIZE: SW_RESTORE);
241 Sleep(20);
242 }
243
244 minimized.clear();
245 }
246 }
247
248
249 BOOL IsAnyDesktopRunning()
250 {
251 HINSTANCE hUser32 = GetModuleHandle(TEXT("user32"));
252
253 SetShellWindow = (BOOL(WINAPI*)(HWND)) GetProcAddress(hUser32, "SetShellWindow");
254 SetShellWindowEx = (BOOL(WINAPI*)(HWND,HWND)) GetProcAddress(hUser32, "SetShellWindowEx");
255
256 return GetShellWindow() != 0;
257 }
258
259
260 BackgroundWindow::BackgroundWindow(HWND hwnd)
261 : super(hwnd)
262 {
263 // set background brush for the short moment of displaying the
264 // background color while moving foreground windows
265 SetClassLongPtr(hwnd, GCL_HBRBACKGROUND, COLOR_BACKGROUND+1);
266
267 _display_version = RegGetDWORDValue(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), TEXT("PaintDesktopVersion"), 1);
268 }
269
270 LRESULT BackgroundWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
271 {
272 switch(nmsg) {
273 case WM_ERASEBKGND:
274 DrawDesktopBkgnd((HDC)wparam);
275 return TRUE;
276
277 case WM_MBUTTONDBLCLK:
278 /* Imagelist icons are missing if MainFrame::Create() is called directly from here!
279 explorer_show_frame(SW_SHOWNORMAL); */
280 PostMessage(g_Globals._hwndDesktop, nmsg, wparam, lparam);
281 break;
282
283 case PM_DISPLAY_VERSION:
284 if (lparam || wparam) {
285 DWORD or_mask = wparam;
286 DWORD reset_mask = LOWORD(lparam);
287 DWORD xor_mask = HIWORD(lparam);
288 _display_version = ((_display_version&~reset_mask) | or_mask) ^ xor_mask;
289 RegSetDWORDValue(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), TEXT("PaintDesktopVersion"), _display_version);
290 ///@todo Changing the PaintDesktopVersion-Flag needs a restart of the shell -> display a message box
291 InvalidateRect(_hwnd, NULL, TRUE);
292 }
293 return _display_version;
294
295 default:
296 return super::WndProc(nmsg, wparam, lparam);
297 }
298
299 return 0;
300 }
301
302 void BackgroundWindow::DrawDesktopBkgnd(HDC hdc)
303 {
304 PaintDesktop(hdc);
305
306 /* special solid background
307 HBRUSH bkgndBrush = CreateSolidBrush(RGB(0,32,160)); // dark blue
308 FillRect(hdc, &rect, bkgndBrush);
309 DeleteBrush(bkgndBrush);
310 */
311 }
312
313
314 DesktopWindow::DesktopWindow(HWND hwnd)
315 : super(hwnd)
316 {
317 _pShellView = NULL;
318 }
319
320 DesktopWindow::~DesktopWindow()
321 {
322 if (_pShellView)
323 _pShellView->Release();
324 }
325
326
327 HWND DesktopWindow::Create()
328 {
329 static IconWindowClass wcDesktop(TEXT("Progman"), IDI_REACTOS, CS_DBLCLKS);
330 /* (disabled because of small ugly temporary artefacts when hiding start menu)
331 wcDesktop.hbrBackground = (HBRUSH)(COLOR_BACKGROUND+1); */
332
333 int width = GetSystemMetrics(SM_CXSCREEN);
334 int height = GetSystemMetrics(SM_CYSCREEN);
335
336 HWND hwndDesktop = Window::Create(WINDOW_CREATOR(DesktopWindow),
337 WS_EX_TOOLWINDOW, wcDesktop, TEXT("Program Manager"), WS_POPUP|WS_VISIBLE, //|WS_CLIPCHILDREN for SDI frames
338 0, 0, width, height, 0);
339
340 // work around to display desktop bar in Wine
341 ShowWindow(GET_WINDOW(DesktopWindow, hwndDesktop)->_desktopBar, SW_SHOW);
342
343 // work around for Windows NT, Win 98, ...
344 // Without this the desktop has mysteriously only a size of 800x600 pixels.
345 MoveWindow(hwndDesktop, 0, 0, width, height, TRUE);
346
347 return hwndDesktop;
348 }
349
350
351 LRESULT DesktopWindow::Init(LPCREATESTRUCT pcs)
352 {
353 if (super::Init(pcs))
354 return 1;
355
356 HRESULT hr = GetDesktopFolder()->CreateViewObject(_hwnd, IID_IShellView, (void**)&_pShellView);
357 /* also possible:
358 SFV_CREATE sfv_create;
359
360 sfv_create.cbSize = sizeof(SFV_CREATE);
361 sfv_create.pshf = GetDesktopFolder();
362 sfv_create.psvOuter = NULL;
363 sfv_create.psfvcb = NULL;
364
365 HRESULT hr = SHCreateShellFolderView(&sfv_create, &_pShellView);
366 */
367 HWND hWndView = 0;
368
369 if (SUCCEEDED(hr)) {
370 FOLDERSETTINGS fs;
371
372 fs.ViewMode = FVM_ICON;
373 fs.fFlags = FWF_DESKTOP|FWF_NOCLIENTEDGE|FWF_NOSCROLL|FWF_BESTFITWINDOW|FWF_SNAPTOGRID; //|FWF_AUTOARRANGE;
374
375 ClientRect rect(_hwnd);
376
377 hr = _pShellView->CreateViewWindow(NULL, &fs, this, &rect, &hWndView);
378
379 ///@todo use IShellBrowser::GetViewStateStream() to restore previous view state -> see SHOpenRegStream()
380
381 if (SUCCEEDED(hr)) {
382 g_Globals._hwndShellView = hWndView;
383
384 // subclass shellview window
385 new DesktopShellView(hWndView, _pShellView);
386
387 _pShellView->UIActivate(SVUIA_ACTIVATE_FOCUS);
388
389 /*
390 IShellView2* pShellView2;
391
392 hr = _pShellView->QueryInterface(IID_IShellView2, (void**)&pShellView2);
393
394 SV2CVW2_PARAMS params;
395 params.cbSize = sizeof(SV2CVW2_PARAMS);
396 params.psvPrev = _pShellView;
397 params.pfs = &fs;
398 params.psbOwner = this;
399 params.prcView = &rect;
400 params.pvid = params.pvid;//@@
401
402 hr = pShellView2->CreateViewWindow2(&params);
403 params.pvid;
404 */
405
406 /*
407 IFolderView* pFolderView;
408
409 hr = _pShellView->QueryInterface(IID_IFolderView, (void**)&pFolderView);
410
411 if (SUCCEEDED(hr)) {
412 hr = pFolderView->GetAutoArrange();
413 hr = pFolderView->SetCurrentViewMode(FVM_DETAILS);
414 }
415 */
416 }
417 }
418
419 if (hWndView && SetShellWindowEx)
420 SetShellWindowEx(_hwnd, hWndView);
421 else if (SetShellWindow)
422 SetShellWindow(_hwnd);
423
424 // create the explorer bar
425 _desktopBar = DesktopBar::Create();
426 g_Globals._hwndDesktopBar = _desktopBar;
427
428 return 0;
429 }
430
431
432 LRESULT DesktopWindow::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
433 {
434 switch(nmsg) {
435 case WM_LBUTTONDBLCLK:
436 case WM_RBUTTONDBLCLK:
437 case WM_MBUTTONDBLCLK:
438 explorer_show_frame(SW_SHOWNORMAL);
439 break;
440
441 case WM_DISPLAYCHANGE:
442 MoveWindow(_hwnd, 0, 0, LOWORD(lparam), HIWORD(lparam), TRUE);
443 MoveWindow(g_Globals._hwndShellView, 0, 0, LOWORD(lparam), HIWORD(lparam), TRUE);
444 MoveWindow(_desktopBar, 0, HIWORD(lparam) - DESKTOPBARBAR_HEIGHT, LOWORD(lparam), DESKTOPBARBAR_HEIGHT, TRUE);
445 break;
446
447 case WM_GETISHELLBROWSER:
448 return (LRESULT)static_cast<IShellBrowser*>(this);
449
450 case WM_DESTROY:
451
452 ///@todo use IShellBrowser::GetViewStateStream() and _pShellView->SaveViewState() to store view state
453
454 if (SetShellWindow)
455 SetShellWindow(0);
456 break;
457
458 case WM_CLOSE:
459 ShowExitWindowsDialog(_hwnd);
460 break;
461
462 case WM_SYSCOMMAND:
463 if (wparam == SC_TASKLIST) {
464 if (_desktopBar)
465 SendMessage(_desktopBar, nmsg, wparam, lparam);
466 }
467 goto def;
468
469 case WM_SYSCOLORCHANGE:
470 // redraw background window - it's done by system
471 //InvalidateRect(g_Globals._hwndShellView, NULL, TRUE);
472
473 // forward message to common controls
474 SendMessage(g_Globals._hwndShellView, WM_SYSCOLORCHANGE, 0, 0);
475 SendMessage(_desktopBar, WM_SYSCOLORCHANGE, 0, 0);
476 break;
477
478 case WM_SETTINGCHANGE:
479 SendMessage(g_Globals._hwndShellView, nmsg, wparam, lparam);
480 break;
481
482 case PM_TRANSLATE_MSG:
483 {
484 /* TranslateAccelerator is called for all explorer windows that are open
485 so we have to decide if this is the correct recipient */
486 LPMSG lpmsg = (LPMSG)lparam;
487 HWND hwnd = lpmsg->hwnd;
488
489 while(hwnd)
490 {
491 if(hwnd == _hwnd)
492 break;
493
494 hwnd = GetParent(hwnd);
495 }
496
497 if (hwnd)
498 return _pShellView->TranslateAccelerator(lpmsg) == S_OK;
499 return false;
500 }
501
502 default: def:
503 return super::WndProc(nmsg, wparam, lparam);
504 }
505
506 return 0;
507 }
508
509
510 HRESULT DesktopWindow::OnDefaultCommand(LPIDA pida)
511 {
512 #ifndef ROSSHELL // in shell-only-mode fall through and let shell32 handle the command
513 if (MainFrameBase::OpenShellFolders(pida, 0))
514 return S_OK;
515 #endif
516
517 return E_NOTIMPL;
518 }
519
520
521 DesktopShellView::DesktopShellView(HWND hwnd, IShellView* pShellView)
522 : super(hwnd),
523 _pShellView(pShellView)
524 {
525 _hwndListView = GetNextWindow(hwnd, GW_CHILD);
526
527 // work around for Windows NT, Win 98, ...
528 // Without this the desktop has mysteriously only a size of 800x600 pixels.
529 ClientRect rect(hwnd);
530 MoveWindow(_hwndListView, 0, 0, rect.right, rect.bottom, TRUE);
531
532 // subclass background window
533 new BackgroundWindow(_hwndListView);
534
535 _icon_algo = 0; // default icon arrangement
536
537 InitDragDrop();
538 }
539
540
541 DesktopShellView::~DesktopShellView()
542 {
543 if (FAILED(RevokeDragDrop(_hwnd)))
544 assert(0);
545 }
546
547
548 bool DesktopShellView::InitDragDrop()
549 {
550 CONTEXT("DesktopShellView::InitDragDrop()");
551
552 DesktopDropTarget * pDropTarget = new DesktopDropTarget(_hwnd);
553
554 if (!pDropTarget)
555 return false;
556
557 pDropTarget->AddRef();
558
559 if (FAILED(RegisterDragDrop(_hwnd, pDropTarget))) {
560 pDropTarget->Release();
561 return false;
562 }
563
564 FORMATETC ftetc;
565
566 ftetc.dwAspect = DVASPECT_CONTENT;
567 ftetc.lindex = -1;
568 ftetc.tymed = TYMED_HGLOBAL;
569 ftetc.cfFormat = CF_HDROP;
570
571 pDropTarget->AddSuportedFormat(ftetc);
572 pDropTarget->Release();
573
574 return true;
575 }
576
577 LRESULT DesktopShellView::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
578 {
579 switch(nmsg) {
580 case WM_CONTEXTMENU:
581 if (!DoContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)))
582 DoDesktopContextMenu(GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam));
583 break;
584
585 case PM_SET_ICON_ALGORITHM:
586 _icon_algo = wparam;
587 PositionIcons();
588 break;
589
590 case PM_GET_ICON_ALGORITHM:
591 return _icon_algo;
592
593 case PM_DISPLAY_VERSION:
594 return SendMessage(_hwndListView, nmsg, wparam, lparam);
595
596 default:
597 return super::WndProc(nmsg, wparam, lparam);
598 }
599
600 return 0;
601 }
602
603 int DesktopShellView::Command(int id, int code)
604 {
605 return super::Command(id, code);
606 }
607
608 int DesktopShellView::Notify(int id, NMHDR* pnmh)
609 {
610 return super::Notify(id, pnmh);
611 }
612
613 bool DesktopShellView::DoContextMenu(int x, int y)
614 {
615 IDataObject* selection;
616
617 HRESULT hr = _pShellView->GetItemObject(SVGIO_SELECTION, IID_IDataObject, (void**)&selection);
618 if (FAILED(hr))
619 return false;
620
621 PIDList pidList;
622
623 hr = pidList.GetData(selection);
624 if (FAILED(hr)) {
625 selection->Release();
626 //CHECKERROR(hr);
627 return false;
628 }
629
630 LPIDA pida = pidList;
631 if (!pida->cidl) {
632 selection->Release();
633 return false;
634 }
635
636 LPCITEMIDLIST parent_pidl = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[0]);
637
638 LPCITEMIDLIST* apidl = (LPCITEMIDLIST*) alloca(pida->cidl*sizeof(LPCITEMIDLIST));
639
640 for(int i=pida->cidl; i>0; --i)
641 apidl[i-1] = (LPCITEMIDLIST) ((LPBYTE)pida+pida->aoffset[i]);
642
643 hr = ShellFolderContextMenu(ShellFolder(parent_pidl), _hwnd, pida->cidl, apidl, x, y, _cm_ifs);
644
645 selection->Release();
646
647 if (SUCCEEDED(hr))
648 refresh();
649 else
650 CHECKERROR(hr);
651
652 return true;
653 }
654
655 HRESULT DesktopShellView::DoDesktopContextMenu(int x, int y)
656 {
657 IContextMenu* pcm;
658
659 HRESULT hr = DesktopFolder()->GetUIObjectOf(_hwnd, 0, NULL, IID_IContextMenu, NULL, (LPVOID*)&pcm);
660
661 if (SUCCEEDED(hr)) {
662 pcm = _cm_ifs.query_interfaces(pcm);
663
664 HMENU hmenu = CreatePopupMenu();
665
666 if (hmenu) {
667 hr = pcm->QueryContextMenu(hmenu, 0, FCIDM_SHVIEWFIRST, FCIDM_SHVIEWLAST-1, CMF_NORMAL|CMF_EXPLORE);
668
669 if (SUCCEEDED(hr)) {
670 AppendMenu(hmenu, MF_SEPARATOR, 0, NULL);
671 AppendMenu(hmenu, 0, FCIDM_SHVIEWLAST-1, ResString(IDS_ABOUT_EXPLORER));
672
673 UINT idCmd = TrackPopupMenu(hmenu, TPM_LEFTALIGN|TPM_RETURNCMD|TPM_RIGHTBUTTON, x, y, 0, _hwnd, NULL);
674
675 _cm_ifs.reset();
676
677 if (idCmd == FCIDM_SHVIEWLAST-1) {
678 explorer_about(_hwnd);
679 } else if (idCmd) {
680 CMINVOKECOMMANDINFO cmi;
681
682 cmi.cbSize = sizeof(CMINVOKECOMMANDINFO);
683 cmi.fMask = 0;
684 cmi.hwnd = _hwnd;
685 cmi.lpVerb = (LPCSTR)(INT_PTR)(idCmd - FCIDM_SHVIEWFIRST);
686 cmi.lpParameters = NULL;
687 cmi.lpDirectory = NULL;
688 cmi.nShow = SW_SHOWNORMAL;
689 cmi.dwHotKey = 0;
690 cmi.hIcon = 0;
691
692 hr = pcm->InvokeCommand(&cmi);
693 }
694 } else
695 _cm_ifs.reset();
696 DestroyMenu(hmenu);
697 }
698
699 pcm->Release();
700 }
701
702 return hr;
703 }
704
705
706 #define ARRANGE_BORDER_DOWN 8
707 #define ARRANGE_BORDER_HV 9
708 #define ARRANGE_ROUNDABOUT 10
709
710 static const POINTS s_align_start[] = {
711 {0, 0}, // left/top
712 {0, 0},
713 {1, 0}, // right/top
714 {1, 0},
715 {0, 1}, // left/bottom
716 {0, 1},
717 {1, 1}, // right/bottom
718 {1, 1},
719
720 {0, 0}, // left/top
721 {0, 0},
722 {0, 0}
723 };
724
725 static const POINTS s_align_dir1[] = {
726 { 0, +1}, // down
727 {+1, 0}, // right
728 {-1, 0}, // left
729 { 0, +1}, // down
730 { 0, -1}, // up
731 {+1, 0}, // right
732 {-1, 0}, // left
733 { 0, -1}, // up
734
735 { 0, +1}, // down
736 {+1, 0}, // right
737 {+1, 0} // right
738 };
739
740 static const POINTS s_align_dir2[] = {
741 {+1, 0}, // right
742 { 0, +1}, // down
743 { 0, +1}, // down
744 {-1, 0}, // left
745 {+1, 0}, // right
746 { 0, -1}, // up
747 { 0, -1}, // up
748 {-1, 0}, // left
749
750 {+1, 0}, // right
751 { 0, +1}, // down
752 { 0, +1} // down
753 };
754
755 typedef pair<int,int> IconPos;
756 typedef map<IconPos, int> IconMap;
757
758 void DesktopShellView::PositionIcons(int dir)
759 {
760 DWORD spacing = ListView_GetItemSpacing(_hwndListView, FALSE);
761
762 RECT work_area;
763 SystemParametersInfo(SPI_GETWORKAREA, 0, &work_area, 0);
764
765 /* disable default allignment */
766 SetWindowStyle(_hwndListView, GetWindowStyle(_hwndListView)&~LVS_ALIGNMASK);//|LVS_ALIGNTOP|LVS_AUTOARRANGE);
767
768 const POINTS& dir1 = s_align_dir1[_icon_algo];
769 const POINTS& dir2 = s_align_dir2[_icon_algo];
770 const POINTS& start_pos = s_align_start[_icon_algo];
771
772 int dir_x1 = dir1.x;
773 int dir_y1 = dir1.y;
774 int dir_x2 = dir2.x;
775 int dir_y2 = dir2.y;
776
777 int cx = LOWORD(spacing);
778 int cy = HIWORD(spacing);
779
780 int dx1 = dir_x1 * cx;
781 int dy1 = dir_y1 * cy;
782 int dx2 = dir_x2 * cx;
783 int dy2 = dir_y2 * cy;
784
785 int xoffset = (cx-32)/2;
786 int yoffset = 4/*(cy-32)/2*/;
787
788 int start_x = start_pos.x * (work_area.right - cx) + xoffset;
789 int start_y = start_pos.y * (work_area.bottom - cy) + yoffset;
790
791 int x = start_x;
792 int y = start_y;
793
794 int all = ListView_GetItemCount(_hwndListView);
795 int i1, i2;
796
797 if (dir > 0) {
798 i1 = 0;
799 i2 = all;
800 } else {
801 i1 = all-1;
802 i2 = -1;
803 }
804
805 IconMap pos_idx;
806 int cnt = 0;
807 int xhv = start_x;
808 int yhv = start_y;
809
810 for(int idx=i1; idx!=i2; idx+=dir) {
811 pos_idx[IconPos(y, x)] = idx;
812
813 if (_icon_algo == ARRANGE_BORDER_DOWN) {
814 if (++cnt & 1)
815 x = work_area.right - x - cx + 2*xoffset;
816 else {
817 y += dy1;
818
819 if (y + cy - 2 * yoffset > work_area.bottom) {
820 y = start_y;
821 start_x += dx2;
822 x = start_x;
823 }
824 }
825
826 continue;
827 }
828 else if (_icon_algo == ARRANGE_BORDER_HV) {
829 if (++cnt & 1)
830 x = work_area.right - x - cx + 2*xoffset;
831 else if (cnt & 2) {
832 yhv += cy;
833 y = yhv;
834 x = start_x;
835
836 if (y + cy - 2 * yoffset > work_area.bottom) {
837 start_x += cx;
838 xhv = start_x;
839 x = xhv;
840 start_y += cy;
841 yhv = start_y;
842 y = yhv;
843 }
844 } else {
845 xhv += cx;
846 x = xhv;
847 y = start_y;
848
849 if (x + cx - 2 * xoffset > work_area.right) {
850 start_x += cx;
851 xhv = start_x;
852 x = xhv;
853 start_y += cy;
854 yhv = start_y;
855 y = yhv;
856 }
857 }
858
859 continue;
860 }
861 else if (_icon_algo == ARRANGE_ROUNDABOUT) {
862
863 ///@todo
864
865 }
866
867 x += dx1;
868 y += dy1;
869
870 if (x<0 || x+cx-2*xoffset>work_area.right) {
871 x = start_x;
872 y += dy2;
873 } else if (y<0 || y+cy-2*yoffset>work_area.bottom) {
874 y = start_y;
875 x += dx2;
876 }
877 }
878
879 // use a little trick to get the icons where we want them to be...
880
881 //for(IconMap::const_iterator it=pos_idx.end(); --it!=pos_idx.begin(); ) {
882 // const IconPos& pos = it->first;
883
884 // ListView_SetItemPosition32(_hwndListView, it->second, pos.second, pos.first);
885 //}
886
887 for(IconMap::const_iterator it=pos_idx.begin(); it!=pos_idx.end(); ++it) {
888 const IconPos& pos = it->first;
889
890 ListView_SetItemPosition32(_hwndListView, it->second, pos.second, pos.first);
891 }
892 }
893
894
895 void DesktopShellView::refresh()
896 {
897 ///@todo
898 }