fix admin and printer folders
[reactos.git] / reactos / subsys / system / explorer / taskbar / startmenu.cpp
1 /*
2 * Copyright 2003 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 // startmenu.cpp
24 //
25 // Explorer start menu
26 //
27 // Martin Fuchs, 19.08.2003
28 //
29 // Credits: Thanks to Everaldo (http://www.everaldo.com) for his nice looking icons.
30 //
31
32
33 #include "../utility/utility.h"
34
35 #include "../explorer.h"
36 #include "../globals.h"
37 #include "../externals.h"
38 #include "../explorer_intres.h"
39
40 #include "desktopbar.h"
41 #include "startmenu.h"
42
43 #include "../dialogs/settings.h"
44
45
46 StartMenu::StartMenu(HWND hwnd)
47 : super(hwnd)
48 {
49 _next_id = IDC_FIRST_MENU;
50 _submenu_id = 0;
51 _border_left = 0;
52 _border_top = 0;
53 _last_pos = WindowRect(hwnd).pos();
54 #ifdef _LIGHT_STARTMENU
55 _selected_id = -1;
56 _last_mouse_pos = 0;
57 #endif
58 }
59
60 StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info)
61 : super(hwnd),
62 _create_info(create_info)
63 {
64 for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
65 if (*it)
66 _dirs.push_back(ShellDirectory(GetDesktopFolder(), *it, _hwnd));
67
68 _next_id = IDC_FIRST_MENU;
69 _submenu_id = 0;
70 _border_left = 0;
71 _border_top = create_info._border_top;
72 _last_pos = WindowRect(hwnd).pos();
73 #ifdef _LIGHT_STARTMENU
74 _selected_id = -1;
75 _last_mouse_pos = 0;
76 #endif
77 }
78
79 StartMenu::~StartMenu()
80 {
81 SendParent(PM_STARTMENU_CLOSED);
82 }
83
84
85 // We need this wrapper function for s_wcStartMenu, it calls the WIN32 API,
86 // though static C++ initializers are not allowed for Winelib applications.
87 BtnWindowClass& StartMenu::GetWndClasss()
88 {
89 static BtnWindowClass s_wcStartMenu(CLASSNAME_STARTMENU);
90
91 return s_wcStartMenu;
92 }
93
94
95 Window::CREATORFUNC_INFO StartMenu::s_def_creator = STARTMENU_CREATOR(StartMenu);
96
97 HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndParent, LPCTSTR title, CREATORFUNC_INFO creator)
98 {
99 UINT style, ex_style;
100 int top_height;
101
102 if (hwndParent) {
103 style = WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE;
104 ex_style = 0;
105 top_height = STARTMENU_TOP_BTN_SPACE;
106 } else {
107 style = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPCHILDREN|WS_VISIBLE;
108 ex_style = WS_EX_TOOLWINDOW;
109 top_height = 0;
110 }
111
112 RECT rect = {x, y-STARTMENU_LINE_HEIGHT-top_height, x+STARTMENU_WIDTH_MIN, y};
113
114 #ifndef _LIGHT_STARTMENU
115 rect.top += STARTMENU_LINE_HEIGHT;
116 #endif
117
118 AdjustWindowRectEx(&rect, style, FALSE, ex_style);
119
120 StartMenuCreateInfo create_info;
121
122 create_info._folders = folders;
123 create_info._border_top = top_height;
124 create_info._creator = creator;
125
126 if (title)
127 create_info._title = title;
128
129 HWND hwnd = Window::Create(creator, &create_info, ex_style, GetWndClasss(), title,
130 style, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndParent);
131
132 // make sure the window is not off the screen
133 MoveVisible(hwnd);
134
135 return hwnd;
136 }
137
138
139 LRESULT StartMenu::Init(LPCREATESTRUCT pcs)
140 {
141 try {
142 AddEntries();
143
144 if (super::Init(pcs))
145 return 1;
146
147 // create buttons for registered entries in _entries
148 for(ShellEntryMap::const_iterator it=_entries.begin(); it!=_entries.end(); ++it) {
149 const StartMenuEntry& sme = it->second;
150 bool hasSubmenu = false;
151
152 for(ShellEntrySet::const_iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it)
153 if ((*it)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
154 hasSubmenu = true;
155
156 #ifdef _LIGHT_STARTMENU
157 _buttons.push_back(SMBtnInfo(sme, it->first, hasSubmenu));
158 #else
159 AddButton(sme._title, sme._hIcon, hasSubmenu, it->first);
160 #endif
161 }
162
163 #ifdef _LIGHT_STARTMENU
164 if (_buttons.empty())
165 #else
166 if (!GetWindow(_hwnd, GW_CHILD))
167 #endif
168 AddButton(ResString(IDS_EMPTY), ICID_NONE, false, 0, false);
169
170 #ifdef _LIGHT_STARTMENU
171 ResizeToButtons();
172 #endif
173
174 #ifdef _LAZY_ICONEXTRACT
175 PostMessage(_hwnd, PM_UPDATE_ICONS, 0, 0);
176 #endif
177 } catch(COMException& e) {
178 HandleException(e, pcs->hwndParent); // destroys the start menu window while switching focus
179 }
180
181 return 0;
182 }
183
184 void StartMenu::AddEntries()
185 {
186 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
187 StartMenuDirectory& smd = *it;
188 ShellDirectory& dir = smd._dir;
189
190 if (!dir._scanned) {
191 WaitCursor wait;
192
193 #ifdef _LAZY_ICONEXTRACT
194 dir.smart_scan(SCAN_FILESYSTEM); // lazy icon extraction, try to read directly from filesystem
195 #else
196 dir.smart_scan(SCAN_EXTRACT_ICONS|SCAN_FILESYSTEM);
197 #endif
198 }
199
200 AddShellEntries(dir, -1, smd._subfolders);
201 }
202 }
203
204
205 void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, bool subfolders)
206 {
207 int cnt = 0;
208
209 for(Entry*entry=dir._down; entry; entry=entry->_next) {
210 // hide files like "desktop.ini"
211 if (entry->_shell_attribs & SFGAO_HIDDEN)
212 //not appropriate for drive roots: if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
213 continue;
214
215 // hide subfolders if requested
216 if (!subfolders)
217 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
218 continue;
219
220 // only 'max' entries shall be added.
221 if (++cnt == max)
222 break;
223
224 if (entry->_etype == ET_SHELL)
225 AddEntry(dir._folder, static_cast<ShellEntry*>(entry));
226 else
227 AddEntry(dir._folder, entry);
228 }
229 }
230
231
232 LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
233 {
234 switch(nmsg) {
235 case WM_PAINT: {
236 PaintCanvas canvas(_hwnd);
237 Paint(canvas);
238 break;}
239
240 case WM_SIZE:
241 ResizeButtons(LOWORD(lparam)-_border_left);
242 break;
243
244 case WM_MOVE: {
245 POINTS& pos = MAKEPOINTS(lparam);
246
247 // move open submenus of floating menus
248 if (_submenu) {
249 int dx = pos.x - _last_pos.x;
250 int dy = pos.y - _last_pos.y;
251
252 if (dx || dy) {
253 WindowRect rt(_submenu);
254 SetWindowPos(_submenu, 0, rt.left+dx, rt.top+dy, 0, 0, SWP_NOSIZE|SWP_NOACTIVATE);
255 //MoveVisible(_submenu);
256 }
257 }
258
259 _last_pos.x = pos.x;
260 _last_pos.y = pos.y;
261 goto def;}
262
263 case WM_NCHITTEST: {
264 LRESULT res = super::WndProc(nmsg, wparam, lparam);
265
266 if (res>=HTSIZEFIRST && res<=HTSIZELAST)
267 return HTCLIENT; // disable window resizing
268
269 return res;}
270
271 case WM_LBUTTONDOWN: {
272 RECT rect;
273
274 // check mouse cursor for coordinates of floating button
275 GetFloatingButtonRect(&rect);
276
277 if (PtInRect(&rect, Point(lparam))) {
278 // create a floating copy of the current start menu
279 WindowRect pos(_hwnd);
280
281 ///@todo do something similar to StartMenuRoot::TrackStartmenu() in order to automatically close submenus when clicking on the desktop background
282 StartMenu::Create(pos.left+3, pos.bottom-3, _create_info._folders, 0, _create_info._title, _create_info._creator);
283 CloseStartMenu();
284 }
285
286 #ifdef _LIGHT_STARTMENU
287 int id = ButtonHitTest(Point(lparam));
288
289 if (id)
290 Command(id, BN_CLICKED);
291 #endif
292 break;}
293
294 case WM_SYSCOMMAND:
295 if ((wparam&0xFFF0) == SC_SIZE)
296 return 0; // disable window resizing
297 goto def;
298
299 case WM_ACTIVATEAPP:
300 // close start menu when activating another application
301 if (!wparam)
302 CloseStartMenu();
303 break; // don't call super::WndProc in case "this" has been deleted
304
305 case WM_CANCELMODE:
306 CloseStartMenu();
307 break;
308
309 #ifdef _LIGHT_STARTMENU
310 case WM_MOUSEMOVE: {
311 // automatically set the focus to startmenu entries when moving the mouse over them
312 if (lparam != _last_mouse_pos) { // don't process WM_MOUSEMOVE when opening submenus using keyboard navigation
313 int new_id = ButtonHitTest(Point(lparam));
314
315 if (new_id != _selected_id)
316 SelectButton(new_id);
317
318 _last_mouse_pos = lparam;
319 }
320 break;}
321
322 case WM_KEYDOWN:
323 ProcessKey(wparam);
324 break;
325 #else
326 case PM_STARTENTRY_FOCUSED: { ///@todo use TrackMouseEvent() and WM_MOUSEHOVER to wait a bit before opening submenus
327 BOOL hasSubmenu = wparam;
328 HWND hctrl = (HWND)lparam;
329
330 // automatically open submenus
331 if (hasSubmenu) {
332 UpdateWindow(_hwnd); // draw focused button before waiting on submenu creation
333 //SendMessage(_hwnd, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hctrl),BN_CLICKED), (LPARAM)hctrl);
334 Command(GetDlgCtrlID(hctrl), BN_CLICKED);
335 } else {
336 // close any open submenu
337 CloseOtherSubmenus();
338 }
339 break;}
340 #endif
341
342 #ifdef _LAZY_ICONEXTRACT
343 case PM_UPDATE_ICONS:
344 UpdateIcons(/*wparam*/);
345 break;
346 #endif
347
348 case PM_STARTENTRY_LAUNCHED:
349 if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus
350 return 0;
351
352 // route message to the parent menu and close menus after launching an entry
353 if (!SendParent(nmsg, wparam, lparam))
354 CloseStartMenu();
355 return 1; // signal that we have received and processed the message
356
357 case PM_STARTMENU_CLOSED:
358 _submenu = 0;
359 break;
360
361 case PM_SELECT_ENTRY:
362 SelectButtonIndex(0, wparam?true:false);
363 break;
364
365 default: def:
366 return super::WndProc(nmsg, wparam, lparam);
367 }
368
369 return 0;
370 }
371
372
373 #ifdef _LIGHT_STARTMENU
374
375 int StartMenu::ButtonHitTest(POINT pt)
376 {
377 ClientRect clnt(_hwnd);
378 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
379
380 if (pt.x<rect.left || pt.x>rect.right)
381 return 0;
382
383 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
384 const SMBtnInfo& info = *it;
385
386 if (rect.top > pt.y)
387 break;
388
389 rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT);
390
391 if (pt.y < rect.bottom) // PtInRect(&rect, pt)
392 return info._id;
393
394 rect.top = rect.bottom;
395 }
396
397 return 0;
398 }
399
400 void StartMenu::InvalidateSelection()
401 {
402 if (!_selected_id)
403 return;
404
405 ClientRect clnt(_hwnd);
406 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
407
408 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
409 const SMBtnInfo& info = *it;
410
411 rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT);
412
413 if (info._id == _selected_id) {
414 InvalidateRect(_hwnd, &rect, TRUE);
415 break;
416 }
417
418 rect.top = rect.bottom;
419 }
420 }
421
422 const SMBtnInfo* StartMenu::GetButtonInfo(int id) const
423 {
424 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it)
425 if (it->_id == id)
426 return &*it;
427
428 return NULL;
429 }
430
431 bool StartMenu::SelectButton(int id, bool open_sub)
432 {
433 if (id == -1)
434 return false;
435
436 if (id == _selected_id)
437 return true;
438
439 InvalidateSelection();
440
441 const SMBtnInfo* btn = GetButtonInfo(id);
442
443 if (btn && btn->_enabled) {
444 _selected_id = id;
445
446 InvalidateSelection();
447
448 // automatically open submenus
449 if (btn->_hasSubmenu) {
450 if (open_sub)
451 OpenSubmenu();
452 } else
453 CloseOtherSubmenus(); // close any open submenu
454
455 return true;
456 } else {
457 _selected_id = -1;
458 return false;
459 }
460 }
461
462 bool StartMenu::OpenSubmenu(bool select_first)
463 {
464 if (_selected_id == -1)
465 return false;
466
467 InvalidateSelection();
468
469 const SMBtnInfo* btn = GetButtonInfo(_selected_id);
470
471 // automatically open submenus
472 if (btn->_hasSubmenu) {
473 //@@ allows destroying of startmenu when processing PM_UPDATE_ICONS -> GPF
474 UpdateWindow(_hwnd); // draw focused button before waiting on submenu creation
475 Command(_selected_id, BN_CLICKED);
476
477 if (select_first && _submenu)
478 SendMessage(_submenu, PM_SELECT_ENTRY, (WPARAM)false, 0);
479
480 return true;
481 } else
482 return false;
483 }
484
485
486 int StartMenu::GetSelectionIndex()
487 {
488 if (_selected_id == -1)
489 return -1;
490
491 for(int i=0; i<(int)_buttons.size(); ++i)
492 if (_buttons[i]._id == _selected_id)
493 return i;
494
495 return -1;
496 }
497
498 bool StartMenu::SelectButtonIndex(int idx, bool open_sub)
499 {
500 if (idx>=0 && idx<(int)_buttons.size())
501 return SelectButton(_buttons[idx]._id, open_sub);
502 else
503 return false;
504 }
505
506 void StartMenu::ProcessKey(int vk)
507 {
508 switch(vk) {
509 case VK_RETURN:
510 if (_selected_id)
511 Command(_selected_id, BN_CLICKED);
512 break;
513
514 case VK_UP:
515 Navigate(-1);
516 break;
517
518 case VK_DOWN:
519 Navigate(+1);
520 break;
521
522 case VK_HOME:
523 SelectButtonIndex(0, false);
524 break;
525
526 case VK_END:
527 SelectButtonIndex(_buttons.size()-1, false);
528 break;
529
530 case VK_LEFT:
531 if (_submenu)
532 CloseOtherSubmenus();
533 else if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) // don't automatically close floating menus
534 DestroyWindow(_hwnd);
535 break;
536
537 case VK_RIGHT:
538 OpenSubmenu(true);
539 break;
540
541 case VK_ESCAPE:
542 CloseStartMenu();
543 break;
544
545 default:
546 if (vk>='0' && vk<='Z')
547 JumpToNextShortcut(vk);
548 }
549 }
550
551 bool StartMenu::Navigate(int step)
552 {
553 int idx = GetSelectionIndex();
554
555 if (idx == -1)
556 if (step > 0)
557 idx = 0 - step;
558 else
559 idx = _buttons.size() - step;
560
561 for(;;) {
562 idx += step;
563
564 if (idx<0 || idx>(int)_buttons.size())
565 break;
566
567 if (SelectButtonIndex(idx, false))
568 return true;
569 }
570
571 return false;
572 }
573
574 bool StartMenu::JumpToNextShortcut(char c)
575 {
576 int cur_idx = GetSelectionIndex();
577
578 if (cur_idx == -1)
579 cur_idx = 0;
580
581 int first_found = 0;
582 int found_more = 0;
583
584 SMBtnVector::const_iterator cur_it = _buttons.begin();
585 cur_it += cur_idx + 1;
586
587 // first search down from current item...
588 SMBtnVector::const_iterator it = cur_it;
589 for(; it!=_buttons.end(); ++it) {
590 const SMBtnInfo& btn = *it;
591
592 if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
593 if (!first_found)
594 first_found = btn._id;
595 else
596 ++found_more;
597 }
598 }
599
600 // ...now search from top to the current item
601 it = _buttons.begin();
602 for(; it!=_buttons.end() && it!=cur_it; ++it) {
603 const SMBtnInfo& btn = *it;
604
605 if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
606 if (!first_found)
607 first_found = btn._id;
608 else
609 ++found_more;
610 }
611 }
612
613 if (first_found) {
614 SelectButton(first_found);
615
616 if (!found_more)
617 Command(first_found, BN_CLICKED);
618
619 return true;
620 } else
621 return false;
622 }
623
624 #endif // _LIGHT_STARTMENU
625
626
627 bool StartMenu::GetButtonRect(int id, PRECT prect) const
628 {
629 #ifdef _LIGHT_STARTMENU
630 ClientRect clnt(_hwnd);
631 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
632
633 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
634 const SMBtnInfo& info = *it;
635
636 rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT);
637
638 if (info._id == id) {
639 *prect = rect;
640 return true;
641 }
642
643 rect.top = rect.bottom;
644 }
645
646 return false;
647 #else
648 HWND btn = GetDlgItem(_hwnd, id);
649
650 if (btn) {
651 GetWindowRect(btn, prect);
652 ScreenToClient(_hwnd, prect);
653
654 return true;
655 } else
656 return false;
657 #endif
658 }
659
660
661 void StartMenu::DrawFloatingButton(HDC hdc)
662 {
663 static ResIconEx floatingIcon(IDI_FLOATING, 8, 4);
664
665 ClientRect clnt(_hwnd);
666
667 DrawIconEx(hdc, clnt.right-12, 0, floatingIcon, 8, 4, 0, 0, DI_NORMAL);
668 }
669
670 void StartMenu::GetFloatingButtonRect(LPRECT prect)
671 {
672 GetClientRect(_hwnd, prect);
673
674 prect->right -= 4;
675 prect->left = prect->right - 8;
676 prect->bottom = 4;
677 }
678
679
680 void StartMenu::Paint(PaintCanvas& canvas)
681 {
682 if (_border_top)
683 DrawFloatingButton(canvas);
684
685 #ifdef _LIGHT_STARTMENU
686 ClientRect clnt(_hwnd);
687 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
688
689 int sep_width = rect.right-rect.left - 4;
690
691 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
692 BkMode bk_mode(canvas, TRANSPARENT);
693
694 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
695 const SMBtnInfo& btn = *it;
696
697 if (rect.top > canvas.rcPaint.bottom)
698 break;
699
700 if (btn._id == -1) { // a separator?
701 rect.bottom = rect.top + STARTMENU_SEP_HEIGHT;
702
703 BrushSelection brush_sel(canvas, GetSysColorBrush(COLOR_BTNSHADOW));
704 PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2-1, sep_width, 1, PATCOPY);
705
706 SelectBrush(canvas, GetSysColorBrush(COLOR_BTNHIGHLIGHT));
707 PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2, sep_width, 1, PATCOPY);
708 } else {
709 rect.bottom = rect.top + STARTMENU_LINE_HEIGHT;
710
711 if (rect.top >= canvas.rcPaint.top)
712 DrawStartMenuButton(canvas, rect, btn._title, btn, btn._id==_selected_id, false);
713 }
714
715 rect.top = rect.bottom;
716 }
717 #endif
718 }
719
720 #ifdef _LAZY_ICONEXTRACT
721 void StartMenu::UpdateIcons(/*int idx*/)
722 {
723 UpdateWindow(_hwnd);
724
725 #ifdef _SINGLE_ICONEXTRACT
726
727 //if (idx >= 0)
728 int idx = 0;
729
730 for(; idx<(int)_buttons.size(); ++idx) {
731 SMBtnInfo& btn = _buttons[idx];
732
733 if (btn._icon_id==ICID_UNKNOWN && btn._id>0) {
734 StartMenuEntry& sme = _entries[btn._id];
735
736 btn._icon_id = ICID_NONE;
737
738 for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
739 Entry* entry = *it;
740
741 if (entry->_icon_id == ICID_UNKNOWN)
742 entry->extract_icon();
743
744 if (entry->_icon_id > ICID_NONE) {
745 btn._icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
746
747 RECT rect;
748
749 GetButtonRect(btn._id, &rect);
750 WindowCanvas canvas(_hwnd);
751 DrawStartMenuButton(canvas, rect, NULL, btn, btn._id==_selected_id, false);
752
753 //InvalidateRect(_hwnd, &rect, FALSE);
754 //UpdateWindow(_hwnd);
755 //break;
756
757 break;
758 }
759 }
760 }
761 }
762
763 // if (++idx < (int)_buttons.size())
764 // PostMessage(_hwnd, PM_UPDATE_ICONS, idx, 0);
765
766 #else
767
768 int icons_extracted = 0;
769 int icons_updated = 0;
770
771 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
772 ShellDirectory& dir = it->_dir;
773
774 icons_extracted += dir.extract_icons();
775 }
776
777 if (icons_extracted) {
778 for(ShellEntryMap::iterator it1=_entries.begin(); it1!=_entries.end(); ++it1) {
779 StartMenuEntry& sme = it1->second;
780
781 if (!sme._hIcon) {
782 sme._hIcon = (HICON)-1;
783
784 for(ShellEntrySet::const_iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
785 const Entry* sm_entry = *it2;
786
787 if (sm_entry->_hIcon) {
788 sme._hIcon = sm_entry->_hIcon;
789 break;
790 }
791 }
792 }
793 }
794
795 for(SMBtnVector::iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
796 SMBtnInfo& info = *it;
797
798 if (info._id>0 && !info._hIcon) {
799 info._hIcon = _entries[info._id]._hIcon;
800 ++icons_updated;
801 }
802 }
803 }
804
805 if (icons_updated) {
806 InvalidateRect(_hwnd, NULL, FALSE);
807 UpdateWindow(_hwnd);
808 }
809 #endif
810 }
811 #endif
812
813
814 // resize child button controls to accomodate for new window size
815 void StartMenu::ResizeButtons(int cx)
816 {
817 HDWP hdwp = BeginDeferWindowPos(10);
818
819 for(HWND ctrl=GetWindow(_hwnd,GW_CHILD); ctrl; ctrl=GetNextWindow(ctrl,GW_HWNDNEXT)) {
820 ClientRect rt(ctrl);
821
822 if (rt.right != cx) {
823 int height = rt.bottom - rt.top;
824
825 // special handling for separator controls
826 if (!height && (GetWindowStyle(ctrl)&SS_TYPEMASK)==SS_ETCHEDHORZ)
827 height = 2;
828
829 hdwp = DeferWindowPos(hdwp, ctrl, 0, 0, 0, cx, height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
830 }
831 }
832
833 EndDeferWindowPos(hdwp);
834 }
835
836
837 int StartMenu::Command(int id, int code)
838 {
839 #ifndef _LIGHT_STARTMENU
840 switch(id) {
841 case IDCANCEL:
842 CloseStartMenu(id);
843 break;
844
845 default: {
846 #endif
847 ShellEntryMap::iterator found = _entries.find(id);
848
849 if (found != _entries.end()) {
850 ActivateEntry(id, found->second._entries);
851 return 0;
852 }
853
854 return super::Command(id, code);
855 #ifndef _LIGHT_STARTMENU
856 }
857 }
858
859 return 0;
860 #endif
861 }
862
863
864 StartMenuEntry& StartMenu::AddEntry(const String& title, ICON_ID icon_id, Entry* entry)
865 {
866 // search for an already existing subdirectory entry with the same name
867 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
868 for(ShellEntryMap::iterator it=_entries.begin(); it!=_entries.end(); ++it) {
869 StartMenuEntry& sme = it->second;
870
871 if (sme._title == title) ///@todo speed up by using a map indexed by name
872 for(ShellEntrySet::iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
873 if ((*it2)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
874 // merge the new shell entry with the existing of the same name
875 sme._entries.insert(entry);
876 return sme;
877 }
878 }
879 }
880
881 StartMenuEntry& sme = AddEntry(title, icon_id);
882
883 sme._entries.insert(entry);
884
885 return sme;
886 }
887
888 StartMenuEntry& StartMenu::AddEntry(const String& title, ICON_ID icon_id, int id)
889 {
890 if (id == -1)
891 id = ++_next_id;
892
893 StartMenuEntry& sme = _entries[id];
894
895 sme._title = title;
896 sme._icon_id = icon_id;
897
898 return sme;
899 }
900
901 StartMenuEntry& StartMenu::AddEntry(const ShellFolder folder, ShellEntry* entry)
902 {
903 ICON_ID icon_id;
904
905 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
906 icon_id = ICID_FOLDER;
907 else
908 icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
909
910 return AddEntry(folder.get_name(entry->_pidl), icon_id, entry);
911 }
912
913 StartMenuEntry& StartMenu::AddEntry(const ShellFolder folder, Entry* entry)
914 {
915 ICON_ID icon_id;
916
917 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
918 icon_id = ICID_FOLDER;
919 else
920 icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
921
922 return AddEntry(entry->_display_name, icon_id, entry);
923 }
924
925
926 void StartMenu::AddButton(LPCTSTR title, ICON_ID icon_id, bool hasSubmenu, int id, bool enabled)
927 {
928 #ifdef _LIGHT_STARTMENU
929 _buttons.push_back(SMBtnInfo(title, icon_id, id, hasSubmenu, enabled));
930 #else
931 DWORD style = enabled? WS_VISIBLE|WS_CHILD|BS_OWNERDRAW: WS_VISIBLE|WS_CHILD|BS_OWNERDRAW|WS_DISABLED;
932
933 WindowRect rect(_hwnd);
934 ClientRect clnt(_hwnd);
935
936 // increase window height to make room for the new button
937 rect.top -= STARTMENU_LINE_HEIGHT;
938
939 // move down if we are too high now
940 if (rect.top < 0) {
941 rect.top += STARTMENU_LINE_HEIGHT;
942 rect.bottom += STARTMENU_LINE_HEIGHT;
943 }
944
945 WindowCanvas canvas(_hwnd);
946 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
947
948 // widen window, if it is too small
949 int text_width = GetStartMenuBtnTextWidth(canvas, title, _hwnd) + 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
950
951 int cx = clnt.right - _border_left;
952 if (text_width > cx)
953 rect.right += text_width-cx;
954
955 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
956
957 StartMenuCtrl(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left,
958 title, id, g_Globals._icon_cache.get_icon(icon_id)._hIcon, hasSubmenu, style);
959 #endif
960 }
961
962 void StartMenu::AddSeparator()
963 {
964 #ifdef _LIGHT_STARTMENU
965 _buttons.push_back(SMBtnInfo(NULL, ICID_NONE, -1, false));
966 #else
967 WindowRect rect(_hwnd);
968 ClientRect clnt(_hwnd);
969
970 // increase window height to make room for the new separator
971 rect.top -= STARTMENU_SEP_HEIGHT;
972
973 // move down if we are too high now
974 if (rect.top < 0) {
975 rect.top += STARTMENU_LINE_HEIGHT;
976 rect.bottom += STARTMENU_LINE_HEIGHT;
977 }
978
979 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
980
981 StartMenuSeparator(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left);
982 #endif
983 }
984
985
986 bool StartMenu::CloseOtherSubmenus(int id)
987 {
988 if (_submenu) {
989 if (IsWindow(_submenu)) {
990 if (_submenu_id == id)
991 return false;
992 else {
993 _submenu_id = 0;
994 DestroyWindow(_submenu);
995 // _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
996 }
997 }
998
999 _submenu = 0;
1000 }
1001
1002 return true;
1003 }
1004
1005
1006 void StartMenu::CreateSubmenu(int id, LPCTSTR title, CREATORFUNC_INFO creator)
1007 {
1008 CreateSubmenu(id, StartMenuFolders(), title, creator);
1009 }
1010
1011 void StartMenu::CreateSubmenu(int id, int folder_id, LPCTSTR title, CREATORFUNC_INFO creator)
1012 {
1013 try {
1014 SpecialFolderPath folder(folder_id, _hwnd);
1015
1016 StartMenuFolders new_folders;
1017 new_folders.push_back(folder);
1018
1019 CreateSubmenu(id, new_folders, title, creator);
1020 } catch(COMException&) {
1021 // ignore Exception and don't display anything
1022 }
1023 }
1024
1025 void StartMenu::CreateSubmenu(int id, int folder_id1, int folder_id2, LPCTSTR title, CREATORFUNC_INFO creator)
1026 {
1027 StartMenuFolders new_folders;
1028
1029 try {
1030 new_folders.push_back(SpecialFolderPath(folder_id1, _hwnd));
1031 } catch(COMException&) {
1032 }
1033
1034 try {
1035 new_folders.push_back(SpecialFolderPath(folder_id2, _hwnd));
1036 } catch(COMException&) {
1037 }
1038
1039 if (!new_folders.empty())
1040 CreateSubmenu(id, new_folders, title, creator);
1041 }
1042
1043 void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTSTR title, CREATORFUNC_INFO creator)
1044 {
1045 // Only open one submenu at a time.
1046 if (!CloseOtherSubmenus(id))
1047 return;
1048
1049 RECT rect;
1050 int x, y;
1051
1052 if (GetButtonRect(id, &rect)) {
1053 ClientToScreen(_hwnd, &rect);
1054
1055 x = rect.right; // Submenus should overlap their parent a bit.
1056 y = rect.top+STARTMENU_LINE_HEIGHT-_border_top;
1057 } else {
1058 WindowRect pos(_hwnd);
1059
1060 x = pos.right;
1061 y = pos.top;
1062 }
1063
1064 _submenu_id = id;
1065 _submenu = StartMenu::Create(x, y, new_folders, _hwnd, title, creator);
1066 }
1067
1068
1069 void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries)
1070 {
1071 StartMenuFolders new_folders;
1072 String title;
1073
1074 for(ShellEntrySet::const_iterator it=entries.begin(); it!=entries.end(); ++it) {
1075 Entry* entry = const_cast<Entry*>(*it);
1076
1077 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1078
1079 ///@todo If the user explicitly clicked on a submenu, display this folder as floating start menu.
1080
1081 if (entry->_etype == ET_SHELL)
1082 new_folders.push_back(entry->create_absolute_pidl());
1083 else {
1084 TCHAR path[MAX_PATH];
1085
1086 if (entry->get_path(path))
1087 new_folders.push_back(path);
1088 }
1089
1090 if (title.empty())
1091 title = entry->_display_name;
1092 } else {
1093 // If the entry is no subdirectory, there can only be one shell entry.
1094 assert(entries.size()==1);
1095
1096 HWND hparent = GetParent(_hwnd);
1097 ShellPath shell_path = entry->create_absolute_pidl();
1098
1099 // close start menus after launching the selected entry
1100 CloseStartMenu(id);
1101
1102 ///@todo launch in the background; specify correct HWND for error message box titles
1103 SHELLEXECUTEINFO shexinfo;
1104
1105 shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
1106 shexinfo.fMask = SEE_MASK_INVOKEIDLIST; // SEE_MASK_IDLIST is also possible.
1107 shexinfo.hwnd = hparent;
1108 shexinfo.lpVerb = NULL;
1109 shexinfo.lpFile = NULL;
1110 shexinfo.lpParameters = NULL;
1111 shexinfo.lpDirectory = NULL;
1112 shexinfo.nShow = SW_SHOWNORMAL;
1113
1114 shexinfo.lpIDList = &*shell_path;
1115
1116 if (!ShellExecuteEx(&shexinfo))
1117 display_error(hparent, GetLastError());
1118
1119 // we may have deleted 'this' - ensure we leave the loop and function
1120 return;
1121 }
1122 }
1123
1124 if (!new_folders.empty()) {
1125 // Only open one submenu at a time.
1126 if (!CloseOtherSubmenus(id))
1127 return;
1128
1129 CreateSubmenu(id, new_folders, title);
1130 }
1131 }
1132
1133
1134 /// close all windows of the start menu popup
1135 void StartMenu::CloseStartMenu(int id)
1136 {
1137 if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) { // don't automatically close floating menus
1138 if (!SendParent(PM_STARTENTRY_LAUNCHED, id, (LPARAM)_hwnd))
1139 DestroyWindow(_hwnd);
1140 } else if (_submenu) // instead close submenus of floating parent menus
1141 CloseSubmenus();
1142 }
1143
1144
1145 int GetStartMenuBtnTextWidth(HDC hdc, LPCTSTR title, HWND hwnd)
1146 {
1147 RECT rect = {0, 0, 0, 0};
1148 DrawText(hdc, title, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
1149
1150 return rect.right-rect.left;
1151 }
1152
1153 #ifdef _LIGHT_STARTMENU
1154 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, const SMBtnInfo& btn, bool has_focus, bool pushed)
1155 #else
1156 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, HICON hIcon,
1157 bool hasSubmenu, bool enabled, bool has_focus, bool pushed);
1158 #endif
1159 {
1160 UINT style = DFCS_BUTTONPUSH;
1161
1162 if (!btn._enabled)
1163 style |= DFCS_INACTIVE;
1164
1165 POINT iconPos = {rect.left+2, (rect.top+rect.bottom-16)/2};
1166 RECT textRect = {rect.left+16+4, rect.top+2, rect.right-4, rect.bottom-4};
1167
1168 if (pushed) {
1169 style |= DFCS_PUSHED;
1170 ++iconPos.x; ++iconPos.y;
1171 ++textRect.left; ++textRect.top;
1172 ++textRect.right; ++textRect.bottom;
1173 }
1174
1175 int bk_color_idx = COLOR_BTNFACE;
1176 int text_color_idx = COLOR_BTNTEXT;
1177
1178 if (has_focus) {
1179 bk_color_idx = COLOR_HIGHLIGHT;
1180 text_color_idx = COLOR_HIGHLIGHTTEXT;
1181 }
1182
1183 COLORREF bk_color = GetSysColor(bk_color_idx);
1184 HBRUSH bk_brush = GetSysColorBrush(bk_color_idx);
1185
1186 if (title)
1187 FillRect(hdc, &rect, bk_brush);
1188
1189 if (btn._icon_id > ICID_NONE)
1190 g_Globals._icon_cache.get_icon(btn._icon_id).draw(hdc, iconPos.x, iconPos.y, 16, 16, bk_color, bk_brush);
1191
1192 // draw submenu arrow at the right
1193 if (btn._hasSubmenu) {
1194 static SmallIcon arrowIcon(IDI_ARROW);
1195 static SmallIcon selArrowIcon(IDI_ARROW_SELECTED);
1196
1197 DrawIconEx(hdc, rect.right-16, iconPos.y,
1198 has_focus? selArrowIcon: arrowIcon,
1199 16, 16, 0, bk_brush, DI_NORMAL);
1200 }
1201
1202 if (title) {
1203 BkMode bk_mode(hdc, TRANSPARENT);
1204
1205 if (!btn._enabled) // dis->itemState & (ODS_DISABLED|ODS_GRAYED)
1206 DrawGrayText(hdc, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
1207 else {
1208 TextColor lcColor(hdc, GetSysColor(text_color_idx));
1209 DrawText(hdc, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
1210 }
1211 }
1212 }
1213
1214
1215 #ifdef _LIGHT_STARTMENU
1216
1217 void StartMenu::ResizeToButtons()
1218 {
1219 WindowRect rect(_hwnd);
1220
1221 WindowCanvas canvas(_hwnd);
1222 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
1223
1224 int max_width = STARTMENU_WIDTH_MIN;
1225 int height = 0;
1226
1227 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
1228 int w = GetStartMenuBtnTextWidth(canvas, it->_title, _hwnd);
1229
1230 if (w > max_width)
1231 max_width = w;
1232
1233 if (it->_id == -1)
1234 height += STARTMENU_SEP_HEIGHT;
1235 else
1236 height += STARTMENU_LINE_HEIGHT;
1237 }
1238
1239 // calculate new window size
1240 int text_width = max_width + 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
1241
1242 RECT rt_hgt = {rect.left, rect.bottom-_border_top-height, rect.left+_border_left+text_width, rect.bottom};
1243 AdjustWindowRectEx(&rt_hgt, GetWindowStyle(_hwnd), FALSE, GetWindowExStyle(_hwnd));
1244
1245 // ignore movement, only look at the size change
1246 rect.right = rect.left + (rt_hgt.right-rt_hgt.left);
1247 rect.top = rect.bottom - (rt_hgt.bottom-rt_hgt.top);
1248
1249 // move down if we are too high
1250 if (rect.top < 0) {
1251 rect.top += STARTMENU_LINE_HEIGHT;
1252 rect.bottom += STARTMENU_LINE_HEIGHT;
1253 }
1254
1255 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
1256 }
1257
1258 #else // _LIGHT_STARTMENU
1259
1260 LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1261 {
1262 switch(nmsg) {
1263 case WM_MOUSEMOVE:
1264 // automatically set the focus to startmenu entries when moving the mouse over them
1265 if (GetFocus()!=_hwnd && !(GetWindowStyle(_hwnd)&WS_DISABLED))
1266 SetFocus(_hwnd);
1267 break;
1268
1269 case WM_SETFOCUS:
1270 PostParent(PM_STARTENTRY_FOCUSED, _hasSubmenu, (LPARAM)_hwnd);
1271 goto def;
1272
1273 case WM_CANCELMODE:
1274 // route WM_CANCELMODE to the startmenu window
1275 return SendParent(nmsg, wparam, lparam);
1276
1277 default: def:
1278 return super::WndProc(nmsg, wparam, lparam);
1279 }
1280
1281 return 0;
1282 }
1283
1284 void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis)
1285 {
1286 TCHAR title[BUFFER_LEN];
1287
1288 GetWindowText(_hwnd, title, BUFFER_LEN);
1289
1290 DrawStartMenuButton(dis->hDC, dis->rcItem, title, _hIcon,
1291 _hasSubmenu,
1292 !(dis->itemState & ODS_DISABLED),
1293 dis->itemState&ODS_FOCUS? true: false,
1294 dis->itemState&ODS_SELECTED? true: false);
1295 }
1296
1297 #endif
1298
1299
1300 StartMenuRoot::StartMenuRoot(HWND hwnd)
1301 : super(hwnd)
1302 {
1303 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1304 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCOMMONGROUPS))
1305 #endif
1306 try {
1307 // insert directory "All Users\Start Menu"
1308 ShellDirectory cmn_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
1309 _dirs.push_back(StartMenuDirectory(cmn_startmenu, false)); // don't add subfolders
1310 } catch(COMException&) {
1311 // ignore exception and don't show additional shortcuts
1312 }
1313
1314 try {
1315 // insert directory "<user name>\Start Menu"
1316
1317 ShellDirectory usr_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
1318 _dirs.push_back(StartMenuDirectory(usr_startmenu, false)); // don't add subfolders
1319 } catch(COMException&) {
1320 // ignore exception and don't show additional shortcuts
1321 }
1322
1323 // read size of logo bitmap
1324 BITMAP bmp_hdr;
1325 GetObject(ResBitmap(IDB_LOGOV), sizeof(BITMAP), &bmp_hdr);
1326 _logo_size.cx = bmp_hdr.bmWidth;
1327 _logo_size.cy = bmp_hdr.bmHeight;
1328
1329 _border_left = _logo_size.cx + 1;
1330 }
1331
1332
1333 HWND StartMenuRoot::Create(HWND hwndOwner)
1334 {
1335 WindowRect pos(hwndOwner);
1336
1337 RECT rect = {pos.left, pos.top-STARTMENU_LINE_HEIGHT-4, pos.left+STARTMENU_WIDTH_MIN, pos.top};
1338
1339 #ifndef _LIGHT_STARTMENU
1340 rect.top += STARTMENU_LINE_HEIGHT;
1341 #endif
1342
1343 AdjustWindowRectEx(&rect, WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE, FALSE, 0);
1344
1345 return Window::Create(WINDOW_CREATOR(StartMenuRoot), 0, GetWndClasss(), TITLE_STARTMENU,
1346 WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,
1347 rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndOwner);
1348 }
1349
1350
1351 void StartMenuRoot::TrackStartmenu()
1352 {
1353 MSG msg;
1354 HWND hwnd = _hwnd;
1355
1356 #ifdef _LIGHT_STARTMENU
1357 _selected_id = -1;
1358 #endif
1359
1360 // show previously hidden start menu
1361 ShowWindow(hwnd, SW_SHOW);
1362 SetForegroundWindow(hwnd);
1363
1364 while(IsWindow(hwnd)) {
1365 if (!GetMessage(&msg, 0, 0, 0)) {
1366 PostQuitMessage(msg.wParam);
1367 break;
1368 }
1369
1370 // Check for a mouse click on any window, which is not part of the start menu
1371 if (msg.message==WM_LBUTTONDOWN || msg.message==WM_MBUTTONDOWN || msg.message==WM_RBUTTONDOWN) {
1372 StartMenu* menu_wnd = NULL;
1373
1374 for(HWND hwnd=msg.hwnd; hwnd; hwnd=GetParent(hwnd)) {
1375 menu_wnd = WINDOW_DYNAMIC_CAST(StartMenu, hwnd);
1376
1377 if (menu_wnd)
1378 break;
1379 }
1380
1381 if (!menu_wnd) {
1382 CloseStartMenu();
1383 break;
1384 }
1385 }
1386
1387 try {
1388 if (pretranslate_msg(&msg))
1389 continue;
1390
1391 if (dispatch_dialog_msg(&msg))
1392 continue;
1393
1394 TranslateMessage(&msg);
1395
1396 try {
1397 DispatchMessage(&msg);
1398 } catch(COMException& e) {
1399 HandleException(e, _hwnd);
1400 }
1401 } catch(COMException& e) {
1402 HandleException(e, _hwnd);
1403 }
1404 }
1405 }
1406
1407
1408 LRESULT StartMenuRoot::Init(LPCREATESTRUCT pcs)
1409 {
1410 // add buttons for entries in _entries
1411 if (super::Init(pcs))
1412 return 1;
1413
1414 AddSeparator();
1415
1416
1417 #ifdef __MINGW32__
1418 HKEY hkey, hkeyAdv;
1419 DWORD value, len;
1420
1421 if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), &hkey))
1422 hkey = 0;
1423
1424 if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hkeyAdv))
1425 hkeyAdv = 0;
1426
1427 #define IS_VALUE_ZERO(hk, name) \
1428 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || !value))
1429
1430 #define IS_VALUE_NOT_ZERO(hk, name) \
1431 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || value>0))
1432 #endif
1433
1434
1435 // insert hard coded start entries
1436 AddButton(ResString(IDS_PROGRAMS), ICID_APPS, true, IDC_PROGRAMS);
1437
1438 AddButton(ResString(IDS_DOCUMENTS), ICID_DOCUMENTS, true, IDC_DOCUMENTS);
1439
1440 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1441 if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
1442 #else
1443 if (IS_VALUE_ZERO(hkey, _T("NoRecentDocsMenu")))
1444 #endif
1445 AddButton(ResString(IDS_RECENT), ICID_DOCUMENTS, true, IDC_RECENT);
1446
1447 AddButton(ResString(IDS_FAVORITES), ICID_FAVORITES, true, IDC_FAVORITES);
1448
1449 AddButton(ResString(IDS_SETTINGS), ICID_CONFIG, true, IDC_SETTINGS);
1450
1451 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1452 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOFIND))
1453 #else
1454 if (IS_VALUE_ZERO(hkey, _T("NoFind")))
1455 #endif
1456 AddButton(ResString(IDS_SEARCH), ICID_SEARCH, true, IDC_SEARCH);
1457
1458 AddButton(ResString(IDS_START_HELP), ICID_INFO, false, IDC_START_HELP);
1459
1460 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1461 if (!g_Globals._SHRestricted || !SHRestricted(REST_NORUN))
1462 #else
1463 if (IS_VALUE_ZERO(hkey, _T("NoRun")))
1464 #endif
1465 AddButton(ResString(IDS_LAUNCH), ICID_ACTION, false, IDC_LAUNCH);
1466
1467
1468 AddSeparator();
1469
1470
1471 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1472 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCLOSE))
1473 #else
1474 if (IS_VALUE_NOT_ZERO(hkeyAdv, _T("StartMenuLogoff")))
1475 #endif
1476 AddButton(ResString(IDS_LOGOFF), ICID_LOGOFF, false, IDC_LOGOFF);
1477
1478
1479 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1480 if (!g_Globals._SHRestricted || SHRestricted(REST_STARTMENULOGOFF) != 1)
1481 #else
1482 if (IS_VALUE_ZERO(hkey, _T("NoClose")))
1483 #endif
1484 AddButton(ResString(IDS_SHUTDOWN), ICID_LOGOFF, false, IDC_SHUTDOWN);
1485
1486
1487 #ifdef __MINGW32__
1488 RegCloseKey(hkeyAdv);
1489 RegCloseKey(hkey);
1490 #endif
1491
1492
1493 #ifdef _LIGHT_STARTMENU
1494 // set the window size to fit all buttons
1495 ResizeToButtons();
1496 #endif
1497
1498 return 0;
1499 }
1500
1501
1502 void StartMenuRoot::AddEntries()
1503 {
1504 super::AddEntries();
1505
1506 AddButton(ResString(IDS_EXPLORE), ICID_EXPLORER, false, IDC_EXPLORE);
1507 }
1508
1509
1510 LRESULT StartMenuRoot::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1511 {
1512 switch(nmsg) {
1513 case WM_PAINT: {
1514 PaintCanvas canvas(_hwnd);
1515 Paint(canvas);
1516 break;}
1517
1518 default:
1519 return super::WndProc(nmsg, wparam, lparam);
1520 }
1521
1522 return 0;
1523 }
1524
1525 void StartMenuRoot::Paint(PaintCanvas& canvas)
1526 {
1527 int clr_bits;
1528 {WindowCanvas dc(_hwnd); clr_bits=GetDeviceCaps(dc, BITSPIXEL);}
1529
1530 MemCanvas mem_dc;
1531 ResBitmap bmp(clr_bits<=8? clr_bits<=4? IDB_LOGOV16: IDB_LOGOV256: IDB_LOGOV);
1532 BitmapSelection sel(mem_dc, bmp);
1533
1534 ClientRect clnt(_hwnd);
1535 int h = min(_logo_size.cy, clnt.bottom);
1536
1537 RECT rect = {0, 0, _logo_size.cx, clnt.bottom-h};
1538 HBRUSH hbr = CreateSolidBrush(GetPixel(mem_dc, 0, 0));
1539 FillRect(canvas, &rect, hbr);
1540 DeleteObject(hbr);
1541
1542 PatBlt(canvas, _logo_size.cx, 0, 1, clnt.bottom, WHITENESS);
1543
1544 BitBlt(canvas, 0, clnt.bottom-h, _logo_size.cx, h, mem_dc, 0, 0, SRCCOPY);
1545
1546 super::Paint(canvas);
1547 }
1548
1549
1550 void StartMenuRoot::CloseStartMenu(int id)
1551 {
1552 if (_submenu)
1553 CloseSubmenus();
1554
1555 ShowWindow(_hwnd, SW_HIDE);
1556 }
1557
1558 void StartMenuRoot::ProcessKey(int vk)
1559 {
1560 switch(vk) {
1561 case VK_LEFT:
1562 if (_submenu)
1563 CloseOtherSubmenus();
1564 // don't close start menu root
1565 break;
1566
1567 default:
1568 super::ProcessKey(vk);
1569 }
1570 }
1571
1572
1573 int StartMenuHandler::Command(int id, int code)
1574 {
1575 switch(id) {
1576
1577 // start menu root
1578
1579 case IDC_PROGRAMS:
1580 CreateSubmenu(id, CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS, ResString(IDS_PROGRAMS));
1581 break;
1582
1583 case IDC_EXPLORE:
1584 CloseStartMenu(id);
1585 explorer_show_frame(SW_SHOWNORMAL);
1586 break;
1587
1588 case IDC_LAUNCH:
1589 CloseStartMenu(id);
1590 ShowLaunchDialog(g_Globals._hwndDesktopBar);
1591 break;
1592
1593 case IDC_DOCUMENTS:
1594 CreateSubmenu(id, CSIDL_PERSONAL, ResString(IDS_DOCUMENTS));
1595 break;
1596
1597 case IDC_RECENT:
1598 CreateSubmenu(id, CSIDL_RECENT, ResString(IDS_RECENT), STARTMENU_CREATOR(RecentStartMenu));
1599 break;
1600
1601 case IDC_FAVORITES:
1602 CreateSubmenu(id, CSIDL_FAVORITES, ResString(IDS_FAVORITES));
1603 break;
1604
1605 case IDC_SETTINGS:
1606 CreateSubmenu(id, ResString(IDS_SETTINGS), STARTMENU_CREATOR(SettingsMenu));
1607 break;
1608
1609 case IDC_SEARCH:
1610 CreateSubmenu(id, ResString(IDS_SEARCH), STARTMENU_CREATOR(SearchMenu));
1611 break;
1612
1613 case IDC_START_HELP:
1614 CloseStartMenu(id);
1615 MessageBox(g_Globals._hwndDesktopBar, TEXT("Help not yet implemented"), ResString(IDS_TITLE), MB_OK);
1616 break;
1617
1618 case IDC_LOGOFF:
1619 /* The shell32 Dialog prompts about some system setting change. This is not what we want to display here.
1620 CloseStartMenu(id);
1621 ShowRestartDialog(g_Globals._hwndDesktopBar, EWX_LOGOFF);*/
1622 DestroyWindow(GetParent(_hwnd));
1623 break;
1624
1625 case IDC_SHUTDOWN:
1626 CloseStartMenu(id);
1627 ShowExitWindowsDialog(g_Globals._hwndDesktopBar);
1628 break;
1629
1630
1631 // settings menu
1632
1633 case ID_DESKTOPBAR_SETTINGS:
1634 CloseStartMenu(id);
1635 ExplorerPropertySheet(g_Globals._hwndDesktopBar);
1636 break;
1637
1638 case IDC_PRINTERS:
1639 CloseStartMenu(id);
1640 MainFrame::Create(SpecialFolderPath(CSIDL_PRINTERS, _hwnd), OWM_PIDL);
1641 break;
1642
1643 case IDC_CONTROL_PANEL:
1644 CloseStartMenu(id);
1645 MainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
1646 break;
1647
1648 case IDC_ADMIN:
1649 CloseStartMenu(id);
1650 MainFrame::Create(SpecialFolderPath(CSIDL_COMMON_ADMINTOOLS, _hwnd), OWM_PIDL);
1651 break;
1652
1653 case IDC_CONNECTIONS:
1654 CloseStartMenu(id);
1655 MainFrame::Create(SpecialFolderPath(CSIDL_CONNECTIONS, _hwnd), OWM_PIDL);
1656 break;
1657
1658
1659 // search menu
1660
1661 case IDC_SEARCH_FILES:
1662 CloseStartMenu(id);
1663 ShowSearchDialog();
1664 break;
1665
1666 case IDC_SEARCH_COMPUTER:
1667 CloseStartMenu(id);
1668 ShowSearchComputer();
1669 break;
1670
1671
1672 default:
1673 return super::Command(id, code);
1674 }
1675
1676 return 0;
1677 }
1678
1679
1680 void StartMenuHandler::ShowSearchDialog()
1681 {
1682 static DynamicFct<SHFINDFILES> SHFindFiles(TEXT("SHELL32"), 90);
1683
1684 if (SHFindFiles)
1685 (*SHFindFiles)(NULL, NULL);
1686 else
1687 MessageBox(0, TEXT("SHFindFiles() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
1688 }
1689
1690 void StartMenuHandler::ShowSearchComputer()
1691 {
1692 static DynamicFct<SHFINDCOMPUTER> SHFindComputer(TEXT("SHELL32"), 91);
1693
1694 if (SHFindComputer)
1695 (*SHFindComputer)(NULL, NULL);
1696 else
1697 MessageBox(0, TEXT("SHFindComputer() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
1698 }
1699
1700 void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner)
1701 {
1702 ///@todo All text phrases should be put into the resources.
1703 static LPCSTR szTitle = "Run";
1704 static LPCSTR szText = "Type the name of a program, folder, document, or Internet resource, and Explorer will open it for you.";
1705
1706 static DynamicFct<RUNFILEDLG> RunFileDlg(TEXT("SHELL32"), 61);
1707
1708 // Show "Run..." dialog
1709 if (RunFileDlg) {
1710 #ifndef _ROS_ /* FIXME: our shell32 always expects Ansi strings */
1711 #define W_VER_NT 0
1712 if ((HIWORD(GetVersion())>>14) == W_VER_NT) {
1713 WCHAR wTitle[40], wText[256];
1714
1715 MultiByteToWideChar(CP_ACP, 0, szTitle, -1, wTitle, 40);
1716 MultiByteToWideChar(CP_ACP, 0, szText, -1, wText, 256);
1717
1718 (*RunFileDlg)(hwndOwner, 0, NULL, (LPCSTR)wTitle, (LPCSTR)wText, RFF_CALCDIRECTORY);
1719 }
1720 else
1721 #endif
1722 (*RunFileDlg)(hwndOwner, 0, NULL, szTitle, szText, RFF_CALCDIRECTORY);
1723 }
1724 }
1725
1726 void StartMenuHandler::ShowRestartDialog(HWND hwndOwner, UINT flags)
1727 {
1728 static DynamicFct<RESTARTWINDOWSDLG> RestartDlg(TEXT("SHELL32"), 59);
1729
1730 if (RestartDlg)
1731 (*RestartDlg)(hwndOwner, (LPWSTR)L"You selected <Log Off>.\n\n", flags); ///@todo ANSI string conversion if needed
1732 else
1733 MessageBox(hwndOwner, TEXT("RestartDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
1734 }
1735
1736 void ShowExitWindowsDialog(HWND hwndOwner)
1737 {
1738 static DynamicFct<EXITWINDOWSDLG> ExitWindowsDlg(TEXT("SHELL32"), 60);
1739
1740 if (ExitWindowsDlg)
1741 (*ExitWindowsDlg)(hwndOwner);
1742 else
1743 MessageBox(hwndOwner, TEXT("ExitWindowsDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
1744 }
1745
1746
1747 void SettingsMenu::AddEntries()
1748 {
1749 super::AddEntries();
1750
1751 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1752 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
1753 #endif
1754 AddButton(ResString(IDS_CONTROL_PANEL), ICID_CONFIG, false, IDC_CONTROL_PANEL);
1755
1756 AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, false, IDC_PRINTERS);
1757 AddButton(ResString(IDS_CONNECTIONS), ICID_NETWORK, false, IDC_CONNECTIONS);
1758 AddButton(ResString(IDS_ADMIN), ICID_CONFIG, false, IDC_ADMIN);
1759
1760 AddButton(ResString(IDS_DESKTOPBAR_SETTINGS), ICID_CONFIG, false, ID_DESKTOPBAR_SETTINGS);
1761 }
1762
1763 void SearchMenu::AddEntries()
1764 {
1765 super::AddEntries();
1766
1767 AddButton(ResString(IDS_SEARCH_FILES), ICID_SEARCH_DOC, false, IDC_SEARCH_FILES);
1768
1769 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1770 if (!g_Globals._SHRestricted || !SHRestricted(REST_HASFINDCOMPUTERS))
1771 #endif
1772 AddButton(ResString(IDS_SEARCH_COMPUTER), ICID_COMPUTER, false, IDC_SEARCH_COMPUTER);
1773 }
1774
1775
1776 void RecentStartMenu::AddEntries()
1777 {
1778 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
1779 StartMenuDirectory& smd = *it;
1780 ShellDirectory& dir = smd._dir;
1781
1782 if (!dir._scanned) {
1783 WaitCursor wait;
1784
1785 #ifdef _LAZY_ICONEXTRACT
1786 dir.smart_scan(SCAN_FILESYSTEM);
1787 #else
1788 dir.smart_scan(SCAN_EXTRACT_ICONS|SCAN_FILESYSTEM);
1789 #endif
1790 }
1791
1792 dir.sort_directory(SORT_DATE);
1793 AddShellEntries(dir, 16, smd._subfolders); ///@todo read max. count of entries from registry
1794 }
1795 }