move search start menu entries into 'Search' submenu
[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
30
31 #include "../utility/utility.h"
32
33 #include "../explorer.h"
34 #include "../globals.h"
35 #include "../externals.h"
36 #include "../explorer_intres.h"
37
38 #include "desktopbar.h"
39 #include "startmenu.h"
40 #include "../dialogs/searchprogram.h"
41
42
43 StartMenu::StartMenu(HWND hwnd)
44 : super(hwnd)
45 {
46 _next_id = IDC_FIRST_MENU;
47 _submenu_id = 0;
48 _border_left = 0;
49 _border_top = 0;
50 _last_pos = WindowRect(hwnd).pos();
51 }
52
53 StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info)
54 : super(hwnd),
55 _create_info(create_info)
56 {
57 for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
58 if (*it)
59 _dirs.push_back(ShellDirectory(Desktop(), *it, _hwnd));
60
61 _next_id = IDC_FIRST_MENU;
62 _submenu_id = 0;
63 _border_left = 0;
64 _border_top = create_info._border_top;
65 _last_pos = WindowRect(hwnd).pos();
66 }
67
68 StartMenu::~StartMenu()
69 {
70 SendParent(PM_STARTMENU_CLOSED);
71 }
72
73
74 // We need this wrapper function for s_wcStartMenu, it calls the WIN32 API,
75 // though static C++ initializers are not allowed for Winelib applications.
76 BtnWindowClass& StartMenu::GetWndClasss()
77 {
78 static BtnWindowClass s_wcStartMenu(CLASSNAME_STARTMENU);
79
80 return s_wcStartMenu;
81 }
82
83
84 Window::CREATORFUNC StartMenu::s_def_creator = STARTMENU_CREATOR(StartMenu);
85
86 HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndParent, LPCTSTR title, CREATORFUNC creator)
87 {
88 UINT style, ex_style;
89 int top_height;
90
91 if (hwndParent) {
92 style = WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE;
93 ex_style = 0;
94 top_height = STARTMENU_TOP_BTN_SPACE;
95 } else {
96 style = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPCHILDREN|WS_VISIBLE;
97 ex_style = WS_EX_TOOLWINDOW;
98 top_height = 0;
99 }
100
101 RECT rect = {x, y, x+STARTMENU_WIDTH_MIN, y+top_height}; // start height before adding an menu button
102
103 AdjustWindowRectEx(&rect, style, FALSE, ex_style);
104
105 StartMenuCreateInfo create_info;
106
107 create_info._folders = folders;
108 create_info._border_top = top_height;
109 create_info._creator = creator;
110
111 if (title)
112 create_info._title = title;
113
114 HWND hwnd = Window::Create(creator, &create_info, ex_style, GetWndClasss(), title,
115 style, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndParent);
116
117 // make sure the window is not off the screen
118 MoveVisible(hwnd);
119
120 return hwnd;
121 }
122
123
124 LRESULT StartMenu::Init(LPCREATESTRUCT pcs)
125 {
126 try {
127 AddEntries();
128
129 if (super::Init(pcs))
130 return 1;
131
132 // create buttons for registered entries in _entries
133 for(ShellEntryMap::const_iterator it=_entries.begin(); it!=_entries.end(); ++it) {
134 const StartMenuEntry& sme = it->second;
135 bool hasSubmenu = false;
136
137 for(ShellEntrySet::const_iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it)
138 if ((*it)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
139 hasSubmenu = true;
140
141 AddButton(sme._title, sme._hIcon, hasSubmenu, it->first);
142 }
143
144 if (!GetWindow(_hwnd, GW_CHILD))
145 AddButton(ResString(IDS_EMPTY), 0, false, (UINT)-1, WS_VISIBLE|WS_CHILD|BS_OWNERDRAW|WS_DISABLED);
146 } catch(COMException& e) {
147 HandleException(e, pcs->hwndParent); // destroys the start menu window while switching focus
148 }
149
150 return 0;
151 }
152
153 void StartMenu::AddEntries()
154 {
155 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
156 StartMenuDirectory& smd = *it;
157 ShellDirectory& dir = smd._dir;
158
159 if (!dir._scanned) {
160 WaitCursor wait;
161
162 dir.smart_scan();
163 }
164
165 AddShellEntries(dir, -1, smd._subfolders);
166 }
167 }
168
169
170 void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, bool subfolders)
171 {
172 int cnt = 0;
173
174 for(const Entry*entry=dir._down; entry; entry=entry->_next) {
175 // hide files like "desktop.ini"
176 if (entry->_shell_attribs & SFGAO_HIDDEN)
177 //not appropriate for drive roots: if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
178 continue;
179
180 // hide subfolders if requested
181 if (!subfolders)
182 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
183 continue;
184
185 // only 'max' entries shall be added.
186 if (++cnt == max)
187 break;
188
189 const ShellEntry* shell_entry = static_cast<const ShellEntry*>(entry);
190
191 AddEntry(dir._folder, shell_entry);
192 }
193 }
194
195
196 LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
197 {
198 switch(nmsg) {
199 case WM_PAINT:
200 DrawFloatingButton(PaintCanvas(_hwnd));
201 break;
202
203 case WM_SIZE:
204 ResizeButtons(LOWORD(lparam)-_border_left);
205 break;
206
207 case WM_MOVE: {
208 POINTS& pos = MAKEPOINTS(lparam);
209
210 // move open submenus of floating menus
211 if (_submenu) {
212 int dx = pos.x - _last_pos.x;
213 int dy = pos.y - _last_pos.y;
214
215 if (dx || dy) {
216 WindowRect rt(_submenu);
217 SetWindowPos(_submenu, 0, rt.left+dx, rt.top+dy, 0, 0, SWP_NOSIZE|SWP_NOACTIVATE);
218 //MoveVisible(_submenu);
219 }
220 }
221
222 _last_pos.x = pos.x;
223 _last_pos.y = pos.y;
224 goto def;}
225
226 case WM_NCHITTEST: {
227 LRESULT res = super::WndProc(nmsg, wparam, lparam);
228
229 if (res>=HTSIZEFIRST && res<=HTSIZELAST)
230 return HTCLIENT; // disable window resizing
231
232 return res;}
233
234 case WM_LBUTTONDOWN: {
235 RECT rect;
236
237 // check mouse cursor for coordinates of floating button
238 GetFloatingButonRect(&rect);
239
240 if (PtInRect(&rect, Point(lparam))) {
241 // create a floating copy of the current start menu
242 WindowRect pos(_hwnd);
243
244 ///@todo do something similar to StartMenuRoot::TrackStartmenu() in order to automatically close submenus when clicking on the desktop background
245 StartMenu::Create(pos.left+3, pos.bottom-3, _create_info._folders, 0, _create_info._title, _create_info._creator);
246 CloseStartMenu();
247 }
248 break;}
249
250 case WM_SYSCOMMAND:
251 if ((wparam&0xFFF0) == SC_SIZE)
252 return 0; // disable window resizing
253 goto def;
254
255 case WM_ACTIVATEAPP:
256 // close start menu when activating another application
257 if (!wparam)
258 CloseStartMenu();
259 break; // don't call super::WndProc in case "this" has been deleted
260
261 case WM_CANCELMODE:
262 CloseStartMenu();
263 break;
264
265 case PM_STARTENTRY_FOCUSED: { ///@todo use TrackMouseEvent() and WM_MOUSEHOVER to wait a bit before opening submenus
266 BOOL hasSubmenu = wparam;
267 HWND hctrl = (HWND)lparam;
268
269 // automatically open submenus
270 if (hasSubmenu) {
271 UpdateWindow(_hwnd); // draw focused button before waiting on submenu creation
272 //SendMessage(_hwnd, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hctrl),BN_CLICKED), (LPARAM)hctrl);
273 Command(GetDlgCtrlID(hctrl), BN_CLICKED);
274 } else {
275 // close any open submenu
276 CloseOtherSubmenus(0);
277 }
278 break;}
279
280 case PM_STARTENTRY_LAUNCHED:
281 if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus
282 return 0;
283
284 // route message to the parent menu and close menus after launching an entry
285 if (!SendParent(nmsg, wparam, lparam))
286 DestroyWindow(_hwnd);
287 return 1; // signal that we have received and processed the message
288
289 case PM_STARTMENU_CLOSED:
290 _submenu = 0;
291 break;
292
293 default: def:
294 return super::WndProc(nmsg, wparam, lparam);
295 }
296
297 return 0;
298 }
299
300
301 void StartMenu::DrawFloatingButton(HDC hdc)
302 {
303 static ResIconEx floatingIcon(IDI_FLOATING, 8, 4);
304
305 ClientRect clnt(_hwnd);
306
307 DrawIconEx(hdc, clnt.right-12, 0, floatingIcon, 8, 4, 0, 0, DI_NORMAL);
308 }
309
310 void StartMenu::GetFloatingButonRect(LPRECT prect)
311 {
312 GetClientRect(_hwnd, prect);
313
314 prect->right -= 4;
315 prect->left = prect->right - 8;
316 prect->bottom = 4;
317 }
318
319
320 // resize child button controls to accomodate for new window size
321 void StartMenu::ResizeButtons(int cx)
322 {
323 HDWP hdwp = BeginDeferWindowPos(10);
324
325 for(HWND ctrl=GetWindow(_hwnd,GW_CHILD); ctrl; ctrl=GetNextWindow(ctrl,GW_HWNDNEXT)) {
326 ClientRect rt(ctrl);
327
328 if (rt.right != cx) {
329 int height = rt.bottom - rt.top;
330
331 // special handling for separator controls
332 if (!height && (GetWindowStyle(ctrl)&SS_TYPEMASK)==SS_ETCHEDHORZ)
333 height = 2;
334
335 hdwp = DeferWindowPos(hdwp, ctrl, 0, 0, 0, cx, height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
336 }
337 }
338
339 EndDeferWindowPos(hdwp);
340 }
341
342
343 int StartMenu::Command(int id, int code)
344 {
345 switch(id) {
346 case IDCANCEL:
347 DestroyWindow(_hwnd);
348 break;
349
350 default: {
351 ShellEntryMap::iterator found = _entries.find(id);
352
353 if (found != _entries.end()) {
354 ActivateEntry(id, found->second._entries);
355 break;
356 }
357
358 return super::Command(id, code);}
359 }
360
361 return 0;
362 }
363
364
365 StartMenuEntry& StartMenu::AddEntry(LPCTSTR title, HICON hIcon, UINT id)
366 {
367 if (id == (UINT)-1)
368 id = ++_next_id;
369
370 StartMenuEntry& sme = _entries[id];
371
372 sme._title = title;
373 sme._hIcon = hIcon;
374
375 return sme;
376 }
377
378 StartMenuEntry& StartMenu::AddEntry(const ShellFolder folder, const ShellEntry* entry)
379 {
380 HICON hIcon = entry->_hIcon;
381
382 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
383 hIcon = SmallIcon(IDI_EXPLORER);
384
385 const String& entry_name = folder.get_name(entry->_pidl);
386
387 // search for an already existing subdirectory entry with the same name
388 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
389 for(ShellEntryMap::iterator it=_entries.begin(); it!=_entries.end(); ++it) {
390 StartMenuEntry& sme = it->second;
391
392 if (sme._title == entry_name) ///@todo speed up by using a map indexed by name
393 for(ShellEntrySet::iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
394 if ((*it2)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
395 // merge the new shell entry with the existing of the same name
396 sme._entries.insert(entry);
397 return sme;
398 }
399 }
400 }
401
402 StartMenuEntry& sme = AddEntry(entry_name, hIcon);
403
404 sme._entries.insert(entry);
405
406 return sme;
407 }
408
409
410 void StartMenu::AddButton(LPCTSTR title, HICON hIcon, bool hasSubmenu, UINT id, DWORD style)
411 {
412 WindowRect rect(_hwnd);
413 ClientRect clnt(_hwnd);
414
415 // increase window height to make room for the new button
416 rect.top -= STARTMENU_LINE_HEIGHT;
417
418 // move down if we are too high now
419 if (rect.top < 0) {
420 rect.top += STARTMENU_LINE_HEIGHT;
421 rect.bottom += STARTMENU_LINE_HEIGHT;
422 }
423
424 // widen window, if it is too small
425 int text_width = StartMenuButton::GetTextWidth(title,_hwnd) + 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
426
427 int cx = clnt.right - _border_left;
428 if (text_width > cx)
429 rect.right += text_width-cx;
430
431 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
432
433 StartMenuCtrl(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left,
434 title, id, hIcon, hasSubmenu, style);
435 }
436
437 void StartMenu::AddSeparator()
438 {
439 WindowRect rect(_hwnd);
440 ClientRect clnt(_hwnd);
441
442 // increase window height to make room for the new separator
443 rect.top -= STARTMENU_SEP_HEIGHT;
444
445 // move down if we are too high now
446 if (rect.top < 0) {
447 rect.top += STARTMENU_LINE_HEIGHT;
448 rect.bottom += STARTMENU_LINE_HEIGHT;
449 }
450
451 MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
452
453 StartMenuSeparator(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left);
454 }
455
456
457 bool StartMenu::CloseOtherSubmenus(int id)
458 {
459 if (_submenu) {
460 if (IsWindow(_submenu)) {
461 if (_submenu_id == id)
462 return false;
463 else {
464 DestroyWindow(_submenu);
465 _submenu_id = 0;
466 // _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
467 }
468 }
469
470 _submenu = 0;
471 }
472
473 return true;
474 }
475
476
477 void StartMenu::CreateSubmenu(int id, LPCTSTR title, CREATORFUNC creator)
478 {
479 CreateSubmenu(id, StartMenuFolders(), title, creator);
480 }
481
482 void StartMenu::CreateSubmenu(int id, int folder_id, LPCTSTR title, CREATORFUNC creator)
483 {
484 try {
485 SpecialFolderPath folder(folder_id, _hwnd);
486
487 StartMenuFolders new_folders;
488 new_folders.push_back(folder);
489
490 CreateSubmenu(id, new_folders, title, creator);
491 } catch(COMException&) {
492 // ignore Exception and don't display anything
493 }
494 }
495
496 void StartMenu::CreateSubmenu(int id, int folder_id1, int folder_id2, LPCTSTR title, CREATORFUNC creator)
497 {
498 StartMenuFolders new_folders;
499
500 try {
501 new_folders.push_back(SpecialFolderPath(folder_id1, _hwnd));
502 } catch(COMException&) {
503 }
504
505 try {
506 new_folders.push_back(SpecialFolderPath(folder_id2, _hwnd));
507 } catch(COMException&) {
508 }
509
510 if (!new_folders.empty())
511 CreateSubmenu(id, new_folders, title, creator);
512 }
513
514 void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTSTR title, CREATORFUNC creator)
515 {
516 // Only open one submenu at a time.
517 if (!CloseOtherSubmenus(id))
518 return;
519
520 HWND btn = GetDlgItem(_hwnd, id);
521 int x, y;
522
523 if (btn) {
524 WindowRect pos(btn);
525
526 x = pos.right; // Submenus should overlap their parent a bit.
527 y = pos.top+STARTMENU_LINE_HEIGHT-_border_top;
528 } else {
529 WindowRect pos(_hwnd);
530
531 x = pos.right;
532 y = pos.top;
533 }
534
535 _submenu_id = id;
536 _submenu = StartMenu::Create(x, y, new_folders, _hwnd, title, creator);
537 }
538
539
540 void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries)
541 {
542 StartMenuFolders new_folders;
543 String title;
544
545 for(ShellEntrySet::const_iterator it=entries.begin(); it!=entries.end(); ++it) {
546 ShellEntry* entry = const_cast<ShellEntry*>(*it);
547
548 if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
549 new_folders.push_back(entry->create_absolute_pidl());
550
551 if (title.empty())
552 title = entry->_display_name;
553 } else {
554 // If the entry is no subdirectory, there can only be one shell entry.
555 assert(entries.size()==1);
556
557 entry->launch_entry(_hwnd); ///@todo launch in the background; specify correct HWND for error message box titles
558
559 // close start menus after launching the selected entry
560 CloseStartMenu(id);
561
562 // we deleted 'this' - ensure we leave loop and function
563 return;
564 }
565 }
566
567 if (!new_folders.empty()) {
568 // Only open one submenu at a time.
569 if (!CloseOtherSubmenus(id))
570 return;
571
572 CreateSubmenu(id, new_folders, title);
573 }
574 }
575
576
577 /// close all windows of the start menu popup
578 void StartMenu::CloseStartMenu(int id)
579 {
580 if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) { // don't automatically close floating menus
581 if (!SendParent(PM_STARTENTRY_LAUNCHED, id, (LPARAM)_hwnd))
582 DestroyWindow(_hwnd);
583 } else if (_submenu) // instead close submenus of floating parent menus
584 CloseSubmenus();
585 }
586
587
588 int StartMenuButton::GetTextWidth(LPCTSTR title, HWND hwnd)
589 {
590 WindowCanvas canvas(hwnd);
591 FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
592
593 RECT rect = {0, 0, 0, 0};
594 DrawText(canvas, title, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
595
596 return rect.right-rect.left;
597 }
598
599
600 LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
601 {
602 switch(nmsg) {
603 case WM_MOUSEMOVE:
604 // automatically set the focus to startmenu entries when moving the mouse over them
605 if (GetFocus()!=_hwnd && !(GetWindowStyle(_hwnd)&WS_DISABLED))
606 SetFocus(_hwnd);
607 break;
608
609 case WM_SETFOCUS:
610 PostParent(PM_STARTENTRY_FOCUSED, _hasSubmenu, (LPARAM)_hwnd);
611 goto def;
612
613 case WM_CANCELMODE:
614 // route WM_CANCELMODE to the startmenu window
615 return SendParent(nmsg, wparam, lparam);
616
617 default: def:
618 return super::WndProc(nmsg, wparam, lparam);
619 }
620
621 return 0;
622 }
623
624 void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis)
625 {
626 UINT style = DFCS_BUTTONPUSH;
627
628 if (dis->itemState & ODS_DISABLED)
629 style |= DFCS_INACTIVE;
630
631 POINT iconPos = {dis->rcItem.left+2, (dis->rcItem.top+dis->rcItem.bottom-16)/2};
632 RECT textRect = {dis->rcItem.left+16+4, dis->rcItem.top+2, dis->rcItem.right-4, dis->rcItem.bottom-4};
633
634 if (dis->itemState & ODS_SELECTED) {
635 style |= DFCS_PUSHED;
636 ++iconPos.x; ++iconPos.y;
637 ++textRect.left; ++textRect.top;
638 ++textRect.right; ++textRect.bottom;
639 }
640
641 int bk_color = COLOR_BTNFACE;
642 int text_color = COLOR_BTNTEXT;
643
644 if (dis->itemState & ODS_FOCUS) {
645 bk_color = COLOR_HIGHLIGHT;
646 text_color = COLOR_HIGHLIGHTTEXT;
647 }
648
649 HBRUSH bk_brush = GetSysColorBrush(bk_color);
650
651 FillRect(dis->hDC, &dis->rcItem, bk_brush);
652 DrawIconEx(dis->hDC, iconPos.x, iconPos.y, _hIcon, 16, 16, 0, bk_brush, DI_NORMAL);
653
654 // draw submenu arrow at the right
655 if (_hasSubmenu) {
656 static SmallIcon arrowIcon(IDI_ARROW);
657 static SmallIcon selArrowIcon(IDI_ARROW_SELECTED);
658
659 DrawIconEx(dis->hDC, dis->rcItem.right-16, iconPos.y,
660 dis->itemState&ODS_FOCUS?selArrowIcon:arrowIcon, 16, 16, 0, bk_brush, DI_NORMAL);
661 }
662
663 TCHAR title[BUFFER_LEN];
664 GetWindowText(_hwnd, title, BUFFER_LEN);
665
666 BkMode bk_mode(dis->hDC, TRANSPARENT);
667
668 if (dis->itemState & (ODS_DISABLED|ODS_GRAYED))
669 DrawGrayText(dis, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
670 else {
671 TextColor lcColor(dis->hDC, GetSysColor(text_color));
672 DrawText(dis->hDC, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
673 }
674 }
675
676
677 StartMenuRoot::StartMenuRoot(HWND hwnd)
678 : super(hwnd)
679 {
680 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
681 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCOMMONGROUPS))
682 #endif
683 try {
684 // insert directory "All Users\Start Menu"
685 ShellDirectory cmn_startmenu(Desktop(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
686 _dirs.push_back(StartMenuDirectory(cmn_startmenu, false)); // don't add subfolders
687 } catch(COMException&) {
688 // ignore exception and don't show additional shortcuts
689 }
690
691 try {
692 // insert directory "<user name>\Start Menu"
693 ShellDirectory usr_startmenu(Desktop(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
694 _dirs.push_back(StartMenuDirectory(usr_startmenu, false)); // don't add subfolders
695 } catch(COMException&) {
696 // ignore exception and don't show additional shortcuts
697 }
698
699 // read size of logo bitmap
700 BITMAP bmp_hdr;
701 GetObject(ResBitmap(IDB_LOGOV), sizeof(BITMAP), &bmp_hdr);
702 _logo_size.cx = bmp_hdr.bmWidth;
703 _logo_size.cy = bmp_hdr.bmHeight;
704
705 _border_left = _logo_size.cx;
706 }
707
708
709 HWND StartMenuRoot::Create(HWND hwndDesktopBar)
710 {
711 WindowRect pos(hwndDesktopBar);
712
713 return Window::Create(WINDOW_CREATOR(StartMenuRoot), 0, GetWndClasss(), TITLE_STARTMENU,
714 WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE, pos.left, pos.top-4, STARTMENU_WIDTH_MIN, 4, hwndDesktopBar);
715 }
716
717
718 void StartMenuRoot::TrackStartmenu()
719 {
720 MSG msg;
721 HWND hwnd = _hwnd;
722
723 while(IsWindow(hwnd)) {
724 if (!GetMessage(&msg, 0, 0, 0)) {
725 PostQuitMessage(msg.wParam);
726 break;
727 }
728
729 // Check for a mouse click on any window, which is not part of the start menu
730 if (msg.message==WM_LBUTTONDOWN || msg.message==WM_MBUTTONDOWN || msg.message==WM_RBUTTONDOWN) {
731 StartMenu* menu_wnd = NULL;
732
733 for(HWND hwnd=msg.hwnd; hwnd; hwnd=GetParent(hwnd)) {
734 menu_wnd = WINDOW_DYNAMIC_CAST(StartMenu, hwnd);
735
736 if (menu_wnd)
737 break;
738 }
739
740 if (!menu_wnd) {
741 DestroyWindow(_hwnd);
742 break;
743 }
744 }
745
746 try {
747 if (pretranslate_msg(&msg))
748 continue;
749
750 if (dispatch_dialog_msg(&msg))
751 continue;
752
753 TranslateMessage(&msg);
754
755 try {
756 DispatchMessage(&msg);
757 } catch(COMException& e) {
758 HandleException(e, _hwnd);
759 }
760 } catch(COMException& e) {
761 HandleException(e, _hwnd);
762 }
763 }
764 }
765
766
767 LRESULT StartMenuRoot::Init(LPCREATESTRUCT pcs)
768 {
769 // add buttons for entries in _entries
770 if (super::Init(pcs))
771 return 1;
772
773 AddSeparator();
774
775
776 #ifdef __MINGW32__
777 HKEY hkey, hkeyAdv;
778 DWORD value, len;
779
780 if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), &hkey))
781 hkey = 0;
782
783 if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hkeyAdv))
784 hkeyAdv = 0;
785
786 #define IS_VALUE_ZERO(hk, name) \
787 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || !value))
788
789 #define IS_VALUE_NOT_ZERO(hk, name) \
790 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || value>0))
791 #endif
792
793
794 // insert hard coded start entries
795 AddButton(ResString(IDS_PROGRAMS), 0, true, IDC_PROGRAMS);
796
797 AddButton(ResString(IDS_DOCUMENTS), 0, true, IDC_DOCUMENTS);
798
799 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
800 if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
801 #else
802 if (IS_VALUE_ZERO(hkey, _T("NoRecentDocsMenu")))
803 #endif
804 AddButton(ResString(IDS_RECENT), 0, true, IDC_RECENT);
805
806 AddButton(ResString(IDS_FAVORITES), 0, true, IDC_FAVORITES);
807
808 AddButton(ResString(IDS_SETTINGS), 0, true, IDC_SETTINGS);
809
810 AddButton(ResString(IDS_BROWSE), 0, true, IDC_BROWSE);
811
812 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
813 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOFIND))
814 #else
815 if (IS_VALUE_ZERO(hkey, _T("NoFind")))
816 #endif
817 AddButton(ResString(IDS_SEARCH), 0, true, IDC_SEARCH);
818
819 AddButton(ResString(IDS_START_HELP), 0, false, IDC_START_HELP);
820
821 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
822 if (!g_Globals._SHRestricted || !SHRestricted(REST_NORUN))
823 #else
824 if (IS_VALUE_ZERO(hkey, _T("NoRun")))
825 #endif
826 AddButton(ResString(IDS_LAUNCH), 0, false, IDC_LAUNCH);
827
828
829 AddSeparator();
830
831
832 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
833 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCLOSE))
834 #else
835 if (IS_VALUE_NOT_ZERO(hkeyAdv, _T("StartMenuLogoff")))
836 #endif
837 AddButton(ResString(IDS_LOGOFF), SmallIcon(IDI_LOGOFF), false, IDC_LOGOFF);
838
839
840 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
841 if (!g_Globals._SHRestricted || SHRestricted(REST_STARTMENULOGOFF) != 1)
842 #else
843 if (IS_VALUE_ZERO(hkey, _T("NoClose")))
844 #endif
845 AddButton(ResString(IDS_SHUTDOWN), SmallIcon(IDI_LOGOFF), false, IDC_SHUTDOWN);
846
847
848 #ifdef __MINGW32__
849 RegCloseKey(hkeyAdv);
850 RegCloseKey(hkey);
851 #endif
852
853 return 0;
854 }
855
856
857 void StartMenuRoot::AddEntries()
858 {
859 super::AddEntries();
860
861 AddButton(ResString(IDS_EXPLORE), SmallIcon(IDI_EXPLORER), false, IDC_EXPLORE);
862 }
863
864
865 LRESULT StartMenuRoot::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
866 {
867 switch(nmsg) {
868 case WM_PAINT: {
869 int clr_bits;
870 {WindowCanvas dc(_hwnd); clr_bits=GetDeviceCaps(dc, BITSPIXEL);}
871 bool logo256 = clr_bits<=8;
872
873 PaintCanvas canvas(_hwnd);
874
875 MemCanvas mem_dc;
876 ResBitmap bmp(logo256? IDB_LOGOV256: IDB_LOGOV);
877 BitmapSelection sel(mem_dc, bmp);
878
879 ClientRect clnt(_hwnd);
880 int h = min(_logo_size.cy, clnt.bottom);
881
882 RECT rect = {0, 0, _logo_size.cx-1, clnt.bottom-h};
883 HBRUSH hbr = CreateSolidBrush(logo256? RGB(166,202,240): RGB(255,255,255)); // same color as the background color in the logo bitmap
884 FillRect(canvas, &rect, hbr);
885 DeleteObject(hbr);
886 //PatBlt(canvas, _logo_size.cx-1, 0, 1, clnt.bottom-h, WHITENESS);
887 PatBlt(canvas, _logo_size.cx-1, 0, 1, clnt.bottom-h, WHITENESS);
888
889 BitBlt(canvas, 0, clnt.bottom-h, _logo_size.cx, h, mem_dc, 0, 0, SRCCOPY);
890
891 if (!logo256) {
892 rect.left = rect.right++;
893 rect.bottom = clnt.bottom;
894 HBRUSH hbr_border = GetStockBrush(GRAY_BRUSH); //CreateSolidBrush(RGB(71,88,85));
895 FillRect(canvas, &rect, hbr_border);
896 //DeleteObject(hbr_border);
897 }
898 break;}
899
900 default:
901 return super::WndProc(nmsg, wparam, lparam);
902 }
903
904 return 0;
905 }
906
907
908 int StartMenuRoot::Command(int id, int code)
909 {
910 switch(id) {
911 case IDC_PROGRAMS:
912 CreateSubmenu(id, CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS, ResString(IDS_PROGRAMS));
913 break;
914
915 case IDC_EXPLORE:
916 CloseStartMenu(id);
917 explorer_show_frame(_hwnd, SW_SHOWNORMAL);
918 break;
919
920 case IDC_LAUNCH: {
921 HWND hwndDesktopBar = GetWindow(_hwnd, GW_OWNER);
922 CloseStartMenu(id);
923 ShowLaunchDialog(hwndDesktopBar);
924 break;}
925
926 case IDC_DOCUMENTS:
927 CreateSubmenu(id, CSIDL_PERSONAL, ResString(IDS_DOCUMENTS));
928 break;
929
930 case IDC_RECENT:
931 CreateSubmenu(id, CSIDL_RECENT, ResString(IDS_RECENT), STARTMENU_CREATOR(RecentStartMenu));
932 break;
933
934 case IDC_FAVORITES:
935 CreateSubmenu(id, CSIDL_FAVORITES, ResString(IDS_FAVORITES));
936 break;
937
938 case IDC_BROWSE:
939 CreateSubmenu(id, ResString(IDS_BROWSE), STARTMENU_CREATOR(BrowseMenu));
940 break;
941
942 case IDC_SETTINGS:
943 CreateSubmenu(id, ResString(IDS_SETTINGS), STARTMENU_CREATOR(SettingsMenu));
944 break;
945
946 case IDC_SEARCH:
947 CreateSubmenu(id, ResString(IDS_SEARCH), STARTMENU_CREATOR(SearchMenu));
948 break;
949
950 case IDC_LOGOFF:
951 /* The shell32 Dialog prompts about some system setting change. This is not what we want to display here.
952 HWND hwndDesktopBar = GetWindow(_hwnd, GW_OWNER);
953 CloseStartMenu(id);
954 ShowRestartDialog(hwndDesktopBar, EWX_LOGOFF);*/
955 DestroyWindow(GetParent(_hwnd));
956 break;
957
958 case IDC_SHUTDOWN: {
959 HWND hwndDesktopBar = GetWindow(_hwnd, GW_OWNER);
960 CloseStartMenu(id);
961 ShowExitWindowsDialog(hwndDesktopBar);
962 break;}
963
964 default:
965 return super::Command(id, code);
966 }
967
968 return 0;
969 }
970
971
972 void StartMenuRoot::ShowLaunchDialog(HWND hwndDesktopBar)
973 {
974 ///@todo All text phrases should be put into the resources.
975 static LPCSTR szTitle = "Create New Task";
976 static LPCSTR szText = "Type the name of a program, folder, document, or Internet resource, and Task Manager will open it for you.";
977
978 static DynamicFct<RUNFILEDLG> RunFileDlg(TEXT("SHELL32"), 61);
979
980 // Show "Run..." dialog
981 if (RunFileDlg) {
982 #define W_VER_NT 0
983 if ((HIWORD(GetVersion())>>14) == W_VER_NT) {
984 WCHAR wTitle[40], wText[256];
985
986 MultiByteToWideChar(CP_ACP, 0, szTitle, -1, wTitle, 40);
987 MultiByteToWideChar(CP_ACP, 0, szText, -1, wText, 256);
988
989 (*RunFileDlg)(hwndDesktopBar, 0, NULL, (LPCSTR)wTitle, (LPCSTR)wText, RFF_CALCDIRECTORY);
990 }
991 else
992 (*RunFileDlg)(hwndDesktopBar, 0, NULL, szTitle, szText, RFF_CALCDIRECTORY);
993 }
994 }
995
996 void StartMenuRoot::ShowExitWindowsDialog(HWND hwndOwner)
997 {
998 static DynamicFct<EXITWINDOWSDLG> ExitWindowsDlg(TEXT("SHELL32"), 60);
999
1000 if (ExitWindowsDlg)
1001 (*ExitWindowsDlg)(hwndOwner);
1002 }
1003
1004 void StartMenuRoot::ShowRestartDialog(HWND hwndOwner, UINT flags)
1005 {
1006 static DynamicFct<RESTARTWINDOWSDLG> RestartDlg(TEXT("SHELL32"), 59);
1007
1008 if (RestartDlg)
1009 (*RestartDlg)(hwndOwner, (LPWSTR)L"You selected <Log Off>.\n\n", flags); ///@todo ANSI string conversion if needed
1010 }
1011
1012
1013 void SettingsMenu::AddEntries()
1014 {
1015 super::AddEntries();
1016
1017 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1018 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
1019 #endif
1020 AddButton(ResString(IDS_CONTROL_PANEL), 0, false, IDC_CONTROL_PANEL);
1021
1022 AddButton(ResString(IDS_PRINTERS), 0, true, IDC_PRINTERS);
1023 AddButton(ResString(IDS_CONNECTIONS), 0, true, IDC_CONNECTIONS);
1024 AddButton(ResString(IDS_ADMIN), 0, true, IDC_ADMIN);
1025
1026 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1027 if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
1028 #endif
1029 AddButton(ResString(IDS_SETTINGS_MENU), 0, true, IDC_SETTINGS_MENU);
1030 }
1031
1032 int SettingsMenu::Command(int id, int code)
1033 {
1034 switch(id) {
1035 case IDC_SETTINGS_MENU:
1036 CreateSubmenu(id, CSIDL_CONTROLS, ResString(IDS_SETTINGS_MENU));
1037 break;
1038
1039 case IDC_PRINTERS:
1040 CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS));
1041 break;
1042
1043 case IDC_CONTROL_PANEL:
1044 CloseStartMenu(id);
1045 MainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), FALSE);
1046 break;
1047
1048 case IDC_ADMIN:
1049 CreateSubmenu(id, CSIDL_COMMON_ADMINTOOLS, CSIDL_ADMINTOOLS, ResString(IDS_ADMIN));
1050 break;
1051
1052 case IDC_CONNECTIONS:
1053 CreateSubmenu(id, CSIDL_CONNECTIONS, ResString(IDS_CONNECTIONS));
1054 break;
1055
1056 default:
1057 return super::Command(id, code);
1058 }
1059
1060 return 0;
1061 }
1062
1063
1064 void BrowseMenu::AddEntries()
1065 {
1066 super::AddEntries();
1067
1068 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1069 if (!g_Globals._SHRestricted || !SHRestricted(REST_NONETHOOD)) // or REST_NOENTIRENETWORK ?
1070 #endif
1071 AddButton(ResString(IDS_NETWORK), 0, true, IDC_NETWORK);
1072
1073 AddButton(ResString(IDS_DRIVES), 0, true, IDC_DRIVES);
1074 }
1075
1076 int BrowseMenu::Command(int id, int code)
1077 {
1078 switch(id) {
1079 case IDC_NETWORK:
1080 CreateSubmenu(id, CSIDL_NETWORK, ResString(IDS_NETWORK));
1081 break;
1082
1083 case IDC_DRIVES:
1084 ///@todo exclude removeable drives
1085 CreateSubmenu(id, CSIDL_DRIVES, ResString(IDS_DRIVES));
1086 break;
1087
1088 default:
1089 return super::Command(id, code);
1090 }
1091
1092 return 0;
1093 }
1094
1095
1096 void SearchMenu::AddEntries()
1097 {
1098 super::AddEntries();
1099
1100 AddButton(ResString(IDS_SEARCH_PRG), 0, false, IDC_SEARCH_PROGRAM);
1101
1102 AddButton(ResString(IDS_SEARCH_FILES), 0, false, IDC_SEARCH_FILES);
1103
1104 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1105 if (!g_Globals._SHRestricted || !SHRestricted(REST_HASFINDCOMPUTERS))
1106 #endif
1107 AddButton(ResString(IDS_SEARCH_COMPUTER), 0, false, IDC_SEARCH_COMPUTER);
1108 }
1109
1110 int SearchMenu::Command(int id, int code)
1111 {
1112 switch(id) {
1113 case IDC_SEARCH_PROGRAM:
1114 CloseStartMenu(id);
1115 Dialog::DoModal(IDD_SEARCH_PROGRAM, WINDOW_CREATOR(FindProgramDlg));
1116 break;
1117
1118 case IDC_SEARCH_FILES:
1119 ShowSearchDialog();
1120 break;
1121
1122 case IDC_SEARCH_COMPUTER:
1123 ShowSearchComputer();
1124 break;
1125
1126 default:
1127 return super::Command(id, code);
1128 }
1129
1130 return 0;
1131 }
1132
1133 void SearchMenu::ShowSearchDialog()
1134 {
1135 static DynamicFct<SHFINDFILES> SHFindFiles(TEXT("SHELL32"), 90);
1136
1137 if (SHFindFiles)
1138 (*SHFindFiles)(NULL, NULL);
1139 }
1140
1141 void SearchMenu::ShowSearchComputer()
1142 {
1143 static DynamicFct<SHFINDCOMPUTER> SHFindComputer(TEXT("SHELL32"), 91);
1144
1145 if (SHFindComputer)
1146 (*SHFindComputer)(NULL, NULL);
1147 }
1148
1149
1150 RecentStartMenu::RecentStartMenu(HWND hwnd, const StartMenuCreateInfo& create_info)
1151 : super(hwnd, create_info)
1152 {
1153 }
1154
1155 void RecentStartMenu::AddEntries()
1156 {
1157 for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
1158 StartMenuDirectory& smd = *it;
1159 ShellDirectory& dir = smd._dir;
1160
1161 if (!dir._scanned) {
1162 WaitCursor wait;
1163
1164 dir.smart_scan();
1165 }
1166
1167 dir.sort_directory(SORT_DATE);
1168 AddShellEntries(dir, 16, smd._subfolders); ///@todo read max. count of entries from registry
1169 }
1170 }