Обернул ScrollBar

Вот обернул ScrollBar в класс. Реализовывалось под свои нужды, поэтому работа ведется только со стандартными скроллбарами контролов(т.е. с вертикальным, горизонтальным или сразу с обоими). Выкладываю код:

class ScrollBar : Window
{
public:
	ScrollBar(HWND hParent)
	{
		_hParent = hParent;
	}

	void enableScrollBar(UINT wSBflags, UINT wArrows)
	{
		::EnableScrollBar(_hParent, wSBflags, wArrows);
	}

	void getScrollBarInfo(LONG idObject, PSCROLLBARINFO psbi)
	{
		::GetScrollBarInfo(_hParent, idObject, psbi);
	}

	void getScrollInfo(int fnBar, LPSCROLLINFO lpsi)
	{
		::GetScrollInfo(_hParent, fnBar, lpsi);
	}

	void scrollWindow(int dx, int dy, const RECT *prcScroll, UINT flags)
	{
		::ScrollWindowEx(_hParent, dx, dy, prcScroll, NULL, NULL, NULL, flags);
	}

	void setScrollInfo(int fnBar, LPCSCROLLINFO lpsi, BOOL fRedraw = TRUE)
	{
		::SetScrollInfo(_hParent, fnBar, lpsi, fRedraw);
	}

	void showScrollBar(int wBar, BOOL bShow = TRUE)
	{
		::ShowScrollBar(_hParent, wBar, bShow);
	}

	static WORD getScrollingReguest(WPARAM wParam)
	{
		return LOWORD(wParam);
	}

};

В принципе дополнить класс для работы со скроллбаром как с отдельным контролом не проблема, может быть как-нибудь займусь(что там заниматься: пару строчек дописать и все :-) )

Люблю, чтоб красиво было!

Вот иногда такой корявый по стилю код получается. Здесь же черт ногу сломит(хотя это всего десяток строк):

RECT rc;
::SetRect(&rc, 0, headerPadding + currentYPos, 12, headerPadding + currentYPos + 12);
if ((*itr)->isExpandable) drawer.drawFrameControl(&rc, DFC_BUTTON,DFCS_BUTTONCHECK|DFCS_FLAT);
::SetRect(&rc, (*itr)->level*levelPadding + 16, headerPadding + currentYPos, hdr->getItemWidth(0) - 16, headerPadding + currentYPos + 16);
drawer.drawText((*itr)->tlvii->name, &rc, DT_END_ELLIPSIS |  DT_LEFT | DT_VCENTER | DT_SINGLELINE);
::SetRect(&rc, hdr->getItemPos(1), headerPadding + currentYPos, hdr->getItemWidth(1) - 16, headerPadding + currentYPos + 16);
drawer.drawText((*itr)->tlvii->name, &rc, DT_END_ELLIPSIS |  DT_LEFT | DT_VCENTER | DT_SINGLELINE);
::SetRect(&rc, hdr->getItemPos(2), headerPadding + currentYPos, hdr->getItemWidth(2) - 16, headerPadding + currentYPos + 16);
drawer.drawText((*itr)->tlvii->name, &rc, DT_END_ELLIPSIS |  DT_LEFT | DT_VCENTER | DT_SINGLELINE);

Вот сейчас сижу и думаю как бы это все по-красивее сделать.

P.S. Вообще это уже полу-черновой(ну и словечко :-) ) кусок кода из моего TreeListView. Была тут свободная минутка… Думаю, если сейчас прикручу скроллбар и немного все это доработаю, то можно будет его запихнуть в мой хоткей менеджер

Header Control Class

Написал оберточку над хедером:

class Header : public Window
{
public:

	Header(HWND hParent, HINSTANCE hInst)
	{
		RECT rcParent;
		HDLAYOUT hdl;
		WINDOWPOS wp;
		::InitCommonControls(); 

		_hSelf = ::CreateWindowEx(0, WC_HEADER, (LPCTSTR) NULL,
								  WS_CHILD | HDS_BUTTONS |  HDS_HORZ | WS_BORDER,
								  0, 0, 0, 0, hParent, 0, _hInst,
								  (LPVOID) NULL);
		HFONT hDefaultFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
		::SendMessage(_hSelf,WM_SETFONT,(WPARAM)hDefaultFont,0);
		::GetClientRect(hParent, &rcParent); 

		hdl.prc = &rcParent;
		hdl.pwpos = ℘
		::SendMessage(_hSelf, HDM_LAYOUT, 0, (LPARAM) &hdl);
		::SetWindowPos(_hSelf, wp.hwndInsertAfter, wp.x, wp.y,
			wp.cx, wp.cy, wp.flags | SWP_SHOWWINDOW);
	};

	int insertItem(int iInsertAfter, int nWidth, LPWSTR lpsz)
	{
		HDITEM hdi;
		int index; 

		hdi.mask = HDI_TEXT | HDI_FORMAT | HDI_WIDTH;
		hdi.pszText = lpsz;
		hdi.cxy = nWidth;
		hdi.cchTextMax = wcslen(lpsz);
		hdi.fmt = HDF_LEFT | HDF_STRING; 

		index = SendMessage(_hSelf, HDM_INSERTITEM,
			(WPARAM) iInsertAfter, (LPARAM) &hdi);
		return index;
	} 

	int getHeight()
	{
		RECT rc;
		::SendMessage(_hSelf, HDM_GETITEMRECT, 0, (LPARAM)&rc);
		return rc.bottom;
	}
};

Несколько замечаний по этому поводу:

  • Код практически слизан с примеров из MSDN(этого практически нельзя было не сделать, т.к. «есть два вида программирования на WinApi: так, как в MSDN и неправильно» :-) )
  • Не забывайте из оконной процедуры родительского окна хедера возвращать по умолчанию FALSE(это чревато непонятными глюками)
  • Реализовно естественно далеко не все, но необходимый мне минимум(если кому надо, то MSDN в руки и пошел)

Вообще сейчас как-то давно не брался ни за свой менеджер, ни за контрол для него, т.к. сейчас мало времени

Некоторые соображения

Решил писать контрол с нуля. Учитывая специфику приложения вот, что я думаю сделать:
1. Завести контейнер, где я буду хранить все узлы контрола. Узел будет иметь следующий вид:

struct node
{
    nodeinfo * ii; //вся информация, которая хранится в узле
    int level; //уровень
}

Отрисовывать я буду в цикле, просто смещая каждый узел на itm->level * someConst влево. Получится своеобразнае дерево. Просто здесь мне так можно сделать, т.к. дочерние узлы у меня в любом случае будут добавляться сразу после родительских
2. Выделение и раскрытие\закрытие узлов буду делать немного извращенно: просто ловить WM_LBUTTONDOWN, определять по координатам выбранный узел, производить изменения, потом перерисовывать контрол
3. Естественно буду использовать header control и scroll bar, логику которых придется писать полностью самостоятельно

Изменения в интерфейсе

Решил изменить интерфейс для более удобной настройки горячих клавиш. Для этого необходимо реализовать контрол, который часто называют TreeListView(т.е. скрещенный ListView и TreeView) на WinApi(что является достаточно сложной работой). В результате должно получится что-то вроде этого:
TreeListView
Буду делать :-)

Сырая бета

Выложил здесь бету своего хоткей менеджера. Прошу добровольцев потестить :-)

Погода…

Приступил к написанию плагинов для своего менеджера(там еще есть, что доделать, но там совсем мелочи). Решил написать плагин, который выводит информацию о погоде для указанного города из интернета. Есть множество сервисов для получения XML-файла с погодными данными. Для работы с ними часто нужна функция, которая «скачивает» файл в строковую переменную. Вот решил такую выложить:

std::wstring downloadFile(std::wstring url)
{
	HINTERNET IntOpen = ::InternetOpen(L"YourProgram", LOCAL_INTERNET_ACCESS, NULL, 0, 0);
	HINTERNET handle = ::InternetOpenUrl(IntOpen, url.c_str(), NULL, NULL, NULL, NULL);

	char Buffer[1024];
	DWORD dwRead =0;
	std::wstring s = L"";
	while(::InternetReadFile(handle, Buffer, sizeof(Buffer), &dwRead) == TRUE)
	{
		if ( dwRead == 0)
			break;
		s += ConvertUTF8ToUnicode(Buffer);
	}
	::InternetCloseHandle(handle);
	return s;
}

Симуляция копирования в буфер обмена

Понадобилось мне копировать выделенный в данный момент текст из любого окна. Для большей универсальности(ведь может использваться нестандартный контрол для ввода текста) решил симулировать нажатие Ctrl+C и читать буфер обмена(этот функционал есть практически везде). Получилось что-то вроде:

SimulateCtrlC();
std::string str = GetClipboardText();

но почему-то в str оказывалось все, что угодно, только не выделенный текст. Как оказалось во многих случаях окну просто не успевает прийти WM_COPY после симуляции нажатия на Ctrl+C в то время как я уже проверяю содержимое буфера обмена. Поэтому нужно немного подождать :-) :

SimulateCtrlC();
Sleep(100);
std::string str = GetClipboardText();

Мир! Дружба! OSD!

После 2х часов попыток сделать OSD-сообщения, я их все-таки реализовал. По правде говоря, там еще не совсем «реализация», там просто заготовка, над которой еще придется немного поработать напильником. Однако таких затруднений, я думаю, уже не будет.
Очень помог нагугленный небольшой французский пример. Казалось бы все очень просто: вызови SetLayeredWindowAttributes и рисуй в WM_PAINT, однако там есть свои заморочки со стилями окна(т.е. чтобы все это применить нужны определенные стили у окна, причем это не толь WS_EX_LAYERED).
Думаю, если ничего не случится, то уже на этой неделе выложу первую версию программы.

DEFAULT_GUI_FONT

Как оказалось, проблема из предыдущего поста была в шрифте. Я поменял шрифт на DEFAULT_GUI_FONT и все получилось:

HFONT hFont = (HFONT) ::GetStockObject(DEFAULT_GUI_FONT);
::SelectObject(dc,(HGDIOBJ)hFont);
::DeleteObject(hFont);