From 938aaa96f63e6cab2803eab57f67e93f5c352df0 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Tue, 14 Jan 2014 15:06:48 +0000 Subject: kernel chat's engine, part 1 (compiles ok, doesn't work at all, not linked to the core) git-svn-id: http://svn.miranda-ng.org/main/trunk@7645 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- src/modules/chat/chat.h | 93 +++ src/modules/chat/chat_opts.cpp | 370 +++++++++ src/modules/chat/chat_svc.cpp | 749 ++++++++++++++++++ src/modules/chat/clist.cpp | 293 +++++++ src/modules/chat/log.cpp | 594 +++++++++++++++ src/modules/chat/manager.cpp | 1639 ++++++++++++++++++++++++++++++++++++++++ src/modules/chat/tools.cpp | 761 +++++++++++++++++++ 7 files changed, 4499 insertions(+) create mode 100644 src/modules/chat/chat.h create mode 100644 src/modules/chat/chat_opts.cpp create mode 100644 src/modules/chat/chat_svc.cpp create mode 100644 src/modules/chat/clist.cpp create mode 100644 src/modules/chat/log.cpp create mode 100644 src/modules/chat/manager.cpp create mode 100644 src/modules/chat/tools.cpp (limited to 'src/modules') diff --git a/src/modules/chat/chat.h b/src/modules/chat/chat.h new file mode 100644 index 0000000000..5fe53a1a25 --- /dev/null +++ b/src/modules/chat/chat.h @@ -0,0 +1,93 @@ +/* + +Chat module plugin for Miranda IM + +Copyright (C) 2003 Jörgen Persson + +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 +#include +#include +#include + +// special service for tweaking performance +#define MS_GC_GETEVENTPTR "GChat/GetNewEventPtr" +typedef INT_PTR(*GETEVENTFUNC)(WPARAM wParam, LPARAM lParam); +struct GCPTRS +{ + GETEVENTFUNC pfnAddEvent; +}; + +extern HGENMENU hJoinMenuItem, hLeaveMenuItem; +extern HANDLE hChatSendEvent; +extern BOOL PopupInstalled; + +//log.c +void LoadMsgLogBitmaps(void); +void FreeMsgLogBitmaps(void); +void ValidateFilename (TCHAR * filename); +TCHAR* MakeTimeStamp(TCHAR* pszStamp, time_t time); +char* Log_CreateRtfHeader(MODULEINFO * mi); + +//clist.c +HANDLE CList_AddRoom(const char* pszModule, const TCHAR* pszRoom, const TCHAR* pszDisplayName, int iType); +BOOL CList_SetOffline(HANDLE hContact, BOOL bHide); +BOOL CList_SetAllOffline(BOOL bHide, const char *pszModule); +int CList_RoomDoubleclicked(WPARAM wParam,LPARAM lParam); +INT_PTR CList_EventDoubleclicked(WPARAM wParam,LPARAM lParam); +INT_PTR CList_JoinChat(WPARAM wParam, LPARAM lParam); +INT_PTR CList_LeaveChat(WPARAM wParam, LPARAM lParam); +int CList_PrebuildContactMenu(WPARAM wParam, LPARAM lParam); +INT_PTR CList_PrebuildContactMenuSvc(WPARAM wParam, LPARAM lParam); +BOOL CList_AddEvent(HANDLE hContact, HICON hIcon, HANDLE hEvent, int type, TCHAR* fmt, ... ) ; +HANDLE CList_FindRoom (const char* pszModule, const TCHAR* pszRoom) ; +int WCCmp(TCHAR* wild, TCHAR*string); + +// options.c +int OptionsInit(void); +int OptionsUnInit(void); +void LoadMsgDlgFont(int i, LOGFONT * lf, COLORREF * colour); +void LoadGlobalSettings(void); +HICON LoadIconEx(char* pszIcoLibName, BOOL big); +void LoadLogFonts(void); + +// services.c +void HookEvents(void); +void UnhookEvents(void); +void CreateServiceFunctions(void); +void CreateHookableEvents(void); +void DestroyHookableEvents(void); +void TabsInit(void); +void ShowRoom(SESSION_INFO *si, WPARAM wp, BOOL bSetForeground); + +//tools.c +int GetTextPixelSize(TCHAR* pszText, HFONT hFont, BOOL bWidth); +TCHAR* RemoveFormatting(const TCHAR* pszText); +BOOL DoSoundsFlashPopupTrayStuff(SESSION_INFO *si, GCEVENT *gce, BOOL bHighlight, int bManyFix); +int GetColorIndex(const char* pszModule, COLORREF cr); +void CheckColorsInModule(const char* pszModule); +const TCHAR* my_strstri(const TCHAR* s1, const TCHAR* s2) ; +int GetRichTextLength(HWND hwnd); +BOOL IsHighlighted(SESSION_INFO *si, const TCHAR* pszText); +UINT CreateGCMenu(HWND hwndDlg, HMENU *hMenu, int iIndex, POINT pt, SESSION_INFO *si, TCHAR* pszUID, TCHAR* pszWordText); +void DestroyGCMenu(HMENU *hMenu, int iIndex); +BOOL DoEventHookAsync(HWND hwnd, const TCHAR *pszID, const char* pszModule, int iType, TCHAR* pszUID, TCHAR* pszText, DWORD dwItem); +BOOL DoEventHook(const TCHAR *pszID, const char* pszModule, int iType, const TCHAR* pszUID, const TCHAR* pszText, DWORD dwItem); +BOOL IsEventSupported(int eventType); +BOOL LogToFile(SESSION_INFO *si, GCEVENT *gce); + +#pragma comment(lib,"comctl32.lib") diff --git a/src/modules/chat/chat_opts.cpp b/src/modules/chat/chat_opts.cpp new file mode 100644 index 0000000000..a2658f3a8f --- /dev/null +++ b/src/modules/chat/chat_opts.cpp @@ -0,0 +1,370 @@ +/* +Chat module plugin for Miranda IM + +Copyright (C) 2003 Jörgen Persson + +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 "..\..\core\commonheaders.h" + +#include "chat.h" + +#include + +extern SESSION_INFO g_TabSession; + +HANDLE g_hOptions = NULL; + +#define FONTF_BOLD 1 +#define FONTF_ITALIC 2 +struct FontOptionsList +{ + const TCHAR* szDescr; + COLORREF defColour; + const TCHAR* szDefFace; + BYTE defCharset, defStyle; + char defSize; + COLORREF colour; + const TCHAR* szFace; + BYTE charset, style; + char size; +} + +//remeber to put these in the Translate( ) template file too +static const fontOptionsList[] = { + { LPGENT("Timestamp"), RGB(50, 50, 240), _T("Terminal"), DEFAULT_CHARSET, 0, -8}, + { LPGENT("Others nicknames"), RGB(0, 0, 0), _T("Verdana"), DEFAULT_CHARSET, FONTF_BOLD, -12}, + { LPGENT("Your nickname"), RGB(0, 0, 0), _T("Verdana"), DEFAULT_CHARSET, FONTF_BOLD, -12}, + { LPGENT("User has joined"), RGB(90, 160, 90), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("User has left"), RGB(160, 160, 90), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("User has disconnected"), RGB(160, 90, 90), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("User kicked ..."), RGB(100, 100, 100), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("User is now known as ..."), RGB(90, 90, 160), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("Notice from user"), RGB(160, 130, 60), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("Incoming message"), RGB(90, 90, 90), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("Outgoing message"), RGB(90, 90, 90), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("The topic is ..."), RGB(70, 70, 160), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("Information messages"), RGB(130, 130, 195), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("User enables status for ..."), RGB(70, 150, 70), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("User disables status for ..."), RGB(150, 70, 70), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("Action message"), RGB(160, 90, 160), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("Highlighted message"), RGB(180, 150, 80), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("Message typing area"), RGB(0, 0, 40), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("User list members (online)"), RGB(0,0, 0), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, + { LPGENT("User list members (away)"), RGB(170, 170, 170), _T("Verdana"), DEFAULT_CHARSET, 0, -12}, +}; + +const int msgDlgFontCount = SIZEOF(fontOptionsList); + +void LoadLogFonts(void) +{ + for (int i = 0; i < OPTIONS_FONTCOUNT; i++) + LoadMsgDlgFont(i, &ci.aFonts[i].lf, &ci.aFonts[i].color); +} + +void LoadMsgDlgFont(int i, LOGFONT* lf, COLORREF* colour) +{ + char str[32]; + int style; + DBVARIANT dbv; + + if (colour) { + mir_snprintf(str, SIZEOF(str), "Font%dCol", i); + *colour = db_get_dw(NULL, "ChatFonts", str, fontOptionsList[i].defColour); + } + if (lf) { + mir_snprintf(str, SIZEOF(str), "Font%dSize", i); + lf->lfHeight = (char)db_get_b(NULL, "ChatFonts", str, fontOptionsList[i].defSize); + lf->lfWidth = 0; + lf->lfEscapement = 0; + lf->lfOrientation = 0; + mir_snprintf(str, SIZEOF(str), "Font%dSty", i); + style = db_get_b(NULL, "ChatFonts", str, fontOptionsList[i].defStyle); + lf->lfWeight = style & FONTF_BOLD ? FW_BOLD : FW_NORMAL; + lf->lfItalic = style & FONTF_ITALIC ? 1 : 0; + lf->lfUnderline = 0; + lf->lfStrikeOut = 0; + mir_snprintf(str, SIZEOF(str), "Font%dSet", i); + lf->lfCharSet = db_get_b(NULL, "ChatFonts", str, fontOptionsList[i].defCharset); + lf->lfOutPrecision = OUT_DEFAULT_PRECIS; + lf->lfClipPrecision = CLIP_DEFAULT_PRECIS; + lf->lfQuality = DEFAULT_QUALITY; + lf->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE; + mir_snprintf(str, SIZEOF(str), "Font%d", i); + if (db_get_ts(NULL, "ChatFonts", str, &dbv)) + lstrcpy(lf->lfFaceName, fontOptionsList[i].szDefFace); + else { + lstrcpyn(lf->lfFaceName, dbv.ptszVal, SIZEOF(lf->lfFaceName)); + db_free(&dbv); + } + } +} + +void RegisterFonts(void) +{ + FontIDT fontid = { 0 }; + ColourIDT colourid; + char idstr[10]; + int index = 0, i; + + fontid.cbSize = sizeof(FontIDT); + fontid.flags = FIDF_ALLOWREREGISTER | FIDF_DEFAULTVALID | FIDF_NEEDRESTART; + for (i = 0; i < msgDlgFontCount; i++, index++) { + strncpy(fontid.dbSettingsGroup, "ChatFonts", sizeof(fontid.dbSettingsGroup)); + _tcsncpy(fontid.group, _T("Chat Module"), SIZEOF(fontid.group)); + _tcsncpy(fontid.name, fontOptionsList[i].szDescr, SIZEOF(fontid.name)); + mir_snprintf(idstr, SIZEOF(idstr), "Font%d", index); + strncpy(fontid.prefix, idstr, sizeof(fontid.prefix)); + fontid.order = index; + + fontid.deffontsettings.charset = fontOptionsList[i].defCharset; + fontid.deffontsettings.colour = fontOptionsList[i].defColour; + fontid.deffontsettings.size = fontOptionsList[i].defSize; + fontid.deffontsettings.style = fontOptionsList[i].defStyle; + _tcsncpy(fontid.deffontsettings.szFace, fontOptionsList[i].szDefFace, SIZEOF(fontid.deffontsettings.szFace)); + _tcsncpy(fontid.backgroundGroup, _T("Chat Module"), SIZEOF(fontid.backgroundGroup)); + switch (i) { + case 17: + _tcsncpy(fontid.backgroundName, _T("Message Background"), SIZEOF(fontid.backgroundName)); + break; + case 18: + case 19: + _tcsncpy(fontid.backgroundName, _T("Userlist Background"), SIZEOF(fontid.backgroundName)); + break; + default: + _tcsncpy(fontid.backgroundName, _T("Background"), SIZEOF(fontid.backgroundName)); + break; + } + FontRegisterT(&fontid); + } + + colourid.cbSize = sizeof(ColourIDT); + colourid.order = 0; + strncpy(colourid.dbSettingsGroup, "Chat", sizeof(colourid.dbSettingsGroup)); + + strncpy(colourid.setting, "ColorLogBG", SIZEOF(colourid.setting)); + _tcsncpy(colourid.name, LPGENT("Background"), SIZEOF(colourid.name)); + _tcsncpy(colourid.group, LPGENT("Chat module"), SIZEOF(colourid.group)); + colourid.defcolour = GetSysColor(COLOR_WINDOW); + ColourRegisterT(&colourid); + + strncpy(colourid.setting, "ColorMessageBG", SIZEOF(colourid.setting)); + _tcsncpy(colourid.name, LPGENT("Message background"), SIZEOF(colourid.name)); + colourid.defcolour = GetSysColor(COLOR_WINDOW); + ColourRegisterT(&colourid); + + strncpy(colourid.setting, "ColorNicklistBG", SIZEOF(colourid.setting)); + _tcsncpy(colourid.name, LPGENT("User list background"), SIZEOF(colourid.name)); + colourid.defcolour = GetSysColor(COLOR_WINDOW); + ColourRegisterT(&colourid); + + strncpy(colourid.setting, "ColorNicklistLines", SIZEOF(colourid.setting)); + _tcsncpy(colourid.name, LPGENT("User list lines"), SIZEOF(colourid.name)); + colourid.defcolour = GetSysColor(COLOR_INACTIVEBORDER); + ColourRegisterT(&colourid); + + strncpy(colourid.setting, "ColorNicklistSelectedBG", SIZEOF(colourid.setting)); + _tcsncpy(colourid.name, LPGENT("User list background (selected)"), SIZEOF(colourid.name)); + colourid.defcolour = GetSysColor(COLOR_HIGHLIGHT); + ColourRegisterT(&colourid); +} + +// load icons from the skinning module if available + +HICON LoadIconEx(char* pszIcoLibName, BOOL big) +{ + char szTemp[256]; + mir_snprintf(szTemp, SIZEOF(szTemp), "chat_%s", pszIcoLibName); + return Skin_GetIcon(szTemp, big); +} + +static void InitSetting(TCHAR** ppPointer, char* pszSetting, TCHAR* pszDefault) +{ + DBVARIANT dbv; + if (!db_get_ts(NULL, "Chat", pszSetting, &dbv)) { + replaceStrT(*ppPointer, dbv.ptszVal); + db_free(&dbv); + } + else replaceStrT(*ppPointer, pszDefault); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void LoadGlobalSettings(void) +{ + LOGFONT lf; + + ci.pSettings->LogLimitNames = db_get_b(NULL, "Chat", "LogLimitNames", 1); + ci.pSettings->ShowTime = db_get_b(NULL, "Chat", "ShowTimeStamp", 1); + ci.pSettings->TabsEnable = db_get_b(NULL, "Chat", "Tabs", 1); + ci.pSettings->TabsAtBottom = db_get_b(NULL, "Chat", "TabBottom", 0); + ci.pSettings->TabCloseOnDblClick = db_get_b(NULL, "Chat", "TabCloseOnDblClick", 0); + ci.pSettings->TabRestore = db_get_b(NULL, "Chat", "TabRestore", 0); + ci.pSettings->SoundsFocus = db_get_b(NULL, "Chat", "SoundsFocus", 0); + ci.pSettings->ShowTimeIfChanged = (BOOL)db_get_b(NULL, "Chat", "ShowTimeStampIfChanged", 0); + ci.pSettings->TimeStampEventColour = (BOOL)db_get_b(NULL, "Chat", "TimeStampEventColour", 0); + ci.pSettings->iEventLimit = db_get_w(NULL, "Chat", "LogLimit", 100); + ci.pSettings->dwIconFlags = db_get_dw(NULL, "Chat", "IconFlags", 0x0000); + ci.pSettings->dwTrayIconFlags = db_get_dw(NULL, "Chat", "TrayIconFlags", 0x1000); + ci.pSettings->dwPopupFlags = db_get_dw(NULL, "Chat", "PopupFlags", 0x0000); + ci.pSettings->LoggingLimit = db_get_w(NULL, "Chat", "LoggingLimit", 100); + ci.pSettings->LoggingEnabled = (BOOL)db_get_b(NULL, "Chat", "LoggingEnabled", 0); + ci.pSettings->FlashWindow = (BOOL)db_get_b(NULL, "Chat", "FlashWindow", 0); + ci.pSettings->HighlightEnabled = (BOOL)db_get_b(NULL, "Chat", "HighlightEnabled", 1); + ci.pSettings->crUserListColor = db_get_dw(NULL, "ChatFonts", "Font18Col", RGB(0, 0, 0)); + ci.pSettings->crUserListBGColor = db_get_dw(NULL, "Chat", "ColorNicklistBG", GetSysColor(COLOR_WINDOW)); + ci.pSettings->crUserListSelectedBGColor = db_get_dw(NULL, "Chat", "ColorNicklistSelectedBG", GetSysColor(COLOR_HIGHLIGHT)); + ci.pSettings->crUserListHeadingsColor = db_get_dw(NULL, "ChatFonts", "Font19Col", RGB(170, 170, 170)); + ci.pSettings->crLogBackground = db_get_dw(NULL, "Chat", "ColorLogBG", GetSysColor(COLOR_WINDOW)); + ci.pSettings->StripFormat = (BOOL)db_get_b(NULL, "Chat", "StripFormatting", 0); + ci.pSettings->TrayIconInactiveOnly = (BOOL)db_get_b(NULL, "Chat", "TrayIconInactiveOnly", 1); + ci.pSettings->PopupInactiveOnly = (BOOL)db_get_b(NULL, "Chat", "PopupInactiveOnly", 1); + ci.pSettings->AddColonToAutoComplete = (BOOL)db_get_b(NULL, "Chat", "AddColonToAutoComplete", 1); + ci.pSettings->iPopupStyle = db_get_b(NULL, "Chat", "PopupStyle", 1); + ci.pSettings->iPopupTimeout = db_get_w(NULL, "Chat", "PopupTimeout", 3); + ci.pSettings->crPUBkgColour = db_get_dw(NULL, "Chat", "PopupColorBG", GetSysColor(COLOR_WINDOW)); + ci.pSettings->crPUTextColour = db_get_dw(NULL, "Chat", "PopupColorText", 0); + ci.pSettings->ShowContactStatus = db_get_b(NULL, "Chat", "ShowContactStatus", 0); + ci.pSettings->ContactStatusFirst = db_get_b(NULL, "Chat", "ContactStatusFirst", 0); + + InitSetting(&ci.pSettings->pszTimeStamp, "HeaderTime", _T("[%H:%M]")); + InitSetting(&ci.pSettings->pszTimeStampLog, "LogTimestamp", _T("[%d %b %y %H:%M]")); + InitSetting(&ci.pSettings->pszIncomingNick, "HeaderIncoming", _T("%n:")); + InitSetting(&ci.pSettings->pszOutgoingNick, "HeaderOutgoing", _T("%n:")); + InitSetting(&ci.pSettings->pszHighlightWords, "HighlightWords", _T("%m")); + + { + TCHAR pszTemp[MAX_PATH]; + DBVARIANT dbv; + ci.pSettings->pszLogDir = (TCHAR *)mir_realloc(ci.pSettings->pszLogDir, MAX_PATH*sizeof(TCHAR)); + if (!db_get_ts(NULL, "Chat", "LogDirectory", &dbv)) { + lstrcpyn(pszTemp, dbv.ptszVal, MAX_PATH); + db_free(&dbv); + } + else { + TCHAR *tmpPath = Utils_ReplaceVarsT(_T("%miranda_logpath%\\Chat")); + lstrcpyn(pszTemp, tmpPath, SIZEOF(pszTemp) - 1); + mir_free(tmpPath); + } + + PathToAbsoluteT(pszTemp, ci.pSettings->pszLogDir); + } + + ci.pSettings->LogIndentEnabled = (db_get_b(NULL, "Chat", "LogIndentEnabled", 1) != 0) ? TRUE : FALSE; + + if (ci.pSettings->MessageBoxFont) + DeleteObject(ci.pSettings->MessageBoxFont); + LoadMsgDlgFont(17, &lf, NULL); + ci.pSettings->MessageBoxFont = CreateFontIndirect(&lf); + + if (ci.pSettings->UserListFont) + DeleteObject(ci.pSettings->UserListFont); + LoadMsgDlgFont(18, &lf, NULL); + ci.pSettings->UserListFont = CreateFontIndirect(&lf); + + if (ci.pSettings->UserListHeadingsFont) + DeleteObject(ci.pSettings->UserListHeadingsFont); + LoadMsgDlgFont(19, &lf, NULL); + ci.pSettings->UserListHeadingsFont = CreateFontIndirect(&lf); +} + +static void FreeGlobalSettings(void) +{ + mir_free(ci.pSettings->pszTimeStamp); + mir_free(ci.pSettings->pszTimeStampLog); + mir_free(ci.pSettings->pszIncomingNick); + mir_free(ci.pSettings->pszOutgoingNick); + mir_free(ci.pSettings->pszHighlightWords); + mir_free(ci.pSettings->pszLogDir); + if (ci.pSettings->MessageBoxFont) + DeleteObject(ci.pSettings->MessageBoxFont); + if (ci.pSettings->UserListFont) + DeleteObject(ci.pSettings->UserListFont); + if (ci.pSettings->UserListHeadingsFont) + DeleteObject(ci.pSettings->UserListHeadingsFont); +} + +int GetTextPixelSize(TCHAR* pszText, HFONT hFont, BOOL bWidth) +{ + HDC hdc; + HFONT hOldFont; + RECT rc = { 0 }; + int i; + + if (!pszText || !hFont) + return 0; + + hdc = GetDC(NULL); + hOldFont = (HFONT)SelectObject(hdc, hFont); + i = DrawText(hdc, pszText, -1, &rc, DT_CALCRECT); + SelectObject(hdc, hOldFont); + ReleaseDC(NULL, hdc); + return bWidth ? rc.right - rc.left : rc.bottom - rc.top; +} + +int OptionsInit(void) +{ + LoadLogFonts(); + LOGFONT lf; + LoadMsgDlgFont(18, &lf, NULL); + lstrcpy(lf.lfFaceName, _T("MS Shell Dlg")); + lf.lfUnderline = lf.lfItalic = lf.lfStrikeOut = 0; + lf.lfHeight = -17; + lf.lfWeight = FW_BOLD; + ci.pSettings->NameFont = CreateFontIndirect(&lf); + ci.pSettings->UserListFont = NULL; + ci.pSettings->UserListHeadingsFont = NULL; + ci.pSettings->MessageBoxFont = NULL; + ci.pSettings->iSplitterX = db_get_w(NULL, "Chat", "SplitterX", 105); + ci.pSettings->iSplitterY = db_get_w(NULL, "Chat", "SplitterY", 90); + ci.pSettings->iX = db_get_dw(NULL, "Chat", "roomx", -1); + ci.pSettings->iY = db_get_dw(NULL, "Chat", "roomy", -1); + ci.pSettings->iWidth = db_get_dw(NULL, "Chat", "roomwidth", -1); + ci.pSettings->iHeight = db_get_dw(NULL, "Chat", "roomheight", -1); + LoadGlobalSettings(); + + SkinAddNewSoundEx("ChatMessage", LPGEN("Group chats"), LPGEN("Incoming message")); + SkinAddNewSoundEx("ChatHighlight", LPGEN("Group chats"), LPGEN("Message is highlighted")); + SkinAddNewSoundEx("ChatAction", LPGEN("Group chats"), LPGEN("User has performed an action")); + SkinAddNewSoundEx("ChatJoin", LPGEN("Group chats"), LPGEN("User has joined")); + SkinAddNewSoundEx("ChatPart", LPGEN("Group chats"), LPGEN("User has left")); + SkinAddNewSoundEx("ChatKick", LPGEN("Group chats"), LPGEN("User has kicked some other user")); + SkinAddNewSoundEx("ChatMode", LPGEN("Group chats"), LPGEN("User's status was changed")); + SkinAddNewSoundEx("ChatNick", LPGEN("Group chats"), LPGEN("User has changed name")); + SkinAddNewSoundEx("ChatNotice", LPGEN("Group chats"), LPGEN("User has sent a notice")); + SkinAddNewSoundEx("ChatQuit", LPGEN("Group chats"), LPGEN("User has disconnected")); + SkinAddNewSoundEx("ChatTopic", LPGEN("Group chats"), LPGEN("The topic has been changed")); + + if (ci.pSettings->LoggingEnabled) + CreateDirectoryTreeT(ci.pSettings->pszLogDir); + + LOGFONT lf2; + LoadMsgDlgFont(0, &lf2, NULL); + HFONT hFont = CreateFontIndirect(&lf2); + int iText = GetTextPixelSize(MakeTimeStamp(ci.pSettings->pszTimeStamp, time(NULL)), hFont, TRUE); + DeleteObject(hFont); + ci.pSettings->LogTextIndent = iText; + ci.pSettings->LogTextIndent = ci.pSettings->LogTextIndent * 12 / 10; + return 0; +} + +int OptionsUnInit(void) +{ + FreeGlobalSettings(); + UnhookEvent(g_hOptions); + DeleteObject(ci.pSettings->NameFont); + return 0; +} diff --git a/src/modules/chat/chat_svc.cpp b/src/modules/chat/chat_svc.cpp new file mode 100644 index 0000000000..5b5492987e --- /dev/null +++ b/src/modules/chat/chat_svc.cpp @@ -0,0 +1,749 @@ +/* +Chat module plugin for Miranda IM + +Copyright 2000-12 Miranda IM, 2012-14 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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 "..\..\core\commonheaders.h" + +#include "chat.h" + +BOOL SmileyAddInstalled, PopupInstalled, IEviewInstalled; + +HANDLE hChatSendEvent; +HANDLE hBuildMenuEvent; +HGENMENU hJoinMenuItem, hLeaveMenuItem; +SESSION_INFO g_TabSession; +CRITICAL_SECTION cs; + +void RegisterFonts( void ); + +static HANDLE + hServiceRegister = NULL, + hServiceNewChat = NULL, + hServiceAddEvent = NULL, + hServiceGetAddEventPtr = NULL, + hServiceGetInfo = NULL, + hServiceGetCount = NULL, + hEventPrebuildMenu = NULL, + hEventDoubleclicked = NULL, + hEventJoinChat = NULL, + hEventLeaveChat = NULL; + +void ShowRoom(SESSION_INFO *si, WPARAM wp, BOOL bSetForeground) +{ + if (!si) + return; + + if (ci.pSettings->TabsEnable) { + // the session is not the current tab, so we copy the necessary + // details into the SESSION_INFO for the tabbed window + if (!si->hWnd) { + g_TabSession.iEventCount = si->iEventCount; + g_TabSession.iStatusCount = si->iStatusCount; + g_TabSession.iType = si->iType; + g_TabSession.nUsersInNicklist = si->nUsersInNicklist; + g_TabSession.pLog = si->pLog; + g_TabSession.pLogEnd = si->pLogEnd; + g_TabSession.pMe = si->pMe; + g_TabSession.dwFlags = si->dwFlags; + g_TabSession.pStatuses = si->pStatuses; + g_TabSession.ptszID = si->ptszID; + g_TabSession.pszModule = si->pszModule; + g_TabSession.ptszName = si->ptszName; + g_TabSession.ptszStatusbarText = si->ptszStatusbarText; + g_TabSession.ptszTopic = si->ptszTopic; + g_TabSession.pUsers = si->pUsers; + g_TabSession.hContact = si->hContact; + g_TabSession.wStatus = si->wStatus; + g_TabSession.lpCommands = si->lpCommands; + g_TabSession.lpCurrentCommand = NULL; + } + + //Do we need to create a tabbed window? +// if (g_TabSession.hWnd == NULL) !!!!!!!!!!!!!!!!!!!!!!! +// g_TabSession.hWnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_CHANNEL), NULL, RoomWndProc, (LPARAM)&g_TabSession); + + SetWindowLongPtr(g_TabSession.hWnd, GWL_EXSTYLE, GetWindowLongPtr(g_TabSession.hWnd, GWL_EXSTYLE) | WS_EX_APPWINDOW); + + // if the session was not the current tab we need to tell the window to + // redraw to show the contents of the current SESSION_INFO + if (!si->hWnd) { + ci.SM_SetTabbedWindowHwnd(si, g_TabSession.hWnd); + SendMessage(g_TabSession.hWnd, GC_ADDTAB, -1, (LPARAM)si); + SendMessage(g_TabSession.hWnd, GC_TABCHANGE, 0, (LPARAM)&g_TabSession); + } + + ci.SetActiveSession(si->ptszID, si->pszModule); + + if (!IsWindowVisible(g_TabSession.hWnd) || wp == WINDOW_HIDDEN) + SendMessage(g_TabSession.hWnd, GC_EVENT_CONTROL + WM_USER + 500, wp, 0); + else { + if (IsIconic(g_TabSession.hWnd)) + ShowWindow(g_TabSession.hWnd, SW_NORMAL); + + PostMessage(g_TabSession.hWnd, WM_SIZE, 0, 0); + if (si->iType != GCW_SERVER) + SendMessage(g_TabSession.hWnd, GC_UPDATENICKLIST, 0, 0); + else + SendMessage(g_TabSession.hWnd, GC_UPDATETITLE, 0, 0); + SendMessage(g_TabSession.hWnd, GC_REDRAWLOG, 0, 0); + SendMessage(g_TabSession.hWnd, GC_UPDATESTATUSBAR, 0, 0); + ShowWindow(g_TabSession.hWnd, SW_SHOW); + if (bSetForeground) + SetForegroundWindow(g_TabSession.hWnd); + } + SendMessage(g_TabSession.hWnd, WM_MOUSEACTIVATE, 0, 0); + SetFocus(GetDlgItem(g_TabSession.hWnd, IDC_MESSAGE)); + return; + } + + //Do we need to create a window? +// if (si->hWnd == NULL) !!!!!!!!!!!!!!!!!!!!!!!s +// si->hWnd = CreateDialogParam(hInst, MAKEINTRESOURCE(IDD_CHANNEL), NULL, RoomWndProc, (LPARAM)si); + + SetWindowLongPtr(si->hWnd, GWL_EXSTYLE, GetWindowLongPtr(si->hWnd, GWL_EXSTYLE) | WS_EX_APPWINDOW); + if (!IsWindowVisible(si->hWnd) || wp == WINDOW_HIDDEN) + SendMessage(si->hWnd, GC_EVENT_CONTROL + WM_USER + 500, wp, 0); + else { + if (IsIconic(si->hWnd)) + ShowWindow(si->hWnd, SW_NORMAL); + ShowWindow(si->hWnd, SW_SHOW); + SetForegroundWindow(si->hWnd); + } + + SendMessage(si->hWnd, WM_MOUSEACTIVATE, 0, 0); + SetFocus(GetDlgItem(si->hWnd, IDC_MESSAGE)); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Post-load event hooks + +static int FontsChanged(WPARAM wParam, LPARAM lParam) +{ + LoadLogFonts(); + { + LOGFONT lf; + HFONT hFont; + int iText; + + LoadMsgDlgFont(0, &lf, NULL); + hFont = CreateFontIndirect(&lf); + iText = GetTextPixelSize(MakeTimeStamp(ci.pSettings->pszTimeStamp, time(NULL)), hFont, TRUE); + DeleteObject(hFont); + ci.pSettings->LogTextIndent = iText; + ci.pSettings->LogTextIndent = ci.pSettings->LogTextIndent * 12 / 10; + ci.pSettings->LogIndentEnabled = (db_get_b(NULL, "Chat", "LogIndentEnabled", 1) != 0) ? TRUE : FALSE; + } + ci.MM_FontsChanged(); + ci.MM_FixColors(); + ci.SM_BroadcastMessage(NULL, GC_SETWNDPROPS, 0, 0, TRUE); + return 0; +} + +static int IconsChanged(WPARAM wParam, LPARAM lParam) +{ + FreeMsgLogBitmaps(); + + LoadMsgLogBitmaps(); + ci.MM_IconsChanged(); + ci.SM_BroadcastMessage(NULL, GC_SETWNDPROPS, 0, 0, FALSE); + return 0; +} + +static int PreShutdown(WPARAM wParam, LPARAM lParam) +{ + ci.SM_BroadcastMessage(NULL, GC_CLOSEWINDOW, 0, 1, FALSE); + + ci.SM_RemoveAll(); + ci.MM_RemoveAll(); + ci.TabM_RemoveAll(); + return 0; +} + +static int SmileyOptionsChanged(WPARAM wParam, LPARAM lParam) +{ + ci.SM_BroadcastMessage(NULL, GC_REDRAWLOG, 0, 1, FALSE); + return 0; +} + +static INT_PTR Service_GetCount(WPARAM wParam, LPARAM lParam) +{ + if (!lParam) + return -1; + + mir_cslock lck(cs); + return ci.SM_GetCount((char *)lParam); +} + +static INT_PTR Service_GetInfo(WPARAM wParam, LPARAM lParam) +{ + GC_INFO *gci = (GC_INFO *)lParam; + SESSION_INFO *si = NULL; + + if (!gci || !gci->pszModule) + return 1; + + mir_cslock lck(cs); + + if (gci->Flags&BYINDEX) + si = ci.SM_FindSessionByIndex(gci->pszModule, gci->iItem); + else + si = ci.SM_FindSession(gci->pszID, gci->pszModule); + + if (si) { + if (gci->Flags & DATA) gci->dwItemData = si->dwItemData; + if (gci->Flags & HCONTACT) gci->hContact = si->hContact; + if (gci->Flags & TYPE) gci->iType = si->iType; + if (gci->Flags & COUNT) gci->iCount = si->nUsersInNicklist; + if (gci->Flags & USERS) gci->pszUsers = ci.SM_GetUsers(si); + if (gci->Flags & ID) gci->pszID = si->ptszID; + if (gci->Flags & NAME) gci->pszName = si->ptszName; + return 0; + } + + return 1; +} + +static INT_PTR Service_Register(WPARAM wParam, LPARAM lParam) +{ + GCREGISTER *gcr = (GCREGISTER *)lParam; + if (gcr == NULL) + return GC_REGISTER_ERROR; + + if (gcr->cbSize != sizeof(GCREGISTER)) + return GC_REGISTER_WRONGVER; + + mir_cslock lck(cs); + MODULEINFO *mi = ci.MM_AddModule(gcr->pszModule); + if (mi == NULL) + return GC_REGISTER_ERROR; + + mi->ptszModDispName = mir_tstrdup(gcr->ptszDispName); + mi->bBold = gcr->dwFlags & GC_BOLD; + mi->bUnderline = gcr->dwFlags & GC_UNDERLINE; + mi->bItalics = gcr->dwFlags & GC_ITALICS; + mi->bColor = gcr->dwFlags & GC_COLOR; + mi->bBkgColor = gcr->dwFlags & GC_BKGCOLOR; + mi->bAckMsg = gcr->dwFlags & GC_ACKMSG; + mi->bChanMgr = gcr->dwFlags & GC_CHANMGR; + mi->iMaxText = gcr->iMaxText; + mi->nColorCount = gcr->nColors; + if (gcr->nColors > 0) { + mi->crColors = (COLORREF *)mir_alloc(sizeof(COLORREF)* gcr->nColors); + memcpy(mi->crColors, gcr->pColors, sizeof(COLORREF)* gcr->nColors); + } + + mi->OnlineIconIndex = ImageList_AddIcon(ci.hIconsList, LoadSkinnedProtoIcon(gcr->pszModule, ID_STATUS_ONLINE)); + mi->hOnlineIcon = ImageList_GetIcon(ci.hIconsList, mi->OnlineIconIndex, ILD_TRANSPARENT); + + mi->hOnlineTalkIcon = ImageList_GetIcon(ci.hIconsList, mi->OnlineIconIndex, ILD_TRANSPARENT | INDEXTOOVERLAYMASK(1)); + ImageList_AddIcon(ci.hIconsList, mi->hOnlineTalkIcon); + + mi->OfflineIconIndex = ImageList_AddIcon(ci.hIconsList, LoadSkinnedProtoIcon(gcr->pszModule, ID_STATUS_OFFLINE)); + mi->hOfflineIcon = ImageList_GetIcon(ci.hIconsList, mi->OfflineIconIndex, ILD_TRANSPARENT); + + mi->hOfflineTalkIcon = ImageList_GetIcon(ci.hIconsList, mi->OfflineIconIndex, ILD_TRANSPARENT | INDEXTOOVERLAYMASK(1)); + ImageList_AddIcon(ci.hIconsList, mi->hOfflineTalkIcon); + + mi->pszHeader = Log_CreateRtfHeader(mi); + + CheckColorsInModule((char*)gcr->pszModule); + CList_SetAllOffline(TRUE, gcr->pszModule); + return 0; +} + +static INT_PTR Service_NewChat(WPARAM wParam, LPARAM lParam) +{ + GCSESSION *gcw = (GCSESSION *)lParam; + if (gcw == NULL) + return GC_NEWSESSION_ERROR; + + if (gcw->cbSize != sizeof(GCSESSION)) + return GC_NEWSESSION_WRONGVER; + + mir_cslock lck(cs); + MODULEINFO* mi = ci.MM_FindModule(gcw->pszModule); + if (mi == NULL) + return GC_NEWSESSION_ERROR; + + SESSION_INFO *si = ci.SM_AddSession(gcw->ptszID, gcw->pszModule); + + // create a new session and set the defaults + if (si != NULL) { + TCHAR szTemp[256]; + + si->dwItemData = gcw->dwItemData; + if (gcw->iType != GCW_SERVER) + si->wStatus = ID_STATUS_ONLINE; + si->iType = gcw->iType; + si->dwFlags = gcw->dwFlags; + si->ptszName = mir_tstrdup(gcw->ptszName); + si->ptszStatusbarText = mir_tstrdup(gcw->ptszStatusbarText); + si->iSplitterX = ci.pSettings->iSplitterX; + si->iSplitterY = ci.pSettings->iSplitterY; + si->iLogFilterFlags = (int)db_get_dw(NULL, "Chat", "FilterFlags", 0x03E0); + si->bFilterEnabled = db_get_b(NULL, "Chat", "FilterEnabled", 0); + si->bNicklistEnabled = db_get_b(NULL, "Chat", "ShowNicklist", 1); + + if (mi->bColor) { + si->iFG = 4; + si->bFGSet = TRUE; + } + if (mi->bBkgColor) { + si->iBG = 2; + si->bBGSet = TRUE; + } + if (si->iType == GCW_SERVER) + mir_sntprintf(szTemp, SIZEOF(szTemp), _T("Server: %s"), si->ptszName); + else + mir_sntprintf(szTemp, SIZEOF(szTemp), _T("%s"), si->ptszName); + si->hContact = CList_AddRoom(gcw->pszModule, gcw->ptszID, szTemp, si->iType); + db_set_s(si->hContact, si->pszModule, "Topic", ""); + db_unset(si->hContact, "CList", "StatusMsg"); + if (si->ptszStatusbarText) + db_set_ts(si->hContact, si->pszModule, "StatusBar", si->ptszStatusbarText); + else + db_set_s(si->hContact, si->pszModule, "StatusBar", ""); + } + else { + SESSION_INFO* si2 = ci.SM_FindSession(gcw->ptszID, gcw->pszModule); + if (si2) { + if (si2->hWnd) + g_TabSession.nUsersInNicklist = 0; + + ci.UM_RemoveAll(&si2->pUsers); + ci.TM_RemoveAll(&si2->pStatuses); + + si2->iStatusCount = 0; + si2->nUsersInNicklist = 0; + + if (!ci.pSettings->TabsEnable) { + if (si2->hWnd) + RedrawWindow(GetDlgItem(si2->hWnd, IDC_LIST), NULL, NULL, RDW_INVALIDATE); + } + else if (g_TabSession.hWnd) + RedrawWindow(GetDlgItem(g_TabSession.hWnd, IDC_LIST), NULL, NULL, RDW_INVALIDATE); + } + } + + return 0; +} + +static int DoControl(GCEVENT *gce, WPARAM wp) +{ + SESSION_INFO *si; + + if (gce->pDest->iType == GC_EVENT_CONTROL) { + switch (wp) { + case WINDOW_HIDDEN: + if (si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule)) { + si->bInitDone = TRUE; + ci.SetActiveSession(si->ptszID, si->pszModule); + if (si->hWnd) + ShowRoom(si, wp, FALSE); + } + return 0; + + case WINDOW_MINIMIZE: + case WINDOW_MAXIMIZE: + case WINDOW_VISIBLE: + case SESSION_INITDONE: + if (si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule)) { + si->bInitDone = TRUE; + if (wp != SESSION_INITDONE || db_get_b(NULL, "Chat", "PopupOnJoin", 0) == 0) + ShowRoom(si, wp, TRUE); + return 0; + } + break; + + case SESSION_OFFLINE: + ci.SM_SetOffline(gce->pDest->ptszID, gce->pDest->pszModule); + // fall through + + case SESSION_ONLINE: + ci.SM_SetStatus(gce->pDest->ptszID, gce->pDest->pszModule, wp == SESSION_ONLINE ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE); + break; + + case WINDOW_CLEARLOG: + if (si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule)) { + ci.LM_RemoveAll(&si->pLog, &si->pLogEnd); + if (si->hWnd) { + g_TabSession.pLog = si->pLog; + g_TabSession.pLogEnd = si->pLogEnd; + } + si->iEventCount = 0; + si->LastTime = 0; + } + break; + + case SESSION_TERMINATE: + return ci.SM_RemoveSession(gce->pDest->ptszID, gce->pDest->pszModule, (gce->dwFlags & GCEF_REMOVECONTACT) != 0); + } + ci.SM_SendMessage(gce->pDest->ptszID, gce->pDest->pszModule, GC_EVENT_CONTROL + WM_USER + 500, wp, 0); + } + + else if (gce->pDest->iType == GC_EVENT_CHUID && gce->ptszText) { + ci.SM_ChangeUID(gce->pDest->ptszID, gce->pDest->pszModule, gce->ptszNick, gce->ptszText); + } + + else if (gce->pDest->iType == GC_EVENT_CHANGESESSIONAME && gce->ptszText) { + if (si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule)) { + replaceStrT(si->ptszName, gce->ptszText); + if (si->hWnd) + SendMessage(si->hWnd, GC_UPDATETITLE, 0, 0); + + if (g_TabSession.hWnd && ci.pSettings->TabsEnable) { + g_TabSession.ptszName = si->ptszName; + SendMessage(g_TabSession.hWnd, GC_SESSIONNAMECHANGE, 0, (LPARAM)si); + } + } + } + + else if (gce->pDest->iType == GC_EVENT_SETITEMDATA) { + if (si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule)) + si->dwItemData = gce->dwItemData; + } + + else if (gce->pDest->iType == GC_EVENT_GETITEMDATA) { + if (si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule)) { + gce->dwItemData = si->dwItemData; + return si->dwItemData; + } + return 0; + } + else if (gce->pDest->iType == GC_EVENT_SETSBTEXT) { + if (si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule)) { + replaceStrT(si->ptszStatusbarText, gce->ptszText); + if (si->ptszStatusbarText) + db_set_ts(si->hContact, si->pszModule, "StatusBar", si->ptszStatusbarText); + else + db_set_s(si->hContact, si->pszModule, "StatusBar", ""); + if (si->hWnd) { + g_TabSession.ptszStatusbarText = si->ptszStatusbarText; + SendMessage(si->hWnd, GC_UPDATESTATUSBAR, 0, 0); + } + } + } + else if (gce->pDest->iType == GC_EVENT_ACK) { + ci.SM_SendMessage(gce->pDest->ptszID, gce->pDest->pszModule, GC_ACKMESSAGE, 0, 0); + } + else if (gce->pDest->iType == GC_EVENT_SENDMESSAGE && gce->ptszText) { + ci.SM_SendUserMessage(gce->pDest->ptszID, gce->pDest->pszModule, gce->ptszText); + } + else if (gce->pDest->iType == GC_EVENT_SETSTATUSEX) { + ci.SM_SetStatusEx(gce->pDest->ptszID, gce->pDest->pszModule, gce->ptszText, gce->dwItemData); + } + else return 1; + + return 0; +} + +static void AddUser(GCEVENT *gce) +{ + SESSION_INFO *si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule); + if (si == NULL) return; + + WORD status = ci.TM_StringToWord(si->pStatuses, gce->ptszStatus); + USERINFO *ui = ci.SM_AddUser(gce->pDest->ptszID, gce->pDest->pszModule, gce->ptszUID, gce->ptszNick, status); + if (ui == NULL) return; + + ui->pszNick = mir_tstrdup(gce->ptszNick); + + if (gce->bIsMe) + si->pMe = ui; + + ui->Status = status; + ui->Status |= si->pStatuses->Status; + + if (si->hWnd) { + g_TabSession.pUsers = si->pUsers; + SendMessage(si->hWnd, GC_UPDATENICKLIST, 0, 0); + } +} + +static INT_PTR Service_AddEvent(WPARAM wParam, LPARAM lParam) +{ + GCEVENT *gce = (GCEVENT*)lParam; + GCDEST *gcd = NULL; + BOOL bIsHighlighted = FALSE; + BOOL bRemoveFlag = FALSE; + + if (gce == NULL) + return GC_EVENT_ERROR; + + gcd = gce->pDest; + if (gcd == NULL) + return GC_EVENT_ERROR; + + if (gce->cbSize != sizeof(GCEVENT)) + return GC_EVENT_WRONGVER; + + if (!IsEventSupported(gcd->iType)) + return GC_EVENT_ERROR; + + mir_cslock lck(cs); + + // Do different things according to type of event + switch (gcd->iType) { + case GC_EVENT_ADDGROUP: + { + STATUSINFO *si = ci.SM_AddStatus(gce->pDest->ptszID, gce->pDest->pszModule, gce->ptszStatus); + if (si && gce->dwItemData) + si->hIcon = CopyIcon((HICON)gce->dwItemData); + } + return 0; + + case GC_EVENT_CHUID: + case GC_EVENT_CHANGESESSIONAME: + case GC_EVENT_SETITEMDATA: + case GC_EVENT_GETITEMDATA: + case GC_EVENT_CONTROL: + case GC_EVENT_SETSBTEXT: + case GC_EVENT_ACK: + case GC_EVENT_SENDMESSAGE: + case GC_EVENT_SETSTATUSEX: + return DoControl(gce, wParam); + + case GC_EVENT_SETCONTACTSTATUS: + return ci.SM_SetContactStatus(gce->pDest->ptszID, gce->pDest->pszModule, gce->ptszUID, (WORD)gce->dwItemData); + + case GC_EVENT_TOPIC: + { + SESSION_INFO *si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule); + if (si) { + if (gce->ptszText) { + replaceStrT(si->ptszTopic, gce->ptszText); + if (si->hWnd) + g_TabSession.ptszTopic = si->ptszTopic; + db_set_ts(si->hContact, si->pszModule, "Topic", RemoveFormatting(si->ptszTopic)); + if (db_get_b(NULL, "Chat", "TopicOnClist", 0)) + db_set_ts(si->hContact, "CList", "StatusMsg", RemoveFormatting(si->ptszTopic)); + } + } + } + break; + + case GC_EVENT_ADDSTATUS: + ci.SM_GiveStatus(gce->pDest->ptszID, gce->pDest->pszModule, gce->ptszUID, gce->ptszStatus); + break; + + case GC_EVENT_REMOVESTATUS: + ci.SM_TakeStatus(gce->pDest->ptszID, gce->pDest->pszModule, gce->ptszUID, gce->ptszStatus); + break; + + case GC_EVENT_MESSAGE: + case GC_EVENT_ACTION: + if (!gce->bIsMe && gce->pDest->ptszID && gce->ptszText) { + SESSION_INFO *si = ci.SM_FindSession(gce->pDest->ptszID, gce->pDest->pszModule); + if (si) + if (IsHighlighted(si, gce->ptszText)) + bIsHighlighted = TRUE; + } + break; + + case GC_EVENT_NICK: + ci.SM_ChangeNick(gce->pDest->ptszID, gce->pDest->pszModule, gce); + break; + + case GC_EVENT_JOIN: + AddUser(gce); + break; + + case GC_EVENT_PART: + case GC_EVENT_QUIT: + case GC_EVENT_KICK: + bRemoveFlag = TRUE; + break; + } + + // Decide which window (log) should have the event + LPCTSTR pWnd = NULL; + LPCSTR pMod = NULL; + if (gcd->ptszID) { + pWnd = gcd->ptszID; + pMod = gcd->pszModule; + } + else if (gcd->iType == GC_EVENT_NOTICE || gcd->iType == GC_EVENT_INFORMATION) { + SESSION_INFO *si = ci.GetActiveSession(); + if (si && !lstrcmpA(si->pszModule, gcd->pszModule)) { + pWnd = si->ptszID; + pMod = si->pszModule; + } + else return 0; + } + else { + // Send the event to all windows with a user pszUID. Used for broadcasting QUIT etc + ci.SM_AddEventToAllMatchingUID(gce); + if (!bRemoveFlag) + return 0; + } + + // add to log + if (pWnd) { + SESSION_INFO *si = ci.SM_FindSession(pWnd, pMod); + + // fix for IRC's old stuyle mode notifications. Should not affect any other protocol + if ((gce->pDest->iType == GC_EVENT_ADDSTATUS || gce->pDest->iType == GC_EVENT_REMOVESTATUS) && !(gce->dwFlags & GCEF_ADDTOLOG)) + return 0; + + if (gce && gce->pDest->iType == GC_EVENT_JOIN && gce->time == 0) + return 0; + + if (si && (si->bInitDone || gce->pDest->iType == GC_EVENT_TOPIC || (gce->pDest->iType == GC_EVENT_JOIN && gce->bIsMe))) { + if (ci.SM_AddEvent(pWnd, pMod, gce, bIsHighlighted) && si->hWnd) { + g_TabSession.pLog = si->pLog; + g_TabSession.pLogEnd = si->pLogEnd; + SendMessage(si->hWnd, GC_ADDLOG, 0, 0); + } + else if (si->hWnd) { + g_TabSession.pLog = si->pLog; + g_TabSession.pLogEnd = si->pLogEnd; + SendMessage(si->hWnd, GC_REDRAWLOG2, 0, 0); + } + if (!(gce->dwFlags & GCEF_NOTNOTIFY)) + DoSoundsFlashPopupTrayStuff(si, gce, bIsHighlighted, 0); + if ((gce->dwFlags & GCEF_ADDTOLOG) && ci.pSettings->LoggingEnabled) + LogToFile(si, gce); + } + + if (!bRemoveFlag) + return 0; + } + + if (bRemoveFlag) + return ci.SM_RemoveUser(gce->pDest->ptszID, gce->pDest->pszModule, gce->ptszUID) == 0; + + return GC_EVENT_ERROR; +} + +static INT_PTR Service_GetAddEventPtr(WPARAM wParam, LPARAM lParam) +{ + GCPTRS *gp = (GCPTRS *)lParam; + + mir_cslock lck(cs); + gp->pfnAddEvent = Service_AddEvent; + return 0; +} + +static int ModuleLoad(WPARAM wParam, LPARAM lParam) +{ + IEviewInstalled = ServiceExists(MS_IEVIEW_WINDOW); + PopupInstalled = ServiceExists(MS_POPUP_ADDPOPUP); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Service creation + +static int ModulesLoaded(WPARAM wParam, LPARAM lParam) +{ + char* mods[3] = { "Chat", "ChatFonts" }; + CallService("DBEditorpp/RegisterModule", (WPARAM)mods, 2); + + RegisterFonts(); + + CLISTMENUITEM mi = { sizeof(mi) }; + mi.position = -2000090001; + mi.flags = CMIF_DEFAULT; + mi.icolibItem = LoadSkinnedIconHandle(SKINICON_CHAT_JOIN); + mi.pszName = LPGEN("&Join"); + mi.pszService = "GChat/JoinChat"; + hJoinMenuItem = Menu_AddContactMenuItem(&mi); + + mi.position = -2000090000; + mi.icolibItem = LoadSkinnedIconHandle(SKINICON_CHAT_LEAVE); + mi.flags = CMIF_NOTOFFLINE; + mi.pszName = LPGEN("&Leave"); + mi.pszService = "GChat/LeaveChat"; + hLeaveMenuItem = Menu_AddContactMenuItem(&mi); + + HookEvent(ME_FONT_RELOAD, FontsChanged); + HookEvent(ME_SKIN2_ICONSCHANGED, IconsChanged); + + if (ServiceExists(MS_SMILEYADD_SHOWSELECTION)) { + SmileyAddInstalled = TRUE; + HookEvent(ME_SMILEYADD_OPTIONSCHANGED, SmileyOptionsChanged); + } + + ModuleLoad(0, 0); + + CList_SetAllOffline(TRUE, NULL); + return 0; +} + +void HookEvents(void) +{ + InitializeCriticalSection(&cs); + + HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); + HookEvent(ME_CLIST_PREBUILDCONTACTMENU, CList_PrebuildContactMenu); + HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown); + HookEvent(ME_SKIN_ICONSCHANGED, IconsChanged); + HookEvent(ME_SYSTEM_MODULELOAD, ModuleLoad); + HookEvent(ME_SYSTEM_MODULEUNLOAD, ModuleLoad); +} + +void UnhookEvents(void) +{ + DeleteCriticalSection(&cs); +} + +void CreateServiceFunctions(void) +{ + CreateServiceFunction(MS_GC_REGISTER, Service_Register); + CreateServiceFunction(MS_GC_NEWSESSION, Service_NewChat); + CreateServiceFunction(MS_GC_EVENT, Service_AddEvent); + CreateServiceFunction(MS_GC_GETEVENTPTR, Service_GetAddEventPtr); + CreateServiceFunction(MS_GC_GETINFO, Service_GetInfo); + CreateServiceFunction(MS_GC_GETSESSIONCOUNT, Service_GetCount); + + CreateServiceFunction("GChat/DblClickEvent", CList_EventDoubleclicked); + CreateServiceFunction("GChat/PrebuildMenuEvent", CList_PrebuildContactMenuSvc); + CreateServiceFunction("GChat/JoinChat", CList_JoinChat); + CreateServiceFunction("GChat/LeaveChat", CList_LeaveChat); +} + +void CreateHookableEvents(void) +{ + hChatSendEvent = CreateHookableEvent(ME_GC_EVENT); + hBuildMenuEvent = CreateHookableEvent(ME_GC_BUILDMENU); +} + +void DestroyHookableEvents(void) +{ + DestroyHookableEvent(hChatSendEvent); + DestroyHookableEvent(hBuildMenuEvent); +} + +void TabsInit(void) +{ + ZeroMemory(&g_TabSession, sizeof(SESSION_INFO)); + + g_TabSession.iType = GCW_TABROOM; + g_TabSession.iSplitterX = ci.pSettings->iSplitterX; + g_TabSession.iSplitterY = ci.pSettings->iSplitterY; + g_TabSession.iLogFilterFlags = (int)db_get_dw(NULL, "Chat", "FilterFlags", 0x03E0); + g_TabSession.bFilterEnabled = db_get_b(NULL, "Chat", "FilterEnabled", 0); + g_TabSession.bNicklistEnabled = db_get_b(NULL, "Chat", "ShowNicklist", 1); + g_TabSession.iFG = 4; + g_TabSession.bFGSet = TRUE; + g_TabSession.iBG = 2; + g_TabSession.bBGSet = TRUE; +} diff --git a/src/modules/chat/clist.cpp b/src/modules/chat/clist.cpp new file mode 100644 index 0000000000..a06c640c75 --- /dev/null +++ b/src/modules/chat/clist.cpp @@ -0,0 +1,293 @@ +/* +Chat module plugin for Miranda IM + +Copyright (C) 2003 Jörgen Persson + +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 "..\..\core\commonheaders.h" + +#include "chat.h" + +HANDLE CList_AddRoom(const char* pszModule, const TCHAR* pszRoom, const TCHAR* pszDisplayName, int iType) +{ + HANDLE hContact = CList_FindRoom(pszModule, pszRoom); + DBVARIANT dbv; + TCHAR pszGroup[50]; + + *pszGroup = '\0'; + if ( !db_get_ts( NULL, "Chat", "AddToGroup", &dbv )) { + if ( lstrlen( dbv.ptszVal ) > 0 ) + lstrcpyn( pszGroup, dbv.ptszVal, 50); + db_free(&dbv); + } + else lstrcpyn( pszGroup, _T("Chat rooms"), 50); + + if ( pszGroup[0] ) + Clist_CreateGroup(0, pszGroup); + + if ( hContact ) { //contact exist, make sure it is in the right group + if (pszGroup[0]) { + ptrT grpName( db_get_tsa(hContact, "CList", "Group")); + if ( !lstrcmp(pszGroup, grpName)) + db_set_ts(hContact, "CList", "Group", pszGroup); + } + + db_set_w( hContact, pszModule, "Status", ID_STATUS_OFFLINE ); + db_set_ts(hContact, pszModule, "Nick", pszDisplayName ); + return hContact; + } + + // here we create a new one since no one is to be found + if (( hContact = (HANDLE) CallService(MS_DB_CONTACT_ADD, 0, 0)) == NULL ) + return NULL; + + CallService(MS_PROTO_ADDTOCONTACT, (WPARAM) hContact, (LPARAM) pszModule ); + if ( pszGroup && lstrlen( pszGroup ) > 0 ) + db_set_ts(hContact, "CList", "Group", pszGroup ); + else + db_unset( hContact, "CList", "Group" ); + db_set_ts( hContact, pszModule, "Nick", pszDisplayName ); + db_set_ts( hContact, pszModule, "ChatRoomID", pszRoom ); + db_set_b( hContact, pszModule, "ChatRoom", (BYTE)iType ); + db_set_w( hContact, pszModule, "Status", ID_STATUS_OFFLINE ); + return hContact; +} + +BOOL CList_SetOffline(HANDLE hContact, BOOL bHide) +{ + if ( hContact ) { + char* szProto = GetContactProto(hContact); + int i = db_get_b(hContact, szProto, "ChatRoom", 0); + db_set_w(hContact, szProto,"ApparentMode",(LPARAM) 0); + db_set_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); + return TRUE; + } + + return FALSE; +} + +BOOL CList_SetAllOffline(BOOL bHide, const char *pszModule) +{ + for (HANDLE hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { + char *szProto = GetContactProto(hContact); + if (!ci.MM_FindModule(szProto)) + continue; + if (pszModule && strcmp(pszModule, szProto)) + continue; + int i = db_get_b(hContact, szProto, "ChatRoom", 0); + if (i != 0) { + db_set_w(hContact, szProto, "ApparentMode", (LPARAM)(WORD)0); + db_set_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); + } + } + + return TRUE; +} + +int CList_RoomDoubleclicked( WPARAM wParam, LPARAM lParam ) +{ + BOOL bRedrawFlag = FALSE; + + HANDLE hContact = (HANDLE)wParam; + if ( !hContact ) + return 0; + + char *szProto = GetContactProto(hContact); + if (ci.MM_FindModule(szProto) == NULL) + return 0; + if (db_get_b(hContact, szProto, "ChatRoom", 0) == 0) + return 0; + + DBVARIANT dbv; + if (!db_get_ts(hContact, szProto, "ChatRoomID", &dbv)) { + SESSION_INFO *si = ci.SM_FindSession(dbv.ptszVal, szProto); + if (si) { + // is the "toggle visibility option set, so we need to close the window? + if (si->hWnd != NULL + && db_get_b(NULL, "Chat", "ToggleVisibility", 0) == 1 + && !CallService(MS_CLIST_GETEVENT, (WPARAM)hContact, 0) + && IsWindowVisible(si->hWnd) + && !IsIconic(si->hWnd)) { + if (ci.pSettings->TabsEnable) + SendMessage(si->hWnd, GC_REMOVETAB, 1, (LPARAM)si); + else + PostMessage(si->hWnd, GC_CLOSEWINDOW, 0, 0); + db_free(&dbv); + return 1; + } + ShowRoom(si, WINDOW_VISIBLE, TRUE); + } + db_free(&dbv); + return 1; + } + + return 0; +} + +INT_PTR CList_EventDoubleclicked(WPARAM wParam,LPARAM lParam) +{ + return CList_RoomDoubleclicked((WPARAM) ((CLISTEVENT*)lParam)->hContact,(LPARAM) 0); +} + +INT_PTR CList_JoinChat(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + if ( hContact ) { + char* szProto = GetContactProto(hContact); + if ( szProto ) { + if ( db_get_w( hContact, szProto, "Status", 0 ) == ID_STATUS_OFFLINE ) + CallProtoService( szProto, PS_JOINCHAT, wParam, lParam ); + else + CList_RoomDoubleclicked( wParam, 0 ); + } } + + return 0; +} + +INT_PTR CList_LeaveChat(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + if ( hContact ) { + char* szProto = GetContactProto(hContact); + if ( szProto ) + CallProtoService( szProto, PS_LEAVECHAT, wParam, lParam ); + } + return 0; +} + +int CList_PrebuildContactMenu(WPARAM wParam, LPARAM lParam) +{ + HANDLE hContact = (HANDLE)wParam; + if (hContact == NULL) + return 0; + + bool bEnabled = false; + char *szProto = GetContactProto(hContact); + if (szProto) { + // display this menu item only for chats + if (db_get_b(hContact, szProto, "ChatRoom", 0)) { + // still hide it for offline protos + if (CallProtoService(szProto, PS_GETSTATUS, 0, 0) != ID_STATUS_OFFLINE) { + CLISTMENUITEM mi = { sizeof(mi) }; + mi.flags = CMIM_NAME; + if (db_get_w(hContact, szProto, "Status", 0) == ID_STATUS_OFFLINE) + mi.pszName = (char*)LPGEN("Join chat"); + else + mi.pszName = (char*)LPGEN("Open chat window"); + Menu_ModifyItem(hJoinMenuItem, &mi); + bEnabled = true; + } + } + } + + Menu_ShowItem(hJoinMenuItem, bEnabled); + Menu_ShowItem(hLeaveMenuItem, bEnabled); + return 0; +} + +INT_PTR CList_PrebuildContactMenuSvc(WPARAM wParam, LPARAM lParam) +{ + return CList_PrebuildContactMenu(wParam, lParam); +} + +BOOL CList_AddEvent(HANDLE hContact, HICON hIcon, HANDLE hEvent, int type, TCHAR* fmt, ... ) +{ + CLISTEVENT cle = {0}; + va_list marker; + TCHAR szBuf[4096]; + + if (!fmt || !fmt[0] || _tcslen(fmt) > 2000) + return FALSE; + + va_start(marker, fmt); + mir_vsntprintf(szBuf, SIZEOF(szBuf), fmt, marker); + va_end(marker); + + cle.cbSize = sizeof(cle); + cle.hContact = hContact; + cle.hDbEvent = hEvent; + cle.flags = type | CLEF_TCHAR; + cle.hIcon = hIcon; + cle.pszService = "GChat/DblClickEvent" ; + cle.ptszTooltip = TranslateTS(szBuf); + if (type) { + if (!CallService(MS_CLIST_GETEVENT, (WPARAM)hContact, 0)) + CallService(MS_CLIST_ADDEVENT, (WPARAM) hContact, (LPARAM) &cle); + } + else { + if (CallService(MS_CLIST_GETEVENT, (WPARAM)hContact, 0)) + CallService(MS_CLIST_REMOVEEVENT, (WPARAM)hContact, (LPARAM)hEvent); + CallService(MS_CLIST_ADDEVENT, (WPARAM)hContact, (LPARAM)&cle); + } + return TRUE; +} + +HANDLE CList_FindRoom (const char* pszModule, const TCHAR* pszRoom) +{ + for (HANDLE hContact = db_find_first(pszModule); hContact; hContact = db_find_next(hContact, pszModule)) { + if ( !db_get_b(hContact, pszModule, "ChatRoom", 0)) + continue; + + DBVARIANT dbv; + if ( !db_get_ts( hContact, pszModule, "ChatRoomID", &dbv )) { + if ( !lstrcmpi(dbv.ptszVal, pszRoom)) { + db_free(&dbv); + return hContact; + } + db_free(&dbv); + } + } + + return 0; +} + +int WCCmp(TCHAR* wild, TCHAR* string) +{ + TCHAR *cp, *mp; + if ( wild == NULL || !wild[0] || string == NULL || !string[0]) + return 0; + + while ((*string) && (*wild != '*')) { + if ((*wild != *string) && (*wild != '?')) + return 0; + + wild++; + string++; + } + + while (*string) { + if (*wild == '*') { + if (!*++wild) + return 1; + + mp = wild; + cp = string+1; + } + else if ((*wild == *string) || (*wild == '?')) { + wild++; + string++; + } + else { + wild = mp; + string = cp++; + } } + + while (*wild == '*') + wild++; + + return !*wild; +} diff --git a/src/modules/chat/log.cpp b/src/modules/chat/log.cpp new file mode 100644 index 0000000000..ea05294de5 --- /dev/null +++ b/src/modules/chat/log.cpp @@ -0,0 +1,594 @@ +/* +Chat module plugin for Miranda IM + +Copyright (C) 2003 Jörgen Persson + +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 "..\..\core\commonheaders.h" + +#include "chat.h" + +// The code for streaming the text is to a large extent copied from +// the srmm module and then modified to fit the chat module. + +static PBYTE pLogIconBmpBits[14]; +static int logIconBmpSize[ SIZEOF(pLogIconBmpBits) ]; + +static int logPixelSY = 0; +static int logPixelSX = 0; + +static int EventToIndex(LOGINFO * lin) +{ + switch (lin->iType) { + case GC_EVENT_MESSAGE: + if (lin->bIsMe) + return 10; + else + return 9; + + case GC_EVENT_JOIN: return 3; + case GC_EVENT_PART: return 4; + case GC_EVENT_QUIT: return 5; + case GC_EVENT_NICK: return 7; + case GC_EVENT_KICK: return 6; + case GC_EVENT_NOTICE: return 8; + case GC_EVENT_TOPIC: return 11; + case GC_EVENT_INFORMATION:return 12; + case GC_EVENT_ADDSTATUS: return 13; + case GC_EVENT_REMOVESTATUS: return 14; + case GC_EVENT_ACTION: return 15; + } + return 0; +} + +static int EventToIcon(LOGINFO * lin) +{ + switch (lin->iType) { + case GC_EVENT_MESSAGE: + if (lin->bIsMe) + return ICON_MESSAGEOUT; + else + return ICON_MESSAGE; + + case GC_EVENT_JOIN: return ICON_JOIN; + case GC_EVENT_PART: return ICON_PART; + case GC_EVENT_QUIT: return ICON_QUIT; + case GC_EVENT_NICK: return ICON_NICK; + case GC_EVENT_KICK: return ICON_KICK; + case GC_EVENT_NOTICE: return ICON_NOTICE; + case GC_EVENT_TOPIC: return ICON_TOPIC; + case GC_EVENT_INFORMATION:return ICON_INFO; + case GC_EVENT_ADDSTATUS: return ICON_ADDSTATUS; + case GC_EVENT_REMOVESTATUS: return ICON_REMSTATUS; + case GC_EVENT_ACTION: return ICON_ACTION; + } + return 0; +} + +static char *Log_SetStyle(int style, int fontindex) +{ + LOGFONT &lf = ci.aFonts[fontindex].lf; + + static char szStyle[128]; + mir_snprintf(szStyle, SIZEOF(szStyle), "\\f%u\\cf%u\\ul0\\highlight0\\b%d\\i%d\\fs%u", + style, style + 1, lf.lfWeight >= FW_BOLD ? 1 : 0, lf.lfItalic, 2 * abs(lf.lfHeight) * 74 / logPixelSY); + return szStyle; +} + +static void Log_Append(char **buffer, int *cbBufferEnd, int *cbBufferAlloced, const char *fmt, ...) +{ + va_list va; + int charsDone = 0; + + va_start(va, fmt); + for (;;) { + charsDone = mir_vsnprintf(*buffer + *cbBufferEnd, *cbBufferAlloced - *cbBufferEnd, fmt, va); + if (charsDone >= 0) + break; + *cbBufferAlloced += 4096; + *buffer = (char *) mir_realloc(*buffer, *cbBufferAlloced); + } + va_end(va); + *cbBufferEnd += charsDone; +} + +static int Log_AppendRTF(LOGSTREAMDATA* streamData, BOOL simpleMode, char **buffer, int *cbBufferEnd, int *cbBufferAlloced, const TCHAR *fmt, ...) +{ + va_list va; + int lineLen, textCharsCount=0; + TCHAR* line = (TCHAR*)alloca(8001 * sizeof(TCHAR)); + char* d; + + va_start(va, fmt); + lineLen = mir_vsntprintf( line, 8000, fmt, va); + if (lineLen < 0) lineLen = 8000; + line[lineLen] = 0; + va_end(va); + + lineLen = lineLen*20 + 8; + if (*cbBufferEnd + lineLen > *cbBufferAlloced) { + cbBufferAlloced[0] += (lineLen + 1024 - lineLen % 1024); + *buffer = (char *) mir_realloc(*buffer, *cbBufferAlloced); + } + + d = *buffer + *cbBufferEnd; + + for (; *line; line++, textCharsCount++) { + if (*line == '\r' && line[1] == '\n') { + CopyMemory(d, "\\par ", 5); + line++; + d += 5; + } + else if (*line == '\n') { + CopyMemory(d, "\\line ", 6); + d += 6; + } + else if (*line == '%' && !simpleMode ) { + char szTemp[200]; + + szTemp[0] = '\0'; + switch ( *++line ) { + case '\0': + case '%': + *d++ = '%'; + break; + + case 'c': + case 'f': + if (ci.pSettings->StripFormat || streamData->bStripFormat) + line += 2; + + else if ( line[1] != '\0' && line[2] != '\0') { + TCHAR szTemp3[3], c = *line; + int col; + szTemp3[0] = line[1]; + szTemp3[1] = line[2]; + szTemp3[2] = '\0'; + line += 2; + + col = _ttoi(szTemp3); + col += (OPTIONS_FONTCOUNT + 1); + mir_snprintf(szTemp, SIZEOF(szTemp), ( c == 'c' ) ? "\\cf%u " : "\\highlight%u ", col); + } + break; + case 'C': + case 'F': + if ( !ci.pSettings->StripFormat && !streamData->bStripFormat) { + int j = streamData->lin->bIsHighlighted ? 16 : EventToIndex(streamData->lin); + if ( *line == 'C' ) + mir_snprintf(szTemp, SIZEOF(szTemp), "\\cf%u ", j+1); + else + mir_snprintf(szTemp, SIZEOF(szTemp), "\\highlight0 "); + } + break; + case 'b': + case 'u': + case 'i': + if ( !streamData->bStripFormat ) + mir_snprintf(szTemp, SIZEOF(szTemp), (*line == 'u') ? "\\%cl " : "\\%c ", *line ); + break; + + case 'B': + case 'U': + case 'I': + if ( !streamData->bStripFormat ) { + mir_snprintf( szTemp, SIZEOF(szTemp), (*line == 'U') ? "\\%cl0 " : "\\%c0 ", *line ); + CharLowerA( szTemp ); + } + break; + + case 'r': + if ( !streamData->bStripFormat ) { + int index = EventToIndex(streamData->lin); + mir_snprintf(szTemp, SIZEOF(szTemp), "%s ", Log_SetStyle(index, index)); + } + break; + } + + if ( szTemp[0] ) { + int iLen = lstrlenA(szTemp); + memcpy( d, szTemp, iLen ); + d += iLen; + } + } + else if (*line == '\t' && !streamData->bStripFormat) { + CopyMemory(d, "\\tab ", 5); + d += 5; + } + else if ((*line == '\\' || *line == '{' || *line == '}') && !streamData->bStripFormat) { + *d++ = '\\'; + *d++ = (char) *line; + } + else if (*line > 0 && *line < 128) { + *d++ = (char) *line; + } + else d += sprintf(d, "\\u%u ?", (WORD)*line); //!!!!!!!!!!! + } + + *cbBufferEnd = (int) (d - *buffer); + return textCharsCount; +} + +static void AddEventToBuffer(char **buffer, int *bufferEnd, int *bufferAlloced, LOGSTREAMDATA *streamData) +{ + TCHAR szTemp[512], szTemp2[512]; + TCHAR* pszNick = NULL; + if ( streamData->lin->ptszNick ) { + if ( ci.pSettings->LogLimitNames && lstrlen( streamData->lin->ptszNick ) > 20 ) { + lstrcpyn( szTemp2, streamData->lin->ptszNick, 20 ); + lstrcpyn( szTemp2+20, _T("..."), 4); + } + else lstrcpyn( szTemp2, streamData->lin->ptszNick, 511 ); + + if ( streamData->lin->ptszUserInfo ) + mir_sntprintf( szTemp, SIZEOF(szTemp), _T("%s (%s)"), szTemp2, streamData->lin->ptszUserInfo ); + else + mir_sntprintf( szTemp, SIZEOF(szTemp), _T("%s"), szTemp2 ); + pszNick = szTemp; + } + + if ( streamData && streamData->lin ) { + switch ( streamData->lin->iType ) { + case GC_EVENT_MESSAGE: + if ( streamData->lin->ptszText ) + Log_AppendRTF( streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T("%s"), streamData->lin->ptszText ); + break; + case GC_EVENT_ACTION: + if ( streamData->lin->ptszNick && streamData->lin->ptszText) { + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, _T("%s "), streamData->lin->ptszNick); + Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T("%s"), streamData->lin->ptszText); + } + break; + case GC_EVENT_JOIN: + if (pszNick) { + if (!streamData->lin->bIsMe) + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s has joined"), pszNick); + else + Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, TranslateT("You have joined %s"), streamData->si->ptszName); + } + break; + case GC_EVENT_PART: + if (pszNick) + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s has left"), pszNick); + if (streamData->lin->ptszText) + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, _T(": %s"), streamData->lin->ptszText); + break; + case GC_EVENT_QUIT: + if (pszNick) + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s has disconnected"), pszNick); + if (streamData->lin->ptszText) + Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T(": %s"), streamData->lin->ptszText); + break; + case GC_EVENT_NICK: + if (pszNick && streamData->lin->ptszText) { + if (!streamData->lin->bIsMe) + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s is now known as %s"), pszNick, streamData->lin->ptszText); + else + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("You are now known as %s"), streamData->lin->ptszText); + } + break; + case GC_EVENT_KICK: + if (streamData->lin->ptszNick && streamData->lin->ptszStatus) + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s kicked %s"), streamData->lin->ptszStatus, streamData->lin->ptszNick); + if (streamData->lin->ptszText) + Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T(": %s"), streamData->lin->ptszText); + break; + case GC_EVENT_NOTICE: + if (pszNick && streamData->lin->ptszText) { + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("Notice from %s: "), pszNick ); + Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, _T("%s"), streamData->lin->ptszText); + } + break; + case GC_EVENT_TOPIC: + if (streamData->lin->ptszText) + Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, TranslateT("The topic is \'%s%s\'"), streamData->lin->ptszText, _T("%r")); + if (streamData->lin->ptszNick) + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, + streamData->lin->ptszUserInfo ? TranslateT(" (set by %s on %s)"): TranslateT(" (set by %s)"), + streamData->lin->ptszNick, streamData->lin->ptszUserInfo); + break; + case GC_EVENT_INFORMATION: + if (streamData->lin->ptszText) + Log_AppendRTF(streamData, FALSE, buffer, bufferEnd, bufferAlloced, (streamData->lin->bIsMe) ? _T("--> %s") : _T("%s"), streamData->lin->ptszText); + break; + case GC_EVENT_ADDSTATUS: + if (streamData->lin->ptszNick && streamData->lin->ptszText && streamData->lin->ptszStatus) + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s enables \'%s\' status for %s"), streamData->lin->ptszText, streamData->lin->ptszStatus, streamData->lin->ptszNick); + break; + case GC_EVENT_REMOVESTATUS: + if (streamData->lin->ptszNick && streamData->lin->ptszText && streamData->lin->ptszStatus) + Log_AppendRTF(streamData, TRUE, buffer, bufferEnd, bufferAlloced, TranslateT("%s disables \'%s\' status for %s"), streamData->lin->ptszText , streamData->lin->ptszStatus, streamData->lin->ptszNick); + break; +} } } + +TCHAR* MakeTimeStamp( TCHAR* pszStamp, time_t time) +{ + static TCHAR szTime[30]; + if ( !_tcsftime(szTime, SIZEOF(szTime)-1, pszStamp, localtime(&time))) + _tcsncpy(szTime, TranslateT(""), SIZEOF(szTime)); + return szTime; +} + +static char* Log_CreateRTF(LOGSTREAMDATA *streamData) +{ + char *buffer, *header; + int bufferAlloced, bufferEnd, i, me = 0; + LOGINFO * lin = streamData->lin; + MODULEINFO *mi = ci.MM_FindModule(streamData->si->pszModule); + + // guesstimate amount of memory for the RTF + bufferEnd = 0; + bufferAlloced = streamData->bRedraw ? 1024 * (streamData->si->iEventCount+2) : 2048; + buffer = (char *) mir_alloc(bufferAlloced); + buffer[0] = '\0'; + + // ### RTF HEADER + header = mi->pszHeader; + + if (header) + Log_Append(&buffer, &bufferEnd, &bufferAlloced, header); + + + // ### RTF BODY (one iteration per event that should be streamed in) + while ( lin ) + { + // filter + if (streamData->si->iType != GCW_CHATROOM || !streamData->si->bFilterEnabled || (streamData->si->iLogFilterFlags&lin->iType) != 0) + { + // create new line, and set font and color + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\par%s ", Log_SetStyle(0, 0)); + + // Insert icon + if (lin->iType&ci.pSettings->dwIconFlags || lin->bIsHighlighted&&ci.pSettings->dwIconFlags&GC_EVENT_HIGHLIGHT) + { + int iIndex = (lin->bIsHighlighted&&ci.pSettings->dwIconFlags&GC_EVENT_HIGHLIGHT) ? ICON_HIGHLIGHT : EventToIcon(lin); + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\f0\\fs14"); + while (bufferAlloced - bufferEnd < logIconBmpSize[0]) + bufferAlloced += 4096; + buffer = (char *) mir_realloc(buffer, bufferAlloced); + CopyMemory(buffer + bufferEnd, pLogIconBmpBits[iIndex], logIconBmpSize[iIndex]); + bufferEnd += logIconBmpSize[iIndex]; + } + + if (ci.pSettings->TimeStampEventColour) + { + LOGFONT &lf = ci.aFonts[0].lf; + + // colored timestamps + static char szStyle[256]; + int iii; + if (lin->ptszNick && lin->iType == GC_EVENT_MESSAGE) + { + iii = lin->bIsHighlighted?16:(lin->bIsMe ? 2 : 1); + mir_snprintf(szStyle, SIZEOF(szStyle), "\\f0\\cf%u\\ul0\\highlight0\\b%d\\i%d\\fs%u", iii + 1, lf.lfWeight >= FW_BOLD ? 1 : 0, lf.lfItalic, 2 * abs(lf.lfHeight) * 74 / logPixelSY); + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", szStyle); + } + else + { + iii = lin->bIsHighlighted?16:EventToIndex(lin); + mir_snprintf(szStyle, SIZEOF(szStyle), "\\f0\\cf%u\\ul0\\highlight0\\b%d\\i%d\\fs%u", iii + 1, lf.lfWeight >= FW_BOLD ? 1 : 0, lf.lfItalic, 2 * abs(lf.lfHeight) * 74 / logPixelSY); + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", szStyle); + } + } + else + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", Log_SetStyle(0, 0 )); + // insert a TAB if necessary to put the timestamp in the right position + if (ci.pSettings->dwIconFlags) + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tab "); + + //insert timestamp + if (ci.pSettings->ShowTime) + { + TCHAR szTimeStamp[30], szOldTimeStamp[30]; + + lstrcpyn( szTimeStamp, MakeTimeStamp(ci.pSettings->pszTimeStamp, lin->time), 30); + lstrcpyn( szOldTimeStamp, MakeTimeStamp(ci.pSettings->pszTimeStamp, streamData->si->LastTime), 30); + if ( !ci.pSettings->ShowTimeIfChanged || streamData->si->LastTime == 0 || lstrcmp(szTimeStamp, szOldTimeStamp )) { + streamData->si->LastTime = lin->time; + Log_AppendRTF( streamData, TRUE, &buffer, &bufferEnd, &bufferAlloced, _T("%s"), szTimeStamp ); + } + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tab "); + } + + // Insert the nick + if (lin->ptszNick && lin->iType == GC_EVENT_MESSAGE) + { + TCHAR pszTemp[300], *p1; + + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", Log_SetStyle(lin->bIsMe ? 2 : 1, lin->bIsMe ? 2 : 1)); + lstrcpyn(pszTemp, lin->bIsMe ? ci.pSettings->pszOutgoingNick : ci.pSettings->pszIncomingNick, 299); + p1 = _tcsstr(pszTemp, _T("%n")); + if (p1) + p1[1] = 's'; + + Log_AppendRTF(streamData, TRUE, &buffer, &bufferEnd, &bufferAlloced, pszTemp, lin->ptszNick); + Log_Append(&buffer, &bufferEnd, &bufferAlloced, " "); + } + + // Insert the message + { + i = lin->bIsHighlighted?16:EventToIndex(lin); + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "%s ", Log_SetStyle(i, i)); + streamData->lin = lin; + AddEventToBuffer(&buffer, &bufferEnd, &bufferAlloced, streamData); + } + + } + lin = lin->prev; + } + + // ### RTF END + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "}"); + return buffer; +} + +static DWORD CALLBACK Log_StreamCallback(DWORD_PTR dwCookie, LPBYTE pbBuff, LONG cb, LONG * pcb) +{ + LOGSTREAMDATA *lstrdat = (LOGSTREAMDATA *) dwCookie; + + if (lstrdat) + { + // create the RTF + if (lstrdat->buffer == NULL) + { + lstrdat->bufferOffset = 0; + lstrdat->buffer = Log_CreateRTF(lstrdat); + lstrdat->bufferLen = lstrlenA(lstrdat->buffer); + } + + // give the RTF to the RE control + *pcb = min(cb, lstrdat->bufferLen - lstrdat->bufferOffset); + CopyMemory(pbBuff, lstrdat->buffer + lstrdat->bufferOffset, *pcb); + lstrdat->bufferOffset += *pcb; + + // free stuff if the streaming operation is complete + if (lstrdat->bufferOffset == lstrdat->bufferLen) + { + mir_free(lstrdat->buffer); + lstrdat->buffer = NULL; + } + } + + return 0; +} + +char* Log_CreateRtfHeader(MODULEINFO * mi) +{ + char *buffer; + int bufferAlloced, bufferEnd, i = 0; + + // guesstimate amount of memory for the RTF header + bufferEnd = 0; + bufferAlloced = 4096; + buffer = (char *) mir_realloc(mi->pszHeader, bufferAlloced); + buffer[0] = '\0'; + + + //get the number of pixels per logical inch + { + HDC hdc; + hdc = GetDC(NULL); + logPixelSY = GetDeviceCaps(hdc, LOGPIXELSY); + logPixelSX = GetDeviceCaps(hdc, LOGPIXELSX); + ReleaseDC(NULL, hdc); + } + + // ### RTF HEADER + + // font table + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "{\\rtf1\\ansi\\deff0{\\fonttbl"); + for (i = 0; i < OPTIONS_FONTCOUNT; i++) + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "{\\f%u\\fnil\\fcharset%u%S;}", i, ci.aFonts[i].lf.lfCharSet, ci.aFonts[i].lf.lfFaceName); + + // colour table + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "}{\\colortbl ;"); + + for (i = 0; i < OPTIONS_FONTCOUNT; i++) + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(ci.aFonts[i].color), GetGValue(ci.aFonts[i].color), GetBValue(ci.aFonts[i].color)); + + for(i = 0; i < mi->nColorCount; i++) + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\red%u\\green%u\\blue%u;", GetRValue(mi->crColors[i]), GetGValue(mi->crColors[i]), GetBValue(mi->crColors[i])); + + // new paragraph + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "}\\pard"); + + // set tabs and indents + { + int iIndent = 0; + + if (ci.pSettings->dwIconFlags) + { + iIndent += (14*1440)/logPixelSX; + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tx%u", iIndent); + } + if (ci.pSettings->ShowTime) + { + int iSize = (ci.pSettings->LogTextIndent*1440)/logPixelSX; + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tx%u", iIndent + iSize ); + if (ci.pSettings->LogIndentEnabled) + iIndent += iSize; + } + /* + { // text indent + int iSize = (135*1440)/logPixelSX; + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\tx%u", iIndent + iSize ); + if (ci.pSettings->LogIndentEnabled) + iIndent += iSize; + + } + */ + Log_Append(&buffer, &bufferEnd, &bufferAlloced, "\\fi-%u\\li%u", iIndent, iIndent); + } + return buffer; +} + +#define RTFPICTHEADERMAXSIZE 78 +void LoadMsgLogBitmaps(void) +{ + HBRUSH hBkgBrush = CreateSolidBrush(db_get_dw(NULL, "Chat", "ColorLogBG", GetSysColor(COLOR_WINDOW))); + + BITMAPINFOHEADER bih = { 0 }; + bih.biSize = sizeof(bih); + bih.biBitCount = 24; + bih.biCompression = BI_RGB; + bih.biHeight = 10; //GetSystemMetrics(SM_CYSMICON); + bih.biPlanes = 1; + bih.biWidth = 10; //GetSystemMetrics(SM_CXSMICON); + int widthBytes = ((bih.biWidth * bih.biBitCount + 31) >> 5) * 4; + + RECT rc; + rc.top = rc.left = 0; + rc.right = bih.biWidth; + rc.bottom = bih.biHeight; + + HDC hdc = GetDC(NULL); + HBITMAP hBmp = CreateCompatibleBitmap(hdc, bih.biWidth, bih.biHeight); + HDC hdcMem = CreateCompatibleDC(hdc); + PBYTE pBmpBits = (PBYTE)mir_alloc(widthBytes * bih.biHeight); + for (int i = 0; i < SIZEOF(pLogIconBmpBits); i++) { + HICON hIcon = ci.hIcons[i]; + size_t size = RTFPICTHEADERMAXSIZE + (bih.biSize + widthBytes * bih.biHeight) * 2; + pLogIconBmpBits[i] = (PBYTE) mir_alloc(size); + int rtfHeaderSize = mir_snprintf((char *)pLogIconBmpBits[i], size, "{\\pict\\dibitmap0\\wbmbitspixel%u\\wbmplanes1\\wbmwidthbytes%u\\picw%u\\pich%u ", bih.biBitCount, widthBytes, bih.biWidth, bih.biHeight); + HBITMAP hoBmp = (HBITMAP)SelectObject(hdcMem, hBmp); + FillRect(hdcMem, &rc, hBkgBrush); + DrawIconEx(hdcMem, 0, 0, hIcon, bih.biWidth, bih.biHeight, 0, NULL, DI_NORMAL); + SelectObject(hdcMem, hoBmp); + GetDIBits(hdc, hBmp, 0, bih.biHeight, pBmpBits, (BITMAPINFO *) & bih, DIB_RGB_COLORS); + { + int n; + for (n = 0; n < sizeof(BITMAPINFOHEADER); n++) + sprintf((char *)pLogIconBmpBits[i] + rtfHeaderSize + n * 2, "%02X", ((PBYTE) & bih)[n]); //!!!!!!!!!!!!! + for (n = 0; n < widthBytes * bih.biHeight; n += 4) + sprintf((char *)pLogIconBmpBits[i] + rtfHeaderSize + (bih.biSize + n) * 2, "%02X%02X%02X%02X", pBmpBits[n], pBmpBits[n + 1], pBmpBits[n + 2], pBmpBits[n + 3]); //!!!!!!!!!!!!! + } + logIconBmpSize[i] = rtfHeaderSize + (bih.biSize + widthBytes * bih.biHeight) * 2 + 1; + pLogIconBmpBits[i][logIconBmpSize[i] - 1] = '}'; + } + mir_free(pBmpBits); + DeleteDC(hdcMem); + DeleteObject(hBmp); + ReleaseDC(NULL, hdc); + DeleteObject(hBkgBrush); +} + +void FreeMsgLogBitmaps(void) +{ + int i; + for (i = 0; i < SIZEOF(pLogIconBmpBits); i++) + mir_free(pLogIconBmpBits[i]); +} diff --git a/src/modules/chat/manager.cpp b/src/modules/chat/manager.cpp new file mode 100644 index 0000000000..b50bc34598 --- /dev/null +++ b/src/modules/chat/manager.cpp @@ -0,0 +1,1639 @@ +/* +Chat module plugin for Miranda IM + +Copyright 2000-12 Miranda IM, 2012-14 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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 "..\..\core\commonheaders.h" + +#include "chat.h" + +#define WINDOWS_COMMANDS_MAX 30 +#define STATUSICONCOUNT 6 + +SESSION_INFO *m_WndList = 0; +TABLIST *g_TabList = 0; +MODULEINFO *m_ModList = 0; + +static void SetActiveSessionEx(SESSION_INFO *si) +{ + if (si) { + replaceStrT(ci.szActiveWndID, si->ptszID); + replaceStr(ci.szActiveWndModule, si->pszModule); + } +} + +static void SetActiveSession(const TCHAR *pszID, const char* pszModule) +{ + SESSION_INFO *si = ci.SM_FindSession(pszID, pszModule); + if (si) + SetActiveSessionEx(si); +} + +static SESSION_INFO* GetActiveSession(void) +{ + SESSION_INFO *si = ci.SM_FindSession(ci.szActiveWndID, ci.szActiveWndModule); + if (si) + return si; + + return m_WndList; +} + +//--------------------------------------------------- +// Session Manager functions +// +// Keeps track of all sessions and its windows +//--------------------------------------------------- + +static SESSION_INFO* SM_AddSession(const TCHAR *pszID, const char* pszModule) +{ + if (!pszID || !pszModule) + return NULL; + + if (!ci.SM_FindSession(pszID, pszModule)) { + SESSION_INFO*node = (SESSION_INFO*)mir_alloc(sizeof(SESSION_INFO)); + ZeroMemory(node, sizeof(SESSION_INFO)); + node->ptszID = mir_tstrdup(pszID); + node->pszModule = mir_strdup(pszModule); + + if (m_WndList == NULL) { // list is empty + m_WndList = node; + node->next = NULL; + } + else { + node->next = m_WndList; + m_WndList = node; + } + return node; + } + return NULL; +} + +static int SM_RemoveSession(const TCHAR *pszID, const char* pszModule, BOOL removeContact) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszModule) + return FALSE; + + while (pTemp != NULL) { + if ((!pszID && pTemp->iType != GCW_SERVER || !lstrcmpi(pTemp->ptszID, pszID)) && !lstrcmpiA(pTemp->pszModule, pszModule)) // match + { + COMMANDINFO *pCurComm; + DWORD dw = pTemp->dwItemData; + + if (!ci.pSettings->TabsEnable) { + if (pTemp->hWnd) + SendMessage(pTemp->hWnd, GC_EVENT_CONTROL + WM_USER + 500, SESSION_TERMINATE, 0); + } + else if (ci.tabSession.hWnd) + SendMessage(ci.tabSession.hWnd, GC_REMOVETAB, 1, (LPARAM)pTemp); + + if (pTemp->hWnd) + ci.tabSession.nUsersInNicklist = 0; + + DoEventHook(pTemp->ptszID, pTemp->pszModule, GC_SESSION_TERMINATE, NULL, NULL, (DWORD)pTemp->dwItemData); + + if (pLast == NULL) + m_WndList = pTemp->next; + else + pLast->next = pTemp->next; + + ci.UM_RemoveAll(&pTemp->pUsers); + ci.TM_RemoveAll(&pTemp->pStatuses); + ci.LM_RemoveAll(&pTemp->pLog, &pTemp->pLogEnd); + pTemp->iStatusCount = 0; + pTemp->nUsersInNicklist = 0; + + // contact may have been deleted here already, since function may be called after deleting + // contact so the handle may be invalid, therefore db_get_b shall return 0 + if (pTemp->hContact && db_get_b(pTemp->hContact, pTemp->pszModule, "ChatRoom", 0) != 0) { + CList_SetOffline(pTemp->hContact, pTemp->iType == GCW_CHATROOM ? TRUE : FALSE); + db_set_s(pTemp->hContact, pTemp->pszModule, "Topic", ""); + db_set_s(pTemp->hContact, pTemp->pszModule, "StatusBar", ""); + db_unset(pTemp->hContact, "CList", "StatusMsg"); + + if (removeContact) + CallService(MS_DB_CONTACT_DELETE, (WPARAM)pTemp->hContact, 0); + } + + mir_free(pTemp->pszModule); + mir_free(pTemp->ptszID); + mir_free(pTemp->ptszName); + mir_free(pTemp->ptszStatusbarText); + mir_free(pTemp->ptszTopic); + mir_free(pTemp->pszID); + mir_free(pTemp->pszName); + + // delete commands + pCurComm = pTemp->lpCommands; + while (pCurComm != NULL) { + COMMANDINFO *pNext = pCurComm->next; + mir_free(pCurComm->lpCommand); + mir_free(pCurComm); + pCurComm = pNext; + } + + mir_free(pTemp); + if (pszID) + return (int)dw; + if (pLast) + pTemp = pLast->next; + else + pTemp = m_WndList; + } + else { + pLast = pTemp; + pTemp = pTemp->next; + } + } + return FALSE; +} + +static SESSION_INFO* SM_FindSession(const TCHAR *pszID, const char* pszModule) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszID || !pszModule) + return NULL; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) + return pTemp; + + pLast = pTemp; + pTemp = pTemp->next; + } + return NULL; +} + +static BOOL SM_SetOffline(const TCHAR *pszID, const char* pszModule) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszModule) + return FALSE; + + while (pTemp != NULL) { + if ((!pszID || !lstrcmpi(pTemp->ptszID, pszID)) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + ci.UM_RemoveAll(&pTemp->pUsers); + pTemp->nUsersInNicklist = 0; + if (pTemp->hWnd) + ci.tabSession.nUsersInNicklist = 0; + if (pTemp->iType != GCW_SERVER) + pTemp->bInitDone = FALSE; + if (ci.pSettings->TabsEnable && pTemp->hWnd) + ci.tabSession.pUsers = 0; + + if (pszID) + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + +static BOOL SM_SetStatusEx(const TCHAR *pszID, const char* pszModule, const TCHAR* pszText, int flags) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszModule) + return FALSE; + + while (pTemp != NULL) { + if ((!pszID || !lstrcmpi(pTemp->ptszID, pszID)) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + ci.UM_SetStatusEx(pTemp->pUsers, pszText, flags); + if (pTemp->hWnd) + RedrawWindow(GetDlgItem(pTemp->hWnd, IDC_LIST), NULL, NULL, RDW_INVALIDATE); + if (pszID) + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + +static HICON SM_GetStatusIcon(SESSION_INFO *si, USERINFO * ui) +{ + if (!ui || !si) + return NULL; + + STATUSINFO *ti = ci.TM_FindStatus(si->pStatuses, ci.TM_WordToString(si->pStatuses, ui->Status)); + if (ti != NULL) { + if ((INT_PTR)ti->hIcon >= STATUSICONCOUNT) + return ti->hIcon; + + int id = si->iStatusCount - (int)ti->hIcon - 1; + if (id == 0) + return ci.hIcons[ICON_STATUS0]; + if (id == 1) + return ci.hIcons[ICON_STATUS1]; + if (id == 2) + return ci.hIcons[ICON_STATUS2]; + if (id == 3) + return ci.hIcons[ICON_STATUS3]; + if (id == 4) + return ci.hIcons[ICON_STATUS4]; + if (id == 5) + return ci.hIcons[ICON_STATUS5]; + } + return ci.hIcons[ICON_STATUS0]; +} + +static BOOL SM_AddEventToAllMatchingUID(GCEVENT *gce) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + int bManyFix = 0; + + while (pTemp != NULL) { + if (!lstrcmpiA(pTemp->pszModule, gce->pDest->pszModule)) { + if (ci.UM_FindUser(pTemp->pUsers, gce->ptszUID)) { + if (pTemp->bInitDone) { + if (ci.SM_AddEvent(pTemp->ptszID, pTemp->pszModule, gce, FALSE) && pTemp->hWnd && pTemp->bInitDone) { + ci.tabSession.pLog = pTemp->pLog; + ci.tabSession.pLogEnd = pTemp->pLogEnd; + SendMessage(pTemp->hWnd, GC_ADDLOG, 0, 0); + } + else if (pTemp->hWnd && pTemp->bInitDone) { + ci.tabSession.pLog = pTemp->pLog; + ci.tabSession.pLogEnd = pTemp->pLogEnd; + SendMessage(pTemp->hWnd, GC_REDRAWLOG2, 0, 0); + } + if (!(gce->dwFlags & GCEF_NOTNOTIFY)) + DoSoundsFlashPopupTrayStuff(pTemp, gce, FALSE, bManyFix); + bManyFix++; + if ((gce->dwFlags & GCEF_ADDTOLOG) && ci.pSettings->LoggingEnabled) + LogToFile(pTemp, gce); + } + } + } + + pLast = pTemp; + pTemp = pTemp->next; + } + + return 0; +} + +static BOOL SM_AddEvent(const TCHAR *pszID, const char* pszModule, GCEVENT *gce, BOOL bIsHighlighted) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszID || !pszModule) + return TRUE; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + LOGINFO *li = ci.LM_AddEvent(&pTemp->pLog, &pTemp->pLogEnd); + pTemp->iEventCount += 1; + + li->iType = gce->pDest->iType; + li->ptszNick = mir_tstrdup(gce->ptszNick); + li->ptszText = mir_tstrdup(gce->ptszText); + li->ptszStatus = mir_tstrdup(gce->ptszStatus); + li->ptszUserInfo = mir_tstrdup(gce->ptszUserInfo); + + li->bIsMe = gce->bIsMe; + li->time = gce->time; + li->bIsHighlighted = bIsHighlighted; + + if (ci.pSettings->iEventLimit > 0 && pTemp->iEventCount > ci.pSettings->iEventLimit + 20) { + ci.LM_TrimLog(&pTemp->pLog, &pTemp->pLogEnd, pTemp->iEventCount - ci.pSettings->iEventLimit); + pTemp->iEventCount = ci.pSettings->iEventLimit; + return FALSE; + } + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + +static USERINFO* SM_AddUser(const TCHAR *pszID, const char* pszModule, const TCHAR* pszUID, const TCHAR* pszNick, WORD wStatus) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszID || !pszModule) + return NULL; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + USERINFO *p = ci.UM_AddUser(pTemp->pStatuses, &pTemp->pUsers, pszUID, pszNick, wStatus); + pTemp->nUsersInNicklist++; + if (pTemp->hWnd) + ci.tabSession.nUsersInNicklist++; + return p; + } + pLast = pTemp; + pTemp = pTemp->next; + } + + return 0; +} + +static BOOL SM_MoveUser(const TCHAR *pszID, const char* pszModule, const TCHAR* pszUID) +{ + SESSION_INFO *pTemp = m_WndList; + + if (!pszID || !pszModule || !pszUID) + return FALSE; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + ci.UM_SortUser(&pTemp->pUsers, pszUID); + return TRUE; + } + pTemp = pTemp->next; + } + + return FALSE; +} + +static BOOL SM_RemoveUser(const TCHAR *pszID, const char* pszModule, const TCHAR* pszUID) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszModule || !pszUID) + return FALSE; + + while (pTemp != NULL) { + if ((!pszID || !lstrcmpi(pTemp->ptszID, pszID)) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + DWORD dw; + USERINFO *ui = ci.UM_FindUser(pTemp->pUsers, pszUID); + if (ui) { + pTemp->nUsersInNicklist--; + if (pTemp->hWnd) { + ci.tabSession.pUsers = pTemp->pUsers; + ci.tabSession.nUsersInNicklist--; + } + + dw = ci.UM_RemoveUser(&pTemp->pUsers, pszUID); + + if (pTemp->hWnd) + SendMessage(pTemp->hWnd, GC_UPDATENICKLIST, 0, 0); + + if (pszID) + return TRUE; + } + } + + pLast = pTemp; + pTemp = pTemp->next; + } + + return 0; +} + +static USERINFO* SM_GetUserFromIndex(const TCHAR *pszID, const char* pszModule, int index) +{ + SESSION_INFO *pTemp = m_WndList; + + if (!pszModule) + return FALSE; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) + return ci.UM_FindUserFromIndex(pTemp->pUsers, index); + pTemp = pTemp->next; + } + + return NULL; +} + + +STATUSINFO * SM_AddStatus(const TCHAR *pszID, const char* pszModule, const TCHAR* pszStatus) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszID || !pszModule) + return NULL; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + STATUSINFO *ti = ci.TM_AddStatus(&pTemp->pStatuses, pszStatus, &pTemp->iStatusCount); + if (ti) + pTemp->iStatusCount++; + if (ci.pSettings->TabsEnable && pTemp->hWnd) + ci.tabSession.pStatuses = pTemp->pStatuses; + return ti; + } + pLast = pTemp; + pTemp = pTemp->next; + } + + return 0; +} + +static BOOL SM_GiveStatus(const TCHAR *pszID, const char* pszModule, const TCHAR* pszUID, const TCHAR* pszStatus) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszID || !pszModule) + return FALSE; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + USERINFO *ui = ci.UM_GiveStatus(pTemp->pUsers, pszUID, ci.TM_StringToWord(pTemp->pStatuses, pszStatus)); + if (ui) { + SM_MoveUser(pTemp->ptszID, pTemp->pszModule, ui->pszUID); + if (pTemp->hWnd) + SendMessage(pTemp->hWnd, GC_UPDATENICKLIST, 0, 0); + } + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + + return FALSE; +} + +static BOOL SM_SetContactStatus(const TCHAR *pszID, const char* pszModule, const TCHAR* pszUID, WORD wStatus) +{ + SESSION_INFO* pTemp = m_WndList, *pLast = NULL; + + if (!pszID || !pszModule) + return FALSE; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + USERINFO *ui = ci.UM_SetContactStatus(pTemp->pUsers, pszUID, wStatus); + if (ui) { + SM_MoveUser(pTemp->ptszID, pTemp->pszModule, ui->pszUID); + if (pTemp->hWnd) + SendMessage(pTemp->hWnd, GC_UPDATENICKLIST, 0, 0); + } + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + + return FALSE; +} + +static BOOL SM_TakeStatus(const TCHAR *pszID, const char* pszModule, const TCHAR* pszUID, const TCHAR* pszStatus) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszID || !pszModule) + return FALSE; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + USERINFO* ui = ci.UM_TakeStatus(pTemp->pUsers, pszUID, ci.TM_StringToWord(pTemp->pStatuses, pszStatus)); + if (ui) { + SM_MoveUser(pTemp->ptszID, pTemp->pszModule, ui->pszUID); + if (pTemp->hWnd) + SendMessage(pTemp->hWnd, GC_UPDATENICKLIST, 0, 0); + } + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + + return FALSE; +} + +static LRESULT SM_SendMessage(const TCHAR *pszID, const char* pszModule, UINT msg, WPARAM wParam, LPARAM lParam) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + while (pTemp && pszModule) { + if ((!pszID || !lstrcmpi(pTemp->ptszID, pszID)) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + if (pTemp->hWnd) { + LRESULT i = SendMessage(pTemp->hWnd, msg, wParam, lParam); + if (pszID) + return i; + } + if (pszID) + return 0; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return 0; +} + +static BOOL SM_PostMessage(const TCHAR *pszID, const char* pszModule, UINT msg, WPARAM wParam, LPARAM lParam) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszID || !pszModule) + return 0; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->ptszID, pszID) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + if (pTemp->hWnd) + return PostMessage(pTemp->hWnd, msg, wParam, lParam); + + return FALSE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return FALSE; +} + +static BOOL SM_BroadcastMessage(const char* pszModule, UINT msg, WPARAM wParam, LPARAM lParam, BOOL bAsync) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + while (pTemp != NULL) { + if (!pszModule || !lstrcmpiA(pTemp->pszModule, pszModule)) { + if (pTemp->hWnd) { + if (bAsync) + PostMessage(pTemp->hWnd, msg, wParam, lParam); + else + SendMessage(pTemp->hWnd, msg, wParam, lParam); + } + + } + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + +static BOOL SM_SetStatus(const TCHAR *pszID, const char* pszModule, int wStatus) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszModule) + return FALSE; + + while (pTemp != NULL) { + if ((!pszID || !lstrcmpi(pTemp->ptszID, pszID)) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + pTemp->wStatus = wStatus; + if (pTemp->hWnd && ci.pSettings->TabsEnable) + ci.tabSession.wStatus = wStatus; + + if (pTemp->hContact) { + if (pTemp->iType != GCW_SERVER && wStatus != ID_STATUS_OFFLINE) + db_unset(pTemp->hContact, "CList", "Hidden"); + + db_set_w(pTemp->hContact, pTemp->pszModule, "Status", (WORD)wStatus); + } + + if (ci.pSettings->TabsEnable && ci.tabSession.hWnd) + PostMessage(ci.tabSession.hWnd, GC_FIXTABICONS, 0, (LPARAM)pTemp); + + if (pszID) + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + +static BOOL SM_SendUserMessage(const TCHAR *pszID, const char* pszModule, const TCHAR* pszText) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszModule || !pszText) + return FALSE; + + while (pTemp != NULL) { + if ((!pszID || !lstrcmpi(pTemp->ptszID, pszID)) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + if (pTemp->iType == GCW_CHATROOM) + DoEventHook(pTemp->ptszID, pTemp->pszModule, GC_USER_MESSAGE, NULL, pszText, 0); + if (pszID) + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + +static SESSION_INFO* SM_GetPrevWindow(SESSION_INFO *si) +{ + BOOL bFound = FALSE; + SESSION_INFO* pTemp = m_WndList; + + if (!si) + return NULL; + + while (pTemp != NULL) { + if (si == pTemp) { + if (bFound) + return NULL; + else + bFound = TRUE; + } + else if (bFound == TRUE && pTemp->hWnd) + return pTemp; + pTemp = pTemp->next; + if (pTemp == NULL && bFound) + pTemp = m_WndList; + } + return NULL; +} + +SESSION_INFO* SM_GetNextWindow(SESSION_INFO *si) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!si) + return NULL; + + while (pTemp != NULL) { + if (si == pTemp) { + if (pLast) { + if (pLast != pTemp) + return pLast; + else + return NULL; + } + } + if (pTemp->hWnd) + pLast = pTemp; + pTemp = pTemp->next; + if (pTemp == NULL) + pTemp = m_WndList; + } + return NULL; +} + +static BOOL SM_ChangeUID(const TCHAR *pszID, const char* pszModule, const TCHAR* pszUID, const TCHAR* pszNewUID) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszModule) + return FALSE; + + while (pTemp != NULL) { + if ((!pszID || !lstrcmpi(pTemp->ptszID, pszID)) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + USERINFO* ui = ci.UM_FindUser(pTemp->pUsers, pszUID); + if (ui) + replaceStrT(ui->pszUID, pszNewUID); + + if (pszID) + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + + +static BOOL SM_SetTabbedWindowHwnd(SESSION_INFO *si, HWND hwnd) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + while (pTemp != NULL) { + if (si && si == pTemp) { + pTemp->hWnd = hwnd; + } + else + pTemp->hWnd = NULL; + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + +static BOOL SM_ChangeNick(const TCHAR *pszID, const char* pszModule, GCEVENT *gce) +{ + SESSION_INFO *pTemp = m_WndList, *pLast = NULL; + + if (!pszModule) + return FALSE; + + while (pTemp != NULL) { + if ((!pszID || !lstrcmpi(pTemp->ptszID, pszID)) && !lstrcmpiA(pTemp->pszModule, pszModule)) { + USERINFO* ui = ci.UM_FindUser(pTemp->pUsers, gce->ptszUID); + if (ui) { + replaceStrT(ui->pszNick, gce->ptszText); + SM_MoveUser(pTemp->ptszID, pTemp->pszModule, ui->pszUID); + if (pTemp->hWnd) + SendMessage(pTemp->hWnd, GC_UPDATENICKLIST, 0, 0); + } + + if (pszID) + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + +static BOOL SM_RemoveAll(void) +{ + while (m_WndList) { + SESSION_INFO*pLast = m_WndList->next; + + if (m_WndList->hWnd) + SendMessage(m_WndList->hWnd, GC_EVENT_CONTROL + WM_USER + 500, SESSION_TERMINATE, 0); + DoEventHook(m_WndList->ptszID, m_WndList->pszModule, GC_SESSION_TERMINATE, NULL, NULL, (DWORD)m_WndList->dwItemData); + if (m_WndList->hContact) + CList_SetOffline(m_WndList->hContact, m_WndList->iType == GCW_CHATROOM ? TRUE : FALSE); + db_set_s(m_WndList->hContact, m_WndList->pszModule, "Topic", ""); + db_unset(m_WndList->hContact, "CList", "StatusMsg"); + db_set_s(m_WndList->hContact, m_WndList->pszModule, "StatusBar", ""); + + ci.UM_RemoveAll(&m_WndList->pUsers); + ci.TM_RemoveAll(&m_WndList->pStatuses); + ci.LM_RemoveAll(&m_WndList->pLog, &m_WndList->pLogEnd); + m_WndList->iStatusCount = 0; + m_WndList->nUsersInNicklist = 0; + + mir_free(m_WndList->pszModule); + mir_free(m_WndList->ptszID); + mir_free(m_WndList->ptszName); + mir_free(m_WndList->ptszStatusbarText); + mir_free(m_WndList->ptszTopic); + mir_free(m_WndList->pszID); + mir_free(m_WndList->pszName); + + while (m_WndList->lpCommands != NULL) { + COMMANDINFO *pNext = m_WndList->lpCommands->next; + mir_free(m_WndList->lpCommands->lpCommand); + mir_free(m_WndList->lpCommands); + m_WndList->lpCommands = pNext; + } + + mir_free(m_WndList); + m_WndList = pLast; + } + m_WndList = NULL; + return TRUE; +} + +static void SM_AddCommand(const TCHAR *pszID, const char* pszModule, const char* lpNewCommand) +{ + SESSION_INFO* pTemp = m_WndList; + while (pTemp != NULL) { + if (lstrcmpi(pTemp->ptszID, pszID) == 0 && lstrcmpiA(pTemp->pszModule, pszModule) == 0) { // match + COMMANDINFO *node = (COMMANDINFO *)mir_alloc(sizeof(COMMANDINFO)); + node->lpCommand = mir_strdup(lpNewCommand); + node->last = NULL; // always added at beginning! + + // new commands are added at start + if (pTemp->lpCommands == NULL) { + node->next = NULL; + pTemp->lpCommands = node; + } + else { + node->next = pTemp->lpCommands; + pTemp->lpCommands->last = node; // hmm, weird + pTemp->lpCommands = node; + } + pTemp->lpCurrentCommand = NULL; // current command + pTemp->wCommandsNum++; + + if (pTemp->wCommandsNum > WINDOWS_COMMANDS_MAX) { + COMMANDINFO *pCurComm = pTemp->lpCommands; + COMMANDINFO *pLast; + while (pCurComm->next != NULL) { pCurComm = pCurComm->next; } + pLast = pCurComm->last; + mir_free(pCurComm->lpCommand); + mir_free(pCurComm); + pLast->next = NULL; + // done + pTemp->wCommandsNum--; + } + } + pTemp = pTemp->next; + } +} + +static char* SM_GetPrevCommand(const TCHAR *pszID, const char* pszModule) // get previous command. returns NULL if previous command does not exist. current command remains as it was. +{ + SESSION_INFO* pTemp = m_WndList; + while (pTemp != NULL) { + if (lstrcmpi(pTemp->ptszID, pszID) == 0 && lstrcmpiA(pTemp->pszModule, pszModule) == 0) { // match + COMMANDINFO *pPrevCmd = NULL; + if (pTemp->lpCurrentCommand != NULL) { + if (pTemp->lpCurrentCommand->next != NULL) // not NULL + pPrevCmd = pTemp->lpCurrentCommand->next; // next command (newest at beginning) + else + pPrevCmd = pTemp->lpCurrentCommand; + } + else pPrevCmd = pTemp->lpCommands; + + pTemp->lpCurrentCommand = pPrevCmd; // make it the new command + return(((pPrevCmd) ? (pPrevCmd->lpCommand) : (NULL))); + } + pTemp = pTemp->next; + } + return(NULL); +} + +static char* SM_GetNextCommand(const TCHAR *pszID, const char* pszModule) // get next command. returns NULL if next command does not exist. current command becomes NULL (a prev command after this one will get you the last command) +{ + SESSION_INFO* pTemp = m_WndList; + while (pTemp != NULL) { + if (lstrcmpi(pTemp->ptszID, pszID) == 0 && lstrcmpiA(pTemp->pszModule, pszModule) == 0) { // match + COMMANDINFO *pNextCmd = NULL; + if (pTemp->lpCurrentCommand != NULL) + pNextCmd = pTemp->lpCurrentCommand->last; // last command (newest at beginning) + + pTemp->lpCurrentCommand = pNextCmd; // make it the new command + return(((pNextCmd) ? (pNextCmd->lpCommand) : (NULL))); + } + pTemp = pTemp->next; + } + return(NULL); +} + +static int SM_GetCount(const char* pszModule) +{ + SESSION_INFO* pTemp = m_WndList; + int count = 0; + + while (pTemp != NULL) { + if (!lstrcmpiA(pszModule, pTemp->pszModule)) + count++; + + pTemp = pTemp->next; + } + return count; +} + +static SESSION_INFO* SM_FindSessionByIndex(const char* pszModule, int iItem) +{ + SESSION_INFO* pTemp = m_WndList; + int count = 0; + while (pTemp != NULL) { + if (!lstrcmpiA(pszModule, pTemp->pszModule)) { + if (iItem == count) + return pTemp; + else + count++; + } + + pTemp = pTemp->next; + } + return NULL; + +} + +static char* SM_GetUsers(SESSION_INFO *si) +{ + SESSION_INFO* pTemp = m_WndList; + USERINFO* utemp = NULL; + char* p = NULL; + int alloced = 0; + + if (si == NULL) + return NULL; + + while (pTemp != NULL) { + if (si == pTemp) { + if ((utemp = pTemp->pUsers) == NULL) + return NULL; + + break; + } + pTemp = pTemp->next; + } + + do { + int pLen = lstrlenA(p), nameLen = lstrlen(utemp->pszUID); + if (pLen + nameLen + 2 > alloced) + p = (char *)mir_realloc(p, alloced += 4096); + + WideCharToMultiByte(CP_ACP, 0, utemp->pszUID, -1, p + pLen, nameLen + 1, 0, 0); + lstrcpyA(p + pLen + nameLen, " "); + utemp = utemp->next; + } + while (utemp != NULL); + return p; +} + + + + + + +//--------------------------------------------------- +// Module Manager functions +// +// Necessary to keep track of all modules +// that has registered with the plugin +//--------------------------------------------------- + +static MODULEINFO* MM_AddModule(const char* pszModule) +{ + if (!pszModule) + return NULL; + if (!ci.MM_FindModule(pszModule)) { + MODULEINFO *node = (MODULEINFO*)mir_alloc(sizeof(MODULEINFO)); + ZeroMemory(node, sizeof(MODULEINFO)); + + node->pszModule = (char*)mir_alloc(lstrlenA(pszModule) + 1); + lstrcpyA(node->pszModule, pszModule); + + if (m_ModList == NULL) // list is empty + { + m_ModList = node; + node->next = NULL; + } + else { + node->next = m_ModList; + m_ModList = node; + } + return node; + } + return FALSE; +} + +static void MM_IconsChanged(void) +{ + MODULEINFO *pTemp = m_ModList, *pLast = NULL; + ImageList_ReplaceIcon(ci.hIconsList, 0, LoadSkinnedIcon(SKINICON_EVENT_MESSAGE)); + // ImageList_ReplaceIcon(ci.hIconsList, 1, LoadIconEx("overlay", FALSE)); + while (pTemp != NULL) { + pTemp->OnlineIconIndex = ImageList_ReplaceIcon(ci.hIconsList, pTemp->OnlineIconIndex, LoadSkinnedProtoIcon(pTemp->pszModule, ID_STATUS_ONLINE)); + pTemp->OfflineIconIndex = ImageList_ReplaceIcon(ci.hIconsList, pTemp->OfflineIconIndex, LoadSkinnedProtoIcon(pTemp->pszModule, ID_STATUS_OFFLINE)); + + if (pTemp->hOfflineIcon) + DestroyIcon(pTemp->hOfflineIcon); + if (pTemp->hOnlineIcon) + DestroyIcon(pTemp->hOnlineIcon); + if (pTemp->hOnlineTalkIcon) + DestroyIcon(pTemp->hOnlineTalkIcon); + if (pTemp->hOfflineTalkIcon) + DestroyIcon(pTemp->hOfflineTalkIcon); + pTemp->hOfflineIcon = ImageList_GetIcon(ci.hIconsList, pTemp->OfflineIconIndex, ILD_TRANSPARENT); + pTemp->hOnlineIcon = ImageList_GetIcon(ci.hIconsList, pTemp->OnlineIconIndex, ILD_TRANSPARENT); + + pTemp->hOnlineTalkIcon = ImageList_GetIcon(ci.hIconsList, pTemp->OnlineIconIndex, ILD_TRANSPARENT | INDEXTOOVERLAYMASK(1)); + ImageList_ReplaceIcon(ci.hIconsList, pTemp->OnlineIconIndex + 1, pTemp->hOnlineTalkIcon); + + pTemp->hOfflineTalkIcon = ImageList_GetIcon(ci.hIconsList, pTemp->OfflineIconIndex, ILD_TRANSPARENT | INDEXTOOVERLAYMASK(1)); + ImageList_ReplaceIcon(ci.hIconsList, pTemp->OfflineIconIndex + 1, pTemp->hOfflineTalkIcon); + + pLast = pTemp; + pTemp = pTemp->next; + } + return; +} + +static void MM_FontsChanged(void) +{ + MODULEINFO *pTemp = m_ModList; + while (pTemp != NULL) { + pTemp->pszHeader = Log_CreateRtfHeader(pTemp); + pTemp = pTemp->next; + } + return; +} + +static MODULEINFO* MM_FindModule(const char* pszModule) +{ + MODULEINFO *pTemp = m_ModList, *pLast = NULL; + + if (!pszModule) + return NULL; + + while (pTemp != NULL) { + if (lstrcmpiA(pTemp->pszModule, pszModule) == 0) + return pTemp; + + pLast = pTemp; + pTemp = pTemp->next; + } + return 0; +} + +// stupid thing.. +static void MM_FixColors() +{ + MODULEINFO *pTemp = m_ModList; + + while (pTemp != NULL) { + CheckColorsInModule(pTemp->pszModule); + pTemp = pTemp->next; + } + return; +} + +static BOOL MM_RemoveAll(void) +{ + while (m_ModList != NULL) { + MODULEINFO *pLast = m_ModList->next; + mir_free(m_ModList->pszModule); + mir_free(m_ModList->ptszModDispName); + mir_free(m_ModList->pszHeader); + mir_free(m_ModList->crColors); + + if (m_ModList->hOfflineIcon) + DestroyIcon(m_ModList->hOfflineIcon); + if (m_ModList->hOnlineIcon) + DestroyIcon(m_ModList->hOnlineIcon); + if (m_ModList->hOnlineTalkIcon) + DestroyIcon(m_ModList->hOnlineTalkIcon); + if (m_ModList->hOfflineTalkIcon) + DestroyIcon(m_ModList->hOfflineTalkIcon); + + mir_free(m_ModList); + m_ModList = pLast; + } + m_ModList = NULL; + return TRUE; +} + + + +//--------------------------------------------------- +// Tab list manager functions +// +// Necessary to keep track of what tabs should +// be restored +//--------------------------------------------------- + +static BOOL TabM_AddTab(const TCHAR *pszID, const char* pszModule) +{ + TABLIST *node = NULL; + if (!pszID || !pszModule) + return FALSE; + + node = (TABLIST*)mir_alloc(sizeof(TABLIST)); + ZeroMemory(node, sizeof(TABLIST)); + node->pszID = mir_tstrdup(pszID); + node->pszModule = mir_strdup(pszModule); + + if (g_TabList == NULL) { // list is empty + g_TabList = node; + node->next = NULL; + } + else { + node->next = g_TabList; + g_TabList = node; + } + return TRUE; +} + +static BOOL TabM_RemoveAll(void) +{ + while (g_TabList != NULL) { + TABLIST * pLast = g_TabList->next; + mir_free(g_TabList->pszModule); + mir_free(g_TabList->pszID); + mir_free(g_TabList); + g_TabList = pLast; + } + g_TabList = NULL; + return TRUE; +} + +//--------------------------------------------------- +// Status manager functions +// +// Necessary to keep track of what user statuses +// per window nicklist that is available +//--------------------------------------------------- + +static STATUSINFO * TM_AddStatus(STATUSINFO** ppStatusList, const TCHAR* pszStatus, int* iCount) +{ + if (!ppStatusList || !pszStatus) + return NULL; + + if (!ci.TM_FindStatus(*ppStatusList, pszStatus)) { + STATUSINFO *node = (STATUSINFO*)mir_alloc(sizeof(STATUSINFO)); + ZeroMemory(node, sizeof(STATUSINFO)); + replaceStrT(node->pszGroup, pszStatus); + node->hIcon = (HICON)(*iCount); + while ((int)node->hIcon > STATUSICONCOUNT - 1) + node->hIcon--; + + if (*ppStatusList == NULL) // list is empty + { + node->Status = 1; + *ppStatusList = node; + node->next = NULL; + } + else { + node->Status = ppStatusList[0]->Status * 2; + node->next = *ppStatusList; + *ppStatusList = node; + } + return node; + + } + return FALSE; +} + +static STATUSINFO* TM_FindStatus(STATUSINFO* pStatusList, const TCHAR* pszStatus) +{ + STATUSINFO *pTemp = pStatusList, *pLast = NULL; + + if (!pStatusList || !pszStatus) + return NULL; + + while (pTemp != NULL) { + if (lstrcmpi(pTemp->pszGroup, pszStatus) == 0) + return pTemp; + + pLast = pTemp; + pTemp = pTemp->next; + } + return 0; +} + +static WORD TM_StringToWord(STATUSINFO* pStatusList, const TCHAR* pszStatus) +{ + STATUSINFO *pTemp = pStatusList, *pLast = NULL; + + if (!pStatusList || !pszStatus) + return 0; + + while (pTemp != NULL) { + if (lstrcmpi(pTemp->pszGroup, pszStatus) == 0) + return pTemp->Status; + + if (pTemp->next == NULL) + return pStatusList->Status; + + pLast = pTemp; + pTemp = pTemp->next; + } + return 0; +} + +static TCHAR* TM_WordToString(STATUSINFO* pStatusList, WORD Status) +{ + STATUSINFO *pTemp = pStatusList, *pLast = NULL; + + if (!pStatusList) + return NULL; + + while (pTemp != NULL) { + if (pTemp->Status&Status) { + Status -= pTemp->Status; + if (Status == 0) + return pTemp->pszGroup; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return 0; +} + +static BOOL TM_RemoveAll(STATUSINFO** ppStatusList) +{ + + if (!ppStatusList) + return FALSE; + + while (*ppStatusList != NULL) { + STATUSINFO *pLast = ppStatusList[0]->next; + mir_free(ppStatusList[0]->pszGroup); + if ((int)ppStatusList[0]->hIcon > 10) + DestroyIcon(ppStatusList[0]->hIcon); + mir_free(*ppStatusList); + *ppStatusList = pLast; + } + *ppStatusList = NULL; + return TRUE; +} + +//--------------------------------------------------- +// User manager functions +// +// Necessary to keep track of the users +// in a window nicklist +//--------------------------------------------------- + +static int UM_CompareItem(USERINFO * u1, const TCHAR* pszNick, WORD wStatus) +{ + int i; + + WORD dw1 = u1->Status; + WORD dw2 = wStatus; + + for (i = 0; i < 8; i++) { + if ((dw1 & 1) && !(dw2 & 1)) + return -1; + if ((dw2 & 1) && !(dw1 & 1)) + return 1; + if ((dw1 & 1) && (dw2 & 1)) + return lstrcmp(u1->pszNick, pszNick); + + dw1 = dw1 >> 1; + dw2 = dw2 >> 1; + } + return lstrcmp(u1->pszNick, pszNick); +} + +static USERINFO * UM_SortUser(USERINFO** ppUserList, const TCHAR* pszUID) +{ + USERINFO * pTemp = *ppUserList, *pLast = NULL; + USERINFO * node = NULL; + + if (!pTemp || !pszUID) + return NULL; + + while (pTemp && lstrcmpi(pTemp->pszUID, pszUID)) { + pLast = pTemp; + pTemp = pTemp->next; + } + + if (pTemp) { + node = pTemp; + if (pLast) + pLast->next = pTemp->next; + else + *ppUserList = pTemp->next; + pTemp = *ppUserList; + + pLast = NULL; + + while (pTemp && UM_CompareItem(pTemp, node->pszNick, node->Status) <= 0) { + pLast = pTemp; + pTemp = pTemp->next; + } + + if (*ppUserList == NULL) { // list is empty + *ppUserList = node; + node->next = NULL; + } + else { + if (pLast) { + node->next = pTemp; + pLast->next = node; + } + else { + node->next = *ppUserList; + *ppUserList = node; + } + } + + return node; + } + return NULL; +} + +USERINFO* UM_AddUser(STATUSINFO* pStatusList, USERINFO** ppUserList, const TCHAR* pszUID, const TCHAR* pszNick, WORD wStatus) +{ + USERINFO * pTemp = *ppUserList, *pLast = NULL; + + if (!pStatusList || !ppUserList) + return NULL; + + while (pTemp && UM_CompareItem(pTemp, pszNick, wStatus) <= 0) { + pLast = pTemp; + pTemp = pTemp->next; + } + + // if (!UM_FindUser(*ppUserList, pszUI, wStatus) + USERINFO *node = (USERINFO*)mir_alloc(sizeof(USERINFO)); + ZeroMemory(node, sizeof(USERINFO)); + replaceStrT(node->pszUID, pszUID); + + if (*ppUserList == NULL) { // list is empty + *ppUserList = node; + node->next = NULL; + } + else { + if (pLast) { + node->next = pTemp; + pLast->next = node; + } + else { + node->next = *ppUserList; + *ppUserList = node; + } + } + + return node; +} + +static USERINFO* UM_FindUser(USERINFO* pUserList, const TCHAR* pszUID) +{ + USERINFO *pTemp = pUserList, *pLast = NULL; + + if (!pUserList || !pszUID) + return NULL; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->pszUID, pszUID)) + return pTemp; + + pLast = pTemp; + pTemp = pTemp->next; + } + return 0; +} + +static USERINFO* UM_FindUserFromIndex(USERINFO* pUserList, int index) +{ + if (!pUserList) + return NULL; + + int i = 0; + USERINFO *pTemp = pUserList; + while (pTemp != NULL) { + if (i == index) + return pTemp; + + pTemp = pTemp->next; + i++; + } + return NULL; +} + +static USERINFO* UM_GiveStatus(USERINFO* pUserList, const TCHAR* pszUID, WORD status) +{ + if (!pUserList || !pszUID) + return NULL; + + USERINFO *pTemp = pUserList, *pLast = NULL; + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->pszUID, pszUID)) { + pTemp->Status |= status; + return pTemp; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return 0; +} + +static USERINFO* UM_SetContactStatus(USERINFO* pUserList, const TCHAR* pszUID, WORD status) +{ + USERINFO *pTemp = pUserList, *pLast = NULL; + + if (!pUserList || !pszUID) + return NULL; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->pszUID, pszUID)) { + pTemp->ContactStatus = status; + return pTemp; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return 0; +} + +static BOOL UM_SetStatusEx(USERINFO* pUserList, const TCHAR* pszText, int flags) +{ + USERINFO *pTemp = pUserList, *pLast = NULL; + int bOnlyMe = (flags & GC_SSE_ONLYLISTED) != 0, bSetStatus = (flags & GC_SSE_ONLINE) != 0; + char cDelimiter = (flags & GC_SSE_TABDELIMITED) ? '\t' : ' '; + + while (pTemp != NULL) { + if (!bOnlyMe) + pTemp->iStatusEx = 0; + + if (pszText != NULL) { + TCHAR* s = (TCHAR *)_tcsstr(pszText, pTemp->pszUID); + if (s) { + pTemp->iStatusEx = 0; + if (s == pszText || s[-1] == cDelimiter) { + int len = lstrlen(pTemp->pszUID); + if (s[len] == cDelimiter || s[len] == '\0') + pTemp->iStatusEx = (!bOnlyMe || bSetStatus) ? 1 : 0; + } + } + } + + pLast = pTemp; + pTemp = pTemp->next; + } + return TRUE; +} + +static USERINFO* UM_TakeStatus(USERINFO* pUserList, const TCHAR* pszUID, WORD status) +{ + if (!pUserList || !pszUID) + return NULL; + + USERINFO *pTemp = pUserList, *pLast = NULL; + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->pszUID, pszUID)) { + pTemp->Status &= ~status; + return pTemp; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return 0; +} + +static TCHAR* UM_FindUserAutoComplete(USERINFO* pUserList, const TCHAR* pszOriginal, const TCHAR* pszCurrent) +{ + if (!pUserList || !pszOriginal || !pszCurrent) + return NULL; + + TCHAR* pszName = NULL; + USERINFO *pTemp = pUserList; + while (pTemp != NULL) { + if (pTemp->pszNick && my_strstri(pTemp->pszNick, pszOriginal) == pTemp->pszNick) + if (lstrcmpi(pTemp->pszNick, pszCurrent) > 0 && (!pszName || lstrcmpi(pTemp->pszNick, pszName) < 0)) + pszName = pTemp->pszNick; + + pTemp = pTemp->next; + } + return pszName; +} + +static BOOL UM_RemoveUser(USERINFO** ppUserList, const TCHAR* pszUID) +{ + USERINFO *pTemp = *ppUserList, *pLast = NULL; + + if (!ppUserList || !pszUID) + return FALSE; + + while (pTemp != NULL) { + if (!lstrcmpi(pTemp->pszUID, pszUID)) { + if (pLast == NULL) + *ppUserList = pTemp->next; + else + pLast->next = pTemp->next; + mir_free(pTemp->pszNick); + mir_free(pTemp->pszUID); + mir_free(pTemp); + return TRUE; + } + pLast = pTemp; + pTemp = pTemp->next; + } + return FALSE; +} + +static BOOL UM_RemoveAll(USERINFO** ppUserList) +{ + if (!ppUserList) + return FALSE; + + while (*ppUserList != NULL) { + USERINFO *pLast = ppUserList[0]->next; + mir_free(ppUserList[0]->pszUID); + mir_free(ppUserList[0]->pszNick); + mir_free(*ppUserList); + *ppUserList = pLast; + } + *ppUserList = NULL; + return TRUE; +} + +//--------------------------------------------------- +// Log manager functions +// +// Necessary to keep track of events +// in a window log +//--------------------------------------------------- + +static LOGINFO* LM_AddEvent(LOGINFO** ppLogListStart, LOGINFO** ppLogListEnd) +{ + if (!ppLogListStart || !ppLogListEnd) + return NULL; + + LOGINFO *node = (LOGINFO*)mir_calloc(sizeof(LOGINFO)); + if (*ppLogListStart == NULL) { // list is empty + *ppLogListStart = node; + *ppLogListEnd = node; + node->next = NULL; + node->prev = NULL; + } + else { + ppLogListStart[0]->prev = node; + node->next = *ppLogListStart; + *ppLogListStart = node; + ppLogListStart[0]->prev = NULL; + } + + return node; +} + +static BOOL LM_TrimLog(LOGINFO** ppLogListStart, LOGINFO** ppLogListEnd, int iCount) +{ + LOGINFO *pTemp = *ppLogListEnd; + while (pTemp != NULL && iCount > 0) { + *ppLogListEnd = pTemp->prev; + if (*ppLogListEnd == NULL) + *ppLogListStart = NULL; + + mir_free(pTemp->ptszNick); + mir_free(pTemp->ptszUserInfo); + mir_free(pTemp->ptszText); + mir_free(pTemp->ptszStatus); + mir_free(pTemp); + pTemp = *ppLogListEnd; + iCount--; + } + ppLogListEnd[0]->next = NULL; + + return TRUE; +} + +static BOOL LM_RemoveAll(LOGINFO** ppLogListStart, LOGINFO** ppLogListEnd) +{ + while (*ppLogListStart != NULL) { + LOGINFO *pLast = ppLogListStart[0]->next; + mir_free(ppLogListStart[0]->ptszText); + mir_free(ppLogListStart[0]->ptszNick); + mir_free(ppLogListStart[0]->ptszStatus); + mir_free(ppLogListStart[0]->ptszUserInfo); + mir_free(*ppLogListStart); + *ppLogListStart = pLast; + } + *ppLogListStart = NULL; + *ppLogListEnd = NULL; + return TRUE; +} + +CHAT_MANAGER ci = +{ + SetActiveSession, + SetActiveSessionEx, + GetActiveSession, + SM_AddSession, + SM_RemoveSession, + SM_FindSession, + SM_AddUser, + SM_ChangeUID, + SM_ChangeNick, + SM_RemoveUser, + SM_SetOffline, + SM_SetTabbedWindowHwnd, + SM_GetStatusIcon, + SM_SetStatus, + SM_SetStatusEx, + SM_SendUserMessage, + SM_AddStatus, + SM_GetNextWindow, + SM_GetPrevWindow, + SM_AddEventToAllMatchingUID, + SM_AddEvent, + SM_SendMessage, + SM_PostMessage, + SM_BroadcastMessage, + SM_RemoveAll, + SM_GiveStatus, + SM_SetContactStatus, + SM_TakeStatus, + SM_MoveUser, + SM_AddCommand, + SM_GetPrevCommand, + SM_GetNextCommand, + SM_GetCount, + SM_FindSessionByIndex, + SM_GetUsers, + SM_GetUserFromIndex, + + MM_AddModule, + MM_FindModule, + MM_FixColors, + MM_FontsChanged, + MM_IconsChanged, + MM_RemoveAll, + + TabM_AddTab, + TabM_RemoveAll, + + TM_AddStatus, + TM_FindStatus, + TM_StringToWord, + TM_WordToString, + TM_RemoveAll, + + UM_SetStatusEx, + UM_AddUser, + UM_SortUser, + UM_FindUser, + UM_FindUserFromIndex, + UM_GiveStatus, + UM_SetContactStatus, + UM_TakeStatus, + UM_FindUserAutoComplete, + UM_RemoveUser, + UM_RemoveAll, + + LM_AddEvent, + LM_TrimLog, + LM_RemoveAll +}; + +INT_PTR SvcGetChatManager(WPARAM, LPARAM) +{ + return (INT_PTR)&ci; +} diff --git a/src/modules/chat/tools.cpp b/src/modules/chat/tools.cpp new file mode 100644 index 0000000000..e2775403d9 --- /dev/null +++ b/src/modules/chat/tools.cpp @@ -0,0 +1,761 @@ +/* +Chat module plugin for Miranda IM + +Copyright 2000-12 Miranda IM, 2012-14 Miranda NG project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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 "..\..\core\commonheaders.h" + +#include "chat.h" + +int GetRichTextLength(HWND hwnd) +{ + GETTEXTLENGTHEX gtl; + gtl.flags = GTL_PRECISE; + gtl.codepage = CP_ACP; + return (int)SendMessage(hwnd, EM_GETTEXTLENGTHEX, (WPARAM)>l, 0); +} + +TCHAR* RemoveFormatting(const TCHAR* pszWord) +{ + static TCHAR szTemp[10000]; + int i = 0; + int j = 0; + + if (pszWord == 0 || lstrlen(pszWord) == 0) + return NULL; + + while (j < 9999 && i <= lstrlen(pszWord)) { + if (pszWord[i] == '%') { + switch (pszWord[i + 1]) { + case '%': + szTemp[j] = '%'; + j++; + i++; i++; + break; + case 'b': + case 'u': + case 'i': + case 'B': + case 'U': + case 'I': + case 'r': + case 'C': + case 'F': + i++; i++; + break; + + case 'c': + case 'f': + i += 4; + break; + + default: + szTemp[j] = pszWord[i]; + j++; + i++; + break; + } + } + else { + szTemp[j] = pszWord[i]; + j++; + i++; + } + } + + return (TCHAR*)&szTemp; +} + +static void __stdcall ShowRoomFromPopup(void * pi) +{ + SESSION_INFO *si = (SESSION_INFO*)pi; + ShowRoom(si, WINDOW_VISIBLE, TRUE); +} + +static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) { + case WM_COMMAND: + if (HIWORD(wParam) == STN_CLICKED) { + SESSION_INFO *si = (SESSION_INFO*)PUGetPluginData(hWnd); + CallFunctionAsync(ShowRoomFromPopup, si); + + PUDeletePopup(hWnd); + return TRUE; + } + break; + case WM_CONTEXTMENU: + SESSION_INFO *si = (SESSION_INFO*)PUGetPluginData(hWnd); + if (si->hContact) + if (CallService(MS_CLIST_GETEVENT, (WPARAM)si->hContact, 0)) + CallService(MS_CLIST_REMOVEEVENT, (WPARAM)si->hContact, (LPARAM)"chaticon"); + + if (si->hWnd && KillTimer(si->hWnd, TIMERID_FLASHWND)) + FlashWindow(si->hWnd, FALSE); + + PUDeletePopup(hWnd); + break; + } + return DefWindowProc(hWnd, message, wParam, lParam); +} + +static int ShowPopup(HANDLE hContact, SESSION_INFO *si, HICON hIcon, char* pszProtoName, TCHAR* pszRoomName, COLORREF crBkg, const TCHAR* fmt, ...) +{ + static TCHAR szBuf[4 * 1024]; + + if (!fmt || lstrlen(fmt) == 0 || lstrlen(fmt) > 2000) + return 0; + + va_list marker; + va_start(marker, fmt); + mir_vsntprintf(szBuf, 4096, fmt, marker); + va_end(marker); + + POPUPDATAT pd = { 0 }; + pd.lchContact = hContact; + + if (hIcon) + pd.lchIcon = hIcon; + else + pd.lchIcon = LoadIconEx("window", FALSE); + + PROTOACCOUNT *pa = ProtoGetAccount(pszProtoName); + mir_sntprintf(pd.lptzContactName, MAX_CONTACTNAME - 1, _T("%s - %s"), + (pa == NULL) ? _A2T(pszProtoName) : pa->tszAccountName, + cli.pfnGetContactDisplayName(hContact, 0)); + + lstrcpyn(pd.lptzText, TranslateTS(szBuf), MAX_SECONDLINE); + pd.iSeconds = ci.pSettings->iPopupTimeout; + + if (ci.pSettings->iPopupStyle == 2) { + pd.colorBack = 0; + pd.colorText = 0; + } + else if (ci.pSettings->iPopupStyle == 3) { + pd.colorBack = ci.pSettings->crPUBkgColour; + pd.colorText = ci.pSettings->crPUTextColour; + } + else { + pd.colorBack = ci.pSettings->crLogBackground; + pd.colorText = crBkg; + } + + pd.PluginWindowProc = PopupDlgProc; + pd.PluginData = si; + return PUAddPopupT(&pd); +} + +static BOOL DoTrayIcon(SESSION_INFO *si, GCEVENT *gce) +{ + int iEvent = gce->pDest->iType; + + if (iEvent&ci.pSettings->dwTrayIconFlags) { + switch (iEvent) { + case GC_EVENT_MESSAGE | GC_EVENT_HIGHLIGHT: + case GC_EVENT_ACTION | GC_EVENT_HIGHLIGHT: + CList_AddEvent(si->hContact, LoadSkinnedIcon(SKINICON_EVENT_MESSAGE), "chaticon", 0, TranslateT("%s wants your attention in %s"), gce->ptszNick, si->ptszName); + break; + case GC_EVENT_MESSAGE: + CList_AddEvent(si->hContact, ci.hIcons[ICON_MESSAGE], "chaticon", CLEF_ONLYAFEW, TranslateT("%s speaks in %s"), gce->ptszNick, si->ptszName); + break; + case GC_EVENT_ACTION: + CList_AddEvent(si->hContact, ci.hIcons[ICON_ACTION], "chaticon", CLEF_ONLYAFEW, TranslateT("%s speaks in %s"), gce->ptszNick, si->ptszName); + break; + case GC_EVENT_JOIN: + CList_AddEvent(si->hContact, ci.hIcons[ICON_JOIN], "chaticon", CLEF_ONLYAFEW, TranslateT("%s has joined %s"), gce->ptszNick, si->ptszName); + break; + case GC_EVENT_PART: + CList_AddEvent(si->hContact, ci.hIcons[ICON_PART], "chaticon", CLEF_ONLYAFEW, TranslateT("%s has left %s"), gce->ptszNick, si->ptszName); + break; + case GC_EVENT_QUIT: + CList_AddEvent(si->hContact, ci.hIcons[ICON_QUIT], "chaticon", CLEF_ONLYAFEW, TranslateT("%s has disconnected"), gce->ptszNick); + break; + case GC_EVENT_NICK: + CList_AddEvent(si->hContact, ci.hIcons[ICON_NICK], "chaticon", CLEF_ONLYAFEW, TranslateT("%s is now known as %s"), gce->ptszNick, gce->ptszText); + break; + case GC_EVENT_KICK: + CList_AddEvent(si->hContact, ci.hIcons[ICON_KICK], "chaticon", CLEF_ONLYAFEW, TranslateT("%s kicked %s from %s"), gce->ptszStatus, gce->ptszNick, si->ptszName); + break; + case GC_EVENT_NOTICE: + CList_AddEvent(si->hContact, ci.hIcons[ICON_NOTICE], "chaticon", CLEF_ONLYAFEW, TranslateT("Notice from %s"), gce->ptszNick); + break; + case GC_EVENT_TOPIC: + CList_AddEvent(si->hContact, ci.hIcons[ICON_TOPIC], "chaticon", CLEF_ONLYAFEW, TranslateT("Topic change in %s"), si->ptszName); + break; + case GC_EVENT_INFORMATION: + CList_AddEvent(si->hContact, ci.hIcons[ICON_INFO], "chaticon", CLEF_ONLYAFEW, TranslateT("Information in %s"), si->ptszName); + break; + case GC_EVENT_ADDSTATUS: + CList_AddEvent(si->hContact, ci.hIcons[ICON_ADDSTATUS], "chaticon", CLEF_ONLYAFEW, TranslateT("%s enables \'%s\' status for %s in %s"), gce->ptszText, gce->ptszStatus, gce->ptszNick, si->ptszName); + break; + case GC_EVENT_REMOVESTATUS: + CList_AddEvent(si->hContact, ci.hIcons[ICON_REMSTATUS], "chaticon", CLEF_ONLYAFEW, TranslateT("%s disables \'%s\' status for %s in %s"), gce->ptszText, gce->ptszStatus, gce->ptszNick, si->ptszName); + break; + } + } + + return TRUE; +} + +static BOOL DoPopup(SESSION_INFO *si, GCEVENT *gce) +{ + int iEvent = gce->pDest->iType; + + if (iEvent & ci.pSettings->dwPopupFlags) { + switch (iEvent) { + case GC_EVENT_MESSAGE | GC_EVENT_HIGHLIGHT: + ShowPopup(si->hContact, si, LoadSkinnedIcon(SKINICON_EVENT_MESSAGE), si->pszModule, si->ptszName, ci.aFonts[16].color, TranslateT("%s says: %s"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_ACTION | GC_EVENT_HIGHLIGHT: + ShowPopup(si->hContact, si, LoadSkinnedIcon(SKINICON_EVENT_MESSAGE), si->pszModule, si->ptszName, ci.aFonts[16].color, _T("%s %s"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_MESSAGE: + ShowPopup(si->hContact, si, ci.hIcons[ICON_MESSAGE], si->pszModule, si->ptszName, ci.aFonts[9].color, TranslateT("%s says: %s"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_ACTION: + ShowPopup(si->hContact, si, ci.hIcons[ICON_ACTION], si->pszModule, si->ptszName, ci.aFonts[15].color, _T("%s %s"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_JOIN: + ShowPopup(si->hContact, si, ci.hIcons[ICON_JOIN], si->pszModule, si->ptszName, ci.aFonts[3].color, TranslateT("%s has joined"), gce->ptszNick); + break; + case GC_EVENT_PART: + if (!gce->ptszText) + ShowPopup(si->hContact, si, ci.hIcons[ICON_PART], si->pszModule, si->ptszName, ci.aFonts[4].color, TranslateT("%s has left"), gce->ptszNick); + else + ShowPopup(si->hContact, si, ci.hIcons[ICON_PART], si->pszModule, si->ptszName, ci.aFonts[4].color, TranslateT("%s has left (%s)"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_QUIT: + if (!gce->ptszText) + ShowPopup(si->hContact, si, ci.hIcons[ICON_QUIT], si->pszModule, si->ptszName, ci.aFonts[5].color, TranslateT("%s has disconnected"), gce->ptszNick); + else + ShowPopup(si->hContact, si, ci.hIcons[ICON_QUIT], si->pszModule, si->ptszName, ci.aFonts[5].color, TranslateT("%s has disconnected (%s)"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_NICK: + ShowPopup(si->hContact, si, ci.hIcons[ICON_NICK], si->pszModule, si->ptszName, ci.aFonts[7].color, TranslateT("%s is now known as %s"), gce->ptszNick, gce->ptszText); + break; + case GC_EVENT_KICK: + if (!gce->ptszText) + ShowPopup(si->hContact, si, ci.hIcons[ICON_KICK], si->pszModule, si->ptszName, ci.aFonts[6].color, TranslateT("%s kicked %s"), (char *)gce->ptszStatus, gce->ptszNick); + else + ShowPopup(si->hContact, si, ci.hIcons[ICON_KICK], si->pszModule, si->ptszName, ci.aFonts[6].color, TranslateT("%s kicked %s (%s)"), (char *)gce->ptszStatus, gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_NOTICE: + ShowPopup(si->hContact, si, ci.hIcons[ICON_NOTICE], si->pszModule, si->ptszName, ci.aFonts[8].color, TranslateT("Notice from %s: %s"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_TOPIC: + if (!gce->ptszNick) + ShowPopup(si->hContact, si, ci.hIcons[ICON_TOPIC], si->pszModule, si->ptszName, ci.aFonts[11].color, TranslateT("The topic is \'%s\'"), RemoveFormatting(gce->ptszText)); + else + ShowPopup(si->hContact, si, ci.hIcons[ICON_TOPIC], si->pszModule, si->ptszName, ci.aFonts[11].color, TranslateT("The topic is \'%s\' (set by %s)"), RemoveFormatting(gce->ptszText), gce->ptszNick); + break; + case GC_EVENT_INFORMATION: + ShowPopup(si->hContact, si, ci.hIcons[ICON_INFO], si->pszModule, si->ptszName, ci.aFonts[12].color, _T("%s"), RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_ADDSTATUS: + ShowPopup(si->hContact, si, ci.hIcons[ICON_ADDSTATUS], si->pszModule, si->ptszName, ci.aFonts[13].color, TranslateT("%s enables \'%s\' status for %s"), gce->ptszText, (char *)gce->ptszStatus, gce->ptszNick); + break; + case GC_EVENT_REMOVESTATUS: + ShowPopup(si->hContact, si, ci.hIcons[ICON_REMSTATUS], si->pszModule, si->ptszName, ci.aFonts[14].color, TranslateT("%s disables \'%s\' status for %s"), gce->ptszText, (char *)gce->ptszStatus, gce->ptszNick); + break; + } + } + + return TRUE; +} + +BOOL DoSoundsFlashPopupTrayStuff(SESSION_INFO *si, GCEVENT *gce, BOOL bHighlight, int bManyFix) +{ + if (!gce || !si || gce->bIsMe || si->iType == GCW_SERVER) + return FALSE; + + BOOL bInactive = si->hWnd == NULL || GetForegroundWindow() != si->hWnd; + // bInactive |= GetActiveWindow() != si->hWnd; // Removed this, because it seemed to be FALSE, even when window was focused, causing incorrect notifications + + int iEvent = gce->pDest->iType; + + if (bHighlight) { + gce->pDest->iType |= GC_EVENT_HIGHLIGHT; + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatHighlight"); + if (!ci.pSettings->TabsEnable && bInactive && si->hWnd && db_get_b(NULL, "Chat", "FlashWindowHighlight", 0) != 0) + SetTimer(si->hWnd, TIMERID_FLASHWND, 900, NULL); + if (db_get_b(si->hContact, "CList", "Hidden", 0) != 0) + db_unset(si->hContact, "CList", "Hidden"); + if (bInactive) + DoTrayIcon(si, gce); + if (bInactive || !ci.pSettings->PopupInactiveOnly) + DoPopup(si, gce); + if (ci.pSettings->TabsEnable && bInactive && ci.tabSession.hWnd) + SendMessage(ci.tabSession.hWnd, GC_SETMESSAGEHIGHLIGHT, 0, (LPARAM)si); + return TRUE; + } + + // do blinking icons in tray + if (bInactive || !ci.pSettings->TrayIconInactiveOnly) + DoTrayIcon(si, gce); + + // stupid thing to not create multiple popups for a QUIT event for instance + if (bManyFix == 0) { + // do popups + if (bInactive || !ci.pSettings->PopupInactiveOnly) + DoPopup(si, gce); + + // do sounds and flashing + switch (iEvent) { + case GC_EVENT_JOIN: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatJoin"); + break; + case GC_EVENT_PART: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatPart"); + break; + case GC_EVENT_QUIT: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatQuit"); + break; + case GC_EVENT_ADDSTATUS: + case GC_EVENT_REMOVESTATUS: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatMode"); + break; + case GC_EVENT_KICK: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatKick"); + break; + case GC_EVENT_MESSAGE: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatMessage"); + if (!ci.pSettings->TabsEnable && bInactive && ci.pSettings->FlashWindow && si->hWnd) + SetTimer(si->hWnd, TIMERID_FLASHWND, 900, NULL); + + if (bInactive && !(si->wState & STATE_TALK)) { + si->wState |= STATE_TALK; + db_set_w(si->hContact, si->pszModule, "ApparentMode", (LPARAM)(WORD)40071); + } + if (ci.pSettings->TabsEnable && bInactive && ci.tabSession.hWnd) + SendMessage(ci.tabSession.hWnd, GC_SETTABHIGHLIGHT, 0, (LPARAM)si); + break; + case GC_EVENT_ACTION: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatAction"); + break; + case GC_EVENT_NICK: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatNick"); + break; + case GC_EVENT_NOTICE: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatNotice"); + break; + case GC_EVENT_TOPIC: + if (bInactive || !ci.pSettings->SoundsFocus) + SkinPlaySound("ChatTopic"); + break; + } + } + + return TRUE; +} + +int GetColorIndex(const char* pszModule, COLORREF cr) +{ + MODULEINFO *pMod = ci.MM_FindModule(pszModule); + int i = 0; + + if (!pMod || pMod->nColorCount == 0) + return -1; + + for (i = 0; i < pMod->nColorCount; i++) + if (pMod->crColors[i] == cr) + return i; + + return -1; +} + +// obscure function that is used to make sure that any of the colors +// passed by the protocol is used as fore- or background color +// in the messagebox. THis is to vvercome limitations in the richedit +// that I do not know currently how to fix + +void CheckColorsInModule(const char* pszModule) +{ + MODULEINFO *pMod = ci.MM_FindModule(pszModule); + int i = 0; + COLORREF crFG; + COLORREF crBG = (COLORREF)db_get_dw(NULL, "Chat", "ColorMessageBG", GetSysColor(COLOR_WINDOW)); + + LoadMsgDlgFont(17, NULL, &crFG); + + if (!pMod) + return; + + for (i = 0; i < pMod->nColorCount; i++) { + if (pMod->crColors[i] == crFG || pMod->crColors[i] == crBG) { + if (pMod->crColors[i] == RGB(255, 255, 255)) + pMod->crColors[i]--; + else + pMod->crColors[i]++; + } + } +} + +const TCHAR* my_strstri(const TCHAR* s1, const TCHAR* s2) +{ + int i, j, k; + for (i = 0; s1[i]; i++) + for (j = i, k = 0; _totlower(s1[j]) == _totlower(s2[k]); j++, k++) + if (!s2[k + 1]) + return s1 + i; + + return NULL; +} + +BOOL IsHighlighted(SESSION_INFO *si, const TCHAR* pszText) +{ + if (ci.pSettings->HighlightEnabled && ci.pSettings->pszHighlightWords && pszText && si->pMe) { + TCHAR* p1 = ci.pSettings->pszHighlightWords; + TCHAR* p2 = NULL; + const TCHAR* p3 = pszText; + static TCHAR szWord1[1000]; + static TCHAR szWord2[1000]; + static TCHAR szTrimString[] = _T(":,.!?;\'>)"); + + // compare word for word + while (*p1 != '\0') { + // find the next/first word in the highlight word string + // skip 'spaces' be4 the word + while (*p1 == ' ' && *p1 != '\0') + p1 += 1; + + //find the end of the word + p2 = _tcschr(p1, ' '); + if (!p2) + p2 = _tcschr(p1, '\0'); + if (p1 == p2) + return FALSE; + + // copy the word into szWord1 + lstrcpyn(szWord1, p1, p2 - p1 > 998 ? 999 : p2 - p1 + 1); + p1 = p2; + + // replace %m with the users nickname + p2 = _tcschr(szWord1, '%'); + if (p2 && p2[1] == 'm') { + TCHAR szTemp[50]; + + p2[1] = 's'; + lstrcpyn(szTemp, szWord1, 999); + mir_sntprintf(szWord1, SIZEOF(szWord1), szTemp, si->pMe->pszNick); + } + + // time to get the next/first word in the incoming text string + while (*p3 != '\0') { + // skip 'spaces' be4 the word + while (*p3 == ' ' && *p3 != '\0') + p3 += 1; + + //find the end of the word + p2 = (TCHAR *)_tcschr(p3, ' '); + if (!p2) + p2 = (TCHAR *)_tcschr(p3, '\0'); + + + if (p3 != p2) { + // eliminate ending character if needed + if (p2 - p3 > 1 && _tcschr(szTrimString, p2[-1])) + p2 -= 1; + + // copy the word into szWord2 and remove formatting + lstrcpyn(szWord2, p3, p2 - p3 > 998 ? 999 : p2 - p3 + 1); + + // reset the pointer if it was touched because of an ending character + if (*p2 != '\0' && *p2 != ' ') + p2 += 1; + p3 = p2; + + CharLower(szWord1); + CharLower(szWord2); + + // compare the words, using wildcards + if (WCCmp(szWord1, RemoveFormatting(szWord2))) + return TRUE; + } + } + + p3 = pszText; + } + } + + return FALSE; +} + +BOOL LogToFile(SESSION_INFO *si, GCEVENT *gce) +{ + TCHAR szBuffer[4096]; + TCHAR szLine[4096]; + TCHAR szTime[100]; + TCHAR szFile[MAX_PATH]; + TCHAR szName[MAX_PATH]; + TCHAR szFolder[MAX_PATH]; + char p = '\0'; + szBuffer[0] = '\0'; + + if (!si || !gce) + return FALSE; + + MODULEINFO *mi = ci.MM_FindModule(si->pszModule); + if (!mi) + return FALSE; + + TCHAR *szModName = mir_a2t(si->pszModule); + mir_sntprintf(szName, MAX_PATH, _T("%s"), mi->ptszModDispName ? mi->ptszModDispName : (szModName = mir_a2t(si->pszModule))); + mir_free(szModName); + ValidateFilename(szName); + mir_sntprintf(szFolder, MAX_PATH, _T("%s\\%s"), ci.pSettings->pszLogDir, szName ); + + CreateDirectoryTreeT(szFolder); + + mir_sntprintf( szName, MAX_PATH, _T("%s.log"), si->ptszID ); + ValidateFilename(szName); + + mir_sntprintf(szFile, MAX_PATH, _T("%s\\%s"), szFolder, szName ); + lstrcpyn(szTime, MakeTimeStamp(ci.pSettings->pszTimeStampLog, gce->time), 99); + + FILE *hFile = _tfopen(szFile, _T("at+")); + if (hFile) { + TCHAR szTemp[512], szTemp2[512]; + TCHAR* pszNick = NULL; + if ( gce->ptszNick ) { + if ( ci.pSettings->LogLimitNames && lstrlen(gce->ptszNick) > 20 ) { + lstrcpyn(szTemp2, gce->ptszNick, 20); + lstrcpyn(szTemp2+20, _T("..."), 4); + } + else lstrcpyn(szTemp2, gce->ptszNick, 511); + + if (gce->ptszUserInfo) + mir_sntprintf(szTemp, SIZEOF(szTemp), _T("%s (%s)"), szTemp2, gce->ptszUserInfo); + else + mir_sntprintf(szTemp, SIZEOF(szTemp), _T("%s"), szTemp2); + pszNick = szTemp; + } + switch (gce->pDest->iType) { + case GC_EVENT_MESSAGE: + case GC_EVENT_MESSAGE|GC_EVENT_HIGHLIGHT: + p = '*'; + mir_sntprintf(szBuffer, SIZEOF(szBuffer), _T("%s * %s"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_ACTION: + case GC_EVENT_ACTION|GC_EVENT_HIGHLIGHT: + p = '*'; + mir_sntprintf(szBuffer, SIZEOF(szBuffer), _T("%s %s"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_JOIN: + p = '>'; + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s has joined"), (char *)pszNick); + break; + case GC_EVENT_PART: + p = '<'; + if (!gce->ptszText) + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s has left"), (char *)pszNick); + else + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s has left (%s)"), (char *)pszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_QUIT: + p = '<'; + if (!gce->ptszText) + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s has disconnected"), (char *)pszNick); + else + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s has disconnected (%s)"), (char *)pszNick,RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_NICK: + p = '^'; + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s is now known as %s"), gce->ptszNick, gce->ptszText); + break; + case GC_EVENT_KICK: + p = '~'; + if (!gce->ptszText) + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s kicked %s"), (char *)gce->ptszStatus, gce->ptszNick); + else + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s kicked %s (%s)"), (char *)gce->ptszStatus, gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_NOTICE: + p = '¤'; + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("Notice from %s: %s"), gce->ptszNick, RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_TOPIC: + p = '#'; + if (!gce->ptszNick) + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("The topic is \'%s\'"), RemoveFormatting(gce->ptszText)); + else + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("The topic is \'%s\' (set by %s)"), RemoveFormatting(gce->ptszText), gce->ptszNick); + break; + case GC_EVENT_INFORMATION: + p = '!'; + mir_sntprintf(szBuffer, SIZEOF(szBuffer), _T("%s"), RemoveFormatting(gce->ptszText)); + break; + case GC_EVENT_ADDSTATUS: + p = '+'; + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s enables \'%s\' status for %s"), gce->ptszText, (char *)gce->ptszStatus, gce->ptszNick); + break; + case GC_EVENT_REMOVESTATUS: + p = '-'; + mir_sntprintf(szBuffer, SIZEOF(szBuffer), TranslateT("%s disables \'%s\' status for %s"), gce->ptszText, (char *)gce->ptszStatus, gce->ptszNick); + break; + } + if (p) + mir_sntprintf(szLine, SIZEOF(szLine), TranslateT("%s %c %s\n"), szTime, p, szBuffer); + else + mir_sntprintf(szLine, SIZEOF(szLine), TranslateT("%s %s\n"), szTime, szBuffer); + + if (szLine[0]) { + char *p = mir_t2a(szLine); + fputs(p, hFile); + mir_free(p); + + if (ci.pSettings->LoggingLimit > 0) { + DWORD dwSize; + DWORD trimlimit; + + fseek(hFile, 0, SEEK_END); + dwSize = ftell(hFile); + rewind(hFile); + trimlimit = ci.pSettings->LoggingLimit * 1024 + 1024 * 10; + if (dwSize > trimlimit) { + BYTE * pBuffer = 0; + BYTE * pBufferTemp = 0; + int read = 0; + + pBuffer = (BYTE *)mir_alloc(ci.pSettings->LoggingLimit * 1024 + 1); + pBuffer[ci.pSettings->LoggingLimit * 1024] = '\0'; + fseek(hFile, -ci.pSettings->LoggingLimit * 1024, SEEK_END); + read = (int)fread(pBuffer, 1, ci.pSettings->LoggingLimit * 1024, hFile); + fclose(hFile); + hFile = NULL; + + // trim to whole lines, should help with broken log files I hope. + pBufferTemp = (BYTE *)strchr((char *)pBuffer, '\n'); + if (pBufferTemp) { + pBufferTemp++; + read -= pBufferTemp - pBuffer; + } + else pBufferTemp = pBuffer; + + if (read > 0) { + hFile = _tfopen(szFile, _T("wt")); + if (hFile) { + fwrite(pBufferTemp, 1, read, hFile); + fclose(hFile); hFile = NULL; + } + } + + mir_free(pBuffer); + } + } + } + + if (hFile) + fclose(hFile); hFile = NULL; + return TRUE; + } + + return FALSE; +} + +BOOL DoEventHookAsync(HWND hwnd, const TCHAR *pszID, const char* pszModule, int iType, TCHAR* pszUID, TCHAR* pszText, DWORD dwItem) +{ + SESSION_INFO *si = ci.SM_FindSession(pszID, pszModule); + if (si == NULL) + return FALSE; + + GCDEST *gcd = (GCDEST*)mir_calloc(sizeof(GCDEST)); + gcd->pszModule = mir_strdup(pszModule); + gcd->ptszID = mir_tstrdup(pszID); + gcd->iType = iType; + + GCHOOK *gch = (GCHOOK*)mir_calloc(sizeof(GCHOOK)); + gch->ptszUID = mir_tstrdup(pszUID); + gch->ptszText = mir_tstrdup(pszText); + gch->dwData = dwItem; + gch->pDest = gcd; + PostMessage(hwnd, GC_FIREHOOK, 0, (LPARAM)gch); + return TRUE; +} + +BOOL DoEventHook(const TCHAR *pszID, const char* pszModule, int iType, const TCHAR* pszUID, const TCHAR* pszText, DWORD dwItem) +{ + SESSION_INFO *si = ci.SM_FindSession(pszID, pszModule); + if (si == NULL) + return FALSE; + + GCDEST gcd = { (char*)pszModule, pszID, iType }; + GCHOOK gch = { 0 }; + gch.ptszUID = (LPTSTR)pszUID; + gch.ptszText = (LPTSTR)pszText; + gch.dwData = dwItem; + gch.pDest = &gcd; + NotifyEventHooks(hChatSendEvent, 0, (WPARAM)&gch); + return TRUE; +} + +BOOL IsEventSupported(int eventType) +{ + // Supported events + switch (eventType) { + case GC_EVENT_JOIN: + case GC_EVENT_PART: + case GC_EVENT_QUIT: + case GC_EVENT_KICK: + case GC_EVENT_NICK: + case GC_EVENT_NOTICE: + case GC_EVENT_MESSAGE: + case GC_EVENT_TOPIC: + case GC_EVENT_INFORMATION: + case GC_EVENT_ACTION: + case GC_EVENT_ADDSTATUS: + case GC_EVENT_REMOVESTATUS: + case GC_EVENT_CHUID: + case GC_EVENT_CHANGESESSIONAME: + case GC_EVENT_ADDGROUP: + case GC_EVENT_SETITEMDATA: + case GC_EVENT_GETITEMDATA: + case GC_EVENT_SETSBTEXT: + case GC_EVENT_ACK: + case GC_EVENT_SENDMESSAGE: + case GC_EVENT_SETSTATUSEX: + case GC_EVENT_CONTROL: + case GC_EVENT_SETCONTACTSTATUS: + return TRUE; + } + + // Other events + return FALSE; +} + +void ValidateFilename(TCHAR *filename) +{ + TCHAR *p1 = filename; + TCHAR szForbidden[] = _T("\\/:*?\"<>|"); + while (*p1 != '\0') { + if (_tcschr(szForbidden, *p1)) + *p1 = '_'; + p1 += 1; + } +} -- cgit v1.2.3