1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/artgtk.cpp
3 // Purpose:     stock wxArtProvider instance with native GTK+ stock icons
4 // Author:      Vaclav Slavik
5 // Modified by:
6 // Created:     2004-08-22
7 // Copyright:   (c) Vaclav Slavik, 2004
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 
24 #include <gtk/gtk.h>
25 #include "wx/gtk/private.h"
26 
27 // compatibility with older GTK+ versions:
28 #ifndef GTK_STOCK_FILE
29     #define GTK_STOCK_FILE "gtk-file"
30 #endif
31 #ifndef GTK_STOCK_DIRECTORY
32     #define GTK_STOCK_DIRECTORY "gtk-directory"
33 #endif
34 
35 
36 // ----------------------------------------------------------------------------
37 // wxGTK2ArtProvider
38 // ----------------------------------------------------------------------------
39 
40 class wxGTK2ArtProvider : public wxArtProvider
41 {
42 protected:
43     virtual wxBitmap CreateBitmap(const wxArtID& id, const wxArtClient& client,
44                                   const wxSize& size);
45     virtual wxIconBundle CreateIconBundle(const wxArtID& id,
46                                           const wxArtClient& client);
47 };
48 
InitNativeProvider()49 /*static*/ void wxArtProvider::InitNativeProvider()
50 {
51     PushBack(new wxGTK2ArtProvider);
52 }
53 
54 // ----------------------------------------------------------------------------
55 // CreateBitmap routine
56 // ----------------------------------------------------------------------------
57 
58 namespace
59 {
60 
wxArtIDToStock(const wxArtID & id)61 wxString wxArtIDToStock(const wxArtID& id)
62 {
63     #define ART(wxid, gtkid) \
64            if (id == wxid) return gtkid;
65 
66     ART(wxART_ERROR,                               GTK_STOCK_DIALOG_ERROR)
67     ART(wxART_INFORMATION,                         GTK_STOCK_DIALOG_INFO)
68     ART(wxART_WARNING,                             GTK_STOCK_DIALOG_WARNING)
69     ART(wxART_QUESTION,                            GTK_STOCK_DIALOG_QUESTION)
70 
71     //ART(wxART_HELP_SIDE_PANEL,                     )
72     ART(wxART_HELP_SETTINGS,                       GTK_STOCK_SELECT_FONT)
73     //ART(wxART_HELP_BOOK,                           )
74     ART(wxART_HELP_FOLDER,                         GTK_STOCK_DIRECTORY)
75     ART(wxART_HELP_PAGE,                           GTK_STOCK_FILE)
76     ART(wxART_MISSING_IMAGE,                       GTK_STOCK_MISSING_IMAGE)
77     ART(wxART_ADD_BOOKMARK,                        GTK_STOCK_ADD)
78     ART(wxART_DEL_BOOKMARK,                        GTK_STOCK_REMOVE)
79     ART(wxART_GO_BACK,                             GTK_STOCK_GO_BACK)
80     ART(wxART_GO_FORWARD,                          GTK_STOCK_GO_FORWARD)
81     ART(wxART_GO_UP,                               GTK_STOCK_GO_UP)
82     ART(wxART_GO_DOWN,                             GTK_STOCK_GO_DOWN)
83     ART(wxART_GO_TO_PARENT,                        GTK_STOCK_GO_UP)
84     ART(wxART_GO_HOME,                             GTK_STOCK_HOME)
85     ART(wxART_GOTO_FIRST,                          GTK_STOCK_GOTO_FIRST)
86     ART(wxART_GOTO_LAST,                           GTK_STOCK_GOTO_LAST)
87     ART(wxART_FILE_OPEN,                           GTK_STOCK_OPEN)
88     ART(wxART_PRINT,                               GTK_STOCK_PRINT)
89     ART(wxART_HELP,                                GTK_STOCK_HELP)
90     ART(wxART_TIP,                                 GTK_STOCK_DIALOG_INFO)
91     //ART(wxART_REPORT_VIEW,                         )
92     //ART(wxART_LIST_VIEW,                           )
93     //ART(wxART_NEW_DIR,                             )
94     ART(wxART_FOLDER,                              GTK_STOCK_DIRECTORY)
95     ART(wxART_FOLDER_OPEN,                         GTK_STOCK_DIRECTORY)
96     //ART(wxART_GO_DIR_UP,                           )
97     ART(wxART_EXECUTABLE_FILE,                     GTK_STOCK_EXECUTE)
98     ART(wxART_NORMAL_FILE,                         GTK_STOCK_FILE)
99     ART(wxART_TICK_MARK,                           GTK_STOCK_APPLY)
100     ART(wxART_CROSS_MARK,                          GTK_STOCK_CANCEL)
101 
102     ART(wxART_FLOPPY,                              GTK_STOCK_FLOPPY)
103     ART(wxART_CDROM,                               GTK_STOCK_CDROM)
104     ART(wxART_HARDDISK,                            GTK_STOCK_HARDDISK)
105     ART(wxART_REMOVABLE,                           GTK_STOCK_HARDDISK)
106 
107     ART(wxART_FILE_SAVE,                           GTK_STOCK_SAVE)
108     ART(wxART_FILE_SAVE_AS,                        GTK_STOCK_SAVE_AS)
109 
110     ART(wxART_COPY,                                GTK_STOCK_COPY)
111     ART(wxART_CUT,                                 GTK_STOCK_CUT)
112     ART(wxART_PASTE,                               GTK_STOCK_PASTE)
113     ART(wxART_DELETE,                              GTK_STOCK_DELETE)
114     ART(wxART_NEW,                                 GTK_STOCK_NEW)
115 
116     ART(wxART_UNDO,                                GTK_STOCK_UNDO)
117     ART(wxART_REDO,                                GTK_STOCK_REDO)
118 
119     ART(wxART_PLUS,                                GTK_STOCK_ADD)
120     ART(wxART_MINUS,                               GTK_STOCK_REMOVE)
121 
122     ART(wxART_CLOSE,                               GTK_STOCK_CLOSE)
123     ART(wxART_QUIT,                                GTK_STOCK_QUIT)
124 
125     ART(wxART_FIND,                                GTK_STOCK_FIND)
126     ART(wxART_FIND_AND_REPLACE,                    GTK_STOCK_FIND_AND_REPLACE)
127 
128     #undef ART
129 
130     // allow passing GTK+ stock IDs to wxArtProvider -- if a recognized wx
131     // ID wasn't found, pass it to GTK+ in the hope it is a GTK+ or theme
132     // icon name:
133     return id;
134 }
135 
ArtClientToIconSize(const wxArtClient & client)136 GtkIconSize ArtClientToIconSize(const wxArtClient& client)
137 {
138     if (client == wxART_TOOLBAR)
139         return GTK_ICON_SIZE_LARGE_TOOLBAR;
140     else if (client == wxART_MENU || client == wxART_FRAME_ICON)
141         return GTK_ICON_SIZE_MENU;
142     else if (client == wxART_CMN_DIALOG || client == wxART_MESSAGE_BOX)
143         return GTK_ICON_SIZE_DIALOG;
144     else if (client == wxART_BUTTON)
145         return GTK_ICON_SIZE_BUTTON;
146     else
147         return GTK_ICON_SIZE_INVALID; // this is arbitrary
148 }
149 
FindClosestIconSize(const wxSize & size)150 GtkIconSize FindClosestIconSize(const wxSize& size)
151 {
152     #define NUM_SIZES 6
153     static struct
154     {
155         GtkIconSize icon;
156         gint x, y;
157     } s_sizes[NUM_SIZES];
158     static bool s_sizesInitialized = false;
159 
160     if (!s_sizesInitialized)
161     {
162         s_sizes[0].icon = GTK_ICON_SIZE_MENU;
163         s_sizes[1].icon = GTK_ICON_SIZE_SMALL_TOOLBAR;
164         s_sizes[2].icon = GTK_ICON_SIZE_LARGE_TOOLBAR;
165         s_sizes[3].icon = GTK_ICON_SIZE_BUTTON;
166         s_sizes[4].icon = GTK_ICON_SIZE_DND;
167         s_sizes[5].icon = GTK_ICON_SIZE_DIALOG;
168         for (size_t i = 0; i < NUM_SIZES; i++)
169         {
170             gtk_icon_size_lookup(s_sizes[i].icon,
171                                  &s_sizes[i].x, &s_sizes[i].y);
172         }
173         s_sizesInitialized = true;
174     }
175 
176     GtkIconSize best = GTK_ICON_SIZE_DIALOG; // presumably largest
177     unsigned distance = INT_MAX;
178     for (size_t i = 0; i < NUM_SIZES; i++)
179     {
180         // only use larger bitmaps, scaling down looks better than scaling up:
181         if (size.x > s_sizes[i].x || size.y > s_sizes[i].y)
182             continue;
183 
184         unsigned dist = (size.x - s_sizes[i].x) * (size.x - s_sizes[i].x) +
185                         (size.y - s_sizes[i].y) * (size.y - s_sizes[i].y);
186         if (dist == 0)
187             return s_sizes[i].icon;
188         else if (dist < distance)
189         {
190             distance = dist;
191             best = s_sizes[i].icon;
192         }
193     }
194     return best;
195 }
196 
CreateStockIcon(const char * stockid,GtkIconSize size)197 GdkPixbuf *CreateStockIcon(const char *stockid, GtkIconSize size)
198 {
199     // FIXME: This code is not 100% correct, because stock pixmap are
200     //        context-dependent and may be affected by theme engine, the
201     //        correct value can only be obtained for given GtkWidget object.
202     //
203     //        Fool-proof implementation of stock bitmaps would extend wxBitmap
204     //        with "stock-id" representation (in addition to pixmap and pixbuf
205     //        ones) and would convert it to pixbuf when rendered.
206 
207     GtkWidget* widget = wxGTKPrivate::GetButtonWidget();
208 #ifdef __WXGTK3__
209     GtkStyleContext* sc = gtk_widget_get_style_context(widget);
210     GtkIconSet* iconset = gtk_style_context_lookup_icon_set(sc, stockid);
211     GdkPixbuf* pixbuf = NULL;
212     if (iconset)
213         pixbuf = gtk_icon_set_render_icon_pixbuf(iconset, sc, size);
214     return pixbuf;
215 #else
216     GtkStyle* style = gtk_widget_get_style(widget);
217     GtkIconSet* iconset = gtk_style_lookup_icon_set(style, stockid);
218 
219     if (!iconset)
220         return NULL;
221 
222     return gtk_icon_set_render_icon(iconset, style,
223                                     gtk_widget_get_default_direction(),
224                                     GTK_STATE_NORMAL, size, NULL, NULL);
225 #endif
226 }
227 
CreateThemeIcon(const char * iconname,int size)228 GdkPixbuf *CreateThemeIcon(const char *iconname, int size)
229 {
230     return gtk_icon_theme_load_icon
231            (
232                gtk_icon_theme_get_default(),
233                iconname,
234                size,
235                (GtkIconLookupFlags)0,
236                NULL
237            );
238 }
239 
240 
241 // creates either stock or theme icon
CreateGtkIcon(const char * icon_name,GtkIconSize stock_size,const wxSize & pixel_size)242 GdkPixbuf *CreateGtkIcon(const char *icon_name,
243                          GtkIconSize stock_size, const wxSize& pixel_size)
244 {
245     // try stock GTK+ icon first
246     GdkPixbuf *pixbuf = CreateStockIcon(icon_name, stock_size);
247     if ( pixbuf )
248         return pixbuf;
249 
250     // if that fails, try theme icon
251     wxSize size(pixel_size);
252     if ( pixel_size == wxDefaultSize )
253         gtk_icon_size_lookup(stock_size, &size.x, &size.y);
254     return CreateThemeIcon(icon_name, size.x);
255 }
256 
257 template<typename SizeType, typename LoaderFunc>
DoCreateIconBundle(const char * stockid,const SizeType * sizes_from,const SizeType * sizes_to,LoaderFunc get_icon)258 wxIconBundle DoCreateIconBundle(const char *stockid,
259                                 const SizeType *sizes_from,
260                                 const SizeType *sizes_to,
261                                 LoaderFunc get_icon)
262 
263 {
264     wxIconBundle bundle;
265 
266     for ( const SizeType *i = sizes_from; i != sizes_to; ++i )
267     {
268         GdkPixbuf *pixbuf = get_icon(stockid, *i);
269         if ( !pixbuf )
270             continue;
271 
272         wxIcon icon;
273         icon.CopyFromBitmap(wxBitmap(pixbuf));
274         bundle.AddIcon(icon);
275     }
276 
277     return bundle;
278 }
279 
280 } // anonymous namespace
281 
CreateBitmap(const wxArtID & id,const wxArtClient & client,const wxSize & size)282 wxBitmap wxGTK2ArtProvider::CreateBitmap(const wxArtID& id,
283                                          const wxArtClient& client,
284                                          const wxSize& size)
285 {
286     const wxString stockid = wxArtIDToStock(id);
287 
288     GtkIconSize stocksize = (size == wxDefaultSize) ?
289                                 ArtClientToIconSize(client) :
290                                 FindClosestIconSize(size);
291     // we must have some size, this is arbitrary
292     if (stocksize == GTK_ICON_SIZE_INVALID)
293         stocksize = GTK_ICON_SIZE_BUTTON;
294 
295     GdkPixbuf *pixbuf = CreateGtkIcon(stockid.utf8_str(), stocksize, size);
296 
297     if (pixbuf && size != wxDefaultSize &&
298         (size.x != gdk_pixbuf_get_width(pixbuf) ||
299          size.y != gdk_pixbuf_get_height(pixbuf)))
300     {
301         GdkPixbuf *p2 = gdk_pixbuf_scale_simple(pixbuf, size.x, size.y,
302                                                 GDK_INTERP_BILINEAR);
303         if (p2)
304         {
305             g_object_unref (pixbuf);
306             pixbuf = p2;
307         }
308     }
309 
310     return wxBitmap(pixbuf);
311 }
312 
313 wxIconBundle
CreateIconBundle(const wxArtID & id,const wxArtClient & WXUNUSED (client))314 wxGTK2ArtProvider::CreateIconBundle(const wxArtID& id,
315                                     const wxArtClient& WXUNUSED(client))
316 {
317     wxIconBundle bundle;
318     const wxString stockid = wxArtIDToStock(id);
319 
320     // try to load the bundle as stock icon first
321     GtkWidget* widget = wxGTKPrivate::GetButtonWidget();
322 #ifdef __WXGTK3__
323     GtkStyleContext* sc = gtk_widget_get_style_context(widget);
324     GtkIconSet* iconset = gtk_style_context_lookup_icon_set(sc, stockid.utf8_str());
325 #else
326     GtkStyle* style = gtk_widget_get_style(widget);
327     GtkIconSet* iconset = gtk_style_lookup_icon_set(style, stockid.utf8_str());
328 #endif
329     if ( iconset )
330     {
331         GtkIconSize *sizes;
332         gint n_sizes;
333         gtk_icon_set_get_sizes(iconset, &sizes, &n_sizes);
334         bundle = DoCreateIconBundle
335                               (
336                                   stockid.utf8_str(),
337                                   sizes, sizes + n_sizes,
338                                   &CreateStockIcon
339                               );
340         g_free(sizes);
341         return bundle;
342     }
343 
344     // otherwise try icon themes
345     gint *sizes = gtk_icon_theme_get_icon_sizes
346                   (
347                       gtk_icon_theme_get_default(),
348                       stockid.utf8_str()
349                   );
350     if ( !sizes )
351         return bundle;
352 
353     gint *last = sizes;
354     while ( *last )
355         last++;
356 
357     bundle = DoCreateIconBundle
358                           (
359                               stockid.utf8_str(),
360                               sizes, last,
361                               &CreateThemeIcon
362                           );
363     g_free(sizes);
364 
365     return bundle;
366 }
367 
368 // ----------------------------------------------------------------------------
369 // wxArtProvider::GetNativeSizeHint()
370 // ----------------------------------------------------------------------------
371 
372 /*static*/
GetNativeSizeHint(const wxArtClient & client)373 wxSize wxArtProvider::GetNativeSizeHint(const wxArtClient& client)
374 {
375     // Gtk has specific sizes for each client, see artgtk.cpp
376     GtkIconSize gtk_size = ArtClientToIconSize(client);
377     // no size hints for this client
378     if (gtk_size == GTK_ICON_SIZE_INVALID)
379         return wxDefaultSize;
380     gint width, height;
381     gtk_icon_size_lookup( gtk_size, &width, &height);
382     return wxSize(width, height);
383 }
384