/*
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"
OptionsType opt;
/////////////////////////////////////////////////////////////////////////////////////////
void OptionsType::Save(void)
{
g_plugin.setByte("EnforceSpaces", EnforceSpaces);
g_plugin.setByte("ScaleToTextheight", ScaleToTextheight);
g_plugin.setByte("UseOneForAll", UseOneForAll);
g_plugin.setByte("UsePhysProto", UsePhysProto);
g_plugin.setByte("SurroundSmileyWithSpaces", SurroundSmileyWithSpaces);
g_plugin.setByte("ScaleAllSmileys", ScaleAllSmileys);
g_plugin.setByte("IEViewStyle", IEViewStyle);
g_plugin.setByte("AnimateSel", AnimateSel);
g_plugin.setByte("AnimateDlg", AnimateDlg);
g_plugin.setByte("InputSmileys", InputSmileys);
g_plugin.setByte("DCursorSmiley", DCursorSmiley);
g_plugin.setByte("HQScaling", HQScaling);
g_plugin.setDword("MaxCustomSmileySize", MaxCustomSmileySize);
g_plugin.setDword("MinSmileySize", MinSmileySize);
g_plugin.setByte("HorizontalSorting", HorizontalSorting);
}
void OptionsType::Load(void)
{
EnforceSpaces = g_plugin.getByte("EnforceSpaces", FALSE) != 0;
ScaleToTextheight = g_plugin.getByte("ScaleToTextheight", FALSE) != 0;
UseOneForAll = g_plugin.getByte("UseOneForAll", TRUE) != 0;
UsePhysProto = g_plugin.getByte("UsePhysProto", FALSE) != 0;
SurroundSmileyWithSpaces = g_plugin.getByte("SurroundSmileyWithSpaces", FALSE) != 0;
ScaleAllSmileys = g_plugin.getByte("ScaleAllSmileys", FALSE) != 0;
IEViewStyle = g_plugin.getByte("IEViewStyle", FALSE) != 0;
AnimateSel = g_plugin.getByte("AnimateSel", TRUE) != 0;
AnimateDlg = g_plugin.getByte("AnimateDlg", TRUE) != 0;
InputSmileys = g_plugin.getByte("InputSmileys", TRUE) != 0;
DCursorSmiley = g_plugin.getByte("DCursorSmiley", FALSE) != 0;
HQScaling = g_plugin.getByte("HQScaling", FALSE) != 0;
SelWndBkgClr = g_plugin.getDword("SelWndBkgClr", GetSysColor(COLOR_WINDOW));
MaxCustomSmileySize = g_plugin.getDword("MaxCustomSmileySize", 0);
MinSmileySize = g_plugin.getDword("MinSmileySize", 0);
HorizontalSorting = g_plugin.getByte("HorizontalSorting", 1) != 0;
}
void OptionsType::ReadPackFileName(CMStringW &filename, const CMStringW &name, const CMStringW &defaultFilename)
{
CMStringW settingKey = name + L"-filename";
ptrW tszValue(g_plugin.getWStringA(_T2A(settingKey.c_str())));
if (tszValue != nullptr)
filename = tszValue;
else {
if (defaultFilename.IsEmpty())
filename.Format(L"%s\\Smileys\\nova\\default.msl", g_plugin.wszDefaultPath);
else
filename = defaultFilename;
}
}
void OptionsType::WritePackFileName(const CMStringW &filename, const CMStringW &name)
{
CMStringW settingKey = name + L"-filename";
g_plugin.setWString(_T2A(settingKey.c_str()), filename.c_str());
}
void OptionsType::ReadCustomCategories(CMStringW &cats)
{
ptrW tszValue(g_plugin.getWStringA("CustomCategories"));
if (tszValue != nullptr)
cats = tszValue;
}
void OptionsType::WriteCustomCategories(const CMStringW &cats)
{
if (cats.IsEmpty())
g_plugin.delSetting("CustomCategories");
else
g_plugin.setWString("CustomCategories", cats.c_str());
}
void OptionsType::ReadContactCategory(MCONTACT hContact, CMStringW &cats)
{
ptrW tszValue(g_plugin.getWStringA(hContact, "CustomCategory"));
if (tszValue != nullptr)
cats = tszValue;
}
void OptionsType::WriteContactCategory(MCONTACT hContact, const CMStringW &cats)
{
if (cats.IsEmpty())
g_plugin.delSetting(hContact, "CustomCategory");
else
g_plugin.setWString(hContact, "CustomCategory", cats.c_str());
}
/////////////////////////////////////////////////////////////////////////////////////////
// Basic class for options
class COptionsBaseDialog : public CDlgBase
{
void OnFinish(CDlgBase*)
{
ProcessAllInputAreas(true);
CloseSmileys();
opt.Save();
NotifyEventHooks(g_hevOptionsChanged, 0, 0);
ProcessAllInputAreas(false);
}
protected:
COptionsBaseDialog(int dlgId) :
CDlgBase(g_plugin, dlgId)
{
m_OnFinishWizard = Callback(this, &COptionsBaseDialog::OnFinish);
}
};
/////////////////////////////////////////////////////////////////////////////////////////
// General options page
class CGeneralOptions : public COptionsBaseDialog
{
CCtrlSpin spinMax, spinMin;
CCtrlHyperlink linkGetMore;
public:
CGeneralOptions() :
COptionsBaseDialog(IDD_OPT_GENERAL),
spinMin(this, IDC_MINSPIN, 99),
spinMax(this, IDC_MAXCUSTSPIN, 99),
linkGetMore(this, IDC_GETMORE, "https://miranda-ng.org/tags/smileyadd/")
{}
bool OnInitDialog() override
{
CheckDlgButton(m_hwnd, IDC_SPACES, opt.EnforceSpaces ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_SCALETOTEXTHEIGHT, opt.ScaleToTextheight ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_APPENDSPACES, opt.SurroundSmileyWithSpaces ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_SCALEALLSMILEYS, opt.ScaleAllSmileys ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_IEVIEWSTYLE, opt.IEViewStyle ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_ANIMATESEL, opt.AnimateSel ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_ANIMATEDLG, opt.AnimateDlg ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_INPUTSMILEYS, opt.InputSmileys ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_DCURSORSMILEY, opt.DCursorSmiley ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_HQSCALING, opt.HQScaling ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(m_hwnd, IDC_SORTING_HORIZONTAL, opt.HorizontalSorting ? BST_CHECKED : BST_UNCHECKED);
spinMin.SetPosition(opt.MinSmileySize);
spinMax.SetPosition(opt.MaxCustomSmileySize);
return true;
}
bool OnApply() override
{
opt.EnforceSpaces = IsDlgButtonChecked(m_hwnd, IDC_SPACES) == BST_CHECKED;
opt.ScaleToTextheight = IsDlgButtonChecked(m_hwnd, IDC_SCALETOTEXTHEIGHT) == BST_CHECKED;
opt.SurroundSmileyWithSpaces = IsDlgButtonChecked(m_hwnd, IDC_APPENDSPACES) == BST_CHECKED;
opt.ScaleAllSmileys = IsDlgButtonChecked(m_hwnd, IDC_SCALEALLSMILEYS) == BST_CHECKED;
opt.IEViewStyle = IsDlgButtonChecked(m_hwnd, IDC_IEVIEWSTYLE) == BST_CHECKED;
opt.AnimateSel = IsDlgButtonChecked(m_hwnd, IDC_ANIMATESEL) == BST_CHECKED;
opt.AnimateDlg = IsDlgButtonChecked(m_hwnd, IDC_ANIMATEDLG) == BST_CHECKED;
opt.InputSmileys = IsDlgButtonChecked(m_hwnd, IDC_INPUTSMILEYS) == BST_CHECKED;
opt.DCursorSmiley = IsDlgButtonChecked(m_hwnd, IDC_DCURSORSMILEY) == BST_CHECKED;
opt.HQScaling = IsDlgButtonChecked(m_hwnd, IDC_HQSCALING) == BST_CHECKED;
opt.HorizontalSorting = IsDlgButtonChecked(m_hwnd, IDC_SORTING_HORIZONTAL) == BST_CHECKED;
opt.MinSmileySize = spinMin.GetPosition();
opt.MaxCustomSmileySize = spinMax.GetPosition();
return true;
}
};
/////////////////////////////////////////////////////////////////////////////////////////
// Categories options page
class CGategoriesOptions : public COptionsBaseDialog
{
SmileyCategoryListType tmpsmcat;
SmileyPackType smPack;
bool BrowseForSmileyPacks(int item)
{
wchar_t filter[512], filename[MAX_PATH] = L"";
mir_snwprintf(filter, L"%s (*.msl;*.asl;*.xep)%c*.msl;*.asl;*.xep%c%s (*.*)%c*.*%c", TranslateT("Smiley packs"), 0, 0, TranslateT("All files"), 0, 0);
CMStringW inidir;
SmileyCategoryType *smc = tmpsmcat.GetSmileyCategory(item);
if (smc->GetFilename().IsEmpty())
inidir = g_plugin.wszDefaultPath;
else {
inidir = VARSW(smc->GetFilename());
inidir.Truncate(inidir.ReverseFind('\\'));
}
OPENFILENAME ofn = {};
ofn.lpstrFile = filename;
ofn.nMaxFile = _countof(filename);
ofn.lpstrInitialDir = inidir.c_str();
ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400;
ofn.hwndOwner = m_hwnd;
ofn.lpstrFilter = filter;
ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_READONLY | OFN_EXPLORER | OFN_LONGNAMES | OFN_NOCHANGEDIR;
ofn.lpstrDefExt = L"msl";
if (!GetOpenFileNameW(&ofn))
return false;
wchar_t szOutPath[MAX_PATH];
PathToRelativeW(filename, szOutPath, g_plugin.wszDefaultPath);
smc->SetFilename(szOutPath);
UpdateControls();
return true;
}
long GetSelProto(HTREEITEM hItem = nullptr)
{
TVITEMEX tvi = {};
tvi.mask = TVIF_PARAM;
tvi.hItem = hItem == nullptr ? categories.GetSelection() : hItem;
categories.GetItem(&tvi);
return (long)tvi.lParam;
}
void PopulateSmPackList(void)
{
categories.SelectItem(nullptr);
categories.DeleteAllItems();
UpdateVisibleSmPackList();
TVINSERTSTRUCT tvi = {};
tvi.hParent = TVI_ROOT;
tvi.hInsertAfter = TVI_LAST;
tvi.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_STATE | TVIF_SELECTEDIMAGE | TVIF_PARAM;
tvi.item.stateMask = TVIS_STATEIMAGEMASK | TVIS_SELECTED;
auto &smc = tmpsmcat.GetSmileyCategoryList();
for (int i = 0; i < smc.getCount(); i++) {
if (smc[i].IsVisible()) {
tvi.item.pszText = (wchar_t *)smc[i].GetDisplayName().c_str();
if (!smc[i].IsProto()) {
tvi.item.iImage = 0;
tvi.item.iSelectedImage = 0;
}
else {
tvi.item.iImage = i;
tvi.item.iSelectedImage = i;
}
tvi.item.lParam = i;
tvi.item.state = INDEXTOSTATEIMAGEMASK(smPack.LoadSmileyFile(smc[i].GetFilename(), smc[i].GetDisplayName(), true, true) ? 2 : 1);
categories.InsertItem(&tvi);
smPack.Clear();
}
}
categories.SelectItem(categories.GetRoot());
}
void UpdateControls(bool force = false)
{
const SmileyCategoryType *smc = tmpsmcat.GetSmileyCategory(GetSelProto());
if (smc == nullptr)
return;
const CMStringW &smf = smc->GetFilename();
SetDlgItemText(m_hwnd, IDC_FILENAME, smf);
if (smPack.GetFilename() != smf || force)
smPack.LoadSmileyFile(smf, smPack.GetName(), false, true);
categories.SetCheckState(categories.GetSelection(), smPack.SmileyCount() != 0);
SetDlgItemText(m_hwnd, IDC_LIBAUTHOR, smPack.GetAuthor().c_str());
SetDlgItemText(m_hwnd, IDC_LIBVERSION, smPack.GetVersion().c_str());
SetDlgItemText(m_hwnd, IDC_LIBNAME, TranslateW(smPack.GetName().c_str()));
}
void UpdateVisibleSmPackList()
{
bool useOne = !chkStdPack.GetState();
bool usePhysProto = chkUsePhys.GetState();
auto &smc = tmpsmcat.GetSmileyCategoryList();
for (auto &it : smc) {
bool visiblecat = usePhysProto ? !it->IsAcc() : !it->IsPhysProto();
bool visible = useOne ? !it->IsProto() : visiblecat;
if (!visible && it->IsAcc() && !useOne) {
CMStringW PhysProtoName = L"AllProto";
CMStringW ProtoName = it->GetName();
DBVARIANT dbv;
if (db_get_ws(0, _T2A(ProtoName.GetBuffer()), "AM_BaseProto", &dbv) == 0) {
ProtoName = dbv.pwszVal;
db_free(&dbv);
}
else ProtoName.Empty();
if (!ProtoName.IsEmpty()) {
PhysProtoName += ProtoName;
SmileyCategoryType *scm = tmpsmcat.GetSmileyCategory(PhysProtoName);
if (scm == nullptr)
visible = false;
else if (scm->GetFilename().IsEmpty())
visible = true;
}
}
it->SetVisible(visible);
}
}
CCtrlEdit edtFilename;
CCtrlCheck chkStdPack, chkUsePhys;
CCtrlButton btnBrowse, btnPreview;
CCtrlTreeView categories;
public:
CGategoriesOptions() :
COptionsBaseDialog(IDD_OPT_CATEGORIES),
btnBrowse(this, IDC_BROWSE),
btnPreview(this, IDC_SMLOPTBUTTON),
chkStdPack(this, IDC_USESTDPACK),
chkUsePhys(this, IDC_USEPHYSPROTO),
categories(this, IDC_CATEGORYLIST),
edtFilename(this, IDC_FILENAME)
{
btnBrowse.OnClick = Callback(this, &CGategoriesOptions::onClick_Browse);
btnPreview.OnClick = Callback(this, &CGategoriesOptions::onClick_Preview);
chkStdPack.OnChange = Callback(this, &CGategoriesOptions::onChange_StdPack);
chkUsePhys.OnChange = Callback(this, &CGategoriesOptions::onChange_UsePhys);
edtFilename.OnChange = Callback(this, &CGategoriesOptions::onChange_Filename);
categories.OnSelChanged = Callback(this, &CGategoriesOptions::onSelectChange_Tree);
categories.OnItemChanged = Callback(this, &CGategoriesOptions::onItemChange_Tree);
}
bool OnInitDialog() override
{
chkStdPack.SetState(!opt.UseOneForAll);
chkUsePhys.SetState(opt.UsePhysProto);
chkUsePhys.Enable(!opt.UseOneForAll);
// Create and populate image list
HIMAGELIST hImList = ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),
ILC_MASK | ILC_COLOR32, g_SmileyCategories.NumberOfSmileyCategories(), 0);
tmpsmcat = g_SmileyCategories;
for (auto &it : g_SmileyCategories.GetSmileyCategoryList()) {
HICON hIcon = nullptr;
if (it->IsProto()) {
hIcon = (HICON)CallProtoService(_T2A(it->GetName().c_str()), PS_LOADICON, PLI_PROTOCOL | PLIF_SMALL, 0);
if (hIcon == nullptr || (INT_PTR)hIcon == CALLSERVICE_NOTFOUND)
hIcon = (HICON)CallProtoService(_T2A(it->GetName().c_str()), PS_LOADICON, PLI_PROTOCOL, 0);
}
if (hIcon == nullptr || hIcon == (HICON)CALLSERVICE_NOTFOUND)
hIcon = GetDefaultIcon();
ImageList_AddIcon(hImList, hIcon);
DestroyIcon(hIcon);
}
categories.SetImageList(hImList, TVSIL_NORMAL);
PopulateSmPackList();
return true;
}
bool OnApply() override
{
opt.UseOneForAll = !chkStdPack.GetState();
opt.UsePhysProto = chkUsePhys.GetState();
// Cleanup database
CMStringW empty;
for (auto &it : g_SmileyCategories.GetSmileyCategoryList())
if (tmpsmcat.GetSmileyCategory(it->GetName()) == nullptr)
opt.WritePackFileName(empty, it->GetName());
g_SmileyCategories = tmpsmcat;
g_SmileyCategories.SaveSettings();
g_SmileyCategories.ClearAndLoadAll();
smPack.LoadSmileyFile(tmpsmcat.GetSmileyCategory(GetSelProto())->GetFilename(), tmpsmcat.GetSmileyCategory(GetSelProto())->GetDisplayName(), false, true);
return true;
}
void OnDestroy() override
{
HIMAGELIST hImList = categories.GetImageList(TVSIL_NORMAL);
ImageList_Destroy(hImList);
}
INT_PTR DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam) override
{
if (uMsg == WM_COMMAND && LOWORD(wParam) == IDC_FILENAME && HIWORD(wParam) == EN_KILLFOCUS)
onChange_Filename(0);
return COptionsBaseDialog::DlgProc(uMsg, wParam, lParam);
}
void onClick_Browse(CCtrlButton*)
{
if (BrowseForSmileyPacks(GetSelProto())) {
UpdateControls(true);
NotifyChange();
}
}
void onClick_Preview(CCtrlButton*)
{
RECT rect;
GetWindowRect(GetDlgItem(m_hwnd, IDC_SMLOPTBUTTON), &rect);
SmileyToolWindowParam *stwp = new SmileyToolWindowParam;
stwp->pSmileyPack = &smPack;
stwp->hWndParent = m_hwnd;
stwp->hWndTarget = nullptr;
stwp->targetMessage = 0;
stwp->targetWParam = 0;
stwp->xPosition = rect.left;
stwp->yPosition = rect.bottom + 4;
stwp->direction = 1;
mir_forkThread(SmileyToolThread, stwp);
}
void onChange_Filename(CCtrlEdit*)
{
wchar_t str[MAX_PATH];
GetDlgItemTextW(m_hwnd, IDC_FILENAME, str, _countof(str));
SmileyCategoryType *smc = tmpsmcat.GetSmileyCategory(GetSelProto());
if (smc->GetFilename() != str) {
smc->SetFilename(str);
UpdateControls();
}
}
void onChange_StdPack(CCtrlCheck*)
{
chkUsePhys.Enable(chkStdPack.GetState());
PopulateSmPackList();
NotifyChange();
}
void onChange_UsePhys(CCtrlCheck*)
{
PopulateSmPackList();
NotifyChange();
}
void onItemChange_Tree(CCtrlTreeView::TEventInfo *ev)
{
HTREEITEM hItem = ev->hItem;
if (!categories.GetCheckState(hItem)) {
if (!BrowseForSmileyPacks(GetSelProto(hItem)))
categories.SetCheckState(hItem, TRUE);
}
else tmpsmcat.GetSmileyCategory(GetSelProto(hItem))->ClearFilename();
if (hItem == categories.GetSelection())
UpdateControls();
else
categories.SelectItem(hItem);
NotifyChange();
PopulateSmPackList();
}
void onSelectChange_Tree(CCtrlTreeView::TEventInfo *ev)
{
if (ev->nmtv->itemNew.state & TVIS_SELECTED)
UpdateControls();
}
};
/////////////////////////////////////////////////////////////////////////////////////////
// Module entry point
int SmileysOptionsInitialize(WPARAM addInfo, LPARAM)
{
OPTIONSDIALOGPAGE odp = {};
odp.flags = ODPF_BOLDGROUPS;
odp.position = 910000000;
odp.szTitle.a = LPGEN("Smileys");
odp.szGroup.a = LPGEN("Customize");
odp.szTab.a = LPGEN("General");
odp.pDialog = new CGeneralOptions();
g_plugin.addOptions(addInfo, &odp);
odp.szTab.a = LPGEN("Categories");
odp.pDialog = new CGategoriesOptions();
g_plugin.addOptions(addInfo, &odp);
return 0;
}