Code

change NR::Matrix to Geom:: for many sp_item_xxx_affine functions
[inkscape.git] / src / interface.cpp
1 #define __SP_INTERFACE_C__
3 /**
4  * Main UI stuff
5  *
6  * Authors:
7  *   Lauris Kaplinski <lauris@kaplinski.com>
8  *   Frank Felfe <innerspace@iname.com>
9  *   bulia byak <buliabyak@users.sf.net>
10  *
11  * Copyright (C) 1999-2005 authors
12  * Copyright (C) 2001-2002 Ximian, Inc.
13  * Copyright (C) 2004 David Turner
14  *
15  * Released under GNU GPL, read the file 'COPYING' for more information
16  */
18 #ifdef HAVE_CONFIG_H
19 # include "config.h"
20 #endif
22 #include <gtk/gtk.h>
23 #include "inkscape-private.h"
24 #include "extension/effect.h"
25 #include "widgets/icon.h"
26 #include "prefs-utils.h"
27 #include "path-prefix.h"
29 #include "shortcuts.h"
31 #include "document.h"
32 #include "desktop-handles.h"
33 #include "file.h"
34 #include "interface.h"
35 #include "desktop.h"
36 #include "ui/context-menu.h"
37 #include "selection.h"
38 #include "selection-chemistry.h"
39 #include "svg-view-widget.h"
40 #include "widgets/desktop-widget.h"
41 #include "sp-item-group.h"
42 #include "sp-text.h"
43 #include "sp-flowtext.h"
44 #include "sp-namedview.h"
45 #include "ui/view/view.h"
47 #include "helper/action.h"
48 #include "helper/gnome-utils.h"
49 #include "helper/window.h"
51 #include "io/sys.h"
52 #include "io/stringstream.h"
53 #include "io/base64stream.h"
55 #include "dialogs/dialog-events.h"
57 #include "message-context.h"
59 // Added for color drag-n-drop
60 #if ENABLE_LCMS
61 #include "lcms.h"
62 #endif // ENABLE_LCMS
63 #include "display/sp-canvas.h"
64 #include "color.h"
65 #include "svg/svg-color.h"
66 #include "desktop-style.h"
67 #include "style.h"
68 #include "event-context.h"
69 #include "gradient-drag.h"
71 // Include Mac OS X menu synchronization on native OSX build
72 #ifdef GDK_WINDOWING_QUARTZ
73 #include "ige-mac-menu.h"
74 #endif
76 using Inkscape::IO::StringOutputStream;
77 using Inkscape::IO::Base64OutputStream;
79 /* Drag and Drop */
80 typedef enum {
81     URI_LIST,
82     SVG_XML_DATA,
83     SVG_DATA,
84     PNG_DATA,
85     JPEG_DATA,
86     IMAGE_DATA,
87     APP_X_INKY_COLOR,
88     APP_X_COLOR
89 } ui_drop_target_info;
91 static GtkTargetEntry ui_drop_target_entries [] = {
92     {(gchar *)"text/uri-list",                0, URI_LIST        },
93     {(gchar *)"image/svg+xml",                0, SVG_XML_DATA    },
94     {(gchar *)"image/svg",                    0, SVG_DATA        },
95     {(gchar *)"image/png",                    0, PNG_DATA        },
96     {(gchar *)"image/jpeg",                   0, JPEG_DATA       },
97 #if ENABLE_MAGIC_COLORS
98     {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
99 #endif // ENABLE_MAGIC_COLORS
100     {(gchar *)"application/x-color",          0, APP_X_COLOR     }
101 };
103 static GtkTargetEntry *completeDropTargets = 0;
104 static int completeDropTargetsCount = 0;
106 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
107 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
108 static void sp_ui_import_files(gchar *buffer);
109 static void sp_ui_import_one_file(char const *filename);
110 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
111 static void sp_ui_drag_data_received(GtkWidget *widget,
112                                      GdkDragContext *drag_context,
113                                      gint x, gint y,
114                                      GtkSelectionData *data,
115                                      guint info,
116                                      guint event_time,
117                                      gpointer user_data);
118 static void sp_ui_drag_motion( GtkWidget *widget,
119                                GdkDragContext *drag_context,
120                                gint x, gint y,
121                                GtkSelectionData *data,
122                                guint info,
123                                guint event_time,
124                                gpointer user_data );
125 static void sp_ui_drag_leave( GtkWidget *widget,
126                               GdkDragContext *drag_context,
127                               guint event_time,
128                               gpointer user_data );
129 static void sp_ui_menu_item_set_sensitive(SPAction *action,
130                                           unsigned int sensitive,
131                                           void *data);
132 static void sp_ui_menu_item_set_name(SPAction *action, 
133                                      Glib::ustring name,
134                                      void *data);
136 SPActionEventVector menu_item_event_vector = {
137     {NULL},
138     NULL,
139     NULL, /* set_active */
140     sp_ui_menu_item_set_sensitive, /* set_sensitive */
141     NULL, /* set_shortcut */
142     sp_ui_menu_item_set_name /* set_name */
143 };
145 static const int MIN_ONSCREEN_DISTANCE = 50;
147 void
148 sp_create_window(SPViewWidget *vw, gboolean editable)
150     g_return_if_fail(vw != NULL);
151     g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
153     Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
155     if (editable) {
156                 g_object_set_data(G_OBJECT(vw), "window", win);
157                 
158                 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
159                 SPDesktop* desktop = desktop_widget->desktop;
160                 
161                 desktop_widget->window = win;
163         win->set_data("desktop", desktop);
164         win->set_data("desktopwidget", desktop_widget);
165                 
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));
169                 
170         gint prefs_geometry = 
171             (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
172         if (prefs_geometry) {
173             gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
174             gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
175             gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
176             gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
177             gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
178             gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
179             if (pw>0 && ph>0) {
180                 gint w = MIN(gdk_screen_width(), pw);
181                 gint h = MIN(gdk_screen_height(), ph);
182                 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
183                 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
184                 if (w>0 && h>0 && x>0 && y>0) {
185                     x = MIN(gdk_screen_width() - w, x);
186                     y = MIN(gdk_screen_height() - h, y);
187                 }
188                 if (w>0 && h>0) {
189                     desktop->setWindowSize(w, h);
190                 }
192                 // Only restore xy for the first window so subsequent windows don't overlap exactly
193                 // with first.  (Maybe rule should be only restore xy if it's different from xy of
194                 // other desktops?)
196                 // Empirically it seems that active_desktop==this desktop only the first time a
197                 // desktop is created.
198                 if (x>0 && y>0) {
199                     SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
200                     if (active_desktop == desktop || active_desktop==NULL) {
201                         desktop->setWindowPosition(NR::Point(x, y));
202                     }
203                 }
204             }
205             if (maxed) {
206                 win->maximize();
207             }
208             if (full) {
209                 win->fullscreen();
210             }
211         }
213     } else {
214         gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
215     }
217     gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
218     gtk_widget_show(GTK_WIDGET(vw));
220     if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
221     {
222         std::vector<gchar*> types;
224         GSList *list = gdk_pixbuf_get_formats();
225         while ( list ) {
226             int i = 0;
227             GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
228             gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
229             for ( i = 0; typesXX[i]; i++ ) {
230                 types.push_back(g_strdup(typesXX[i]));
231             }
232             g_strfreev(typesXX);
234             list = g_slist_next(list);
235         }
236         completeDropTargetsCount = nui_drop_target_entries + types.size();
237         completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
238         for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
239             completeDropTargets[i] = ui_drop_target_entries[i];
240         }
241         int pos = nui_drop_target_entries;
243         for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
244             completeDropTargets[pos].target = *it;
245             completeDropTargets[pos].flags = 0;
246             completeDropTargets[pos].info = IMAGE_DATA;
247             pos++;
248         }
249     }
251     gtk_drag_dest_set((GtkWidget*)win->gobj(),
252                       GTK_DEST_DEFAULT_ALL,
253                       completeDropTargets,
254                       completeDropTargetsCount,
255                       GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
258     g_signal_connect(G_OBJECT(win->gobj()),
259                      "drag_data_received",
260                      G_CALLBACK(sp_ui_drag_data_received),
261                      NULL);
262     g_signal_connect(G_OBJECT(win->gobj()),
263                      "drag_motion",
264                      G_CALLBACK(sp_ui_drag_motion),
265                      NULL);
266     g_signal_connect(G_OBJECT(win->gobj()),
267                      "drag_leave",
268                      G_CALLBACK(sp_ui_drag_leave),
269                      NULL);
270     win->show();
272     // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
273     inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
276 void
277 sp_ui_new_view()
279     SPDocument *document;
280     SPViewWidget *dtw;
282     document = SP_ACTIVE_DOCUMENT;
283     if (!document) return;
285     dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
286     g_return_if_fail(dtw != NULL);
288     sp_create_window(dtw, TRUE);
289     sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
290     sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
293 /* TODO: not yet working */
294 /* To be re-enabled (by adding to menu) once it works. */
295 void
296 sp_ui_new_view_preview()
298     SPDocument *document;
299     SPViewWidget *dtw;
301     document = SP_ACTIVE_DOCUMENT;
302     if (!document) return;
304     dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
305     g_return_if_fail(dtw != NULL);
306     sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
308     sp_create_window(dtw, FALSE);
311 /**
312  * \param widget unused
313  */
314 void
315 sp_ui_close_view(GtkWidget */*widget*/)
317     if (SP_ACTIVE_DESKTOP == NULL) {
318         return;
319     }
320     if ((SP_ACTIVE_DESKTOP)->shutdown()) {
321         return;
322     }
323     SP_ACTIVE_DESKTOP->destroyWidget();
327 /**
328  *  sp_ui_close_all
329  *
330  *  This function is called to exit the program, and iterates through all
331  *  open document view windows, attempting to close each in turn.  If the
332  *  view has unsaved information, the user will be prompted to save,
333  *  discard, or cancel.
334  *
335  *  Returns FALSE if the user cancels the close_all operation, TRUE
336  *  otherwise.
337  */
338 unsigned int
339 sp_ui_close_all(void)
341     /* Iterate through all the windows, destroying each in the order they
342        become active */
343     while (SP_ACTIVE_DESKTOP) {
344         if ((SP_ACTIVE_DESKTOP)->shutdown()) {
345             /* The user cancelled the operation, so end doing the close */
346             return FALSE;
347         }
348         SP_ACTIVE_DESKTOP->destroyWidget();
349     }
351     return TRUE;
354 /*
355  * Some day when the right-click menus are ready to start working
356  * smarter with the verbs, we'll need to change this NULL being
357  * sent to sp_action_perform to something useful, or set some kind
358  * of global "right-clicked position" variable for actions to
359  * investigate when they're called.
360  */
361 static void
362 sp_ui_menu_activate(void */*object*/, SPAction *action)
364     sp_action_perform(action, NULL);
367 static void
368 sp_ui_menu_select_action(void */*object*/, SPAction *action)
370     action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
373 static void
374 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
376     action->view->tipsMessageContext()->clear();
379 static void
380 sp_ui_menu_select(gpointer object, gpointer tip)
382     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
383     view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
386 static void
387 sp_ui_menu_deselect(gpointer object)
389     Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*>  (g_object_get_data(G_OBJECT(object), "view"));
390     view->tipsMessageContext()->clear();
393 /**
394  * sp_ui_menuitem_add_icon
395  *
396  * Creates and attaches a scaled icon to the given menu item.
397  *
398  */
399 void
400 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
402     GtkWidget *icon;
404     icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
405     gtk_widget_show(icon);
406     gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
407 } // end of sp_ui_menu_add_icon
409 /**
410  * sp_ui_menu_append_item
411  *
412  * Appends a UI item with specific info for Inkscape/Sodipodi.
413  *
414  */
415 static GtkWidget *
416 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
417                         gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
418                         gpointer data, gboolean with_mnemonic = TRUE )
420     GtkWidget *item;
422     if (stock) {
423         item = gtk_image_menu_item_new_from_stock(stock, NULL);
424     } else if (label) {
425         item = (with_mnemonic)
426             ? gtk_image_menu_item_new_with_mnemonic(label) :
427             gtk_image_menu_item_new_with_label(label);
428     } else {
429         item = gtk_separator_menu_item_new();
430     }
432     gtk_widget_show(item);
434     if (callback) {
435         g_signal_connect(G_OBJECT(item), "activate", callback, data);
436     }
438     if (tip && view) {
439         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
440         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
441         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
442     }
444     gtk_menu_append(GTK_MENU(menu), item);
446     return item;
448 } // end of sp_ui_menu_append_item()
450 /**
451 \brief  a wrapper around gdk_keyval_name producing (when possible) characters, not names
452  */
453 static gchar const *
454 sp_key_name(guint keyval)
456     /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
457        simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
458     gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
460     if      (!strcmp(n, "asciicircum"))  return "^";
461     else if (!strcmp(n, "parenleft"  ))  return "(";
462     else if (!strcmp(n, "parenright" ))  return ")";
463     else if (!strcmp(n, "plus"       ))  return "+";
464     else if (!strcmp(n, "minus"      ))  return "-";
465     else if (!strcmp(n, "asterisk"   ))  return "*";
466     else if (!strcmp(n, "KP_Multiply"))  return "*";
467     else if (!strcmp(n, "Delete"     ))  return "Del";
468     else if (!strcmp(n, "Page_Up"    ))  return "PgUp";
469     else if (!strcmp(n, "Page_Down"  ))  return "PgDn";
470     else if (!strcmp(n, "grave"      ))  return "`";
471     else if (!strcmp(n, "numbersign" ))  return "#";
472     else if (!strcmp(n, "bar"        ))  return "|";
473     else if (!strcmp(n, "slash"      ))  return "/";
474     else if (!strcmp(n, "exclam"     ))  return "!";
475     else if (!strcmp(n, "percent"    ))  return "%";
476     else return n;
480 /**
481  * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
482  * \param c Points to a buffer at least 256 bytes long.
483  */
484 void
485 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
487     /* TODO: This function shouldn't exist.  Our callers should use GtkAccelLabel instead of
488      * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
489      * Will probably need to change sp_shortcut_invoke callers.
490      *
491      * The existing gtk_label_new_with_mnemonic call can be replaced with
492      * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
493      * gtk_label_set_text_with_mnemonic(lbl, str).
494      */
495     static GtkAccelLabelClass const &accel_lbl_cls
496         = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
498     struct { unsigned test; char const *name; } const modifier_tbl[] = {
499         { SP_SHORTCUT_SHIFT_MASK,   accel_lbl_cls.mod_name_shift   },
500         { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
501         { SP_SHORTCUT_ALT_MASK,     accel_lbl_cls.mod_name_alt     }
502     };
504     gchar *p = c;
505     gchar *end = p + 256;
507     for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
508         if ((shortcut & modifier_tbl[i].test)
509             && (p < end))
510         {
511             p += g_snprintf(p, end - p, "%s%s",
512                             modifier_tbl[i].name,
513                             accel_lbl_cls.mod_separator);
514         }
515     }
516     if (p < end) {
517         p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
518     }
519     end[-1] = '\0';  // snprintf doesn't guarantee to nul-terminate the string.
522 void
523 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
525     SPAction     *action;
526     unsigned int shortcut;
527     gchar        *s;
528     gchar        key[256];
529     gchar        *atitle;
531     action = verb->get_action(NULL);
532     if (!action)
533         return;
535     atitle = sp_action_get_title(action);
537     s = g_stpcpy(c, atitle);
539     g_free(atitle);
541     shortcut = sp_shortcut_get_primary(verb);
542     if (shortcut) {
543         s = g_stpcpy(s, " (");
544         sp_ui_shortcut_string(shortcut, key);
545         s = g_stpcpy(s, key);
546         s = g_stpcpy(s, ")");
547     }
551 /**
552  * sp_ui_menu_append_item_from_verb
553  *
554  * Appends a custom menu UI from a verb.
555  *
556  */
558 static GtkWidget *
559 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
561     SPAction *action;
562     GtkWidget *item;
564     if (verb->get_code() == SP_VERB_NONE) {
566         item = gtk_separator_menu_item_new();
568     } else {
569         unsigned int shortcut;
571         action = verb->get_action(view);
573         if (!action) return NULL;
575         shortcut = sp_shortcut_get_primary(verb);
576         if (shortcut) {
577             gchar c[256];
578             sp_ui_shortcut_string(shortcut, c);
579             GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
580             GtkWidget *const name_lbl = gtk_label_new("");
581             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
582             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
583             gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
584             GtkWidget *const accel_lbl = gtk_label_new(c);
585             gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
586             gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
587             gtk_widget_show_all(hb);
588             if (radio) {
589                 item = gtk_radio_menu_item_new (group);
590             } else {
591                 item = gtk_image_menu_item_new();
592             }
593             gtk_container_add((GtkContainer *) item, hb);
594         } else {
595             if (radio) {
596                 item = gtk_radio_menu_item_new (group);
597             } else {
598                 item = gtk_image_menu_item_new ();
599             }
600             GtkWidget *const name_lbl = gtk_label_new("");
601             gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
602             gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
603             gtk_container_add((GtkContainer *) item, name_lbl);
604         }
606         nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
607         if (!action->sensitive) {
608             gtk_widget_set_sensitive(item, FALSE);
609         }
611         if (action->image) {
612             sp_ui_menuitem_add_icon(item, action->image);
613         }
614         gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
615         g_signal_connect( G_OBJECT(item), "activate",
616                           G_CALLBACK(sp_ui_menu_activate), action );
618         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
619         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
620     }
622     gtk_widget_show(item);
623     gtk_menu_append(GTK_MENU(menu), item);
625     return item;
627 } // end of sp_ui_menu_append_item_from_verb
630 static void
631 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
633     gchar const *pref = (gchar const *) user_data;
634     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
636     gchar const *pref_path;
637     if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
638         pref_path = g_strconcat("fullscreen.", pref, NULL);
639     else
640         pref_path = g_strconcat("window.", pref, NULL);
642     gboolean checked = gtk_check_menu_item_get_active(menuitem);
643     prefs_set_int_attribute(pref_path, "state", checked);
645     reinterpret_cast<SPDesktop*>(view)->layoutWidget();
648 static gboolean
649 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
651     GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
653     gchar const *pref = (gchar const *) user_data;
654     Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
656     gchar const *pref_path;
657     if ((static_cast<SPDesktop*>(view))->is_fullscreen())
658         pref_path = g_strconcat("fullscreen.", pref, NULL);
659     else
660         pref_path = g_strconcat("window.", pref, NULL);
662     gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
664     g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
665     gtk_check_menu_item_set_active(menuitem, ison);
666     g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
668     return FALSE;
672 void
673 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
674                                        void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
675                                        gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
676                                        Inkscape::Verb *verb)
678     GtkWidget *item;
680     unsigned int shortcut = 0;
681     SPAction *action = NULL;
683     if (verb) {
684         shortcut = sp_shortcut_get_primary(verb);
685         action = verb->get_action(view);
686     }
688     if (verb && shortcut) {
689         gchar c[256];
690         sp_ui_shortcut_string(shortcut, c);
692         GtkWidget *hb = gtk_hbox_new(FALSE, 16);
694         {
695             GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
696             gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
697             gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
698         }
700         {
701             GtkWidget *l = gtk_label_new(c);
702             gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
703             gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
704         }
706         gtk_widget_show_all(hb);
708         item = gtk_check_menu_item_new();
709         gtk_container_add((GtkContainer *) item, hb);
710     } else {
711         GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
712         gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
713         item = gtk_check_menu_item_new();
714         gtk_container_add((GtkContainer *) item, l);
715     }
716 #if 0
717     nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
718     if (!action->sensitive) {
719         gtk_widget_set_sensitive(item, FALSE);
720     }
721 #endif
722     gtk_widget_show(item);
724     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
726     g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
728     g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
729     g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
731     g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
732     g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
735 static void
736 sp_recent_open(GtkWidget */*widget*/, gchar const *uri)
738     sp_file_open(uri, NULL);
741 static void
742 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
744     sp_file_new(uri);
747 void
748 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
750     std::list<gchar *> sources;
751     sources.push_back( profile_path("templates") ); // first try user's local dir
752     sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
754     // Use this loop to iterate through a list of possible document locations.
755     while (!sources.empty()) {
756         gchar *dirname = sources.front();
758         if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
759             GError *err = 0;
760             GDir *dir = g_dir_open(dirname, 0, &err);
762             if (dir) {
763                 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
764                     if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
765                         continue; // skip non-svg files
767                     gchar *basename = g_path_get_basename(file);
768                     if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
769                         continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
771                     gchar const *filepath = g_build_filename(dirname, file, NULL);
772                     gchar *dupfile = g_strndup(file, strlen(file) - 4);
773                     gchar *filename =  g_filename_to_utf8(dupfile,  -1, NULL, NULL, NULL);
774                     g_free(dupfile);
775                     GtkWidget *item = gtk_menu_item_new_with_label(filename);
776                     g_free(filename);
778                     gtk_widget_show(item);
779                     // how does "filepath" ever get freed?
780                     g_signal_connect(G_OBJECT(item),
781                                      "activate",
782                                      G_CALLBACK(sp_file_new_from_template),
783                                      (gpointer) filepath);
785                     if (view) {
786                         // set null tip for now; later use a description from the template file
787                         g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
788                         g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
789                         g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
790                     }
792                     gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
793                 }
794                 g_dir_close(dir);
795             }
796         }
798         // toss the dirname
799         g_free(dirname);
800         sources.pop_front();
801     }
804 void
805 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
807     gchar const **recent = prefs_get_recent_files();
808     if (recent) {
809         int i;
811         for (i = 0; recent[i] != NULL; i += 2) {
812             gchar const *uri = recent[i];
813             gchar const *name = recent[i + 1];
815             GtkWidget *item = gtk_menu_item_new_with_label(name);
816             gtk_widget_show(item);
817             g_signal_connect(G_OBJECT(item),
818                              "activate",
819                              G_CALLBACK(sp_recent_open),
820                              (gpointer)uri);
821             gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
822         }
824         g_free(recent);
825     } else {
826         GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
827         gtk_widget_show(item);
828         gtk_widget_set_sensitive(item, FALSE);
829         gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
830     }
833 void
834 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
836     //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
837     //                                       checkitem_toggled, checkitem_update, 0);
838     sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
839                                            checkitem_toggled, checkitem_update, 0);
840     sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
841                                            checkitem_toggled, checkitem_update, 0);
842     sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
843                                            checkitem_toggled, checkitem_update, 0);
844     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
845                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
846     sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
847                                            checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
848     sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
849                                            checkitem_toggled, checkitem_update, 0);
850     sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
851                                            checkitem_toggled, checkitem_update, 0);
854 /** \brief  This function turns XML into a menu
855     \param  menus  This is the XML that defines the menu
856     \param  menu   Menu to be added to
857     \param  view   The View that this menu is being built for
859     This function is realitively simple as it just goes through the XML
860     and parses the individual elements.  In the case of a submenu, it
861     just calls itself recursively.  Because it is only reasonable to have
862     a couple of submenus, it is unlikely this will go more than two or
863     three times.
865     In the case of an unreconginzed verb, a menu item is made to identify
866     the verb that is missing, and display that.  The menu item is also made
867     insensitive.
868 */
869 void
870 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
872     if (menus == NULL) return;
873     if (menu == NULL)  return;
874     GSList *group = NULL;
876     for (Inkscape::XML::Node *menu_pntr = menus;
877          menu_pntr != NULL;
878          menu_pntr = menu_pntr->next()) {
879         if (!strcmp(menu_pntr->name(), "submenu")) {
880             GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
881             GtkWidget *submenu = gtk_menu_new();
882             sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
883             gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
884             gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
885             continue;
886         }
887         if (!strcmp(menu_pntr->name(), "verb")) {
888             gchar const *verb_name = menu_pntr->attribute("verb-id");
889             Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
891             if (verb != NULL) {
892                 if (menu_pntr->attribute("radio") != NULL) {
893                     GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
894                     group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
895                     if (menu_pntr->attribute("default") != NULL) {
896                         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
897                     }
898                 } else {
899                     sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
900                     group = NULL;
901                 }
902             } else {
903                 gchar string[120];
904                 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
905                 string[119] = '\0'; /* may not be terminated */
906                 GtkWidget *item = gtk_menu_item_new_with_label(string);
907                 gtk_widget_set_sensitive(item, false);
908                 gtk_widget_show(item);
909                 gtk_menu_append(GTK_MENU(menu), item);
910             }
911             continue;
912         }
913         if (!strcmp(menu_pntr->name(), "separator")
914                 // This was spelt wrong in the original version
915                 // and so this is for backward compatibility.  It can
916                 // probably be dropped after the 0.44 release.
917              || !strcmp(menu_pntr->name(), "seperator")) {
918             GtkWidget *item = gtk_separator_menu_item_new();
919             gtk_widget_show(item);
920             gtk_menu_append(GTK_MENU(menu), item);
921             continue;
922         }
923         if (!strcmp(menu_pntr->name(), "template-list")) {
924             sp_menu_append_new_templates(menu, view);
925             continue;
926         }
927         if (!strcmp(menu_pntr->name(), "recent-file-list")) {
928             sp_menu_append_recent_documents(menu, view);
929             continue;
930         }
931         if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
932             sp_ui_checkboxes_menus(GTK_MENU(menu), view);
933             continue;
934         }
935     }
938 /** \brief  Build the main tool bar
939     \param  view  View to build the bar for
941     Currently the main tool bar is built as a dynamic XML menu using
942     \c sp_ui_build_dyn_menus.  This function builds the bar, and then
943     pass it to get items attached to it.
944 */
945 GtkWidget *
946 sp_ui_main_menubar(Inkscape::UI::View::View *view)
948     GtkWidget *mbar = gtk_menu_bar_new();
950 #ifdef GDK_WINDOWING_QUARTZ
951         ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
952 #endif
954     sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
956 #ifdef GDK_WINDOWING_QUARTZ
957         return NULL;
958 #else
959     return mbar;
960 #endif
963 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
964     desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
967 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
968     desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
969     sp_desktop_selection(desktop)->clear();
972 GtkWidget *
973 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
975     GtkWidget *m;
976     SPDesktop *dt;
978     dt = static_cast<SPDesktop*>(view);
980     m = gtk_menu_new();
982     /* Undo and Redo */
983     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
984     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
986     /* Separator */
987     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
989     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
990     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
991     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
993     /* Separator */
994     sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
996     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
997     sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
999     /* Item menu */
1000     if (item) {
1001         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1002         sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1003     }
1005     /* layer menu */
1006     SPGroup *group=NULL;
1007     if (item) {
1008         if (SP_IS_GROUP(item)) {
1009             group = SP_GROUP(item);
1010         } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1011             group = SP_GROUP(SP_OBJECT_PARENT(item));
1012         }
1013     }
1015     if (( group && group != dt->currentLayer() ) ||
1016         ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1017         /* Separator */
1018         sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1019     }
1021     if ( group && group != dt->currentLayer() ) {
1022         /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1023         gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1024         GtkWidget *w = gtk_menu_item_new_with_label(label);
1025         g_free(label);
1026         g_object_set_data(G_OBJECT(w), "group", group);
1027         g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1028         gtk_widget_show(w);
1029         gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1030     }
1032     if ( dt->currentLayer() != dt->currentRoot() ) {
1033         if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1034             GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1035             g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1036             gtk_widget_show(w);
1037             gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1039         }
1040     }
1042     return m;
1045 /* Drag and Drop */
1046 void
1047 sp_ui_drag_data_received(GtkWidget *widget,
1048                          GdkDragContext *drag_context,
1049                          gint x, gint y,
1050                          GtkSelectionData *data,
1051                          guint info,
1052                          guint /*event_time*/,
1053                          gpointer /*user_data*/)
1055     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1056     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1058     switch (info) {
1059 #if ENABLE_MAGIC_COLORS
1060         case APP_X_INKY_COLOR:
1061         {
1062             int destX = 0;
1063             int destY = 0;
1064             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1065             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1067             SPItem *item = desktop->item_at_point( where, true );
1068             if ( item )
1069             {
1070                 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1072                 if ( data->length >= 8 ) {
1073                     cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1075                     gchar c[64] = {0};
1076                     // Careful about endian issues.
1077                     guint16* dataVals = (guint16*)data->data;
1078                     sp_svg_write_color( c, sizeof(c),
1079                                         SP_RGBA32_U_COMPOSE(
1080                                             0x0ff & (dataVals[0] >> 8),
1081                                             0x0ff & (dataVals[1] >> 8),
1082                                             0x0ff & (dataVals[2] >> 8),
1083                                             0xff // can't have transparency in the color itself
1084                                             //0x0ff & (data->data[3] >> 8),
1085                                             ));
1086                     SPCSSAttr *css = sp_repr_css_attr_new();
1087                     bool updatePerformed = false;
1089                     if ( data->length > 14 ) {
1090                         int flags = dataVals[4];
1092                         // piggie-backed palette entry info
1093                         int index = dataVals[5];
1094                         Glib::ustring palName;
1095                         for ( int i = 0; i < dataVals[6]; i++ ) {
1096                             palName += (gunichar)dataVals[7+i];
1097                         }
1099                         // Now hook in a magic tag of some sort.
1100                         if ( !palName.empty() && (flags & 1) ) {
1101                             gchar* str = g_strdup_printf("%d|", index);
1102                             palName.insert( 0, str );
1103                             g_free(str);
1104                             str = 0;
1106                             sp_object_setAttribute( SP_OBJECT(item),
1107                                                     fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1108                                                     palName.c_str(),
1109                                                     false );
1110                             item->updateRepr();
1112                             sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1113                             updatePerformed = true;
1114                         }
1115                     }
1117                     if ( !updatePerformed ) {
1118                         sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1119                     }
1121                     sp_desktop_apply_css_recursive( item, css, true );
1122                     item->updateRepr();
1124                     sp_document_done( doc , SP_VERB_NONE, 
1125                                       _("Drop color"));
1127                     if ( srgbProf ) {
1128                         cmsCloseProfile( srgbProf );
1129                     }
1130                 }
1131             }
1132         }
1133         break;
1134 #endif // ENABLE_MAGIC_COLORS
1136         case APP_X_COLOR:
1137         {
1138             int destX = 0;
1139             int destY = 0;
1140             gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1141             NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1142             NR::Point const button_dt(desktop->w2d(where));
1143             NR::Point const button_doc(desktop->dt2doc(button_dt));
1145             if ( data->length == 8 ) {
1146                 gchar c[64] = {0};
1147                 // Careful about endian issues.
1148                 guint16* dataVals = (guint16*)data->data;
1149                 sp_svg_write_color( c, 64,
1150                                     SP_RGBA32_U_COMPOSE(
1151                                         0x0ff & (dataVals[0] >> 8),
1152                                         0x0ff & (dataVals[1] >> 8),
1153                                         0x0ff & (dataVals[2] >> 8),
1154                                         0xff // can't have transparency in the color itself
1155                                         //0x0ff & (data->data[3] >> 8),
1156                                         ));
1158                 SPItem *item = desktop->item_at_point( where, true );
1160                 bool consumed = false;
1161                 if (desktop->event_context && desktop->event_context->get_drag()) {
1162                     consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1163                     if (consumed) {
1164                         sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1165                         desktop->event_context->get_drag()->updateDraggers();
1166                     }
1167                 }
1169                 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1170                 //    consumed = sp_text_context_drop_color(c, button_doc);
1171                 //    if (consumed) {
1172                 //        sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1173                 //    }
1174                 //}
1176                 if (!consumed && item) {
1177                     bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1178                     if (fillnotstroke && 
1179                         (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1180                         Path *livarot_path = Path_for_item(item, true, true);
1181                         livarot_path->ConvertWithBackData(0.04);
1183                         NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1184                         if (position) {
1185                             NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1186                             NR::Point delta = nearest - button_doc;
1187                             delta = desktop->d2w(delta);
1188                             double stroke_tolerance =
1189                                 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1190                                   desktop->current_zoom() *
1191                                   SP_OBJECT_STYLE (item)->stroke_width.computed *
1192                                   NR::expansion(from_2geom(sp_item_i2d_affine(item))) * 0.5
1193                                   : 0.0)
1194                                 + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100); 
1196                             if (NR::L2 (delta) < stroke_tolerance) {
1197                                 fillnotstroke = false;
1198                             }
1199                         }
1200                         delete livarot_path;
1201                     }
1203                     SPCSSAttr *css = sp_repr_css_attr_new();
1204                     sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1206                     sp_desktop_apply_css_recursive( item, css, true );
1207                     item->updateRepr();
1209                     sp_document_done( doc , SP_VERB_NONE, 
1210                                       _("Drop color"));
1211                 }
1212             }
1213         }
1214         break;
1216         case SVG_DATA:
1217         case SVG_XML_DATA: {
1218             gchar *svgdata = (gchar *)data->data;
1220             Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1222             if (rnewdoc == NULL) {
1223                 sp_ui_error_dialog(_("Could not parse SVG data"));
1224                 return;
1225             }
1227             Inkscape::XML::Node *repr = rnewdoc->root();
1228             gchar const *style = repr->attribute("style");
1230             Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1231             newgroup->setAttribute("style", style);
1233             Inkscape::XML::Document * xml_doc =  sp_document_repr_doc(doc);
1234             for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1235                 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1236                 newgroup->appendChild(newchild);
1237             }
1239             Inkscape::GC::release(rnewdoc);
1241             // Add it to the current layer
1243             // Greg's edits to add intelligent positioning of svg drops
1244             SPObject *new_obj = NULL;
1245             new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1247             Inkscape::Selection *selection = sp_desktop_selection(desktop);
1248             selection->set(SP_ITEM(new_obj));
1249             // To move the imported object, we must temporarily set the "transform pattern with
1250             // object" option.
1251             {
1252                 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1253                 prefs_set_int_attribute("options.transform", "pattern", 1);
1254                 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1255                 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1256                 if (sel_bbox) {
1257                     NR::Point m( desktop->point() - sel_bbox->midpoint() );
1258                     sp_selection_move_relative(selection, m);
1259                 }
1260                 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1261             }
1263             Inkscape::GC::release(newgroup);
1264             sp_document_done(doc, SP_VERB_NONE, 
1265                              _("Drop SVG"));
1266             break;
1267         }
1269         case URI_LIST: {
1270             gchar *uri = (gchar *)data->data;
1271             sp_ui_import_files(uri);
1272             break;
1273         }
1275         case PNG_DATA:
1276         case JPEG_DATA:
1277         case IMAGE_DATA: {
1278             char tmp[1024];
1280             StringOutputStream outs;
1281             Base64OutputStream b64out(outs);
1282             b64out.setColumnWidth(0);
1284             Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1286             Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1288             for ( int i = 0; i < data->length; i++ ) {
1289                 b64out.put( data->data[i] );
1290             }
1291             b64out.close();
1294             Glib::ustring str = outs.getString();
1296             snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1297             str.insert( 0, tmp );
1298             newImage->setAttribute("xlink:href", str.c_str());
1300             GError *error = NULL;
1301             GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1302             if ( loader ) {
1303                 error = NULL;
1304                 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1305                     GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1306                     if ( pbuf ) {
1307                         int width = gdk_pixbuf_get_width(pbuf);
1308                         int height = gdk_pixbuf_get_height(pbuf);
1309                         snprintf( tmp, sizeof(tmp), "%d", width );
1310                         newImage->setAttribute("width", tmp);
1312                         snprintf( tmp, sizeof(tmp), "%d", height );
1313                         newImage->setAttribute("height", tmp);
1314                     }
1315                 }
1316             }
1318             // Add it to the current layer
1319             desktop->currentLayer()->appendChildRepr(newImage);
1321             Inkscape::GC::release(newImage);
1322             sp_document_done( doc , SP_VERB_NONE, 
1323                               _("Drop bitmap image"));
1324             break;
1325         }
1326     }
1329 #include "gradient-context.h"
1331 void sp_ui_drag_motion( GtkWidget */*widget*/,
1332                         GdkDragContext */*drag_context*/,
1333                         gint /*x*/, gint /*y*/,
1334                         GtkSelectionData */*data*/,
1335                         guint /*info*/,
1336                         guint /*event_time*/,
1337                         gpointer /*user_data*/)
1339 //     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1340 //     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1343 //     g_message("drag-n-drop motion (%4d, %4d)  at %d", x, y, event_time);
1346 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1347                               GdkDragContext */*drag_context*/,
1348                               guint /*event_time*/,
1349                               gpointer /*user_data*/ )
1351 //     g_message("drag-n-drop leave                at %d", event_time);
1354 static void
1355 sp_ui_import_files(gchar *buffer)
1357     GList *list = gnome_uri_list_extract_filenames(buffer);
1358     if (!list)
1359         return;
1360     g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1361     g_list_foreach(list, (GFunc) g_free, NULL);
1362     g_list_free(list);
1365 static void
1366 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1368     if (filename) {
1369         if (strlen((char const *)filename) > 2)
1370             sp_ui_import_one_file((char const *)filename);
1371     }
1374 static void
1375 sp_ui_import_one_file(char const *filename)
1377     SPDocument *doc = SP_ACTIVE_DOCUMENT;
1378     if (!doc) return;
1380     if (filename == NULL) return;
1382     // Pass off to common implementation
1383     // TODO might need to get the proper type of Inkscape::Extension::Extension
1384     file_import( doc, filename, NULL );
1387 void
1388 sp_ui_error_dialog(gchar const *message)
1390     GtkWidget *dlg;
1391     gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1393     dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1394                                  GTK_BUTTONS_CLOSE, "%s", safeMsg);
1395     sp_transientize(dlg);
1396     gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1397     gtk_dialog_run(GTK_DIALOG(dlg));
1398     gtk_widget_destroy(dlg);
1399     g_free(safeMsg);
1402 bool
1403 sp_ui_overwrite_file(gchar const *filename)
1405     bool return_value = FALSE;
1407     if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1408         Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1409         gchar* baseName = g_path_get_basename( filename );
1410         gchar* dirName = g_path_get_dirname( filename );
1411         GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1412                                                                 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1413                                                                 GTK_MESSAGE_QUESTION,
1414                                                                 GTK_BUTTONS_NONE,
1415                                                                 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1416                                                                    "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1417                                                                 baseName,
1418                                                                 dirName
1419             );
1420         gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1421                                 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1422                                 _("Replace"), GTK_RESPONSE_YES,
1423                                 NULL );
1424         gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1426         if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1427             return_value = TRUE;
1428         } else {
1429             return_value = FALSE;
1430         }
1431         gtk_widget_destroy(dialog);
1432         g_free( baseName );
1433         g_free( dirName );
1434     } else {
1435         return_value = TRUE;
1436     }
1438     return return_value;
1441 static void
1442 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1444     return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1447 static void
1448 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1450     void *child = GTK_BIN (data)->child;
1451     //child is either
1452     //- a GtkHBox, whose first child is a label displaying name if the menu
1453     //item has an accel key
1454     //- a GtkLabel if the menu has no accel key
1455     if (GTK_IS_LABEL(child)) {
1456         gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1457     } else if (GTK_IS_HBOX(child)) {
1458         gtk_label_set_markup_with_mnemonic(
1459         GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data), 
1460         name.c_str());
1461     }//else sp_ui_menu_append_item_from_verb has been modified and can set
1462     //a menu item in yet another way...
1466 /*
1467   Local Variables:
1468   mode:c++
1469   c-file-style:"stroustrup"
1470   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1471   indent-tabs-mode:nil
1472   fill-column:99
1473   End:
1474 */
1475 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :