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