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