📄 startmenu.cpp
字号:
if (id == -1)
return false;
if (id == _selected_id)
return true;
InvalidateSelection();
const SMBtnInfo* btn = GetButtonInfo(id);
if (btn && btn->_enabled) {
_selected_id = id;
InvalidateSelection();
// automatically open submenus
if (btn->_hasSubmenu) {
if (open_sub)
OpenSubmenu();
} else
CloseOtherSubmenus(); // close any open submenu
return true;
} else {
_selected_id = -1;
return false;
}
}
bool StartMenu::OpenSubmenu(bool select_first)
{
if (_selected_id == -1)
return false;
InvalidateSelection();
const SMBtnInfo* btn = GetButtonInfo(_selected_id);
// automatically open submenus
if (btn->_hasSubmenu) {
//@@ allows destroying of startmenu when processing PM_UPDATE_ICONS -> GPF
UpdateWindow(_hwnd); // draw focused button before waiting on submenu creation
Command(_selected_id, BN_CLICKED);
if (select_first && _submenu)
SendMessage(_submenu, PM_SELECT_ENTRY, (WPARAM)false, 0);
return true;
} else
return false;
}
int StartMenu::GetSelectionIndex()
{
if (_selected_id == -1)
return -1;
for(int i=0; i<(int)_buttons.size(); ++i)
if (_buttons[i]._id == _selected_id)
return i;
return -1;
}
bool StartMenu::SelectButtonIndex(int idx, bool open_sub)
{
if (idx>=0 && idx<(int)_buttons.size())
return SelectButton(_buttons[idx]._id, open_sub);
else
return false;
}
void StartMenu::ProcessKey(int vk)
{
switch(vk) {
case VK_RETURN:
if (_selected_id)
Command(_selected_id, BN_CLICKED);
break;
case VK_UP:
Navigate(-1);
break;
case VK_DOWN:
Navigate(+1);
break;
case VK_HOME:
SelectButtonIndex(0, false);
break;
case VK_END:
SelectButtonIndex(_buttons.size()-1, false);
break;
case VK_LEFT:
if (_submenu)
CloseOtherSubmenus();
else if (!(GetWindowStyle(_hwnd) & WS_CAPTION)) // don't automatically close floating menus
DestroyWindow(_hwnd);
break;
case VK_RIGHT:
OpenSubmenu(true);
break;
case VK_ESCAPE:
CloseStartMenu();
break;
default:
if (vk>='0' && vk<='Z')
JumpToNextShortcut(vk);
}
}
bool StartMenu::Navigate(int step)
{
int idx = GetSelectionIndex();
if (idx == -1)
if (step > 0)
idx = 0 - step;
else
idx = _buttons.size() - step;
for(;;) {
idx += step;
if (_buttons.size()<=1 && (idx<0 || idx>(int)_buttons.size()))
break;
if (idx < 0)
idx += _buttons.size();
if (idx > (int)_buttons.size())
idx -= _buttons.size()+1;
if (SelectButtonIndex(idx, false))
return true;
}
return false;
}
bool StartMenu::JumpToNextShortcut(char c)
{
int cur_idx = GetSelectionIndex();
if (cur_idx == -1)
cur_idx = 0;
int first_found = 0;
int found_more = 0;
SMBtnVector::const_iterator cur_it = _buttons.begin();
cur_it += cur_idx + 1;
// first search down from current item...
SMBtnVector::const_iterator it = cur_it;
for(; it!=_buttons.end(); ++it) {
const SMBtnInfo& btn = *it;
if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
if (!first_found)
first_found = btn._id;
else
++found_more;
}
}
// ...now search from top to the current item
it = _buttons.begin();
for(; it!=_buttons.end() && it!=cur_it; ++it) {
const SMBtnInfo& btn = *it;
if (!btn._title.empty() && toupper((TBYTE)btn._title.at(0)) == c) {
if (!first_found)
first_found = btn._id;
else
++found_more;
}
}
if (first_found) {
SelectButton(first_found);
if (!found_more)
Command(first_found, BN_CLICKED);
return true;
} else
return false;
}
#endif // _LIGHT_STARTMENU
bool StartMenu::GetButtonRect(int id, PRECT prect) const
{
#ifdef _LIGHT_STARTMENU
ClientRect clnt(_hwnd);
const int icon_size = _icon_size;
RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
const SMBtnInfo& info = *it;
rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
if (info._id == id) {
*prect = rect;
return true;
}
rect.top = rect.bottom;
}
return false;
#else
HWND btn = GetDlgItem(_hwnd, id);
if (btn) {
GetWindowRect(btn, prect);
ScreenToClient(_hwnd, prect);
return true;
} else
return false;
#endif
}
void StartMenu::DrawFloatingButton(HDC hdc)
{
static ResIconEx floatingIcon(IDI_FLOATING, 8, 4);
ClientRect clnt(_hwnd);
DrawIconEx(hdc, clnt.right-12, 0, floatingIcon, 8, 4, 0, 0, DI_NORMAL);
}
void StartMenu::GetFloatingButtonRect(LPRECT prect)
{
GetClientRect(_hwnd, prect);
prect->right -= 4;
prect->left = prect->right - 8;
prect->bottom = 4;
}
void StartMenu::DrawArrows(HDC hdc, int icon_size)
{
int cx = icon_size / 2;
int cy = icon_size / 4;
ResIconEx arrowUpIcon(IDI_ARROW_UP, cx, cy);
ResIconEx arrowDownIcon(IDI_ARROW_DOWN, cx, cy);
ClientRect clnt(_hwnd);
DrawIconEx(hdc, clnt.right/2-cx/2, _floating_btn?3:1, arrowUpIcon, cx, cy, 0, 0, DI_NORMAL);
DrawIconEx(hdc, clnt.right/2-cx/2, clnt.bottom-cy-1, arrowDownIcon, cx, cy, 0, 0, DI_NORMAL);
}
void StartMenu::GetArrowButtonRects(LPRECT prect_up, LPRECT prect_down, int icon_size)
{
int cx = icon_size / 2;
int cy = icon_size / 4;
GetClientRect(_hwnd, prect_up);
*prect_down = *prect_up;
// prect_up->left = prect_up->right/2 - cx/2;
// prect_up->right = prect_up->left + cy;
prect_up->right -= cx;
prect_up->top = _floating_btn? cy-1: 1;
prect_up->bottom = prect_up->top + cy;
// prect_down->left = prect_down->right/2 - cx/2;
// prect_down->right = prect_down->left + cy;
prect_down->right -= cx;
prect_down->top = prect_down->bottom - cy - 1;
}
void StartMenu::Paint(PaintCanvas& canvas)
{
if (_floating_btn)
DrawFloatingButton(canvas);
#ifdef _LIGHT_STARTMENU
if (_arrow_btns)
DrawArrows(canvas, _icon_size);
ClientRect clnt(_hwnd);
const int icon_size = _icon_size;
RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
int sep_width = rect.right-rect.left - 4;
FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));
BkMode bk_mode(canvas, TRANSPARENT);
for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
const SMBtnInfo& btn = *it;
if (rect.top > canvas.rcPaint.bottom)
break;
if (btn._id == -1) { // a separator?
rect.bottom = rect.top + STARTMENU_SEP_HEIGHT(icon_size);
if (rect.bottom > _bottom_max)
break;
BrushSelection brush_sel(canvas, GetSysColorBrush(COLOR_BTNSHADOW));
PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT(icon_size)/2-1, sep_width, 1, PATCOPY);
SelectBrush(canvas, GetSysColorBrush(COLOR_BTNHIGHLIGHT));
PatBlt(canvas, rect.left+2, rect.top+STARTMENU_SEP_HEIGHT(icon_size)/2, sep_width, 1, PATCOPY);
} else {
rect.bottom = rect.top + STARTMENU_LINE_HEIGHT(icon_size);
if (rect.bottom > _bottom_max)
break;
if (rect.top >= canvas.rcPaint.top)
DrawStartMenuButton(canvas, rect, btn._title, btn, btn._id==_selected_id, false, _icon_size);
}
rect.top = rect.bottom;
}
#endif
}
#ifdef _LAZY_ICONEXTRACT
void StartMenu::UpdateIcons(/*int idx*/)
{
UpdateWindow(_hwnd);
#ifdef _SINGLE_ICONEXTRACT
//if (idx >= 0)
int idx = _scroll_pos;
for(; idx<(int)_buttons.size(); ++idx) {
SMBtnInfo& btn = _buttons[idx];
if (btn._icon_id==ICID_UNKNOWN && btn._id>0) {
StartMenuEntry& sme = _entries[btn._id];
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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -