Matthias Kupfer (matthias DOT kupfer AT informatik DOT tu-chemnitz DOT de)
[reactos.git] / reactos / base / shell / explorer / taskbar / startmenu.cpp
1 /*
2 * Copyright 2003, 2004 Martin Fuchs
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19
20 //
21 // Explorer clone
22 //
23 // 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 <precomp.h>
34
35 #include "../resource.h"
36
37 #include "desktopbar.h"
38 #include "startmenu.h"
39
40 #include "../dialogs/searchprogram.h"
41 #include "../dialogs/settings.h"
42
43
44 #define SHELLPATH_CONTROL_PANEL TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}")
45 #define SHELLPATH_PRINTERS TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")
46 #define SHELLPATH_NET_CONNECTIONS TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}")
47
48
49 StartMenu::StartMenu(HWND hwnd, int icon_size)
50 : super(hwnd),
51 _icon_size(icon_size)
52 {
53 _next_id = IDC_FIRST_MENU;
54 _submenu_id = 0;
55
56 _border_left = 0;
57 _border_top = 0;
58 _bottom_max = INT_MAX;
59
60 _floating_btn = false;
61 _arrow_btns = false;
62 _scroll_mode = SCROLL_NOT;
63 _scroll_pos = 0;
64 _invisible_lines = 0;
65
66 _last_pos = WindowRect(hwnd).pos();
67 #ifdef _LIGHT_STARTMENU
68 _selected_id = -1;
69 _last_mouse_pos = 0;
70 #endif
71 }
72
73 StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info, int icon_size)
74 : super(hwnd),
75 _create_info(create_info),
76 _icon_size(icon_size)
77 {
78 for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
79 if (*it)
80 _dirs.push_back(ShellDirectory(GetDesktopFolder(), *it, _hwnd));
81
82 _next_id = IDC_FIRST_MENU;
83 _submenu_id = 0;
84
85 _border_left = 0;
86 _border_top = create_info._border_top;
87 _bottom_max = INT_MAX;
88
89 _floating_btn = create_info._border_top? true: false;
90 _arrow_btns = false;
91 _scroll_mode = SCROLL_NOT;
92 _scroll_pos = 0;
93 _invisible_lines = 0;
94
95 _last_pos = WindowRect(hwnd).pos();
96 #ifdef _LIGHT_STARTMENU
97 _selected_id = -1;
98 _last_mouse_pos = 0;
99 #endif
100 }
101
102 StartMenu::~StartMenu()
103 {
104 SendParent(PM_STARTMENU_CLOSED);
105 }
106
107
108 // We need this wrapper function for s_wcStartMenu, it calls the WIN32 API,
109 // though static C++ initializers are not allowed for Winelib applications.
110 BtnWindowClass& StartMenu::GetWndClasss()
111 {
112 static BtnWindowClass s_wcStartMenu(CLASSNAME_STARTMENU);
113
114 return s_wcStartMenu;
115 }
116
117
118 Window::CREATORFUNC_INFO StartMenu::s_def_creator = STARTMENU_CREATOR(StartMenu);
119
120 HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndParent, LPCTSTR title,
121 CREATORFUNC_INFO creator, void* info, const String& filter)
122 {
123 UINT style, ex_style;
124 int top_height;
125
126 if (hwndParent) {
127 style = WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE;
128 ex_style = 0;
129 top_height = STARTMENU_TOP_BTN_SPACE;
130 } else {
131 style = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPCHILDREN|WS_VISIBLE;
132 ex_style = WS_EX_TOOLWINDOW;
133 top_height = 0;
134 }
135
136 int icon_size = ICON_SIZE_SMALL;
137 RECT rect = {x, y-STARTMENU_LINE_HEIGHT(icon_size)-top_height, x+STARTMENU_WIDTH_MIN, y};
138
139 #ifndef _LIGHT_STARTMENU
140 rect.top += STARTMENU_LINE_HEIGHT(icon_size);
141 #endif
142
143 AdjustWindowRectEx(&rect, style, FALSE, ex_style);
144
145 StartMenuCreateInfo create_info;
146
147 create_info._folders = folders;
148 create_info._border_top = top_height;
149 create_info._creator = creator;
150 create_info._info = info;
151 create_info._filter = filter;
152
153 if (title)
154 create_info._title = title;
155
156 HWND hwnd = Window::Create(creator, &create_info, ex_style, GetWndClasss(), title,
157 style, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndParent);
158
159 // make sure the window is not off the screen
160 MoveVisible(hwnd);
161
162 return hwnd;
163 }
164
165
166 LRESULT StartMenu::Init(LPCREATESTRUCT pcs)
167 {
168 try {
169 AddEntries();
170
171 if (super::Init(pcs))
172 return 1;
173
174 // create buttons for registered entries in _entries
175 for(ShellEntryMap::const_iterator it=_entries.begin(); it!=_entries.end(); ++it) {
176 const StartMenuEntry& sme = it->second;
177 bool hasSubmenu = false;
178
179 for(ShellEntrySet::const_iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it)
180 if ((*it)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
181 hasSubmenu = true;
182
183 #ifdef _LIGHT_STARTMENU
184 _buttons.push_back(SMBtnInfo(sme, it->first, hasSubmenu));
185 #else
186 AddButton(sme._title, sme._hIcon, hasSubmenu, it->first);
187 #endif
188 }
189
190 #ifdef _LIGHT_STARTMENU
191 if (_buttons.empty())
192 #else
193 if (!GetWindow(_hwnd, GW_CHILD))
194 #endif
195 AddButton(ResString(IDS_EMPTY), ICID_NONE, false, 0, false);
196
197 #ifdef _LIGHT_STARTMENU
198 ResizeToButtons();
199 #endif
200
201 #ifdef _LAZY_ICONEXTRACT
202 PostMessage(_hwnd, PM_UPDATE_ICONS, 0, 0);
203 #endif
204 } catch(COMException& e) {
205 HandleException(e, pcs->hwndParent); // destroys the start menu window while switching focus
206 }
207
208 return 0;
209 }
210
211 void StartMenu::AddEntries()
212 {
213 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
214 StartMenuDirectory& smd = *it;
215 ShellDirectory& dir = smd._dir;
216
217 if (!dir._scanned) {
218 WaitCursor wait;
219
220 #ifdef _LAZY_ICONEXTRACT
221 dir.smart_scan(SORT_NAME, SCAN_DONT_EXTRACT_ICONS); // lazy icon extraction, try to read directly from filesystem
222 #else
223 dir.smart_scan(SORT_NAME);
224 #endif
225 }
226
227 AddShellEntries(dir, -1, smd._ignore);
228 }
229 }
230
231
232 static LPTSTR trim_path_slash(LPTSTR path)
233 {
234 LPTSTR p = path;
235
236 while(*p)
237 ++p;
238
239 if (p>path && (p[-1]=='\\' || p[-1]=='/'))
240 *--p = '\0';
241
242 return path;
243 }
244
245 void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, const String& ignore)
246 {
247 TCHAR ignore_path[MAX_PATH], ignore_dir[MAX_PATH], ignore_name[_MAX_FNAME], ignore_ext[_MAX_EXT];
248 TCHAR dir_path[MAX_PATH];
249
250 if (!ignore.empty()) {
251 _tsplitpath_s(ignore, ignore_path, COUNTOF(ignore_path), ignore_dir, COUNTOF(ignore_dir), ignore_name, COUNTOF(ignore_name), ignore_ext, COUNTOF(ignore_ext));
252
253 _tcscat(ignore_path, ignore_dir);
254 _tcscat(ignore_name, ignore_ext);
255
256 dir.get_path(dir_path, COUNTOF(dir_path));
257
258 if (_tcsicmp(trim_path_slash(dir_path), trim_path_slash(ignore_path)))
259 *ignore_name = '\0';
260 } else
261 *ignore_name = '\0';
262
263 String lwr_filter = _create_info._filter;
264 lwr_filter.toLower();
265
266 int cnt = 0;
267 for(Entry*entry=dir._down; entry; entry=entry->_next) {
268 // hide files like "desktop.ini"
269 if (entry->_shell_attribs & SFGAO_HIDDEN)
270 //not appropriate for drive roots: if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
271 continue;
272
273 // hide "Programs" subfolders if requested
274 if (*ignore_name && !_tcsicmp(entry->_data.cFileName, ignore_name))
275 continue;
276
277 // only 'max' entries shall be added.
278 if (++cnt == max)
279 break;
280
281 // filter only non-directory entries
282 if (!(entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && !lwr_filter.empty()) {
283 String lwr_name = entry->_data.cFileName;
284 String lwr_disp = entry->_display_name;
285
286 lwr_name.toLower();
287 lwr_disp.toLower();
288
289 if (!_tcsstr(lwr_name,lwr_filter) && !_tcsstr(lwr_disp,lwr_filter))
290 continue;
291 }
292
293 if (entry->_etype == ET_SHELL)
294 AddEntry(dir._folder, static_cast<ShellEntry*>(entry));
295 else
296 AddEntry(dir._folder, entry);
297 }
298 }
299
300
301 LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
302 {
303 switch(nmsg) {
304 case WM_PAINT: {
305 PaintCanvas canvas(_hwnd);
306 Paint(canvas);
307 break;}
308
309 case WM_SIZE:
310 ResizeButtons(LOWORD(lparam)-_border_left);
311 break;
312
313 case WM_MOVE: {
314 POINTS pos;
315 pos.x = LOWORD(lparam);
316 pos.y = HIWORD(lparam);
317
318 // move open submenus of floating menus
319 if (_submenu) {
320 int dx = pos.x - _last_pos.x;
321 int dy = pos.y - _last_pos.y;
322
323 if (dx || dy) {
324 WindowRect rt(_submenu);
325 SetWindowPos(_submenu, 0, rt.left+dx, rt.top+dy, 0, 0, SWP_NOSIZE|SWP_NOACTIVATE);
326 //MoveVisible(_submenu);
327 }
328 }
329
330 _last_pos.x = pos.x;
331 _last_pos.y = pos.y;
332 goto def;}
333
334 case WM_NCHITTEST: {
335 LRESULT res = super::WndProc(nmsg, wparam, lparam);
336
337 if (res>=HTSIZEFIRST && res<=HTSIZELAST)
338 return HTCLIENT; // disable window resizing
339
340 return res;}
341
342 case WM_LBUTTONDOWN: {
343 RECT rect;
344
345 // check mouse cursor for coordinates of floating button
346 GetFloatingButtonRect(&rect);
347
348 if (PtInRect(&rect, Point(lparam))) {
349 // create a floating copy of the current start menu
350 WindowRect pos(_hwnd);
351
352 ///@todo do something similar to StartMenuRoot::TrackStartmenu() in order to automatically close submenus when clicking on the desktop background
353 StartMenu::Create(pos.left+3, pos.bottom-3, _create_info._folders, 0, _create_info._title, _create_info._creator, _create_info._info);
354 CloseStartMenu();
355 }
356
357 #ifdef _LIGHT_STARTMENU
358 int id = ButtonHitTest(Point(lparam));
359
360 if (id)
361 Command(id, BN_CLICKED);
362 #endif
363 break;}
364
365 case WM_SYSCOMMAND:
366 if ((wparam&0xFFF0) == SC_SIZE)
367 return 0; // disable window resizing
368 goto def;
369
370 case WM_ACTIVATEAPP:
371 // close start menu when activating another application
372 if (!wparam)
373 CloseStartMenu();
374 break; // don't call super::WndProc in case "this" has been deleted
375
376 case WM_CANCELMODE:
377 CloseStartMenu();
378
379 #ifdef _LIGHT_STARTMENU
380 if (_scroll_mode != SCROLL_NOT) {
381 ReleaseCapture();
382 KillTimer(_hwnd, 0);
383 }
384 #endif
385 break;
386
387 #ifdef _LIGHT_STARTMENU
388 case WM_MOUSEMOVE: {
389 // automatically set the focus to startmenu entries when moving the mouse over them
390 if (lparam != _last_mouse_pos) { // don't process WM_MOUSEMOVE when opening submenus using keyboard navigation
391 Point pt(lparam);
392
393 if (_arrow_btns) {
394 RECT rect_up, rect_down;
395
396 GetArrowButtonRects(&rect_up, &rect_down, _icon_size);
397
398 SCROLL_MODE scroll_mode = SCROLL_NOT;
399
400 if (PtInRect(&rect_up, pt))
401 scroll_mode = SCROLL_UP;
402 else if (PtInRect(&rect_down, pt))
403 scroll_mode = SCROLL_DOWN;
404
405 if (scroll_mode != _scroll_mode) {
406 if (scroll_mode == SCROLL_NOT) {
407 ReleaseCapture();
408 KillTimer(_hwnd, 0);
409 } else {
410 CloseSubmenus();
411 SetTimer(_hwnd, 0, 150, NULL); // 150 ms scroll interval
412 SetCapture(_hwnd);
413 }
414
415 _scroll_mode = scroll_mode;
416 }
417 }
418
419 int new_id = ButtonHitTest(pt);
420
421 if (new_id>0 && new_id!=_selected_id)
422 SelectButton(new_id);
423
424 _last_mouse_pos = lparam;
425 }
426 break;}
427
428 case WM_TIMER:
429 if (_scroll_mode == SCROLL_UP) {
430 if (_scroll_pos > 0) {
431 --_scroll_pos;
432 InvalidateRect(_hwnd, NULL, TRUE);
433 }
434 } else {
435 if (_scroll_pos <= _invisible_lines) {
436 ++_scroll_pos;
437 InvalidateRect(_hwnd, NULL, TRUE);
438 }
439 }
440 break;
441
442 case WM_KEYDOWN:
443 ProcessKey(wparam);
444 break;
445 #else
446 case PM_STARTENTRY_FOCUSED: { ///@todo use TrackMouseEvent() and WM_MOUSEHOVER to wait a bit before opening submenus
447 BOOL hasSubmenu = wparam;
448 HWND hctrl = (HWND)lparam;
449
450 // automatically open submenus
451 if (hasSubmenu) {
452 UpdateWindow(_hwnd); // draw focused button before waiting on submenu creation
453 //SendMessage(_hwnd, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hctrl),BN_CLICKED), (LPARAM)hctrl);
454 Command(GetDlgCtrlID(hctrl), BN_CLICKED);
455 } else {
456 // close any open submenu
457 CloseOtherSubmenus();
458 }
459 break;}
460 #endif
461
462 #ifdef _LAZY_ICONEXTRACT
463 case PM_UPDATE_ICONS:
464 UpdateIcons(/*wparam*/);
465 break;
466 #endif
467
468 case PM_STARTENTRY_LAUNCHED:
469 if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus
470 return 0;
471
472 // route message to the parent menu and close menus after launching an entry
473 if (!SendParent(nmsg, wparam, lparam))
474 CloseStartMenu();
475 return 1; // signal that we have received and processed the message
476
477 case PM_STARTMENU_CLOSED:
478 _submenu = 0;
479 break;
480
481 case PM_SELECT_ENTRY:
482 SelectButtonIndex(0, wparam!=0);
483 break;
484
485 #ifdef _LIGHT_STARTMENU
486 case WM_CONTEXTMENU: {
487 Point screen_pt(lparam), clnt_pt=screen_pt;
488 ScreenToClient(_hwnd, &clnt_pt);
489
490 int id = ButtonHitTest(clnt_pt);
491
492 if (id) {
493 StartMenuEntry& sme = _entries[id];
494
495 for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
496 Entry* entry = *it;
497
498 if (entry) {
499 CHECKERROR(entry->do_context_menu(_hwnd, screen_pt, _cm_ifs)); // may close start menu because of focus loss
500 ///@todo refresh on successfull context menu execution?
501 break; ///@todo handle context menu for more than one entry
502 }
503 }
504 }
505 break;}
506 #endif
507
508 default: def:
509 return super::WndProc(nmsg, wparam, lparam);
510 }
511
512 return 0;
513 }
514
515
516 #ifdef _LIGHT_STARTMENU
517
518 int StartMenu::ButtonHitTest(POINT pt)
519 {
520 ClientRect clnt(_hwnd);
521 const int icon_size = _icon_size;
522 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
523
524 if (pt.x<rect.left || pt.x>rect.right)
525 return 0;
526
527 for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
528 const SMBtnInfo& info = *it;
529
530 if (rect.top > pt.y)
531 break;
532
533 rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
534
535 if (rect.bottom > _bottom_max)
536 break;
537
538 if (pt.y < rect.bottom) // PtInRect(&rect, pt)
539 return info._id;
540
541 rect.top = rect.bottom;
542 }
543
544 return 0;
545 }
546
547 void StartMenu::InvalidateSelection()
548 {
549 if (_selected_id <= 0)
550 return;
551
552 ClientRect clnt(_hwnd);
553 const int icon_size = _icon_size;
554 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
555
556 for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
557 const SMBtnInfo& info = *it;
558
559 rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
560
561 if (info._id == _selected_id) {
562 InvalidateRect(_hwnd, &rect, TRUE);
563 break;
564 }
565
566 rect.top = rect.bottom;
567 }
568 }
569
570 const SMBtnInfo* StartMenu::GetButtonInfo(int id) const
571 {
572 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it)
573 if (it->_id == id)
574 return &*it;
575
576 return NULL;
577 }
578
579 bool StartMenu::SelectButton(int id, bool open_sub)
580 {
581 if (id == -1)
582 return false;
583
584 if (id == _selected_id)
585 return true;
586
587 InvalidateSelection();
588
589 const SMBtnInfo* btn = GetButtonInfo(id);
590
591 if (btn && btn->_enabled) {
592 _selected_id = id;
593
594 InvalidateSelection();
595
596 // automatically open submenus
597 if (btn->_hasSubmenu) {
598 if (open_sub)
599 OpenSubmenu();
600 } else
601 CloseOtherSubmenus(); // close any open submenu
602
603 return true;
604 } else {
605 _selected_id = -1;
606 return false;
607 }
608 }
609
610 bool StartMenu::OpenSubmenu(bool select_first)
611 {
612 if (_selected_id == -1)
613 return false;
614
615 InvalidateSelection();
616
617 const SMBtnInfo* btn = GetButtonInfo(_selected_id);
618
619 // automatically open submenus
620 if (btn->_hasSubmenu) {
621 //@@ allows destroying of startmenu when processing PM_UPDATE_ICONS -> GPF
622 UpdateWindow(_hwnd); // draw focused button before waiting on submenu creation
623 Command(_selected_id, BN_CLICKED);
624
625 if (select_first && _submenu)
626 SendMessage(_submenu, PM_SELECT_ENTRY, (WPARAM)false, 0);
627
628 return true;
629 } else
630 return false;
631 }
632
633
634 int StartMenu::GetSelectionIndex()
635 {
636 if (_selected_id == -1)
637 return -1;
638
639 for(int i=0; i<(int)_buttons.size(); ++i)
640 if (_buttons[i]._id == _selected_id)
641 return i;
642
643 return -1;
644 }
645
646 bool StartMenu::SelectButtonIndex(int idx, bool open_sub)
647 {
648 if (idx>=0 && idx<(int)_buttons.size())
649 return SelectButton(_buttons[idx]._id, open_sub);
650 else
651 return false;
652 }
653
654 void StartMenu::ProcessKey(int vk)
655 {
656 switch(vk) {
657 case VK_RETURN:
658 if (_selected_id)
659 Command(_selected_id, BN_CLICKED);
660 break;
661
662 case VK_UP:
663 Navigate(-1);
664 break;
665
666 case VK_DOWN:
667 Navigate(+1);
668 break;
669
670 case VK_HOME:
671 SelectButtonIndex(0, false);
672 break;
673
674 case VK_END:
675 SelectButtonIndex(_buttons.size()-1, false);
676 break;
677
678 case VK_LEFT:
679 if (_submenu)
680 CloseOtherSubmenus();
681 else if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) // don't automatically close floating menus
682 DestroyWindow(_hwnd);
683 break;
684
685 case VK_RIGHT:
686 OpenSubmenu(true);
687 break;
688
689 case VK_ESCAPE:
690 CloseStartMenu();
691 break;
692
693 default:
694 if (vk>='0' && vk<='Z')
695 JumpToNextShortcut(vk);
696 }
697 }
698
699 bool StartMenu::Navigate(int step)
700 {
701 int idx = GetSelectionIndex();
702
703 if (idx == -1)
704 {
705 if (step > 0)
706 idx = 0 - step;
707 else
708 idx = _buttons.size() - step;
709 }
710
711 for(;;) {
712 idx += step;
713
714 if (_buttons.size()<=1 && (idx<0 || idx>(int)_buttons.size()))
715 break;
716
717 if (idx < 0)
718 idx += _buttons.size();
719
720 if (idx > (int)_buttons.size())
721 idx -= _buttons.size()+1;
722
723 if (SelectButtonIndex(idx, false))
724 return true;
725 }
726
727 return false;
728 }
729
730 bool StartMenu::JumpToNextShortcut(char c)
731 {
732 int cur_idx = GetSelectionIndex();
733
734 if (cur_idx == -1)
735 cur_idx = 0;
736
737 int first_found = 0;
738 int found_more = 0;
739
740 SMBtnVector::const_iterator cur_it = _buttons.begin();
741 cur_it += cur_idx + 1;
742
743 // first search down from current item...
744 SMBtnVector::const_iterator it = cur_it;
745 for(; it!=_buttons.end(); ++it) {
746 const SMBtnInfo& btn = *it;
747
748 if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
749 if (!first_found)
750 first_found = btn._id;
751 else
752 ++found_more;
753 }
754 }
755
756 // ...now search from top to the current item
757 it = _buttons.begin();
758 for(; it!=_buttons.end() && it!=cur_it; ++it) {
759 const SMBtnInfo& btn = *it;
760
761 if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
762 if (!first_found)
763 first_found = btn._id;
764 else
765 ++found_more;
766 }
767 }
768
769 if (first_found) {
770 SelectButton(first_found);
771
772 if (!found_more)
773 Command(first_found, BN_CLICKED);
774
775 return true;
776 } else
777 return false;
778 }
779
780 #endif // _LIGHT_STARTMENU
781
782
783 bool StartMenu::GetButtonRect(int id, PRECT prect) const
784 {
785 #ifdef _LIGHT_STARTMENU
786 ClientRect clnt(_hwnd);
787 const int icon_size = _icon_size;
788 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
789
790 for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
791 const SMBtnInfo& info = *it;
792
793 rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
794
795 if (info._id == id) {
796 *prect = rect;
797 return true;
798 }
799
800 rect.top = rect.bottom;
801 }
802
803 return false;
804 #else
805 HWND btn = GetDlgItem(_hwnd, id);
806
807 if (btn) {
808 GetWindowRect(btn, prect);
809 ScreenToClient(_hwnd, prect);
810
811 return true;
812 } else
813 return false;
814 #endif
815 }
816
817
818 void StartMenu::DrawFloatingButton(HDC hdc)
819 {
820 static ResIconEx floatingIcon(IDI_FLOATING, 8, 4);
821
822 ClientRect clnt(_hwnd);
823
824 DrawIconEx(hdc, clnt.right-12, 0, floatingIcon, 8, 4, 0, 0, DI_NORMAL);
825 }
826
827 void StartMenu::GetFloatingButtonRect(LPRECT prect)
828 {
829 GetClientRect(_hwnd, prect);
830
831 prect->right -= 4;
832 prect->left = prect->right - 8;
833 prect->bottom = 4;
834 }
835
836
837 void StartMenu::DrawArrows(HDC hdc, int icon_size)
838 {
839 int cx = icon_size / 2;
840 int cy = icon_size / 4;
841
842 ResIconEx arrowUpIcon(IDI_ARROW_UP, cx, cy);
843 ResIconEx arrowDownIcon(IDI_ARROW_DOWN, cx, cy);
844
845 ClientRect clnt(_hwnd);
846
847 DrawIconEx(hdc, clnt.right/2-cx/2, _floating_btn?3:1, arrowUpIcon, cx, cy, 0, 0, DI_NORMAL);
848 DrawIconEx(hdc, clnt.right/2-cx/2, clnt.bottom-cy-1, arrowDownIcon, cx, cy, 0, 0, DI_NORMAL);
849 }
850
851 void StartMenu::GetArrowButtonRects(LPRECT prect_up, LPRECT prect_down, int icon_size)
852 {
853 int cx = icon_size / 2;
854 int cy = icon_size / 4;
855
856 GetClientRect(_hwnd, prect_up);
857 *prect_down = *prect_up;
858
859 // prect_up->left = prect_up->right/2 - cx/2;
860 // prect_up->right = prect_up->left + cy;
861 prect_up->right -= cx;
862 prect_up->top = _floating_btn? cy-1: 1;
863 prect_up->bottom = prect_up->top + cy;
864
865 // prect_down->left = prect_down->right/2 - cx/2;
866 // prect_down->right = prect_down->left + cy;
867 prect_down->right -= cx;
868 prect_down->top = prect_down->bottom - cy - 1;
869 }
870
871
872 void StartMenu::Paint(PaintCanvas& canvas)
873 {
874 if (_floating_btn)
875 DrawFloatingButton(canvas);
876
877 #ifdef _LIGHT_STARTMENU
878 if (_arrow_btns)
879 DrawArrows(canvas, _icon_size);
880
881 ClientRect clnt(_hwnd);
882 const int icon_size = _icon_size;
883 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
884
885 int sep_width = rect.right-rect.left - 4;
886
887 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
888 BkMode bk_mode(canvas, TRANSPARENT);
889
890 for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
891 const SMBtnInfo& btn = *it;
892
893 if (rect.top > canvas.rcPaint.bottom)
894 break;
895
896 if (btn._id == -1) { // a separator?
897 rect.bottom = rect.top + STARTMENU_SEP_HEIGHT(icon_size);
898
899 if (rect.bottom > _bottom_max)
900 break;
901
902 BrushSelection brush_sel(canvas, GetSysColorBrush(COLOR_BTNSHADOW));
903 PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT(icon_size)/2-1, sep_width, 1, PATCOPY);
904
905 SelectBrush(canvas, GetSysColorBrush(COLOR_BTNHIGHLIGHT));
906 PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT(icon_size)/2, sep_width, 1, PATCOPY);
907 } else {
908 rect.bottom = rect.top + STARTMENU_LINE_HEIGHT(icon_size);
909
910 if (rect.bottom > _bottom_max)
911 break;
912
913 if (rect.top >= canvas.rcPaint.top)
914 DrawStartMenuButton(canvas, rect, btn._title, btn, btn._id==_selected_id, false, _icon_size);
915 }
916
917 rect.top = rect.bottom;
918 }
919 #endif
920 }
921
922 #ifdef _LAZY_ICONEXTRACT
923 void StartMenu::UpdateIcons(/*int idx*/)
924 {
925 UpdateWindow(_hwnd);
926
927 #ifdef _SINGLE_ICONEXTRACT
928
929 //if (idx >= 0)
930 int idx = _scroll_pos;
931
932 for(; idx<(int)_buttons.size(); ++idx) {
933 SMBtnInfo& btn = _buttons[idx];
934
935 if (btn._icon_id==ICID_UNKNOWN && btn._id>0) {
936 StartMenuEntry& sme = _entries[btn._id];
937
938 btn._icon_id = ICID_NONE;
939
940 for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
941 Entry* entry = *it;
942
943 if (entry->_icon_id == ICID_UNKNOWN)
944 entry->_icon_id = entry->safe_extract_icon(ICF_FROM_ICON_SIZE(_icon_size));
945
946 if (entry->_icon_id > ICID_NONE) {
947 btn._icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
948
949 RECT rect;
950
951 GetButtonRect(btn._id, &rect);
952
953 if (rect.bottom > _bottom_max)
954 break;
955
956 WindowCanvas canvas(_hwnd);
957 DrawStartMenuButton(canvas, rect, NULL, btn, btn._id==_selected_id, false, _icon_size);
958
959 //InvalidateRect(_hwnd, &rect, FALSE);
960 //UpdateWindow(_hwnd);
961 //break;
962
963 break;
964 }
965 }
966 }
967 }
968
969 // if (++idx < (int)_buttons.size())
970 // PostMessage(_hwnd, PM_UPDATE_ICONS, idx, 0);
971
972 #else
973
974 int icons_extracted = 0;
975 int icons_updated = 0;
976
977 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
978 ShellDirectory& dir = it->_dir;
979
980 icons_extracted += dir.extract_icons(icon_size);
981 }
982
983 if (icons_extracted) {
984 for(ShellEntryMap::iterator it1=_entries.begin(); it1!=_entries.end(); ++it1) {
985 StartMenuEntry& sme = it1->second;
986
987 if (!sme._hIcon) {
988 sme._hIcon = (HICON)-1;
989
990 for(ShellEntrySet::const_iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
991 const Entry* sm_entry = *it2;
992
993 if (sm_entry->_hIcon) {
994 sme._hIcon = sm_entry->_hIcon;
995 break;
996 }
997 }
998 }
999 }
1000
1001 for(SMBtnVector::iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
1002 SMBtnInfo& info = *it;
1003
1004 if (info._id>0 && !info._hIcon) {
1005 info._hIcon = _entries[info._id]._hIcon;
1006 ++icons_updated;
1007 }
1008 }
1009 }
1010
1011 if (icons_updated) {
1012 InvalidateRect(_hwnd, NULL, FALSE);
1013 UpdateWindow(_hwnd);
1014 }
1015 #endif
1016 }
1017 #endif
1018
1019
1020 // resize child button controls to accomodate for new window size
1021 void StartMenu::ResizeButtons(int cx)
1022 {
1023 HDWP hdwp = BeginDeferWindowPos(10);
1024
1025 for(HWND ctrl=GetWindow(_hwnd,GW_CHILD); ctrl; ctrl=GetNextWindow(ctrl,GW_HWNDNEXT)) {
1026 ClientRect rt(ctrl);
1027
1028 if (rt.right != cx) {
1029 int height = rt.bottom - rt.top;
1030
1031 // special handling for separator controls
1032 if (!height && (GetWindowStyle(ctrl)&SS_TYPEMASK)==SS_ETCHEDHORZ)
1033 height = 2;
1034
1035 hdwp = DeferWindowPos(hdwp, ctrl, 0, 0, 0, cx, height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
1036 }
1037 }
1038
1039 EndDeferWindowPos(hdwp);
1040 }
1041
1042
1043 int StartMenu::Command(int id, int code)
1044 {
1045 #ifndef _LIGHT_STARTMENU
1046 switch(id) {
1047 case IDCANCEL:
1048 CloseStartMenu(id);
1049 break;
1050
1051 default: {
1052 #endif
1053 ShellEntryMap::iterator found = _entries.find(id);
1054
1055 if (found != _entries.end()) {
1056 ActivateEntry(id, found->second._entries);
1057 return 0;
1058 }
1059
1060 return super::Command(id, code);
1061 #ifndef _LIGHT_STARTMENU
1062 }
1063 }
1064
1065 return 0;
1066 #endif
1067 }
1068
1069
1070 ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, Entry* entry)
1071 {
1072 // search for an already existing subdirectory entry with the same name
1073 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1074 {
1075 for(ShellEntryMap::iterator it=_entries.begin(); it!=_entries.end(); ++it) {
1076 StartMenuEntry& sme = it->second;
1077
1078 if (!_tcsicmp(sme._title, title)) ///@todo speed up by using a map indexed by name
1079 {
1080 for(ShellEntrySet::iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
1081 if ((*it2)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1082 // merge the new shell entry with the existing of the same name
1083 sme._entries.insert(entry);
1084
1085 return it;
1086 }
1087 }
1088 }
1089 }
1090 }
1091
1092 ShellEntryMap::iterator sme = AddEntry(title, icon_id);
1093
1094 sme->second._entries.insert(entry);
1095
1096 return sme;
1097 }
1098
1099 ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, int id)
1100 {
1101 if (id == -1)
1102 id = ++_next_id;
1103
1104 StartMenuEntry sme;
1105
1106 sme._title = title;
1107 sme._icon_id = icon_id;
1108
1109 ShellEntryMap::iterator it = _entries.insert(make_pair(id, sme)).first;
1110
1111 return it;
1112 }
1113
1114 ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, ShellEntry* entry)
1115 {
1116 ICON_ID icon_id;
1117
1118 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1119 icon_id = ICID_APPS;
1120 else
1121 icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
1122
1123 return AddEntry(folder.get_name(entry->_pidl), icon_id, entry);
1124 }
1125
1126 ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, Entry* entry)
1127 {
1128 ICON_ID icon_id;
1129
1130 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1131 icon_id = ICID_APPS;
1132 else
1133 icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
1134
1135 return AddEntry(entry->_display_name, icon_id, entry);
1136 }
1137
1138
1139 void StartMenu::AddButton(LPCTSTR title, ICON_ID icon_id, bool hasSubmenu, int id, bool enabled)
1140 {
1141 #ifdef _LIGHT_STARTMENU
1142 _buttons.push_back(SMBtnInfo(title, icon_id, id, hasSubmenu, enabled));
1143 #else
1144 DWORD style = enabled? WS_VISIBLE|WS_CHILD|BS_OWNERDRAW: WS_VISIBLE|WS_CHILD|BS_OWNERDRAW|WS_DISABLED;
1145
1146 WindowRect rect(_hwnd);
1147 ClientRect clnt(_hwnd);
1148
1149 // increase window height to make room for the new button
1150 rect.top -= STARTMENU_LINE_HEIGHT(icon_size);
1151
1152 // move down if we are too high now
1153 if (rect.top < 0) {
1154 rect.top += STARTMENU_LINE_HEIGHT(icon_size);
1155 rect.bottom += STARTMENU_LINE_HEIGHT(icon_size);
1156 }
1157
1158 WindowCanvas canvas(_hwnd);
1159 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
1160
1161 // widen window, if it is too small
1162 int text_width = GetStartMenuBtnTextWidth(canvas, title, _hwnd) + icon_size + 10/*placeholder*/ + 16/*arrow*/;
1163
1164 int cx = clnt.right - _border_left;
1165 if (text_width > cx)
1166 rect.right += text_width-cx;
1167
1168 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
1169
1170 StartMenuCtrl(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left,
1171 title, id, g_Globals._icon_cache.get_icon(icon_id)._hIcon, hasSubmenu, style);
1172 #endif
1173 }
1174
1175 void StartMenu::AddSeparator()
1176 {
1177 #ifdef _LIGHT_STARTMENU
1178 _buttons.push_back(SMBtnInfo(NULL, ICID_NONE, -1, false));
1179 #else
1180 WindowRect rect(_hwnd);
1181 ClientRect clnt(_hwnd);
1182
1183 // increase window height to make room for the new separator
1184 rect.top -= STARTMENU_SEP_HEIGHT(icon_size);
1185
1186 // move down if we are too high now
1187 if (rect.top < 0) {
1188 rect.top += STARTMENU_LINE_HEIGHT(icon_size);
1189 rect.bottom += STARTMENU_LINE_HEIGHT(icon_size);
1190 }
1191
1192 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
1193
1194 StartMenuSeparator(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left);
1195 #endif
1196 }
1197
1198
1199 bool StartMenu::CloseOtherSubmenus(int id)
1200 {
1201 if (_submenu) {
1202 if (IsWindow(_submenu)) {
1203 if (_submenu_id == id)
1204 return false;
1205 else {
1206 _submenu_id = 0;
1207 DestroyWindow(_submenu);
1208 // _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
1209 }
1210 }
1211
1212 _submenu = 0;
1213 }
1214
1215 return true;
1216 }
1217
1218
1219 void StartMenu::CreateSubmenu(int id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
1220 {
1221 CreateSubmenu(id, StartMenuFolders(), title, creator, info);
1222 }
1223
1224 bool StartMenu::CreateSubmenu(int id, int folder_id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
1225 {
1226 try {
1227 SpecialFolderPath folder(folder_id, _hwnd);
1228
1229 StartMenuFolders new_folders;
1230 new_folders.push_back(folder);
1231
1232 CreateSubmenu(id, new_folders, title, creator, info);
1233
1234 return true;
1235 } catch(COMException&) {
1236 // ignore Exception and don't display anything
1237 CloseOtherSubmenus(id);
1238 _buttons[GetSelectionIndex()]._enabled = false; // disable entries for non-existing folders
1239 return false;
1240 }
1241 }
1242
1243 bool StartMenu::CreateSubmenu(int id, int folder_id1, int folder_id2, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
1244 {
1245 StartMenuFolders new_folders;
1246
1247 try {
1248 new_folders.push_back(SpecialFolderPath(folder_id1, _hwnd));
1249 } catch(COMException&) {
1250 }
1251
1252 try {
1253 new_folders.push_back(SpecialFolderPath(folder_id2, _hwnd));
1254 } catch(COMException&) {
1255 }
1256
1257 if (!new_folders.empty()) {
1258 CreateSubmenu(id, new_folders, title, creator, info);
1259 return true;
1260 } else {
1261 CloseOtherSubmenus(id);
1262 _buttons[GetSelectionIndex()]._enabled = false; // disable entries for non-existing folders
1263 return false;
1264 }
1265 }
1266
1267 void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
1268 {
1269 // Only open one submenu at a time.
1270 if (!CloseOtherSubmenus(id))
1271 return;
1272
1273 RECT rect;
1274 int x, y;
1275
1276 if (GetButtonRect(id, &rect)) {
1277 ClientToScreen(_hwnd, &rect);
1278
1279 x = rect.right; // Submenus should overlap their parent a bit.
1280 const int icon_size = _icon_size;
1281 y = rect.top+STARTMENU_LINE_HEIGHT(icon_size) +_border_top/*own border*/ -STARTMENU_TOP_BTN_SPACE/*border of new submenu*/;
1282 } else {
1283 WindowRect pos(_hwnd);
1284
1285 x = pos.right;
1286 y = pos.top;
1287 }
1288
1289 _submenu_id = id;
1290 _submenu = StartMenu::Create(x, y, new_folders, _hwnd, title, creator, info, _create_info._filter);
1291 }
1292
1293
1294 void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries)
1295 {
1296 StartMenuFolders new_folders;
1297 String title;
1298
1299 for(ShellEntrySet::const_iterator it=entries.begin(); it!=entries.end(); ++it) {
1300 Entry* entry = const_cast<Entry*>(*it);
1301
1302 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1303
1304 ///@todo If the user explicitly clicked on a submenu, display this folder as floating start menu.
1305
1306 if (entry->_etype == ET_SHELL)
1307 new_folders.push_back(entry->create_absolute_pidl());
1308 else {
1309 TCHAR path[MAX_PATH];
1310
1311 if (entry->get_path(path, COUNTOF(path)))
1312 new_folders.push_back(path);
1313 }
1314
1315 if (title.empty())
1316 title = entry->_display_name;
1317 } else {
1318 // The entry is no subdirectory, so there can only be one shell entry.
1319 assert(entries.size()==1);
1320
1321 HWND hparent = GetParent(_hwnd);
1322 ShellPath shell_path = entry->create_absolute_pidl();
1323
1324 // close start menus when launching the selected entry
1325 CloseStartMenu(id);
1326
1327 ///@todo launch in the background; specify correct HWND for error message box titles
1328 SHELLEXECUTEINFO shexinfo;
1329
1330 shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
1331 shexinfo.fMask = SEE_MASK_IDLIST; // SEE_MASK_INVOKEIDLIST is also possible.
1332 shexinfo.hwnd = hparent;
1333 shexinfo.lpVerb = NULL;
1334 shexinfo.lpFile = NULL;
1335 shexinfo.lpParameters = NULL;
1336 shexinfo.lpDirectory = NULL;
1337 shexinfo.nShow = SW_SHOWNORMAL;
1338
1339 shexinfo.lpIDList = &*shell_path;
1340
1341 // add PIDL to the recent file list
1342 SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList);
1343
1344 if (!ShellExecuteEx(&shexinfo))
1345 display_error(hparent, GetLastError());
1346
1347 // we may have deleted 'this' - ensure we leave the loop and function
1348 return;
1349 }
1350 }
1351
1352 if (!new_folders.empty()) {
1353 // Only open one submenu at a time.
1354 if (!CloseOtherSubmenus(id))
1355 return;
1356
1357 CreateSubmenu(id, new_folders, title);
1358 }
1359 }
1360
1361
1362 /// close all windows of the start menu popup
1363 void StartMenu::CloseStartMenu(int id)
1364 {
1365 if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) { // don't automatically close floating menus
1366 if (!SendParent(PM_STARTENTRY_LAUNCHED, id, (LPARAM)_hwnd))
1367 DestroyWindow(_hwnd);
1368 } else if (_submenu) // instead close submenus of floating parent menus
1369 CloseSubmenus();
1370 }
1371
1372
1373 int GetStartMenuBtnTextWidth(HDC hdc, LPCTSTR title, HWND hwnd)
1374 {
1375 RECT rect = {0, 0, 0, 0};
1376 DrawText(hdc, title, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
1377
1378 return rect.right-rect.left;
1379 }
1380
1381 #ifdef _LIGHT_STARTMENU
1382 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, const SMBtnInfo& btn, bool has_focus, bool pushed, int icon_size)
1383 #else
1384 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, HICON hIcon,
1385 bool hasSubmenu, bool enabled, bool has_focus, bool pushed, int icon_size);
1386 #endif
1387 {
1388 UINT style = DFCS_BUTTONPUSH;
1389
1390 if (!btn._enabled)
1391 style |= DFCS_INACTIVE;
1392
1393 POINT iconPos = {rect.left+2, (rect.top+rect.bottom-icon_size)/2};
1394 RECT textRect = {rect.left+icon_size+4, rect.top+2, rect.right-4, rect.bottom-4};
1395
1396 if (pushed) {
1397 style |= DFCS_PUSHED;
1398 ++iconPos.x; ++iconPos.y;
1399 ++textRect.left; ++textRect.top;
1400 ++textRect.right; ++textRect.bottom;
1401 }
1402
1403 int bk_color_idx = COLOR_BTNFACE;
1404 int text_color_idx = COLOR_BTNTEXT;
1405
1406 if (has_focus) {
1407 bk_color_idx = COLOR_HIGHLIGHT;
1408 text_color_idx = COLOR_HIGHLIGHTTEXT;
1409 }
1410
1411 COLORREF bk_color = GetSysColor(bk_color_idx);
1412 HBRUSH bk_brush = GetSysColorBrush(bk_color_idx);
1413
1414 if (title)
1415 FillRect(hdc, &rect, bk_brush);
1416
1417 if (btn._icon_id > ICID_NONE)
1418 g_Globals._icon_cache.get_icon(btn._icon_id).draw(hdc, iconPos.x, iconPos.y, icon_size, icon_size, bk_color, bk_brush/*, icon_size*/);
1419
1420 // draw submenu arrow at the right
1421 if (btn._hasSubmenu) {
1422 ResIconEx arrowIcon(IDI_ARROW, icon_size, icon_size);
1423 ResIconEx selArrowIcon(IDI_ARROW_SELECTED, icon_size, icon_size);
1424
1425 DrawIconEx(hdc, rect.right-icon_size, iconPos.y,
1426 has_focus? selArrowIcon: arrowIcon,
1427 icon_size, icon_size, 0, bk_brush, DI_NORMAL);
1428 }
1429
1430 if (title) {
1431 BkMode bk_mode(hdc, TRANSPARENT);
1432
1433 if (!btn._enabled) // dis->itemState & (ODS_DISABLED|ODS_GRAYED)
1434 DrawGrayText(hdc, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
1435 else {
1436 TextColor lcColor(hdc, GetSysColor(text_color_idx));
1437 DrawText(hdc, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
1438 }
1439 }
1440 }
1441
1442
1443 #ifdef _LIGHT_STARTMENU
1444
1445 void StartMenu::ResizeToButtons()
1446 {
1447 WindowRect rect(_hwnd);
1448
1449 WindowCanvas canvas(_hwnd);
1450 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
1451
1452 const int icon_size = _icon_size;
1453
1454 int max_width = STARTMENU_WIDTH_MIN;
1455 int height = 0;
1456
1457 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
1458 int w = GetStartMenuBtnTextWidth(canvas, it->_title, _hwnd);
1459
1460 if (w > max_width)
1461 max_width = w;
1462
1463 if (it->_id == -1)
1464 height += STARTMENU_SEP_HEIGHT(icon_size);
1465 else
1466 height += STARTMENU_LINE_HEIGHT(icon_size);
1467 }
1468
1469 // calculate new window size
1470 int text_width = max_width + icon_size + 10/*placeholder*/ + 16/*arrow*/;
1471
1472 RECT rt_hgt = {rect.left, rect.bottom-_border_top-height, rect.left+_border_left+text_width, rect.bottom};
1473 AdjustWindowRectEx(&rt_hgt, GetWindowStyle(_hwnd), FALSE, GetWindowExStyle(_hwnd));
1474
1475 // ignore movement, only look at the size change
1476 rect.right = rect.left + (rt_hgt.right-rt_hgt.left);
1477 rect.top = rect.bottom - (rt_hgt.bottom-rt_hgt.top);
1478
1479 // move down if we are too high
1480 if (rect.top < 0) {
1481 int dy = -rect.top;
1482 rect.top += dy;
1483 rect.bottom += dy;
1484 }
1485
1486 // enable scroll mode for long start menus, which span more than the whole screen height
1487 int cyscreen = GetSystemMetrics(SM_CYSCREEN);
1488 int bottom_max = 0;
1489
1490 if (rect.bottom > cyscreen) {
1491 _arrow_btns = true;
1492
1493 _invisible_lines = (rect.bottom-cyscreen+(STARTMENU_LINE_HEIGHT(icon_size)+1))/STARTMENU_LINE_HEIGHT(icon_size)+1;
1494 rect.bottom -= _invisible_lines * STARTMENU_LINE_HEIGHT(icon_size);
1495
1496 bottom_max = rect.bottom;
1497
1498 if (_floating_btn)
1499 rect.bottom += 6; // lower scroll arrow
1500 else {
1501 _border_top += 6; // upper scroll arrow
1502 rect.bottom += 2*6; // upper+lower scroll arrow
1503 }
1504 }
1505
1506 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
1507
1508 if (bottom_max) {
1509 POINT pt = {0, bottom_max};
1510
1511 ScreenToClient(_hwnd, &pt);
1512
1513 _bottom_max = pt.y;
1514 }
1515 }
1516
1517 #else // _LIGHT_STARTMENU
1518
1519 LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1520 {
1521 switch(nmsg) {
1522 case WM_MOUSEMOVE:
1523 // automatically set the focus to startmenu entries when moving the mouse over them
1524 if (GetFocus()!=_hwnd && !(GetWindowStyle(_hwnd)&WS_DISABLED))
1525 SetFocus(_hwnd);
1526 break;
1527
1528 case WM_SETFOCUS:
1529 PostParent(PM_STARTENTRY_FOCUSED, _hasSubmenu, (LPARAM)_hwnd);
1530 goto def;
1531
1532 case WM_CANCELMODE:
1533 // route WM_CANCELMODE to the startmenu window
1534 return SendParent(nmsg, wparam, lparam);
1535
1536 default: def:
1537 return super::WndProc(nmsg, wparam, lparam);
1538 }
1539
1540 return 0;
1541 }
1542
1543 void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis)
1544 {
1545 TCHAR title[BUFFER_LEN];
1546
1547 GetWindowText(_hwnd, title, BUFFER_LEN);
1548
1549 DrawStartMenuButton(dis->hDC, dis->rcItem, title, _hIcon,
1550 _hasSubmenu,
1551 !(dis->itemState & ODS_DISABLED),
1552 dis->itemState&ODS_FOCUS? true: false,
1553 dis->itemState&ODS_SELECTED? true: false);
1554 }
1555
1556 #endif
1557
1558
1559 StartMenuRoot::StartMenuRoot(HWND hwnd, const StartMenuRootCreateInfo& info)
1560 : super(hwnd, info._icon_size)
1561 {
1562 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1563 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCOMMONGROUPS))
1564 #endif
1565 try {
1566 // insert directory "All Users\Start Menu"
1567 ShellDirectory cmn_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
1568 _dirs.push_back(StartMenuDirectory(cmn_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_COMMON_PROGRAMS, _hwnd)));
1569 } catch(COMException&) {
1570 // ignore exception and don't show additional shortcuts
1571 }
1572
1573 try {
1574 // insert directory "<user name>\Start Menu"
1575 ShellDirectory usr_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
1576 _dirs.push_back(StartMenuDirectory(usr_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_PROGRAMS, _hwnd)));
1577 } catch(COMException&) {
1578 // ignore exception and don't show additional shortcuts
1579 }
1580
1581 ReadLogoSize();
1582 }
1583
1584 void StartMenuRoot::ReadLogoSize()
1585 {
1586 // read size of logo bitmap
1587 BITMAP bmp_hdr;
1588 GetObject(ResBitmap(GetLogoResId()), sizeof(BITMAP), &bmp_hdr);
1589 _logo_size.cx = bmp_hdr.bmWidth;
1590 _logo_size.cy = bmp_hdr.bmHeight;
1591
1592 // cache logo width
1593 _border_left = _logo_size.cx + 1;
1594 }
1595
1596
1597 static void CalculateStartPos(HWND hwndOwner, RECT& rect, int icon_size)
1598 {
1599 WindowRect pos(hwndOwner);
1600
1601 rect.left = pos.left;
1602 rect.top = pos.top-STARTMENU_LINE_HEIGHT(icon_size)-4;
1603 rect.right = pos.left+STARTMENU_WIDTH_MIN;
1604 rect.bottom = pos.top;
1605
1606 #ifndef _LIGHT_STARTMENU
1607 rect.top += STARTMENU_LINE_HEIGHT(icon_size);
1608 #endif
1609
1610 AdjustWindowRectEx(&rect, WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE, FALSE, 0);
1611 }
1612
1613 HWND StartMenuRoot::Create(HWND hwndOwner, int icon_size)
1614 {
1615 RECT rect;
1616
1617 CalculateStartPos(hwndOwner, rect, icon_size);
1618
1619 StartMenuRootCreateInfo create_info;
1620
1621 create_info._icon_size = icon_size;
1622
1623 return Window::Create(WINDOW_CREATOR_INFO(StartMenuRoot,StartMenuRootCreateInfo), &create_info, 0, GetWndClasss(), TITLE_STARTMENU,
1624 WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,
1625 rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndOwner);
1626 }
1627
1628
1629 void StartMenuRoot::TrackStartmenu()
1630 {
1631 MSG msg;
1632 HWND hwnd = _hwnd;
1633
1634 #ifdef _LIGHT_STARTMENU
1635 _selected_id = -1;
1636 #endif
1637
1638 #ifdef _LIGHT_STARTMENU
1639 // recalculate start menu root position
1640 RECT rect;
1641
1642 CalculateStartPos(GetParent(hwnd), rect, _icon_size);
1643
1644 SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);
1645
1646 ResizeToButtons();
1647 #endif
1648
1649 // show previously hidden start menu
1650 ShowWindow(hwnd, SW_SHOW);
1651 SetForegroundWindow(hwnd);
1652
1653 while(IsWindow(hwnd) && IsWindowVisible(hwnd)) {
1654 if (!GetMessage(&msg, 0, 0, 0)) {
1655 PostQuitMessage(msg.wParam);
1656 break;
1657 }
1658
1659 // Check for a mouse click on any window, that is not part of the start menu
1660 if (msg.message==WM_LBUTTONDOWN || msg.message==WM_MBUTTONDOWN || msg.message==WM_RBUTTONDOWN) {
1661 StartMenu* menu_wnd = NULL;
1662
1663 for(HWND hwnd=msg.hwnd; hwnd; hwnd=GetParent(hwnd)) {
1664 menu_wnd = WINDOW_DYNAMIC_CAST(StartMenu, hwnd);
1665
1666 if (menu_wnd)
1667 break;
1668 }
1669
1670 if (!menu_wnd) {
1671 CloseStartMenu();
1672 break;
1673 }
1674 }
1675
1676 try {
1677 if (pretranslate_msg(&msg))
1678 continue;
1679
1680 if (dispatch_dialog_msg(&msg))
1681 continue;
1682
1683 TranslateMessage(&msg);
1684
1685 try {
1686 DispatchMessage(&msg);
1687 } catch(COMException& e) {
1688 HandleException(e, _hwnd);
1689 }
1690 } catch(COMException& e) {
1691 HandleException(e, _hwnd);
1692 }
1693 }
1694 }
1695
1696
1697 LRESULT StartMenuRoot::Init(LPCREATESTRUCT pcs)
1698 {
1699 // add buttons for entries in _entries
1700 if (super::Init(pcs))
1701 return 1;
1702
1703 AddSeparator();
1704
1705
1706 #ifdef __MINGW32__
1707 HKEY hkey, hkeyAdv;
1708 DWORD value, len;
1709
1710 if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), &hkey))
1711 hkey = 0;
1712
1713 if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hkeyAdv))
1714 hkeyAdv = 0;
1715
1716 #define IS_VALUE_ZERO(hk, name) \
1717 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || !value))
1718
1719 #define IS_VALUE_NOT_ZERO(hk, name) \
1720 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || value>0))
1721 #endif
1722
1723
1724 // insert hard coded start entries
1725 AddButton(ResString(IDS_PROGRAMS), ICID_APPS, true, IDC_PROGRAMS);
1726
1727 AddButton(ResString(IDS_DOCUMENTS), ICID_DOCUMENTS, true, IDC_DOCUMENTS);
1728
1729 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1730 if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
1731 #else
1732 if (IS_VALUE_ZERO(hkey, _T("NoRecentDocsMenu")))
1733 #endif
1734 AddButton(ResString(IDS_RECENT), ICID_RECENT, true, IDC_RECENT);
1735
1736 AddButton(ResString(IDS_FAVORITES), ICID_FAVORITES, true, IDC_FAVORITES);
1737
1738 AddButton(ResString(IDS_SETTINGS), ICID_CONFIG, true, IDC_SETTINGS);
1739
1740 AddButton(ResString(IDS_BROWSE), ICID_FOLDER, true, IDC_BROWSE);
1741
1742 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1743 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOFIND))
1744 #else
1745 if (IS_VALUE_ZERO(hkey, _T("NoFind")))
1746 #endif
1747 AddButton(ResString(IDS_SEARCH), ICID_SEARCH, true, IDC_SEARCH);
1748
1749 AddButton(ResString(IDS_START_HELP), ICID_INFO, false, IDC_START_HELP);
1750
1751 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1752 if (!g_Globals._SHRestricted || !SHRestricted(REST_NORUN))
1753 #else
1754 if (IS_VALUE_ZERO(hkey, _T("NoRun")))
1755 #endif
1756 AddButton(ResString(IDS_LAUNCH), ICID_ACTION, false, IDC_LAUNCH);
1757
1758
1759 AddSeparator();
1760
1761
1762 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1763 if (!g_Globals._SHRestricted || SHRestricted(REST_STARTMENULOGOFF) != 1)
1764 #else
1765 if (IS_VALUE_NOT_ZERO(hkeyAdv, _T("StartMenuLogoff")))
1766 #endif
1767 AddButton(ResString(IDS_LOGOFF), ICID_LOGOFF, false, IDC_LOGOFF);
1768
1769 #ifdef _ROS_
1770 AddButton(ResString(IDS_RESTART), ICID_RESTART, false, IDC_RESTART);
1771 #endif
1772
1773 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1774 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCLOSE))
1775 #else
1776 if (IS_VALUE_ZERO(hkey, _T("NoClose")))
1777 #endif
1778 AddButton(ResString(IDS_SHUTDOWN), ICID_SHUTDOWN, false, IDC_SHUTDOWN);
1779
1780 #ifndef _ROS_
1781 AddButton(ResString(IDS_TERMINATE), ICID_LOGOFF, false, IDC_TERMINATE);
1782 #endif
1783
1784
1785 #ifdef __MINGW32__
1786 RegCloseKey(hkeyAdv);
1787 RegCloseKey(hkey);
1788 #endif
1789
1790
1791 #ifdef _LIGHT_STARTMENU
1792 // set the window size to fit all buttons
1793 ResizeToButtons();
1794 #endif
1795
1796 return 0;
1797 }
1798
1799
1800 void StartMenuRoot::AddEntries()
1801 {
1802 super::AddEntries();
1803
1804 AddButton(ResString(IDS_EXPLORE), ICID_EXPLORER, false, IDC_EXPLORE);
1805 }
1806
1807
1808 LRESULT StartMenuRoot::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1809 {
1810 switch(nmsg) {
1811 case WM_PAINT: {
1812 PaintCanvas canvas(_hwnd);
1813 Paint(canvas);
1814 break;}
1815
1816 case WM_DISPLAYCHANGE:
1817 // re-evaluate logo size using the correct color depth
1818 ReadLogoSize();
1819 break;
1820
1821 default:
1822 return super::WndProc(nmsg, wparam, lparam);
1823 }
1824
1825 return 0;
1826 }
1827
1828 void StartMenuRoot::Paint(PaintCanvas& canvas)
1829 {
1830 MemCanvas mem_dc;
1831 ResBitmap bmp(GetLogoResId());
1832 BitmapSelection sel(mem_dc, bmp);
1833
1834 ClientRect clnt(_hwnd);
1835 int h = min(_logo_size.cy, clnt.bottom);
1836
1837 RECT rect = {0, 0, _logo_size.cx, clnt.bottom-h};
1838 HBRUSH hbr = CreateSolidBrush(GetPixel(mem_dc, 0, 0));
1839 FillRect(canvas, &rect, hbr);
1840 DeleteObject(hbr);
1841
1842 PatBlt(canvas, _logo_size.cx, 0, 1, clnt.bottom, WHITENESS);
1843
1844 BitBlt(canvas, 0, clnt.bottom-h, _logo_size.cx, h, mem_dc, 0, ( h<_logo_size.cy ? _logo_size.cy-h : 0) , SRCCOPY);
1845
1846 super::Paint(canvas);
1847 }
1848
1849 UINT StartMenuRoot::GetLogoResId()
1850 {
1851 WindowCanvas dc(_hwnd);
1852
1853 int clr_bits = GetDeviceCaps(dc, BITSPIXEL);
1854
1855 if (clr_bits > 8)
1856 return IDB_LOGOV;
1857 else if (clr_bits > 4)
1858 return IDB_LOGOV256;
1859 else
1860 return IDB_LOGOV16;
1861 }
1862
1863
1864 void StartMenuRoot::CloseStartMenu(int id)
1865 {
1866 if (_submenu)
1867 CloseSubmenus();
1868
1869 ShowWindow(_hwnd, SW_HIDE);
1870 }
1871
1872 void StartMenuRoot::ProcessKey(int vk)
1873 {
1874 switch(vk) {
1875 case VK_LEFT:
1876 if (_submenu)
1877 CloseOtherSubmenus();
1878 // don't close start menu root
1879 break;
1880
1881 default:
1882 super::ProcessKey(vk);
1883 }
1884 }
1885
1886
1887 int StartMenuHandler::Command(int id, int code)
1888 {
1889 switch(id) {
1890
1891 // start menu root
1892
1893 case IDC_PROGRAMS:
1894 CreateSubmenu(id, CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS, ResString(IDS_PROGRAMS));
1895 break;
1896
1897 case IDC_EXPLORE:
1898 CloseStartMenu(id);
1899 explorer_show_frame(SW_SHOWNORMAL);
1900 break;
1901
1902 case IDC_LAUNCH:
1903 CloseStartMenu(id);
1904 ShowLaunchDialog(g_Globals._hwndDesktopBar);
1905 break;
1906
1907 case IDC_DOCUMENTS:
1908 CreateSubmenu(id, CSIDL_PERSONAL, ResString(IDS_DOCUMENTS));
1909 break;
1910
1911 case IDC_RECENT:
1912 CreateSubmenu(id, CSIDL_RECENT, ResString(IDS_RECENT), STARTMENU_CREATOR(RecentStartMenu));
1913 break;
1914
1915 case IDC_FAVORITES:
1916 #ifndef _SHELL32_FAVORITES
1917 CreateSubmenu(id, ResString(IDS_FAVORITES), STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(g_Globals._favorites));
1918 #else
1919 CreateSubmenu(id, CSIDL_COMMON_FAVORITES, CSIDL_FAVORITES, ResString(IDS_FAVORITES));
1920 #endif
1921 break;
1922
1923 case IDC_BROWSE:
1924 CreateSubmenu(id, ResString(IDS_BROWSE), STARTMENU_CREATOR(BrowseMenu));
1925 break;
1926
1927 case IDC_SETTINGS:
1928 CreateSubmenu(id, ResString(IDS_SETTINGS), STARTMENU_CREATOR(SettingsMenu));
1929 break;
1930
1931 case IDC_SEARCH:
1932 CreateSubmenu(id, ResString(IDS_SEARCH), STARTMENU_CREATOR(SearchMenu));
1933 break;
1934
1935 case IDC_START_HELP:
1936 CloseStartMenu(id);
1937 MessageBox(g_Globals._hwndDesktopBar, TEXT("Help not yet implemented"), ResString(IDS_TITLE), MB_OK);
1938 break;
1939
1940 case IDC_LOGOFF:
1941 CloseStartMenu(id);
1942 ShowLogoffDialog(g_Globals._hwndDesktopBar);
1943 break;
1944
1945 #ifndef _ROS_
1946 case IDC_TERMINATE:
1947 DestroyWindow(GetParent(_hwnd));
1948 break;
1949 #endif
1950
1951 case IDC_SHUTDOWN:
1952 CloseStartMenu(id);
1953 ShowExitWindowsDialog(g_Globals._hwndDesktop);
1954 break;
1955
1956 case IDC_RESTART:
1957 CloseStartMenu(id);
1958 ShowRestartDialog(g_Globals._hwndDesktop, EWX_REBOOT);
1959 /* An alternative way to do restart without shell32 help */
1960 //launch_file(_hwnd, TEXT("shutdown.exe"), SW_HIDE, TEXT("-r"));
1961 break;
1962
1963 // settings menu
1964
1965 case ID_DESKTOPBAR_SETTINGS:
1966 CloseStartMenu(id);
1967 ExplorerPropertySheet(g_Globals._hwndDesktopBar);
1968 break;
1969
1970 case IDC_CONTROL_PANEL: {
1971 CloseStartMenu(id);
1972 #ifndef ROSSHELL
1973 #ifndef _NO_MDI
1974 XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
1975 bool mdi = XMLBool(explorer_options, "mdi", true);
1976
1977 if (mdi)
1978 MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
1979 else
1980 #endif
1981 SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
1982 #else
1983 launch_file(_hwnd, SHELLPATH_CONTROL_PANEL);
1984 #endif
1985 break;}
1986
1987 case IDC_SETTINGS_MENU:
1988 CreateSubmenu(id, CSIDL_CONTROLS, ResString(IDS_SETTINGS_MENU));
1989 break;
1990
1991 case IDC_PRINTERS: {
1992 CloseStartMenu(id);
1993
1994 #ifndef ROSSHELL
1995 #ifndef _NO_MDI
1996 XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
1997 bool mdi = XMLBool(explorer_options, "mdi", true);
1998
1999 if (mdi)
2000 MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
2001 else
2002 #endif
2003 SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
2004 #else
2005 launch_file(_hwnd, SHELLPATH_PRINTERS);
2006 #endif
2007 break;}
2008
2009 #if 0 ///@todo use printer start menu folder per default and allow opening "printers" cabinet window using the context menu
2010 case IDC_PRINTERS_MENU:
2011 CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS));
2012 /* StartMenuFolders new_folders;
2013
2014 try {
2015 new_folders.push_back(ShellPath(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")));
2016 } catch(COMException&) {
2017 }
2018
2019 CreateSubmenu(id, new_folders, ResString(IDS_PRINTERS));*/
2020 break;
2021 #endif
2022
2023 case IDC_ADMIN:
2024 #ifndef ROSSHELL
2025 CreateSubmenu(id, CSIDL_COMMON_ADMINTOOLS, CSIDL_ADMINTOOLS, ResString(IDS_ADMIN));
2026 //CloseStartMenu(id);
2027 //MainFrame::Create(SpecialFolderPath(CSIDL_COMMON_ADMINTOOLS, _hwnd), OWM_PIDL);
2028 #else
2029 launch_file(_hwnd, SpecialFolderFSPath(CSIDL_COMMON_ADMINTOOLS, _hwnd));
2030 #endif
2031 break;
2032
2033 case IDC_CONNECTIONS:
2034 #ifndef ROSSHELL
2035 #ifdef _ROS_ // to be removed when RAS will be implemented
2036 MessageBox(0, TEXT("RAS folder not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2037 #else
2038 CreateSubmenu(id, CSIDL_CONNECTIONS, ResString(IDS_CONNECTIONS));
2039 //CloseStartMenu(id);
2040 //MainFrame::Create(SpecialFolderPath(CSIDL_CONNECTIONS, _hwnd), OWM_PIDL);
2041 #endif
2042 #else
2043 launch_file(_hwnd, SHELLPATH_NET_CONNECTIONS);
2044 #endif
2045 break;
2046
2047
2048 // browse menu
2049
2050 case IDC_NETWORK:
2051 #ifdef _ROS_ ///@todo to be removed when network browsing will be implemented in shell namespace
2052 MessageBox(0, TEXT("network not yet implemented"), ResString(IDS_TITLE), MB_OK);
2053 #else
2054 CreateSubmenu(id, CSIDL_NETWORK, ResString(IDS_NETWORK));
2055 #endif
2056 break;
2057
2058 case IDC_DRIVES:
2059 ///@todo exclude removable drives
2060 CreateSubmenu(id, CSIDL_DRIVES, ResString(IDS_DRIVES));
2061 break;
2062
2063
2064 // search menu
2065
2066 case IDC_SEARCH_PROGRAM:
2067 CloseStartMenu(id);
2068 Dialog::DoModal(IDD_SEARCH_PROGRAM, WINDOW_CREATOR(FindProgramDlg));
2069 break;
2070
2071 case IDC_SEARCH_FILES:
2072 CloseStartMenu(id);
2073 ShowSearchDialog();
2074 break;
2075
2076 case IDC_SEARCH_COMPUTER:
2077 CloseStartMenu(id);
2078 ShowSearchComputer();
2079 break;
2080
2081
2082 default:
2083 return super::Command(id, code);
2084 }
2085
2086 return 0;
2087 }
2088
2089
2090 void StartMenuHandler::ShowSearchDialog()
2091 {
2092 #ifndef _ROS_ ///@todo to be removed when SHFindFiles() will be implemented in shell32.dll
2093 static DynamicFct<SHFINDFILES> SHFindFiles(TEXT("SHELL32"), 90);
2094
2095 if (SHFindFiles)
2096 (*SHFindFiles)(NULL, NULL);
2097 else
2098 #endif
2099 MessageBox(0, TEXT("SHFindFiles() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2100 }
2101
2102 void StartMenuHandler::ShowSearchComputer()
2103 {
2104 #ifndef _ROS_ ///@todo to be removed when SHFindComputer() will be implemented in shell32.dll
2105 static DynamicFct<SHFINDCOMPUTER> SHFindComputer(TEXT("SHELL32"), 91);
2106
2107 if (SHFindComputer)
2108 (*SHFindComputer)(NULL, NULL);
2109 else
2110 #endif
2111 MessageBox(0, TEXT("SHFindComputer() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2112 }
2113
2114 void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner)
2115 {
2116 static DynamicFct<RUNFILEDLG> RunFileDlg(TEXT("SHELL32"), 61);
2117
2118 // Show "Run..." dialog
2119 if (RunFileDlg) {
2120 (*RunFileDlg)(hwndOwner, 0, NULL, NULL, NULL, RFF_CALCDIRECTORY);
2121 }
2122 }
2123
2124 void StartMenuHandler::ShowLogoffDialog(HWND hwndOwner)
2125 {
2126 static DynamicFct<LOGOFFWINDOWSDIALOG> LogoffWindowsDialog(TEXT("SHELL32"), 54);
2127 // static DynamicFct<RESTARTWINDOWSDLG> RestartDialog(TEXT("SHELL32"), 59);
2128
2129 if (LogoffWindowsDialog)
2130 (*LogoffWindowsDialog)(0);
2131 /* The RestartDialog function prompts about some system setting change. This is not what we want to display here.
2132 else if (RestartDialog)
2133 return (*RestartDialog)(hwndOwner, (LPWSTR)L"You selected <Log Off>.\n\n", EWX_LOGOFF) == 1; ///@todo ANSI string conversion if needed
2134 */
2135 else
2136 MessageBox(hwndOwner, TEXT("LogoffWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2137 }
2138
2139 void ShowExitWindowsDialog(HWND hwndOwner)
2140 {
2141 static DynamicFct<EXITWINDOWSDLG> ExitWindowsDialog(TEXT("SHELL32"), 60);
2142
2143 if (ExitWindowsDialog)
2144 (*ExitWindowsDialog)(hwndOwner);
2145 else
2146 MessageBox(hwndOwner, TEXT("ExitWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2147 }
2148
2149 void StartMenuHandler::ShowRestartDialog(HWND hwndOwner, UINT flags)
2150 {
2151 static DynamicFct<RESTARTWINDOWSDLG> RestartDlg(TEXT("SHELL32"), 59);
2152
2153 if (RestartDlg)
2154 (*RestartDlg)(hwndOwner, (LPWSTR)L"You selected restart.\n\n", flags);
2155 else
2156 MessageBox(hwndOwner, TEXT("RestartDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2157 }
2158
2159 void SettingsMenu::AddEntries()
2160 {
2161 super::AddEntries();
2162
2163 #if defined(ROSSHELL) || defined(_ROS_) // _ROS_ to be removed when printer/network will be implemented
2164 //TODO AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, false, IDC_PRINTERS_MENU);
2165 AddButton(ResString(IDS_CONNECTIONS), ICID_NETWORK, false, IDC_CONNECTIONS);
2166 #else
2167 //TODO AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, true, IDC_PRINTERS_MENU);
2168 AddButton(ResString(IDS_CONNECTIONS), ICID_NETCONNS, false, IDC_CONNECTIONS);
2169 #endif
2170 AddButton(ResString(IDS_ADMIN), ICID_ADMIN, true, IDC_ADMIN);
2171
2172 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
2173 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
2174 #endif
2175 AddButton(ResString(IDS_SETTINGS_MENU), ICID_CONFIG, true, IDC_SETTINGS_MENU);
2176
2177 AddButton(ResString(IDS_DESKTOPBAR_SETTINGS), ICID_DESKSETTING, false, ID_DESKTOPBAR_SETTINGS);
2178
2179 AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, false, IDC_PRINTERS);
2180
2181 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
2182 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
2183 #endif
2184 AddButton(ResString(IDS_CONTROL_PANEL), ICID_CONTROLPAN, false, IDC_CONTROL_PANEL);
2185 }
2186
2187 void BrowseMenu::AddEntries()
2188 {
2189 super::AddEntries();
2190
2191 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
2192 if (!g_Globals._SHRestricted || !SHRestricted(REST_NONETHOOD)) // or REST_NOENTIRENETWORK ?
2193 #endif
2194 #if defined(ROSSHELL) || defined(_ROS_) // _ROS_ to be removed when printer/network will be implemented
2195 AddButton(ResString(IDS_NETWORK), ICID_NETWORK, false, IDC_NETWORK);
2196 #else
2197 AddButton(ResString(IDS_NETWORK), ICID_NETWORK, true, IDC_NETWORK);
2198 #endif
2199
2200 AddButton(ResString(IDS_DRIVES), ICID_FOLDER, true, IDC_DRIVES);
2201 }
2202
2203 void SearchMenu::AddEntries()
2204 {
2205 super::AddEntries();
2206
2207 AddButton(ResString(IDS_SEARCH_FILES), ICID_SEARCH_DOC, false, IDC_SEARCH_FILES);
2208
2209 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
2210 if (!g_Globals._SHRestricted || !SHRestricted(REST_HASFINDCOMPUTERS))
2211 #endif
2212 AddButton(ResString(IDS_SEARCH_COMPUTER),ICID_COMPUTER, false, IDC_SEARCH_COMPUTER);
2213
2214 AddButton(ResString(IDS_SEARCH_PRG), ICID_APPS, false, IDC_SEARCH_PROGRAM);
2215 }
2216
2217
2218 void RecentStartMenu::AddEntries()
2219 {
2220 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
2221 StartMenuDirectory& smd = *it;
2222 ShellDirectory& dir = smd._dir;
2223
2224 if (!dir._scanned) {
2225 WaitCursor wait;
2226
2227 #ifdef _LAZY_ICONEXTRACT
2228 dir.smart_scan(SORT_NAME, SCAN_DONT_EXTRACT_ICONS);
2229 #else
2230 dir.smart_scan(SORT_NAME);
2231 #endif
2232 }
2233
2234 dir.sort_directory(SORT_DATE);
2235 AddShellEntries(dir, RECENT_DOCS_COUNT, smd._ignore); ///@todo read max. count of entries from registry
2236 }
2237 }
2238
2239
2240 #ifndef _SHELL32_FAVORITES
2241
2242 void FavoritesMenu::AddEntries()
2243 {
2244 super::AddEntries();
2245
2246 String lwr_filter = _create_info._filter;
2247 lwr_filter.toLower();
2248
2249 for(BookmarkList::iterator it=_bookmarks.begin(); it!=_bookmarks.end(); ++it) {
2250 BookmarkNode& node = *it;
2251
2252 int id = ++_next_id;
2253
2254 _entries[id] = node;
2255
2256 if (node._type == BookmarkNode::BMNT_FOLDER) {
2257 BookmarkFolder& folder = *node._pfolder;
2258
2259 AddButton(folder._name, ICID_FOLDER, true, id);
2260 } else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
2261 Bookmark& bookmark = *node._pbookmark;
2262
2263 ICON_ID icon = ICID_NONE;
2264
2265 if (!bookmark._icon_path.empty())
2266 icon = g_Globals._icon_cache.extract(bookmark._icon_path, bookmark._icon_idx);
2267
2268 // filter non-directory entries
2269 if (!lwr_filter.empty()) {
2270 String lwr_name = bookmark._name;
2271 String lwr_desc = bookmark._description;
2272 String lwr_url = bookmark._url;
2273
2274 lwr_name.toLower();
2275 lwr_desc.toLower();
2276 lwr_url.toLower();
2277
2278 if (!_tcsstr(lwr_name,lwr_filter) && !_tcsstr(lwr_desc,lwr_filter) && !_tcsstr(lwr_url,lwr_filter))
2279 continue;
2280 }
2281
2282 AddButton(bookmark._name, icon!=ICID_NONE?icon:ICID_BOOKMARK, false, id);
2283 }
2284 }
2285 }
2286
2287 int FavoritesMenu::Command(int id, int code)
2288 {
2289 BookmarkMap::iterator found = _entries.find(id);
2290
2291 if (found != _entries.end()) {
2292 BookmarkNode& node = found->second;
2293
2294 if (node._type == BookmarkNode::BMNT_FOLDER) {
2295 BookmarkFolder& folder = *node._pfolder;
2296
2297 if (CloseOtherSubmenus(id))
2298 CreateSubmenu(id, folder._name, STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(folder._bookmarks));
2299 } else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
2300 Bookmark& bookmark = *node._pbookmark;
2301
2302 String url = bookmark._url;
2303 HWND hparent = GetParent(_hwnd);
2304
2305 CloseStartMenu(id);
2306
2307 launch_file(hparent, url, SW_SHOWNORMAL);
2308 }
2309
2310 return 0;
2311 }
2312
2313 return super::Command(id, code);
2314 }
2315
2316 #endif