| 1 | ///////////////////////////////////////////////////////////////////////////// |
| 2 | // Name: artstd.cpp |
| 3 | // Purpose: stock wxArtProvider instance with native GTK+ stock icons |
| 4 | // Author: Vaclav Slavik |
| 5 | // Modified by: |
| 6 | // Created: 2004-08-22 |
| 7 | // RCS-ID: $Id$ |
| 8 | // Copyright: (c) Vaclav Slavik, 2004 |
| 9 | // Licence: wxWindows licence |
| 10 | ///////////////////////////////////////////////////////////////////////////// |
| 11 | |
| 12 | // --------------------------------------------------------------------------- |
| 13 | // headers |
| 14 | // --------------------------------------------------------------------------- |
| 15 | |
| 16 | // For compilers that support precompilation, includes "wx.h". |
| 17 | #include "wx/wxprec.h" |
| 18 | |
| 19 | #if defined(__BORLANDC__) |
| 20 | #pragma hdrstop |
| 21 | #endif |
| 22 | |
| 23 | #if defined(__WXGTK20__) && !defined(__WXUNIVERSAL__) |
| 24 | |
| 25 | #include "wx/artprov.h" |
| 26 | #include "wx/module.h" |
| 27 | |
| 28 | #include <gtk/gtk.h> |
| 29 | |
| 30 | // compatibility with older GTK+ versions: |
| 31 | #ifndef GTK_STOCK_FILE |
| 32 | #define GTK_STOCK_FILE "gtk-file" |
| 33 | #endif |
| 34 | #ifndef GTK_STOCK_DIRECTORY |
| 35 | #define GTK_STOCK_DIRECTORY "gtk-directory" |
| 36 | #endif |
| 37 | |
| 38 | |
| 39 | // ---------------------------------------------------------------------------- |
| 40 | // wxGTK2ArtProvider |
| 41 | // ---------------------------------------------------------------------------- |
| 42 | |
| 43 | class wxGTK2ArtProvider : public wxArtProvider |
| 44 | { |
| 45 | protected: |
| 46 | virtual wxBitmap CreateBitmap(const wxArtID& id, const wxArtClient& client, |
| 47 | const wxSize& size); |
| 48 | }; |
| 49 | |
| 50 | /*static*/ void wxArtProvider::InitNativeProvider() |
| 51 | { |
| 52 | wxArtProvider::PushProvider(new wxGTK2ArtProvider); |
| 53 | } |
| 54 | |
| 55 | // ---------------------------------------------------------------------------- |
| 56 | // CreateBitmap routine |
| 57 | // ---------------------------------------------------------------------------- |
| 58 | |
| 59 | static const char *wxArtIDToStock(const wxArtID& id) |
| 60 | { |
| 61 | #define ART(wxid, gtkid) \ |
| 62 | if (id == wxid) return gtkid; |
| 63 | |
| 64 | ART(wxART_ERROR, GTK_STOCK_DIALOG_ERROR) |
| 65 | ART(wxART_INFORMATION, GTK_STOCK_DIALOG_INFO) |
| 66 | ART(wxART_WARNING, GTK_STOCK_DIALOG_WARNING) |
| 67 | ART(wxART_QUESTION, GTK_STOCK_DIALOG_QUESTION) |
| 68 | |
| 69 | //ART(wxART_HELP_SIDE_PANEL, ) |
| 70 | ART(wxART_HELP_SETTINGS, GTK_STOCK_SELECT_FONT) |
| 71 | //ART(wxART_HELP_BOOK, ) |
| 72 | ART(wxART_HELP_FOLDER, GTK_STOCK_DIRECTORY) |
| 73 | ART(wxART_HELP_PAGE, GTK_STOCK_FILE) |
| 74 | ART(wxART_MISSING_IMAGE, GTK_STOCK_MISSING_IMAGE) |
| 75 | ART(wxART_ADD_BOOKMARK, GTK_STOCK_ADD) |
| 76 | ART(wxART_DEL_BOOKMARK, GTK_STOCK_REMOVE) |
| 77 | ART(wxART_GO_BACK, GTK_STOCK_GO_BACK) |
| 78 | ART(wxART_GO_FORWARD, GTK_STOCK_GO_FORWARD) |
| 79 | ART(wxART_GO_UP, GTK_STOCK_GO_UP) |
| 80 | ART(wxART_GO_DOWN, GTK_STOCK_GO_DOWN) |
| 81 | ART(wxART_GO_TO_PARENT, GTK_STOCK_GO_UP) |
| 82 | ART(wxART_GO_HOME, GTK_STOCK_HOME) |
| 83 | ART(wxART_FILE_OPEN, GTK_STOCK_OPEN) |
| 84 | ART(wxART_PRINT, GTK_STOCK_PRINT) |
| 85 | ART(wxART_HELP, GTK_STOCK_HELP) |
| 86 | ART(wxART_TIP, GTK_STOCK_DIALOG_INFO) |
| 87 | //ART(wxART_REPORT_VIEW, ) |
| 88 | //ART(wxART_LIST_VIEW, ) |
| 89 | //ART(wxART_NEW_DIR, ) |
| 90 | #ifdef __WXGTK24__ |
| 91 | ART(wxART_FOLDER, GTK_STOCK_DIRECTORY) |
| 92 | ART(wxART_FOLDER_OPEN, GTK_STOCK_DIRECTORY) |
| 93 | #endif |
| 94 | //ART(wxART_GO_DIR_UP, ) |
| 95 | ART(wxART_EXECUTABLE_FILE, GTK_STOCK_EXECUTE) |
| 96 | ART(wxART_NORMAL_FILE, GTK_STOCK_FILE) |
| 97 | ART(wxART_TICK_MARK, GTK_STOCK_APPLY) |
| 98 | ART(wxART_CROSS_MARK, GTK_STOCK_CANCEL) |
| 99 | |
| 100 | #ifdef __WXGTK24__ |
| 101 | ART(wxART_FLOPPY, GTK_STOCK_FLOPPY) |
| 102 | ART(wxART_CDROM, GTK_STOCK_CDROM) |
| 103 | ART(wxART_HARDDISK, GTK_STOCK_HARDDISK) |
| 104 | ART(wxART_REMOVABLE, GTK_STOCK_HARDDISK) |
| 105 | #endif |
| 106 | |
| 107 | return NULL; |
| 108 | |
| 109 | #undef ART |
| 110 | } |
| 111 | |
| 112 | static GtkIconSize wxArtClientToIconSize(const wxArtClient& client) |
| 113 | { |
| 114 | if (client == wxART_TOOLBAR) |
| 115 | return GTK_ICON_SIZE_LARGE_TOOLBAR; |
| 116 | else if (client == wxART_MENU) |
| 117 | return GTK_ICON_SIZE_MENU; |
| 118 | else if (client == wxART_CMN_DIALOG || client == wxART_MESSAGE_BOX) |
| 119 | return GTK_ICON_SIZE_DIALOG; |
| 120 | else if (client == wxART_BUTTON) |
| 121 | return GTK_ICON_SIZE_BUTTON; |
| 122 | else |
| 123 | return GTK_ICON_SIZE_BUTTON; // this is arbitrary |
| 124 | } |
| 125 | |
| 126 | static GtkIconSize FindClosestIconSize(const wxSize& size) |
| 127 | { |
| 128 | #define NUM_SIZES 6 |
| 129 | static struct |
| 130 | { |
| 131 | GtkIconSize icon; |
| 132 | gint x, y; |
| 133 | } s_sizes[NUM_SIZES]; |
| 134 | static bool s_sizesInitialized = false; |
| 135 | |
| 136 | if (!s_sizesInitialized) |
| 137 | { |
| 138 | s_sizes[0].icon = GTK_ICON_SIZE_MENU; |
| 139 | s_sizes[1].icon = GTK_ICON_SIZE_SMALL_TOOLBAR; |
| 140 | s_sizes[2].icon = GTK_ICON_SIZE_LARGE_TOOLBAR; |
| 141 | s_sizes[3].icon = GTK_ICON_SIZE_BUTTON; |
| 142 | s_sizes[4].icon = GTK_ICON_SIZE_DND; |
| 143 | s_sizes[5].icon = GTK_ICON_SIZE_DIALOG; |
| 144 | for (size_t i = 0; i < NUM_SIZES; i++) |
| 145 | { |
| 146 | gtk_icon_size_lookup(s_sizes[i].icon, |
| 147 | &s_sizes[i].x, &s_sizes[i].y); |
| 148 | } |
| 149 | s_sizesInitialized = true; |
| 150 | } |
| 151 | |
| 152 | GtkIconSize best = GTK_ICON_SIZE_DIALOG; // presumably largest |
| 153 | unsigned distance = INT_MAX; |
| 154 | for (size_t i = 0; i < NUM_SIZES; i++) |
| 155 | { |
| 156 | // only use larger bitmaps, scaling down looks better than scaling up: |
| 157 | if (size.x > s_sizes[i].x || size.y > s_sizes[i].y) |
| 158 | continue; |
| 159 | |
| 160 | unsigned dist = (size.x - s_sizes[i].x) * (size.x - s_sizes[i].x) + |
| 161 | (size.y - s_sizes[i].y) * (size.y - s_sizes[i].y); |
| 162 | if (dist == 0) |
| 163 | return s_sizes[i].icon; |
| 164 | else if (dist < distance) |
| 165 | { |
| 166 | distance = dist; |
| 167 | best = s_sizes[i].icon; |
| 168 | } |
| 169 | } |
| 170 | return best; |
| 171 | } |
| 172 | |
| 173 | |
| 174 | static GtkStyle *gs_gtkStyle = NULL; |
| 175 | |
| 176 | static GdkPixbuf *CreateStockIcon(const char *stockid, GtkIconSize size) |
| 177 | { |
| 178 | // FIXME: This code is not 100% correct, because stock pixmap are |
| 179 | // context-dependent and may be affected by theme engine, the |
| 180 | // correct value can only be obtained for given GtkWidget object. |
| 181 | // |
| 182 | // Fool-proof implementation of stock bitmaps would extend wxBitmap |
| 183 | // with "stock-id" representation (in addition to pixmap and pixbuf |
| 184 | // ones) and would convert it to pixbuf when rendered. |
| 185 | |
| 186 | if (gs_gtkStyle == NULL) |
| 187 | { |
| 188 | GtkWidget *widget = gtk_button_new(); |
| 189 | gs_gtkStyle = gtk_rc_get_style(widget); |
| 190 | wxASSERT( gs_gtkStyle != NULL ); |
| 191 | g_object_ref(G_OBJECT(gs_gtkStyle)); |
| 192 | gtk_widget_destroy(widget); |
| 193 | } |
| 194 | |
| 195 | GtkIconSet *iconset = gtk_style_lookup_icon_set(gs_gtkStyle, stockid); |
| 196 | |
| 197 | if (!iconset) |
| 198 | return NULL; |
| 199 | |
| 200 | return gtk_icon_set_render_icon(iconset, gs_gtkStyle, |
| 201 | gtk_widget_get_default_direction(), |
| 202 | GTK_STATE_NORMAL, size, NULL, NULL); |
| 203 | } |
| 204 | |
| 205 | #ifdef __WXGTK24__ |
| 206 | static GdkPixbuf *CreateThemeIcon(const char *iconname, |
| 207 | GtkIconSize iconsize, const wxSize& sz) |
| 208 | { |
| 209 | wxSize size(sz); |
| 210 | if (size == wxDefaultSize) |
| 211 | { |
| 212 | gtk_icon_size_lookup(iconsize, &size.x, &size.y); |
| 213 | } |
| 214 | |
| 215 | return gtk_icon_theme_load_icon( |
| 216 | gtk_icon_theme_get_default(), |
| 217 | iconname, |
| 218 | size.x, |
| 219 | (GtkIconLookupFlags)0, NULL); |
| 220 | } |
| 221 | #endif |
| 222 | |
| 223 | wxBitmap wxGTK2ArtProvider::CreateBitmap(const wxArtID& id, |
| 224 | const wxArtClient& client, |
| 225 | const wxSize& size) |
| 226 | { |
| 227 | wxCharBuffer stockid = wxArtIDToStock(id); |
| 228 | GtkIconSize stocksize = (size == wxDefaultSize) ? |
| 229 | wxArtClientToIconSize(client) : |
| 230 | FindClosestIconSize(size); |
| 231 | |
| 232 | // allow passing GTK+ stock IDs to wxArtProvider: |
| 233 | if (!stockid) |
| 234 | stockid = id.ToAscii(); |
| 235 | |
| 236 | GdkPixbuf *pixbuf = CreateStockIcon(stockid, stocksize); |
| 237 | |
| 238 | #ifdef __WXGTK24__ |
| 239 | if (!gtk_check_version(2,4,0)) |
| 240 | { |
| 241 | if (!pixbuf) |
| 242 | pixbuf = CreateThemeIcon(stockid, stocksize, size); |
| 243 | } |
| 244 | #endif |
| 245 | |
| 246 | if (pixbuf && size != wxDefaultSize && |
| 247 | (size.x != gdk_pixbuf_get_width(pixbuf) || |
| 248 | size.y != gdk_pixbuf_get_height(pixbuf))) |
| 249 | { |
| 250 | GdkPixbuf *p2 = gdk_pixbuf_scale_simple(pixbuf, size.x, size.y, |
| 251 | GDK_INTERP_BILINEAR); |
| 252 | if (p2) |
| 253 | { |
| 254 | gdk_pixbuf_unref(pixbuf); |
| 255 | pixbuf = p2; |
| 256 | } |
| 257 | } |
| 258 | |
| 259 | if (!pixbuf) |
| 260 | return wxNullBitmap; |
| 261 | |
| 262 | wxBitmap bmp; |
| 263 | bmp.SetWidth(gdk_pixbuf_get_width(pixbuf)); |
| 264 | bmp.SetHeight(gdk_pixbuf_get_height(pixbuf)); |
| 265 | bmp.SetPixbuf(pixbuf); |
| 266 | |
| 267 | return bmp; |
| 268 | } |
| 269 | |
| 270 | // ---------------------------------------------------------------------------- |
| 271 | // Cleanup |
| 272 | // ---------------------------------------------------------------------------- |
| 273 | |
| 274 | class wxArtGtkModule: public wxModule |
| 275 | { |
| 276 | public: |
| 277 | bool OnInit() { return true; } |
| 278 | void OnExit() |
| 279 | { |
| 280 | if (gs_gtkStyle) |
| 281 | { |
| 282 | g_object_unref(G_OBJECT(gs_gtkStyle)); |
| 283 | gs_gtkStyle = NULL; |
| 284 | } |
| 285 | } |
| 286 | |
| 287 | DECLARE_DYNAMIC_CLASS(wxArtGtkModule) |
| 288 | }; |
| 289 | |
| 290 | IMPLEMENT_DYNAMIC_CLASS(wxArtGtkModule, wxModule) |
| 291 | |
| 292 | #endif // defined(__WXGTK20__) && !defined(__WXUNIVERSAL__) |