1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/common/iconbndl.cpp
3 // Purpose:     wxIconBundle
4 // Author:      Mattia Barbon, Vadim Zeitlin
5 // Created:     23.03.2002
6 // Copyright:   (c) Mattia barbon
7 // Licence:     wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9 
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
12 
13 #ifdef __BORLANDC__
14     #pragma hdrstop
15 #endif
16 
17 #include "wx/iconbndl.h"
18 
19 #ifndef WX_PRECOMP
20     #include "wx/settings.h"
21     #include "wx/log.h"
22     #include "wx/intl.h"
23     #include "wx/bitmap.h"
24     #include "wx/image.h"
25     #include "wx/stream.h"
26 #endif
27 
28 #include "wx/wfstream.h"
29 
30 #include "wx/arrimpl.cpp"
31 WX_DEFINE_OBJARRAY(wxIconArray)
32 
33 IMPLEMENT_DYNAMIC_CLASS(wxIconBundle, wxGDIObject)
34 
35 #define M_ICONBUNDLEDATA static_cast<wxIconBundleRefData*>(m_refData)
36 
37 // ----------------------------------------------------------------------------
38 // wxIconBundleRefData
39 // ----------------------------------------------------------------------------
40 
41 class WXDLLEXPORT wxIconBundleRefData : public wxGDIRefData
42 {
43 public:
wxIconBundleRefData()44     wxIconBundleRefData() { }
45 
46     // We need the copy ctor for CloneGDIRefData() but notice that we use the
47     // base class default ctor in it and not the copy one which it doesn't have.
wxIconBundleRefData(const wxIconBundleRefData & other)48     wxIconBundleRefData(const wxIconBundleRefData& other)
49         : wxGDIRefData(),
50           m_icons(other.m_icons)
51     {
52     }
53 
54     // default assignment operator and dtor are ok
55 
IsOk() const56     virtual bool IsOk() const { return !m_icons.empty(); }
57 
58     wxIconArray m_icons;
59 };
60 
61 // ============================================================================
62 // wxIconBundle implementation
63 // ============================================================================
64 
wxIconBundle()65 wxIconBundle::wxIconBundle()
66 {
67 }
68 
69 #if wxUSE_STREAMS && wxUSE_IMAGE
70 
71 #if wxUSE_FFILE || wxUSE_FILE
wxIconBundle(const wxString & file,wxBitmapType type)72 wxIconBundle::wxIconBundle(const wxString& file, wxBitmapType type)
73             : wxGDIObject()
74 {
75     AddIcon(file, type);
76 }
77 #endif // wxUSE_FFILE || wxUSE_FILE
78 
wxIconBundle(wxInputStream & stream,wxBitmapType type)79 wxIconBundle::wxIconBundle(wxInputStream& stream, wxBitmapType type)
80             : wxGDIObject()
81 {
82     AddIcon(stream, type);
83 }
84 #endif // wxUSE_STREAMS && wxUSE_IMAGE
85 
wxIconBundle(const wxIcon & icon)86 wxIconBundle::wxIconBundle(const wxIcon& icon)
87             : wxGDIObject()
88 {
89     AddIcon(icon);
90 }
91 
CreateGDIRefData() const92 wxGDIRefData *wxIconBundle::CreateGDIRefData() const
93 {
94     return new wxIconBundleRefData;
95 }
96 
CloneGDIRefData(const wxGDIRefData * data) const97 wxGDIRefData *wxIconBundle::CloneGDIRefData(const wxGDIRefData *data) const
98 {
99     return new wxIconBundleRefData(*static_cast<const wxIconBundleRefData *>(data));
100 }
101 
DeleteIcons()102 void wxIconBundle::DeleteIcons()
103 {
104     UnRef();
105 }
106 
107 #if wxUSE_STREAMS && wxUSE_IMAGE
108 
109 namespace
110 {
111 
112 // Adds icon from 'input' to the bundle. Shows 'errorMessage' on failure
113 // (it must contain "%d", because it is used to report # of image in the file
114 // that failed to load):
DoAddIcon(wxIconBundle & bundle,wxInputStream & input,wxBitmapType type,const wxString & errorMessage)115 void DoAddIcon(wxIconBundle& bundle,
116                wxInputStream& input,
117                wxBitmapType type,
118                const wxString& errorMessage)
119 {
120     wxImage image;
121 
122     const wxFileOffset posOrig = input.TellI();
123 
124     const size_t count = wxImage::GetImageCount(input, type);
125     for ( size_t i = 0; i < count; ++i )
126     {
127         if ( i )
128         {
129             // the call to LoadFile() for the first sub-image updated the
130             // stream position but we need to start reading the subsequent
131             // sub-image at the image beginning too
132             input.SeekI(posOrig);
133         }
134 
135         if ( !image.LoadFile(input, type, i) )
136         {
137             wxLogError(errorMessage, i);
138             continue;
139         }
140 
141         if ( type == wxBITMAP_TYPE_ANY )
142         {
143             // store the type so that we don't need to try all handlers again
144             // for the subsequent images, they should all be of the same type
145             type = image.GetType();
146         }
147 
148         wxIcon tmp;
149         tmp.CopyFromBitmap(wxBitmap(image));
150         bundle.AddIcon(tmp);
151     }
152 }
153 
154 } // anonymous namespace
155 
156 #if wxUSE_FFILE || wxUSE_FILE
157 
AddIcon(const wxString & file,wxBitmapType type)158 void wxIconBundle::AddIcon(const wxString& file, wxBitmapType type)
159 {
160 #ifdef __WXMAC__
161     // Deal with standard icons
162     if ( type == wxBITMAP_TYPE_ICON_RESOURCE )
163     {
164         wxIcon tmp(file, type);
165         if (tmp.IsOk())
166         {
167             AddIcon(tmp);
168             return;
169         }
170     }
171 #endif // __WXMAC__
172 
173 #if wxUSE_FFILE
174     wxFFileInputStream stream(file);
175 #elif wxUSE_FILE
176     wxFileInputStream stream(file);
177 #endif
178     DoAddIcon
179     (
180         *this,
181         stream, type,
182         wxString::Format(_("Failed to load image %%d from file '%s'."), file)
183     );
184 }
185 
186 #endif // wxUSE_FFILE || wxUSE_FILE
187 
AddIcon(wxInputStream & stream,wxBitmapType type)188 void wxIconBundle::AddIcon(wxInputStream& stream, wxBitmapType type)
189 {
190     DoAddIcon(*this, stream, type, _("Failed to load image %d from stream."));
191 }
192 
193 #endif // wxUSE_STREAMS && wxUSE_IMAGE
194 
GetIcon(const wxSize & size,int flags) const195 wxIcon wxIconBundle::GetIcon(const wxSize& size, int flags) const
196 {
197     wxASSERT( size == wxDefaultSize || (size.x >= 0 && size.y > 0) );
198 
199     // We need the standard system icon size when using FALLBACK_SYSTEM.
200     wxCoord sysX = 0,
201             sysY = 0;
202     if ( flags & FALLBACK_SYSTEM )
203     {
204         sysX = wxSystemSettings::GetMetric(wxSYS_ICON_X);
205         sysY = wxSystemSettings::GetMetric(wxSYS_ICON_Y);
206     }
207 
208     // If size == wxDefaultSize, we use system default icon size by convention.
209     wxCoord sizeX = size.x;
210     wxCoord sizeY = size.y;
211     if ( size == wxDefaultSize )
212     {
213         wxASSERT_MSG( flags == FALLBACK_SYSTEM,
214                       wxS("Must have valid size if not using FALLBACK_SYSTEM") );
215 
216         sizeX = sysX;
217         sizeY = sysY;
218     }
219 
220     // Iterate over all icons searching for the exact match or the closest icon
221     // for FALLBACK_NEAREST_LARGER.
222     wxIcon iconBest;
223     int bestDiff = 0;
224     bool bestIsLarger = false;
225     bool bestIsSystem = false;
226 
227     const size_t count = GetIconCount();
228 
229     const wxIconArray& iconArray = M_ICONBUNDLEDATA->m_icons;
230     for ( size_t i = 0; i < count; i++ )
231     {
232         const wxIcon& icon = iconArray[i];
233         if ( !icon.IsOk() )
234             continue;
235         wxCoord sx = icon.GetWidth(),
236                 sy = icon.GetHeight();
237 
238         // Exact match ends search immediately in any case.
239         if ( sx == sizeX && sy == sizeY )
240         {
241             iconBest = icon;
242             break;
243         }
244 
245         if ( flags & FALLBACK_SYSTEM )
246         {
247             if ( sx == sysX && sy == sysY )
248             {
249                 iconBest = icon;
250                 bestIsSystem = true;
251                 continue;
252             }
253         }
254 
255         if ( !bestIsSystem && (flags & FALLBACK_NEAREST_LARGER) )
256         {
257             bool iconLarger = (sx >= sizeX) && (sy >= sizeY);
258             int iconDiff = abs(sx - sizeX) + abs(sy - sizeY);
259 
260             // Use current icon as candidate for the best icon, if either:
261             // - we have no candidate yet
262             // - we have no candidate larger than desired size and current icon is
263             // - current icon is closer to desired size than candidate
264             if ( !iconBest.IsOk() ||
265                     (!bestIsLarger && iconLarger) ||
266                         (iconLarger && (iconDiff < bestDiff)) )
267             {
268                 iconBest = icon;
269                 bestIsLarger = iconLarger;
270                 bestDiff = iconDiff;
271                 continue;
272             }
273         }
274     }
275 
276 #if defined( __WXMAC__ ) && wxOSX_USE_CARBON
277     if (!iconBest.IsOk())
278         return wxNullIcon;
279 
280     return wxIcon(iconBest.GetHICON(), size);
281 #else
282     return iconBest;
283 #endif
284 }
285 
GetIconOfExactSize(const wxSize & size) const286 wxIcon wxIconBundle::GetIconOfExactSize(const wxSize& size) const
287 {
288     return GetIcon(size, FALLBACK_NONE);
289 }
290 
AddIcon(const wxIcon & icon)291 void wxIconBundle::AddIcon(const wxIcon& icon)
292 {
293     wxCHECK_RET( icon.IsOk(), wxT("invalid icon") );
294 
295     AllocExclusive();
296 
297     wxIconArray& iconArray = M_ICONBUNDLEDATA->m_icons;
298 
299     // replace existing icon with the same size if we already have it
300     const size_t count = iconArray.size();
301     for ( size_t i = 0; i < count; ++i )
302     {
303         wxIcon& tmp = iconArray[i];
304         if ( tmp.IsOk() &&
305                 tmp.GetWidth() == icon.GetWidth() &&
306                 tmp.GetHeight() == icon.GetHeight() )
307         {
308             tmp = icon;
309             return;
310         }
311     }
312 
313     // if we don't, add an icon with new size
314     iconArray.Add(icon);
315 }
316 
GetIconCount() const317 size_t wxIconBundle::GetIconCount() const
318 {
319     return IsOk() ? M_ICONBUNDLEDATA->m_icons.size() : 0;
320 }
321 
GetIconByIndex(size_t n) const322 wxIcon wxIconBundle::GetIconByIndex(size_t n) const
323 {
324     wxCHECK_MSG( n < GetIconCount(), wxNullIcon, wxT("invalid index") );
325 
326     return M_ICONBUNDLEDATA->m_icons[n];
327 }
328