📄 startmenu.cpp
字号:
btn._icon_id = ICID_NONE;
for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
Entry* entry = *it;
if (entry->_icon_id == ICID_UNKNOWN)
entry->_icon_id = entry->safe_extract_icon(ICF_FROM_ICON_SIZE(_icon_size));
if (entry->_icon_id > ICID_NONE) {
btn._icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
RECT rect;
GetButtonRect(btn._id, &rect);
if (rect.bottom > _bottom_max)
break;
WindowCanvas canvas(_hwnd);
DrawStartMenuButton(canvas, rect, NULL, btn, btn._id==_selected_id, false, _icon_size);
//InvalidateRect(_hwnd, &rect, FALSE);
//UpdateWindow(_hwnd);
//break;
break;
}
}
}
}
// if (++idx < (int)_buttons.size())
// PostMessage(_hwnd, PM_UPDATE_ICONS, idx, 0);
#else
int icons_extracted = 0;
int icons_updated = 0;
for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
ShellDirectory& dir = it->_dir;
icons_extracted += dir.extract_icons(icon_size);
}
if (icons_extracted) {
for(ShellEntryMap::iterator it1=_entries.begin(); it1!=_entries.end(); ++it1) {
StartMenuEntry& sme = it1->second;
if (!sme._hIcon) {
sme._hIcon = (HICON)-1;
for(ShellEntrySet::const_iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
const Entry* sm_entry = *it2;
if (sm_entry->_hIcon) {
sme._hIcon = sm_entry->_hIcon;
break;
}
}
}
}
for(SMBtnVector::iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
SMBtnInfo& info = *it;
if (info._id>0 && !info._hIcon) {
info._hIcon = _entries[info._id]._hIcon;
++icons_updated;
}
}
}
if (icons_updated) {
InvalidateRect(_hwnd, NULL, FALSE);
UpdateWindow(_hwnd);
}
#endif
}
#endif
// resize child button controls to accomodate for new window size
void StartMenu::ResizeButtons(int cx)
{
HDWP hdwp = BeginDeferWindowPos(10);
for(HWND ctrl=GetWindow(_hwnd,GW_CHILD); ctrl; ctrl=GetNextWindow(ctrl,GW_HWNDNEXT)) {
ClientRect rt(ctrl);
if (rt.right != cx) {
int height = rt.bottom - rt.top;
// special handling for separator controls
if (!height && (GetWindowStyle(ctrl)&SS_TYPEMASK)==SS_ETCHEDHORZ)
height = 2;
hdwp = DeferWindowPos(hdwp, ctrl, 0, 0, 0, cx, height, SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
}
}
EndDeferWindowPos(hdwp);
}
int StartMenu::Command(int id, int code)
{
#ifndef _LIGHT_STARTMENU
switch(id) {
case IDCANCEL:
CloseStartMenu(id);
break;
default: {
#endif
ShellEntryMap::iterator found = _entries.find(id);
if (found != _entries.end()) {
ActivateEntry(id, found->second._entries);
return 0;
}
return super::Command(id, code);
#ifndef _LIGHT_STARTMENU
}
}
return 0;
#endif
}
ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, Entry* entry)
{
// search for an already existing subdirectory entry with the same name
if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
for(ShellEntryMap::iterator it=_entries.begin(); it!=_entries.end(); ++it) {
StartMenuEntry& sme = it->second;
if (!_tcsicmp(sme._title, title)) ///@todo speed up by using a map indexed by name
for(ShellEntrySet::iterator it2=sme._entries.begin(); it2!=sme._entries.end(); ++it2) {
if ((*it2)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
// merge the new shell entry with the existing of the same name
sme._entries.insert(entry);
return it;
}
}
}
ShellEntryMap::iterator sme = AddEntry(title, icon_id);
sme->second._entries.insert(entry);
return sme;
}
ShellEntryMap::iterator StartMenu::AddEntry(const String& title, ICON_ID icon_id, int id)
{
if (id == -1)
id = ++_next_id;
StartMenuEntry sme;
sme._title = title;
sme._icon_id = icon_id;
ShellEntryMap::iterator it = _entries.insert(make_pair(id, sme)).first;
return it;
}
ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, ShellEntry* entry)
{
ICON_ID icon_id;
if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
icon_id = ICID_APPS;
else
icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
return AddEntry(folder.get_name(entry->_pidl), icon_id, entry);
}
ShellEntryMap::iterator StartMenu::AddEntry(const ShellFolder folder, Entry* entry)
{
ICON_ID icon_id;
if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
icon_id = ICID_APPS;
else
icon_id = (ICON_ID)/*@@*/ entry->_icon_id;
return AddEntry(entry->_display_name, icon_id, entry);
}
void StartMenu::AddButton(LPCTSTR title, ICON_ID icon_id, bool hasSubmenu, int id, bool enabled)
{
#ifdef _LIGHT_STARTMENU
_buttons.push_back(SMBtnInfo(title, icon_id, id, hasSubmenu, enabled));
#else
DWORD style = enabled? WS_VISIBLE|WS_CHILD|BS_OWNERDRAW: WS_VISIBLE|WS_CHILD|BS_OWNERDRAW|WS_DISABLED;
WindowRect rect(_hwnd);
ClientRect clnt(_hwnd);
// increase window height to make room for the new button
rect.top -= STARTMENU_LINE_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);
}
WindowCanvas canvas(_hwnd);
FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
// widen window, if it is too small
int text_width = GetStartMenuBtnTextWidth(canvas, title, _hwnd) + icon_size + 10/*placeholder*/ + 16/*arrow*/;
int cx = clnt.right - _border_left;
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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -