Code

remove temporary prefs hack
[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>
25 #include "inkscape-private.h"
26 #include "extension/effect.h"
27 #include "widgets/icon.h"
28 #include "preferences.h"
29 #include "path-prefix.h"
30 #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-gradient-fns.h"
44 #include "sp-gradient.h"
45 #include "sp-flowtext.h"
46 #include "sp-namedview.h"
47 #include "ui/view/view.h"
48 #include "helper/action.h"
49 #include "helper/gnome-utils.h"
50 #include "helper/window.h"
51 #include "io/sys.h"
52 #include "dialogs/dialog-events.h"
53 #include "message-context.h"
55 // Added for color drag-n-drop
56 #if ENABLE_LCMS
57 #include "lcms.h"
58 #endif // ENABLE_LCMS
59 #include "display/sp-canvas.h"
60 #include "color.h"
61 #include "svg/svg-color.h"
62 #include "desktop-style.h"
63 #include "style.h"
64 #include "event-context.h"
65 #include "gradient-drag.h"
66 #include "widgets/ege-paint-def.h"
68 // Include Mac OS X menu synchronization on native OSX build
69 #ifdef GDK_WINDOWING_QUARTZ
70 #include "ige-mac-menu.h"
71 #endif
73 /* Drag and Drop */
74 typedef enum {
75     URI_LIST,
76     SVG_XML_DATA,
77     SVG_DATA,
78     PNG_DATA,
79     JPEG_DATA,
80     IMAGE_DATA,
81     APP_X_INKY_COLOR,
82     APP_X_COLOR,
83     APP_OSWB_COLOR,
84 } ui_drop_target_info;
86 static GtkTargetEntry ui_drop_target_entries [] = {
87     {(gchar *)"text/uri-list",                0, URI_LIST        },
88     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
89     {(gchar *)"image/svg",                    0, SVG_DATA        },
90     {(gchar *)"image/png",                    0, PNG_DATA        },
91     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
92 #if ENABLE_MAGIC_COLORS
93     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
94 #endif // ENABLE_MAGIC_COLORS
95     {(gchar *)"application/x-oswb-color",     0, APP_OSWB_COLOR  },
96     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
97 };
99 static GtkTargetEntry *completeDropTargets = 0;
100 static int completeDropTargetsCount = 0;
102 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
103 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
104 static void sp_ui_import_files(gchar *buffer);
105 static void sp_ui_import_one_file(char const *filename);
106 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
107 static void sp_ui_drag_data_received(GtkWidget *widget,
108                                      GdkDragContext *drag_context,
109                                      gint x, gint y,
110                                      GtkSelectionData *data,
111                                      guint info,
112                                      guint event_time,
113                                      gpointer user_data);
114 static void sp_ui_drag_motion( GtkWidget *widget,
115                                GdkDragContext *drag_context,
116                                gint x, gint y,
117                                GtkSelectionData *data,
118                                guint info,
119                                guint event_time,
120                                gpointer user_data );
121 static void sp_ui_drag_leave( GtkWidget *widget,
122                               GdkDragContext *drag_context,
123                               guint event_time,
124                               gpointer user_data );
125 static void sp_ui_menu_item_set_sensitive(SPAction *action,
126                                           unsigned int sensitive,
127                                           void *data);
128 static void sp_ui_menu_item_set_name(SPAction *action,
129                                      Glib::ustring name,
130                                      void *data);
131 static void sp_recent_open(GtkRecentChooser *, gpointer);
133 SPActionEventVector menu_item_event_vector = {
134     {NULL},
135     NULL,
136     NULL, /* set_active */
137     sp_ui_menu_item_set_sensitive, /* set_sensitive */
138     NULL, /* set_shortcut */
139     sp_ui_menu_item_set_name /* set_name */
140 };
142 static const int MIN_ONSCREEN_DISTANCE = 50;
144 void
145 sp_create_window(SPViewWidget *vw, gboolean editable)
147     g_return_if_fail(vw != NULL);
148     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
150     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
152     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
153     gtk_widget_show(GTK_WIDGET(vw));
155     if (editable) {
156         g_object_set_data(G_OBJECT(vw), "window", win);
158         SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
159         SPDesktop* desktop = desktop_widget->desktop;
161         desktop_widget->window = win;
163         win->set_data("desktop", desktop);
164         win->set_data("desktopwidget", desktop_widget);
166         win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
167         win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
168         win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
170         Inkscape::Preferences *prefs = Inkscape::Preferences::get();
171         gint prefs_geometry =
172             (2==prefs->getInt("/options/savewindowgeometry/value", 0));
173         if (prefs_geometry) {
174             gint pw = prefs->getInt("/desktop/geometry/width", -1);
175             gint ph = prefs->getInt("/desktop/geometry/height", -1);
176             gint px = prefs->getInt("/desktop/geometry/x", -1);
177             gint py = prefs->getInt("/desktop/geometry/y", -1);
178             gint full = prefs->getBool("/desktop/geometry/fullscreen");
179             gint maxed = prefs->getBool("/desktop/geometry/maximized");
180             if (pw>0 && ph>0) {
181                 gint w = MIN(gdk_screen_width(), pw);
182                 gint h = MIN(gdk_screen_height(), ph);
183                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
184                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
185                 if (w>0 && h>0 && x>0 && y>0) {
186                     x = MIN(gdk_screen_width() - w, x);
187                     y = MIN(gdk_screen_height() - h, y);
188                 }
189                 if (w>0 && h>0) {
190                     desktop->setWindowSize(w, h);
191                 }
193                 // Only restore xy for the first window so subsequent windows don't overlap exactly
194                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
195                 // other desktops?)
197                 // Empirically it seems that active_desktop==this desktop only the first time a
198                 // desktop is created.
199                 if (x>0 && y>0) {
200                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
201                     if (active_desktop == desktop || active_desktop==NULL) {
202                         desktop->setWindowPosition(Geom::Point(x, y));
203                     }
204                 }
205             }
206             if (maxed) {
207                 win->maximize();
208             }
209             if (full) {
210                 win->fullscreen();
211             }
212         }
214     } else {
215         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
216     }
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     Glib::ustring pref_path;
635     if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
636         pref_path = "/focus/";
637     } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
638         pref_path = "/fullscreen/";
639     } else {
640         pref_path = "/window/";
641     }
642     pref_path += pref;
643     pref_path += "/state";
645     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
646     gboolean checked = gtk_check_menu_item_get_active(menuitem);
647     prefs->setBool(pref_path, checked);
649     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
652 static gboolean
653 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
655     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
657     gchar const *pref = (gchar const *) user_data;
658     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
660     Glib::ustring pref_path;
661     if ((static_cast<SPDesktop*>(view))->is_fullscreen()) {
662         pref_path = "/fullscreen/";
663     } else {
664         pref_path = "/window/";
665     }
666     pref_path += pref;
668     Inkscape::Preferences *prefs = Inkscape::Preferences::get();
669     bool ison = prefs->getBool(pref_path + "/state", true);
671     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
672     gtk_check_menu_item_set_active(menuitem, ison);
673     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
675     return FALSE;
679 void
680 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
681                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
682                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
683                                        Inkscape::Verb *verb)
685     GtkWidget *item;
687     unsigned int shortcut = 0;
688     SPAction *action = NULL;
690     if (verb) {
691         shortcut = sp_shortcut_get_primary(verb);
692         action = verb->get_action(view);
693     }
695     if (verb && shortcut) {
696         gchar c[256];
697         sp_ui_shortcut_string(shortcut, c);
699         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
701         {
702             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
703             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
704             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
705         }
707         {
708             GtkWidget *l = gtk_label_new(c);
709             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
710             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
711         }
713         gtk_widget_show_all(hb);
715         item = gtk_check_menu_item_new();
716         gtk_container_add((GtkContainer *) item, hb);
717     } else {
718         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
719         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
720         item = gtk_check_menu_item_new();
721         gtk_container_add((GtkContainer *) item, l);
722     }
723 #if 0
724     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
725     if (!action->sensitive) {
726         gtk_widget_set_sensitive(item, FALSE);
727     }
728 #endif
729     gtk_widget_show(item);
731     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
733     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
735     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
736     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
738     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
739     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
742 static void
743 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
745     // dealing with the bizarre filename convention in Inkscape for now
746     gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
747     gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
748     gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
749     sp_file_open(utf8_fn, NULL);
750     g_free(utf8_fn);
751     g_free(local_fn);
752     g_free(uri);
755 static void
756 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
758     sp_file_new(uri);
761 void
762 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
764     std::list<gchar *> sources;
765     sources.push_back( profile_path("templates") ); // first try user's local dir
766     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
768     // Use this loop to iterate through a list of possible document locations.
769     while (!sources.empty()) {
770         gchar *dirname = sources.front();
772         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
773             GError *err = 0;
774             GDir *dir = g_dir_open(dirname, 0, &err);
776             if (dir) {
777                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
778                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
779                         continue; // skip non-svg files
781                     gchar *basename = g_path_get_basename(file);
782                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
783                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
785                     gchar const *filepath = g_build_filename(dirname, file, NULL);
786                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
787                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
788                     g_free(dupfile);
789                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
790                     g_free(filename);
792                     gtk_widget_show(item);
793                     // how does "filepath" ever get freed?
794                     g_signal_connect(G_OBJECT(item),
795                                      "activate",
796                                      G_CALLBACK(sp_file_new_from_template),
797                                      (gpointer) filepath);
799                     if (view) {
800                         // set null tip for now; later use a description from the template file
801                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
802                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
803                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
804                     }
806                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
807                 }
808                 g_dir_close(dir);
809             }
810         }
812         // toss the dirname
813         g_free(dirname);
814         sources.pop_front();
815     }
818 void
819 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
821     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
822     //                                       checkitem_toggled, checkitem_update, 0);
823     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
824                                            checkitem_toggled, checkitem_update, 0);
825     sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
826                                            checkitem_toggled, checkitem_update, 0);
827     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
828                                            checkitem_toggled, checkitem_update, 0);
829     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
830                                            checkitem_toggled, checkitem_update, 0);
831     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
832                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
833     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
834                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
835     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
836                                            checkitem_toggled, checkitem_update, 0);
837     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
838                                            checkitem_toggled, checkitem_update, 0);
841 /** @brief Observer that updates the recent list's max document count */
842 class MaxRecentObserver : public Inkscape::Preferences::Observer {
843 public:
844     MaxRecentObserver(GtkWidget *recent_menu) :
845         Observer("/options/maxrecentdocuments/value"),
846         _rm(recent_menu)
847     {}
848     virtual void notify(Inkscape::Preferences::Entry const &e) {
849         gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
850         // hack: the recent menu doesn't repopulate after changing the limit, so we force it
851         g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
852     }
853 private:
854     GtkWidget *_rm;
855 };
857 /** \brief  This function turns XML into a menu
858     \param  menus  This is the XML that defines the menu
859     \param  menu   Menu to be added to
860     \param  view   The View that this menu is being built for
862     This function is realitively simple as it just goes through the XML
863     and parses the individual elements.  In the case of a submenu, it
864     just calls itself recursively.  Because it is only reasonable to have
865     a couple of submenus, it is unlikely this will go more than two or
866     three times.
868     In the case of an unreconginzed verb, a menu item is made to identify
869     the verb that is missing, and display that.  The menu item is also made
870     insensitive.
871 */
872 void
873 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
875     if (menus == NULL) return;
876     if (menu == NULL)  return;
877     GSList *group = NULL;
879     for (Inkscape::XML::Node *menu_pntr = menus;
880          menu_pntr != NULL;
881          menu_pntr = menu_pntr->next()) {
882         if (!strcmp(menu_pntr->name(), "submenu")) {
883             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
884             GtkWidget *submenu = gtk_menu_new();
885             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
886             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
887             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
888             continue;
889         }
890         if (!strcmp(menu_pntr->name(), "verb")) {
891             gchar const *verb_name = menu_pntr->attribute("verb-id");
892             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
894             if (verb != NULL) {
895                 if (menu_pntr->attribute("radio") != NULL) {
896                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
897                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
898                     if (menu_pntr->attribute("default") != NULL) {
899                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
900                     }
901                 } else {
902                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
903                     group = NULL;
904                 }
905             } else {
906                 gchar string[120];
907                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
908                 string[119] = '\0'; /* may not be terminated */
909                 GtkWidget *item = gtk_menu_item_new_with_label(string);
910                 gtk_widget_set_sensitive(item, false);
911                 gtk_widget_show(item);
912                 gtk_menu_append(GTK_MENU(menu), item);
913             }
914             continue;
915         }
916         if (!strcmp(menu_pntr->name(), "separator")
917                 // This was spelt wrong in the original version
918                 // and so this is for backward compatibility.  It can
919                 // probably be dropped after the 0.44 release.
920              || !strcmp(menu_pntr->name(), "seperator")) {
921             GtkWidget *item = gtk_separator_menu_item_new();
922             gtk_widget_show(item);
923             gtk_menu_append(GTK_MENU(menu), item);
924             continue;
925         }
926         if (!strcmp(menu_pntr->name(), "template-list")) {
927             sp_menu_append_new_templates(menu, view);
928             continue;
929         }
930         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
931             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
933             // create recent files menu
934             int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
935             GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
936             gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
937             // sort most recently used documents first to preserve previous behavior
938             gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
939             g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
941             // add filter to only open files added by Inkscape
942             GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
943             gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
944             gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
946             GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
947             gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
949             gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
950             // this will just sit and update the list's item count
951             static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
952             prefs->addObserver(*mro);
953             continue;
954         }
955         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
956             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
957             continue;
958         }
959     }
962 /** \brief  Build the main tool bar
963     \param  view  View to build the bar for
965     Currently the main tool bar is built as a dynamic XML menu using
966     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
967     pass it to get items attached to it.
968 */
969 GtkWidget *
970 sp_ui_main_menubar(Inkscape::UI::View::View *view)
972     GtkWidget *mbar = gtk_menu_bar_new();
974 #ifdef GDK_WINDOWING_QUARTZ
975     ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
976 #endif
978     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
980 #ifdef GDK_WINDOWING_QUARTZ
981     return NULL;
982 #else
983     return mbar;
984 #endif
987 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
988     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
991 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
992     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
993     sp_desktop_selection(desktop)->clear();
996 GtkWidget *
997 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
999     GtkWidget *m;
1000     SPDesktop *dt;
1002     dt = static_cast<SPDesktop*>(view);
1004     m = gtk_menu_new();
1006     /* Undo and Redo */
1007     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1008     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1010     /* Separator */
1011     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1013     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1014     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1015     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1017     /* Separator */
1018     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1020     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1021     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1023     /* Item menu */
1024     if (item) {
1025         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1026         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1027     }
1029     /* layer menu */
1030     SPGroup *group=NULL;
1031     if (item) {
1032         if (SP_IS_GROUP(item)) {
1033             group = SP_GROUP(item);
1034         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1035             group = SP_GROUP(SP_OBJECT_PARENT(item));
1036         }
1037     }
1039     if (( group && group != dt->currentLayer() ) ||
1040         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1041         /* Separator */
1042         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1043     }
1045     if ( group && group != dt->currentLayer() ) {
1046         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1047         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1048         GtkWidget *w = gtk_menu_item_new_with_label(label);
1049         g_free(label);
1050         g_object_set_data(G_OBJECT(w), "group", group);
1051         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1052         gtk_widget_show(w);
1053         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1054     }
1056     if ( dt->currentLayer() != dt->currentRoot() ) {
1057         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1058             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1059             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1060             gtk_widget_show(w);
1061             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1063         }
1064     }
1066     return m;
1069 /* Drag and Drop */
1070 void
1071 sp_ui_drag_data_received(GtkWidget *widget,
1072                          GdkDragContext *drag_context,
1073                          gint x, gint y,
1074                          GtkSelectionData *data,
1075                          guint info,
1076                          guint /*event_time*/,
1077                          gpointer /*user_data*/)
1079     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1080     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1082     switch (info) {
1083 #if ENABLE_MAGIC_COLORS
1084         case APP_X_INKY_COLOR:
1085         {
1086             int destX = 0;
1087             int destY = 0;
1088             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1089             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1091             SPItem *item = desktop->item_at_point( where, true );
1092             if ( item )
1093             {
1094                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1096                 if ( data->length >= 8 ) {
1097                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1099                     gchar c[64] = {0};
1100                     // Careful about endian issues.
1101                     guint16* dataVals = (guint16*)data->data;
1102                     sp_svg_write_color( c, sizeof(c),
1103                                         SP_RGBA32_U_COMPOSE(
1104                                             0x0ff & (dataVals[0] >> 8),
1105                                             0x0ff & (dataVals[1] >> 8),
1106                                             0x0ff & (dataVals[2] >> 8),
1107                                             0xff // can't have transparency in the color itself
1108                                             //0x0ff & (data->data[3] >> 8),
1109                                             ));
1110                     SPCSSAttr *css = sp_repr_css_attr_new();
1111                     bool updatePerformed = false;
1113                     if ( data->length > 14 ) {
1114                         int flags = dataVals[4];
1116                         // piggie-backed palette entry info
1117                         int index = dataVals[5];
1118                         Glib::ustring palName;
1119                         for ( int i = 0; i < dataVals[6]; i++ ) {
1120                             palName += (gunichar)dataVals[7+i];
1121                         }
1123                         // Now hook in a magic tag of some sort.
1124                         if ( !palName.empty() && (flags & 1) ) {
1125                             gchar* str = g_strdup_printf("%d|", index);
1126                             palName.insert( 0, str );
1127                             g_free(str);
1128                             str = 0;
1130                             sp_object_setAttribute( SP_OBJECT(item),
1131                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1132                                                     palName.c_str(),
1133                                                     false );
1134                             item->updateRepr();
1136                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1137                             updatePerformed = true;
1138                         }
1139                     }
1141                     if ( !updatePerformed ) {
1142                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1143                     }
1145                     sp_desktop_apply_css_recursive( item, css, true );
1146                     item->updateRepr();
1148                     sp_document_done( doc , SP_VERB_NONE,
1149                                       _("Drop color"));
1151                     if ( srgbProf ) {
1152                         cmsCloseProfile( srgbProf );
1153                     }
1154                 }
1155             }
1156         }
1157         break;
1158 #endif // ENABLE_MAGIC_COLORS
1160         case APP_X_COLOR:
1161         {
1162             int destX = 0;
1163             int destY = 0;
1164             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1165             Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1166             Geom::Point const button_dt(desktop->w2d(where));
1167             Geom::Point const button_doc(desktop->dt2doc(button_dt));
1169             if ( data->length == 8 ) {
1170                 gchar colorspec[64] = {0};
1171                 // Careful about endian issues.
1172                 guint16* dataVals = (guint16*)data->data;
1173                 sp_svg_write_color( colorspec, sizeof(colorspec),
1174                                     SP_RGBA32_U_COMPOSE(
1175                                         0x0ff & (dataVals[0] >> 8),
1176                                         0x0ff & (dataVals[1] >> 8),
1177                                         0x0ff & (dataVals[2] >> 8),
1178                                         0xff // can't have transparency in the color itself
1179                                         //0x0ff & (data->data[3] >> 8),
1180                                         ));
1182                 SPItem *item = desktop->item_at_point( where, true );
1184                 bool consumed = false;
1185                 if (desktop->event_context && desktop->event_context->get_drag()) {
1186                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
1187                     if (consumed) {
1188                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1189                         desktop->event_context->get_drag()->updateDraggers();
1190                     }
1191                 }
1193                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1194                 //    consumed = sp_text_context_drop_color(c, button_doc);
1195                 //    if (consumed) {
1196                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1197                 //    }
1198                 //}
1200                 if (!consumed && item) {
1201                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1202                     if (fillnotstroke &&
1203                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1204                         Path *livarot_path = Path_for_item(item, true, true);
1205                         livarot_path->ConvertWithBackData(0.04);
1207                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1208                         if (position) {
1209                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1210                             Geom::Point delta = nearest - button_doc;
1211                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1212                             delta = desktop->d2w(delta);
1213                             double stroke_tolerance =
1214                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1215                                   desktop->current_zoom() *
1216                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1217                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1218                                   : 0.0)
1219                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1221                             if (Geom::L2 (delta) < stroke_tolerance) {
1222                                 fillnotstroke = false;
1223                             }
1224                         }
1225                         delete livarot_path;
1226                     }
1228                     SPCSSAttr *css = sp_repr_css_attr_new();
1229                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
1231                     sp_desktop_apply_css_recursive( item, css, true );
1232                     item->updateRepr();
1234                     sp_document_done( doc , SP_VERB_NONE,
1235                                       _("Drop color"));
1236                 }
1237             }
1238         }
1239         break;
1241         case APP_OSWB_COLOR:
1242         {
1243             bool worked = false;
1244             Glib::ustring colorspec;
1245             if ( data->format == 8 ) {
1246                 ege::PaintDef color;
1247                 worked = color.fromMIMEData("application/x-oswb-color",
1248                                             reinterpret_cast<char*>(data->data),
1249                                             data->length,
1250                                             data->format);
1251                 if ( worked ) {
1252                     if ( color.getType() == ege::PaintDef::CLEAR ) {
1253                         colorspec = ""; // TODO check if this is sufficient
1254                     } else if ( color.getType() == ege::PaintDef::NONE ) {
1255                         colorspec = "none";
1256                     } else {
1257                         unsigned int r = color.getR();
1258                         unsigned int g = color.getG();
1259                         unsigned int b = color.getB();
1261                         SPGradient* matches = 0;
1262                         const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
1263                         for (const GSList *item = gradients; item; item = item->next) {
1264                             SPGradient* grad = SP_GRADIENT(item->data);
1265                             if ( color.descr == grad->id ) {
1266                                 if ( grad->has_stops ) {
1267                                     matches = grad;
1268                                     break;
1269                                 }
1270                             }
1271                         }
1272                         if (matches) {
1273                             colorspec = "url(#";
1274                             colorspec += matches->id;
1275                             colorspec += ")";
1276                         } else {
1277                             gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
1278                             colorspec = tmp;
1279                             g_free(tmp);
1280                         }
1281                     }
1282                 }
1283             }
1284             if ( worked ) {
1285                 int destX = 0;
1286                 int destY = 0;
1287                 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1288                 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1289                 Geom::Point const button_dt(desktop->w2d(where));
1290                 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1292                 SPItem *item = desktop->item_at_point( where, true );
1294                 bool consumed = false;
1295                 if (desktop->event_context && desktop->event_context->get_drag()) {
1296                     consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
1297                     if (consumed) {
1298                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1299                         desktop->event_context->get_drag()->updateDraggers();
1300                     }
1301                 }
1303                 if (!consumed && item) {
1304                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1305                     if (fillnotstroke &&
1306                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1307                         Path *livarot_path = Path_for_item(item, true, true);
1308                         livarot_path->ConvertWithBackData(0.04);
1310                         boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1311                         if (position) {
1312                             Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1313                             Geom::Point delta = nearest - button_doc;
1314                             Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1315                             delta = desktop->d2w(delta);
1316                             double stroke_tolerance =
1317                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1318                                   desktop->current_zoom() *
1319                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1320                                   to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1321                                   : 0.0)
1322                                 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1324                             if (Geom::L2 (delta) < stroke_tolerance) {
1325                                 fillnotstroke = false;
1326                             }
1327                         }
1328                         delete livarot_path;
1329                     }
1331                     SPCSSAttr *css = sp_repr_css_attr_new();
1332                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
1334                     sp_desktop_apply_css_recursive( item, css, true );
1335                     item->updateRepr();
1337                     sp_document_done( doc , SP_VERB_NONE,
1338                                       _("Drop color"));
1339                 }
1340             }
1341         }
1342         break;
1344         case SVG_DATA:
1345         case SVG_XML_DATA: {
1346             gchar *svgdata = (gchar *)data->data;
1348             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1350             if (rnewdoc == NULL) {
1351                 sp_ui_error_dialog(_("Could not parse SVG data"));
1352                 return;
1353             }
1355             Inkscape::XML::Node *repr = rnewdoc->root();
1356             gchar const *style = repr->attribute("style");
1358             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1359             newgroup->setAttribute("style", style);
1361             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1362             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1363                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1364                 newgroup->appendChild(newchild);
1365             }
1367             Inkscape::GC::release(rnewdoc);
1369             // Add it to the current layer
1371             // Greg's edits to add intelligent positioning of svg drops
1372             SPObject *new_obj = NULL;
1373             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1375             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1376             selection->set(SP_ITEM(new_obj));
1378             // move to mouse pointer
1379             {
1380                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1381                 Geom::OptRect sel_bbox = selection->bounds();
1382                 if (sel_bbox) {
1383                     Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1384                     sp_selection_move_relative(selection, m, false);
1385                 }
1386             }
1388             Inkscape::GC::release(newgroup);
1389             sp_document_done(doc, SP_VERB_NONE,
1390                              _("Drop SVG"));
1391             break;
1392         }
1394         case URI_LIST: {
1395             gchar *uri = (gchar *)data->data;
1396             sp_ui_import_files(uri);
1397             break;
1398         }
1400         case PNG_DATA:
1401         case JPEG_DATA:
1402         case IMAGE_DATA: {
1403             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1404             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1405             gchar *atom_name = gdk_atom_name(data->type);
1407             // this formula taken from Glib docs
1408             guint needed_size = data->length * 4 / 3 + data->length * 4 / (3 * 72) + 7;
1409             needed_size += 5 + 8 + strlen(atom_name); // 5 bytes for data:, 8 for ;base64,
1411             gchar *buffer = (gchar *) g_malloc(needed_size), *buf_work = buffer;
1412             buf_work += g_sprintf(buffer, "data:%s;base64,", atom_name);
1414             gint state = 0, save = 0;
1415             g_base64_encode_step(data->data, data->length, TRUE, buf_work, &state, &save);
1416             g_base64_encode_close(TRUE, buf_work, &state, &save);
1418             newImage->setAttribute("xlink:href", buffer);
1419             g_free(buffer);
1421             GError *error = NULL;
1422             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1423             if ( loader ) {
1424                 error = NULL;
1425                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1426                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1427                     if ( pbuf ) {
1428                         char tmp[1024];
1429                         int width = gdk_pixbuf_get_width(pbuf);
1430                         int height = gdk_pixbuf_get_height(pbuf);
1431                         snprintf( tmp, sizeof(tmp), "%d", width );
1432                         newImage->setAttribute("width", tmp);
1434                         snprintf( tmp, sizeof(tmp), "%d", height );
1435                         newImage->setAttribute("height", tmp);
1436                     }
1437                 }
1438             }
1439             g_free(atom_name);
1441             // Add it to the current layer
1442             desktop->currentLayer()->appendChildRepr(newImage);
1444             Inkscape::GC::release(newImage);
1445             sp_document_done( doc , SP_VERB_NONE,
1446                               _("Drop bitmap image"));
1447             break;
1448         }
1449     }
1452 #include "gradient-context.h"
1454 void sp_ui_drag_motion( GtkWidget */*widget*/,
1455                         GdkDragContext */*drag_context*/,
1456                         gint /*x*/, gint /*y*/,
1457                         GtkSelectionData */*data*/,
1458                         guint /*info*/,
1459                         guint /*event_time*/,
1460                         gpointer /*user_data*/)
1462 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1463 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1466 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1469 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1470                               GdkDragContext */*drag_context*/,
1471                               guint /*event_time*/,
1472                               gpointer /*user_data*/ )
1474 //     g_message("drag-n-drop leave                at %d", event_time);
1477 static void
1478 sp_ui_import_files(gchar *buffer)
1480     GList *list = gnome_uri_list_extract_filenames(buffer);
1481     if (!list)
1482         return;
1483     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1484     g_list_foreach(list, (GFunc) g_free, NULL);
1485     g_list_free(list);
1488 static void
1489 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1491     if (filename) {
1492         if (strlen((char const *)filename) > 2)
1493             sp_ui_import_one_file((char const *)filename);
1494     }
1497 static void
1498 sp_ui_import_one_file(char const *filename)
1500     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1501     if (!doc) return;
1503     if (filename == NULL) return;
1505     // Pass off to common implementation
1506     // TODO might need to get the proper type of Inkscape::Extension::Extension
1507     file_import( doc, filename, NULL );
1510 void
1511 sp_ui_error_dialog(gchar const *message)
1513     GtkWidget *dlg;
1514     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1516     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1517                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1518     sp_transientize(dlg);
1519     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1520     gtk_dialog_run(GTK_DIALOG(dlg));
1521     gtk_widget_destroy(dlg);
1522     g_free(safeMsg);
1525 bool
1526 sp_ui_overwrite_file(gchar const *filename)
1528     bool return_value = FALSE;
1530     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1531         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1532         gchar* baseName = g_path_get_basename( filename );
1533         gchar* dirName = g_path_get_dirname( filename );
1534         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1535                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1536                                                                 GTK_MESSAGE_QUESTION,
1537                                                                 GTK_BUTTONS_NONE,
1538                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1539                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1540                                                                 baseName,
1541                                                                 dirName
1542             );
1543         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1544                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1545                                 _("Replace"), GTK_RESPONSE_YES,
1546                                 NULL );
1547         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1549         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1550             return_value = TRUE;
1551         } else {
1552             return_value = FALSE;
1553         }
1554         gtk_widget_destroy(dialog);
1555         g_free( baseName );
1556         g_free( dirName );
1557     } else {
1558         return_value = TRUE;
1559     }
1561     return return_value;
1564 static void
1565 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1567     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1570 static void
1571 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1573     void *child = GTK_BIN (data)->child;
1574     //child is either
1575     //- a GtkHBox, whose first child is a label displaying name if the menu
1576     //item has an accel key
1577     //- a GtkLabel if the menu has no accel key
1578     if (GTK_IS_LABEL(child)) {
1579         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1580     } else if (GTK_IS_HBOX(child)) {
1581         gtk_label_set_markup_with_mnemonic(
1582         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1583         name.c_str());
1584     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1585     //a menu item in yet another way...
1589 /*
1590   Local Variables:
1591   mode:c++
1592   c-file-style:"stroustrup"
1593   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1594   indent-tabs-mode:nil
1595   fill-column:99
1596   End:
1597 */
1598 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :