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; } } }
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]; }
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; }
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); } }
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]; }
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; }
// 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; }
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)); } }
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); }
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); }