📄 startmenu.cpp
字号:
if (text_width > cx)
rect.right += text_width-cx;
MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
StartMenuCtrl(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left,
title, id, g_Globals._icon_cache.get_icon(icon_id)._hIcon, hasSubmenu, style);
#endif
}
void StartMenu::AddSeparator()
{
#ifdef _LIGHT_STARTMENU
_buttons.push_back(SMBtnInfo(NULL, ICID_NONE, -1, false));
#else
WindowRect rect(_hwnd);
ClientRect clnt(_hwnd);
// increase window height to make room for the new separator
rect.top -= STARTMENU_SEP_HEIGHT(icon_size);
// move down if we are too high now
if (rect.top < 0) {
rect.top += STARTMENU_LINE_HEIGHT(icon_size);
rect.bottom += STARTMENU_LINE_HEIGHT(icon_size);
}
MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
StartMenuSeparator(_hwnd, _border_left, clnt.bottom, rect.right-rect.left-_border_left);
#endif
}
bool StartMenu::CloseOtherSubmenus(int id)
{
if (_submenu) {
if (IsWindow(_submenu)) {
if (_submenu_id == id)
return false;
else {
_submenu_id = 0;
DestroyWindow(_submenu);
// _submenu should be reset automatically by PM_STARTMENU_CLOSED, but safety first...
}
}
_submenu = 0;
}
return true;
}
void StartMenu::CreateSubmenu(int id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
{
CreateSubmenu(id, StartMenuFolders(), title, creator, info);
}
bool StartMenu::CreateSubmenu(int id, int folder_id, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
{
try {
SpecialFolderPath folder(folder_id, _hwnd);
StartMenuFolders new_folders;
new_folders.push_back(folder);
CreateSubmenu(id, new_folders, title, creator, info);
return true;
} catch(COMException&) {
// ignore Exception and don't display anything
CloseOtherSubmenus(id);
_buttons[GetSelectionIndex()]._enabled = false; // disable entries for non-existing folders
return false;
}
}
bool StartMenu::CreateSubmenu(int id, int folder_id1, int folder_id2, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
{
StartMenuFolders new_folders;
try {
new_folders.push_back(SpecialFolderPath(folder_id1, _hwnd));
} catch(COMException&) {
}
try {
new_folders.push_back(SpecialFolderPath(folder_id2, _hwnd));
} catch(COMException&) {
}
if (!new_folders.empty()) {
CreateSubmenu(id, new_folders, title, creator, info);
return true;
} else {
CloseOtherSubmenus(id);
_buttons[GetSelectionIndex()]._enabled = false; // disable entries for non-existing folders
return false;
}
}
void StartMenu::CreateSubmenu(int id, const StartMenuFolders& new_folders, LPCTSTR title, CREATORFUNC_INFO creator, void* info)
{
// Only open one submenu at a time.
if (!CloseOtherSubmenus(id))
return;
RECT rect;
int x, y;
if (GetButtonRect(id, &rect)) {
ClientToScreen(_hwnd, &rect);
x = rect.right; // Submenus should overlap their parent a bit.
const int icon_size = _icon_size;
y = rect.top+STARTMENU_LINE_HEIGHT(icon_size) +_border_top/*own border*/ -STARTMENU_TOP_BTN_SPACE/*border of new submenu*/;
} else {
WindowRect pos(_hwnd);
x = pos.right;
y = pos.top;
}
_submenu_id = id;
_submenu = StartMenu::Create(x, y, new_folders, _hwnd, title, creator, info, _create_info._filter);
}
void StartMenu::ActivateEntry(int id, const ShellEntrySet& entries)
{
StartMenuFolders new_folders;
String title;
for(ShellEntrySet::const_iterator it=entries.begin(); it!=entries.end(); ++it) {
Entry* entry = const_cast<Entry*>(*it);
if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
///@todo If the user explicitly clicked on a submenu, display this folder as floating start menu.
if (entry->_etype == ET_SHELL)
new_folders.push_back(entry->create_absolute_pidl());
else {
TCHAR path[MAX_PATH];
if (entry->get_path(path, COUNTOF(path)))
new_folders.push_back(path);
}
if (title.empty())
title = entry->_display_name;
} else {
// The entry is no subdirectory, so there can only be one shell entry.
assert(entries.size()==1);
HWND hparent = GetParent(_hwnd);
ShellPath shell_path = entry->create_absolute_pidl();
// close start menus when launching the selected entry
CloseStartMenu(id);
///@todo launch in the background; specify correct HWND for error message box titles
SHELLEXECUTEINFO shexinfo;
shexinfo.cbSize = sizeof(SHELLEXECUTEINFO);
shexinfo.fMask = SEE_MASK_IDLIST; // SEE_MASK_INVOKEIDLIST is also possible.
shexinfo.hwnd = hparent;
shexinfo.lpVerb = NULL;
shexinfo.lpFile = NULL;
shexinfo.lpParameters = NULL;
shexinfo.lpDirectory = NULL;
shexinfo.nShow = SW_SHOWNORMAL;
shexinfo.lpIDList = &*shell_path;
// add PIDL to the recent file list
SHAddToRecentDocs(SHARD_PIDL, shexinfo.lpIDList);
if (!ShellExecuteEx(&shexinfo))
display_error(hparent, GetLastError());
// we may have deleted 'this' - ensure we leave the loop and function
return;
}
}
if (!new_folders.empty()) {
// Only open one submenu at a time.
if (!CloseOtherSubmenus(id))
return;
CreateSubmenu(id, new_folders, title);
}
}
/// close all windows of the start menu popup
void StartMenu::CloseStartMenu(int id)
{
if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) { // don't automatically close floating menus
if (!SendParent(PM_STARTENTRY_LAUNCHED, id, (LPARAM)_hwnd))
DestroyWindow(_hwnd);
} else if (_submenu) // instead close submenus of floating parent menus
CloseSubmenus();
}
int GetStartMenuBtnTextWidth(HDC hdc, LPCTSTR title, HWND hwnd)
{
RECT rect = {0, 0, 0, 0};
DrawText(hdc, title, -1, &rect, DT_SINGLELINE|DT_NOPREFIX|DT_CALCRECT);
return rect.right-rect.left;
}
#ifdef _LIGHT_STARTMENU
void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, const SMBtnInfo& btn, bool has_focus, bool pushed, int icon_size)
#else
void DrawStartMenuButton(HDC hdc, const RECT& rect, LPCTSTR title, HICON hIcon,
bool hasSubmenu, bool enabled, bool has_focus, bool pushed, int icon_size);
#endif
{
UINT style = DFCS_BUTTONPUSH;
if (!btn._enabled)
style |= DFCS_INACTIVE;
POINT iconPos = {rect.left+2, (rect.top+rect.bottom-icon_size)/2};
RECT textRect = {rect.left+icon_size+4, rect.top+2, rect.right-4, rect.bottom-4};
if (pushed) {
style |= DFCS_PUSHED;
++iconPos.x; ++iconPos.y;
++textRect.left; ++textRect.top;
++textRect.right; ++textRect.bottom;
}
int bk_color_idx = COLOR_BTNFACE;
int text_color_idx = COLOR_BTNTEXT;
if (has_focus) {
bk_color_idx = COLOR_HIGHLIGHT;
text_color_idx = COLOR_HIGHLIGHTTEXT;
}
COLORREF bk_color = GetSysColor(bk_color_idx);
HBRUSH bk_brush = GetSysColorBrush(bk_color_idx);
if (title)
FillRect(hdc, &rect, bk_brush);
if (btn._icon_id > ICID_NONE)
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*/);
// draw submenu arrow at the right
if (btn._hasSubmenu) {
ResIconEx arrowIcon(IDI_ARROW, icon_size, icon_size);
ResIconEx selArrowIcon(IDI_ARROW_SELECTED, icon_size, icon_size);
DrawIconEx(hdc, rect.right-icon_size, iconPos.y,
has_focus? selArrowIcon: arrowIcon,
icon_size, icon_size, 0, bk_brush, DI_NORMAL);
}
if (title) {
BkMode bk_mode(hdc, TRANSPARENT);
if (!btn._enabled) // dis->itemState & (ODS_DISABLED|ODS_GRAYED)
DrawGrayText(hdc, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
else {
TextColor lcColor(hdc, GetSysColor(text_color_idx));
DrawText(hdc, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
}
}
}
#ifdef _LIGHT_STARTMENU
void StartMenu::ResizeToButtons()
{
WindowRect rect(_hwnd);
WindowCanvas canvas(_hwnd);
FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
const int icon_size = _icon_size;
int max_width = STARTMENU_WIDTH_MIN;
int height = 0;
for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
int w = GetStartMenuBtnTextWidth(canvas, it->_title, _hwnd);
if (w > max_width)
max_width = w;
if (it->_id == -1)
height += STARTMENU_SEP_HEIGHT(icon_size);
else
height += STARTMENU_LINE_HEIGHT(icon_size);
}
// calculate new window size
int text_width = max_width + icon_size + 10/*placeholder*/ + 16/*arrow*/;
RECT rt_hgt = {rect.left, rect.bottom-_border_top-height, rect.left+_border_left+text_width, rect.bottom};
AdjustWindowRectEx(&rt_hgt, GetWindowStyle(_hwnd), FALSE, GetWindowExStyle(_hwnd));
// ignore movement, only look at the size change
rect.right = rect.left + (rt_hgt.right-rt_hgt.left);
rect.top = rect.bottom - (rt_hgt.bottom-rt_hgt.top);
// move down if we are too high
if (rect.top < 0) {
int dy = -rect.top;
rect.top += dy;
rect.bottom += dy;
}
// enable scroll mode for long start menus, which span more than the whole screen height
int cyscreen = GetSystemMetrics(SM_CYSCREEN);
int bottom_max = 0;
if (rect.bottom > cyscreen) {
_arrow_btns = true;
_invisible_lines = (rect.bottom-cyscreen+(STARTMENU_LINE_HEIGHT(icon_size)+1))/STARTMENU_LINE_HEIGHT(icon_size)+1;
rect.bottom -= _invisible_lines * STARTMENU_LINE_HEIGHT(icon_size);
bottom_max = rect.bottom;
if (_floating_btn)
rect.bottom += 6; // lower scroll arrow
else {
_border_top += 6; // upper scroll arrow
rect.bottom += 2*6; // upper+lower scroll arrow
}
}
MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);
if (bottom_max) {
POINT pt = {0, bottom_max};
ScreenToClient(_hwnd, &pt);
_bottom_max = pt.y;
}
}
#else // _LIGHT_STARTMENU
LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
{
switch(nmsg) {
case WM_MOUSEMOVE:
// automatically set the focus to startmenu entries when moving the mouse over them
if (GetFocus()!=_hwnd && !(GetWindowStyle(_hwnd)&WS_DISABLED))
SetFocus(_hwnd);
break;
case WM_SETFOCUS:
PostParent(PM_STARTENTRY_FOCUSED, _hasSubmenu, (LPARAM)_hwnd);
goto def;
case WM_CANCELMODE:
// route WM_CANCELMODE to the startmenu window
return SendParent(nmsg, wparam, lparam);
default: def:
return super::WndProc(nmsg, wparam, lparam);
}
return 0;
}
void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis)
{
TCHAR title[BUFFER_LEN];
GetWindowText(_hwnd, title, BUFFER_LEN);
DrawStartMenuButton(dis->hDC, dis->rcItem, title, _hIcon,
_hasSubmenu,
!(dis->itemState & ODS_DISABLED),
dis->itemState&ODS_FOCUS? true: false,
dis->itemState&ODS_SELECTED? true: false);
}
#endif
StartMenuRoot::StartMenuRoot(HWND hwnd, const StartMenuRootCreateInfo& info)
: super(hwnd, info._icon_size)
{
#ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCOMMONGROUPS))
#endif
try {
// insert directory "All Users\Start Menu"
ShellDirectory cmn_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
_dirs.push_back(StartMenuDirectory(cmn_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_COMMON_PROGRAMS, _hwnd)));
} catch(COMException&) {
// ignore exception and don't show additional shortcuts
}
try {
// insert directory "<user name>\Start Menu"
ShellDirectory usr_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
_dirs.push_back(StartMenuDirectory(usr_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_PROGRAMS, _hwnd)));
} catch(COMException&) {
// ignore exception and don't show additional shortcuts
}
ReadLogoSize();
}
void StartMenuRoot::ReadLogoSize()
{
// read size of logo bitmap
BITMAP bmp_hdr;
GetObject(ResBitmap(GetLogoResId()), sizeof(BITMAP), &bmp_hdr);
_logo_size.cx = bmp_hdr.bmWidth;
_logo_size.cy = bmp_hdr.bmHeight;
// cache logo width
_border_left = _logo_size.cx + 1;
}
static void CalculateStartPos(HWND hwndOwner, RECT& rect, int icon_size)
{
WindowRect pos(hwndOwner);
rect.left = pos.left;
rect.top = pos.top-STARTMENU_LINE_HEIGHT(icon_size)-4;
rect.right = pos.left+STARTMENU_WIDTH_MIN;
rect.bottom = pos.top;
#ifndef _LIGHT_STARTMENU
rect.top += STARTMENU_LINE_HEIGHT(icon_size);
#endif
AdjustWindowRectEx(&rect, WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE, FALSE, 0);
}
HWND StartMenuRoot::Create(HWND hwndOwner, int icon_size)
{
RECT rect;
CalculateStartPos(hwndOwner, rect, icon_size);
StartMenuRootCreateInfo create_info;
create_info._icon_size = icon_size;
return Window::Create(WINDOW_CREATOR_INFO(StartMenuRoot,StartMenuRootCreateInfo), &create_info, 0, GetWndClasss(), TITLE_STARTMENU,
WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,
rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndOwner);
}
void StartMenuRoot::TrackStartmenu()
{
MSG msg;
HWND hwnd = _hwnd;
#ifdef _LIGHT_STARTMENU
_selected_id = -1;
#endif
#ifdef _LIGHT_STARTMENU
// recalculate start menu root position
RECT rect;
CalculateStartPos(GetParent(hwnd), rect, _icon_size);
SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);
ResizeToButtons();
#endif
// show previously hidden start menu
ShowWindow(hwnd, SW_SHOW);
SetForegroundWindow(hwnd);
while(IsWindow(hwnd) && IsWindowVisible(hwnd)) {
if (!GetMessage(&msg, 0, 0, 0)) {
PostQuitMessage(msg.wParam);
break;
}
// Check for a mouse click on any window, that is not part of the start menu
if (msg.message==WM_LBUTTONDOWN || msg.message==WM_MBUTTONDOWN || msg.message==WM_RBUTTONDOWN) {
StartMenu* menu_wnd = NULL;
for(HWND hwnd=msg.hwnd; hwnd; hwnd=GetParent(hwnd)) {
menu_wnd = WINDOW_DYNAMIC_CAST(StartMenu, hwnd);
if (menu_wnd)
break;
}
if (!menu_wnd) {
CloseStartMenu();
break;
}
}
try {
if (pretranslate_msg(&msg))
continue;
if (dispatch_dialog_msg(&msg))
continue;
TranslateMessage(&msg);
try {
DispatchMessage(&msg);
} catch(COMException& e) {
HandleException(e, _hwnd);
}
} catch(COMException& e) {
HandleException(e, _hwnd);
}
}
}
LRESULT StartMenuRoot::Init(LPCREATESTRUCT pcs)
{
// add buttons for entries in _entries
if (super::Init(pcs))
return 1;
AddSeparator();
#ifdef __MINGW32__
HKEY hkey, hkeyAdv;
DWORD value, len;
if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), &hkey))
hkey = 0;
if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hkeyAdv))
hkeyAdv = 0;
#define IS_VALUE_ZERO(hk, name) \
(!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || !value))
#define IS_VALUE_NOT_ZERO(hk, name) \
(!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || value>0))
#endif
// insert hard coded start entries
AddButton(ResString(IDS_PROGRAMS), ICID_APPS, true, IDC_PROGRAMS);
AddButton(ResString(IDS_DOCUMENTS), ICID_DOCUMENTS, true, IDC_DOCUMENTS);
#ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
#else
if (IS_VALUE_ZERO(hkey, _T("NoRecentDocsMenu")))
#endif
AddButton(ResString(IDS_RECENT), ICID_RECENT, true, IDC_RECENT);
AddButton(ResString(IDS_FAVORITES), ICID_FAVORITES, true, IDC_FAVORITES);
AddButton(ResString(IDS_SETTINGS), ICID_CONFIG, true, IDC_SETTINGS);
AddButton(ResString(IDS_BROWSE), ICID_FOLDER, true, IDC_BROWSE);
#ifndef __MINGW32__ // SHRestricted() missing in MinGW (as of 29.10.2003)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -