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 {"text/uri-list", 0, URI_LIST},
93 {"image/svg+xml", 0, SVG_XML_DATA},
94 {"image/svg", 0, SVG_DATA},
95 {"image/png", 0, PNG_DATA},
96 {"image/jpeg", 0, JPEG_DATA},
97 #if ENABLE_MAGIC_COLORS
98 {"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
99 #endif // ENABLE_MAGIC_COLORS
100 {"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 if (editable) {
156 g_object_set_data(G_OBJECT(vw), "window", win);
158 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
159 SPDesktop* desktop = desktop_widget->desktop;
161 desktop_widget->window = win;
163 /* fixme: doesn't allow making window any smaller than this */
164 win->set_default_size(640, 480);
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(NR::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 gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
221 gtk_widget_show(GTK_WIDGET(vw));
223 if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
224 {
225 std::vector<gchar*> types;
227 GSList *list = gdk_pixbuf_get_formats();
228 while ( list ) {
229 int i = 0;
230 GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
231 gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
232 for ( i = 0; typesXX[i]; i++ ) {
233 types.push_back(g_strdup(typesXX[i]));
234 }
235 g_strfreev(typesXX);
237 list = g_slist_next(list);
238 }
239 completeDropTargetsCount = nui_drop_target_entries + types.size();
240 completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
241 for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
242 completeDropTargets[i] = ui_drop_target_entries[i];
243 }
244 int pos = nui_drop_target_entries;
246 for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
247 completeDropTargets[pos].target = *it;
248 completeDropTargets[pos].flags = 0;
249 completeDropTargets[pos].info = IMAGE_DATA;
250 pos++;
251 }
252 }
254 gtk_drag_dest_set((GtkWidget*)win->gobj(),
255 GTK_DEST_DEFAULT_ALL,
256 completeDropTargets,
257 completeDropTargetsCount,
258 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
261 g_signal_connect(G_OBJECT(win->gobj()),
262 "drag_data_received",
263 G_CALLBACK(sp_ui_drag_data_received),
264 NULL);
265 g_signal_connect(G_OBJECT(win->gobj()),
266 "drag_motion",
267 G_CALLBACK(sp_ui_drag_motion),
268 NULL);
269 g_signal_connect(G_OBJECT(win->gobj()),
270 "drag_leave",
271 G_CALLBACK(sp_ui_drag_leave),
272 NULL);
273 win->show();
275 // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
276 inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
277 }
279 void
280 sp_ui_new_view()
281 {
282 SPDocument *document;
283 SPViewWidget *dtw;
285 document = SP_ACTIVE_DOCUMENT;
286 if (!document) return;
288 dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
289 g_return_if_fail(dtw != NULL);
291 sp_create_window(dtw, TRUE);
292 sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
293 sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
294 }
296 /* TODO: not yet working */
297 /* To be re-enabled (by adding to menu) once it works. */
298 void
299 sp_ui_new_view_preview()
300 {
301 SPDocument *document;
302 SPViewWidget *dtw;
304 document = SP_ACTIVE_DOCUMENT;
305 if (!document) return;
307 dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
308 g_return_if_fail(dtw != NULL);
309 sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
311 sp_create_window(dtw, FALSE);
312 }
314 /**
315 * \param widget unused
316 */
317 void
318 sp_ui_close_view(GtkWidget */*widget*/)
319 {
320 if (SP_ACTIVE_DESKTOP == NULL) {
321 return;
322 }
323 if ((SP_ACTIVE_DESKTOP)->shutdown()) {
324 return;
325 }
326 SP_ACTIVE_DESKTOP->destroyWidget();
327 }
330 /**
331 * sp_ui_close_all
332 *
333 * This function is called to exit the program, and iterates through all
334 * open document view windows, attempting to close each in turn. If the
335 * view has unsaved information, the user will be prompted to save,
336 * discard, or cancel.
337 *
338 * Returns FALSE if the user cancels the close_all operation, TRUE
339 * otherwise.
340 */
341 unsigned int
342 sp_ui_close_all(void)
343 {
344 /* Iterate through all the windows, destroying each in the order they
345 become active */
346 while (SP_ACTIVE_DESKTOP) {
347 if ((SP_ACTIVE_DESKTOP)->shutdown()) {
348 /* The user cancelled the operation, so end doing the close */
349 return FALSE;
350 }
351 SP_ACTIVE_DESKTOP->destroyWidget();
352 }
354 return TRUE;
355 }
357 /*
358 * Some day when the right-click menus are ready to start working
359 * smarter with the verbs, we'll need to change this NULL being
360 * sent to sp_action_perform to something useful, or set some kind
361 * of global "right-clicked position" variable for actions to
362 * investigate when they're called.
363 */
364 static void
365 sp_ui_menu_activate(void */*object*/, SPAction *action)
366 {
367 sp_action_perform(action, NULL);
368 }
370 static void
371 sp_ui_menu_select_action(void */*object*/, SPAction *action)
372 {
373 action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
374 }
376 static void
377 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
378 {
379 action->view->tipsMessageContext()->clear();
380 }
382 static void
383 sp_ui_menu_select(gpointer object, gpointer tip)
384 {
385 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
386 view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
387 }
389 static void
390 sp_ui_menu_deselect(gpointer object)
391 {
392 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
393 view->tipsMessageContext()->clear();
394 }
396 /**
397 * sp_ui_menuitem_add_icon
398 *
399 * Creates and attaches a scaled icon to the given menu item.
400 *
401 */
402 void
403 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
404 {
405 GtkWidget *icon;
407 icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
408 gtk_widget_show(icon);
409 gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
410 } // end of sp_ui_menu_add_icon
412 /**
413 * sp_ui_menu_append_item
414 *
415 * Appends a UI item with specific info for Inkscape/Sodipodi.
416 *
417 */
418 static GtkWidget *
419 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
420 gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
421 gpointer data, gboolean with_mnemonic = TRUE )
422 {
423 GtkWidget *item;
425 if (stock) {
426 item = gtk_image_menu_item_new_from_stock(stock, NULL);
427 } else if (label) {
428 item = (with_mnemonic)
429 ? gtk_image_menu_item_new_with_mnemonic(label) :
430 gtk_image_menu_item_new_with_label(label);
431 } else {
432 item = gtk_separator_menu_item_new();
433 }
435 gtk_widget_show(item);
437 if (callback) {
438 g_signal_connect(G_OBJECT(item), "activate", callback, data);
439 }
441 if (tip && view) {
442 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
443 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
444 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
445 }
447 gtk_menu_append(GTK_MENU(menu), item);
449 return item;
451 } // end of sp_ui_menu_append_item()
453 /**
454 \brief a wrapper around gdk_keyval_name producing (when possible) characters, not names
455 */
456 static gchar const *
457 sp_key_name(guint keyval)
458 {
459 /* TODO: Compare with the definition of gtk_accel_label_refetch in gtk/gtkaccellabel.c (or
460 simply use GtkAccelLabel as the TODO comment in sp_ui_shortcut_string suggests). */
461 gchar const *n = gdk_keyval_name(gdk_keyval_to_upper(keyval));
463 if (!strcmp(n, "asciicircum")) return "^";
464 else if (!strcmp(n, "parenleft" )) return "(";
465 else if (!strcmp(n, "parenright" )) return ")";
466 else if (!strcmp(n, "plus" )) return "+";
467 else if (!strcmp(n, "minus" )) return "-";
468 else if (!strcmp(n, "asterisk" )) return "*";
469 else if (!strcmp(n, "KP_Multiply")) return "*";
470 else if (!strcmp(n, "Delete" )) return "Del";
471 else if (!strcmp(n, "Page_Up" )) return "PgUp";
472 else if (!strcmp(n, "Page_Down" )) return "PgDn";
473 else if (!strcmp(n, "grave" )) return "`";
474 else if (!strcmp(n, "numbersign" )) return "#";
475 else if (!strcmp(n, "bar" )) return "|";
476 else if (!strcmp(n, "slash" )) return "/";
477 else if (!strcmp(n, "exclam" )) return "!";
478 else if (!strcmp(n, "percent" )) return "%";
479 else return n;
480 }
483 /**
484 * \param shortcut A GDK keyval OR'd with SP_SHORTCUT_blah_MASK values.
485 * \param c Points to a buffer at least 256 bytes long.
486 */
487 void
488 sp_ui_shortcut_string(unsigned const shortcut, gchar *const c)
489 {
490 /* TODO: This function shouldn't exist. Our callers should use GtkAccelLabel instead of
491 * a generic GtkLabel containing this string, and should call gtk_widget_add_accelerator.
492 * Will probably need to change sp_shortcut_invoke callers.
493 *
494 * The existing gtk_label_new_with_mnemonic call can be replaced with
495 * g_object_new(GTK_TYPE_ACCEL_LABEL, NULL) followed by
496 * gtk_label_set_text_with_mnemonic(lbl, str).
497 */
498 static GtkAccelLabelClass const &accel_lbl_cls
499 = *(GtkAccelLabelClass const *) g_type_class_peek_static(GTK_TYPE_ACCEL_LABEL);
501 struct { unsigned test; char const *name; } const modifier_tbl[] = {
502 { SP_SHORTCUT_SHIFT_MASK, accel_lbl_cls.mod_name_shift },
503 { SP_SHORTCUT_CONTROL_MASK, accel_lbl_cls.mod_name_control },
504 { SP_SHORTCUT_ALT_MASK, accel_lbl_cls.mod_name_alt }
505 };
507 gchar *p = c;
508 gchar *end = p + 256;
510 for (unsigned i = 0; i < G_N_ELEMENTS(modifier_tbl); ++i) {
511 if ((shortcut & modifier_tbl[i].test)
512 && (p < end))
513 {
514 p += g_snprintf(p, end - p, "%s%s",
515 modifier_tbl[i].name,
516 accel_lbl_cls.mod_separator);
517 }
518 }
519 if (p < end) {
520 p += g_snprintf(p, end - p, "%s", sp_key_name(shortcut & 0xffffff));
521 }
522 end[-1] = '\0'; // snprintf doesn't guarantee to nul-terminate the string.
523 }
525 void
526 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
527 {
528 SPAction *action;
529 unsigned int shortcut;
530 gchar *s;
531 gchar key[256];
532 gchar *atitle;
534 action = verb->get_action(NULL);
535 if (!action)
536 return;
538 atitle = sp_action_get_title(action);
540 s = g_stpcpy(c, atitle);
542 g_free(atitle);
544 shortcut = sp_shortcut_get_primary(verb);
545 if (shortcut) {
546 s = g_stpcpy(s, " (");
547 sp_ui_shortcut_string(shortcut, key);
548 s = g_stpcpy(s, key);
549 s = g_stpcpy(s, ")");
550 }
551 }
554 /**
555 * sp_ui_menu_append_item_from_verb
556 *
557 * Appends a custom menu UI from a verb.
558 *
559 */
561 static GtkWidget *
562 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
563 {
564 SPAction *action;
565 GtkWidget *item;
567 if (verb->get_code() == SP_VERB_NONE) {
569 item = gtk_separator_menu_item_new();
571 } else {
572 unsigned int shortcut;
574 action = verb->get_action(view);
576 if (!action) return NULL;
578 shortcut = sp_shortcut_get_primary(verb);
579 if (shortcut) {
580 gchar c[256];
581 sp_ui_shortcut_string(shortcut, c);
582 GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
583 GtkWidget *const name_lbl = gtk_label_new("");
584 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
585 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
586 gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
587 GtkWidget *const accel_lbl = gtk_label_new(c);
588 gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
589 gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
590 gtk_widget_show_all(hb);
591 if (radio) {
592 item = gtk_radio_menu_item_new (group);
593 } else {
594 item = gtk_image_menu_item_new();
595 }
596 gtk_container_add((GtkContainer *) item, hb);
597 } else {
598 if (radio) {
599 item = gtk_radio_menu_item_new (group);
600 } else {
601 item = gtk_image_menu_item_new ();
602 }
603 GtkWidget *const name_lbl = gtk_label_new("");
604 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
605 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
606 gtk_container_add((GtkContainer *) item, name_lbl);
607 }
609 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
610 if (!action->sensitive) {
611 gtk_widget_set_sensitive(item, FALSE);
612 }
614 if (action->image) {
615 sp_ui_menuitem_add_icon(item, action->image);
616 }
617 gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
618 g_signal_connect( G_OBJECT(item), "activate",
619 G_CALLBACK(sp_ui_menu_activate), action );
621 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
622 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
623 }
625 gtk_widget_show(item);
626 gtk_menu_append(GTK_MENU(menu), item);
628 return item;
630 } // end of sp_ui_menu_append_item_from_verb
633 static void
634 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
635 {
636 gchar const *pref = (gchar const *) user_data;
637 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
639 gchar const *pref_path;
640 if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen())
641 pref_path = g_strconcat("fullscreen.", pref, NULL);
642 else
643 pref_path = g_strconcat("window.", pref, NULL);
645 gboolean checked = gtk_check_menu_item_get_active(menuitem);
646 prefs_set_int_attribute(pref_path, "state", checked);
648 reinterpret_cast<SPDesktop*>(view)->layoutWidget();
649 }
651 static gboolean
652 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
653 {
654 GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
656 gchar const *pref = (gchar const *) user_data;
657 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
659 gchar const *pref_path;
660 if ((static_cast<SPDesktop*>(view))->is_fullscreen())
661 pref_path = g_strconcat("fullscreen.", pref, NULL);
662 else
663 pref_path = g_strconcat("window.", pref, NULL);
665 gint ison = prefs_get_int_attribute_limited(pref_path, "state", 1, 0, 1);
667 g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
668 gtk_check_menu_item_set_active(menuitem, ison);
669 g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
671 return FALSE;
672 }
675 void
676 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
677 void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
678 gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
679 Inkscape::Verb *verb)
680 {
681 GtkWidget *item;
683 unsigned int shortcut = 0;
684 SPAction *action = NULL;
686 if (verb) {
687 shortcut = sp_shortcut_get_primary(verb);
688 action = verb->get_action(view);
689 }
691 if (verb && shortcut) {
692 gchar c[256];
693 sp_ui_shortcut_string(shortcut, c);
695 GtkWidget *hb = gtk_hbox_new(FALSE, 16);
697 {
698 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
699 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
700 gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
701 }
703 {
704 GtkWidget *l = gtk_label_new(c);
705 gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
706 gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
707 }
709 gtk_widget_show_all(hb);
711 item = gtk_check_menu_item_new();
712 gtk_container_add((GtkContainer *) item, hb);
713 } else {
714 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
715 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
716 item = gtk_check_menu_item_new();
717 gtk_container_add((GtkContainer *) item, l);
718 }
719 #if 0
720 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
721 if (!action->sensitive) {
722 gtk_widget_set_sensitive(item, FALSE);
723 }
724 #endif
725 gtk_widget_show(item);
727 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
729 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
731 g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
732 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
734 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
735 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
736 }
738 static void
739 sp_recent_open(GtkWidget */*widget*/, gchar const *uri)
740 {
741 sp_file_open(uri, NULL);
742 }
744 static void
745 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
746 {
747 sp_file_new(uri);
748 }
750 void
751 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
752 {
753 std::list<gchar *> sources;
754 sources.push_back( profile_path("templates") ); // first try user's local dir
755 sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
757 // Use this loop to iterate through a list of possible document locations.
758 while (!sources.empty()) {
759 gchar *dirname = sources.front();
761 if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
762 GError *err = 0;
763 GDir *dir = g_dir_open(dirname, 0, &err);
765 if (dir) {
766 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
767 if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
768 continue; // skip non-svg files
770 gchar *basename = g_path_get_basename(file);
771 if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
772 continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
774 gchar const *filepath = g_build_filename(dirname, file, NULL);
775 gchar *dupfile = g_strndup(file, strlen(file) - 4);
776 gchar *filename = g_filename_to_utf8(dupfile, -1, NULL, NULL, NULL);
777 g_free(dupfile);
778 GtkWidget *item = gtk_menu_item_new_with_label(filename);
779 g_free(filename);
781 gtk_widget_show(item);
782 // how does "filepath" ever get freed?
783 g_signal_connect(G_OBJECT(item),
784 "activate",
785 G_CALLBACK(sp_file_new_from_template),
786 (gpointer) filepath);
788 if (view) {
789 // set null tip for now; later use a description from the template file
790 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
791 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
792 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
793 }
795 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
796 }
797 g_dir_close(dir);
798 }
799 }
801 // toss the dirname
802 g_free(dirname);
803 sources.pop_front();
804 }
805 }
807 void
808 sp_menu_append_recent_documents(GtkWidget *menu, Inkscape::UI::View::View* /* view */)
809 {
810 gchar const **recent = prefs_get_recent_files();
811 if (recent) {
812 int i;
814 for (i = 0; recent[i] != NULL; i += 2) {
815 gchar const *uri = recent[i];
816 gchar const *name = recent[i + 1];
818 GtkWidget *item = gtk_menu_item_new_with_label(name);
819 gtk_widget_show(item);
820 g_signal_connect(G_OBJECT(item),
821 "activate",
822 G_CALLBACK(sp_recent_open),
823 (gpointer)uri);
824 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
825 }
827 g_free(recent);
828 } else {
829 GtkWidget *item = gtk_menu_item_new_with_label(_("None"));
830 gtk_widget_show(item);
831 gtk_widget_set_sensitive(item, FALSE);
832 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
833 }
834 }
836 void
837 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
838 {
839 //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
840 // checkitem_toggled, checkitem_update, 0);
841 sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
842 checkitem_toggled, checkitem_update, 0);
843 sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
844 checkitem_toggled, checkitem_update, 0);
845 sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
846 checkitem_toggled, checkitem_update, 0);
847 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
848 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
849 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
850 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
851 sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
852 checkitem_toggled, checkitem_update, 0);
853 sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
854 checkitem_toggled, checkitem_update, 0);
855 }
857 /** \brief This function turns XML into a menu
858 \param menus This is the XML that defines the menu
859 \param menu Menu to be added to
860 \param view The View that this menu is being built for
862 This function is realitively simple as it just goes through the XML
863 and parses the individual elements. In the case of a submenu, it
864 just calls itself recursively. Because it is only reasonable to have
865 a couple of submenus, it is unlikely this will go more than two or
866 three times.
868 In the case of an unreconginzed verb, a menu item is made to identify
869 the verb that is missing, and display that. The menu item is also made
870 insensitive.
871 */
872 void
873 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
874 {
875 if (menus == NULL) return;
876 if (menu == NULL) return;
877 GSList *group = NULL;
879 for (Inkscape::XML::Node *menu_pntr = menus;
880 menu_pntr != NULL;
881 menu_pntr = menu_pntr->next()) {
882 if (!strcmp(menu_pntr->name(), "submenu")) {
883 GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
884 GtkWidget *submenu = gtk_menu_new();
885 sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
886 gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
887 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
888 continue;
889 }
890 if (!strcmp(menu_pntr->name(), "verb")) {
891 gchar const *verb_name = menu_pntr->attribute("verb-id");
892 Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
894 if (verb != NULL) {
895 if (menu_pntr->attribute("radio") != NULL) {
896 GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
897 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
898 if (menu_pntr->attribute("default") != NULL) {
899 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
900 }
901 } else {
902 sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
903 group = NULL;
904 }
905 } else {
906 gchar string[120];
907 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
908 string[119] = '\0'; /* may not be terminated */
909 GtkWidget *item = gtk_menu_item_new_with_label(string);
910 gtk_widget_set_sensitive(item, false);
911 gtk_widget_show(item);
912 gtk_menu_append(GTK_MENU(menu), item);
913 }
914 continue;
915 }
916 if (!strcmp(menu_pntr->name(), "separator")
917 // This was spelt wrong in the original version
918 // and so this is for backward compatibility. It can
919 // probably be dropped after the 0.44 release.
920 || !strcmp(menu_pntr->name(), "seperator")) {
921 GtkWidget *item = gtk_separator_menu_item_new();
922 gtk_widget_show(item);
923 gtk_menu_append(GTK_MENU(menu), item);
924 continue;
925 }
926 if (!strcmp(menu_pntr->name(), "template-list")) {
927 sp_menu_append_new_templates(menu, view);
928 continue;
929 }
930 if (!strcmp(menu_pntr->name(), "recent-file-list")) {
931 sp_menu_append_recent_documents(menu, view);
932 continue;
933 }
934 if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
935 sp_ui_checkboxes_menus(GTK_MENU(menu), view);
936 continue;
937 }
938 }
939 }
941 /** \brief Build the main tool bar
942 \param view View to build the bar for
944 Currently the main tool bar is built as a dynamic XML menu using
945 \c sp_ui_build_dyn_menus. This function builds the bar, and then
946 pass it to get items attached to it.
947 */
948 GtkWidget *
949 sp_ui_main_menubar(Inkscape::UI::View::View *view)
950 {
951 GtkWidget *mbar = gtk_menu_bar_new();
953 #ifdef GDK_WINDOWING_QUARTZ
954 ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
955 #endif
957 sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
959 #ifdef GDK_WINDOWING_QUARTZ
960 return NULL;
961 #else
962 return mbar;
963 #endif
964 }
966 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
967 desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
968 }
970 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
971 desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
972 sp_desktop_selection(desktop)->clear();
973 }
975 GtkWidget *
976 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
977 {
978 GtkWidget *m;
979 SPDesktop *dt;
981 dt = static_cast<SPDesktop*>(view);
983 m = gtk_menu_new();
985 /* Undo and Redo */
986 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
987 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
989 /* Separator */
990 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
992 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
993 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
994 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
996 /* Separator */
997 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
999 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1000 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1002 /* Item menu */
1003 if (item) {
1004 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1005 sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1006 }
1008 /* layer menu */
1009 SPGroup *group=NULL;
1010 if (item) {
1011 if (SP_IS_GROUP(item)) {
1012 group = SP_GROUP(item);
1013 } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1014 group = SP_GROUP(SP_OBJECT_PARENT(item));
1015 }
1016 }
1018 if (( group && group != dt->currentLayer() ) ||
1019 ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1020 /* Separator */
1021 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1022 }
1024 if ( group && group != dt->currentLayer() ) {
1025 /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1026 gchar *label=g_strdup_printf(_("Enter group #%s"), SP_OBJECT_ID(group));
1027 GtkWidget *w = gtk_menu_item_new_with_label(label);
1028 g_free(label);
1029 g_object_set_data(G_OBJECT(w), "group", group);
1030 g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1031 gtk_widget_show(w);
1032 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1033 }
1035 if ( dt->currentLayer() != dt->currentRoot() ) {
1036 if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1037 GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1038 g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1039 gtk_widget_show(w);
1040 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1042 }
1043 }
1045 return m;
1046 }
1048 /* Drag and Drop */
1049 void
1050 sp_ui_drag_data_received(GtkWidget *widget,
1051 GdkDragContext *drag_context,
1052 gint x, gint y,
1053 GtkSelectionData *data,
1054 guint info,
1055 guint /*event_time*/,
1056 gpointer /*user_data*/)
1057 {
1058 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1059 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1061 switch (info) {
1062 #if ENABLE_MAGIC_COLORS
1063 case APP_X_INKY_COLOR:
1064 {
1065 int destX = 0;
1066 int destY = 0;
1067 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1068 NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1070 SPItem *item = desktop->item_at_point( where, true );
1071 if ( item )
1072 {
1073 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1075 if ( data->length >= 8 ) {
1076 cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1078 gchar c[64] = {0};
1079 // Careful about endian issues.
1080 guint16* dataVals = (guint16*)data->data;
1081 sp_svg_write_color( c, sizeof(c),
1082 SP_RGBA32_U_COMPOSE(
1083 0x0ff & (dataVals[0] >> 8),
1084 0x0ff & (dataVals[1] >> 8),
1085 0x0ff & (dataVals[2] >> 8),
1086 0xff // can't have transparency in the color itself
1087 //0x0ff & (data->data[3] >> 8),
1088 ));
1089 SPCSSAttr *css = sp_repr_css_attr_new();
1090 bool updatePerformed = false;
1092 if ( data->length > 14 ) {
1093 int flags = dataVals[4];
1095 // piggie-backed palette entry info
1096 int index = dataVals[5];
1097 Glib::ustring palName;
1098 for ( int i = 0; i < dataVals[6]; i++ ) {
1099 palName += (gunichar)dataVals[7+i];
1100 }
1102 // Now hook in a magic tag of some sort.
1103 if ( !palName.empty() && (flags & 1) ) {
1104 gchar* str = g_strdup_printf("%d|", index);
1105 palName.insert( 0, str );
1106 g_free(str);
1107 str = 0;
1109 sp_object_setAttribute( SP_OBJECT(item),
1110 fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1111 palName.c_str(),
1112 false );
1113 item->updateRepr();
1115 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1116 updatePerformed = true;
1117 }
1118 }
1120 if ( !updatePerformed ) {
1121 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1122 }
1124 sp_desktop_apply_css_recursive( item, css, true );
1125 item->updateRepr();
1127 sp_document_done( doc , SP_VERB_NONE,
1128 _("Drop color"));
1130 if ( srgbProf ) {
1131 cmsCloseProfile( srgbProf );
1132 }
1133 }
1134 }
1135 }
1136 break;
1137 #endif // ENABLE_MAGIC_COLORS
1139 case APP_X_COLOR:
1140 {
1141 int destX = 0;
1142 int destY = 0;
1143 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1144 NR::Point where( sp_canvas_window_to_world( desktop->canvas, NR::Point( destX, destY ) ) );
1145 NR::Point const button_dt(desktop->w2d(where));
1146 NR::Point const button_doc(desktop->dt2doc(button_dt));
1148 if ( data->length == 8 ) {
1149 gchar c[64] = {0};
1150 // Careful about endian issues.
1151 guint16* dataVals = (guint16*)data->data;
1152 sp_svg_write_color( c, 64,
1153 SP_RGBA32_U_COMPOSE(
1154 0x0ff & (dataVals[0] >> 8),
1155 0x0ff & (dataVals[1] >> 8),
1156 0x0ff & (dataVals[2] >> 8),
1157 0xff // can't have transparency in the color itself
1158 //0x0ff & (data->data[3] >> 8),
1159 ));
1161 SPItem *item = desktop->item_at_point( where, true );
1163 bool consumed = false;
1164 if (desktop->event_context && desktop->event_context->get_drag()) {
1165 consumed = desktop->event_context->get_drag()->dropColor(item, c, button_dt);
1166 if (consumed) {
1167 sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1168 desktop->event_context->get_drag()->updateDraggers();
1169 }
1170 }
1172 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1173 // consumed = sp_text_context_drop_color(c, button_doc);
1174 // if (consumed) {
1175 // sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1176 // }
1177 //}
1179 if (!consumed && item) {
1180 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1181 if (fillnotstroke &&
1182 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1183 Path *livarot_path = Path_for_item(item, true, true);
1184 livarot_path->ConvertWithBackData(0.04);
1186 NR::Maybe<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1187 if (position) {
1188 NR::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1189 NR::Point delta = nearest - button_doc;
1190 delta = desktop->d2w(delta);
1191 double stroke_tolerance =
1192 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1193 desktop->current_zoom() *
1194 SP_OBJECT_STYLE (item)->stroke_width.computed *
1195 sp_item_i2d_affine (item).expansion() * 0.5
1196 : 0.0)
1197 + prefs_get_int_attribute_limited("options.dragtolerance", "value", 0, 0, 100);
1199 if (NR::L2 (delta) < stroke_tolerance) {
1200 fillnotstroke = false;
1201 }
1202 }
1203 delete livarot_path;
1204 }
1206 SPCSSAttr *css = sp_repr_css_attr_new();
1207 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1209 sp_desktop_apply_css_recursive( item, css, true );
1210 item->updateRepr();
1212 sp_document_done( doc , SP_VERB_NONE,
1213 _("Drop color"));
1214 }
1215 }
1216 }
1217 break;
1219 case SVG_DATA:
1220 case SVG_XML_DATA: {
1221 gchar *svgdata = (gchar *)data->data;
1223 Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1225 if (rnewdoc == NULL) {
1226 sp_ui_error_dialog(_("Could not parse SVG data"));
1227 return;
1228 }
1230 Inkscape::XML::Node *repr = rnewdoc->root();
1231 gchar const *style = repr->attribute("style");
1233 Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1234 newgroup->setAttribute("style", style);
1236 Inkscape::XML::Document * xml_doc = sp_document_repr_doc(doc);
1237 for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1238 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1239 newgroup->appendChild(newchild);
1240 }
1242 Inkscape::GC::release(rnewdoc);
1244 // Add it to the current layer
1246 // Greg's edits to add intelligent positioning of svg drops
1247 SPObject *new_obj = NULL;
1248 new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1250 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1251 selection->set(SP_ITEM(new_obj));
1252 // To move the imported object, we must temporarily set the "transform pattern with
1253 // object" option.
1254 {
1255 int const saved_pref = prefs_get_int_attribute("options.transform", "pattern", 1);
1256 prefs_set_int_attribute("options.transform", "pattern", 1);
1257 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1258 NR::Maybe<NR::Rect> sel_bbox = selection->bounds();
1259 if (sel_bbox) {
1260 NR::Point m( desktop->point() - sel_bbox->midpoint() );
1261 sp_selection_move_relative(selection, m);
1262 }
1263 prefs_set_int_attribute("options.transform", "pattern", saved_pref);
1264 }
1266 Inkscape::GC::release(newgroup);
1267 sp_document_done(doc, SP_VERB_NONE,
1268 _("Drop SVG"));
1269 break;
1270 }
1272 case URI_LIST: {
1273 gchar *uri = (gchar *)data->data;
1274 sp_ui_import_files(uri);
1275 break;
1276 }
1278 case PNG_DATA:
1279 case JPEG_DATA:
1280 case IMAGE_DATA: {
1281 char tmp[1024];
1283 StringOutputStream outs;
1284 Base64OutputStream b64out(outs);
1285 b64out.setColumnWidth(0);
1287 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(doc);
1289 Inkscape::XML::Node *newImage = xml_doc->createElement("svg:image");
1291 for ( int i = 0; i < data->length; i++ ) {
1292 b64out.put( data->data[i] );
1293 }
1294 b64out.close();
1297 Glib::ustring str = outs.getString();
1299 snprintf( tmp, sizeof(tmp), "data:%s;base64,", gdk_atom_name(data->type) );
1300 str.insert( 0, tmp );
1301 newImage->setAttribute("xlink:href", str.c_str());
1303 GError *error = NULL;
1304 GdkPixbufLoader *loader = gdk_pixbuf_loader_new_with_mime_type( gdk_atom_name(data->type), &error );
1305 if ( loader ) {
1306 error = NULL;
1307 if ( gdk_pixbuf_loader_write( loader, data->data, data->length, &error) ) {
1308 GdkPixbuf *pbuf = gdk_pixbuf_loader_get_pixbuf(loader);
1309 if ( pbuf ) {
1310 int width = gdk_pixbuf_get_width(pbuf);
1311 int height = gdk_pixbuf_get_height(pbuf);
1312 snprintf( tmp, sizeof(tmp), "%d", width );
1313 newImage->setAttribute("width", tmp);
1315 snprintf( tmp, sizeof(tmp), "%d", height );
1316 newImage->setAttribute("height", tmp);
1317 }
1318 }
1319 }
1321 // Add it to the current layer
1322 desktop->currentLayer()->appendChildRepr(newImage);
1324 Inkscape::GC::release(newImage);
1325 sp_document_done( doc , SP_VERB_NONE,
1326 _("Drop bitmap image"));
1327 break;
1328 }
1329 }
1330 }
1332 #include "gradient-context.h"
1334 void sp_ui_drag_motion( GtkWidget */*widget*/,
1335 GdkDragContext */*drag_context*/,
1336 gint /*x*/, gint /*y*/,
1337 GtkSelectionData */*data*/,
1338 guint /*info*/,
1339 guint /*event_time*/,
1340 gpointer /*user_data*/)
1341 {
1342 // SPDocument *doc = SP_ACTIVE_DOCUMENT;
1343 // SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1346 // g_message("drag-n-drop motion (%4d, %4d) at %d", x, y, event_time);
1347 }
1349 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1350 GdkDragContext */*drag_context*/,
1351 guint /*event_time*/,
1352 gpointer /*user_data*/ )
1353 {
1354 // g_message("drag-n-drop leave at %d", event_time);
1355 }
1357 static void
1358 sp_ui_import_files(gchar *buffer)
1359 {
1360 GList *list = gnome_uri_list_extract_filenames(buffer);
1361 if (!list)
1362 return;
1363 g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1364 g_list_foreach(list, (GFunc) g_free, NULL);
1365 g_list_free(list);
1366 }
1368 static void
1369 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1370 {
1371 if (filename) {
1372 if (strlen((char const *)filename) > 2)
1373 sp_ui_import_one_file((char const *)filename);
1374 }
1375 }
1377 static void
1378 sp_ui_import_one_file(char const *filename)
1379 {
1380 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1381 if (!doc) return;
1383 if (filename == NULL) return;
1385 // Pass off to common implementation
1386 // TODO might need to get the proper type of Inkscape::Extension::Extension
1387 file_import( doc, filename, NULL );
1388 }
1390 void
1391 sp_ui_error_dialog(gchar const *message)
1392 {
1393 GtkWidget *dlg;
1394 gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1396 dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1397 GTK_BUTTONS_CLOSE, "%s", safeMsg);
1398 sp_transientize(dlg);
1399 gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1400 gtk_dialog_run(GTK_DIALOG(dlg));
1401 gtk_widget_destroy(dlg);
1402 g_free(safeMsg);
1403 }
1405 bool
1406 sp_ui_overwrite_file(gchar const *filename)
1407 {
1408 bool return_value = FALSE;
1410 if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1411 Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1412 gchar* baseName = g_path_get_basename( filename );
1413 gchar* dirName = g_path_get_dirname( filename );
1414 GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1415 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1416 GTK_MESSAGE_QUESTION,
1417 GTK_BUTTONS_NONE,
1418 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1419 "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1420 baseName,
1421 dirName
1422 );
1423 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1424 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1425 _("Replace"), GTK_RESPONSE_YES,
1426 NULL );
1427 gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1429 if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1430 return_value = TRUE;
1431 } else {
1432 return_value = FALSE;
1433 }
1434 gtk_widget_destroy(dialog);
1435 g_free( baseName );
1436 g_free( dirName );
1437 } else {
1438 return_value = TRUE;
1439 }
1441 return return_value;
1442 }
1444 static void
1445 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1446 {
1447 return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1448 }
1450 static void
1451 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1452 {
1453 void *child = GTK_BIN (data)->child;
1454 //child is either
1455 //- a GtkHBox, whose first child is a label displaying name if the menu
1456 //item has an accel key
1457 //- a GtkLabel if the menu has no accel key
1458 if (GTK_IS_LABEL(child)) {
1459 gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1460 } else if (GTK_IS_HBOX(child)) {
1461 gtk_label_set_markup_with_mnemonic(
1462 GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1463 name.c_str());
1464 }//else sp_ui_menu_append_item_from_verb has been modified and can set
1465 //a menu item in yet another way...
1466 }
1469 /*
1470 Local Variables:
1471 mode:c++
1472 c-file-style:"stroustrup"
1473 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1474 indent-tabs-mode:nil
1475 fill-column:99
1476 End:
1477 */
1478 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :