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