summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/modules/clist/clcitems.cpp1408
-rw-r--r--src/modules/database/profilemanager.cpp1801
2 files changed, 1605 insertions, 1604 deletions
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<SavedContactState_t> saveContact(10, NumericKeySortT);
- OBJLIST<SavedGroupState_t> saveGroup(100, NumericKeySortT);
- OBJLIST<SavedInfoState_t> 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<SavedContactState_t> saveContact(10, NumericKeySortT);
+ OBJLIST<SavedGroupState_t> saveGroup(100, NumericKeySortT);
+ OBJLIST<SavedInfoState_t> 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 <sys/stat.h>
-
-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("<In use>");
- else
- item.pszText = TranslateTS(dblink->szFullName);
- }
- else item.pszText = TranslateT("<Unknown format>");
-
- 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 <sys/stat.h>
+
+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("<In use>");
+ else
+ item.pszText = TranslateTS(dblink->szFullName);
+ }
+ else item.pszText = TranslateT("<Unknown format>");
+
+ 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;
+}