/* ======================================================================== 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 "cslist.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 fdwReason, LPVOID lpvReserved) { g_hInst = hinstDLL; return TRUE; } // ====[ PLUGIN INFO ]======================================================== extern "C" __declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD) { return &pluginInfoEx; } // ====[ LOADER ]============================================================= static int OnDbChanged(WPARAM wparam, LPARAM lparam) { HANDLE hContact = (HANDLE)wparam; 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 && !lstrcmpA(cws->szSetting, (char*)szUniqueID)) pcli->pfnReloadProtoMenus(); } return 0; } static int OnInitOptions(WPARAM wparam, LPARAM lparam) { OPTIONSDIALOGPAGE odp = { sizeof(odp) }; odp.position = 955000000; odp.hInstance = g_hInst; odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS); odp.pszTitle = MODULENAME; odp.pfnDlgProc = CSOptionsProc; odp.pszGroup = LPGEN("Status"); odp.flags = ODPF_BOLDGROUPS; Options_AddPage(wparam, &odp); return 0; } static int OnCreateMenuItems(WPARAM, LPARAM) { int protoCount; PROTOACCOUNT** pdesc; ProtoEnumAccounts(&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 wparam, LPARAM 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); mir_getCLI(); // support for ComboBoxEx INITCOMMONCONTROLSEX icc; icc.dwSize = sizeof(icc); icc.dwICC = ICC_USEREX_CLASSES; InitCommonControlsEx(&icc); // init icons TCHAR tszFile[MAX_PATH]; GetModuleFileName( g_hInst, tszFile, MAX_PATH ); SKINICONDESC sid = { sizeof(sid) }; sid.ptszDefaultFile = tszFile; sid.flags = SIDF_ALL_TCHAR; sid.cx = sid.cy = 16; sid.ptszSection = _T(MODULENAME); for (int i=0; i < SIZEOF(forms); i++) { char szSettingName[64]; mir_snprintf(szSettingName, SIZEOF(szSettingName), "%s_%s", MODNAME, forms[i].pszIconIcoLib); sid.pszName = szSettingName; sid.ptszDescription = forms[i].ptszDescr; sid.iDefaultIndex = -forms[i].iconNoIcoLib; forms[i].hIcoLibItem = Skin_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() { arWindows.destroy(); return 0; } // ====[ FUN ]================================================================ void RegisterHotkeys(char buf[200], TCHAR* accName, int Number) { HOTKEYDESC hotkey = { sizeof(hotkey) }; hotkey.dwFlags = HKD_TCHAR; hotkey.pszName = buf; hotkey.ptszDescription = accName; hotkey.ptszSection = LPGENT("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 = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)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_TCHAR; if (code == IDC_CANCEL) { statusToSet = 0; ics.ptszName = _T(""); ics.ptszMessage = _T(""); } 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; ProtoCallService(szAccName, PS_SETCUSTOMSTATUSEX, 0, (LPARAM)&ics); } INT_PTR showList(WPARAM wparam, LPARAM lparam, LPARAM param) { char* szProto = (char*)param; for (int i=0; i < arWindows.getCount(); i++) { CSWindow *p = arWindows[i]; if ( !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 = ProtoGetAccount(protoName); if (pdescr == NULL) return; HGENMENU hRoot = MO_GetProtoRootMenu(pdescr->szModuleName); if (hRoot == NULL) return; char buf[200]; mir_snprintf(buf, SIZEOF(buf), "CSList/ShowList/%s", protoName); if ( !ServiceExists(buf)) CreateServiceFunctionParam(buf, showList, (LPARAM)protoName); CLISTMENUITEM mi = { sizeof(mi) }; mi.flags = CMIF_CHILDPOPUP | CMIF_TCHAR; mi.icolibItem = forms[0].hIcoLibItem; mi.ptszName = _T(MODULENAME); mi.position = 2000040000; mi.pszService = buf; mi.hParentMenu = 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, 32, "XStatus%dName", i); if ( !db_get_ts( NULL, protoName, bufTitle, &dbv )) { lstrcpy(si->m_tszTitle, dbv.ptszVal); db_free(&dbv); } else si->m_tszTitle[0] = 0; mir_snprintf(bufMessage, 32, "XStatus%dMsg", i); if ( !db_get_ts( NULL, protoName, bufMessage, &dbv )) { lstrcpy(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 = ProtoGetAccount(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 < SIZEOF(forms); i++) { if ( forms[i].idc < 0 ) continue; SendDlgItemMessage( m_handle, forms[i].idc, BM_SETIMAGE, IMAGE_ICON, (LPARAM)Skin_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 )TranslateTS(forms[i].ptszTitle), BATF_TCHAR ); } } 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 ) { TCHAR filter[MAX_PATH]; GetDlgItemText( m_handle, IDC_FILTER_FIELD, filter, MAX_PATH ); if ( lstrlen( filter )) { TCHAR title[EXTRASTATUS_TITLE_LIMIT], message[EXTRASTATUS_MESSAGE_LIMIT]; lstrcpy( title, li->m_item->m_tszTitle ); lstrcpy( message, li->m_item->m_tszMessage ); if ( strpos( _tcslwr( title ), _tcslwr( filter )) == -1 ) if ( strpos( _tcslwr( message ), _tcslwr( 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 ); SetForegroundWindow( hFilter ); if ( isEnabled ) SetFocus( hFilter ); else { TCHAR filterText[255]; GetDlgItemText(m_handle, IDC_FILTER_FIELD, filterText, SIZEOF(filterText)); if ( lstrlen( filterText ) > 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 = ProtoGetAccount(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; TCHAR tszName[100]; CUSTOM_STATUS cs = { sizeof(cs) }; cs.flags = CSSF_MASK_NAME | CSSF_DEFAULT_NAME | CSSF_TCHAR; 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 = TranslateTS(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 ) { TCHAR* ptszInputText = (TCHAR*)mir_alloc((limit + 8) * sizeof(TCHAR)); GetDlgItemText( m_handle, item, ptszInputText, limit + 8 ); if ( _tcslen( ptszInputText ) > limit ) { #if ( WINVER >= 0x501 ) TCHAR tszPopupTip[MAX_PATH]; EDITBALLOONTIP ebt = { 0 }; ebt.cbStruct = sizeof(ebt); ebt.pszTitle = TranslateT("Warning"); mir_sntprintf(tszPopupTip, SIZEOF(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; SendMessage( GetDlgItem( m_handle, item ), EM_SHOWBALLOONTIP, 0, (LPARAM)&ebt ); #endif TCHAR* ptszOutputText = (TCHAR*)mir_alloc((limit + 1) * sizeof(TCHAR)); 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; TCHAR tszInputMessage[EXTRASTATUS_MESSAGE_LIMIT]; GetDlgItemText( m_handle, IDC_MESSAGE, tszInputMessage, EXTRASTATUS_MESSAGE_LIMIT ); PROTOACCOUNT* pdescr = (PROTOACCOUNT*)CallService(MS_PROTO_GETACCOUNT, 0, (LPARAM)m_parent->m_protoName); if (pdescr == NULL) return; WPARAM i = SendMessage(m_hCombo, CB_GETCURSEL, 0, 0)+1; TCHAR tszTitle[100]; CUSTOM_STATUS cs = { sizeof(cs) }; cs.flags = CSSF_MASK_NAME | CSSF_DEFAULT_NAME | CSSF_TCHAR; cs.ptszName = tszTitle; cs.wParam = &i; if ( CallProtoService(pdescr->szModuleName, PS_GETCUSTOMSTATUSEX, 0, (LPARAM)&cs) == 0) lstrcpyn(m_item->m_tszTitle, TranslateTS(tszTitle), SIZEOF(m_item->m_tszTitle)); if ( lstrcmp(m_item->m_tszMessage, tszInputMessage)) lstrcpy( m_item->m_tszMessage, tszInputMessage), m_bChanged = true; } CSListView::CSListView(HWND hwnd, CSWindow* parent) { m_handle = hwnd; m_parent = parent; #if (_WIN32_WINNT >= 0x501) 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 ); #endif 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 ); #if (_WIN32_IE >= 0x0400) ListView_SetExtendedListViewStyleEx( m_handle, 0, LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | LVS_EX_HEADERDRAGDROP | LVS_EX_INFOTIP ); #endif 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.cchTextMax = 256; lvi.iItem = ListView_GetItemCount( m_handle ); lvi.lParam = itemNumber; #if ( WINVER >= 0x501 ) lvi.iGroupId = ( item->m_bFavourite == TRUE ) ? 0 : 1; #endif // 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 = lstrcmp(p1->m_tszTitle, p2->m_tszTitle); ttlRes = result; result = lstrcmp(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, SIZEOF(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, SIZEOF(dbSetting), "%s_Item%dIcon", protoName, i); item->m_iIcon = getByte(dbSetting, DEFAULT_ITEM_ICON); mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dTitle", protoName, i); if ( !getTString(dbSetting, &dbv)) { lstrcpy(item->m_tszTitle, dbv.ptszVal); db_free(&dbv); } else item->m_tszTitle[0] = 0; mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dMessage", protoName, i); if ( !getTString(dbSetting, &dbv)) { lstrcpy(item->m_tszMessage, dbv.ptszVal); db_free(&dbv); } else item->m_tszMessage[0] = 0; mir_snprintf(dbSetting, SIZEOF(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, SIZEOF(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, SIZEOF(dbSetting), "%s_Item%dIcon", protoName, i); setByte(dbSetting, item->m_iIcon); mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dTitle", protoName, i); setTString(dbSetting, item->m_tszTitle); mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dMessage", protoName, i); setTString(dbSetting, item->m_tszMessage); mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dFavourite", protoName, i); setByte(dbSetting, item->m_bFavourite); } mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_ItemsCount", protoName); setWord(dbSetting, m_list->getCount()); for ( ; i <= oldItemsCount; i++) { mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dIcon", protoName, i); deleteSetting(dbSetting); mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dTitle", protoName, i); deleteSetting(dbSetting); mir_snprintf(dbSetting, SIZEOF(dbSetting), "%s_Item%dMessage", protoName, i); deleteSetting(dbSetting); mir_snprintf(dbSetting, SIZEOF(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, TranslateT(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?"), TranslateT(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?"), TranslateT(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 ) if (csw->m_itemslist!=NULL) 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 ( BOOL )GetStockObject( WHITE_BRUSH ); return FALSE; 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 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; }