1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/msw/artmsw.cpp
3 // Purpose:     stock wxArtProvider instance with native MSW stock icons
4 // Author:      Vaclav Slavik
5 // Modified by:
6 // Created:     2008-10-15
7 // Copyright:   (c) Vaclav Slavik, 2008
8 // Licence:     wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10 
11 // ---------------------------------------------------------------------------
12 // headers
13 // ---------------------------------------------------------------------------
14 
15 // For compilers that support precompilation, includes "wx.h".
16 #include "wx/wxprec.h"
17 
18 #if defined(__BORLANDC__)
19     #pragma hdrstop
20 #endif
21 
22 #include "wx/artprov.h"
23 #include "wx/image.h"
24 #include "wx/dynlib.h"
25 #include "wx/volume.h"
26 #include "wx/msw/private.h"
27 #include "wx/msw/wrapwin.h"
28 
29 #ifdef SHGSI_ICON
30     #define wxHAS_SHGetStockIconInfo
31 #endif
32 
33 namespace
34 {
35 
36 #ifdef wxHAS_SHGetStockIconInfo
37 
MSWGetStockIconIdForArtProviderId(const wxArtID & art_id)38 SHSTOCKICONID MSWGetStockIconIdForArtProviderId(const wxArtID& art_id)
39 {
40     // try to find an equivalent MSW stock icon id for wxArtID
41     if ( art_id == wxART_ERROR)             return SIID_ERROR;
42     else if ( art_id == wxART_QUESTION )    return SIID_HELP;
43     else if ( art_id == wxART_WARNING )     return SIID_WARNING;
44     else if ( art_id == wxART_INFORMATION ) return SIID_INFO;
45     else if ( art_id == wxART_HELP )        return SIID_HELP;
46     else if ( art_id == wxART_FOLDER )      return SIID_FOLDER;
47     else if ( art_id == wxART_FOLDER_OPEN ) return SIID_FOLDEROPEN;
48     else if ( art_id == wxART_DELETE )      return SIID_DELETE;
49     else if ( art_id == wxART_FIND )        return SIID_FIND;
50     else if ( art_id == wxART_HARDDISK )    return SIID_DRIVEFIXED;
51     else if ( art_id == wxART_FLOPPY )      return SIID_DRIVE35;
52     else if ( art_id == wxART_CDROM )       return SIID_DRIVECD;
53     else if ( art_id == wxART_REMOVABLE )   return SIID_DRIVEREMOVE;
54 
55     return SIID_INVALID;
56 };
57 
58 
59 // try to load SHGetStockIconInfo dynamically, so this code runs
60 // even on pre-Vista Windows versions
61 HRESULT
MSW_SHGetStockIconInfo(SHSTOCKICONID siid,UINT uFlags,SHSTOCKICONINFO * psii)62 MSW_SHGetStockIconInfo(SHSTOCKICONID siid,
63                        UINT uFlags,
64                        SHSTOCKICONINFO *psii)
65 {
66     typedef HRESULT (WINAPI *PSHGETSTOCKICONINFO)(SHSTOCKICONID, UINT, SHSTOCKICONINFO *);
67     static PSHGETSTOCKICONINFO pSHGetStockIconInfo = (PSHGETSTOCKICONINFO)-1;
68 
69     if ( pSHGetStockIconInfo == (PSHGETSTOCKICONINFO)-1 )
70     {
71         wxDynamicLibrary shell32(wxT("shell32.dll"));
72 
73         pSHGetStockIconInfo = (PSHGETSTOCKICONINFO)shell32.RawGetSymbol( wxT("SHGetStockIconInfo") );
74     }
75 
76     if ( !pSHGetStockIconInfo )
77         return E_FAIL;
78 
79     return pSHGetStockIconInfo(siid, uFlags, psii);
80 }
81 
82 #endif // #ifdef wxHAS_SHGetStockIconInfo
83 
84 wxBitmap
MSWGetBitmapForPath(const wxString & path,const wxSize & size,DWORD uFlags=0)85 MSWGetBitmapForPath(const wxString& path, const wxSize& size, DWORD uFlags = 0)
86 {
87     SHFILEINFO fi;
88     wxZeroMemory(fi);
89 
90     uFlags |= SHGFI_USEFILEATTRIBUTES | SHGFI_ICON;
91     if ( size != wxDefaultSize )
92     {
93         if ( size.x <= 16 )
94             uFlags |= SHGFI_SMALLICON;
95         else if ( size.x >= 64 )
96             uFlags |= SHGFI_LARGEICON;
97     }
98 
99     if ( !SHGetFileInfo(path.t_str(), FILE_ATTRIBUTE_DIRECTORY,
100                         &fi, sizeof(SHFILEINFO), uFlags) )
101         return wxNullBitmap;
102 
103     wxIcon icon;
104     icon.CreateFromHICON((WXHICON)fi.hIcon);
105 
106     wxBitmap bitmap(icon);
107     ::DestroyIcon(fi.hIcon);
108 
109     return bitmap;
110 }
111 
112 #if wxUSE_FSVOLUME
113 
114 wxBitmap
GetDriveBitmapForVolumeType(const wxFSVolumeKind & volKind,const wxSize & size)115 GetDriveBitmapForVolumeType(const wxFSVolumeKind& volKind, const wxSize& size)
116 {
117     // get all volumes and try to find one with a matching type
118     wxArrayString volumes = wxFSVolume::GetVolumes();
119     for ( size_t i = 0; i < volumes.Count(); i++ )
120     {
121         wxFSVolume vol( volumes[i] );
122         if ( vol.GetKind() == volKind )
123         {
124             return MSWGetBitmapForPath(volumes[i], size);
125         }
126     }
127 
128     return wxNullBitmap;
129 }
130 
131 #endif // wxUSE_FSVOLUME
132 
133 } // anonymous namespace
134 
135 // ----------------------------------------------------------------------------
136 // wxWindowsArtProvider
137 // ----------------------------------------------------------------------------
138 
139 class wxWindowsArtProvider : public wxArtProvider
140 {
141 protected:
142     virtual wxBitmap CreateBitmap(const wxArtID& id, const wxArtClient& client,
143                                   const wxSize& size);
144 };
145 
CreateFromStdIcon(const char * iconName,const wxArtClient & client)146 static wxBitmap CreateFromStdIcon(const char *iconName,
147                                   const wxArtClient& client)
148 {
149     wxIcon icon(iconName);
150     wxBitmap bmp;
151     bmp.CopyFromIcon(icon);
152 
153 #if wxUSE_IMAGE
154     // The standard native message box icons are in message box size (32x32).
155     // If they are requested in any size other than the default or message
156     // box size, they must be rescaled first.
157     if ( client != wxART_MESSAGE_BOX && client != wxART_OTHER )
158     {
159         const wxSize size = wxArtProvider::GetNativeSizeHint(client);
160         if ( size != wxDefaultSize )
161         {
162             wxImage img = bmp.ConvertToImage();
163             img.Rescale(size.x, size.y);
164             bmp = wxBitmap(img);
165         }
166     }
167 #endif // wxUSE_IMAGE
168 
169     return bmp;
170 }
171 
CreateBitmap(const wxArtID & id,const wxArtClient & client,const wxSize & size)172 wxBitmap wxWindowsArtProvider::CreateBitmap(const wxArtID& id,
173                                             const wxArtClient& client,
174                                             const wxSize& size)
175 {
176     wxBitmap bitmap;
177 
178 #ifdef wxHAS_SHGetStockIconInfo
179     // first try to use SHGetStockIconInfo, available only on Vista and higher
180     SHSTOCKICONID stockIconId = MSWGetStockIconIdForArtProviderId( id );
181     if ( stockIconId != SIID_INVALID )
182     {
183         WinStruct<SHSTOCKICONINFO> sii;
184 
185         UINT uFlags = SHGSI_ICON;
186         if ( size != wxDefaultSize )
187         {
188             if ( size.x <= 16 )
189                 uFlags |= SHGSI_SMALLICON;
190             else if ( size.x >= 64 )
191                 uFlags |= SHGSI_LARGEICON;
192         }
193 
194         HRESULT res = MSW_SHGetStockIconInfo(stockIconId, uFlags, &sii);
195         if ( res == S_OK )
196         {
197             wxIcon icon;
198             icon.CreateFromHICON( (WXHICON)sii.hIcon );
199 
200             bitmap = wxBitmap(icon);
201             ::DestroyIcon(sii.hIcon);
202 
203             if ( bitmap.IsOk() )
204                 return bitmap;
205         }
206     }
207 #endif // wxHAS_SHGetStockIconInfo
208 
209 
210 #if wxUSE_FSVOLUME
211     // now try SHGetFileInfo
212     wxFSVolumeKind volKind = wxFS_VOL_OTHER;
213     if ( id == wxART_HARDDISK )
214         volKind = wxFS_VOL_DISK;
215     else if ( id == wxART_FLOPPY )
216         volKind = wxFS_VOL_FLOPPY;
217     else if ( id == wxART_CDROM )
218         volKind = wxFS_VOL_CDROM;
219 
220     if ( volKind != wxFS_VOL_OTHER )
221     {
222         bitmap = GetDriveBitmapForVolumeType(volKind, size);
223         if ( bitmap.IsOk() )
224             return bitmap;
225     }
226 #endif // wxUSE_FSVOLUME
227 
228     // notice that the directory used here doesn't need to exist
229     if ( id == wxART_FOLDER )
230         bitmap = MSWGetBitmapForPath("C:\\wxdummydir\\", size );
231     else if ( id == wxART_FOLDER_OPEN )
232         bitmap = MSWGetBitmapForPath("C:\\wxdummydir\\", size, SHGFI_OPENICON );
233 
234     if ( !bitmap.IsOk() )
235     {
236         // handle message box icons specially (wxIcon ctor treat these names
237         // as special cases via wxICOResourceHandler::LoadIcon):
238         const char *name = NULL;
239         if ( id == wxART_ERROR )
240             name = "wxICON_ERROR";
241         else if ( id == wxART_INFORMATION )
242             name = "wxICON_INFORMATION";
243         else if ( id == wxART_WARNING )
244             name = "wxICON_WARNING";
245         else if ( id == wxART_QUESTION )
246             name = "wxICON_QUESTION";
247 
248         if ( name )
249             return CreateFromStdIcon(name, client);
250     }
251 
252     // for anything else, fall back to generic provider:
253     return bitmap;
254 }
255 
256 // ----------------------------------------------------------------------------
257 // wxArtProvider::InitNativeProvider()
258 // ----------------------------------------------------------------------------
259 
InitNativeProvider()260 /*static*/ void wxArtProvider::InitNativeProvider()
261 {
262     PushBack(new wxWindowsArtProvider);
263 }
264 
265 // ----------------------------------------------------------------------------
266 // wxArtProvider::GetNativeSizeHint()
267 // ----------------------------------------------------------------------------
268 
269 /*static*/
GetNativeSizeHint(const wxArtClient & client)270 wxSize wxArtProvider::GetNativeSizeHint(const wxArtClient& client)
271 {
272     if ( client == wxART_TOOLBAR )
273     {
274         return wxSize(24, 24);
275     }
276     else if ( client == wxART_MENU )
277     {
278         return wxSize(16, 16);
279     }
280     else if ( client == wxART_FRAME_ICON )
281     {
282         return wxSize(::GetSystemMetrics(SM_CXSMICON),
283                       ::GetSystemMetrics(SM_CYSMICON));
284     }
285     else if ( client == wxART_CMN_DIALOG ||
286               client == wxART_MESSAGE_BOX )
287     {
288         return wxSize(::GetSystemMetrics(SM_CXICON),
289                       ::GetSystemMetrics(SM_CYICON));
290     }
291     else if (client == wxART_BUTTON)
292     {
293         return wxSize(16, 16);
294     }
295     else if (client == wxART_LIST)
296     {
297         return wxSize(16, 16);
298     }
299 
300     return wxDefaultSize;
301 }
302