desktop settings dialog sheet (for now only too look at it - no real functionality)
[reactos.git] / reactos / subsys / system / explorer / taskbar / startmenu.cpp
1 /*
2 * Copyright 2003 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 "../utility/utility.h"
34
35 #include "../explorer.h"
36 #include "../globals.h"
37 #include "../externals.h"
38 #include "../explorer_intres.h"
39
40 #include "desktopbar.h"
41 #include "startmenu.h"
42
43 #include "../dialogs/searchprogram.h"
44 #include "../dialogs/settings.h"
45
46
47 StartMenu::StartMenu(HWND hwnd)
48 : super(hwnd)
49 {
50 _next_id = IDC_FIRST_MENU;
51 _submenu_id = 0;
52 _border_left = 0;
53 _border_top = 0;
54 _last_pos = WindowRect(hwnd).pos();
55 #ifdef _LIGHT_STARTMENU
56 _selected_id = -1;
57 _last_mouse_pos = 0;
58 #endif
59 }
60
61 StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info)
62 : super(hwnd),
63 _create_info(create_info)
64 {
65 for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
66 if (*it)
67 _dirs.push_back(ShellDirectory(Desktop(), *it, _hwnd));
68
69 _next_id = IDC_FIRST_MENU;
70 _submenu_id = 0;
71 _border_left = 0;
72 _border_top = create_info._border_top;
73 _last_pos = WindowRect(hwnd).pos();
74 #ifdef _LIGHT_STARTMENU
75 _selected_id = -1;
76 _last_mouse_pos = 0;
77 #endif
78 }
79
80 StartMenu::~StartMenu()
81 {
82 SendParent(PM_STARTMENU_CLOSED);
83 }
84
85
86 // We need this wrapper function for s_wcStartMenu, it calls the WIN32 API,
87 // though static C++ initializers are not allowed for Winelib applications.
88 BtnWindowClass& StartMenu::GetWndClasss()
89 {
90 static BtnWindowClass s_wcStartMenu(CLASSNAME_STARTMENU);
91
92 return s_wcStartMenu;
93 }
94
95
96 Window::CREATORFUNC_INFO StartMenu::s_def_creator = STARTMENU_CREATOR(StartMenu);
97
98 HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndParent, LPCTSTR title, CREATORFUNC_INFO creator)
99 {
100 UINT style, ex_style;
101 int top_height;
102
103 if (hwndParent) {
104 style = WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE;
105 ex_style = 0;
106 top_height = STARTMENU_TOP_BTN_SPACE;
107 } else {
108 style = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPCHILDREN|WS_VISIBLE;
109 ex_style = WS_EX_TOOLWINDOW;
110 top_height = 0;
111 }
112
113 RECT rect = {x, y-STARTMENU_LINE_HEIGHT-top_height, x+STARTMENU_WIDTH_MIN, y};
114
115 #ifndef _LIGHT_STARTMENU
116 rect.top += STARTMENU_LINE_HEIGHT;
117 #endif
118
119 AdjustWindowRectEx(&rect, style, FALSE, ex_style);
120
121 StartMenuCreateInfo create_info;
122
123 create_info._folders = folders;
124 create_info._border_top = top_height;
125 create_info._creator = creator;
126
127 if (title)
128 create_info._title = title;
129
130 HWND hwnd = Window::Create(creator, &create_info, ex_style, GetWndClasss(), title,
131 style, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndParent);
132
133 // make sure the window is not off the screen
134 MoveVisible(hwnd);
135
136 return hwnd;
137 }
138
139
140 LRESULT StartMenu::Init(LPCREATESTRUCT pcs)
141 {
142 try {
143 AddEntries();
144
145 if (super::Init(pcs))
146 return 1;
147
148 // create buttons for registered entries in _entries
149 for(ShellEntryMap::const_iterator it=_entries.begin(); it!=_entries.end(); ++it) {
150 const StartMenuEntry& sme = it->second;
151 bool hasSubmenu = false;
152
153 for(ShellEntrySet::const_iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it)
154 if ((*it)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
155 hasSubmenu = true;
156
157 #ifdef _LIGHT_STARTMENU
158 _buttons.push_back(SMBtnInfo(sme, it->first, hasSubmenu));
159 #else
160 AddButton(sme._title, sme._hIcon, hasSubmenu, it->first);
161 #endif
162 }
163
164 #ifdef _LIGHT_STARTMENU
165 if (_buttons.empty())
166 #else
167 if (!GetWindow(_hwnd, GW_CHILD))
168 #endif
169 AddButton(ResString(IDS_EMPTY), ICID_NONE, false, 0, false);
170
171 #ifdef _LIGHT_STARTMENU
172 ResizeToButtons();
173 #endif
174
175 #ifdef _LAZY_ICONEXTRACT
176 PostMessage(_hwnd, PM_UPDATE_ICONS, 0, 0);
177 #endif
178 } catch(COMException& e) {
179 HandleException(e, pcs->hwndParent); // destroys the start menu window while switching focus
180 }
181
182 return 0;
183 }
184
185 void StartMenu::AddEntries()
186 {
187 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
188 StartMenuDirectory& smd = *it;
189 ShellDirectory& dir = smd._dir;
190
191 if (!dir._scanned) {
192 WaitCursor wait;
193
194 #ifdef _LAZY_ICONEXTRACT
195 dir.smart_scan(SCAN_FILESYSTEM); // lazy icon extraction, try to read directly from filesystem
196 #else
197 dir.smart_scan(SCAN_EXTRACT_ICONS|SCAN_FILESYSTEM);
198 #endif
199 }
200
201 AddShellEntries(dir, -1, smd._subfolders);
202 }
203 }
204
205
206 void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, bool subfolders)
207 {
208 int cnt = 0;
209
210 for(Entry*entry=dir._down; entry; entry=entry->_next) {
211 // hide files like "desktop.ini"
212 if (entry->_shell_attribs & SFGAO_HIDDEN)
213 //not appropriate for drive roots: if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
214 continue;
215
216 // hide subfolders if requested
217 if (!subfolders)
218 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
219 continue;
220
221 // only 'max' entries shall be added.
222 if (++cnt == max)
223 break;
224
225 if (entry->_etype == ET_SHELL)
226 AddEntry(dir._folder, static_cast<ShellEntry*>(entry));
227 else
228 AddEntry(dir._folder, entry);
229 }
230 }
231
232
233 LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
234 {
235 switch(nmsg) {
236 case WM_PAINT: {
237 PaintCanvas canvas(_hwnd);
238 Paint(canvas);
239 break;}
240
241 case WM_SIZE:
242 ResizeButtons(LOWORD(lparam)-_border_left);
243 break;
244
245 case WM_MOVE: {
246 POINTS& pos = MAKEPOINTS(lparam);
247
248 // move open submenus of floating menus
249 if (_submenu) {
250 int dx = pos.x - _last_pos.x;
251 int dy = pos.y - _last_pos.y;
252
253 if (dx || dy) {
254 WindowRect rt(_submenu);
255 SetWindowPos(_submenu, 0, rt.left+dx, rt.top+dy, 0, 0, SWP_NOSIZE|SWP_NOACTIVATE);
256 //MoveVisible(_submenu);
257 }
258 }
259
260 _last_pos.x = pos.x;
261 _last_pos.y = pos.y;
262 goto def;}
263
264 case WM_NCHITTEST: {
265 LRESULT res = super::WndProc(nmsg, wparam, lparam);
266
267 if (res>=HTSIZEFIRST && res<=HTSIZELAST)
268 return HTCLIENT; // disable window resizing
269
270 return res;}
271
272 case WM_LBUTTONDOWN: {
273 RECT rect;
274
275 // check mouse cursor for coordinates of floating button
276 GetFloatingButtonRect(&rect);
277
278 if (PtInRect(&rect, Point(lparam))) {
279 // create a floating copy of the current start menu
280 WindowRect pos(_hwnd);
281
282 ///@todo do something similar to StartMenuRoot::TrackStartmenu() in order to automatically close submenus when clicking on the desktop background
283 StartMenu::Create(pos.left+3, pos.bottom-3, _create_info._folders, 0, _create_info._title, _create_info._creator);
284 CloseStartMenu();
285 }
286
287 #ifdef _LIGHT_STARTMENU
288 int id = ButtonHitTest(Point(lparam));
289
290 if (id)
291 Command(id, BN_CLICKED);
292 #endif
293 break;}
294
295 case WM_SYSCOMMAND:
296 if ((wparam&0xFFF0) == SC_SIZE)
297 return 0; // disable window resizing
298 goto def;
299
300 case WM_ACTIVATEAPP:
301 // close start menu when activating another application
302 if (!wparam)
303 CloseStartMenu();
304 break; // don't call super::WndProc in case "this" has been deleted
305
306 case WM_CANCELMODE:
307 CloseStartMenu();
308 break;
309
310 #ifdef _LIGHT_STARTMENU
311 case WM_MOUSEMOVE: {
312 // automatically set the focus to startmenu entries when moving the mouse over them
313 if (lparam != _last_mouse_pos) { // don't process WM_MOUSEMOVE when opening submenus using keyboard navigation
314 int new_id = ButtonHitTest(Point(lparam));
315
316 if (new_id != _selected_id)
317 SelectButton(new_id);
318
319 _last_mouse_pos = lparam;
320 }
321 break;}
322
323 case WM_KEYDOWN:
324 ProcessKey(wparam);
325 break;
326 #else
327 case PM_STARTENTRY_FOCUSED: { ///@todo use TrackMouseEvent() and WM_MOUSEHOVER to wait a bit before opening submenus
328 BOOL hasSubmenu = wparam;
329 HWND hctrl = (HWND)lparam;
330
331 // automatically open submenus
332 if (hasSubmenu) {
333 UpdateWindow(_hwnd); // draw focused button before waiting on submenu creation
334 //SendMessage(_hwnd, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hctrl),BN_CLICKED), (LPARAM)hctrl);
335 Command(GetDlgCtrlID(hctrl), BN_CLICKED);
336 } else {
337 // close any open submenu
338 CloseOtherSubmenus();
339 }
340 break;}
341 #endif
342
343 #ifdef _LAZY_ICONEXTRACT
344 case PM_UPDATE_ICONS:
345 UpdateIcons(/*wparam*/);
346 break;
347 #endif
348
349 case PM_STARTENTRY_LAUNCHED:
350 if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus
351 return 0;
352
353 // route message to the parent menu and close menus after launching an entry
354 if (!SendParent(nmsg, wparam, lparam))
355 CloseStartMenu();
356 return 1; // signal that we have received and processed the message
357
358 case PM_STARTMENU_CLOSED:
359 _submenu = 0;
360 break;
361
362 case PM_SELECT_ENTRY:
363 SelectButtonIndex(0, wparam?true:false);
364 break;
365
366 default: def:
367 return super::WndProc(nmsg, wparam, lparam);
368 }
369
370 return 0;
371 }
372
373
374 #ifdef _LIGHT_STARTMENU
375
376 int StartMenu::ButtonHitTest(POINT pt)
377 {
378 ClientRect clnt(_hwnd);
379 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
380
381 if (pt.x<rect.left || pt.x>rect.right)
382 return 0;
383
384 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
385 const SMBtnInfo& info = *it;
386
387 if (rect.top > pt.y)
388 break;
389
390 rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT);
391
392 if (pt.y < rect.bottom) // PtInRect(&rect, pt)
393 return info._id;
394
395 rect.top = rect.bottom;
396 }
397
398 return 0;
399 }
400
401 void StartMenu::InvalidateSelection()
402 {
403 if (!_selected_id)
404 return;
405
406 ClientRect clnt(_hwnd);
407 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
408
409 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
410 const SMBtnInfo& info = *it;
411
412 rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT);
413
414 if (info._id == _selected_id) {
415 InvalidateRect(_hwnd, &rect, TRUE);
416 break;
417 }
418
419 rect.top = rect.bottom;
420 }
421 }
422
423 const SMBtnInfo* StartMenu::GetButtonInfo(int id) const
424 {
425 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it)
426 if (it->_id == id)
427 return &*it;
428
429 return NULL;
430 }
431
432 bool StartMenu::SelectButton(int id, bool open_sub)
433 {
434 if (id == -1)
435 return false;
436
437 if (id == _selected_id)
438 return true;
439
440 InvalidateSelection();
441
442 const SMBtnInfo* btn = GetButtonInfo(id);
443
444 if (btn && btn->_enabled) {
445 _selected_id = id;
446
447 InvalidateSelection();
448
449 // automatically open submenus
450 if (btn->_hasSubmenu) {
451 if (open_sub)
452 OpenSubmenu();
453 } else
454 CloseOtherSubmenus(); // close any open submenu
455
456 return true;
457 } else {
458 _selected_id = -1;
459 return false;
460 }
461 }
462
463 bool StartMenu::OpenSubmenu(bool select_first)
464 {
465 if (_selected_id == -1)
466 return false;
467
468 InvalidateSelection();
469
470 const SMBtnInfo* btn = GetButtonInfo(_selected_id);
471
472 // automatically open submenus
473 if (btn->_hasSubmenu) {
474 //@@ allows destroying of startmenu when processing PM_UPDATE_ICONS -> GPF
475 UpdateWindow(_hwnd); // draw focused button before waiting on submenu creation
476 Command(_selected_id, BN_CLICKED);
477
478 if (select_first && _submenu)
479 SendMessage(_submenu, PM_SELECT_ENTRY, (WPARAM)false, 0);
480
481 return true;
482 } else
483 return false;
484 }
485
486
487 int StartMenu::GetSelectionIndex()
488 {
489 if (_selected_id == -1)
490 return -1;
491
492 for(int i=0; i<(int)_buttons.size(); ++i)
493 if (_buttons[i]._id == _selected_id)
494 return i;
495
496 return -1;
497 }
498
499 bool StartMenu::SelectButtonIndex(int idx, bool open_sub)
500 {
501 if (idx>=0 && idx<(int)_buttons.size())
502 return SelectButton(_buttons[idx]._id, open_sub);
503 else
504 return false;
505 }
506
507 void StartMenu::ProcessKey(int vk)
508 {
509 switch(vk) {
510 case VK_RETURN:
511 if (_selected_id)
512 Command(_selected_id, BN_CLICKED);
513 break;
514
515 case VK_UP:
516 Navigate(-1);
517 break;
518
519 case VK_DOWN:
520 Navigate(+1);
521 break;
522
523 case VK_HOME:
524 SelectButtonIndex(0, false);
525 break;
526
527 case VK_END:
528 SelectButtonIndex(_buttons.size()-1, false);
529 break;
530
531 case VK_LEFT:
532 if (_submenu)
533 CloseOtherSubmenus();
534 else if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) // don't automatically close floating menus
535 DestroyWindow(_hwnd);
536 break;
537
538 case VK_RIGHT:
539 OpenSubmenu(true);
540 break;
541
542 case VK_ESCAPE:
543 CloseStartMenu();
544 break;
545 }
546 }
547
548 bool StartMenu::Navigate(int step)
549 {
550 int idx = GetSelectionIndex();
551
552 if (idx == -1)
553 if (step > 0)
554 idx = 0 - step;
555 else
556 idx = _buttons.size() - step;
557
558 for(;;) {
559 idx += step;
560
561 if (idx<0 || idx>(int)_buttons.size())
562 break;
563
564 if (SelectButtonIndex(idx, false))
565 return true;
566 }
567
568 return false;
569 }
570
571 #endif // _LIGHT_STARTMENU
572
573
574 bool StartMenu::GetButtonRect(int id, PRECT prect) const
575 {
576 #ifdef _LIGHT_STARTMENU
577 ClientRect clnt(_hwnd);
578 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
579
580 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
581 const SMBtnInfo& info = *it;
582
583 rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT: STARTMENU_LINE_HEIGHT);
584
585 if (info._id == id) {
586 *prect = rect;
587 return true;
588 }
589
590 rect.top = rect.bottom;
591 }
592
593 return false;
594 #else
595 HWND btn = GetDlgItem(_hwnd, id);
596
597 if (btn) {
598 GetWindowRect(btn, prect);
599 ScreenToClient(_hwnd, prect);
600
601 return true;
602 } else
603 return false;
604 #endif
605 }
606
607
608 void StartMenu::DrawFloatingButton(HDC hdc)
609 {
610 static ResIconEx floatingIcon(IDI_FLOATING, 8, 4);
611
612 ClientRect clnt(_hwnd);
613
614 DrawIconEx(hdc, clnt.right-12, 0, floatingIcon, 8, 4, 0, 0, DI_NORMAL);
615 }
616
617 void StartMenu::GetFloatingButtonRect(LPRECT prect)
618 {
619 GetClientRect(_hwnd, prect);
620
621 prect->right -= 4;
622 prect->left = prect->right - 8;
623 prect->bottom = 4;
624 }
625
626
627 void StartMenu::Paint(PaintCanvas& canvas)
628 {
629 if (_border_top)
630 DrawFloatingButton(canvas);
631
632 #ifdef _LIGHT_STARTMENU
633 ClientRect clnt(_hwnd);
634 RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT};
635
636 int sep_width = rect.right-rect.left - 4;
637
638 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
639 BkMode bk_mode(canvas, TRANSPARENT);
640
641 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
642 const SMBtnInfo& btn = *it;
643
644 if (rect.top > canvas.rcPaint.bottom)
645 break;
646
647 if (btn._id == -1) { // a separator?
648 rect.bottom = rect.top + STARTMENU_SEP_HEIGHT;
649
650 BrushSelection brush_sel(canvas, GetSysColorBrush(COLOR_BTNSHADOW));
651 PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2-1, sep_width, 1, PATCOPY);
652
653 SelectBrush(canvas, GetSysColorBrush(COLOR_BTNHIGHLIGHT));
654 PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT/2, sep_width, 1, PATCOPY);
655 } else {
656 rect.bottom = rect.top + STARTMENU_LINE_HEIGHT;
657
658 if (rect.top >= canvas.rcPaint.top)
659 DrawStartMenuButton(canvas, rect, btn._title, btn, btn._id==_selected_id, false);
660 }
661
662 rect.top = rect.bottom;
663 }
664 #endif
665 }
666
667 #ifdef _LAZY_ICONEXTRACT
668 void StartMenu::UpdateIcons(/*int idx*/)
669 {
670 UpdateWindow(_hwnd);
671
672 #ifdef _SINGLE_ICONEXTRACT
673
674 //if (idx >= 0)
675 int idx = 0;
676
677 for(; idx<(int)_buttons.size(); ++idx) {
678 SMBtnInfo& btn = _buttons[idx];
679
680 if (btn._icon_id==ICID_UNKNOWN && btn._id>0) {
681 StartMenuEntry& sme = _entries[btn._id];
682
683 btn._icon_id = ICID_NONE;
684
685 for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
686 Entry* entry = *it;
687
688 if (entry->_icon_id == ICID_UNKNOWN)
689 entry->extract_icon();
690
691 if (entry->_icon_id > ICID_NONE) {
692 btn._icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
693
694 RECT rect;
695
696 GetButtonRect(btn._id, &rect);
697 WindowCanvas canvas(_hwnd);
698 DrawStartMenuButton(canvas, rect, NULL, btn, btn._id==_selected_id, false);
699
700 //InvalidateRect(_hwnd, &rect, FALSE);
701 //UpdateWindow(_hwnd);
702 //break;
703
704 break;
705 }
706 }
707 }
708 }
709
710 // if (++idx < (int)_buttons.size())
711 // PostMessage(_hwnd, PM_UPDATE_ICONS, idx, 0);
712
713 #else
714
715 int icons_extracted = 0;
716 int icons_updated = 0;
717
718 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
719 ShellDirectory& dir = it->_dir;
720
721 icons_extracted += dir.extract_icons();
722 }
723
724 if (icons_extracted) {
725 for(ShellEntryMap::iterator it1=_entries.begin(); it1!=_entries.end(); ++it1) {
726 StartMenuEntry& sme = it1->second;
727
728 if (!sme._hIcon) {
729 sme._hIcon = (HICON)-1;
730
731 for(ShellEntrySet::const_iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
732 const Entry* sm_entry = *it2;
733
734 if (sm_entry->_hIcon) {
735 sme._hIcon = sm_entry->_hIcon;
736 break;
737 }
738 }
739 }
740 }
741
742 for(SMBtnVector::iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
743 SMBtnInfo& info = *it;
744
745 if (info._id>0 && !info._hIcon) {
746 info._hIcon = _entries[info._id]._hIcon;
747 ++icons_updated;
748 }
749 }
750 }
751
752 if (icons_updated) {
753 InvalidateRect(_hwnd, NULL, FALSE);
754 UpdateWindow(_hwnd);
755 }
756 #endif
757 }
758 #endif
759
760
761 // resize child button controls to accomodate for new window size
762 void StartMenu::ResizeButtons(int cx)
763 {
764 HDWP hdwp = BeginDeferWindowPos(10);
765
766 for(HWND ctrl=GetWindow(_hwnd,GW_CHILD); ctrl; ctrl=GetNextWindow(ctrl,GW_HWNDNEXT)) {
767 ClientRect rt(ctrl);
768
769 if (rt.right != cx) {
770 int height = rt.bottom - rt.top;
771
772 // special handling for separator controls
773 if (!height && (GetWindowStyle(ctrl)&SS_TYPEMASK)==SS_ETCHEDHORZ)
774 height = 2;
775
776 hdwp = DeferWindowPos(hdwp, ctrl, 0, 0, 0, cx, height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
777 }
778 }
779
780 EndDeferWindowPos(hdwp);
781 }
782
783
784 int StartMenu::Command(int id, int code)
785 {
786 #ifndef _LIGHT_STARTMENU
787 switch(id) {
788 case IDCANCEL:
789 CloseStartMenu(id);
790 break;
791
792 default: {
793 #endif
794 ShellEntryMap::iterator found = _entries.find(id);
795
796 if (found != _entries.end()) {
797 ActivateEntry(id, found->second._entries);
798 return 0;
799 }
800
801 return super::Command(id, code);
802 #ifndef _LIGHT_STARTMENU
803 }
804 }
805
806 return 0;
807 #endif
808 }
809
810
811 StartMenuEntry& StartMenu::AddEntry(const String& title, ICON_ID icon_id, Entry* entry)
812 {
813 // search for an already existing subdirectory entry with the same name
814 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
815 for(ShellEntryMap::iterator it=_entries.begin(); it!=_entries.end(); ++it) {
816 StartMenuEntry& sme = it->second;
817
818 if (sme._title == title) ///@todo speed up by using a map indexed by name
819 for(ShellEntrySet::iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
820 if ((*it2)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
821 // merge the new shell entry with the existing of the same name
822 sme._entries.insert(entry);
823 return sme;
824 }
825 }
826 }
827
828 StartMenuEntry& sme = AddEntry(title, icon_id);
829
830 sme._entries.insert(entry);
831
832 return sme;
833 }
834
835 StartMenuEntry& StartMenu::AddEntry(const String& title, ICON_ID icon_id, int id)
836 {
837 if (id == -1)
838 id = ++_next_id;
839
840 StartMenuEntry& sme = _entries[id];
841
842 sme._title = title;
843 sme._icon_id = icon_id;
844
845 return sme;
846 }
847
848 StartMenuEntry& StartMenu::AddEntry(const ShellFolder folder, ShellEntry* entry)
849 {
850 ICON_ID icon_id;
851
852 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
853 icon_id = ICID_FOLDER;
854 else
855 icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
856
857 return AddEntry(folder.get_name(entry->_pidl), icon_id, entry);
858 }
859
860 StartMenuEntry& StartMenu::AddEntry(const ShellFolder folder, Entry* entry)
861 {
862 ICON_ID icon_id;
863
864 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
865 icon_id = ICID_FOLDER;
866 else
867 icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
868
869 return AddEntry(entry->_display_name, icon_id, entry);
870 }
871
872
873 void StartMenu::AddButton(LPCTSTR title, ICON_ID icon_id, bool hasSubmenu, int id, bool enabled)
874 {
875 #ifdef _LIGHT_STARTMENU
876 _buttons.push_back(SMBtnInfo(title, icon_id, id, hasSubmenu, enabled));
877 #else
878 DWORD style = enabled? WS_VISIBLE|WS_CHILD|BS_OWNERDRAW: WS_VISIBLE|WS_CHILD|BS_OWNERDRAW|WS_DISABLED;
879
880 WindowRect rect(_hwnd);
881 ClientRect clnt(_hwnd);
882
883 // increase window height to make room for the new button
884 rect.top -= STARTMENU_LINE_HEIGHT;
885
886 // move down if we are too high now
887 if (rect.top < 0) {
888 rect.top += STARTMENU_LINE_HEIGHT;
889 rect.bottom += STARTMENU_LINE_HEIGHT;
890 }
891
892 WindowCanvas canvas(_hwnd);
893 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
894
895 // widen window, if it is too small
896 int text_width = GetStartMenuBtnTextWidth(canvas, title, _hwnd) + 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
897
898 int cx = clnt.right - _border_left;
899 if (text_width > cx)
900 rect.right += text_width-cx;
901
902 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
903
904 StartMenuCtrl(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left,
905 title, id, g_Globals._icon_cache.get_icon(icon_id)._hIcon, hasSubmenu, style);
906 #endif
907 }
908
909 void StartMenu::AddSeparator()
910 {
911 #ifdef _LIGHT_STARTMENU
912 _buttons.push_back(SMBtnInfo(NULL, ICID_NONE, -1, false));
913 #else
914 WindowRect rect(_hwnd);
915 ClientRect clnt(_hwnd);
916
917 // increase window height to make room for the new separator
918 rect.top -= STARTMENU_SEP_HEIGHT;
919
920 // move down if we are too high now
921 if (rect.top < 0) {
922 rect.top += STARTMENU_LINE_HEIGHT;
923 rect.bottom += STARTMENU_LINE_HEIGHT;
924 }
925
926 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
927
928 StartMenuSeparator(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left);
929 #endif
930 }
931
932
933 bool StartMenu::CloseOtherSubmenus(int id)
934 {
935 if (_submenu) {
936 if (IsWindow(_submenu)) {
937 if (_submenu_id == id)
938 return false;
939 else {
940 _submenu_id = 0;
941 DestroyWindow(_submenu);
942 // _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
943 }
944 }
945
946 _submenu = 0;
947 }
948
949 return true;
950 }
951
952
953 void StartMenu::CreateSubmenu(int id, LPCTSTR title, CREATORFUNC_INFO creator)
954 {
955 CreateSubmenu(id, StartMenuFolders(), title, creator);
956 }
957
958 void StartMenu::CreateSubmenu(int id, int folder_id, LPCTSTR title, CREATORFUNC_INFO creator)
959 {
960 try {
961 SpecialFolderPath folder(folder_id, _hwnd);
962
963 StartMenuFolders new_folders;
964 new_folders.push_back(folder);
965
966 CreateSubmenu(id, new_folders, title, creator);
967 } catch(COMException&) {
968 // ignore Exception and don't display anything
969 }
970 }
971
972 void StartMenu::CreateSubmenu(int id, int folder_id1, int folder_id2, LPCTSTR title, CREATORFUNC_INFO creator)
973 {
974 StartMenuFolders new_folders;
975
976 try {
977 new_folders.push_back(SpecialFolderPath(folder_id1, _hwnd));
978 } catch(COMException&) {
979 }
980
981 try {
982 new_folders.push_back(SpecialFolderPath(folder_id2, _hwnd));
983 } catch(COMException&) {
984 }
985
986 if (!new_folders.empty())
987 CreateSubmenu(id, new_folders, title, creator);
988 }
989
990 void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTSTR title, CREATORFUNC_INFO creator)
991 {
992 // Only open one submenu at a time.
993 if (!CloseOtherSubmenus(id))
994 return;
995
996 RECT rect;
997 int x, y;
998
999 if (GetButtonRect(id, &rect)) {
1000 ClientToScreen(_hwnd, &rect);
1001
1002 x = rect.right; // Submenus should overlap their parent a bit.
1003 y = rect.top+STARTMENU_LINE_HEIGHT-_border_top;
1004 } else {
1005 WindowRect pos(_hwnd);
1006
1007 x = pos.right;
1008 y = pos.top;
1009 }
1010
1011 _submenu_id = id;
1012 _submenu = StartMenu::Create(x, y, new_folders, _hwnd, title, creator);
1013 }
1014
1015
1016 void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries)
1017 {
1018 StartMenuFolders new_folders;
1019 String title;
1020
1021 for(ShellEntrySet::const_iterator it=entries.begin(); it!=entries.end(); ++it) {
1022 Entry* entry = const_cast<Entry*>(*it);
1023
1024 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1025
1026 ///@todo If the user explicitly clicked on a submenu, display this folder as floating start menu.
1027
1028 if (entry->_etype == ET_SHELL)
1029 new_folders.push_back(entry->create_absolute_pidl());
1030 else {
1031 TCHAR path[MAX_PATH];
1032
1033 if (entry->get_path(path))
1034 new_folders.push_back(path);
1035 }
1036
1037 if (title.empty())
1038 title = entry->_display_name;
1039 } else {
1040 // If the entry is no subdirectory, there can only be one shell entry.
1041 assert(entries.size()==1);
1042
1043 HWND hparent = GetParent(_hwnd);
1044 ShellPath shell_path = entry->create_absolute_pidl();
1045
1046 // close start menus after launching the selected entry
1047 CloseStartMenu(id);
1048
1049 ///@todo launch in the background; specify correct HWND for error message box titles
1050 SHELLEXECUTEINFO shexinfo;
1051
1052 shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
1053 shexinfo.fMask = SEE_MASK_INVOKEIDLIST; // SEE_MASK_IDLIST is also possible.
1054 shexinfo.hwnd = hparent;
1055 shexinfo.lpVerb = NULL;
1056 shexinfo.lpFile = NULL;
1057 shexinfo.lpParameters = NULL;
1058 shexinfo.lpDirectory = NULL;
1059 shexinfo.nShow = SW_SHOWNORMAL;
1060
1061 shexinfo.lpIDList = &*shell_path;
1062
1063 if (!ShellExecuteEx(&shexinfo))
1064 display_error(hparent, GetLastError());
1065
1066 // we may have deleted 'this' - ensure we leave the loop and function
1067 return;
1068 }
1069 }
1070
1071 if (!new_folders.empty()) {
1072 // Only open one submenu at a time.
1073 if (!CloseOtherSubmenus(id))
1074 return;
1075
1076 CreateSubmenu(id, new_folders, title);
1077 }
1078 }
1079
1080
1081 /// close all windows of the start menu popup
1082 void StartMenu::CloseStartMenu(int id)
1083 {
1084 if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) { // don't automatically close floating menus
1085 if (!SendParent(PM_STARTENTRY_LAUNCHED, id, (LPARAM)_hwnd))
1086 DestroyWindow(_hwnd);
1087 } else if (_submenu) // instead close submenus of floating parent menus
1088 CloseSubmenus();
1089 }
1090
1091
1092 int GetStartMenuBtnTextWidth(HDC hdc, LPCTSTR title, HWND hwnd)
1093 {
1094 RECT rect = {0, 0, 0, 0};
1095 DrawText(hdc, title, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
1096
1097 return rect.right-rect.left;
1098 }
1099
1100 #ifdef _LIGHT_STARTMENU
1101 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, const SMBtnInfo& btn, bool has_focus, bool pushed)
1102 #else
1103 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, HICON hIcon,
1104 bool hasSubmenu, bool enabled, bool has_focus, bool pushed);
1105 #endif
1106 {
1107 UINT style = DFCS_BUTTONPUSH;
1108
1109 if (!btn._enabled)
1110 style |= DFCS_INACTIVE;
1111
1112 POINT iconPos = {rect.left+2, (rect.top+rect.bottom-16)/2};
1113 RECT textRect = {rect.left+16+4, rect.top+2, rect.right-4, rect.bottom-4};
1114
1115 if (pushed) {
1116 style |= DFCS_PUSHED;
1117 ++iconPos.x; ++iconPos.y;
1118 ++textRect.left; ++textRect.top;
1119 ++textRect.right; ++textRect.bottom;
1120 }
1121
1122 int bk_color_idx = COLOR_BTNFACE;
1123 int text_color_idx = COLOR_BTNTEXT;
1124
1125 if (has_focus) {
1126 bk_color_idx = COLOR_HIGHLIGHT;
1127 text_color_idx = COLOR_HIGHLIGHTTEXT;
1128 }
1129
1130 COLORREF bk_color = GetSysColor(bk_color_idx);
1131 HBRUSH bk_brush = GetSysColorBrush(bk_color_idx);
1132
1133 if (title)
1134 FillRect(hdc, &rect, bk_brush);
1135
1136 if (btn._icon_id > ICID_NONE)
1137 g_Globals._icon_cache.get_icon(btn._icon_id).draw(hdc, iconPos.x, iconPos.y, 16, 16, bk_color, bk_brush);
1138
1139 // draw submenu arrow at the right
1140 if (btn._hasSubmenu) {
1141 static SmallIcon arrowIcon(IDI_ARROW);
1142 static SmallIcon selArrowIcon(IDI_ARROW_SELECTED);
1143
1144 DrawIconEx(hdc, rect.right-16, iconPos.y,
1145 has_focus? selArrowIcon: arrowIcon,
1146 16, 16, 0, bk_brush, DI_NORMAL);
1147 }
1148
1149 if (title) {
1150 BkMode bk_mode(hdc, TRANSPARENT);
1151
1152 if (!btn._enabled) // dis->itemState & (ODS_DISABLED|ODS_GRAYED)
1153 DrawGrayText(hdc, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
1154 else {
1155 TextColor lcColor(hdc, GetSysColor(text_color_idx));
1156 DrawText(hdc, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
1157 }
1158 }
1159 }
1160
1161
1162 #ifdef _LIGHT_STARTMENU
1163
1164 void StartMenu::ResizeToButtons()
1165 {
1166 WindowRect rect(_hwnd);
1167
1168 WindowCanvas canvas(_hwnd);
1169 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
1170
1171 int max_width = STARTMENU_WIDTH_MIN;
1172 int height = 0;
1173
1174 for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
1175 int w = GetStartMenuBtnTextWidth(canvas, it->_title, _hwnd);
1176
1177 if (w > max_width)
1178 max_width = w;
1179
1180 if (it->_id == -1)
1181 height += STARTMENU_SEP_HEIGHT;
1182 else
1183 height += STARTMENU_LINE_HEIGHT;
1184 }
1185
1186 // calculate new window size
1187 int text_width = max_width + 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
1188
1189 RECT rt_hgt = {rect.left, rect.bottom-_border_top-height, rect.left+_border_left+text_width, rect.bottom};
1190 AdjustWindowRectEx(&rt_hgt, GetWindowStyle(_hwnd), FALSE, GetWindowExStyle(_hwnd));
1191
1192 // ignore movement, only look at the size change
1193 rect.right = rect.left + (rt_hgt.right-rt_hgt.left);
1194 rect.top = rect.bottom - (rt_hgt.bottom-rt_hgt.top);
1195
1196 // move down if we are too high
1197 if (rect.top < 0) {
1198 rect.top += STARTMENU_LINE_HEIGHT;
1199 rect.bottom += STARTMENU_LINE_HEIGHT;
1200 }
1201
1202 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
1203 }
1204
1205 #else // _LIGHT_STARTMENU
1206
1207 LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1208 {
1209 switch(nmsg) {
1210 case WM_MOUSEMOVE:
1211 // automatically set the focus to startmenu entries when moving the mouse over them
1212 if (GetFocus()!=_hwnd && !(GetWindowStyle(_hwnd)&WS_DISABLED))
1213 SetFocus(_hwnd);
1214 break;
1215
1216 case WM_SETFOCUS:
1217 PostParent(PM_STARTENTRY_FOCUSED, _hasSubmenu, (LPARAM)_hwnd);
1218 goto def;
1219
1220 case WM_CANCELMODE:
1221 // route WM_CANCELMODE to the startmenu window
1222 return SendParent(nmsg, wparam, lparam);
1223
1224 default: def:
1225 return super::WndProc(nmsg, wparam, lparam);
1226 }
1227
1228 return 0;
1229 }
1230
1231 void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis)
1232 {
1233 TCHAR title[BUFFER_LEN];
1234
1235 GetWindowText(_hwnd, title, BUFFER_LEN);
1236
1237 DrawStartMenuButton(dis->hDC, dis->rcItem, title, _hIcon,
1238 _hasSubmenu,
1239 !(dis->itemState & ODS_DISABLED),
1240 dis->itemState&ODS_FOCUS? true: false,
1241 dis->itemState&ODS_SELECTED? true: false);
1242 }
1243
1244 #endif
1245
1246
1247 StartMenuRoot::StartMenuRoot(HWND hwnd)
1248 : super(hwnd)
1249 {
1250 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1251 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCOMMONGROUPS))
1252 #endif
1253 try {
1254 // insert directory "All Users\Start Menu"
1255 ShellDirectory cmn_startmenu(Desktop(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
1256 _dirs.push_back(StartMenuDirectory(cmn_startmenu, false)); // don't add subfolders
1257 } catch(COMException&) {
1258 // ignore exception and don't show additional shortcuts
1259 }
1260
1261 try {
1262 // insert directory "<user name>\Start Menu"
1263 ShellDirectory usr_startmenu(Desktop(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
1264 _dirs.push_back(StartMenuDirectory(usr_startmenu, false)); // don't add subfolders
1265 } catch(COMException&) {
1266 // ignore exception and don't show additional shortcuts
1267 }
1268
1269 // read size of logo bitmap
1270 BITMAP bmp_hdr;
1271 GetObject(ResBitmap(IDB_LOGOV), sizeof(BITMAP), &bmp_hdr);
1272 _logo_size.cx = bmp_hdr.bmWidth;
1273 _logo_size.cy = bmp_hdr.bmHeight;
1274
1275 _border_left = _logo_size.cx;
1276 }
1277
1278
1279 HWND StartMenuRoot::Create(HWND hwndOwner)
1280 {
1281 WindowRect pos(hwndOwner);
1282
1283 RECT rect = {pos.left, pos.top-STARTMENU_LINE_HEIGHT-4, pos.left+STARTMENU_WIDTH_MIN, pos.top};
1284
1285 #ifndef _LIGHT_STARTMENU
1286 rect.top += STARTMENU_LINE_HEIGHT;
1287 #endif
1288
1289 AdjustWindowRectEx(&rect, WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE, FALSE, 0);
1290
1291 return Window::Create(WINDOW_CREATOR(StartMenuRoot), 0, GetWndClasss(), TITLE_STARTMENU,
1292 WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,
1293 rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndOwner);
1294 }
1295
1296
1297 void StartMenuRoot::TrackStartmenu()
1298 {
1299 MSG msg;
1300 HWND hwnd = _hwnd;
1301
1302 #ifdef _LIGHT_STARTMENU
1303 _selected_id = -1;
1304 #endif
1305
1306 // show previously hidden start menu
1307 ShowWindow(hwnd, SW_SHOW);
1308
1309 while(IsWindow(hwnd)) {
1310 if (!GetMessage(&msg, 0, 0, 0)) {
1311 PostQuitMessage(msg.wParam);
1312 break;
1313 }
1314
1315 // Check for a mouse click on any window, which is not part of the start menu
1316 if (msg.message==WM_LBUTTONDOWN || msg.message==WM_MBUTTONDOWN || msg.message==WM_RBUTTONDOWN) {
1317 StartMenu* menu_wnd = NULL;
1318
1319 for(HWND hwnd=msg.hwnd; hwnd; hwnd=GetParent(hwnd)) {
1320 menu_wnd = WINDOW_DYNAMIC_CAST(StartMenu, hwnd);
1321
1322 if (menu_wnd)
1323 break;
1324 }
1325
1326 if (!menu_wnd) {
1327 CloseStartMenu();
1328 break;
1329 }
1330 }
1331
1332 try {
1333 if (pretranslate_msg(&msg))
1334 continue;
1335
1336 if (dispatch_dialog_msg(&msg))
1337 continue;
1338
1339 TranslateMessage(&msg);
1340
1341 try {
1342 DispatchMessage(&msg);
1343 } catch(COMException& e) {
1344 HandleException(e, _hwnd);
1345 }
1346 } catch(COMException& e) {
1347 HandleException(e, _hwnd);
1348 }
1349 }
1350 }
1351
1352
1353 LRESULT StartMenuRoot::Init(LPCREATESTRUCT pcs)
1354 {
1355 // add buttons for entries in _entries
1356 if (super::Init(pcs))
1357 return 1;
1358
1359 AddSeparator();
1360
1361
1362 #ifdef __MINGW32__
1363 HKEY hkey, hkeyAdv;
1364 DWORD value, len;
1365
1366 if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), &hkey))
1367 hkey = 0;
1368
1369 if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hkeyAdv))
1370 hkeyAdv = 0;
1371
1372 #define IS_VALUE_ZERO(hk, name) \
1373 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || !value))
1374
1375 #define IS_VALUE_NOT_ZERO(hk, name) \
1376 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || value>0))
1377 #endif
1378
1379
1380 // insert hard coded start entries
1381 AddButton(ResString(IDS_PROGRAMS), ICID_APPS, true, IDC_PROGRAMS);
1382
1383 AddButton(ResString(IDS_DOCUMENTS), ICID_DOCUMENTS, true, IDC_DOCUMENTS);
1384
1385 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1386 if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
1387 #else
1388 if (IS_VALUE_ZERO(hkey, _T("NoRecentDocsMenu")))
1389 #endif
1390 AddButton(ResString(IDS_RECENT), ICID_DOCUMENTS, true, IDC_RECENT);
1391
1392 AddButton(ResString(IDS_FAVORITES), ICID_FAVORITES, true, IDC_FAVORITES);
1393
1394 AddButton(ResString(IDS_SETTINGS), ICID_CONFIG, true, IDC_SETTINGS);
1395
1396 AddButton(ResString(IDS_BROWSE), ICID_FOLDER, true, IDC_BROWSE);
1397
1398 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1399 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOFIND))
1400 #else
1401 if (IS_VALUE_ZERO(hkey, _T("NoFind")))
1402 #endif
1403 AddButton(ResString(IDS_SEARCH), ICID_SEARCH, true, IDC_SEARCH);
1404
1405 AddButton(ResString(IDS_START_HELP), ICID_INFO, false, IDC_START_HELP);
1406
1407 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1408 if (!g_Globals._SHRestricted || !SHRestricted(REST_NORUN))
1409 #else
1410 if (IS_VALUE_ZERO(hkey, _T("NoRun")))
1411 #endif
1412 AddButton(ResString(IDS_LAUNCH), ICID_ACTION, false, IDC_LAUNCH);
1413
1414
1415 AddSeparator();
1416
1417
1418 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1419 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCLOSE))
1420 #else
1421 if (IS_VALUE_NOT_ZERO(hkeyAdv, _T("StartMenuLogoff")))
1422 #endif
1423 AddButton(ResString(IDS_LOGOFF), ICID_LOGOFF, false, IDC_LOGOFF);
1424
1425
1426 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1427 if (!g_Globals._SHRestricted || SHRestricted(REST_STARTMENULOGOFF) != 1)
1428 #else
1429 if (IS_VALUE_ZERO(hkey, _T("NoClose")))
1430 #endif
1431 AddButton(ResString(IDS_SHUTDOWN), ICID_LOGOFF, false, IDC_SHUTDOWN);
1432
1433
1434 #ifdef __MINGW32__
1435 RegCloseKey(hkeyAdv);
1436 RegCloseKey(hkey);
1437 #endif
1438
1439
1440 #ifdef _LIGHT_STARTMENU
1441 // set the window size to fit all buttons
1442 ResizeToButtons();
1443 #endif
1444
1445 return 0;
1446 }
1447
1448
1449 void StartMenuRoot::AddEntries()
1450 {
1451 super::AddEntries();
1452
1453 AddButton(ResString(IDS_EXPLORE), ICID_EXPLORER, false, IDC_EXPLORE);
1454 }
1455
1456
1457 LRESULT StartMenuRoot::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1458 {
1459 switch(nmsg) {
1460 case WM_PAINT: {
1461 PaintCanvas canvas(_hwnd);
1462 Paint(canvas);
1463 break;}
1464
1465 default:
1466 return super::WndProc(nmsg, wparam, lparam);
1467 }
1468
1469 return 0;
1470 }
1471
1472 void StartMenuRoot::Paint(PaintCanvas& canvas)
1473 {
1474 int clr_bits;
1475 {WindowCanvas dc(_hwnd); clr_bits=GetDeviceCaps(dc, BITSPIXEL);}
1476 bool logo256 = clr_bits<=8;
1477
1478 MemCanvas mem_dc;
1479 ResBitmap bmp(logo256? IDB_LOGOV256: IDB_LOGOV);
1480 BitmapSelection sel(mem_dc, bmp);
1481
1482 ClientRect clnt(_hwnd);
1483 int h = min(_logo_size.cy, clnt.bottom);
1484
1485 RECT rect = {0, 0, _logo_size.cx-1, clnt.bottom-h};
1486 HBRUSH hbr = CreateSolidBrush(logo256? RGB(166,202,240): RGB(255,255,255)); // same color as the background color in the logo bitmap
1487 FillRect(canvas, &rect, hbr);
1488 DeleteObject(hbr);
1489 //PatBlt(canvas, _logo_size.cx-1, 0, 1, clnt.bottom-h, WHITENESS);
1490 PatBlt(canvas, _logo_size.cx-1, 0, 1, clnt.bottom-h, WHITENESS);
1491
1492 BitBlt(canvas, 0, clnt.bottom-h, _logo_size.cx, h, mem_dc, 0, 0, SRCCOPY);
1493
1494 if (!logo256) {
1495 rect.left = rect.right++;
1496 rect.bottom = clnt.bottom;
1497 HBRUSH hbr_border = GetStockBrush(GRAY_BRUSH); //CreateSolidBrush(RGB(71,88,85));
1498 FillRect(canvas, &rect, hbr_border);
1499 //DeleteObject(hbr_border);
1500 }
1501
1502 super::Paint(canvas);
1503 }
1504
1505
1506 void StartMenuRoot::CloseStartMenu(int id)
1507 {
1508 if (_submenu)
1509 CloseSubmenus();
1510
1511 ShowWindow(_hwnd, SW_HIDE);
1512 }
1513
1514 void StartMenuRoot::ProcessKey(int vk)
1515 {
1516 switch(vk) {
1517 case VK_LEFT:
1518 if (_submenu)
1519 CloseOtherSubmenus();
1520 // don't close start menu root
1521 break;
1522
1523 default:
1524 super::ProcessKey(vk);
1525 }
1526 }
1527
1528
1529 int StartMenuHandler::Command(int id, int code)
1530 {
1531 switch(id) {
1532
1533 // start menu root
1534
1535 case IDC_PROGRAMS:
1536 CreateSubmenu(id, CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS, ResString(IDS_PROGRAMS));
1537 break;
1538
1539 case IDC_EXPLORE:
1540 CloseStartMenu(id);
1541 explorer_show_frame(_hwnd, SW_SHOWNORMAL);
1542 break;
1543
1544 case IDC_LAUNCH:
1545 CloseStartMenu(id);
1546 ShowLaunchDialog(g_Globals._hwndDesktopBar);
1547 break;
1548
1549 case IDC_DOCUMENTS:
1550 CreateSubmenu(id, CSIDL_PERSONAL, ResString(IDS_DOCUMENTS));
1551 break;
1552
1553 case IDC_RECENT:
1554 CreateSubmenu(id, CSIDL_RECENT, ResString(IDS_RECENT), STARTMENU_CREATOR(RecentStartMenu));
1555 break;
1556
1557 case IDC_FAVORITES:
1558 CreateSubmenu(id, CSIDL_FAVORITES, ResString(IDS_FAVORITES));
1559 break;
1560
1561 case IDC_BROWSE:
1562 CreateSubmenu(id, ResString(IDS_BROWSE), STARTMENU_CREATOR(BrowseMenu));
1563 break;
1564
1565 case IDC_SETTINGS:
1566 CreateSubmenu(id, ResString(IDS_SETTINGS), STARTMENU_CREATOR(SettingsMenu));
1567 break;
1568
1569 case IDC_SEARCH:
1570 CreateSubmenu(id, ResString(IDS_SEARCH), STARTMENU_CREATOR(SearchMenu));
1571 break;
1572
1573 case IDC_START_HELP:
1574 CloseStartMenu(id);
1575 MessageBox(g_Globals._hwndDesktopBar, TEXT("Help not yet implemented"), ResString(IDS_TITLE), MB_OK);
1576 break;
1577
1578 case IDC_LOGOFF:
1579 /* The shell32 Dialog prompts about some system setting change. This is not what we want to display here.
1580 CloseStartMenu(id);
1581 ShowRestartDialog(g_Globals._hwndDesktopBar, EWX_LOGOFF);*/
1582 DestroyWindow(GetParent(_hwnd));
1583 break;
1584
1585 case IDC_SHUTDOWN:
1586 CloseStartMenu(id);
1587 ShowExitWindowsDialog(g_Globals._hwndDesktopBar);
1588 break;
1589
1590
1591 // settings menu
1592
1593 case ID_DESKTOPBAR_SETTINGS:
1594 CloseStartMenu(id);
1595 ExplorerPropertySheet(g_Globals._hwndDesktopBar);
1596 break;
1597
1598 case IDC_SETTINGS_MENU:
1599 CreateSubmenu(id, CSIDL_CONTROLS, ResString(IDS_SETTINGS_MENU));
1600 break;
1601
1602 case IDC_PRINTERS:
1603 CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS));
1604 break;
1605
1606 case IDC_CONTROL_PANEL:
1607 CloseStartMenu(id);
1608 MainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), FALSE);
1609 break;
1610
1611 case IDC_ADMIN:
1612 CreateSubmenu(id, CSIDL_COMMON_ADMINTOOLS, CSIDL_ADMINTOOLS, ResString(IDS_ADMIN));
1613 break;
1614
1615 case IDC_CONNECTIONS:
1616 CreateSubmenu(id, CSIDL_CONNECTIONS, ResString(IDS_CONNECTIONS));
1617 break;
1618
1619
1620 // browse menu
1621
1622 case IDC_NETWORK:
1623 CreateSubmenu(id, CSIDL_NETWORK, ResString(IDS_NETWORK));
1624 break;
1625
1626 case IDC_DRIVES:
1627 ///@todo exclude removeable drives
1628 CreateSubmenu(id, CSIDL_DRIVES, ResString(IDS_DRIVES));
1629 break;
1630
1631
1632 // search menu
1633
1634 case IDC_SEARCH_PROGRAM:
1635 CloseStartMenu(id);
1636 Dialog::DoModal(IDD_SEARCH_PROGRAM, WINDOW_CREATOR(FindProgramDlg));
1637 break;
1638
1639 case IDC_SEARCH_FILES:
1640 CloseStartMenu(id);
1641 ShowSearchDialog();
1642 break;
1643
1644 case IDC_SEARCH_COMPUTER:
1645 CloseStartMenu(id);
1646 ShowSearchComputer();
1647 break;
1648
1649
1650 default:
1651 return super::Command(id, code);
1652 }
1653
1654 return 0;
1655 }
1656
1657
1658 void StartMenuHandler::ShowSearchDialog()
1659 {
1660 static DynamicFct<SHFINDFILES> SHFindFiles(TEXT("SHELL32"), 90);
1661
1662 if (SHFindFiles)
1663 (*SHFindFiles)(NULL, NULL);
1664 else
1665 MessageBox(0, TEXT("SHFindFiles() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
1666 }
1667
1668 void StartMenuHandler::ShowSearchComputer()
1669 {
1670 static DynamicFct<SHFINDCOMPUTER> SHFindComputer(TEXT("SHELL32"), 91);
1671
1672 if (SHFindComputer)
1673 (*SHFindComputer)(NULL, NULL);
1674 else
1675 MessageBox(0, TEXT("SHFindComputer() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
1676 }
1677
1678 void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner)
1679 {
1680 ///@todo All text phrases should be put into the resources.
1681 static LPCSTR szTitle = "Create New Task";
1682 static LPCSTR szText = "Type the name of a program, folder, document, or Internet resource, and Task Manager will open it for you.";
1683
1684 static DynamicFct<RUNFILEDLG> RunFileDlg(TEXT("SHELL32"), 61);
1685
1686 // Show "Run..." dialog
1687 if (RunFileDlg) {
1688 #define W_VER_NT 0
1689 if ((HIWORD(GetVersion())>>14) == W_VER_NT) {
1690 WCHAR wTitle[40], wText[256];
1691
1692 MultiByteToWideChar(CP_ACP, 0, szTitle, -1, wTitle, 40);
1693 MultiByteToWideChar(CP_ACP, 0, szText, -1, wText, 256);
1694
1695 (*RunFileDlg)(hwndOwner, 0, NULL, (LPCSTR)wTitle, (LPCSTR)wText, RFF_CALCDIRECTORY);
1696 }
1697 else
1698 (*RunFileDlg)(hwndOwner, 0, NULL, szTitle, szText, RFF_CALCDIRECTORY);
1699 }
1700 }
1701
1702 void StartMenuHandler::ShowRestartDialog(HWND hwndOwner, UINT flags)
1703 {
1704 static DynamicFct<RESTARTWINDOWSDLG> RestartDlg(TEXT("SHELL32"), 59);
1705
1706 if (RestartDlg)
1707 (*RestartDlg)(hwndOwner, (LPWSTR)L"You selected <Log Off>.\n\n", flags); ///@todo ANSI string conversion if needed
1708 else
1709 MessageBox(hwndOwner, TEXT("RestartDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
1710 }
1711
1712 void ShowExitWindowsDialog(HWND hwndOwner)
1713 {
1714 static DynamicFct<EXITWINDOWSDLG> ExitWindowsDlg(TEXT("SHELL32"), 60);
1715
1716 if (ExitWindowsDlg)
1717 (*ExitWindowsDlg)(hwndOwner);
1718 else
1719 MessageBox(hwndOwner, TEXT("ExitWindowsDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
1720 }
1721
1722
1723 void SettingsMenu::AddEntries()
1724 {
1725 super::AddEntries();
1726
1727 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1728 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
1729 #endif
1730 AddButton(ResString(IDS_CONTROL_PANEL), ICID_CONFIG, false, IDC_CONTROL_PANEL);
1731
1732 AddButton(ResString(IDS_PRINTERS), ICID_PRINTER, true, IDC_PRINTERS);
1733 AddButton(ResString(IDS_CONNECTIONS), ICID_NETWORK, true, IDC_CONNECTIONS);
1734 AddButton(ResString(IDS_ADMIN), ICID_CONFIG, true, IDC_ADMIN);
1735
1736 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1737 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
1738 #endif
1739 AddButton(ResString(IDS_SETTINGS_MENU), ICID_CONFIG, true, IDC_SETTINGS_MENU);
1740
1741 AddButton(ResString(IDS_DESKTOPBAR_SETTINGS), ICID_CONFIG, false, ID_DESKTOPBAR_SETTINGS);
1742 }
1743
1744 void BrowseMenu::AddEntries()
1745 {
1746 super::AddEntries();
1747
1748 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1749 if (!g_Globals._SHRestricted || !SHRestricted(REST_NONETHOOD)) // or REST_NOENTIRENETWORK ?
1750 #endif
1751 AddButton(ResString(IDS_NETWORK), ICID_NETWORK, true, IDC_NETWORK);
1752
1753 AddButton(ResString(IDS_DRIVES), ICID_FOLDER, true, IDC_DRIVES);
1754 }
1755
1756 void SearchMenu::AddEntries()
1757 {
1758 super::AddEntries();
1759
1760 AddButton(ResString(IDS_SEARCH_PRG), ICID_APPS, false, IDC_SEARCH_PROGRAM);
1761
1762 AddButton(ResString(IDS_SEARCH_FILES), ICID_SEARCH_DOC, false, IDC_SEARCH_FILES);
1763
1764 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1765 if (!g_Globals._SHRestricted || !SHRestricted(REST_HASFINDCOMPUTERS))
1766 #endif
1767 AddButton(ResString(IDS_SEARCH_COMPUTER), ICID_COMPUTER, false, IDC_SEARCH_COMPUTER);
1768 }
1769
1770
1771 void RecentStartMenu::AddEntries()
1772 {
1773 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
1774 StartMenuDirectory& smd = *it;
1775 ShellDirectory& dir = smd._dir;
1776
1777 if (!dir._scanned) {
1778 WaitCursor wait;
1779
1780 #ifdef _LAZY_ICONEXTRACT
1781 dir.smart_scan(SCAN_FILESYSTEM);
1782 #else
1783 dir.smart_scan(SCAN_EXTRACT_ICONS|SCAN_FILESYSTEM);
1784 #endif
1785 }
1786
1787 dir.sort_directory(SORT_DATE);
1788 AddShellEntries(dir, 16, smd._subfolders); ///@todo read max. count of entries from registry
1789 }
1790 }