/* ======================================================================== Custom Status List __________________ Custom Status List plugin for Miranda-IM (www.miranda-im.org) Follower of Custom Status History List by HANAX Copyright © 2006-2008 HANAX Copyright © 2007-2009 jarvis Occasionally rewritten in 2012 by George Hazan 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 "stdafx.h" #include "strpos.h" CLIST_INTERFACE *pcli; int hLangpack; HINSTANCE g_hInst; static LIST arWindows(3, HandleKeySortT); PLUGININFOEX pluginInfoEx = { sizeof(PLUGININFOEX), __PLUGIN_NAME, PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), __DESCRIPTION, __AUTHOR, __AUTHOREMAIL, __COPYRIGHT, __AUTHORWEB, UNICODE_AWARE, // {C8CC7414-6507-4AF6-925A-83C1D2F7BE8C} { 0xc8cc7414, 0x6507, 0x4af6, { 0x92, 0x5a, 0x83, 0xc1, 0xd2, 0xf7, 0xbe, 0x8c } } }; // ====[ MAIN ]=============================================================== BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD, LPVOID) { g_hInst = hinstDLL; return TRUE; } // ====[ PLUGIN INFO ]======================================================== extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD) { return &pluginInfoEx; } // ====[ LOADER ]============================================================= static int OnDbChanged(WPARAM hContact, LPARAM lparam) { DBCONTACTWRITESETTING *cws = (DBCONTACTWRITESETTING*)lparam; // if user changes his UIN or JID on any account if (hContact == NULL) { INT_PTR szUniqueID = CallProtoService(cws->szModule, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0); if (szUniqueID != CALLSERVICE_NOTFOUND && !mir_strcmp(cws->szSetting, (char*)szUniqueID)) pcli->pfnReloadProtoMenus(); } return 0; } static int OnInitOptions(WPARAM wparam, LPARAM) { OPTIONSDIALOGPAGE odp = { 0 }; odp.position = 955000000; odp.hInstance = g_hInst; odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS); odp.pfnDlgProc = CSOptionsProc; odp.pwszGroup = L"Status"; odp.pwszTitle = MODULENAME; odp.flags = ODPF_BOLDGROUPS | ODPF_UNICODE; Options_AddPage(wparam, &odp); return 0; } static int OnCreateMenuItems(WPARAM, LPARAM) { int protoCount; PROTOACCOUNT** pdesc; Proto_EnumAccounts(&protoCount, &pdesc); for (int i = 0; i < protoCount; i++) if (ProtoServiceExists(pdesc[i]->szModuleName, PS_SETCUSTOMSTATUSEX)) addProtoStatusMenuItem(pdesc[i]->szModuleName); return 0; } static int OnPreshutdown(WPARAM, LPARAM) { for (int i = 0; i < arWindows.getCount(); i++) DestroyWindow(arWindows[i]->m_handle); return 0; } extern "C" __declspec(dllexport) int Load() { mir_getLP(&pluginInfoEx); pcli = Clist_GetInterface(); // support for ComboBoxEx INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(icc); icc.dwICC = ICC_USEREX_CLASSES; InitCommonControlsEx(&icc); // init icons wchar_t tszFile[MAX_PATH]; GetModuleFileName(g_hInst, tszFile, MAX_PATH); SKINICONDESC sid = { 0 }; sid.defaultFile.w = tszFile; sid.flags = SIDF_ALL_UNICODE; sid.section.w = MODULENAME; for (int i = 0; i < _countof(forms); i++) { char szSettingName[64]; mir_snprintf(szSettingName, "%s_%s", MODNAME, forms[i].pszIconIcoLib); sid.pszName = szSettingName; sid.description.w = forms[i].ptszDescr; sid.iDefaultIndex = -forms[i].iconNoIcoLib; forms[i].hIcoLibItem = IcoLib_AddIcon(&sid); } HookEvent(ME_OPT_INITIALISE, OnInitOptions); HookEvent(ME_DB_CONTACT_SETTINGCHANGED, OnDbChanged); HookEvent(ME_CLIST_PREBUILDSTATUSMENU, OnCreateMenuItems); HookEvent(ME_SYSTEM_PRESHUTDOWN, OnPreshutdown); // if we load a plugin dynamically, it will work, otherwise fail OnCreateMenuItems(0, 0); return 0; } // ====[ UNLOADER ]=========================================================== extern "C" __declspec(dllexport) int Unload() { return 0; } // ====[ FUN ]================================================================ void RegisterHotkeys(char buf[200], wchar_t* accName, int Number) { HOTKEYDESC hotkey = { sizeof(hotkey) }; hotkey.dwFlags = HKD_UNICODE; hotkey.pszName = buf; hotkey.pwszDescription = accName; hotkey.pwszSection = LPGENW("Custom Status List"); hotkey.pszService = buf; hotkey.DefHotKey = HOTKEYCODE(HOTKEYF_CONTROL | HOTKEYF_SHIFT, '0' + Number); Hotkey_Register(&hotkey); } void SetStatus(WORD code, StatusItem* item, char *szAccName) { if (code == IDCLOSE) return; PROTOACCOUNT* pdescr = Proto_GetAccount(szAccName); if (pdescr == NULL) return; if (!ProtoServiceExists(szAccName, PS_SETCUSTOMSTATUSEX)) return; int statusToSet; CUSTOM_STATUS ics = { sizeof(CUSTOM_STATUS) }; ics.flags = CSSF_MASK_STATUS | CSSF_MASK_NAME | CSSF_MASK_MESSAGE | CSSF_UNICODE; if (code == IDC_CANCEL) { statusToSet = 0; ics.ptszName = L""; ics.ptszMessage = L""; } else if (code == IDOK && item != NULL) { statusToSet = item->m_iIcon + 1; ics.ptszName = variables_parsedup(item->m_tszTitle, NULL, NULL); ics.ptszMessage = variables_parsedup(item->m_tszMessage, NULL, NULL); } else return; ics.status = &statusToSet; CallProtoService(szAccName, PS_SETCUSTOMSTATUSEX, 0, (LPARAM)&ics); } INT_PTR showList(WPARAM, LPARAM, LPARAM param) { char* szProto = (char*)param; for (int i = 0; i < arWindows.getCount(); i++) { CSWindow *p = arWindows[i]; if (!mir_strcmp(szProto, p->m_protoName)) { ShowWindow(p->m_handle, SW_SHOW); SetForegroundWindow(p->m_handle); return 1; } } CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_CSLIST), NULL, CSWindowProc, (LPARAM)new CSWindow(szProto)); return 0; } void addProtoStatusMenuItem(char *protoName) { PROTOACCOUNT *pdescr = Proto_GetAccount(protoName); if (pdescr == NULL || pdescr->ppro == NULL) return; HGENMENU hRoot = Menu_GetProtocolRoot(pdescr->ppro); if (hRoot == NULL) return; char buf[200]; mir_snprintf(buf, "CSList/ShowList/%s", protoName); if (!ServiceExists(buf)) CreateServiceFunctionParam(buf, showList, (LPARAM)protoName); CMenuItem mi; mi.flags = CMIF_UNICODE; mi.hIcolibItem = forms[0].hIcoLibItem; mi.name.w = MODULENAME; mi.position = 2000040000; mi.pszService = buf; mi.root = hRoot; Menu_AddStatusMenuItem(&mi); RegisterHotkeys(buf, pdescr->tszAccountName, pdescr->iOrder); } /////////////////////////////////////////////////////////////////////////////// void importCustomStatuses(CSWindow* csw, int result) { DBVARIANT dbv; char bufTitle[32], bufMessage[32], *protoName = csw->m_protoName; for (int i = 0; i < csw->m_statusCount; i++) { StatusItem* si = new StatusItem(); si->m_iIcon = i - 1; mir_snprintf(bufTitle, "XStatus%dName", i); if (!db_get_ws(NULL, protoName, bufTitle, &dbv)) { mir_wstrcpy(si->m_tszTitle, dbv.ptszVal); db_free(&dbv); } else si->m_tszTitle[0] = 0; mir_snprintf(bufMessage, "XStatus%dMsg", i); if (!db_get_ws(NULL, protoName, bufMessage, &dbv)) { mir_wstrcpy(si->m_tszMessage, dbv.ptszVal); db_free(&dbv); } else si->m_tszMessage[0] = 0; if (si->m_tszTitle[0] || si->m_tszMessage[0]) { csw->m_itemslist->m_list->add(si); csw->m_bSomethingChanged = TRUE; } else delete si; if (result == IDYES) { db_unset(NULL, protoName, bufTitle); db_unset(NULL, protoName, bufMessage); } } csw->m_listview->reinitItems(csw->m_itemslist->m_list->getListHead()); } /////////////////////////////////////////////////////////////////////////////// // CSWindow class CSWindow::CSWindow(char *protoName) { m_protoName = protoName; m_handle = NULL; m_bExtraIcons = getByte("AllowExtraIcons", DEFAULT_ALLOW_EXTRA_ICONS); m_itemslist = new CSItemsList(m_protoName); m_listview = NULL; m_addModifyDlg = NULL; m_bSomethingChanged = FALSE; m_filterString = NULL; } void __fastcall SAFE_FREE(void** p) { if (*p) { free(*p); *p = NULL; } } CSWindow::~CSWindow() { delete m_itemslist; SAFE_FREE((void**)&m_filterString); } void CSWindow::initIcons() { PROTOACCOUNT *pdescr = Proto_GetAccount(m_protoName); if (pdescr == NULL) return; char *szUniqueID = (char*)CallProtoService(m_protoName, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0); if (szUniqueID == NULL) return; DBVARIANT dbv; if (db_get(NULL, pdescr->szModuleName, szUniqueID, &dbv)) return; db_free(&dbv); WPARAM iNumStatuses = 0; CUSTOM_STATUS cs = { sizeof(cs) }; cs.flags = CSSF_STATUSES_COUNT; cs.wParam = &iNumStatuses; if (CallProtoService(pdescr->szModuleName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cs) != 0) return; m_statusCount = (int)iNumStatuses; if (NULL == (m_icons = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, m_statusCount, 0))) return; for (int i = 1; i <= m_statusCount; i++) { HICON hIcon = (HICON)CallProtoService(pdescr->szModuleName, PS_GETCUSTOMSTATUSICON, i, 0); if (hIcon) { ImageList_AddIcon(m_icons, hIcon); DestroyIcon(hIcon); } } } void CSWindow::deinitIcons() { ImageList_Destroy(m_icons); } void CSWindow::initButtons() { for (int i = 0; i < _countof(forms); i++) { if (forms[i].idc < 0) continue; SendDlgItemMessage(m_handle, forms[i].idc, BM_SETIMAGE, IMAGE_ICON, (LPARAM)IcoLib_GetIconByHandle(forms[i].hIcoLibItem)); SendDlgItemMessage(m_handle, forms[i].idc, BUTTONSETASFLATBTN, TRUE, 0); //maybe set as BUTTONSETDEFAULT? SendDlgItemMessage(m_handle, forms[i].idc, BUTTONADDTOOLTIP, (WPARAM)TranslateW(forms[i].ptszTitle), BATF_UNICODE); } } void CSWindow::loadWindowPosition() { if (getByte("RememberWindowPosition", DEFAULT_REMEMBER_WINDOW_POSITION)) Utils_RestoreWindowPosition(m_handle, NULL, MODNAME, "Position"); } void CSWindow::toggleEmptyListMessage() { HWND hwnd = GetDlgItem(m_handle, IDC_NO_ITEMS); ShowWindow(hwnd, (ListView_GetItemCount(m_listview->m_handle) > 0) ? FALSE : TRUE); SetForegroundWindow(hwnd); hwnd = GetDlgItem(m_handle, IDC_ADD_SAMPLE); ShowWindow(hwnd, (ListView_GetItemCount(m_listview->m_handle) > 0) ? FALSE : TRUE); SetForegroundWindow(hwnd); } BOOL CSWindow::itemPassedFilter(ListItem< StatusItem >* li) { wchar_t filter[MAX_PATH]; GetDlgItemText(m_handle, IDC_FILTER_FIELD, filter, _countof(filter)); if (mir_wstrlen(filter)) { wchar_t title[EXTRASTATUS_TITLE_LIMIT], message[EXTRASTATUS_MESSAGE_LIMIT]; mir_wstrcpy(title, li->m_item->m_tszTitle); mir_wstrcpy(message, li->m_item->m_tszMessage); if (strpos(wcslwr(title), wcslwr(filter)) == -1) if (strpos(wcslwr(message), wcslwr(filter)) == -1) return FALSE; } return TRUE; } void CSWindow::toggleFilter() { HWND hFilter = GetDlgItem(m_handle, IDC_FILTER_FIELD); BOOL isEnabled = !IsWindowEnabled(hFilter) ? TRUE : FALSE; // ! = + isEnabled = !isEnabled in one EnableWindow(hFilter, isEnabled); ShowWindow(hFilter, isEnabled); CheckDlgButton(m_handle, IDC_FILTER, isEnabled ? BST_CHECKED : BST_UNCHECKED); SetForegroundWindow(hFilter); if (isEnabled) SetFocus(hFilter); else { wchar_t filterText[255]; GetDlgItemText(m_handle, IDC_FILTER_FIELD, filterText, _countof(filterText)); if (filterText[0] != 0) SetDlgItemText(m_handle, IDC_FILTER_FIELD, TEXT("")); } } BOOL CSWindow::toggleButtons() { int selection = ListView_GetSelectedItemMacro(m_listview->m_handle); BOOL validSelection = (selection >= 0 && (unsigned int)selection < m_itemslist->m_list->getCount()) ? TRUE : FALSE; BOOL filterEnabled = IsWindowVisible(GetDlgItem(m_handle, IDC_FILTER_FIELD)); BOOL somethingChanged = m_bSomethingChanged; EnableWindow(GetDlgItem(m_handle, IDC_ADD), !filterEnabled); EnableWindow(GetDlgItem(m_handle, IDC_MODIFY), validSelection && !filterEnabled); EnableWindow(GetDlgItem(m_handle, IDC_REMOVE), validSelection && !filterEnabled); EnableWindow(GetDlgItem(m_handle, IDC_FAVOURITE), validSelection && !filterEnabled); EnableWindow(GetDlgItem(m_handle, IDC_UNDO), somethingChanged && !filterEnabled); EnableWindow(GetDlgItem(m_handle, IDC_IMPORT), !filterEnabled); EnableWindow(GetDlgItem(m_handle, IDOK), validSelection); return validSelection; } /////////////////////////////////////////////////////////////////////////////// // CSAMWindow class - add form window CSAMWindow::CSAMWindow(WORD action, CSWindow* parent) { m_action = action; m_parent = parent; m_bChanged = FALSE; m_hCombo = m_hMessage = NULL; if (m_action == IDC_ADD) m_item = new StatusItem(); else m_item = new StatusItem(*m_parent->m_itemslist->m_list->get(m_parent->m_listview->getPositionInList())); } CSAMWindow::~CSAMWindow() { if (!m_bChanged) delete m_item; } void CSAMWindow::exec() { DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_ADDMODIFY), NULL, CSAMWindowProc, (LPARAM)this); } void CSAMWindow::setCombo() { PROTOACCOUNT *pdescr = Proto_GetAccount(m_parent->m_protoName); if (pdescr == NULL) return; char *szUniqueID = (char*)CallProtoService(pdescr->szModuleName, PS_GETCAPS, PFLAG_UNIQUEIDSETTING, 0); if (szUniqueID == NULL) return; DBVARIANT dbv; if (db_get(NULL, pdescr->szModuleName, szUniqueID, &dbv)) return; db_free(&dbv); WPARAM iStatus; wchar_t tszName[100]; CUSTOM_STATUS cs = { sizeof(cs) }; cs.flags = CSSF_MASK_NAME | CSSF_DEFAULT_NAME | CSSF_UNICODE; cs.ptszName = tszName; cs.wParam = &iStatus; SendMessage(m_hCombo, CBEM_SETIMAGELIST, 0, (LPARAM)m_parent->m_icons); for (int i = 1; i <= m_parent->m_statusCount; i++) { iStatus = i; if (CallProtoService(pdescr->szModuleName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cs) != 0) continue; COMBOBOXEXITEM cbi = { 0 }; cbi.mask = CBEIF_TEXT | CBEIF_IMAGE | CBEIF_SELECTEDIMAGE; cbi.iItem = -1; cbi.iImage = cbi.iSelectedImage = i - 1; cbi.pszText = TranslateW(tszName); SendMessage(m_hCombo, CBEM_INSERTITEM, 0, (LPARAM)&cbi); } SendMessage(m_hCombo, CB_SETCURSEL, 0, 0); // first 0 sets selection to top } void CSAMWindow::fillDialog() { if (m_action == IDC_ADD) { SetWindowText(m_handle, TranslateT("Add new item")); SetDlgItemText(m_handle, IDOK, TranslateT("Add")); } else { SetWindowText(m_handle, TranslateT("Modify item")); SetDlgItemText(m_handle, IDOK, TranslateT("Modify")); } SendMessage(m_hCombo, CB_SETCURSEL, m_item->m_iIcon, 0); SetDlgItemText(m_handle, IDC_MESSAGE, m_item->m_tszMessage); } void CSAMWindow::checkFieldLimit(WORD action, WORD item) { BOOL type = (item == IDC_MESSAGE) ? TRUE : FALSE; unsigned int limit = type ? EXTRASTATUS_MESSAGE_LIMIT : EXTRASTATUS_TITLE_LIMIT; if (action == EN_CHANGE) { wchar_t* ptszInputText = (wchar_t*)mir_alloc((limit + 8) * sizeof(wchar_t)); GetDlgItemText(m_handle, item, ptszInputText, limit + 8); if (mir_wstrlen(ptszInputText) > limit) { wchar_t tszPopupTip[MAX_PATH]; EDITBALLOONTIP ebt = { 0 }; ebt.cbStruct = sizeof(ebt); ebt.pszTitle = TranslateT("Warning"); mir_snwprintf(tszPopupTip, TranslateT("This field doesn't accept string longer than %d characters. The string will be truncated."), limit); ebt.pszText = tszPopupTip; ebt.ttiIcon = TTI_WARNING; SendDlgItemMessage(m_handle, item, EM_SHOWBALLOONTIP, 0, (LPARAM)&ebt); wchar_t* ptszOutputText = (wchar_t*)mir_alloc((limit + 1) * sizeof(wchar_t)); GetDlgItemText(m_handle, item, ptszOutputText, limit + 1); SetDlgItemText(m_handle, item, ptszOutputText); mir_free(ptszOutputText); } mir_free(ptszInputText); } } void CSAMWindow::checkItemValidity() { COMBOBOXEXITEM cbi = { 0 }; cbi.mask = CBEIF_IMAGE; cbi.iItem = SendDlgItemMessage(m_handle, IDC_COMBO, CB_GETCURSEL, 0, 0); SendDlgItemMessage(m_handle, IDC_COMBO, CBEM_GETITEM, 0, (LPARAM)&cbi); if (m_item->m_iIcon != cbi.iImage) m_item->m_iIcon = cbi.iImage, m_bChanged = TRUE; wchar_t tszInputMessage[EXTRASTATUS_MESSAGE_LIMIT]; GetDlgItemText(m_handle, IDC_MESSAGE, tszInputMessage, _countof(tszInputMessage)); PROTOACCOUNT *pdescr = Proto_GetAccount(m_parent->m_protoName); if (pdescr == NULL) return; WPARAM i = SendMessage(m_hCombo, CB_GETCURSEL, 0, 0) + 1; wchar_t tszTitle[100]; CUSTOM_STATUS cs = { sizeof(cs) }; cs.flags = CSSF_MASK_NAME | CSSF_DEFAULT_NAME | CSSF_UNICODE; cs.ptszName = tszTitle; cs.wParam = &i; if (CallProtoService(pdescr->szModuleName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cs) == 0) mir_wstrncpy(m_item->m_tszTitle, TranslateW(tszTitle), _countof(m_item->m_tszTitle)); if (mir_wstrcmp(m_item->m_tszMessage, tszInputMessage)) mir_wstrcpy(m_item->m_tszMessage, tszInputMessage), m_bChanged = true; } CSListView::CSListView(HWND hwnd, CSWindow* parent) { m_handle = hwnd; m_parent = parent; LVGROUP lg = { 0 }; lg.cbSize = sizeof(LVGROUP); lg.mask = LVGF_HEADER | LVGF_GROUPID; lg.pszHeader = TranslateT("Favorites"); lg.iGroupId = 0; ListView_InsertGroup(m_handle, -1, &lg); lg.pszHeader = TranslateT("Regular statuses"); lg.iGroupId = 1; ListView_InsertGroup(m_handle, -1, &lg); ListView_EnableGroupView(m_handle, TRUE); LVCOLUMN lvc = { 0 }; lvc.mask = LVCF_TEXT | LVCF_FMT | LVCF_WIDTH | LVCF_SUBITEM; lvc.fmt = LVCFMT_LEFT; lvc.cx = 0x00; lvc.pszText = TEXT(""); lvc.cx = 0x16; SendMessage(m_handle, LVM_INSERTCOLUMN, 0, (LPARAM)&lvc); lvc.pszText = TranslateT("Title"); lvc.cx = 0x64; SendMessage(m_handle, LVM_INSERTCOLUMN, 1, (LPARAM)&lvc); lvc.pszText = TranslateT("Message"); lvc.cx = 0xa8; SendMessage(m_handle, LVM_INSERTCOLUMN, 2, (LPARAM)&lvc); ListView_SetExtendedListViewStyleEx(m_handle, 0, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP); ListView_SetImageList(m_handle, m_parent->m_icons, LVSIL_SMALL); } void CSListView::addItem(StatusItem* item, int itemNumber) { LVITEM lvi = { 0 }; lvi.mask = LVIF_IMAGE | LVIF_GROUPID | LVIF_PARAM; lvi.iItem = ListView_GetItemCount(m_handle); lvi.lParam = itemNumber; lvi.iGroupId = item->m_bFavourite ? 0 : 1; // first column lvi.iSubItem = 0; lvi.iImage = item->m_iIcon; // use selected xicon SendMessage(m_handle, LVM_INSERTITEM, 0, (LPARAM)&lvi); // second column lvi.mask = LVIF_TEXT; lvi.iSubItem = 1; lvi.pszText = item->m_tszTitle; SendMessage(m_handle, LVM_SETITEM, 0, (LPARAM)&lvi); // third column lvi.iSubItem = 2; lvi.pszText = item->m_tszMessage; SendMessage(m_handle, LVM_SETITEM, 0, (LPARAM)&lvi); } void CSListView::initItems(ListItem< StatusItem >* items) { ListItem< StatusItem >* help = items; for (int i = 0; help != NULL; help = help->m_next, i++) if (m_parent->itemPassedFilter(help)) addItem(help->m_item, i); } void CSListView::reinitItems(ListItem< StatusItem >* items) { EnableWindow(m_handle, FALSE); removeItems(); initItems(items); EnableWindow(m_handle, TRUE); } void CSListView::removeItems() { ListView_DeleteAllItems(m_handle); } int CSListView::getPositionInList() { LVITEM lvi = { 0 }; lvi.iItem = ListView_GetSelectedItemMacro(m_parent->m_listview->m_handle); lvi.iSubItem = 0; lvi.mask = LVIF_PARAM; ListView_GetItem(m_parent->m_listview->m_handle, &lvi); return lvi.lParam; } void CSListView::setFullFocusedSelection(int selection) { ListView_SetItemState(m_handle, -1, 0, LVIS_SELECTED); ListView_EnsureVisible(m_handle, selection, FALSE); ListView_SetItemState(m_handle, selection, LVIS_SELECTED, LVIS_SELECTED); ListView_SetItemState(m_handle, selection, LVIS_FOCUSED, LVIS_FOCUSED); SetFocus(m_handle); } // ====[ LIST MANAGEMENT ]==================================================== CSItemsList::CSItemsList(char *protoName) { m_list = new List< StatusItem >(compareItems); loadItems(protoName); } CSItemsList::~CSItemsList() { delete m_list; } int CSItemsList::compareItems(const StatusItem* p1, const StatusItem* p2) { int favRes = 0, icoRes = 0, ttlRes = 0, msgRes = 0; if (p1->m_bFavourite < p2->m_bFavourite) favRes = 1; else if (p1->m_bFavourite > p2->m_bFavourite) favRes = -1; int result; if (p1->m_iIcon > p2->m_iIcon) icoRes = 1; else if (p1->m_iIcon < p2->m_iIcon) icoRes = -1; result = mir_wstrcmp(p1->m_tszTitle, p2->m_tszTitle); ttlRes = result; result = mir_wstrcmp(p1->m_tszMessage, p2->m_tszMessage); msgRes = result; if (!icoRes && !ttlRes && !msgRes) return 0; if (favRes != 0) return favRes; if (icoRes != 0) return icoRes; if (ttlRes != 0) return ttlRes; if (msgRes != 0) return msgRes; return 0; } void CSItemsList::loadItems(char *protoName) { char dbSetting[32]; mir_snprintf(dbSetting, "%s_ItemsCount", protoName); unsigned int itemsCount = getWord(dbSetting, DEFAULT_ITEMS_COUNT); for (unsigned int i = 1; i <= itemsCount; i++) { StatusItem* item = new StatusItem(); DBVARIANT dbv; mir_snprintf(dbSetting, "%s_Item%dIcon", protoName, i); item->m_iIcon = getByte(dbSetting, DEFAULT_ITEM_ICON); mir_snprintf(dbSetting, "%s_Item%dTitle", protoName, i); if (!getWString(dbSetting, &dbv)) { mir_wstrcpy(item->m_tszTitle, dbv.ptszVal); db_free(&dbv); } else item->m_tszTitle[0] = 0; mir_snprintf(dbSetting, "%s_Item%dMessage", protoName, i); if (!getWString(dbSetting, &dbv)) { mir_wstrcpy(item->m_tszMessage, dbv.ptszVal); db_free(&dbv); } else item->m_tszMessage[0] = 0; mir_snprintf(dbSetting, "%s_Item%dFavourite", protoName, i); item->m_bFavourite = (BOOL)getByte(dbSetting, DEFAULT_ITEM_IS_FAVOURITE); m_list->add(item); } } void CSItemsList::saveItems(char *protoName) { unsigned int i; char dbSetting[32]; mir_snprintf(dbSetting, "%s_ItemsCount", protoName); unsigned int oldItemsCount = getWord(dbSetting, DEFAULT_ITEMS_COUNT); for (i = 1; i <= m_list->getCount(); i++) { StatusItem* item = m_list->get(i - 1); mir_snprintf(dbSetting, "%s_Item%dIcon", protoName, i); setByte(dbSetting, item->m_iIcon); mir_snprintf(dbSetting, "%s_Item%dTitle", protoName, i); setWString(dbSetting, item->m_tszTitle); mir_snprintf(dbSetting, "%s_Item%dMessage", protoName, i); setWString(dbSetting, item->m_tszMessage); mir_snprintf(dbSetting, "%s_Item%dFavourite", protoName, i); setByte(dbSetting, item->m_bFavourite); } mir_snprintf(dbSetting, "%s_ItemsCount", protoName); setWord(dbSetting, m_list->getCount()); for (; i <= oldItemsCount; i++) { mir_snprintf(dbSetting, "%s_Item%dIcon", protoName, i); deleteSetting(dbSetting); mir_snprintf(dbSetting, "%s_Item%dTitle", protoName, i); deleteSetting(dbSetting); mir_snprintf(dbSetting, "%s_Item%dMessage", protoName, i); deleteSetting(dbSetting); mir_snprintf(dbSetting, "%s_Item%dFavourite", protoName, i); deleteSetting(dbSetting); } } // ====[ PROCEDURES ]========================================================= INT_PTR CALLBACK CSWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { CSWindow* csw = (CSWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA); switch (message) { case WM_INITDIALOG: TranslateDialogDefault(hwnd); csw = (CSWindow*)lparam; arWindows.insert(csw); SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam); csw->m_handle = hwnd; csw->initIcons(); csw->initButtons(); csw->m_listview = new CSListView(GetDlgItem(hwnd, IDC_CSLIST), csw); csw->m_listview->initItems(csw->m_itemslist->m_list->getListHead()); csw->toggleButtons(); csw->toggleEmptyListMessage(); csw->loadWindowPosition(); SetWindowText(hwnd, MODULENAME); return TRUE; case WM_COMMAND: switch (LOWORD(wparam)) { case IDC_MODIFY: case IDC_ADD: csw->m_addModifyDlg = new CSAMWindow(LOWORD(wparam), csw); csw->m_addModifyDlg->exec(); if (csw->m_addModifyDlg->m_bChanged) { if (LOWORD(wparam) == IDC_MODIFY) csw->m_itemslist->m_list->remove(csw->m_listview->getPositionInList()); int selection = csw->m_itemslist->m_list->add(csw->m_addModifyDlg->m_item); csw->m_bSomethingChanged = TRUE; csw->m_listview->reinitItems(csw->m_itemslist->m_list->getListHead()); csw->m_listview->setFullFocusedSelection(selection); csw->toggleButtons(); csw->toggleEmptyListMessage(); } delete csw->m_addModifyDlg; break; case IDC_REMOVE: if (getByte("ConfirmDeletion", DEFAULT_PLUGIN_CONFIRM_ITEMS_DELETION)) if (MessageBox(hwnd, TranslateT("Do you really want to delete selected item?"), TranslateW(MODULENAME), MB_YESNO | MB_DEFBUTTON2 | MB_ICONQUESTION) == IDNO) break; csw->m_itemslist->m_list->remove(csw->m_listview->getPositionInList()); csw->m_bSomethingChanged = TRUE; csw->m_listview->reinitItems(csw->m_itemslist->m_list->getListHead()); csw->toggleButtons(); csw->toggleEmptyListMessage(); break; case IDC_FAVOURITE: { int selection = csw->m_listview->getPositionInList(); StatusItem* f = new StatusItem(*csw->m_itemslist->m_list->get(selection)); f->m_bFavourite = !f->m_bFavourite; csw->m_itemslist->m_list->remove(selection); selection = csw->m_itemslist->m_list->add(f); csw->m_bSomethingChanged = TRUE; csw->m_listview->reinitItems(csw->m_itemslist->m_list->getListHead()); csw->m_listview->setFullFocusedSelection(selection); csw->toggleButtons(); } break; case IDC_UNDO: csw->m_itemslist->m_list->destroy(); csw->m_itemslist->loadItems(csw->m_protoName); csw->m_bSomethingChanged = FALSE; csw->m_listview->reinitItems(csw->m_itemslist->m_list->getListHead()); csw->toggleButtons(); csw->toggleEmptyListMessage(); break; case IDC_IMPORT: { int result = getByte("DeleteAfterImport", DEFAULT_PLUGIN_DELETE_AFTER_IMPORT); if (result == TRUE) result = IDYES; else { result = MessageBox(hwnd, TranslateT("Do you want old database entries to be deleted after Import?"), TranslateW(MODULENAME), MB_YESNOCANCEL | MB_DEFBUTTON2 | MB_ICONQUESTION); if (result == IDCANCEL) break; } importCustomStatuses(csw, result); csw->m_bSomethingChanged = TRUE; csw->toggleButtons(); csw->toggleEmptyListMessage(); } break; case IDC_FILTER: csw->toggleFilter(); csw->toggleButtons(); break; case IDC_FILTER_FIELD: if (HIWORD(wparam) == EN_CHANGE) csw->m_listview->reinitItems(csw->m_itemslist->m_list->getListHead()); break; case IDCLOSE: // close and save, no custom status case IDCANCEL: // close and save, no custom status case IDC_CANCEL: // close and save, cancel custom status case IDOK: // close and save, set selected custom status if (LOWORD(wparam) == IDOK && csw->toggleButtons()) SetStatus(IDOK, csw->m_itemslist->m_list->get(csw->m_listview->getPositionInList()), csw->m_protoName); if (LOWORD(wparam) == IDC_CANCEL) SetStatus(IDC_CANCEL, NULL, csw->m_protoName); if (csw->m_bSomethingChanged) csw->m_itemslist->saveItems(csw->m_protoName); csw->saveWindowPosition(csw->m_handle); EndDialog(hwnd, FALSE); break; } return FALSE; case WM_NOTIFY: if (wparam == IDC_CSLIST) { NMHDR* pnmh = (NMHDR*)lparam; switch (pnmh->code) { case NM_DBLCLK: PostMessage(hwnd, WM_COMMAND, IDOK, 0L); break; case LVN_ITEMCHANGED: case NM_CLICK: csw->toggleButtons(); break; } } return FALSE; case WM_CTLCOLORSTATIC: SetTextColor((HDC)wparam, RGB(174, 174, 174)); if (((HWND)lparam == GetDlgItem(hwnd, IDC_NO_ITEMS)) || ((HWND)lparam == GetDlgItem(hwnd, IDC_ADD_SAMPLE))) return (INT_PTR)GetStockObject(WHITE_BRUSH); return NULL; case WM_DESTROY: SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); arWindows.remove(csw); delete csw->m_listview; csw->deinitIcons(); delete csw; break; } return FALSE; } INT_PTR CALLBACK CSAMWindowProc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) { CSAMWindow* csamw = (CSAMWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA); switch (message) { case WM_INITDIALOG: csamw = (CSAMWindow*)lparam; SetWindowLongPtr(hwnd, GWLP_USERDATA, lparam); csamw->m_handle = hwnd; EnableWindow(csamw->m_parent->m_handle, FALSE); csamw->m_hCombo = GetDlgItem(hwnd, IDC_COMBO); csamw->m_hMessage = GetDlgItem(hwnd, IDC_MESSAGE); csamw->setCombo(); csamw->fillDialog(); TranslateDialogDefault(hwnd); break; case WM_COMMAND: switch (LOWORD(wparam)) { case IDC_MESSAGE: csamw->checkFieldLimit(HIWORD(wparam), LOWORD(wparam)); break; case IDOK: csamw->checkItemValidity(); case IDCANCEL: EnableWindow(csamw->m_parent->m_handle, TRUE); EndDialog(hwnd, LOWORD(wparam)); break; } break; } return FALSE; } INT_PTR CALLBACK CSOptionsProc(HWND hwnd, UINT message, WPARAM, LPARAM lparam) { switch (message) { case WM_INITDIALOG: TranslateDialogDefault(hwnd); CheckDlgButton(hwnd, IDC_CONFIRM_DELETION, getByte("ConfirmDeletion", DEFAULT_PLUGIN_CONFIRM_ITEMS_DELETION) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hwnd, IDC_DELETE_AFTER_IMPORT, getByte("DeleteAfterImport", DEFAULT_PLUGIN_DELETE_AFTER_IMPORT) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hwnd, IDC_REMEMBER_POSITION, getByte("RememberWindowPosition", DEFAULT_REMEMBER_WINDOW_POSITION) ? BST_CHECKED : BST_UNCHECKED); return TRUE; case WM_NOTIFY: switch (((LPNMHDR)lparam)->code) { case PSN_APPLY: setByte("ConfirmDeletion", IsDlgButtonChecked(hwnd, IDC_CONFIRM_DELETION) ? 1 : 0); setByte("DeleteAfterImport", IsDlgButtonChecked(hwnd, IDC_DELETE_AFTER_IMPORT) ? 1 : 0); setByte("RememberWindowPosition", IsDlgButtonChecked(hwnd, IDC_REMEMBER_POSITION) ? 1 : 0); pcli->pfnReloadProtoMenus(); break; } return TRUE; } return FALSE; }