📄 startmenu.cpp
字号:
/*
* Copyright 2003, 2004 Martin Fuchs
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
//
// Explorer clone
//
// startmenu.cpp
//
// Explorer start menu
//
// Martin Fuchs, 19.08.2003
//
// Credits: Thanks to Everaldo (http://www.everaldo.com) for his nice looking icons.
//
#include <precomp.h>
#include "../resource.h"
#include "desktopbar.h"
#include "startmenu.h"
#include "../dialogs/searchprogram.h"
#include "../dialogs/settings.h"
#define SHELLPATH_CONTROL_PANEL TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}")
#define SHELLPATH_PRINTERS TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{2227A280-3AEA-1069-A2DE-08002B30309D}")
#define SHELLPATH_NET_CONNECTIONS TEXT("::{20D04FE0-3AEA-1069-A2D8-08002B30309D}\\::{21EC2020-3AEA-1069-A2DD-08002B30309D}\\::{7007ACC7-3202-11D1-AAD2-00805FC1270E}")
StartMenu::StartMenu(HWND hwnd, int icon_size)
: super(hwnd),
_icon_size(icon_size)
{
_next_id = IDC_FIRST_MENU;
_submenu_id = 0;
_border_left = 0;
_border_top = 0;
_bottom_max = INT_MAX;
_floating_btn = false;
_arrow_btns = false;
_scroll_mode = SCROLL_NOT;
_scroll_pos = 0;
_invisible_lines = 0;
_last_pos = WindowRect(hwnd).pos();
#ifdef _LIGHT_STARTMENU
_selected_id = -1;
_last_mouse_pos = 0;
#endif
}
StartMenu::StartMenu(HWND hwnd, const StartMenuCreateInfo& create_info, int icon_size)
: super(hwnd),
_create_info(create_info),
_icon_size(icon_size)
{
for(StartMenuFolders::const_iterator it=create_info._folders.begin(); it!=create_info._folders.end(); ++it)
if (*it)
_dirs.push_back(ShellDirectory(GetDesktopFolder(), *it, _hwnd));
_next_id = IDC_FIRST_MENU;
_submenu_id = 0;
_border_left = 0;
_border_top = create_info._border_top;
_bottom_max = INT_MAX;
_floating_btn = create_info._border_top? true: false;
_arrow_btns = false;
_scroll_mode = SCROLL_NOT;
_scroll_pos = 0;
_invisible_lines = 0;
_last_pos = WindowRect(hwnd).pos();
#ifdef _LIGHT_STARTMENU
_selected_id = -1;
_last_mouse_pos = 0;
#endif
}
StartMenu::~StartMenu()
{
SendParent(PM_STARTMENU_CLOSED);
}
// We need this wrapper function for s_wcStartMenu, it calls the WIN32 API,
// though static C++ initializers are not allowed for Winelib applications.
BtnWindowClass& StartMenu::GetWndClasss()
{
static BtnWindowClass s_wcStartMenu(CLASSNAME_STARTMENU);
return s_wcStartMenu;
}
Window::CREATORFUNC_INFO StartMenu::s_def_creator = STARTMENU_CREATOR(StartMenu);
HWND StartMenu::Create(int x, int y, const StartMenuFolders& folders, HWND hwndParent, LPCTSTR title,
CREATORFUNC_INFO creator, void* info, const String& filter)
{
UINT style, ex_style;
int top_height;
if (hwndParent) {
style = WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE;
ex_style = 0;
top_height = STARTMENU_TOP_BTN_SPACE;
} else {
style = WS_POPUP|WS_CAPTION|WS_SYSMENU|WS_CLIPCHILDREN|WS_VISIBLE;
ex_style = WS_EX_TOOLWINDOW;
top_height = 0;
}
int icon_size = ICON_SIZE_SMALL;
RECT rect = {x, y-STARTMENU_LINE_HEIGHT(icon_size)-top_height, x+STARTMENU_WIDTH_MIN, y};
#ifndef _LIGHT_STARTMENU
rect.top += STARTMENU_LINE_HEIGHT(icon_size);
#endif
AdjustWindowRectEx(&rect, style, FALSE, ex_style);
StartMenuCreateInfo create_info;
create_info._folders = folders;
create_info._border_top = top_height;
create_info._creator = creator;
create_info._info = info;
create_info._filter = filter;
if (title)
create_info._title = title;
HWND hwnd = Window::Create(creator, &create_info, ex_style, GetWndClasss(), title,
style, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndParent);
// make sure the window is not off the screen
MoveVisible(hwnd);
return hwnd;
}
LRESULT StartMenu::Init(LPCREATESTRUCT pcs)
{
try {
AddEntries();
if (super::Init(pcs))
return 1;
// create buttons for registered entries in _entries
for(ShellEntryMap::const_iterator it=_entries.begin(); it!=_entries.end(); ++it) {
const StartMenuEntry& sme = it->second;
bool hasSubmenu = false;
for(ShellEntrySet::const_iterator it=sme._entries.begin(); it!=sme._entries.end(); ++it)
if ((*it)->_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
hasSubmenu = true;
#ifdef _LIGHT_STARTMENU
_buttons.push_back(SMBtnInfo(sme, it->first, hasSubmenu));
#else
AddButton(sme._title, sme._hIcon, hasSubmenu, it->first);
#endif
}
#ifdef _LIGHT_STARTMENU
if (_buttons.empty())
#else
if (!GetWindow(_hwnd, GW_CHILD))
#endif
AddButton(ResString(IDS_EMPTY), ICID_NONE, false, 0, false);
#ifdef _LIGHT_STARTMENU
ResizeToButtons();
#endif
#ifdef _LAZY_ICONEXTRACT
PostMessage(_hwnd, PM_UPDATE_ICONS, 0, 0);
#endif
} catch(COMException& e) {
HandleException(e, pcs->hwndParent); // destroys the start menu window while switching focus
}
return 0;
}
void StartMenu::AddEntries()
{
for(StartMenuShellDirs::iterator it=_dirs.begin(); it!=_dirs.end(); ++it) {
StartMenuDirectory& smd = *it;
ShellDirectory& dir = smd._dir;
if (!dir._scanned) {
WaitCursor wait;
#ifdef _LAZY_ICONEXTRACT
dir.smart_scan(SORT_NAME, SCAN_DONT_EXTRACT_ICONS); // lazy icon extraction, try to read directly from filesystem
#else
dir.smart_scan(SORT_NAME);
#endif
}
AddShellEntries(dir, -1, smd._ignore);
}
}
static LPTSTR trim_path_slash(LPTSTR path)
{
LPTSTR p = path;
while(*p)
++p;
if (p>path && (p[-1]=='\\' || p[-1]=='/'))
*--p = '\0';
return path;
}
void StartMenu::AddShellEntries(const ShellDirectory& dir, int max, const String& ignore)
{
TCHAR ignore_path[MAX_PATH], ignore_dir[MAX_PATH], ignore_name[_MAX_FNAME], ignore_ext[_MAX_EXT];
TCHAR dir_path[MAX_PATH];
if (!ignore.empty()) {
_tsplitpath_s(ignore, ignore_path, COUNTOF(ignore_path), ignore_dir, COUNTOF(ignore_dir), ignore_name, COUNTOF(ignore_name), ignore_ext, COUNTOF(ignore_ext));
_tcscat(ignore_path, ignore_dir);
_tcscat(ignore_name, ignore_ext);
dir.get_path(dir_path, COUNTOF(dir_path));
if (_tcsicmp(trim_path_slash(dir_path), trim_path_slash(ignore_path)))
*ignore_name = '\0';
} else
*ignore_name = '\0';
String lwr_filter = _create_info._filter;
lwr_filter.toLower();
int cnt = 0;
for(Entry*entry=dir._down; entry; entry=entry->_next) {
// hide files like "desktop.ini"
if (entry->_shell_attribs & SFGAO_HIDDEN)
//not appropriate for drive roots: if (entry->_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
continue;
// hide "Programs" subfolders if requested
if (*ignore_name && !_tcsicmp(entry->_data.cFileName, ignore_name))
continue;
// only 'max' entries shall be added.
if (++cnt == max)
break;
// filter only non-directory entries
if (!(entry->_data.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) && !lwr_filter.empty()) {
String lwr_name = entry->_data.cFileName;
String lwr_disp = entry->_display_name;
lwr_name.toLower();
lwr_disp.toLower();
if (!_tcsstr(lwr_name,lwr_filter) && !_tcsstr(lwr_disp,lwr_filter))
continue;
}
if (entry->_etype == ET_SHELL)
AddEntry(dir._folder, static_cast<ShellEntry*>(entry));
else
AddEntry(dir._folder, entry);
}
}
LRESULT StartMenu::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
{
switch(nmsg) {
case WM_PAINT: {
PaintCanvas canvas(_hwnd);
Paint(canvas);
break;}
case WM_SIZE:
ResizeButtons(LOWORD(lparam)-_border_left);
break;
case WM_MOVE: {
const POINTS& pos = MAKEPOINTS(lparam);
// move open submenus of floating menus
if (_submenu) {
int dx = pos.x - _last_pos.x;
int dy = pos.y - _last_pos.y;
if (dx || dy) {
WindowRect rt(_submenu);
SetWindowPos(_submenu, 0, rt.left+dx, rt.top+dy, 0, 0, SWP_NOSIZE|SWP_NOACTIVATE);
//MoveVisible(_submenu);
}
}
_last_pos.x = pos.x;
_last_pos.y = pos.y;
goto def;}
case WM_NCHITTEST: {
LRESULT res = super::WndProc(nmsg, wparam, lparam);
if (res>=HTSIZEFIRST && res<=HTSIZELAST)
return HTCLIENT; // disable window resizing
return res;}
case WM_LBUTTONDOWN: {
RECT rect;
// check mouse cursor for coordinates of floating button
GetFloatingButtonRect(&rect);
if (PtInRect(&rect, Point(lparam))) {
// create a floating copy of the current start menu
WindowRect pos(_hwnd);
///@todo do something similar to StartMenuRoot::TrackStartmenu() in order to automatically close submenus when clicking on the desktop background
StartMenu::Create(pos.left+3, pos.bottom-3, _create_info._folders, 0, _create_info._title, _create_info._creator, _create_info._info);
CloseStartMenu();
}
#ifdef _LIGHT_STARTMENU
int id = ButtonHitTest(Point(lparam));
if (id)
Command(id, BN_CLICKED);
#endif
break;}
case WM_SYSCOMMAND:
if ((wparam&0xFFF0) == SC_SIZE)
return 0; // disable window resizing
goto def;
case WM_ACTIVATEAPP:
// close start menu when activating another application
if (!wparam)
CloseStartMenu();
break; // don't call super::WndProc in case "this" has been deleted
case WM_CANCELMODE:
CloseStartMenu();
#ifdef _LIGHT_STARTMENU
if (_scroll_mode != SCROLL_NOT) {
ReleaseCapture();
KillTimer(_hwnd, 0);
}
#endif
break;
#ifdef _LIGHT_STARTMENU
case WM_MOUSEMOVE: {
// automatically set the focus to startmenu entries when moving the mouse over them
if (lparam != _last_mouse_pos) { // don't process WM_MOUSEMOVE when opening submenus using keyboard navigation
Point pt(lparam);
if (_arrow_btns) {
RECT rect_up, rect_down;
GetArrowButtonRects(&rect_up, &rect_down, _icon_size);
SCROLL_MODE scroll_mode = SCROLL_NOT;
if (PtInRect(&rect_up, pt))
scroll_mode = SCROLL_UP;
else if (PtInRect(&rect_down, pt))
scroll_mode = SCROLL_DOWN;
if (scroll_mode != _scroll_mode) {
if (scroll_mode == SCROLL_NOT) {
ReleaseCapture();
KillTimer(_hwnd, 0);
} else {
CloseSubmenus();
SetTimer(_hwnd, 0, 150, NULL); // 150 ms scroll interval
SetCapture(_hwnd);
}
_scroll_mode = scroll_mode;
}
}
int new_id = ButtonHitTest(pt);
if (new_id>0 && new_id!=_selected_id)
SelectButton(new_id);
_last_mouse_pos = lparam;
}
break;}
case WM_TIMER:
if (_scroll_mode == SCROLL_UP) {
if (_scroll_pos > 0) {
--_scroll_pos;
InvalidateRect(_hwnd, NULL, TRUE);
}
} else {
if (_scroll_pos <= _invisible_lines) {
++_scroll_pos;
InvalidateRect(_hwnd, NULL, TRUE);
}
}
break;
case WM_KEYDOWN:
ProcessKey(wparam);
break;
#else
case PM_STARTENTRY_FOCUSED: { ///@todo use TrackMouseEvent() and WM_MOUSEHOVER to wait a bit before opening submenus
BOOL hasSubmenu = wparam;
HWND hctrl = (HWND)lparam;
// automatically open submenus
if (hasSubmenu) {
UpdateWindow(_hwnd); // draw focused button before waiting on submenu creation
//SendMessage(_hwnd, WM_COMMAND, MAKEWPARAM(GetDlgCtrlID(hctrl),BN_CLICKED), (LPARAM)hctrl);
Command(GetDlgCtrlID(hctrl), BN_CLICKED);
} else {
// close any open submenu
CloseOtherSubmenus();
}
break;}
#endif
#ifdef _LAZY_ICONEXTRACT
case PM_UPDATE_ICONS:
UpdateIcons(/*wparam*/);
break;
#endif
case PM_STARTENTRY_LAUNCHED:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -