📄 startmenu.cpp
字号:
if (GetWindowStyle(_hwnd) & WS_CAPTION) // don't automatically close floating menus
return 0;
// route message to the parent menu and close menus after launching an entry
if (!SendParent(nmsg, wparam, lparam))
CloseStartMenu();
return 1; // signal that we have received and processed the message
case PM_STARTMENU_CLOSED:
_submenu = 0;
break;
case PM_SELECT_ENTRY:
SelectButtonIndex(0, wparam!=0);
break;
#ifdef _LIGHT_STARTMENU
case WM_CONTEXTMENU: {
Point screen_pt(lparam), clnt_pt=screen_pt;
ScreenToClient(_hwnd, &clnt_pt);
int id = ButtonHitTest(clnt_pt);
if (id) {
StartMenuEntry& sme = _entries[id];
for(ShellEntrySet::iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it) {
Entry* entry = *it;
if (entry) {
CHECKERROR(entry->do_context_menu(_hwnd, screen_pt, _cm_ifs)); // may close start menu because of focus loss
///@todo refresh on successfull context menu execution?
break; ///@todo handle context menu for more than one entry
}
}
}
break;}
#endif
default: def:
return super::WndProc(nmsg, wparam, lparam);
}
return 0;
}
#ifdef _LIGHT_STARTMENU
int StartMenu::ButtonHitTest(POINT pt)
{
ClientRect clnt(_hwnd);
const int icon_size = _icon_size;
RECT rect = {_border_left, _border_top, clnt.right, STARTMENU_LINE_HEIGHT(icon_size)};
if (pt.x<rect.left || pt.x>rect.right)
return 0;
for(SMBtnVector::const_iterator it=_buttons.begin()+_scroll_pos; it!=_buttons.end(); ++it) {
const SMBtnInfo& info = *it;
if (rect.top > pt.y)
break;
rect.bottom = rect.top + (info._id==-1? STARTMENU_SEP_HEIGHT(icon_size): STARTMENU_LINE_HEIGHT(icon_size));
if (rect.bottom > _bottom_max)
break;
if (pt.y < rect.bottom) // PtInRect(&rect, pt)
return info._id;
rect.top = rect.bottom;
}
return 0;
}
void StartMenu::InvalidateSelection()
{
if (_selected_id <= 0)
return;
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 == _selected_id) {
InvalidateRect(_hwnd, &rect, TRUE);
break;
}
rect.top = rect.bottom;
}
}
const SMBtnInfo* StartMenu::GetButtonInfo(int id) const
{
for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it)
if (it->_id == id)
return &*it;
return NULL;
}
bool StartMenu::SelectButton(int id, bool open_sub)
{
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];
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -