예제 #1
0
CBaseTreeItem* CMsgTree::GetNextItem(int Flags, CBaseTreeItem* Item) // Item is 'int ID' if MTGN_BYID flag is set; returns CBaseTreeItem* or NULL
{
	COptItem_TreeCtrl *TreeCtrl = GetTreeCtrl();
	CBaseTreeItem* TreeItem = Item;
	if (Flags & MTGN_BYID) {
		int Order = TreeCtrl->IDToOrder((int)Item);
		_ASSERT(Order != -1);
		TreeItem = (Order <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(Order)] : (CBaseTreeItem*)&TreeCtrl->Value[Order];
	}

	int TVFlag = 0;
	switch (Flags & ~MTGN_BYID) {
		case MTGN_ROOT: TVFlag = TVGN_ROOT; break;
		case MTGN_CHILD: TVFlag = TVGN_CHILD; break;
		case MTGN_PARENT: TVFlag = TVGN_PARENT; break;
		case MTGN_NEXT: TVFlag = TVGN_NEXT; break;
		case MTGN_PREV: TVFlag = TVGN_PREVIOUS; break;
		default: _ASSERT(0);
	}
	int Order = TreeCtrl->hItemToOrder(TreeView_GetNextItem(hTreeView, TreeItem ? TreeItem->hItem : NULL, TVFlag));
	if (Order == -1)
		return NULL;

	return (Order <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(Order)] : (CBaseTreeItem*)&TreeCtrl->Value[Order];
}
예제 #2
0
void CMsgTree::UpdateItem(int ID) // updates item title, and expanded/collapsed state for groups
{
	COptItem_TreeCtrl *TreeCtrl = GetTreeCtrl();
	int Order = TreeCtrl->IDToOrder(ID);
	if (Order != -1) {
		CBaseTreeItem* TreeItem = (Order <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(Order)] : (CBaseTreeItem*)&TreeCtrl->Value[Order];
		TCString NewTitle;
		TVITEM tvi;
		tvi.mask = TVIF_HANDLE | TVIF_TEXT;
		tvi.hItem = TreeItem->hItem;
		tvi.pszText = NewTitle.GetBuffer(TREEITEMTITLE_MAXLEN);
		tvi.cchTextMax = TREEITEMTITLE_MAXLEN;
		TreeView_GetItem(hTreeView, &tvi);
		if (TreeItem->Title != (const TCHAR*)tvi.pszText) {
			TreeCtrl->SetModified(true);
			NMMSGTREE nm = { 0 };
			nm.ItemNew = TreeItem;
			nm.hdr.code = MTN_ITEMRENAMED;
			nm.hdr.hwndFrom = hTreeView;
			nm.hdr.idFrom = GetDlgCtrlID(hTreeView);
			SendMessage(GetParent(hTreeView), WM_NOTIFY, 0, (LPARAM)&nm);
		}
		tvi.mask = TVIF_HANDLE | TVIF_TEXT;
		tvi.pszText = TreeItem->Title;
		TreeView_SetItem(hTreeView, &tvi);
		TreeView_Expand(hTreeView, tvi.hItem, (TreeItem->Flags & TIF_EXPANDED) ? TVE_EXPAND : TVE_COLLAPSE);
	}
}
예제 #3
0
bool CMsgTree::DeleteSelectedItem() // returns true if the item was deleted
{
	COptItem_TreeCtrl *TreeCtrl = GetTreeCtrl();
	int Order = TreeCtrl->IDToOrder(TreeCtrl->GetSelectedItemID(GetParent(hTreeView)));
	_ASSERT(Order >= 0);
	CTreeItem *SelectedItem = &TreeCtrl->Value[Order];

	//NightFox: fix for langpack and fix cut char space in text
	if (MessageBox(GetParent(hTreeView),
		((SelectedItem->Flags & TIF_GROUP) ?
			TranslateT("Do you really want to delete this category with its messages?") :
			TranslateT("Do you really want to delete this message?")),
		TranslateT("New Away System"), MB_ICONQUESTION | MB_YESNO | MB_DEFBUTTON2) == IDYES) {
		NMMSGTREE nm = { 0 };
		nm.ItemOld = SelectedItem;
		nm.hdr.code = MTN_DELETEITEM;
		nm.hdr.hwndFrom = hTreeView;
		nm.hdr.idFrom = GetDlgCtrlID(hTreeView);
		if (!SendMessage(GetParent(hTreeView), WM_NOTIFY, 0, (LPARAM)&nm)) {
			TreeCtrl->Delete(GetParent(hTreeView), TreeCtrl->GetSelectedItemID(GetParent(hTreeView)));
			return true;
		}
	}
	return false;
}
예제 #4
0
void CMsgTree::SetDefMsg(int iMode, int ID)
{
	for (int i = 0; i < _countof(SettingsList); i++) {
		if (SettingsList[i].Status == iMode) {
			if (MsgTreePage.GetValue(SettingsList[i].DBSetting) != ID) {
				RECT rc;
				COptItem_TreeCtrl *TreeCtrl = GetTreeCtrl();
				int OrderOld = TreeCtrl->IDToOrder(MsgTreePage.GetValue(SettingsList[i].DBSetting));
				if (OrderOld >= 0 && TreeView_GetItemRect(hTreeView, TreeCtrl->Value[OrderOld].hItem, &rc, false))
					InvalidateRect(hTreeView, &rc, true); // refresh icons of previous default tree item

				int OrderNew = TreeCtrl->IDToOrder(ID);
				if (OrderNew >= 0 && TreeView_GetItemRect(hTreeView, TreeCtrl->Value[OrderNew].hItem, &rc, false))
					InvalidateRect(hTreeView, &rc, true); // refresh new default item icons

				MsgTreePage.SetValue(SettingsList[i].DBSetting, ID);
				NMMSGTREE nm = { 0 };
				if (OrderOld >= 0)
					nm.ItemOld = &TreeCtrl->Value[OrderOld];
				if (OrderNew >= 0)
					nm.ItemNew = &TreeCtrl->Value[OrderNew];
				nm.hdr.code = MTN_DEFMSGCHANGED;
				nm.hdr.hwndFrom = hTreeView;
				nm.hdr.idFrom = GetDlgCtrlID(hTreeView);
				SendMessage(GetParent(hTreeView), WM_NOTIFY, 0, (LPARAM)&nm);
			}
			break;
		}
	}
}
예제 #5
0
CBaseTreeItem* CMsgTree::GetSelection() // returns NULL if there's nothing selected
{
	COptItem_TreeCtrl *TreeCtrl = GetTreeCtrl();
	int Order = TreeCtrl->IDToOrder(TreeCtrl->GetSelectedItemID(GetParent(hTreeView)));
	if (Order == -1)
		return NULL;
	return (Order <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(Order)] : (CBaseTreeItem*)&TreeCtrl->Value[Order];
}
예제 #6
0
bool CMsgTree::SetSelection(int ID, int Flags) // set ID = -1 to unselect; returns TRUE on unselect and on successful select
{
	COptItem_TreeCtrl *TreeCtrl = GetTreeCtrl();
	int Order = (Flags & MTSS_BYORDER) ? ID : TreeCtrl->IDToOrder(ID);
	if (Order == -1 && ID != -1)
		return false;

	TreeView_SelectItem(hTreeView, (Order == -1) ? NULL : ((Order <= TREECTRL_ROOTORDEROFFS) ? TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(Order)].hItem : TreeCtrl->Value[Order].hItem));
	return true;
}
예제 #7
0
int GetRecentGroupID(int iMode)
{ // returns an ID of a group where recent messages are stored, accordingly to current settings and status mode.
	// -1 if the group is not found
	//	COptPage MoreOptData(g_MoreOptPage);
	COptPage MsgTreeData(g_MsgTreePage);
	COptItem_TreeCtrl *TreeCtrl = (COptItem_TreeCtrl*)MsgTreeData.Find(IDV_MSGTREE);
	TreeCtrl->DBToMem(CString(MOD_NAME));

	if (!g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_PERSTATUSMRM))
		return g_Messages_RecentRootID;

	for (int Order = 0; Order < TreeCtrl->Value.GetSize(); Order++) // find a group named accordingly to the current status
		if (TreeCtrl->Value[Order].ParentID == g_Messages_RecentRootID && TreeCtrl->Value[Order].Flags & TIF_GROUP && !_tcsicmp(TreeCtrl->Value[Order].Title, iMode ? pcli->pfnGetStatusModeDescription(iMode, 0) : MSGTREE_RECENT_OTHERGROUP))
			return TreeCtrl->Value[Order].ID;

	return -1;
}
예제 #8
0
CTreeItem* CMsgTree::AddMessage()
{
	COptItem_TreeCtrl *TreeCtrl = GetTreeCtrl();
	CTreeItem* TreeItem = TreeCtrl->InsertItem(GetParent(hTreeView), CTreeItem(_T(""), 0, 0));
	TVITEM tvi;
	tvi.mask = TVIF_HANDLE | TVIF_TEXT;
	tvi.hItem = TreeItem->hItem;
	TreeItem->Title = tvi.pszText = TranslateT("New message");
	TreeView_SetItem(hTreeView, &tvi);
	TreeView_EditLabel(hTreeView, TreeItem->hItem);
	NMMSGTREE nm = { 0 };
	nm.ItemNew = TreeItem;
	nm.hdr.code = MTN_NEWMESSAGE;
	nm.hdr.hwndFrom = hTreeView;
	nm.hdr.idFrom = GetDlgCtrlID(hTreeView);
	SendMessage(GetParent(hTreeView), WM_NOTIFY, 0, (LPARAM)&nm);
	return TreeItem;
}
예제 #9
0
CMsgTree::CMsgTree(HWND hTreeView) : MsgTreePage(g_MsgTreePage), hTreeView(hTreeView), hDragItem(NULL), hPrevDropTarget(NULL), UpdateLock(0)
{
	CWndUserData(GetParent(hTreeView)).SetMsgTree(this);
	OrigParentProc = (WNDPROC)SetWindowLongPtr(GetParent(hTreeView), GWLP_WNDPROC, (LONG_PTR)ParentSubclassProc);
	OrigTreeViewProc = (WNDPROC)SetWindowLongPtr(hTreeView, GWLP_WNDPROC, (LONG_PTR)MsgTreeSubclassProc);
	MsgTreePage.SetWnd(GetParent(hTreeView));
	COptItem_TreeCtrl* TreeCtrl = (COptItem_TreeCtrl*)MsgTreePage.Find(IDV_MSGTREE);
	TreeCtrl->SetDlgItemID(GetDlgCtrlID(hTreeView));
	hImageList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32 | ILC_MASK, 5, 2);
	ImageList_AddIcon(hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_NEWMESSAGE)));
	ImageList_AddIcon(hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_NEWCATEGORY)));
	ImageList_AddIcon(hImageList, LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_DELETE)));
	MsgTreePage.DBToMemToPage();
	if (!g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_RECENTMSGSCOUNT)) // show "Recent messages" group only when RECENTMSGSCOUNT is not set to 0.
		TreeView_DeleteItem(hTreeView, TreeCtrl->RootItems[g_Messages_RecentRootID].hItem);

	WindowList_Add(hMTWindowList, hTreeView, NULL);
}
예제 #10
0
int GetRecentGroupID(int iMode)
{ // returns an ID of a group where recent messages are stored, accordingly to current settings and status mode.
	// -1 if the group is not found
//	COptPage MoreOptData(g_MoreOptPage);
	COptPage MsgTreeData(g_MsgTreePage);
	COptItem_TreeCtrl *TreeCtrl = (COptItem_TreeCtrl*)MsgTreeData.Find(IDV_MSGTREE);
	TreeCtrl->DBToMem(CString(MOD_NAME));
	int Order;
	if (g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_PERSTATUSMRM))
	{
		for (Order = 0; Order < TreeCtrl->Value.GetSize(); Order++) // find a group named accordingly to the current status
		{
			if (TreeCtrl->Value[Order].ParentID == g_Messages_RecentRootID && TreeCtrl->Value[Order].Flags & TIF_GROUP && !_tcsicmp(TreeCtrl->Value[Order].Title, iMode ? (TCHAR*)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, iMode, GSMDF_TCHAR) : MSGTREE_RECENT_OTHERGROUP))
			{
				return TreeCtrl->Value[Order].ID;
			}
		}
	} else // simply use Recent Messages category
	{
		return g_Messages_RecentRootID;
	}
	return -1;
}
예제 #11
0
// returns the requested message; sets Order to the order of the message in the message tree, if available; or to -1 otherwise.
TCString CProtoSettings::GetMsgFormat(int Flags, int *pOrder)
{
	TCString Message = NULL;
	if (pOrder)
		*pOrder = -1;

	if (Flags & GMF_TEMPORARY) {
		_ASSERT(!Status || Status == g_ProtoStates[szProto].Status);
		if (g_ProtoStates[szProto].TempMsg.IsSet()) {
			Message = g_ProtoStates[szProto].TempMsg;
			Flags &= ~GMF_PERSONAL; // don't allow personal message to overwrite our NULL temporary message
		}
	}
	if (Flags & GMF_PERSONAL && Message == NULL) // try getting personal message (it overrides global)
		Message = db_get_s(NULL, MOD_NAME, ProtoStatusToDBSetting(DB_STATUSMSG, IDC_MOREOPTDLG_PERSTATUSPROTOMSGS), (TCHAR*)NULL);

	if (Flags & GMF_PROTOORGLOBAL && Message == NULL) {
		Message = CProtoSettings().GetMsgFormat(GMF_PERSONAL | (Flags & GMF_TEMPORARY), pOrder);
		return (Message == NULL) ? _T("") : Message; // global message can't be NULL
	}

	if (Flags & GMF_LASTORDEFAULT && Message == NULL) { // try to get the last or default message, depending on current settings
		COptPage MsgTreeData(g_MsgTreePage);
		COptItem_TreeCtrl *TreeCtrl = (COptItem_TreeCtrl*)MsgTreeData.Find(IDV_MSGTREE);
		TreeCtrl->DBToMem(CString(MOD_NAME));
		Message = NULL;
		if (g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_USELASTMSG)) { // if using last message by default...
			Message = db_get_s(NULL, MOD_NAME, ProtoStatusToDBSetting(DB_STATUSMSG, IDC_MOREOPTDLG_PERSTATUSPROTOMSGS), (TCHAR*)NULL); // try per-protocol message first
			if (Message.IsEmpty()) {
				Message = NULL; // to be sure it's NULL, not "" - as we're checking 'Message == NULL' later
				int RecentGroupID = GetRecentGroupID(Status);
				if (RecentGroupID != -1) {
					for (int i = 0; i < TreeCtrl->Value.GetSize(); i++) { // find first message in the group
						if (TreeCtrl->Value[i].ParentID == RecentGroupID && !(TreeCtrl->Value[i].Flags & TIF_GROUP)) {
							Message = TreeCtrl->Value[i].User_Str1;
							if (pOrder)
								*pOrder = i;
							break;
						}
					}
				}
			}
		} // else, if using default message by default...

		if (Message == NULL) { // ...or we didn't succeed retrieving the last message
			// get default message for this status
			int DefMsgID = -1;
			static struct {
				int DBSetting, Status;
			}
			DefMsgDlgItems[] = {
				IDS_MESSAGEDLG_DEF_ONL, ID_STATUS_ONLINE,
				IDS_MESSAGEDLG_DEF_AWAY, ID_STATUS_AWAY,
				IDS_MESSAGEDLG_DEF_NA, ID_STATUS_NA,
				IDS_MESSAGEDLG_DEF_OCC, ID_STATUS_OCCUPIED,
				IDS_MESSAGEDLG_DEF_DND, ID_STATUS_DND,
				IDS_MESSAGEDLG_DEF_FFC, ID_STATUS_FREECHAT,
				IDS_MESSAGEDLG_DEF_INV, ID_STATUS_INVISIBLE,
				IDS_MESSAGEDLG_DEF_OTP, ID_STATUS_ONTHEPHONE,
				IDS_MESSAGEDLG_DEF_OTL, ID_STATUS_OUTTOLUNCH
			};

			for (int i = 0; i < SIZEOF(DefMsgDlgItems); i++) {
				if (DefMsgDlgItems[i].Status == Status) {
					DefMsgID = MsgTreeData.GetDBValue(DefMsgDlgItems[i].DBSetting);
					break;
				}
			}
			if (DefMsgID == -1)
				DefMsgID = MsgTreeData.GetDBValue(IDS_MESSAGEDLG_DEF_AWAY); // use away message for unknown statuses

			int Order = TreeCtrl->IDToOrder(DefMsgID); // this will return -1 in any case if something goes wrong
			if (Order >= 0)
				Message = TreeCtrl->Value[Order].User_Str1;

			if (pOrder)
				*pOrder = Order;
		}
		if (Message == NULL)
			Message = _T(""); // last or default message can't be NULL.. otherwise ICQ won't reply to status message requests and won't notify us of status message requests at all
	}
	return Message;
}
예제 #12
0
void CProtoSettings::SetMsgFormat(int Flags, TCString Message)
{
	if (Flags & (SMF_TEMPORARY | SMF_PERSONAL) && g_AutoreplyOptPage.GetDBValueCopy(IDC_REPLYDLG_RESETCOUNTERWHENSAMEICON) && GetMsgFormat(Flags & (SMF_TEMPORARY | SMF_PERSONAL)) != (const TCHAR*)Message)
		ResetSettingsOnStatusChange(szProto);

	if (Flags & SMF_TEMPORARY) {
		_ASSERT(!Status || Status == g_ProtoStates[szProto].Status);
		g_ProtoStates[szProto].TempMsg = (szProto || Message != NULL) ? Message : CProtoSettings(NULL, Status).GetMsgFormat(GMF_LASTORDEFAULT);
	}

	if (Flags & SMF_PERSONAL) { // set a "personal" message for a protocol. also it's used to set global status message (hContact = NULL).
		// if Message == NULL, then we'll use the "default" message - i.e. it's either the global message for szProto != NULL (we delete the per-proto DB setting), or it's just a default message for a given status for szProto == NULL.
		g_ProtoStates[szProto].TempMsg.Unset();
		CString DBSetting(ProtoStatusToDBSetting(DB_STATUSMSG, IDC_MOREOPTDLG_PERSTATUSPROTOMSGS));
		if (Message != NULL)
			db_set_ts(NULL, MOD_NAME, DBSetting, Message);
		else {
			if (!szProto)
				db_set_ts(NULL, MOD_NAME, DBSetting, CProtoSettings(NULL, Status).GetMsgFormat(GMF_LASTORDEFAULT)); // global message can't be NULL; we can use an empty string instead if it's really necessary
			else
				db_unset(NULL, MOD_NAME, DBSetting);
		}
	}

	if (Flags & SMF_LAST) {
		COptPage MsgTreeData(g_MsgTreePage);
		COptItem_TreeCtrl *TreeCtrl = (COptItem_TreeCtrl*)MsgTreeData.Find(IDV_MSGTREE);
		TreeCtrl->DBToMem(CString(MOD_NAME));
		int RecentGroupID = GetRecentGroupID(Status);
		if (RecentGroupID == -1) { // we didn't find the group, it also means that we're using per status messages; so we need to create it
			TreeCtrl->Value.AddElem(CTreeItem(Status ? pcli->pfnGetStatusModeDescription(Status, 0) : MSGTREE_RECENT_OTHERGROUP, g_Messages_RecentRootID, RecentGroupID = TreeCtrl->GenerateID(), TIF_GROUP));
			TreeCtrl->SetModified(true);
		}
		int i;
		// try to find an identical message in the same group (to prevent saving multiple identical messages), 
		// or at least if we'll find an identical message somewhere else, then we'll use its title for our new message
		TCString Title(_T(""));
		for (i = 0; i < TreeCtrl->Value.GetSize(); i++) {
			if (!(TreeCtrl->Value[i].Flags & TIF_GROUP) && TreeCtrl->Value[i].User_Str1 == (const TCHAR*)Message) {
				if (TreeCtrl->Value[i].ParentID == RecentGroupID) { // found it in the same group
					int GroupOrder = TreeCtrl->IDToOrder(RecentGroupID);
					TreeCtrl->Value.MoveElem(i, (GroupOrder >= 0) ? (GroupOrder + 1) : 0); // now move it to the top of recent messages list
					TreeCtrl->SetModified(true);
					break; // no reason to search for anything else
				}
				if (Title.IsEmpty()) // it's not in the same group, but at least we'll use its title
					Title = TreeCtrl->Value[i].Title;
			}
		}
		if (i == TreeCtrl->Value.GetSize()) { // we didn't find an identical message in the same group, so we'll add our new message here
			if (Title.IsEmpty()) { // didn't find a title for our message either
				if (Message.GetLen() > MRM_MAX_GENERATED_TITLE_LEN)
					Title = Message.Left(MRM_MAX_GENERATED_TITLE_LEN - 3) + _T("...");
				else
					Title = Message;

				TCHAR *p = Title.GetBuffer();
				while (*p) { // remove "garbage"
					if (!(p = _tcspbrk(p, _T("\r\n\t"))))
						break;

					*p++ = ' ';
				}
				Title.ReleaseBuffer();
			}
			int GroupOrder = TreeCtrl->IDToOrder(RecentGroupID);
			TreeCtrl->Value.InsertElem(CTreeItem(Title, RecentGroupID, TreeCtrl->GenerateID(), 0, Message), (GroupOrder >= 0) ? (GroupOrder + 1) : 0);
			TreeCtrl->SetModified(true);
		}

		// now clean up here
		int MRMNum = 0;
		int MaxMRMNum = g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_RECENTMSGSCOUNT);
		for (i = 0; i < TreeCtrl->Value.GetSize(); i++) {
			if (TreeCtrl->Value[i].ParentID == RecentGroupID) { // found a child of our group
				if (TreeCtrl->Value[i].Flags & TIF_GROUP || ++MRMNum > MaxMRMNum) { // what groups are doing here?! :))
					TreeCtrl->Value.RemoveElem(i);
					TreeCtrl->SetModified(true);
					i--;
				}
			}
		}

		// if we're saving recent messages per status, then remove any messages that were left at the recent messages' root
		if (g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_PERSTATUSMRM)) {
			for (i = 0; i < TreeCtrl->Value.GetSize(); i++) {
				if (TreeCtrl->Value[i].ParentID == g_Messages_RecentRootID) {
					if (!(TreeCtrl->Value[i].Flags & TIF_GROUP)) {
						TreeCtrl->Value.RemoveElem(i);
						TreeCtrl->SetModified(true);
						i--;
					}
				}
			}
		}
		TreeCtrl->MemToDB(CString(MOD_NAME));
	}
}
예제 #13
0
INT_PTR srvVariablesHandler(WPARAM, LPARAM lParam)
{
    ARGUMENTSINFO *ai = (ARGUMENTSINFO*)lParam;
    ai->flags = AIF_DONTPARSE;
    TCString Result;
    if (!mir_tstrcmp(ai->targv[0], VAR_AWAYSINCE_TIME)) {
        GetTimeFormat(LOCALE_USER_DEFAULT, 0, g_ProtoStates[VarParseData.szProto].AwaySince, (ai->argc > 1 && *ai->targv[1]) ? ai->targv[1] : _T("H:mm"), Result.GetBuffer(256), 256);
        Result.ReleaseBuffer();
    }
    else if (!mir_tstrcmp(ai->targv[0], VAR_AWAYSINCE_DATE)) {
        GetDateFormat(LOCALE_USER_DEFAULT, 0, g_ProtoStates[VarParseData.szProto].AwaySince, (ai->argc > 1 && *ai->targv[1]) ? ai->targv[1] : NULL, Result.GetBuffer(256), 256);
        Result.ReleaseBuffer();
    }
    else if (!mir_tstrcmp(ai->targv[0], VAR_STATDESC)) {
        Result = (VarParseData.Flags & VPF_XSTATUS) ? STR_XSTATUSDESC : pcli->pfnGetStatusModeDescription(g_ProtoStates[VarParseData.szProto].Status, 0);
    }
    else if (!mir_tstrcmp(ai->targv[0], VAR_MYNICK)) {
        if (g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_MYNICKPERPROTO) && VarParseData.szProto)
            Result = db_get_s(NULL, VarParseData.szProto, "Nick", (TCHAR*)NULL);

        if (Result == NULL)
            Result = pcli->pfnGetContactDisplayName(NULL, 0);

        if (Result == NULL)
            Result = TranslateT("Stranger");
    }
    else if (!mir_tstrcmp(ai->targv[0], VAR_REQUESTCOUNT)) {
        mir_sntprintf(Result.GetBuffer(16), 16, _T("%d"), db_get_w(ai->fi->hContact, MOD_NAME, DB_REQUESTCOUNT, 0));
        Result.ReleaseBuffer();
    }
    else if (!mir_tstrcmp(ai->targv[0], VAR_MESSAGENUM)) {
        mir_sntprintf(Result.GetBuffer(16), 16, _T("%d"), db_get_w(ai->fi->hContact, MOD_NAME, DB_MESSAGECOUNT, 0));
        Result.ReleaseBuffer();
    }
    else if (!mir_tstrcmp(ai->targv[0], VAR_TIMEPASSED)) {
        ULARGE_INTEGER ul_AwaySince, ul_Now;
        SYSTEMTIME st;
        GetLocalTime(&st);
        SystemTimeToFileTime(&st, (LPFILETIME)&ul_Now);
        SystemTimeToFileTime(g_ProtoStates[VarParseData.szProto].AwaySince, (LPFILETIME)&ul_AwaySince);
        ul_Now.QuadPart -= ul_AwaySince.QuadPart;
        ul_Now.QuadPart /= 10000000; // now it's in seconds
        Result.GetBuffer(256);
        if (ul_Now.LowPart >= 7200) // more than 2 hours
            mir_sntprintf(Result, 256, TranslateT("%d hours"), ul_Now.LowPart / 3600);
        else if (ul_Now.LowPart >= 120) // more than 2 minutes
            mir_sntprintf(Result, 256, TranslateT("%d minutes"), ul_Now.LowPart / 60);
        else
            mir_sntprintf(Result, 256, TranslateT("%d seconds"), ul_Now.LowPart);
        Result.ReleaseBuffer();
    }
    else if (!mir_tstrcmp(ai->targv[0], VAR_PREDEFINEDMESSAGE)) {
        ai->flags = 0; // reset AIF_DONTPARSE flag
        if (ai->argc != 2)
            return NULL;

        COptPage MsgTreeData(g_MsgTreePage);
        COptItem_TreeCtrl *TreeCtrl = (COptItem_TreeCtrl*)MsgTreeData.Find(IDV_MSGTREE);
        TreeCtrl->DBToMem(CString(MOD_NAME));

        for (int i = 0; i < TreeCtrl->Value.GetSize(); i++) {
            if (!(TreeCtrl->Value[i].Flags & TIF_GROUP) && !mir_tstrcmpi(TreeCtrl->Value[i].Title, ai->targv[1])) {
                Result = TreeCtrl->Value[i].User_Str1;
                break;
            }
        }
        if (Result == NULL) // if we didn't find a message with specified title
            return NULL; // return it now, as later we change NULL to ""
    }
    else if (!mir_tstrcmp(ai->targv[0], VAR_PROTOCOL)) {
        if (VarParseData.szProto) {
            CString AnsiResult;
            CallProtoService(VarParseData.szProto, PS_GETNAME, 256, (LPARAM)AnsiResult.GetBuffer(256));
            AnsiResult.ReleaseBuffer();
            Result = _A2T(AnsiResult);
        }
        if (Result == NULL) // if we didn't find a message with specified title
            return NULL; // return it now, as later we change NULL to ""
    }
    TCHAR *szResult = (TCHAR*)malloc((Result.GetLen() + 1) * sizeof(TCHAR));
    if (!szResult)
        return NULL;

    mir_tstrcpy(szResult, (Result != NULL) ? Result : _T(""));
    return (INT_PTR)szResult;
}
예제 #14
0
static LRESULT CALLBACK ParentSubclassProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	CMsgTree *dat = CWndUserData(hWnd).GetMsgTree();
	switch (Msg) {
	case WM_NOTIFY:
		if (((LPNMHDR)lParam)->hwndFrom == dat->hTreeView) {
			switch (((LPNMHDR)lParam)->code) {
			case TVN_BEGINDRAG:
				{
					LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam;
					NMMSGTREE nm = { 0 };
					COptItem_TreeCtrl *TreeCtrl = dat->GetTreeCtrl();
					int Order = TreeCtrl->hItemToOrder(pnmtv->itemNew.hItem);
					_ASSERT(Order != -1);
					if (Order != -1) {
						nm.ItemOld = (Order <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(Order)] : (CBaseTreeItem*)&TreeCtrl->Value[Order];
						nm.hdr.code = MTN_BEGINDRAG;
						nm.hdr.hwndFrom = dat->hTreeView;
						nm.hdr.idFrom = GetDlgCtrlID(dat->hTreeView);
						if (!SendMessage(hWnd, WM_NOTIFY, 0, (LPARAM)&nm)) {
							SetCapture(hWnd);
							dat->hPrevDropTarget = dat->hDragItem = pnmtv->itemNew.hItem;
							SetFocus(dat->hTreeView);
							TreeView_SelectItem(dat->hTreeView, dat->hDragItem);
						}
					}
				}
				break;

			case TVN_SELCHANGED:
				if (dat->UpdateLock)
					return 0;
				else {
					LPNMTREEVIEW pnmtv = (LPNMTREEVIEW)lParam;
					NMMSGTREE nm = { 0 };
					COptItem_TreeCtrl *TreeCtrl = dat->GetTreeCtrl();
					if (pnmtv->itemOld.hItem) {
						int Order = TreeCtrl->IDToOrder(pnmtv->itemOld.lParam);
						if (Order != -1) {
							nm.ItemOld = (Order <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(Order)] : (CBaseTreeItem*)&TreeCtrl->Value[Order];
						}
					}
					if (pnmtv->itemNew.hItem) {
						int Order = TreeCtrl->IDToOrder(pnmtv->itemNew.lParam);
						if (Order != -1) {
							nm.ItemNew = (Order <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(Order)] : (CBaseTreeItem*)&TreeCtrl->Value[Order];
						}
					}
					nm.hdr.code = MTN_SELCHANGED;
					nm.hdr.hwndFrom = dat->hTreeView;
					nm.hdr.idFrom = GetDlgCtrlID(dat->hTreeView);
					SendMessage(hWnd, WM_NOTIFY, 0, (LPARAM)&nm);
				}
				break;

			case TVN_BEGINLABELEDIT:
				if (dat->GetTreeCtrl()->IDToOrder(((LPNMTVDISPINFO)lParam)->item.lParam) < 0)
					return true; // cancel editing

				g_OrigEditProc = (WNDPROC)SetWindowLongPtr(TreeView_GetEditControl(dat->hTreeView), GWLP_WNDPROC, (LONG_PTR)EditSubclassProc);
				break;

			case TVN_ENDLABELEDIT:
				{
					LPNMTVDISPINFO ptvdi = (LPNMTVDISPINFO)lParam;
					if (ptvdi->item.pszText) {
						COptItem_TreeCtrl *TreeCtrl = dat->GetTreeCtrl();
						int Order = TreeCtrl->IDToOrder(ptvdi->item.lParam);
						if (Order >= 0) {
							TreeCtrl->Value[Order].Title = ptvdi->item.pszText;
							TreeCtrl->SetModified(true);
							NMMSGTREE nm = { 0 };
							nm.ItemNew = &TreeCtrl->Value[Order];
							nm.hdr.code = MTN_ITEMRENAMED;
							nm.hdr.hwndFrom = dat->hTreeView;
							nm.hdr.idFrom = GetDlgCtrlID(dat->hTreeView);
							SendMessage(GetParent(dat->hTreeView), WM_NOTIFY, 0, (LPARAM)&nm);
							return true; // commit new text
						}
					}
				}
				break;

			case NM_CLICK:
			case NM_RCLICK:
				{
					TVHITTESTINFO hitTest;
					hitTest.pt.x = (short)LOWORD(GetMessagePos());
					hitTest.pt.y = (short)HIWORD(GetMessagePos());
					ScreenToClient(dat->hTreeView, &hitTest.pt);
					TreeView_HitTest(dat->hTreeView, &hitTest);
					if (hitTest.hItem) {
						if (TreeView_GetSelection(dat->hTreeView) == hitTest.hItem) // make sure TVN_SELCHANGED notification is sent always, even if previous selected item was the same as new
							TreeView_SelectItem(dat->hTreeView, NULL);
						TreeView_SelectItem(dat->hTreeView, hitTest.hItem);
					}
				}
				break;

			case NM_CUSTOMDRAW:
				NMTVCUSTOMDRAW *lpNMCD = (NMTVCUSTOMDRAW*)lParam;
				switch (lpNMCD->nmcd.dwDrawStage) {
				case CDDS_PREPAINT: // the control is about to start painting
					return CDRF_NOTIFYITEMDRAW; // instruct the control to return information when it draws items
				case CDDS_ITEMPREPAINT:
					return CDRF_NOTIFYPOSTPAINT;
				case CDDS_ITEMPOSTPAINT:
					RECT rc;
					TreeView_GetItemRect(lpNMCD->nmcd.hdr.hwndFrom, (HTREEITEM)lpNMCD->nmcd.dwItemSpec, &rc, true);
					int iSize = GetSystemMetrics(SM_CXSMICON);
					int x = rc.left - iSize - 5;
					for (int i = 0; i < _countof(SettingsList); i++) {
						if (lpNMCD->nmcd.lItemlParam == dat->MsgTreePage.GetValue(SettingsList[i].DBSetting)) {
							DrawIconEx(lpNMCD->nmcd.hdc, x, rc.top, Skin_LoadProtoIcon(NULL, SettingsList[i].Status), iSize, iSize, 0, GetSysColorBrush(COLOR_WINDOW), DI_NORMAL);
							x -= iSize + 1;
						}
					}
				}
			}
		}
		break;

	case WM_MOUSEMOVE:
		if (dat->hDragItem) {
			TVHITTESTINFO hti;
			hti.pt.x = (short)LOWORD(lParam);
			hti.pt.y = (short)HIWORD(lParam);
			ClientToScreen(hWnd, &hti.pt);
			ScreenToClient(dat->hTreeView, &hti.pt);
			TreeView_HitTest(dat->hTreeView, &hti);
			if (hti.hItem) {
				TreeView_SelectDropTarget(dat->hTreeView, hti.hItem);
				SetTimer(hWnd, MSGTREE_TIMER_ID, MSGTREE_DRAGANDDROP_GROUPEXPANDTIME, NULL);
			}
			else {
				if (hti.flags & TVHT_ABOVE)
					SendMessage(dat->hTreeView, WM_VSCROLL, MAKEWPARAM(SB_LINEUP, 0), 0);
				if (hti.flags & TVHT_BELOW)
					SendMessage(dat->hTreeView, WM_VSCROLL, MAKEWPARAM(SB_LINEDOWN, 0), 0);

				TreeView_SelectDropTarget(dat->hTreeView, NULL);
				KillTimer(hWnd, MSGTREE_TIMER_ID);
			}
		}
		break;

	case WM_LBUTTONUP:
		if (dat->hDragItem) {
			TreeView_SelectDropTarget(dat->hTreeView, NULL);
			KillTimer(hWnd, MSGTREE_TIMER_ID);
			ReleaseCapture();
			TVHITTESTINFO hti;
			hti.pt.x = (short)LOWORD(lParam);
			hti.pt.y = (short)HIWORD(lParam);
			ClientToScreen(hWnd, &hti.pt);
			ScreenToClient(dat->hTreeView, &hti.pt);
			TreeView_HitTest(dat->hTreeView, &hti);
			if (hti.hItem && dat->hDragItem != hti.hItem) {
				NMMSGTREE nm = { 0 };
				COptItem_TreeCtrl *TreeCtrl = dat->GetTreeCtrl();
				int OrderOld = TreeCtrl->hItemToOrder(dat->hDragItem);
				int OrderNew = TreeCtrl->hItemToOrder(hti.hItem);
				_ASSERT(OrderOld != -1 && OrderNew != -1);
				nm.ItemOld = (OrderOld <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(OrderOld)] : (CBaseTreeItem*)&TreeCtrl->Value[OrderOld];
				nm.ItemNew = (OrderNew <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(OrderNew)] : (CBaseTreeItem*)&TreeCtrl->Value[OrderNew];
				nm.hdr.code = MTN_ENDDRAG;
				nm.hdr.hwndFrom = dat->hTreeView;
				nm.hdr.idFrom = GetDlgCtrlID(dat->hTreeView);
				if (!SendMessage(hWnd, WM_NOTIFY, 0, (LPARAM)&nm)) {
					dat->UpdateLock++;
					dat->GetTreeCtrl()->MoveItem(hWnd, dat->hDragItem, hti.hItem);
					dat->UpdateLock--;
				}
			}
			dat->hDragItem = NULL;
		}
		break;

	case WM_TIMER:
		if (wParam == MSGTREE_TIMER_ID) {
			KillTimer(hWnd, MSGTREE_TIMER_ID);
			TVHITTESTINFO hti;
			hti.pt.x = (short)LOWORD(GetMessagePos());
			hti.pt.y = (short)HIWORD(GetMessagePos());
			ScreenToClient(dat->hTreeView, &hti.pt);
			TreeView_HitTest(dat->hTreeView, &hti);
			if (hti.hItem && dat->hDragItem != hti.hItem && TreeView_GetChild(dat->hTreeView, hti.hItem)) // target is a group and is not the same item that we're dragging
				TreeView_Expand(dat->hTreeView, hti.hItem, TVE_EXPAND);
		}
	}
	return CallWindowProc(dat->OrigParentProc, hWnd, Msg, wParam, lParam);
}
예제 #15
0
static LRESULT CALLBACK MsgTreeSubclassProc(HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
{
	CMsgTree *dat = CWndUserData(GetParent(hWnd)).GetMsgTree();
	switch (Msg) {
	case UM_MSGTREE_UPDATE: // returns TRUE if updated
		{
			bool Modified = dat->MsgTreePage.GetModified();
			TCString WndTitle;
			if (Modified) {
				WndTitle.GetBuffer(256);
				HWND hCurWnd = hWnd;
				do {
					hCurWnd = GetParent(hCurWnd);
				} while (hCurWnd && !GetWindowText(hCurWnd, WndTitle, 256));
				WndTitle.ReleaseBuffer();
			}
			if (!Modified || MessageBox(GetParent(hWnd), TCString(TranslateT("You've made changes to multiple message trees at a time.\r\nDo you want to leave changes in \"")) + WndTitle + TranslateT("\" dialog?\r\nPress Yes to leave changes in this dialog, or No to discard its changes and save changes of the other message tree instead."), WndTitle + _T(" - ") + TranslateT("New Away System"), MB_ICONQUESTION | MB_YESNO) == IDNO) {
				COptItem_TreeCtrl *TreeCtrl = dat->GetTreeCtrl();
				TCString OldTitle, OldMsg, NewTitle, NewMsg;
				int OldOrder = TreeCtrl->IDToOrder(TreeCtrl->GetSelectedItemID(GetParent(hWnd)));
				if (OldOrder != -1) {
					CBaseTreeItem* ItemOld = (OldOrder <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(OldOrder)] : (CBaseTreeItem*)&TreeCtrl->Value[OldOrder];
					OldTitle = ItemOld->Title;
					if (!(ItemOld->Flags & TIF_ROOTITEM))
						OldMsg = ((CTreeItem*)ItemOld)->User_Str1;
				}
				dat->UpdateLock++;
				dat->MsgTreePage.DBToMemToPage();
				dat->UpdateLock--;
				NMMSGTREE nm = { 0 };
				int Order = TreeCtrl->IDToOrder(TreeCtrl->GetSelectedItemID(GetParent(hWnd)));
				if (Order != -1) {
					nm.ItemNew = (Order <= TREECTRL_ROOTORDEROFFS) ? (CBaseTreeItem*)&TreeCtrl->RootItems[ROOT_ORDER_TO_INDEX(Order)] : (CBaseTreeItem*)&TreeCtrl->Value[Order];
					NewTitle = nm.ItemNew->Title;
					if (!(nm.ItemNew->Flags & TIF_ROOTITEM))
						NewMsg = ((CTreeItem*)nm.ItemNew)->User_Str1;
				}
				if (OldTitle.IsEmpty())
					OldTitle = _T(""); // to be sure that NULL will be equal to "" in the latter comparisons
				if (OldMsg.IsEmpty())
					OldMsg = _T("");
				if (NewTitle.IsEmpty())
					NewTitle = _T("");
				if (NewMsg.IsEmpty())
					NewMsg = _T("");

				if (OldTitle != (const TCHAR*)NewTitle || OldMsg != (const TCHAR*)NewMsg) {
					// probably it's better to leave nm.ItemOld = NULL, to prevent accidental rewriting of it with old data from an edit control etc.
					nm.hdr.code = MTN_SELCHANGED;
					nm.hdr.hwndFrom = hWnd;
					nm.hdr.idFrom = GetDlgCtrlID(hWnd);
					SendMessage(GetParent(hWnd), WM_NOTIFY, 0, (LPARAM)&nm);
				}
				return true;
			}
		}
		return false;

	case WM_KEYDOWN:
		switch (wParam) {
		case VK_DELETE:
			dat->DeleteSelectedItem();
			break;
		case VK_INSERT:
			dat->AddMessage();
			break;
		}
		break;

	case WM_RBUTTONDOWN:
		SetFocus(hWnd);
		{
			TVHITTESTINFO hitTest;
			hitTest.pt.x = (short)LOWORD(lParam);
			hitTest.pt.y = (short)HIWORD(lParam);
			TreeView_HitTest(hWnd, &hitTest);
			if (hitTest.hItem && hitTest.flags & TVHT_ONITEM)
				TreeView_SelectItem(hWnd, hitTest.hItem);

			return DefWindowProc(hWnd, Msg, wParam, lParam);
		}
		break;

	case WM_CONTEXTMENU:
		{
			TVHITTESTINFO ht;
			ht.pt.x = GET_X_LPARAM(lParam);
			ht.pt.y = GET_Y_LPARAM(lParam);
			TVITEM tvi = { 0 };
			if (ht.pt.x == -1 && ht.pt.y == -1) { // use selected item 
				if (tvi.hItem = TreeView_GetSelection(hWnd)) {
					TreeView_EnsureVisible(hWnd, tvi.hItem);
					RECT rc;
					TreeView_GetItemRect(hWnd, tvi.hItem, &rc, true);
					ht.pt.x = rc.left;
					ht.pt.y = rc.bottom;
				}
			}
			else {
				ScreenToClient(hWnd, &ht.pt);
				TreeView_HitTest(hWnd, &ht);
				if (ht.hItem && ht.flags & TVHT_ONITEM) {
					tvi.hItem = ht.hItem;
				}
			}
			if (tvi.hItem) {
				COptItem_TreeCtrl *TreeCtrl = dat->GetTreeCtrl();
				tvi.mask = TVIF_HANDLE | TVIF_PARAM;
				TreeView_GetItem(hWnd, &tvi);
				int Order = TreeCtrl->IDToOrder(tvi.lParam);
				if (Order >= 0) {
					HMENU hMenu;
					if (TreeCtrl->Value[Order].Flags & TIF_GROUP)
						hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MSGTREE_CATEGORYMENU));
					else
						hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MSGTREE_MESSAGEMENU));

					_ASSERT(hMenu);
					HMENU hPopupMenu = GetSubMenu(hMenu, 0);
					TranslateMenu(hPopupMenu);
					ClientToScreen(hWnd, &ht.pt);
					struct
					{
						int ItemID, IconID;
					}
					MenuItems[] = {
						IDM_MSGTREEMENU_NEWMESSAGE, IMGLIST_NEWMESSAGE,
						IDM_MSGTREEMENU_NEWCATEGORY, IMGLIST_NEWCATEGORY,
						IDM_MSGTREEMENU_DELETE, IMGLIST_DELETE
					};
					MENUITEMINFO mii = { 0 };
					mii.cbSize = sizeof(mii);
					mii.fMask = MIIM_BITMAP | MIIM_DATA | MIIM_STATE | MIIM_CHECKMARKS;
					mii.hbmpItem = HBMMENU_CALLBACK;
					int i;
					for (i = 0; i < _countof(MenuItems); i++) { // set icons
						mii.dwItemData = MenuItems[i].IconID;
						SetMenuItemInfo(hPopupMenu, MenuItems[i].ItemID, false, &mii);
					}
					mii.fMask = MIIM_STATE;
					mii.fState = MFS_CHECKED;
					for (i = 0; i < _countof(SettingsList); i++) // set checkmarks
						if (TreeCtrl->Value[Order].ID == dat->MsgTreePage.GetValue(SettingsList[i].DBSetting))
							SetMenuItemInfo(hPopupMenu, SettingsList[i].MenuItemID, false, &mii);

					int MenuResult = TrackPopupMenu(hPopupMenu, TPM_RIGHTBUTTON | TPM_RETURNCMD, ht.pt.x, ht.pt.y, 0, hWnd, NULL);
					switch (MenuResult) {
					case IDM_MSGTREEMENU_NEWMESSAGE:
						dat->AddMessage();
						break;
					case IDM_MSGTREEMENU_NEWCATEGORY:
						dat->AddCategory();
						break;
					case IDM_MSGTREEMENU_RENAME:
						TreeView_EditLabel(hWnd, tvi.hItem);
						break;
					case IDM_MSGTREEMENU_DELETE:
						dat->DeleteSelectedItem();
						break;
					case IDR_MSGTREEMENU_DEF_ONL:
					case IDR_MSGTREEMENU_DEF_AWAY:
					case IDR_MSGTREEMENU_DEF_NA:
					case IDR_MSGTREEMENU_DEF_OCC:
					case IDR_MSGTREEMENU_DEF_DND:
					case IDR_MSGTREEMENU_DEF_FFC:
					case IDR_MSGTREEMENU_DEF_INV:
					case IDR_MSGTREEMENU_DEF_OTP:
					case IDR_MSGTREEMENU_DEF_OTL:
						for (int i = 0; i < _countof(SettingsList); i++) {
							if (SettingsList[i].MenuItemID == MenuResult) {
								dat->SetDefMsg(SettingsList[i].Status, tvi.lParam);
								break;
							}
						}
					}
					DestroyMenu(hMenu);
					return 0;
				}
			}
		}
		break;

	case WM_MEASUREITEM:
		{
			LPMEASUREITEMSTRUCT lpmi = (LPMEASUREITEMSTRUCT)lParam;
			if (lpmi->CtlType == ODT_MENU) {
				lpmi->itemWidth = max(0, GetSystemMetrics(SM_CXSMICON) - GetSystemMetrics(SM_CXMENUCHECK) + 4);
				lpmi->itemHeight = GetSystemMetrics(SM_CYSMICON) + 2;
				return true;
			}
		}
		break;
	
	case WM_DRAWITEM:
		{
			LPDRAWITEMSTRUCT dis = (LPDRAWITEMSTRUCT)lParam;
			if (dis->CtlType == ODT_MENU) {
				ImageList_DrawEx(dat->hImageList, dis->itemData, dis->hDC, 2, (dis->rcItem.bottom + dis->rcItem.top - GetSystemMetrics(SM_CYSMICON)) / 2 + 1, 0, 0, GetSysColor(COLOR_WINDOW), CLR_NONE, ILD_NORMAL);
				return true;
			}
		} 
		break;
	}
	return CallWindowProc(dat->OrigTreeViewProc, hWnd, Msg, wParam, lParam);
}