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