1 /*
2  * Copyright 2003, 2004 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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  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 <precomp.h>
34 
35 #include "desktopbar.h"
36 #include "startmenu.h"
37 
38 #include "../dialogs/searchprogram.h"
39 #include "../dialogs/settings.h"
40 
41 
42 #define	SHELLPATH_CONTROL_PANEL		TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}")
43 #define	SHELLPATH_PRINTERS			TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")
44 #define	SHELLPATH_NET_CONNECTIONS	TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}")
45 
46 
StartMenu(HWND hwnd,int icon_size)47 StartMenu::StartMenu(HWND hwnd, int icon_size)
48  :	super(hwnd),
49 	_icon_size(icon_size)
50 {
51 	_next_id = IDC_FIRST_MENU;
52 	_submenu_id = 0;
53 
54 	_border_left = 0;
55 	_border_top = 0;
56 	_bottom_max = INT_MAX;
57 
58 	_floating_btn = false;
59 	_arrow_btns = false;
60 	_scroll_mode = SCROLL_NOT;
61 	_scroll_pos = 0;
62 	_invisible_lines = 0;
63 
64 	_last_pos = WindowRect(hwnd).pos();
65 #ifdef _LIGHT_STARTMENU
66 	_selected_id = -1;
67 	_last_mouse_pos = 0;
68 #endif
69 }
70 
StartMenu(HWND hwnd,const StartMenuCreateInfo & create_info,int icon_size)71 StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info, int icon_size)
72  :	super(hwnd),
73 	_create_info(create_info),
74 	_icon_size(icon_size)
75 {
76 	for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
77 		if (*it)
78 			_dirs.push_back(ShellDirectory(GetDesktopFolder(), *it, _hwnd));
79 
80 	_next_id = IDC_FIRST_MENU;
81 	_submenu_id = 0;
82 
83 	_border_left = 0;
84 	_border_top = create_info._border_top;
85 	_bottom_max = INT_MAX;
86 
87 	_floating_btn = create_info._border_top? true: false;
88 	_arrow_btns = false;
89 	_scroll_mode = SCROLL_NOT;
90 	_scroll_pos = 0;
91 	_invisible_lines = 0;
92 
93 	_last_pos = WindowRect(hwnd).pos();
94 #ifdef _LIGHT_STARTMENU
95 	_selected_id = -1;
96 	_last_mouse_pos = 0;
97 #endif
98 }
99 
~StartMenu()100 StartMenu::~StartMenu()
101 {
102 	SendParent(PM_STARTMENU_CLOSED);
103 }
104 
105 
106  // We need this wrapper function for s_wcStartMenu, it calls the WIN32 API,
107  // though static C++ initializers are not allowed for Winelib applications.
GetWndClasss()108 BtnWindowClass& StartMenu::GetWndClasss()
109 {
110 	static BtnWindowClass s_wcStartMenu(CLASSNAME_STARTMENU);
111 
112 	return s_wcStartMenu;
113 }
114 
115 
116 Window::CREATORFUNC_INFO StartMenu::s_def_creator = STARTMENU_CREATOR(StartMenu);
117 
Create(int x,int y,const StartMenuFolders & folders,HWND hwndParent,LPCTSTR title,CREATORFUNC_INFO creator,void * info,const String & filter)118 HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndParent, LPCTSTR title,
119 						CREATORFUNC_INFO creator, void* info, const String& filter)
120 {
121 	UINT style, ex_style;
122 	int top_height;
123 
124 	if (hwndParent) {
125 		style = WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE;
126 		ex_style = 0;
127 		top_height = STARTMENU_TOP_BTN_SPACE;
128 	} else {
129 		style = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPCHILDREN|WS_VISIBLE;
130 		ex_style = WS_EX_TOOLWINDOW;
131 		top_height = 0;
132 	}
133 
134 	int icon_size = ICON_SIZE_SMALL;
135 	RECT rect = {x, y-STARTMENU_LINE_HEIGHT(icon_size)-top_height, x+STARTMENU_WIDTH_MIN, y};
136 
137 #ifndef _LIGHT_STARTMENU
138 	rect.top += STARTMENU_LINE_HEIGHT(icon_size);
139 #endif
140 
141 	AdjustWindowRectEx(&rect, style, FALSE, ex_style);
142 
143 	StartMenuCreateInfo create_info;
144 
145 	create_info._folders = folders;
146 	create_info._border_top = top_height;
147 	create_info._creator = creator;
148 	create_info._info = info;
149 	create_info._filter = filter;
150 
151 	if (title)
152 		create_info._title = title;
153 
154 	HWND hwnd = Window::Create(creator, &create_info, ex_style, GetWndClasss(), title,
155 								style, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndParent);
156 
157 	 // make sure the window is not off the screen
158 	MoveVisible(hwnd);
159 
160 	return hwnd;
161 }
162 
163 
Init(LPCREATESTRUCT pcs)164 LRESULT StartMenu::Init(LPCREATESTRUCT pcs)
165 {
166 	try {
167 		AddEntries();
168 
169 		if (super::Init(pcs))
170 			return 1;
171 
172 		 // create buttons for registered entries in _entries
173 		for(ShellEntryMap::const_iterator it=_entries.begin(); it!=_entries.end(); ++it) {
174 			const StartMenuEntry& sme = it->second;
175 			bool hasSubmenu = false;
176 
177 			for(ShellEntrySet::const_iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it)
178 				if ((*it)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
179 					hasSubmenu = true;
180 
181 #ifdef _LIGHT_STARTMENU
182 			_buttons.push_back(SMBtnInfo(sme, it->first, hasSubmenu));
183 #else
184 			AddButton(sme._title, sme._hIcon, hasSubmenu, it->first);
185 #endif
186 		}
187 
188 #ifdef _LIGHT_STARTMENU
189 		if (_buttons.empty())
190 #else
191 		if (!GetWindow(_hwnd, GW_CHILD))
192 #endif
193 			AddButton(ResString(IDS_EMPTY), ICID_NONE, false, 0, false);
194 
195 #ifdef _LIGHT_STARTMENU
196 		ResizeToButtons();
197 #endif
198 
199 #ifdef _LAZY_ICONEXTRACT
200 		PostMessage(_hwnd, PM_UPDATE_ICONS, 0, 0);
201 #endif
202 	} catch(COMException& e) {
203 		HandleException(e, pcs->hwndParent);	// destroys the start menu window while switching focus
204 	}
205 
206 	return 0;
207 }
208 
AddEntries()209 void StartMenu::AddEntries()
210 {
211 	for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
212 		StartMenuDirectory& smd = *it;
213 		ShellDirectory& dir = smd._dir;
214 
215 		if (!dir._scanned) {
216 			WaitCursor wait;
217 
218 #ifdef _LAZY_ICONEXTRACT
219 			dir.smart_scan(SORT_NAME, SCAN_DONT_EXTRACT_ICONS);	// lazy icon extraction, try to read directly from filesystem
220 #else
221 			dir.smart_scan(SORT_NAME);
222 #endif
223 		}
224 
225 		AddShellEntries(dir, -1, smd._ignore);
226 	}
227 }
228 
229 
trim_path_slash(LPTSTR path)230 static LPTSTR trim_path_slash(LPTSTR path)
231 {
232 	LPTSTR p = path;
233 
234 	while(*p)
235 		++p;
236 
237 	if (p>path && (p[-1]=='\\' || p[-1]=='/'))
238 		*--p = '\0';
239 
240 	return path;
241 }
242 
AddShellEntries(const ShellDirectory & dir,int max,const String & ignore)243 void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, const String& ignore)
244 {
245 	TCHAR ignore_path[MAX_PATH], ignore_dir[MAX_PATH], ignore_name[_MAX_FNAME], ignore_ext[_MAX_EXT];
246 	TCHAR dir_path[MAX_PATH];
247 
248 	if (!ignore.empty()) {
249 		_tsplitpath_s(ignore, ignore_path, COUNTOF(ignore_path), ignore_dir, COUNTOF(ignore_dir), ignore_name, COUNTOF(ignore_name), ignore_ext, COUNTOF(ignore_ext));
250 
251 		_tcscat(ignore_path, ignore_dir);
252 		_tcscat(ignore_name, ignore_ext);
253 
254 		dir.get_path(dir_path, COUNTOF(dir_path));
255 
256 		if (_tcsicmp(trim_path_slash(dir_path), trim_path_slash(ignore_path)))
257 			*ignore_name = '\0';
258 	} else
259 		*ignore_name = '\0';
260 
261 	String lwr_filter = _create_info._filter;
262 	lwr_filter.toLower();
263 
264 	int cnt = 0;
265 	for(Entry*entry=dir._down; entry; entry=entry->_next) {
266 		 // hide files like "desktop.ini"
267 		if (entry->_shell_attribs & SFGAO_HIDDEN)
268 		//not appropriate for drive roots: if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
269 			continue;
270 
271 		 // hide "Programs" subfolders if requested
272 		if (*ignore_name && !_tcsicmp(entry->_data.cFileName, ignore_name))
273 			continue;
274 
275 		 // only 'max' entries shall be added.
276 		if (++cnt == max)
277 			break;
278 
279 		 // filter only non-directory entries
280 		if (!(entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && !lwr_filter.empty()) {
281 			String lwr_name = entry->_data.cFileName;
282 			String lwr_disp = entry->_display_name;
283 
284 			lwr_name.toLower();
285 			lwr_disp.toLower();
286 
287 			if (!_tcsstr(lwr_name,lwr_filter) && !_tcsstr(lwr_disp,lwr_filter))
288 				continue;
289 		}
290 
291 		if (entry->_etype == ET_SHELL)
292 			AddEntry(dir._folder, static_cast<ShellEntry*>(entry));
293 		else
294 			AddEntry(dir._folder, entry);
295 	}
296 }
297 
298 
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)299 LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
300 {
301 	switch(nmsg) {
302 	  case WM_PAINT: {
303 		PaintCanvas canvas(_hwnd);
304 		Paint(canvas);
305 		break;}
306 
307 	  case WM_SIZE:
308 		ResizeButtons(LOWORD(lparam)-_border_left);
309 		break;
310 
311 	  case WM_MOVE: {
312 		POINTS pos;
313 		pos.x = LOWORD(lparam);
314 		pos.y = HIWORD(lparam);
315 
316 		 // move open submenus of floating menus
317 		if (_submenu) {
318 			int dx = pos.x - _last_pos.x;
319 			int dy = pos.y - _last_pos.y;
320 
321 			if (dx || dy) {
322 				WindowRect rt(_submenu);
323 				SetWindowPos(_submenu, 0, rt.left+dx, rt.top+dy, 0, 0, SWP_NOSIZE|SWP_NOACTIVATE);
324 				//MoveVisible(_submenu);
325 			}
326 		}
327 
328 		_last_pos.x = pos.x;
329 		_last_pos.y = pos.y;
330 		goto def;}
331 
332 	  case WM_NCHITTEST: {
333 		LRESULT res = super::WndProc(nmsg, wparam, lparam);
334 
335 		if (res>=HTSIZEFIRST && res<=HTSIZELAST)
336 			return HTCLIENT;	// disable window resizing
337 
338 		return res;}
339 
340 	  case WM_LBUTTONDOWN: {
341 		RECT rect;
342 
343 		 // check mouse cursor for coordinates of floating button
344 		GetFloatingButtonRect(&rect);
345 
346 		if (PtInRect(&rect, Point(lparam))) {
347 			 // create a floating copy of the current start menu
348  			WindowRect pos(_hwnd);
349 
350 			///@todo do something similar to StartMenuRoot::TrackStartmenu() in order to automatically close submenus when clicking on the desktop background
351 			StartMenu::Create(pos.left+3, pos.bottom-3, _create_info._folders, 0, _create_info._title, _create_info._creator, _create_info._info);
352 			CloseStartMenu();
353 		}
354 
355 #ifdef _LIGHT_STARTMENU
356 		int id = ButtonHitTest(Point(lparam));
357 
358 		if (id)
359 			Command(id, BN_CLICKED);
360 #endif
361 		break;}
362 
363 	  case WM_SYSCOMMAND:
364 		if ((wparam&0xFFF0) == SC_SIZE)
365 			return 0;			// disable window resizing
366 		goto def;
367 
368 	  case WM_ACTIVATEAPP:
369 		 // close start menu when activating another application
370 		if (!wparam)
371 			CloseStartMenu();
372 		break;	// don't call super::WndProc in case "this" has been deleted
373 
374 	  case WM_CANCELMODE:
375 		CloseStartMenu();
376 
377 #ifdef _LIGHT_STARTMENU
378 		if (_scroll_mode != SCROLL_NOT) {
379 			ReleaseCapture();
380 			KillTimer(_hwnd, 0);
381 		}
382 #endif
383 		break;
384 
385 #ifdef _LIGHT_STARTMENU
386 	  case WM_MOUSEMOVE: {
387 		 // automatically set the focus to startmenu entries when moving the mouse over them
388 		if (lparam != _last_mouse_pos) { // don't process WM_MOUSEMOVE when opening submenus using keyboard navigation
389 			Point pt(lparam);
390 
391 			if (_arrow_btns) {
392 				RECT rect_up, rect_down;
393 
394 				GetArrowButtonRects(&rect_up, &rect_down, _icon_size);
395 
396 				SCROLL_MODE scroll_mode = SCROLL_NOT;
397 
398 				if (PtInRect(&rect_up, pt))
399 					scroll_mode = SCROLL_UP;
400 				else if (PtInRect(&rect_down, pt))
401 					scroll_mode = SCROLL_DOWN;
402 
403 				if (scroll_mode != _scroll_mode) {
404 					if (scroll_mode == SCROLL_NOT) {
405 						ReleaseCapture();
406 						KillTimer(_hwnd, 0);
407 					} else {
408 						CloseSubmenus();
409 						SetTimer(_hwnd, 0, 150, NULL);	// 150 ms scroll interval
410 						SetCapture(_hwnd);
411 					}
412 
413 					_scroll_mode = scroll_mode;
414 				}
415 			}
416 
417 			int new_id = ButtonHitTest(pt);
418 
419 			if (new_id>0 && new_id!=_selected_id)
420 				SelectButton(new_id);
421 
422 			_last_mouse_pos = lparam;
423 		}
424 		break;}
425 
426 	  case WM_TIMER:
427 		if (_scroll_mode == SCROLL_UP) {
428 			if (_scroll_pos > 0) {
429 				--_scroll_pos;
430 				InvalidateRect(_hwnd, NULL, TRUE);
431 			}
432 		} else {
433 			if (_scroll_pos <= _invisible_lines) {
434 				++_scroll_pos;
435 				InvalidateRect(_hwnd, NULL, TRUE);
436 			}
437 		}
438 		break;
439 
440 	  case WM_KEYDOWN:
441 		ProcessKey(wparam);
442 		break;
443 #else
444 	  case PM_STARTENTRY_FOCUSED: {	///@todo use TrackMouseEvent() and WM_MOUSEHOVER to wait a bit before opening submenus
445 		BOOL hasSubmenu = wparam;
446 		HWND hctrl = (HWND)lparam;
447 
448 		 // automatically open submenus
449 		if (hasSubmenu) {
450 			UpdateWindow(_hwnd);	// draw focused button before waiting on submenu creation
451 			//SendMessage(_hwnd, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hctrl),BN_CLICKED), (LPARAM)hctrl);
452 			Command(GetDlgCtrlID(hctrl), BN_CLICKED);
453 		} else {
454 			 // close any open submenu
455 			CloseOtherSubmenus();
456 		}
457 		break;}
458 #endif
459 
460 #ifdef _LAZY_ICONEXTRACT
461 	  case PM_UPDATE_ICONS:
462 		UpdateIcons(/*wparam*/);
463 		break;
464 #endif
465 
466 	  case PM_STARTENTRY_LAUNCHED:
467 		if (GetWindowStyle(_hwnd) & WS_CAPTION)	// don't automatically close floating menus
468 			return 0;
469 
470 		 // route message to the parent menu and close menus after launching an entry
471 		if (!SendParent(nmsg, wparam, lparam))
472 			CloseStartMenu();
473 		return 1;	// signal that we have received and processed the message
474 
475 	  case PM_STARTMENU_CLOSED:
476 		_submenu = 0;
477 		break;
478 
479 	  case PM_SELECT_ENTRY:
480 		SelectButtonIndex(0, wparam!=0);
481 		break;
482 
483 #ifdef _LIGHT_STARTMENU
484 	  case WM_CONTEXTMENU: {
485 		Point screen_pt(lparam), clnt_pt=screen_pt;
486 		ScreenToClient(_hwnd, &clnt_pt);
487 
488 		int id = ButtonHitTest(clnt_pt);
489 
490 		if (id) {
491 			StartMenuEntry& sme = _entries[id];
492 
493 			for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
494 				Entry* entry = *it;
495 
496 				if (entry) {
497 					CHECKERROR(entry->do_context_menu(_hwnd, screen_pt, _cm_ifs));	// may close start menu because of focus loss
498 					///@todo refresh on successfull context menu execution?
499 					break;	///@todo handle context menu for more than one entry
500 				}
501 			}
502 		}
503 		break;}
504 #endif
505 
506 	  default: def:
507 		return super::WndProc(nmsg, wparam, lparam);
508 	}
509 
510 	return 0;
511 }
512 
513 
514 #ifdef _LIGHT_STARTMENU
515 
ButtonHitTest(POINT pt)516 int StartMenu::ButtonHitTest(POINT pt)
517 {
518 	ClientRect clnt(_hwnd);
519 	const int icon_size = _icon_size;
520 	RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
521 
522 	if (pt.x<rect.left || pt.x>rect.right)
523 		return 0;
524 
525 	for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
526 		const SMBtnInfo& info = *it;
527 
528 		if (rect.top > pt.y)
529 			break;
530 
531 		rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
532 
533 		if (rect.bottom > _bottom_max)
534 			break;
535 
536 		if (pt.y < rect.bottom)	// PtInRect(&rect, pt)
537 			return info._id;
538 
539 		rect.top = rect.bottom;
540 	}
541 
542 	return 0;
543 }
544 
InvalidateSelection()545 void StartMenu::InvalidateSelection()
546 {
547 	if (_selected_id <= 0)
548 		return;
549 
550 	ClientRect clnt(_hwnd);
551 	const int icon_size = _icon_size;
552 	RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
553 
554 	for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
555 		const SMBtnInfo& info = *it;
556 
557 		rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
558 
559 		if (info._id == _selected_id) {
560 			InvalidateRect(_hwnd, &rect, TRUE);
561 			break;
562 		}
563 
564 		rect.top = rect.bottom;
565 	}
566 }
567 
GetButtonInfo(int id) const568 const SMBtnInfo* StartMenu::GetButtonInfo(int id) const
569 {
570 	for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it)
571 		if (it->_id == id)
572 			return &*it;
573 
574 	return NULL;
575 }
576 
SelectButton(int id,bool open_sub)577 bool StartMenu::SelectButton(int id, bool open_sub)
578 {
579 	if (id == -1)
580 		return false;
581 
582 	if (id == _selected_id)
583 		return true;
584 
585 	InvalidateSelection();
586 
587 	const SMBtnInfo* btn = GetButtonInfo(id);
588 
589 	if (btn && btn->_enabled) {
590 		_selected_id = id;
591 
592 		InvalidateSelection();
593 
594 		 // automatically open submenus
595 		if (btn->_hasSubmenu) {
596 			if (open_sub)
597 				OpenSubmenu();
598 		} else
599 			CloseOtherSubmenus();	// close any open submenu
600 
601 		return true;
602 	} else {
603 		_selected_id = -1;
604 		return false;
605 	}
606 }
607 
OpenSubmenu(bool select_first)608 bool StartMenu::OpenSubmenu(bool select_first)
609 {
610 	if (_selected_id == -1)
611 		return false;
612 
613 	InvalidateSelection();
614 
615 	const SMBtnInfo* btn = GetButtonInfo(_selected_id);
616 
617 	 // automatically open submenus
618 	if (btn->_hasSubmenu) {
619 		//@@ allows destroying of startmenu when processing PM_UPDATE_ICONS -> GPF
620 		UpdateWindow(_hwnd);	// draw focused button before waiting on submenu creation
621 		Command(_selected_id, BN_CLICKED);
622 
623 		if (select_first && _submenu)
624 			SendMessage(_submenu, PM_SELECT_ENTRY, (WPARAM)false, 0);
625 
626 		return true;
627 	} else
628 		return false;
629 }
630 
631 
GetSelectionIndex()632 int StartMenu::GetSelectionIndex()
633 {
634 	if (_selected_id == -1)
635 		return -1;
636 
637 	for(int i=0; i<(int)_buttons.size(); ++i)
638 		if (_buttons[i]._id == _selected_id)
639 			return i;
640 
641 	return -1;
642 }
643 
SelectButtonIndex(int idx,bool open_sub)644 bool StartMenu::SelectButtonIndex(int idx, bool open_sub)
645 {
646 	if (idx>=0 && idx<(int)_buttons.size())
647 		return SelectButton(_buttons[idx]._id, open_sub);
648 	else
649 		return false;
650 }
651 
ProcessKey(int vk)652 void StartMenu::ProcessKey(int vk)
653 {
654 	switch(vk) {
655 	  case VK_RETURN:
656 		if (_selected_id)
657 			Command(_selected_id, BN_CLICKED);
658 		break;
659 
660 	  case VK_UP:
661 		Navigate(-1);
662 		break;
663 
664 	  case VK_DOWN:
665 		Navigate(+1);
666 		break;
667 
668 	  case VK_HOME:
669 		SelectButtonIndex(0, false);
670 		break;
671 
672 	  case VK_END:
673 		SelectButtonIndex(_buttons.size()-1, false);
674 		break;
675 
676 	  case VK_LEFT:
677 		if (_submenu)
678 			CloseOtherSubmenus();
679 		else if (!(GetWindowStyle(_hwnd) & WS_CAPTION))	// don't automatically close floating menus
680 			DestroyWindow(_hwnd);
681 		break;
682 
683 	  case VK_RIGHT:
684 		OpenSubmenu(true);
685 		break;
686 
687 	  case VK_ESCAPE:
688 		CloseStartMenu();
689 		break;
690 
691 	  default:
692 		if (vk>='0' && vk<='Z')
693 			JumpToNextShortcut(vk);
694 	}
695 }
696 
Navigate(int step)697 bool StartMenu::Navigate(int step)
698 {
699 	int idx = GetSelectionIndex();
700 
701 	if (idx == -1)
702 	{
703 		if (step > 0)
704 			idx = 0 - step;
705 		else
706 			idx = _buttons.size() - step;
707 	}
708 
709 	for(;;) {
710 		idx += step;
711 
712 		if (_buttons.size()<=1 && (idx<0 || idx>(int)_buttons.size()))
713 			break;
714 
715 		if (idx < 0)
716 			idx += _buttons.size();
717 
718 		if (idx > (int)_buttons.size())
719 			idx -= _buttons.size()+1;
720 
721 		if (SelectButtonIndex(idx, false))
722 			return true;
723 	}
724 
725 	return false;
726 }
727 
JumpToNextShortcut(char c)728 bool StartMenu::JumpToNextShortcut(char c)
729 {
730 	int cur_idx = GetSelectionIndex();
731 
732 	if (cur_idx == -1)
733 		cur_idx = 0;
734 
735 	int first_found = 0;
736 	int found_more = 0;
737 
738 	SMBtnVector::const_iterator cur_it = _buttons.begin();
739 	cur_it += cur_idx + 1;
740 
741 	 // first search down from current item...
742 	SMBtnVector::const_iterator it = cur_it;
743 	for(; it!=_buttons.end(); ++it) {
744 		const SMBtnInfo& btn = *it;
745 
746 		if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
747 			if (!first_found)
748 				first_found = btn._id;
749 			else
750 				++found_more;
751 		}
752 	}
753 
754 	 // ...now search from top to the current item
755 	it = _buttons.begin();
756 	for(; it!=_buttons.end() && it!=cur_it; ++it) {
757 		const SMBtnInfo& btn = *it;
758 
759 		if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
760 			if (!first_found)
761 				first_found = btn._id;
762 			else
763 				++found_more;
764 		}
765 	}
766 
767 	if (first_found) {
768 		SelectButton(first_found);
769 
770 		if (!found_more)
771 			Command(first_found, BN_CLICKED);
772 
773 		return true;
774 	} else
775 		return false;
776 }
777 
778 #endif // _LIGHT_STARTMENU
779 
780 
GetButtonRect(int id,PRECT prect) const781 bool StartMenu::GetButtonRect(int id, PRECT prect) const
782 {
783 #ifdef _LIGHT_STARTMENU
784 	ClientRect clnt(_hwnd);
785 	const int icon_size = _icon_size;
786 	RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
787 
788 	for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
789 		const SMBtnInfo& info = *it;
790 
791 		rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
792 
793 		if (info._id == id) {
794 			*prect = rect;
795 			return true;
796 		}
797 
798 		rect.top = rect.bottom;
799 	}
800 
801 	return false;
802 #else
803 	HWND btn = GetDlgItem(_hwnd, id);
804 
805 	if (btn) {
806 		GetWindowRect(btn, prect);
807 		ScreenToClient(_hwnd, prect);
808 
809 		return true;
810 	} else
811 		return false;
812 #endif
813 }
814 
815 
DrawFloatingButton(HDC hdc)816 void StartMenu::DrawFloatingButton(HDC hdc)
817 {
818 	static ResIconEx floatingIcon(IDI_FLOATING, 8, 4);
819 
820 	ClientRect clnt(_hwnd);
821 
822 	DrawIconEx(hdc, clnt.right-12, 0, floatingIcon, 8, 4, 0, 0, DI_NORMAL);
823 }
824 
GetFloatingButtonRect(LPRECT prect)825 void StartMenu::GetFloatingButtonRect(LPRECT prect)
826 {
827 	GetClientRect(_hwnd, prect);
828 
829 	prect->right -= 4;
830 	prect->left = prect->right - 8;
831 	prect->bottom = 4;
832 }
833 
834 
DrawArrows(HDC hdc,int icon_size)835 void StartMenu::DrawArrows(HDC hdc, int icon_size)
836 {
837 	int cx = icon_size / 2;
838 	int cy = icon_size / 4;
839 
840 	ResIconEx arrowUpIcon(IDI_ARROW_UP, cx, cy);
841 	ResIconEx arrowDownIcon(IDI_ARROW_DOWN, cx, cy);
842 
843 	ClientRect clnt(_hwnd);
844 
845 	DrawIconEx(hdc, clnt.right/2-cx/2, _floating_btn?3:1, arrowUpIcon, cx, cy, 0, 0, DI_NORMAL);
846 	DrawIconEx(hdc, clnt.right/2-cx/2, clnt.bottom-cy-1, arrowDownIcon, cx, cy, 0, 0, DI_NORMAL);
847 }
848 
GetArrowButtonRects(LPRECT prect_up,LPRECT prect_down,int icon_size)849 void StartMenu::GetArrowButtonRects(LPRECT prect_up, LPRECT prect_down, int icon_size)
850 {
851 	int cx = icon_size / 2;
852 	int cy = icon_size / 4;
853 
854 	GetClientRect(_hwnd, prect_up);
855 	*prect_down = *prect_up;
856 
857 //	prect_up->left = prect_up->right/2 - cx/2;
858 //	prect_up->right = prect_up->left + cy;
859 	prect_up->right -= cx;
860 	prect_up->top = _floating_btn? cy-1: 1;
861 	prect_up->bottom = prect_up->top + cy;
862 
863 //	prect_down->left = prect_down->right/2 - cx/2;
864 //	prect_down->right = prect_down->left + cy;
865 	prect_down->right -= cx;
866 	prect_down->top = prect_down->bottom - cy - 1;
867 }
868 
869 
Paint(PaintCanvas & canvas)870 void StartMenu::Paint(PaintCanvas& canvas)
871 {
872 	if (_floating_btn)
873 		DrawFloatingButton(canvas);
874 
875 #ifdef _LIGHT_STARTMENU
876 	if (_arrow_btns)
877 		DrawArrows(canvas, _icon_size);
878 
879 	ClientRect clnt(_hwnd);
880 	const int icon_size = _icon_size;
881 	RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
882 
883 	int sep_width = rect.right-rect.left - 4;
884 
885 	FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
886 	BkMode bk_mode(canvas, TRANSPARENT);
887 
888 	for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
889 		const SMBtnInfo& btn = *it;
890 
891 		if (rect.top > canvas.rcPaint.bottom)
892 			break;
893 
894 		if (btn._id == -1) {	// a separator?
895 			rect.bottom = rect.top + STARTMENU_SEP_HEIGHT(icon_size);
896 
897 			if (rect.bottom > _bottom_max)
898 				break;
899 
900 			BrushSelection brush_sel(canvas, GetSysColorBrush(COLOR_BTNSHADOW));
901 			PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT(icon_size)/2-1, sep_width, 1, PATCOPY);
902 
903 			SelectBrush(canvas, GetSysColorBrush(COLOR_BTNHIGHLIGHT));
904 			PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT(icon_size)/2, sep_width, 1, PATCOPY);
905 		} else {
906 			rect.bottom = rect.top + STARTMENU_LINE_HEIGHT(icon_size);
907 
908 			if (rect.bottom > _bottom_max)
909 				break;
910 
911 			if (rect.top >= canvas.rcPaint.top)
912 				DrawStartMenuButton(canvas, rect, btn._title, btn, btn._id==_selected_id, false, _icon_size);
913 		}
914 
915 		rect.top = rect.bottom;
916 	}
917 #endif
918 }
919 
920 #ifdef _LAZY_ICONEXTRACT
UpdateIcons()921 void StartMenu::UpdateIcons(/*int idx*/)
922 {
923 	UpdateWindow(_hwnd);
924 
925 #ifdef _SINGLE_ICONEXTRACT
926 
927 	//if (idx >= 0)
928 	int idx = _scroll_pos;
929 
930 	for(; idx<(int)_buttons.size(); ++idx) {
931 		SMBtnInfo& btn = _buttons[idx];
932 
933 		if (btn._icon_id==ICID_UNKNOWN && btn._id>0) {
934 			StartMenuEntry& sme = _entries[btn._id];
935 
936 			btn._icon_id = ICID_NONE;
937 
938 			for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
939 				Entry* entry = *it;
940 
941 				if (entry->_icon_id == ICID_UNKNOWN)
942 					entry->_icon_id = entry->safe_extract_icon(ICF_FROM_ICON_SIZE(_icon_size));
943 
944 				if (entry->_icon_id > ICID_NONE) {
945 					btn._icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
946 
947 					RECT rect;
948 
949 					GetButtonRect(btn._id, &rect);
950 
951 					if (rect.bottom > _bottom_max)
952 						break;
953 
954 					WindowCanvas canvas(_hwnd);
955 					DrawStartMenuButton(canvas, rect, NULL, btn, btn._id==_selected_id, false, _icon_size);
956 
957 					//InvalidateRect(_hwnd, &rect, FALSE);
958 					//UpdateWindow(_hwnd);
959 					//break;
960 
961 					break;
962 				}
963 			}
964 		}
965 	}
966 
967 //	if (++idx < (int)_buttons.size())
968 //		PostMessage(_hwnd, PM_UPDATE_ICONS, idx, 0);
969 
970 #else
971 
972 	int icons_extracted = 0;
973 	int icons_updated = 0;
974 
975 	for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
976 		ShellDirectory& dir = it->_dir;
977 
978 		icons_extracted += dir.extract_icons(icon_size);
979 	}
980 
981 	if (icons_extracted) {
982 		for(ShellEntryMap::iterator it1=_entries.begin(); it1!=_entries.end(); ++it1) {
983 			StartMenuEntry& sme = it1->second;
984 
985 			if (!sme._hIcon) {
986 				sme._hIcon = (HICON)-1;
987 
988 				for(ShellEntrySet::const_iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
989 					const Entry* sm_entry = *it2;
990 
991 					if (sm_entry->_hIcon) {
992 						sme._hIcon = sm_entry->_hIcon;
993 						break;
994 					}
995 				}
996 			}
997 		}
998 
999 		for(SMBtnVector::iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
1000 			SMBtnInfo& info = *it;
1001 
1002 			if (info._id>0 && !info._hIcon) {
1003 				info._hIcon = _entries[info._id]._hIcon;
1004 				++icons_updated;
1005 			}
1006 		}
1007 	}
1008 
1009 	if (icons_updated) {
1010 		InvalidateRect(_hwnd, NULL, FALSE);
1011 		UpdateWindow(_hwnd);
1012 	}
1013 #endif
1014 }
1015 #endif
1016 
1017 
1018  // resize child button controls to accomodate for new window size
ResizeButtons(int cx)1019 void StartMenu::ResizeButtons(int cx)
1020 {
1021 	HDWP hdwp = BeginDeferWindowPos(10);
1022 
1023 	for(HWND ctrl=GetWindow(_hwnd,GW_CHILD); ctrl; ctrl=GetNextWindow(ctrl,GW_HWNDNEXT)) {
1024 		ClientRect rt(ctrl);
1025 
1026 		if (rt.right != cx) {
1027 			int height = rt.bottom - rt.top;
1028 
1029 			 // special handling for separator controls
1030 			if (!height && (GetWindowStyle(ctrl)&SS_TYPEMASK)==SS_ETCHEDHORZ)
1031 				height = 2;
1032 
1033 			hdwp = DeferWindowPos(hdwp, ctrl, 0, 0, 0, cx, height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
1034 		}
1035 	}
1036 
1037 	EndDeferWindowPos(hdwp);
1038 }
1039 
1040 
Command(int id,int code)1041 int StartMenu::Command(int id, int code)
1042 {
1043 #ifndef _LIGHT_STARTMENU
1044 	switch(id) {
1045 	  case IDCANCEL:
1046 		CloseStartMenu(id);
1047 		break;
1048 
1049 	  default: {
1050 #endif
1051 		ShellEntryMap::iterator found = _entries.find(id);
1052 
1053 		if (found != _entries.end()) {
1054 			ActivateEntry(id, found->second._entries);
1055 			return 0;
1056 		}
1057 
1058 		return super::Command(id, code);
1059 #ifndef _LIGHT_STARTMENU
1060 	  }
1061 	}
1062 
1063 	return 0;
1064 #endif
1065 }
1066 
1067 
AddEntry(const String & title,ICON_ID icon_id,Entry * entry)1068 ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, Entry* entry)
1069 {
1070 	 // search for an already existing subdirectory entry with the same name
1071 	if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1072 	{
1073 		for(ShellEntryMap::iterator it=_entries.begin(); it!=_entries.end(); ++it) {
1074 			StartMenuEntry& sme = it->second;
1075 
1076 			if (!_tcsicmp(sme._title, title))	///@todo speed up by using a map indexed by name
1077 			{
1078 				for(ShellEntrySet::iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
1079 					if ((*it2)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1080 						 // merge the new shell entry with the existing of the same name
1081 						sme._entries.insert(entry);
1082 
1083 						return it;
1084 					}
1085 				}
1086 			}
1087 		}
1088 	}
1089 
1090 	ShellEntryMap::iterator sme = AddEntry(title, icon_id);
1091 
1092 	sme->second._entries.insert(entry);
1093 
1094 	return sme;
1095 }
1096 
AddEntry(const String & title,ICON_ID icon_id,int id)1097 ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, int id)
1098 {
1099 	if (id == -1)
1100 		id = ++_next_id;
1101 
1102 	StartMenuEntry sme;
1103 
1104 	sme._title = title;
1105 	sme._icon_id = icon_id;
1106 
1107 	ShellEntryMap::iterator it = _entries.insert(make_pair(id, sme)).first;
1108 
1109 	return it;
1110 }
1111 
AddEntry(const ShellFolder folder,ShellEntry * entry)1112 ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, ShellEntry* entry)
1113 {
1114 	ICON_ID icon_id;
1115 
1116 	if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1117 		icon_id = ICID_APPS;
1118 	else
1119 		icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
1120 
1121 	return AddEntry(folder.get_name(entry->_pidl), icon_id, entry);
1122 }
1123 
AddEntry(const ShellFolder folder,Entry * entry)1124 ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, Entry* entry)
1125 {
1126 	ICON_ID icon_id;
1127 
1128 	if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
1129 		icon_id = ICID_APPS;
1130 	else
1131 		icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
1132 
1133 	return AddEntry(entry->_display_name, icon_id, entry);
1134 }
1135 
1136 
AddButton(LPCTSTR title,ICON_ID icon_id,bool hasSubmenu,int id,bool enabled)1137 void StartMenu::AddButton(LPCTSTR title, ICON_ID icon_id, bool hasSubmenu, int id, bool enabled)
1138 {
1139 #ifdef _LIGHT_STARTMENU
1140 	_buttons.push_back(SMBtnInfo(title, icon_id, id, hasSubmenu, enabled));
1141 #else
1142 	DWORD style = enabled? WS_VISIBLE|WS_CHILD|BS_OWNERDRAW: WS_VISIBLE|WS_CHILD|BS_OWNERDRAW|WS_DISABLED;
1143 
1144 	WindowRect rect(_hwnd);
1145 	ClientRect clnt(_hwnd);
1146 
1147 	 // increase window height to make room for the new button
1148 	rect.top -= STARTMENU_LINE_HEIGHT(icon_size);
1149 
1150 	 // move down if we are too high now
1151 	if (rect.top < 0) {
1152 		rect.top += STARTMENU_LINE_HEIGHT(icon_size);
1153 		rect.bottom += STARTMENU_LINE_HEIGHT(icon_size);
1154 	}
1155 
1156 	WindowCanvas canvas(_hwnd);
1157 	FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
1158 
1159 	 // widen window, if it is too small
1160 	int text_width = GetStartMenuBtnTextWidth(canvas, title, _hwnd) + icon_size + 10/*placeholder*/ + 16/*arrow*/;
1161 
1162 	int cx = clnt.right - _border_left;
1163 	if (text_width > cx)
1164 		rect.right += text_width-cx;
1165 
1166 	MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
1167 
1168 	StartMenuCtrl(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left,
1169 					title, id, g_Globals._icon_cache.get_icon(icon_id)._hIcon, hasSubmenu, style);
1170 #endif
1171 }
1172 
AddSeparator()1173 void StartMenu::AddSeparator()
1174 {
1175 #ifdef _LIGHT_STARTMENU
1176 	_buttons.push_back(SMBtnInfo(NULL, ICID_NONE, -1, false));
1177 #else
1178 	WindowRect rect(_hwnd);
1179 	ClientRect clnt(_hwnd);
1180 
1181 	 // increase window height to make room for the new separator
1182 	rect.top -= STARTMENU_SEP_HEIGHT(icon_size);
1183 
1184 	 // move down if we are too high now
1185 	if (rect.top < 0) {
1186 		rect.top += STARTMENU_LINE_HEIGHT(icon_size);
1187 		rect.bottom += STARTMENU_LINE_HEIGHT(icon_size);
1188 	}
1189 
1190 	MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
1191 
1192 	StartMenuSeparator(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left);
1193 #endif
1194 }
1195 
1196 
CloseOtherSubmenus(int id)1197 bool StartMenu::CloseOtherSubmenus(int id)
1198 {
1199 	if (_submenu) {
1200 		if (IsWindow(_submenu)) {
1201 			if (_submenu_id == id)
1202 				return false;
1203 			else {
1204 				_submenu_id = 0;
1205 				DestroyWindow(_submenu);
1206 				// _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
1207 			}
1208 		}
1209 
1210 		_submenu = 0;
1211 	}
1212 
1213 	return true;
1214 }
1215 
1216 
CreateSubmenu(int id,LPCTSTR title,CREATORFUNC_INFO creator,void * info)1217 void StartMenu::CreateSubmenu(int id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
1218 {
1219 	CreateSubmenu(id, StartMenuFolders(), title, creator, info);
1220 }
1221 
CreateSubmenu(int id,int folder_id,LPCTSTR title,CREATORFUNC_INFO creator,void * info)1222 bool StartMenu::CreateSubmenu(int id, int folder_id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
1223 {
1224 	try {
1225 		SpecialFolderPath folder(folder_id, _hwnd);
1226 
1227 		StartMenuFolders new_folders;
1228 		new_folders.push_back(folder);
1229 
1230 		CreateSubmenu(id, new_folders, title, creator, info);
1231 
1232 		return true;
1233 	} catch(COMException&) {
1234 		// ignore Exception and don't display anything
1235 		CloseOtherSubmenus(id);
1236 		_buttons[GetSelectionIndex()]._enabled = false;	// disable entries for non-existing folders
1237 		return false;
1238 	}
1239 }
1240 
CreateSubmenu(int id,int folder_id1,int folder_id2,LPCTSTR title,CREATORFUNC_INFO creator,void * info)1241 bool StartMenu::CreateSubmenu(int id, int folder_id1, int folder_id2, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
1242 {
1243 	StartMenuFolders new_folders;
1244 
1245 	try {
1246 		new_folders.push_back(SpecialFolderPath(folder_id1, _hwnd));
1247 	} catch(COMException&) {
1248 	}
1249 
1250 	try {
1251 		new_folders.push_back(SpecialFolderPath(folder_id2, _hwnd));
1252 	} catch(COMException&) {
1253 	}
1254 
1255 	if (!new_folders.empty()) {
1256 		CreateSubmenu(id, new_folders, title, creator, info);
1257 		return true;
1258 	} else {
1259 		CloseOtherSubmenus(id);
1260 		_buttons[GetSelectionIndex()]._enabled = false;	// disable entries for non-existing folders
1261 		return false;
1262 	}
1263 }
1264 
CreateSubmenu(int id,const StartMenuFolders & new_folders,LPCTSTR title,CREATORFUNC_INFO creator,void * info)1265 void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
1266 {
1267 	 // Only open one submenu at a time.
1268 	if (!CloseOtherSubmenus(id))
1269 		return;
1270 
1271 	RECT rect;
1272 	int x, y;
1273 
1274 	if (GetButtonRect(id, &rect)) {
1275 		ClientToScreen(_hwnd, &rect);
1276 
1277 		x = rect.right;	// Submenus should overlap their parent a bit.
1278 		const int icon_size = _icon_size;
1279 		y = rect.top+STARTMENU_LINE_HEIGHT(icon_size) +_border_top/*own border*/ -STARTMENU_TOP_BTN_SPACE/*border of new submenu*/;
1280 	} else {
1281 		WindowRect pos(_hwnd);
1282 
1283 		x = pos.right;
1284 		y = pos.top;
1285 	}
1286 
1287 	_submenu_id = id;
1288 	_submenu = StartMenu::Create(x, y, new_folders, _hwnd, title, creator, info, _create_info._filter);
1289 }
1290 
1291 
ActivateEntry(int id,const ShellEntrySet & entries)1292 void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries)
1293 {
1294 	StartMenuFolders new_folders;
1295 	String title;
1296 
1297 	for(ShellEntrySet::const_iterator it=entries.begin(); it!=entries.end(); ++it) {
1298 		Entry* entry = const_cast<Entry*>(*it);
1299 
1300 		if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
1301 
1302 			///@todo If the user explicitly clicked on a submenu, display this folder as floating start menu.
1303 
1304 			if (entry->_etype == ET_SHELL)
1305 				new_folders.push_back(entry->create_absolute_pidl());
1306 			else {
1307 				TCHAR path[MAX_PATH];
1308 
1309 				if (entry->get_path(path, COUNTOF(path)))
1310 					new_folders.push_back(path);
1311 			}
1312 
1313 			if (title.empty())
1314 				title = entry->_display_name;
1315 		} else {
1316 			 // The entry is no subdirectory, so there can only be one shell entry.
1317 			assert(entries.size()==1);
1318 
1319 			HWND hparent = GetParent(_hwnd);
1320 			ShellPath shell_path = entry->create_absolute_pidl();
1321 
1322 			 // close start menus when launching the selected entry
1323 			CloseStartMenu(id);
1324 
1325 			///@todo launch in the background; specify correct HWND for error message box titles
1326 			SHELLEXECUTEINFO shexinfo;
1327 
1328 			shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
1329 			shexinfo.fMask = SEE_MASK_IDLIST;	// SEE_MASK_INVOKEIDLIST is also possible.
1330 			shexinfo.hwnd = hparent;
1331 			shexinfo.lpVerb = NULL;
1332 			shexinfo.lpFile = NULL;
1333 			shexinfo.lpParameters = NULL;
1334 			shexinfo.lpDirectory = NULL;
1335 			shexinfo.nShow = SW_SHOWNORMAL;
1336 
1337 			shexinfo.lpIDList = &*shell_path;
1338 
1339 			 // add PIDL to the recent file list
1340 			SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList);
1341 
1342 			if (!ShellExecuteEx(&shexinfo))
1343 				display_error(hparent, GetLastError());
1344 
1345 			 // we may have deleted 'this' - ensure we leave the loop and function
1346 			return;
1347 		}
1348 	}
1349 
1350 	if (!new_folders.empty()) {
1351 		 // Only open one submenu at a time.
1352 		if (!CloseOtherSubmenus(id))
1353 			return;
1354 
1355 		CreateSubmenu(id, new_folders, title);
1356 	}
1357 }
1358 
1359 
1360  /// close all windows of the start menu popup
CloseStartMenu(int id)1361 void StartMenu::CloseStartMenu(int id)
1362 {
1363 	if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) {	// don't automatically close floating menus
1364 		if (!SendParent(PM_STARTENTRY_LAUNCHED, id, (LPARAM)_hwnd))
1365 			DestroyWindow(_hwnd);
1366 	} else if (_submenu)	// instead close submenus of floating parent menus
1367 		CloseSubmenus();
1368 }
1369 
1370 
GetStartMenuBtnTextWidth(HDC hdc,LPCTSTR title,HWND hwnd)1371 int GetStartMenuBtnTextWidth(HDC hdc, LPCTSTR title, HWND hwnd)
1372 {
1373 	RECT rect = {0, 0, 0, 0};
1374 	DrawText(hdc, title, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
1375 
1376 	return rect.right-rect.left;
1377 }
1378 
1379 #ifdef _LIGHT_STARTMENU
DrawStartMenuButton(HDC hdc,const RECT & rect,LPCTSTR title,const SMBtnInfo & btn,bool has_focus,bool pushed,int icon_size)1380 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, const SMBtnInfo& btn, bool has_focus, bool pushed, int icon_size)
1381 #else
1382 void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, HICON hIcon,
1383 								bool hasSubmenu, bool enabled, bool has_focus, bool pushed, int icon_size);
1384 #endif
1385 {
1386 	UINT style = DFCS_BUTTONPUSH;
1387 
1388 	if (!btn._enabled)
1389 		style |= DFCS_INACTIVE;
1390 
1391 	POINT iconPos = {rect.left+2, (rect.top+rect.bottom-icon_size)/2};
1392 	RECT textRect = {rect.left+icon_size+4, rect.top+2, rect.right-4, rect.bottom-4};
1393 
1394 	if (pushed) {
1395 		style |= DFCS_PUSHED;
1396 		++iconPos.x;		++iconPos.y;
1397 		++textRect.left;	++textRect.top;
1398 		++textRect.right;	++textRect.bottom;
1399 	}
1400 
1401 	int bk_color_idx = COLOR_BTNFACE;
1402 	int text_color_idx = COLOR_BTNTEXT;
1403 
1404 	if (has_focus) {
1405 		bk_color_idx = COLOR_HIGHLIGHT;
1406 		text_color_idx = COLOR_HIGHLIGHTTEXT;
1407 	}
1408 
1409 	COLORREF bk_color = GetSysColor(bk_color_idx);
1410 	HBRUSH bk_brush = GetSysColorBrush(bk_color_idx);
1411 
1412 	if (title)
1413 		FillRect(hdc, &rect, bk_brush);
1414 
1415 	if (btn._icon_id > ICID_NONE)
1416 		g_Globals._icon_cache.get_icon(btn._icon_id).draw(hdc, iconPos.x, iconPos.y, icon_size, icon_size, bk_color, bk_brush/*, icon_size*/);
1417 
1418 	 // draw submenu arrow at the right
1419 	if (btn._hasSubmenu) {
1420 		ResIconEx arrowIcon(IDI_ARROW, icon_size, icon_size);
1421 		ResIconEx selArrowIcon(IDI_ARROW_SELECTED, icon_size, icon_size);
1422 
1423 		DrawIconEx(hdc, rect.right-icon_size, iconPos.y,
1424 					has_focus? selArrowIcon: arrowIcon,
1425 					icon_size, icon_size, 0, bk_brush, DI_NORMAL);
1426 	}
1427 
1428 	if (title) {
1429 		BkMode bk_mode(hdc, TRANSPARENT);
1430 
1431 		if (!btn._enabled)	// dis->itemState & (ODS_DISABLED|ODS_GRAYED)
1432 			DrawGrayText(hdc, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
1433 		else {
1434 			TextColor lcColor(hdc, GetSysColor(text_color_idx));
1435 			DrawText(hdc, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
1436 		}
1437 	}
1438 }
1439 
1440 
1441 #ifdef _LIGHT_STARTMENU
1442 
ResizeToButtons()1443 void StartMenu::ResizeToButtons()
1444 {
1445 	WindowRect rect(_hwnd);
1446 
1447 	WindowCanvas canvas(_hwnd);
1448 	FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
1449 
1450 	const int icon_size = _icon_size;
1451 
1452 	int max_width = STARTMENU_WIDTH_MIN;
1453 	int height = 0;
1454 
1455 	for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
1456 		int w = GetStartMenuBtnTextWidth(canvas, it->_title, _hwnd);
1457 
1458 		if (w > max_width)
1459 			max_width = w;
1460 
1461 		if (it->_id == -1)
1462 			height += STARTMENU_SEP_HEIGHT(icon_size);
1463 		else
1464 			height += STARTMENU_LINE_HEIGHT(icon_size);
1465 	}
1466 
1467 	 // calculate new window size
1468 	int text_width = max_width + icon_size + 10/*placeholder*/ + 16/*arrow*/;
1469 
1470 	RECT rt_hgt = {rect.left, rect.bottom-_border_top-height, rect.left+_border_left+text_width, rect.bottom};
1471 	AdjustWindowRectEx(&rt_hgt, GetWindowStyle(_hwnd), FALSE, GetWindowExStyle(_hwnd));
1472 
1473 	 // ignore movement, only look at the size change
1474 	rect.right = rect.left + (rt_hgt.right-rt_hgt.left);
1475 	rect.top = rect.bottom - (rt_hgt.bottom-rt_hgt.top);
1476 
1477 	 // move down if we are too high
1478 	if (rect.top < 0) {
1479 		int dy = -rect.top;
1480 		rect.top += dy;
1481 		rect.bottom += dy;
1482 	}
1483 
1484 	 // enable scroll mode for long start menus, which span more than the whole screen height
1485 	int cyscreen = GetSystemMetrics(SM_CYSCREEN);
1486 	int bottom_max = 0;
1487 
1488 	if (rect.bottom > cyscreen) {
1489 		_arrow_btns = true;
1490 
1491 		_invisible_lines = (rect.bottom-cyscreen+(STARTMENU_LINE_HEIGHT(icon_size)+1))/STARTMENU_LINE_HEIGHT(icon_size)+1;
1492 		rect.bottom -= _invisible_lines * STARTMENU_LINE_HEIGHT(icon_size);
1493 
1494 		bottom_max = rect.bottom;
1495 
1496 		if (_floating_btn)
1497 			rect.bottom += 6;	// lower scroll arrow
1498 		else {
1499 			_border_top += 6;	// upper scroll arrow
1500 			rect.bottom += 2*6;	// upper+lower scroll arrow
1501 		}
1502 	}
1503 
1504 	MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
1505 
1506 	if (bottom_max) {
1507 		POINT pt = {0, bottom_max};
1508 
1509 		ScreenToClient(_hwnd, &pt);
1510 
1511 		_bottom_max = pt.y;
1512 	}
1513 }
1514 
1515 #else // _LIGHT_STARTMENU
1516 
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)1517 LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1518 {
1519 	switch(nmsg) {
1520 	  case WM_MOUSEMOVE:
1521 		 // automatically set the focus to startmenu entries when moving the mouse over them
1522 		if (GetFocus()!=_hwnd && !(GetWindowStyle(_hwnd)&WS_DISABLED))
1523 			SetFocus(_hwnd);
1524 		break;
1525 
1526 	  case WM_SETFOCUS:
1527 		PostParent(PM_STARTENTRY_FOCUSED, _hasSubmenu, (LPARAM)_hwnd);
1528 		goto def;
1529 
1530 	  case WM_CANCELMODE:
1531 		 // route WM_CANCELMODE to the startmenu window
1532 		return SendParent(nmsg, wparam, lparam);
1533 
1534 	  default: def:
1535 		return super::WndProc(nmsg, wparam, lparam);
1536 	}
1537 
1538 	return 0;
1539 }
1540 
DrawItem(LPDRAWITEMSTRUCT dis)1541 void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis)
1542 {
1543 	TCHAR title[BUFFER_LEN];
1544 
1545 	GetWindowText(_hwnd, title, BUFFER_LEN);
1546 
1547 	DrawStartMenuButton(dis->hDC, dis->rcItem, title, _hIcon,
1548 						_hasSubmenu,
1549 						!(dis->itemState & ODS_DISABLED),
1550 						dis->itemState&ODS_FOCUS? true: false,
1551 						dis->itemState&ODS_SELECTED? true: false);
1552 }
1553 
1554 #endif
1555 
1556 
StartMenuRoot(HWND hwnd,const StartMenuRootCreateInfo & info)1557 StartMenuRoot::StartMenuRoot(HWND hwnd, const StartMenuRootCreateInfo& info)
1558  :	super(hwnd, info._icon_size),
1559 	_hwndStartButton(0)
1560 {
1561 	if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCOMMONGROUPS))
1562 		try {
1563 			 // insert directory "All Users\Start Menu"
1564 			ShellDirectory cmn_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
1565 			_dirs.push_back(StartMenuDirectory(cmn_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_COMMON_PROGRAMS, _hwnd)));
1566 		} catch(COMException&) {
1567 			// ignore exception and don't show additional shortcuts
1568 		}
1569 
1570 	try {
1571 		 // insert directory "<user name>\Start Menu"
1572 		ShellDirectory usr_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
1573 		_dirs.push_back(StartMenuDirectory(usr_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_PROGRAMS, _hwnd)));
1574 	} catch(COMException&) {
1575 		// ignore exception and don't show additional shortcuts
1576 	}
1577 
1578 	ReadLogoSize();
1579 }
1580 
ReadLogoSize()1581 void StartMenuRoot::ReadLogoSize()
1582 {
1583 	 // read size of logo bitmap
1584 	BITMAP bmp_hdr;
1585 	GetObject(ResBitmap(GetLogoResId()), sizeof(BITMAP), &bmp_hdr);
1586 	_logo_size.cx = bmp_hdr.bmWidth;
1587 	_logo_size.cy = bmp_hdr.bmHeight;
1588 
1589 	 // cache logo width
1590 	_border_left = _logo_size.cx + 1;
1591 }
1592 
1593 
CalculateStartPos(HWND hwndOwner,RECT & rect,int icon_size)1594 static void CalculateStartPos(HWND hwndOwner, RECT& rect, int icon_size)
1595 {
1596 	WindowRect pos(hwndOwner);
1597 
1598 	rect.left = pos.left;
1599 	rect.top = pos.top-STARTMENU_LINE_HEIGHT(icon_size)-4;
1600 	rect.right = pos.left+STARTMENU_WIDTH_MIN;
1601 	rect.bottom = pos.top;
1602 
1603 #ifndef _LIGHT_STARTMENU
1604 	rect.top += STARTMENU_LINE_HEIGHT(icon_size);
1605 #endif
1606 }
1607 
Create(HWND hwndOwner,int icon_size)1608 HWND StartMenuRoot::Create(HWND hwndOwner, int icon_size)
1609 {
1610 	RECT rect;
1611 
1612 	CalculateStartPos(hwndOwner, rect, icon_size);
1613 
1614 	StartMenuRootCreateInfo create_info;
1615 
1616 	create_info._icon_size = icon_size;
1617 
1618 	return Window::Create(WINDOW_CREATOR_INFO(StartMenuRoot,StartMenuRootCreateInfo), &create_info, 0, GetWndClasss(), TITLE_STARTMENU,
1619 							WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,
1620 							rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndOwner);
1621 }
1622 
1623 
TrackStartmenu()1624 void StartMenuRoot::TrackStartmenu()
1625 {
1626 	MSG msg;
1627 	HWND hwnd = _hwnd;
1628 
1629 #ifdef _LIGHT_STARTMENU
1630 	_selected_id = -1;
1631 #endif
1632 
1633 #ifdef _LIGHT_STARTMENU
1634 	 // recalculate start menu root position
1635 	RECT rect;
1636 
1637 	CalculateStartPos(_hwndStartButton, rect, _icon_size);
1638 
1639 	SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);
1640 
1641 	ResizeToButtons();
1642 #endif
1643 
1644 	 // show previously hidden start menu
1645 	ShowWindow(hwnd, SW_SHOW);
1646 	SetForegroundWindow(hwnd);
1647 
1648 	while(IsWindow(hwnd) && IsWindowVisible(hwnd)) {
1649 		if (!GetMessage(&msg, 0, 0, 0)) {
1650 			PostQuitMessage(msg.wParam);
1651 			break;
1652 		}
1653 
1654 		 // Check for a mouse click on any window, that is not part of the start menu
1655 		if (msg.message==WM_LBUTTONDOWN || msg.message==WM_MBUTTONDOWN || msg.message==WM_RBUTTONDOWN) {
1656 			StartMenu* menu_wnd = NULL;
1657 
1658 			for(HWND hwnd=msg.hwnd; hwnd; hwnd=GetParent(hwnd)) {
1659 				menu_wnd = WINDOW_DYNAMIC_CAST(StartMenu, hwnd);
1660 
1661 				if (menu_wnd)
1662 					break;
1663 			}
1664 
1665 			if (!menu_wnd) {
1666 				CloseStartMenu();
1667 				break;
1668 			}
1669 		}
1670 
1671 		try {
1672 			if (pretranslate_msg(&msg))
1673 				continue;
1674 
1675 			if (dispatch_dialog_msg(&msg))
1676 				continue;
1677 
1678 			TranslateMessage(&msg);
1679 
1680 			try {
1681 				DispatchMessage(&msg);
1682 			} catch(COMException& e) {
1683 				HandleException(e, _hwnd);
1684 			}
1685 		} catch(COMException& e) {
1686 			HandleException(e, _hwnd);
1687 		}
1688 	}
1689 }
1690 
Command(int id,int code)1691 int StartMenuRoot::Command(int id, int code)
1692 {
1693 	return super::Command(id, code);
1694 }
1695 
Init(LPCREATESTRUCT pcs)1696 LRESULT	StartMenuRoot::Init(LPCREATESTRUCT pcs)
1697 {
1698 	 // add buttons for entries in _entries
1699 	if (super::Init(pcs))
1700 		return 1;
1701 
1702 	AddSeparator();
1703 
1704 
1705 	 // insert hard coded start entries
1706 	AddButton(ResString(IDS_PROGRAMS),		ICID_APPS, true, IDC_PROGRAMS);
1707 
1708 	AddButton(ResString(IDS_DOCUMENTS),		ICID_DOCUMENTS, true, IDC_DOCUMENTS);
1709 
1710 	if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
1711 		AddButton(ResString(IDS_RECENT),	ICID_RECENT, true, IDC_RECENT);
1712 
1713 	AddButton(ResString(IDS_FAVORITES),		ICID_FAVORITES, true, IDC_FAVORITES);
1714 
1715 	AddButton(ResString(IDS_SETTINGS),		ICID_CONFIG, true, IDC_SETTINGS);
1716 
1717 	AddButton(ResString(IDS_BROWSE),		ICID_FOLDER, true, IDC_BROWSE);
1718 
1719 	if (!g_Globals._SHRestricted || !SHRestricted(REST_NOFIND))
1720 		AddButton(ResString(IDS_SEARCH),	ICID_SEARCH, true, IDC_SEARCH);
1721 
1722 	AddButton(ResString(IDS_START_HELP),	ICID_INFO, false, IDC_START_HELP);
1723 
1724 	if (!g_Globals._SHRestricted || !SHRestricted(REST_NORUN))
1725 		AddButton(ResString(IDS_LAUNCH),	ICID_ACTION, false, IDC_LAUNCH);
1726 
1727 
1728 	AddSeparator();
1729 
1730 
1731 	if (!g_Globals._SHRestricted || SHRestricted(REST_STARTMENULOGOFF) != 1)
1732 		AddButton(ResString(IDS_LOGOFF),	ICID_LOGOFF, false, IDC_LOGOFF);
1733 
1734 #ifdef __REACTOS__
1735 		AddButton(ResString(IDS_RESTART), ICID_RESTART, false, IDC_RESTART);
1736 #endif
1737 
1738 	if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCLOSE))
1739 		AddButton(ResString(IDS_SHUTDOWN),	ICID_SHUTDOWN, false, IDC_SHUTDOWN);
1740 
1741 #ifndef __REACTOS__
1742 	AddButton(ResString(IDS_TERMINATE),	ICID_LOGOFF, false, IDC_TERMINATE);
1743 #endif
1744 
1745 
1746 
1747 
1748 #ifdef _LIGHT_STARTMENU
1749 	 // set the window size to fit all buttons
1750 	ResizeToButtons();
1751 #endif
1752 
1753 	return 0;
1754 }
1755 
1756 
AddEntries()1757 void StartMenuRoot::AddEntries()
1758 {
1759 	super::AddEntries();
1760 
1761 	AddButton(ResString(IDS_EXPLORE),	ICID_EXPLORER, false, IDC_EXPLORE);
1762 }
1763 
1764 
WndProc(UINT nmsg,WPARAM wparam,LPARAM lparam)1765 LRESULT	StartMenuRoot::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
1766 {
1767 	switch(nmsg) {
1768 	  case WM_PAINT: {
1769 		PaintCanvas canvas(_hwnd);
1770 		Paint(canvas);
1771 		break;}
1772 
1773 	  case WM_DISPLAYCHANGE:
1774 		 // re-evaluate logo size using the correct color depth
1775 		ReadLogoSize();
1776 		break;
1777 
1778 	  default:
1779 		return super::WndProc(nmsg, wparam, lparam);
1780 	}
1781 
1782 	return 0;
1783 }
1784 
Paint(PaintCanvas & canvas)1785 void StartMenuRoot::Paint(PaintCanvas& canvas)
1786 {
1787 	MemCanvas mem_dc;
1788 	ResBitmap bmp(GetLogoResId());
1789 	BitmapSelection sel(mem_dc, bmp);
1790 
1791 	ClientRect clnt(_hwnd);
1792 	int h = min(_logo_size.cy, clnt.bottom);
1793 
1794 	RECT rect = {0, 0, _logo_size.cx, clnt.bottom-h};
1795 	HBRUSH hbr = CreateSolidBrush(GetPixel(mem_dc, 0, 0));
1796 	FillRect(canvas, &rect, hbr);
1797 	DeleteObject(hbr);
1798 
1799 	PatBlt(canvas, _logo_size.cx, 0, 1, clnt.bottom, WHITENESS);
1800 
1801     BitBlt(canvas, 0, clnt.bottom-h, _logo_size.cx, h, mem_dc, 0, ( h<_logo_size.cy ? _logo_size.cy-h : 0) , SRCCOPY);
1802 
1803 	super::Paint(canvas);
1804 }
1805 
GetLogoResId()1806 UINT StartMenuRoot::GetLogoResId()
1807 {
1808 	WindowCanvas dc(_hwnd);
1809 
1810 	int clr_bits = GetDeviceCaps(dc, BITSPIXEL);
1811 
1812 	if (clr_bits > 8)
1813 		return IDB_LOGOV;
1814 	else if (clr_bits > 4)
1815 		return IDB_LOGOV256;
1816 	else
1817 		return IDB_LOGOV16;
1818 }
1819 
1820 
CloseStartMenu(int id)1821 void StartMenuRoot::CloseStartMenu(int id)
1822 {
1823 	if (_submenu)
1824 		CloseSubmenus();
1825 
1826 	ShowWindow(_hwnd, SW_HIDE);
1827 }
1828 
IsStartMenuVisible() const1829 bool StartMenuRoot::IsStartMenuVisible() const
1830 {
1831     return IsWindowVisible(_hwnd) != FALSE;
1832 }
1833 
ProcessKey(int vk)1834 void StartMenuRoot::ProcessKey(int vk)
1835 {
1836 	switch(vk) {
1837 	  case VK_LEFT:
1838 		if (_submenu)
1839 			CloseOtherSubmenus();
1840 		// don't close start menu root
1841 		break;
1842 
1843 	  default:
1844 		super::ProcessKey(vk);
1845 	}
1846 }
1847 
1848 
Command(int id,int code)1849 int StartMenuHandler::Command(int id, int code)
1850 {
1851 	switch(id) {
1852 
1853 	// start menu root
1854 
1855 	  case IDC_PROGRAMS:
1856 		CreateSubmenu(id, CSIDL_COMMON_PROGRAMS, CSIDL_PROGRAMS, ResString(IDS_PROGRAMS));
1857 		break;
1858 
1859 	  case IDC_EXPLORE:
1860 		CloseStartMenu(id);
1861 		explorer_show_frame(SW_SHOWNORMAL);
1862 		break;
1863 
1864 	  case IDC_LAUNCH:
1865 		CloseStartMenu(id);
1866 		ShowLaunchDialog(g_Globals._hwndDesktopBar);
1867 		break;
1868 
1869 	  case IDC_DOCUMENTS:
1870 		CreateSubmenu(id, CSIDL_PERSONAL, ResString(IDS_DOCUMENTS));
1871 		break;
1872 
1873 	  case IDC_RECENT:
1874 		CreateSubmenu(id, CSIDL_RECENT, ResString(IDS_RECENT), STARTMENU_CREATOR(RecentStartMenu));
1875 		break;
1876 
1877 	  case IDC_FAVORITES:
1878 #ifndef _SHELL32_FAVORITES
1879 		CreateSubmenu(id, ResString(IDS_FAVORITES), STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(g_Globals._favorites));
1880 #else
1881 		CreateSubmenu(id, CSIDL_COMMON_FAVORITES, CSIDL_FAVORITES, ResString(IDS_FAVORITES));
1882 #endif
1883 		break;
1884 
1885 	  case IDC_BROWSE:
1886 		CreateSubmenu(id, ResString(IDS_BROWSE), STARTMENU_CREATOR(BrowseMenu));
1887 		break;
1888 
1889 	  case IDC_SETTINGS:
1890 		CreateSubmenu(id, ResString(IDS_SETTINGS), STARTMENU_CREATOR(SettingsMenu));
1891 		break;
1892 
1893 	  case IDC_SEARCH:
1894 		CreateSubmenu(id, ResString(IDS_SEARCH), STARTMENU_CREATOR(SearchMenu));
1895 		break;
1896 
1897 	  case IDC_START_HELP:
1898 		CloseStartMenu(id);
1899 		MessageBox(g_Globals._hwndDesktopBar, TEXT("Help not yet implemented"), ResString(IDS_TITLE), MB_OK);
1900 		break;
1901 
1902 	  case IDC_LOGOFF:
1903 		CloseStartMenu(id);
1904 		ShowLogoffDialog(g_Globals._hwndDesktopBar);
1905 		break;
1906 
1907 #ifndef __REACTOS__
1908 	  case IDC_TERMINATE:
1909 		DestroyWindow(g_Globals._hwndDesktopBar);
1910 		DestroyWindow(g_Globals._hwndDesktop);
1911 		break;
1912 #endif
1913 
1914 	  case IDC_SHUTDOWN:
1915 		CloseStartMenu(id);
1916 		ShowExitWindowsDialog(g_Globals._hwndDesktop);
1917 		break;
1918 
1919       case IDC_RESTART:
1920 		CloseStartMenu(id);
1921         ShowRestartDialog(g_Globals._hwndDesktop, EWX_REBOOT);
1922         /* An alternative way to do restart without shell32 help */
1923         //launch_file(_hwnd, TEXT("shutdown.exe"), SW_HIDE, TEXT("-r"));
1924 		break;
1925 
1926 	// settings menu
1927 
1928 	  case ID_DESKTOPBAR_SETTINGS:
1929 		CloseStartMenu(id);
1930 		ExplorerPropertySheet(g_Globals._hwndDesktopBar);
1931 		break;
1932 
1933 	  case IDC_CONTROL_PANEL: {
1934 		CloseStartMenu(id);
1935 #ifndef ROSSHELL
1936 #ifndef _NO_MDI
1937 		XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
1938 		bool mdi = XMLBool(explorer_options, "mdi", true);
1939 
1940 		if (mdi)
1941 			MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
1942 		else
1943 #endif
1944 			SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
1945 #else
1946 		launch_file(_hwnd, SHELLPATH_CONTROL_PANEL);
1947 #endif
1948 		break;}
1949 
1950 	  case IDC_SETTINGS_MENU:
1951 		CreateSubmenu(id, CSIDL_CONTROLS, ResString(IDS_SETTINGS_MENU));
1952 		break;
1953 
1954 	  case IDC_PRINTERS: {
1955 		CloseStartMenu(id);
1956 
1957 #ifndef ROSSHELL
1958 #ifndef _NO_MDI
1959 		XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
1960 		bool mdi = XMLBool(explorer_options, "mdi", true);
1961 
1962 		if (mdi)
1963 			MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
1964 		else
1965 #endif
1966 			SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}"), 0);
1967 #else
1968 		launch_file(_hwnd, SHELLPATH_PRINTERS);
1969 #endif
1970 		break;}
1971 
1972 #if 0	///@todo use printer start menu folder per default and allow opening "printers" cabinet window using the context menu
1973 	  case IDC_PRINTERS_MENU:
1974 		CreateSubmenu(id, CSIDL_PRINTERS, CSIDL_PRINTHOOD, ResString(IDS_PRINTERS));
1975 /*		StartMenuFolders new_folders;
1976 
1977 		try {
1978 			new_folders.push_back(ShellPath(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")));
1979 		} catch(COMException&) {
1980 		}
1981 
1982 		CreateSubmenu(id, new_folders, ResString(IDS_PRINTERS));*/
1983 		break;
1984 #endif
1985 
1986 	  case IDC_ADMIN:
1987 #ifndef ROSSHELL
1988 		CreateSubmenu(id, CSIDL_COMMON_ADMINTOOLS, CSIDL_ADMINTOOLS, ResString(IDS_ADMIN));
1989 		//CloseStartMenu(id);
1990 		//MainFrame::Create(SpecialFolderPath(CSIDL_COMMON_ADMINTOOLS, _hwnd), OWM_PIDL);
1991 #else
1992 		launch_file(_hwnd, SpecialFolderFSPath(CSIDL_COMMON_ADMINTOOLS, _hwnd));
1993 #endif
1994 		break;
1995 
1996 		case IDC_CONNECTIONS:{
1997 		CloseStartMenu(id);
1998 #ifndef ROSSHELL
1999 #ifndef _NO_MDI
2000 		XMLPos explorer_options = g_Globals.get_cfg("general/explorer");
2001 		bool mdi = XMLBool(explorer_options, "mdi", true);
2002 
2003 		if (mdi)
2004 			MDIMainFrame::Create(SHELLPATH_NET_CONNECTIONS, 0);
2005 		else
2006 #endif
2007 			SDIMainFrame::Create(SHELLPATH_NET_CONNECTIONS, 0);
2008 #else
2009 		launch_file(_hwnd, SHELLPATH_NET_CONNECTIONS);
2010 #endif
2011 		break;}
2012 
2013 
2014 	// browse menu
2015 
2016 	  case IDC_NETWORK:
2017 #ifdef __REACTOS__	///@todo to be removed when network browsing will be implemented in shell namespace
2018 		MessageBox(0, TEXT("not yet implemented"), ResString(IDS_TITLE), MB_OK);
2019 #else
2020 		CreateSubmenu(id, CSIDL_NETWORK, ResString(IDS_NETWORK));
2021 #endif
2022 		break;
2023 
2024 	  case IDC_DRIVES:
2025 		///@todo exclude removable drives
2026 		CreateSubmenu(id, CSIDL_DRIVES, ResString(IDS_DRIVES));
2027 		break;
2028 
2029 
2030 	// search menu
2031 
2032 	  case IDC_SEARCH_PROGRAM:
2033 		CloseStartMenu(id);
2034 		Dialog::DoModal(IDD_SEARCH_PROGRAM, WINDOW_CREATOR(FindProgramDlg));
2035 		break;
2036 
2037 	  case IDC_SEARCH_FILES:
2038 		CloseStartMenu(id);
2039 		ShowSearchDialog();
2040 		break;
2041 
2042 	  case IDC_SEARCH_COMPUTER:
2043 		CloseStartMenu(id);
2044 		ShowSearchComputer();
2045 		break;
2046 
2047 
2048 	  default:
2049 		return super::Command(id, code);
2050 	}
2051 
2052 	return 0;
2053 }
2054 
2055 
ShowSearchDialog()2056 void StartMenuHandler::ShowSearchDialog()
2057 {
2058 #ifndef __REACTOS__	///@todo to be removed when SHFindFiles() will be implemented in shell32.dll
2059 	static DynamicFct<SHFINDFILES> SHFindFiles(TEXT("SHELL32"), 90);
2060 
2061 	if (SHFindFiles)
2062 		(*SHFindFiles)(NULL, NULL);
2063 	else
2064 #endif
2065 		MessageBox(0, TEXT("SHFindFiles() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2066 }
2067 
ShowSearchComputer()2068 void StartMenuHandler::ShowSearchComputer()
2069 {
2070 #ifndef __REACTOS__	///@todo to be removed when SHFindComputer() will be implemented in shell32.dll
2071 	static DynamicFct<SHFINDCOMPUTER> SHFindComputer(TEXT("SHELL32"), 91);
2072 
2073 	if (SHFindComputer)
2074 		(*SHFindComputer)(NULL, NULL);
2075 	else
2076 #endif
2077 		MessageBox(0, TEXT("SHFindComputer() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2078 }
2079 
2080 struct RunDialogThread : public Thread
2081 {
2082 	int	Run();
2083 };
2084 
Run()2085 int RunDialogThread::Run()
2086 {
2087 	static DynamicFct<RUNFILEDLG> RunFileDlg(TEXT("SHELL32"), 61);
2088 
2089 	// RunFileDlg needs owner window to properly position dialog
2090 	// that window will be disabled so we can't use DesktopBar
2091 	RECT rect = {0};
2092 #ifndef TASKBAR_AT_TOP
2093 	rect.top = GetSystemMetrics(SM_CYSCREEN) - DESKTOPBARBAR_HEIGHT;
2094 #endif
2095 	rect.right = GetSystemMetrics(SM_CXSCREEN);
2096 	rect.bottom = rect.top + DESKTOPBARBAR_HEIGHT;
2097 	Static dlgOwner(0, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0, 0);
2098 
2099 	 // Show "Run..." dialog
2100 	if (RunFileDlg) {
2101 		(*RunFileDlg)(dlgOwner, 0, NULL, NULL, NULL, RFF_CALCDIRECTORY);
2102 	}
2103 	DestroyWindow(dlgOwner);
2104 	return 0;
2105 }
2106 
ShowLaunchDialog(HWND hwndOwner)2107 void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner)
2108 {
2109 	RunDialogThread * rdt = new RunDialogThread();
2110 	rdt->Start();
2111 }
2112 
ShowLogoffDialog(HWND hwndOwner)2113 void StartMenuHandler::ShowLogoffDialog(HWND hwndOwner)
2114 {
2115 	static DynamicFct<LOGOFFWINDOWSDIALOG> LogoffWindowsDialog(TEXT("SHELL32"), 54);
2116 //	static DynamicFct<RESTARTWINDOWSDLG> RestartDialog(TEXT("SHELL32"), 59);
2117 
2118 	if (LogoffWindowsDialog)
2119 		(*LogoffWindowsDialog)(0);
2120 /* The RestartDialog function prompts about some system setting change. This is not what we want to display here.
2121 	else if (RestartDialog)
2122 		return (*RestartDialog)(hwndOwner, (LPWSTR)L"You selected <Log Off>.\n\n", EWX_LOGOFF) == 1;	///@todo ANSI string conversion if needed
2123 */
2124 	else
2125 		MessageBox(hwndOwner, TEXT("LogoffWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2126 }
2127 
ShowExitWindowsDialog(HWND hwndOwner)2128 void ShowExitWindowsDialog(HWND hwndOwner)
2129 {
2130 	static DynamicFct<EXITWINDOWSDLG> ExitWindowsDialog(TEXT("SHELL32"), 60);
2131 
2132 	if (ExitWindowsDialog)
2133 		(*ExitWindowsDialog)(hwndOwner);
2134 	else
2135 		MessageBox(hwndOwner, TEXT("ExitWindowsDialog() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2136 }
2137 
ShowRestartDialog(HWND hwndOwner,UINT flags)2138 void StartMenuHandler::ShowRestartDialog(HWND hwndOwner, UINT flags)
2139 {
2140 	static DynamicFct<RESTARTWINDOWSDLG> RestartDlg(TEXT("SHELL32"), 59);
2141 
2142 	if (RestartDlg)
2143 		(*RestartDlg)(hwndOwner, (LPWSTR)L"You selected restart.\n\n", flags);
2144 	else
2145 		MessageBox(hwndOwner, TEXT("RestartDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE), MB_OK);
2146 }
2147 
AddEntries()2148 void SettingsMenu::AddEntries()
2149 {
2150 	super::AddEntries();
2151 
2152 #if defined(ROSSHELL) || defined(__REACTOS__)	// __REACTOS__ to be removed when printers will be implemented
2153 //TODO	AddButton(ResString(IDS_PRINTERS),			ICID_PRINTER, false, IDC_PRINTERS_MENU);
2154 #else
2155 //TODO	AddButton(ResString(IDS_PRINTERS),			ICID_PRINTER, true, IDC_PRINTERS_MENU);
2156 #endif
2157 
2158 	AddButton(ResString(IDS_CONNECTIONS),		ICID_NETCONNS, false, IDC_CONNECTIONS);
2159 
2160 	AddButton(ResString(IDS_ADMIN),				ICID_ADMIN, true, IDC_ADMIN);
2161 
2162 	if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
2163 		AddButton(ResString(IDS_SETTINGS_MENU),	ICID_CONFIG, true, IDC_SETTINGS_MENU);
2164 
2165 	AddButton(ResString(IDS_DESKTOPBAR_SETTINGS), ICID_DESKSETTING, false, ID_DESKTOPBAR_SETTINGS);
2166 
2167 	AddButton(ResString(IDS_PRINTERS),			ICID_PRINTER, false, IDC_PRINTERS);
2168 
2169 	if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCONTROLPANEL))
2170 		AddButton(ResString(IDS_CONTROL_PANEL),	ICID_CONTROLPAN, false, IDC_CONTROL_PANEL);
2171 }
2172 
AddEntries()2173 void BrowseMenu::AddEntries()
2174 {
2175 	super::AddEntries();
2176 
2177 	if (!g_Globals._SHRestricted || !SHRestricted(REST_NONETHOOD))	// or REST_NOENTIRENETWORK ?
2178 #if defined(ROSSHELL) || defined(__REACTOS__)	// __REACTOS__ to be removed when printer/network will be implemented
2179 		AddButton(ResString(IDS_NETWORK),		ICID_NETWORK, false, IDC_NETWORK);
2180 #else
2181 		AddButton(ResString(IDS_NETWORK),		ICID_NETWORK, true, IDC_NETWORK);
2182 #endif
2183 
2184 	AddButton(ResString(IDS_DRIVES),			ICID_FOLDER, true, IDC_DRIVES);
2185 }
2186 
AddEntries()2187 void SearchMenu::AddEntries()
2188 {
2189 	super::AddEntries();
2190 
2191 	AddButton(ResString(IDS_SEARCH_FILES),		ICID_SEARCH_DOC, false, IDC_SEARCH_FILES);
2192 
2193 	if (!g_Globals._SHRestricted || !SHRestricted(REST_HASFINDCOMPUTERS))
2194 		AddButton(ResString(IDS_SEARCH_COMPUTER),ICID_COMPUTER, false, IDC_SEARCH_COMPUTER);
2195 
2196 	AddButton(ResString(IDS_SEARCH_PRG),		ICID_APPS, false, IDC_SEARCH_PROGRAM);
2197 }
2198 
2199 
AddEntries()2200 void RecentStartMenu::AddEntries()
2201 {
2202 	for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
2203 		StartMenuDirectory& smd = *it;
2204 		ShellDirectory& dir = smd._dir;
2205 
2206 		if (!dir._scanned) {
2207 			WaitCursor wait;
2208 
2209 #ifdef _LAZY_ICONEXTRACT
2210 			dir.smart_scan(SORT_NAME, SCAN_DONT_EXTRACT_ICONS);
2211 #else
2212 			dir.smart_scan(SORT_NAME);
2213 #endif
2214 		}
2215 
2216 		dir.sort_directory(SORT_DATE);
2217 		AddShellEntries(dir, RECENT_DOCS_COUNT, smd._ignore);	///@todo read max. count of entries from registry
2218 	}
2219 }
2220 
2221 
2222 #ifndef _SHELL32_FAVORITES
2223 
AddEntries()2224 void FavoritesMenu::AddEntries()
2225 {
2226 	super::AddEntries();
2227 
2228 	String lwr_filter = _create_info._filter;
2229 	lwr_filter.toLower();
2230 
2231 	for(BookmarkList::iterator it=_bookmarks.begin(); it!=_bookmarks.end(); ++it) {
2232 		BookmarkNode& node = *it;
2233 
2234 		int id = ++_next_id;
2235 
2236 		_entries[id] = node;
2237 
2238 		if (node._type == BookmarkNode::BMNT_FOLDER) {
2239 			BookmarkFolder& folder = *node._pfolder;
2240 
2241 			AddButton(folder._name, ICID_FOLDER, true, id);
2242 		} else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
2243 			Bookmark& bookmark = *node._pbookmark;
2244 
2245 			ICON_ID icon = ICID_NONE;
2246 
2247 			if (!bookmark._icon_path.empty())
2248 				icon = g_Globals._icon_cache.extract(bookmark._icon_path, bookmark._icon_idx);
2249 
2250 			 // filter non-directory entries
2251 			if (!lwr_filter.empty()) {
2252 				String lwr_name = bookmark._name;
2253 				String lwr_desc = bookmark._description;
2254 				String lwr_url = bookmark._url;
2255 
2256 				lwr_name.toLower();
2257 				lwr_desc.toLower();
2258 				lwr_url.toLower();
2259 
2260 				if (!_tcsstr(lwr_name,lwr_filter) && !_tcsstr(lwr_desc,lwr_filter) && !_tcsstr(lwr_url,lwr_filter))
2261 					continue;
2262 			}
2263 
2264 			AddButton(bookmark._name, icon!=ICID_NONE?icon:ICID_BOOKMARK, false, id);
2265 		}
2266 	}
2267 }
2268 
Command(int id,int code)2269 int FavoritesMenu::Command(int id, int code)
2270 {
2271 	BookmarkMap::iterator found = _entries.find(id);
2272 
2273 	if (found != _entries.end()) {
2274 		BookmarkNode& node = found->second;
2275 
2276 		if (node._type == BookmarkNode::BMNT_FOLDER) {
2277 			BookmarkFolder& folder = *node._pfolder;
2278 
2279 			if (CloseOtherSubmenus(id))
2280 				CreateSubmenu(id, folder._name, STARTMENU_CREATOR(FavoritesMenu), &static_cast<BookmarkList&>(folder._bookmarks));
2281 		} else if (node._type == BookmarkNode::BMNT_BOOKMARK) {
2282 			Bookmark& bookmark = *node._pbookmark;
2283 
2284 			String url = bookmark._url;
2285 			HWND hparent = GetParent(_hwnd);
2286 
2287 			CloseStartMenu(id);
2288 
2289 			launch_file(hparent, url, SW_SHOWNORMAL);
2290 		}
2291 
2292 		return 0;
2293 	}
2294 
2295 	return super::Command(id, code);
2296 }
2297 
2298 #endif
2299