/*
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"
/////////////////////////////////////////////////////////////////////////////////////////
// Service functions
SmileyPackType* FindSmileyPack(const char *proto, MCONTACT hContact, SmileyPackCType **smlc)
{
hContact = DecodeMetaContact(hContact);
if (smlc)
*smlc = g_SmileyPackCStore.GetSmileyPack(proto);
if (proto != nullptr && IsBadStringPtrA(proto, 10))
return nullptr;
CMStringW categoryName;
if (hContact != 0) {
opt.ReadContactCategory(hContact, categoryName);
if (categoryName == L"") return nullptr;
if (!categoryName.IsEmpty() && g_SmileyCategories.GetSmileyCategory(categoryName) == nullptr) {
categoryName.Empty();
opt.WriteContactCategory(hContact, categoryName);
}
if (categoryName.IsEmpty() && !opt.UseOneForAll) {
char *protonam = Proto_GetBaseAccountName(hContact);
if (protonam != nullptr) {
DBVARIANT dbv;
if (db_get_ws(hContact, protonam, "Transport", &dbv) == 0) {
categoryName = dbv.pwszVal;
db_free(&dbv);
}
else if (opt.UsePhysProto && db_get_ws(0, protonam, "AM_BaseProto", &dbv) == 0) {
categoryName = L"AllProto";
categoryName += dbv.pwszVal;
db_free(&dbv);
auto *p = g_SmileyCategories.GetSmileyCategory(categoryName);
if (!p || p->GetFilename().IsEmpty())
categoryName = protonam;
}
else categoryName = protonam;
}
}
}
if (categoryName.IsEmpty()) {
if (proto == nullptr || proto[0] == 0)
categoryName = L"Standard";
else {
categoryName = proto;
if (opt.UseOneForAll) {
SmileyCategoryType *smc = g_SmileyCategories.GetSmileyCategory(categoryName);
if (smc == nullptr || smc->IsProto())
categoryName = L"Standard";
}
}
}
return g_SmileyCategories.GetSmileyPack(categoryName);
}
/////////////////////////////////////////////////////////////////////////////////////////
static INT_PTR ReplaceSmileysCommand(WPARAM, LPARAM lParam)
{
SMADD_RICHEDIT *smre = (SMADD_RICHEDIT*)lParam;
if (smre == nullptr)
return FALSE;
SMADD_RICHEDIT smrec = {};
memcpy(&smrec, smre, sizeof(smrec));
static const CHARRANGE selection = { 0, LONG_MAX };
if (smre->rangeToReplace == nullptr)
smrec.rangeToReplace = (CHARRANGE*)&selection;
else if (smrec.rangeToReplace->cpMax < 0)
smrec.rangeToReplace->cpMax = LONG_MAX;
SmileyPackCType *smcp = nullptr;
SmileyPackType *SmileyPack = FindSmileyPack(smrec.Protocolname, smrec.hContact, (smrec.flags & SAFLRE_NOCUSTOM) ? nullptr : &smcp);
ReplaceSmileys(smre->hwndRichEditControl, SmileyPack, smcp, *smrec.rangeToReplace,
smrec.hContact == 0, false, false, (smre->flags & SAFLRE_FIREVIEW) ? true : false);
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////////////
static int GetInfoCommandE(SMADD_INFO *smre, bool retDup)
{
if (smre == nullptr)
return FALSE;
SmileyPackType *SmileyPack = FindSmileyPack(smre->Protocolname);
if (SmileyPack == nullptr || SmileyPack->SmileyCount() == 0) {
smre->ButtonIcon = nullptr;
smre->NumberOfSmileys = 0;
smre->NumberOfVisibleSmileys = 0;
return FALSE;
}
SmileyType *sml = FindButtonSmiley(SmileyPack);
if (sml != nullptr)
smre->ButtonIcon = retDup ? sml->GetIconDup() : sml->GetIcon();
else
smre->ButtonIcon = GetDefaultIcon(retDup);
smre->NumberOfSmileys = SmileyPack->SmileyCount();
smre->NumberOfVisibleSmileys = SmileyPack->VisibleSmileyCount();
return TRUE;
}
static INT_PTR GetInfoCommand(WPARAM, LPARAM lParam)
{
return GetInfoCommandE((SMADD_INFO*)lParam, false);
}
static INT_PTR GetInfoCommand2(WPARAM, LPARAM lParam)
{
return GetInfoCommandE((SMADD_INFO*)lParam, true);
}
/////////////////////////////////////////////////////////////////////////////////////////
static INT_PTR ParseTextBatch(WPARAM, LPARAM lParam)
{
SMADD_BATCHPARSE *smre = (SMADD_BATCHPARSE*)lParam;
if (smre == nullptr)
return FALSE;
SmileyPackCType *smcp = nullptr;
SmileysQueueType smllist;
SmileyPackType *SmileyPack = FindSmileyPack(smre->Protocolname, smre->hContact, (smre->flag & (SAFL_OUTGOING | SAFL_NOCUSTOM)) ? nullptr : &smcp);
if (smre->flag & SAFL_UNICODE)
LookupAllSmileys(SmileyPack, smcp, smre->str.w, smllist, false);
else
LookupAllSmileys(SmileyPack, smcp, _A2T(smre->str.a), smllist, false);
if (smllist.getCount() == 0)
return 0;
SMADD_BATCHPARSERES *res = new SMADD_BATCHPARSERES[smllist.getCount()];
SMADD_BATCHPARSERES *cres = res;
for (auto &it : smllist) {
cres->startChar = it->loc.cpMin;
cres->size = it->loc.cpMax - it->loc.cpMin;
if (it->sml) {
if (smre->flag & SAFL_PATH)
cres->filepath = it->sml->GetFilePath().c_str();
else
cres->hIcon = it->sml->GetIconDup();
}
else {
if (smre->flag & SAFL_PATH)
cres->filepath = it->smlc->GetFilePath().c_str();
else
cres->hIcon = it->smlc->GetIcon();
}
cres++;
}
smre->numSmileys = smllist.getCount();
smre->oflag = smre->flag | SAFL_UNICODE;
return (INT_PTR)res;
}
/////////////////////////////////////////////////////////////////////////////////////////
static INT_PTR FreeTextBatch(WPARAM, LPARAM lParam)
{
delete[](SMADD_BATCHPARSERES*)lParam;
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////////////
static INT_PTR RegisterPack(WPARAM, LPARAM lParam)
{
SMADD_REGCAT *smre = (SMADD_REGCAT*)lParam;
if (smre == nullptr)
return FALSE;
if (IsBadStringPtrA(smre->name, 50) || IsBadStringPtrA(smre->dispname, 50))
return FALSE;
g_SmileyCategories.AddAndLoad(_A2T(smre->name), _A2T(smre->dispname));
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////////////
static LIST menuHandleArray(5);
static int RebuildContactMenu(WPARAM wParam, LPARAM)
{
auto &smc = g_SmileyCategories.GetSmileyCategoryList();
char *protnam = Proto_GetBaseAccountName(wParam);
bool haveMenu = IsSmileyProto(protnam);
if (haveMenu && opt.UseOneForAll) {
unsigned cnt = 0;
for (auto &it : smc)
cnt += it->IsCustom();
haveMenu = cnt != 0;
}
Menu_ShowItem(hContactMenuItem, haveMenu);
for (auto &it : menuHandleArray)
Menu_RemoveItem((HGENMENU)it);
menuHandleArray.destroy();
if (haveMenu) {
CMStringW cat;
opt.ReadContactCategory(wParam, cat);
CMenuItem mi(&g_plugin);
mi.root = hContactMenuItem;
mi.flags = CMIF_UNICODE | CMIF_SYSTEM;
mi.pszService = MS_SMILEYADD_CUSTOMCATMENU;
bool nonecheck = true;
HGENMENU hMenu;
for (int i = 0; i < smc.getCount(); i++) {
if (smc[i].IsExt() || (smc[i].IsProto() && opt.UseOneForAll) || smc[i].GetFilename().IsEmpty())
continue;
const int ind = i + 3;
mi.position = ind;
mi.name.w = (wchar_t *)smc[i].GetDisplayName().c_str();
if (cat == smc[i].GetName()) {
mi.flags |= CMIF_CHECKED;
nonecheck = false;
}
hMenu = Menu_AddContactMenuItem(&mi);
Menu_ConfigureItem(hMenu, MCI_OPT_EXECPARAM, ind);
menuHandleArray.insert(hMenu);
mi.flags &= ~CMIF_CHECKED;
}
mi.position = 1;
mi.name.w = L"";
if (cat == L"") {
mi.flags |= CMIF_CHECKED;
nonecheck = false;
}
hMenu = Menu_AddContactMenuItem(&mi);
Menu_ConfigureItem(hMenu, MCI_OPT_EXECPARAM, 1);
menuHandleArray.insert(hMenu);
mi.position = 2;
mi.name.w = LPGENW("Protocol specific");
if (nonecheck) mi.flags |= CMIF_CHECKED; else mi.flags &= ~CMIF_CHECKED;
hMenu = Menu_AddContactMenuItem(&mi);
Menu_ConfigureItem(hMenu, MCI_OPT_EXECPARAM, 2);
menuHandleArray.insert(hMenu);
}
return 0;
}
INT_PTR CustomCatMenu(WPARAM hContact, LPARAM lParam)
{
if (lParam != 0) {
SmileyCategoryType *smct = g_SmileyCategories.GetSmileyCategory((unsigned)lParam - 3);
if (smct != nullptr)
opt.WriteContactCategory(hContact, smct->GetName());
else {
CMStringW empty;
if (lParam == 1) empty = L"";
opt.WriteContactCategory(hContact, empty);
}
NotifyEventHooks(g_hevOptionsChanged, hContact, 0);
}
for (auto &it : menuHandleArray)
Menu_RemoveItem((HGENMENU)it);
menuHandleArray.destroy();
return TRUE;
}
/////////////////////////////////////////////////////////////////////////////////////////
static INT_PTR ReloadPack(WPARAM, LPARAM lParam)
{
if (lParam) {
SmileyCategoryType *smc = g_SmileyCategories.GetSmileyCategory(_A2T((char *)lParam));
if (smc != nullptr)
smc->Load();
}
else {
g_SmileyCategories.ClearAll();
g_SmileyCategories.AddAllProtocolsAsCategory();
g_SmileyCategories.ClearAndLoadAll();
}
NotifyEventHooks(g_hevOptionsChanged, 0, 0);
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////
static INT_PTR LoadContactSmileys(WPARAM, LPARAM lParam)
{
SMADD_CONT *cont = (SMADD_CONT *)lParam;
switch (cont->type) {
case SMADD_SMILEPACK:
g_SmileyPackCStore.AddSmileyPack(cont->pszModule, cont->path);
NotifyEventHooks(g_hevOptionsChanged, 0, (WPARAM)cont->pszModule);
break;
case SMADD_FILE:
if (g_SmileyPackCStore.AddSmiley(cont->pszModule, cont->path))
NotifyEventHooks(g_hevOptionsChanged, 0, (WPARAM)cont->pszModule);
break;
case SMADD_FOLDER:
auto *p = wcsrchr(cont->path, '\\');
CMStringW wszPath(cont->path, (p == nullptr) ? lstrlen(cont->path) : p - cont->path + 1);
for (auto &it : MFilePath(cont->path).search())
if (mir_wstrcmp(it.getPath(), L".") && mir_wstrcmp(it.getPath(), L".."))
g_SmileyPackCStore.AddSmiley(cont->pszModule, wszPath + it.getPath());
}
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////
static INT_PTR SelectSmiley(WPARAM, LPARAM lParam)
{
auto *pParam = (SMADD_SELECTSMILEY *)lParam;
if (pParam == nullptr || !g_pEmoji)
return 1;
SmileyPackType *pPack;
if (pParam->pszSmileys) {
ptrW pText(mir_utf8decodeW(pParam->pszSmileys));
pPack = new SmileyPackType();
auto &pList = g_pEmoji->GetSmileyList();
for (auto *p = wcstok(pText, L" "); p; p = wcstok(0, L" ")) {
for (auto &it : pList) {
if (it->GetTriggerText() == p) {
pPack->GetSmileyList().insert(new SmileyType(*it));
break;
}
}
}
}
else pPack = new SmileyPackType(*g_pEmoji);
SmileyToolWindowParam *stwp = new SmileyToolWindowParam;
stwp->pSmileyPack = pPack;
stwp->bOwnsPack = true;
stwp->hWndParent = pParam->hWndParent;
stwp->hWndTarget = pParam->hWndTarget;
stwp->targetMessage = pParam->targetMessage;
stwp->targetWParam = pParam->targetWParam;
stwp->direction = pParam->direction;
stwp->xPosition = pParam->xPosition;
stwp->yPosition = pParam->yPosition;
mir_forkThread(SmileyToolThread, stwp);
return 0;
}
/////////////////////////////////////////////////////////////////////////////////////////
// Module entry point
void InitServices()
{
HookEvent(ME_CLIST_PREBUILDCONTACTMENU, RebuildContactMenu);
CreateServiceFunction(MS_SMILEYADD_REPLACESMILEYS, ReplaceSmileysCommand);
CreateServiceFunction(MS_SMILEYADD_GETINFO, GetInfoCommand);
CreateServiceFunction(MS_SMILEYADD_GETINFO2, GetInfoCommand2);
CreateServiceFunction(MS_SMILEYADD_REGISTERCATEGORY, RegisterPack);
CreateServiceFunction(MS_SMILEYADD_BATCHPARSE, ParseTextBatch);
CreateServiceFunction(MS_SMILEYADD_BATCHFREE, FreeTextBatch);
CreateServiceFunction(MS_SMILEYADD_CUSTOMCATMENU, CustomCatMenu);
CreateServiceFunction(MS_SMILEYADD_RELOAD, ReloadPack);
CreateServiceFunction("SmileyAdd/LoadContactSmileys", LoadContactSmileys);
CreateServiceFunction(MS_SMILEYADD_SELECTSMILEY, SelectSmiley);
}