diff options
Diffstat (limited to 'plugins/StatusManager/src/ss_main.cpp')
-rw-r--r-- | plugins/StatusManager/src/ss_main.cpp | 455 |
1 files changed, 455 insertions, 0 deletions
diff --git a/plugins/StatusManager/src/ss_main.cpp b/plugins/StatusManager/src/ss_main.cpp new file mode 100644 index 0000000000..9b586ebece --- /dev/null +++ b/plugins/StatusManager/src/ss_main.cpp @@ -0,0 +1,455 @@ +/* + StartupStatus Plugin for Miranda-IM (www.miranda-im.org) + Copyright 2003-2006 P. Boon + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "stdafx.h" + +CFakePlugin SSPlugin(SSMODULENAME); + +static HANDLE hServices[3], hEvents[3]; +static UINT_PTR setStatusTimerId = 0; + +///////////////////////////////////////////////////////////////////////////////////////// + +static HANDLE hProtoAckHook, hCSStatusChangeHook, hStatusChangeHook; + +static HWND hMessageWindow; + +static BYTE showDialogOnStartup = 0; + +///////////////////////////////////////////////////////////////////////////////////////// +// command line options + +static PROTOCOLSETTINGEX* IsValidProtocol(TProtoSettings &protoSettings, const char *protoName) +{ + for (auto &it : protoSettings) + if (!it->ssDisabled && !strncmp(it->m_szName, protoName, mir_strlen(it->m_szName))) + return it; + + return nullptr; +} + +static int IsValidStatusDesc(char* statusDesc) +{ + if (!strncmp("away", statusDesc, 4)) + return ID_STATUS_AWAY; + if (!strncmp("na", statusDesc, 2)) + return ID_STATUS_NA; + if (!strncmp("dnd", statusDesc, 3)) + return ID_STATUS_DND; + if (!strncmp("occupied", statusDesc, 8)) + return ID_STATUS_OCCUPIED; + if (!strncmp("freechat", statusDesc, 8)) + return ID_STATUS_FREECHAT; + if (!strncmp("online", statusDesc, 6)) + return ID_STATUS_ONLINE; + if (!strncmp("offline", statusDesc, 7)) + return ID_STATUS_OFFLINE; + if (!strncmp("invisible", statusDesc, 9)) + return ID_STATUS_INVISIBLE; + if (!strncmp("last", statusDesc, 4)) + return ID_STATUS_LAST; + + return 0; +} + +static void ProcessCommandLineOptions(TProtoSettings &protoSettings) +{ + if (protoSettings.getCount() == 0) + return; + + char *cmdl = GetCommandLineA(); + while (*cmdl != '\0') { + while (*cmdl != '/') { + if (*cmdl == '\0') + return; + + cmdl++; + } + if (*cmdl == '\0') + return; + + cmdl++; + if (!strncmp(cmdl, "showdialog", 10)) { + showDialogOnStartup = TRUE; + continue; + } + char *protoName = cmdl; // first protocol ? + PROTOCOLSETTINGEX* protoSetting = IsValidProtocol(protoSettings, protoName); + if (protoSetting != nullptr) { + while (*cmdl != '=') { + if (*cmdl == '\0') + return; + + cmdl++; // skip to status + } + + if (*cmdl == '\0') + return; + + cmdl++; + char *statusDesc = cmdl; + int status = IsValidStatusDesc(statusDesc); + if (status != 0) + protoSetting->m_status = status; + } + } +} + +static void SetLastStatusMessages(TProtoSettings &ps) +{ + for (auto &it : ps) { + if (it->m_status != ID_STATUS_LAST) + continue; + + char dbSetting[128]; + mir_snprintf(dbSetting, "%s%s", PREFIX_LASTMSG, it->m_szName); + it->m_szMsg = SSPlugin.getWStringA(dbSetting); + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Account control event + +// 'allow override' +static int ProcessProtoAck(WPARAM, LPARAM lParam) +{ + // 'something' made a status change + ACKDATA *ack = (ACKDATA*)lParam; + if (ack->type != ACKTYPE_STATUS && ack->result != ACKRESULT_FAILED) + return 0; + + if (!SSPlugin.getByte(SETTING_OVERRIDE, 1) || protoList.getCount() == 0) + return 0; + + for (auto &it : protoList) { + if (!mir_strcmp(ack->szModule, it->m_szName)) { + it->ssDisabled = true; + log_debug(0, "StartupStatus: %s overridden by ME_PROTO_ACK, status will not be set", ack->szModule); + } + } + + return 0; +} + +static int StatusChange(WPARAM, LPARAM lParam) +{ + // change by menu + if (!SSPlugin.getByte(SETTING_OVERRIDE, 1) || protoList.getCount() == 0) + return 0; + + char *szProto = (char *)lParam; + if (szProto == nullptr) { // global status change + for (auto &it : protoList) { + it->ssDisabled = true; + log_debug(0, "StartupStatus: all protos overridden by ME_CLIST_STATUSMODECHANGE, status will not be set"); + } + } + else { + for (auto &it : protoList) { + if (!mir_strcmp(it->m_szName, szProto)) { + it->ssDisabled = true; + log_debug(0, "StartupStatus: %s overridden by ME_CLIST_STATUSMODECHANGE, status will not be set", szProto); + } + } + } + + return 0; +} + +static int CSStatusChangeEx(WPARAM wParam, LPARAM) +{ + // another status plugin made the change + if (!SSPlugin.getByte(SETTING_OVERRIDE, 1) || protoList.getCount() == 0) + return 0; + + if (wParam != 0) { + PROTOCOLSETTINGEX **ps = *(PROTOCOLSETTINGEX***)wParam; + if (ps == nullptr) + return -1; + + for (int i = 0; i < protoList.getCount(); i++) { + for (auto &it : protoList) { + if (ps[i]->m_szName == nullptr || it->m_szName == nullptr) + continue; + + if (!mir_strcmp(ps[i]->m_szName, it->m_szName)) { + log_debug(0, "StartupStatus: %s overridden by MS_CS_SETSTATUSEX, status will not be set", ps[i]->m_szName); + it->ssDisabled = true; + } + } + } + } + + return 0; +} + +static void CALLBACK SetStatusTimed(HWND, UINT, UINT_PTR, DWORD) +{ + KillTimer(nullptr, setStatusTimerId); + UnhookEvent(hProtoAckHook); + UnhookEvent(hCSStatusChangeHook); + UnhookEvent(hStatusChangeHook); + + TProtoSettings ps(protoList); + for (auto &it : ps) + if (it->ssDisabled) + it->m_status = ID_STATUS_DISABLED; + + SetStatusEx(ps); +} + +static int OnOkToExit(WPARAM, LPARAM) +{ + // save last protocolstatus + for (auto &pa : Accounts()) { + if (!IsSuitableProto(pa)) + continue; + + if (!Proto_GetAccount(pa->szModuleName)) + continue; + + char lastName[128], lastMsg[128]; + mir_snprintf(lastName, "%s%s", PREFIX_LAST, pa->szModuleName); + SSPlugin.setWord(lastName, pa->iRealStatus); + mir_snprintf(lastMsg, "%s%s", PREFIX_LASTMSG, pa->szModuleName); + SSPlugin.delSetting(lastMsg); + + if (!(CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND & ~PF1_INDIVMODEMSG)) + continue; + + if (!(CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_3, 0) & Proto_Status2Flag(pa->iRealStatus))) + continue; + + // NewAwaySys + if (ServiceExists(MS_NAS_GETSTATE)) { + NAS_PROTOINFO npi = { sizeof(npi) }; + npi.szProto = pa->szModuleName; + CallService(MS_NAS_GETSTATE, (WPARAM)&npi, 1); + if (npi.szMsg == nullptr) { + npi.status = 0; + npi.szProto = nullptr; + CallService(MS_NAS_GETSTATE, (WPARAM)&npi, 1); + } + if (npi.szMsg != nullptr) { + SSPlugin.setWString(lastMsg, npi.tszMsg); + mir_free(npi.tszMsg); + } + } + } + + if (SSPlugin.getByte(SETTING_SETPROFILE, 1) || SSPlugin.getByte(SETTING_OFFLINECLOSE, 0)) + Clist_SetStatusMode(ID_STATUS_OFFLINE); + + return 0; +} + +static int OnShutdown(WPARAM, LPARAM) +{ + // set windowstate and docked for next startup + if (SSPlugin.getByte(SETTING_SETWINSTATE, 0)) { + int state = SSPlugin.getByte(SETTING_WINSTATE, SETTING_STATE_NORMAL); + HWND hClist = g_clistApi.hwndContactList; + BOOL isHidden = !IsWindowVisible(hClist); + switch (state) { + case SETTING_STATE_HIDDEN: + // try to use services where possible + if (!isHidden) + g_clistApi.pfnShowHide(); + break; + + case SETTING_STATE_MINIMIZED: + if (!db_get_b(0, MODULE_CLIST, SETTING_TOOLWINDOW, 0)) + ShowWindow(hClist, SW_SHOWMINIMIZED); + break; + + case SETTING_STATE_NORMAL: + // try to use services where possible (that's what they're for) + if (isHidden) + g_clistApi.pfnShowHide(); + break; + } + } + + // hangup + if (SSPlugin.getByte(SETTING_AUTOHANGUP, 0)) + InternetAutodialHangup(0); + + int state = SSPlugin.getByte(SETTING_WINSTATE, SETTING_STATE_NORMAL); + // set windowstate and docked for next startup + if (SSPlugin.getByte(SETTING_SETWINSTATE, 0)) + db_set_b(0, MODULE_CLIST, SETTING_WINSTATE, (BYTE)state); + + if (hMessageWindow) + DestroyWindow(hMessageWindow); + + protoList.destroy(); + return 0; +} + +/* Window proc for poweroff event */ +static DWORD CALLBACK MessageWndProc(HWND, UINT msg, WPARAM wParam, LPARAM) +{ + switch (msg) { + case WM_ENDSESSION: + log_debug(0, "WM_ENDSESSION"); + if (wParam) { + log_debug(0, "WM_ENDSESSION: calling exit"); + OnShutdown(0, 0); + log_debug(0, "WM_ENDSESSION: exit called"); + } + break; + } + + return TRUE; +} + +int SSModuleLoaded(WPARAM, LPARAM) +{ + InitProfileModule(); + + hEvents[0] = HookEvent(ME_OPT_INITIALISE, StartupStatusOptionsInit); + + /* shutdown hook for normal shutdown */ + hEvents[1] = HookEvent(ME_SYSTEM_OKTOEXIT, OnOkToExit); + hEvents[2] = HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown); + /* message window for poweroff */ + hMessageWindow = CreateWindowEx(0, L"STATIC", nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr); + SetWindowLongPtr(hMessageWindow, GWLP_WNDPROC, (LONG_PTR)MessageWndProc); + + GetProfile(-1, protoList); + + // override with cmdl + ProcessCommandLineOptions(protoList); + if (protoList.getCount() == 0) + return 0;// no protocols are loaded + + SetLastStatusMessages(protoList); + showDialogOnStartup = (showDialogOnStartup || SSPlugin.getByte(SETTING_SHOWDIALOG, 0)); + + // dial + if (showDialogOnStartup || SSPlugin.getByte(SETTING_SETPROFILE, 1)) + if (SSPlugin.getByte(SETTING_AUTODIAL, 0)) + InternetAutodial(0, nullptr); + + // set the status! + if (showDialogOnStartup || SSPlugin.getByte(SETTING_SHOWDIALOG, 0)) + ShowConfirmDialogEx((TProtoSettings*)&protoList, SSPlugin.getDword(SETTING_DLGTIMEOUT, 5)); + else if (SSPlugin.getByte(SETTING_SETPROFILE, 1)) { + // set hooks for override + if (SSPlugin.getByte(SETTING_OVERRIDE, 1)) { + hProtoAckHook = HookEvent(ME_PROTO_ACK, ProcessProtoAck); + hCSStatusChangeHook = HookEvent(ME_CS_STATUSCHANGEEX, CSStatusChangeEx); + hStatusChangeHook = HookEvent(ME_CLIST_STATUSMODECHANGE, StatusChange); + } + setStatusTimerId = SetTimer(nullptr, 0, SSPlugin.getDword(SETTING_SETPROFILEDELAY, 500), SetStatusTimed); + } + + // win size and location + if (SSPlugin.getByte(SETTING_SETWINLOCATION, 0) || SSPlugin.getByte(SETTING_SETWINSIZE, 0)) { + HWND hClist = g_clistApi.hwndContactList; + + // store in db + if (SSPlugin.getByte(SETTING_SETWINLOCATION, 0)) { + db_set_dw(0, MODULE_CLIST, SETTING_XPOS, SSPlugin.getDword(SETTING_XPOS, 0)); + db_set_dw(0, MODULE_CLIST, SETTING_YPOS, SSPlugin.getDword(SETTING_YPOS, 0)); + } + if (SSPlugin.getByte(SETTING_SETWINSIZE, 0)) { + db_set_dw(0, MODULE_CLIST, SETTING_WIDTH, SSPlugin.getDword(SETTING_WIDTH, 0)); + if (!db_get_b(0, MODULE_CLUI, SETTING_AUTOSIZE, 0)) + db_set_dw(0, MODULE_CLIST, SETTING_HEIGHT, SSPlugin.getDword(SETTING_HEIGHT, 0)); + } + + WINDOWPLACEMENT wndpl = { sizeof(wndpl) }; + if (GetWindowPlacement(hClist, &wndpl)) { + if (wndpl.showCmd == SW_SHOWNORMAL && !Clist_IsDocked()) { + RECT rc; + if (GetWindowRect(hClist, &rc)) { + int x = rc.left; + int y = rc.top; + int width = rc.right - rc.left; + int height = rc.bottom - rc.top; + if (SSPlugin.getByte(SETTING_SETWINLOCATION, 0)) { + x = SSPlugin.getDword(SETTING_XPOS, x); + y = SSPlugin.getDword(SETTING_YPOS, y); + } + if (SSPlugin.getByte(SETTING_SETWINSIZE, 0)) { + width = SSPlugin.getDword(SETTING_WIDTH, width); + if (!db_get_b(0, MODULE_CLUI, SETTING_AUTOSIZE, 0)) + height = SSPlugin.getDword(SETTING_HEIGHT, height); + } + MoveWindow(hClist, x, y, width, height, TRUE); + } + } + } + } + + return 0; +} + +static INT_PTR SrvGetProfile(WPARAM wParam, LPARAM lParam) +{ + return GetProfile((int)wParam, *(TProtoSettings*)lParam); +} + +void StartupStatusLoad() +{ + if (g_bMirandaLoaded) + SSModuleLoaded(0, 0); + else + HookEvent(ME_SYSTEM_MODULESLOADED, SSModuleLoaded); + + if (SSPlugin.getByte(SETTING_SETPROFILE, 1) || SSPlugin.getByte(SETTING_OFFLINECLOSE, 0)) + db_set_w(0, "CList", "Status", (WORD)ID_STATUS_OFFLINE); + + // docking + if (SSPlugin.getByte(SETTING_SETDOCKED, 0)) { + int docked = SSPlugin.getByte(SETTING_DOCKED, DOCKED_NONE); + if (docked == DOCKED_LEFT || docked == DOCKED_RIGHT) + docked = -docked; + + db_set_b(0, MODULE_CLIST, SETTING_DOCKED, (BYTE)docked); + } + + // Create service functions; the get functions are created here; they don't rely on commonstatus + hServices[0] = CreateServiceFunction(MS_SS_GETPROFILE, SrvGetProfile); + hServices[1] = CreateServiceFunction(MS_SS_GETPROFILECOUNT, GetProfileCount); + hServices[2] = CreateServiceFunction(MS_SS_GETPROFILENAME, GetProfileName); + + LoadProfileModule(); +} + +void StartupStatusUnload() +{ + if (g_bMirandaLoaded) + OnShutdown(0, 0); + + KillModuleOptions(&SSPlugin); + + for (auto &it : hServices) { + DestroyServiceFunction(it); + it = nullptr; + } + + for (auto &it : hEvents) { + UnhookEvent(it); + it = nullptr; + } + + DeinitProfilesModule(); +} |