int StatusChanged(WPARAM wParam, LPARAM lParam) { LogMessage("MS_CLIST_SETSTATUSMODE called. szProto=%s, Status=%d", lParam ? (char*)lParam : "NULL", wParam); g_ProtoStates[(char*)lParam].Status = wParam; // let's check if we handle this thingy if (g_fNoProcessing) { // we're told not to do anything g_fNoProcessing = false; // take it off return 0; } DWORD Flag1 = 0; DWORD Flag3 = 0; if (lParam) { Flag1 = CallProtoService((char*)lParam, PS_GETCAPS, PFLAGNUM_1, 0); Flag3 = CallProtoService((char*)lParam, PS_GETCAPS, PFLAGNUM_3, 0); } else { PROTOACCOUNT **accs; int numAccs = 0; Proto_EnumAccounts(&numAccs, &accs); for (int i = 0; i < numAccs; i++) { Flag1 |= CallProtoService(accs[i]->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0); Flag3 |= CallProtoService(accs[i]->szModuleName, PS_GETCAPS, PFLAGNUM_3, 0); } } if (!(Flag1 & PF1_MODEMSGSEND || Flag3 & Proto_Status2Flag(wParam) || (Flag1 & PF1_IM) == PF1_IM)) return 0; // there are no protocols with changed status that support autoreply or away messages for this status if (g_SetAwayMsgPage.GetWnd()) { SetForegroundWindow(g_SetAwayMsgPage.GetWnd()); return 0; } int i; for (i = _countof(StatusModeList) - 1; i >= 0; i--) if (wParam == StatusModeList[i].Status) break; if (i < 0) return 0; BOOL bScreenSaverRunning; SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &bScreenSaverRunning, 0); if (bScreenSaverRunning || g_MoreOptPage.GetDBValueCopy(StatusModeList[i].DontShowDialogCtlID)) { CProtoSettings((char*)lParam).SetMsgFormat(SMF_PERSONAL, CProtoSettings((char*)lParam).GetMsgFormat(GMF_LASTORDEFAULT)); ChangeProtoMessages((char*)lParam, wParam, TCString()); } else { SetAwayMsgData *dat = new SetAwayMsgData; memset(dat, 0, sizeof(SetAwayMsgData)); dat->szProtocol = (char*)lParam; dat->IsModeless = false; DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_SETAWAYMSG), NULL, SetAwayMsgDlgProc, (LPARAM)dat); } return 0; }
TCString CContactSettings::GetMsgFormat(int Flags, int *pOrder, char *szProtoOverride) // returns the requested message; sets Order to the order of the message in the message tree, if available; or to -1 otherwise. // szProtoOverride is needed only to get status message of the right protocol when the ICQ contact is on list, but not with the same // protocol on which it requests the message - this way we can still get contact details. { TCString Message = NULL; if (pOrder) *pOrder = -1; if (Flags & GMF_PERSONAL) // try getting personal message (it overrides global) Message = db_get_s(hContact, MOD_NAME, StatusToDBSetting(Status, DB_STATUSMSG, IDC_MOREOPTDLG_PERSTATUSPERSONAL), (TCHAR*)NULL); if (Flags & (GMF_LASTORDEFAULT | GMF_PROTOORGLOBAL | GMF_TEMPORARY) && Message.IsEmpty()) { char *szProto = szProtoOverride ? szProtoOverride : (hContact ? GetContactProto(hContact) : NULL); // we mustn't pass here by GMF_TEMPORARY flag, as otherwise we'll handle GMF_TEMPORARY | GMF_PERSONAL combination incorrectly, // which is supposed to get only per-contact messages, and at the same time also may be used with NULL contact to get the global status message if (Flags & (GMF_LASTORDEFAULT | GMF_PROTOORGLOBAL)) Message = CProtoSettings(szProto).GetMsgFormat(Flags, pOrder); else if (!hContact) { // handle global temporary message too if (g_ProtoStates[szProto].TempMsg.IsSet()) Message = g_ProtoStates[szProto].TempMsg; } } return Message; }
CProtoState::CStatus& CProtoState::CStatus::operator = (int Status) { _ASSERT(Status >= ID_STATUS_OFFLINE && Status <= ID_STATUS_OUTTOLUNCH); if (Status < ID_STATUS_OFFLINE || Status > ID_STATUS_OUTTOLUNCH) return *this; // ignore status change if the new status is unknown bool bModified = false; if (szProto) { if (this->Status != Status) { this->Status = Status; (*GrandParent)[szProto].AwaySince.Reset(); ResetSettingsOnStatusChange(szProto, true, Status); bModified = true; } if ((*GrandParent)[szProto].TempMsg.IsSet()) { (*GrandParent)[szProto].TempMsg.Unset(); bModified = true; } } else { // global status change int bStatusModified = false; for (int i = 0; i < GrandParent->GetSize(); i++) { CProtoState &State = (*GrandParent)[i]; if (!db_get_b(NULL, State.GetProto(), "LockMainStatus", 0)) { // if the protocol isn't locked if (State.Status != Status) { State.Status.Status = Status; // "Status.Status" - changing Status directly to prevent recursive calls to the function State.AwaySince.Reset(); bModified = true; bStatusModified = true; } if (State.TempMsg.IsSet()) { State.TempMsg.Unset(); bModified = true; } if (g_MoreOptPage.GetDBValueCopy(IDC_MOREOPTDLG_RESETPROTOMSGS)) CProtoSettings(State.szProto, Status).SetMsgFormat(SMF_PERSONAL, NULL); } } if (bStatusModified) ResetSettingsOnStatusChange(NULL, true, Status); } if (bModified && g_SetAwayMsgPage.GetWnd()) SendMessage(g_SetAwayMsgPage.GetWnd(), UM_SAM_PROTOSTATUSCHANGED, (WPARAM)(char*)szProto, 0); return *this; }
void CContactSettings::SetMsgFormat(int Flags, TCString Message) { if (Flags & SMF_PERSONAL) { // set a personal message for a contact. also it's used to set global status message (hContact = NULL). // if Message == NULL, then the function deletes the message. CString DBSetting(StatusToDBSetting(Status, DB_STATUSMSG, IDC_MOREOPTDLG_PERSTATUSPERSONAL)); if (g_AutoreplyOptPage.GetDBValueCopy(IDC_REPLYDLG_RESETCOUNTERWHENSAMEICON) && GetMsgFormat(SMF_PERSONAL) != (const TCHAR*)Message) ResetContactSettingsOnStatusChange(hContact); if (Message != NULL) db_set_ts(hContact, MOD_NAME, DBSetting, Message); else db_unset(hContact, MOD_NAME, DBSetting); } if (Flags & (SMF_LAST | SMF_TEMPORARY)) { _ASSERT(!hContact); CProtoSettings().SetMsgFormat(Flags & (SMF_LAST | SMF_TEMPORARY), Message); } }
// wParam = PROTOCOLSETTINGEX** protoSettings int CSStatusChange(WPARAM wParam, LPARAM lParam) // CommonStatus plugins (StartupStatus and AdvancedAutoAway) { PROTOCOLSETTINGEX **ps = *(PROTOCOLSETTINGEX***)wParam; if (!ps) return -1; LogMessage("ME_CS_STATUSCHANGEEX event:"); for (int i = 0; i < lParam; i++) { LogMessage("%d: cbSize=%d, szProto=%s, status=%d, lastStatus=%d, szMsg:", i + 1, ps[i]->cbSize, ps[i]->szName ? (char*)ps[i]->szName : "NULL", ps[i]->status, ps[i]->lastStatus, ps[i]->szMsg ? ps[i]->szMsg : _T("NULL")); if (ps[i]->status != ID_STATUS_DISABLED) { if (ps[i]->status != ID_STATUS_CURRENT) g_ProtoStates[ps[i]->szName].Status = (ps[i]->status == ID_STATUS_LAST) ? ps[i]->lastStatus : ps[i]->status; CProtoSettings(ps[i]->szName).SetMsgFormat(SMF_TEMPORARY, ps[i]->szMsg ? ps[i]->szMsg : CProtoSettings(ps[i]->szName).GetMsgFormat(GMF_LASTORDEFAULT)); } } return 0; }
TCString GetDynamicStatMsg(MCONTACT hContact, char *szProto, DWORD UIN, int iStatus) { // hContact is the contact that requests the status message if (hContact != INVALID_CONTACT_ID) VarParseData.Message = CContactSettings(iStatus, hContact).GetMsgFormat(GMF_ANYCURRENT, NULL, szProto); else // contact is unknown VarParseData.Message = CProtoSettings(szProto, iStatus).GetMsgFormat(iStatus ? GMF_LASTORDEFAULT : GMF_ANYCURRENT); TCString sTime; VarParseData.szProto = szProto ? szProto : ((hContact && hContact != INVALID_CONTACT_ID) ? GetContactProto(hContact) : NULL); VarParseData.UIN = UIN; VarParseData.Flags = 0; if (ServiceExists(MS_VARS_FORMATSTRING) && !g_SetAwayMsgPage.GetDBValueCopy(IDS_SAWAYMSG_DISABLEVARIABLES)) { TCHAR *szResult = variables_parse(VarParseData.Message, 0, hContact); if (szResult) { VarParseData.Message = szResult; mir_free(szResult); } } return VarParseData.Message = VarParseData.Message.Left(AWAY_MSGDATA_MAX); }
// 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)); } }