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