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