diff options
Diffstat (limited to 'plugins/StatusManager/src/StartupStatus/startupstatus.cpp')
-rw-r--r-- | plugins/StatusManager/src/StartupStatus/startupstatus.cpp | 524 |
1 files changed, 524 insertions, 0 deletions
diff --git a/plugins/StatusManager/src/StartupStatus/startupstatus.cpp b/plugins/StatusManager/src/StartupStatus/startupstatus.cpp new file mode 100644 index 0000000000..2af4dc146d --- /dev/null +++ b/plugins/StatusManager/src/StartupStatus/startupstatus.cpp @@ -0,0 +1,524 @@ +/* + 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" + +static UINT_PTR setStatusTimerId = 0; + +static TSettingsList startupSettings(10, SSCompareSettings); + +TSSSetting::TSSSetting(PROTOACCOUNT *pa) +{ + cbSize = sizeof(PROTOCOLSETTINGEX); + szName = pa->szModuleName; + tszAccName = pa->tszAccountName; + status = lastStatus = CallProtoService(pa->szModuleName, PS_GETSTATUS, 0, 0); + szMsg = NULL; +} + +TSSSetting::TSSSetting(int profile, PROTOACCOUNT *pa) +{ + cbSize = sizeof(PROTOCOLSETTINGEX); + + // copy name + szName = pa->szModuleName; + tszAccName = pa->tszAccountName; + + // load status + char setting[80]; + mir_snprintf(setting, "%d_%s", profile, pa->szModuleName); + int iStatus = db_get_w(NULL, SSMODULENAME, setting, 0); + if (iStatus < MIN_STATUS || iStatus > MAX_STATUS) + iStatus = DEFAULT_STATUS; + status = iStatus; + + // load last status + mir_snprintf(setting, "%s%s", PREFIX_LAST, szName); + iStatus = db_get_w(NULL, SSMODULENAME, setting, 0); + if (iStatus < MIN_STATUS || iStatus > MAX_STATUS) + iStatus = DEFAULT_STATUS; + lastStatus = iStatus; + + szMsg = GetStatusMessage(profile, szName); + if (szMsg) + szMsg = wcsdup(szMsg); +} + +TSSSetting::~TSSSetting() +{ + free(szMsg); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +static HANDLE hProtoAckHook, hCSStatusChangeHook, hStatusChangeHook; + +static HWND hMessageWindow; + +static BYTE showDialogOnStartup = 0; + +///////////////////////////////////////////////////////////////////////////////////////// +// command line options + +static PROTOCOLSETTINGEX* IsValidProtocol(TSettingsList& protoSettings, char* protoName) +{ + for (int i = 0; i < protoSettings.getCount(); i++) + if (!strncmp(protoSettings[i].szName, protoName, mir_strlen(protoSettings[i].szName))) + return &protoSettings[i]; + + return NULL; +} + +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("onthephone", statusDesc, 10)) + return ID_STATUS_ONTHEPHONE; + if (!strncmp("outtolunch", statusDesc, 10)) + return ID_STATUS_OUTTOLUNCH; + if (!strncmp("last", statusDesc, 4)) + return ID_STATUS_LAST; + + return 0; +} + +static void ProcessCommandLineOptions(TSettingsList& 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 != NULL) { + 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->status = status; + } + } +} + +static void SetLastStatusMessages(TSettingsList &ps) +{ + for (int i = 0; i < ps.getCount(); i++) { + if (ps[i].status != ID_STATUS_LAST) + continue; + + char dbSetting[128]; + mir_snprintf(dbSetting, "%s%s", PREFIX_LASTMSG, ps[i].szName); + + DBVARIANT dbv; + if (ps[i].szMsg == NULL && !db_get_ws(NULL, SSMODULENAME, dbSetting, &dbv)) { + ps[i].szMsg = wcsdup(dbv.ptszVal); // remember this won't be freed + db_free(&dbv); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Account control event + +int OnSSAccChanged(WPARAM wParam, LPARAM lParam) +{ + PROTOACCOUNT *pa = (PROTOACCOUNT*)lParam; + switch (wParam) { + case PRAC_ADDED: + startupSettings.insert(new TSSSetting(-1, pa)); + break; + + case PRAC_REMOVED: + for (int i = 0; i < startupSettings.getCount(); i++) { + if (!mir_strcmp(startupSettings[i].szName, pa->szModuleName)) { + startupSettings.remove(i); + break; + } + } + break; + } + + return 0; +} + +// '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 (!db_get_b(NULL, SSMODULENAME, SETTING_OVERRIDE, 1) || startupSettings.getCount() == 0) + return 0; + + for (int i = 0; i < startupSettings.getCount(); i++) { + if (!mir_strcmp(ack->szModule, startupSettings[i].szName)) { + startupSettings[i].szName = ""; + log_debugA("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 (!db_get_b(NULL, SSMODULENAME, SETTING_OVERRIDE, 1) || startupSettings.getCount() == 0) + return 0; + + char *szProto = (char *)lParam; + if (szProto == NULL) { // global status change + for (int i = 0; i < startupSettings.getCount(); i++) { + startupSettings[i].szName = ""; + log_debugA("StartupStatus: all protos overridden by ME_CLIST_STATUSMODECHANGE, status will not be set"); + } + } + else { + for (int i = 0; i < startupSettings.getCount(); i++) { + if (!mir_strcmp(startupSettings[i].szName, szProto)) { + startupSettings[i].szName = ""; + log_debugA("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 (!db_get_b(NULL, SSMODULENAME, SETTING_OVERRIDE, 1) || startupSettings.getCount() == 0) + return 0; + + if (wParam != 0) { + PROTOCOLSETTINGEX** ps = *(PROTOCOLSETTINGEX***)wParam; + if (ps == NULL) + return -1; + + for (int i = 0; i < startupSettings.getCount(); i++) { + for (int j = 0; j < startupSettings.getCount(); j++) { + if (ps[i]->szName == NULL || startupSettings[j].szName == NULL) + continue; + + if (!mir_strcmp(ps[i]->szName, startupSettings[j].szName)) { + log_debugA("StartupStatus: %s overridden by MS_CS_SETSTATUSEX, status will not be set", ps[i]->szName); + // use a hack to disable this proto + startupSettings[j].szName = ""; + } + } + } + } + + return 0; +} + +static void CALLBACK SetStatusTimed(HWND, UINT, UINT_PTR, DWORD) +{ + KillTimer(NULL, setStatusTimerId); + UnhookEvent(hProtoAckHook); + UnhookEvent(hCSStatusChangeHook); + UnhookEvent(hStatusChangeHook); + CallService(MS_CS_SETSTATUSEX, (WPARAM)&startupSettings, 0); +} + +static int OnOkToExit(WPARAM, LPARAM) +{ + // save last protocolstatus + int count; + PROTOACCOUNT** protos; + Proto_EnumAccounts(&count, &protos); + + for (int i = 0; i < count; i++) { + PROTOACCOUNT *pa = protos[i]; + if (!IsSuitableProto(pa)) + continue; + + if (!Proto_GetAccount(pa->szModuleName)) + continue; + + char lastName[128], lastMsg[128]; + mir_snprintf(lastName, "%s%s", PREFIX_LAST, pa->szModuleName); + db_set_w(NULL, SSMODULENAME, lastName, (WORD)CallProtoService(pa->szModuleName, PS_GETSTATUS, 0, 0)); + mir_snprintf(lastMsg, "%s%s", PREFIX_LASTMSG, pa->szModuleName); + db_unset(NULL, SSMODULENAME, lastMsg); + + if (!(CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_1, 0) & PF1_MODEMSGSEND & ~PF1_INDIVMODEMSG)) + continue; + + int status = CallProtoService(pa->szModuleName, PS_GETSTATUS, 0, 0); + if (!(CallProtoService(pa->szModuleName, PS_GETCAPS, PFLAGNUM_3, 0) & Proto_Status2Flag(status))) + 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 == NULL) { + npi.status = 0; + npi.szProto = NULL; + CallService(MS_NAS_GETSTATE, (WPARAM)&npi, 1); + } + if (npi.szMsg != NULL) { + db_set_ws(NULL, SSMODULENAME, lastMsg, npi.tszMsg); + mir_free(npi.tszMsg); + } + } + } + + if (db_get_b(NULL, SSMODULENAME, SETTING_SETPROFILE, 1) || db_get_b(NULL, SSMODULENAME, SETTING_OFFLINECLOSE, 0)) { + if (ServiceExists(MS_CLIST_SETSTATUSMODE)) + CallService(MS_CLIST_SETSTATUSMODE, ID_STATUS_OFFLINE, 0); + else + log_debugA("StartupStatus: MS_CLIST_SETSTATUSMODE not available!"); + } + + return 0; +} + +static int OnShutdown(WPARAM, LPARAM) +{ + DeinitProfilesModule(); + UnloadProfileModule(); + + // set windowstate and docked for next startup + if (db_get_b(NULL, SSMODULENAME, SETTING_SETWINSTATE, 0)) { + int state = db_get_b(NULL, SSMODULENAME, SETTING_WINSTATE, SETTING_STATE_NORMAL); + HWND hClist = pcli->hwndContactList; + BOOL isHidden = !IsWindowVisible(hClist); + switch (state) { + case SETTING_STATE_HIDDEN: + // try to use services where possible + if (!isHidden) + pcli->pfnShowHide(); + break; + + case SETTING_STATE_MINIMIZED: + if (!db_get_b(NULL, 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) + pcli->pfnShowHide(); + break; + } + } + + // hangup + if (db_get_b(NULL, SSMODULENAME, SETTING_AUTOHANGUP, 0)) + InternetAutodialHangup(0); + + int state = db_get_b(NULL, SSMODULENAME, SETTING_WINSTATE, SETTING_STATE_NORMAL); + // set windowstate and docked for next startup + if (db_get_b(NULL, SSMODULENAME, SETTING_SETWINSTATE, 0)) + db_set_b(NULL, MODULE_CLIST, SETTING_WINSTATE, (BYTE)state); + + if (hMessageWindow) + DestroyWindow(hMessageWindow); + + startupSettings.destroy(); + return 0; +} + +/* Window proc for poweroff event */ +static DWORD CALLBACK MessageWndProc(HWND, UINT msg, WPARAM wParam, LPARAM) +{ + switch (msg) { + case WM_ENDSESSION: + log_debugA("WM_ENDSESSION"); + if (wParam) { + log_debugA("WM_ENDSESSION: calling exit"); + OnShutdown(0, 0); + log_debugA("WM_ENDSESSION: exit called"); + } + break; + } + + return TRUE; +} + +int SSModuleLoaded(WPARAM, LPARAM) +{ + protoList = (OBJLIST<PROTOCOLSETTINGEX>*)&startupSettings; + + InitProfileModule(); + + HookEvent(ME_PROTO_ACCLISTCHANGED, OnSSAccChanged); + HookEvent(ME_OPT_INITIALISE, StartupStatusOptionsInit); + + /* shutdown hook for normal shutdown */ + HookEvent(ME_SYSTEM_OKTOEXIT, OnOkToExit); + HookEvent(ME_SYSTEM_PRESHUTDOWN, OnShutdown); + /* message window for poweroff */ + hMessageWindow = CreateWindowEx(0, L"STATIC", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + SetWindowLongPtr(hMessageWindow, GWLP_WNDPROC, (LONG_PTR)MessageWndProc); + + GetProfile(-1, startupSettings); + + // override with cmdl + ProcessCommandLineOptions(startupSettings); + if (startupSettings.getCount() == 0) + return 0;// no protocols are loaded + + SetLastStatusMessages(startupSettings); + showDialogOnStartup = (showDialogOnStartup || db_get_b(NULL, SSMODULENAME, SETTING_SHOWDIALOG, 0)); + + // dial + if (showDialogOnStartup || db_get_b(NULL, SSMODULENAME, SETTING_SETPROFILE, 1)) + if (db_get_b(NULL, SSMODULENAME, SETTING_AUTODIAL, 0)) + InternetAutodial(0, NULL); + + // set the status! + if (showDialogOnStartup || db_get_b(NULL, SSMODULENAME, SETTING_SHOWDIALOG, 0)) + CallService(MS_CS_SHOWCONFIRMDLGEX, (WPARAM)&startupSettings, db_get_dw(NULL, SSMODULENAME, SETTING_DLGTIMEOUT, 5)); + else if (db_get_b(NULL, SSMODULENAME, SETTING_SETPROFILE, 1)) { + // set hooks for override + if (db_get_b(NULL, SSMODULENAME, SETTING_OVERRIDE, 1)) { + hProtoAckHook = HookEvent(ME_PROTO_ACK, ProcessProtoAck); + hCSStatusChangeHook = HookEvent(ME_CS_STATUSCHANGEEX, CSStatusChangeEx); + hStatusChangeHook = HookEvent(ME_CLIST_STATUSMODECHANGE, StatusChange); + } + setStatusTimerId = SetTimer(NULL, 0, db_get_dw(NULL, SSMODULENAME, SETTING_SETPROFILEDELAY, 500), SetStatusTimed); + } + + // win size and location + if (db_get_b(NULL, SSMODULENAME, SETTING_SETWINLOCATION, 0) || db_get_b(NULL, SSMODULENAME, SETTING_SETWINSIZE, 0)) { + HWND hClist = pcli->hwndContactList; + + // store in db + if (db_get_b(NULL, SSMODULENAME, SETTING_SETWINLOCATION, 0)) { + db_set_dw(NULL, MODULE_CLIST, SETTING_XPOS, db_get_dw(NULL, SSMODULENAME, SETTING_XPOS, 0)); + db_set_dw(NULL, MODULE_CLIST, SETTING_YPOS, db_get_dw(NULL, SSMODULENAME, SETTING_YPOS, 0)); + } + if (db_get_b(NULL, SSMODULENAME, SETTING_SETWINSIZE, 0)) { + db_set_dw(NULL, MODULE_CLIST, SETTING_WIDTH, db_get_dw(NULL, SSMODULENAME, SETTING_WIDTH, 0)); + if (!db_get_b(NULL, MODULE_CLUI, SETTING_AUTOSIZE, 0)) + db_set_dw(NULL, MODULE_CLIST, SETTING_HEIGHT, db_get_dw(NULL, SSMODULENAME, 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 (db_get_b(NULL, SSMODULENAME, SETTING_SETWINLOCATION, 0)) { + x = db_get_dw(NULL, SSMODULENAME, SETTING_XPOS, x); + y = db_get_dw(NULL, SSMODULENAME, SETTING_YPOS, y); + } + if (db_get_b(NULL, SSMODULENAME, SETTING_SETWINSIZE, 0)) { + width = db_get_dw(NULL, SSMODULENAME, SETTING_WIDTH, width); + if (!db_get_b(NULL, MODULE_CLUI, SETTING_AUTOSIZE, 0)) + height = db_get_dw(NULL, SSMODULENAME, SETTING_HEIGHT, height); + } + MoveWindow(hClist, x, y, width, height, TRUE); + } } } } + + return 0; +} + +HANDLE hSSModuleLoadedHook = NULL, + hGetProfileService, + hGetProfileCountService, + hGetProfileNameService; + +void StartupStatusLoad() +{ + hSSModuleLoadedHook = HookEvent(ME_SYSTEM_MODULESLOADED, SSModuleLoaded); + + if (db_get_b(NULL, SSMODULENAME, SETTING_SETPROFILE, 1) || + db_get_b(NULL, SSMODULENAME, SETTING_OFFLINECLOSE, 0)) + db_set_w(NULL, "CList", "Status", (WORD)ID_STATUS_OFFLINE); + + // docking + if (db_get_b(NULL, SSMODULENAME, SETTING_SETDOCKED, 0)) { + int docked = db_get_b(NULL, SSMODULENAME, SETTING_DOCKED, DOCKED_NONE); + if (docked == DOCKED_LEFT || docked == DOCKED_RIGHT) + docked = -docked; + + db_set_b(NULL, MODULE_CLIST, SETTING_DOCKED, (BYTE)docked); + } + + // Create service functions; the get functions are created here; they don't rely on commonstatus + hGetProfileService = CreateServiceFunction(MS_SS_GETPROFILE, SrvGetProfile); + hGetProfileCountService = CreateServiceFunction(MS_SS_GETPROFILECOUNT, GetProfileCount); + hGetProfileNameService = CreateServiceFunction(MS_SS_GETPROFILENAME, GetProfileName); + + LoadProfileModule(); +} + +void StartupStatusUnload() +{ + DeinitProfilesModule(); + UnloadProfileModule(); + + DestroyServiceFunction(hGetProfileService); + DestroyServiceFunction(hGetProfileCountService); + DestroyServiceFunction(hGetProfileNameService); + + UnhookEvent(hSSModuleLoadedHook); +} |