/*
Miranda NG SmileyAdd Plugin
Copyright (C) 2012 - 2017 Miranda NG project (http://miranda-ng.org)
Copyright (C) 2005 - 2011 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003 - 2004 Rein-Peter de Boer
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 version 2
of the License.
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, see .
*/
#include "stdafx.h"
SmileyPackListType g_SmileyPacks;
SmileyCategoryListType g_SmileyCategories;
static HWND hwndHidden = NULL;
static void CALLBACK timerProc(HWND, UINT, UINT_PTR param, DWORD)
{
SmileyType *pType = (SmileyType*)param;
pType->MoveToNextFrame();
}
// these two functions must be called from the main thread
static void CALLBACK sttStartTimer(PVOID obj)
{
if (hwndHidden == NULL)
hwndHidden = CreateWindowEx(0, L"STATIC", NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL);
SmileyType *pType = (SmileyType*)obj;
pType->SetFrameDelay();
}
static void CALLBACK sttStopTimer(PVOID obj)
{
KillTimer(hwndHidden, (DWORD_PTR)obj);
}
//
// SmileyType
//
SmileyType::SmileyType(void) :
m_arSmileys(10, PtrKeySortT)
{
m_SmileyIcon = NULL;
m_xepimg = NULL;
m_flags = 0;
m_index = 0;
m_size.cx = 0;
m_size.cy = 0;
}
SmileyType::~SmileyType()
{
if (m_xepimg) {
m_xepimg->Release();
m_xepimg = NULL;
}
if (m_SmileyIcon != NULL) {
DestroyIcon(m_SmileyIcon);
m_SmileyIcon = NULL;
}
}
void SmileyType::AddObject(ISmileyBase *pObject)
{
if (m_arSmileys.getCount() == 0) {
if (m_xepimg == NULL)
m_xepimg = AddCacheImage(m_filepath, m_index);
CallFunctionAsync(sttStartTimer, this);
}
m_arSmileys.insert(pObject);
}
void SmileyType::RemoveObject(ISmileyBase *pObject)
{
int idx = m_arSmileys.getIndex(pObject);
if (idx == -1)
return;
m_arSmileys.remove(idx);
if (m_arSmileys.getCount() == 0)
CallFunctionAsync(sttStopTimer, this);
}
void SmileyType::SetFrameDelay()
{
int iFrameDelay = (m_xepimg == NULL) ? 0 : m_xepimg->GetFrameDelay();
if (iFrameDelay <= 0)
KillTimer(hwndHidden, (DWORD_PTR)this);
else
SetTimer(hwndHidden, (DWORD_PTR)this, iFrameDelay*10, timerProc);
}
void SmileyType::MoveToNextFrame()
{
m_index = m_xepimg->SelectNextFrame(m_index);
for (int i = 0; i < m_arSmileys.getCount(); i++)
m_arSmileys[i]->Draw();
SetFrameDelay(); // reset timer
}
HICON SmileyType::GetIcon(void)
{
if (m_SmileyIcon == NULL) {
ImageBase *img = CreateCachedImage();
if (!img)
return NULL;
img->SelectFrame(m_index);
m_SmileyIcon = img->GetIcon();
img->Release();
}
return m_SmileyIcon;
}
HICON SmileyType::GetIconDup(void)
{
ImageBase *img = CreateCachedImage();
img->SelectFrame(m_index);
HICON hIcon = img->GetIcon();
img->Release();
return hIcon;
}
bool SmileyType::LoadFromImage(IStream *pStream)
{
if (m_xepimg)
m_xepimg->Release();
CMStringW name;
m_xepimg = new ImageType(0, name, pStream);
return true;
}
bool SmileyType::LoadFromResource(const CMStringW &file, const int index)
{
m_index = index;
m_filepath = file;
return true;
}
void SmileyType::GetSize(SIZE &size)
{
if (m_size.cy == 0) {
ImageBase *img = CreateCachedImage();
if (img) {
img->GetSize(m_size);
img->Release();
}
}
size = m_size;
}
ImageBase* SmileyType::CreateCachedImage(void)
{
if (m_xepimg) {
m_xepimg->AddRef();
return m_xepimg;
}
return AddCacheImage(m_filepath, m_index);
}
void SmileyType::SetImList(HIMAGELIST hImLst, long i)
{
if (m_xepimg) m_xepimg->Release();
m_xepimg = new ImageListItemType(0, hImLst, i);
}
HBITMAP SmileyType::GetBitmap(COLORREF bkgClr, int sizeX, int sizeY)
{
ImageBase *img = CreateCachedImage();
if (!img) return NULL;
img->SelectFrame(m_index);
HBITMAP hBmp = img->GetBitmap(bkgClr, sizeX, sizeY);
img->Release();
return hBmp;
}
//
// SmileyPackType
//
SmileyPackType::SmileyPackType()
{
m_hSmList = NULL;
errorFound = false;
}
SmileyType* SmileyPackType::GetSmiley(unsigned index)
{
return (index < (unsigned)m_SmileyList.getCount()) ? &m_SmileyList[index] : NULL;
}
static DWORD_PTR ConvertServiceParam(MCONTACT hContact, const wchar_t *param)
{
if (param == NULL)
return 0;
if (mir_wstrcmpi(L"hContact", param) == 0)
return hContact;
if (iswdigit(*param))
return _wtoi(param);
return (DWORD_PTR)param;
}
void SmileyType::CallSmileyService(MCONTACT hContact)
{
_TPattern *srvsplit = _TPattern::compile(L"(.*)\\|(.*)\\|(.*)");
_TMatcher *m0 = srvsplit->createTMatcher(GetTriggerText());
m0->findFirstMatch();
CMStringW name = m0->getGroup(1);
CMStringW par1 = m0->getGroup(2);
CMStringW par2 = m0->getGroup(3);
delete m0;
delete srvsplit;
char str[MAXMODULELABELLENGTH];
const char *proto = "";
if (name[0] == '/') {
proto = (const char*)GetContactProto(hContact);
if (proto == NULL) return;
}
mir_snprintf(str, "%s%s", proto, _T2A(name.c_str()));
CallService(str,
ConvertServiceParam(hContact, par1.c_str()),
ConvertServiceParam(hContact, par2.c_str()));
}
SmileyPackType::~SmileyPackType()
{
if (m_hSmList != NULL) ImageList_Destroy(m_hSmList);
}
static const wchar_t urlRegEx[] =
L"(?:ftp|https|http|file|aim|webcal|irc|msnim|xmpp|gopher|mailto|news|nntp|telnet|wais|prospero)://?[\\w.?%:/$+;]*";
static const wchar_t pathRegEx[] = L"[\\s\"][a-zA-Z]:[\\\\/][\\w.\\-\\\\/]*";
static const wchar_t timeRegEx[] = L"\\d{1,2}:\\d{2}:\\d{2}|\\d{1,2}:\\d{2}";
void SmileyPackType::AddTriggersToSmileyLookup(void)
{
_TPattern *p = _TPattern::compile(L"\\s+");
{
CMStringW emptystr;
m_SmileyLookup.insert(new SmileyLookup(urlRegEx, true, -1, emptystr));
m_SmileyLookup.insert(new SmileyLookup(pathRegEx, true, -1, emptystr));
m_SmileyLookup.insert(new SmileyLookup(timeRegEx, true, -1, emptystr));
}
for (int dist = 0; dist < m_SmileyList.getCount(); dist++) {
if (m_SmileyList[dist].IsRegEx()) {
SmileyLookup *dats = new SmileyLookup(m_SmileyList[dist].GetTriggerText(), true, dist, GetFilename());
if (dats->IsValid())
m_SmileyLookup.insert(dats);
else
errorFound = true;
if (m_SmileyList[dist].m_InsertText.IsEmpty())
m_SmileyList[dist].m_InsertText = m_SmileyList[dist].m_ToolText;
}
else if (!m_SmileyList[dist].IsService()) {
bool first = true;
int li = 0;
_TMatcher *m0 = p->createTMatcher(m_SmileyList[dist].GetTriggerText());
while (m0->findNextMatch()) {
int stind = m0->getStartingIndex();
if (li != stind) {
CMStringW out;
ReplaceAllSpecials(m0->getString().Mid(li, stind - li), out);
SmileyLookup *dats = new SmileyLookup(out, false, dist, GetFilename());
if (dats->IsValid()) {
m_SmileyLookup.insert(dats);
if (first) {
m_SmileyList[dist].m_InsertText = out;
first = false;
}
} // fallthrough
}
li = m0->getEndingIndex();
}
int stind = (int)m0->getString().GetLength();
if (li < stind) {
CMStringW out;
ReplaceAllSpecials(m0->getString().Mid(li, stind - li), out);
SmileyLookup *dats = new SmileyLookup(out, false, dist, GetFilename());
if (dats->IsValid()) {
m_SmileyLookup.insert(dats);
if (first) {
m_SmileyList[dist].m_InsertText = out;
first = false;
}
}
else
delete dats;
}
delete m0;
}
}
delete p;
}
void SmileyPackType::ReplaceAllSpecials(const CMStringW &Input, CMStringW &Output)
{
Output = _TPattern::replace(L"%%_{1,2}%%", Input, L" ");
Output = _TPattern::replace(L"%%''%%", Output, L"\"");
}
void SmileyPackType::Clear(void)
{
m_SmileyList.destroy();
m_SmileyLookup.destroy();
if (m_hSmList != NULL) { ImageList_Destroy(m_hSmList); m_hSmList = NULL; }
m_Filename.Empty();
m_Name.Empty();
m_Date.Empty();
m_Version.Empty();
m_Author.Empty();
m_VisibleCount = 0;
m_ButtonSmiley.Empty();
errorFound = false;
}
bool SmileyPackType::LoadSmileyFile(const CMStringW &filename, const CMStringW &packname, bool onlyInfo, bool noerr)
{
Clear();
if (filename.IsEmpty()) {
m_Name = L"Nothing loaded";
return false;
}
CMStringW modpath;
pathToAbsolute(filename, modpath);
// Load xep file
int fh = _wopen(modpath.c_str(), _O_BINARY | _O_RDONLY);
if (fh == -1) {
if (!noerr) {
static const wchar_t errmsg[] = LPGENW("Smiley pack %s for category \"%s\" not found.\nSelect correct smiley pack in the Options -> Customize -> Smileys.");
wchar_t msgtxt[1024];
mir_snwprintf(msgtxt, TranslateW(errmsg), modpath.c_str(), packname);
ReportError(msgtxt);
}
m_Name = L"Nothing loaded";
return false;
}
m_Filename = filename;
// Find file size
const long flen = _filelength(fh);
// Allocate file buffer
char *buf = new char[flen + sizeof(wchar_t)];
// Read xep file in
int len = _read(fh, buf, flen);
*(wchar_t*)(buf + len) = 0;
// Close file
_close(fh);
CMStringW tbuf;
if (len > 2 && *(wchar_t*)buf == 0xfeff)
tbuf = ((wchar_t*)buf + 1);
else if (len > 3 && buf[0] == '\xef' && buf[1] == '\xbb' && buf[2] == '\xbf')
tbuf = _A2T(buf + 3, CP_UTF8);
else
tbuf = _A2T(buf);
delete[] buf;
bool res;
if (filename.Find(L".xep") == -1)
res = LoadSmileyFileMSL(tbuf, onlyInfo, modpath);
else
res = LoadSmileyFileXEP(tbuf, onlyInfo, modpath);
if (errorFound)
ReportError(TranslateT("There were problems loading smiley pack (it should be corrected).\nSee network log for details."));
return res;
}
bool SmileyPackType::LoadSmileyFileMSL(CMStringW &tbuf, bool onlyInfo, CMStringW &modpath)
{
CMStringW pathstr, packstr;
{
_TPattern *pathsplit = _TPattern::compile(L"(.*\\\\)(.*)\\.|$");
_TMatcher *m0 = pathsplit->createTMatcher(modpath);
m0->findFirstMatch();
pathstr = m0->getGroup(1);
packstr = m0->getGroup(2);
delete m0;
delete pathsplit;
}
{
_TPattern *otherf = _TPattern::compile(
L"^\\s*(Name|Author|Date|Version|ButtonSmiley)\\s*=\\s*\"(.*)\"",
_TPattern::MULTILINE_MATCHING);
_TMatcher *m0 = otherf->createTMatcher(tbuf);
while (m0->findNextMatch()) {
if (m0->getGroup(1) == L"Name") m_Name = m0->getGroup(2);
if (m0->getGroup(1) == L"Author") m_Author = m0->getGroup(2);
if (m0->getGroup(1) == L"Date") m_Date = m0->getGroup(2);
if (m0->getGroup(1) == L"Version") m_Version = m0->getGroup(2);
if (m0->getGroup(1) == L"ButtonSmiley") m_ButtonSmiley = m0->getGroup(2);
}
delete m0;
delete otherf;
}
if (!onlyInfo) {
selec.x = selec.y = win.x = win.y = 0;
{
_TPattern *pat = _TPattern::compile(
L"^\\s*(Selection|Window)Size\\s*=\\s*(\\d+)\\s*,\\s*(\\d+)",
_TPattern::MULTILINE_MATCHING);
_TMatcher *m0 = pat->createTMatcher(tbuf);
while (m0->findNextMatch()) {
POINT tpt;
tpt.x = _wtol(m0->getGroup(2).c_str());
tpt.y = _wtol(m0->getGroup(3).c_str());
if (m0->getGroup(1) == L"Selection")
selec = tpt;
else if (m0->getGroup(1) == L"Window")
win = tpt;
}
delete m0;
delete pat;
}
_TPattern *smiley = _TPattern::compile(
L"^\\s*Smiley(\\*)?\\s*=" // Is Hidden
L"(?:\\s*\"(.*)\")" // Smiley file name
L"(?:[\\s,]+(\\-?\\d+))" // Icon resource id
L"(?:[\\s,]+(R|S)?\"(.*?)\")" // Trigger text
L"(?:[\\s,]+\"(.*?)\")?" // Tooltip or insert text
L"(?:[\\s,]+\"(.*?)\")?", // Tooltip text
_TPattern::MULTILINE_MATCHING);
SmileyVectorType hiddenSmileys;
unsigned smnum = 0;
{
_TMatcher *m0 = smiley->createTMatcher(tbuf);
while (m0->findNextMatch()) {
CMStringW resname = m0->getGroup(2);
if (resname.Find(L"http://") != -1) {
if (GetSmileyFile(resname, packstr))
continue;
}
else if (!resname.IsEmpty())
resname.Insert(0, pathstr);
SmileyType *dat = new SmileyType;
const int iconIndex = _wtol(m0->getGroup(3).c_str());
dat->SetHidden(m0->getStartingIndex(1) >= 0);
if (m0->getStartingIndex(4) >= 0) {
dat->SetRegEx(m0->getGroup(4) == L"R");
dat->SetService(m0->getGroup(4) == L"S");
}
dat->m_TriggerText = m0->getGroup(5);
if (dat->IsRegEx()) {
if (m0->getStartingIndex(6) >= 0)
ReplaceAllSpecials(m0->getGroup(6), dat->m_InsertText);
if (m0->getStartingIndex(7) >= 0)
ReplaceAllSpecials(m0->getGroup(7), dat->m_ToolText);
else
dat->m_ToolText = dat->m_InsertText;
}
else {
if (m0->getStartingIndex(6) >= 0)
ReplaceAllSpecials(m0->getGroup(6), dat->m_ToolText);
else
ReplaceAllSpecials(dat->m_TriggerText, dat->m_ToolText);
}
bool noerr;
if (resname.IsEmpty()) {
dat->SetHidden(true);
dat->SetText(true);
noerr = true;
}
else noerr = dat->LoadFromResource(resname, iconIndex);
if (dat->IsHidden())
hiddenSmileys.insert(dat);
else
m_SmileyList.insert(dat);
if (!noerr) {
static const wchar_t errmsg[] = LPGENW("Smiley #%u in file %s for smiley pack %s not found.");
wchar_t msgtxt[1024];
mir_snwprintf(msgtxt, TranslateW(errmsg), smnum, resname.c_str(), modpath.c_str());
CallService(MS_NETLIB_LOG, (WPARAM)hNetlibUser, _T2A(msgtxt));
errorFound = true;
}
smnum++;
}
delete smiley;
delete m0;
}
m_VisibleCount = m_SmileyList.getCount();
m_SmileyList.splice(hiddenSmileys);
AddTriggersToSmileyLookup();
}
return true;
}
static void DecodeHTML(CMStringW &str)
{
if (str.Find('&') != -1) {
str = _TPattern::replace(CMStringW(L"<"), str, CMStringW(L"<"));
str = _TPattern::replace(CMStringW(L">"), str, CMStringW(L">"));
}
}
static IStream* DecodeBase64Data(const char *pData)
{
unsigned dataLen;
ptrA data((char*)mir_base64_decode(pData, &dataLen));
if (data == NULL)
return NULL;
// Read image list
HGLOBAL hBuffer = GlobalAlloc(GMEM_MOVEABLE, dataLen);
if (!hBuffer)
return NULL;
void *dst = GlobalLock(hBuffer);
memcpy(dst, data, dataLen);
GlobalUnlock(hBuffer);
IStream *pStream = NULL;
CreateStreamOnHGlobal(hBuffer, TRUE, &pStream);
return pStream;
}
bool SmileyPackType::LoadSmileyFileXEP(CMStringW &tbuf, bool onlyInfo, CMStringW&)
{
_TMatcher *m0, *m1, *m2;
_TPattern *dbname_re = _TPattern::compile(L"\\s*\"(.*?)\"\\s*",
_TPattern::MULTILINE_MATCHING);
_TPattern *author_re = _TPattern::compile(L"\\s*\"(.*?)\"\\s*",
_TPattern::MULTILINE_MATCHING);
_TPattern *settings_re = _TPattern::compile(L"(.*?)",
_TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL);
m0 = settings_re->createTMatcher(tbuf);
if (m0->findFirstMatch()) {
CMStringW settings = m0->getGroup(1);
m1 = author_re->createTMatcher(settings);
if (m1->findFirstMatch()) {
m_Author = m1->getGroup(1);
DecodeHTML(m_Author);
}
delete m1;
m1 = dbname_re->createTMatcher(settings);
if (m1->findFirstMatch()) {
m_Name = m1->getGroup(1);
DecodeHTML(m_Name);
}
delete m1;
}
delete m0;
delete dbname_re;
delete author_re;
delete settings_re;
if (!onlyInfo) {
_TPattern *record_re = _TPattern::compile(L"(?:\\s*\"(.*?)\"?(.*?))",
_TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL);
_TPattern *expression_re = _TPattern::compile(L"\\s*\"(.*?)\"\\s*",
_TPattern::MULTILINE_MATCHING);
_TPattern *pastetext_re = _TPattern::compile(L"\\s*\"(.*?)\"\\s*",
_TPattern::MULTILINE_MATCHING);
_TPattern *images_re = _TPattern::compile(L"(.*?)",
_TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL);
_TPattern *image_re = _TPattern::compile(L"(.*?)",
_TPattern::MULTILINE_MATCHING | _TPattern::DOT_MATCHES_ALL);
_TPattern *imagedt_re = _TPattern::compile(L"",
_TPattern::MULTILINE_MATCHING);
m0 = images_re->createTMatcher(tbuf);
if (m0->findFirstMatch()) {
CMStringW images = m0->getGroup(1);
m1 = imagedt_re->createTMatcher(images);
if (m1->findFirstMatch()) {
IStream *pStream = DecodeBase64Data(_T2A(m1->getGroup(1).c_str()));
if (pStream != NULL) {
if (m_hSmList != NULL) ImageList_Destroy(m_hSmList);
m_hSmList = ImageList_Read(pStream);
pStream->Release();
}
}
delete m1;
}
delete m0;
m0 = record_re->createTMatcher(tbuf);
while (m0->findNextMatch()) {
SmileyType *dat = new SmileyType;
dat->SetRegEx(true);
dat->SetImList(m_hSmList, _wtol(m0->getGroup(1).c_str()));
dat->m_ToolText = m0->getGroup(2);
DecodeHTML(dat->m_ToolText);
CMStringW rec = m0->getGroup(3);
m1 = expression_re->createTMatcher(rec);
if (m1->findFirstMatch()) {
dat->m_TriggerText = m1->getGroup(1);
DecodeHTML(dat->m_TriggerText);
}
delete m1;
m1 = pastetext_re->createTMatcher(rec);
if (m1->findFirstMatch()) {
dat->m_InsertText = m1->getGroup(1);
DecodeHTML(dat->m_InsertText);
}
delete m1;
dat->SetHidden(dat->m_InsertText.IsEmpty());
m1 = image_re->createTMatcher(rec);
if (m1->findFirstMatch()) {
CMStringW images = m1->getGroup(1);
m2 = imagedt_re->createTMatcher(images);
if (m2->findFirstMatch()) {
IStream *pStream = DecodeBase64Data(_T2A(m2->getGroup(1).c_str()));
if (pStream != NULL) {
dat->LoadFromImage(pStream);
pStream->Release();
}
}
delete m2;
}
delete m1;
m_SmileyList.insert(dat);
}
delete m0;
delete record_re;
delete expression_re;
delete pastetext_re;
delete images_re;
delete image_re;
delete imagedt_re;
}
m_VisibleCount = m_SmileyList.getCount();
AddTriggersToSmileyLookup();
selec.x = 0;
selec.y = 0;
win.x = 0;
win.y = 0;
return true;
}
//
// SmileyPackListType
//
bool SmileyPackListType::AddSmileyPack(CMStringW &filename, CMStringW &packname)
{
bool res = true;
if (GetSmileyPack(filename) == NULL) { //not exist yet, so add
SmileyPackType *smileyPack = new SmileyPackType;
res = smileyPack->LoadSmileyFile(filename, packname, FALSE);
if (res)
m_SmileyPacks.insert(smileyPack);
else
delete smileyPack;
}
return res;
}
SmileyPackType* SmileyPackListType::GetSmileyPack(CMStringW &filename)
{
CMStringW modpath;
pathToAbsolute(filename, modpath);
for (int i = 0; i < m_SmileyPacks.getCount(); i++) {
CMStringW modpath1;
pathToAbsolute(m_SmileyPacks[i].GetFilename(), modpath1);
if (mir_wstrcmpi(modpath.c_str(), modpath1.c_str()) == 0) return &m_SmileyPacks[i];
}
return NULL;
}
void SmileyPackListType::ClearAndFreeAll()
{
m_SmileyPacks.destroy();
}
//
// SmileyCategoryType
//
SmileyCategoryType::SmileyCategoryType(SmileyPackListType *pSPS, const CMStringW &name,
const CMStringW &displayName, const CMStringW &defaultFilename, SmcType typ)
{
m_pSmileyPackStore = pSPS;
type = typ;
m_Name = name;
m_DisplayName = displayName;
visible = true;
opt.ReadPackFileName(m_Filename, m_Name, defaultFilename);
}
void SmileyCategoryType::Load(void)
{
bool bVisibleCat = opt.UsePhysProto ? !IsAcc() : !IsPhysProto();
bool bVisible = opt.UseOneForAll ? !IsProto() : bVisibleCat;
if (bVisible && !m_Filename.IsEmpty()) {
bool loaded = m_pSmileyPackStore->AddSmileyPack(m_Filename, m_DisplayName);
if (!loaded) {
ClearFilename();
SaveSettings();
}
}
}
SmileyPackType* SmileyCategoryType::GetSmileyPack(void)
{
return m_pSmileyPackStore->GetSmileyPack(m_Filename);
}
void SmileyCategoryType::SaveSettings(void)
{
opt.WritePackFileName(m_Filename, m_Name);
}
//
// SmileyCategoryListType
//
void SmileyCategoryListType::ClearAndLoadAll(void)
{
m_pSmileyPackStore->ClearAndFreeAll();
for (int i = 0; i < m_SmileyCategories.getCount(); i++)
m_SmileyCategories[i].Load();
}
SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(const CMStringW &name)
{
for (int i = 0; i < m_SmileyCategories.getCount(); i++)
if (name.CompareNoCase(m_SmileyCategories[i].GetName()) == 0)
return &m_SmileyCategories[i];
return NULL;
}
SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(unsigned index)
{
return index < (unsigned)m_SmileyCategories.getCount() ? &m_SmileyCategories[index] : NULL;
}
SmileyPackType* SmileyCategoryListType::GetSmileyPack(CMStringW &categoryname)
{
SmileyCategoryType *smc = GetSmileyCategory(categoryname);
return smc != NULL ? smc->GetSmileyPack() : NULL;
}
void SmileyCategoryListType::SaveSettings(void)
{
CMStringW catstr;
for (int i = 0; i < m_SmileyCategories.getCount(); i++) {
m_SmileyCategories[i].SaveSettings();
if (m_SmileyCategories[i].IsCustom()) {
if (!catstr.IsEmpty()) catstr += '#';
catstr += m_SmileyCategories[i].GetName();
}
}
opt.WriteCustomCategories(catstr);
}
void SmileyCategoryListType::AddAndLoad(const CMStringW &name, const CMStringW &displayName)
{
if (GetSmileyCategory(name) != NULL)
return;
AddCategory(name, displayName, smcExt);
// Load only if other smileys have been loaded already
if (m_SmileyCategories.getCount() > 1)
m_SmileyCategories[m_SmileyCategories.getCount() - 1].Load();
}
void SmileyCategoryListType::AddCategory(const CMStringW &name, const CMStringW &displayName, SmcType typ, const CMStringW &defaultFilename)
{
if (GetSmileyCategory(name) == NULL)
m_SmileyCategories.insert(new SmileyCategoryType(m_pSmileyPackStore, name, displayName, defaultFilename, typ));
}
bool SmileyCategoryListType::DeleteCustomCategory(int index)
{
if (index < m_SmileyCategories.getCount()) {
if (m_SmileyCategories[index].IsCustom()) {
m_SmileyCategories.remove(index);
return true;
}
}
return false;
}
void SmileyCategoryListType::AddAccountAsCategory(PROTOACCOUNT *acc, const CMStringW &defaultFile)
{
if (Proto_IsAccountEnabled(acc) && acc->szProtoName && IsSmileyProto(acc->szModuleName)) {
CMStringW displayName(acc->tszAccountName ? acc->tszAccountName : _A2T(acc->szModuleName));
CMStringW PhysProtoName, paths;
DBVARIANT dbv;
if (db_get_ws(NULL, acc->szModuleName, "AM_BaseProto", &dbv) == 0) {
PhysProtoName = L"AllProto";
PhysProtoName += dbv.ptszVal;
db_free(&dbv);
}
if (!PhysProtoName.IsEmpty())
paths = g_SmileyCategories.GetSmileyCategory(PhysProtoName) ? g_SmileyCategories.GetSmileyCategory(PhysProtoName)->GetFilename() : L"";
if (paths.IsEmpty()) {
const char *packnam = acc->szProtoName;
if (mir_strcmp(packnam, "JABBER") == 0)
packnam = "JGMail";
else if (strstr(packnam, "SIP") != NULL)
packnam = "MSN";
char path[MAX_PATH];
mir_snprintf(path, "Smileys\\nova\\%s.msl", packnam);
paths = _A2T(path);
CMStringW patha;
pathToAbsolute(paths, patha);
if (_waccess(patha.c_str(), 0) != 0)
paths = defaultFile;
}
CMStringW tname(_A2T(acc->szModuleName));
AddCategory(tname, displayName, acc->bIsVirtual ? smcVirtualProto : smcProto, paths);
}
}
void SmileyCategoryListType::AddProtoAsCategory(char *acc, const CMStringW &defaultFile)
{
if (acc == NULL)
return;
const char *packnam = acc;
if (mir_strcmp(packnam, "JABBER") == 0)
packnam = "JGMail";
else if (strstr(packnam, "SIP") != NULL)
packnam = "MSN";
char path[MAX_PATH];
mir_snprintf(path, "Smileys\\nova\\%s.msl", packnam);
CMStringW paths = _A2T(path), patha;
pathToAbsolute(paths, patha);
if (_waccess(patha.c_str(), 0) != 0)
paths = defaultFile;
CMStringW dName(acc), displayName;
displayName.AppendFormat(TranslateT("%s global smiley pack"), dName.GetBuffer());
CMStringW tname("AllProto");
tname += _A2T(acc);
AddCategory(tname, displayName, smcPhysProto, paths);
}
void SmileyCategoryListType::DeleteAccountAsCategory(PROTOACCOUNT *acc)
{
CMStringW tname(_A2T(acc->szModuleName));
for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) {
char *proto = GetContactProto(hContact);
if (proto == NULL)
continue;
DBVARIANT dbv;
if (!db_get_ws(hContact, proto, "Transport", &dbv)) {
bool found = (tname.CompareNoCase(dbv.ptszVal) == 0);
db_free(&dbv);
if (found)
return;
}
}
for (int i = 0; i < m_SmileyCategories.getCount(); i++) {
if (tname.CompareNoCase(m_SmileyCategories[i].GetName()) == 0) {
m_SmileyCategories.remove(i);
break;
}
}
}
void SmileyCategoryListType::AddContactTransportAsCategory(MCONTACT hContact, const CMStringW &defaultFile)
{
char *proto = GetContactProto(hContact);
if (proto == NULL)
return;
DBVARIANT dbv;
if (!db_get_ws(hContact, proto, "Transport", &dbv)) {
if (dbv.ptszVal[0] == '\0') {
db_free(&dbv);
return;
}
char *trsp = mir_strdup(_T2A(dbv.ptszVal));
_strlwr(trsp);
const char *packname = NULL;
if (strstr(trsp, "msn") != NULL)
packname = "msn";
else if (strstr(trsp, "icq") != NULL)
packname = "icq";
else if (strstr(trsp, "yahoo") != NULL)
packname = "yahoo";
else if (strstr(trsp, "aim") != NULL)
packname = "aim";
else if (strstr(trsp, "lcs") != NULL)
packname = "msn";
mir_free(trsp);
CMStringW displayName = dbv.ptszVal;
if (packname != NULL) {
char path[MAX_PATH];
mir_snprintf(path, "Smileys\\nova\\%s.msl", packname);
CMStringW paths = _A2T(path), patha;
pathToAbsolute(paths, patha);
if (_waccess(patha.c_str(), 0) != 0)
paths = defaultFile;
AddCategory(displayName, displayName, smcTransportProto, paths);
}
else AddCategory(displayName, displayName, smcTransportProto, defaultFile);
db_free(&dbv);
}
}
void SmileyCategoryListType::AddAllProtocolsAsCategory(void)
{
CMStringW displayName = TranslateT("Standard");
CMStringW tname = L"Standard";
AddCategory(tname, displayName, smcStd);
const CMStringW &defaultFile = GetSmileyCategory(tname)->GetFilename();
PROTOCOLDESCRIPTOR **proto;
int protoCount = 0;
Proto_EnumProtocols(&protoCount, &proto);
for (int i = 0; i < protoCount; i++) {
PROTOCOLDESCRIPTOR *pd = proto[i];
if (pd->type == PROTOTYPE_PROTOCOL && pd->cbSize == sizeof(*pd))
AddProtoAsCategory(pd->szName, defaultFile);
}
PROTOACCOUNT **accList;
Proto_EnumAccounts(&protoCount, &accList);
for (int i = 0; i < protoCount; i++)
AddAccountAsCategory(accList[i], defaultFile);
for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact))
AddContactTransportAsCategory(hContact, defaultFile);
CMStringW cats;
opt.ReadCustomCategories(cats);
int cppv = 0;
for (;;) {
int cp = cats.Find('#', cppv);
if (cp == -1)
break;
displayName = cats.Mid(cppv, cp - cppv);
AddCategory(displayName, displayName, smcCustom, defaultFile);
cppv = cp + 1;
}
if (cppv != cats.GetLength()) {
displayName = cats.Mid(cppv);
AddCategory(displayName, displayName, smcCustom, defaultFile);
}
}
SmileyLookup::SmileyLookup(const CMStringW &str, const bool regexs, const int ind, const CMStringW &smpt)
{
wchar_t msgtxt[1024];
m_ind = ind;
if (regexs) {
static const CMStringW testString(L"Test String");
m_pattern = _TPattern::compile(str);
m_valid = m_pattern != NULL;
if (m_valid) {
_TMatcher *matcher = m_pattern->createTMatcher(testString);
m_valid &= (!matcher->findFirstMatch() ||
matcher->getStartingIndex() != matcher->getEndingIndex());
if (!m_valid) {
static const wchar_t errmsg[] = LPGENW("Regular expression \"%s\" in smiley pack \"%s\" could produce \"empty matches\".");
mir_snwprintf(msgtxt, TranslateW(errmsg), str.c_str(), smpt.c_str());
}
delete matcher;
}
else {
static const wchar_t errmsg[] = LPGENW("Regular expression \"%s\" in smiley pack \"%s\" malformed.");
mir_snwprintf(msgtxt, TranslateW(errmsg), str.c_str(), smpt.c_str());
}
if (!m_valid)
CallService(MS_NETLIB_LOG, (WPARAM)hNetlibUser, _T2A(msgtxt));
}
else {
m_text = str;
m_pattern = NULL;
m_valid = !str.IsEmpty();
}
}
SmileyLookup::~SmileyLookup()
{
delete m_pattern;
}
void SmileyLookup::Find(const CMStringW &str, SmileyLocVecType &smlcur, bool firstOnly) const
{
if (!m_valid) return;
if (m_text.IsEmpty()) {
_TMatcher *matcher = m_pattern->createTMatcher(str);
while (matcher->findNextMatch()) {
int st = matcher->getStartingIndex();
int sz = matcher->getEndingIndex() - st;
if (sz != 0) {
smlcur.insert(new SmileyLocType(st, sz));
if (firstOnly && m_ind != -1)
return;
}
}
delete matcher;
}
else {
const wchar_t *pos = str.c_str();
while ((pos = wcsstr(pos, m_text.c_str())) != NULL) {
smlcur.insert(new SmileyLocType(pos - str.c_str(), m_text.GetLength()));
pos += m_text.GetLength();
if (firstOnly && m_ind != -1)
return;
}
}
}