#include #include "systemimagelist.h" #ifdef __WXMSW__ #include "shlobj.h" // Once again lots of the shell stuff is missing in MinGW's headers #ifndef IDO_SHGIOI_LINK #define IDO_SHGIOI_LINK 0x0FFFFFFE #endif #ifndef SHGetIconOverlayIndex extern "C" int WINAPI SHGetIconOverlayIndexW(LPCWSTR pszIconPath, int iIconIndex); extern "C" int WINAPI SHGetIconOverlayIndexA(LPCSTR pszIconPath, int iIconIndex); #define SHGetIconOverlayIndex SHGetIconOverlayIndexW #endif #endif #ifndef __WXMSW__ #include "themeprovider.h" #include #endif wxImageListEx::wxImageListEx() : wxImageList() { } wxImageListEx::wxImageListEx(int width, int height, const bool mask /*=true*/, int initialCount /*=1*/) : wxImageList(width, height, mask, initialCount) { } #ifdef __WXMSW__ HIMAGELIST wxImageListEx::Detach() { HIMAGELIST hImageList = (HIMAGELIST)m_hImageList; m_hImageList = 0; return hImageList; } #endif #ifndef __WXMSW__ static inline void AlphaComposite_Over_Inplace(wxAlphaPixelData::Iterator &a, wxAlphaPixelData::Iterator &b) { // Alpha compositing of a single pixel, b gets composited over a // (well-known over operator), result stored in a. // All RGB and A values have range from 0 to 255, RGB values aren't // premultiplied by A. // Safe for multiple compositions. if (!b.Alpha()) { // Nothing to do return; } int new_alpha = a.Alpha() + b.Alpha() - a.Alpha() * b.Alpha() / 255; // Could only get 0 if both alphas were 0, caught that already. a.Red() = ((int)a.Red() * (255 - b.Alpha()) * a.Alpha() / 255 + (int)b.Red() * b.Alpha()) / new_alpha; a.Green() = ((int)a.Green() * (255 - b.Alpha()) * a.Alpha() / 255 + (int)b.Green() * b.Alpha()) / new_alpha; a.Blue() = ((int)a.Blue() * (255 - b.Alpha()) * a.Alpha() / 255 + (int)b.Blue() * b.Alpha()) / new_alpha; a.Alpha() = new_alpha; } static void OverlaySymlink(wxBitmap& bmp) { // This is ugly, but apparently needed so that the data is _really_ in the right internal format bmp = bmp.ConvertToImage(); wxBitmap symlink = wxArtProvider::GetBitmap(_T("ART_SYMLINK"), wxART_OTHER, wxSize(bmp.GetWidth(), bmp.GetHeight())).ConvertToImage(); wxAlphaPixelData target(bmp); wxAlphaPixelData source(symlink); int sx = bmp.GetWidth(); if (symlink.GetWidth() < sx) sx = symlink.GetWidth(); int sy = bmp.GetHeight(); if (symlink.GetHeight() < sy) sy = symlink.GetHeight(); // Do some rudimentary alpha copying wxAlphaPixelData::Iterator t(target); wxAlphaPixelData::Iterator s(source); for (int y = 0; y < sy; y++) { s.MoveTo(source, 0, y); t.MoveTo(target, 0, y); for (int x = 0; x < sx; x++, s++, t++) AlphaComposite_Over_Inplace(t, s); } } #endif CSystemImageList::CSystemImageList(int size /*=-1*/) : m_pImageList() { if (size != -1) CreateSystemImageList(size); } bool CSystemImageList::CreateSystemImageList(int size) { if (m_pImageList) return true; #ifdef __WXMSW__ SHFILEINFO shFinfo{}; wxChar buffer[MAX_PATH + 10]; if (!GetWindowsDirectory(buffer, MAX_PATH)) #ifdef _tcscpy _tcscpy(buffer, _T("C:\\")); #else strcpy(buffer, _T("C:\\")); #endif m_pImageList = new wxImageListEx((WXHIMAGELIST)SHGetFileInfo(buffer, 0, &shFinfo, sizeof( shFinfo ), SHGFI_SYSICONINDEX | ((size != 16) ? SHGFI_ICON : SHGFI_SMALLICON) )); #else m_pImageList = new wxImageListEx(size, size); wxBitmap file = wxArtProvider::GetBitmap(_T("ART_FILE"), wxART_OTHER, wxSize(size, size)); wxBitmap folderclosed = wxArtProvider::GetBitmap(_T("ART_FOLDERCLOSED"), wxART_OTHER, wxSize(size, size)); wxBitmap folder = wxArtProvider::GetBitmap(_T("ART_FOLDER"), wxART_OTHER, wxSize(size, size)); m_pImageList->Add(file); m_pImageList->Add(folderclosed); m_pImageList->Add(folder); OverlaySymlink(file); OverlaySymlink(folderclosed); OverlaySymlink(folder); m_pImageList->Add(file); m_pImageList->Add(folderclosed); m_pImageList->Add(folder); #endif return true; } CSystemImageList::~CSystemImageList() { if (!m_pImageList) return; #ifdef __WXMSW__ m_pImageList->Detach(); #endif delete m_pImageList; m_pImageList = 0; } #ifndef __WXMSW__ // This function converts to the right size with the given background colour wxBitmap PrepareIcon(wxIcon icon, wxSize size) { if (icon.GetWidth() == size.GetWidth() && icon.GetHeight() == size.GetHeight()) return icon; wxBitmap bmp; bmp.CopyFromIcon(icon); return bmp.ConvertToImage().Rescale(size.GetWidth(), size.GetHeight()); } #endif int CSystemImageList::GetIconIndex(iconType type, const wxString& fileName /*=_T("")*/, bool physical /*=true*/, bool symlink /*=false*/) { if (!m_pImageList) return -1; #ifdef __WXMSW__ if (fileName.empty()) physical = false; SHFILEINFO shFinfo; memset(&shFinfo, 0, sizeof(SHFILEINFO)); if (SHGetFileInfo(!fileName.empty() ? fileName.wc_str() : _T("{B97D3074-1830-4b4a-9D8A-17A38B074052}"), (type != iconType::file) ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL, &shFinfo, sizeof(SHFILEINFO), SHGFI_ICON | ((type == iconType::opened_dir) ? SHGFI_OPENICON : 0) | ((physical) ? 0 : SHGFI_USEFILEATTRIBUTES) ) ) { int icon = shFinfo.iIcon; // we only need the index from the system image list DestroyIcon(shFinfo.hIcon); return icon; } #else (void)physical; int icon; switch (type) { case iconType::file: default: icon = symlink ? 3 : 0; break; case iconType::dir: return symlink ? 4 : 1; case iconType::opened_dir: return symlink ? 5 : 2; } wxFileName fn(fileName); wxString ext = fn.GetExt(); if (ext.empty()) return icon; if (symlink) { auto cacheIter = m_iconCache.find(ext); if (cacheIter != m_iconCache.end()) return cacheIter->second; } else { auto cacheIter = m_iconSymlinkCache.find(ext); if (cacheIter != m_iconSymlinkCache.end()) return cacheIter->second; } wxFileType *pType = wxTheMimeTypesManager->GetFileTypeFromExtension(ext); if (!pType) { m_iconCache[ext] = icon; return icon; } wxIconLocation loc; if (pType->GetIcon(&loc) && loc.IsOk()) { wxLogNull nul; wxIcon newIcon(loc); if (newIcon.Ok()) { wxBitmap bmp = PrepareIcon(newIcon, CThemeProvider::GetIconSize(iconSizeSmall)); if (symlink) OverlaySymlink(bmp); int index = m_pImageList->Add(bmp); if (index > 0) icon = index; } } delete pType; if (symlink) m_iconCache[ext] = icon; else m_iconSymlinkCache[ext] = icon; return icon; #endif return -1; } #ifdef __WXMSW__ int CSystemImageList::GetLinkOverlayIndex() { static int overlay = -1; if (overlay == -1) { overlay = SHGetIconOverlayIndex(0, IDO_SHGIOI_LINK); if (overlay < 0) overlay = 0; } return overlay; } #endif