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