⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 startmenu.cpp

📁 ReactOS是一些高手根据Windows XP的内核编写出的类XP。内核实现机理和API函数调用几乎相同。甚至可以兼容XP的程序。喜欢研究系统内核的人可以看一看。
💻 CPP
📖 第 1 页 / 共 5 页
字号:
/*
 * 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 + -