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 
Desktop(HDESK hdesktop)41 Desktop::Desktop(HDESK hdesktop/*, HWINSTA hwinsta*/)
42  :	_hdesktop(hdesktop)
43 //	_hwinsta(hwinsta)
44 {
45 }
46 
~Desktop()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 
Desktops()64 Desktops::Desktops()
65  :	_current_desktop(0)
66 {
67 }
68 
~Desktops()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 
init()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 
SwitchToDesktop(int idx)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 
Run()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 
SwitchDesktopEnumFct(HWND hwnd,LPARAM lparam)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 
SwitchToDesktop(int idx)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 
MinimizeDesktopEnumFct(HWND hwnd,LPARAM lparam)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
ToggleMinimize()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 
IsAnyDesktopRunning()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 
BackgroundWindow(HWND hwnd)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 
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)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 
DrawDesktopBkgnd(HDC hdc)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 
DesktopWindow(HWND hwnd)313 DesktopWindow::DesktopWindow(HWND hwnd)
314  :	super(hwnd)
315 {
316 	_pShellView = NULL;
317 }
318 
~DesktopWindow()319 DesktopWindow::~DesktopWindow()
320 {
321 	if (_pShellView)
322 		_pShellView->Release();
323 }
324 
325 
Create()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 
Init(LPCREATESTRUCT pcs)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 
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)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 
OnDefaultCommand(LPIDA pida)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 
DesktopShellView(HWND hwnd,IShellView * pShellView)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 
~DesktopShellView()540 DesktopShellView::~DesktopShellView()
541 {
542 	if (FAILED(RevokeDragDrop(_hwnd)))
543 		assert(0);
544 }
545 
546 
InitDragDrop()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 
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)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 
Command(int id,int code)602 int DesktopShellView::Command(int id, int code)
603 {
604 	return super::Command(id, code);
605 }
606 
Notify(int id,NMHDR * pnmh)607 int DesktopShellView::Notify(int id, NMHDR* pnmh)
608 {
609 	return super::Notify(id, pnmh);
610 }
611 
DoContextMenu(int x,int y)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 
DoDesktopContextMenu(int x,int y)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 
PositionIcons(int dir)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 
refresh()894 void DesktopShellView::refresh()
895 {
896 	///@todo
897 }
898