Code

Merge from fe-moved
[inkscape.git] / src / interface.cpp
1 #define __SP_INTERFACE_C__
3 /** @file
4  * @brief 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 <glib.h>
24 #include "inkscape-private.h"
25 #include "extension/effect.h"
26 #include "widgets/icon.h"
27 #include "preferences.h"
28 #include "path-prefix.h"
30 #include "shortcuts.h"
32 #include "document.h"
33 #include "desktop-handles.h"
34 #include "file.h"
35 #include "interface.h"
36 #include "desktop.h"
37 #include "ui/context-menu.h"
38 #include "selection.h"
39 #include "selection-chemistry.h"
40 #include "svg-view-widget.h"
41 #include "widgets/desktop-widget.h"
42 #include "sp-item-group.h"
43 #include "sp-text.h"
44 #include "sp-flowtext.h"
45 #include "sp-namedview.h"
46 #include "ui/view/view.h"
48 #include "helper/action.h"
49 #include "helper/gnome-utils.h"
50 #include "helper/window.h"
52 #include "io/sys.h"
53 #include "io/stringstream.h"
54 #include "io/base64stream.h"
56 #include "dialogs/dialog-events.h"
58 #include "message-context.h"
60 // Added for color drag-n-drop
61 #if ENABLE_LCMS
62 #include "lcms.h"
63 #endif // ENABLE_LCMS
64 #include "display/sp-canvas.h"
65 #include "color.h"
66 #include "svg/svg-color.h"
67 #include "desktop-style.h"
68 #include "style.h"
69 #include "event-context.h"
70 #include "gradient-drag.h"
72 // Include Mac OS X menu synchronization on native OSX build
73 #ifdef GDK_WINDOWING_QUARTZ
74 #include "ige-mac-menu.h"
75 #endif
77 using Inkscape::IO::StringOutputStream;
78 using Inkscape::IO::Base64OutputStream;
80 /* Drag and Drop */
81 typedef enum {
82     URI_LIST,
83     SVG_XML_DATA,
84     SVG_DATA,
85     PNG_DATA,
86     JPEG_DATA,
87     IMAGE_DATA,
88     APP_X_INKY_COLOR,
89     APP_X_COLOR
90 } ui_drop_target_info;
92 static GtkTargetEntry ui_drop_target_entries [] = {
93     {(gchar *)"text/uri-list",                0, URI_LIST        },
94     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
95     {(gchar *)"image/svg",                    0, SVG_DATA        },
96     {(gchar *)"image/png",                    0, PNG_DATA        },
97     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
98 #if ENABLE_MAGIC_COLORS
99     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
100 #endif // ENABLE_MAGIC_COLORS
101     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
102 };
104 static GtkTargetEntry *completeDropTargets = 0;
105 static int completeDropTargetsCount = 0;
107 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
108 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
109 static void sp_ui_import_files(gchar *buffer);
110 static void sp_ui_import_one_file(char const *filename);
111 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
112 static void sp_ui_drag_data_received(GtkWidget *widget,
113                                      GdkDragContext *drag_context,
114                                      gint x, gint y,
115                                      GtkSelectionData *data,
116                                      guint info,
117                                      guint event_time,
118                                      gpointer user_data);
119 static void sp_ui_drag_motion( GtkWidget *widget,
120                                GdkDragContext *drag_context,
121                                gint x, gint y,
122                                GtkSelectionData *data,
123                                guint info,
124                                guint event_time,
125                                gpointer user_data );
126 static void sp_ui_drag_leave( GtkWidget *widget,
127                               GdkDragContext *drag_context,
128                               guint event_time,
129                               gpointer user_data );
130 static void sp_ui_menu_item_set_sensitive(SPAction *action,
131                                           unsigned int sensitive,
132                                           void *data);
133 static void sp_ui_menu_item_set_name(SPAction *action, 
134                                      Glib::ustring name,
135                                      void *data);
136 static void sp_recent_open(GtkRecentChooser *, gpointer);
138 SPActionEventVector menu_item_event_vector = {
139     {NULL},
140     NULL,
141     NULL, /* set_active */
142     sp_ui_menu_item_set_sensitive, /* set_sensitive */
143     NULL, /* set_shortcut */
144     sp_ui_menu_item_set_name /* set_name */
145 };
147 static const int MIN_ONSCREEN_DISTANCE = 50;
149 void
150 sp_create_window(SPViewWidget *vw, gboolean editable)
152     g_return_if_fail(vw != NULL);
153     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
155     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
157     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
158     gtk_widget_show(GTK_WIDGET(vw));
160     if (editable) {
161                 g_object_set_data(G_OBJECT(vw), "window", win);
162                 
163                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
164                 SPDesktop* desktop = desktop_widget->desktop;
165                 
166                 desktop_widget->window = win;
168         win->set_data("desktop", desktop);
169         win->set_data("desktopwidget", desktop_widget);
170                 
171         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
172                 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
173                 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
174         
175         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
176         gint prefs_geometry = 
177             (2==prefs->getInt("/options/savewindowgeometry/value", 0));
178         if (prefs_geometry) {
179             gint pw = prefs->getInt("/desktop/geometry/width", -1);
180             gint ph = prefs->getInt("/desktop/geometry/height", -1);
181             gint px = prefs->getInt("/desktop/geometry/x", -1);
182             gint py = prefs->getInt("/desktop/geometry/y", -1);
183             gint full = prefs->getBool("/desktop/geometry/fullscreen");
184             gint maxed = prefs->getBool("/desktop/geometry/maximized");
185             if (pw>0 && ph>0) {
186                 gint w = MIN(gdk_screen_width(), pw);
187                 gint h = MIN(gdk_screen_height(), ph);
188                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
189                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
190                 if (w>0 && h>0 && x>0 && y>0) {
191                     x = MIN(gdk_screen_width() - w, x);
192                     y = MIN(gdk_screen_height() - h, y);
193                 }
194                 if (w>0 && h>0) {
195                     desktop->setWindowSize(w, h);
196                 }
198                 // Only restore xy for the first window so subsequent windows don't overlap exactly
199                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
200                 // other desktops?)
202                 // Empirically it seems that active_desktop==this desktop only the first time a
203                 // desktop is created.
204                 if (x>0 && y>0) {
205                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
206                     if (active_desktop == desktop || active_desktop==NULL) {
207                         desktop->setWindowPosition(Geom::Point(x, y));
208                     }
209                 }
210             }
211             if (maxed) {
212                 win->maximize();
213             }
214             if (full) {
215                 win->fullscreen();
216             }
217         }
219     } else {
220         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
221     }
223     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
224     {
225         std::vector<gchar*> types;
227         GSList *list = gdk_pixbuf_get_formats();
228         while ( list ) {
229             int i = 0;
230             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
231             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
232             for ( i = 0; typesXX[i]; i++ ) {
233                 types.push_back(g_strdup(typesXX[i]));
234             }
235             g_strfreev(typesXX);
237             list = g_slist_next(list);
238         }
239         completeDropTargetsCount = nui_drop_target_entries + types.size();
240         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
241         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
242             completeDropTargets[i] = ui_drop_target_entries[i];
243         }
244         int pos = nui_drop_target_entries;
246         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
247             completeDropTargets[pos].target = *it;
248             completeDropTargets[pos].flags = 0;
249             completeDropTargets[pos].info = IMAGE_DATA;
250             pos++;
251         }
252     }
254     gtk_drag_dest_set((GtkWidget*)win->gobj(),
255                       GTK_DEST_DEFAULT_ALL,
256                       completeDropTargets,
257                       completeDropTargetsCount,
258                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
261     g_signal_connect(G_OBJECT(win->gobj()),
262                      "drag_data_received",
263                      G_CALLBACK(sp_ui_drag_data_received),
264                      NULL);
265     g_signal_connect(G_OBJECT(win->gobj()),
266                      "drag_motion",
267                      G_CALLBACK(sp_ui_drag_motion),
268                      NULL);
269     g_signal_connect(G_OBJECT(win->gobj()),
270                      "drag_leave",
271                      G_CALLBACK(sp_ui_drag_leave),
272                      NULL);
273     win->show();
275     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
276     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
279 void
280 sp_ui_new_view()
282     SPDocument *document;
283     SPViewWidget *dtw;
285     document = SP_ACTIVE_DOCUMENT;
286     if (!document) return;
288     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
289     g_return_if_fail(dtw != NULL);
291     sp_create_window(dtw, TRUE);
292     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
293     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
296 /* TODO: not yet working */
297 /* To be re-enabled (by adding to menu) once it works. */
298 void
299 sp_ui_new_view_preview()
301     SPDocument *document;
302     SPViewWidget *dtw;
304     document = SP_ACTIVE_DOCUMENT;
305     if (!document) return;
307     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
308     g_return_if_fail(dtw != NULL);
309     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
311     sp_create_window(dtw, FALSE);
314 /**
315  * \param widget unused
316  */
317 void
318 sp_ui_close_view(GtkWidget */*widget*/)
320     if (SP_ACTIVE_DESKTOP == NULL) {
321         return;
322     }
323     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
324         return;
325     }
326     SP_ACTIVE_DESKTOP->destroyWidget();
330 /**
331  *  sp_ui_close_all
332  *
333  *  This function is called to exit the program, and iterates through all
334  *  open document view windows, attempting to close each in turn.  If the
335  *  view has unsaved information, the user will be prompted to save,
336  *  discard, or cancel.
337  *
338  *  Returns FALSE if the user cancels the close_all operation, TRUE
339  *  otherwise.
340  */
341 unsigned int
342 sp_ui_close_all(void)
344     /* Iterate through all the windows, destroying each in the order they
345        become active */
346     while (SP_ACTIVE_DESKTOP) {
347         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
348             /* The user cancelled the operation, so end doing the close */
349             return FALSE;
350         }
351         SP_ACTIVE_DESKTOP->destroyWidget();
352     }
354     return TRUE;
357 /*
358  * Some day when the right-click menus are ready to start working
359  * smarter with the verbs, we'll need to change this NULL being
360  * sent to sp_action_perform to something useful, or set some kind
361  * of global "right-clicked position" variable for actions to
362  * investigate when they're called.
363  */
364 static void
365 sp_ui_menu_activate(void */*object*/, SPAction *action)
367     sp_action_perform(action, NULL);
370 static void
371 sp_ui_menu_select_action(void */*object*/, SPAction *action)
373     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
376 static void
377 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
379     action->view->tipsMessageContext()->clear();
382 static void
383 sp_ui_menu_select(gpointer object, gpointer tip)
385     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
386     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
389 static void
390 sp_ui_menu_deselect(gpointer object)
392     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
393     view->tipsMessageContext()->clear();
396 /**
397  * sp_ui_menuitem_add_icon
398  *
399  * Creates and attaches a scaled icon to the given menu item.
400  *
401  */
402 void
403 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
405     GtkWidget *icon;
407     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
408     gtk_widget_show(icon);
409     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
410 } // end of sp_ui_menu_add_icon
412 /**
413  * sp_ui_menu_append_item
414  *
415  * Appends a UI item with specific info for Inkscape/Sodipodi.
416  *
417  */
418 static GtkWidget *
419 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
420                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
421                         gpointer data, gboolean with_mnemonic = TRUE )
423     GtkWidget *item;
425     if (stock) {
426         item = gtk_image_menu_item_new_from_stock(stock, NULL);
427     } else if (label) {
428         item = (with_mnemonic)
429             ? gtk_image_menu_item_new_with_mnemonic(label) :
430             gtk_image_menu_item_new_with_label(label);
431     } else {
432         item = gtk_separator_menu_item_new();
433     }
435     gtk_widget_show(item);
437     if (callback) {
438         g_signal_connect(G_OBJECT(item), "activate", callback, data);
439     }
441     if (tip && view) {
442         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
443         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
444         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
445     }
447     gtk_menu_append(GTK_MENU(menu), item);
449     return item;
451 } // end of sp_ui_menu_append_item()
453 /**
454 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
455  */
456 static gchar const *
457 sp_key_name(guint keyval)
459     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
460        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
461     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
463     if      (!strcmp(n, "asciicircum"))  return "^";
464     else if (!strcmp(n, "parenleft"  ))  return "(";
465     else if (!strcmp(n, "parenright" ))  return ")";
466     else if (!strcmp(n, "plus"       ))  return "+";
467     else if (!strcmp(n, "minus"      ))  return "-";
468     else if (!strcmp(n, "asterisk"   ))  return "*";
469     else if (!strcmp(n, "KP_Multiply"))  return "*";
470     else if (!strcmp(n, "Delete"     ))  return "Del";
471     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
472     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
473     else if (!strcmp(n, "grave"      ))  return "`";
474     else if (!strcmp(n, "numbersign" ))  return "#";
475     else if (!strcmp(n, "bar"        ))  return "|";
476     else if (!strcmp(n, "slash"      ))  return "/";
477     else if (!strcmp(n, "exclam"     ))  return "!";
478     else if (!strcmp(n, "percent"    ))  return "%";
479     else return n;
483 /**
484  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
485  * \param c Points to a buffer at least 256 bytes long.
486  */
487 void
488 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
490     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
491      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
492      * Will probably need to change sp_shortcut_invoke callers.
493      *
494      * The existing gtk_label_new_with_mnemonic call can be replaced with
495      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
496      * gtk_label_set_text_with_mnemonic(lbl, str).
497      */
498     static GtkAccelLabelClass const &accel_lbl_cls
499         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
501     struct { unsigned test; char const *name; } const modifier_tbl[] = {
502         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
503         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
504         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
505     };
507     gchar *p = c;
508     gchar *end = p + 256;
510     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
511         if ((shortcut & modifier_tbl[i].test)
512             && (p < end))
513         {
514             p += g_snprintf(p, end - p, "%s%s",
515                             modifier_tbl[i].name,
516                             accel_lbl_cls.mod_separator);
517         }
518     }
519     if (p < end) {
520         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
521     }
522     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
525 void
526 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
528     SPAction     *action;
529     unsigned int shortcut;
530     gchar        *s;
531     gchar        key[256];
532     gchar        *atitle;
534     action = verb->get_action(NULL);
535     if (!action)
536         return;
538     atitle = sp_action_get_title(action);
540     s = g_stpcpy(c, atitle);
542     g_free(atitle);
544     shortcut = sp_shortcut_get_primary(verb);
545     if (shortcut) {
546         s = g_stpcpy(s, " (");
547         sp_ui_shortcut_string(shortcut, key);
548         s = g_stpcpy(s, key);
549         s = g_stpcpy(s, ")");
550     }
554 /**
555  * sp_ui_menu_append_item_from_verb
556  *
557  * Appends a custom menu UI from a verb.
558  *
559  */
561 static GtkWidget *
562 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
564     SPAction *action;
565     GtkWidget *item;
567     if (verb->get_code() == SP_VERB_NONE) {
569         item = gtk_separator_menu_item_new();
571     } else {
572         unsigned int shortcut;
574         action = verb->get_action(view);
576         if (!action) return NULL;
578         shortcut = sp_shortcut_get_primary(verb);
579         if (shortcut) {
580             gchar c[256];
581             sp_ui_shortcut_string(shortcut, c);
582             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
583             GtkWidget *const name_lbl = gtk_label_new("");
584             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
585             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
586             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
587             GtkWidget *const accel_lbl = gtk_label_new(c);
588             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
589             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
590             gtk_widget_show_all(hb);
591             if (radio) {
592                 item = gtk_radio_menu_item_new (group);
593             } else {
594                 item = gtk_image_menu_item_new();
595             }
596             gtk_container_add((GtkContainer *) item, hb);
597         } else {
598             if (radio) {
599                 item = gtk_radio_menu_item_new (group);
600             } else {
601                 item = gtk_image_menu_item_new ();
602             }
603             GtkWidget *const name_lbl = gtk_label_new("");
604             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
605             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
606             gtk_container_add((GtkContainer *) item, name_lbl);
607         }
609         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
610         if (!action->sensitive) {
611             gtk_widget_set_sensitive(item, FALSE);
612         }
614         if (action->image) {
615             sp_ui_menuitem_add_icon(item, action->image);
616         }
617         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
618         g_signal_connect( G_OBJECT(item), "activate",
619                           G_CALLBACK(sp_ui_menu_activate), action );
621         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
622         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
623     }
625     gtk_widget_show(item);
626     gtk_menu_append(GTK_MENU(menu), item);
628     return item;
630 } // end of sp_ui_menu_append_item_from_verb
633 static void
634 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
636     gchar const *pref = (gchar const *) user_data;
637     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
639     Glib::ustring pref_path;
640     if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
641         pref_path = "/focus/";
642     } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
643         pref_path = "/fullscreen/";
644     } else {
645         pref_path = "/window/";
646     }
647     pref_path += pref;
648     pref_path += "/state";
650     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
651     gboolean checked = gtk_check_menu_item_get_active(menuitem);
652     prefs->setBool(pref_path, checked);
654     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
657 static gboolean
658 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
660     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
662     gchar const *pref = (gchar const *) user_data;
663     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
665     Glib::ustring pref_path;
666     if ((static_cast<SPDesktop*>(view))->is_fullscreen()) {
667         pref_path = "/fullscreen/";
668     } else {
669         pref_path = "/window/";
670     }
671     pref_path += pref;
673     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
674     bool ison = prefs->getBool(pref_path + "/state", true);
676     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
677     gtk_check_menu_item_set_active(menuitem, ison);
678     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
680     return FALSE;
684 void
685 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
686                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
687                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
688                                        Inkscape::Verb *verb)
690     GtkWidget *item;
692     unsigned int shortcut = 0;
693     SPAction *action = NULL;
695     if (verb) {
696         shortcut = sp_shortcut_get_primary(verb);
697         action = verb->get_action(view);
698     }
700     if (verb && shortcut) {
701         gchar c[256];
702         sp_ui_shortcut_string(shortcut, c);
704         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
706         {
707             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
708             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
709             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
710         }
712         {
713             GtkWidget *l = gtk_label_new(c);
714             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
715             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
716         }
718         gtk_widget_show_all(hb);
720         item = gtk_check_menu_item_new();
721         gtk_container_add((GtkContainer *) item, hb);
722     } else {
723         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
724         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
725         item = gtk_check_menu_item_new();
726         gtk_container_add((GtkContainer *) item, l);
727     }
728 #if 0
729     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
730     if (!action->sensitive) {
731         gtk_widget_set_sensitive(item, FALSE);
732     }
733 #endif
734     gtk_widget_show(item);
736     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
738     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
740     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
741     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
743     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
744     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
747 static void
748 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
750     // dealing with the bizarre filename convention in Inkscape for now
751     gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
752     gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
753     gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
754     sp_file_open(utf8_fn, NULL);
755     g_free(utf8_fn);
756     g_free(local_fn);
757     g_free(uri);
760 static void
761 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
763     sp_file_new(uri);
766 void
767 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
769     std::list<gchar *> sources;
770     sources.push_back( profile_path("templates") ); // first try user's local dir
771     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
773     // Use this loop to iterate through a list of possible document locations.
774     while (!sources.empty()) {
775         gchar *dirname = sources.front();
777         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
778             GError *err = 0;
779             GDir *dir = g_dir_open(dirname, 0, &err);
781             if (dir) {
782                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
783                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
784                         continue; // skip non-svg files
786                     gchar *basename = g_path_get_basename(file);
787                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
788                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
790                     gchar const *filepath = g_build_filename(dirname, file, NULL);
791                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
792                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
793                     g_free(dupfile);
794                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
795                     g_free(filename);
797                     gtk_widget_show(item);
798                     // how does "filepath" ever get freed?
799                     g_signal_connect(G_OBJECT(item),
800                                      "activate",
801                                      G_CALLBACK(sp_file_new_from_template),
802                                      (gpointer) filepath);
804                     if (view) {
805                         // set null tip for now; later use a description from the template file
806                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
807                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
808                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
809                     }
811                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
812                 }
813                 g_dir_close(dir);
814             }
815         }
817         // toss the dirname
818         g_free(dirname);
819         sources.pop_front();
820     }
823 void
824 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
826     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
827     //                                       checkitem_toggled, checkitem_update, 0);
828     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
829                                            checkitem_toggled, checkitem_update, 0);
830     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
831                                            checkitem_toggled, checkitem_update, 0);
832     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
833                                            checkitem_toggled, checkitem_update, 0);
834     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
835                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
836     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
837                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
838     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
839                                            checkitem_toggled, checkitem_update, 0);
840     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
841                                            checkitem_toggled, checkitem_update, 0);
844 /** \brief  This function turns XML into a menu
845     \param  menus  This is the XML that defines the menu
846     \param  menu   Menu to be added to
847     \param  view   The View that this menu is being built for
849     This function is realitively simple as it just goes through the XML
850     and parses the individual elements.  In the case of a submenu, it
851     just calls itself recursively.  Because it is only reasonable to have
852     a couple of submenus, it is unlikely this will go more than two or
853     three times.
855     In the case of an unreconginzed verb, a menu item is made to identify
856     the verb that is missing, and display that.  The menu item is also made
857     insensitive.
858 */
859 void
860 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
862     if (menus == NULL) return;
863     if (menu == NULL)  return;
864     GSList *group = NULL;
866     for (Inkscape::XML::Node *menu_pntr = menus;
867          menu_pntr != NULL;
868          menu_pntr = menu_pntr->next()) {
869         if (!strcmp(menu_pntr->name(), "submenu")) {
870             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
871             GtkWidget *submenu = gtk_menu_new();
872             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
873             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
874             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
875             continue;
876         }
877         if (!strcmp(menu_pntr->name(), "verb")) {
878             gchar const *verb_name = menu_pntr->attribute("verb-id");
879             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
881             if (verb != NULL) {
882                 if (menu_pntr->attribute("radio") != NULL) {
883                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
884                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
885                     if (menu_pntr->attribute("default") != NULL) {
886                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
887                     }
888                 } else {
889                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
890                     group = NULL;
891                 }
892             } else {
893                 gchar string[120];
894                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
895                 string[119] = '\0'; /* may not be terminated */
896                 GtkWidget *item = gtk_menu_item_new_with_label(string);
897                 gtk_widget_set_sensitive(item, false);
898                 gtk_widget_show(item);
899                 gtk_menu_append(GTK_MENU(menu), item);
900             }
901             continue;
902         }
903         if (!strcmp(menu_pntr->name(), "separator")
904                 // This was spelt wrong in the original version
905                 // and so this is for backward compatibility.  It can
906                 // probably be dropped after the 0.44 release.
907              || !strcmp(menu_pntr->name(), "seperator")) {
908             GtkWidget *item = gtk_separator_menu_item_new();
909             gtk_widget_show(item);
910             gtk_menu_append(GTK_MENU(menu), item);
911             continue;
912         }
913         if (!strcmp(menu_pntr->name(), "template-list")) {
914             sp_menu_append_new_templates(menu, view);
915             continue;
916         }
917         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
918             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
920             // create recent files menu
921             int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
922             GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
923             gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
924             // sort most recently used documents first to preserve previous behavior
925             gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
926             g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
927             
928             // add filter to only open files added by Inkscape
929             GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
930             gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
931             gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
933             GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
934             gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
935             
936             gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
937             continue;
938         }
939         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
940             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
941             continue;
942         }
943     }
946 /** \brief  Build the main tool bar
947     \param  view  View to build the bar for
949     Currently the main tool bar is built as a dynamic XML menu using
950     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
951     pass it to get items attached to it.
952 */
953 GtkWidget *
954 sp_ui_main_menubar(Inkscape::UI::View::View *view)
956     GtkWidget *mbar = gtk_menu_bar_new();
958 #ifdef GDK_WINDOWING_QUARTZ
959         ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
960 #endif
962     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
964 #ifdef GDK_WINDOWING_QUARTZ
965         return NULL;
966 #else
967     return mbar;
968 #endif
971 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
972     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
975 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
976     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
977     sp_desktop_selection(desktop)->clear();
980 GtkWidget *
981 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
983     GtkWidget *m;
984     SPDesktop *dt;
986     dt = static_cast<SPDesktop*>(view);
988     m = gtk_menu_new();
990     /* Undo and Redo */
991     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
992     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
994     /* Separator */
995     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
997     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
998     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
999     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1001     /* Separator */
1002     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1004     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1005     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1007     /* Item menu */
1008     if (item) {
1009         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1010         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1011     }
1013     /* layer menu */
1014     SPGroup *group=NULL;
1015     if (item) {
1016         if (SP_IS_GROUP(item)) {
1017             group = SP_GROUP(item);
1018         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1019             group = SP_GROUP(SP_OBJECT_PARENT(item));
1020         }
1021     }
1023     if (( group && group != dt->currentLayer() ) ||
1024         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1025         /* Separator */
1026         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1027     }
1029     if ( group && group != dt->currentLayer() ) {
1030         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1031         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1032         GtkWidget *w = gtk_menu_item_new_with_label(label);
1033         g_free(label);
1034         g_object_set_data(G_OBJECT(w), "group", group);
1035         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1036         gtk_widget_show(w);
1037         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1038     }
1040     if ( dt->currentLayer() != dt->currentRoot() ) {
1041         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1042             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1043             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1044             gtk_widget_show(w);
1045             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1047         }
1048     }
1050     return m;
1053 /* Drag and Drop */
1054 void
1055 sp_ui_drag_data_received(GtkWidget *widget,
1056                          GdkDragContext *drag_context,
1057                          gint x, gint y,
1058                          GtkSelectionData *data,
1059                          guint info,
1060                          guint /*event_time*/,
1061                          gpointer /*user_data*/)
1063     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1064     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1066     switch (info) {
1067 #if ENABLE_MAGIC_COLORS
1068         case APP_X_INKY_COLOR:
1069         {
1070             int destX = 0;
1071             int destY = 0;
1072             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1073             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1075             SPItem *item = desktop->item_at_point( where, true );
1076             if ( item )
1077             {
1078                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1080                 if ( data->length >= 8 ) {
1081                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1083                     gchar c[64] = {0};
1084                     // Careful about endian issues.
1085                     guint16* dataVals = (guint16*)data->data;
1086                     sp_svg_write_color( c, sizeof(c),
1087                                         SP_RGBA32_U_COMPOSE(
1088                                             0x0ff & (dataVals[0] >> 8),
1089                                             0x0ff & (dataVals[1] >> 8),
1090                                             0x0ff & (dataVals[2] >> 8),
1091                                             0xff // can't have transparency in the color itself
1092                                             //0x0ff & (data->data[3] >> 8),
1093                                             ));
1094                     SPCSSAttr *css = sp_repr_css_attr_new();
1095                     bool updatePerformed = false;
1097                     if ( data->length > 14 ) {
1098                         int flags = dataVals[4];
1100                         // piggie-backed palette entry info
1101                         int index = dataVals[5];
1102                         Glib::ustring palName;
1103                         for ( int i = 0; i < dataVals[6]; i++ ) {
1104                             palName += (gunichar)dataVals[7+i];
1105                         }
1107                         // Now hook in a magic tag of some sort.
1108                         if ( !palName.empty() && (flags & 1) ) {
1109                             gchar* str = g_strdup_printf("%d|", index);
1110                             palName.insert( 0, str );
1111                             g_free(str);
1112                             str = 0;
1114                             sp_object_setAttribute( SP_OBJECT(item),
1115                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1116                                                     palName.c_str(),
1117                                                     false );
1118                             item->updateRepr();
1120                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1121                             updatePerformed = true;
1122                         }
1123                     }
1125                     if ( !updatePerformed ) {
1126                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1127                     }
1129                     sp_desktop_apply_css_recursive( item, css, true );
1130                     item->updateRepr();
1132                     sp_document_done( doc , SP_VERB_NONE, 
1133                                       _("Drop color"));
1135                     if ( srgbProf ) {
1136                         cmsCloseProfile( srgbProf );
1137                     }
1138                 }
1139             }
1140         }
1141         break;
1142 #endif // ENABLE_MAGIC_COLORS
1144         case APP_X_COLOR:
1145         {
1146             int destX = 0;
1147             int destY = 0;
1148             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1149             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1150             NR::Point const button_dt(desktop->w2d(where));
1151             NR::Point const button_doc(desktop->dt2doc(button_dt));
1153             if ( data->length == 8 ) {
1154                 gchar c[64] = {0};
1155                 // Careful about endian issues.
1156                 guint16* dataVals = (guint16*)data->data;
1157                 sp_svg_write_color( c, 64,
1158                                     SP_RGBA32_U_COMPOSE(
1159                                         0x0ff & (dataVals[0] >> 8),
1160                                         0x0ff & (dataVals[1] >> 8),
1161                                         0x0ff & (dataVals[2] >> 8),
1162                                         0xff // can't have transparency in the color itself
1163                                         //0x0ff & (data->data[3] >> 8),
1164                                         ));
1166                 SPItem *item = desktop->item_at_point( where, true );
1168                 bool consumed = false;
1169                 if (desktop->event_context && desktop->event_context->get_drag()) {
1170                     consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1171                     if (consumed) {
1172                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1173                         desktop->event_context->get_drag()->updateDraggers();
1174                     }
1175                 }
1177                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1178                 //    consumed = sp_text_context_drop_color(c, button_doc);
1179                 //    if (consumed) {
1180                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1181                 //    }
1182                 //}
1184                 if (!consumed && item) {
1185                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1186                     if (fillnotstroke && 
1187                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1188                         Path *livarot_path = Path_for_item(item, true, true);
1189                         livarot_path->ConvertWithBackData(0.04);
1191                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1192                         if (position) {
1193                             NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1194                             NR::Point delta = nearest - button_doc;
1195                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1196                             delta = desktop->d2w(delta);
1197                             double stroke_tolerance =
1198                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1199                                   desktop->current_zoom() *
1200                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1201                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1202                                   : 0.0)
1203                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100); 
1205                             if (NR::L2 (delta) < stroke_tolerance) {
1206                                 fillnotstroke = false;
1207                             }
1208                         }
1209                         delete livarot_path;
1210                     }
1212                     SPCSSAttr *css = sp_repr_css_attr_new();
1213                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1215                     sp_desktop_apply_css_recursive( item, css, true );
1216                     item->updateRepr();
1218                     sp_document_done( doc , SP_VERB_NONE, 
1219                                       _("Drop color"));
1220                 }
1221             }
1222         }
1223         break;
1225         case SVG_DATA:
1226         case SVG_XML_DATA: {
1227             gchar *svgdata = (gchar *)data->data;
1229             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1231             if (rnewdoc == NULL) {
1232                 sp_ui_error_dialog(_("Could not parse SVG data"));
1233                 return;
1234             }
1236             Inkscape::XML::Node *repr = rnewdoc->root();
1237             gchar const *style = repr->attribute("style");
1239             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1240             newgroup->setAttribute("style", style);
1242             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1243             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1244                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1245                 newgroup->appendChild(newchild);
1246             }
1248             Inkscape::GC::release(rnewdoc);
1250             // Add it to the current layer
1252             // Greg's edits to add intelligent positioning of svg drops
1253             SPObject *new_obj = NULL;
1254             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1256             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1257             selection->set(SP_ITEM(new_obj));
1258             // To move the imported object, we must temporarily set the "transform pattern with
1259             // object" option.
1260             {
1261                 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1262                 bool const saved_pref = prefs->getBool("/options/transform/pattern", true);
1263                 prefs->setBool("/options/transform/pattern", true);
1264                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1265                 Geom::OptRect sel_bbox = selection->bounds();
1266                 if (sel_bbox) {
1267                     Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1268                     sp_selection_move_relative(selection, m);
1269                 }
1270                 prefs->setBool("/options/transform/pattern", saved_pref);
1271             }
1273             Inkscape::GC::release(newgroup);
1274             sp_document_done(doc, SP_VERB_NONE, 
1275                              _("Drop SVG"));
1276             break;
1277         }
1279         case URI_LIST: {
1280             gchar *uri = (gchar *)data->data;
1281             sp_ui_import_files(uri);
1282             break;
1283         }
1285         case PNG_DATA:
1286         case JPEG_DATA:
1287         case IMAGE_DATA: {
1288             char tmp[1024];
1290             StringOutputStream outs;
1291             Base64OutputStream b64out(outs);
1292             b64out.setColumnWidth(0);
1294             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1296             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1298             for ( int i = 0; i < data->length; i++ ) {
1299                 b64out.put( data->data[i] );
1300             }
1301             b64out.close();
1304             Glib::ustring str = outs.getString();
1306             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1307             str.insert( 0, tmp );
1308             newImage->setAttribute("xlink:href", str.c_str());
1310             GError *error = NULL;
1311             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1312             if ( loader ) {
1313                 error = NULL;
1314                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1315                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1316                     if ( pbuf ) {
1317                         int width = gdk_pixbuf_get_width(pbuf);
1318                         int height = gdk_pixbuf_get_height(pbuf);
1319                         snprintf( tmp, sizeof(tmp), "%d", width );
1320                         newImage->setAttribute("width", tmp);
1322                         snprintf( tmp, sizeof(tmp), "%d", height );
1323                         newImage->setAttribute("height", tmp);
1324                     }
1325                 }
1326             }
1328             // Add it to the current layer
1329             desktop->currentLayer()->appendChildRepr(newImage);
1331             Inkscape::GC::release(newImage);
1332             sp_document_done( doc , SP_VERB_NONE, 
1333                               _("Drop bitmap image"));
1334             break;
1335         }
1336     }
1339 #include "gradient-context.h"
1341 void sp_ui_drag_motion( GtkWidget */*widget*/,
1342                         GdkDragContext */*drag_context*/,
1343                         gint /*x*/, gint /*y*/,
1344                         GtkSelectionData */*data*/,
1345                         guint /*info*/,
1346                         guint /*event_time*/,
1347                         gpointer /*user_data*/)
1349 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1350 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1353 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1356 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1357                               GdkDragContext */*drag_context*/,
1358                               guint /*event_time*/,
1359                               gpointer /*user_data*/ )
1361 //     g_message("drag-n-drop leave                at %d", event_time);
1364 static void
1365 sp_ui_import_files(gchar *buffer)
1367     GList *list = gnome_uri_list_extract_filenames(buffer);
1368     if (!list)
1369         return;
1370     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1371     g_list_foreach(list, (GFunc) g_free, NULL);
1372     g_list_free(list);
1375 static void
1376 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1378     if (filename) {
1379         if (strlen((char const *)filename) > 2)
1380             sp_ui_import_one_file((char const *)filename);
1381     }
1384 static void
1385 sp_ui_import_one_file(char const *filename)
1387     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1388     if (!doc) return;
1390     if (filename == NULL) return;
1392     // Pass off to common implementation
1393     // TODO might need to get the proper type of Inkscape::Extension::Extension
1394     file_import( doc, filename, NULL );
1397 void
1398 sp_ui_error_dialog(gchar const *message)
1400     GtkWidget *dlg;
1401     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1403     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1404                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1405     sp_transientize(dlg);
1406     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1407     gtk_dialog_run(GTK_DIALOG(dlg));
1408     gtk_widget_destroy(dlg);
1409     g_free(safeMsg);
1412 bool
1413 sp_ui_overwrite_file(gchar const *filename)
1415     bool return_value = FALSE;
1417     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1418         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1419         gchar* baseName = g_path_get_basename( filename );
1420         gchar* dirName = g_path_get_dirname( filename );
1421         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1422                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1423                                                                 GTK_MESSAGE_QUESTION,
1424                                                                 GTK_BUTTONS_NONE,
1425                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1426                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1427                                                                 baseName,
1428                                                                 dirName
1429             );
1430         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1431                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1432                                 _("Replace"), GTK_RESPONSE_YES,
1433                                 NULL );
1434         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1436         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1437             return_value = TRUE;
1438         } else {
1439             return_value = FALSE;
1440         }
1441         gtk_widget_destroy(dialog);
1442         g_free( baseName );
1443         g_free( dirName );
1444     } else {
1445         return_value = TRUE;
1446     }
1448     return return_value;
1451 static void
1452 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1454     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1457 static void
1458 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1460     void *child = GTK_BIN (data)->child;
1461     //child is either
1462     //- a GtkHBox, whose first child is a label displaying name if the menu
1463     //item has an accel key
1464     //- a GtkLabel if the menu has no accel key
1465     if (GTK_IS_LABEL(child)) {
1466         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1467     } else if (GTK_IS_HBOX(child)) {
1468         gtk_label_set_markup_with_mnemonic(
1469         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
1470         name.c_str());
1471     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1472     //a menu item in yet another way...
1476 /*
1477   Local Variables:
1478   mode:c++
1479   c-file-style:"stroustrup"
1480   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1481   indent-tabs-mode:nil
1482   fill-column:99
1483   End:
1484 */
1485 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :