2 * Copyright 2003, 2004 Martin Fuchs
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.
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.
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
25 // Explorer start menu
27 // Martin Fuchs, 19.08.2003
29 // Credits: Thanks to Everaldo (http://www.everaldo.com) for his nice looking icons.
35 #include "../explorer_intres.h"
37 #include "desktopbar.h"
38 #include "startmenu.h"
40 #include "../dialogs/searchprogram.h"
41 #include "../dialogs/settings.h"
44 StartMenu::StartMenu(HWND hwnd
)
47 _next_id
= IDC_FIRST_MENU
;
52 _bottom_max
= INT_MAX
;
54 _floating_btn
= false;
56 _scroll_mode
= SCROLL_NOT
;
60 _last_pos
= WindowRect(hwnd
).pos();
61 #ifdef _LIGHT_STARTMENU
67 StartMenu::StartMenu(HWND hwnd
, const StartMenuCreateInfo
& create_info
)
69 _create_info(create_info
)
71 for(StartMenuFolders::const_iterator it
=create_info
._folders
.begin(); it
!=create_info
._folders
.end(); ++it
)
73 _dirs
.push_back(ShellDirectory(GetDesktopFolder(), *it
, _hwnd
));
75 _next_id
= IDC_FIRST_MENU
;
79 _border_top
= create_info
._border_top
;
80 _bottom_max
= INT_MAX
;
82 _floating_btn
= create_info
._border_top
? true: false;
84 _scroll_mode
= SCROLL_NOT
;
88 _last_pos
= WindowRect(hwnd
).pos();
89 #ifdef _LIGHT_STARTMENU
95 StartMenu::~StartMenu()
97 SendParent(PM_STARTMENU_CLOSED
);
101 // We need this wrapper function for s_wcStartMenu, it calls the WIN32 API,
102 // though static C++ initializers are not allowed for Winelib applications.
103 BtnWindowClass
& StartMenu::GetWndClasss()
105 static BtnWindowClass
s_wcStartMenu(CLASSNAME_STARTMENU
);
107 return s_wcStartMenu
;
111 Window::CREATORFUNC_INFO
StartMenu::s_def_creator
= STARTMENU_CREATOR(StartMenu
);
113 HWND
StartMenu::Create(int x
, int y
, const StartMenuFolders
& folders
, HWND hwndParent
, LPCTSTR title
, CREATORFUNC_INFO creator
, void* info
)
115 UINT style
, ex_style
;
119 style
= WS_POPUP
|WS_THICKFRAME
|WS_CLIPCHILDREN
|WS_VISIBLE
;
121 top_height
= STARTMENU_TOP_BTN_SPACE
;
123 style
= WS_POPUP
|WS_CAPTION
|WS_SYSMENU
|WS_CLIPCHILDREN
|WS_VISIBLE
;
124 ex_style
= WS_EX_TOOLWINDOW
;
128 RECT rect
= {x
, y
-STARTMENU_LINE_HEIGHT
-top_height
, x
+STARTMENU_WIDTH_MIN
, y
};
130 #ifndef _LIGHT_STARTMENU
131 rect
.top
+= STARTMENU_LINE_HEIGHT
;
134 AdjustWindowRectEx(&rect
, style
, FALSE
, ex_style
);
136 StartMenuCreateInfo create_info
;
138 create_info
._folders
= folders
;
139 create_info
._border_top
= top_height
;
140 create_info
._creator
= creator
;
141 create_info
._info
= info
;
144 create_info
._title
= title
;
146 HWND hwnd
= Window::Create(creator
, &create_info
, ex_style
, GetWndClasss(), title
,
147 style
, rect
.left
, rect
.top
, rect
.right
-rect
.left
, rect
.bottom
-rect
.top
, hwndParent
);
149 // make sure the window is not off the screen
156 LRESULT
StartMenu::Init(LPCREATESTRUCT pcs
)
161 if (super::Init(pcs
))
164 // create buttons for registered entries in _entries
165 for(ShellEntryMap::const_iterator it
=_entries
.begin(); it
!=_entries
.end(); ++it
) {
166 const StartMenuEntry
& sme
= it
->second
;
167 bool hasSubmenu
= false;
169 for(ShellEntrySet::const_iterator it
=sme
._entries
.begin(); it
!=sme
._entries
.end(); ++it
)
170 if ((*it
)->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
173 #ifdef _LIGHT_STARTMENU
174 _buttons
.push_back(SMBtnInfo(sme
, it
->first
, hasSubmenu
));
176 AddButton(sme
._title
, sme
._hIcon
, hasSubmenu
, it
->first
);
180 #ifdef _LIGHT_STARTMENU
181 if (_buttons
.empty())
183 if (!GetWindow(_hwnd
, GW_CHILD
))
185 AddButton(ResString(IDS_EMPTY
), ICID_NONE
, false, 0, false);
187 #ifdef _LIGHT_STARTMENU
191 #ifdef _LAZY_ICONEXTRACT
192 PostMessage(_hwnd
, PM_UPDATE_ICONS
, 0, 0);
194 } catch(COMException
& e
) {
195 HandleException(e
, pcs
->hwndParent
); // destroys the start menu window while switching focus
201 void StartMenu::AddEntries()
203 for(StartMenuShellDirs::iterator it
=_dirs
.begin(); it
!=_dirs
.end(); ++it
) {
204 StartMenuDirectory
& smd
= *it
;
205 ShellDirectory
& dir
= smd
._dir
;
210 #ifdef _LAZY_ICONEXTRACT
211 dir
.smart_scan(SORT_NAME
, SCAN_FILESYSTEM
); // lazy icon extraction, try to read directly from filesystem
213 dir
.smart_scan(SORT_NAME
, SCAN_EXTRACT_ICONS
|SCAN_FILESYSTEM
);
217 AddShellEntries(dir
, -1, smd
._subfolders
);
222 void StartMenu::AddShellEntries(const ShellDirectory
& dir
, int max
, bool subfolders
)
226 for(Entry
*entry
=dir
._down
; entry
; entry
=entry
->_next
) {
227 // hide files like "desktop.ini"
228 if (entry
->_shell_attribs
& SFGAO_HIDDEN
)
229 //not appropriate for drive roots: if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
232 // hide subfolders if requested
234 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
237 // only 'max' entries shall be added.
241 if (entry
->_etype
== ET_SHELL
)
242 AddEntry(dir
._folder
, static_cast<ShellEntry
*>(entry
));
244 AddEntry(dir
._folder
, entry
);
249 LRESULT
StartMenu::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
253 PaintCanvas
canvas(_hwnd
);
258 ResizeButtons(LOWORD(lparam
)-_border_left
);
262 POINTS
& pos
= MAKEPOINTS(lparam
);
264 // move open submenus of floating menus
266 int dx
= pos
.x
- _last_pos
.x
;
267 int dy
= pos
.y
- _last_pos
.y
;
270 WindowRect
rt(_submenu
);
271 SetWindowPos(_submenu
, 0, rt
.left
+dx
, rt
.top
+dy
, 0, 0, SWP_NOSIZE
|SWP_NOACTIVATE
);
272 //MoveVisible(_submenu);
281 LRESULT res
= super::WndProc(nmsg
, wparam
, lparam
);
283 if (res
>=HTSIZEFIRST
&& res
<=HTSIZELAST
)
284 return HTCLIENT
; // disable window resizing
288 case WM_LBUTTONDOWN
: {
291 // check mouse cursor for coordinates of floating button
292 GetFloatingButtonRect(&rect
);
294 if (PtInRect(&rect
, Point(lparam
))) {
295 // create a floating copy of the current start menu
296 WindowRect
pos(_hwnd
);
298 ///@todo do something similar to StartMenuRoot::TrackStartmenu() in order to automatically close submenus when clicking on the desktop background
299 StartMenu::Create(pos
.left
+3, pos
.bottom
-3, _create_info
._folders
, 0, _create_info
._title
, _create_info
._creator
, _create_info
._info
);
303 #ifdef _LIGHT_STARTMENU
304 int id
= ButtonHitTest(Point(lparam
));
307 Command(id
, BN_CLICKED
);
312 if ((wparam
&0xFFF0) == SC_SIZE
)
313 return 0; // disable window resizing
317 // close start menu when activating another application
320 break; // don't call super::WndProc in case "this" has been deleted
325 #ifdef _LIGHT_STARTMENU
326 if (_scroll_mode
!= SCROLL_NOT
) {
333 #ifdef _LIGHT_STARTMENU
335 // automatically set the focus to startmenu entries when moving the mouse over them
336 if (lparam
!= _last_mouse_pos
) { // don't process WM_MOUSEMOVE when opening submenus using keyboard navigation
340 RECT rect_up
, rect_down
;
342 GetArrowButtonRects(&rect_up
, &rect_down
);
344 SCROLL_MODE scroll_mode
= SCROLL_NOT
;
346 if (PtInRect(&rect_up
, pt
))
347 scroll_mode
= SCROLL_UP
;
348 else if (PtInRect(&rect_down
, pt
))
349 scroll_mode
= SCROLL_DOWN
;
351 if (scroll_mode
!= _scroll_mode
) {
352 if (scroll_mode
== SCROLL_NOT
) {
357 SetTimer(_hwnd
, 0, 150, NULL
); // 150 ms scroll interval
361 _scroll_mode
= scroll_mode
;
365 int new_id
= ButtonHitTest(pt
);
367 if (new_id
> 0 && new_id
!= _selected_id
)
368 SelectButton(new_id
);
370 _last_mouse_pos
= lparam
;
375 if (_scroll_mode
== SCROLL_UP
) {
376 if (_scroll_pos
> 0) {
378 InvalidateRect(_hwnd
, NULL
, TRUE
);
381 if (_scroll_pos
<= _invisible_lines
) {
383 InvalidateRect(_hwnd
, NULL
, TRUE
);
392 case PM_STARTENTRY_FOCUSED
: { ///@todo use TrackMouseEvent() and WM_MOUSEHOVER to wait a bit before opening submenus
393 BOOL hasSubmenu
= wparam
;
394 HWND hctrl
= (HWND
)lparam
;
396 // automatically open submenus
398 UpdateWindow(_hwnd
); // draw focused button before waiting on submenu creation
399 //SendMessage(_hwnd, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hctrl),BN_CLICKED), (LPARAM)hctrl);
400 Command(GetDlgCtrlID(hctrl
), BN_CLICKED
);
402 // close any open submenu
403 CloseOtherSubmenus();
408 #ifdef _LAZY_ICONEXTRACT
409 case PM_UPDATE_ICONS
:
410 UpdateIcons(/*wparam*/);
414 case PM_STARTENTRY_LAUNCHED
:
415 if (GetWindowStyle(_hwnd
) & WS_CAPTION
) // don't automatically close floating menus
418 // route message to the parent menu and close menus after launching an entry
419 if (!SendParent(nmsg
, wparam
, lparam
))
421 return 1; // signal that we have received and processed the message
423 case PM_STARTMENU_CLOSED
:
427 case PM_SELECT_ENTRY
:
428 SelectButtonIndex(0, wparam
!=0);
431 #ifdef _LIGHT_STARTMENU
432 case WM_CONTEXTMENU
: {
433 Point
screen_pt(lparam
), clnt_pt
=screen_pt
;
434 ScreenToClient(_hwnd
, &clnt_pt
);
436 int id
= ButtonHitTest(clnt_pt
);
439 StartMenuEntry
& sme
= _entries
[id
];
441 for(ShellEntrySet::iterator it
=sme
._entries
.begin(); it
!=sme
._entries
.end(); ++it
) {
445 CHECKERROR(entry
->do_context_menu(_hwnd
, screen_pt
, _cm_ifs
)); // may close start menu because of focus loss
446 break; ///@todo handle context menu for more than one entry
454 return super::WndProc(nmsg
, wparam
, lparam
);
461 #ifdef _LIGHT_STARTMENU
463 int StartMenu::ButtonHitTest(POINT pt
)
465 ClientRect
clnt(_hwnd
);
466 RECT rect
= {_border_left
, _border_top
, clnt
.right
, STARTMENU_LINE_HEIGHT
};
468 if (pt
.x
<rect
.left
|| pt
.x
>rect
.right
)
471 for(SMBtnVector::const_iterator it
=_buttons
.begin()+_scroll_pos
; it
!=_buttons
.end(); ++it
) {
472 const SMBtnInfo
& info
= *it
;
477 rect
.bottom
= rect
.top
+ (info
._id
==-1? STARTMENU_SEP_HEIGHT
: STARTMENU_LINE_HEIGHT
);
479 if (rect
.bottom
> _bottom_max
)
482 if (pt
.y
< rect
.bottom
) // PtInRect(&rect, pt)
485 rect
.top
= rect
.bottom
;
491 void StartMenu::InvalidateSelection()
493 if (_selected_id
<= 0)
496 ClientRect
clnt(_hwnd
);
497 RECT rect
= {_border_left
, _border_top
, clnt
.right
, STARTMENU_LINE_HEIGHT
};
499 for(SMBtnVector::const_iterator it
=_buttons
.begin()+_scroll_pos
; it
!=_buttons
.end(); ++it
) {
500 const SMBtnInfo
& info
= *it
;
502 rect
.bottom
= rect
.top
+ (info
._id
==-1? STARTMENU_SEP_HEIGHT
: STARTMENU_LINE_HEIGHT
);
504 if (info
._id
== _selected_id
) {
505 InvalidateRect(_hwnd
, &rect
, TRUE
);
509 rect
.top
= rect
.bottom
;
513 const SMBtnInfo
* StartMenu::GetButtonInfo(int id
) const
515 for(SMBtnVector::const_iterator it
=_buttons
.begin(); it
!=_buttons
.end(); ++it
)
522 bool StartMenu::SelectButton(int id
, bool open_sub
)
527 if (id
== _selected_id
)
530 InvalidateSelection();
532 const SMBtnInfo
* btn
= GetButtonInfo(id
);
534 if (btn
&& btn
->_enabled
) {
537 InvalidateSelection();
539 // automatically open submenus
540 if (btn
->_hasSubmenu
) {
544 CloseOtherSubmenus(); // close any open submenu
553 bool StartMenu::OpenSubmenu(bool select_first
)
555 if (_selected_id
== -1)
558 InvalidateSelection();
560 const SMBtnInfo
* btn
= GetButtonInfo(_selected_id
);
562 // automatically open submenus
563 if (btn
->_hasSubmenu
) {
564 //@@ allows destroying of startmenu when processing PM_UPDATE_ICONS -> GPF
565 UpdateWindow(_hwnd
); // draw focused button before waiting on submenu creation
566 Command(_selected_id
, BN_CLICKED
);
568 if (select_first
&& _submenu
)
569 SendMessage(_submenu
, PM_SELECT_ENTRY
, (WPARAM
)false, 0);
577 int StartMenu::GetSelectionIndex()
579 if (_selected_id
== -1)
582 for(int i
=0; i
<(int)_buttons
.size(); ++i
)
583 if (_buttons
[i
]._id
== _selected_id
)
589 bool StartMenu::SelectButtonIndex(int idx
, bool open_sub
)
591 if (idx
>=0 && idx
<(int)_buttons
.size())
592 return SelectButton(_buttons
[idx
]._id
, open_sub
);
597 void StartMenu::ProcessKey(int vk
)
602 Command(_selected_id
, BN_CLICKED
);
614 SelectButtonIndex(0, false);
618 SelectButtonIndex(_buttons
.size()-1, false);
623 CloseOtherSubmenus();
624 else if (!(GetWindowStyle(_hwnd
) & WS_CAPTION
)) // don't automatically close floating menus
625 DestroyWindow(_hwnd
);
637 if (vk
>='0' && vk
<='Z')
638 JumpToNextShortcut(vk
);
642 bool StartMenu::Navigate(int step
)
644 int idx
= GetSelectionIndex();
650 idx
= _buttons
.size() - step
;
655 if (idx
<0 || idx
>(int)_buttons
.size())
658 if (SelectButtonIndex(idx
, false))
665 bool StartMenu::JumpToNextShortcut(char c
)
667 int cur_idx
= GetSelectionIndex();
675 SMBtnVector::const_iterator cur_it
= _buttons
.begin();
676 cur_it
+= cur_idx
+ 1;
678 // first search down from current item...
679 SMBtnVector::const_iterator it
= cur_it
;
680 for(; it
!=_buttons
.end(); ++it
) {
681 const SMBtnInfo
& btn
= *it
;
683 if (!btn
._title
.empty() && toupper((TBYTE
)btn
._title
.at(0)) == c
) {
685 first_found
= btn
._id
;
691 // ...now search from top to the current item
692 it
= _buttons
.begin();
693 for(; it
!=_buttons
.end() && it
!=cur_it
; ++it
) {
694 const SMBtnInfo
& btn
= *it
;
696 if (!btn
._title
.empty() && toupper((TBYTE
)btn
._title
.at(0)) == c
) {
698 first_found
= btn
._id
;
705 SelectButton(first_found
);
708 Command(first_found
, BN_CLICKED
);
715 #endif // _LIGHT_STARTMENU
718 bool StartMenu::GetButtonRect(int id
, PRECT prect
) const
720 #ifdef _LIGHT_STARTMENU
721 ClientRect
clnt(_hwnd
);
722 RECT rect
= {_border_left
, _border_top
, clnt
.right
, STARTMENU_LINE_HEIGHT
};
724 for(SMBtnVector::const_iterator it
=_buttons
.begin()+_scroll_pos
; it
!=_buttons
.end(); ++it
) {
725 const SMBtnInfo
& info
= *it
;
727 rect
.bottom
= rect
.top
+ (info
._id
==-1? STARTMENU_SEP_HEIGHT
: STARTMENU_LINE_HEIGHT
);
729 if (info
._id
== id
) {
734 rect
.top
= rect
.bottom
;
739 HWND btn
= GetDlgItem(_hwnd
, id
);
742 GetWindowRect(btn
, prect
);
743 ScreenToClient(_hwnd
, prect
);
752 void StartMenu::DrawFloatingButton(HDC hdc
)
754 static ResIconEx
floatingIcon(IDI_FLOATING
, 8, 4);
756 ClientRect
clnt(_hwnd
);
758 DrawIconEx(hdc
, clnt
.right
-12, 0, floatingIcon
, 8, 4, 0, 0, DI_NORMAL
);
761 void StartMenu::GetFloatingButtonRect(LPRECT prect
)
763 GetClientRect(_hwnd
, prect
);
766 prect
->left
= prect
->right
- 8;
771 void StartMenu::DrawArrows(HDC hdc
)
773 static ResIconEx
arrowUpIcon(IDI_ARROW_UP
, 8, 4);
774 static ResIconEx
arrowDownIcon(IDI_ARROW_DOWN
, 8, 4);
776 ClientRect
clnt(_hwnd
);
778 DrawIconEx(hdc
, clnt
.right
/2-4, _floating_btn
?3:1, arrowUpIcon
, 8, 4, 0, 0, DI_NORMAL
);
779 DrawIconEx(hdc
, clnt
.right
/2-4, clnt
.bottom
-5, arrowDownIcon
, 8, 4, 0, 0, DI_NORMAL
);
782 void StartMenu::GetArrowButtonRects(LPRECT prect_up
, LPRECT prect_down
)
784 GetClientRect(_hwnd
, prect_up
);
785 *prect_down
= *prect_up
;
787 // prect_up->left = prect_up->right/2 - 4;
788 // prect_up->right = prect_up->left + 8;
789 prect_up
->right
-= 8;
790 prect_up
->top
= _floating_btn
? 3: 1;
791 prect_up
->bottom
= prect_up
->top
+ 4;
793 // prect_down->left = prect_down->right/2 - 4;
794 // prect_down->right = prect_down->left + 8;
795 prect_down
->right
-= 8;
796 prect_down
->top
= prect_down
->bottom
- 5;
800 void StartMenu::Paint(PaintCanvas
& canvas
)
803 DrawFloatingButton(canvas
);
805 #ifdef _LIGHT_STARTMENU
809 ClientRect
clnt(_hwnd
);
810 RECT rect
= {_border_left
, _border_top
, clnt
.right
, STARTMENU_LINE_HEIGHT
};
812 int sep_width
= rect
.right
-rect
.left
- 4;
814 FontSelection
font(canvas
, GetStockFont(DEFAULT_GUI_FONT
));
815 BkMode
bk_mode(canvas
, TRANSPARENT
);
817 for(SMBtnVector::const_iterator it
=_buttons
.begin()+_scroll_pos
; it
!=_buttons
.end(); ++it
) {
818 const SMBtnInfo
& btn
= *it
;
820 if (rect
.top
> canvas
.rcPaint
.bottom
)
823 if (btn
._id
== -1) { // a separator?
824 rect
.bottom
= rect
.top
+ STARTMENU_SEP_HEIGHT
;
826 if (rect
.bottom
> _bottom_max
)
829 BrushSelection
brush_sel(canvas
, GetSysColorBrush(COLOR_BTNSHADOW
));
830 PatBlt(canvas
, rect
.left
+2, rect
.top
+STARTMENU_SEP_HEIGHT
/2-1, sep_width
, 1, PATCOPY
);
832 SelectBrush(canvas
, GetSysColorBrush(COLOR_BTNHIGHLIGHT
));
833 PatBlt(canvas
, rect
.left
+2, rect
.top
+STARTMENU_SEP_HEIGHT
/2, sep_width
, 1, PATCOPY
);
835 rect
.bottom
= rect
.top
+ STARTMENU_LINE_HEIGHT
;
837 if (rect
.bottom
> _bottom_max
)
840 if (rect
.top
>= canvas
.rcPaint
.top
)
841 DrawStartMenuButton(canvas
, rect
, btn
._title
, btn
, btn
._id
==_selected_id
, false);
844 rect
.top
= rect
.bottom
;
849 #ifdef _LAZY_ICONEXTRACT
850 void StartMenu::UpdateIcons(/*int idx*/)
854 #ifdef _SINGLE_ICONEXTRACT
857 int idx
= _scroll_pos
;
859 for(; idx
<(int)_buttons
.size(); ++idx
) {
860 SMBtnInfo
& btn
= _buttons
[idx
];
862 if (btn
._icon_id
==ICID_UNKNOWN
&& btn
._id
>0) {
863 StartMenuEntry
& sme
= _entries
[btn
._id
];
865 btn
._icon_id
= ICID_NONE
;
867 for(ShellEntrySet::iterator it
=sme
._entries
.begin(); it
!=sme
._entries
.end(); ++it
) {
870 if (entry
->_icon_id
== ICID_UNKNOWN
)
872 entry
->extract_icon();
873 } catch(COMException
&) {
874 // ignore unexpected exceptions while extracting icons
877 if (entry
->_icon_id
> ICID_NONE
) {
878 btn
._icon_id
= (ICON_ID
)/*@@*/ entry
->_icon_id
;
882 GetButtonRect(btn
._id
, &rect
);
884 if (rect
.bottom
> _bottom_max
)
887 WindowCanvas
canvas(_hwnd
);
888 DrawStartMenuButton(canvas
, rect
, NULL
, btn
, btn
._id
==_selected_id
, false);
890 //InvalidateRect(_hwnd, &rect, FALSE);
891 //UpdateWindow(_hwnd);
900 // if (++idx < (int)_buttons.size())
901 // PostMessage(_hwnd, PM_UPDATE_ICONS, idx, 0);
905 int icons_extracted
= 0;
906 int icons_updated
= 0;
908 for(StartMenuShellDirs::iterator it
=_dirs
.begin(); it
!=_dirs
.end(); ++it
) {
909 ShellDirectory
& dir
= it
->_dir
;
911 icons_extracted
+= dir
.extract_icons();
914 if (icons_extracted
) {
915 for(ShellEntryMap::iterator it1
=_entries
.begin(); it1
!=_entries
.end(); ++it1
) {
916 StartMenuEntry
& sme
= it1
->second
;
919 sme
._hIcon
= (HICON
)-1;
921 for(ShellEntrySet::const_iterator it2
=sme
._entries
.begin(); it2
!=sme
._entries
.end(); ++it2
) {
922 const Entry
* sm_entry
= *it2
;
924 if (sm_entry
->_hIcon
) {
925 sme
._hIcon
= sm_entry
->_hIcon
;
932 for(SMBtnVector::iterator it
=_buttons
.begin(); it
!=_buttons
.end(); ++it
) {
933 SMBtnInfo
& info
= *it
;
935 if (info
._id
>0 && !info
._hIcon
) {
936 info
._hIcon
= _entries
[info
._id
]._hIcon
;
943 InvalidateRect(_hwnd
, NULL
, FALSE
);
951 // resize child button controls to accomodate for new window size
952 void StartMenu::ResizeButtons(int cx
)
954 HDWP hdwp
= BeginDeferWindowPos(10);
956 for(HWND ctrl
=GetWindow(_hwnd
,GW_CHILD
); ctrl
; ctrl
=GetNextWindow(ctrl
,GW_HWNDNEXT
)) {
959 if (rt
.right
!= cx
) {
960 int height
= rt
.bottom
- rt
.top
;
962 // special handling for separator controls
963 if (!height
&& (GetWindowStyle(ctrl
)&SS_TYPEMASK
)==SS_ETCHEDHORZ
)
966 hdwp
= DeferWindowPos(hdwp
, ctrl
, 0, 0, 0, cx
, height
, SWP_NOMOVE
|SWP_NOZORDER
|SWP_NOACTIVATE
);
970 EndDeferWindowPos(hdwp
);
974 int StartMenu::Command(int id
, int code
)
976 #ifndef _LIGHT_STARTMENU
984 ShellEntryMap::iterator found
= _entries
.find(id
);
986 if (found
!= _entries
.end()) {
987 ActivateEntry(id
, found
->second
._entries
);
991 return super::Command(id
, code
);
992 #ifndef _LIGHT_STARTMENU
1001 ShellEntryMap::iterator
StartMenu::AddEntry(const String
& title
, ICON_ID icon_id
, Entry
* entry
)
1003 // search for an already existing subdirectory entry with the same name
1004 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1005 for(ShellEntryMap::iterator it
=_entries
.begin(); it
!=_entries
.end(); ++it
) {
1006 StartMenuEntry
& sme
= it
->second
;
1008 if (!_tcsicmp(sme
._title
, title
)) ///@todo speed up by using a map indexed by name
1009 for(ShellEntrySet::iterator it2
=sme
._entries
.begin(); it2
!=sme
._entries
.end(); ++it2
) {
1010 if ((*it2
)->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1011 // merge the new shell entry with the existing of the same name
1012 sme
._entries
.insert(entry
);
1019 ShellEntryMap::iterator sme
= AddEntry(title
, icon_id
);
1021 sme
->second
._entries
.insert(entry
);
1026 ShellEntryMap::iterator
StartMenu::AddEntry(const String
& title
, ICON_ID icon_id
, int id
)
1034 sme
._icon_id
= icon_id
;
1036 ShellEntryMap::iterator it
= _entries
.insert(make_pair(id
, sme
)).first
;
1041 ShellEntryMap::iterator
StartMenu::AddEntry(const ShellFolder folder
, ShellEntry
* entry
)
1045 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1046 icon_id
= ICID_FOLDER
;
1048 icon_id
= (ICON_ID
)/*@@*/ entry
->_icon_id
;
1050 return AddEntry(folder
.get_name(entry
->_pidl
), icon_id
, entry
);
1053 ShellEntryMap::iterator
StartMenu::AddEntry(const ShellFolder folder
, Entry
* entry
)
1057 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
1058 icon_id
= ICID_FOLDER
;
1060 icon_id
= (ICON_ID
)/*@@*/ entry
->_icon_id
;
1062 return AddEntry(entry
->_display_name
, icon_id
, entry
);
1066 void StartMenu::AddButton(LPCTSTR title
, ICON_ID icon_id
, bool hasSubmenu
, int id
, bool enabled
)
1068 #ifdef _LIGHT_STARTMENU
1069 _buttons
.push_back(SMBtnInfo(title
, icon_id
, id
, hasSubmenu
, enabled
));
1071 DWORD style
= enabled
? WS_VISIBLE
|WS_CHILD
|BS_OWNERDRAW
: WS_VISIBLE
|WS_CHILD
|BS_OWNERDRAW
|WS_DISABLED
;
1073 WindowRect
rect(_hwnd
);
1074 ClientRect
clnt(_hwnd
);
1076 // increase window height to make room for the new button
1077 rect
.top
-= STARTMENU_LINE_HEIGHT
;
1079 // move down if we are too high now
1081 rect
.top
+= STARTMENU_LINE_HEIGHT
;
1082 rect
.bottom
+= STARTMENU_LINE_HEIGHT
;
1085 WindowCanvas
canvas(_hwnd
);
1086 FontSelection
font(canvas
, GetStockFont(DEFAULT_GUI_FONT
));
1088 // widen window, if it is too small
1089 int text_width
= GetStartMenuBtnTextWidth(canvas
, title
, _hwnd
) + 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
1091 int cx
= clnt
.right
- _border_left
;
1092 if (text_width
> cx
)
1093 rect
.right
+= text_width
-cx
;
1095 MoveWindow(_hwnd
, rect
.left
, rect
.top
, rect
.right
-rect
.left
, rect
.bottom
-rect
.top
, TRUE
);
1097 StartMenuCtrl(_hwnd
, _border_left
, clnt
.bottom
, rect
.right
-rect
.left
-_border_left
,
1098 title
, id
, g_Globals
._icon_cache
.get_icon(icon_id
)._hIcon
, hasSubmenu
, style
);
1102 void StartMenu::AddSeparator()
1104 #ifdef _LIGHT_STARTMENU
1105 _buttons
.push_back(SMBtnInfo(NULL
, ICID_NONE
, -1, false));
1107 WindowRect
rect(_hwnd
);
1108 ClientRect
clnt(_hwnd
);
1110 // increase window height to make room for the new separator
1111 rect
.top
-= STARTMENU_SEP_HEIGHT
;
1113 // move down if we are too high now
1115 rect
.top
+= STARTMENU_LINE_HEIGHT
;
1116 rect
.bottom
+= STARTMENU_LINE_HEIGHT
;
1119 MoveWindow(_hwnd
, rect
.left
, rect
.top
, rect
.right
-rect
.left
, rect
.bottom
-rect
.top
, TRUE
);
1121 StartMenuSeparator(_hwnd
, _border_left
, clnt
.bottom
, rect
.right
-rect
.left
-_border_left
);
1126 bool StartMenu::CloseOtherSubmenus(int id
)
1129 if (IsWindow(_submenu
)) {
1130 if (_submenu_id
== id
)
1134 DestroyWindow(_submenu
);
1135 // _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
1146 void StartMenu::CreateSubmenu(int id
, LPCTSTR title
, CREATORFUNC_INFO creator
, void* info
)
1148 CreateSubmenu(id
, StartMenuFolders(), title
, creator
, info
);
1151 bool StartMenu::CreateSubmenu(int id
, int folder_id
, LPCTSTR title
, CREATORFUNC_INFO creator
, void* info
)
1154 SpecialFolderPath
folder(folder_id
, _hwnd
);
1156 StartMenuFolders new_folders
;
1157 new_folders
.push_back(folder
);
1159 CreateSubmenu(id
, new_folders
, title
, creator
, info
);
1162 } catch(COMException
&) {
1163 // ignore Exception and don't display anything
1164 CloseOtherSubmenus(id
);
1165 _buttons
[GetSelectionIndex()]._enabled
= false; // disable entries for non-existing folders
1170 bool StartMenu::CreateSubmenu(int id
, int folder_id1
, int folder_id2
, LPCTSTR title
, CREATORFUNC_INFO creator
, void* info
)
1172 StartMenuFolders new_folders
;
1175 new_folders
.push_back(SpecialFolderPath(folder_id1
, _hwnd
));
1176 } catch(COMException
&) {
1180 new_folders
.push_back(SpecialFolderPath(folder_id2
, _hwnd
));
1181 } catch(COMException
&) {
1184 if (!new_folders
.empty()) {
1185 CreateSubmenu(id
, new_folders
, title
, creator
, info
);
1188 CloseOtherSubmenus(id
);
1189 _buttons
[GetSelectionIndex()]._enabled
= false; // disable entries for non-existing folders
1194 void StartMenu::CreateSubmenu(int id
, const StartMenuFolders
& new_folders
, LPCTSTR title
, CREATORFUNC_INFO creator
, void* info
)
1196 // Only open one submenu at a time.
1197 if (!CloseOtherSubmenus(id
))
1203 if (GetButtonRect(id
, &rect
)) {
1204 ClientToScreen(_hwnd
, &rect
);
1206 x
= rect
.right
; // Submenus should overlap their parent a bit.
1207 y
= rect
.top
+STARTMENU_LINE_HEIGHT
+_border_top
/*own border*/ -STARTMENU_TOP_BTN_SPACE
/*border of new submenu*/;
1209 WindowRect
pos(_hwnd
);
1216 _submenu
= StartMenu::Create(x
, y
, new_folders
, _hwnd
, title
, creator
, info
);
1220 void StartMenu::ActivateEntry(int id
, const ShellEntrySet
& entries
)
1222 StartMenuFolders new_folders
;
1225 for(ShellEntrySet::const_iterator it
=entries
.begin(); it
!=entries
.end(); ++it
) {
1226 Entry
* entry
= const_cast<Entry
*>(*it
);
1228 if (entry
->_data
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
) {
1230 ///@todo If the user explicitly clicked on a submenu, display this folder as floating start menu.
1232 if (entry
->_etype
== ET_SHELL
)
1233 new_folders
.push_back(entry
->create_absolute_pidl());
1235 TCHAR path
[MAX_PATH
];
1237 if (entry
->get_path(path
))
1238 new_folders
.push_back(path
);
1242 title
= entry
->_display_name
;
1244 // The entry is no subdirectory, so there can only be one shell entry.
1245 assert(entries
.size()==1);
1247 HWND hparent
= GetParent(_hwnd
);
1248 ShellPath shell_path
= entry
->create_absolute_pidl();
1250 // close start menus when launching the selected entry
1253 ///@todo launch in the background; specify correct HWND for error message box titles
1254 SHELLEXECUTEINFO shexinfo
;
1256 shexinfo
.cbSize
= sizeof(SHELLEXECUTEINFO
);
1257 shexinfo
.fMask
= SEE_MASK_IDLIST
; // SEE_MASK_INVOKEIDLIST is also possible.
1258 shexinfo
.hwnd
= hparent
;
1259 shexinfo
.lpVerb
= NULL
;
1260 shexinfo
.lpFile
= NULL
;
1261 shexinfo
.lpParameters
= NULL
;
1262 shexinfo
.lpDirectory
= NULL
;
1263 shexinfo
.nShow
= SW_SHOWNORMAL
;
1265 shexinfo
.lpIDList
= &*shell_path
;
1267 // add PIDL to the recent file list
1268 SHAddToRecentDocs(SHARD_PIDL
, shexinfo
.lpIDList
);
1270 if (!ShellExecuteEx(&shexinfo
))
1271 display_error(hparent
, GetLastError());
1273 // we may have deleted 'this' - ensure we leave the loop and function
1278 if (!new_folders
.empty()) {
1279 // Only open one submenu at a time.
1280 if (!CloseOtherSubmenus(id
))
1283 CreateSubmenu(id
, new_folders
, title
);
1288 /// close all windows of the start menu popup
1289 void StartMenu::CloseStartMenu(int id
)
1291 if (!(GetWindowStyle(_hwnd
) & WS_CAPTION
)) { // don't automatically close floating menus
1292 if (!SendParent(PM_STARTENTRY_LAUNCHED
, id
, (LPARAM
)_hwnd
))
1293 DestroyWindow(_hwnd
);
1294 } else if (_submenu
) // instead close submenus of floating parent menus
1299 int GetStartMenuBtnTextWidth(HDC hdc
, LPCTSTR title
, HWND hwnd
)
1301 RECT rect
= {0, 0, 0, 0};
1302 DrawText(hdc
, title
, -1, &rect
, DT_SINGLELINE
|DT_NOPREFIX
|DT_CALCRECT
);
1304 return rect
.right
-rect
.left
;
1307 #ifdef _LIGHT_STARTMENU
1308 void DrawStartMenuButton(HDC hdc
, const RECT
& rect
, LPCTSTR title
, const SMBtnInfo
& btn
, bool has_focus
, bool pushed
)
1310 void DrawStartMenuButton(HDC hdc
, const RECT
& rect
, LPCTSTR title
, HICON hIcon
,
1311 bool hasSubmenu
, bool enabled
, bool has_focus
, bool pushed
);
1314 UINT style
= DFCS_BUTTONPUSH
;
1317 style
|= DFCS_INACTIVE
;
1319 POINT iconPos
= {rect
.left
+2, (rect
.top
+rect
.bottom
-16)/2};
1320 RECT textRect
= {rect
.left
+16+4, rect
.top
+2, rect
.right
-4, rect
.bottom
-4};
1323 style
|= DFCS_PUSHED
;
1324 ++iconPos
.x
; ++iconPos
.y
;
1325 ++textRect
.left
; ++textRect
.top
;
1326 ++textRect
.right
; ++textRect
.bottom
;
1329 int bk_color_idx
= COLOR_BTNFACE
;
1330 int text_color_idx
= COLOR_BTNTEXT
;
1333 bk_color_idx
= COLOR_HIGHLIGHT
;
1334 text_color_idx
= COLOR_HIGHLIGHTTEXT
;
1337 COLORREF bk_color
= GetSysColor(bk_color_idx
);
1338 HBRUSH bk_brush
= GetSysColorBrush(bk_color_idx
);
1341 FillRect(hdc
, &rect
, bk_brush
);
1343 if (btn
._icon_id
> ICID_NONE
)
1344 g_Globals
._icon_cache
.get_icon(btn
._icon_id
).draw(hdc
, iconPos
.x
, iconPos
.y
, 16, 16, bk_color
, bk_brush
);
1346 // draw submenu arrow at the right
1347 if (btn
._hasSubmenu
) {
1348 static SmallIcon
arrowIcon(IDI_ARROW
);
1349 static SmallIcon
selArrowIcon(IDI_ARROW_SELECTED
);
1351 DrawIconEx(hdc
, rect
.right
-16, iconPos
.y
,
1352 has_focus
? selArrowIcon
: arrowIcon
,
1353 16, 16, 0, bk_brush
, DI_NORMAL
);
1357 BkMode
bk_mode(hdc
, TRANSPARENT
);
1359 if (!btn
._enabled
) // dis->itemState & (ODS_DISABLED|ODS_GRAYED)
1360 DrawGrayText(hdc
, &textRect
, title
, DT_SINGLELINE
|DT_NOPREFIX
|DT_VCENTER
);
1362 TextColor
lcColor(hdc
, GetSysColor(text_color_idx
));
1363 DrawText(hdc
, title
, -1, &textRect
, DT_SINGLELINE
|DT_NOPREFIX
|DT_VCENTER
);
1369 #ifdef _LIGHT_STARTMENU
1371 void StartMenu::ResizeToButtons()
1373 WindowRect
rect(_hwnd
);
1375 WindowCanvas
canvas(_hwnd
);
1376 FontSelection
font(canvas
, GetStockFont(DEFAULT_GUI_FONT
));
1378 int max_width
= STARTMENU_WIDTH_MIN
;
1381 for(SMBtnVector::const_iterator it
=_buttons
.begin(); it
!=_buttons
.end(); ++it
) {
1382 int w
= GetStartMenuBtnTextWidth(canvas
, it
->_title
, _hwnd
);
1388 height
+= STARTMENU_SEP_HEIGHT
;
1390 height
+= STARTMENU_LINE_HEIGHT
;
1393 // calculate new window size
1394 int text_width
= max_width
+ 16/*icon*/ + 10/*placeholder*/ + 16/*arrow*/;
1396 RECT rt_hgt
= {rect
.left
, rect
.bottom
-_border_top
-height
, rect
.left
+_border_left
+text_width
, rect
.bottom
};
1397 AdjustWindowRectEx(&rt_hgt
, GetWindowStyle(_hwnd
), FALSE
, GetWindowExStyle(_hwnd
));
1399 // ignore movement, only look at the size change
1400 rect
.right
= rect
.left
+ (rt_hgt
.right
-rt_hgt
.left
);
1401 rect
.top
= rect
.bottom
- (rt_hgt
.bottom
-rt_hgt
.top
);
1403 // move down if we are too high
1410 // enable scroll mode for long start menus, which span more than the whole screen height
1411 int cyscreen
= GetSystemMetrics(SM_CYSCREEN
);
1414 if (rect
.bottom
> cyscreen
) {
1417 _invisible_lines
= (rect
.bottom
-cyscreen
+(STARTMENU_LINE_HEIGHT
-1))/STARTMENU_LINE_HEIGHT
+ 1;
1418 rect
.bottom
-= _invisible_lines
* STARTMENU_LINE_HEIGHT
;
1420 bottom_max
= rect
.bottom
;
1423 rect
.bottom
+= 6; // lower scroll arrow
1425 _border_top
+= 6; // upper scroll arrow
1426 rect
.bottom
+= 2*6; // upper+lower scroll arrow
1430 MoveWindow(_hwnd
, rect
.left
, rect
.top
, rect
.right
-rect
.left
, rect
.bottom
-rect
.top
, TRUE
);
1433 POINT pt
= {0, bottom_max
};
1435 ScreenToClient(_hwnd
, &pt
);
1441 #else // _LIGHT_STARTMENU
1443 LRESULT
StartMenuButton::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
1447 // automatically set the focus to startmenu entries when moving the mouse over them
1448 if (GetFocus()!=_hwnd
&& !(GetWindowStyle(_hwnd
)&WS_DISABLED
))
1453 PostParent(PM_STARTENTRY_FOCUSED
, _hasSubmenu
, (LPARAM
)_hwnd
);
1457 // route WM_CANCELMODE to the startmenu window
1458 return SendParent(nmsg
, wparam
, lparam
);
1461 return super::WndProc(nmsg
, wparam
, lparam
);
1467 void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis
)
1469 TCHAR title
[BUFFER_LEN
];
1471 GetWindowText(_hwnd
, title
, BUFFER_LEN
);
1473 DrawStartMenuButton(dis
->hDC
, dis
->rcItem
, title
, _hIcon
,
1475 !(dis
->itemState
& ODS_DISABLED
),
1476 dis
->itemState
&ODS_FOCUS
? true: false,
1477 dis
->itemState
&ODS_SELECTED
? true: false);
1483 StartMenuRoot::StartMenuRoot(HWND hwnd
)
1486 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1487 if (!g_Globals
._SHRestricted
|| !SHRestricted(REST_NOCOMMONGROUPS
))
1490 // insert directory "All Users\Start Menu"
1491 ShellDirectory
cmn_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_STARTMENU
, _hwnd
), _hwnd
);
1492 _dirs
.push_back(StartMenuDirectory(cmn_startmenu
, false)); // don't add subfolders
1493 } catch(COMException
&) {
1494 // ignore exception and don't show additional shortcuts
1498 // insert directory "<user name>\Start Menu"
1500 ShellDirectory
usr_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_STARTMENU
, _hwnd
), _hwnd
);
1501 _dirs
.push_back(StartMenuDirectory(usr_startmenu
, false)); // don't add subfolders
1502 } catch(COMException
&) {
1503 // ignore exception and don't show additional shortcuts
1506 // read size of logo bitmap
1508 GetObject(ResBitmap(IDB_LOGOV
), sizeof(BITMAP
), &bmp_hdr
);
1509 _logo_size
.cx
= bmp_hdr
.bmWidth
;
1510 _logo_size
.cy
= bmp_hdr
.bmHeight
;
1512 _border_left
= _logo_size
.cx
+ 1;
1516 static void CalculateStartPos(HWND hwndOwner
, RECT
& rect
)
1518 WindowRect
pos(hwndOwner
);
1520 rect
.left
= pos
.left
;
1521 rect
.top
= pos
.top
-STARTMENU_LINE_HEIGHT
-4;
1522 rect
.right
= pos
.left
+STARTMENU_WIDTH_MIN
;
1523 rect
.bottom
= pos
.top
;
1525 #ifndef _LIGHT_STARTMENU
1526 rect
.top
+= STARTMENU_LINE_HEIGHT
;
1529 AdjustWindowRectEx(&rect
, WS_POPUP
|WS_THICKFRAME
|WS_CLIPCHILDREN
|WS_VISIBLE
, FALSE
, 0);
1532 HWND
StartMenuRoot::Create(HWND hwndOwner
)
1536 CalculateStartPos(hwndOwner
, rect
);
1538 return Window::Create(WINDOW_CREATOR(StartMenuRoot
), 0, GetWndClasss(), TITLE_STARTMENU
,
1539 WS_POPUP
|WS_THICKFRAME
|WS_CLIPCHILDREN
,
1540 rect
.left
, rect
.top
, rect
.right
-rect
.left
, rect
.bottom
-rect
.top
, hwndOwner
);
1544 void StartMenuRoot::TrackStartmenu()
1549 #ifdef _LIGHT_STARTMENU
1553 #ifdef _LIGHT_STARTMENU
1554 // recalculate start menu root position
1557 CalculateStartPos(GetParent(hwnd
), rect
);
1559 SetWindowPos(hwnd
, 0, rect
.left
, rect
.top
, rect
.right
-rect
.left
, rect
.bottom
-rect
.top
, 0);
1564 // show previously hidden start menu
1565 ShowWindow(hwnd
, SW_SHOW
);
1566 SetForegroundWindow(hwnd
);
1568 while(IsWindow(hwnd
)) {
1569 if (!GetMessage(&msg
, 0, 0, 0)) {
1570 PostQuitMessage(msg
.wParam
);
1574 // Check for a mouse click on any window, which is not part of the start menu
1575 if (msg
.message
==WM_LBUTTONDOWN
|| msg
.message
==WM_MBUTTONDOWN
|| msg
.message
==WM_RBUTTONDOWN
) {
1576 StartMenu
* menu_wnd
= NULL
;
1578 for(HWND hwnd
=msg
.hwnd
; hwnd
; hwnd
=GetParent(hwnd
)) {
1579 menu_wnd
= WINDOW_DYNAMIC_CAST(StartMenu
, hwnd
);
1592 if (pretranslate_msg(&msg
))
1595 if (dispatch_dialog_msg(&msg
))
1598 TranslateMessage(&msg
);
1601 DispatchMessage(&msg
);
1602 } catch(COMException
& e
) {
1603 HandleException(e
, _hwnd
);
1605 } catch(COMException
& e
) {
1606 HandleException(e
, _hwnd
);
1612 LRESULT
StartMenuRoot::Init(LPCREATESTRUCT pcs
)
1614 // add buttons for entries in _entries
1615 if (super::Init(pcs
))
1625 if (RegOpenKey(HKEY_CURRENT_USER
, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), &hkey
))
1628 if (RegOpenKey(HKEY_CURRENT_USER
, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hkeyAdv
))
1631 #define IS_VALUE_ZERO(hk, name) \
1632 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || !value))
1634 #define IS_VALUE_NOT_ZERO(hk, name) \
1635 (!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || value>0))
1639 // insert hard coded start entries
1640 AddButton(ResString(IDS_PROGRAMS
), ICID_APPS
, true, IDC_PROGRAMS
);
1642 AddButton(ResString(IDS_DOCUMENTS
), ICID_DOCUMENTS
, true, IDC_DOCUMENTS
);
1644 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1645 if (!g_Globals
._SHRestricted
|| !SHRestricted(REST_NORECENTDOCSMENU
))
1647 if (IS_VALUE_ZERO(hkey
, _T("NoRecentDocsMenu")))
1649 AddButton(ResString(IDS_RECENT
), ICID_DOCUMENTS
, true, IDC_RECENT
);
1651 AddButton(ResString(IDS_FAVORITES
), ICID_FAVORITES
, true, IDC_FAVORITES
);
1653 AddButton(ResString(IDS_SETTINGS
), ICID_CONFIG
, true, IDC_SETTINGS
);
1655 AddButton(ResString(IDS_BROWSE
), ICID_FOLDER
, true, IDC_BROWSE
);
1657 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1658 if (!g_Globals
._SHRestricted
|| !SHRestricted(REST_NOFIND
))
1660 if (IS_VALUE_ZERO(hkey
, _T("NoFind")))
1662 AddButton(ResString(IDS_SEARCH
), ICID_SEARCH
, true, IDC_SEARCH
);
1664 AddButton(ResString(IDS_START_HELP
), ICID_INFO
, false, IDC_START_HELP
);
1666 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1667 if (!g_Globals
._SHRestricted
|| !SHRestricted(REST_NORUN
))
1669 if (IS_VALUE_ZERO(hkey
, _T("NoRun")))
1671 AddButton(ResString(IDS_LAUNCH
), ICID_ACTION
, false, IDC_LAUNCH
);
1677 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1678 if (!g_Globals
._SHRestricted
|| !SHRestricted(REST_NOCLOSE
))
1680 if (IS_VALUE_NOT_ZERO(hkeyAdv
, _T("StartMenuLogoff")))
1682 AddButton(ResString(IDS_LOGOFF
), ICID_LOGOFF
, false, IDC_LOGOFF
);
1685 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
1686 if (!g_Globals
._SHRestricted
|| SHRestricted(REST_STARTMENULOGOFF
) != 1)
1688 if (IS_VALUE_ZERO(hkey
, _T("NoClose")))
1690 AddButton(ResString(IDS_SHUTDOWN
), ICID_LOGOFF
, false, IDC_SHUTDOWN
);
1694 RegCloseKey(hkeyAdv
);
1699 #ifdef _LIGHT_STARTMENU
1700 // set the window size to fit all buttons
1708 void StartMenuRoot::AddEntries()
1710 super::AddEntries();
1712 AddButton(ResString(IDS_EXPLORE
), ICID_EXPLORER
, false, IDC_EXPLORE
);
1716 LRESULT
StartMenuRoot::WndProc(UINT nmsg
, WPARAM wparam
, LPARAM lparam
)
1720 PaintCanvas
canvas(_hwnd
);
1725 return super::WndProc(nmsg
, wparam
, lparam
);
1731 void StartMenuRoot::Paint(PaintCanvas
& canvas
)
1734 {WindowCanvas
dc(_hwnd
); clr_bits
=GetDeviceCaps(dc
, BITSPIXEL
);}
1737 ResBitmap
bmp(clr_bits
<=8? clr_bits
<=4? IDB_LOGOV16
: IDB_LOGOV256
: IDB_LOGOV
);
1738 BitmapSelection
sel(mem_dc
, bmp
);
1740 ClientRect
clnt(_hwnd
);
1741 int h
= min(_logo_size
.cy
, clnt
.bottom
);
1743 RECT rect
= {0, 0, _logo_size
.cx
, clnt
.bottom
-h
};
1744 HBRUSH hbr
= CreateSolidBrush(GetPixel(mem_dc
, 0, 0));
1745 FillRect(canvas
, &rect
, hbr
);
1748 PatBlt(canvas
, _logo_size
.cx
, 0, 1, clnt
.bottom
, WHITENESS
);
1750 BitBlt(canvas
, 0, clnt
.bottom
-h
, _logo_size
.cx
, h
, mem_dc
, 0, 0, SRCCOPY
);
1752 super::Paint(canvas
);
1756 void StartMenuRoot::CloseStartMenu(int id
)
1761 ShowWindow(_hwnd
, SW_HIDE
);
1764 void StartMenuRoot::ProcessKey(int vk
)
1769 CloseOtherSubmenus();
1770 // don't close start menu root
1774 super::ProcessKey(vk
);
1779 int StartMenuHandler::Command(int id
, int code
)
1786 CreateSubmenu(id
, CSIDL_COMMON_PROGRAMS
, CSIDL_PROGRAMS
, ResString(IDS_PROGRAMS
));
1791 explorer_show_frame(SW_SHOWNORMAL
);
1796 ShowLaunchDialog(g_Globals
._hwndDesktopBar
);
1800 CreateSubmenu(id
, CSIDL_PERSONAL
, ResString(IDS_DOCUMENTS
));
1804 CreateSubmenu(id
, CSIDL_RECENT
, ResString(IDS_RECENT
), STARTMENU_CREATOR(RecentStartMenu
));
1808 #ifndef _SHELL32_FAVORITES
1809 CreateSubmenu(id
, ResString(IDS_FAVORITES
), STARTMENU_CREATOR(FavoritesMenu
), &static_cast<BookmarkList
&>(g_Globals
._favorites
));
1811 CreateSubmenu(id
, CSIDL_COMMON_FAVORITES
, CSIDL_FAVORITES
, ResString(IDS_FAVORITES
));
1816 CreateSubmenu(id
, ResString(IDS_BROWSE
), STARTMENU_CREATOR(BrowseMenu
));
1820 CreateSubmenu(id
, ResString(IDS_SETTINGS
), STARTMENU_CREATOR(SettingsMenu
));
1824 CreateSubmenu(id
, ResString(IDS_SEARCH
), STARTMENU_CREATOR(SearchMenu
));
1827 case IDC_START_HELP
:
1829 MessageBox(g_Globals
._hwndDesktopBar
, TEXT("Help not yet implemented"), ResString(IDS_TITLE
), MB_OK
);
1833 /* The shell32 Dialog prompts about some system setting change. This is not what we want to display here.
1835 ShowRestartDialog(g_Globals._hwndDesktopBar, EWX_LOGOFF);*/
1836 DestroyWindow(GetParent(_hwnd
));
1841 ShowExitWindowsDialog(g_Globals
._hwndDesktopBar
);
1847 case ID_DESKTOPBAR_SETTINGS
:
1849 ExplorerPropertySheet(g_Globals
._hwndDesktopBar
);
1852 case IDC_SETTINGS_MENU
:
1853 CreateSubmenu(id
, CSIDL_CONTROLS
, ResString(IDS_SETTINGS_MENU
));
1857 #ifdef _ROS_ // to be removed when printer folder will be implemented
1858 MessageBox(0, TEXT("printer folder not yet implemented in SHELL32"), ResString(IDS_TITLE
), MB_OK
);
1860 CreateSubmenu(id
, CSIDL_PRINTERS
, CSIDL_PRINTHOOD
, ResString(IDS_PRINTERS
));
1864 case IDC_CONTROL_PANEL
: {
1867 XMLPos explorer_options
= g_Globals
.get_cfg("general/explorer");
1868 bool mdi
= XMLBool(explorer_options
, "mdi", true);
1871 MDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
1874 SDIMainFrame::Create(TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}"), 0);
1878 CreateSubmenu(id
, CSIDL_COMMON_ADMINTOOLS
, CSIDL_ADMINTOOLS
, ResString(IDS_ADMIN
));
1881 case IDC_CONNECTIONS
:
1882 #ifdef _ROS_ // to be removed when RAS will be implemented
1883 MessageBox(0, TEXT("RAS folder not yet implemented in SHELL32"), ResString(IDS_TITLE
), MB_OK
);
1885 CreateSubmenu(id
, CSIDL_CONNECTIONS
, ResString(IDS_CONNECTIONS
));
1893 #ifdef _ROS_ // to be removed when network will be implemented
1894 MessageBox(0, TEXT("network not yet implemented"), ResString(IDS_TITLE
), MB_OK
);
1896 CreateSubmenu(id
, CSIDL_NETWORK
, ResString(IDS_NETWORK
));
1901 ///@todo exclude removable drives
1902 CreateSubmenu(id
, CSIDL_DRIVES
, ResString(IDS_DRIVES
));
1908 case IDC_SEARCH_PROGRAM
:
1910 Dialog::DoModal(IDD_SEARCH_PROGRAM
, WINDOW_CREATOR(FindProgramDlg
));
1913 case IDC_SEARCH_FILES
:
1918 case IDC_SEARCH_COMPUTER
:
1920 ShowSearchComputer();
1925 return super::Command(id
, code
);
1932 void StartMenuHandler::ShowSearchDialog()
1934 static DynamicFct
<SHFINDFILES
> SHFindFiles(TEXT("SHELL32"), 90);
1937 (*SHFindFiles
)(NULL
, NULL
);
1939 MessageBox(0, TEXT("SHFindFiles() not yet implemented in SHELL32"), ResString(IDS_TITLE
), MB_OK
);
1942 void StartMenuHandler::ShowSearchComputer()
1944 static DynamicFct
<SHFINDCOMPUTER
> SHFindComputer(TEXT("SHELL32"), 91);
1947 (*SHFindComputer
)(NULL
, NULL
);
1949 MessageBox(0, TEXT("SHFindComputer() not yet implemented in SHELL32"), ResString(IDS_TITLE
), MB_OK
);
1952 void StartMenuHandler::ShowLaunchDialog(HWND hwndOwner
)
1954 ///@todo All text phrases should be put into the resources.
1955 static LPCSTR szTitle
= "Run";
1956 static LPCSTR szText
= "Type the name of a program, folder, document, or Internet resource, and Explorer will open it for you.";
1958 static DynamicFct
<RUNFILEDLG
> RunFileDlg(TEXT("SHELL32"), 61);
1960 // Show "Run..." dialog
1962 #ifndef _ROS_ /* FIXME: our shell32 always expects Ansi strings */
1963 if ((HIWORD(GetVersion())>>14) == W_VER_NT
) {
1964 WCHAR wTitle
[40], wText
[256];
1966 MultiByteToWideChar(CP_ACP
, 0, szTitle
, -1, wTitle
, 40);
1967 MultiByteToWideChar(CP_ACP
, 0, szText
, -1, wText
, 256);
1969 (*RunFileDlg
)(hwndOwner
, 0, NULL
, (LPCSTR
)wTitle
, (LPCSTR
)wText
, RFF_CALCDIRECTORY
);
1973 (*RunFileDlg
)(hwndOwner
, 0, NULL
, szTitle
, szText
, RFF_CALCDIRECTORY
);
1977 void StartMenuHandler::ShowRestartDialog(HWND hwndOwner
, UINT flags
)
1979 static DynamicFct
<RESTARTWINDOWSDLG
> RestartDlg(TEXT("SHELL32"), 59);
1982 (*RestartDlg
)(hwndOwner
, (LPWSTR
)L
"You selected <Log Off>.\n\n", flags
); ///@todo ANSI string conversion if needed
1984 MessageBox(hwndOwner
, TEXT("RestartDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE
), MB_OK
);
1987 void ShowExitWindowsDialog(HWND hwndOwner
)
1989 static DynamicFct
<EXITWINDOWSDLG
> ExitWindowsDlg(TEXT("SHELL32"), 60);
1992 (*ExitWindowsDlg
)(hwndOwner
);
1994 MessageBox(hwndOwner
, TEXT("ExitWindowsDlg() not yet implemented in SHELL32"), ResString(IDS_TITLE
), MB_OK
);
1998 void SettingsMenu::AddEntries()
2000 super::AddEntries();
2002 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
2003 if (!g_Globals
._SHRestricted
|| !SHRestricted(REST_NOCONTROLPANEL
))
2005 AddButton(ResString(IDS_CONTROL_PANEL
), ICID_CONFIG
, false, IDC_CONTROL_PANEL
);
2007 #ifdef _ROS_ // to be removed when printer/network will be implemented
2008 AddButton(ResString(IDS_PRINTERS
), ICID_PRINTER
, false, IDC_PRINTERS
);
2009 AddButton(ResString(IDS_CONNECTIONS
), ICID_NETWORK
, false, IDC_CONNECTIONS
);
2011 AddButton(ResString(IDS_PRINTERS
), ICID_PRINTER
, true, IDC_PRINTERS
);
2012 AddButton(ResString(IDS_CONNECTIONS
), ICID_NETWORK
, true, IDC_CONNECTIONS
);
2014 AddButton(ResString(IDS_ADMIN
), ICID_CONFIG
, true, IDC_ADMIN
);
2016 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
2017 if (!g_Globals
._SHRestricted
|| !SHRestricted(REST_NOCONTROLPANEL
))
2019 AddButton(ResString(IDS_SETTINGS_MENU
), ICID_CONFIG
, true, IDC_SETTINGS_MENU
);
2021 AddButton(ResString(IDS_DESKTOPBAR_SETTINGS
), ICID_CONFIG
, false, ID_DESKTOPBAR_SETTINGS
);
2024 void BrowseMenu::AddEntries()
2026 super::AddEntries();
2028 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
2029 if (!g_Globals
._SHRestricted
|| !SHRestricted(REST_NONETHOOD
)) // or REST_NOENTIRENETWORK ?
2031 #ifdef _ROS_ // to be removed when printer/network will be implemented
2032 AddButton(ResString(IDS_NETWORK
), ICID_NETWORK
, false, IDC_NETWORK
);
2034 AddButton(ResString(IDS_NETWORK
), ICID_NETWORK
, true, IDC_NETWORK
);
2037 AddButton(ResString(IDS_DRIVES
), ICID_FOLDER
, true, IDC_DRIVES
);
2040 void SearchMenu::AddEntries()
2042 super::AddEntries();
2044 AddButton(ResString(IDS_SEARCH_FILES
), ICID_SEARCH_DOC
, false, IDC_SEARCH_FILES
);
2046 #ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
2047 if (!g_Globals
._SHRestricted
|| !SHRestricted(REST_HASFINDCOMPUTERS
))
2049 AddButton(ResString(IDS_SEARCH_COMPUTER
),ICID_COMPUTER
, false, IDC_SEARCH_COMPUTER
);
2051 AddButton(ResString(IDS_SEARCH_PRG
), ICID_APPS
, false, IDC_SEARCH_PROGRAM
);
2055 void RecentStartMenu::AddEntries()
2057 for(StartMenuShellDirs::iterator it
=_dirs
.begin(); it
!=_dirs
.end(); ++it
) {
2058 StartMenuDirectory
& smd
= *it
;
2059 ShellDirectory
& dir
= smd
._dir
;
2061 if (!dir
._scanned
) {
2064 #ifdef _LAZY_ICONEXTRACT
2065 dir
.smart_scan(SORT_NAME
, SCAN_FILESYSTEM
);
2067 dir
.smart_scan(SORT_NAME
, SCAN_EXTRACT_ICONS
|SCAN_FILESYSTEM
);
2071 dir
.sort_directory(SORT_DATE
);
2072 AddShellEntries(dir
, RECENT_DOCS_COUNT
, smd
._subfolders
);
2077 #ifndef _SHELL32_FAVORITES
2079 void FavoritesMenu::AddEntries()
2081 super::AddEntries();
2083 for(BookmarkList::iterator it
=_bookmarks
.begin(); it
!=_bookmarks
.end(); ++it
) {
2084 BookmarkNode
& node
= *it
;
2086 int id
= ++_next_id
;
2088 _entries
[id
] = node
;
2090 if (node
._type
== BookmarkNode::BMNT_FOLDER
) {
2091 BookmarkFolder
& folder
= *node
._pfolder
;
2093 AddButton(folder
._name
, ICID_FOLDER
, true, id
);
2094 } else if (node
._type
== BookmarkNode::BMNT_BOOKMARK
) {
2095 Bookmark
& bookmark
= *node
._pbookmark
;
2097 ICON_ID icon
= ICID_NONE
;
2099 if (!bookmark
._icon_path
.empty())
2100 icon
= g_Globals
._icon_cache
.extract(bookmark
._icon_path
, bookmark
._icon_idx
);
2102 AddButton(bookmark
._name
, icon
!=ICID_NONE
?icon
:ICID_BOOKMARK
, false, id
);
2107 int FavoritesMenu::Command(int id
, int code
)
2109 BookmarkMap::iterator found
= _entries
.find(id
);
2111 if (found
!= _entries
.end()) {
2112 BookmarkNode
& node
= found
->second
;
2114 if (node
._type
== BookmarkNode::BMNT_FOLDER
) {
2115 BookmarkFolder
& folder
= *node
._pfolder
;
2117 if (CloseOtherSubmenus(id
))
2118 CreateSubmenu(id
, folder
._name
, STARTMENU_CREATOR(FavoritesMenu
), &static_cast<BookmarkList
&>(folder
._bookmarks
));
2119 } else if (node
._type
== BookmarkNode::BMNT_BOOKMARK
) {
2120 Bookmark
& bookmark
= *node
._pbookmark
;
2122 String url
= bookmark
._url
;
2123 HWND hparent
= GetParent(_hwnd
);
2127 launch_file(hparent
, url
, SW_SHOWNORMAL
);
2133 return super::Command(id
, code
);