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