1// SysIconUtils.cpp
2
3#include "StdAfx.h"
4
5#ifndef _UNICODE
6#include "../../../Common/StringConvert.h"
7#endif
8
9#include "../../../Windows/FileDir.h"
10
11#include "SysIconUtils.h"
12
13#if defined(__MINGW32__) || defined(__MINGW64__)
14#include <shlobj.h>
15#else
16#include <ShlObj.h>
17#endif
18
19#ifndef _UNICODE
20extern bool g_IsNT;
21#endif
22
23int GetIconIndexForCSIDL(int csidl)
24{
25  LPITEMIDLIST pidl = NULL;
26  SHGetSpecialFolderLocation(NULL, csidl, &pidl);
27  if (pidl)
28  {
29    SHFILEINFO shellInfo;
30    shellInfo.iIcon = 0;
31    const DWORD_PTR res = SHGetFileInfo((LPCTSTR)(const void *)(pidl), FILE_ATTRIBUTE_NORMAL,
32        &shellInfo, sizeof(shellInfo),
33        SHGFI_PIDL | SHGFI_SYSICONINDEX);
34    /*
35    IMalloc *pMalloc;
36    SHGetMalloc(&pMalloc);
37    if (pMalloc)
38    {
39      pMalloc->Free(pidl);
40      pMalloc->Release();
41    }
42    */
43    // we use OLE2.dll function here
44    CoTaskMemFree(pidl);
45    if (res)
46      return shellInfo.iIcon;
47  }
48  return 0;
49}
50
51#ifndef _UNICODE
52typedef DWORD_PTR (WINAPI * Func_SHGetFileInfoW)(LPCWSTR pszPath, DWORD attrib, SHFILEINFOW *psfi, UINT cbFileInfo, UINT uFlags);
53
54static struct C_SHGetFileInfo_Init
55{
56  Func_SHGetFileInfoW f_SHGetFileInfoW;
57  C_SHGetFileInfo_Init()
58  {
59       f_SHGetFileInfoW = Z7_GET_PROC_ADDRESS(
60    Func_SHGetFileInfoW, ::GetModuleHandleW(L"shell32.dll"),
61        "SHGetFileInfoW");
62  }
63} g_SHGetFileInfo_Init;
64#endif
65
66static DWORD_PTR My_SHGetFileInfoW(LPCWSTR pszPath, DWORD attrib, SHFILEINFOW *psfi, UINT cbFileInfo, UINT uFlags)
67{
68  #ifdef _UNICODE
69  return SHGetFileInfo
70  #else
71  if (!g_SHGetFileInfo_Init.f_SHGetFileInfoW)
72    return 0;
73  return g_SHGetFileInfo_Init.f_SHGetFileInfoW
74  #endif
75  (pszPath, attrib, psfi, cbFileInfo, uFlags);
76}
77
78DWORD_PTR GetRealIconIndex(CFSTR path, DWORD attrib, int &iconIndex)
79{
80  #ifndef _UNICODE
81  if (!g_IsNT)
82  {
83    SHFILEINFO shellInfo;
84    const DWORD_PTR res = ::SHGetFileInfo(fs2fas(path), FILE_ATTRIBUTE_NORMAL | attrib, &shellInfo,
85      sizeof(shellInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX);
86    iconIndex = shellInfo.iIcon;
87    return res;
88  }
89  else
90  #endif
91  {
92    SHFILEINFOW shellInfo;
93    const DWORD_PTR res = ::My_SHGetFileInfoW(fs2us(path), FILE_ATTRIBUTE_NORMAL | attrib, &shellInfo,
94      sizeof(shellInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX);
95    iconIndex = shellInfo.iIcon;
96    return res;
97  }
98}
99
100/*
101DWORD_PTR GetRealIconIndex(const UString &fileName, DWORD attrib, int &iconIndex, UString *typeName)
102{
103  #ifndef _UNICODE
104  if (!g_IsNT)
105  {
106    SHFILEINFO shellInfo;
107    shellInfo.szTypeName[0] = 0;
108    DWORD_PTR res = ::SHGetFileInfoA(GetSystemString(fileName), FILE_ATTRIBUTE_NORMAL | attrib, &shellInfo,
109        sizeof(shellInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_TYPENAME);
110    if (typeName)
111      *typeName = GetUnicodeString(shellInfo.szTypeName);
112    iconIndex = shellInfo.iIcon;
113    return res;
114  }
115  else
116  #endif
117  {
118    SHFILEINFOW shellInfo;
119    shellInfo.szTypeName[0] = 0;
120    DWORD_PTR res = ::My_SHGetFileInfoW(fileName, FILE_ATTRIBUTE_NORMAL | attrib, &shellInfo,
121        sizeof(shellInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_TYPENAME);
122    if (typeName)
123      *typeName = shellInfo.szTypeName;
124    iconIndex = shellInfo.iIcon;
125    return res;
126  }
127}
128*/
129
130static int FindInSorted_Attrib(const CRecordVector<CAttribIconPair> &vect, DWORD attrib, unsigned &insertPos)
131{
132  unsigned left = 0, right = vect.Size();
133  while (left != right)
134  {
135    const unsigned mid = (left + right) / 2;
136    const DWORD midAttrib = vect[mid].Attrib;
137    if (attrib == midAttrib)
138      return (int)mid;
139    if (attrib < midAttrib)
140      right = mid;
141    else
142      left = mid + 1;
143  }
144  insertPos = left;
145  return -1;
146}
147
148static int FindInSorted_Ext(const CObjectVector<CExtIconPair> &vect, const wchar_t *ext, unsigned &insertPos)
149{
150  unsigned left = 0, right = vect.Size();
151  while (left != right)
152  {
153    const unsigned mid = (left + right) / 2;
154    const int compare = MyStringCompareNoCase(ext, vect[mid].Ext);
155    if (compare == 0)
156      return (int)mid;
157    if (compare < 0)
158      right = mid;
159    else
160      left = mid + 1;
161  }
162  insertPos = left;
163  return -1;
164}
165
166int CExtToIconMap::GetIconIndex(DWORD attrib, const wchar_t *fileName /*, UString *typeName */)
167{
168  int dotPos = -1;
169  unsigned i;
170  for (i = 0;; i++)
171  {
172    const wchar_t c = fileName[i];
173    if (c == 0)
174      break;
175    if (c == '.')
176      dotPos = (int)i;
177  }
178
179  /*
180  if (MyStringCompareNoCase(fileName, L"$Recycle.Bin") == 0)
181  {
182    char s[256];
183    sprintf(s, "SPEC i = %3d, attr = %7x", _attribMap.Size(), attrib);
184    OutputDebugStringA(s);
185    OutputDebugStringW(fileName);
186  }
187  */
188
189  if ((attrib & FILE_ATTRIBUTE_DIRECTORY) != 0 || dotPos < 0)
190  {
191    unsigned insertPos = 0;
192    const int index = FindInSorted_Attrib(_attribMap, attrib, insertPos);
193    if (index >= 0)
194    {
195      // if (typeName) *typeName = _attribMap[index].TypeName;
196      return _attribMap[(unsigned)index].IconIndex;
197    }
198    CAttribIconPair pair;
199    GetRealIconIndex(
200        #ifdef UNDER_CE
201        FTEXT("\\")
202        #endif
203        FTEXT("__DIR__")
204        , attrib, pair.IconIndex
205        // , pair.TypeName
206        );
207
208    /*
209    char s[256];
210    sprintf(s, "i = %3d, attr = %7x", _attribMap.Size(), attrib);
211    OutputDebugStringA(s);
212    */
213
214    pair.Attrib = attrib;
215    _attribMap.Insert(insertPos, pair);
216    // if (typeName) *typeName = pair.TypeName;
217    return pair.IconIndex;
218  }
219
220  const wchar_t *ext = fileName + dotPos + 1;
221  unsigned insertPos = 0;
222  const int index = FindInSorted_Ext(_extMap, ext, insertPos);
223  if (index >= 0)
224  {
225    const CExtIconPair &pa = _extMap[index];
226    // if (typeName) *typeName = pa.TypeName;
227    return pa.IconIndex;
228  }
229
230  for (i = 0;; i++)
231  {
232    const wchar_t c = ext[i];
233    if (c == 0)
234      break;
235    if (c < L'0' || c > L'9')
236      break;
237  }
238  if (i != 0 && ext[i] == 0)
239  {
240    // GetRealIconIndex is too slow for big number of split extensions: .001, .002, .003
241    if (!SplitIconIndex_Defined)
242    {
243      GetRealIconIndex(
244          #ifdef UNDER_CE
245          FTEXT("\\")
246          #endif
247          FTEXT("__FILE__.001"), 0, SplitIconIndex);
248      SplitIconIndex_Defined = true;
249    }
250    return SplitIconIndex;
251  }
252
253  CExtIconPair pair;
254  pair.Ext = ext;
255  GetRealIconIndex(us2fs(fileName + dotPos), attrib, pair.IconIndex);
256  _extMap.Insert(insertPos, pair);
257  // if (typeName) *typeName = pair.TypeName;
258  return pair.IconIndex;
259}
260
261/*
262int CExtToIconMap::GetIconIndex(DWORD attrib, const UString &fileName)
263{
264  return GetIconIndex(attrib, fileName, NULL);
265}
266*/
267
268HIMAGELIST GetSysImageList(bool smallIcons)
269{
270  SHFILEINFO shellInfo;
271  return (HIMAGELIST)SHGetFileInfo(TEXT(""),
272      FILE_ATTRIBUTE_NORMAL |
273      FILE_ATTRIBUTE_DIRECTORY,
274      &shellInfo, sizeof(shellInfo),
275      SHGFI_USEFILEATTRIBUTES |
276      SHGFI_SYSICONINDEX |
277      (smallIcons ? SHGFI_SMALLICON : SHGFI_ICON));
278}
279