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