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