diff --git a/docs/doxygen/overviews/xrc_format.h b/docs/doxygen/overviews/xrc_format.h index 0c676c36fe..42fb8f73d4 100644 --- a/docs/doxygen/overviews/xrc_format.h +++ b/docs/doxygen/overviews/xrc_format.h @@ -340,8 +340,9 @@ or translations are done. @subsection overview_xrcformat_type_bitmap Bitmap -Bitmap properties contain specification of a single bitmap or icon. In the most -basic form, their text value is simply a relative URL of the bitmap to use. +Bitmap properties contain specification of a single bitmap, icon, a set of bitmaps +or SVG file. In the most basic form, their text value is simply a relative URL of +the bitmap to use. For example: @code @@ -363,6 +364,31 @@ percent-encoded, e.g. here is the correct way to specify a bitmap with the path Bitmap file paths can include environment variables that are expanded if wxXRC_USE_ENVVARS was passed to the wxXmlResource constructor. +It is possible to specify the multi-resolution bitmap by a set of bitmaps or +an SVG file, which are mutually exclusive. The set of bitmaps should contain +one or more relative URLs of a bitmap, separated by @c ';'. +For example, to specify two bitmaps, to be used in standard and 200% DPI +scaling respectively, you could write: +@code +new.png;new_2x.png +@endcode + +Here the first bitmap is special, as its size determines the logical size of +the bitmap. In other words, this bitmap is the one used when DPI scaling +is not in effect. Any subsequent bitmaps can come in any order and will be used +when the DPI scaling factor is equal, or at least close, to the ratio of their +size to the size of the first bitmap. Using @c _2x naming convention here is common, +but @e not required, the names of the bitmaps can be arbitrary, e.g. +@code +new_32x32.png;new_64x64.png +@endcode +would work just as well. +When using SVG file you must also specify @c default_size attribute +(even if the size is specified in SVG file, it may be different from the size needed here): +@code +new.svg +@endcode + Alternatively, it is possible to specify the bitmap using wxArtProvider IDs. In this case, the property element has no textual value (filename) and instead has the @c stock_id XML attribute that contains stock art ID as accepted by diff --git a/include/wx/xrc/xmlres.h b/include/wx/xrc/xmlres.h index 603b35eff4..85be98a649 100644 --- a/include/wx/xrc/xmlres.h +++ b/include/wx/xrc/xmlres.h @@ -583,6 +583,11 @@ public: const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER), wxSize size = wxDefaultSize) wxOVERRIDE; + // Gets a bitmap bundle. + wxBitmapBundle GetBitmapBundle(const wxString& param = wxT("bitmap"), + const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER), + wxSize size = wxDefaultSize) wxOVERRIDE; + // Gets an icon. wxIcon GetIcon(const wxString& param = wxT("icon"), const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER), diff --git a/include/wx/xrc/xmlreshandler.h b/include/wx/xrc/xmlreshandler.h index 7f132b99a9..531630dd7b 100644 --- a/include/wx/xrc/xmlreshandler.h +++ b/include/wx/xrc/xmlreshandler.h @@ -90,6 +90,9 @@ public: virtual wxBitmap GetBitmap(const wxXmlNode* node, const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER), wxSize size = wxDefaultSize) = 0; + virtual wxBitmapBundle GetBitmapBundle(const wxString& param = wxT("bitmap"), + const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER), + wxSize size = wxDefaultSize) = 0; virtual wxIcon GetIcon(const wxString& param = wxT("icon"), const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER), wxSize size = wxDefaultSize) = 0; @@ -332,6 +335,12 @@ protected: { return GetImpl()->GetBitmap(node, defaultArtClient, size); } + wxBitmapBundle GetBitmapBundle(const wxString& param = wxT("bitmap"), + const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER), + wxSize size = wxDefaultSize) + { + return GetImpl()->GetBitmapBundle(param, defaultArtClient, size); + } wxIcon GetIcon(const wxString& param = wxT("icon"), const wxArtClient& defaultArtClient = wxASCII_STR(wxART_OTHER), wxSize size = wxDefaultSize) diff --git a/misc/schema/xrc_schema.rnc b/misc/schema/xrc_schema.rnc index 742190bc0c..49f56405f8 100644 --- a/misc/schema/xrc_schema.rnc +++ b/misc/schema/xrc_schema.rnc @@ -467,7 +467,8 @@ t_bitmap = t_url?, ( attribute stock_id { t_identifier}, attribute stock_client { t_identifier}? - )? + )?, + attribute default_size { t_size}? t_font = ( [xrc:p="o"] element size {_, t_float }* & diff --git a/src/xrc/xmlres.cpp b/src/xrc/xmlres.cpp index 046b0004c4..e83229b612 100644 --- a/src/xrc/xmlres.cpp +++ b/src/xrc/xmlres.cpp @@ -1852,6 +1852,52 @@ bool GetStockArtAttrs(const wxXmlNode *paramNode, return false; } +// Load a bitmap from a file system element. +wxBitmap LoadBitmapFromFS(wxXmlResourceHandlerImpl* impl, + const wxString& path, + wxSize size, + const wxString& nodeName) +{ + if (path.empty()) return wxNullBitmap; +#if wxUSE_FILESYSTEM + wxFSFile *fsfile = impl->GetCurFileSystem().OpenFile(path, wxFS_READ | wxFS_SEEKABLE); + if (fsfile == NULL) + { + impl->ReportParamError + ( + nodeName, + wxString::Format("cannot open bitmap resource \"%s\"", path) + ); + return wxNullBitmap; + } + wxImage img(*(fsfile->GetStream())); + delete fsfile; +#else + wxImage img(name); +#endif + + if (!img.IsOk()) + { + impl->ReportParamError + ( + nodeName, + wxString::Format("cannot create bitmap from \"%s\"", path) + ); + return wxNullBitmap; + } + if (!(size == wxDefaultSize)) img.Rescale(size.x, size.y); + return wxBitmap(img); +} + +// forward declaration +template +T +ParseStringInPixels(wxXmlResourceHandlerImpl* impl, + const wxString& param, + const wxString& str, + const T& defaultValue, + wxWindow *windowToUse = NULL); + } // anonymous namespace wxBitmap wxXmlResourceHandlerImpl::GetBitmap(const wxString& param, @@ -1892,36 +1938,126 @@ wxBitmap wxXmlResourceHandlerImpl::GetBitmap(const wxXmlNode* node, } /* ...or load the bitmap from file: */ - wxString name = GetFilePath(node); - if (name.empty()) return wxNullBitmap; -#if wxUSE_FILESYSTEM - wxFSFile *fsfile = GetCurFileSystem().OpenFile(name, wxFS_READ | wxFS_SEEKABLE); - if (fsfile == NULL) - { - ReportParamError - ( - node->GetName(), - wxString::Format("cannot open bitmap resource \"%s\"", name) - ); - return wxNullBitmap; - } - wxImage img(*(fsfile->GetStream())); - delete fsfile; -#else - wxImage img(name); -#endif + return LoadBitmapFromFS(this, GetFilePath(node), size, node->GetName()); +} - if (!img.IsOk()) + +wxBitmapBundle wxXmlResourceHandlerImpl::GetBitmapBundle(const wxString& param, + const wxArtClient& defaultArtClient, + wxSize size) +{ + wxASSERT_MSG( !param.empty(), "bitmap parameter name can't be empty" ); + + const wxXmlNode* const node = GetParamNode(param); + + if ( !node ) { - ReportParamError - ( - node->GetName(), - wxString::Format("cannot create bitmap from \"%s\"", name) - ); + // this is not an error as bitmap parameter could be optional return wxNullBitmap; } - if (!(size == wxDefaultSize)) img.Rescale(size.x, size.y); - return wxBitmap(img); + + /* If the bitmap is specified as stock item, query wxArtProvider for it: */ + wxString art_id, art_client; + if ( GetStockArtAttrs(node, defaultArtClient, + art_id, art_client) ) + { + wxBitmapBundle stockArt(wxArtProvider::GetBitmapBundle(art_id, art_client, size)); + if ( stockArt.IsOk() ) + return stockArt; + } + + wxBitmapBundle bitmapBundle; + wxString paramValue = GetParamValue(node); + if ( paramValue.EndsWith(".svg") ) + { + if ( paramValue.Contains(";") ) + { + ReportParamError + ( + param, + "may contain either one SVG file or a list of files separated by ';'" + ); + return bitmapBundle; + } + + // it is a bundle from svg file + wxString svgDefaultSizeAttr = node->GetAttribute("default_size", ""); + if ( svgDefaultSizeAttr.empty() ) + { + ReportParamError + ( + param, + "'default_size' attribute required with svg file" + ); + } + else + { +#ifdef wxHAS_SVG + wxSize svgDefaultSize = ParseStringInPixels(this, param, svgDefaultSizeAttr, wxDefaultSize); +#if wxUSE_FILESYSTEM + wxFSFile* fsfile = GetCurFileSystem().OpenFile(paramValue, wxFS_READ | wxFS_SEEKABLE); + if (fsfile == NULL) + { + ReportParamError + ( + param, + wxString::Format("cannot open SVG resource \"%s\"", paramValue) + ); + } + else + { + wxInputStream* s = fsfile->GetStream(); + const size_t len = static_cast(s->GetLength()); + wxCharBuffer buf(len); + char* const ptr = buf.data(); + + if (s->ReadAll(ptr, len)) + { + bitmapBundle = wxBitmapBundle::FromSVG(ptr, svgDefaultSize); + } + delete fsfile; + } +#else + bitmapBundle = wxBitmapBundle::FromSVGFile(paramValue, svgDefaultSize); +#endif +#else // !wxHAS_SVG + ReportParamError + ( + param, + "SVG bitmaps are not supported in this build of the library" + ); +#endif // wxHAS_SVG/!wxHAS_SVG + } + } + else + { + if ( paramValue.Contains(".svg;") ) + { + ReportParamError + ( + param, + "may contain either one SVG file or a list of files separated by ';'" + ); + return bitmapBundle; + } + + // it is a bundle from bitmaps + wxVector bitmaps; + wxArrayString paths = wxSplit(paramValue, ';', '\0'); + for ( wxArrayString::const_iterator i = paths.begin(); i != paths.end(); ++i ) + { + wxBitmap bmpNext = LoadBitmapFromFS(this, *i, size, param); + if ( !bmpNext.IsOk() ) + { + // error in loading wxBitmap, return invalid wxBitmapBundle + return bitmapBundle; + } + bitmaps.push_back(bmpNext); + } + bitmapBundle = wxBitmapBundle::FromBitmaps(bitmaps); + } + + return bitmapBundle; } @@ -2182,17 +2318,17 @@ void XRCConvertFromDLU(wxWindow* w, T& value) value = w->ConvertDialogToPixels(value); } -// Helper for parsing values (of type T, for which XRCConvertFromAbsValue() and -// XRCConvertFromDLU() functions must be defined) which can be expressed either -// in pixels or dialog units. +// Helper for parsing strings which contain values (of type T, for which +// XRCConvertFromAbsValue() and XRCConvertFromDLU() functions must be defined) +// which can be expressed either in pixels or dialog units. template T -ParseValueInPixels(wxXmlResourceHandlerImpl* impl, +ParseStringInPixels(wxXmlResourceHandlerImpl* impl, const wxString& param, + const wxString& s, const T& defaultValue, - wxWindow *windowToUse = NULL) + wxWindow *windowToUse) { - const wxString s = impl->GetParamValue(param); if ( s.empty() ) return defaultValue; @@ -2235,6 +2371,17 @@ ParseValueInPixels(wxXmlResourceHandlerImpl* impl, return value; } +// Helper for parsing values which uses ParseStringInPixels() above. +template +T +ParseValueInPixels(wxXmlResourceHandlerImpl* impl, + const wxString& param, + const T& defaultValue, + wxWindow *windowToUse = NULL) +{ + return ParseStringInPixels(impl, param, impl->GetParamValue(param), defaultValue, windowToUse); +} + } // anonymous namespace wxSize wxXmlResourceHandlerImpl::GetSize(const wxString& param,