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

📄 startmenu.cpp

📁 winNT技术操作系统,国外开放的原代码和LIUX一样
💻 CPP
📖 第 1 页 / 共 4 页
字号:
	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) {
		bk_color_idx = COLOR_HIGHLIGHT;
		text_color_idx = COLOR_HIGHLIGHTTEXT;
	}

	COLORREF bk_color = GetSysColor(bk_color_idx);
	HBRUSH bk_brush = GetSysColorBrush(bk_color_idx);

	if (title)
		FillRect(hdc, &rect, bk_brush);

	if (btn._icon_id > ICID_NONE)
		g_Globals._icon_cache.get_icon(btn._icon_id).draw(hdc, iconPos.x, iconPos.y, icon_size, icon_size, bk_color, bk_brush/*, icon_size*/);

	 // draw submenu arrow at the right
	if (btn._hasSubmenu) {
		ResIconEx arrowIcon(IDI_ARROW, icon_size, icon_size);
		ResIconEx selArrowIcon(IDI_ARROW_SELECTED, icon_size, icon_size);

		DrawIconEx(hdc, rect.right-icon_size, iconPos.y,
					has_focus? selArrowIcon: arrowIcon,
					icon_size, icon_size, 0, bk_brush, DI_NORMAL);
	}

	if (title) {
		BkMode bk_mode(hdc, TRANSPARENT);

		if (!btn._enabled)	// dis->itemState & (ODS_DISABLED|ODS_GRAYED)
			DrawGrayText(hdc, &textRect, title, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
		else {
			TextColor lcColor(hdc, GetSysColor(text_color_idx));
			DrawText(hdc, title, -1, &textRect, DT_SINGLELINE|DT_NOPREFIX|DT_VCENTER);
		}
	}
}


#ifdef _LIGHT_STARTMENU

void StartMenu::ResizeToButtons()
{
	WindowRect rect(_hwnd);

	WindowCanvas canvas(_hwnd);
	FontSelection font(canvas, GetStockFont(DEFAULT_GUI_FONT));

	const int icon_size = _icon_size;

	int max_width = STARTMENU_WIDTH_MIN;
	int height = 0;

	for(SMBtnVector::const_iterator it=_buttons.begin(); it!=_buttons.end(); ++it) {
		int w = GetStartMenuBtnTextWidth(canvas, it->_title, _hwnd);

		if (w > max_width)
			max_width = w;

		if (it->_id == -1)
			height += STARTMENU_SEP_HEIGHT(icon_size);
		else
			height += STARTMENU_LINE_HEIGHT(icon_size);
	}

	 // calculate new window size
	int text_width = max_width + icon_size + 10/*placeholder*/ + 16/*arrow*/;

	RECT rt_hgt = {rect.left, rect.bottom-_border_top-height, rect.left+_border_left+text_width, rect.bottom};
	AdjustWindowRectEx(&rt_hgt, GetWindowStyle(_hwnd), FALSE, GetWindowExStyle(_hwnd));

	 // ignore movement, only look at the size change
	rect.right = rect.left + (rt_hgt.right-rt_hgt.left);
	rect.top = rect.bottom - (rt_hgt.bottom-rt_hgt.top);

	 // move down if we are too high
	if (rect.top < 0) {
		int dy = -rect.top;
		rect.top += dy;
		rect.bottom += dy;
	}

	 // enable scroll mode for long start menus, which span more than the whole screen height
	int cyscreen = GetSystemMetrics(SM_CYSCREEN);
	int bottom_max = 0;

	if (rect.bottom > cyscreen) {
		_arrow_btns = true;

		_invisible_lines = (rect.bottom-cyscreen+(STARTMENU_LINE_HEIGHT(icon_size)+1))/STARTMENU_LINE_HEIGHT(icon_size)+1; 
		rect.bottom -= _invisible_lines * STARTMENU_LINE_HEIGHT(icon_size);

		bottom_max = rect.bottom;

		if (_floating_btn)
			rect.bottom += 6;	// lower scroll arrow
		else {
			_border_top += 6;	// upper scroll arrow
			rect.bottom += 2*6;	// upper+lower scroll arrow
		}
	}

	MoveWindow(_hwnd, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, TRUE);

	if (bottom_max) {
		POINT pt = {0, bottom_max};

		ScreenToClient(_hwnd, &pt);

		_bottom_max = pt.y;
	}
}

#else // _LIGHT_STARTMENU

LRESULT StartMenuButton::WndProc(UINT nmsg, WPARAM wparam, LPARAM lparam)
{
	switch(nmsg) {
	  case WM_MOUSEMOVE:
		 // automatically set the focus to startmenu entries when moving the mouse over them
		if (GetFocus()!=_hwnd && !(GetWindowStyle(_hwnd)&WS_DISABLED))
			SetFocus(_hwnd);
		break;

	  case WM_SETFOCUS:
		PostParent(PM_STARTENTRY_FOCUSED, _hasSubmenu, (LPARAM)_hwnd);
		goto def;

	  case WM_CANCELMODE:
		 // route WM_CANCELMODE to the startmenu window
		return SendParent(nmsg, wparam, lparam);

	  default: def:
		return super::WndProc(nmsg, wparam, lparam);
	}

	return 0;
}

void StartMenuButton::DrawItem(LPDRAWITEMSTRUCT dis)
{
	TCHAR title[BUFFER_LEN];

	GetWindowText(_hwnd, title, BUFFER_LEN);

	DrawStartMenuButton(dis->hDC, dis->rcItem, title, _hIcon,
						_hasSubmenu,
						!(dis->itemState & ODS_DISABLED),
						dis->itemState&ODS_FOCUS? true: false,
						dis->itemState&ODS_SELECTED? true: false);
}

#endif


StartMenuRoot::StartMenuRoot(HWND hwnd, const StartMenuRootCreateInfo& info)
 :	super(hwnd, info._icon_size)
{
#ifndef __MINGW32__	// SHRestricted() missing in MinGW (as of 29.10.2003)
	if (!g_Globals._SHRestricted || !SHRestricted(REST_NOCOMMONGROUPS))
#endif
		try {
			 // insert directory "All Users\Start Menu"
			ShellDirectory cmn_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_COMMON_STARTMENU, _hwnd), _hwnd);
			_dirs.push_back(StartMenuDirectory(cmn_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_COMMON_PROGRAMS, _hwnd)));
		} catch(COMException&) {
			// ignore exception and don't show additional shortcuts
		}

	try {
		 // insert directory "<user name>\Start Menu"
		ShellDirectory usr_startmenu(GetDesktopFolder(), SpecialFolderPath(CSIDL_STARTMENU, _hwnd), _hwnd);
		_dirs.push_back(StartMenuDirectory(usr_startmenu, (LPCTSTR)SpecialFolderFSPath(CSIDL_PROGRAMS, _hwnd)));
	} catch(COMException&) {
		// ignore exception and don't show additional shortcuts
	}

	ReadLogoSize();
}

void StartMenuRoot::ReadLogoSize()
{
	 // read size of logo bitmap
	BITMAP bmp_hdr;
	GetObject(ResBitmap(GetLogoResId()), sizeof(BITMAP), &bmp_hdr);
	_logo_size.cx = bmp_hdr.bmWidth;
	_logo_size.cy = bmp_hdr.bmHeight;

	 // cache logo width
	_border_left = _logo_size.cx + 1;
}


static void CalculateStartPos(HWND hwndOwner, RECT& rect, int icon_size)
{
	WindowRect pos(hwndOwner);

	rect.left = pos.left;
	rect.top = pos.top-STARTMENU_LINE_HEIGHT(icon_size)-4;
	rect.right = pos.left+STARTMENU_WIDTH_MIN;
	rect.bottom = pos.top;

#ifndef _LIGHT_STARTMENU
	rect.top += STARTMENU_LINE_HEIGHT(icon_size);
#endif

	AdjustWindowRectEx(&rect, WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN|WS_VISIBLE, FALSE, 0);
}

HWND StartMenuRoot::Create(HWND hwndOwner, int icon_size)
{
	RECT rect;

	CalculateStartPos(hwndOwner, rect, icon_size);

	StartMenuRootCreateInfo create_info;

	create_info._icon_size = icon_size;

	return Window::Create(WINDOW_CREATOR_INFO(StartMenuRoot,StartMenuRootCreateInfo), &create_info, 0, GetWndClasss(), TITLE_STARTMENU,
							WS_POPUP|WS_THICKFRAME|WS_CLIPCHILDREN,
							rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, hwndOwner);
}


void StartMenuRoot::TrackStartmenu()
{
	MSG msg;
	HWND hwnd = _hwnd;

#ifdef _LIGHT_STARTMENU
	_selected_id = -1;
#endif

#ifdef _LIGHT_STARTMENU
	 // recalculate start menu root position
	RECT rect;

	CalculateStartPos(GetParent(hwnd), rect, _icon_size);

	SetWindowPos(hwnd, 0, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top, 0);

	ResizeToButtons();
#endif

	 // show previously hidden start menu
	ShowWindow(hwnd, SW_SHOW);
	SetForegroundWindow(hwnd);

	while(IsWindow(hwnd) && IsWindowVisible(hwnd)) {
		if (!GetMessage(&msg, 0, 0, 0)) {
			PostQuitMessage(msg.wParam);
			break;
		}

		 // Check for a mouse click on any window, that is not part of the start menu
		if (msg.message==WM_LBUTTONDOWN || msg.message==WM_MBUTTONDOWN || msg.message==WM_RBUTTONDOWN) {
			StartMenu* menu_wnd = NULL;

			for(HWND hwnd=msg.hwnd; hwnd; hwnd=GetParent(hwnd)) {
				menu_wnd = WINDOW_DYNAMIC_CAST(StartMenu, hwnd);

				if (menu_wnd)
					break;
			}

			if (!menu_wnd) {
				CloseStartMenu();
				break;
			}
		}

		try {
			if (pretranslate_msg(&msg))
				continue;

			if (dispatch_dialog_msg(&msg))
				continue;

			TranslateMessage(&msg);

			try {
				DispatchMessage(&msg);
			} catch(COMException& e) {
				HandleException(e, _hwnd);
			}
		} catch(COMException& e) {
			HandleException(e, _hwnd);
		}
	}
}


LRESULT	StartMenuRoot::Init(LPCREATESTRUCT pcs)
{
	 // add buttons for entries in _entries
	if (super::Init(pcs))
		return 1;

	AddSeparator();


#ifdef __MINGW32__
	HKEY hkey, hkeyAdv;
	DWORD value, len;

	if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"), &hkey))
		hkey = 0;

	if (RegOpenKey(HKEY_CURRENT_USER, _T("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"), &hkeyAdv))
		hkeyAdv = 0;

#define	IS_VALUE_ZERO(hk, name) \
	(!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || !value))

#define	IS_VALUE_NOT_ZERO(hk, name) \
	(!hk || (len=sizeof(value),RegQueryValueEx(hk, name, NULL, NULL, (LPBYTE)&value, &len) || value>0))
#endif


	 // insert hard coded start entries
	AddButton(ResString(IDS_PROGRAMS),		ICID_APPS, true, IDC_PROGRAMS);

	AddButton(ResString(IDS_DOCUMENTS),		ICID_DOCUMENTS, true, IDC_DOCUMENTS);

#ifndef __MINGW32__	// SHRestricted() missing in MinGW (as of 29.10.2003)
	if (!g_Globals._SHRestricted || !SHRestricted(REST_NORECENTDOCSMENU))
#else
	if (IS_VALUE_ZERO(hkey, _T("NoRecentDocsMenu")))
#endif
		AddButton(ResString(IDS_RECENT),	ICID_RECENT, true, IDC_RECENT);

	AddButton(ResString(IDS_FAVORITES),		ICID_FAVORITES, true, IDC_FAVORITES);

	AddButton(ResString(IDS_SETTINGS),		ICID_CONFIG, true, IDC_SETTINGS);

	AddButton(ResString(IDS_BROWSE),		ICID_FOLDER, true, IDC_BROWSE);

#ifndef __MINGW32__	// SHRestricted() missing in MinGW (as of 29.10.2003)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -