/* Miranda IM: the free IM client for Microsoft* Windows* Copyright 2000-2009 Miranda ICQ/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 "IcoLib.h" static BOOL bModuleInitialized = FALSE; static HANDLE hIcons2ChangedEvent, hIconsChangedEvent; static HICON hIconBlank = NULL; HANDLE hIcoLib_AddNewIcon, hIcoLib_RemoveIcon, hIcoLib_GetIcon, hIcoLib_GetIcon2, hIcoLib_GetIconHandle, hIcoLib_IsManaged, hIcoLib_AddRef, hIcoLib_ReleaseIcon; static int iconEventActive = 0; static BOOL bNeedRebuild = FALSE; struct IcoLibOptsData { HWND hwndIndex; }; CRITICAL_SECTION csIconList; #define SECTIONPARAM_MAKE(index, level, flags) MAKELONG((index)&0xFFFF, MAKEWORD(level, flags)) #define SECTIONPARAM_INDEX(lparam) LOWORD(lparam) #define SECTIONPARAM_LEVEL(lparam) LOBYTE(HIWORD(lparam)) #define SECTIONPARAM_FLAGS(lparam) HIBYTE(HIWORD(lparam)) #define SECTIONPARAM_HAVEPAGE 0x0001 static int sttCompareSections(const SectionItem* p1, const SectionItem* p2) { return _tcscmp(p1->name, p2->name); } static LIST sectionList(20, sttCompareSections); static int sttCompareIconSourceFiles(const IconSourceFile* p1, const IconSourceFile* p2) { return _tcsicmp(p1->file, p2->file); } static LIST iconSourceFileList(10, sttCompareIconSourceFiles); static int sttCompareIconSourceItems(const IconSourceItem* p1, const IconSourceItem* p2) { if (p1->indx < p2->indx) return -1; if (p1->indx > p2->indx) return 1; if (p1->cx < p2->cx) return -1; if (p1->cx > p2->cx) return 1; if (p1->cy < p2->cy) return -1; if (p1->cy > p2->cy) return 1; if (p1->file == p2->file) return 0; return (p1->file > p2->file) ? 1 : -1; } static LIST iconSourceList(20, sttCompareIconSourceItems); static int sttCompareIcons(const IconItem* p1, const IconItem* p2) { return strcmp(p1->name, p2->name); } static LIST iconList(20, sttCompareIcons); ///////////////////////////////////////////////////////////////////////////////////////// // Utility functions static void __fastcall MySetCursor(TCHAR* nCursor) { SetCursor(LoadCursor(NULL, nCursor)); } static void __fastcall SAFE_FREE(void** p) { if (*p) { mir_free(*p); *p = NULL; } } static void __fastcall SafeDestroyIcon(HICON* icon) { if (*icon) { DestroyIcon(*icon); *icon = NULL; } } // Helper functions to manage Icon resources IconSourceFile* IconSourceFile_Get(const TCHAR* file, bool isPath) { TCHAR fileFull[ MAX_PATH ]; if ( !file) return NULL; if (isPath) pathToAbsoluteT(file, fileFull, NULL); /// TODO: convert path to long - eliminate duplicate items else _tcscpy(fileFull, file); IconSourceFile key = { fileFull }; int ix; if ((ix = iconSourceFileList.getIndex(&key)) != -1) { iconSourceFileList[ ix ]->ref_count++; return iconSourceFileList[ ix ]; } IconSourceFile* newItem = (IconSourceFile*)mir_calloc(sizeof(IconSourceFile)); newItem->file = mir_tstrdup(fileFull); newItem->ref_count = 1; iconSourceFileList.insert(newItem); return newItem; } int IconSourceFile_Release(IconSourceFile** pitem) { if (pitem && *pitem && (*pitem)->ref_count) { IconSourceFile* item = *pitem; if (--item->ref_count <= 0) { int indx; if ((indx = iconSourceFileList.getIndex(item)) != -1) { SAFE_FREE((void**)&item->file); iconSourceFileList.remove(indx); SAFE_FREE((void**)&item); } } *pitem = NULL; return 0; } return 1; } static int BytesPerScanLine(int PixelsPerScanline, int BitsPerPixel, int Alignment) { Alignment--; int bytes = ((PixelsPerScanline * BitsPerPixel) + Alignment) & ~Alignment; return bytes / 8; } static int InitializeBitmapInfoHeader(HBITMAP bitmap, BITMAPINFOHEADER* bi) { DIBSECTION DS; int bytes; DS.dsBmih.biSize = 0; bytes = GetObject(bitmap, sizeof(DS), &DS); if (bytes == 0) return 1; // Failure else if ((bytes >= (sizeof(DS.dsBm) + sizeof(DS.dsBmih))) && (DS.dsBmih.biSize >= DWORD(sizeof(DS.dsBmih)))) *bi = DS.dsBmih; else { memset(bi, 0, sizeof(BITMAPINFOHEADER)); bi->biSize = sizeof(BITMAPINFOHEADER); bi->biWidth = DS.dsBm.bmWidth; bi->biHeight = DS.dsBm.bmHeight; } bi->biBitCount = DS.dsBm.bmBitsPixel * DS.dsBm.bmPlanes; bi->biPlanes = 1; if (bi->biClrImportant > bi->biClrUsed) bi->biClrImportant = bi->biClrUsed; if ( !bi->biSizeImage) bi->biSizeImage = BytesPerScanLine(bi->biWidth, bi->biBitCount, 32) * abs(bi->biHeight); return 0; // Success } static int InternalGetDIBSizes(HBITMAP bitmap, int* InfoHeaderSize, int* ImageSize) { BITMAPINFOHEADER bi; if (InitializeBitmapInfoHeader(bitmap, &bi)) return 1; // Failure if (bi.biBitCount > 8) { *InfoHeaderSize = sizeof(BITMAPINFOHEADER); if ((bi.biCompression & BI_BITFIELDS) != 0) *InfoHeaderSize += 12; } else { if (bi.biClrUsed == 0) *InfoHeaderSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * (int)(1 << bi.biBitCount); else *InfoHeaderSize = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD) * bi.biClrUsed; } *ImageSize = bi.biSizeImage; return 0; // Success } static int InternalGetDIB(HBITMAP bitmap, HPALETTE palette, void* bitmapInfo, void* Bits) { HPALETTE oldPal = 0; if (InitializeBitmapInfoHeader(bitmap, (BITMAPINFOHEADER*)bitmapInfo)) return 1; // Failure HDC DC = CreateCompatibleDC(0); if (palette) { oldPal = SelectPalette(DC, palette, FALSE); RealizePalette(DC); } int result = GetDIBits(DC, bitmap, 0, ((BITMAPINFOHEADER*)bitmapInfo)->biHeight, Bits, (BITMAPINFO*)bitmapInfo, DIB_RGB_COLORS) == 0; if (oldPal) SelectPalette(DC, oldPal, FALSE); DeleteDC(DC); return result; } static int GetIconData(HICON icon, BYTE** data, int* size) { ICONINFO iconInfo; int MonoInfoSize, ColorInfoSize; int MonoBitsSize, ColorBitsSize; if ( !data || !size) return 1; // Failure if ( !GetIconInfo(icon, &iconInfo)) return 1; // Failure if (InternalGetDIBSizes(iconInfo.hbmMask, &MonoInfoSize, &MonoBitsSize) || InternalGetDIBSizes(iconInfo.hbmColor, &ColorInfoSize, &ColorBitsSize)) { DeleteObject(iconInfo.hbmColor); DeleteObject(iconInfo.hbmMask); return 1; // Failure } void* MonoInfo = mir_alloc(MonoInfoSize); void* MonoBits = mir_alloc(MonoBitsSize); void* ColorInfo = mir_alloc(ColorInfoSize); void* ColorBits = mir_alloc(ColorBitsSize); if (InternalGetDIB(iconInfo.hbmMask, 0, MonoInfo, MonoBits) || InternalGetDIB(iconInfo.hbmColor, 0, ColorInfo, ColorBits)) { SAFE_FREE(&MonoInfo); SAFE_FREE(&MonoBits); SAFE_FREE(&ColorInfo); SAFE_FREE(&ColorBits); DeleteObject(iconInfo.hbmColor); DeleteObject(iconInfo.hbmMask); return 1; // Failure } *size = ColorInfoSize + ColorBitsSize + MonoBitsSize; *data = (BYTE*)mir_alloc(*size); BYTE* buf = *data; ((BITMAPINFOHEADER*)ColorInfo)->biHeight *= 2; // color height includes mono bits memcpy(buf, ColorInfo, ColorInfoSize); buf += ColorInfoSize; memcpy(buf, ColorBits, ColorBitsSize); buf += ColorBitsSize; memcpy(buf, MonoBits, MonoBitsSize); SAFE_FREE(&MonoInfo); SAFE_FREE(&MonoBits); SAFE_FREE(&ColorInfo); SAFE_FREE(&ColorBits); DeleteObject(iconInfo.hbmColor); DeleteObject(iconInfo.hbmMask); return 0; // Success } #define VER30 0x00030000 static HICON IconSourceItem_GetIcon(IconSourceItem* item) { if (item->icon) { item->icon_ref_count++; return item->icon; } if (item->icon_size) { item->icon = CreateIconFromResourceEx(item->icon_data, item->icon_size, TRUE, VER30, item->cx, item->cy, LR_COLOR); if (item->icon) { item->icon_ref_count++; return item->icon; } } //SHOULD BE REPLACED WITH GOOD ENOUGH FUNCTION _ExtractIconEx(item->file->file, item->indx, item->cx, item->cy, &item->icon, LR_COLOR); if (item->icon) item->icon_ref_count++; return item->icon; } static int IconSourceItem_ReleaseIcon(IconSourceItem* item) { if (item && item->icon_ref_count) { item->icon_ref_count--; if ( !item->icon_ref_count) { if ( !item->icon_size) if (GetIconData(item->icon, &item->icon_data, &item->icon_size)) item->icon_size = 0; // Failure SafeDestroyIcon(&item->icon); } return 0; // Success } return 1; // Failure } IconSourceItem* GetIconSourceItem(const TCHAR* file, int indx, int cxIcon, int cyIcon) { if ( !file) return NULL; IconSourceFile* r_file = IconSourceFile_Get(file, true); IconSourceItem key = { r_file, indx, cxIcon, cyIcon }; int ix; if ((ix = iconSourceList.getIndex(&key)) != -1) { IconSourceFile_Release(&r_file); iconSourceList[ ix ]->ref_count++; return iconSourceList[ ix ]; } IconSourceItem* newItem = (IconSourceItem*)mir_calloc(sizeof(IconSourceItem)); newItem->file = r_file; newItem->indx = indx; newItem->ref_count = 1; newItem->cx = cxIcon; newItem->cy = cyIcon; iconSourceList.insert(newItem); return newItem; } IconSourceItem* GetIconSourceItemFromPath(const TCHAR* path, int cxIcon, int cyIcon) { TCHAR *comma; TCHAR file[ MAX_PATH ]; int n; if ( !path) return NULL; lstrcpyn(file, path, SIZEOF(file)); comma = _tcsrchr(file, ','); if ( !comma) n = 0; else { n = _ttoi(comma+1); *comma = 0; } return GetIconSourceItem(file, n, cxIcon, cyIcon); } IconSourceItem* CreateStaticIconSourceItem(int cxIcon, int cyIcon) { TCHAR sourceName[ MAX_PATH ]; IconSourceFile key = { sourceName }; int i = 0; do { // find new unique name mir_sntprintf(sourceName, SIZEOF(sourceName), _T("*StaticIcon_%d"), ++i); } while (iconSourceFileList.getIndex(&key) != -1); IconSourceItem* newItem = (IconSourceItem*)mir_calloc(sizeof(IconSourceItem)); newItem->file = IconSourceFile_Get(sourceName, false); newItem->indx = 0; newItem->ref_count = 1; newItem->cx = cxIcon; newItem->cy = cyIcon; iconSourceList.insert(newItem); return newItem; } static int IconSourceItem_Release(IconSourceItem** pitem) { if (pitem && *pitem && (*pitem)->ref_count) { IconSourceItem* item = *pitem; item->ref_count--; if ( !item->ref_count) { int indx; if ((indx = iconSourceList.getIndex(item)) != -1) { IconSourceFile_Release(&item->file); SafeDestroyIcon(&item->icon); SAFE_FREE((void**)&item->icon_data); iconSourceList.remove(indx); SAFE_FREE((void**)&item); } } *pitem = NULL; return 0; } return 1; } ///////////////////////////////////////////////////////////////////////////////////////// // Service functions static HICON ExtractIconFromPath(const TCHAR *path, int cxIcon, int cyIcon) { TCHAR *comma; TCHAR file[ MAX_PATH ], fileFull[ MAX_PATH ]; int n; HICON hIcon; if ( !path) return (HICON)NULL; lstrcpyn(file, path, SIZEOF(file)); comma = _tcsrchr(file, ','); if ( !comma) n = 0; else { n = _ttoi(comma+1); *comma = 0; } pathToAbsoluteT(file, fileFull, NULL); hIcon = NULL; //SHOULD BE REPLACED WITH GOOD ENOUGH FUNCTION _ExtractIconEx(fileFull, n, cxIcon, cyIcon, &hIcon, LR_COLOR); return hIcon; } static SectionItem* IcoLib_AddSection(TCHAR *sectionName, BOOL create_new) { if ( !sectionName) return NULL; int indx; SectionItem key = { sectionName, 0 }; if ((indx = sectionList.getIndex(&key)) != -1) return sectionList[ indx ]; if (create_new) { SectionItem* newItem = (SectionItem*)mir_calloc(sizeof(SectionItem)); newItem->name = mir_tstrdup(sectionName); newItem->flags = 0; sectionList.insert(newItem); bNeedRebuild = TRUE; return newItem; } return NULL; } static void IcoLib_RemoveSection(SectionItem* section) { if ( !section) return; int indx; if ((indx = sectionList.getIndex(section)) != -1) { sectionList.remove(indx); SAFE_FREE((void**)§ion->name); SAFE_FREE((void**)§ion); bNeedRebuild = TRUE; } } static IconItem* IcoLib_FindIcon(const char* pszIconName) { int indx; IconItem key = { (char*)pszIconName }; if ((indx = iconList.getIndex(&key)) != -1) return iconList[ indx ]; return NULL; } static IconItem* IcoLib_FindHIcon(HICON hIcon, bool &big) { IconItem* item = NULL; int indx; for (indx = 0; indx < iconList.getCount(); indx++) { if (iconList[ indx ]->source_small && iconList[ indx ]->source_small->icon == hIcon) { item = iconList[ indx ]; big = false; break; } else if (iconList[ indx ]->source_big && iconList[ indx ]->source_big->icon == hIcon) { item = iconList[ indx ]; big = true; break; } } return item; } static void IcoLib_FreeIcon(IconItem* icon) { if ( !icon) return; SAFE_FREE((void**)&icon->name); SAFE_FREE((void**)&icon->description); SAFE_FREE((void**)&icon->default_file); SAFE_FREE((void**)&icon->temp_file); if (icon->section) { if ( !--icon->section->ref_count) IcoLib_RemoveSection(icon->section); icon->section = NULL; } IconSourceItem_Release(&icon->source_small); IconSourceItem_Release(&icon->source_big); IconSourceItem_Release(&icon->default_icon); SafeDestroyIcon(&icon->temp_icon); } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib_AddNewIcon HANDLE IcoLib_AddNewIcon(SKINICONDESC* sid) { int utf = 0, utf_path = 0; IconItem* item; if ( !sid->cbSize) return NULL; if (sid->cbSize < SKINICONDESC_SIZE_V1) return NULL; if (sid->cbSize >= SKINICONDESC_SIZE) { utf = sid->flags & SIDF_UNICODE ? 1 : 0; utf_path = sid->flags & SIDF_PATH_UNICODE ? 1 : 0; } EnterCriticalSection(&csIconList); item = IcoLib_FindIcon(sid->pszName); if ( !item) { item = (IconItem*)mir_alloc(sizeof(IconItem)); item->name = sid->pszName; iconList.insert(item); } else IcoLib_FreeIcon(item); ZeroMemory(item, sizeof(*item)); item->name = mir_strdup(sid->pszName); if (utf) { item->description = mir_u2t(sid->pwszDescription); item->section = IcoLib_AddSection(sid->pwszSection, TRUE); } else { item->description = mir_a2t(sid->pszDescription); WCHAR* pwszSection = sid->pszSection ? mir_a2u(sid->pszSection) : NULL; item->section = IcoLib_AddSection(pwszSection, TRUE); SAFE_FREE((void**)&pwszSection); } if (item->section) { item->section->ref_count++; item->orderID = ++item->section->maxOrder; } else item->orderID = 0; if (sid->pszDefaultFile) { if (utf_path) { WCHAR fileFull[ MAX_PATH ]; pathToAbsoluteW(sid->pwszDefaultFile, fileFull, NULL); item->default_file = mir_wstrdup(fileFull); } else { WCHAR *file = mir_a2u(sid->pszDefaultFile); WCHAR fileFull[ MAX_PATH ]; pathToAbsoluteW(file, fileFull, NULL); SAFE_FREE((void**)&file); item->default_file = mir_wstrdup(fileFull); } } item->default_indx = sid->iDefaultIndex; if (sid->cbSize >= SKINICONDESC_SIZE_V3) { item->cx = sid->cx; item->cy = sid->cy; } if (sid->cbSize >= SKINICONDESC_SIZE_V2 && sid->hDefaultIcon) { bool big; IconItem* def_item = IcoLib_FindHIcon(sid->hDefaultIcon, big); if (def_item) { item->default_icon = big ? def_item->source_big : def_item->source_small; item->default_icon->ref_count++; } else { int cx = item->cx ? item->cx : GetSystemMetrics(SM_CXSMICON); int cy = item->cy ? item->cy : GetSystemMetrics(SM_CYSMICON); item->default_icon = CreateStaticIconSourceItem(cx, cy); if (GetIconData(sid->hDefaultIcon, &item->default_icon->icon_data, &item->default_icon->icon_size)) { IconSourceItem_Release(&item->default_icon); } } } if (sid->cbSize >= SKINICONDESC_SIZE && item->section) item->section->flags = sid->flags & SIDF_SORTED; LeaveCriticalSection(&csIconList); return item; } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib_RemoveIcon static INT_PTR IcoLib_RemoveIcon(WPARAM, LPARAM lParam) { if (lParam) { int indx; EnterCriticalSection(&csIconList); if ((indx = iconList.getIndex((IconItem*)&lParam)) != -1) { IconItem *item = iconList[ indx ]; IcoLib_FreeIcon(item); iconList.remove(indx); SAFE_FREE((void**)&item); } LeaveCriticalSection(&csIconList); return (indx == -1) ? 1 : 0; } return 1; // Failed } HICON IconItem_GetDefaultIcon(IconItem* item, bool big) { HICON hIcon = NULL; if (item->default_icon && !big) { IconSourceItem_Release(&item->source_small); item->source_small = item->default_icon; item->source_small->ref_count++; hIcon = IconSourceItem_GetIcon(item->source_small); } if ( !hIcon && item->default_file) { int cx = item->cx ? item->cx : GetSystemMetrics(big ? SM_CXICON : SM_CXSMICON); int cy = item->cy ? item->cy : GetSystemMetrics(big ? SM_CYICON : SM_CYSMICON); IconSourceItem* def_icon = GetIconSourceItem(item->default_file, item->default_indx, cx, cy); if (big) { if (def_icon != item->source_big) { IconSourceItem_Release(&item->source_big); item->source_big = def_icon; if (def_icon) { def_icon->ref_count++; hIcon = IconSourceItem_GetIcon(def_icon); } } else IconSourceItem_Release(&def_icon); } else { if (def_icon != item->default_icon) { IconSourceItem_Release(&item->default_icon); item->default_icon = def_icon; if (def_icon) { IconSourceItem_Release(&item->source_small); item->source_small = def_icon; def_icon->ref_count++; hIcon = IconSourceItem_GetIcon(def_icon); } } else IconSourceItem_Release(&def_icon); } } return hIcon; } ///////////////////////////////////////////////////////////////////////////////////////// // IconItem_GetIcon HICON IconItem_GetIcon(IconItem* item, bool big) { DBVARIANT dbv = {0}; HICON hIcon = NULL; big = big && !item->cx; IconSourceItem* &source = big ? item->source_big : item->source_small; if ( !source && !DBGetContactSettingTString(NULL, "SkinIcons", item->name, &dbv)) { int cx = item->cx ? item->cx : GetSystemMetrics(big ? SM_CXICON : SM_CXSMICON); int cy = item->cy ? item->cy : GetSystemMetrics(big ? SM_CYICON : SM_CYSMICON); source = GetIconSourceItemFromPath(dbv.ptszVal, cx, cy); DBFreeVariant(&dbv); } if (source) hIcon = IconSourceItem_GetIcon(source); if ( !hIcon) hIcon = IconItem_GetDefaultIcon(item, big); if ( !hIcon) return hIconBlank; return hIcon; } ///////////////////////////////////////////////////////////////////////////////////////// // IconItem_GetIcon_Preview HICON IconItem_GetIcon_Preview(IconItem* item) { HICON hIcon = NULL; if ( !item->temp_reset) { HICON hRefIcon = IconItem_GetIcon(item, false); hIcon = CopyIcon(hRefIcon); if (item->source_small && item->source_small->icon == hRefIcon) IconSourceItem_ReleaseIcon(item->source_small); } else { if (item->default_icon) { HICON hRefIcon = IconSourceItem_GetIcon(item->default_icon); if (hRefIcon) { hIcon = CopyIcon(hRefIcon); if (item->default_icon->icon == hRefIcon) IconSourceItem_ReleaseIcon(item->default_icon); } } if ( !hIcon && item->default_file) { IconSourceItem_Release(&item->default_icon); item->default_icon = GetIconSourceItem(item->default_file, item->default_indx, item->cx, item->cy); if (item->default_icon) { HICON hRefIcon = IconSourceItem_GetIcon(item->default_icon); if (hRefIcon) { hIcon = CopyIcon(hRefIcon); if (item->default_icon->icon == hRefIcon) IconSourceItem_ReleaseIcon(item->default_icon); } } } if ( !hIcon) return CopyIcon(hIconBlank); } return hIcon; } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib_GetIcon // lParam: pszIconName // wParam: PLOADIMAGEPARAM or NULL. // if wParam == NULL, default is used: // uType = IMAGE_ICON // cx/cyDesired = GetSystemMetrics(SM_CX/CYSMICON) // fuLoad = 0 HICON IcoLib_GetIcon(const char* pszIconName, bool big) { IconItem* item; HICON result = NULL; if ( !pszIconName) return hIconBlank; EnterCriticalSection(&csIconList); item = IcoLib_FindIcon(pszIconName); if (item) { result = IconItem_GetIcon(item, big); } LeaveCriticalSection(&csIconList); return result; } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib_GetIconHandle // lParam: pszIconName HANDLE IcoLib_GetIconHandle(const char* pszIconName) { IconItem* item; if ( !pszIconName) return NULL; EnterCriticalSection(&csIconList); item = IcoLib_FindIcon(pszIconName); LeaveCriticalSection(&csIconList); return (HANDLE)item; } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib_GetIconByHandle // lParam: icolib item handle // wParam: 0 HICON IcoLib_GetIconByHandle(HANDLE hItem, bool big) { if (hItem == NULL) return NULL; HICON result = hIconBlank; IconItem* pi = (IconItem*)hItem; EnterCriticalSection(&csIconList); // we can get any junk here... but getIndex() is MUCH faster than indexOf(). __try { if (iconList.getIndex(pi) != -1) result = IconItem_GetIcon(pi, big); } __except(EXCEPTION_EXECUTE_HANDLER) { } LeaveCriticalSection(&csIconList); return result; } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib_IsManaged // lParam: NULL // wParam: HICON HANDLE IcoLib_IsManaged(HICON hIcon) { IconItem* item; EnterCriticalSection(&csIconList); bool big; item = IcoLib_FindHIcon(hIcon, big); if (item) { IconSourceItem* source = big && !item->cx ? item->source_big : item->source_small; if (source->icon_ref_count == 0) item = NULL; } LeaveCriticalSection(&csIconList); return NULL; } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib_AddRef // lParam: NULL // wParam: HICON static INT_PTR IcoLib_AddRef(WPARAM wParam, LPARAM) { EnterCriticalSection(&csIconList); bool big; IconItem *item = IcoLib_FindHIcon((HICON)wParam, big); INT_PTR res = 1; if (item) { IconSourceItem* source = big && !item->cx ? item->source_big : item->source_small; if (source->icon_ref_count) { source->icon_ref_count++; res = 0; } } LeaveCriticalSection(&csIconList); return res; } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib_ReleaseIcon // lParam: pszIconName or NULL // wParam: HICON or NULL int IcoLib_ReleaseIcon(HICON hIcon, char* szIconName, bool big) { IconItem *item = NULL; EnterCriticalSection(&csIconList); if (szIconName) item = IcoLib_FindIcon(szIconName); if ( !item && hIcon) // find by HICON item = IcoLib_FindHIcon(hIcon, big); int res = 1; if (item) { IconSourceItem* source = big && !item->cx ? item->source_big : item->source_small; if (source && source->icon_ref_count) { if (iconEventActive) source->icon_ref_count--; else IconSourceItem_ReleaseIcon(source); res = 0; } } LeaveCriticalSection(&csIconList); return res; } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib GUI service routines static void LoadSectionIcons(TCHAR *filename, SectionItem* sectionActive) { TCHAR path[ MAX_PATH ]; int suffIndx; HICON hIcon; int indx; mir_sntprintf(path, SIZEOF(path), _T("%s,"), filename); suffIndx = lstrlen(path); EnterCriticalSection(&csIconList); for (indx = 0; indx < iconList.getCount(); indx++) { IconItem *item = iconList[ indx ]; if (item->default_file && item->section == sectionActive) { _itot(item->default_indx, path + suffIndx, 10); hIcon = ExtractIconFromPath(path, item->cx, item->cy); if (hIcon) { SAFE_FREE((void**)&item->temp_file); SafeDestroyIcon(&item->temp_icon); item->temp_file = mir_tstrdup(path); item->temp_icon = hIcon; item->temp_reset = FALSE; } } } LeaveCriticalSection(&csIconList); } void LoadSubIcons(HWND htv, TCHAR *filename, HTREEITEM hItem) { TVITEM tvi; TreeItem *treeItem; SectionItem* sectionActive; tvi.mask = TVIF_HANDLE | TVIF_PARAM; tvi.hItem = hItem; TreeView_GetItem(htv, &tvi); treeItem = (TreeItem *)tvi.lParam; sectionActive = sectionList[ SECTIONPARAM_INDEX(treeItem->value) ]; tvi.hItem = TreeView_GetChild(htv, tvi.hItem); while (tvi.hItem) { LoadSubIcons(htv, filename, tvi.hItem); tvi.hItem = TreeView_GetNextSibling(htv, tvi.hItem); } if (SECTIONPARAM_FLAGS(treeItem->value) & SECTIONPARAM_HAVEPAGE) LoadSectionIcons(filename, sectionActive); } static void UndoChanges(int iconIndx, int cmd) { IconItem *item = iconList[ iconIndx ]; if ( !item->temp_file && !item->temp_icon && item->temp_reset && cmd == ID_CANCELCHANGE) item->temp_reset = FALSE; else { SAFE_FREE((void**)&item->temp_file); SafeDestroyIcon(&item->temp_icon); } if (cmd == ID_RESET) item->temp_reset = TRUE; } void UndoSubItemChanges(HWND htv, HTREEITEM hItem, int cmd) { TVITEM tvi = {0}; TreeItem *treeItem; int indx; tvi.mask = TVIF_HANDLE | TVIF_PARAM; tvi.hItem = hItem; TreeView_GetItem(htv, &tvi); treeItem = (TreeItem *)tvi.lParam; if (SECTIONPARAM_FLAGS(treeItem->value) & SECTIONPARAM_HAVEPAGE) { EnterCriticalSection(&csIconList); for (indx = 0; indx < iconList.getCount(); indx++) if (iconList[ indx ]->section == sectionList[ SECTIONPARAM_INDEX(treeItem->value) ]) UndoChanges(indx, cmd); LeaveCriticalSection(&csIconList); } tvi.hItem = TreeView_GetChild(htv, tvi.hItem); while (tvi.hItem) { UndoSubItemChanges(htv, tvi.hItem, cmd); tvi.hItem = TreeView_GetNextSibling(htv, tvi.hItem); } } static void OpenIconsPage() { CallService(MS_UTILS_OPENURL, 1, (LPARAM)"http://addons.miranda-im.org/index.php?action=display&id=35"); } static int OpenPopupMenu(HWND hwndDlg) { HMENU hMenu, hPopup; POINT pt; int cmd; GetCursorPos(&pt); hMenu = LoadMenu(hMirandaInst, MAKEINTRESOURCE(IDR_ICOLIB_CONTEXT)); hPopup = GetSubMenu(hMenu, 0); TranslateMenu(hPopup); cmd = TrackPopupMenu(hPopup, TPM_RIGHTBUTTON|TPM_RETURNCMD, pt.x, pt.y, 0, hwndDlg, NULL); DestroyMenu(hMenu); return cmd; } static TCHAR* OpenFileDlg(HWND hParent, const TCHAR* szFile, BOOL bAll) { OPENFILENAME ofn = {0}; TCHAR filter[512], *pfilter, file[MAX_PATH*2]; ofn.lStructSize = OPENFILENAME_SIZE_VERSION_400; ofn.hwndOwner = hParent; lstrcpy(filter, TranslateT("Icon Sets")); if (bAll) lstrcat(filter, _T(" (*.dll;*.icl;*.exe;*.ico)")); else lstrcat(filter, _T(" (*.dll)")); pfilter=filter+lstrlen(filter)+1; if (bAll) lstrcpy(pfilter, _T("*.DLL;*.ICL;*.EXE;*.ICO")); else lstrcpy(pfilter, _T("*.DLL")); pfilter += lstrlen(pfilter) + 1; lstrcpy(pfilter, TranslateT("All Files")); lstrcat(pfilter, _T(" (*)")); pfilter += lstrlen(pfilter) + 1; lstrcpy(pfilter, _T("*")); pfilter += lstrlen(pfilter) + 1; *pfilter='\0'; ofn.lpstrFilter = filter; ofn.lpstrDefExt = _T("dll"); lstrcpyn(file, szFile, SIZEOF(file)); ofn.lpstrFile = file; ofn.Flags = OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_DONTADDTORECENT; ofn.nMaxFile = MAX_PATH*2; if ( !GetOpenFileName(&ofn)) return NULL; return mir_tstrdup(file); } // // User interface // #define DM_REBUILDICONSPREVIEW (WM_USER+10) #define DM_CHANGEICON (WM_USER+11) #define DM_CHANGESPECIFICICON (WM_USER+12) #define DM_UPDATEICONSPREVIEW (WM_USER+13) #define DM_REBUILD_CTREE (WM_USER+14) INT_PTR CALLBACK DlgProcIconImport(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam); void DoOptionsChanged(HWND hwndDlg) { SendMessage(hwndDlg, DM_UPDATEICONSPREVIEW, 0, 0); SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); } void DoIconsChanged(HWND hwndDlg) { SendMessage(hwndDlg, DM_UPDATEICONSPREVIEW, 0, 0); iconEventActive = 1; // Disable icon destroying - performance boost NotifyEventHooks(hIconsChangedEvent, 0, 0); NotifyEventHooks(hIcons2ChangedEvent, 0, 0); iconEventActive = 0; EnterCriticalSection(&csIconList); // Destroy unused icons for (int indx = 0; indx < iconList.getCount(); indx++) { IconItem *item = iconList[indx]; if (item->source_small && !item->source_small->icon_ref_count) { item->source_small->icon_ref_count++; IconSourceItem_ReleaseIcon(item->source_small); } if (item->source_big && !item->source_big->icon_ref_count) { item->source_big->icon_ref_count++; IconSourceItem_ReleaseIcon(item->source_big); } } LeaveCriticalSection(&csIconList); } static HTREEITEM FindNamedTreeItemAt(HWND hwndTree, HTREEITEM hItem, const TCHAR *name) { TVITEM tvi = {0}; TCHAR str[MAX_PATH]; if (hItem) tvi.hItem = TreeView_GetChild(hwndTree, hItem); else tvi.hItem = TreeView_GetRoot(hwndTree); if ( !name) return tvi.hItem; tvi.mask = TVIF_TEXT; tvi.pszText = str; tvi.cchTextMax = MAX_PATH; while (tvi.hItem) { TreeView_GetItem(hwndTree, &tvi); if ( !lstrcmp(tvi.pszText, name)) return tvi.hItem; tvi.hItem = TreeView_GetNextSibling(hwndTree, tvi.hItem); } return NULL; } ///////////////////////////////////////////////////////////////////////////////////////// // icon import dialog's window procedure static int IconDlg_Resize(HWND, LPARAM, UTILRESIZECONTROL *urc) { switch (urc->wId) { case IDC_ICONSET: return RD_ANCHORX_WIDTH | RD_ANCHORY_TOP; case IDC_BROWSE: return RD_ANCHORX_RIGHT | RD_ANCHORY_TOP; case IDC_PREVIEW: return RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT; case IDC_GETMORE: return RD_ANCHORX_CENTRE | RD_ANCHORY_BOTTOM; } return RD_ANCHORX_LEFT | RD_ANCHORY_TOP; // default } INT_PTR CALLBACK DlgProcIconImport(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { static HWND hwndParent, hwndDragOver; static int dragging; static int dragItem, dropHiLite; static HWND hPreview = NULL; switch (msg) { case WM_INITDIALOG: TranslateDialogDefault(hwndDlg); hwndParent = (HWND)lParam; hPreview = GetDlgItem(hwndDlg, IDC_PREVIEW); dragging = dragItem = 0; ListView_SetImageList(hPreview, ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32|ILC_MASK, 0, 100), LVSIL_NORMAL); ListView_SetIconSpacing(hPreview, 56, 67); { RECT rcThis, rcParent; int cxScreen = GetSystemMetrics(SM_CXSCREEN); GetWindowRect(hwndDlg, &rcThis); GetWindowRect(hwndParent, &rcParent); OffsetRect(&rcThis, rcParent.right-rcThis.left, 0); OffsetRect(&rcThis, 0, rcParent.top-rcThis.top); GetWindowRect(GetParent(hwndParent), &rcParent); if (rcThis.right > cxScreen) { OffsetRect(&rcParent, cxScreen-rcThis.right, 0); OffsetRect(&rcThis, cxScreen-rcThis.right, 0); MoveWindow(GetParent(hwndParent), rcParent.left, rcParent.top, rcParent.right-rcParent.left, rcParent.bottom-rcParent.top, TRUE); } MoveWindow(hwndDlg, rcThis.left, rcThis.top, rcThis.right-rcThis.left, rcThis.bottom-rcThis.top, FALSE); GetClientRect(hwndDlg, &rcThis); SendMessage(hwndDlg, WM_SIZE, 0, MAKELPARAM(rcThis.right-rcThis.left, rcThis.bottom-rcThis.top)); } if (shAutoComplete) shAutoComplete(GetDlgItem(hwndDlg, IDC_ICONSET), 1); SetDlgItemText(hwndDlg, IDC_ICONSET, _T("icons.dll")); return TRUE; case DM_REBUILDICONSPREVIEW: { LVITEM lvi; TCHAR filename[MAX_PATH], caption[64]; HIMAGELIST hIml; int count, i; HICON hIcon; MySetCursor(IDC_WAIT); ListView_DeleteAllItems(hPreview); hIml = ListView_GetImageList(hPreview, LVSIL_NORMAL); ImageList_RemoveAll(hIml); GetDlgItemText(hwndDlg, IDC_ICONSET, filename, SIZEOF(filename)); { RECT rcPreview, rcGroup; GetWindowRect(hPreview, &rcPreview); GetWindowRect(GetDlgItem(hwndDlg, IDC_IMPORTMULTI), &rcGroup); //SetWindowPos(hPreview, 0, 0, 0, rcPreview.right-rcPreview.left, rcGroup.bottom-rcPreview.top, SWP_NOZORDER|SWP_NOMOVE); } if (_taccess(filename, 0) != 0) { MySetCursor(IDC_ARROW); break; } lvi.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM; lvi.iSubItem = 0; lvi.iItem = 0; count = (int)_ExtractIconEx(filename, -1, 16, 16, NULL, LR_DEFAULTCOLOR); for (i = 0; i < count; lvi.iItem++, i++) { mir_sntprintf(caption, SIZEOF(caption), _T("%d"), i+1); lvi.pszText = caption; //hIcon = ExtractIcon(hMirandaInst, filename, i); _ExtractIconEx(filename, i, 16, 16, &hIcon, LR_DEFAULTCOLOR); lvi.iImage = ImageList_AddIcon(hIml, hIcon); DestroyIcon(hIcon); lvi.lParam = i; ListView_InsertItem(hPreview, &lvi); } MySetCursor(IDC_ARROW); } break; case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_BROWSE: { TCHAR str[MAX_PATH]; TCHAR *file; GetDlgItemText(hwndDlg, IDC_ICONSET, str, SIZEOF(str)); if ( !(file = OpenFileDlg(GetParent(hwndDlg), str, TRUE))) break; SetDlgItemText(hwndDlg, IDC_ICONSET, file); SAFE_FREE((void**)&file); } break; case IDC_GETMORE: OpenIconsPage(); break; case IDC_ICONSET: if (HIWORD(wParam) == EN_CHANGE) SendMessage(hwndDlg, DM_REBUILDICONSPREVIEW, 0, 0); break; } break; case WM_MOUSEMOVE: if (dragging) { LVHITTESTINFO lvhti; int onItem=0; HWND hwndOver; RECT rc; POINT ptDrag; HWND hPPreview = GetDlgItem(hwndParent, IDC_PREVIEW); lvhti.pt.x = (short)LOWORD(lParam); lvhti.pt.y = (short)HIWORD(lParam); ClientToScreen(hwndDlg, &lvhti.pt); hwndOver = WindowFromPoint(lvhti.pt); GetWindowRect(hwndOver, &rc); ptDrag.x = lvhti.pt.x - rc.left; ptDrag.y = lvhti.pt.y - rc.top; if (hwndOver != hwndDragOver) { ImageList_DragLeave(hwndDragOver); hwndDragOver = hwndOver; ImageList_DragEnter(hwndDragOver, ptDrag.x, ptDrag.y); } ImageList_DragMove(ptDrag.x, ptDrag.y); if (hwndOver == hPPreview) { ScreenToClient(hPPreview, &lvhti.pt); if (ListView_HitTest(hPPreview, &lvhti) != -1) { if (lvhti.iItem != dropHiLite) { ImageList_DragLeave(hwndDragOver); if (dropHiLite != -1) ListView_SetItemState(hPPreview, dropHiLite, 0, LVIS_DROPHILITED); dropHiLite = lvhti.iItem; ListView_SetItemState(hPPreview, dropHiLite, LVIS_DROPHILITED, LVIS_DROPHILITED); UpdateWindow(hPPreview); ImageList_DragEnter(hwndDragOver, ptDrag.x, ptDrag.y); } onItem = 1; } } if ( !onItem && dropHiLite != -1) { ImageList_DragLeave(hwndDragOver); ListView_SetItemState(hPPreview, dropHiLite, 0, LVIS_DROPHILITED); UpdateWindow(hPPreview); ImageList_DragEnter(hwndDragOver, ptDrag.x, ptDrag.y); dropHiLite = -1; } MySetCursor(onItem ? IDC_ARROW : IDC_NO); } break; case WM_LBUTTONUP: if (dragging) { ReleaseCapture(); ImageList_EndDrag(); dragging = 0; if (dropHiLite != -1) { TCHAR path[MAX_PATH], fullPath[MAX_PATH], filename[MAX_PATH]; LVITEM lvi; GetDlgItemText(hwndDlg, IDC_ICONSET, fullPath, SIZEOF(fullPath)); CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)fullPath, (LPARAM)filename); lvi.mask=LVIF_PARAM; lvi.iItem = dragItem; lvi.iSubItem = 0; ListView_GetItem(hPreview, &lvi); mir_sntprintf(path, MAX_PATH, _T("%s,%d"), filename, (int)lvi.lParam); SendMessage(hwndParent, DM_CHANGEICON, dropHiLite, (LPARAM)path); ListView_SetItemState(GetDlgItem(hwndParent, IDC_PREVIEW), dropHiLite, 0, LVIS_DROPHILITED); } } break; case WM_NOTIFY: switch (((LPNMHDR)lParam)->idFrom) { case IDC_PREVIEW: switch (((LPNMHDR)lParam)->code) { case LVN_BEGINDRAG: SetCapture(hwndDlg); dragging = 1; dragItem = ((LPNMLISTVIEW)lParam)->iItem; dropHiLite = -1; ImageList_BeginDrag(ListView_GetImageList(hPreview, LVSIL_NORMAL), dragItem, GetSystemMetrics(SM_CXICON)/2, GetSystemMetrics(SM_CYICON)/2); { POINT pt; RECT rc; GetCursorPos(&pt); GetWindowRect(hwndDlg, &rc); ImageList_DragEnter(hwndDlg, pt.x - rc.left, pt.y - rc.top); hwndDragOver = hwndDlg; } break; } break; } break; case WM_CLOSE: DestroyWindow(hwndDlg); EnableWindow(GetDlgItem(hwndParent, IDC_IMPORT), TRUE); break; case WM_SIZE: { // make the dlg resizeable UTILRESIZEDIALOG urd = {0}; if (IsIconic(hwndDlg)) break; urd.cbSize = sizeof(urd); urd.hInstance = hMirandaInst; urd.hwndDlg = hwndDlg; urd.lParam = 0; // user-defined urd.lpTemplate = MAKEINTRESOURCEA(IDD_ICOLIB_IMPORT); urd.pfnResizer = IconDlg_Resize; CallService(MS_UTILS_RESIZEDIALOG, 0, (LPARAM)&urd); break; } } return FALSE; } ///////////////////////////////////////////////////////////////////////////////////////// // IcoLib options window procedure static int CALLBACK DoSortIconsFunc(LPARAM lParam1, LPARAM lParam2, LPARAM) { return lstrcmpi(TranslateTS(iconList[lParam1]->description), TranslateTS(iconList[lParam2]->description)); } static int CALLBACK DoSortIconsFuncByOrder(LPARAM lParam1, LPARAM lParam2, LPARAM) { return iconList[lParam1]->orderID - iconList[lParam2]->orderID; } static void SaveCollapseState(HWND hwndTree) { HTREEITEM hti; TVITEM tvi; hti = TreeView_GetRoot(hwndTree); while (hti != NULL) { HTREEITEM ht; TreeItem *treeItem; tvi.mask = TVIF_STATE | TVIF_HANDLE | TVIF_CHILDREN | TVIF_PARAM; tvi.hItem = hti; tvi.stateMask = (DWORD)-1; TreeView_GetItem(hwndTree, &tvi); if (tvi.cChildren > 0) { treeItem = (TreeItem *)tvi.lParam; if (tvi.state & TVIS_EXPANDED) DBWriteContactSettingByte(NULL, "SkinIconsUI", treeItem->paramName, TVIS_EXPANDED); else DBWriteContactSettingByte(NULL, "SkinIconsUI", treeItem->paramName, 0); } ht = TreeView_GetChild(hwndTree, hti); if (ht == NULL) { ht = TreeView_GetNextSibling(hwndTree, hti); while (ht == NULL) { hti = TreeView_GetParent(hwndTree, hti); if (hti == NULL) break; ht = TreeView_GetNextSibling(hwndTree, hti); } } hti = ht; } } INT_PTR CALLBACK DlgProcIcoLibOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { struct IcoLibOptsData *dat; static HTREEITEM prevItem = 0; static HWND hPreview = NULL; dat = (struct IcoLibOptsData*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA); switch (msg) { case WM_INITDIALOG: TranslateDialogDefault(hwndDlg); hPreview = GetDlgItem(hwndDlg, IDC_PREVIEW); dat = (struct IcoLibOptsData*)mir_alloc(sizeof(struct IcoLibOptsData)); dat->hwndIndex = NULL; SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)dat); // // Reset temporary data & upload sections list // EnterCriticalSection(&csIconList); { int indx; for (indx = 0; indx < iconList.getCount(); indx++) { iconList[indx]->temp_file = NULL; iconList[indx]->temp_icon = NULL; iconList[indx]->temp_reset = FALSE; } bNeedRebuild = FALSE; } LeaveCriticalSection(&csIconList); // // Setup preview listview // ListView_SetUnicodeFormat(hPreview, TRUE); ListView_SetExtendedListViewStyleEx(hPreview, LVS_EX_INFOTIP, LVS_EX_INFOTIP); ListView_SetImageList(hPreview, ImageList_Create(GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), ILC_COLOR32|ILC_MASK, 0, 30), LVSIL_NORMAL); ListView_SetIconSpacing(hPreview, 56, 67); SendMessage(hwndDlg, DM_REBUILD_CTREE, 0, 0); return TRUE; case DM_REBUILD_CTREE: { HWND hwndTree = GetDlgItem(hwndDlg, IDC_CATEGORYLIST); int indx; TCHAR itemName[1024]; HTREEITEM hSection; if ( !hwndTree) break; TreeView_SelectItem(hwndTree, NULL); TreeView_DeleteAllItems(hwndTree); for (indx = 0; indx < sectionList.getCount(); indx++) { TCHAR* sectionName; int sectionLevel = 0; hSection = NULL; lstrcpy(itemName, sectionList[indx]->name); sectionName = itemName; while (sectionName) { // allow multi-level tree TCHAR* pItemName = sectionName; HTREEITEM hItem; if (sectionName = _tcschr(sectionName, '/')) { // one level deeper *sectionName = 0; } pItemName = TranslateTS(pItemName); hItem = FindNamedTreeItemAt(hwndTree, hSection, pItemName); if ( !sectionName || !hItem) { if ( !hItem) { TVINSERTSTRUCT tvis = {0}; TreeItem *treeItem = (TreeItem *)mir_alloc(sizeof(TreeItem)); treeItem->value = SECTIONPARAM_MAKE(indx, sectionLevel, sectionName?0:SECTIONPARAM_HAVEPAGE); treeItem->paramName = mir_t2a(itemName); tvis.hParent = hSection; tvis.hInsertAfter = TVI_SORT; //TVI_LAST; tvis.item.mask = TVIF_TEXT|TVIF_PARAM|TVIF_STATE; tvis.item.pszText = pItemName; tvis.item.lParam = (LPARAM) treeItem; tvis.item.state = tvis.item.stateMask = DBGetContactSettingByte(NULL, "SkinIconsUI", treeItem->paramName, TVIS_EXPANDED); hItem = TreeView_InsertItem(hwndTree, &tvis); } else { TVITEM tvi = {0}; TreeItem *treeItem; tvi.hItem = hItem; tvi.mask = TVIF_HANDLE | TVIF_PARAM; TreeView_GetItem(hwndTree, &tvi); treeItem = (TreeItem *)tvi.lParam; treeItem->value = SECTIONPARAM_MAKE(indx, sectionLevel, SECTIONPARAM_HAVEPAGE); } } if (sectionName) { *sectionName = '/'; sectionName++; } sectionLevel++; hSection = hItem; } } ShowWindow(hwndTree, SW_SHOW); TreeView_SelectItem(hwndTree, FindNamedTreeItemAt(hwndTree, 0, NULL)); } break; // Rebuild preview to new section case DM_REBUILDICONSPREVIEW: { LVITEM lvi = {0}; HIMAGELIST hIml; HICON hIcon; SectionItem* sectionActive = (SectionItem*)lParam; int indx; EnableWindow(hPreview, sectionActive != NULL); ListView_DeleteAllItems(hPreview); hIml = ListView_GetImageList(hPreview, LVSIL_NORMAL); ImageList_RemoveAll(hIml); if (sectionActive == NULL) break; lvi.mask = LVIF_TEXT|LVIF_IMAGE|LVIF_PARAM; EnterCriticalSection(&csIconList); for (indx = 0; indx < iconList.getCount(); indx++) { IconItem *item = iconList[indx]; if (item->section == sectionActive) { lvi.pszText = TranslateTS(item->description); hIcon = item->temp_icon; if ( !hIcon) hIcon = IconItem_GetIcon_Preview(item); lvi.iImage = ImageList_AddIcon(hIml, hIcon); lvi.lParam = indx; ListView_InsertItem(hPreview, &lvi); if (hIcon != item->temp_icon) SafeDestroyIcon(&hIcon); } } LeaveCriticalSection(&csIconList); if (sectionActive->flags & SIDF_SORTED) ListView_SortItems(hPreview, DoSortIconsFunc, 0); else ListView_SortItems(hPreview, DoSortIconsFuncByOrder, 0); } break; // Refresh preview to new section case DM_UPDATEICONSPREVIEW: { LVITEM lvi = {0}; HICON hIcon; int indx, count; HIMAGELIST hIml = ListView_GetImageList(hPreview, LVSIL_NORMAL); lvi.mask = LVIF_IMAGE|LVIF_PARAM; count = ListView_GetItemCount(hPreview); for (indx = 0; indx < count; indx++) { lvi.iItem = indx; ListView_GetItem(hPreview, &lvi); EnterCriticalSection(&csIconList); hIcon = iconList[lvi.lParam]->temp_icon; if ( !hIcon) hIcon = IconItem_GetIcon_Preview(iconList[lvi.lParam]); LeaveCriticalSection(&csIconList); if (hIcon) ImageList_ReplaceIcon(hIml, lvi.iImage, hIcon); if (hIcon != iconList[lvi.lParam]->temp_icon) SafeDestroyIcon(&hIcon); } ListView_RedrawItems(hPreview, 0, count); } break; // Temporary change icon - only inside options dialog case DM_CHANGEICON: { TCHAR *path=(TCHAR*)lParam; LVITEM lvi = {0}; IconItem *item; lvi.mask = LVIF_PARAM; lvi.iItem = wParam; ListView_GetItem(hPreview, &lvi); EnterCriticalSection(&csIconList); item = iconList[ lvi.lParam ]; SAFE_FREE((void**)&item->temp_file); SafeDestroyIcon(&item->temp_icon); item->temp_file = mir_tstrdup(path); item->temp_icon = (HICON)ExtractIconFromPath(path, item->cx, item->cy); item->temp_reset = FALSE; LeaveCriticalSection(&csIconList); DoOptionsChanged(hwndDlg); } break; case WM_COMMAND: if (LOWORD(wParam) == IDC_IMPORT) { dat->hwndIndex = CreateDialogParam(hMirandaInst, MAKEINTRESOURCE(IDD_ICOLIB_IMPORT), GetParent(hwndDlg), DlgProcIconImport, (LPARAM)hwndDlg); EnableWindow((HWND)lParam, FALSE); } else if (LOWORD(wParam) == IDC_GETMORE) { OpenIconsPage(); break; } else if (LOWORD(wParam) == IDC_LOADICONS) { TCHAR filetmp[1] = {0}; TCHAR *file; if (file = OpenFileDlg(hwndDlg, filetmp, FALSE)) { HWND htv = GetDlgItem(hwndDlg, IDC_CATEGORYLIST); TCHAR filename[ MAX_PATH ]; CallService(MS_UTILS_PATHTORELATIVET, (WPARAM)file, (LPARAM)filename); SAFE_FREE((void**)&file); MySetCursor(IDC_WAIT); LoadSubIcons(htv, filename, TreeView_GetSelection(htv)); MySetCursor(IDC_ARROW); DoOptionsChanged(hwndDlg); } } break; case WM_CONTEXTMENU: if ((HWND)wParam == hPreview) { UINT count = ListView_GetSelectedCount(hPreview); if (count > 0) { int cmd = OpenPopupMenu(hwndDlg); switch(cmd) { case ID_CANCELCHANGE: case ID_RESET: { LVITEM lvi = {0}; int itemIndx = -1; while ((itemIndx = ListView_GetNextItem(hPreview, itemIndx, LVNI_SELECTED)) != -1) { lvi.mask = LVIF_PARAM; lvi.iItem = itemIndx; //lvhti.iItem; ListView_GetItem(hPreview, &lvi); UndoChanges(lvi.lParam, cmd); } DoOptionsChanged(hwndDlg); break; } } } } else { HWND htv = GetDlgItem(hwndDlg, IDC_CATEGORYLIST); if ((HWND)wParam == htv) { int cmd = OpenPopupMenu(hwndDlg); switch(cmd) { case ID_CANCELCHANGE: case ID_RESET: UndoSubItemChanges(htv, TreeView_GetSelection(htv), cmd); DoOptionsChanged(hwndDlg); break; } } } break; case WM_NOTIFY: switch(((LPNMHDR)lParam)->idFrom) { case 0: switch(((LPNMHDR)lParam)->code) { case PSN_APPLY: { int indx; EnterCriticalSection(&csIconList); for (indx = 0; indx < iconList.getCount(); indx++) { IconItem *item = iconList[indx]; if (item->temp_reset) { DBDeleteContactSetting(NULL, "SkinIcons", item->name); if (item->source_small != item->default_icon) { IconSourceItem_Release(&item->source_small); } } else if (item->temp_file) { DBWriteContactSettingTString(NULL, "SkinIcons", item->name, item->temp_file); IconSourceItem_Release(&item->source_small); SafeDestroyIcon(&item->temp_icon); } } LeaveCriticalSection(&csIconList); DoIconsChanged(hwndDlg); return TRUE; } } break; case IDC_PREVIEW: if (((LPNMHDR)lParam)->code == LVN_GETINFOTIP) { IconItem *item; NMLVGETINFOTIP *pInfoTip = (NMLVGETINFOTIP *)lParam; LVITEM lvi; lvi.mask = LVIF_PARAM; lvi.iItem = pInfoTip->iItem; ListView_GetItem(pInfoTip->hdr.hwndFrom, &lvi); if (lvi.lParam < iconList.getCount()) { item = iconList[lvi.lParam]; if (item->temp_file) _tcsncpy(pInfoTip->pszText, item->temp_file, pInfoTip->cchTextMax); else if (item->default_file) mir_sntprintf(pInfoTip->pszText, pInfoTip->cchTextMax, _T("%s, %d"), item->default_file, item->default_indx); } } if (bNeedRebuild) { EnterCriticalSection(&csIconList); bNeedRebuild = FALSE; LeaveCriticalSection(&csIconList); SendMessage(hwndDlg, DM_REBUILD_CTREE, 0, 0); } break; case IDC_CATEGORYLIST: switch(((NMHDR*)lParam)->code) { case TVN_SELCHANGEDA: // !!!! This needs to be here - both !! case TVN_SELCHANGEDW: { NMTREEVIEW *pnmtv = (NMTREEVIEW*)lParam; TVITEM tvi = pnmtv->itemNew; TreeItem *treeItem = (TreeItem *)tvi.lParam; if (treeItem) SendMessage(hwndDlg, DM_REBUILDICONSPREVIEW, 0, (LPARAM)( (SECTIONPARAM_FLAGS(treeItem->value)&SECTIONPARAM_HAVEPAGE)? sectionList[ SECTIONPARAM_INDEX(treeItem->value) ] : NULL)); break; } case TVN_DELETEITEMA: // no idea why both TVN_SELCHANGEDA/W should be there but let's keep this both too... case TVN_DELETEITEMW: { TreeItem *treeItem = (TreeItem *)(((LPNMTREEVIEW)lParam)->itemOld.lParam); if (treeItem) { mir_free(treeItem->paramName); mir_free(treeItem); } break; } } if (bNeedRebuild) { EnterCriticalSection(&csIconList); bNeedRebuild = FALSE; LeaveCriticalSection(&csIconList); SendMessage(hwndDlg, DM_REBUILD_CTREE, 0, 0); } } break; case WM_DESTROY: { int indx; SaveCollapseState(GetDlgItem(hwndDlg, IDC_CATEGORYLIST)); DestroyWindow(dat->hwndIndex); EnterCriticalSection(&csIconList); for (indx = 0; indx < iconList.getCount(); indx++) { IconItem *item = iconList[indx]; SAFE_FREE((void**)&item->temp_file); SafeDestroyIcon(&item->temp_icon); } LeaveCriticalSection(&csIconList); SAFE_FREE((void**)&dat); break; } } return FALSE; } static UINT iconsExpertOnlyControls[]={IDC_IMPORT}; static int SkinOptionsInit(WPARAM wParam, LPARAM) { OPTIONSDIALOGPAGE odp = {0}; odp.cbSize = sizeof(odp); odp.hInstance = hMirandaInst; odp.flags = ODPF_BOLDGROUPS; odp.position = -180000000; odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_ICOLIB); odp.pszTitle = LPGEN("Icons"); odp.pszGroup = LPGEN("Customize"); odp.pfnDlgProc = DlgProcIcoLibOpts; odp.expertOnlyControls = iconsExpertOnlyControls; odp.nExpertOnlyControls = SIZEOF(iconsExpertOnlyControls); Options_AddPage(wParam, &odp); return 0; } static int SkinSystemModulesLoaded(WPARAM, LPARAM) { HookEvent(ME_OPT_INITIALISE, SkinOptionsInit); return 0; } ///////////////////////////////////////////////////////////////////////////////////////// // Module initialization and finalization procedure static INT_PTR sttIcoLib_AddNewIcon(WPARAM, LPARAM lParam) { return (INT_PTR)IcoLib_AddNewIcon((SKINICONDESC*)lParam); } static INT_PTR sttIcoLib_GetIcon(WPARAM wParam, LPARAM lParam) { return (INT_PTR)IcoLib_GetIcon((const char*)lParam, wParam != 0); } static INT_PTR sttIcoLib_GetIconHandle(WPARAM, LPARAM lParam) { return (INT_PTR)IcoLib_GetIconHandle((const char*)lParam); } static INT_PTR sttIcoLib_GetIconByHandle(WPARAM wParam, LPARAM lParam) { return (INT_PTR)IcoLib_GetIconByHandle((HANDLE)lParam, wParam != 0); } static INT_PTR sttIcoLib_ReleaseIcon(WPARAM wParam, LPARAM lParam) { return (INT_PTR)IcoLib_ReleaseIcon((HICON)wParam, (char*)lParam, false); } static INT_PTR sttIcoLib_ReleaseIconBig(WPARAM wParam, LPARAM lParam) { return (INT_PTR)IcoLib_ReleaseIcon((HICON)wParam, (char*)lParam, true); } static INT_PTR sttIcoLib_IsManaged(WPARAM wParam, LPARAM) { return (INT_PTR)IcoLib_IsManaged((HICON)wParam); } int LoadIcoLibModule(void) { bModuleInitialized = TRUE; hIconBlank = LoadIconEx(NULL, MAKEINTRESOURCE(IDI_BLANK), 0); InitializeCriticalSection(&csIconList); hIcoLib_AddNewIcon = CreateServiceFunction(MS_SKIN2_ADDICON, sttIcoLib_AddNewIcon); hIcoLib_RemoveIcon = CreateServiceFunction(MS_SKIN2_REMOVEICON, IcoLib_RemoveIcon); hIcoLib_GetIcon = CreateServiceFunction(MS_SKIN2_GETICON, sttIcoLib_GetIcon); hIcoLib_GetIconHandle = CreateServiceFunction(MS_SKIN2_GETICONHANDLE, sttIcoLib_GetIconHandle); hIcoLib_GetIcon2 = CreateServiceFunction(MS_SKIN2_GETICONBYHANDLE, sttIcoLib_GetIconByHandle); hIcoLib_IsManaged = CreateServiceFunction(MS_SKIN2_ISMANAGEDICON, sttIcoLib_IsManaged); hIcoLib_AddRef = CreateServiceFunction(MS_SKIN2_ADDREFICON, IcoLib_AddRef); hIcoLib_ReleaseIcon = CreateServiceFunction(MS_SKIN2_RELEASEICON, sttIcoLib_ReleaseIcon); hIcoLib_ReleaseIcon = CreateServiceFunction(MS_SKIN2_RELEASEICONBIG, sttIcoLib_ReleaseIconBig); hIcons2ChangedEvent = CreateHookableEvent(ME_SKIN2_ICONSCHANGED); hIconsChangedEvent = CreateHookableEvent(ME_SKIN_ICONSCHANGED); HookEvent(ME_SYSTEM_MODULESLOADED, SkinSystemModulesLoaded); return 0; } void UnloadIcoLibModule(void) { int indx; if ( !bModuleInitialized) return; DestroyHookableEvent(hIconsChangedEvent); DestroyHookableEvent(hIcons2ChangedEvent); DestroyServiceFunction(hIcoLib_AddNewIcon); DestroyServiceFunction(hIcoLib_RemoveIcon); DestroyServiceFunction(hIcoLib_GetIcon); DestroyServiceFunction(hIcoLib_GetIconHandle); DestroyServiceFunction(hIcoLib_GetIcon2); DestroyServiceFunction(hIcoLib_IsManaged); DestroyServiceFunction(hIcoLib_AddRef); DestroyServiceFunction(hIcoLib_ReleaseIcon); DeleteCriticalSection(&csIconList); for (indx = iconList.getCount()-1; indx >= 0; indx--) { IconItem* I = iconList[indx]; iconList.remove(indx); IcoLib_FreeIcon(I); mir_free(I); } iconList.destroy(); for (indx = iconSourceList.getCount()-1; indx >= 0; indx--) { IconSourceItem* I = iconSourceList[indx]; iconSourceList.remove(indx); IconSourceFile_Release(&I->file); SafeDestroyIcon(&I->icon); SAFE_FREE((void**)&I->icon_data); SAFE_FREE((void**)&I); } iconSourceList.destroy(); for (indx = iconSourceFileList.getCount()-1; indx >= 0; indx--) { IconSourceFile* I = iconSourceFileList[indx]; iconSourceFileList.remove(indx); SAFE_FREE((void**)&I->file); SAFE_FREE((void**)&I); } iconSourceFileList.destroy(); for (indx = 0; indx < sectionList.getCount(); indx++) { SAFE_FREE((void**)§ionList[indx]->name); mir_free(sectionList[indx]); } sectionList.destroy(); SafeDestroyIcon(&hIconBlank); }