Code

moved and renamed object-ui.h and object-ui.cpp to src/ui/context-menu.cpp & context...
[inkscape.git] / src / interface.cpp
1 #define __SP_INTERFACE_C__
3 /**
4  * Main UI stuff
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Frank Felfe <innerspace@iname.com>
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Copyright (C) 1999-2005 authors
12  * Copyright (C) 2001-2002 Ximian, Inc.
13  * Copyright (C) 2004 David Turner
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include <gtk/gtk.h>
23 #include "inkscape-private.h"
24 #include "extension/effect.h"
25 #include "widgets/icon.h"
26 #include "prefs-utils.h"
27 #include "path-prefix.h"
29 #include "shortcuts.h"
31 #include "document.h"
32 #include "desktop-handles.h"
33 #include "file.h"
34 #include "interface.h"
35 #include "desktop.h"
36 #include "ui/context-menu.h"
37 #include "selection.h"
38 #include "selection-chemistry.h"
39 #include "svg-view-widget.h"
40 #include "widgets/desktop-widget.h"
41 #include "sp-item-group.h"
42 #include "sp-namedview.h"
44 #include "helper/action.h"
45 #include "helper/gnome-utils.h"
46 #include "helper/window.h"
48 #include "io/sys.h"
49 #include "io/stringstream.h"
50 #include "io/base64stream.h"
52 #include "dialogs/dialog-events.h"
54 #include "message-context.h"
56 // Added for color drag-n-drop
57 #if ENABLE_LCMS
58 #include "lcms.h"
59 #endif // ENABLE_LCMS
60 #include "display/sp-canvas.h"
61 #include "color.h"
62 #include "svg/svg-color.h"
63 #include "desktop-style.h"
64 #include "style.h"
67 using Inkscape::IO::StringOutputStream;
68 using Inkscape::IO::Base64OutputStream;
70 /* forward declaration */
71 static gint sp_ui_delete(GtkWidget *widget, GdkEvent *event, Inkscape::UI::View::View *view);
73 /* Drag and Drop */
74 typedef enum {
75     URI_LIST,
76     SVG_XML_DATA,
77     SVG_DATA,
78     PNG_DATA,
79     JPEG_DATA,
80     IMAGE_DATA,
81     APP_X_INKY_COLOR,
82     APP_X_COLOR
83 } ui_drop_target_info;
85 static GtkTargetEntry ui_drop_target_entries [] = {
86     {"text/uri-list", 0, URI_LIST},
87     {"image/svg+xml", 0, SVG_XML_DATA},
88     {"image/svg",     0, SVG_DATA},
89     {"image/png",     0, PNG_DATA},
90     {"image/jpeg",    0, JPEG_DATA},
91 #if ENABLE_LCMS
92     {"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
93 #endif // ENABLE_LCMS
94     {"application/x-color", 0, APP_X_COLOR}
95 };
97 static GtkTargetEntry *completeDropTargets = 0;
98 static int completeDropTargetsCount = 0;
100 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
101 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
102 static void sp_ui_import_files(gchar *buffer);
103 static void sp_ui_import_one_file(char const *filename);
104 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
105 static void sp_ui_drag_data_received(GtkWidget *widget,
106                                      GdkDragContext *drag_context,
107                                      gint x, gint y,
108                                      GtkSelectionData *data,
109                                      guint info,
110                                      guint event_time,
111                                      gpointer user_data);
112 static void sp_ui_menu_item_set_sensitive(SPAction *action,
113                                           unsigned int sensitive,
114                                           void *data);
116 SPActionEventVector menu_item_event_vector = {
117     {NULL},
118     NULL,
119     NULL, /* set_active */
120     sp_ui_menu_item_set_sensitive, /* set_sensitive */
121     NULL  /* set_shortcut */
122 };
124 void
125 sp_create_window(SPViewWidget *vw, gboolean editable)
127     GtkWidget *w, *hb;
129     g_return_if_fail(vw != NULL);
130     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
132     w = sp_window_new("", TRUE);
134     if (editable) {
135       g_object_set_data(G_OBJECT(vw), "window", w);
136       reinterpret_cast<SPDesktopWidget*>(vw)->window =
137         static_cast<GtkWindow*>((void*)w);
138     }
140     hb = gtk_hbox_new(FALSE, 0);
141     gtk_widget_show(hb);
142     gtk_container_add(GTK_CONTAINER(w), hb);
143     g_object_set_data(G_OBJECT(w), "hbox", hb);
145     /* fixme: */
146     if (editable) {
147         gtk_window_set_default_size((GtkWindow *) w, 640, 480);
148         g_object_set_data(G_OBJECT(w), "desktop", SP_DESKTOP_WIDGET(vw)->desktop);
149         g_object_set_data(G_OBJECT(w), "desktopwidget", vw);
150         g_signal_connect(G_OBJECT(w), "delete_event", G_CALLBACK(sp_ui_delete), vw->view);
151         g_signal_connect(G_OBJECT(w), "focus_in_event", G_CALLBACK(sp_desktop_widget_set_focus), vw);
152     } else {
153         gtk_window_set_policy(GTK_WINDOW(w), TRUE, TRUE, TRUE);
154     }
156     gtk_box_pack_end(GTK_BOX(hb), GTK_WIDGET(vw), TRUE, TRUE, 0);
157     gtk_widget_show(GTK_WIDGET(vw));
160     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
161     {
162         std::vector<gchar*> types;
164         GSList *list = gdk_pixbuf_get_formats();
165         while ( list ) {
166             int i = 0;
167             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
168             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
169             for ( i = 0; typesXX[i]; i++ ) {
170                 types.push_back(g_strdup(typesXX[i]));
171             }
172             g_strfreev(typesXX);
174             list = g_slist_next(list);
175         }
176         completeDropTargetsCount = nui_drop_target_entries + types.size();
177         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
178         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
179             completeDropTargets[i] = ui_drop_target_entries[i];
180         }
181         int pos = nui_drop_target_entries;
183         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
184             completeDropTargets[pos].target = *it;
185             completeDropTargets[pos].flags = 0;
186             completeDropTargets[pos].info = IMAGE_DATA;
187             pos++;
188         }
189     }
191     gtk_drag_dest_set(w,
192                       GTK_DEST_DEFAULT_ALL,
193                       completeDropTargets,
194                       completeDropTargetsCount,
195                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
196     g_signal_connect(G_OBJECT(w),
197                      "drag_data_received",
198                      G_CALLBACK(sp_ui_drag_data_received),
199                      NULL);
200     gtk_widget_show(w);
202     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
203     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
206 void
207 sp_ui_new_view()
209     SPDocument *document;
210     SPViewWidget *dtw;
212     document = SP_ACTIVE_DOCUMENT;
213     if (!document) return;
215     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
216     g_return_if_fail(dtw != NULL);
218     sp_create_window(dtw, TRUE);
219     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
222 /* TODO: not yet working */
223 /* To be re-enabled (by adding to menu) once it works. */
224 void
225 sp_ui_new_view_preview()
227     SPDocument *document;
228     SPViewWidget *dtw;
230     document = SP_ACTIVE_DOCUMENT;
231     if (!document) return;
233     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
234     g_return_if_fail(dtw != NULL);
235     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
237     sp_create_window(dtw, FALSE);
240 /**
241  * \param widget unused
242  */
243 void
244 sp_ui_close_view(GtkWidget *widget)
246     if (SP_ACTIVE_DESKTOP == NULL) {
247         return;
248     }
249     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
250         return;
251     }
252     SP_ACTIVE_DESKTOP->destroyWidget();
256 /**
257  *  sp_ui_close_all
258  *
259  *  This function is called to exit the program, and iterates through all
260  *  open document view windows, attempting to close each in turn.  If the
261  *  view has unsaved information, the user will be prompted to save,
262  *  discard, or cancel.
263  *
264  *  Returns FALSE if the user cancels the close_all operation, TRUE
265  *  otherwise.
266  */
267 unsigned int
268 sp_ui_close_all(void)
270     /* Iterate through all the windows, destroying each in the order they
271        become active */
272     while (SP_ACTIVE_DESKTOP) {
273         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
274             /* The user cancelled the operation, so end doing the close */
275             return FALSE;
276         }
277         SP_ACTIVE_DESKTOP->destroyWidget();
278     }
280     return TRUE;
283 static gint
284 sp_ui_delete(GtkWidget *widget, GdkEvent *event, Inkscape::UI::View::View *view)
286     return view->shutdown();
289 /*
290  * Some day when the right-click menus are ready to start working
291  * smarter with the verbs, we'll need to change this NULL being
292  * sent to sp_action_perform to something useful, or set some kind
293  * of global "right-clicked position" variable for actions to
294  * investigate when they're called.
295  */
296 static void
297 sp_ui_menu_activate(void *object, SPAction *action)
299     sp_action_perform(action, NULL);
302 static void
303 sp_ui_menu_select_action(void *object, SPAction *action)
305     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
308 static void
309 sp_ui_menu_deselect_action(void *object, SPAction *action)
311     action->view->tipsMessageContext()->clear();
314 static void
315 sp_ui_menu_select(gpointer object, gpointer tip)
317     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
318     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
321 static void
322 sp_ui_menu_deselect(gpointer object)
324     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
325     view->tipsMessageContext()->clear();
328 /**
329  * sp_ui_menuitem_add_icon
330  *
331  * Creates and attaches a scaled icon to the given menu item.
332  *
333  */
334 void
335 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
337     GtkWidget *icon;
339     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
340     gtk_widget_show(icon);
341     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
342 } // end of sp_ui_menu_add_icon
344 /**
345  * sp_ui_menu_append_item
346  *
347  * Appends a UI item with specific info for Inkscape/Sodipodi.
348  *
349  */
350 static GtkWidget *
351 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
352                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
353                         gpointer data, gboolean with_mnemonic = TRUE )
355     GtkWidget *item;
357     if (stock) {
358         item = gtk_image_menu_item_new_from_stock(stock, NULL);
359     } else if (label) {
360         item = (with_mnemonic)
361             ? gtk_image_menu_item_new_with_mnemonic(label) :
362             gtk_image_menu_item_new_with_label(label);
363     } else {
364         item = gtk_separator_menu_item_new();
365     }
367     gtk_widget_show(item);
369     if (callback) {
370         g_signal_connect(G_OBJECT(item), "activate", callback, data);
371     }
373     if (tip && view) {
374         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
375         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
376         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
377     }
379     gtk_menu_append(GTK_MENU(menu), item);
381     return item;
383 } // end of sp_ui_menu_append_item()
385 /**
386 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
387  */
388 static gchar const *
389 sp_key_name(guint keyval)
391     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
392        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
393     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
395     if      (!strcmp(n, "asciicircum"))  return "^";
396     else if (!strcmp(n, "parenleft"  ))  return "(";
397     else if (!strcmp(n, "parenright" ))  return ")";
398     else if (!strcmp(n, "plus"       ))  return "+";
399     else if (!strcmp(n, "minus"      ))  return "-";
400     else if (!strcmp(n, "asterisk"   ))  return "*";
401     else if (!strcmp(n, "KP_Multiply"))  return "*";
402     else if (!strcmp(n, "Delete"     ))  return "Del";
403     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
404     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
405     else if (!strcmp(n, "grave"      ))  return "`";
406     else if (!strcmp(n, "numbersign" ))  return "#";
407     else if (!strcmp(n, "bar" ))  return "|";
408     else if (!strcmp(n, "slash" ))  return "/";
409     else if (!strcmp(n, "exclam" ))  return "!";
410     else return n;
414 /**
415  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
416  * \param c Points to a buffer at least 256 bytes long.
417  */
418 void
419 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
421     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
422      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
423      * Will probably need to change sp_shortcut_invoke callers.
424      *
425      * The existing gtk_label_new_with_mnemonic call can be replaced with
426      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
427      * gtk_label_set_text_with_mnemonic(lbl, str).
428      */
429     static GtkAccelLabelClass const &accel_lbl_cls
430         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
432     struct { unsigned test; char const *name; } const modifier_tbl[] = {
433         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
434         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
435         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
436     };
438     gchar *p = c;
439     gchar *end = p + 256;
441     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
442         if ((shortcut & modifier_tbl[i].test)
443             && (p < end))
444         {
445             p += g_snprintf(p, end - p, "%s%s",
446                             modifier_tbl[i].name,
447                             accel_lbl_cls.mod_separator);
448         }
449     }
450     if (p < end) {
451         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
452     }
453     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
456 void
457 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
459     SPAction     *action;
460     unsigned int shortcut;
461     gchar        *s;
462     gchar        key[256];
463     gchar        *atitle;
465     action = verb->get_action(NULL);
466     if (!action)
467         return;
469     atitle = sp_action_get_title(action);
471     s = g_stpcpy(c, atitle);
473     g_free(atitle);
475     shortcut = sp_shortcut_get_primary(verb);
476     if (shortcut) {
477         s = g_stpcpy(s, " (");
478         sp_ui_shortcut_string(shortcut, key);
479         s = g_stpcpy(s, key);
480         s = g_stpcpy(s, ")");
481     }
485 /**
486  * sp_ui_menu_append_item_from_verb
487  *
488  * Appends a custom menu UI from a verb.
489  *
490  */
492 static GtkWidget *
493 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
495     SPAction *action;
496     GtkWidget *item;
498     if (verb->get_code() == SP_VERB_NONE) {
500         item = gtk_separator_menu_item_new();
502     } else {
503         unsigned int shortcut;
505         action = verb->get_action(view);
507         if (!action) return NULL;
509         shortcut = sp_shortcut_get_primary(verb);
510         if (shortcut) {
511             gchar c[256];
512             sp_ui_shortcut_string(shortcut, c);
513             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
514             GtkWidget *const name_lbl = gtk_label_new("");
515             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
516             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
517             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
518             GtkWidget *const accel_lbl = gtk_label_new(c);
519             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
520             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
521             gtk_widget_show_all(hb);
522             if (radio) {
523                 item = gtk_radio_menu_item_new (group);
524             } else {
525                 item = gtk_image_menu_item_new();
526             }
527             gtk_container_add((GtkContainer *) item, hb);
528         } else {
529             if (radio) {
530                 item = gtk_radio_menu_item_new (group);
531             } else {
532                 item = gtk_image_menu_item_new ();
533             }
534             GtkWidget *const name_lbl = gtk_label_new("");
535             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
536             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
537             gtk_container_add((GtkContainer *) item, name_lbl);
538         }
540         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
541         if (!action->sensitive) {
542             gtk_widget_set_sensitive(item, FALSE);
543         }
545         if (action->image) {
546             sp_ui_menuitem_add_icon(item, action->image);
547         }
548         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
549         g_signal_connect( G_OBJECT(item), "activate",
550                           G_CALLBACK(sp_ui_menu_activate), action );
552         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
553         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
554     }
556     gtk_widget_show(item);
557     gtk_menu_append(GTK_MENU(menu), item);
559     return item;
561 } // end of sp_ui_menu_append_item_from_verb
564 static void
565 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
567     gchar const *pref = (gchar const *) user_data;
568     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
570     gchar const *pref_path;
571     if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen)
572         pref_path = g_strconcat("fullscreen.", pref, NULL);
573     else
574         pref_path = g_strconcat("window.", pref, NULL);
576     gboolean checked = gtk_check_menu_item_get_active(menuitem);
577     prefs_set_int_attribute(pref_path, "state", checked);
579     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
582 static gboolean
583 checkitem_update(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
585     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
587     gchar const *pref = (gchar const *) user_data;
588     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
590     gchar const *pref_path;
591     if (static_cast<SPDesktop*>(view)->is_fullscreen)
592         pref_path = g_strconcat("fullscreen.", pref, NULL);
593     else
594         pref_path = g_strconcat("window.", pref, NULL);
596     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
598     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
599     gtk_check_menu_item_set_active(menuitem, ison);
600     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
602     return FALSE;
606 void
607 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
608                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
609                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
610                                        Inkscape::Verb *verb)
612     GtkWidget *item;
614     unsigned int shortcut = 0;
615     SPAction *action = NULL;
617     if (verb) {
618         shortcut = sp_shortcut_get_primary(verb);
619         action = verb->get_action(view);
620     }
622     if (verb && shortcut) {
623         gchar c[256];
624         sp_ui_shortcut_string(shortcut, c);
626         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
628         {
629             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
630             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
631             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
632         }
634         {
635             GtkWidget *l = gtk_label_new(c);
636             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
637             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
638         }
640         gtk_widget_show_all(hb);
642         item = gtk_check_menu_item_new();
643         gtk_container_add((GtkContainer *) item, hb);
644     } else {
645         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
646         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
647         item = gtk_check_menu_item_new();
648         gtk_container_add((GtkContainer *) item, l);
649     }
650 #if 0
651     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
652     if (!action->sensitive) {
653         gtk_widget_set_sensitive(item, FALSE);
654     }
655 #endif
656     gtk_widget_show(item);
658     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
660     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
662     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
663     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
665     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
666     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
669 static void
670 sp_recent_open(GtkWidget *widget, gchar const *uri)
672     sp_file_open(uri, NULL);
675 static void
676 sp_file_new_from_template(GtkWidget *widget, gchar const *uri)
678     sp_file_new(uri);
681 void
682 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
684     std::list<gchar *> sources;
685     sources.push_back( profile_path("templates") ); // first try user's local dir
686     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
688     // Use this loop to iterate through a list of possible document locations.
689     while (!sources.empty()) {
690         gchar *dirname = sources.front();
692         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
693             GError *err = 0;
694             GDir *dir = g_dir_open(dirname, 0, &err);
696             if (dir) {
697                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
698                     if (!g_str_has_suffix(file, ".svg"))
699                         continue; // skip non-svg files
701                     gchar *basename = g_path_get_basename(file);
702                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
703                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
705                     gchar const *filepath = g_build_filename(dirname, file, NULL);
706                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
707                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
708                     g_free(dupfile);
709                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
710                     g_free(filename);
712                     gtk_widget_show(item);
713                     // how does "filepath" ever get freed?
714                     g_signal_connect(G_OBJECT(item),
715                                      "activate",
716                                      G_CALLBACK(sp_file_new_from_template),
717                                      (gpointer) filepath);
719                     if (view) {
720                         // set null tip for now; later use a description from the template file
721                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
722                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
723                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
724                     }
726                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
727                 }
728                 g_dir_close(dir);
729             }
730         }
732         // toss the dirname
733         g_free(dirname);
734         sources.pop_front();
735     }
738 void
739 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
741     gchar const **recent = prefs_get_recent_files();
742     if (recent) {
743         int i;
745         for (i = 0; recent[i] != NULL; i += 2) {
746             gchar const *uri = recent[i];
747             gchar const *name = recent[i + 1];
749             GtkWidget *item = gtk_menu_item_new_with_label(name);
750             gtk_widget_show(item);
751             g_signal_connect(G_OBJECT(item),
752                              "activate",
753                              G_CALLBACK(sp_recent_open),
754                              (gpointer)uri);
755             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
756         }
758         g_free(recent);
759     } else {
760         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
761         gtk_widget_show(item);
762         gtk_widget_set_sensitive(item, FALSE);
763         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
764     }
767 void
768 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
770     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
771     //                                       checkitem_toggled, checkitem_update, 0);
772     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
773                                            checkitem_toggled, checkitem_update, 0);
774     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
775                                            checkitem_toggled, checkitem_update, 0);
776     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
777                                            checkitem_toggled, checkitem_update, 0);
778     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
779                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
780     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
781                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
782     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
783                                            checkitem_toggled, checkitem_update, 0);
784     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
785                                            checkitem_toggled, checkitem_update, 0);
788 /** \brief  This function turns XML into a menu
789     \param  menus  This is the XML that defines the menu
790     \param  menu   Menu to be added to
791     \param  view   The View that this menu is being built for
793     This function is realitively simple as it just goes through the XML
794     and parses the individual elements.  In the case of a submenu, it
795     just calls itself recursively.  Because it is only reasonable to have
796     a couple of submenus, it is unlikely this will go more than two or
797     three times.
799     In the case of an unreconginzed verb, a menu item is made to identify
800     the verb that is missing, and display that.  The menu item is also made
801     insensitive.
802 */
803 void
804 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
806     if (menus == NULL) return;
807     if (menu == NULL)  return;
808     GSList *group = NULL;
810     for (Inkscape::XML::Node *menu_pntr = menus;
811          menu_pntr != NULL;
812          menu_pntr = menu_pntr->next()) {
813         if (!strcmp(menu_pntr->name(), "submenu")) {
814             if (!strcmp(menu_pntr->attribute("name"), "Effects") && !prefs_get_int_attribute("extensions", "show-effects-menu", 0)) {
815                 continue;
816             }
817             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
818             GtkWidget *submenu = gtk_menu_new();
819             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
820             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
821             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
822             continue;
823         }
824         if (!strcmp(menu_pntr->name(), "verb")) {
825             gchar const *verb_name = menu_pntr->attribute("verb-id");
826             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
828             if (verb != NULL) {
829                 if (menu_pntr->attribute("radio") != NULL) {
830                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
831                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
832                     if (menu_pntr->attribute("default") != NULL) {
833                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
834                     }
835                 } else {
836                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
837                     group = NULL;
838                 }
839             } else {
840                 gchar string[120];
841                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
842                 string[119] = '\0'; /* may not be terminated */
843                 GtkWidget *item = gtk_menu_item_new_with_label(string);
844                 gtk_widget_set_sensitive(item, false);
845                 gtk_widget_show(item);
846                 gtk_menu_append(GTK_MENU(menu), item);
847             }
848             continue;
849         }
850         if (!strcmp(menu_pntr->name(), "separator")
851                 // This was spelt wrong in the original version
852                 // and so this is for backward compatibility.  It can
853                 // probably be dropped after the 0.44 release.
854              || !strcmp(menu_pntr->name(), "seperator")) {
855             GtkWidget *item = gtk_separator_menu_item_new();
856             gtk_widget_show(item);
857             gtk_menu_append(GTK_MENU(menu), item);
858             continue;
859         }
860         if (!strcmp(menu_pntr->name(), "template-list")) {
861             sp_menu_append_new_templates(menu, view);
862             continue;
863         }
864         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
865             sp_menu_append_recent_documents(menu, view);
866             continue;
867         }
868         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
869             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
870             continue;
871         }
872     }
875 /** \brief  Build the main tool bar
876     \param  view  View to build the bar for
878     Currently the main tool bar is built as a dynamic XML menu using
879     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
880     pass it to get items attached to it.
881 */
882 GtkWidget *
883 sp_ui_main_menubar(Inkscape::UI::View::View *view)
885     GtkWidget *mbar = gtk_menu_bar_new();
887     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
889     return mbar;
892 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
893     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
896 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
897     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
898     sp_desktop_selection(desktop)->clear();
901 GtkWidget *
902 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
904     GtkWidget *m;
905     SPDesktop *dt;
907     dt = static_cast<SPDesktop*>(view);
909     m = gtk_menu_new();
911     /* Undo and Redo */
912     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
913     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
915     /* Separator */
916     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
918     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
919     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
920     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
922     /* Separator */
923     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
925     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
926     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
928     /* Item menu */
929     if (item) {
930         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
931         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
932     }
934     /* layer menu */
935     SPGroup *group=NULL;
936     if (item) {
937         if (SP_IS_GROUP(item)) {
938             group = SP_GROUP(item);
939         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
940             group = SP_GROUP(SP_OBJECT_PARENT(item));
941         }
942     }
944     if (( group && group != dt->currentLayer() ) ||
945         ( dt->currentLayer() != dt->currentRoot() ) ) {
946         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
947     }
949     if ( group && group != dt->currentLayer() ) {
950         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
951         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
952         GtkWidget *w = gtk_menu_item_new_with_label(label);
953         g_free(label);
954         g_object_set_data(G_OBJECT(w), "group", group);
955         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
956         gtk_widget_show(w);
957         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
958     }
960     if ( dt->currentLayer() != dt->currentRoot() ) {
961         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
962             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
963             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
964             gtk_widget_show(w);
965             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
967         }
968     }
970     return m;
973 /* Drag and Drop */
974 void
975 sp_ui_drag_data_received(GtkWidget *widget,
976                          GdkDragContext *drag_context,
977                          gint x, gint y,
978                          GtkSelectionData *data,
979                          guint info,
980                          guint event_time,
981                          gpointer user_data)
983     switch (info) {
984 #if ENABLE_LCMS
985         case APP_X_INKY_COLOR:
986         {
987             SPDesktop *desktop = SP_ACTIVE_DESKTOP;
988             int destX = 0;
989             int destY = 0;
990             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
991             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
993             SPItem *item = desktop->item_at_point( where, true );
994             if ( item )
995             {
996                 if ( data->length >= 8 ) {
997                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
999                     gchar c[64] = {0};
1000                     // Careful about endian issues.
1001                     guint16* dataVals = (guint16*)data->data;
1002                     sp_svg_write_color( c, 64,
1003                                         SP_RGBA32_U_COMPOSE(
1004                                             0x0ff & (dataVals[0] >> 8),
1005                                             0x0ff & (dataVals[1] >> 8),
1006                                             0x0ff & (dataVals[2] >> 8),
1007                                             0xff // can't have transparency in the color itself
1008                                             //0x0ff & (data->data[3] >> 8),
1009                                             ));
1010                     SPCSSAttr *css = sp_repr_css_attr_new();
1011                     bool updatePerformed = false;
1013                     if ( data->length > 14 ) {
1014                         int flags = dataVals[4];
1016                         // piggie-backed palette entry info
1017                         int index = dataVals[5];
1018                         Glib::ustring palName;
1019                         for ( int i = 0; i < dataVals[6]; i++ ) {
1020                             palName += (gunichar)dataVals[7+i];
1021                         }
1023                         // Now hook in a magic tag of some sort.
1024                         if ( !palName.empty() && (flags & 1) ) {
1025                             gchar* str = g_strdup_printf("%d|", index);
1026                             palName.insert( 0, str );
1027                             g_free(str);
1028                             str = 0;
1030                             sp_object_setAttribute( SP_OBJECT(item),
1031                                                     (drag_context->action != GDK_ACTION_MOVE) ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1032                                                     palName.c_str(),
1033                                                     false );
1034                             item->updateRepr();
1036                             sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c );
1037                             updatePerformed = true;
1038                         }
1039                     }
1041                     if ( !updatePerformed ) {
1042                         sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c );
1043                     }
1045                     sp_desktop_apply_css_recursive( item, css, true );
1046                     item->updateRepr();
1048                     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1049                     sp_document_done( doc );
1051                     if ( srgbProf ) {
1052                         cmsCloseProfile( srgbProf );
1053                     }
1054                 }
1055             }
1056         }
1057         break;
1058 #endif // ENABLE_LCMS
1060         case APP_X_COLOR:
1061         {
1062             SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1063             int destX = 0;
1064             int destY = 0;
1065             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1066             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1068             SPItem *item = desktop->item_at_point( where, true );
1069             if ( item )
1070             {
1071                 if ( data->length == 8 ) {
1072                     gchar c[64] = {0};
1073                     // Careful about endian issues.
1074                     guint16* dataVals = (guint16*)data->data;
1075                     sp_svg_write_color( c, 64,
1076                                         SP_RGBA32_U_COMPOSE(
1077                                             0x0ff & (dataVals[0] >> 8),
1078                                             0x0ff & (dataVals[1] >> 8),
1079                                             0x0ff & (dataVals[2] >> 8),
1080                                             0xff // can't have transparency in the color itself
1081                                             //0x0ff & (data->data[3] >> 8),
1082                                             ));
1083                     SPCSSAttr *css = sp_repr_css_attr_new();
1084                     sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c );
1086                     sp_desktop_apply_css_recursive( item, css, true );
1087                     item->updateRepr();
1089                     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1090                     sp_document_done( doc );
1091                 }
1092             }
1093         }
1094         break;
1096         case SVG_DATA:
1097         case SVG_XML_DATA: {
1098             gchar *svgdata = (gchar *)data->data;
1100             SPDocument *doc = SP_ACTIVE_DOCUMENT;
1102             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1104             if (rnewdoc == NULL) {
1105                 sp_ui_error_dialog(_("Could not parse SVG data"));
1106                 return;
1107             }
1109             Inkscape::XML::Node *repr = sp_repr_document_root(rnewdoc);
1110             gchar const *style = repr->attribute("style");
1112             Inkscape::XML::Node *newgroup = sp_repr_new("svg:g");
1113             newgroup->setAttribute("style", style);
1115             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1116                 Inkscape::XML::Node *newchild = child->duplicate();
1117                 newgroup->appendChild(newchild);
1118             }
1120             Inkscape::GC::release(rnewdoc);
1122             SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1123             // Add it to the current layer
1125             // Greg's edits to add intelligent positioning of svg drops
1126             SPObject *new_obj = NULL;
1127             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1129             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1130             selection->set(SP_ITEM(new_obj));
1131             // To move the imported object, we must temporarily set the "transform pattern with
1132             // object" option.
1133             {
1134                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1135                 prefs_set_int_attribute("options.transform", "pattern", 1);
1136                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1137                 NR::Point m( desktop->point() - selection->bounds().midpoint() );
1138                 sp_selection_move_relative(selection, m);
1139                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1140             }
1142             Inkscape::GC::release(newgroup);
1143             sp_document_done(doc);
1144             break;
1145         }
1147         case URI_LIST: {
1148             gchar *uri = (gchar *)data->data;
1149             sp_ui_import_files(uri);
1150             break;
1151         }
1153         case PNG_DATA:
1154         case JPEG_DATA:
1155         case IMAGE_DATA: {
1156             char tmp[1024];
1158             StringOutputStream outs;
1159             Base64OutputStream b64out(outs);
1160             b64out.setColumnWidth(0);
1162             SPDocument *doc = SP_ACTIVE_DOCUMENT;
1164             Inkscape::XML::Node *newImage = sp_repr_new("svg:image");
1166             for ( int i = 0; i < data->length; i++ ) {
1167                 b64out.put( data->data[i] );
1168             }
1169             b64out.close();
1172             Glib::ustring str = outs.getString();
1174             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1175             str.insert( 0, tmp );
1176             newImage->setAttribute("xlink:href", str.c_str());
1178             GError *error = NULL;
1179             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1180             if ( loader ) {
1181                 error = NULL;
1182                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1183                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1184                     if ( pbuf ) {
1185                         int width = gdk_pixbuf_get_width(pbuf);
1186                         int height = gdk_pixbuf_get_height(pbuf);
1187                         snprintf( tmp, sizeof(tmp), "%d", width );
1188                         newImage->setAttribute("width", tmp);
1190                         snprintf( tmp, sizeof(tmp), "%d", height );
1191                         newImage->setAttribute("height", tmp);
1192                     }
1193                 }
1194             }
1196             SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1198             // Add it to the current layer
1199             desktop->currentLayer()->appendChildRepr(newImage);
1201             Inkscape::GC::release(newImage);
1202             sp_document_done( doc );
1203             break;
1204         }
1205     }
1208 static void
1209 sp_ui_import_files(gchar *buffer)
1211     GList *list = gnome_uri_list_extract_filenames(buffer);
1212     if (!list)
1213         return;
1214     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1215     g_list_foreach(list, (GFunc) g_free, NULL);
1216     g_list_free(list);
1219 static void
1220 sp_ui_import_one_file_with_check(gpointer filename, gpointer unused)
1222     if (filename) {
1223         if (strlen((char const *)filename) > 2)
1224             sp_ui_import_one_file((char const *)filename);
1225     }
1228 static void
1229 sp_ui_import_one_file(char const *filename)
1231     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1232     if (!doc) return;
1234     if (filename == NULL) return;
1236     // Pass off to common implementation
1237     // TODO might need to get the proper type of Inkscape::Extension::Extension
1238     file_import( doc, filename, NULL );
1241 void
1242 sp_ui_error_dialog(gchar const *message)
1244     GtkWidget *dlg;
1245     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1247     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1248                                  GTK_BUTTONS_CLOSE, safeMsg);
1249     sp_transientize(dlg);
1250     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1251     gtk_dialog_run(GTK_DIALOG(dlg));
1252     gtk_widget_destroy(dlg);
1253     g_free(safeMsg);
1256 bool
1257 sp_ui_overwrite_file(gchar const *filename)
1259     bool return_value = FALSE;
1260     GtkWidget *dialog;
1261     GtkWidget *hbox;
1262     GtkWidget *boxdata;
1263     gchar *title;
1264     gchar *text;
1266     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1268         title = g_strdup_printf(_("Overwrite %s"), filename);
1269         dialog = gtk_dialog_new_with_buttons(title,
1270                                              NULL,
1271                                              (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1272                                              GTK_STOCK_NO,
1273                                              GTK_RESPONSE_NO,
1274                                              GTK_STOCK_YES,
1275                                              GTK_RESPONSE_YES,
1276                                              NULL);
1277         gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_YES);
1279         sp_transientize(dialog);
1280         gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE);
1282         hbox = gtk_hbox_new(FALSE, 5);
1284         // TODO - replace with Inkscape-specific call
1285         boxdata = gtk_image_new_from_stock(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
1287         gtk_widget_show(boxdata);
1288         gtk_box_pack_start(GTK_BOX(hbox), boxdata, TRUE, TRUE, 5);
1289         text = g_strdup_printf(_("The file %s already exists.  Do you want to overwrite that file with the current document?"), filename);
1290         boxdata = gtk_label_new(text);
1291         gtk_label_set_line_wrap(GTK_LABEL(boxdata), TRUE);
1292         gtk_widget_show(boxdata);
1293         gtk_box_pack_start(GTK_BOX(hbox), boxdata, FALSE, FALSE, 5);
1294         gtk_widget_show(hbox);
1295         gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), hbox, TRUE, TRUE, 5);
1297         if (gtk_dialog_run(GTK_DIALOG(dialog)) == GTK_RESPONSE_YES) {
1298             return_value = TRUE;
1299         } else {
1300             return_value = FALSE;
1301         }
1303         gtk_widget_destroy(dialog);
1304         g_free(title);
1305         g_free(text);
1306     } else {
1307         return_value = TRUE;
1308     }
1310     return return_value;
1313 static void
1314 sp_ui_menu_item_set_sensitive(SPAction *action, unsigned int sensitive, void *data)
1316     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1319 /*
1320   Local Variables:
1321   mode:c++
1322   c-file-style:"stroustrup"
1323   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1324   indent-tabs-mode:nil
1325   fill-column:99
1326   End:
1327 */
1328 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :