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