Code

Modification of the parameters/return value of SPDesktop::getToplevel
[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"
43 #include "ui/view/view.h"
45 #include "helper/action.h"
46 #include "helper/gnome-utils.h"
47 #include "helper/window.h"
49 #include "io/sys.h"
50 #include "io/stringstream.h"
51 #include "io/base64stream.h"
53 #include "dialogs/dialog-events.h"
55 #include "message-context.h"
57 // Added for color drag-n-drop
58 #if ENABLE_LCMS
59 #include "lcms.h"
60 #endif // ENABLE_LCMS
61 #include "display/sp-canvas.h"
62 #include "color.h"
63 #include "svg/svg-color.h"
64 #include "desktop-style.h"
65 #include "style.h"
67 using Inkscape::IO::StringOutputStream;
68 using Inkscape::IO::Base64OutputStream;
70 /* Drag and Drop */
71 typedef enum {
72     URI_LIST,
73     SVG_XML_DATA,
74     SVG_DATA,
75     PNG_DATA,
76     JPEG_DATA,
77     IMAGE_DATA,
78     APP_X_INKY_COLOR,
79     APP_X_COLOR
80 } ui_drop_target_info;
82 static GtkTargetEntry ui_drop_target_entries [] = {
83     {"text/uri-list", 0, URI_LIST},
84     {"image/svg+xml", 0, SVG_XML_DATA},
85     {"image/svg",     0, SVG_DATA},
86     {"image/png",     0, PNG_DATA},
87     {"image/jpeg",    0, JPEG_DATA},
88 #if ENABLE_MAGIC_COLORS
89     {"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
90 #endif // ENABLE_MAGIC_COLORS
91     {"application/x-color", 0, APP_X_COLOR}
92 };
94 static GtkTargetEntry *completeDropTargets = 0;
95 static int completeDropTargetsCount = 0;
97 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
98 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
99 static void sp_ui_import_files(gchar *buffer);
100 static void sp_ui_import_one_file(char const *filename);
101 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
102 static void sp_ui_drag_data_received(GtkWidget *widget,
103                                      GdkDragContext *drag_context,
104                                      gint x, gint y,
105                                      GtkSelectionData *data,
106                                      guint info,
107                                      guint event_time,
108                                      gpointer user_data);
109 static void sp_ui_menu_item_set_sensitive(SPAction *action,
110                                           unsigned int sensitive,
111                                           void *data);
112 static void sp_ui_menu_item_set_name(SPAction *action, 
113                                      Glib::ustring name,
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     sp_ui_menu_item_set_name /* set_name */
123 };
125 static const int MIN_ONSCREEN_DISTANCE = 50;
127 void
128 sp_create_window(SPViewWidget *vw, gboolean editable)
130     g_return_if_fail(vw != NULL);
131     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
133     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
135     if (editable) {
136                 g_object_set_data(G_OBJECT(vw), "window", win);
137                 
138                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
139                 SPDesktop* desktop = desktop_widget->desktop;
140                 
141                 desktop_widget->window = win;
143         /* fixme: doesn't allow making window any smaller than this */
144         win->set_default_size(640, 480);
145                 
146         win->set_data("desktop", desktop);
147         win->set_data("desktopwidget", desktop_widget);
148                 
149         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
150                 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
151                 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
152                 
153         gint prefs_geometry = 
154             (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
155         if (prefs_geometry) {
156             gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
157             gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
158             gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
159             gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
160             gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
161             gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
162             if (pw>0 && ph>0) {
163                 gint w = MIN(gdk_screen_width(), pw);
164                 gint h = MIN(gdk_screen_height(), ph);
165                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
166                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
167                 if (w>0 && h>0 && x>0 && y>0) {
168                     x = MIN(gdk_screen_width() - w, x);
169                     y = MIN(gdk_screen_height() - h, y);
170                 }
171                 if (w>0 && h>0) {
172                     desktop->setWindowSize(w, h);
173                 }
175                 // Only restore xy for the first window so subsequent windows don't overlap exactly
176                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
177                 // other desktops?)
179                 // Empirically it seems that active_desktop==this desktop only the first time a
180                 // desktop is created.
181                 if (x>0 && y>0) {
182                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
183                     if (active_desktop == desktop || active_desktop==NULL) {
184                         desktop->setWindowPosition(NR::Point(x, y));
185                     }
186                 }
187             }
188             if (maxed) {
189                 win->maximize();
190             }
191             if (full) {
192                 win->fullscreen();
193             }
194         }
196     } else {
197         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
198     }
200     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
201     gtk_widget_show(GTK_WIDGET(vw));
203     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
204     {
205         std::vector<gchar*> types;
207         GSList *list = gdk_pixbuf_get_formats();
208         while ( list ) {
209             int i = 0;
210             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
211             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
212             for ( i = 0; typesXX[i]; i++ ) {
213                 types.push_back(g_strdup(typesXX[i]));
214             }
215             g_strfreev(typesXX);
217             list = g_slist_next(list);
218         }
219         completeDropTargetsCount = nui_drop_target_entries + types.size();
220         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
221         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
222             completeDropTargets[i] = ui_drop_target_entries[i];
223         }
224         int pos = nui_drop_target_entries;
226         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
227             completeDropTargets[pos].target = *it;
228             completeDropTargets[pos].flags = 0;
229             completeDropTargets[pos].info = IMAGE_DATA;
230             pos++;
231         }
232     }
234     gtk_drag_dest_set((GtkWidget*)win->gobj(),
235                       GTK_DEST_DEFAULT_ALL,
236                       completeDropTargets,
237                       completeDropTargetsCount,
238                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
239     g_signal_connect(G_OBJECT(win->gobj()),
240                      "drag_data_received",
241                      G_CALLBACK(sp_ui_drag_data_received),
242                      NULL);
243     win->show();
245     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
246     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
249 void
250 sp_ui_new_view()
252     SPDocument *document;
253     SPViewWidget *dtw;
255     document = SP_ACTIVE_DOCUMENT;
256     if (!document) return;
258     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
259     g_return_if_fail(dtw != NULL);
261     sp_create_window(dtw, TRUE);
262     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
263     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
266 /* TODO: not yet working */
267 /* To be re-enabled (by adding to menu) once it works. */
268 void
269 sp_ui_new_view_preview()
271     SPDocument *document;
272     SPViewWidget *dtw;
274     document = SP_ACTIVE_DOCUMENT;
275     if (!document) return;
277     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
278     g_return_if_fail(dtw != NULL);
279     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
281     sp_create_window(dtw, FALSE);
284 /**
285  * \param widget unused
286  */
287 void
288 sp_ui_close_view(GtkWidget *widget)
290     if (SP_ACTIVE_DESKTOP == NULL) {
291         return;
292     }
293     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
294         return;
295     }
296     SP_ACTIVE_DESKTOP->destroyWidget();
300 /**
301  *  sp_ui_close_all
302  *
303  *  This function is called to exit the program, and iterates through all
304  *  open document view windows, attempting to close each in turn.  If the
305  *  view has unsaved information, the user will be prompted to save,
306  *  discard, or cancel.
307  *
308  *  Returns FALSE if the user cancels the close_all operation, TRUE
309  *  otherwise.
310  */
311 unsigned int
312 sp_ui_close_all(void)
314     /* Iterate through all the windows, destroying each in the order they
315        become active */
316     while (SP_ACTIVE_DESKTOP) {
317         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
318             /* The user cancelled the operation, so end doing the close */
319             return FALSE;
320         }
321         SP_ACTIVE_DESKTOP->destroyWidget();
322     }
324     return TRUE;
327 /*
328  * Some day when the right-click menus are ready to start working
329  * smarter with the verbs, we'll need to change this NULL being
330  * sent to sp_action_perform to something useful, or set some kind
331  * of global "right-clicked position" variable for actions to
332  * investigate when they're called.
333  */
334 static void
335 sp_ui_menu_activate(void *object, SPAction *action)
337     sp_action_perform(action, NULL);
340 static void
341 sp_ui_menu_select_action(void *object, SPAction *action)
343     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
346 static void
347 sp_ui_menu_deselect_action(void *object, SPAction *action)
349     action->view->tipsMessageContext()->clear();
352 static void
353 sp_ui_menu_select(gpointer object, gpointer tip)
355     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
356     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
359 static void
360 sp_ui_menu_deselect(gpointer object)
362     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
363     view->tipsMessageContext()->clear();
366 /**
367  * sp_ui_menuitem_add_icon
368  *
369  * Creates and attaches a scaled icon to the given menu item.
370  *
371  */
372 void
373 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
375     GtkWidget *icon;
377     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
378     gtk_widget_show(icon);
379     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
380 } // end of sp_ui_menu_add_icon
382 /**
383  * sp_ui_menu_append_item
384  *
385  * Appends a UI item with specific info for Inkscape/Sodipodi.
386  *
387  */
388 static GtkWidget *
389 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
390                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
391                         gpointer data, gboolean with_mnemonic = TRUE )
393     GtkWidget *item;
395     if (stock) {
396         item = gtk_image_menu_item_new_from_stock(stock, NULL);
397     } else if (label) {
398         item = (with_mnemonic)
399             ? gtk_image_menu_item_new_with_mnemonic(label) :
400             gtk_image_menu_item_new_with_label(label);
401     } else {
402         item = gtk_separator_menu_item_new();
403     }
405     gtk_widget_show(item);
407     if (callback) {
408         g_signal_connect(G_OBJECT(item), "activate", callback, data);
409     }
411     if (tip && view) {
412         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
413         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
414         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
415     }
417     gtk_menu_append(GTK_MENU(menu), item);
419     return item;
421 } // end of sp_ui_menu_append_item()
423 /**
424 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
425  */
426 static gchar const *
427 sp_key_name(guint keyval)
429     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
430        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
431     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
433     if      (!strcmp(n, "asciicircum"))  return "^";
434     else if (!strcmp(n, "parenleft"  ))  return "(";
435     else if (!strcmp(n, "parenright" ))  return ")";
436     else if (!strcmp(n, "plus"       ))  return "+";
437     else if (!strcmp(n, "minus"      ))  return "-";
438     else if (!strcmp(n, "asterisk"   ))  return "*";
439     else if (!strcmp(n, "KP_Multiply"))  return "*";
440     else if (!strcmp(n, "Delete"     ))  return "Del";
441     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
442     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
443     else if (!strcmp(n, "grave"      ))  return "`";
444     else if (!strcmp(n, "numbersign" ))  return "#";
445     else if (!strcmp(n, "bar" ))  return "|";
446     else if (!strcmp(n, "slash" ))  return "/";
447     else if (!strcmp(n, "exclam" ))  return "!";
448     else return n;
452 /**
453  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
454  * \param c Points to a buffer at least 256 bytes long.
455  */
456 void
457 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
459     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
460      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
461      * Will probably need to change sp_shortcut_invoke callers.
462      *
463      * The existing gtk_label_new_with_mnemonic call can be replaced with
464      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
465      * gtk_label_set_text_with_mnemonic(lbl, str).
466      */
467     static GtkAccelLabelClass const &accel_lbl_cls
468         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
470     struct { unsigned test; char const *name; } const modifier_tbl[] = {
471         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
472         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
473         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
474     };
476     gchar *p = c;
477     gchar *end = p + 256;
479     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
480         if ((shortcut & modifier_tbl[i].test)
481             && (p < end))
482         {
483             p += g_snprintf(p, end - p, "%s%s",
484                             modifier_tbl[i].name,
485                             accel_lbl_cls.mod_separator);
486         }
487     }
488     if (p < end) {
489         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
490     }
491     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
494 void
495 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
497     SPAction     *action;
498     unsigned int shortcut;
499     gchar        *s;
500     gchar        key[256];
501     gchar        *atitle;
503     action = verb->get_action(NULL);
504     if (!action)
505         return;
507     atitle = sp_action_get_title(action);
509     s = g_stpcpy(c, atitle);
511     g_free(atitle);
513     shortcut = sp_shortcut_get_primary(verb);
514     if (shortcut) {
515         s = g_stpcpy(s, " (");
516         sp_ui_shortcut_string(shortcut, key);
517         s = g_stpcpy(s, key);
518         s = g_stpcpy(s, ")");
519     }
523 /**
524  * sp_ui_menu_append_item_from_verb
525  *
526  * Appends a custom menu UI from a verb.
527  *
528  */
530 static GtkWidget *
531 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
533     SPAction *action;
534     GtkWidget *item;
536     if (verb->get_code() == SP_VERB_NONE) {
538         item = gtk_separator_menu_item_new();
540     } else {
541         unsigned int shortcut;
543         action = verb->get_action(view);
545         if (!action) return NULL;
547         shortcut = sp_shortcut_get_primary(verb);
548         if (shortcut) {
549             gchar c[256];
550             sp_ui_shortcut_string(shortcut, c);
551             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
552             GtkWidget *const name_lbl = gtk_label_new("");
553             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
554             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
555             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
556             GtkWidget *const accel_lbl = gtk_label_new(c);
557             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
558             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
559             gtk_widget_show_all(hb);
560             if (radio) {
561                 item = gtk_radio_menu_item_new (group);
562             } else {
563                 item = gtk_image_menu_item_new();
564             }
565             gtk_container_add((GtkContainer *) item, hb);
566         } else {
567             if (radio) {
568                 item = gtk_radio_menu_item_new (group);
569             } else {
570                 item = gtk_image_menu_item_new ();
571             }
572             GtkWidget *const name_lbl = gtk_label_new("");
573             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
574             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
575             gtk_container_add((GtkContainer *) item, name_lbl);
576         }
578         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
579         if (!action->sensitive) {
580             gtk_widget_set_sensitive(item, FALSE);
581         }
583         if (action->image) {
584             sp_ui_menuitem_add_icon(item, action->image);
585         }
586         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
587         g_signal_connect( G_OBJECT(item), "activate",
588                           G_CALLBACK(sp_ui_menu_activate), action );
590         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
591         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
592     }
594     gtk_widget_show(item);
595     gtk_menu_append(GTK_MENU(menu), item);
597     return item;
599 } // end of sp_ui_menu_append_item_from_verb
602 static void
603 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
605     gchar const *pref = (gchar const *) user_data;
606     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
608     gchar const *pref_path;
609     if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
610         pref_path = g_strconcat("fullscreen.", pref, NULL);
611     else
612         pref_path = g_strconcat("window.", pref, NULL);
614     gboolean checked = gtk_check_menu_item_get_active(menuitem);
615     prefs_set_int_attribute(pref_path, "state", checked);
617     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
620 static gboolean
621 checkitem_update(GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
623     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
625     gchar const *pref = (gchar const *) user_data;
626     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
628     gchar const *pref_path;
629     if ((static_cast<SPDesktop*>(view))->is_fullscreen())
630         pref_path = g_strconcat("fullscreen.", pref, NULL);
631     else
632         pref_path = g_strconcat("window.", pref, NULL);
634     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
636     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
637     gtk_check_menu_item_set_active(menuitem, ison);
638     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
640     return FALSE;
644 void
645 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
646                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
647                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
648                                        Inkscape::Verb *verb)
650     GtkWidget *item;
652     unsigned int shortcut = 0;
653     SPAction *action = NULL;
655     if (verb) {
656         shortcut = sp_shortcut_get_primary(verb);
657         action = verb->get_action(view);
658     }
660     if (verb && shortcut) {
661         gchar c[256];
662         sp_ui_shortcut_string(shortcut, c);
664         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
666         {
667             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
668             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
669             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
670         }
672         {
673             GtkWidget *l = gtk_label_new(c);
674             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
675             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
676         }
678         gtk_widget_show_all(hb);
680         item = gtk_check_menu_item_new();
681         gtk_container_add((GtkContainer *) item, hb);
682     } else {
683         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
684         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
685         item = gtk_check_menu_item_new();
686         gtk_container_add((GtkContainer *) item, l);
687     }
688 #if 0
689     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
690     if (!action->sensitive) {
691         gtk_widget_set_sensitive(item, FALSE);
692     }
693 #endif
694     gtk_widget_show(item);
696     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
698     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
700     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
701     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
703     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
704     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
707 static void
708 sp_recent_open(GtkWidget *widget, gchar const *uri)
710     sp_file_open(uri, NULL);
713 static void
714 sp_file_new_from_template(GtkWidget *widget, gchar const *uri)
716     sp_file_new(uri);
719 void
720 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
722     std::list<gchar *> sources;
723     sources.push_back( profile_path("templates") ); // first try user's local dir
724     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
726     // Use this loop to iterate through a list of possible document locations.
727     while (!sources.empty()) {
728         gchar *dirname = sources.front();
730         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
731             GError *err = 0;
732             GDir *dir = g_dir_open(dirname, 0, &err);
734             if (dir) {
735                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
736                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
737                         continue; // skip non-svg files
739                     gchar *basename = g_path_get_basename(file);
740                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
741                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
743                     gchar const *filepath = g_build_filename(dirname, file, NULL);
744                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
745                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
746                     g_free(dupfile);
747                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
748                     g_free(filename);
750                     gtk_widget_show(item);
751                     // how does "filepath" ever get freed?
752                     g_signal_connect(G_OBJECT(item),
753                                      "activate",
754                                      G_CALLBACK(sp_file_new_from_template),
755                                      (gpointer) filepath);
757                     if (view) {
758                         // set null tip for now; later use a description from the template file
759                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
760                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
761                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
762                     }
764                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
765                 }
766                 g_dir_close(dir);
767             }
768         }
770         // toss the dirname
771         g_free(dirname);
772         sources.pop_front();
773     }
776 void
777 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
779     gchar const **recent = prefs_get_recent_files();
780     if (recent) {
781         int i;
783         for (i = 0; recent[i] != NULL; i += 2) {
784             gchar const *uri = recent[i];
785             gchar const *name = recent[i + 1];
787             GtkWidget *item = gtk_menu_item_new_with_label(name);
788             gtk_widget_show(item);
789             g_signal_connect(G_OBJECT(item),
790                              "activate",
791                              G_CALLBACK(sp_recent_open),
792                              (gpointer)uri);
793             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
794         }
796         g_free(recent);
797     } else {
798         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
799         gtk_widget_show(item);
800         gtk_widget_set_sensitive(item, FALSE);
801         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
802     }
805 void
806 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
808     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
809     //                                       checkitem_toggled, checkitem_update, 0);
810     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
811                                            checkitem_toggled, checkitem_update, 0);
812     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
813                                            checkitem_toggled, checkitem_update, 0);
814     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
815                                            checkitem_toggled, checkitem_update, 0);
816     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
817                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
818     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
819                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
820     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
821                                            checkitem_toggled, checkitem_update, 0);
822     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
823                                            checkitem_toggled, checkitem_update, 0);
826 /** \brief  This function turns XML into a menu
827     \param  menus  This is the XML that defines the menu
828     \param  menu   Menu to be added to
829     \param  view   The View that this menu is being built for
831     This function is realitively simple as it just goes through the XML
832     and parses the individual elements.  In the case of a submenu, it
833     just calls itself recursively.  Because it is only reasonable to have
834     a couple of submenus, it is unlikely this will go more than two or
835     three times.
837     In the case of an unreconginzed verb, a menu item is made to identify
838     the verb that is missing, and display that.  The menu item is also made
839     insensitive.
840 */
841 void
842 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
844     if (menus == NULL) return;
845     if (menu == NULL)  return;
846     GSList *group = NULL;
848     for (Inkscape::XML::Node *menu_pntr = menus;
849          menu_pntr != NULL;
850          menu_pntr = menu_pntr->next()) {
851         if (!strcmp(menu_pntr->name(), "submenu")) {
852             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
853             GtkWidget *submenu = gtk_menu_new();
854             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
855             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
856             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
857             continue;
858         }
859         if (!strcmp(menu_pntr->name(), "verb")) {
860             gchar const *verb_name = menu_pntr->attribute("verb-id");
861             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
863             if (verb != NULL) {
864                 if (menu_pntr->attribute("radio") != NULL) {
865                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
866                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
867                     if (menu_pntr->attribute("default") != NULL) {
868                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
869                     }
870                 } else {
871                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
872                     group = NULL;
873                 }
874             } else {
875                 gchar string[120];
876                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
877                 string[119] = '\0'; /* may not be terminated */
878                 GtkWidget *item = gtk_menu_item_new_with_label(string);
879                 gtk_widget_set_sensitive(item, false);
880                 gtk_widget_show(item);
881                 gtk_menu_append(GTK_MENU(menu), item);
882             }
883             continue;
884         }
885         if (!strcmp(menu_pntr->name(), "separator")
886                 // This was spelt wrong in the original version
887                 // and so this is for backward compatibility.  It can
888                 // probably be dropped after the 0.44 release.
889              || !strcmp(menu_pntr->name(), "seperator")) {
890             GtkWidget *item = gtk_separator_menu_item_new();
891             gtk_widget_show(item);
892             gtk_menu_append(GTK_MENU(menu), item);
893             continue;
894         }
895         if (!strcmp(menu_pntr->name(), "template-list")) {
896             sp_menu_append_new_templates(menu, view);
897             continue;
898         }
899         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
900             sp_menu_append_recent_documents(menu, view);
901             continue;
902         }
903         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
904             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
905             continue;
906         }
907     }
910 /** \brief  Build the main tool bar
911     \param  view  View to build the bar for
913     Currently the main tool bar is built as a dynamic XML menu using
914     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
915     pass it to get items attached to it.
916 */
917 GtkWidget *
918 sp_ui_main_menubar(Inkscape::UI::View::View *view)
920     GtkWidget *mbar = gtk_menu_bar_new();
922     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
924     return mbar;
927 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
928     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
931 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
932     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
933     sp_desktop_selection(desktop)->clear();
936 GtkWidget *
937 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
939     GtkWidget *m;
940     SPDesktop *dt;
942     dt = static_cast<SPDesktop*>(view);
944     m = gtk_menu_new();
946     /* Undo and Redo */
947     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
948     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
950     /* Separator */
951     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
953     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
954     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
955     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
957     /* Separator */
958     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
960     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
961     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
963     /* Item menu */
964     if (item) {
965         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
966         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
967     }
969     /* layer menu */
970     SPGroup *group=NULL;
971     if (item) {
972         if (SP_IS_GROUP(item)) {
973             group = SP_GROUP(item);
974         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
975             group = SP_GROUP(SP_OBJECT_PARENT(item));
976         }
977     }
979     if (( group && group != dt->currentLayer() ) ||
980         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
981         /* Separator */
982         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
983     }
985     if ( group && group != dt->currentLayer() ) {
986         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
987         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
988         GtkWidget *w = gtk_menu_item_new_with_label(label);
989         g_free(label);
990         g_object_set_data(G_OBJECT(w), "group", group);
991         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
992         gtk_widget_show(w);
993         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
994     }
996     if ( dt->currentLayer() != dt->currentRoot() ) {
997         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
998             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
999             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1000             gtk_widget_show(w);
1001             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1003         }
1004     }
1006     return m;
1009 /* Drag and Drop */
1010 void
1011 sp_ui_drag_data_received(GtkWidget *widget,
1012                          GdkDragContext *drag_context,
1013                          gint x, gint y,
1014                          GtkSelectionData *data,
1015                          guint info,
1016                          guint event_time,
1017                          gpointer user_data)
1019     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1020     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1022     switch (info) {
1023 #if ENABLE_MAGIC_COLORS
1024         case APP_X_INKY_COLOR:
1025         {
1026             int destX = 0;
1027             int destY = 0;
1028             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1029             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1031             SPItem *item = desktop->item_at_point( where, true );
1032             if ( item )
1033             {
1034                 if ( data->length >= 8 ) {
1035                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1037                     gchar c[64] = {0};
1038                     // Careful about endian issues.
1039                     guint16* dataVals = (guint16*)data->data;
1040                     sp_svg_write_color( c, 64,
1041                                         SP_RGBA32_U_COMPOSE(
1042                                             0x0ff & (dataVals[0] >> 8),
1043                                             0x0ff & (dataVals[1] >> 8),
1044                                             0x0ff & (dataVals[2] >> 8),
1045                                             0xff // can't have transparency in the color itself
1046                                             //0x0ff & (data->data[3] >> 8),
1047                                             ));
1048                     SPCSSAttr *css = sp_repr_css_attr_new();
1049                     bool updatePerformed = false;
1051                     if ( data->length > 14 ) {
1052                         int flags = dataVals[4];
1054                         // piggie-backed palette entry info
1055                         int index = dataVals[5];
1056                         Glib::ustring palName;
1057                         for ( int i = 0; i < dataVals[6]; i++ ) {
1058                             palName += (gunichar)dataVals[7+i];
1059                         }
1061                         // Now hook in a magic tag of some sort.
1062                         if ( !palName.empty() && (flags & 1) ) {
1063                             gchar* str = g_strdup_printf("%d|", index);
1064                             palName.insert( 0, str );
1065                             g_free(str);
1066                             str = 0;
1068                             sp_object_setAttribute( SP_OBJECT(item),
1069                                                     (drag_context->action != GDK_ACTION_MOVE) ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1070                                                     palName.c_str(),
1071                                                     false );
1072                             item->updateRepr();
1074                             sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c );
1075                             updatePerformed = true;
1076                         }
1077                     }
1079                     if ( !updatePerformed ) {
1080                         sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c );
1081                     }
1083                     sp_desktop_apply_css_recursive( item, css, true );
1084                     item->updateRepr();
1086                     sp_document_done( doc , SP_VERB_NONE, 
1087                                       _("Drop color"));
1089                     if ( srgbProf ) {
1090                         cmsCloseProfile( srgbProf );
1091                     }
1092                 }
1093             }
1094         }
1095         break;
1096 #endif // ENABLE_MAGIC_COLORS
1098         case APP_X_COLOR:
1099         {
1100             int destX = 0;
1101             int destY = 0;
1102             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1103             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1105             SPItem *item = desktop->item_at_point( where, true );
1106             if ( item )
1107             {
1108                 if ( data->length == 8 ) {
1109                     gchar c[64] = {0};
1110                     // Careful about endian issues.
1111                     guint16* dataVals = (guint16*)data->data;
1112                     sp_svg_write_color( c, 64,
1113                                         SP_RGBA32_U_COMPOSE(
1114                                             0x0ff & (dataVals[0] >> 8),
1115                                             0x0ff & (dataVals[1] >> 8),
1116                                             0x0ff & (dataVals[2] >> 8),
1117                                             0xff // can't have transparency in the color itself
1118                                             //0x0ff & (data->data[3] >> 8),
1119                                             ));
1120                     SPCSSAttr *css = sp_repr_css_attr_new();
1121                     sp_repr_css_set_property( css, (drag_context->action != GDK_ACTION_MOVE) ? "fill":"stroke", c );
1123                     sp_desktop_apply_css_recursive( item, css, true );
1124                     item->updateRepr();
1126                     sp_document_done( doc , SP_VERB_NONE, 
1127                                       _("Drop color"));
1128                 }
1129             }
1130         }
1131         break;
1133         case SVG_DATA:
1134         case SVG_XML_DATA: {
1135             gchar *svgdata = (gchar *)data->data;
1137             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1139             if (rnewdoc == NULL) {
1140                 sp_ui_error_dialog(_("Could not parse SVG data"));
1141                 return;
1142             }
1144             Inkscape::XML::Node *repr = rnewdoc->root();
1145             gchar const *style = repr->attribute("style");
1147             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1148             newgroup->setAttribute("style", style);
1150             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1151             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1152                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1153                 newgroup->appendChild(newchild);
1154             }
1156             Inkscape::GC::release(rnewdoc);
1158             // Add it to the current layer
1160             // Greg's edits to add intelligent positioning of svg drops
1161             SPObject *new_obj = NULL;
1162             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1164             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1165             selection->set(SP_ITEM(new_obj));
1166             // To move the imported object, we must temporarily set the "transform pattern with
1167             // object" option.
1168             {
1169                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1170                 prefs_set_int_attribute("options.transform", "pattern", 1);
1171                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1172                 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1173                 if (sel_bbox) {
1174                     NR::Point m( desktop->point() - sel_bbox->midpoint() );
1175                     sp_selection_move_relative(selection, m);
1176                 }
1177                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1178             }
1180             Inkscape::GC::release(newgroup);
1181             sp_document_done(doc, SP_VERB_NONE, 
1182                              _("Drop SVG"));
1183             break;
1184         }
1186         case URI_LIST: {
1187             gchar *uri = (gchar *)data->data;
1188             sp_ui_import_files(uri);
1189             break;
1190         }
1192         case PNG_DATA:
1193         case JPEG_DATA:
1194         case IMAGE_DATA: {
1195             char tmp[1024];
1197             StringOutputStream outs;
1198             Base64OutputStream b64out(outs);
1199             b64out.setColumnWidth(0);
1201             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1203             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1205             for ( int i = 0; i < data->length; i++ ) {
1206                 b64out.put( data->data[i] );
1207             }
1208             b64out.close();
1211             Glib::ustring str = outs.getString();
1213             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1214             str.insert( 0, tmp );
1215             newImage->setAttribute("xlink:href", str.c_str());
1217             GError *error = NULL;
1218             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1219             if ( loader ) {
1220                 error = NULL;
1221                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1222                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1223                     if ( pbuf ) {
1224                         int width = gdk_pixbuf_get_width(pbuf);
1225                         int height = gdk_pixbuf_get_height(pbuf);
1226                         snprintf( tmp, sizeof(tmp), "%d", width );
1227                         newImage->setAttribute("width", tmp);
1229                         snprintf( tmp, sizeof(tmp), "%d", height );
1230                         newImage->setAttribute("height", tmp);
1231                     }
1232                 }
1233             }
1235             // Add it to the current layer
1236             desktop->currentLayer()->appendChildRepr(newImage);
1238             Inkscape::GC::release(newImage);
1239             sp_document_done( doc , SP_VERB_NONE, 
1240                               _("Drop bitmap image"));
1241             break;
1242         }
1243     }
1246 static void
1247 sp_ui_import_files(gchar *buffer)
1249     GList *list = gnome_uri_list_extract_filenames(buffer);
1250     if (!list)
1251         return;
1252     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1253     g_list_foreach(list, (GFunc) g_free, NULL);
1254     g_list_free(list);
1257 static void
1258 sp_ui_import_one_file_with_check(gpointer filename, gpointer unused)
1260     if (filename) {
1261         if (strlen((char const *)filename) > 2)
1262             sp_ui_import_one_file((char const *)filename);
1263     }
1266 static void
1267 sp_ui_import_one_file(char const *filename)
1269     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1270     if (!doc) return;
1272     if (filename == NULL) return;
1274     // Pass off to common implementation
1275     // TODO might need to get the proper type of Inkscape::Extension::Extension
1276     file_import( doc, filename, NULL );
1279 void
1280 sp_ui_error_dialog(gchar const *message)
1282     GtkWidget *dlg;
1283     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1285     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1286                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1287     sp_transientize(dlg);
1288     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1289     gtk_dialog_run(GTK_DIALOG(dlg));
1290     gtk_widget_destroy(dlg);
1291     g_free(safeMsg);
1294 bool
1295 sp_ui_overwrite_file(gchar const *filename)
1297     bool return_value = FALSE;
1299     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1300         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1301         gchar* baseName = g_path_get_basename( filename );
1302         gchar* dirName = g_path_get_dirname( filename );
1303         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1304                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1305                                                                 GTK_MESSAGE_QUESTION,
1306                                                                 GTK_BUTTONS_NONE,
1307                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1308                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1309                                                                 baseName,
1310                                                                 dirName
1311             );
1312         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1313                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1314                                 _("Replace"), GTK_RESPONSE_YES,
1315                                 NULL );
1316         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1318         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1319             return_value = TRUE;
1320         } else {
1321             return_value = FALSE;
1322         }
1323         gtk_widget_destroy(dialog);
1324         g_free( baseName );
1325         g_free( dirName );
1326     } else {
1327         return_value = TRUE;
1328     }
1330     return return_value;
1333 static void
1334 sp_ui_menu_item_set_sensitive(SPAction *action, unsigned int sensitive, void *data)
1336     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1339 static void
1340 sp_ui_menu_item_set_name(SPAction *action, Glib::ustring name, void *data)
1342     void *child = GTK_BIN (data)->child;
1343     //child is either
1344     //- a GtkHBox, whose first child is a label displaying name if the menu
1345     //item has an accel key
1346     //- a GtkLabel if the menu has no accel key
1347     if (GTK_IS_LABEL(child)) {
1348         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1349     } else if (GTK_IS_HBOX(child)) {
1350         gtk_label_set_markup_with_mnemonic(
1351         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
1352         name.c_str());
1353     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1354     //a menu item in yet another way...
1358 /*
1359   Local Variables:
1360   mode:c++
1361   c-file-style:"stroustrup"
1362   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1363   indent-tabs-mode:nil
1364   fill-column:99
1365   End:
1366 */
1367 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :