]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/iconbndl.cpp
Added further API for intercepting deletion and content insertion
[wxWidgets.git] / src / common / iconbndl.cpp
index 825a6431e645c8f3670f452fedbaf5774b5cb90e..d589943453e98af4d6057971c3f09bee82d56d69 100644 (file)
@@ -26,6 +26,8 @@
     #include "wx/stream.h"
 #endif
 
+#include "wx/wfstream.h"
+
 #include "wx/arrimpl.cpp"
 WX_DEFINE_OBJARRAY(wxIconArray)
 
@@ -40,7 +42,17 @@ IMPLEMENT_DYNAMIC_CLASS(wxIconBundle, wxGDIObject)
 class WXDLLEXPORT wxIconBundleRefData : public wxGDIRefData
 {
 public:
-    // default and copy ctors and assignment operators are ok
+    wxIconBundleRefData() { }
+
+    // We need the copy ctor for CloneGDIRefData() but notice that we use the
+    // base class default ctor in it and not the copy one which it doesn't have.
+    wxIconBundleRefData(const wxIconBundleRefData& other)
+        : wxGDIRefData(),
+          m_icons(other.m_icons)
+    {
+    }
+
+    // default assignment operator and dtor are ok
 
     virtual bool IsOk() const { return !m_icons.empty(); }
 
@@ -55,19 +67,22 @@ wxIconBundle::wxIconBundle()
 {
 }
 
+#if wxUSE_STREAMS && wxUSE_IMAGE
+
+#if wxUSE_FFILE || wxUSE_FILE
 wxIconBundle::wxIconBundle(const wxString& file, wxBitmapType type)
             : wxGDIObject()
 {
     AddIcon(file, type);
 }
+#endif // wxUSE_FFILE || wxUSE_FILE
 
-#if wxUSE_STREAMS
 wxIconBundle::wxIconBundle(wxInputStream& stream, wxBitmapType type)
             : wxGDIObject()
 {
     AddIcon(stream, type);
 }
-#endif // wxUSE_STREAMS
+#endif // wxUSE_STREAMS && wxUSE_IMAGE
 
 wxIconBundle::wxIconBundle(const wxIcon& icon)
             : wxGDIObject()
@@ -90,41 +105,57 @@ void wxIconBundle::DeleteIcons()
     UnRef();
 }
 
+#if wxUSE_STREAMS && wxUSE_IMAGE
+
 namespace
 {
 
 // Adds icon from 'input' to the bundle. Shows 'errorMessage' on failure
 // (it must contain "%d", because it is used to report # of image in the file
 // that failed to load):
-template<typename T>
 void DoAddIcon(wxIconBundle& bundle,
-               T& input, wxBitmapType type,
+               wxInputStream& input,
+               wxBitmapType type,
                const wxString& errorMessage)
 {
-#if wxUSE_IMAGE && (!defined(__WXMSW__) || wxUSE_WXDIB)
     wxImage image;
 
+    const wxFileOffset posOrig = input.TellI();
+
     const size_t count = wxImage::GetImageCount(input, type);
     for ( size_t i = 0; i < count; ++i )
     {
+        if ( i )
+        {
+            // the call to LoadFile() for the first sub-image updated the
+            // stream position but we need to start reading the subsequent
+            // sub-image at the image beginning too
+            input.SeekI(posOrig);
+        }
+
         if ( !image.LoadFile(input, type, i) )
         {
             wxLogError(errorMessage, i);
             continue;
         }
 
+        if ( type == wxBITMAP_TYPE_ANY )
+        {
+            // store the type so that we don't need to try all handlers again
+            // for the subsequent images, they should all be of the same type
+            type = image.GetType();
+        }
+
         wxIcon tmp;
         tmp.CopyFromBitmap(wxBitmap(image));
         bundle.AddIcon(tmp);
     }
-#else // !wxUSE_IMAGE
-    wxUnusedVar(input);
-    wxUnusedVar(type);
-#endif // wxUSE_IMAGE/!wxUSE_IMAGE
 }
 
 } // anonymous namespace
 
+#if wxUSE_FFILE || wxUSE_FILE
+
 void wxIconBundle::AddIcon(const wxString& file, wxBitmapType type)
 {
 #ifdef __WXMAC__
@@ -132,7 +163,7 @@ void wxIconBundle::AddIcon(const wxString& file, wxBitmapType type)
     if ( type == wxBITMAP_TYPE_ICON_RESOURCE )
     {
         wxIcon tmp(file, type);
-        if (tmp.Ok())
+        if (tmp.IsOk())
         {
             AddIcon(tmp);
             return;
@@ -140,64 +171,113 @@ void wxIconBundle::AddIcon(const wxString& file, wxBitmapType type)
     }
 #endif // __WXMAC__
 
+#if wxUSE_FFILE
+    wxFFileInputStream stream(file);
+#elif wxUSE_FILE
+    wxFileInputStream stream(file);
+#endif
     DoAddIcon
     (
         *this,
-        file, type,
+        stream, type,
         wxString::Format(_("Failed to load image %%d from file '%s'."), file)
     );
 }
 
-#if wxUSE_STREAMS
+#endif // wxUSE_FFILE || wxUSE_FILE
+
 void wxIconBundle::AddIcon(wxInputStream& stream, wxBitmapType type)
 {
     DoAddIcon(*this, stream, type, _("Failed to load image %d from stream."));
 }
-#endif // wxUSE_STREAMS
 
-wxIcon wxIconBundle::GetIcon(const wxSize& size) const
+#endif // wxUSE_STREAMS && wxUSE_IMAGE
+
+wxIcon wxIconBundle::GetIcon(const wxSize& size, int flags) const
 {
-    const size_t count = GetIconCount();
+    wxASSERT( size == wxDefaultSize || (size.x >= 0 && size.y > 0) );
+
+    // We need the standard system icon size when using FALLBACK_SYSTEM.
+    wxCoord sysX = 0,
+            sysY = 0;
+    if ( flags & FALLBACK_SYSTEM )
+    {
+        sysX = wxSystemSettings::GetMetric(wxSYS_ICON_X);
+        sysY = wxSystemSettings::GetMetric(wxSYS_ICON_Y);
+    }
 
-    // optimize for the common case of icon bundles containing one icon only
+    // If size == wxDefaultSize, we use system default icon size by convention.
+    wxCoord sizeX = size.x;
+    wxCoord sizeY = size.y;
+    if ( size == wxDefaultSize )
+    {
+        wxASSERT_MSG( flags == FALLBACK_SYSTEM,
+                      wxS("Must have valid size if not using FALLBACK_SYSTEM") );
+
+        sizeX = sysX;
+        sizeY = sysY;
+    }
+
+    // Iterate over all icons searching for the exact match or the closest icon
+    // for FALLBACK_NEAREST_LARGER.
     wxIcon iconBest;
-    switch ( count )
+    int bestDiff = 0;
+    bool bestIsLarger = false;
+    bool bestIsSystem = false;
+
+    const size_t count = GetIconCount();
+
+    const wxIconArray& iconArray = M_ICONBUNDLEDATA->m_icons;
+    for ( size_t i = 0; i < count; i++ )
     {
-        case 0:
-            // nothing to do, iconBest is already invalid
-            break;
+        const wxIcon& icon = iconArray[i];
+        if ( !icon.IsOk() )
+            continue;
+        wxCoord sx = icon.GetWidth(),
+                sy = icon.GetHeight();
 
-        case 1:
-            iconBest = M_ICONBUNDLEDATA->m_icons[0];
+        // Exact match ends search immediately in any case.
+        if ( sx == sizeX && sy == sizeY )
+        {
+            iconBest = icon;
             break;
+        }
 
-        default:
-            // there is more than one icon, find the best match:
-            wxCoord sysX = wxSystemSettings::GetMetric( wxSYS_ICON_X ),
-                    sysY = wxSystemSettings::GetMetric( wxSYS_ICON_Y );
+        if ( flags & FALLBACK_SYSTEM )
+        {
+            if ( sx == sysX && sy == sysY )
+            {
+                iconBest = icon;
+                bestIsSystem = true;
+                continue;
+            }
+        }
 
-            const wxIconArray& iconArray = M_ICONBUNDLEDATA->m_icons;
-            for ( size_t i = 0; i < count; i++ )
+        if ( !bestIsSystem && (flags & FALLBACK_NEAREST_LARGER) )
+        {
+            bool iconLarger = (sx >= sizeX) && (sy >= sizeY);
+            int iconDiff = abs(sx - sizeX) + abs(sy - sizeY);
+
+            // Use current icon as candidate for the best icon, if either:
+            // - we have no candidate yet
+            // - we have no candidate larger than desired size and current icon is
+            // - current icon is closer to desired size than candidate
+            if ( !iconBest.IsOk() ||
+                    (!bestIsLarger && iconLarger) ||
+                        (iconLarger && (iconDiff < bestDiff)) )
             {
-                const wxIcon& icon = iconArray[i];
-                wxCoord sx = icon.GetWidth(),
-                        sy = icon.GetHeight();
-
-                // if we got an icon of exactly the requested size, we're done
-                if ( sx == size.x && sy == size.y )
-                {
-                    iconBest = icon;
-                    break;
-                }
-
-                // the best icon is by default (arbitrarily) the first one but
-                // if we find a system-sized icon, take it instead
-                if ((sx == sysX && sy == sysY) || !iconBest.IsOk())
-                    iconBest = icon;
+                iconBest = icon;
+                bestIsLarger = iconLarger;
+                bestDiff = iconDiff;
+                continue;
             }
+        }
     }
 
 #if defined( __WXMAC__ ) && wxOSX_USE_CARBON
+    if (!iconBest.IsOk())
+        return wxNullIcon;
+
     return wxIcon(iconBest.GetHICON(), size);
 #else
     return iconBest;
@@ -206,19 +286,12 @@ wxIcon wxIconBundle::GetIcon(const wxSize& size) const
 
 wxIcon wxIconBundle::GetIconOfExactSize(const wxSize& size) const
 {
-    wxIcon icon = GetIcon(size);
-    if ( icon.Ok() &&
-            (icon.GetWidth() != size.x || icon.GetHeight() != size.y) )
-    {
-        icon = wxNullIcon;
-    }
-
-    return icon;
+    return GetIcon(size, FALLBACK_NONE);
 }
 
 void wxIconBundle::AddIcon(const wxIcon& icon)
 {
-    wxCHECK_RET( icon.IsOk(), _T("invalid icon") );
+    wxCHECK_RET( icon.IsOk(), wxT("invalid icon") );
 
     AllocExclusive();
 
@@ -229,7 +302,7 @@ void wxIconBundle::AddIcon(const wxIcon& icon)
     for ( size_t i = 0; i < count; ++i )
     {
         wxIcon& tmp = iconArray[i];
-        if ( tmp.Ok() &&
+        if ( tmp.IsOk() &&
                 tmp.GetWidth() == icon.GetWidth() &&
                 tmp.GetHeight() == icon.GetHeight() )
         {
@@ -249,7 +322,7 @@ size_t wxIconBundle::GetIconCount() const
 
 wxIcon wxIconBundle::GetIconByIndex(size_t n) const
 {
-    wxCHECK_MSG( n < GetIconCount(), wxNullIcon, _T("invalid index") );
+    wxCHECK_MSG( n < GetIconCount(), wxNullIcon, wxT("invalid index") );
 
     return M_ICONBUNDLEDATA->m_icons[n];
 }