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