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