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)
149 {
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 gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
156 gtk_widget_show(GTK_WIDGET(vw));
158 if (editable) {
159 g_object_set_data(G_OBJECT(vw), "window", win);
161 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
162 SPDesktop* desktop = desktop_widget->desktop;
164 desktop_widget->window = win;
166 win->set_data("desktop", desktop);
167 win->set_data("desktopwidget", desktop_widget);
169 win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
170 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
171 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
173 gint prefs_geometry =
174 (2==prefs_get_int_attribute("options.savewindowgeometry", "value", 0));
175 if (prefs_geometry) {
176 gint pw = prefs_get_int_attribute("desktop.geometry", "width", -1);
177 gint ph = prefs_get_int_attribute("desktop.geometry", "height", -1);
178 gint px = prefs_get_int_attribute("desktop.geometry", "x", -1);
179 gint py = prefs_get_int_attribute("desktop.geometry", "y", -1);
180 gint full = prefs_get_int_attribute("desktop.geometry", "fullscreen", 0);
181 gint maxed = prefs_get_int_attribute("desktop.geometry", "maximized", 0);
182 if (pw>0 && ph>0) {
183 gint w = MIN(gdk_screen_width(), pw);
184 gint h = MIN(gdk_screen_height(), ph);
185 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
186 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
187 if (w>0 && h>0 && x>0 && y>0) {
188 x = MIN(gdk_screen_width() - w, x);
189 y = MIN(gdk_screen_height() - h, y);
190 }
191 if (w>0 && h>0) {
192 desktop->setWindowSize(w, h);
193 }
195 // Only restore xy for the first window so subsequent windows don't overlap exactly
196 // with first. (Maybe rule should be only restore xy if it's different from xy of
197 // other desktops?)
199 // Empirically it seems that active_desktop==this desktop only the first time a
200 // desktop is created.
201 if (x>0 && y>0) {
202 SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
203 if (active_desktop == desktop || active_desktop==NULL) {
204 desktop->setWindowPosition(Geom::Point(x, y));
205 }
206 }
207 }
208 if (maxed) {
209 win->maximize();
210 }
211 if (full) {
212 win->fullscreen();
213 }
214 }
216 } else {
217 gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
218 }
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);
274 }
276 void
277 sp_ui_new_view()
278 {
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));
291 }
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()
297 {
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);
309 }
311 /**
312 * \param widget unused
313 */
314 void
315 sp_ui_close_view(GtkWidget */*widget*/)
316 {
317 if (SP_ACTIVE_DESKTOP == NULL) {
318 return;
319 }
320 if ((SP_ACTIVE_DESKTOP)->shutdown()) {
321 return;
322 }
323 SP_ACTIVE_DESKTOP->destroyWidget();
324 }
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)
340 {
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;
352 }
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)
363 {
364 sp_action_perform(action, NULL);
365 }
367 static void
368 sp_ui_menu_select_action(void */*object*/, SPAction *action)
369 {
370 action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
371 }
373 static void
374 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
375 {
376 action->view->tipsMessageContext()->clear();
377 }
379 static void
380 sp_ui_menu_select(gpointer object, gpointer tip)
381 {
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);
384 }
386 static void
387 sp_ui_menu_deselect(gpointer object)
388 {
389 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
390 view->tipsMessageContext()->clear();
391 }
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 )
401 {
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 )
419 {
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)
455 {
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;
477 }
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)
486 {
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.
520 }
522 void
523 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
524 {
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 }
548 }
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)
560 {
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)
632 {
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 *pref_path;
637 if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
638 pref_path = g_strconcat("focus.", pref, NULL);
639 } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
640 pref_path = g_strconcat("fullscreen.", pref, NULL);
641 } else {
642 pref_path = g_strconcat("window.", pref, NULL);
643 }
645 gboolean checked = gtk_check_menu_item_get_active(menuitem);
646 prefs_set_int_attribute(pref_path, "state", checked);
647 g_free(pref_path);
649 reinterpret_cast<SPDesktop*>(view)->layoutWidget();
650 }
652 static gboolean
653 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
654 {
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 gchar const *pref_path;
661 if ((static_cast<SPDesktop*>(view))->is_fullscreen())
662 pref_path = g_strconcat("fullscreen.", pref, NULL);
663 else
664 pref_path = g_strconcat("window.", pref, NULL);
666 gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
668 g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
669 gtk_check_menu_item_set_active(menuitem, ison);
670 g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
672 return FALSE;
673 }
676 void
677 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
678 void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
679 gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
680 Inkscape::Verb *verb)
681 {
682 GtkWidget *item;
684 unsigned int shortcut = 0;
685 SPAction *action = NULL;
687 if (verb) {
688 shortcut = sp_shortcut_get_primary(verb);
689 action = verb->get_action(view);
690 }
692 if (verb && shortcut) {
693 gchar c[256];
694 sp_ui_shortcut_string(shortcut, c);
696 GtkWidget *hb = gtk_hbox_new(FALSE, 16);
698 {
699 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
700 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
701 gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
702 }
704 {
705 GtkWidget *l = gtk_label_new(c);
706 gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
707 gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
708 }
710 gtk_widget_show_all(hb);
712 item = gtk_check_menu_item_new();
713 gtk_container_add((GtkContainer *) item, hb);
714 } else {
715 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
716 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
717 item = gtk_check_menu_item_new();
718 gtk_container_add((GtkContainer *) item, l);
719 }
720 #if 0
721 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
722 if (!action->sensitive) {
723 gtk_widget_set_sensitive(item, FALSE);
724 }
725 #endif
726 gtk_widget_show(item);
728 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
730 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
732 g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
733 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
735 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
736 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
737 }
739 static void
740 sp_recent_open(GtkWidget */*widget*/, gchar const *uri)
741 {
742 sp_file_open(uri, NULL);
743 }
745 static void
746 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
747 {
748 sp_file_new(uri);
749 }
751 void
752 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
753 {
754 std::list<gchar *> sources;
755 sources.push_back( profile_path("templates") ); // first try user's local dir
756 sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
758 // Use this loop to iterate through a list of possible document locations.
759 while (!sources.empty()) {
760 gchar *dirname = sources.front();
762 if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
763 GError *err = 0;
764 GDir *dir = g_dir_open(dirname, 0, &err);
766 if (dir) {
767 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
768 if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
769 continue; // skip non-svg files
771 gchar *basename = g_path_get_basename(file);
772 if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
773 continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
775 gchar const *filepath = g_build_filename(dirname, file, NULL);
776 gchar *dupfile = g_strndup(file, strlen(file) - 4);
777 gchar *filename = g_filename_to_utf8(dupfile, -1, NULL, NULL, NULL);
778 g_free(dupfile);
779 GtkWidget *item = gtk_menu_item_new_with_label(filename);
780 g_free(filename);
782 gtk_widget_show(item);
783 // how does "filepath" ever get freed?
784 g_signal_connect(G_OBJECT(item),
785 "activate",
786 G_CALLBACK(sp_file_new_from_template),
787 (gpointer) filepath);
789 if (view) {
790 // set null tip for now; later use a description from the template file
791 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
792 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
793 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
794 }
796 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
797 }
798 g_dir_close(dir);
799 }
800 }
802 // toss the dirname
803 g_free(dirname);
804 sources.pop_front();
805 }
806 }
808 void
809 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
810 {
811 gchar const **recent = prefs_get_recent_files();
812 if (recent) {
813 int i;
815 for (i = 0; recent[i] != NULL; i += 2) {
816 gchar const *uri = recent[i];
817 gchar const *name = recent[i + 1];
819 GtkWidget *item = gtk_menu_item_new_with_label(name);
820 gtk_widget_show(item);
821 g_signal_connect(G_OBJECT(item),
822 "activate",
823 G_CALLBACK(sp_recent_open),
824 (gpointer)uri);
825 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
826 }
828 g_free(recent);
829 } else {
830 GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
831 gtk_widget_show(item);
832 gtk_widget_set_sensitive(item, FALSE);
833 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
834 }
835 }
837 void
838 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
839 {
840 //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
841 // checkitem_toggled, checkitem_update, 0);
842 sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
843 checkitem_toggled, checkitem_update, 0);
844 sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
845 checkitem_toggled, checkitem_update, 0);
846 sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
847 checkitem_toggled, checkitem_update, 0);
848 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
849 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
850 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
851 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
852 sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
853 checkitem_toggled, checkitem_update, 0);
854 sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
855 checkitem_toggled, checkitem_update, 0);
856 }
858 /** \brief This function turns XML into a menu
859 \param menus This is the XML that defines the menu
860 \param menu Menu to be added to
861 \param view The View that this menu is being built for
863 This function is realitively simple as it just goes through the XML
864 and parses the individual elements. In the case of a submenu, it
865 just calls itself recursively. Because it is only reasonable to have
866 a couple of submenus, it is unlikely this will go more than two or
867 three times.
869 In the case of an unreconginzed verb, a menu item is made to identify
870 the verb that is missing, and display that. The menu item is also made
871 insensitive.
872 */
873 void
874 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
875 {
876 if (menus == NULL) return;
877 if (menu == NULL) return;
878 GSList *group = NULL;
880 for (Inkscape::XML::Node *menu_pntr = menus;
881 menu_pntr != NULL;
882 menu_pntr = menu_pntr->next()) {
883 if (!strcmp(menu_pntr->name(), "submenu")) {
884 GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
885 GtkWidget *submenu = gtk_menu_new();
886 sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
887 gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
888 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
889 continue;
890 }
891 if (!strcmp(menu_pntr->name(), "verb")) {
892 gchar const *verb_name = menu_pntr->attribute("verb-id");
893 Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
895 if (verb != NULL) {
896 if (menu_pntr->attribute("radio") != NULL) {
897 GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
898 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
899 if (menu_pntr->attribute("default") != NULL) {
900 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
901 }
902 } else {
903 sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
904 group = NULL;
905 }
906 } else {
907 gchar string[120];
908 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
909 string[119] = '\0'; /* may not be terminated */
910 GtkWidget *item = gtk_menu_item_new_with_label(string);
911 gtk_widget_set_sensitive(item, false);
912 gtk_widget_show(item);
913 gtk_menu_append(GTK_MENU(menu), item);
914 }
915 continue;
916 }
917 if (!strcmp(menu_pntr->name(), "separator")
918 // This was spelt wrong in the original version
919 // and so this is for backward compatibility. It can
920 // probably be dropped after the 0.44 release.
921 || !strcmp(menu_pntr->name(), "seperator")) {
922 GtkWidget *item = gtk_separator_menu_item_new();
923 gtk_widget_show(item);
924 gtk_menu_append(GTK_MENU(menu), item);
925 continue;
926 }
927 if (!strcmp(menu_pntr->name(), "template-list")) {
928 sp_menu_append_new_templates(menu, view);
929 continue;
930 }
931 if (!strcmp(menu_pntr->name(), "recent-file-list")) {
932 sp_menu_append_recent_documents(menu, view);
933 continue;
934 }
935 if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
936 sp_ui_checkboxes_menus(GTK_MENU(menu), view);
937 continue;
938 }
939 }
940 }
942 /** \brief Build the main tool bar
943 \param view View to build the bar for
945 Currently the main tool bar is built as a dynamic XML menu using
946 \c sp_ui_build_dyn_menus. This function builds the bar, and then
947 pass it to get items attached to it.
948 */
949 GtkWidget *
950 sp_ui_main_menubar(Inkscape::UI::View::View *view)
951 {
952 GtkWidget *mbar = gtk_menu_bar_new();
954 #ifdef GDK_WINDOWING_QUARTZ
955 ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
956 #endif
958 sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
960 #ifdef GDK_WINDOWING_QUARTZ
961 return NULL;
962 #else
963 return mbar;
964 #endif
965 }
967 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
968 desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
969 }
971 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
972 desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
973 sp_desktop_selection(desktop)->clear();
974 }
976 GtkWidget *
977 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
978 {
979 GtkWidget *m;
980 SPDesktop *dt;
982 dt = static_cast<SPDesktop*>(view);
984 m = gtk_menu_new();
986 /* Undo and Redo */
987 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
988 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
990 /* Separator */
991 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
993 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
994 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
995 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
997 /* Separator */
998 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1000 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1001 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1003 /* Item menu */
1004 if (item) {
1005 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1006 sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1007 }
1009 /* layer menu */
1010 SPGroup *group=NULL;
1011 if (item) {
1012 if (SP_IS_GROUP(item)) {
1013 group = SP_GROUP(item);
1014 } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1015 group = SP_GROUP(SP_OBJECT_PARENT(item));
1016 }
1017 }
1019 if (( group && group != dt->currentLayer() ) ||
1020 ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1021 /* Separator */
1022 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1023 }
1025 if ( group && group != dt->currentLayer() ) {
1026 /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1027 gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1028 GtkWidget *w = gtk_menu_item_new_with_label(label);
1029 g_free(label);
1030 g_object_set_data(G_OBJECT(w), "group", group);
1031 g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1032 gtk_widget_show(w);
1033 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1034 }
1036 if ( dt->currentLayer() != dt->currentRoot() ) {
1037 if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1038 GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1039 g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1040 gtk_widget_show(w);
1041 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1043 }
1044 }
1046 return m;
1047 }
1049 /* Drag and Drop */
1050 void
1051 sp_ui_drag_data_received(GtkWidget *widget,
1052 GdkDragContext *drag_context,
1053 gint x, gint y,
1054 GtkSelectionData *data,
1055 guint info,
1056 guint /*event_time*/,
1057 gpointer /*user_data*/)
1058 {
1059 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1060 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1062 switch (info) {
1063 #if ENABLE_MAGIC_COLORS
1064 case APP_X_INKY_COLOR:
1065 {
1066 int destX = 0;
1067 int destY = 0;
1068 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1069 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1071 SPItem *item = desktop->item_at_point( where, true );
1072 if ( item )
1073 {
1074 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1076 if ( data->length >= 8 ) {
1077 cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1079 gchar c[64] = {0};
1080 // Careful about endian issues.
1081 guint16* dataVals = (guint16*)data->data;
1082 sp_svg_write_color( c, sizeof(c),
1083 SP_RGBA32_U_COMPOSE(
1084 0x0ff & (dataVals[0] >> 8),
1085 0x0ff & (dataVals[1] >> 8),
1086 0x0ff & (dataVals[2] >> 8),
1087 0xff // can't have transparency in the color itself
1088 //0x0ff & (data->data[3] >> 8),
1089 ));
1090 SPCSSAttr *css = sp_repr_css_attr_new();
1091 bool updatePerformed = false;
1093 if ( data->length > 14 ) {
1094 int flags = dataVals[4];
1096 // piggie-backed palette entry info
1097 int index = dataVals[5];
1098 Glib::ustring palName;
1099 for ( int i = 0; i < dataVals[6]; i++ ) {
1100 palName += (gunichar)dataVals[7+i];
1101 }
1103 // Now hook in a magic tag of some sort.
1104 if ( !palName.empty() && (flags & 1) ) {
1105 gchar* str = g_strdup_printf("%d|", index);
1106 palName.insert( 0, str );
1107 g_free(str);
1108 str = 0;
1110 sp_object_setAttribute( SP_OBJECT(item),
1111 fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1112 palName.c_str(),
1113 false );
1114 item->updateRepr();
1116 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1117 updatePerformed = true;
1118 }
1119 }
1121 if ( !updatePerformed ) {
1122 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1123 }
1125 sp_desktop_apply_css_recursive( item, css, true );
1126 item->updateRepr();
1128 sp_document_done( doc , SP_VERB_NONE,
1129 _("Drop color"));
1131 if ( srgbProf ) {
1132 cmsCloseProfile( srgbProf );
1133 }
1134 }
1135 }
1136 }
1137 break;
1138 #endif // ENABLE_MAGIC_COLORS
1140 case APP_X_COLOR:
1141 {
1142 int destX = 0;
1143 int destY = 0;
1144 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1145 NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1146 NR::Point const button_dt(desktop->w2d(where));
1147 NR::Point const button_doc(desktop->dt2doc(button_dt));
1149 if ( data->length == 8 ) {
1150 gchar c[64] = {0};
1151 // Careful about endian issues.
1152 guint16* dataVals = (guint16*)data->data;
1153 sp_svg_write_color( c, 64,
1154 SP_RGBA32_U_COMPOSE(
1155 0x0ff & (dataVals[0] >> 8),
1156 0x0ff & (dataVals[1] >> 8),
1157 0x0ff & (dataVals[2] >> 8),
1158 0xff // can't have transparency in the color itself
1159 //0x0ff & (data->data[3] >> 8),
1160 ));
1162 SPItem *item = desktop->item_at_point( where, true );
1164 bool consumed = false;
1165 if (desktop->event_context && desktop->event_context->get_drag()) {
1166 consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1167 if (consumed) {
1168 sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1169 desktop->event_context->get_drag()->updateDraggers();
1170 }
1171 }
1173 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1174 // consumed = sp_text_context_drop_color(c, button_doc);
1175 // if (consumed) {
1176 // sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1177 // }
1178 //}
1180 if (!consumed && item) {
1181 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1182 if (fillnotstroke &&
1183 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1184 Path *livarot_path = Path_for_item(item, true, true);
1185 livarot_path->ConvertWithBackData(0.04);
1187 boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1188 if (position) {
1189 NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1190 NR::Point delta = nearest - button_doc;
1191 delta = desktop->d2w(delta);
1192 double stroke_tolerance =
1193 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1194 desktop->current_zoom() *
1195 SP_OBJECT_STYLE (item)->stroke_width.computed *
1196 to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1197 : 0.0)
1198 + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100);
1200 if (NR::L2 (delta) < stroke_tolerance) {
1201 fillnotstroke = false;
1202 }
1203 }
1204 delete livarot_path;
1205 }
1207 SPCSSAttr *css = sp_repr_css_attr_new();
1208 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1210 sp_desktop_apply_css_recursive( item, css, true );
1211 item->updateRepr();
1213 sp_document_done( doc , SP_VERB_NONE,
1214 _("Drop color"));
1215 }
1216 }
1217 }
1218 break;
1220 case SVG_DATA:
1221 case SVG_XML_DATA: {
1222 gchar *svgdata = (gchar *)data->data;
1224 Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1226 if (rnewdoc == NULL) {
1227 sp_ui_error_dialog(_("Could not parse SVG data"));
1228 return;
1229 }
1231 Inkscape::XML::Node *repr = rnewdoc->root();
1232 gchar const *style = repr->attribute("style");
1234 Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1235 newgroup->setAttribute("style", style);
1237 Inkscape::XML::Document * xml_doc = sp_document_repr_doc(doc);
1238 for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1239 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1240 newgroup->appendChild(newchild);
1241 }
1243 Inkscape::GC::release(rnewdoc);
1245 // Add it to the current layer
1247 // Greg's edits to add intelligent positioning of svg drops
1248 SPObject *new_obj = NULL;
1249 new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1251 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1252 selection->set(SP_ITEM(new_obj));
1253 // To move the imported object, we must temporarily set the "transform pattern with
1254 // object" option.
1255 {
1256 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1257 prefs_set_int_attribute("options.transform", "pattern", 1);
1258 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1259 boost::optional<Geom::Rect> sel_bbox = selection->bounds();
1260 if (sel_bbox) {
1261 Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1262 sp_selection_move_relative(selection, m);
1263 }
1264 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1265 }
1267 Inkscape::GC::release(newgroup);
1268 sp_document_done(doc, SP_VERB_NONE,
1269 _("Drop SVG"));
1270 break;
1271 }
1273 case URI_LIST: {
1274 gchar *uri = (gchar *)data->data;
1275 sp_ui_import_files(uri);
1276 break;
1277 }
1279 case PNG_DATA:
1280 case JPEG_DATA:
1281 case IMAGE_DATA: {
1282 char tmp[1024];
1284 StringOutputStream outs;
1285 Base64OutputStream b64out(outs);
1286 b64out.setColumnWidth(0);
1288 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1290 Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1292 for ( int i = 0; i < data->length; i++ ) {
1293 b64out.put( data->data[i] );
1294 }
1295 b64out.close();
1298 Glib::ustring str = outs.getString();
1300 snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1301 str.insert( 0, tmp );
1302 newImage->setAttribute("xlink:href", str.c_str());
1304 GError *error = NULL;
1305 GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1306 if ( loader ) {
1307 error = NULL;
1308 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1309 GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1310 if ( pbuf ) {
1311 int width = gdk_pixbuf_get_width(pbuf);
1312 int height = gdk_pixbuf_get_height(pbuf);
1313 snprintf( tmp, sizeof(tmp), "%d", width );
1314 newImage->setAttribute("width", tmp);
1316 snprintf( tmp, sizeof(tmp), "%d", height );
1317 newImage->setAttribute("height", tmp);
1318 }
1319 }
1320 }
1322 // Add it to the current layer
1323 desktop->currentLayer()->appendChildRepr(newImage);
1325 Inkscape::GC::release(newImage);
1326 sp_document_done( doc , SP_VERB_NONE,
1327 _("Drop bitmap image"));
1328 break;
1329 }
1330 }
1331 }
1333 #include "gradient-context.h"
1335 void sp_ui_drag_motion( GtkWidget */*widget*/,
1336 GdkDragContext */*drag_context*/,
1337 gint /*x*/, gint /*y*/,
1338 GtkSelectionData */*data*/,
1339 guint /*info*/,
1340 guint /*event_time*/,
1341 gpointer /*user_data*/)
1342 {
1343 // SPDocument *doc = SP_ACTIVE_DOCUMENT;
1344 // SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1347 // g_message("drag-n-drop motion (%4d, %4d) at %d", x, y, event_time);
1348 }
1350 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1351 GdkDragContext */*drag_context*/,
1352 guint /*event_time*/,
1353 gpointer /*user_data*/ )
1354 {
1355 // g_message("drag-n-drop leave at %d", event_time);
1356 }
1358 static void
1359 sp_ui_import_files(gchar *buffer)
1360 {
1361 GList *list = gnome_uri_list_extract_filenames(buffer);
1362 if (!list)
1363 return;
1364 g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1365 g_list_foreach(list, (GFunc) g_free, NULL);
1366 g_list_free(list);
1367 }
1369 static void
1370 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1371 {
1372 if (filename) {
1373 if (strlen((char const *)filename) > 2)
1374 sp_ui_import_one_file((char const *)filename);
1375 }
1376 }
1378 static void
1379 sp_ui_import_one_file(char const *filename)
1380 {
1381 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1382 if (!doc) return;
1384 if (filename == NULL) return;
1386 // Pass off to common implementation
1387 // TODO might need to get the proper type of Inkscape::Extension::Extension
1388 file_import( doc, filename, NULL );
1389 }
1391 void
1392 sp_ui_error_dialog(gchar const *message)
1393 {
1394 GtkWidget *dlg;
1395 gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1397 dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1398 GTK_BUTTONS_CLOSE, "%s", safeMsg);
1399 sp_transientize(dlg);
1400 gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1401 gtk_dialog_run(GTK_DIALOG(dlg));
1402 gtk_widget_destroy(dlg);
1403 g_free(safeMsg);
1404 }
1406 bool
1407 sp_ui_overwrite_file(gchar const *filename)
1408 {
1409 bool return_value = FALSE;
1411 if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1412 Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1413 gchar* baseName = g_path_get_basename( filename );
1414 gchar* dirName = g_path_get_dirname( filename );
1415 GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1416 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1417 GTK_MESSAGE_QUESTION,
1418 GTK_BUTTONS_NONE,
1419 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1420 "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1421 baseName,
1422 dirName
1423 );
1424 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1425 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1426 _("Replace"), GTK_RESPONSE_YES,
1427 NULL );
1428 gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1430 if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1431 return_value = TRUE;
1432 } else {
1433 return_value = FALSE;
1434 }
1435 gtk_widget_destroy(dialog);
1436 g_free( baseName );
1437 g_free( dirName );
1438 } else {
1439 return_value = TRUE;
1440 }
1442 return return_value;
1443 }
1445 static void
1446 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1447 {
1448 return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1449 }
1451 static void
1452 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1453 {
1454 void *child = GTK_BIN (data)->child;
1455 //child is either
1456 //- a GtkHBox, whose first child is a label displaying name if the menu
1457 //item has an accel key
1458 //- a GtkLabel if the menu has no accel key
1459 if (GTK_IS_LABEL(child)) {
1460 gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1461 } else if (GTK_IS_HBOX(child)) {
1462 gtk_label_set_markup_with_mnemonic(
1463 GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1464 name.c_str());
1465 }//else sp_ui_menu_append_item_from_verb has been modified and can set
1466 //a menu item in yet another way...
1467 }
1470 /*
1471 Local Variables:
1472 mode:c++
1473 c-file-style:"stroustrup"
1474 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1475 indent-tabs-mode:nil
1476 fill-column:99
1477 End:
1478 */
1479 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :