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

📄 startmenu.cpp

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

			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;
	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) {

⌨️ 快捷键说明

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