/*
Miranda NG SmileyAdd Plugin
Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org)
Copyright (C) 2005-11 Boris Krasnovskiy All Rights Reserved
Copyright (C) 2003-04 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"
SmileyPackType *g_pEmoji = nullptr;
SmileyPackListType g_SmileyPacks;
SmileyCategoryListType g_SmileyCategories;
static HWND hwndHidden = nullptr;
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 == nullptr)
hwndHidden = CreateWindowEx(0, L"STATIC", nullptr, 0, 0, 0, 0, 0, nullptr, nullptr, nullptr, nullptr);
SmileyType *pType = (SmileyType*)obj;
pType->SetFrameDelay();
}
static void CALLBACK sttStopTimer(PVOID obj)
{
KillTimer(hwndHidden, (DWORD_PTR)obj);
}
void DestroyProxyWindow()
{
if (hwndHidden)
DestroyWindow(hwndHidden);
}
//
// SmileyType
//
SmileyType::SmileyType(void) :
m_arSmileys(10, PtrKeySortT)
{
m_SmileyIcon = nullptr;
m_xepimg = nullptr;
m_flags = 0;
m_index = 0;
m_size.cx = 0;
m_size.cy = 0;
}
SmileyType::~SmileyType()
{
if (m_xepimg) {
m_xepimg->Release();
m_xepimg = nullptr;
}
if (m_SmileyIcon != nullptr) {
DestroyIcon(m_SmileyIcon);
m_SmileyIcon = nullptr;
}
}
void SmileyType::AddObject(ISmileyBase *pObject)
{
if (m_arSmileys.getCount() == 0) {
if (m_xepimg == nullptr)
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 == nullptr) ? 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 (auto &it : m_arSmileys.rev_iter())
it->Draw();
SetFrameDelay(); // reset timer
}
HICON SmileyType::GetIcon(void)
{
if (m_SmileyIcon == nullptr) {
ImageBase *img = CreateCachedImage();
if (!img)
return nullptr;
img->SelectFrame(m_index);
m_SmileyIcon = img->GetIcon();
img->Release();
}
return m_SmileyIcon;
}
HICON SmileyType::GetIconDup(void)
{
if (ImageBase *img = CreateCachedImage()) {
img->SelectFrame(m_index);
HICON hIcon = img->GetIcon();
img->Release();
return hIcon;
}
return nullptr;
}
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 nullptr;
img->SelectFrame(m_index);
HBITMAP hBmp = img->GetBitmap(bkgClr, sizeX, sizeY);
img->Release();
return hBmp;
}
//
// SmileyPackType
//
SmileyPackType::SmileyPackType()
{
m_hSmList = nullptr;
errorFound = false;
}
SmileyType* SmileyPackType::GetSmiley(unsigned index)
{
return (index < (unsigned)m_SmileyList.getCount()) ? &m_SmileyList[index] : nullptr;
}
SmileyPackType::~SmileyPackType()
{
if (m_hSmList != nullptr) 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)
{
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++) {
auto &p = m_SmileyList[dist];
if (p.IsRegEx()) {
SmileyLookup *dats = new SmileyLookup(p.GetTriggerText(), true, dist, GetFilename());
if (dats->IsValid())
m_SmileyLookup.insert(dats);
else
errorFound = true;
if (p.m_InsertText.IsEmpty())
p.m_InsertText = p.m_ToolText;
}
else if (!p.IsService()) {
bool first = true;
const CMStringW &text = p.GetTriggerText();
int iStart = 0;
while (true) {
CMStringW wszWord = text.Tokenize(L" \t", iStart);
if (iStart == -1)
break;
ReplaceAllSpecials(wszWord, wszWord);
SmileyLookup *dats = new SmileyLookup(wszWord, false, dist, GetFilename());
if (dats->IsValid()) {
m_SmileyLookup.insert(dats);
if (first) {
p.m_InsertText = wszWord;
first = false;
}
}
else delete dats;
}
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////
static MRegexp16 isCode(L"\\&\\#(\\d*)\\;");
static void replaceCodes(CMStringW &str)
{
if (isCode.match(str) < 0)
return;
str.Delete(isCode.getPos(), isCode.getLength());
uint32_t iCode = _wtoi(isCode.getGroup(1));
wchar_t tmp[3] = { 0, 0, 0 };
if (iCode < 0x10000)
tmp[0] = LOWORD(iCode), tmp[1] = HIWORD(iCode);
else {
iCode -= 0x10000;
tmp[0] = 0xD800 + (iCode >> 10);
tmp[1] = 0xDC00 + (iCode & 0x3FF);
}
str.Insert(isCode.getPos(), tmp);
}
void SmileyPackType::ReplaceAllSpecials(const CMStringW &Input, CMStringW &Output)
{
Output = Input;
Output.Replace(L"%%_%%", L" ");
Output.Replace(L"%%__%%", L" ");
Output.Replace(L"%%''%%", L"\"");
replaceCodes(Output);
}
void SmileyPackType::Clear(void)
{
m_SmileyList.destroy();
m_SmileyLookup.destroy();
if (m_hSmList != nullptr) { ImageList_Destroy(m_hSmList); m_hSmList = nullptr; }
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;
}
wchar_t wszTmp[MAX_PATH];
CMStringW modpath = VARSW(filename);
if (_waccess(modpath, 4) != 0) {
PathToAbsoluteW(filename, wszTmp, g_plugin.wszDefaultPath);
if (_waccess(wszTmp, 4) != 0) {
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.c_str());
ReportError(msgtxt);
}
m_Name = L"Nothing loaded";
return false;
}
}
else PathToAbsoluteW(modpath, wszTmp, g_plugin.wszDefaultPath);
modpath = wszTmp;
m_Filename = filename;
// Load file
bool res;
if (filename.Find(L".xep") == -1)
res = LoadSmileyFileMSL(modpath, onlyInfo, modpath);
else
res = LoadSmileyFileXEP(modpath, onlyInfo);
if (errorFound)
ReportError(TranslateT("There were problems loading smiley pack (it should be corrected).\nSee network log for details."));
return res;
}
static IStream* DecodeBase64Data(const char *pString)
{
if (pString == nullptr)
return nullptr;
size_t dataLen;
ptrA data((char*)mir_base64_decode(pString, &dataLen));
if (data == nullptr)
return nullptr;
// Read image list
HGLOBAL hBuffer = GlobalAlloc(GMEM_MOVEABLE, dataLen);
if (!hBuffer)
return nullptr;
void *dst = GlobalLock(hBuffer);
memcpy(dst, data, dataLen);
GlobalUnlock(hBuffer);
IStream *pStream = nullptr;
CreateStreamOnHGlobal(hBuffer, TRUE, &pStream);
return pStream;
}
static CMStringW FilterQuotes(const char *pStr)
{
CMStringW res(pStr);
int iStart = res.Find('\"', 0);
if (iStart != -1) {
int iEnd = res.Find('\"', ++iStart);
if (iEnd != -1)
res = res.Mid(iStart, iEnd - iStart);
}
return res.Trim();
}
bool SmileyPackType::LoadSmileyFileXEP(const CMStringW &fileName, bool onlyInfo)
{
FILE *in = _wfopen(fileName, L"rb");
if (in == nullptr)
return false;
TiXmlDocument doc;
int ret = doc.LoadFile(in);
fclose(in);
if (ret != 0)
return false;
auto *pSettings = doc.FirstChildElement("settings");
if (pSettings != nullptr) {
if (auto *pNode = pSettings->FirstChildElement("DataBaseName"))
m_Name = CMStringW(Utf2T(pNode->GetText())).Trim();
if (auto *pNode = pSettings->FirstChildElement("PackageAuthor"))
m_Author = CMStringW(Utf2T(pNode->GetText())).Trim();
}
if (!onlyInfo) {
auto *pImages = TiXmlConst(&doc)["lists"]["images"].ToElement();
if (pImages) {
IStream *pStream = DecodeBase64Data(pImages->GetText());
if (pStream) {
if (m_hSmList != nullptr)
ImageList_Destroy(m_hSmList);
m_hSmList = ImageList_Read(pStream);
pStream->Release();
}
}
for (auto *nRec : TiXmlFilter(doc.FirstChildElement("dataroot"), "record")) {
int idx = nRec->IntAttribute("ImageIndex", -1);
if (idx == -1)
continue;
SmileyType *dat = new SmileyType;
dat->SetRegEx(true);
dat->SetImList(m_hSmList, idx);
dat->m_ToolText = FilterQuotes(nRec->GetText());
if (auto *pNode = nRec->FirstChildElement("Expression"))
dat->m_TriggerText = FilterQuotes(pNode->GetText());
if (auto *pNode = nRec->FirstChildElement("PasteText"))
dat->m_InsertText = FilterQuotes(pNode->GetText());
dat->SetHidden(dat->m_InsertText.IsEmpty());
if (auto *pNode = nRec->FirstChildElement("Image")) {
IStream *pStream = DecodeBase64Data(pNode->GetText());
if (pStream) {
dat->LoadFromImage(pStream);
pStream->Release();
}
}
m_SmileyList.insert(dat);
}
}
m_VisibleCount = m_SmileyList.getCount();
AddTriggersToSmileyLookup();
selec.x = selec.y = win.x = win.y = 0;
return true;
}
bool SmileyPackType::LoadSmileyFileMSL(const CMStringW &filename, bool onlyInfo, CMStringW &modpath)
{
int fh = _wopen(filename.c_str(), _O_BINARY | _O_RDONLY);
if (fh == -1)
return false;
// Find file size
const long flen = _filelength(fh);
// Allocate file buffer
char *buf = new char[flen + sizeof(wchar_t)];
// Read 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 = Utf2T(buf + 3);
else
tbuf = _A2T(buf);
delete[] buf;
CMStringW pathstr, packstr;
{
MRegexp16 pathsplit(L"(.*\\\\)(.*)\\.|$");
pathsplit.match(modpath);
pathstr = pathsplit.getGroup(1);
packstr = pathsplit.getGroup(2);
}
if (!onlyInfo)
selec.x = selec.y = win.x = win.y = 0;
int iStart = 0;
MRegexp16 otherf(L"^\\s*(Name|Author|Date|Version|ButtonSmiley)\\s*=\\s*\"(.*)\"");
MRegexp16 size(L"^\\s*(Selection|Window)Size\\s*=\\s*(\\d+)\\s*,\\s*(\\d+)");
MRegexp16 smiley(
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
SmileyVectorType hiddenSmileys;
unsigned smnum = 0;
while (true) {
CMStringW line = tbuf.Tokenize(L"\r\n", iStart);
if (iStart == -1)
break;
if (line.IsEmpty() || line[0] == ';')
continue;
if (otherf.match(line) >= 0) {
CMStringW key(otherf.getGroup(1)), value(otherf.getGroup(2));
if (key == L"Name")
m_Name = value;
else if (key == L"Author")
m_Author = value;
else if (key == L"Date")
m_Date = value;
else if (key == L"Version")
m_Version = value;
else if (key == L"ButtonSmiley")
m_ButtonSmiley = value;
continue;
}
if (onlyInfo)
continue;
if (size.match(line) >= 0) {
POINT tpt;
tpt.x = _wtol(size.getGroup(2));
tpt.y = _wtol(size.getGroup(3));
if (size.getGroup(1) == L"Selection")
selec = tpt;
else if (size.getGroup(1) == L"Window")
win = tpt;
continue;
}
if (smiley.match(line)) {
CMStringW resname = smiley.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(smiley.getGroup(3));
dat->SetHidden(!smiley.getGroup(1).IsEmpty());
CMStringW wszGrp4(smiley.getGroup(4));
if (!wszGrp4.IsEmpty()) {
dat->SetRegEx(wszGrp4 == L"R");
dat->SetService(wszGrp4 == L"S");
}
dat->m_TriggerText = smiley.getGroup(5);
CMStringW wszGrp6(smiley.getGroup(6)), wszGrp7(smiley.getGroup(7));
if (dat->IsRegEx()) {
if (!wszGrp6.IsEmpty())
ReplaceAllSpecials(wszGrp6, dat->m_InsertText);
if (!wszGrp7.IsEmpty())
ReplaceAllSpecials(wszGrp7, dat->m_ToolText);
else
dat->m_ToolText = dat->m_InsertText;
}
else {
if (!wszGrp6.IsEmpty())
ReplaceAllSpecials(wszGrp6, 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());
Netlib_LogW(hNetlibUser, msgtxt);
errorFound = true;
}
smnum++;
}
}
m_VisibleCount = m_SmileyList.getCount();
m_SmileyList.splice(hiddenSmileys);
AddTriggersToSmileyLookup();
return true;
}
/////////////////////////////////////////////////////////////////////////////////////////
// SmileyPackListType
bool SmileyPackListType::AddSmileyPack(CMStringW &filename, CMStringW &packname)
{
bool res = true;
if (GetSmileyPack(filename) == nullptr) { //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 = VARSW(filename);
for (auto &it : m_SmileyPacks) {
CMStringW modpath1(VARSW(it->GetFilename()));
if (mir_wstrcmpi(modpath.c_str(), modpath1.c_str()) == 0)
return it;
}
return nullptr;
}
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 (auto &it : m_SmileyCategories) {
it->Load();
if (it->GetName() == L"Emoji")
g_pEmoji = it->GetSmileyPack();
}
}
SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(const wchar_t *pwszName)
{
if (!pwszName)
return nullptr;
for (auto &it : m_SmileyCategories)
if (it->GetName().CompareNoCase(pwszName) == 0)
return it;
return nullptr;
}
SmileyCategoryType* SmileyCategoryListType::GetSmileyCategory(unsigned index)
{
return index < (unsigned)m_SmileyCategories.getCount() ? &m_SmileyCategories[index] : nullptr;
}
SmileyPackType* SmileyCategoryListType::GetSmileyPack(const CMStringW &categoryname)
{
SmileyCategoryType *smc = GetSmileyCategory(categoryname);
return smc != nullptr ? smc->GetSmileyPack() : nullptr;
}
void SmileyCategoryListType::SaveSettings(void)
{
CMStringW catstr;
for (auto &it : m_SmileyCategories) {
it->SaveSettings();
if (it->IsCustom()) {
if (!catstr.IsEmpty())
catstr += '#';
catstr += it->GetName();
}
}
opt.WriteCustomCategories(catstr);
}
void SmileyCategoryListType::AddAndLoad(const wchar_t *name, const wchar_t *displayName)
{
if (auto *pCategory = AddCategory(name, displayName, smcExt))
pCategory->Load();
}
SmileyCategoryType* SmileyCategoryListType::AddCategory(const wchar_t *name, const wchar_t *displayName, SmcType typ, const wchar_t *defaultFilename)
{
auto *pCategory = GetSmileyCategory(name);
if (pCategory == nullptr) {
pCategory = new SmileyCategoryType(m_pSmileyPackStore, name, displayName, defaultFilename, typ);
m_SmileyCategories.insert(pCategory);
}
return pCategory;
}
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 (!acc->IsEnabled() || !acc->szProtoName || !IsSmileyProto(acc->szModuleName))
return;
CMStringW PhysProtoName, paths;
DBVARIANT dbv;
if (db_get_ws(0, acc->szModuleName, "AM_BaseProto", &dbv) == 0) {
PhysProtoName = L"AllProto";
PhysProtoName += dbv.pwszVal;
db_free(&dbv);
}
if (!PhysProtoName.IsEmpty()) {
auto *p = g_SmileyCategories.GetSmileyCategory(PhysProtoName);
paths = (p) ? p->GetFilename() : L"";
}
// assemble default path
if (paths.IsEmpty()) {
const char *packnam = acc->szProtoName;
if (mir_strcmp(packnam, "JABBER") == 0)
packnam = "JGMail";
wchar_t path[MAX_PATH];
mir_snwprintf(path, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam);
if (_waccess(path, 0) != 0)
paths = defaultFile;
}
_A2T wszModule(acc->szModuleName);
if (acc->tszAccountName)
AddCategory(wszModule, acc->tszAccountName, acc->bIsVirtual ? smcVirtualProto : smcProto, paths);
else
AddCategory(wszModule, wszModule, acc->bIsVirtual ? smcVirtualProto : smcProto, paths);
}
void SmileyCategoryListType::AddProtoAsCategory(char *acc, const CMStringW &defaultFile)
{
if (acc == nullptr)
return;
const char *packnam = acc;
if (mir_strcmp(packnam, "JABBER") == 0)
packnam = "JGMail";
// assemble default path
CMStringW paths(FORMAT, L"%s\\Smileys\\nova\\%S.msl", g_plugin.wszDefaultPath, packnam);
paths = VARSW(paths);
if (_waccess(paths.c_str(), 0) != 0)
paths = defaultFile;
CMStringW dName(acc), displayName;
displayName.AppendFormat(TranslateT("%s global smiley pack"), dName.c_str());
AddCategory(L"AllProto" + dName, displayName, smcPhysProto, paths);
}
void SmileyCategoryListType::DeleteAccountAsCategory(PROTOACCOUNT *acc)
{
CMStringW tname(acc->szModuleName);
for (auto &hContact : Contacts()) {
char *proto = Proto_GetBaseAccountName(hContact);
if (proto == nullptr)
continue;
DBVARIANT dbv;
if (!db_get_ws(hContact, proto, "Transport", &dbv)) {
bool found = (tname.CompareNoCase(dbv.pwszVal) == 0);
db_free(&dbv);
if (found)
return;
}
}
for (auto &it : m_SmileyCategories) {
if (tname.CompareNoCase(it->GetName()) == 0) {
m_SmileyCategories.removeItem(&it);
break;
}
}
}
void SmileyCategoryListType::AddContactTransportAsCategory(MCONTACT hContact, const CMStringW &defaultFile)
{
if (char *proto = Proto_GetBaseAccountName(hContact)) {
CMStringW displayName(db_get_wsm(hContact, proto, "Transport"));
if (!displayName.IsEmpty())
AddCategory(displayName, displayName, smcTransportProto, defaultFile);
}
}
void SmileyCategoryListType::AddAllProtocolsAsCategory(void)
{
AddCategory(L"Standard", TranslateT("Standard"), smcStd);
AddCategory(L"Emoji", TranslateT("Emoji"), smcStd);
auto &defaultFile = GetSmileyCategory(L"Standard")->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_PROTOWITHACCS)
AddProtoAsCategory(pd->szName, defaultFile);
}
for (auto &pa : Accounts())
AddAccountAsCategory(pa, defaultFile);
for (auto &hContact : Contacts())
AddContactTransportAsCategory(hContact, defaultFile);
CMStringW cats, displayName;
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);
}
}
static const CMStringW testString(L"Test String");
SmileyLookup::SmileyLookup(const CMStringW &str, const bool regexs, const int ind, const CMStringW &smpt)
{
m_ind = ind;
if (regexs) {
m_pattern.compile(str);
m_valid = m_pattern.isValid();
if (!m_valid) {
wchar_t msgtxt[1024];
mir_snwprintf(msgtxt, TranslateT("Regular expression \"%s\" in smiley pack \"%s\" malformed."), str.c_str(), smpt.c_str());
Netlib_LogW(hNetlibUser, msgtxt);
}
}
else {
m_text = str;
replaceCodes(m_text);
m_valid = !str.IsEmpty();
}
}
SmileyLookup::~SmileyLookup()
{
}
void SmileyLookup::Find(const CMStringW &str, OBJLIST &smlcur, bool firstOnly)
{
if (!m_valid)
return;
if (m_text.IsEmpty()) {
while (m_pattern.nextMatch(str) >= 0) {
CMStringW wszMatch(m_pattern.getMatch());
smlcur.insert(new SmileyLocType(m_pattern.getPos(), wszMatch.GetLength(), this));
if (firstOnly && m_ind != -1)
return;
}
}
else {
const wchar_t *pos = str.c_str();
while ((pos = wcsstr(pos, m_text.c_str())) != nullptr) {
smlcur.insert(new SmileyLocType(pos - str.c_str(), m_text.GetLength(), this));
pos += m_text.GetLength();
if (firstOnly && m_ind != -1)
return;
}
}
}