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