1 /** \file
2 * SPIcon: Generic icon widget
3 */
4 /*
5 * Author:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * Jon A. Cruz <jon@joncruz.org>
8 *
9 * Copyright (C) 2002 Lauris Kaplinski
10 *
11 * Released under GNU GPL, read the file 'COPYING' for more information
12 */
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
18 #include <cstring>
19 #include <glib/gmem.h>
20 #include <gtk/gtk.h>
21 #include <gtkmm.h>
23 #include "path-prefix.h"
24 #include "preferences.h"
25 #include "inkscape.h"
26 #include "document.h"
27 #include "sp-item.h"
28 #include "display/nr-arena.h"
29 #include "display/nr-arena-item.h"
30 #include "io/sys.h"
32 #include "icon.h"
34 static void sp_icon_class_init(SPIconClass *klass);
35 static void sp_icon_init(SPIcon *icon);
36 static void sp_icon_dispose(GObject *object);
38 static void sp_icon_reset(SPIcon *icon);
39 static void sp_icon_clear(SPIcon *icon);
41 static void sp_icon_size_request(GtkWidget *widget, GtkRequisition *requisition);
42 static void sp_icon_size_allocate(GtkWidget *widget, GtkAllocation *allocation);
43 static int sp_icon_expose(GtkWidget *widget, GdkEventExpose *event);
45 static void sp_icon_paint(SPIcon *icon, GdkRectangle const *area);
47 static void sp_icon_screen_changed( GtkWidget *widget, GdkScreen *previous_screen );
48 static void sp_icon_style_set( GtkWidget *widget, GtkStyle *previous_style );
49 static void sp_icon_theme_changed( SPIcon *icon );
51 static GdkPixbuf *sp_icon_image_load_pixmap(gchar const *name, unsigned lsize, unsigned psize);
52 static GdkPixbuf *sp_icon_image_load_svg(gchar const *name, GtkIconSize lsize, unsigned psize);
54 static void sp_icon_overlay_pixels( guchar *px, int width, int height, int stride,
55 unsigned r, unsigned g, unsigned b );
57 static void injectCustomSize();
59 static GtkWidgetClass *parent_class;
61 static bool sizeDirty = true;
63 static bool sizeMapDone = false;
64 static GtkIconSize iconSizeLookup[] = {
65 GTK_ICON_SIZE_INVALID,
66 GTK_ICON_SIZE_MENU,
67 GTK_ICON_SIZE_SMALL_TOOLBAR,
68 GTK_ICON_SIZE_LARGE_TOOLBAR,
69 GTK_ICON_SIZE_BUTTON,
70 GTK_ICON_SIZE_DND,
71 GTK_ICON_SIZE_DIALOG,
72 GTK_ICON_SIZE_MENU, // for Inkscape::ICON_SIZE_DECORATION
73 };
75 static std::map<Glib::ustring, Glib::ustring> legacyNames;
77 class IconCacheItem
78 {
79 public:
80 IconCacheItem( GtkIconSize lsize, GdkPixbuf* pb ) :
81 _lsize( lsize ),
82 _pb( pb )
83 {}
84 GtkIconSize _lsize;
85 GdkPixbuf* _pb;
86 };
88 static Glib::RefPtr<Gtk::IconFactory> inkyIcons;
90 GType
91 sp_icon_get_type()
92 {
93 //TODO: switch to GObject
94 // GtkType and such calls were deprecated a while back with the
95 // introduction of GObject as a separate layer, with GType instead. --JonCruz
97 static GType type = 0;
98 if (!type) {
99 GTypeInfo info = {
100 sizeof(SPIconClass),
101 NULL,
102 NULL,
103 (GClassInitFunc) sp_icon_class_init,
104 NULL,
105 NULL,
106 sizeof(SPIcon),
107 0,
108 (GInstanceInitFunc) sp_icon_init,
109 NULL
110 };
111 type = g_type_register_static(GTK_TYPE_WIDGET, "SPIcon", &info, (GTypeFlags)0);
112 }
113 return type;
114 }
116 static void
117 sp_icon_class_init(SPIconClass *klass)
118 {
119 GObjectClass *object_class;
120 GtkWidgetClass *widget_class;
122 object_class = (GObjectClass *) klass;
123 widget_class = (GtkWidgetClass *) klass;
125 parent_class = (GtkWidgetClass*)g_type_class_peek_parent(klass);
127 object_class->dispose = sp_icon_dispose;
129 widget_class->size_request = sp_icon_size_request;
130 widget_class->size_allocate = sp_icon_size_allocate;
131 widget_class->expose_event = sp_icon_expose;
132 widget_class->screen_changed = sp_icon_screen_changed;
133 widget_class->style_set = sp_icon_style_set;
134 }
137 static void
138 sp_icon_init(SPIcon *icon)
139 {
140 GTK_WIDGET_FLAGS(icon) |= GTK_NO_WINDOW;
141 icon->lsize = Inkscape::ICON_SIZE_BUTTON;
142 icon->psize = 0;
143 icon->name = 0;
144 icon->pb = 0;
145 }
147 static void
148 sp_icon_dispose(GObject *object)
149 {
150 SPIcon *icon = SP_ICON(object);
151 sp_icon_clear(icon);
152 if ( icon->name ) {
153 g_free( icon->name );
154 icon->name = 0;
155 }
157 ((GObjectClass *) (parent_class))->dispose(object);
158 }
160 static void sp_icon_reset( SPIcon *icon ) {
161 icon->psize = 0;
162 sp_icon_clear(icon);
163 }
165 static void sp_icon_clear( SPIcon *icon ) {
166 if (icon->pb) {
167 g_object_unref(G_OBJECT(icon->pb));
168 icon->pb = NULL;
169 }
170 }
172 static void
173 sp_icon_size_request(GtkWidget *widget, GtkRequisition *requisition)
174 {
175 SPIcon const *icon = SP_ICON(widget);
177 int const size = ( icon->psize
178 ? icon->psize
179 : sp_icon_get_phys_size(icon->lsize) );
180 requisition->width = size;
181 requisition->height = size;
182 }
184 static void
185 sp_icon_size_allocate(GtkWidget *widget, GtkAllocation *allocation)
186 {
187 widget->allocation = *allocation;
189 if (GTK_WIDGET_DRAWABLE(widget)) {
190 gtk_widget_queue_draw(widget);
191 }
192 }
194 static int sp_icon_expose(GtkWidget *widget, GdkEventExpose *event)
195 {
196 if ( GTK_WIDGET_DRAWABLE(widget) ) {
197 SPIcon *icon = SP_ICON(widget);
198 if ( !icon->pb ) {
199 sp_icon_fetch_pixbuf( icon );
200 }
202 sp_icon_paint(SP_ICON(widget), &event->area);
203 }
205 return TRUE;
206 }
208 static GdkPixbuf* renderup( gchar const* name, Inkscape::IconSize lsize, unsigned psize );
210 // PUBLIC CALL:
211 void sp_icon_fetch_pixbuf( SPIcon *icon )
212 {
213 g_message("sp_icon_fetch_pixbuf(%p) [%s]", icon, icon->name);
214 if ( icon ) {
215 if ( !icon->pb ) {
216 icon->psize = sp_icon_get_phys_size(icon->lsize);
217 icon->pb = renderup(icon->name, icon->lsize, icon->psize);
218 }
219 }
220 }
222 static GdkPixbuf* renderup( gchar const* name, Inkscape::IconSize lsize, unsigned psize ) {
223 GtkIconTheme *theme = gtk_icon_theme_get_default();
225 GdkPixbuf *pb = 0;
226 if (gtk_icon_theme_has_icon(theme, name)) {
227 pb = gtk_icon_theme_load_icon(theme, name, psize, (GtkIconLookupFlags) 0, NULL);
228 }
229 if (!pb) {
230 pb = sp_icon_image_load_svg( name, Inkscape::getRegisteredIconSize(lsize), psize );
231 if (!pb && (legacyNames.find(name) != legacyNames.end())) {
232 if ( Inkscape::Preferences::get()->getBool("/debug/icons/dumpSvg") ) {
233 g_message("Checking fallback [%s]->[%s]", name, legacyNames[name].c_str());
234 }
235 pb = sp_icon_image_load_svg( legacyNames[name].c_str(), Inkscape::getRegisteredIconSize(lsize), psize );
236 }
238 // if this was loaded from SVG, add it as a builtin icon
239 if (pb) {
240 gtk_icon_theme_add_builtin_icon(name, psize, pb);
241 }
242 }
243 if (!pb) {
244 pb = sp_icon_image_load_pixmap( name, lsize, psize );
245 }
246 if ( !pb ) {
247 // TODO: We should do something more useful if we can't load the image.
248 g_warning ("failed to load icon '%s'", name);
249 }
250 return pb;
251 }
253 static void sp_icon_screen_changed( GtkWidget *widget, GdkScreen *previous_screen )
254 {
255 if ( GTK_WIDGET_CLASS( parent_class )->screen_changed ) {
256 GTK_WIDGET_CLASS( parent_class )->screen_changed( widget, previous_screen );
257 }
258 SPIcon *icon = SP_ICON(widget);
259 sp_icon_theme_changed(icon);
260 }
262 static void sp_icon_style_set( GtkWidget *widget, GtkStyle *previous_style )
263 {
264 if ( GTK_WIDGET_CLASS( parent_class )->style_set ) {
265 GTK_WIDGET_CLASS( parent_class )->style_set( widget, previous_style );
266 }
267 SPIcon *icon = SP_ICON(widget);
268 sp_icon_theme_changed(icon);
269 }
271 static void sp_icon_theme_changed( SPIcon *icon )
272 {
273 //g_message("Got a change bump for this icon");
274 sizeDirty = true;
275 sp_icon_reset(icon);
276 gtk_widget_queue_draw( GTK_WIDGET(icon) );
277 }
280 static Glib::ustring icon_cache_key(gchar const *name, unsigned lsize, unsigned psize);
281 static GdkPixbuf *get_cached_pixbuf(Glib::ustring const &key);
283 static void setupLegacyNaming() {
284 legacyNames["document-import"] ="file_import";
285 legacyNames["document-export"] ="file_export";
286 legacyNames["document-import-ocal"] ="ocal_import";
287 legacyNames["document-export-ocal"] ="ocal_export";
288 legacyNames["document-metadata"] ="document_metadata";
289 legacyNames["dialog-input-devices"] ="input_devices";
290 legacyNames["edit-duplicate"] ="edit_duplicate";
291 legacyNames["edit-clone"] ="edit_clone";
292 legacyNames["edit-clone-unlink"] ="edit_unlink_clone";
293 legacyNames["edit-select-original"] ="edit_select_original";
294 legacyNames["edit-undo-history"] ="edit_undo_history";
295 legacyNames["edit-paste-in-place"] ="selection_paste_in_place";
296 legacyNames["edit-paste-style"] ="selection_paste_style";
297 legacyNames["selection-make-bitmap-copy"] ="selection_bitmap";
298 legacyNames["edit-select-all"] ="selection_select_all";
299 legacyNames["edit-select-all-layers"] ="selection_select_all_in_all_layers";
300 legacyNames["edit-select-invert"] ="selection_invert";
301 legacyNames["edit-select-none"] ="selection_deselect";
302 legacyNames["dialog-xml-editor"] ="xml_editor";
303 legacyNames["zoom-original"] ="zoom_1_to_1";
304 legacyNames["zoom-half-size"] ="zoom_1_to_2";
305 legacyNames["zoom-double-size"] ="zoom_2_to_1";
306 legacyNames["zoom-fit-selection"] ="zoom_select";
307 legacyNames["zoom-fit-drawing"] ="zoom_draw";
308 legacyNames["zoom-fit-page"] ="zoom_page";
309 legacyNames["zoom-fit-width"] ="zoom_pagewidth";
310 legacyNames["zoom-previous"] ="zoom_previous";
311 legacyNames["zoom-next"] ="zoom_next";
312 legacyNames["zoom-in"] ="zoom_in";
313 legacyNames["zoom-out"] ="zoom_out";
314 legacyNames["show-grid"] ="grid";
315 legacyNames["show-guides"] ="guides";
316 legacyNames["color-management"] ="color_management";
317 legacyNames["show-dialogs"] ="dialog_toggle";
318 legacyNames["dialog-messages"] ="messages";
319 legacyNames["dialog-scripts"] ="scripts";
320 legacyNames["window-previous"] ="window_previous";
321 legacyNames["window-next"] ="window_next";
322 legacyNames["dialog-icon-preview"] ="view_icon_preview";
323 legacyNames["window-new"] ="view_new";
324 legacyNames["view-fullscreen"] ="fullscreen";
325 legacyNames["layer-new"] ="new_layer";
326 legacyNames["layer-rename"] ="rename_layer";
327 legacyNames["layer-previous"] ="switch_to_layer_above";
328 legacyNames["layer-next"] ="switch_to_layer_below";
329 legacyNames["selection-move-to-layer-above"] ="move_selection_above";
330 legacyNames["selection-move-to-layer-below"] ="move_selection_below";
331 legacyNames["layer-raise"] ="raise_layer";
332 legacyNames["layer-lower"] ="lower_layer";
333 legacyNames["layer-top"] ="layer_to_top";
334 legacyNames["layer-bottom"] ="layer_to_bottom";
335 legacyNames["layer-delete"] ="delete_layer";
336 legacyNames["dialog-layers"] ="layers";
337 legacyNames["dialog-fill-and-stroke"] ="fill_and_stroke";
338 legacyNames["dialog-object-properties"] ="dialog_item_properties";
339 legacyNames["object-group"] ="selection_group";
340 legacyNames["object-ungroup"] ="selection_ungroup";
341 legacyNames["selection-raise"] ="selection_up";
342 legacyNames["selection-lower"] ="selection_down";
343 legacyNames["selection-top"] ="selection_top";
344 legacyNames["selection-bottom"] ="selection_bot";
345 legacyNames["object-rotate-left"] ="object_rotate_90_CCW";
346 legacyNames["object-rotate-right"] ="object_rotate_90_CW";
347 legacyNames["object-flip-horizontal"] ="object_flip_hor";
348 legacyNames["object-flip-vertical"] ="object_flip_ver";
349 legacyNames["dialog-transform"] ="object_trans";
350 legacyNames["dialog-align-and-distribute"] ="object_align";
351 legacyNames["dialog-rows-and-columns"] ="grid_arrange";
352 legacyNames["object-to-path"] ="object_tocurve";
353 legacyNames["stroke-to-path"] ="stroke_tocurve";
354 legacyNames["bitmap-trace"] ="selection_trace";
355 legacyNames["path-union"] ="union";
356 legacyNames["path-difference"] ="difference";
357 legacyNames["path-intersection"] ="intersection";
358 legacyNames["path-exclusion"] ="exclusion";
359 legacyNames["path-division"] ="division";
360 legacyNames["path-cut"] ="cut_path";
361 legacyNames["path-combine"] ="selection_combine";
362 legacyNames["path-break-apart"] ="selection_break";
363 legacyNames["path-outset"] ="outset_path";
364 legacyNames["path-inset"] ="inset_path";
365 legacyNames["path-offset-dynamic"] ="dynamic_offset";
366 legacyNames["path-offset-linked"] ="linked_offset";
367 legacyNames["path-simplify"] ="simplify";
368 legacyNames["path-reverse"] ="selection_reverse";
369 legacyNames["dialog-text-and-font"] ="object_font";
370 legacyNames["text-put-on-path"] ="put_on_path";
371 legacyNames["text-remove-from-path"] ="remove_from_path";
372 legacyNames["text-flow-into-frame"] ="flow_into_frame";
373 legacyNames["text-unflow"] ="unflow";
374 legacyNames["text-convert-to-regular"] ="convert_to_text";
375 legacyNames["text-unkern"] ="remove_manual_kerns";
376 legacyNames["help-keyboard-shortcuts"] ="help_keys";
377 legacyNames["help-contents"] ="help_tutorials";
378 legacyNames["inkscape"] ="inkscape_options";
379 legacyNames["dialog-memory"] ="about_memory";
380 legacyNames["tool-pointer"] ="draw_select";
381 legacyNames["tool-node-editor"] ="draw_node";
382 legacyNames["tool-tweak"] ="draw_tweak";
383 legacyNames["zoom"] ="draw_zoom";
384 legacyNames["draw-rectangle"] ="draw_rect";
385 legacyNames["draw-cuboid"] ="draw_3dbox";
386 legacyNames["draw-ellipse"] ="draw_arc";
387 legacyNames["draw-polygon-star"] ="draw_star";
388 legacyNames["draw-spiral"] ="draw_spiral";
389 legacyNames["draw-freehand"] ="draw_freehand";
390 legacyNames["draw-path"] ="draw_pen";
391 legacyNames["draw-calligraphic"] ="draw_calligraphic";
392 legacyNames["draw-eraser"] ="draw_erase";
393 legacyNames["color-fill"] ="draw_paintbucket";
394 legacyNames["draw-text"] ="draw_text";
395 legacyNames["draw-connector"] ="draw_connector";
396 legacyNames["color-gradient"] ="draw_gradient";
397 legacyNames["color-picker"] ="draw_dropper";
398 legacyNames["transform-affect-stroke"] ="transform_stroke";
399 legacyNames["transform-affect-rounded-corners"] ="transform_corners";
400 legacyNames["transform-affect-gradient"] ="transform_gradient";
401 legacyNames["transform-affect-pattern"] ="transform_pattern";
402 legacyNames["node-add"] ="node_insert";
403 legacyNames["node-delete"] ="node_delete";
404 legacyNames["node-join"] ="node_join";
405 legacyNames["node-break"] ="node_break";
406 legacyNames["node-join-segment"] ="node_join_segment";
407 legacyNames["node-delete-segment"] ="node_delete_segment";
408 legacyNames["node-type-cusp"] ="node_cusp";
409 legacyNames["node-type-smooth"] ="node_smooth";
410 legacyNames["node-type-symmetric"] ="node_symmetric";
411 legacyNames["node-type-auto-smooth"] ="node_auto";
412 legacyNames["node-segment-curve"] ="node_curve";
413 legacyNames["node-segment-line"] ="node_line";
414 legacyNames["show-node-handles"] ="nodes_show_handles";
415 legacyNames["path-effect-parameter-next"] ="edit_next_parameter";
416 legacyNames["show-path-outline"] ="nodes_show_helperpath";
417 legacyNames["path-clip-edit"] ="nodeedit-clippath";
418 legacyNames["path-mask-edit"] ="nodeedit-mask";
419 legacyNames["node-type-cusp"] ="node_cusp";
420 legacyNames["object-tweak-push"] ="tweak_move_mode";
421 legacyNames["object-tweak-attract"] ="tweak_move_mode_inout";
422 legacyNames["object-tweak-randomize"] ="tweak_move_mode_jitter";
423 legacyNames["object-tweak-shrink"] ="tweak_scale_mode";
424 legacyNames["object-tweak-rotate"] ="tweak_rotate_mode";
425 legacyNames["object-tweak-duplicate"] ="tweak_moreless_mode";
426 legacyNames["object-tweak-push"] ="tweak_move_mode";
427 legacyNames["path-tweak-push"] ="tweak_push_mode";
428 legacyNames["path-tweak-shrink"] ="tweak_shrink_mode";
429 legacyNames["path-tweak-attract"] ="tweak_attract_mode";
430 legacyNames["path-tweak-roughen"] ="tweak_roughen_mode";
431 legacyNames["object-tweak-paint"] ="tweak_colorpaint_mode";
432 legacyNames["object-tweak-jitter-color"] ="tweak_colorjitter_mode";
433 legacyNames["object-tweak-blur"] ="tweak_blur_mode";
434 legacyNames["rectangle-make-corners-sharp"] ="squared_corner";
435 legacyNames["perspective-parallel"] ="toggle_vp_x";
436 legacyNames["draw-ellipse-whole"] ="reset_circle";
437 legacyNames["draw-ellipse-segment"] ="circle_closed_arc";
438 legacyNames["draw-ellipse-arc"] ="circle_open_arc";
439 legacyNames["draw-polygon"] ="star_flat";
440 legacyNames["draw-star"] ="star_angled";
441 legacyNames["path-mode-bezier"] ="bezier_mode";
442 legacyNames["path-mode-spiro"] ="spiro_splines_mode";
443 legacyNames["path-mode-polyline"] ="polylines_mode";
444 legacyNames["path-mode-polyline-paraxial"] ="paraxial_lines_mode";
445 legacyNames["draw-use-tilt"] ="guse_tilt";
446 legacyNames["draw-use-pressure"] ="guse_pressure";
447 legacyNames["draw-trace-background"] ="trace_background";
448 legacyNames["draw-eraser-delete-objects"] ="delete_object";
449 legacyNames["format-text-direction-vertical"] ="writing_mode_tb";
450 legacyNames["format-text-direction-horizontal"] ="writing_mode_lr";
451 legacyNames["connector-avoid"] ="connector_avoid";
452 legacyNames["connector-ignore"] ="connector_ignore";
453 legacyNames["object-fill"] ="controls_fill";
454 legacyNames["object-stroke"] ="controls_stroke";
455 legacyNames["snap"] ="toggle_snap_global";
456 legacyNames["snap-bounding-box"] ="toggle_snap_bbox";
457 legacyNames["snap-bounding-box-edges"] ="toggle_snap_to_bbox_path";
458 legacyNames["snap-bounding-box-corners"] ="toggle_snap_to_bbox_node";
459 legacyNames["snap-bounding-box-midpoints"] ="toggle_snap_to_bbox_edge_midpoints";
460 legacyNames["snap-bounding-box-center"] ="toggle_snap_to_bbox_midpoints";
461 legacyNames["snap-nodes"] ="toggle_snap_nodes";
462 legacyNames["snap-nodes-path"] ="toggle_snap_to_paths";
463 legacyNames["snap-nodes-cusp"] ="toggle_snap_to_nodes";
464 legacyNames["snap-nodes-smooth"] ="toggle_snap_to_smooth_nodes";
465 legacyNames["snap-nodes-midpoint"] ="toggle_snap_to_midpoints";
466 legacyNames["snap-nodes-intersection"] ="toggle_snap_to_path_intersections";
467 legacyNames["snap-nodes-center"] ="toggle_snap_to_bbox_midpoints-3";
468 legacyNames["snap-nodes-rotation-center"] ="toggle_snap_center";
469 legacyNames["snap-page"] ="toggle_snap_page_border";
470 legacyNames["snap-grid-guide-intersections"] ="toggle_snap_grid_guide_intersections";
471 legacyNames["align-horizontal-right-to-anchor"] ="al_left_out";
472 legacyNames["align-horizontal-left"] ="al_left_in";
473 legacyNames["align-horizontal-center"] ="al_center_hor";
474 legacyNames["align-horizontal-right"] ="al_right_in";
475 legacyNames["align-horizontal-left-to-anchor"] ="al_right_out";
476 legacyNames["align-horizontal-baseline"] ="al_baselines_vert";
477 legacyNames["align-vertical-bottom-to-anchor"] ="al_top_out";
478 legacyNames["align-vertical-top"] ="al_top_in";
479 legacyNames["align-vertical-center"] ="al_center_ver";
480 legacyNames["align-vertical-bottom"] ="al_bottom_in";
481 legacyNames["align-vertical-top-to-anchor"] ="al_bottom_out";
482 legacyNames["align-vertical-baseline"] ="al_baselines_hor";
483 legacyNames["distribute-horizontal-left"] ="distribute_left";
484 legacyNames["distribute-horizontal-center"] ="distribute_hcentre";
485 legacyNames["distribute-horizontal-right"] ="distribute_right";
486 legacyNames["distribute-horizontal-baseline"] ="distribute_baselines_hor";
487 legacyNames["distribute-vertical-bottom"] ="distribute_bottom";
488 legacyNames["distribute-vertical-center"] ="distribute_vcentre";
489 legacyNames["distribute-vertical-top"] ="distribute_top";
490 legacyNames["distribute-vertical-baseline"] ="distribute_baselines_vert";
491 legacyNames["distribute-randomize"] ="distribute_randomize";
492 legacyNames["distribute-unclump"] ="unclump";
493 legacyNames["distribute-graph"] ="graph_layout";
494 legacyNames["distribute-graph-directed"] ="directed_graph";
495 legacyNames["distribute-remove-overlaps"] ="remove_overlaps";
496 legacyNames["align-horizontal-node"] ="node_valign";
497 legacyNames["align-vertical-node"] ="node_halign";
498 legacyNames["distribute-vertical-node"] ="node_vdistribute";
499 legacyNames["distribute-horizontal-node"] ="node_hdistribute";
500 legacyNames["xml-element-new"] ="add_xml_element_node";
501 legacyNames["xml-text-new"] ="add_xml_text_node";
502 legacyNames["xml-node-delete"] ="delete_xml_node";
503 legacyNames["xml-node-duplicate"] ="duplicate_xml_node";
504 legacyNames["xml-attribute-delete"] ="delete_xml_attribute";
505 legacyNames["transform-move-horizontal"] ="arrows_hor";
506 legacyNames["transform-move-vertical"] ="arrows_ver";
507 legacyNames["transform-scale-horizontal"] ="transform_scale_hor";
508 legacyNames["transform-scale-vertical"] ="transform_scale_ver";
509 legacyNames["transform-skew-horizontal"] ="transform_scew_hor";
510 legacyNames["transform-skew-vertical"] ="transform_scew_ver";
511 legacyNames["object-fill"] ="properties_fill";
512 legacyNames["object-stroke"] ="properties_stroke_paint";
513 legacyNames["object-stroke-style"] ="properties_stroke";
514 legacyNames["paint-none"] ="fill_none";
515 legacyNames["paint-solid"] ="fill_solid";
516 legacyNames["paint-gradient-linear"] ="fill_gradient";
517 legacyNames["paint-gradient-radial"] ="fill_radial";
518 legacyNames["paint-pattern"] ="fill_pattern";
519 legacyNames["paint-unknown"] ="fill_unset";
520 legacyNames["fill-rule-even-odd"] ="fillrule_evenodd";
521 legacyNames["fill-rule-nonzero"] ="fillrule_nonzero";
522 legacyNames["stroke-join-miter"] ="join_miter";
523 legacyNames["stroke-join-bevel"] ="join_bevel";
524 legacyNames["stroke-join-round"] ="join_round";
525 legacyNames["stroke-cap-butt"] ="cap_butt";
526 legacyNames["stroke-cap-square"] ="cap_square";
527 legacyNames["stroke-cap-round"] ="cap_round";
528 legacyNames["guides"] ="guide";
529 legacyNames["grid-rectangular"] ="grid_xy";
530 legacyNames["grid-axonometric"] ="grid_axonom";
531 legacyNames["object-visible"] ="visible";
532 legacyNames["object-hidden"] ="hidden";
533 legacyNames["object-unlocked"] ="lock_unlocked";
534 legacyNames["object-locked"] ="width_height_lock";
535 legacyNames["zoom"] ="sticky_zoom";
536 }
538 static GtkWidget *
539 sp_icon_new_full( Inkscape::IconSize lsize, gchar const *name )
540 {
541 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
542 static bool dump = prefs->getBool( "/debug/icons/dumpGtk");
544 GtkWidget *widget = 0;
545 gint trySize = CLAMP( static_cast<gint>(lsize), 0, static_cast<gint>(G_N_ELEMENTS(iconSizeLookup) - 1) );
546 if ( !sizeMapDone ) {
547 injectCustomSize();
548 }
549 GtkIconSize mappedSize = iconSizeLookup[trySize];
551 GtkStockItem stock;
552 gboolean stockFound = gtk_stock_lookup( name, &stock );
554 GtkWidget *img = 0;
555 if ( legacyNames.empty() ) {
556 setupLegacyNaming();
557 }
559 if ( stockFound ) {
560 img = gtk_image_new_from_stock( name, mappedSize );
561 } else {
562 img = gtk_image_new_from_icon_name( name, mappedSize );
563 if ( dump ) {
564 g_message("gtk_image_new_from_icon_name( '%s', %d ) = %p", name, mappedSize, img);
565 GtkImageType thing = gtk_image_get_storage_type(GTK_IMAGE(img));
566 g_message(" Type is %d %s", (int)thing, (thing == GTK_IMAGE_EMPTY ? "Empty" : "ok"));
567 }
568 }
570 if ( img ) {
571 GtkImageType type = gtk_image_get_storage_type( GTK_IMAGE(img) );
572 if ( type == GTK_IMAGE_STOCK ) {
573 if ( !stockFound ) {
574 // It's not showing as a stock ID, so assume it will be present internally
575 // TODO restore: populate_placeholder_icon( name, mappedSize );
576 // TODO restore: addPreRender( mappedSize, name );
578 // Add a hook to render if set visible before prerender is done.
579 // TODO restore g_signal_connect( G_OBJECT(img), "map", G_CALLBACK(imageMapCB), GINT_TO_POINTER(static_cast<int>(mappedSize)) );
580 if ( dump ) {
581 g_message(" connecting %p for imageMapCB for [%s] %d", img, name, (int)mappedSize);
582 }
583 }
584 widget = GTK_WIDGET(img);
585 img = 0;
586 if ( dump ) {
587 g_message( "loaded gtk '%s' %d (GTK_IMAGE_STOCK) %s on %p", name, mappedSize, (stockFound ? "STOCK" : "local"), widget );
588 }
589 } else if ( type == GTK_IMAGE_ICON_NAME ) {
590 widget = GTK_WIDGET(img);
591 img = 0;
593 if (!gtk_icon_theme_has_icon(gtk_icon_theme_get_default(), name)) {
594 // TODO temporary work-around. until background rendering is restored.
595 int psize = sp_icon_get_phys_size(lsize);
596 renderup(name, lsize, psize);
597 }
599 // Add a hook to render if set visible before prerender is done.
600 // TODO restore g_signal_connect( G_OBJECT(widget), "map", G_CALLBACK(imageMapNamedCB), GINT_TO_POINTER(0) );
602 if ( prefs->getBool("/options/iconrender/named_nodelay") ) {
603 // TODO restore int psize = sp_icon_get_phys_size(lsize);
604 // TODO restore prerender_icon(name, mappedSize, psize);
605 } else {
606 // TODO restore addPreRender( mappedSize, name );
607 }
608 } else {
609 if ( dump ) {
610 g_message( "skipped gtk '%s' %d (not GTK_IMAGE_STOCK)", name, lsize );
611 }
612 //g_object_unref( (GObject *)img );
613 img = 0;
614 }
615 }
617 if ( !widget ) {
618 //g_message("Creating an SPIcon instance for %s:%d", name, (int)lsize);
619 SPIcon *icon = (SPIcon *)g_object_new(SP_TYPE_ICON, NULL);
620 icon->lsize = lsize;
621 icon->name = g_strdup(name);
622 icon->psize = sp_icon_get_phys_size(lsize);
624 widget = GTK_WIDGET(icon);
625 }
627 return widget;
628 }
630 GtkWidget *
631 sp_icon_new( Inkscape::IconSize lsize, gchar const *name )
632 {
633 return sp_icon_new_full( lsize, name );
634 }
636 // PUBLIC CALL:
637 Gtk::Widget *sp_icon_get_icon( Glib::ustring const &oid, Inkscape::IconSize size )
638 {
639 Gtk::Widget *result = 0;
640 GtkWidget *widget = sp_icon_new_full( static_cast<Inkscape::IconSize>(Inkscape::getRegisteredIconSize(size)), oid.c_str() );
642 if ( widget ) {
643 if ( GTK_IS_IMAGE(widget) ) {
644 GtkImage *img = GTK_IMAGE(widget);
645 result = Glib::wrap( img );
646 } else {
647 result = Glib::wrap( widget );
648 }
649 }
651 return result;
652 }
654 GtkIconSize
655 sp_icon_get_gtk_size(int size)
656 {
657 static GtkIconSize sizemap[64] = {(GtkIconSize)0};
658 size = CLAMP(size, 4, 63);
659 if (!sizemap[size]) {
660 static int count = 0;
661 char c[64];
662 g_snprintf(c, 64, "InkscapeIcon%d", count++);
663 sizemap[size] = gtk_icon_size_register(c, size, size);
664 }
665 return sizemap[size];
666 }
669 static void injectCustomSize()
670 {
671 // TODO - still need to handle the case of theme changes and resize, especially as we can't re-register a string.
672 if ( !sizeMapDone )
673 {
674 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
675 bool dump = prefs->getBool( "/debug/icons/dumpDefault");
676 gint width = 0;
677 gint height = 0;
678 if ( gtk_icon_size_lookup(GTK_ICON_SIZE_MENU, &width, &height ) ) {
679 gint newWidth = ((width * 3) / 4);
680 gint newHeight = ((height * 3) / 4);
681 GtkIconSize newSizeEnum = gtk_icon_size_register( "inkscape-decoration", newWidth, newHeight );
682 if ( newSizeEnum ) {
683 if ( dump ) {
684 g_message("Registered (%d, %d) <= (%d, %d) as index %d", newWidth, newHeight, width, height, newSizeEnum);
685 }
686 guint index = static_cast<guint>(Inkscape::ICON_SIZE_DECORATION);
687 if ( index < G_N_ELEMENTS(iconSizeLookup) ) {
688 iconSizeLookup[index] = newSizeEnum;
689 } else if ( dump ) {
690 g_message("size lookup array too small to store entry");
691 }
692 }
693 }
694 sizeMapDone = true;
695 }
697 static bool hit = false;
698 if ( !hit ) {
699 hit = true;
700 inkyIcons = Gtk::IconFactory::create();
701 inkyIcons->add_default();
702 }
703 }
705 GtkIconSize Inkscape::getRegisteredIconSize( Inkscape::IconSize size )
706 {
707 GtkIconSize other = GTK_ICON_SIZE_MENU;
708 injectCustomSize();
709 size = CLAMP( size, Inkscape::ICON_SIZE_MENU, Inkscape::ICON_SIZE_DECORATION );
710 if ( size == Inkscape::ICON_SIZE_DECORATION ) {
711 other = gtk_icon_size_from_name("inkscape-decoration");
712 } else {
713 other = static_cast<GtkIconSize>(size);
714 }
716 return other;
717 }
720 // PUBLIC CALL:
721 int sp_icon_get_phys_size(int size)
722 {
723 static bool init = false;
724 static int lastSys[Inkscape::ICON_SIZE_DECORATION + 1];
725 static int vals[Inkscape::ICON_SIZE_DECORATION + 1];
727 size = CLAMP( size, GTK_ICON_SIZE_MENU, Inkscape::ICON_SIZE_DECORATION );
729 if ( !sizeMapDone ) {
730 injectCustomSize();
731 }
733 if ( sizeDirty && init ) {
734 GtkIconSize const gtkSizes[] = {
735 GTK_ICON_SIZE_MENU,
736 GTK_ICON_SIZE_SMALL_TOOLBAR,
737 GTK_ICON_SIZE_LARGE_TOOLBAR,
738 GTK_ICON_SIZE_BUTTON,
739 GTK_ICON_SIZE_DND,
740 GTK_ICON_SIZE_DIALOG,
741 static_cast<guint>(Inkscape::ICON_SIZE_DECORATION) < G_N_ELEMENTS(iconSizeLookup) ?
742 iconSizeLookup[static_cast<int>(Inkscape::ICON_SIZE_DECORATION)] :
743 GTK_ICON_SIZE_MENU
744 };
745 for (unsigned i = 0; i < G_N_ELEMENTS(gtkSizes) && init; ++i) {
746 guint const val_ix = (gtkSizes[i] <= GTK_ICON_SIZE_DIALOG) ? (guint)gtkSizes[i] : (guint)Inkscape::ICON_SIZE_DECORATION;
748 g_assert( val_ix < G_N_ELEMENTS(vals) );
750 gint width = 0;
751 gint height = 0;
752 if ( gtk_icon_size_lookup(gtkSizes[i], &width, &height ) ) {
753 init &= (lastSys[val_ix] == std::max(width, height));
754 }
755 }
756 }
758 if ( !init ) {
759 sizeDirty = false;
760 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
761 bool dump = prefs->getBool("/debug/icons/dumpDefault");
763 if ( dump ) {
764 g_message( "Default icon sizes:" );
765 }
766 memset( vals, 0, sizeof(vals) );
767 memset( lastSys, 0, sizeof(lastSys) );
768 GtkIconSize const gtkSizes[] = {
769 GTK_ICON_SIZE_MENU,
770 GTK_ICON_SIZE_SMALL_TOOLBAR,
771 GTK_ICON_SIZE_LARGE_TOOLBAR,
772 GTK_ICON_SIZE_BUTTON,
773 GTK_ICON_SIZE_DND,
774 GTK_ICON_SIZE_DIALOG,
775 static_cast<guint>(Inkscape::ICON_SIZE_DECORATION) < G_N_ELEMENTS(iconSizeLookup) ?
776 iconSizeLookup[static_cast<int>(Inkscape::ICON_SIZE_DECORATION)] :
777 GTK_ICON_SIZE_MENU
778 };
779 gchar const *const names[] = {
780 "GTK_ICON_SIZE_MENU",
781 "GTK_ICON_SIZE_SMALL_TOOLBAR",
782 "GTK_ICON_SIZE_LARGE_TOOLBAR",
783 "GTK_ICON_SIZE_BUTTON",
784 "GTK_ICON_SIZE_DND",
785 "GTK_ICON_SIZE_DIALOG",
786 "inkscape-decoration"
787 };
789 GtkWidget *icon = (GtkWidget *)g_object_new(SP_TYPE_ICON, NULL);
791 for (unsigned i = 0; i < G_N_ELEMENTS(gtkSizes); ++i) {
792 guint const val_ix = (gtkSizes[i] <= GTK_ICON_SIZE_DIALOG) ? (guint)gtkSizes[i] : (guint)Inkscape::ICON_SIZE_DECORATION;
794 g_assert( val_ix < G_N_ELEMENTS(vals) );
796 gint width = 0;
797 gint height = 0;
798 bool used = false;
799 if ( gtk_icon_size_lookup(gtkSizes[i], &width, &height ) ) {
800 vals[val_ix] = std::max(width, height);
801 lastSys[val_ix] = vals[val_ix];
802 used = true;
803 }
804 if (dump) {
805 g_message(" =-- %u size:%d %c(%d, %d) '%s'",
806 i, gtkSizes[i],
807 ( used ? ' ' : 'X' ), width, height, names[i]);
808 }
809 gchar const *id = GTK_STOCK_OPEN;
810 GdkPixbuf *pb = gtk_widget_render_icon( icon, id, gtkSizes[i], NULL);
811 if (pb) {
812 width = gdk_pixbuf_get_width(pb);
813 height = gdk_pixbuf_get_height(pb);
814 int newSize = std::max( width, height );
815 // TODO perhaps check a few more stock icons to get a range on sizes.
816 if ( newSize > 0 ) {
817 vals[val_ix] = newSize;
818 }
819 if (dump) {
820 g_message(" %u size:%d (%d, %d)", i, gtkSizes[i], width, height);
821 }
823 g_object_unref(G_OBJECT(pb));
824 }
825 }
826 //g_object_unref(icon);
827 init = true;
828 }
830 // Fixup workaround
831 if ((size == GTK_ICON_SIZE_MENU) || (size == GTK_ICON_SIZE_SMALL_TOOLBAR) || (size == GTK_ICON_SIZE_LARGE_TOOLBAR)) {
832 gint width = 0;
833 gint height = 0;
834 if ( gtk_icon_size_lookup( static_cast<GtkIconSize>(size), &width, &height ) ) {
835 vals[size] = std::max( width, height );
836 }
837 }
839 return vals[size];
840 }
842 static void sp_icon_paint(SPIcon *icon, GdkRectangle const */*area*/)
843 {
844 GtkWidget &widget = *GTK_WIDGET(icon);
845 GdkPixbuf *image = icon->pb;
846 bool unref_image = false;
848 /* copied from the expose function of GtkImage */
849 if (GTK_WIDGET_STATE (icon) != GTK_STATE_NORMAL && image) {
850 GtkIconSource *source = gtk_icon_source_new();
851 gtk_icon_source_set_pixbuf(source, icon->pb);
852 gtk_icon_source_set_size(source, GTK_ICON_SIZE_SMALL_TOOLBAR); // note: this is boilerplate and not used
853 gtk_icon_source_set_size_wildcarded(source, FALSE);
854 image = gtk_style_render_icon (widget.style, source, gtk_widget_get_direction(&widget),
855 (GtkStateType) GTK_WIDGET_STATE(&widget), (GtkIconSize)-1, &widget, "gtk-image");
856 gtk_icon_source_free(source);
857 unref_image = true;
858 }
860 if (image) {
861 int x = floor(widget.allocation.x + ((widget.allocation.width - widget.requisition.width) * 0.5));
862 int y = floor(widget.allocation.y + ((widget.allocation.height - widget.requisition.height) * 0.5));
863 int width = gdk_pixbuf_get_width(image);
864 int height = gdk_pixbuf_get_height(image);
865 // Limit drawing to when we actually have something. Avoids some crashes.
866 if ( (width > 0) && (height > 0) ) {
867 gdk_draw_pixbuf(GDK_DRAWABLE(widget.window), widget.style->black_gc, image,
868 0, 0, x, y, width, height,
869 GDK_RGB_DITHER_NORMAL, x, y);
870 }
871 }
873 if (unref_image) {
874 g_object_unref(G_OBJECT(image));
875 }
876 }
878 GdkPixbuf *sp_icon_image_load_pixmap(gchar const *name, unsigned /*lsize*/, unsigned psize)
879 {
880 gchar *path = (gchar *) g_strdup_printf("%s/%s.png", INKSCAPE_PIXMAPDIR, name);
881 // TODO: bulia, please look over
882 gsize bytesRead = 0;
883 gsize bytesWritten = 0;
884 GError *error = NULL;
885 gchar *localFilename = g_filename_from_utf8( path,
886 -1,
887 &bytesRead,
888 &bytesWritten,
889 &error);
890 GdkPixbuf *pb = gdk_pixbuf_new_from_file(localFilename, NULL);
891 g_free(localFilename);
892 g_free(path);
893 if (!pb) {
894 path = (gchar *) g_strdup_printf("%s/%s.xpm", INKSCAPE_PIXMAPDIR, name);
895 // TODO: bulia, please look over
896 gsize bytesRead = 0;
897 gsize bytesWritten = 0;
898 GError *error = NULL;
899 gchar *localFilename = g_filename_from_utf8( path,
900 -1,
901 &bytesRead,
902 &bytesWritten,
903 &error);
904 pb = gdk_pixbuf_new_from_file(localFilename, NULL);
905 g_free(localFilename);
906 g_free(path);
907 }
909 if (pb) {
910 if (!gdk_pixbuf_get_has_alpha(pb)) {
911 gdk_pixbuf_add_alpha(pb, FALSE, 0, 0, 0);
912 }
914 if ( ( static_cast<unsigned>(gdk_pixbuf_get_width(pb)) != psize )
915 || ( static_cast<unsigned>(gdk_pixbuf_get_height(pb)) != psize ) ) {
916 GdkPixbuf *spb = gdk_pixbuf_scale_simple(pb, psize, psize, GDK_INTERP_HYPER);
917 g_object_unref(G_OBJECT(pb));
918 pb = spb;
919 }
920 }
922 return pb;
923 }
925 // takes doc, root, icon, and icon name to produce pixels
926 extern "C" guchar *
927 sp_icon_doc_icon( SPDocument *doc, NRArenaItem *root,
928 gchar const *name, unsigned psize )
929 {
930 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
931 bool const dump = prefs->getBool("/debug/icons/dumpSvg");
932 guchar *px = NULL;
934 if (doc) {
935 SPObject *object = doc->getObjectById(name);
936 if (object && SP_IS_ITEM(object)) {
937 /* Find bbox in document */
938 Geom::Matrix const i2doc(sp_item_i2doc_affine(SP_ITEM(object)));
939 Geom::OptRect dbox = SP_ITEM(object)->getBounds(i2doc);
941 if ( SP_OBJECT_PARENT(object) == NULL )
942 {
943 dbox = Geom::Rect(Geom::Point(0, 0),
944 Geom::Point(sp_document_width(doc), sp_document_height(doc)));
945 }
947 /* This is in document coordinates, i.e. pixels */
948 if ( dbox ) {
949 NRGC gc(NULL);
950 /* Update to renderable state */
951 double sf = 1.0;
952 nr_arena_item_set_transform(root, (Geom::Matrix)Geom::Scale(sf, sf));
953 gc.transform.setIdentity();
954 nr_arena_item_invoke_update( root, NULL, &gc,
955 NR_ARENA_ITEM_STATE_ALL,
956 NR_ARENA_ITEM_STATE_NONE );
957 /* Item integer bbox in points */
958 NRRectL ibox;
959 ibox.x0 = (int) floor(sf * dbox->min()[Geom::X] + 0.5);
960 ibox.y0 = (int) floor(sf * dbox->min()[Geom::Y] + 0.5);
961 ibox.x1 = (int) floor(sf * dbox->max()[Geom::X] + 0.5);
962 ibox.y1 = (int) floor(sf * dbox->max()[Geom::Y] + 0.5);
964 if ( dump ) {
965 g_message( " box --'%s' (%f,%f)-(%f,%f)", name, (double)ibox.x0, (double)ibox.y0, (double)ibox.x1, (double)ibox.y1 );
966 }
968 /* Find button visible area */
969 int width = ibox.x1 - ibox.x0;
970 int height = ibox.y1 - ibox.y0;
972 if ( dump ) {
973 g_message( " vis --'%s' (%d,%d)", name, width, height );
974 }
976 {
977 int block = std::max(width, height);
978 if (block != static_cast<int>(psize) ) {
979 if ( dump ) {
980 g_message(" resizing" );
981 }
982 sf = (double)psize / (double)block;
984 nr_arena_item_set_transform(root, (Geom::Matrix)Geom::Scale(sf, sf));
985 gc.transform.setIdentity();
986 nr_arena_item_invoke_update( root, NULL, &gc,
987 NR_ARENA_ITEM_STATE_ALL,
988 NR_ARENA_ITEM_STATE_NONE );
989 /* Item integer bbox in points */
990 ibox.x0 = (int) floor(sf * dbox->min()[Geom::X] + 0.5);
991 ibox.y0 = (int) floor(sf * dbox->min()[Geom::Y] + 0.5);
992 ibox.x1 = (int) floor(sf * dbox->max()[Geom::X] + 0.5);
993 ibox.y1 = (int) floor(sf * dbox->max()[Geom::Y] + 0.5);
995 if ( dump ) {
996 g_message( " box2 --'%s' (%f,%f)-(%f,%f)", name, (double)ibox.x0, (double)ibox.y0, (double)ibox.x1, (double)ibox.y1 );
997 }
999 /* Find button visible area */
1000 width = ibox.x1 - ibox.x0;
1001 height = ibox.y1 - ibox.y0;
1002 if ( dump ) {
1003 g_message( " vis2 --'%s' (%d,%d)", name, width, height );
1004 }
1005 }
1006 }
1008 int dx, dy;
1009 //dx = (psize - width) / 2;
1010 //dy = (psize - height) / 2;
1011 dx=dy=psize;
1012 dx=(dx-width)/2; // watch out for psize, since 'unsigned'-'signed' can cause problems if the result is negative
1013 dy=(dy-height)/2;
1014 NRRectL area;
1015 area.x0 = ibox.x0 - dx;
1016 area.y0 = ibox.y0 - dy;
1017 area.x1 = area.x0 + psize;
1018 area.y1 = area.y0 + psize;
1019 /* Actual renderable area */
1020 NRRectL ua;
1021 ua.x0 = MAX(ibox.x0, area.x0);
1022 ua.y0 = MAX(ibox.y0, area.y0);
1023 ua.x1 = MIN(ibox.x1, area.x1);
1024 ua.y1 = MIN(ibox.y1, area.y1);
1026 if ( dump ) {
1027 g_message( " area --'%s' (%f,%f)-(%f,%f)", name, (double)area.x0, (double)area.y0, (double)area.x1, (double)area.y1 );
1028 g_message( " ua --'%s' (%f,%f)-(%f,%f)", name, (double)ua.x0, (double)ua.y0, (double)ua.x1, (double)ua.y1 );
1029 }
1030 /* Set up pixblock */
1031 px = g_new(guchar, 4 * psize * psize);
1032 memset(px, 0x00, 4 * psize * psize);
1033 /* Render */
1034 NRPixBlock B;
1035 nr_pixblock_setup_extern( &B, NR_PIXBLOCK_MODE_R8G8B8A8N,
1036 ua.x0, ua.y0, ua.x1, ua.y1,
1037 px + 4 * psize * (ua.y0 - area.y0) +
1038 4 * (ua.x0 - area.x0),
1039 4 * psize, FALSE, FALSE );
1040 nr_arena_item_invoke_render(NULL, root, &ua, &B,
1041 NR_ARENA_ITEM_RENDER_NO_CACHE );
1042 nr_pixblock_release(&B);
1044 bool useOverlay = prefs->getBool("/debug/icons/overlaySvg");
1045 if ( useOverlay ) {
1046 sp_icon_overlay_pixels( px, psize, psize, 4 * psize, 0x00, 0x00, 0xff );
1047 }
1048 }
1049 }
1050 }
1052 return px;
1053 } // end of sp_icon_doc_icon()
1057 struct svg_doc_cache_t
1058 {
1059 SPDocument *doc;
1060 NRArenaItem *root;
1061 };
1063 static std::map<Glib::ustring, svg_doc_cache_t *> doc_cache;
1064 static std::map<Glib::ustring, GdkPixbuf *> pb_cache;
1066 Glib::ustring icon_cache_key(gchar const *name,
1067 unsigned lsize, unsigned psize)
1068 {
1069 Glib::ustring key=name;
1070 key += ":";
1071 key += lsize;
1072 key += ":";
1073 key += psize;
1074 return key;
1075 }
1077 GdkPixbuf *get_cached_pixbuf(Glib::ustring const &key) {
1078 std::map<Glib::ustring, GdkPixbuf *>::iterator found = pb_cache.find(key);
1079 if ( found != pb_cache.end() ) {
1080 return found->second;
1081 }
1082 return NULL;
1083 }
1085 static std::list<gchar*> &icons_svg_paths()
1086 {
1087 static std::list<gchar *> sources;
1088 static bool initialized = false;
1089 if (!initialized) {
1090 // Fall back from user prefs dir into system locations.
1091 gchar *userdir = profile_path("icons");
1092 sources.push_back(g_build_filename(userdir,"icons.svg", NULL));
1093 sources.push_back(g_build_filename(INKSCAPE_PIXMAPDIR, "icons.svg", NULL));
1094 g_free(userdir);
1095 initialized = true;
1096 }
1097 return sources;
1098 }
1100 // this function renders icons from icons.svg and returns the pixels.
1101 static guchar *load_svg_pixels(gchar const *name,
1102 unsigned /*lsize*/, unsigned psize)
1103 {
1104 SPDocument *doc = NULL;
1105 NRArenaItem *root = NULL;
1106 svg_doc_cache_t *info = NULL;
1108 std::list<gchar *> &sources = icons_svg_paths();
1110 // Try each document in turn until we successfully load the icon from one
1111 guchar *px=NULL;
1112 for (std::list<gchar*>::iterator i = sources.begin(); i != sources.end() && !px; ++i) {
1113 gchar *doc_filename = *i;
1115 // Did we already load this doc?
1116 Glib::ustring key(doc_filename);
1117 info = 0;
1118 {
1119 std::map<Glib::ustring, svg_doc_cache_t *>::iterator i = doc_cache.find(key);
1120 if ( i != doc_cache.end() ) {
1121 info = i->second;
1122 }
1123 }
1125 /* Try to load from document. */
1126 if (!info &&
1127 Inkscape::IO::file_test( doc_filename, G_FILE_TEST_IS_REGULAR ) &&
1128 (doc = sp_document_new( doc_filename, FALSE )) ) {
1130 //g_message("Loaded icon file %s", doc_filename);
1131 // prep the document
1132 sp_document_ensure_up_to_date(doc);
1133 /* Create new arena */
1134 NRArena *arena = NRArena::create();
1135 /* Create ArenaItem and set transform */
1136 unsigned visionkey = sp_item_display_key_new(1);
1137 /* fixme: Memory manage root if needed (Lauris) */
1138 root = sp_item_invoke_show( SP_ITEM(SP_DOCUMENT_ROOT(doc)),
1139 arena, visionkey, SP_ITEM_SHOW_DISPLAY );
1141 // store into the cache
1142 info = new svg_doc_cache_t;
1143 g_assert(info);
1145 info->doc=doc;
1146 info->root=root;
1147 doc_cache[key]=info;
1148 }
1149 if (info) {
1150 doc=info->doc;
1151 root=info->root;
1152 }
1154 // move on to the next document if we couldn't get anything
1155 if (!info && !doc) {
1156 continue;
1157 }
1159 px = sp_icon_doc_icon( doc, root, name, psize );
1160 // if (px) {
1161 // g_message("Found icon %s in %s", name, doc_filename);
1162 // }
1163 }
1165 // if (!px) {
1166 // g_message("Not found icon %s", name);
1167 // }
1168 return px;
1169 }
1171 static GdkPixbuf *sp_icon_image_load_svg(gchar const *name, GtkIconSize lsize, unsigned psize)
1172 {
1173 Glib::ustring key = icon_cache_key(name, lsize, psize);
1175 // did we already load this icon at this scale/size?
1176 GdkPixbuf* pb = get_cached_pixbuf(key);
1177 if (!pb) {
1178 guchar *px = load_svg_pixels(name, lsize, psize);
1179 if (px) {
1180 pb = gdk_pixbuf_new_from_data(px, GDK_COLORSPACE_RGB, TRUE, 8,
1181 psize, psize, psize * 4,
1182 (GdkPixbufDestroyNotify)g_free, NULL);
1183 pb_cache[key] = pb;
1184 }
1185 }
1187 if ( pb ) {
1188 // increase refcount since we're handing out ownership
1189 g_object_ref(G_OBJECT(pb));
1190 }
1191 return pb;
1192 }
1194 void sp_icon_overlay_pixels(guchar *px, int width, int height, int stride,
1195 unsigned r, unsigned g, unsigned b)
1196 {
1197 int bytesPerPixel = 4;
1198 int spacing = 4;
1199 for ( int y = 0; y < height; y += spacing ) {
1200 guchar *ptr = px + y * stride;
1201 for ( int x = 0; x < width; x += spacing ) {
1202 *(ptr++) = r;
1203 *(ptr++) = g;
1204 *(ptr++) = b;
1205 *(ptr++) = 0xff;
1207 ptr += bytesPerPixel * (spacing - 1);
1208 }
1209 }
1211 if ( width > 1 && height > 1 ) {
1212 // point at the last pixel
1213 guchar *ptr = px + ((height-1) * stride) + ((width - 1) * bytesPerPixel);
1215 if ( width > 2 ) {
1216 px[4] = r;
1217 px[5] = g;
1218 px[6] = b;
1219 px[7] = 0xff;
1221 ptr[-12] = r;
1222 ptr[-11] = g;
1223 ptr[-10] = b;
1224 ptr[-9] = 0xff;
1225 }
1227 ptr[-4] = r;
1228 ptr[-3] = g;
1229 ptr[-2] = b;
1230 ptr[-1] = 0xff;
1232 px[0 + stride] = r;
1233 px[1 + stride] = g;
1234 px[2 + stride] = b;
1235 px[3 + stride] = 0xff;
1237 ptr[0 - stride] = r;
1238 ptr[1 - stride] = g;
1239 ptr[2 - stride] = b;
1240 ptr[3 - stride] = 0xff;
1242 if ( height > 2 ) {
1243 ptr[0 - stride * 3] = r;
1244 ptr[1 - stride * 3] = g;
1245 ptr[2 - stride * 3] = b;
1246 ptr[3 - stride * 3] = 0xff;
1247 }
1248 }
1249 }
1252 /*
1253 Local Variables:
1254 mode:c++
1255 c-file-style:"stroustrup"
1256 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1257 indent-tabs-mode:nil
1258 fill-column:99
1259 End:
1260 */
1261 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :