summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/chat/chat.h93
-rw-r--r--src/modules/chat/chat_opts.cpp370
-rw-r--r--src/modules/chat/chat_svc.cpp749
-rw-r--r--src/modules/chat/clist.cpp293
-rw-r--r--src/modules/chat/log.cpp594
-rw-r--r--src/modules/chat/manager.cpp1639
-rw-r--r--src/modules/chat/tools.cpp761
7 files changed, 4499 insertions, 0 deletions
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 <m_ieview.h>
+#include <m_smileyadd.h>
+#include <m_popup.h>
+#include <m_fontservice.h>
+
+// 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 <m_fontservice.h>
+
+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("<invalid>"), 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)&gtl, 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;
+ }
+}