From 9b048d56368949c544bdf6e85489405a5fd99ace Mon Sep 17 00:00:00 2001 From: Rozhuk Ivan Date: Tue, 25 Nov 2014 16:22:35 +0000 Subject: code cleanup git-svn-id: http://svn.miranda-ng.org/main/trunk@11062 1316c22d-e87f-b044-9b9b-93d7a3e3ba9c --- src/modules/clist/clcitems.cpp | 1408 ++++++++++++------------ src/modules/database/profilemanager.cpp | 1801 +++++++++++++++---------------- 2 files changed, 1605 insertions(+), 1604 deletions(-) (limited to 'src/modules') diff --git a/src/modules/clist/clcitems.cpp b/src/modules/clist/clcitems.cpp index b3a79541f6..5e78b3e00c 100644 --- a/src/modules/clist/clcitems.cpp +++ b/src/modules/clist/clcitems.cpp @@ -1,703 +1,705 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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 "..\..\core\commonheaders.h" -#include "clc.h" - -//routines for managing adding/removal of items in the list, including sorting - -int fnAddItemToGroup(ClcGroup *group, int iAboveItem) -{ - ClcContact* newItem = cli.pfnCreateClcContact(); - newItem->type = CLCIT_DIVIDER; - newItem->flags = 0; - newItem->szText[0] = '\0'; - memset(newItem->iExtraImage, 0xFF, sizeof(newItem->iExtraImage)); - - List_Insert((SortedList*)&group->cl, newItem, iAboveItem); - return iAboveItem; -} - -ClcGroup* fnAddGroup(HWND hwnd, struct ClcData *dat, const TCHAR *szName, DWORD flags, int groupId, int calcTotalMembers) -{ - TCHAR *pBackslash, *pNextField, szThisField[ SIZEOF(dat->list.cl.items[0]->szText) ]; - ClcGroup *group = &dat->list; - int i, compareResult; - - dat->needsResort = 1; - if (!(GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_USEGROUPS)) - return &dat->list; - - pNextField = (TCHAR*)szName; - do { - pBackslash = _tcschr(pNextField, '\\'); - if (pBackslash == NULL) { - lstrcpyn(szThisField, pNextField, SIZEOF(szThisField)); - pNextField = NULL; - } - else { - lstrcpyn(szThisField, pNextField, min(SIZEOF(szThisField), pBackslash - pNextField + 1)); - pNextField = pBackslash + 1; - } - compareResult = 1; - for (i=0; i < group->cl.count; i++) { - if (group->cl.items[i]->type == CLCIT_CONTACT) - break; - if (group->cl.items[i]->type != CLCIT_GROUP) - continue; - compareResult = lstrcmp(szThisField, group->cl.items[i]->szText); - if (compareResult == 0) { - if (pNextField == NULL && flags != (DWORD) - 1) { - group->cl.items[i]->groupId = (WORD) groupId; - group = group->cl.items[i]->group; - group->expanded = (flags & GROUPF_EXPANDED) != 0; - group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; - group->groupId = groupId; - } - else - group = group->cl.items[i]->group; - break; - } - if (pNextField == NULL && group->cl.items[i]->groupId == 0) - break; - if (!(dat->exStyle & CLS_EX_SORTGROUPSALPHA) && groupId && group->cl.items[i]->groupId > groupId) - break; - } - if (compareResult) { - if (groupId == 0) - return NULL; - i = cli.pfnAddItemToGroup(group, i); - group->cl.items[i]->type = CLCIT_GROUP; - lstrcpyn(group->cl.items[i]->szText, szThisField, SIZEOF(group->cl.items[i]->szText)); - group->cl.items[i]->groupId = (WORD) (pNextField ? 0 : groupId); - group->cl.items[i]->group = (ClcGroup *) mir_alloc(sizeof(ClcGroup)); - group->cl.items[i]->group->parent = group; - group = group->cl.items[i]->group; - memset(&group->cl, 0, sizeof(group->cl)); - group->cl.increment = 10; - if (flags == (DWORD) - 1 || pNextField != NULL) { - group->expanded = 0; - group->hideOffline = 0; - } - else { - group->expanded = (flags & GROUPF_EXPANDED) != 0; - group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; - } - group->groupId = pNextField ? 0 : groupId; - group->totalMembers = 0; - if (flags != (DWORD) - 1 && pNextField == NULL && calcTotalMembers) { - DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE); - for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { - ClcCacheEntry *cache = cli.pfnGetCacheEntry(hContact); - if (!lstrcmp(cache->tszGroup, szName) && (style & CLS_SHOWHIDDEN || !cache->bIsHidden)) - group->totalMembers++; - } - } - } - } - while (pNextField); - return group; -} - -void fnFreeContact(ClcContact* p) -{ - if (p->type == CLCIT_GROUP) { - cli.pfnFreeGroup(p->group); - mir_free(p->group); - p->group = NULL; - } -} - -void fnFreeGroup(ClcGroup *group) -{ - int i; - for (i=0; i < group->cl.count; i++) { - cli.pfnFreeContact(group->cl.items[i]); - mir_free(group->cl.items[i]); - } - if (group->cl.items) - mir_free(group->cl.items); - group->cl.limit = group->cl.count = 0; - group->cl.items = NULL; -} - -static int iInfoItemUniqueHandle = 0; -int fnAddInfoItemToGroup(ClcGroup *group, int flags, const TCHAR *pszText) -{ - int i=0; - - if (flags & CLCIIF_BELOWCONTACTS) - i = group->cl.count; - else if (flags & CLCIIF_BELOWGROUPS) { - for (; i < group->cl.count; i++) - if (group->cl.items[i]->type == CLCIT_CONTACT) - break; - } - else - for (; i < group->cl.count; i++) - if (group->cl.items[i]->type != CLCIT_INFO) - break; - i = cli.pfnAddItemToGroup(group, i); - iInfoItemUniqueHandle = LOWORD(iInfoItemUniqueHandle+1); - if (iInfoItemUniqueHandle == 0) - ++iInfoItemUniqueHandle; - group->cl.items[i]->type = CLCIT_INFO; - group->cl.items[i]->flags = (BYTE) flags; - group->cl.items[i]->hContact = (MCONTACT)++iInfoItemUniqueHandle; - lstrcpyn(group->cl.items[i]->szText, pszText, SIZEOF(group->cl.items[i]->szText)); - return i; -} - -int fnAddContactToGroup(struct ClcData *dat, ClcGroup *group, MCONTACT hContact) -{ - int i, index = -1; - - dat->needsResort = 1; - for (i = group->cl.count - 1; i >= 0; i--) { - if (group->cl.items[i]->hContact == hContact) - return i; - - if (index == -1) - if (group->cl.items[i]->type != CLCIT_INFO || !(group->cl.items[i]->flags & CLCIIF_BELOWCONTACTS)) - index = i; - } - - i = cli.pfnAddItemToGroup(group, index + 1); - char *szProto = GetContactProto(hContact); - group->cl.items[i]->type = CLCIT_CONTACT; - group->cl.items[i]->iImage = CallService(MS_CLIST_GETCONTACTICON, hContact, 0); - group->cl.items[i]->hContact = hContact; - group->cl.items[i]->proto = szProto; - if (szProto != NULL && !cli.pfnIsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE))) - group->cl.items[i]->flags |= CONTACTF_ONLINE; - WORD apparentMode = szProto != NULL ? db_get_w(hContact, szProto, "ApparentMode", 0) : 0; - if (apparentMode == ID_STATUS_OFFLINE) - group->cl.items[i]->flags |= CONTACTF_INVISTO; - else if (apparentMode == ID_STATUS_ONLINE) - group->cl.items[i]->flags |= CONTACTF_VISTO; - else if (apparentMode) - group->cl.items[i]->flags |= CONTACTF_VISTO | CONTACTF_INVISTO; - if (db_get_b(hContact, "CList", "NotOnList", 0)) - group->cl.items[i]->flags |= CONTACTF_NOTONLIST; - DWORD idleMode = szProto != NULL ? db_get_dw(hContact, szProto, "IdleTS", 0) : 0; - if (idleMode) - group->cl.items[i]->flags |= CONTACTF_IDLE; - lstrcpyn(group->cl.items[i]->szText, cli.pfnGetContactDisplayName(hContact, 0), SIZEOF(group->cl.items[i]->szText)); - - ClcCacheEntry *p = cli.pfnGetCacheEntry(hContact); - if (p != NULL) - replaceStrT(p->tszGroup, NULL); - - return i; -} - -void fnAddContactToTree(HWND hwnd, struct ClcData *dat, MCONTACT hContact, int updateTotalCount, int checkHideOffline) -{ - ClcGroup *group; - DBVARIANT dbv; - DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE); - WORD status = ID_STATUS_OFFLINE; - char *szProto = GetContactProto(hContact); - - dat->needsResort = 1; - if (style & CLS_NOHIDEOFFLINE) - checkHideOffline = 0; - if (checkHideOffline) - if (szProto != NULL) - status = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); - - if (db_get_ts(hContact, "CList", "Group", &dbv)) - group = &dat->list; - else { - group = cli.pfnAddGroup(hwnd, dat, dbv.ptszVal, (DWORD) - 1, 0, 0); - if (group == NULL) { - int i, len; - DWORD groupFlags; - TCHAR *szGroupName; - if (!(style & CLS_HIDEEMPTYGROUPS)) { - mir_free(dbv.ptszVal); - return; - } - if (checkHideOffline && cli.pfnIsHiddenMode(dat, status)) { - for (i = 1;; i++) { - szGroupName = cli.pfnGetGroupName(i, &groupFlags); - if (szGroupName == NULL) { - mir_free(dbv.ptszVal); - return; - } - if (!lstrcmp(szGroupName, dbv.ptszVal)) - break; - } - if (groupFlags & GROUPF_HIDEOFFLINE) { - mir_free(dbv.ptszVal); - return; - } - } - for (i = 1;; i++) { - szGroupName = cli.pfnGetGroupName(i, &groupFlags); - if (szGroupName == NULL) { - mir_free(dbv.ptszVal); - return; - } - if (!lstrcmp(szGroupName, dbv.ptszVal)) - break; - len = lstrlen(szGroupName); - if (!_tcsncmp(szGroupName, dbv.ptszVal, len) && dbv.ptszVal[len] == '\\') - cli.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 1); - } - group = cli.pfnAddGroup(hwnd, dat, dbv.ptszVal, groupFlags, i, 1); - } - mir_free(dbv.ptszVal); - } - if (checkHideOffline) { - if (cli.pfnIsHiddenMode(dat, status) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { - if (updateTotalCount) - group->totalMembers++; - return; - } - } - cli.pfnAddContactToGroup(dat, group, hContact); - if (updateTotalCount) - group->totalMembers++; -} - -ClcGroup* fnRemoveItemFromGroup(HWND hwnd, ClcGroup *group, ClcContact *contact, int updateTotalCount) -{ - int iContact; - if ((iContact = List_IndexOf((SortedList*)&group->cl, contact)) == -1) - return group; - - if (contact->type == CLCIT_CONTACT) { - if (updateTotalCount) - group->totalMembers--; - - ClcCacheEntry *p = cli.pfnGetCacheEntry(contact->hContact); - if (p != NULL) - replaceStrT(p->tszGroup, NULL); - } - - cli.pfnFreeContact(group->cl.items[iContact]); - mir_free(group->cl.items[iContact]); - List_Remove((SortedList*)&group->cl, iContact); - - if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.count == 0 && group->parent != NULL) - for (int i=0; i < group->parent->cl.count; i++) - if (group->parent->cl.items[i]->type == CLCIT_GROUP && group->parent->cl.items[i]->groupId == group->groupId) - return cli.pfnRemoveItemFromGroup(hwnd, group->parent, group->parent->cl.items[i], 0); - - return group; -} - -void fnDeleteItemFromTree(HWND hwnd, MCONTACT hItem) -{ - ClcContact *contact; - ClcGroup *group; - struct ClcData *dat = (struct ClcData *) GetWindowLongPtr(hwnd, 0); - - dat->needsResort = 1; - if (!cli.pfnFindItem(hwnd, dat, hItem, &contact, &group, NULL)) { - DBVARIANT dbv; - int i, nameOffset; - if (!IsHContactContact(hItem)) - return; - if (db_get_ts(hItem, "CList", "Group", &dbv)) - return; - - //decrease member counts of all parent groups too - group = &dat->list; - nameOffset = 0; - for (i=0;; i++) { - if (group->scanIndex == group->cl.count) - break; - if (group->cl.items[i]->type == CLCIT_GROUP) { - int len = lstrlen(group->cl.items[i]->szText); - if (!_tcsncmp(group->cl.items[i]->szText, dbv.ptszVal + nameOffset, len) && - (dbv.ptszVal[nameOffset + len] == '\\' || dbv.ptszVal[nameOffset + len] == '\0')) { - group->totalMembers--; - if (dbv.ptszVal[nameOffset + len] == '\0') - break; - } - } - } - mir_free(dbv.ptszVal); - } - else cli.pfnRemoveItemFromGroup(hwnd, group, contact, 1); -} - -void fnRebuildEntireList(HWND hwnd, struct ClcData *dat) -{ - DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE); - ClcGroup *group; - - dat->list.expanded = 1; - dat->list.hideOffline = db_get_b(NULL, "CLC", "HideOfflineRoot", 0) && style&CLS_USEGROUPS; - dat->list.cl.count = dat->list.cl.limit = 0; - dat->selection = -1; - - for (int i = 1;; i++) { - DWORD groupFlags; - TCHAR *szGroupName = cli.pfnGetGroupName(i, &groupFlags); - if (szGroupName == NULL) - break; - cli.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 0); - } - - for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { - if (style & CLS_SHOWHIDDEN || !db_get_b(hContact, "CList", "Hidden", 0)) { - DBVARIANT dbv; - if (db_get_ts(hContact, "CList", "Group", &dbv)) - group = &dat->list; - else { - group = cli.pfnAddGroup(hwnd, dat, dbv.ptszVal, (DWORD) - 1, 0, 0); - if (group == NULL && style & CLS_SHOWHIDDEN) group = &dat->list; - mir_free(dbv.ptszVal); - } - - if (group != NULL) { - group->totalMembers++; - - if (dat->filterSearch && dat->szQuickSearch[0] != '\0') { - TCHAR *name = cli.pfnGetContactDisplayName(hContact, 0); - TCHAR *lowered_name = CharLowerW(NEWTSTR_ALLOCA(name)); - TCHAR *lowered_search = CharLowerW(NEWTSTR_ALLOCA(dat->szQuickSearch)); - - if (_tcsstr(lowered_name, lowered_search)) - cli.pfnAddContactToGroup(dat, group, hContact); - } - else if (!(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { - char *szProto = GetContactProto(hContact); - if (szProto == NULL) { - if (!cli.pfnIsHiddenMode(dat, ID_STATUS_OFFLINE)) - cli.pfnAddContactToGroup(dat, group, hContact); - } - else if (!cli.pfnIsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE))) - cli.pfnAddContactToGroup(dat, group, hContact); - } - else cli.pfnAddContactToGroup(dat, group, hContact); - } - } - } - - if (style & CLS_HIDEEMPTYGROUPS) { - group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.count) { - group = group->parent; - if (group == NULL) - break; - } - else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { - if (group->cl.items[group->scanIndex]->group->cl.count == 0) { - group = cli.pfnRemoveItemFromGroup(hwnd, group, group->cl.items[group->scanIndex], 0); - } - else { - group = group->cl.items[group->scanIndex]->group; - group->scanIndex = 0; - } - continue; - } - group->scanIndex++; - } - } - - cli.pfnSortCLC(hwnd, dat, 0); - cli.pfnSetAllExtraIcons(0); -} - -int fnGetGroupContentsCount(ClcGroup *group, int visibleOnly) -{ - int count = group->cl.count; - ClcGroup *topgroup = group; - - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.count) { - if (group == topgroup) - break; - group = group->parent; - } - else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP && (!visibleOnly || group->cl.items[group->scanIndex]->group->expanded)) { - group = group->cl.items[group->scanIndex]->group; - group->scanIndex = 0; - count += group->cl.count; - continue; - } - group->scanIndex++; - } - return count; -} - -static int __cdecl GroupSortProc(const void* p1, const void* p2) -{ - ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; - - return lstrcmpi(contact1[0]->szText, contact2[0]->szText); -} - -static int __cdecl ContactSortProc(const void* p1, const void* p2) -{ - ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; - - int result = cli.pfnCompareContacts(contact1[0], contact2[0]); - if (result) - return result; - //nothing to distinguish them, so make sure they stay in the same order - return (int)((INT_PTR) contact2[0]->hContact - (INT_PTR) contact1[0]->hContact); -} - -static void InsertionSort(ClcContact **pContactArray, int nArray, int (*CompareProc) (const void *, const void *)) -{ - int i, j; - ClcContact* testElement; - - for (i = 1; i < nArray; i++) { - if (CompareProc(&pContactArray[i - 1], &pContactArray[i]) > 0) { - testElement = pContactArray[i]; - for (j = i - 2; j >= 0; j--) - if (CompareProc(&pContactArray[j], &testElement) <= 0) - break; - j++; - memmove(&pContactArray[j + 1], &pContactArray[j], sizeof(void*) * (i - j)); - pContactArray[j] = testElement; - } - } -} - -static void SortGroup(struct ClcData *dat, ClcGroup *group, int useInsertionSort) -{ - int i, sortCount; - - for (i = group->cl.count - 1; i >= 0; i--) { - if (group->cl.items[i]->type == CLCIT_DIVIDER) { - mir_free(group->cl.items[i]); - List_Remove((SortedList*)&group->cl, i); - } - } - - for (i=0; i < group->cl.count; i++) - if (group->cl.items[i]->type != CLCIT_INFO) - break; - if (i > group->cl.count - 2) - return; - if (group->cl.items[i]->type == CLCIT_GROUP) { - if (dat->exStyle & CLS_EX_SORTGROUPSALPHA) { - for (sortCount = 0; i + sortCount < group->cl.count; sortCount++) - if (group->cl.items[i + sortCount]->type != CLCIT_GROUP) - break; - qsort(group->cl.items + i, sortCount, sizeof(void*), GroupSortProc); - i = i + sortCount; - } - for (; i < group->cl.count; i++) - if (group->cl.items[i]->type == CLCIT_CONTACT) - break; - if (group->cl.count - i < 2) - return; - } - for (sortCount = 0; i + sortCount < group->cl.count; sortCount++) - if (group->cl.items[i + sortCount]->type != CLCIT_CONTACT) - break; - if (useInsertionSort) - InsertionSort(group->cl.items + i, sortCount, ContactSortProc); - else - qsort(group->cl.items + i, sortCount, sizeof(void*), ContactSortProc); - if (dat->exStyle & CLS_EX_DIVIDERONOFF) { - int prevContactOnline = 0; - for (i=0; i < group->cl.count; i++) { - if (group->cl.items[i]->type != CLCIT_CONTACT) - continue; - if (group->cl.items[i]->flags & CONTACTF_ONLINE) - prevContactOnline = 1; - else { - if (prevContactOnline) { - i = cli.pfnAddItemToGroup(group, i); - group->cl.items[i]->type = CLCIT_DIVIDER; - lstrcpy(group->cl.items[i]->szText, TranslateT("Offline")); - } - break; - } - } - } -} - -void fnSortCLC(HWND hwnd, struct ClcData *dat, int useInsertionSort) -{ - ClcContact *selcontact; - ClcGroup *group = &dat->list, *selgroup; - MCONTACT hSelItem; - - if (dat->needsResort) { - if (cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) == -1) - hSelItem = NULL; - else - hSelItem = (MCONTACT)cli.pfnContactToHItem(selcontact); - group->scanIndex = 0; - SortGroup(dat, group, useInsertionSort); - for (;;) { - if (group->scanIndex == group->cl.count) { - group = group->parent; - if (group == NULL) - break; - } - else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { - group = group->cl.items[group->scanIndex]->group; - group->scanIndex = 0; - SortGroup(dat, group, useInsertionSort); - continue; - } - group->scanIndex++; - } - if (hSelItem) - if (cli.pfnFindItem(hwnd, dat, hSelItem, &selcontact, &selgroup, NULL)) - dat->selection = cli.pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*)&selgroup->cl, selcontact)); - - cli.pfnRecalcScrollBar(hwnd, dat); - } - dat->needsResort = 0; - cli.pfnInvalidateRect(hwnd, NULL, FALSE); -} - -struct SavedContactState_t -{ - MCONTACT hContact; - WORD iExtraImage[EXTRA_ICON_COUNT]; - int checked; -}; - -struct SavedGroupState_t -{ - int groupId, expanded; -}; - -struct SavedInfoState_t -{ - int parentId; - ClcContact contact; -}; - -void fnSaveStateAndRebuildList(HWND hwnd, struct ClcData *dat) -{ - NMCLISTCONTROL nm; - int i, j; - ClcGroup *group; - ClcContact *contact; - - cli.pfnHideInfoTip(hwnd, dat); - KillTimer(hwnd, TIMERID_INFOTIP); - KillTimer(hwnd, TIMERID_RENAME); - cli.pfnEndRename(hwnd, dat, 1); - - OBJLIST saveContact(10, NumericKeySortT); - OBJLIST saveGroup(100, NumericKeySortT); - OBJLIST saveInfo(10, NumericKeySortT); - - dat->needsResort = 1; - group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.count) { - group = group->parent; - if (group == NULL) - break; - } - else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { - group = group->cl.items[group->scanIndex]->group; - group->scanIndex = 0; - - SavedGroupState_t* p = new SavedGroupState_t; - p->groupId = group->groupId; - p->expanded = group->expanded; - saveGroup.insert(p); - continue; - } - else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT) { - SavedContactState_t* p = new SavedContactState_t; - p->hContact = group->cl.items[group->scanIndex]->hContact; - memcpy(p->iExtraImage, group->cl.items[group->scanIndex]->iExtraImage, sizeof(p->iExtraImage)); - p->checked = group->cl.items[group->scanIndex]->flags & CONTACTF_CHECKED; - saveContact.insert(p); - } - else if (group->cl.items[group->scanIndex]->type == CLCIT_INFO) { - SavedInfoState_t* p = new SavedInfoState_t; - p->parentId = (group->parent == NULL) ? -1 : group->groupId; - p->contact = *group->cl.items[group->scanIndex]; - saveInfo.insert(p); - } - group->scanIndex++; - } - - cli.pfnFreeGroup(&dat->list); - cli.pfnRebuildEntireList(hwnd, dat); - - group = &dat->list; - group->scanIndex = 0; - for (;;) { - if (group->scanIndex == group->cl.count) { - group = group->parent; - if (group == NULL) - break; - } - else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { - group = group->cl.items[group->scanIndex]->group; - group->scanIndex = 0; - - SavedGroupState_t tmp, *p; - tmp.groupId = group->groupId; - if ((p = saveGroup.find(&tmp)) != NULL) - group->expanded = p->expanded; - continue; - } - else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT) { - SavedContactState_t tmp, *p; - tmp.hContact = group->cl.items[group->scanIndex]->hContact; - if ((p = saveContact.find(&tmp)) != NULL) { - memcpy(group->cl.items[group->scanIndex]->iExtraImage, p->iExtraImage, sizeof(p->iExtraImage)); - if (p->checked) - group->cl.items[group->scanIndex]->flags |= CONTACTF_CHECKED; - } - } - - group->scanIndex++; - } - - for (i=0; i < saveInfo.getCount(); i++) { - if (saveInfo[i].parentId == -1) - group = &dat->list; - else { - if (!cli.pfnFindItem(hwnd, dat, saveInfo[i].parentId | HCONTACT_ISGROUP, &contact, NULL, NULL)) - continue; - group = contact->group; - } - j = cli.pfnAddInfoItemToGroup(group, saveInfo[i].contact.flags, _T("")); - *group->cl.items[j] = saveInfo[i].contact; - } - - cli.pfnRecalculateGroupCheckboxes(hwnd, dat); - - cli.pfnRecalcScrollBar(hwnd, dat); - nm.hdr.code = CLN_LISTREBUILT; - nm.hdr.hwndFrom = hwnd; - nm.hdr.idFrom = GetDlgCtrlID(hwnd); - SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm); -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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 "..\..\core\commonheaders.h" +#include "clc.h" + +//routines for managing adding/removal of items in the list, including sorting + +int fnAddItemToGroup(ClcGroup *group, int iAboveItem) +{ + ClcContact* newItem = cli.pfnCreateClcContact(); + newItem->type = CLCIT_DIVIDER; + newItem->flags = 0; + newItem->szText[0] = '\0'; + memset(newItem->iExtraImage, 0xFF, sizeof(newItem->iExtraImage)); + + List_Insert((SortedList*)&group->cl, newItem, iAboveItem); + return iAboveItem; +} + +ClcGroup* fnAddGroup(HWND hwnd, struct ClcData *dat, const TCHAR *szName, DWORD flags, int groupId, int calcTotalMembers) +{ + TCHAR *pBackslash, *pNextField, szThisField[ SIZEOF(dat->list.cl.items[0]->szText) ]; + ClcGroup *group = &dat->list; + int i, compareResult; + + dat->needsResort = 1; + if (!(GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_USEGROUPS)) + return &dat->list; + + pNextField = (TCHAR*)szName; + do { + pBackslash = _tcschr(pNextField, '\\'); + if (pBackslash == NULL) { + lstrcpyn(szThisField, pNextField, SIZEOF(szThisField)); + pNextField = NULL; + } + else { + lstrcpyn(szThisField, pNextField, min(SIZEOF(szThisField), pBackslash - pNextField + 1)); + pNextField = pBackslash + 1; + } + compareResult = 1; + for (i=0; i < group->cl.count; i++) { + if (group->cl.items[i]->type == CLCIT_CONTACT) + break; + if (group->cl.items[i]->type != CLCIT_GROUP) + continue; + compareResult = lstrcmp(szThisField, group->cl.items[i]->szText); + if (compareResult == 0) { + if (pNextField == NULL && flags != (DWORD) - 1) { + group->cl.items[i]->groupId = (WORD) groupId; + group = group->cl.items[i]->group; + group->expanded = (flags & GROUPF_EXPANDED) != 0; + group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; + group->groupId = groupId; + } + else + group = group->cl.items[i]->group; + break; + } + if (pNextField == NULL && group->cl.items[i]->groupId == 0) + break; + if (!(dat->exStyle & CLS_EX_SORTGROUPSALPHA) && groupId && group->cl.items[i]->groupId > groupId) + break; + } + if (compareResult) { + if (groupId == 0) + return NULL; + i = cli.pfnAddItemToGroup(group, i); + group->cl.items[i]->type = CLCIT_GROUP; + lstrcpyn(group->cl.items[i]->szText, szThisField, SIZEOF(group->cl.items[i]->szText)); + group->cl.items[i]->groupId = (WORD) (pNextField ? 0 : groupId); + group->cl.items[i]->group = (ClcGroup *) mir_alloc(sizeof(ClcGroup)); + group->cl.items[i]->group->parent = group; + group = group->cl.items[i]->group; + memset(&group->cl, 0, sizeof(group->cl)); + group->cl.increment = 10; + if (flags == (DWORD) - 1 || pNextField != NULL) { + group->expanded = 0; + group->hideOffline = 0; + } + else { + group->expanded = (flags & GROUPF_EXPANDED) != 0; + group->hideOffline = (flags & GROUPF_HIDEOFFLINE) != 0; + } + group->groupId = pNextField ? 0 : groupId; + group->totalMembers = 0; + if (flags != (DWORD) - 1 && pNextField == NULL && calcTotalMembers) { + DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE); + for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { + ClcCacheEntry *cache = cli.pfnGetCacheEntry(hContact); + if (!lstrcmp(cache->tszGroup, szName) && (style & CLS_SHOWHIDDEN || !cache->bIsHidden)) + group->totalMembers++; + } + } + } + } + while (pNextField); + return group; +} + +void fnFreeContact(ClcContact* p) +{ + if (p->type == CLCIT_GROUP) { + cli.pfnFreeGroup(p->group); + mir_free(p->group); + p->group = NULL; + } +} + +void fnFreeGroup(ClcGroup *group) +{ + if (!group) + return; + if (group->cl.items) { + for (int i=0; i < group->cl.count; i++) { + cli.pfnFreeContact(group->cl.items[i]); + mir_free(group->cl.items[i]); + } + mir_free(group->cl.items); + group->cl.items = NULL; + } + group->cl.limit = group->cl.count = 0; +} + +static int iInfoItemUniqueHandle = 0; +int fnAddInfoItemToGroup(ClcGroup *group, int flags, const TCHAR *pszText) +{ + int i=0; + + if (flags & CLCIIF_BELOWCONTACTS) + i = group->cl.count; + else if (flags & CLCIIF_BELOWGROUPS) { + for (; i < group->cl.count; i++) + if (group->cl.items[i]->type == CLCIT_CONTACT) + break; + } + else + for (; i < group->cl.count; i++) + if (group->cl.items[i]->type != CLCIT_INFO) + break; + i = cli.pfnAddItemToGroup(group, i); + iInfoItemUniqueHandle = LOWORD(iInfoItemUniqueHandle+1); + if (iInfoItemUniqueHandle == 0) + ++iInfoItemUniqueHandle; + group->cl.items[i]->type = CLCIT_INFO; + group->cl.items[i]->flags = (BYTE) flags; + group->cl.items[i]->hContact = (MCONTACT)++iInfoItemUniqueHandle; + lstrcpyn(group->cl.items[i]->szText, pszText, SIZEOF(group->cl.items[i]->szText)); + return i; +} + +int fnAddContactToGroup(struct ClcData *dat, ClcGroup *group, MCONTACT hContact) +{ + int i, index = -1; + + dat->needsResort = 1; + for (i = group->cl.count - 1; i >= 0; i--) { + if (group->cl.items[i]->hContact == hContact) + return i; + + if (index == -1) + if (group->cl.items[i]->type != CLCIT_INFO || !(group->cl.items[i]->flags & CLCIIF_BELOWCONTACTS)) + index = i; + } + + i = cli.pfnAddItemToGroup(group, index + 1); + char *szProto = GetContactProto(hContact); + group->cl.items[i]->type = CLCIT_CONTACT; + group->cl.items[i]->iImage = CallService(MS_CLIST_GETCONTACTICON, hContact, 0); + group->cl.items[i]->hContact = hContact; + group->cl.items[i]->proto = szProto; + if (szProto != NULL && !cli.pfnIsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE))) + group->cl.items[i]->flags |= CONTACTF_ONLINE; + WORD apparentMode = szProto != NULL ? db_get_w(hContact, szProto, "ApparentMode", 0) : 0; + if (apparentMode == ID_STATUS_OFFLINE) + group->cl.items[i]->flags |= CONTACTF_INVISTO; + else if (apparentMode == ID_STATUS_ONLINE) + group->cl.items[i]->flags |= CONTACTF_VISTO; + else if (apparentMode) + group->cl.items[i]->flags |= CONTACTF_VISTO | CONTACTF_INVISTO; + if (db_get_b(hContact, "CList", "NotOnList", 0)) + group->cl.items[i]->flags |= CONTACTF_NOTONLIST; + DWORD idleMode = szProto != NULL ? db_get_dw(hContact, szProto, "IdleTS", 0) : 0; + if (idleMode) + group->cl.items[i]->flags |= CONTACTF_IDLE; + lstrcpyn(group->cl.items[i]->szText, cli.pfnGetContactDisplayName(hContact, 0), SIZEOF(group->cl.items[i]->szText)); + + ClcCacheEntry *p = cli.pfnGetCacheEntry(hContact); + if (p != NULL) + replaceStrT(p->tszGroup, NULL); + + return i; +} + +void fnAddContactToTree(HWND hwnd, struct ClcData *dat, MCONTACT hContact, int updateTotalCount, int checkHideOffline) +{ + ClcGroup *group; + DBVARIANT dbv; + DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE); + WORD status = ID_STATUS_OFFLINE; + char *szProto = GetContactProto(hContact); + + dat->needsResort = 1; + if (style & CLS_NOHIDEOFFLINE) + checkHideOffline = 0; + if (checkHideOffline) + if (szProto != NULL) + status = db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE); + + if (db_get_ts(hContact, "CList", "Group", &dbv)) + group = &dat->list; + else { + group = cli.pfnAddGroup(hwnd, dat, dbv.ptszVal, (DWORD) - 1, 0, 0); + if (group == NULL) { + int i, len; + DWORD groupFlags; + TCHAR *szGroupName; + if (!(style & CLS_HIDEEMPTYGROUPS)) { + mir_free(dbv.ptszVal); + return; + } + if (checkHideOffline && cli.pfnIsHiddenMode(dat, status)) { + for (i = 1;; i++) { + szGroupName = cli.pfnGetGroupName(i, &groupFlags); + if (szGroupName == NULL) { + mir_free(dbv.ptszVal); + return; + } + if (!lstrcmp(szGroupName, dbv.ptszVal)) + break; + } + if (groupFlags & GROUPF_HIDEOFFLINE) { + mir_free(dbv.ptszVal); + return; + } + } + for (i = 1;; i++) { + szGroupName = cli.pfnGetGroupName(i, &groupFlags); + if (szGroupName == NULL) { + mir_free(dbv.ptszVal); + return; + } + if (!lstrcmp(szGroupName, dbv.ptszVal)) + break; + len = lstrlen(szGroupName); + if (!_tcsncmp(szGroupName, dbv.ptszVal, len) && dbv.ptszVal[len] == '\\') + cli.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 1); + } + group = cli.pfnAddGroup(hwnd, dat, dbv.ptszVal, groupFlags, i, 1); + } + mir_free(dbv.ptszVal); + } + if (checkHideOffline) { + if (cli.pfnIsHiddenMode(dat, status) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { + if (updateTotalCount) + group->totalMembers++; + return; + } + } + cli.pfnAddContactToGroup(dat, group, hContact); + if (updateTotalCount) + group->totalMembers++; +} + +ClcGroup* fnRemoveItemFromGroup(HWND hwnd, ClcGroup *group, ClcContact *contact, int updateTotalCount) +{ + int iContact; + if ((iContact = List_IndexOf((SortedList*)&group->cl, contact)) == -1) + return group; + + if (contact->type == CLCIT_CONTACT) { + if (updateTotalCount) + group->totalMembers--; + + ClcCacheEntry *p = cli.pfnGetCacheEntry(contact->hContact); + if (p != NULL) + replaceStrT(p->tszGroup, NULL); + } + + cli.pfnFreeContact(group->cl.items[iContact]); + mir_free(group->cl.items[iContact]); + List_Remove((SortedList*)&group->cl, iContact); + + if ((GetWindowLongPtr(hwnd, GWL_STYLE) & CLS_HIDEEMPTYGROUPS) && group->cl.count == 0 && group->parent != NULL) + for (int i=0; i < group->parent->cl.count; i++) + if (group->parent->cl.items[i]->type == CLCIT_GROUP && group->parent->cl.items[i]->groupId == group->groupId) + return cli.pfnRemoveItemFromGroup(hwnd, group->parent, group->parent->cl.items[i], 0); + + return group; +} + +void fnDeleteItemFromTree(HWND hwnd, MCONTACT hItem) +{ + ClcContact *contact; + ClcGroup *group; + struct ClcData *dat = (struct ClcData *) GetWindowLongPtr(hwnd, 0); + + dat->needsResort = 1; + if (!cli.pfnFindItem(hwnd, dat, hItem, &contact, &group, NULL)) { + DBVARIANT dbv; + int i, nameOffset; + if (!IsHContactContact(hItem)) + return; + if (db_get_ts(hItem, "CList", "Group", &dbv)) + return; + + //decrease member counts of all parent groups too + group = &dat->list; + nameOffset = 0; + for (i=0;; i++) { + if (group->scanIndex == group->cl.count) + break; + if (group->cl.items[i]->type == CLCIT_GROUP) { + int len = lstrlen(group->cl.items[i]->szText); + if (!_tcsncmp(group->cl.items[i]->szText, dbv.ptszVal + nameOffset, len) && + (dbv.ptszVal[nameOffset + len] == '\\' || dbv.ptszVal[nameOffset + len] == '\0')) { + group->totalMembers--; + if (dbv.ptszVal[nameOffset + len] == '\0') + break; + } + } + } + mir_free(dbv.ptszVal); + } + else cli.pfnRemoveItemFromGroup(hwnd, group, contact, 1); +} + +void fnRebuildEntireList(HWND hwnd, struct ClcData *dat) +{ + DWORD style = GetWindowLongPtr(hwnd, GWL_STYLE); + ClcGroup *group; + + dat->list.expanded = 1; + dat->list.hideOffline = db_get_b(NULL, "CLC", "HideOfflineRoot", 0) && style&CLS_USEGROUPS; + dat->list.cl.count = dat->list.cl.limit = 0; + dat->selection = -1; + + for (int i = 1;; i++) { + DWORD groupFlags; + TCHAR *szGroupName = cli.pfnGetGroupName(i, &groupFlags); + if (szGroupName == NULL) + break; + cli.pfnAddGroup(hwnd, dat, szGroupName, groupFlags, i, 0); + } + + for (MCONTACT hContact = db_find_first(); hContact; hContact = db_find_next(hContact)) { + if (style & CLS_SHOWHIDDEN || !db_get_b(hContact, "CList", "Hidden", 0)) { + DBVARIANT dbv; + if (db_get_ts(hContact, "CList", "Group", &dbv)) + group = &dat->list; + else { + group = cli.pfnAddGroup(hwnd, dat, dbv.ptszVal, (DWORD) - 1, 0, 0); + if (group == NULL && style & CLS_SHOWHIDDEN) group = &dat->list; + mir_free(dbv.ptszVal); + } + + if (group != NULL) { + group->totalMembers++; + + if (dat->filterSearch && dat->szQuickSearch[0] != '\0') { + TCHAR *name = cli.pfnGetContactDisplayName(hContact, 0); + TCHAR *lowered_name = CharLowerW(NEWTSTR_ALLOCA(name)); + TCHAR *lowered_search = CharLowerW(NEWTSTR_ALLOCA(dat->szQuickSearch)); + + if (_tcsstr(lowered_name, lowered_search)) + cli.pfnAddContactToGroup(dat, group, hContact); + } + else if (!(style & CLS_NOHIDEOFFLINE) && (style & CLS_HIDEOFFLINE || group->hideOffline)) { + char *szProto = GetContactProto(hContact); + if (szProto == NULL) { + if (!cli.pfnIsHiddenMode(dat, ID_STATUS_OFFLINE)) + cli.pfnAddContactToGroup(dat, group, hContact); + } + else if (!cli.pfnIsHiddenMode(dat, db_get_w(hContact, szProto, "Status", ID_STATUS_OFFLINE))) + cli.pfnAddContactToGroup(dat, group, hContact); + } + else cli.pfnAddContactToGroup(dat, group, hContact); + } + } + } + + if (style & CLS_HIDEEMPTYGROUPS) { + group = &dat->list; + group->scanIndex = 0; + for (;;) { + if (group->scanIndex == group->cl.count) { + group = group->parent; + if (group == NULL) + break; + } + else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { + if (group->cl.items[group->scanIndex]->group->cl.count == 0) { + group = cli.pfnRemoveItemFromGroup(hwnd, group, group->cl.items[group->scanIndex], 0); + } + else { + group = group->cl.items[group->scanIndex]->group; + group->scanIndex = 0; + } + continue; + } + group->scanIndex++; + } + } + + cli.pfnSortCLC(hwnd, dat, 0); + cli.pfnSetAllExtraIcons(0); +} + +int fnGetGroupContentsCount(ClcGroup *group, int visibleOnly) +{ + int count = group->cl.count; + ClcGroup *topgroup = group; + + group->scanIndex = 0; + for (;;) { + if (group->scanIndex == group->cl.count) { + if (group == topgroup) + break; + group = group->parent; + } + else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP && (!visibleOnly || group->cl.items[group->scanIndex]->group->expanded)) { + group = group->cl.items[group->scanIndex]->group; + group->scanIndex = 0; + count += group->cl.count; + continue; + } + group->scanIndex++; + } + return count; +} + +static int __cdecl GroupSortProc(const void* p1, const void* p2) +{ + ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; + + return lstrcmpi(contact1[0]->szText, contact2[0]->szText); +} + +static int __cdecl ContactSortProc(const void* p1, const void* p2) +{ + ClcContact **contact1 = (ClcContact**)p1, **contact2 = (ClcContact**)p2; + + int result = cli.pfnCompareContacts(contact1[0], contact2[0]); + if (result) + return result; + //nothing to distinguish them, so make sure they stay in the same order + return (int)((INT_PTR) contact2[0]->hContact - (INT_PTR) contact1[0]->hContact); +} + +static void InsertionSort(ClcContact **pContactArray, int nArray, int (*CompareProc) (const void *, const void *)) +{ + int i, j; + ClcContact* testElement; + + for (i = 1; i < nArray; i++) { + if (CompareProc(&pContactArray[i - 1], &pContactArray[i]) > 0) { + testElement = pContactArray[i]; + for (j = i - 2; j >= 0; j--) + if (CompareProc(&pContactArray[j], &testElement) <= 0) + break; + j++; + memmove(&pContactArray[j + 1], &pContactArray[j], sizeof(void*) * (i - j)); + pContactArray[j] = testElement; + } + } +} + +static void SortGroup(struct ClcData *dat, ClcGroup *group, int useInsertionSort) +{ + int i, sortCount; + + for (i = group->cl.count - 1; i >= 0; i--) { + if (group->cl.items[i]->type == CLCIT_DIVIDER) { + mir_free(group->cl.items[i]); + List_Remove((SortedList*)&group->cl, i); + } + } + + for (i=0; i < group->cl.count; i++) + if (group->cl.items[i]->type != CLCIT_INFO) + break; + if (i > group->cl.count - 2) + return; + if (group->cl.items[i]->type == CLCIT_GROUP) { + if (dat->exStyle & CLS_EX_SORTGROUPSALPHA) { + for (sortCount = 0; i + sortCount < group->cl.count; sortCount++) + if (group->cl.items[i + sortCount]->type != CLCIT_GROUP) + break; + qsort(group->cl.items + i, sortCount, sizeof(void*), GroupSortProc); + i = i + sortCount; + } + for (; i < group->cl.count; i++) + if (group->cl.items[i]->type == CLCIT_CONTACT) + break; + if (group->cl.count - i < 2) + return; + } + for (sortCount = 0; i + sortCount < group->cl.count; sortCount++) + if (group->cl.items[i + sortCount]->type != CLCIT_CONTACT) + break; + if (useInsertionSort) + InsertionSort(group->cl.items + i, sortCount, ContactSortProc); + else + qsort(group->cl.items + i, sortCount, sizeof(void*), ContactSortProc); + if (dat->exStyle & CLS_EX_DIVIDERONOFF) { + int prevContactOnline = 0; + for (i=0; i < group->cl.count; i++) { + if (group->cl.items[i]->type != CLCIT_CONTACT) + continue; + if (group->cl.items[i]->flags & CONTACTF_ONLINE) + prevContactOnline = 1; + else { + if (prevContactOnline) { + i = cli.pfnAddItemToGroup(group, i); + group->cl.items[i]->type = CLCIT_DIVIDER; + lstrcpy(group->cl.items[i]->szText, TranslateT("Offline")); + } + break; + } + } + } +} + +void fnSortCLC(HWND hwnd, struct ClcData *dat, int useInsertionSort) +{ + ClcContact *selcontact; + ClcGroup *group = &dat->list, *selgroup; + MCONTACT hSelItem; + + if (dat->needsResort) { + if (cli.pfnGetRowByIndex(dat, dat->selection, &selcontact, NULL) == -1) + hSelItem = NULL; + else + hSelItem = (MCONTACT)cli.pfnContactToHItem(selcontact); + group->scanIndex = 0; + SortGroup(dat, group, useInsertionSort); + for (;;) { + if (group->scanIndex == group->cl.count) { + group = group->parent; + if (group == NULL) + break; + } + else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { + group = group->cl.items[group->scanIndex]->group; + group->scanIndex = 0; + SortGroup(dat, group, useInsertionSort); + continue; + } + group->scanIndex++; + } + if (hSelItem) + if (cli.pfnFindItem(hwnd, dat, hSelItem, &selcontact, &selgroup, NULL)) + dat->selection = cli.pfnGetRowsPriorTo(&dat->list, selgroup, List_IndexOf((SortedList*)&selgroup->cl, selcontact)); + + cli.pfnRecalcScrollBar(hwnd, dat); + } + dat->needsResort = 0; + cli.pfnInvalidateRect(hwnd, NULL, FALSE); +} + +struct SavedContactState_t +{ + MCONTACT hContact; + WORD iExtraImage[EXTRA_ICON_COUNT]; + int checked; +}; + +struct SavedGroupState_t +{ + int groupId, expanded; +}; + +struct SavedInfoState_t +{ + int parentId; + ClcContact contact; +}; + +void fnSaveStateAndRebuildList(HWND hwnd, struct ClcData *dat) +{ + NMCLISTCONTROL nm; + int i, j; + ClcGroup *group; + ClcContact *contact; + + cli.pfnHideInfoTip(hwnd, dat); + KillTimer(hwnd, TIMERID_INFOTIP); + KillTimer(hwnd, TIMERID_RENAME); + cli.pfnEndRename(hwnd, dat, 1); + + OBJLIST saveContact(10, NumericKeySortT); + OBJLIST saveGroup(100, NumericKeySortT); + OBJLIST saveInfo(10, NumericKeySortT); + + dat->needsResort = 1; + group = &dat->list; + group->scanIndex = 0; + for (;;) { + if (group->scanIndex == group->cl.count) { + group = group->parent; + if (group == NULL) + break; + } + else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { + group = group->cl.items[group->scanIndex]->group; + group->scanIndex = 0; + + SavedGroupState_t* p = new SavedGroupState_t; + p->groupId = group->groupId; + p->expanded = group->expanded; + saveGroup.insert(p); + continue; + } + else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT) { + SavedContactState_t* p = new SavedContactState_t; + p->hContact = group->cl.items[group->scanIndex]->hContact; + memcpy(p->iExtraImage, group->cl.items[group->scanIndex]->iExtraImage, sizeof(p->iExtraImage)); + p->checked = group->cl.items[group->scanIndex]->flags & CONTACTF_CHECKED; + saveContact.insert(p); + } + else if (group->cl.items[group->scanIndex]->type == CLCIT_INFO) { + SavedInfoState_t* p = new SavedInfoState_t; + p->parentId = (group->parent == NULL) ? -1 : group->groupId; + p->contact = *group->cl.items[group->scanIndex]; + saveInfo.insert(p); + } + group->scanIndex++; + } + + cli.pfnFreeGroup(&dat->list); + cli.pfnRebuildEntireList(hwnd, dat); + + group = &dat->list; + group->scanIndex = 0; + for (;;) { + if (group->scanIndex == group->cl.count) { + group = group->parent; + if (group == NULL) + break; + } + else if (group->cl.items[group->scanIndex]->type == CLCIT_GROUP) { + group = group->cl.items[group->scanIndex]->group; + group->scanIndex = 0; + + SavedGroupState_t tmp, *p; + tmp.groupId = group->groupId; + if ((p = saveGroup.find(&tmp)) != NULL) + group->expanded = p->expanded; + continue; + } + else if (group->cl.items[group->scanIndex]->type == CLCIT_CONTACT) { + SavedContactState_t tmp, *p; + tmp.hContact = group->cl.items[group->scanIndex]->hContact; + if ((p = saveContact.find(&tmp)) != NULL) { + memcpy(group->cl.items[group->scanIndex]->iExtraImage, p->iExtraImage, sizeof(p->iExtraImage)); + if (p->checked) + group->cl.items[group->scanIndex]->flags |= CONTACTF_CHECKED; + } + } + + group->scanIndex++; + } + + for (i=0; i < saveInfo.getCount(); i++) { + if (saveInfo[i].parentId == -1) + group = &dat->list; + else { + if (!cli.pfnFindItem(hwnd, dat, saveInfo[i].parentId | HCONTACT_ISGROUP, &contact, NULL, NULL)) + continue; + group = contact->group; + } + j = cli.pfnAddInfoItemToGroup(group, saveInfo[i].contact.flags, _T("")); + *group->cl.items[j] = saveInfo[i].contact; + } + + cli.pfnRecalculateGroupCheckboxes(hwnd, dat); + + cli.pfnRecalcScrollBar(hwnd, dat); + nm.hdr.code = CLN_LISTREBUILT; + nm.hdr.hwndFrom = hwnd; + nm.hdr.idFrom = GetDlgCtrlID(hwnd); + SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) & nm); +} diff --git a/src/modules/database/profilemanager.cpp b/src/modules/database/profilemanager.cpp index 5952c7e97b..82b44a090c 100644 --- a/src/modules/database/profilemanager.cpp +++ b/src/modules/database/profilemanager.cpp @@ -1,901 +1,900 @@ -/* - -Miranda NG: the free IM client for Microsoft* Windows* - -Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org), -Copyright (c) 2000-12 Miranda IM project, -all portions of this codebase are copyrighted to the people -listed in contributors.txt. - -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 "..\..\core\commonheaders.h" -#include "..\plugins\plugins.h" -#include "..\langpack\langpack.h" -#include "profilemanager.h" -#include - -void EnsureCheckerLoaded(bool); - -#define WM_INPUTCHANGED (WM_USER + 0x3000) -#define WM_FOCUSTEXTBOX (WM_USER + 0x3001) - -typedef BOOL (__cdecl *ENUMPROFILECALLBACK) (TCHAR *tszFullPath, TCHAR *profile, LPARAM lParam); - -struct DetailsPageInit -{ - int pageCount; - OPTIONSDIALOGPAGE *odp; -}; - -struct DetailsPageData -{ - DLGTEMPLATE *pTemplate; - HINSTANCE hInst; - DLGPROC dlgProc; - HWND hwnd; - int changed; -}; - -struct DlgProfData -{ - PROPSHEETHEADER *psh; - HWND hwndOK; - PROFILEMANAGERDATA *pd; - HANDLE hFileNotify; -}; - -struct DetailsData -{ - HINSTANCE hInstIcmp; - HFONT hBoldFont; - int pageCount; - int currentPage; - DetailsPageData *opd; - RECT rcDisplay; - DlgProfData *prof; -}; - -struct ProfileEnumData -{ - HWND hwnd; - TCHAR* szProfile; -}; - -void SetServiceModePlugin(pluginEntry *p); - -static void ThemeDialogBackground(HWND hwnd) -{ - EnableThemeDialogTexture(hwnd, ETDT_ENABLETAB); -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Profile creator - -static int findProfiles(TCHAR *szProfileDir, ENUMPROFILECALLBACK callback, LPARAM lParam) -{ - // find in Miranda NG profile subfolders - TCHAR searchspec[MAX_PATH]; - mir_sntprintf(searchspec, SIZEOF(searchspec), _T("%s\\*.*"), szProfileDir); - - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFile(searchspec, &ffd); - if (hFind == INVALID_HANDLE_VALUE) - return 0; - - do { - // find all subfolders except "." and ".." - if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && _tcscmp(ffd.cFileName, _T(".")) && _tcscmp(ffd.cFileName, _T(".."))) { - TCHAR buf[MAX_PATH], profile[MAX_PATH]; - mir_sntprintf(buf, SIZEOF(buf), _T("%s\\%s\\%s.dat"), szProfileDir, ffd.cFileName, ffd.cFileName); - if (_taccess(buf, 0) == 0) { - mir_sntprintf(profile, SIZEOF(profile), _T("%s.dat"), ffd.cFileName); - if (!callback(buf, profile, lParam)) - break; - } - } - } - while (FindNextFile(hFind, &ffd)); - - FindClose(hFind); - return 1; -} - -static int CreateProfile(TCHAR *profile, DATABASELINK *link, HWND hwndDlg) -{ - TCHAR buf[256]; - int err = 0; - // check if the file already exists - TCHAR *file = _tcsrchr(profile, '\\'); - if (file) file++; - if (_taccess(profile, 0) == 0) { - // file already exists! - mir_sntprintf(buf, SIZEOF(buf), - TranslateT("The profile '%s' already exists. Do you want to move it to the Recycle Bin?\n\nWARNING: The profile will be deleted if Recycle Bin is disabled.\nWARNING: A profile may contain confidential information and should be properly deleted."), - file); - if (MessageBox(hwndDlg, buf, TranslateT("The profile already exists"), MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2) != IDYES) - return 0; - - // move the file - SHFILEOPSTRUCT sf = {0}; - sf.wFunc = FO_DELETE; - sf.pFrom = buf; - sf.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO; - mir_sntprintf(buf, SIZEOF(buf), _T("%s\0"), profile); - if (SHFileOperation(&sf) != 0) { - mir_sntprintf(buf, SIZEOF(buf), TranslateT("Couldn't move '%s' to the Recycle Bin. Please select another profile name."), file); - MessageBox(0, buf, TranslateT("Problem moving profile"), MB_ICONINFORMATION|MB_OK); - return 0; - } - // now the file should be gone! - } - // ask the database to create the profile - CreatePathToFileT(profile); - if ((err = link->makeDatabase(profile)) != ERROR_SUCCESS) { - mir_sntprintf(buf, SIZEOF(buf), TranslateT("Unable to create the profile '%s', the error was %x"), file, err); - MessageBox(hwndDlg, buf, TranslateT("Problem creating profile"), MB_ICONERROR|MB_OK); - return 0; - } - - // the profile has been created! - g_bDbCreated = true; - return 1; -} - -static LRESULT CALLBACK ProfileNameValidate(HWND edit, UINT msg, WPARAM wParam, LPARAM lParam) -{ - if (msg == WM_CHAR) { - if (_tcschr(_T(".?/\\#' "), (TCHAR)wParam) != 0) - return 0; - PostMessage(GetParent(edit), WM_INPUTCHANGED, 0, 0); - } - return mir_callNextSubclass(edit, ProfileNameValidate, msg, wParam, lParam); -} - -static INT_PTR CALLBACK DlgProfileNew(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - DlgProfData *dat = (DlgProfData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); - dat = (DlgProfData*)lParam; - { - HWND hwndCombo = GetDlgItem(hwndDlg, IDC_PROFILEDRIVERS); - - // what, no plugins?! - if (arDbPlugins.getCount() == 0) { - EnableWindow(hwndCombo, FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILENAME), FALSE); - ShowWindow(GetDlgItem(hwndDlg, IDC_NODBDRIVERS), TRUE); - } - else { - for (int i = 0; i < arDbPlugins.getCount(); i++) { - DATABASELINK *p = arDbPlugins[i]; - LRESULT index = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)TranslateTS(p->szFullName)); - SendMessage(hwndCombo, CB_SETITEMDATA, index, (LPARAM)p); - } - } - - // default item - SendMessage(hwndCombo, CB_SETCURSEL, 0, 0); - - // subclass the profile name box - mir_subclassWindow(GetDlgItem(hwndDlg, IDC_PROFILENAME), ProfileNameValidate); - } - - // decide if there is a default profile name given in the INI and if it should be used - if (dat->pd->noProfiles || (shouldAutoCreate(dat->pd->ptszProfile) && _taccess(dat->pd->ptszProfile, 0))) { - TCHAR *profile = _tcsrchr(dat->pd->ptszProfile, '\\'); - if (profile) ++profile; - else profile = dat->pd->ptszProfile; - - TCHAR *p = _tcsrchr(profile, '.'); - TCHAR c = 0; - if (p) { c = *p; *p = 0; } - - SetDlgItemText(hwndDlg, IDC_PROFILENAME, profile); - if (c) *p = c; - } - - // focus on the textbox - PostMessage(hwndDlg, WM_FOCUSTEXTBOX, 0, 0); - return TRUE; - - case WM_FOCUSTEXTBOX: - SetFocus(GetDlgItem(hwndDlg, IDC_PROFILENAME)); - break; - - case WM_INPUTCHANGED: // when input in the edit box changes - SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); - EnableWindow(dat->hwndOK, GetWindowTextLength(GetDlgItem(hwndDlg, IDC_PROFILENAME)) > 0); - break; - - case WM_SHOWWINDOW: - if (wParam) { - SetWindowText(dat->hwndOK, TranslateT("&Create")); - SendMessage(hwndDlg, WM_INPUTCHANGED, 0, 0); - } - break; - - case WM_NOTIFY: - NMHDR *hdr = (NMHDR*)lParam; - if (hdr && hdr->code == PSN_APPLY && dat && IsWindowVisible(hwndDlg)) { - TCHAR szName[MAX_PATH]; - LRESULT curSel = SendDlgItemMessage(hwndDlg, IDC_PROFILEDRIVERS, CB_GETCURSEL, 0, 0); - if (curSel == CB_ERR) - break; // should never happen - - GetDlgItemText(hwndDlg, IDC_PROFILENAME, szName, SIZEOF(szName)); - if (szName[0] == 0) - break; - - // profile placed in "profile_name" subfolder - mir_sntprintf(dat->pd->ptszProfile, MAX_PATH, _T("%s\\%s\\%s.dat"), dat->pd->ptszProfileDir, szName, szName); - dat->pd->newProfile = 1; - dat->pd->dblink = (DATABASELINK *)SendDlgItemMessage(hwndDlg, IDC_PROFILEDRIVERS, CB_GETITEMDATA, (WPARAM)curSel, 0); - - if (CreateProfile(dat->pd->ptszProfile, dat->pd->dblink, hwndDlg) == 0) - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); - else - dat->pd->bRun = true; - } - break; - } - - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Profile selector - -BOOL EnumProfilesForList(TCHAR *tszFullPath, TCHAR *profile, LPARAM lParam) -{ - ProfileEnumData *ped = (ProfileEnumData*)lParam; - HWND hwndList = GetDlgItem(ped->hwnd, IDC_PROFILELIST); - - TCHAR sizeBuf[64]; - bool bFileLocked = true; - - TCHAR *p = _tcsrchr(profile, '.'); - _tcscpy(sizeBuf, _T("0 KB")); - if (p != NULL) *p = 0; - - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_IMAGE; - item.pszText = profile; - item.iItem = 0; - - struct _stat statbuf; - if (_tstat(tszFullPath, &statbuf) == 0) { - if (statbuf.st_size > 1000000) { - mir_sntprintf(sizeBuf, SIZEOF(sizeBuf), _T("%.3lf"), (double)statbuf.st_size / 1048576.0); - _tcscpy(sizeBuf + 5, _T(" MB")); - } - else { - mir_sntprintf(sizeBuf, SIZEOF(sizeBuf), _T("%.3lf"), (double)statbuf.st_size / 1024.0); - _tcscpy(sizeBuf + 5, _T(" KB")); - } - bFileLocked = !fileExist(tszFullPath); - } - - DATABASELINK *dblink; - bool bNeedConversion = false; - switch (touchDatabase(tszFullPath, &dblink)) { - case ERROR_SUCCESS: - item.iImage = bFileLocked; - break; - - case EGROKPRF_OBSOLETE: - bNeedConversion = true; - item.iImage = 2; - break; - - default: - item.iImage = 3; - } - - int iItem = SendMessage(hwndList, LVM_INSERTITEM, 0, (LPARAM)&item); - if (lstrcmpi(ped->szProfile, tszFullPath) == 0) - ListView_SetItemState(hwndList, iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); - - item.iItem = iItem; - item.iSubItem = 2; - item.pszText = sizeBuf; - SendMessage(hwndList, LVM_SETITEMTEXT, iItem, (LPARAM)&item); - - if (dblink != NULL) { - if (bFileLocked) // file locked - item.pszText = TranslateT(""); - else - item.pszText = TranslateTS(dblink->szFullName); - } - else item.pszText = TranslateT(""); - - item.iSubItem = 1; - SendMessage(hwndList, LVM_SETITEMTEXT, iItem, (LPARAM)&item); - return TRUE; -} - -void CheckProfile(HWND hwndList, int iItem, DlgProfData *dat) -{ - if (iItem < 0) - return; - - TCHAR profile[MAX_PATH], fullName[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_IMAGE; - item.iItem = iItem; - item.pszText = profile; - item.cchTextMax = SIZEOF(profile); - if (!ListView_GetItem(hwndList, &item)) - return; - - mir_sntprintf(fullName, SIZEOF(fullName), _T("%s\\%s\\%s.dat"), dat->pd->ptszProfileDir, profile, profile); - CallService(MS_DB_CHECKPROFILE, (WPARAM)fullName, item.iImage == 2); -} - -void DeleteProfile(HWND hwndList, int iItem, DlgProfData *dat) -{ - if (iItem < 0) - return; - - TCHAR profile[MAX_PATH], profilef[MAX_PATH * 2]; - - LVITEM item = { 0 }; - item.mask = LVIF_TEXT; - item.iItem = iItem; - item.pszText = profile; - item.cchTextMax = SIZEOF(profile); - if (!ListView_GetItem(hwndList, &item)) - return; - - mir_sntprintf(profilef, SIZEOF(profilef), TranslateT("Are you sure you want to remove profile \"%s\"?"), profile); - if (IDYES != MessageBox(NULL, profilef, _T("Miranda NG"), MB_YESNO | MB_TASKMODAL | MB_ICONWARNING)) - return; - - mir_sntprintf(profilef, SIZEOF(profilef), _T("%s\\%s%c"), dat->pd->ptszProfileDir, profile, 0); - - SHFILEOPSTRUCT sf = { 0 }; - sf.wFunc = FO_DELETE; - sf.pFrom = profilef; - sf.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_ALLOWUNDO; - SHFileOperation(&sf); - ListView_DeleteItem(hwndList, item.iItem); -} - -static void CheckRun(HWND hwndDlg, int uMsg) -{ - DlgProfData *dat = (DlgProfData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - if (dat == NULL) - return; - - HWND hwndList = GetDlgItem(hwndDlg, IDC_PROFILELIST); - EnableWindow(dat->hwndOK, ListView_GetSelectedCount(hwndList) == 1); - - TCHAR profile[MAX_PATH]; - LVITEM item = { 0 }; - item.mask = LVIF_TEXT | LVIF_IMAGE; - item.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED | LVNI_ALL); - item.pszText = profile; - item.cchTextMax = SIZEOF(profile); - if (!ListView_GetItem(hwndList, &item)) - return; - - switch(item.iImage) { - case 3: - EnableWindow(dat->hwndOK, false); - return; - - case 2: - SetWindowText(dat->hwndOK, TranslateT("&Convert")); - dat->pd->bRun = false; - break; - - default: - SetWindowText(dat->hwndOK, TranslateT("&Run")); - dat->pd->bRun = true; - } - - // profile is placed in "profile_name" subfolder - - TCHAR tmpPath[MAX_PATH]; - mir_sntprintf(tmpPath, SIZEOF(tmpPath), _T("%s\\%s.dat"), dat->pd->ptszProfileDir, profile); - if (_taccess(tmpPath, 2)) - mir_sntprintf(dat->pd->ptszProfile, MAX_PATH, _T("%s\\%s\\%s.dat"), dat->pd->ptszProfileDir, profile, profile); - else - _tcsncpy_s(dat->pd->ptszProfile, MAX_PATH, tmpPath, _TRUNCATE); - - if (uMsg == NM_DBLCLK) - EndDialog(GetParent(hwndDlg), 1); -} - -static void ExecuteMenu(HWND hwndDlg, LPARAM lParam) -{ - HWND hwndList = GetDlgItem(hwndDlg, IDC_PROFILELIST); - DlgProfData *dat = (DlgProfData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - LVHITTESTINFO lvht = { 0 }; - lvht.pt.x = GET_X_LPARAM(lParam); - lvht.pt.y = GET_Y_LPARAM(lParam); - ScreenToClient(hwndList, &lvht.pt); - - if (ListView_HitTest(hwndList, &lvht) == -1) - return; - - if (lvht.iItem == -1) - return; - - LVITEM tvi = { 0 }; - tvi.mask = LVIF_IMAGE; - tvi.iItem = lvht.iItem; - if (!ListView_GetItem(hwndList, &tvi)) - return; - - bool bConvert = (tvi.iImage == 2); - - lvht.pt.x = GET_X_LPARAM(lParam); - lvht.pt.y = GET_Y_LPARAM(lParam); - - HMENU hMenu = CreatePopupMenu(); - if (tvi.iImage < 2) { - AppendMenu(hMenu, MF_STRING, 1, TranslateT("Run")); - AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); - } - if (tvi.iImage != 3 && ServiceExists(MS_DB_CHECKPROFILE)) { - if (bConvert) - AppendMenu(hMenu, MF_STRING, 2, TranslateT("Convert database")); - else - AppendMenu(hMenu, MF_STRING, 2, TranslateT("Check database")); - AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); - } - AppendMenu(hMenu, MF_STRING, 3, TranslateT("Delete")); - int index = TrackPopupMenu(hMenu, TPM_RETURNCMD, lvht.pt.x, lvht.pt.y, 0, hwndDlg, NULL); - switch (index) { - case 1: - SendMessage(GetParent(hwndDlg), WM_COMMAND, IDOK, 0); - break; - - case 2: - CheckProfile(hwndList, lvht.iItem, dat); - break; - - case 3: - DeleteProfile(hwndList, lvht.iItem, dat); - break; - } - DestroyMenu(hMenu); -} - -static INT_PTR CALLBACK DlgProfileSelect(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - DlgProfData *dat = (DlgProfData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - HWND hwndList = GetDlgItem(hwndDlg, IDC_PROFILELIST); - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - dat = (DlgProfData*)lParam; - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - - // set columns - LVCOLUMN col; - col.mask = LVCF_TEXT | LVCF_WIDTH; - col.pszText = TranslateT("Profile"); - col.cx = 100; - ListView_InsertColumn(hwndList, 0, &col); - - col.pszText = TranslateT("Driver"); - col.cx = 150 - GetSystemMetrics(SM_CXVSCROLL); - ListView_InsertColumn(hwndList, 1, &col); - - col.pszText = TranslateT("Size"); - col.cx = 60; - ListView_InsertColumn(hwndList, 2, &col); - - // icons - HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 2, 1); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_USERDETAILS)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_DELETE)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MWARNING)); - ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MFATAL)); - - // LV will destroy the image list - SetWindowLongPtr(hwndList, GWL_STYLE, GetWindowLongPtr(hwndList, GWL_STYLE) | LVS_SORTASCENDING); - ListView_SetImageList(hwndList, hImgList, LVSIL_SMALL); - ListView_SetExtendedListViewStyle(hwndList, - ListView_GetExtendedListViewStyle(hwndList) | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT); - - // find all the profiles - ProfileEnumData ped = { hwndDlg, dat->pd->ptszProfile }; - findProfiles(dat->pd->ptszProfileDir, EnumProfilesForList, (LPARAM)&ped); - PostMessage(hwndDlg, WM_FOCUSTEXTBOX, 0, 0); - - dat->hFileNotify = FindFirstChangeNotification(dat->pd->ptszProfileDir, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE); - if (dat->hFileNotify != INVALID_HANDLE_VALUE) - SetTimer(hwndDlg, 0, 1200, NULL); - return TRUE; - } - - case WM_DESTROY: - KillTimer(hwndDlg, 0); - FindCloseChangeNotification(dat->hFileNotify); - break; - - case WM_TIMER: - if (WaitForSingleObject(dat->hFileNotify, 0) == WAIT_OBJECT_0) { - ListView_DeleteAllItems(hwndList); - ProfileEnumData ped = { hwndDlg, dat->pd->ptszProfile }; - findProfiles(dat->pd->ptszProfileDir, EnumProfilesForList, (LPARAM)&ped); - FindNextChangeNotification(dat->hFileNotify); - } - break; - - case WM_FOCUSTEXTBOX: - SetFocus(hwndList); - if (dat->pd->ptszProfile[0] == 0 || ListView_GetSelectedCount(hwndList) == 0) - ListView_SetItemState(hwndList, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); - break; - - case WM_SHOWWINDOW: - if (wParam) - CheckRun(hwndDlg, 0); - break; - - case WM_CONTEXTMENU: - ExecuteMenu(hwndDlg, lParam); - break; - - case WM_NOTIFY: - LPNMHDR hdr = (LPNMHDR)lParam; - if (hdr == NULL) - break; - - if (hdr->idFrom == 0) { - CheckRun(hwndDlg, 0); - break; - } - - if (hdr->idFrom == IDC_PROFILELIST) { - switch (hdr->code) { - case LVN_ITEMCHANGED: - case NM_DBLCLK: - CheckRun(hwndDlg, hdr->code); - return TRUE; - - case LVN_KEYDOWN: - if (((LPNMLVKEYDOWN)lParam)->wVKey == VK_DELETE) - DeleteProfile(hwndList, ListView_GetNextItem(hwndList, -1, LVNI_SELECTED | LVNI_ALL), dat); - break; - - case LVN_GETINFOTIP: - NMLVGETINFOTIP *pInfoTip = (NMLVGETINFOTIP *)lParam; - if (pInfoTip != NULL) { - TCHAR profilename[MAX_PATH], tszFullPath[MAX_PATH]; - struct _stat statbuf; - ListView_GetItemText(hwndList, pInfoTip->iItem, 0, profilename, MAX_PATH); - mir_sntprintf(tszFullPath, SIZEOF(tszFullPath), _T("%s\\%s\\%s.dat"), dat->pd->ptszProfileDir, profilename, profilename); - _tstat(tszFullPath, &statbuf); - mir_sntprintf(pInfoTip->pszText, pInfoTip->cchTextMax, _T("%s\n%s: %s\n%s: %s"), tszFullPath, TranslateT("Created"), rtrimt(NEWTSTR_ALLOCA(_tctime(&statbuf.st_ctime))), TranslateT("Modified"), rtrimt(NEWTSTR_ALLOCA(_tctime(&statbuf.st_mtime)))); - } - } - } - break; - } - - return FALSE; -} - -///////////////////////////////////////////////////////////////////////////////////////// -// Tab manager + its envelope - -static INT_PTR CALLBACK DlgProfileManager(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) -{ - DetailsData *dat = (DetailsData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); - - switch (msg) { - case WM_INITDIALOG: - TranslateDialogDefault(hwndDlg); - { - DlgProfData *prof = (DlgProfData*)lParam; - PROPSHEETHEADER *psh = prof->psh; - SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadImage(hInst, MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0)); - SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadImage(hInst, MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0)); - dat = (DetailsData*)mir_alloc(sizeof(DetailsData)); - dat->prof = prof; - prof->hwndOK = GetDlgItem(hwndDlg, IDOK); - EnableWindow(prof->hwndOK, FALSE); - SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); - - TCHAR buf[512]; - mir_sntprintf(buf, SIZEOF(buf), _T("%s\n%s"), TranslateT("Miranda NG profile manager"), TranslateT("Manage your Miranda NG profile")); - SetDlgItemText(hwndDlg, IDC_NAME, buf); - - dat->currentPage = 0; - dat->pageCount = psh->nPages; - dat->opd = (DetailsPageData*)mir_calloc(sizeof(DetailsPageData)*dat->pageCount); - OPTIONSDIALOGPAGE *odp = (OPTIONSDIALOGPAGE*)psh->ppsp; - - TCITEM tci; - tci.mask = TCIF_TEXT; - for (int i = 0; i < dat->pageCount; i++) { - dat->opd[i].pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(odp[i].hInstance, FindResourceA(odp[i].hInstance, odp[i].pszTemplate, MAKEINTRESOURCEA(5)))); - dat->opd[i].dlgProc = odp[i].pfnDlgProc; - dat->opd[i].hInst = odp[i].hInstance; - dat->opd[i].hwnd = NULL; - dat->opd[i].changed = 0; - tci.pszText = (TCHAR*)odp[i].ptszTitle; - if (dat->prof->pd->noProfiles || shouldAutoCreate(dat->prof->pd->ptszProfile)) - dat->currentPage = 1; - TabCtrl_InsertItem(GetDlgItem(hwndDlg, IDC_TABS), i, &tci); - } - - GetWindowRect(GetDlgItem(hwndDlg, IDC_TABS), &dat->rcDisplay); - TabCtrl_AdjustRect(GetDlgItem(hwndDlg, IDC_TABS), FALSE, &dat->rcDisplay); - - POINT pt = { 0, 0 }; - ClientToScreen(hwndDlg, &pt); - OffsetRect(&dat->rcDisplay, -pt.x, -pt.y); - - TabCtrl_SetCurSel(GetDlgItem(hwndDlg, IDC_TABS), dat->currentPage); - dat->opd[dat->currentPage].hwnd = CreateDialogIndirectParam(dat->opd[dat->currentPage].hInst, dat->opd[dat->currentPage].pTemplate, hwndDlg, dat->opd[dat->currentPage].dlgProc, (LPARAM)dat->prof); - ThemeDialogBackground(dat->opd[dat->currentPage].hwnd); - SetWindowPos(dat->opd[dat->currentPage].hwnd, HWND_TOP, dat->rcDisplay.left, dat->rcDisplay.top, 0, 0, SWP_NOSIZE); - - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); - - // service mode combobox - if (servicePlugins.getCount() == 0) { - ShowWindow(GetDlgItem(hwndDlg, IDC_SM_LABEL), FALSE); - ShowWindow(GetDlgItem(hwndDlg, IDC_SM_COMBO), FALSE); - } - else { - HWND hwndCombo = GetDlgItem(hwndDlg, IDC_SM_COMBO); - LRESULT index = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)_T("")); - SendMessage(hwndCombo, CB_SETITEMDATA, index, (LPARAM)-1); - SendMessage(hwndCombo, CB_SETCURSEL, 0, 0); - for (int i = 0; i < servicePlugins.getCount(); i++) { - pluginEntry *p = servicePlugins[i]; - index = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)TranslateTS(p->pluginname)); - SendMessage(hwndCombo, CB_SETITEMDATA, index, (LPARAM)i); - } - } - ShowWindow(dat->opd[dat->currentPage].hwnd, SW_SHOW); - } - return TRUE; - - case WM_CTLCOLORSTATIC: - switch (GetDlgCtrlID((HWND)lParam)) { - case IDC_WHITERECT: - SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); - return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); - } - break; - - case PSM_CHANGED: - dat->opd[dat->currentPage].changed = 1; - return TRUE; - - case PSM_FORCECHANGED: - { - PSHNOTIFY pshn; - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - for (int i = 0; i < dat->pageCount; i++) { - pshn.hdr.hwndFrom = dat->opd[i].hwnd; - if (dat->opd[i].hwnd != NULL) - SendMessage(dat->opd[i].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); - } - } - break; - - case WM_NOTIFY: - switch (wParam) { - case IDC_TABS: - switch (((LPNMHDR)lParam)->code) { - case TCN_SELCHANGING: - if (dat->currentPage != -1 && dat->opd[dat->currentPage].hwnd != NULL) { - PSHNOTIFY pshn; - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - if (SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn)) { - SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); - return TRUE; - } - } - break; - - case TCN_SELCHANGE: - if (dat->currentPage != -1 && dat->opd[dat->currentPage].hwnd != NULL) - ShowWindow(dat->opd[dat->currentPage].hwnd, SW_HIDE); - - dat->currentPage = TabCtrl_GetCurSel(GetDlgItem(hwndDlg, IDC_TABS)); - if (dat->currentPage != -1) { - if (dat->opd[dat->currentPage].hwnd == NULL) { - PSHNOTIFY pshn; - dat->opd[dat->currentPage].hwnd = CreateDialogIndirectParam(dat->opd[dat->currentPage].hInst, dat->opd[dat->currentPage].pTemplate, hwndDlg, dat->opd[dat->currentPage].dlgProc, (LPARAM)dat->prof); - ThemeDialogBackground(dat->opd[dat->currentPage].hwnd); - SetWindowPos(dat->opd[dat->currentPage].hwnd, HWND_TOP, dat->rcDisplay.left, dat->rcDisplay.top, 0, 0, SWP_NOSIZE); - pshn.hdr.code = PSN_INFOCHANGED; - pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); - } - ShowWindow(dat->opd[dat->currentPage].hwnd, SW_SHOW); - } - break; - } - break; - } - break; - - case WM_COMMAND: - switch (LOWORD(wParam)) { - case IDCANCEL: - { - PSHNOTIFY pshn; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - pshn.hdr.code = PSN_RESET; - for (int i = 0; i < dat->pageCount; i++) { - if (dat->opd[i].hwnd == NULL || !dat->opd[i].changed) - continue; - - pshn.hdr.hwndFrom = dat->opd[i].hwnd; - SendMessage(dat->opd[i].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); - } - EndDialog(hwndDlg, 0); - } - break; - - case IDC_REMOVE: - if (!dat->prof->pd->noProfiles) { - HWND hwndList = GetDlgItem(dat->opd[0].hwnd, IDC_PROFILELIST); - DeleteProfile(hwndList, ListView_GetNextItem(hwndList, -1, LVNI_SELECTED | LVNI_ALL), dat->prof); - } - break; - - case IDOK: - PSHNOTIFY pshn; - pshn.hdr.idFrom = 0; - pshn.lParam = 0; - if (dat->currentPage != -1) { - pshn.hdr.code = PSN_KILLACTIVE; - pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd; - if (SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn)) - break; - } - - pshn.hdr.code = PSN_APPLY; - for (int i = 0; i < dat->pageCount; i++) { - if (dat->opd[i].hwnd == NULL || !dat->opd[i].changed) - continue; - - pshn.hdr.hwndFrom = dat->opd[i].hwnd; - SendMessage(dat->opd[i].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); - if (GetWindowLongPtr(dat->opd[i].hwnd, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { - TabCtrl_SetCurSel(GetDlgItem(hwndDlg, IDC_TABS), i); - if (dat->currentPage != -1) - ShowWindow(dat->opd[dat->currentPage].hwnd, SW_HIDE); - dat->currentPage = i; - ShowWindow(dat->opd[dat->currentPage].hwnd, SW_SHOW); - return 0; - } - } - EndDialog(hwndDlg, 1); - } - break; - - case WM_DESTROY: - LRESULT curSel = SendDlgItemMessage(hwndDlg, IDC_SM_COMBO, CB_GETCURSEL, 0, 0); - if (curSel != CB_ERR) { - int idx = SendDlgItemMessage(hwndDlg, IDC_SM_COMBO, CB_GETITEMDATA, (WPARAM)curSel, 0); - if (idx != CB_ERR) - SetServiceModePlugin(servicePlugins[idx]); - } - - DestroyIcon((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, 0)); - DestroyIcon((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_BIG, 0)); - DeleteObject(dat->hBoldFont); - - for (int i = 0; i < dat->pageCount; i++) - if (dat->opd[i].hwnd != NULL) - DestroyWindow(dat->opd[i].hwnd); - - mir_free(dat->opd); - mir_free(dat); - break; - } - return FALSE; -} - -static int AddProfileManagerPage(struct DetailsPageInit *opi, OPTIONSDIALOGPAGE *odp) -{ - if (odp->cbSize != sizeof(OPTIONSDIALOGPAGE)) - return 1; - - opi->odp = (OPTIONSDIALOGPAGE*)mir_realloc(opi->odp, sizeof(OPTIONSDIALOGPAGE)*(opi->pageCount + 1)); - - OPTIONSDIALOGPAGE *p = opi->odp + opi->pageCount++; - p->cbSize = sizeof(OPTIONSDIALOGPAGE); - p->hInstance = odp->hInstance; - p->pfnDlgProc = odp->pfnDlgProc; - p->position = odp->position; - p->ptszTitle = Langpack_PcharToTchar(odp->pszTitle); - p->pszGroup = NULL; - p->groupPosition = odp->groupPosition; - p->hGroupIcon = odp->hGroupIcon; - p->hIcon = odp->hIcon; - if ((DWORD_PTR)odp->pszTemplate & 0xFFFF0000) - p->pszTemplate = mir_strdup(odp->pszTemplate); - else - p->pszTemplate = odp->pszTemplate; - return 0; -} - -int getProfileManager(PROFILEMANAGERDATA *pd) -{ - EnsureCheckerLoaded(true); - - DetailsPageInit opi = { 0 }; - - OPTIONSDIALOGPAGE odp = { 0 }; - odp.cbSize = sizeof(odp); - - odp.pszTitle = LPGEN("My profiles"); - odp.pfnDlgProc = DlgProfileSelect; - odp.pszTemplate = MAKEINTRESOURCEA(IDD_PROFILE_SELECTION); - odp.hInstance = hInst; - AddProfileManagerPage(&opi, &odp); - - odp.pszTitle = LPGEN("New profile"); - odp.pszTemplate = MAKEINTRESOURCEA(IDD_PROFILE_NEW); - odp.pfnDlgProc = DlgProfileNew; - AddProfileManagerPage(&opi, &odp); - - PROPSHEETHEADER psh = { 0 }; - psh.dwSize = sizeof(psh); - psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW; - psh.nPages = opi.pageCount; - psh.ppsp = (PROPSHEETPAGE*)opi.odp; - - DlgProfData prof; - prof.pd = pd; - prof.psh = &psh; - int rc = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_PROFILEMANAGER), NULL, DlgProfileManager, (LPARAM)&prof); - if (rc != -1) - for (int i = 0; i < opi.pageCount; i++) { - mir_free((char*)opi.odp[i].pszTitle); - mir_free(opi.odp[i].pszGroup); - if ((DWORD_PTR)opi.odp[i].pszTemplate & 0xFFFF0000) - mir_free((char*)opi.odp[i].pszTemplate); - } - - if (opi.odp != NULL) - mir_free(opi.odp); - - return rc; -} +/* + +Miranda NG: the free IM client for Microsoft* Windows* + +Copyright (c) 2012-14 Miranda NG project (http://miranda-ng.org), +Copyright (c) 2000-12 Miranda IM project, +all portions of this codebase are copyrighted to the people +listed in contributors.txt. + +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 "..\..\core\commonheaders.h" +#include "..\plugins\plugins.h" +#include "..\langpack\langpack.h" +#include "profilemanager.h" +#include + +void EnsureCheckerLoaded(bool); + +#define WM_INPUTCHANGED (WM_USER + 0x3000) +#define WM_FOCUSTEXTBOX (WM_USER + 0x3001) + +typedef BOOL (__cdecl *ENUMPROFILECALLBACK) (TCHAR *tszFullPath, TCHAR *profile, LPARAM lParam); + +struct DetailsPageInit +{ + int pageCount; + OPTIONSDIALOGPAGE *odp; +}; + +struct DetailsPageData +{ + DLGTEMPLATE *pTemplate; + HINSTANCE hInst; + DLGPROC dlgProc; + HWND hwnd; + int changed; +}; + +struct DlgProfData +{ + PROPSHEETHEADER *psh; + HWND hwndOK; + PROFILEMANAGERDATA *pd; + HANDLE hFileNotify; +}; + +struct DetailsData +{ + HINSTANCE hInstIcmp; + HFONT hBoldFont; + int pageCount; + int currentPage; + DetailsPageData *opd; + RECT rcDisplay; + DlgProfData *prof; +}; + +struct ProfileEnumData +{ + HWND hwnd; + TCHAR* szProfile; +}; + +void SetServiceModePlugin(pluginEntry *p); + +static void ThemeDialogBackground(HWND hwnd) +{ + EnableThemeDialogTexture(hwnd, ETDT_ENABLETAB); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Profile creator + +static int findProfiles(TCHAR *szProfileDir, ENUMPROFILECALLBACK callback, LPARAM lParam) +{ + // find in Miranda NG profile subfolders + TCHAR searchspec[MAX_PATH]; + mir_sntprintf(searchspec, SIZEOF(searchspec), _T("%s\\*.*"), szProfileDir); + + WIN32_FIND_DATA ffd; + HANDLE hFind = FindFirstFile(searchspec, &ffd); + if (hFind == INVALID_HANDLE_VALUE) + return 0; + + do { + // find all subfolders except "." and ".." + if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && _tcscmp(ffd.cFileName, _T(".")) && _tcscmp(ffd.cFileName, _T(".."))) { + TCHAR buf[MAX_PATH], profile[MAX_PATH]; + mir_sntprintf(buf, SIZEOF(buf), _T("%s\\%s\\%s.dat"), szProfileDir, ffd.cFileName, ffd.cFileName); + if (_taccess(buf, 0) == 0) { + mir_sntprintf(profile, SIZEOF(profile), _T("%s.dat"), ffd.cFileName); + if (!callback(buf, profile, lParam)) + break; + } + } + } + while (FindNextFile(hFind, &ffd)); + + FindClose(hFind); + return 1; +} + +static int CreateProfile(TCHAR *profile, DATABASELINK *link, HWND hwndDlg) +{ + TCHAR buf[256]; + int err = 0; + // check if the file already exists + TCHAR *file = _tcsrchr(profile, '\\'); + if (file) file++; + if (_taccess(profile, 0) == 0) { + // file already exists! + mir_sntprintf(buf, SIZEOF(buf), + TranslateT("The profile '%s' already exists. Do you want to move it to the Recycle Bin?\n\nWARNING: The profile will be deleted if Recycle Bin is disabled.\nWARNING: A profile may contain confidential information and should be properly deleted."), + file); + if (MessageBox(hwndDlg, buf, TranslateT("The profile already exists"), MB_ICONQUESTION|MB_YESNO|MB_DEFBUTTON2) != IDYES) + return 0; + + // move the file + SHFILEOPSTRUCT sf = {0}; + sf.wFunc = FO_DELETE; + sf.pFrom = buf; + sf.fFlags = FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_SILENT | FOF_ALLOWUNDO; + mir_sntprintf(buf, SIZEOF(buf), _T("%s\0"), profile); + if (SHFileOperation(&sf) != 0) { + mir_sntprintf(buf, SIZEOF(buf), TranslateT("Couldn't move '%s' to the Recycle Bin. Please select another profile name."), file); + MessageBox(0, buf, TranslateT("Problem moving profile"), MB_ICONINFORMATION|MB_OK); + return 0; + } + // now the file should be gone! + } + // ask the database to create the profile + CreatePathToFileT(profile); + if ((err = link->makeDatabase(profile)) != ERROR_SUCCESS) { + mir_sntprintf(buf, SIZEOF(buf), TranslateT("Unable to create the profile '%s', the error was %x"), file, err); + MessageBox(hwndDlg, buf, TranslateT("Problem creating profile"), MB_ICONERROR|MB_OK); + return 0; + } + + // the profile has been created! + g_bDbCreated = true; + return 1; +} + +static LRESULT CALLBACK ProfileNameValidate(HWND edit, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_CHAR) { + if (_tcschr(_T(".?/\\#' "), (TCHAR)wParam) != 0) + return 0; + PostMessage(GetParent(edit), WM_INPUTCHANGED, 0, 0); + } + return mir_callNextSubclass(edit, ProfileNameValidate, msg, wParam, lParam); +} + +static INT_PTR CALLBACK DlgProfileNew(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + DlgProfData *dat = (DlgProfData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, lParam); + dat = (DlgProfData*)lParam; + { + HWND hwndCombo = GetDlgItem(hwndDlg, IDC_PROFILEDRIVERS); + + // what, no plugins?! + if (arDbPlugins.getCount() == 0) { + EnableWindow(hwndCombo, FALSE); + EnableWindow(GetDlgItem(hwndDlg, IDC_PROFILENAME), FALSE); + ShowWindow(GetDlgItem(hwndDlg, IDC_NODBDRIVERS), TRUE); + } + else { + for (int i = 0; i < arDbPlugins.getCount(); i++) { + DATABASELINK *p = arDbPlugins[i]; + LRESULT index = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)TranslateTS(p->szFullName)); + SendMessage(hwndCombo, CB_SETITEMDATA, index, (LPARAM)p); + } + } + + // default item + SendMessage(hwndCombo, CB_SETCURSEL, 0, 0); + + // subclass the profile name box + mir_subclassWindow(GetDlgItem(hwndDlg, IDC_PROFILENAME), ProfileNameValidate); + } + + // decide if there is a default profile name given in the INI and if it should be used + if (dat->pd->noProfiles || (shouldAutoCreate(dat->pd->ptszProfile) && _taccess(dat->pd->ptszProfile, 0))) { + TCHAR *profile = _tcsrchr(dat->pd->ptszProfile, '\\'); + if (profile) ++profile; + else profile = dat->pd->ptszProfile; + + TCHAR *p = _tcsrchr(profile, '.'); + TCHAR c = 0; + if (p) { c = *p; *p = 0; } + + SetDlgItemText(hwndDlg, IDC_PROFILENAME, profile); + if (c) *p = c; + } + + // focus on the textbox + PostMessage(hwndDlg, WM_FOCUSTEXTBOX, 0, 0); + return TRUE; + + case WM_FOCUSTEXTBOX: + SetFocus(GetDlgItem(hwndDlg, IDC_PROFILENAME)); + break; + + case WM_INPUTCHANGED: // when input in the edit box changes + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + EnableWindow(dat->hwndOK, GetWindowTextLength(GetDlgItem(hwndDlg, IDC_PROFILENAME)) > 0); + break; + + case WM_SHOWWINDOW: + if (wParam) { + SetWindowText(dat->hwndOK, TranslateT("&Create")); + SendMessage(hwndDlg, WM_INPUTCHANGED, 0, 0); + } + break; + + case WM_NOTIFY: + NMHDR *hdr = (NMHDR*)lParam; + if (hdr && hdr->code == PSN_APPLY && dat && IsWindowVisible(hwndDlg)) { + TCHAR szName[MAX_PATH]; + LRESULT curSel = SendDlgItemMessage(hwndDlg, IDC_PROFILEDRIVERS, CB_GETCURSEL, 0, 0); + if (curSel == CB_ERR) + break; // should never happen + + GetDlgItemText(hwndDlg, IDC_PROFILENAME, szName, SIZEOF(szName)); + if (szName[0] == 0) + break; + + // profile placed in "profile_name" subfolder + mir_sntprintf(dat->pd->ptszProfile, MAX_PATH, _T("%s\\%s\\%s.dat"), dat->pd->ptszProfileDir, szName, szName); + dat->pd->newProfile = 1; + dat->pd->dblink = (DATABASELINK *)SendDlgItemMessage(hwndDlg, IDC_PROFILEDRIVERS, CB_GETITEMDATA, (WPARAM)curSel, 0); + + if (CreateProfile(dat->pd->ptszProfile, dat->pd->dblink, hwndDlg) == 0) + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, PSNRET_INVALID_NOCHANGEPAGE); + else + dat->pd->bRun = true; + } + break; + } + + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Profile selector + +BOOL EnumProfilesForList(TCHAR *tszFullPath, TCHAR *profile, LPARAM lParam) +{ + ProfileEnumData *ped = (ProfileEnumData*)lParam; + HWND hwndList = GetDlgItem(ped->hwnd, IDC_PROFILELIST); + + TCHAR sizeBuf[64]; + bool bFileLocked = true; + + TCHAR *p = _tcsrchr(profile, '.'); + _tcscpy(sizeBuf, _T("0 KB")); + if (p != NULL) *p = 0; + + LVITEM item = { 0 }; + item.mask = LVIF_TEXT | LVIF_IMAGE; + item.pszText = profile; + item.iItem = 0; + + struct _stat statbuf; + if (_tstat(tszFullPath, &statbuf) == 0) { + if (statbuf.st_size > 1000000) { + mir_sntprintf(sizeBuf, SIZEOF(sizeBuf), _T("%.3lf"), (double)statbuf.st_size / 1048576.0); + _tcscpy(sizeBuf + 5, _T(" MB")); + } + else { + mir_sntprintf(sizeBuf, SIZEOF(sizeBuf), _T("%.3lf"), (double)statbuf.st_size / 1024.0); + _tcscpy(sizeBuf + 5, _T(" KB")); + } + bFileLocked = !fileExist(tszFullPath); + } + + DATABASELINK *dblink; + bool bNeedConversion = false; + switch (touchDatabase(tszFullPath, &dblink)) { + case ERROR_SUCCESS: + item.iImage = bFileLocked; + break; + + case EGROKPRF_OBSOLETE: + bNeedConversion = true; + item.iImage = 2; + break; + + default: + item.iImage = 3; + } + + int iItem = SendMessage(hwndList, LVM_INSERTITEM, 0, (LPARAM)&item); + if (lstrcmpi(ped->szProfile, tszFullPath) == 0) + ListView_SetItemState(hwndList, iItem, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + + item.iItem = iItem; + item.iSubItem = 2; + item.pszText = sizeBuf; + SendMessage(hwndList, LVM_SETITEMTEXT, iItem, (LPARAM)&item); + + if (dblink != NULL) { + if (bFileLocked) // file locked + item.pszText = TranslateT(""); + else + item.pszText = TranslateTS(dblink->szFullName); + } + else item.pszText = TranslateT(""); + + item.iSubItem = 1; + SendMessage(hwndList, LVM_SETITEMTEXT, iItem, (LPARAM)&item); + return TRUE; +} + +void CheckProfile(HWND hwndList, int iItem, DlgProfData *dat) +{ + if (iItem < 0) + return; + + TCHAR profile[MAX_PATH], fullName[MAX_PATH]; + LVITEM item = { 0 }; + item.mask = LVIF_TEXT | LVIF_IMAGE; + item.iItem = iItem; + item.pszText = profile; + item.cchTextMax = SIZEOF(profile); + if (!ListView_GetItem(hwndList, &item)) + return; + + mir_sntprintf(fullName, SIZEOF(fullName), _T("%s\\%s\\%s.dat"), dat->pd->ptszProfileDir, profile, profile); + CallService(MS_DB_CHECKPROFILE, (WPARAM)fullName, item.iImage == 2); +} + +void DeleteProfile(HWND hwndList, int iItem, DlgProfData *dat) +{ + if (iItem < 0) + return; + + TCHAR profile[MAX_PATH], profilef[MAX_PATH * 2]; + + LVITEM item = { 0 }; + item.mask = LVIF_TEXT; + item.iItem = iItem; + item.pszText = profile; + item.cchTextMax = SIZEOF(profile); + if (!ListView_GetItem(hwndList, &item)) + return; + + mir_sntprintf(profilef, SIZEOF(profilef), TranslateT("Are you sure you want to remove profile \"%s\"?"), profile); + if (IDYES != MessageBox(NULL, profilef, _T("Miranda NG"), MB_YESNO | MB_TASKMODAL | MB_ICONWARNING)) + return; + + mir_sntprintf(profilef, SIZEOF(profilef), _T("%s\\%s%c"), dat->pd->ptszProfileDir, profile, 0); + + SHFILEOPSTRUCT sf = { 0 }; + sf.wFunc = FO_DELETE; + sf.pFrom = profilef; + sf.fFlags = FOF_NOCONFIRMATION | FOF_SILENT | FOF_ALLOWUNDO; + SHFileOperation(&sf); + ListView_DeleteItem(hwndList, item.iItem); +} + +static void CheckRun(HWND hwndDlg, int uMsg) +{ + DlgProfData *dat = (DlgProfData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + if (dat == NULL) + return; + + HWND hwndList = GetDlgItem(hwndDlg, IDC_PROFILELIST); + EnableWindow(dat->hwndOK, ListView_GetSelectedCount(hwndList) == 1); + + TCHAR profile[MAX_PATH]; + LVITEM item = { 0 }; + item.mask = LVIF_TEXT | LVIF_IMAGE; + item.iItem = ListView_GetNextItem(hwndList, -1, LVNI_SELECTED | LVNI_ALL); + item.pszText = profile; + item.cchTextMax = SIZEOF(profile); + if (!ListView_GetItem(hwndList, &item)) + return; + + switch(item.iImage) { + case 3: + EnableWindow(dat->hwndOK, false); + return; + + case 2: + SetWindowText(dat->hwndOK, TranslateT("&Convert")); + dat->pd->bRun = false; + break; + + default: + SetWindowText(dat->hwndOK, TranslateT("&Run")); + dat->pd->bRun = true; + } + + // profile is placed in "profile_name" subfolder + + TCHAR tmpPath[MAX_PATH]; + mir_sntprintf(tmpPath, SIZEOF(tmpPath), _T("%s\\%s.dat"), dat->pd->ptszProfileDir, profile); + if (_taccess(tmpPath, 2)) + mir_sntprintf(dat->pd->ptszProfile, MAX_PATH, _T("%s\\%s\\%s.dat"), dat->pd->ptszProfileDir, profile, profile); + else + _tcsncpy_s(dat->pd->ptszProfile, MAX_PATH, tmpPath, _TRUNCATE); + + if (uMsg == NM_DBLCLK) + EndDialog(GetParent(hwndDlg), 1); +} + +static void ExecuteMenu(HWND hwndDlg, LPARAM lParam) +{ + HWND hwndList = GetDlgItem(hwndDlg, IDC_PROFILELIST); + DlgProfData *dat = (DlgProfData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + LVHITTESTINFO lvht = { 0 }; + lvht.pt.x = GET_X_LPARAM(lParam); + lvht.pt.y = GET_Y_LPARAM(lParam); + ScreenToClient(hwndList, &lvht.pt); + + if (ListView_HitTest(hwndList, &lvht) == -1) + return; + + if (lvht.iItem == -1) + return; + + LVITEM tvi = { 0 }; + tvi.mask = LVIF_IMAGE; + tvi.iItem = lvht.iItem; + if (!ListView_GetItem(hwndList, &tvi)) + return; + + bool bConvert = (tvi.iImage == 2); + + lvht.pt.x = GET_X_LPARAM(lParam); + lvht.pt.y = GET_Y_LPARAM(lParam); + + HMENU hMenu = CreatePopupMenu(); + if (tvi.iImage < 2) { + AppendMenu(hMenu, MF_STRING, 1, TranslateT("Run")); + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + } + if (tvi.iImage != 3 && ServiceExists(MS_DB_CHECKPROFILE)) { + if (bConvert) + AppendMenu(hMenu, MF_STRING, 2, TranslateT("Convert database")); + else + AppendMenu(hMenu, MF_STRING, 2, TranslateT("Check database")); + AppendMenu(hMenu, MF_SEPARATOR, 0, NULL); + } + AppendMenu(hMenu, MF_STRING, 3, TranslateT("Delete")); + int index = TrackPopupMenu(hMenu, TPM_RETURNCMD, lvht.pt.x, lvht.pt.y, 0, hwndDlg, NULL); + switch (index) { + case 1: + SendMessage(GetParent(hwndDlg), WM_COMMAND, IDOK, 0); + break; + + case 2: + CheckProfile(hwndList, lvht.iItem, dat); + break; + + case 3: + DeleteProfile(hwndList, lvht.iItem, dat); + break; + } + DestroyMenu(hMenu); +} + +static INT_PTR CALLBACK DlgProfileSelect(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + DlgProfData *dat = (DlgProfData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + HWND hwndList = GetDlgItem(hwndDlg, IDC_PROFILELIST); + + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + { + dat = (DlgProfData*)lParam; + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + + // set columns + LVCOLUMN col; + col.mask = LVCF_TEXT | LVCF_WIDTH; + col.pszText = TranslateT("Profile"); + col.cx = 100; + ListView_InsertColumn(hwndList, 0, &col); + + col.pszText = TranslateT("Driver"); + col.cx = 150 - GetSystemMetrics(SM_CXVSCROLL); + ListView_InsertColumn(hwndList, 1, &col); + + col.pszText = TranslateT("Size"); + col.cx = 60; + ListView_InsertColumn(hwndList, 2, &col); + + // icons + HIMAGELIST hImgList = ImageList_Create(16, 16, ILC_MASK | ILC_COLOR32, 2, 1); + ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_USERDETAILS)); + ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_DELETE)); + ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MWARNING)); + ImageList_AddIcon_NotShared(hImgList, MAKEINTRESOURCE(IDI_MFATAL)); + + // LV will destroy the image list + SetWindowLongPtr(hwndList, GWL_STYLE, GetWindowLongPtr(hwndList, GWL_STYLE) | LVS_SORTASCENDING); + ListView_SetImageList(hwndList, hImgList, LVSIL_SMALL); + ListView_SetExtendedListViewStyle(hwndList, + ListView_GetExtendedListViewStyle(hwndList) | LVS_EX_DOUBLEBUFFER | LVS_EX_INFOTIP | LVS_EX_LABELTIP | LVS_EX_FULLROWSELECT); + + // find all the profiles + ProfileEnumData ped = { hwndDlg, dat->pd->ptszProfile }; + findProfiles(dat->pd->ptszProfileDir, EnumProfilesForList, (LPARAM)&ped); + PostMessage(hwndDlg, WM_FOCUSTEXTBOX, 0, 0); + + dat->hFileNotify = FindFirstChangeNotification(dat->pd->ptszProfileDir, TRUE, FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE); + if (dat->hFileNotify != INVALID_HANDLE_VALUE) + SetTimer(hwndDlg, 0, 1200, NULL); + return TRUE; + } + + case WM_DESTROY: + KillTimer(hwndDlg, 0); + FindCloseChangeNotification(dat->hFileNotify); + break; + + case WM_TIMER: + if (WaitForSingleObject(dat->hFileNotify, 0) == WAIT_OBJECT_0) { + ListView_DeleteAllItems(hwndList); + ProfileEnumData ped = { hwndDlg, dat->pd->ptszProfile }; + findProfiles(dat->pd->ptszProfileDir, EnumProfilesForList, (LPARAM)&ped); + FindNextChangeNotification(dat->hFileNotify); + } + break; + + case WM_FOCUSTEXTBOX: + SetFocus(hwndList); + if (dat->pd->ptszProfile[0] == 0 || ListView_GetSelectedCount(hwndList) == 0) + ListView_SetItemState(hwndList, 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED); + break; + + case WM_SHOWWINDOW: + if (wParam) + CheckRun(hwndDlg, 0); + break; + + case WM_CONTEXTMENU: + ExecuteMenu(hwndDlg, lParam); + break; + + case WM_NOTIFY: + LPNMHDR hdr = (LPNMHDR)lParam; + if (hdr == NULL) + break; + + if (hdr->idFrom == 0) { + CheckRun(hwndDlg, 0); + break; + } + + if (hdr->idFrom == IDC_PROFILELIST) { + switch (hdr->code) { + case LVN_ITEMCHANGED: + case NM_DBLCLK: + CheckRun(hwndDlg, hdr->code); + return TRUE; + + case LVN_KEYDOWN: + if (((LPNMLVKEYDOWN)lParam)->wVKey == VK_DELETE) + DeleteProfile(hwndList, ListView_GetNextItem(hwndList, -1, LVNI_SELECTED | LVNI_ALL), dat); + break; + + case LVN_GETINFOTIP: + NMLVGETINFOTIP *pInfoTip = (NMLVGETINFOTIP *)lParam; + if (pInfoTip != NULL) { + TCHAR profilename[MAX_PATH], tszFullPath[MAX_PATH]; + struct _stat statbuf; + ListView_GetItemText(hwndList, pInfoTip->iItem, 0, profilename, MAX_PATH); + mir_sntprintf(tszFullPath, SIZEOF(tszFullPath), _T("%s\\%s\\%s.dat"), dat->pd->ptszProfileDir, profilename, profilename); + _tstat(tszFullPath, &statbuf); + mir_sntprintf(pInfoTip->pszText, pInfoTip->cchTextMax, _T("%s\n%s: %s\n%s: %s"), tszFullPath, TranslateT("Created"), rtrimt(NEWTSTR_ALLOCA(_tctime(&statbuf.st_ctime))), TranslateT("Modified"), rtrimt(NEWTSTR_ALLOCA(_tctime(&statbuf.st_mtime)))); + } + } + } + break; + } + + return FALSE; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Tab manager + its envelope + +static INT_PTR CALLBACK DlgProfileManager(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + DetailsData *dat = (DetailsData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); + + switch (msg) { + case WM_INITDIALOG: + TranslateDialogDefault(hwndDlg); + { + DlgProfData *prof = (DlgProfData*)lParam; + PROPSHEETHEADER *psh = prof->psh; + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)LoadImage(hInst, MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), 0)); + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadImage(hInst, MAKEINTRESOURCE(IDI_DETAILSLOGO), IMAGE_ICON, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), 0)); + dat = (DetailsData*)mir_alloc(sizeof(DetailsData)); + dat->prof = prof; + prof->hwndOK = GetDlgItem(hwndDlg, IDOK); + EnableWindow(prof->hwndOK, FALSE); + SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); + + TCHAR buf[512]; + mir_sntprintf(buf, SIZEOF(buf), _T("%s\n%s"), TranslateT("Miranda NG profile manager"), TranslateT("Manage your Miranda NG profile")); + SetDlgItemText(hwndDlg, IDC_NAME, buf); + + dat->currentPage = 0; + dat->pageCount = psh->nPages; + dat->opd = (DetailsPageData*)mir_calloc(sizeof(DetailsPageData)*dat->pageCount); + OPTIONSDIALOGPAGE *odp = (OPTIONSDIALOGPAGE*)psh->ppsp; + + TCITEM tci; + tci.mask = TCIF_TEXT; + for (int i = 0; i < dat->pageCount; i++) { + dat->opd[i].pTemplate = (DLGTEMPLATE*)LockResource(LoadResource(odp[i].hInstance, FindResourceA(odp[i].hInstance, odp[i].pszTemplate, MAKEINTRESOURCEA(5)))); + dat->opd[i].dlgProc = odp[i].pfnDlgProc; + dat->opd[i].hInst = odp[i].hInstance; + dat->opd[i].hwnd = NULL; + dat->opd[i].changed = 0; + tci.pszText = (TCHAR*)odp[i].ptszTitle; + if (dat->prof->pd->noProfiles || shouldAutoCreate(dat->prof->pd->ptszProfile)) + dat->currentPage = 1; + TabCtrl_InsertItem(GetDlgItem(hwndDlg, IDC_TABS), i, &tci); + } + + GetWindowRect(GetDlgItem(hwndDlg, IDC_TABS), &dat->rcDisplay); + TabCtrl_AdjustRect(GetDlgItem(hwndDlg, IDC_TABS), FALSE, &dat->rcDisplay); + + POINT pt = { 0, 0 }; + ClientToScreen(hwndDlg, &pt); + OffsetRect(&dat->rcDisplay, -pt.x, -pt.y); + + TabCtrl_SetCurSel(GetDlgItem(hwndDlg, IDC_TABS), dat->currentPage); + dat->opd[dat->currentPage].hwnd = CreateDialogIndirectParam(dat->opd[dat->currentPage].hInst, dat->opd[dat->currentPage].pTemplate, hwndDlg, dat->opd[dat->currentPage].dlgProc, (LPARAM)dat->prof); + ThemeDialogBackground(dat->opd[dat->currentPage].hwnd); + SetWindowPos(dat->opd[dat->currentPage].hwnd, HWND_TOP, dat->rcDisplay.left, dat->rcDisplay.top, 0, 0, SWP_NOSIZE); + + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); + + // service mode combobox + if (servicePlugins.getCount() == 0) { + ShowWindow(GetDlgItem(hwndDlg, IDC_SM_LABEL), FALSE); + ShowWindow(GetDlgItem(hwndDlg, IDC_SM_COMBO), FALSE); + } + else { + HWND hwndCombo = GetDlgItem(hwndDlg, IDC_SM_COMBO); + LRESULT index = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)_T("")); + SendMessage(hwndCombo, CB_SETITEMDATA, index, (LPARAM)-1); + SendMessage(hwndCombo, CB_SETCURSEL, 0, 0); + for (int i = 0; i < servicePlugins.getCount(); i++) { + pluginEntry *p = servicePlugins[i]; + index = SendMessage(hwndCombo, CB_ADDSTRING, 0, (LPARAM)TranslateTS(p->pluginname)); + SendMessage(hwndCombo, CB_SETITEMDATA, index, (LPARAM)i); + } + } + ShowWindow(dat->opd[dat->currentPage].hwnd, SW_SHOW); + } + return TRUE; + + case WM_CTLCOLORSTATIC: + switch (GetDlgCtrlID((HWND)lParam)) { + case IDC_WHITERECT: + SetBkColor((HDC)wParam, GetSysColor(COLOR_WINDOW)); + return (INT_PTR)GetSysColorBrush(COLOR_WINDOW); + } + break; + + case PSM_CHANGED: + dat->opd[dat->currentPage].changed = 1; + return TRUE; + + case PSM_FORCECHANGED: + { + PSHNOTIFY pshn; + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + for (int i = 0; i < dat->pageCount; i++) { + pshn.hdr.hwndFrom = dat->opd[i].hwnd; + if (dat->opd[i].hwnd != NULL) + SendMessage(dat->opd[i].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); + } + } + break; + + case WM_NOTIFY: + switch (wParam) { + case IDC_TABS: + switch (((LPNMHDR)lParam)->code) { + case TCN_SELCHANGING: + if (dat->currentPage != -1 && dat->opd[dat->currentPage].hwnd != NULL) { + PSHNOTIFY pshn; + pshn.hdr.code = PSN_KILLACTIVE; + pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + if (SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn)) { + SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, TRUE); + return TRUE; + } + } + break; + + case TCN_SELCHANGE: + if (dat->currentPage != -1 && dat->opd[dat->currentPage].hwnd != NULL) + ShowWindow(dat->opd[dat->currentPage].hwnd, SW_HIDE); + + dat->currentPage = TabCtrl_GetCurSel(GetDlgItem(hwndDlg, IDC_TABS)); + if (dat->currentPage != -1) { + if (dat->opd[dat->currentPage].hwnd == NULL) { + PSHNOTIFY pshn; + dat->opd[dat->currentPage].hwnd = CreateDialogIndirectParam(dat->opd[dat->currentPage].hInst, dat->opd[dat->currentPage].pTemplate, hwndDlg, dat->opd[dat->currentPage].dlgProc, (LPARAM)dat->prof); + ThemeDialogBackground(dat->opd[dat->currentPage].hwnd); + SetWindowPos(dat->opd[dat->currentPage].hwnd, HWND_TOP, dat->rcDisplay.left, dat->rcDisplay.top, 0, 0, SWP_NOSIZE); + pshn.hdr.code = PSN_INFOCHANGED; + pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); + } + ShowWindow(dat->opd[dat->currentPage].hwnd, SW_SHOW); + } + break; + } + break; + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDCANCEL: + { + PSHNOTIFY pshn; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + pshn.hdr.code = PSN_RESET; + for (int i = 0; i < dat->pageCount; i++) { + if (dat->opd[i].hwnd == NULL || !dat->opd[i].changed) + continue; + + pshn.hdr.hwndFrom = dat->opd[i].hwnd; + SendMessage(dat->opd[i].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); + } + EndDialog(hwndDlg, 0); + } + break; + + case IDC_REMOVE: + if (!dat->prof->pd->noProfiles) { + HWND hwndList = GetDlgItem(dat->opd[0].hwnd, IDC_PROFILELIST); + DeleteProfile(hwndList, ListView_GetNextItem(hwndList, -1, LVNI_SELECTED | LVNI_ALL), dat->prof); + } + break; + + case IDOK: + PSHNOTIFY pshn; + pshn.hdr.idFrom = 0; + pshn.lParam = 0; + if (dat->currentPage != -1) { + pshn.hdr.code = PSN_KILLACTIVE; + pshn.hdr.hwndFrom = dat->opd[dat->currentPage].hwnd; + if (SendMessage(dat->opd[dat->currentPage].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn)) + break; + } + + pshn.hdr.code = PSN_APPLY; + for (int i = 0; i < dat->pageCount; i++) { + if (dat->opd[i].hwnd == NULL || !dat->opd[i].changed) + continue; + + pshn.hdr.hwndFrom = dat->opd[i].hwnd; + SendMessage(dat->opd[i].hwnd, WM_NOTIFY, 0, (LPARAM)&pshn); + if (GetWindowLongPtr(dat->opd[i].hwnd, DWLP_MSGRESULT) == PSNRET_INVALID_NOCHANGEPAGE) { + TabCtrl_SetCurSel(GetDlgItem(hwndDlg, IDC_TABS), i); + if (dat->currentPage != -1) + ShowWindow(dat->opd[dat->currentPage].hwnd, SW_HIDE); + dat->currentPage = i; + ShowWindow(dat->opd[dat->currentPage].hwnd, SW_SHOW); + return 0; + } + } + EndDialog(hwndDlg, 1); + } + break; + + case WM_DESTROY: + LRESULT curSel = SendDlgItemMessage(hwndDlg, IDC_SM_COMBO, CB_GETCURSEL, 0, 0); + if (curSel != CB_ERR) { + int idx = SendDlgItemMessage(hwndDlg, IDC_SM_COMBO, CB_GETITEMDATA, (WPARAM)curSel, 0); + if (idx != CB_ERR) + SetServiceModePlugin(servicePlugins[idx]); + } + + DestroyIcon((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, 0)); + DestroyIcon((HICON)SendMessage(hwndDlg, WM_SETICON, ICON_BIG, 0)); + DeleteObject(dat->hBoldFont); + + for (int i = 0; i < dat->pageCount; i++) + if (dat->opd[i].hwnd != NULL) + DestroyWindow(dat->opd[i].hwnd); + + mir_free(dat->opd); + mir_free(dat); + break; + } + return FALSE; +} + +static int AddProfileManagerPage(struct DetailsPageInit *opi, OPTIONSDIALOGPAGE *odp) +{ + if (odp->cbSize != sizeof(OPTIONSDIALOGPAGE)) + return 1; + + opi->odp = (OPTIONSDIALOGPAGE*)mir_realloc(opi->odp, sizeof(OPTIONSDIALOGPAGE)*(opi->pageCount + 1)); + + OPTIONSDIALOGPAGE *p = opi->odp + opi->pageCount++; + p->cbSize = sizeof(OPTIONSDIALOGPAGE); + p->hInstance = odp->hInstance; + p->pfnDlgProc = odp->pfnDlgProc; + p->position = odp->position; + p->ptszTitle = Langpack_PcharToTchar(odp->pszTitle); + p->pszGroup = NULL; + p->groupPosition = odp->groupPosition; + p->hGroupIcon = odp->hGroupIcon; + p->hIcon = odp->hIcon; + if ((DWORD_PTR)odp->pszTemplate & 0xFFFF0000) + p->pszTemplate = mir_strdup(odp->pszTemplate); + else + p->pszTemplate = odp->pszTemplate; + return 0; +} + +int getProfileManager(PROFILEMANAGERDATA *pd) +{ + EnsureCheckerLoaded(true); + + DetailsPageInit opi = { 0 }; + + OPTIONSDIALOGPAGE odp = { 0 }; + odp.cbSize = sizeof(odp); + + odp.pszTitle = LPGEN("My profiles"); + odp.pfnDlgProc = DlgProfileSelect; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_PROFILE_SELECTION); + odp.hInstance = hInst; + AddProfileManagerPage(&opi, &odp); + + odp.pszTitle = LPGEN("New profile"); + odp.pszTemplate = MAKEINTRESOURCEA(IDD_PROFILE_NEW); + odp.pfnDlgProc = DlgProfileNew; + AddProfileManagerPage(&opi, &odp); + + PROPSHEETHEADER psh = { 0 }; + psh.dwSize = sizeof(psh); + psh.dwFlags = PSH_PROPSHEETPAGE | PSH_NOAPPLYNOW; + psh.nPages = opi.pageCount; + psh.ppsp = (PROPSHEETPAGE*)opi.odp; + + DlgProfData prof; + prof.pd = pd; + prof.psh = &psh; + int rc = DialogBoxParam(hInst, MAKEINTRESOURCE(IDD_PROFILEMANAGER), NULL, DlgProfileManager, (LPARAM)&prof); + if (rc != -1) { + for (int i = 0; i < opi.pageCount; i++) { + mir_free(opi.odp[i].pszTitle); + mir_free(opi.odp[i].pszGroup); + if ((DWORD_PTR)opi.odp[i].pszTemplate & 0xFFFF0000) + mir_free((char*)opi.odp[i].pszTemplate); + } + } + mir_free(opi.odp); + + return rc; +} -- cgit v1.2.3