Code

Use "%"-key for toggling the snapper, and modify the related sp-desktop code
[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-text.h"
43 #include "sp-flowtext.h"
44 #include "sp-namedview.h"
45 #include "ui/view/view.h"
47 #include "helper/action.h"
48 #include "helper/gnome-utils.h"
49 #include "helper/window.h"
51 #include "io/sys.h"
52 #include "io/stringstream.h"
53 #include "io/base64stream.h"
55 #include "dialogs/dialog-events.h"
57 #include "message-context.h"
59 // Added for color drag-n-drop
60 #if ENABLE_LCMS
61 #include "lcms.h"
62 #endif // ENABLE_LCMS
63 #include "display/sp-canvas.h"
64 #include "color.h"
65 #include "svg/svg-color.h"
66 #include "desktop-style.h"
67 #include "style.h"
68 #include "event-context.h"
69 #include "gradient-drag.h"
71 using Inkscape::IO::StringOutputStream;
72 using Inkscape::IO::Base64OutputStream;
74 /* Drag and Drop */
75 typedef enum {
76     URI_LIST,
77     SVG_XML_DATA,
78     SVG_DATA,
79     PNG_DATA,
80     JPEG_DATA,
81     IMAGE_DATA,
82     APP_X_INKY_COLOR,
83     APP_X_COLOR
84 } ui_drop_target_info;
86 static GtkTargetEntry ui_drop_target_entries [] = {
87     {"text/uri-list", 0, URI_LIST},
88     {"image/svg+xml", 0, SVG_XML_DATA},
89     {"image/svg",     0, SVG_DATA},
90     {"image/png",     0, PNG_DATA},
91     {"image/jpeg",    0, JPEG_DATA},
92 #if ENABLE_MAGIC_COLORS
93     {"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
94 #endif // ENABLE_MAGIC_COLORS
95     {"application/x-color", 0, APP_X_COLOR}
96 };
98 static GtkTargetEntry *completeDropTargets = 0;
99 static int completeDropTargetsCount = 0;
101 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
102 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
103 static void sp_ui_import_files(gchar *buffer);
104 static void sp_ui_import_one_file(char const *filename);
105 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
106 static void sp_ui_drag_data_received(GtkWidget *widget,
107                                      GdkDragContext *drag_context,
108                                      gint x, gint y,
109                                      GtkSelectionData *data,
110                                      guint info,
111                                      guint event_time,
112                                      gpointer user_data);
113 static void sp_ui_drag_motion( GtkWidget *widget,
114                                GdkDragContext *drag_context,
115                                gint x, gint y,
116                                GtkSelectionData *data,
117                                guint info,
118                                guint event_time,
119                                gpointer user_data );
120 static void sp_ui_drag_leave( GtkWidget *widget,
121                               GdkDragContext *drag_context,
122                               guint event_time,
123                               gpointer user_data );
124 static void sp_ui_menu_item_set_sensitive(SPAction *action,
125                                           unsigned int sensitive,
126                                           void *data);
127 static void sp_ui_menu_item_set_name(SPAction *action, 
128                                      Glib::ustring name,
129                                      void *data);
131 SPActionEventVector menu_item_event_vector = {
132     {NULL},
133     NULL,
134     NULL, /* set_active */
135     sp_ui_menu_item_set_sensitive, /* set_sensitive */
136     NULL, /* set_shortcut */
137     sp_ui_menu_item_set_name /* set_name */
138 };
140 static const int MIN_ONSCREEN_DISTANCE = 50;
142 void
143 sp_create_window(SPViewWidget *vw, gboolean editable)
145     g_return_if_fail(vw != NULL);
146     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
148     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
150     if (editable) {
151                 g_object_set_data(G_OBJECT(vw), "window", win);
152                 
153                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
154                 SPDesktop* desktop = desktop_widget->desktop;
155                 
156                 desktop_widget->window = win;
158         /* fixme: doesn't allow making window any smaller than this */
159         win->set_default_size(640, 480);
160                 
161         win->set_data("desktop", desktop);
162         win->set_data("desktopwidget", desktop_widget);
163                 
164         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
165                 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
166                 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
167                 
168         gint prefs_geometry = 
169             (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
170         if (prefs_geometry) {
171             gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
172             gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
173             gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
174             gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
175             gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
176             gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
177             if (pw>0 && ph>0) {
178                 gint w = MIN(gdk_screen_width(), pw);
179                 gint h = MIN(gdk_screen_height(), ph);
180                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
181                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
182                 if (w>0 && h>0 && x>0 && y>0) {
183                     x = MIN(gdk_screen_width() - w, x);
184                     y = MIN(gdk_screen_height() - h, y);
185                 }
186                 if (w>0 && h>0) {
187                     desktop->setWindowSize(w, h);
188                 }
190                 // Only restore xy for the first window so subsequent windows don't overlap exactly
191                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
192                 // other desktops?)
194                 // Empirically it seems that active_desktop==this desktop only the first time a
195                 // desktop is created.
196                 if (x>0 && y>0) {
197                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
198                     if (active_desktop == desktop || active_desktop==NULL) {
199                         desktop->setWindowPosition(NR::Point(x, y));
200                     }
201                 }
202             }
203             if (maxed) {
204                 win->maximize();
205             }
206             if (full) {
207                 win->fullscreen();
208             }
209         }
211     } else {
212         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
213     }
215     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
216     gtk_widget_show(GTK_WIDGET(vw));
218     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
219     {
220         std::vector<gchar*> types;
222         GSList *list = gdk_pixbuf_get_formats();
223         while ( list ) {
224             int i = 0;
225             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
226             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
227             for ( i = 0; typesXX[i]; i++ ) {
228                 types.push_back(g_strdup(typesXX[i]));
229             }
230             g_strfreev(typesXX);
232             list = g_slist_next(list);
233         }
234         completeDropTargetsCount = nui_drop_target_entries + types.size();
235         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
236         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
237             completeDropTargets[i] = ui_drop_target_entries[i];
238         }
239         int pos = nui_drop_target_entries;
241         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
242             completeDropTargets[pos].target = *it;
243             completeDropTargets[pos].flags = 0;
244             completeDropTargets[pos].info = IMAGE_DATA;
245             pos++;
246         }
247     }
249     gtk_drag_dest_set((GtkWidget*)win->gobj(),
250                       GTK_DEST_DEFAULT_ALL,
251                       completeDropTargets,
252                       completeDropTargetsCount,
253                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
256     g_signal_connect(G_OBJECT(win->gobj()),
257                      "drag_data_received",
258                      G_CALLBACK(sp_ui_drag_data_received),
259                      NULL);
260     g_signal_connect(G_OBJECT(win->gobj()),
261                      "drag_motion",
262                      G_CALLBACK(sp_ui_drag_motion),
263                      NULL);
264     g_signal_connect(G_OBJECT(win->gobj()),
265                      "drag_leave",
266                      G_CALLBACK(sp_ui_drag_leave),
267                      NULL);
268     win->show();
270     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
271     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
274 void
275 sp_ui_new_view()
277     SPDocument *document;
278     SPViewWidget *dtw;
280     document = SP_ACTIVE_DOCUMENT;
281     if (!document) return;
283     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
284     g_return_if_fail(dtw != NULL);
286     sp_create_window(dtw, TRUE);
287     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
288     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
291 /* TODO: not yet working */
292 /* To be re-enabled (by adding to menu) once it works. */
293 void
294 sp_ui_new_view_preview()
296     SPDocument *document;
297     SPViewWidget *dtw;
299     document = SP_ACTIVE_DOCUMENT;
300     if (!document) return;
302     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
303     g_return_if_fail(dtw != NULL);
304     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
306     sp_create_window(dtw, FALSE);
309 /**
310  * \param widget unused
311  */
312 void
313 sp_ui_close_view(GtkWidget */*widget*/)
315     if (SP_ACTIVE_DESKTOP == NULL) {
316         return;
317     }
318     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
319         return;
320     }
321     SP_ACTIVE_DESKTOP->destroyWidget();
325 /**
326  *  sp_ui_close_all
327  *
328  *  This function is called to exit the program, and iterates through all
329  *  open document view windows, attempting to close each in turn.  If the
330  *  view has unsaved information, the user will be prompted to save,
331  *  discard, or cancel.
332  *
333  *  Returns FALSE if the user cancels the close_all operation, TRUE
334  *  otherwise.
335  */
336 unsigned int
337 sp_ui_close_all(void)
339     /* Iterate through all the windows, destroying each in the order they
340        become active */
341     while (SP_ACTIVE_DESKTOP) {
342         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
343             /* The user cancelled the operation, so end doing the close */
344             return FALSE;
345         }
346         SP_ACTIVE_DESKTOP->destroyWidget();
347     }
349     return TRUE;
352 /*
353  * Some day when the right-click menus are ready to start working
354  * smarter with the verbs, we'll need to change this NULL being
355  * sent to sp_action_perform to something useful, or set some kind
356  * of global "right-clicked position" variable for actions to
357  * investigate when they're called.
358  */
359 static void
360 sp_ui_menu_activate(void */*object*/, SPAction *action)
362     sp_action_perform(action, NULL);
365 static void
366 sp_ui_menu_select_action(void */*object*/, SPAction *action)
368     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
371 static void
372 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
374     action->view->tipsMessageContext()->clear();
377 static void
378 sp_ui_menu_select(gpointer object, gpointer tip)
380     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
381     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
384 static void
385 sp_ui_menu_deselect(gpointer object)
387     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
388     view->tipsMessageContext()->clear();
391 /**
392  * sp_ui_menuitem_add_icon
393  *
394  * Creates and attaches a scaled icon to the given menu item.
395  *
396  */
397 void
398 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
400     GtkWidget *icon;
402     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
403     gtk_widget_show(icon);
404     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
405 } // end of sp_ui_menu_add_icon
407 /**
408  * sp_ui_menu_append_item
409  *
410  * Appends a UI item with specific info for Inkscape/Sodipodi.
411  *
412  */
413 static GtkWidget *
414 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
415                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
416                         gpointer data, gboolean with_mnemonic = TRUE )
418     GtkWidget *item;
420     if (stock) {
421         item = gtk_image_menu_item_new_from_stock(stock, NULL);
422     } else if (label) {
423         item = (with_mnemonic)
424             ? gtk_image_menu_item_new_with_mnemonic(label) :
425             gtk_image_menu_item_new_with_label(label);
426     } else {
427         item = gtk_separator_menu_item_new();
428     }
430     gtk_widget_show(item);
432     if (callback) {
433         g_signal_connect(G_OBJECT(item), "activate", callback, data);
434     }
436     if (tip && view) {
437         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
438         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
439         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
440     }
442     gtk_menu_append(GTK_MENU(menu), item);
444     return item;
446 } // end of sp_ui_menu_append_item()
448 /**
449 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
450  */
451 static gchar const *
452 sp_key_name(guint keyval)
454     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
455        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
456     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
458     if      (!strcmp(n, "asciicircum"))  return "^";
459     else if (!strcmp(n, "parenleft"  ))  return "(";
460     else if (!strcmp(n, "parenright" ))  return ")";
461     else if (!strcmp(n, "plus"       ))  return "+";
462     else if (!strcmp(n, "minus"      ))  return "-";
463     else if (!strcmp(n, "asterisk"   ))  return "*";
464     else if (!strcmp(n, "KP_Multiply"))  return "*";
465     else if (!strcmp(n, "Delete"     ))  return "Del";
466     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
467     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
468     else if (!strcmp(n, "grave"      ))  return "`";
469     else if (!strcmp(n, "numbersign" ))  return "#";
470     else if (!strcmp(n, "bar"        ))  return "|";
471     else if (!strcmp(n, "slash"      ))  return "/";
472     else if (!strcmp(n, "exclam"     ))  return "!";
473     else if (!strcmp(n, "percent"    ))  return "%";
474     else return n;
478 /**
479  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
480  * \param c Points to a buffer at least 256 bytes long.
481  */
482 void
483 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
485     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
486      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
487      * Will probably need to change sp_shortcut_invoke callers.
488      *
489      * The existing gtk_label_new_with_mnemonic call can be replaced with
490      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
491      * gtk_label_set_text_with_mnemonic(lbl, str).
492      */
493     static GtkAccelLabelClass const &accel_lbl_cls
494         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
496     struct { unsigned test; char const *name; } const modifier_tbl[] = {
497         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
498         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
499         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
500     };
502     gchar *p = c;
503     gchar *end = p + 256;
505     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
506         if ((shortcut & modifier_tbl[i].test)
507             && (p < end))
508         {
509             p += g_snprintf(p, end - p, "%s%s",
510                             modifier_tbl[i].name,
511                             accel_lbl_cls.mod_separator);
512         }
513     }
514     if (p < end) {
515         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
516     }
517     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
520 void
521 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
523     SPAction     *action;
524     unsigned int shortcut;
525     gchar        *s;
526     gchar        key[256];
527     gchar        *atitle;
529     action = verb->get_action(NULL);
530     if (!action)
531         return;
533     atitle = sp_action_get_title(action);
535     s = g_stpcpy(c, atitle);
537     g_free(atitle);
539     shortcut = sp_shortcut_get_primary(verb);
540     if (shortcut) {
541         s = g_stpcpy(s, " (");
542         sp_ui_shortcut_string(shortcut, key);
543         s = g_stpcpy(s, key);
544         s = g_stpcpy(s, ")");
545     }
549 /**
550  * sp_ui_menu_append_item_from_verb
551  *
552  * Appends a custom menu UI from a verb.
553  *
554  */
556 static GtkWidget *
557 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
559     SPAction *action;
560     GtkWidget *item;
562     if (verb->get_code() == SP_VERB_NONE) {
564         item = gtk_separator_menu_item_new();
566     } else {
567         unsigned int shortcut;
569         action = verb->get_action(view);
571         if (!action) return NULL;
573         shortcut = sp_shortcut_get_primary(verb);
574         if (shortcut) {
575             gchar c[256];
576             sp_ui_shortcut_string(shortcut, c);
577             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
578             GtkWidget *const name_lbl = gtk_label_new("");
579             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
580             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
581             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
582             GtkWidget *const accel_lbl = gtk_label_new(c);
583             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
584             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
585             gtk_widget_show_all(hb);
586             if (radio) {
587                 item = gtk_radio_menu_item_new (group);
588             } else {
589                 item = gtk_image_menu_item_new();
590             }
591             gtk_container_add((GtkContainer *) item, hb);
592         } else {
593             if (radio) {
594                 item = gtk_radio_menu_item_new (group);
595             } else {
596                 item = gtk_image_menu_item_new ();
597             }
598             GtkWidget *const name_lbl = gtk_label_new("");
599             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
600             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
601             gtk_container_add((GtkContainer *) item, name_lbl);
602         }
604         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
605         if (!action->sensitive) {
606             gtk_widget_set_sensitive(item, FALSE);
607         }
609         if (action->image) {
610             sp_ui_menuitem_add_icon(item, action->image);
611         }
612         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
613         g_signal_connect( G_OBJECT(item), "activate",
614                           G_CALLBACK(sp_ui_menu_activate), action );
616         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
617         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
618     }
620     gtk_widget_show(item);
621     gtk_menu_append(GTK_MENU(menu), item);
623     return item;
625 } // end of sp_ui_menu_append_item_from_verb
628 static void
629 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
631     gchar const *pref = (gchar const *) user_data;
632     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
634     gchar const *pref_path;
635     if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
636         pref_path = g_strconcat("fullscreen.", pref, NULL);
637     else
638         pref_path = g_strconcat("window.", pref, NULL);
640     gboolean checked = gtk_check_menu_item_get_active(menuitem);
641     prefs_set_int_attribute(pref_path, "state", checked);
643     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
646 static gboolean
647 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
649     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
651     gchar const *pref = (gchar const *) user_data;
652     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
654     gchar const *pref_path;
655     if ((static_cast<SPDesktop*>(view))->is_fullscreen())
656         pref_path = g_strconcat("fullscreen.", pref, NULL);
657     else
658         pref_path = g_strconcat("window.", pref, NULL);
660     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
662     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
663     gtk_check_menu_item_set_active(menuitem, ison);
664     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
666     return FALSE;
670 void
671 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
672                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
673                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
674                                        Inkscape::Verb *verb)
676     GtkWidget *item;
678     unsigned int shortcut = 0;
679     SPAction *action = NULL;
681     if (verb) {
682         shortcut = sp_shortcut_get_primary(verb);
683         action = verb->get_action(view);
684     }
686     if (verb && shortcut) {
687         gchar c[256];
688         sp_ui_shortcut_string(shortcut, c);
690         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
692         {
693             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
694             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
695             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
696         }
698         {
699             GtkWidget *l = gtk_label_new(c);
700             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
701             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
702         }
704         gtk_widget_show_all(hb);
706         item = gtk_check_menu_item_new();
707         gtk_container_add((GtkContainer *) item, hb);
708     } else {
709         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
710         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
711         item = gtk_check_menu_item_new();
712         gtk_container_add((GtkContainer *) item, l);
713     }
714 #if 0
715     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
716     if (!action->sensitive) {
717         gtk_widget_set_sensitive(item, FALSE);
718     }
719 #endif
720     gtk_widget_show(item);
722     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
724     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
726     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
727     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
729     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
730     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
733 static void
734 sp_recent_open(GtkWidget */*widget*/, gchar const *uri)
736     sp_file_open(uri, NULL);
739 static void
740 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
742     sp_file_new(uri);
745 void
746 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
748     std::list<gchar *> sources;
749     sources.push_back( profile_path("templates") ); // first try user's local dir
750     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
752     // Use this loop to iterate through a list of possible document locations.
753     while (!sources.empty()) {
754         gchar *dirname = sources.front();
756         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
757             GError *err = 0;
758             GDir *dir = g_dir_open(dirname, 0, &err);
760             if (dir) {
761                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
762                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
763                         continue; // skip non-svg files
765                     gchar *basename = g_path_get_basename(file);
766                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
767                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
769                     gchar const *filepath = g_build_filename(dirname, file, NULL);
770                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
771                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
772                     g_free(dupfile);
773                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
774                     g_free(filename);
776                     gtk_widget_show(item);
777                     // how does "filepath" ever get freed?
778                     g_signal_connect(G_OBJECT(item),
779                                      "activate",
780                                      G_CALLBACK(sp_file_new_from_template),
781                                      (gpointer) filepath);
783                     if (view) {
784                         // set null tip for now; later use a description from the template file
785                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
786                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
787                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
788                     }
790                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
791                 }
792                 g_dir_close(dir);
793             }
794         }
796         // toss the dirname
797         g_free(dirname);
798         sources.pop_front();
799     }
802 void
803 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
805     gchar const **recent = prefs_get_recent_files();
806     if (recent) {
807         int i;
809         for (i = 0; recent[i] != NULL; i += 2) {
810             gchar const *uri = recent[i];
811             gchar const *name = recent[i + 1];
813             GtkWidget *item = gtk_menu_item_new_with_label(name);
814             gtk_widget_show(item);
815             g_signal_connect(G_OBJECT(item),
816                              "activate",
817                              G_CALLBACK(sp_recent_open),
818                              (gpointer)uri);
819             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
820         }
822         g_free(recent);
823     } else {
824         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
825         gtk_widget_show(item);
826         gtk_widget_set_sensitive(item, FALSE);
827         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
828     }
831 void
832 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
834     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
835     //                                       checkitem_toggled, checkitem_update, 0);
836     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
837                                            checkitem_toggled, checkitem_update, 0);
838     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
839                                            checkitem_toggled, checkitem_update, 0);
840     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
841                                            checkitem_toggled, checkitem_update, 0);
842     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
843                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
844     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
845                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
846     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
847                                            checkitem_toggled, checkitem_update, 0);
848     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
849                                            checkitem_toggled, checkitem_update, 0);
852 /** \brief  This function turns XML into a menu
853     \param  menus  This is the XML that defines the menu
854     \param  menu   Menu to be added to
855     \param  view   The View that this menu is being built for
857     This function is realitively simple as it just goes through the XML
858     and parses the individual elements.  In the case of a submenu, it
859     just calls itself recursively.  Because it is only reasonable to have
860     a couple of submenus, it is unlikely this will go more than two or
861     three times.
863     In the case of an unreconginzed verb, a menu item is made to identify
864     the verb that is missing, and display that.  The menu item is also made
865     insensitive.
866 */
867 void
868 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
870     if (menus == NULL) return;
871     if (menu == NULL)  return;
872     GSList *group = NULL;
874     for (Inkscape::XML::Node *menu_pntr = menus;
875          menu_pntr != NULL;
876          menu_pntr = menu_pntr->next()) {
877         if (!strcmp(menu_pntr->name(), "submenu")) {
878             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
879             GtkWidget *submenu = gtk_menu_new();
880             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
881             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
882             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
883             continue;
884         }
885         if (!strcmp(menu_pntr->name(), "verb")) {
886             gchar const *verb_name = menu_pntr->attribute("verb-id");
887             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
889             if (verb != NULL) {
890                 if (menu_pntr->attribute("radio") != NULL) {
891                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
892                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
893                     if (menu_pntr->attribute("default") != NULL) {
894                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
895                     }
896                 } else {
897                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
898                     group = NULL;
899                 }
900             } else {
901                 gchar string[120];
902                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
903                 string[119] = '\0'; /* may not be terminated */
904                 GtkWidget *item = gtk_menu_item_new_with_label(string);
905                 gtk_widget_set_sensitive(item, false);
906                 gtk_widget_show(item);
907                 gtk_menu_append(GTK_MENU(menu), item);
908             }
909             continue;
910         }
911         if (!strcmp(menu_pntr->name(), "separator")
912                 // This was spelt wrong in the original version
913                 // and so this is for backward compatibility.  It can
914                 // probably be dropped after the 0.44 release.
915              || !strcmp(menu_pntr->name(), "seperator")) {
916             GtkWidget *item = gtk_separator_menu_item_new();
917             gtk_widget_show(item);
918             gtk_menu_append(GTK_MENU(menu), item);
919             continue;
920         }
921         if (!strcmp(menu_pntr->name(), "template-list")) {
922             sp_menu_append_new_templates(menu, view);
923             continue;
924         }
925         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
926             sp_menu_append_recent_documents(menu, view);
927             continue;
928         }
929         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
930             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
931             continue;
932         }
933     }
936 /** \brief  Build the main tool bar
937     \param  view  View to build the bar for
939     Currently the main tool bar is built as a dynamic XML menu using
940     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
941     pass it to get items attached to it.
942 */
943 GtkWidget *
944 sp_ui_main_menubar(Inkscape::UI::View::View *view)
946     GtkWidget *mbar = gtk_menu_bar_new();
948     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
950     return mbar;
953 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
954     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
957 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
958     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
959     sp_desktop_selection(desktop)->clear();
962 GtkWidget *
963 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
965     GtkWidget *m;
966     SPDesktop *dt;
968     dt = static_cast<SPDesktop*>(view);
970     m = gtk_menu_new();
972     /* Undo and Redo */
973     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
974     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
976     /* Separator */
977     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
979     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
980     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
981     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
983     /* Separator */
984     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
986     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
987     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
989     /* Item menu */
990     if (item) {
991         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
992         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
993     }
995     /* layer menu */
996     SPGroup *group=NULL;
997     if (item) {
998         if (SP_IS_GROUP(item)) {
999             group = SP_GROUP(item);
1000         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1001             group = SP_GROUP(SP_OBJECT_PARENT(item));
1002         }
1003     }
1005     if (( group && group != dt->currentLayer() ) ||
1006         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1007         /* Separator */
1008         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1009     }
1011     if ( group && group != dt->currentLayer() ) {
1012         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1013         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1014         GtkWidget *w = gtk_menu_item_new_with_label(label);
1015         g_free(label);
1016         g_object_set_data(G_OBJECT(w), "group", group);
1017         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1018         gtk_widget_show(w);
1019         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1020     }
1022     if ( dt->currentLayer() != dt->currentRoot() ) {
1023         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1024             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1025             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1026             gtk_widget_show(w);
1027             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1029         }
1030     }
1032     return m;
1035 /* Drag and Drop */
1036 void
1037 sp_ui_drag_data_received(GtkWidget *widget,
1038                          GdkDragContext *drag_context,
1039                          gint x, gint y,
1040                          GtkSelectionData *data,
1041                          guint info,
1042                          guint /*event_time*/,
1043                          gpointer /*user_data*/)
1045     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1046     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1048     switch (info) {
1049 #if ENABLE_MAGIC_COLORS
1050         case APP_X_INKY_COLOR:
1051         {
1052             int destX = 0;
1053             int destY = 0;
1054             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1055             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1057             SPItem *item = desktop->item_at_point( where, true );
1058             if ( item )
1059             {
1060                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1062                 if ( data->length >= 8 ) {
1063                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1065                     gchar c[64] = {0};
1066                     // Careful about endian issues.
1067                     guint16* dataVals = (guint16*)data->data;
1068                     sp_svg_write_color( c, sizeof(c),
1069                                         SP_RGBA32_U_COMPOSE(
1070                                             0x0ff & (dataVals[0] >> 8),
1071                                             0x0ff & (dataVals[1] >> 8),
1072                                             0x0ff & (dataVals[2] >> 8),
1073                                             0xff // can't have transparency in the color itself
1074                                             //0x0ff & (data->data[3] >> 8),
1075                                             ));
1076                     SPCSSAttr *css = sp_repr_css_attr_new();
1077                     bool updatePerformed = false;
1079                     if ( data->length > 14 ) {
1080                         int flags = dataVals[4];
1082                         // piggie-backed palette entry info
1083                         int index = dataVals[5];
1084                         Glib::ustring palName;
1085                         for ( int i = 0; i < dataVals[6]; i++ ) {
1086                             palName += (gunichar)dataVals[7+i];
1087                         }
1089                         // Now hook in a magic tag of some sort.
1090                         if ( !palName.empty() && (flags & 1) ) {
1091                             gchar* str = g_strdup_printf("%d|", index);
1092                             palName.insert( 0, str );
1093                             g_free(str);
1094                             str = 0;
1096                             sp_object_setAttribute( SP_OBJECT(item),
1097                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1098                                                     palName.c_str(),
1099                                                     false );
1100                             item->updateRepr();
1102                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1103                             updatePerformed = true;
1104                         }
1105                     }
1107                     if ( !updatePerformed ) {
1108                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1109                     }
1111                     sp_desktop_apply_css_recursive( item, css, true );
1112                     item->updateRepr();
1114                     sp_document_done( doc , SP_VERB_NONE, 
1115                                       _("Drop color"));
1117                     if ( srgbProf ) {
1118                         cmsCloseProfile( srgbProf );
1119                     }
1120                 }
1121             }
1122         }
1123         break;
1124 #endif // ENABLE_MAGIC_COLORS
1126         case APP_X_COLOR:
1127         {
1128             int destX = 0;
1129             int destY = 0;
1130             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1131             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1132             NR::Point const button_dt(desktop->w2d(where));
1133             NR::Point const button_doc(desktop->dt2doc(button_dt));
1135             if ( data->length == 8 ) {
1136                 gchar c[64] = {0};
1137                 // Careful about endian issues.
1138                 guint16* dataVals = (guint16*)data->data;
1139                 sp_svg_write_color( c, 64,
1140                                     SP_RGBA32_U_COMPOSE(
1141                                         0x0ff & (dataVals[0] >> 8),
1142                                         0x0ff & (dataVals[1] >> 8),
1143                                         0x0ff & (dataVals[2] >> 8),
1144                                         0xff // can't have transparency in the color itself
1145                                         //0x0ff & (data->data[3] >> 8),
1146                                         ));
1148                 SPItem *item = desktop->item_at_point( where, true );
1150                 bool consumed = false;
1151                 if (desktop->event_context && desktop->event_context->get_drag()) {
1152                     consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1153                     if (consumed) {
1154                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1155                         desktop->event_context->get_drag()->updateDraggers();
1156                     }
1157                 }
1159                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1160                 //    consumed = sp_text_context_drop_color(c, button_doc);
1161                 //    if (consumed) {
1162                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1163                 //    }
1164                 //}
1166                 if (!consumed && item) {
1167                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1168                     if (fillnotstroke && 
1169                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1170                         Path *livarot_path = Path_for_item(item, true, true);
1171                         livarot_path->ConvertWithBackData(0.04);
1173                         NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1174                         if (position) {
1175                             NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1176                             NR::Point delta = nearest - button_doc;
1177                             delta = desktop->d2w(delta);
1178                             double stroke_tolerance =
1179                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1180                                   desktop->current_zoom() *
1181                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1182                                   sp_item_i2d_affine (item).expansion() * 0.5
1183                                   : 0.0)
1184                                 + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); 
1186                             if (NR::L2 (delta) < stroke_tolerance) {
1187                                 fillnotstroke = false;
1188                             }
1189                         }
1190                         delete livarot_path;
1191                     }
1193                     SPCSSAttr *css = sp_repr_css_attr_new();
1194                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1196                     sp_desktop_apply_css_recursive( item, css, true );
1197                     item->updateRepr();
1199                     sp_document_done( doc , SP_VERB_NONE, 
1200                                       _("Drop color"));
1201                 }
1202             }
1203         }
1204         break;
1206         case SVG_DATA:
1207         case SVG_XML_DATA: {
1208             gchar *svgdata = (gchar *)data->data;
1210             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1212             if (rnewdoc == NULL) {
1213                 sp_ui_error_dialog(_("Could not parse SVG data"));
1214                 return;
1215             }
1217             Inkscape::XML::Node *repr = rnewdoc->root();
1218             gchar const *style = repr->attribute("style");
1220             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1221             newgroup->setAttribute("style", style);
1223             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1224             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1225                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1226                 newgroup->appendChild(newchild);
1227             }
1229             Inkscape::GC::release(rnewdoc);
1231             // Add it to the current layer
1233             // Greg's edits to add intelligent positioning of svg drops
1234             SPObject *new_obj = NULL;
1235             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1237             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1238             selection->set(SP_ITEM(new_obj));
1239             // To move the imported object, we must temporarily set the "transform pattern with
1240             // object" option.
1241             {
1242                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1243                 prefs_set_int_attribute("options.transform", "pattern", 1);
1244                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1245                 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1246                 if (sel_bbox) {
1247                     NR::Point m( desktop->point() - sel_bbox->midpoint() );
1248                     sp_selection_move_relative(selection, m);
1249                 }
1250                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1251             }
1253             Inkscape::GC::release(newgroup);
1254             sp_document_done(doc, SP_VERB_NONE, 
1255                              _("Drop SVG"));
1256             break;
1257         }
1259         case URI_LIST: {
1260             gchar *uri = (gchar *)data->data;
1261             sp_ui_import_files(uri);
1262             break;
1263         }
1265         case PNG_DATA:
1266         case JPEG_DATA:
1267         case IMAGE_DATA: {
1268             char tmp[1024];
1270             StringOutputStream outs;
1271             Base64OutputStream b64out(outs);
1272             b64out.setColumnWidth(0);
1274             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1276             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1278             for ( int i = 0; i < data->length; i++ ) {
1279                 b64out.put( data->data[i] );
1280             }
1281             b64out.close();
1284             Glib::ustring str = outs.getString();
1286             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1287             str.insert( 0, tmp );
1288             newImage->setAttribute("xlink:href", str.c_str());
1290             GError *error = NULL;
1291             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1292             if ( loader ) {
1293                 error = NULL;
1294                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1295                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1296                     if ( pbuf ) {
1297                         int width = gdk_pixbuf_get_width(pbuf);
1298                         int height = gdk_pixbuf_get_height(pbuf);
1299                         snprintf( tmp, sizeof(tmp), "%d", width );
1300                         newImage->setAttribute("width", tmp);
1302                         snprintf( tmp, sizeof(tmp), "%d", height );
1303                         newImage->setAttribute("height", tmp);
1304                     }
1305                 }
1306             }
1308             // Add it to the current layer
1309             desktop->currentLayer()->appendChildRepr(newImage);
1311             Inkscape::GC::release(newImage);
1312             sp_document_done( doc , SP_VERB_NONE, 
1313                               _("Drop bitmap image"));
1314             break;
1315         }
1316     }
1319 #include "gradient-context.h"
1321 void sp_ui_drag_motion( GtkWidget */*widget*/,
1322                         GdkDragContext */*drag_context*/,
1323                         gint /*x*/, gint /*y*/,
1324                         GtkSelectionData */*data*/,
1325                         guint /*info*/,
1326                         guint /*event_time*/,
1327                         gpointer /*user_data*/)
1329 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1330 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1333 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1336 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1337                               GdkDragContext */*drag_context*/,
1338                               guint /*event_time*/,
1339                               gpointer /*user_data*/ )
1341 //     g_message("drag-n-drop leave                at %d", event_time);
1344 static void
1345 sp_ui_import_files(gchar *buffer)
1347     GList *list = gnome_uri_list_extract_filenames(buffer);
1348     if (!list)
1349         return;
1350     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1351     g_list_foreach(list, (GFunc) g_free, NULL);
1352     g_list_free(list);
1355 static void
1356 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1358     if (filename) {
1359         if (strlen((char const *)filename) > 2)
1360             sp_ui_import_one_file((char const *)filename);
1361     }
1364 static void
1365 sp_ui_import_one_file(char const *filename)
1367     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1368     if (!doc) return;
1370     if (filename == NULL) return;
1372     // Pass off to common implementation
1373     // TODO might need to get the proper type of Inkscape::Extension::Extension
1374     file_import( doc, filename, NULL );
1377 void
1378 sp_ui_error_dialog(gchar const *message)
1380     GtkWidget *dlg;
1381     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1383     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1384                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1385     sp_transientize(dlg);
1386     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1387     gtk_dialog_run(GTK_DIALOG(dlg));
1388     gtk_widget_destroy(dlg);
1389     g_free(safeMsg);
1392 bool
1393 sp_ui_overwrite_file(gchar const *filename)
1395     bool return_value = FALSE;
1397     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1398         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1399         gchar* baseName = g_path_get_basename( filename );
1400         gchar* dirName = g_path_get_dirname( filename );
1401         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1402                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1403                                                                 GTK_MESSAGE_QUESTION,
1404                                                                 GTK_BUTTONS_NONE,
1405                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1406                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1407                                                                 baseName,
1408                                                                 dirName
1409             );
1410         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1411                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1412                                 _("Replace"), GTK_RESPONSE_YES,
1413                                 NULL );
1414         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1416         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1417             return_value = TRUE;
1418         } else {
1419             return_value = FALSE;
1420         }
1421         gtk_widget_destroy(dialog);
1422         g_free( baseName );
1423         g_free( dirName );
1424     } else {
1425         return_value = TRUE;
1426     }
1428     return return_value;
1431 static void
1432 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1434     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1437 static void
1438 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1440     void *child = GTK_BIN (data)->child;
1441     //child is either
1442     //- a GtkHBox, whose first child is a label displaying name if the menu
1443     //item has an accel key
1444     //- a GtkLabel if the menu has no accel key
1445     if (GTK_IS_LABEL(child)) {
1446         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1447     } else if (GTK_IS_HBOX(child)) {
1448         gtk_label_set_markup_with_mnemonic(
1449         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
1450         name.c_str());
1451     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1452     //a menu item in yet another way...
1456 /*
1457   Local Variables:
1458   mode:c++
1459   c-file-style:"stroustrup"
1460   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1461   indent-tabs-mode:nil
1462   fill-column:99
1463   End:
1464 */
1465 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :