1 #define __SP_INTERFACE_C__
3 /** @file
4 * @brief 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 <glib.h>
25 #include "inkscape-private.h"
26 #include "extension/db.h"
27 #include "extension/effect.h"
28 #include "extension/input.h"
29 #include "widgets/icon.h"
30 #include "preferences.h"
31 #include "path-prefix.h"
32 #include "shortcuts.h"
33 #include "document.h"
34 #include "desktop-handles.h"
35 #include "file.h"
36 #include "interface.h"
37 #include "desktop.h"
38 #include "ui/context-menu.h"
39 #include "selection.h"
40 #include "selection-chemistry.h"
41 #include "svg-view-widget.h"
42 #include "widgets/desktop-widget.h"
43 #include "sp-item-group.h"
44 #include "sp-text.h"
45 #include "sp-gradient-fns.h"
46 #include "sp-gradient.h"
47 #include "sp-flowtext.h"
48 #include "sp-namedview.h"
49 #include "ui/view/view.h"
50 #include "helper/action.h"
51 #include "helper/gnome-utils.h"
52 #include "helper/window.h"
53 #include "io/sys.h"
54 #include "dialogs/dialog-events.h"
55 #include "message-context.h"
56 #include "ui/uxmanager.h"
58 // Added for color drag-n-drop
59 #if ENABLE_LCMS
60 #include "lcms.h"
61 #endif // ENABLE_LCMS
62 #include "display/sp-canvas.h"
63 #include "color.h"
64 #include "svg/svg-color.h"
65 #include "desktop-style.h"
66 #include "style.h"
67 #include "event-context.h"
68 #include "gradient-drag.h"
69 #include "widgets/ege-paint-def.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 /* Drag and Drop */
77 typedef enum {
78 URI_LIST,
79 SVG_XML_DATA,
80 SVG_DATA,
81 PNG_DATA,
82 JPEG_DATA,
83 IMAGE_DATA,
84 APP_X_INKY_COLOR,
85 APP_X_COLOR,
86 APP_OSWB_COLOR,
87 } ui_drop_target_info;
89 static GtkTargetEntry ui_drop_target_entries [] = {
90 {(gchar *)"text/uri-list", 0, URI_LIST },
91 {(gchar *)"image/svg+xml", 0, SVG_XML_DATA },
92 {(gchar *)"image/svg", 0, SVG_DATA },
93 {(gchar *)"image/png", 0, PNG_DATA },
94 {(gchar *)"image/jpeg", 0, JPEG_DATA },
95 #if ENABLE_MAGIC_COLORS
96 {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
97 #endif // ENABLE_MAGIC_COLORS
98 {(gchar *)"application/x-oswb-color", 0, APP_OSWB_COLOR },
99 {(gchar *)"application/x-color", 0, APP_X_COLOR }
100 };
102 static GtkTargetEntry *completeDropTargets = 0;
103 static int completeDropTargetsCount = 0;
104 static bool temporarily_block_actions = false;
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);
135 static void sp_recent_open(GtkRecentChooser *, gpointer);
137 SPActionEventVector menu_item_event_vector = {
138 {NULL},
139 NULL,
140 NULL, /* set_active */
141 sp_ui_menu_item_set_sensitive, /* set_sensitive */
142 NULL, /* set_shortcut */
143 sp_ui_menu_item_set_name /* set_name */
144 };
146 static const int MIN_ONSCREEN_DISTANCE = 50;
148 void
149 sp_create_window(SPViewWidget *vw, gboolean editable)
150 {
151 g_return_if_fail(vw != NULL);
152 g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
154 Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
156 gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
157 gtk_widget_show(GTK_WIDGET(vw));
159 if (editable) {
160 g_object_set_data(G_OBJECT(vw), "window", win);
162 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
163 SPDesktop* desktop = desktop_widget->desktop;
165 desktop_widget->window = win;
167 win->set_data("desktop", desktop);
168 win->set_data("desktopwidget", desktop_widget);
170 win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
171 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
172 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
174 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
175 gint prefs_geometry =
176 (2==prefs->getInt("/options/savewindowgeometry/value", 0));
177 if (prefs_geometry) {
178 gint pw = prefs->getInt("/desktop/geometry/width", -1);
179 gint ph = prefs->getInt("/desktop/geometry/height", -1);
180 gint px = prefs->getInt("/desktop/geometry/x", -1);
181 gint py = prefs->getInt("/desktop/geometry/y", -1);
182 gint full = prefs->getBool("/desktop/geometry/fullscreen");
183 gint maxed = prefs->getBool("/desktop/geometry/maximized");
184 if (pw>0 && ph>0) {
185 gint w = MIN(gdk_screen_width(), pw);
186 gint h = MIN(gdk_screen_height(), ph);
187 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
188 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
189 if (w>0 && h>0) {
190 x = MIN(gdk_screen_width() - w, x);
191 y = MIN(gdk_screen_height() - h, y);
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 SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
202 if (active_desktop == desktop || active_desktop==NULL) {
203 desktop->setWindowPosition(Geom::Point(x, y));
204 }
205 }
206 if (maxed) {
207 win->maximize();
208 }
209 if (full) {
210 win->fullscreen();
211 }
212 }
214 } else {
215 gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
216 }
218 if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
219 {
220 std::vector<gchar*> types;
222 GSList *list = gdk_pixbuf_get_formats();
223 while ( list ) {
224 int i = 0;
225 GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
226 gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
227 for ( i = 0; typesXX[i]; i++ ) {
228 types.push_back(g_strdup(typesXX[i]));
229 }
230 g_strfreev(typesXX);
232 list = g_slist_next(list);
233 }
234 completeDropTargetsCount = nui_drop_target_entries + types.size();
235 completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
236 for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
237 completeDropTargets[i] = ui_drop_target_entries[i];
238 }
239 int pos = nui_drop_target_entries;
241 for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
242 completeDropTargets[pos].target = *it;
243 completeDropTargets[pos].flags = 0;
244 completeDropTargets[pos].info = IMAGE_DATA;
245 pos++;
246 }
247 }
249 gtk_drag_dest_set((GtkWidget*)win->gobj(),
250 GTK_DEST_DEFAULT_ALL,
251 completeDropTargets,
252 completeDropTargetsCount,
253 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
256 g_signal_connect(G_OBJECT(win->gobj()),
257 "drag_data_received",
258 G_CALLBACK(sp_ui_drag_data_received),
259 NULL);
260 g_signal_connect(G_OBJECT(win->gobj()),
261 "drag_motion",
262 G_CALLBACK(sp_ui_drag_motion),
263 NULL);
264 g_signal_connect(G_OBJECT(win->gobj()),
265 "drag_leave",
266 G_CALLBACK(sp_ui_drag_leave),
267 NULL);
268 win->show();
270 // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
271 inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
272 }
274 void
275 sp_ui_new_view()
276 {
277 SPDocument *document;
278 SPViewWidget *dtw;
280 document = SP_ACTIVE_DOCUMENT;
281 if (!document) return;
283 dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
284 g_return_if_fail(dtw != NULL);
286 sp_create_window(dtw, TRUE);
287 sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
288 sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
289 }
291 /* TODO: not yet working */
292 /* To be re-enabled (by adding to menu) once it works. */
293 void
294 sp_ui_new_view_preview()
295 {
296 SPDocument *document;
297 SPViewWidget *dtw;
299 document = SP_ACTIVE_DOCUMENT;
300 if (!document) return;
302 dtw = (SPViewWidget *) sp_svg_view_widget_new(document);
303 g_return_if_fail(dtw != NULL);
304 sp_svg_view_widget_set_resize(SP_SVG_VIEW_WIDGET(dtw), TRUE, 400.0, 400.0);
306 sp_create_window(dtw, FALSE);
307 }
309 /**
310 * \param widget unused
311 */
312 void
313 sp_ui_close_view(GtkWidget */*widget*/)
314 {
315 SPDesktop *dt = SP_ACTIVE_DESKTOP;
317 if (dt == NULL) {
318 return;
319 }
321 if (dt->shutdown()) {
322 return; // Shutdown operation has been canceled, so do nothing
323 }
325 // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
326 // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
327 dt->destroyWidget();
328 }
331 /**
332 * sp_ui_close_all
333 *
334 * This function is called to exit the program, and iterates through all
335 * open document view windows, attempting to close each in turn. If the
336 * view has unsaved information, the user will be prompted to save,
337 * discard, or cancel.
338 *
339 * Returns FALSE if the user cancels the close_all operation, TRUE
340 * otherwise.
341 */
342 unsigned int
343 sp_ui_close_all(void)
344 {
345 /* Iterate through all the windows, destroying each in the order they
346 become active */
347 while (SP_ACTIVE_DESKTOP) {
348 SPDesktop *dt = SP_ACTIVE_DESKTOP;
349 if (dt->shutdown()) {
350 /* The user canceled the operation, so end doing the close */
351 return FALSE;
352 }
353 // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
354 // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
355 dt->destroyWidget();
356 }
358 return TRUE;
359 }
361 /*
362 * Some day when the right-click menus are ready to start working
363 * smarter with the verbs, we'll need to change this NULL being
364 * sent to sp_action_perform to something useful, or set some kind
365 * of global "right-clicked position" variable for actions to
366 * investigate when they're called.
367 */
368 static void
369 sp_ui_menu_activate(void */*object*/, SPAction *action)
370 {
371 if (!temporarily_block_actions) {
372 sp_action_perform(action, NULL);
373 }
374 }
376 static void
377 sp_ui_menu_select_action(void */*object*/, SPAction *action)
378 {
379 action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
380 }
382 static void
383 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
384 {
385 action->view->tipsMessageContext()->clear();
386 }
388 static void
389 sp_ui_menu_select(gpointer object, gpointer tip)
390 {
391 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
392 view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
393 }
395 static void
396 sp_ui_menu_deselect(gpointer object)
397 {
398 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
399 view->tipsMessageContext()->clear();
400 }
402 /**
403 * sp_ui_menuitem_add_icon
404 *
405 * Creates and attaches a scaled icon to the given menu item.
406 *
407 */
408 void
409 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
410 {
411 GtkWidget *icon;
413 icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
414 gtk_widget_show(icon);
415 gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
416 } // end of sp_ui_menu_add_icon
418 /**
419 * sp_ui_menu_append_item
420 *
421 * Appends a UI item with specific info for Inkscape/Sodipodi.
422 *
423 */
424 static GtkWidget *
425 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
426 gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
427 gpointer data, gboolean with_mnemonic = TRUE )
428 {
429 GtkWidget *item;
431 if (stock) {
432 item = gtk_image_menu_item_new_from_stock(stock, NULL);
433 } else if (label) {
434 item = (with_mnemonic)
435 ? gtk_image_menu_item_new_with_mnemonic(label) :
436 gtk_image_menu_item_new_with_label(label);
437 } else {
438 item = gtk_separator_menu_item_new();
439 }
441 gtk_widget_show(item);
443 if (callback) {
444 g_signal_connect(G_OBJECT(item), "activate", callback, data);
445 }
447 if (tip && view) {
448 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
449 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
450 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
451 }
453 gtk_menu_append(GTK_MENU(menu), item);
455 return item;
457 } // end of sp_ui_menu_append_item()
459 void
460 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
461 {
462 SPAction *action;
463 unsigned int shortcut;
464 gchar *s;
465 gchar *atitle;
467 action = verb->get_action(NULL);
468 if (!action)
469 return;
471 atitle = sp_action_get_title(action);
473 s = g_stpcpy(c, atitle);
475 g_free(atitle);
477 shortcut = sp_shortcut_get_primary(verb);
478 if (shortcut!=GDK_VoidSymbol) {
479 gchar* key = sp_shortcut_get_label(shortcut);
480 s = g_stpcpy(s, " (");
481 s = g_stpcpy(s, key);
482 s = g_stpcpy(s, ")");
483 g_free(key);
484 }
485 }
488 /**
489 * sp_ui_menu_append_item_from_verb
490 *
491 * Appends a custom menu UI from a verb.
492 *
493 */
495 static GtkWidget *
496 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
497 {
498 SPAction *action;
499 GtkWidget *item;
501 if (verb->get_code() == SP_VERB_NONE) {
503 item = gtk_separator_menu_item_new();
505 } else {
506 unsigned int shortcut;
508 action = verb->get_action(view);
510 if (!action) return NULL;
512 shortcut = sp_shortcut_get_primary(verb);
513 if (shortcut!=GDK_VoidSymbol) {
514 gchar* c = sp_shortcut_get_label(shortcut);
515 GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
516 GtkWidget *const name_lbl = gtk_label_new("");
517 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
518 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
519 gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
520 GtkWidget *const accel_lbl = gtk_label_new(c);
521 gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
522 gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
523 gtk_widget_show_all(hb);
524 if (radio) {
525 item = gtk_radio_menu_item_new (group);
526 } else {
527 item = gtk_image_menu_item_new();
528 }
529 gtk_container_add((GtkContainer *) item, hb);
530 g_free(c);
531 } else {
532 if (radio) {
533 item = gtk_radio_menu_item_new (group);
534 } else {
535 item = gtk_image_menu_item_new ();
536 }
537 GtkWidget *const name_lbl = gtk_label_new("");
538 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
539 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
540 gtk_container_add((GtkContainer *) item, name_lbl);
541 }
543 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
544 if (!action->sensitive) {
545 gtk_widget_set_sensitive(item, FALSE);
546 }
548 if (action->image) {
549 sp_ui_menuitem_add_icon(item, action->image);
550 }
551 gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
552 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
553 g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
554 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
555 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
556 }
558 gtk_widget_show(item);
559 gtk_menu_append(GTK_MENU(menu), item);
561 return item;
563 } // end of sp_ui_menu_append_item_from_verb
566 static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view )
567 {
568 Glib::ustring prefPath;
570 if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
571 prefPath = "/focus/";
572 } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
573 prefPath = "/fullscreen/";
574 } else {
575 prefPath = "/window/";
576 }
578 return prefPath;
579 }
581 static void
582 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
583 {
584 gchar const *pref = (gchar const *) user_data;
585 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
587 Glib::ustring pref_path = getLayoutPrefPath( view );
588 pref_path += pref;
589 pref_path += "/state";
591 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
592 gboolean checked = gtk_check_menu_item_get_active(menuitem);
593 prefs->setBool(pref_path, checked);
595 reinterpret_cast<SPDesktop*>(view)->layoutWidget();
596 }
598 static gboolean
599 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
600 {
601 GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
603 gchar const *pref = (gchar const *) user_data;
604 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
606 Glib::ustring pref_path = getLayoutPrefPath( view );
607 pref_path += pref;
608 pref_path += "/state";
610 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
611 bool ison = prefs->getBool(pref_path, true);
613 g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
614 gtk_check_menu_item_set_active(menuitem, ison);
615 g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
617 return FALSE;
618 }
620 static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData)
621 {
622 if ( gtk_check_menu_item_get_active(menuitem) ) {
623 gint taskNum = GPOINTER_TO_INT(userData);
624 taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum;
626 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
628 // note: this will change once more options are in the task set support:
629 Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum );
630 }
631 }
634 /**
635 * \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline)
636 */
638 static gboolean
639 update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
640 {
641 SPAction *action = (SPAction *) user_data;
642 g_assert(action->id != NULL);
644 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view");
645 SPDesktop *dt = static_cast<SPDesktop*>(view);
646 Inkscape::RenderMode mode = dt->getMode();
648 bool new_state = false;
649 if (!strcmp(action->id, "ViewModeNormal")) {
650 new_state = mode == Inkscape::RENDERMODE_NORMAL;
651 } else if (!strcmp(action->id, "ViewModeNoFilters")) {
652 new_state = mode == Inkscape::RENDERMODE_NO_FILTERS;
653 } else if (!strcmp(action->id, "ViewModeOutline")) {
654 new_state = mode == Inkscape::RENDERMODE_OUTLINE;
655 } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) {
656 new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW;
657 } else {
658 g_warning("update_view_menu does not handle this verb");
659 }
661 if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated
662 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) {
663 // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second
664 // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled.
665 // This emission however should not invoke any actions, hence we block it here:
666 temporarily_block_actions = true;
667 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
668 temporarily_block_actions = false;
669 }
670 }
672 return FALSE;
673 }
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 unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0;
682 SPAction *action = (verb) ? verb->get_action(view) : 0;
683 GtkWidget *item = gtk_check_menu_item_new();
685 if (verb && shortcut!=GDK_VoidSymbol) {
686 gchar* c = sp_shortcut_get_label(shortcut);
688 GtkWidget *hb = gtk_hbox_new(FALSE, 16);
690 {
691 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
692 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
693 gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
694 }
696 {
697 GtkWidget *l = gtk_label_new(c);
698 gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
699 gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
700 }
702 gtk_widget_show_all(hb);
704 gtk_container_add((GtkContainer *) item, hb);
705 g_free(c);
706 } else {
707 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
708 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
709 gtk_container_add((GtkContainer *) item, l);
710 }
711 #if 0
712 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
713 if (!action->sensitive) {
714 gtk_widget_set_sensitive(item, FALSE);
715 }
716 #endif
717 gtk_widget_show(item);
719 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
721 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
723 g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
724 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
726 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
727 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
728 }
730 static void
731 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
732 {
733 // dealing with the bizarre filename convention in Inkscape for now
734 gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
735 gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
736 gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
737 sp_file_open(utf8_fn, NULL);
738 g_free(utf8_fn);
739 g_free(local_fn);
740 g_free(uri);
741 }
743 static void
744 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
745 {
746 sp_file_new(uri);
747 }
749 void
750 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
751 {
752 std::list<gchar *> sources;
753 sources.push_back( profile_path("templates") ); // first try user's local dir
754 sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
756 // Use this loop to iterate through a list of possible document locations.
757 while (!sources.empty()) {
758 gchar *dirname = sources.front();
760 if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
761 GError *err = 0;
762 GDir *dir = g_dir_open(dirname, 0, &err);
764 if (dir) {
765 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
766 if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
767 continue; // skip non-svg files
769 gchar *basename = g_path_get_basename(file);
770 if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
771 continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
773 gchar const *filepath = g_build_filename(dirname, file, NULL);
774 gchar *dupfile = g_strndup(file, strlen(file) - 4);
775 gchar *filename = g_filename_to_utf8(dupfile, -1, NULL, NULL, NULL);
776 g_free(dupfile);
777 GtkWidget *item = gtk_menu_item_new_with_label(filename);
778 g_free(filename);
780 gtk_widget_show(item);
781 // how does "filepath" ever get freed?
782 g_signal_connect(G_OBJECT(item),
783 "activate",
784 G_CALLBACK(sp_file_new_from_template),
785 (gpointer) filepath);
787 if (view) {
788 // set null tip for now; later use a description from the template file
789 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
790 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
791 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
792 }
794 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
795 }
796 g_dir_close(dir);
797 }
798 }
800 // toss the dirname
801 g_free(dirname);
802 sources.pop_front();
803 }
804 }
806 void
807 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
808 {
809 //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
810 // checkitem_toggled, checkitem_update, 0);
811 sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
812 checkitem_toggled, checkitem_update, 0);
813 sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
814 checkitem_toggled, checkitem_update, 0);
815 sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
816 checkitem_toggled, checkitem_update, 0);
817 sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
818 checkitem_toggled, checkitem_update, 0);
819 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
820 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
821 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
822 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
823 sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
824 checkitem_toggled, checkitem_update, 0);
825 sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
826 checkitem_toggled, checkitem_update, 0);
827 }
830 void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view)
831 {
832 gchar const* data[] = {
833 _("Default"), _("Default interface setup"),
834 _("Custom"), _("Set the custom task"),
835 _("Wide"), _("Setup for widescreen work"),
836 0, 0
837 };
839 GSList *group = 0;
840 int count = 0;
841 gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) );
842 for (gchar const **strs = data; strs[0]; strs += 2, count++)
843 {
844 GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] );
845 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) );
846 if ( count == active )
847 {
848 gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE );
849 }
851 g_object_set_data( G_OBJECT(item), "view", view );
852 g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) );
853 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) );
854 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 );
856 gtk_widget_show( item );
857 gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
858 }
859 }
862 /** @brief Observer that updates the recent list's max document count */
863 class MaxRecentObserver : public Inkscape::Preferences::Observer {
864 public:
865 MaxRecentObserver(GtkWidget *recent_menu) :
866 Observer("/options/maxrecentdocuments/value"),
867 _rm(recent_menu)
868 {}
869 virtual void notify(Inkscape::Preferences::Entry const &e) {
870 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
871 // hack: the recent menu doesn't repopulate after changing the limit, so we force it
872 g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
873 }
874 private:
875 GtkWidget *_rm;
876 };
878 /** \brief This function turns XML into a menu
879 \param menus This is the XML that defines the menu
880 \param menu Menu to be added to
881 \param view The View that this menu is being built for
883 This function is realitively simple as it just goes through the XML
884 and parses the individual elements. In the case of a submenu, it
885 just calls itself recursively. Because it is only reasonable to have
886 a couple of submenus, it is unlikely this will go more than two or
887 three times.
889 In the case of an unrecognized verb, a menu item is made to identify
890 the verb that is missing, and display that. The menu item is also made
891 insensitive.
892 */
893 void
894 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
895 {
896 if (menus == NULL) return;
897 if (menu == NULL) return;
898 GSList *group = NULL;
900 for (Inkscape::XML::Node *menu_pntr = menus;
901 menu_pntr != NULL;
902 menu_pntr = menu_pntr->next()) {
903 if (!strcmp(menu_pntr->name(), "submenu")) {
904 GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
905 GtkWidget *submenu = gtk_menu_new();
906 sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
907 gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
908 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
909 continue;
910 }
911 if (!strcmp(menu_pntr->name(), "verb")) {
912 gchar const *verb_name = menu_pntr->attribute("verb-id");
913 Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
915 if (verb != NULL) {
916 if (menu_pntr->attribute("radio") != NULL) {
917 GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
918 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
919 if (menu_pntr->attribute("default") != NULL) {
920 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
921 }
922 if (verb->get_code() != SP_VERB_NONE) {
923 SPAction *action = verb->get_action(view);
924 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action);
925 }
926 } else {
927 sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
928 group = NULL;
929 }
930 } else {
931 gchar string[120];
932 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
933 string[119] = '\0'; /* may not be terminated */
934 GtkWidget *item = gtk_menu_item_new_with_label(string);
935 gtk_widget_set_sensitive(item, false);
936 gtk_widget_show(item);
937 gtk_menu_append(GTK_MENU(menu), item);
938 }
939 continue;
940 }
941 if (!strcmp(menu_pntr->name(), "separator")
942 // This was spelt wrong in the original version
943 // and so this is for backward compatibility. It can
944 // probably be dropped after the 0.44 release.
945 || !strcmp(menu_pntr->name(), "seperator")) {
946 GtkWidget *item = gtk_separator_menu_item_new();
947 gtk_widget_show(item);
948 gtk_menu_append(GTK_MENU(menu), item);
949 continue;
950 }
951 if (!strcmp(menu_pntr->name(), "template-list")) {
952 sp_menu_append_new_templates(menu, view);
953 continue;
954 }
955 if (!strcmp(menu_pntr->name(), "recent-file-list")) {
956 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
958 // create recent files menu
959 int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
960 GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
961 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
962 // sort most recently used documents first to preserve previous behavior
963 gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
964 g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
966 // add filter to only open files added by Inkscape
967 GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
968 gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
969 gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
971 gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE);
972 gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE);
974 GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
975 gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
977 gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
978 // this will just sit and update the list's item count
979 static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
980 prefs->addObserver(*mro);
981 continue;
982 }
983 if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
984 sp_ui_checkboxes_menus(GTK_MENU(menu), view);
985 continue;
986 }
987 if (!strcmp(menu_pntr->name(), "task-checkboxes")) {
988 addTaskMenuItems(GTK_MENU(menu), view);
989 continue;
990 }
991 }
992 }
994 /** \brief Build the main tool bar
995 \param view View to build the bar for
997 Currently the main tool bar is built as a dynamic XML menu using
998 \c sp_ui_build_dyn_menus. This function builds the bar, and then
999 pass it to get items attached to it.
1000 */
1001 GtkWidget *
1002 sp_ui_main_menubar(Inkscape::UI::View::View *view)
1003 {
1004 GtkWidget *mbar = gtk_menu_bar_new();
1006 #ifdef GDK_WINDOWING_QUARTZ
1007 ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
1008 #endif
1010 sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
1012 #ifdef GDK_WINDOWING_QUARTZ
1013 return NULL;
1014 #else
1015 return mbar;
1016 #endif
1017 }
1019 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
1020 desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
1021 }
1023 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
1024 desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
1025 sp_desktop_selection(desktop)->clear();
1026 }
1028 GtkWidget *
1029 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
1030 {
1031 GtkWidget *m;
1032 SPDesktop *dt;
1034 dt = static_cast<SPDesktop*>(view);
1036 m = gtk_menu_new();
1038 /* Undo and Redo */
1039 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1040 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1042 /* Separator */
1043 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1045 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1046 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1047 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1049 /* Separator */
1050 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1052 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1053 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1055 /* Item menu */
1056 if (item) {
1057 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1058 sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1059 }
1061 /* layer menu */
1062 SPGroup *group=NULL;
1063 if (item) {
1064 if (SP_IS_GROUP(item)) {
1065 group = SP_GROUP(item);
1066 } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1067 group = SP_GROUP(SP_OBJECT_PARENT(item));
1068 }
1069 }
1071 if (( group && group != dt->currentLayer() ) ||
1072 ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1073 /* Separator */
1074 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1075 }
1077 if ( group && group != dt->currentLayer() ) {
1078 /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1079 gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId());
1080 GtkWidget *w = gtk_menu_item_new_with_label(label);
1081 g_free(label);
1082 g_object_set_data(G_OBJECT(w), "group", group);
1083 g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1084 gtk_widget_show(w);
1085 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1086 }
1088 if ( dt->currentLayer() != dt->currentRoot() ) {
1089 if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1090 GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1091 g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1092 gtk_widget_show(w);
1093 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1095 }
1096 }
1098 return m;
1099 }
1101 /* Drag and Drop */
1102 void
1103 sp_ui_drag_data_received(GtkWidget *widget,
1104 GdkDragContext *drag_context,
1105 gint x, gint y,
1106 GtkSelectionData *data,
1107 guint info,
1108 guint /*event_time*/,
1109 gpointer /*user_data*/)
1110 {
1111 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1112 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1114 switch (info) {
1115 #if ENABLE_MAGIC_COLORS
1116 case APP_X_INKY_COLOR:
1117 {
1118 int destX = 0;
1119 int destY = 0;
1120 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1121 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1123 SPItem *item = desktop->item_at_point( where, true );
1124 if ( item )
1125 {
1126 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1128 if ( data->length >= 8 ) {
1129 cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1131 gchar c[64] = {0};
1132 // Careful about endian issues.
1133 guint16* dataVals = (guint16*)data->data;
1134 sp_svg_write_color( c, sizeof(c),
1135 SP_RGBA32_U_COMPOSE(
1136 0x0ff & (dataVals[0] >> 8),
1137 0x0ff & (dataVals[1] >> 8),
1138 0x0ff & (dataVals[2] >> 8),
1139 0xff // can't have transparency in the color itself
1140 //0x0ff & (data->data[3] >> 8),
1141 ));
1142 SPCSSAttr *css = sp_repr_css_attr_new();
1143 bool updatePerformed = false;
1145 if ( data->length > 14 ) {
1146 int flags = dataVals[4];
1148 // piggie-backed palette entry info
1149 int index = dataVals[5];
1150 Glib::ustring palName;
1151 for ( int i = 0; i < dataVals[6]; i++ ) {
1152 palName += (gunichar)dataVals[7+i];
1153 }
1155 // Now hook in a magic tag of some sort.
1156 if ( !palName.empty() && (flags & 1) ) {
1157 gchar* str = g_strdup_printf("%d|", index);
1158 palName.insert( 0, str );
1159 g_free(str);
1160 str = 0;
1162 sp_object_setAttribute( SP_OBJECT(item),
1163 fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1164 palName.c_str(),
1165 false );
1166 item->updateRepr();
1168 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1169 updatePerformed = true;
1170 }
1171 }
1173 if ( !updatePerformed ) {
1174 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1175 }
1177 sp_desktop_apply_css_recursive( item, css, true );
1178 item->updateRepr();
1180 sp_document_done( doc , SP_VERB_NONE,
1181 _("Drop color"));
1183 if ( srgbProf ) {
1184 cmsCloseProfile( srgbProf );
1185 }
1186 }
1187 }
1188 }
1189 break;
1190 #endif // ENABLE_MAGIC_COLORS
1192 case APP_X_COLOR:
1193 {
1194 int destX = 0;
1195 int destY = 0;
1196 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1197 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1198 Geom::Point const button_dt(desktop->w2d(where));
1199 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1201 if ( data->length == 8 ) {
1202 gchar colorspec[64] = {0};
1203 // Careful about endian issues.
1204 guint16* dataVals = (guint16*)data->data;
1205 sp_svg_write_color( colorspec, sizeof(colorspec),
1206 SP_RGBA32_U_COMPOSE(
1207 0x0ff & (dataVals[0] >> 8),
1208 0x0ff & (dataVals[1] >> 8),
1209 0x0ff & (dataVals[2] >> 8),
1210 0xff // can't have transparency in the color itself
1211 //0x0ff & (data->data[3] >> 8),
1212 ));
1214 SPItem *item = desktop->item_at_point( where, true );
1216 bool consumed = false;
1217 if (desktop->event_context && desktop->event_context->get_drag()) {
1218 consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
1219 if (consumed) {
1220 sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1221 desktop->event_context->get_drag()->updateDraggers();
1222 }
1223 }
1225 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1226 // consumed = sp_text_context_drop_color(c, button_doc);
1227 // if (consumed) {
1228 // sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1229 // }
1230 //}
1232 if (!consumed && item) {
1233 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1234 if (fillnotstroke &&
1235 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1236 Path *livarot_path = Path_for_item(item, true, true);
1237 livarot_path->ConvertWithBackData(0.04);
1239 boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1240 if (position) {
1241 Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1242 Geom::Point delta = nearest - button_doc;
1243 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1244 delta = desktop->d2w(delta);
1245 double stroke_tolerance =
1246 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1247 desktop->current_zoom() *
1248 SP_OBJECT_STYLE (item)->stroke_width.computed *
1249 to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1250 : 0.0)
1251 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1253 if (Geom::L2 (delta) < stroke_tolerance) {
1254 fillnotstroke = false;
1255 }
1256 }
1257 delete livarot_path;
1258 }
1260 SPCSSAttr *css = sp_repr_css_attr_new();
1261 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
1263 sp_desktop_apply_css_recursive( item, css, true );
1264 item->updateRepr();
1266 sp_document_done( doc , SP_VERB_NONE,
1267 _("Drop color"));
1268 }
1269 }
1270 }
1271 break;
1273 case APP_OSWB_COLOR:
1274 {
1275 bool worked = false;
1276 Glib::ustring colorspec;
1277 if ( data->format == 8 ) {
1278 ege::PaintDef color;
1279 worked = color.fromMIMEData("application/x-oswb-color",
1280 reinterpret_cast<char*>(data->data),
1281 data->length,
1282 data->format);
1283 if ( worked ) {
1284 if ( color.getType() == ege::PaintDef::CLEAR ) {
1285 colorspec = ""; // TODO check if this is sufficient
1286 } else if ( color.getType() == ege::PaintDef::NONE ) {
1287 colorspec = "none";
1288 } else {
1289 unsigned int r = color.getR();
1290 unsigned int g = color.getG();
1291 unsigned int b = color.getB();
1293 SPGradient* matches = 0;
1294 const GSList *gradients = sp_document_get_resource_list(doc, "gradient");
1295 for (const GSList *item = gradients; item; item = item->next) {
1296 SPGradient* grad = SP_GRADIENT(item->data);
1297 if ( color.descr == grad->getId() ) {
1298 if ( grad->hasStops() ) {
1299 matches = grad;
1300 break;
1301 }
1302 }
1303 }
1304 if (matches) {
1305 colorspec = "url(#";
1306 colorspec += matches->getId();
1307 colorspec += ")";
1308 } else {
1309 gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
1310 colorspec = tmp;
1311 g_free(tmp);
1312 }
1313 }
1314 }
1315 }
1316 if ( worked ) {
1317 int destX = 0;
1318 int destY = 0;
1319 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1320 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1321 Geom::Point const button_dt(desktop->w2d(where));
1322 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1324 SPItem *item = desktop->item_at_point( where, true );
1326 bool consumed = false;
1327 if (desktop->event_context && desktop->event_context->get_drag()) {
1328 consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
1329 if (consumed) {
1330 sp_document_done( doc , SP_VERB_NONE, _("Drop color on gradient"));
1331 desktop->event_context->get_drag()->updateDraggers();
1332 }
1333 }
1335 if (!consumed && item) {
1336 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1337 if (fillnotstroke &&
1338 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1339 Path *livarot_path = Path_for_item(item, true, true);
1340 livarot_path->ConvertWithBackData(0.04);
1342 boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1343 if (position) {
1344 Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1345 Geom::Point delta = nearest - button_doc;
1346 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1347 delta = desktop->d2w(delta);
1348 double stroke_tolerance =
1349 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1350 desktop->current_zoom() *
1351 SP_OBJECT_STYLE (item)->stroke_width.computed *
1352 to_2geom(sp_item_i2d_affine(item)).descrim() * 0.5
1353 : 0.0)
1354 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1356 if (Geom::L2 (delta) < stroke_tolerance) {
1357 fillnotstroke = false;
1358 }
1359 }
1360 delete livarot_path;
1361 }
1363 SPCSSAttr *css = sp_repr_css_attr_new();
1364 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
1366 sp_desktop_apply_css_recursive( item, css, true );
1367 item->updateRepr();
1369 sp_document_done( doc , SP_VERB_NONE,
1370 _("Drop color"));
1371 }
1372 }
1373 }
1374 break;
1376 case SVG_DATA:
1377 case SVG_XML_DATA: {
1378 gchar *svgdata = (gchar *)data->data;
1380 Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1382 if (rnewdoc == NULL) {
1383 sp_ui_error_dialog(_("Could not parse SVG data"));
1384 return;
1385 }
1387 Inkscape::XML::Node *repr = rnewdoc->root();
1388 gchar const *style = repr->attribute("style");
1390 Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1391 newgroup->setAttribute("style", style);
1393 Inkscape::XML::Document * xml_doc = sp_document_repr_doc(doc);
1394 for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1395 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1396 newgroup->appendChild(newchild);
1397 }
1399 Inkscape::GC::release(rnewdoc);
1401 // Add it to the current layer
1403 // Greg's edits to add intelligent positioning of svg drops
1404 SPObject *new_obj = NULL;
1405 new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1407 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1408 selection->set(SP_ITEM(new_obj));
1410 // move to mouse pointer
1411 {
1412 sp_document_ensure_up_to_date(sp_desktop_document(desktop));
1413 Geom::OptRect sel_bbox = selection->bounds();
1414 if (sel_bbox) {
1415 Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1416 sp_selection_move_relative(selection, m, false);
1417 }
1418 }
1420 Inkscape::GC::release(newgroup);
1421 sp_document_done(doc, SP_VERB_NONE,
1422 _("Drop SVG"));
1423 break;
1424 }
1426 case URI_LIST: {
1427 gchar *uri = (gchar *)data->data;
1428 sp_ui_import_files(uri);
1429 break;
1430 }
1432 case PNG_DATA:
1433 case JPEG_DATA:
1434 case IMAGE_DATA: {
1435 const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
1437 Inkscape::Extension::DB::InputList o;
1438 Inkscape::Extension::db.get_input_list(o);
1439 Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
1440 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1441 ++i;
1442 }
1443 Inkscape::Extension::Extension *ext = *i;
1444 bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0);
1445 ext->set_param_optiongroup("link", "embed");
1446 ext->set_gui(false);
1448 gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL );
1449 g_file_set_contents(filename, reinterpret_cast<gchar const *>(data->data), data->length, NULL);
1450 file_import(doc, filename, ext);
1451 g_free(filename);
1453 ext->set_param_optiongroup("link", save ? "embed" : "link");
1454 ext->set_gui(true);
1455 sp_document_done( doc , SP_VERB_NONE,
1456 _("Drop bitmap image"));
1457 break;
1458 }
1459 }
1460 }
1462 #include "gradient-context.h"
1464 void sp_ui_drag_motion( GtkWidget */*widget*/,
1465 GdkDragContext */*drag_context*/,
1466 gint /*x*/, gint /*y*/,
1467 GtkSelectionData */*data*/,
1468 guint /*info*/,
1469 guint /*event_time*/,
1470 gpointer /*user_data*/)
1471 {
1472 // SPDocument *doc = SP_ACTIVE_DOCUMENT;
1473 // SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1476 // g_message("drag-n-drop motion (%4d, %4d) at %d", x, y, event_time);
1477 }
1479 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1480 GdkDragContext */*drag_context*/,
1481 guint /*event_time*/,
1482 gpointer /*user_data*/ )
1483 {
1484 // g_message("drag-n-drop leave at %d", event_time);
1485 }
1487 static void
1488 sp_ui_import_files(gchar *buffer)
1489 {
1490 GList *list = gnome_uri_list_extract_filenames(buffer);
1491 if (!list)
1492 return;
1493 g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1494 g_list_foreach(list, (GFunc) g_free, NULL);
1495 g_list_free(list);
1496 }
1498 static void
1499 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1500 {
1501 if (filename) {
1502 if (strlen((char const *)filename) > 2)
1503 sp_ui_import_one_file((char const *)filename);
1504 }
1505 }
1507 static void
1508 sp_ui_import_one_file(char const *filename)
1509 {
1510 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1511 if (!doc) return;
1513 if (filename == NULL) return;
1515 // Pass off to common implementation
1516 // TODO might need to get the proper type of Inkscape::Extension::Extension
1517 file_import( doc, filename, NULL );
1518 }
1520 void
1521 sp_ui_error_dialog(gchar const *message)
1522 {
1523 GtkWidget *dlg;
1524 gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1526 dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1527 GTK_BUTTONS_CLOSE, "%s", safeMsg);
1528 sp_transientize(dlg);
1529 gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1530 gtk_dialog_run(GTK_DIALOG(dlg));
1531 gtk_widget_destroy(dlg);
1532 g_free(safeMsg);
1533 }
1535 bool
1536 sp_ui_overwrite_file(gchar const *filename)
1537 {
1538 bool return_value = FALSE;
1540 if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1541 Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1542 gchar* baseName = g_path_get_basename( filename );
1543 gchar* dirName = g_path_get_dirname( filename );
1544 GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1545 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1546 GTK_MESSAGE_QUESTION,
1547 GTK_BUTTONS_NONE,
1548 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1549 "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1550 baseName,
1551 dirName
1552 );
1553 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1554 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1555 _("Replace"), GTK_RESPONSE_YES,
1556 NULL );
1557 gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1559 if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1560 return_value = TRUE;
1561 } else {
1562 return_value = FALSE;
1563 }
1564 gtk_widget_destroy(dialog);
1565 g_free( baseName );
1566 g_free( dirName );
1567 } else {
1568 return_value = TRUE;
1569 }
1571 return return_value;
1572 }
1574 static void
1575 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1576 {
1577 return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1578 }
1580 static void
1581 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1582 {
1583 void *child = GTK_BIN (data)->child;
1584 //child is either
1585 //- a GtkHBox, whose first child is a label displaying name if the menu
1586 //item has an accel key
1587 //- a GtkLabel if the menu has no accel key
1588 if (GTK_IS_LABEL(child)) {
1589 gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1590 } else if (GTK_IS_HBOX(child)) {
1591 gtk_label_set_markup_with_mnemonic(
1592 GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1593 name.c_str());
1594 }//else sp_ui_menu_append_item_from_verb has been modified and can set
1595 //a menu item in yet another way...
1596 }
1599 /*
1600 Local Variables:
1601 mode:c++
1602 c-file-style:"stroustrup"
1603 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1604 indent-tabs-mode:nil
1605 fill-column:99
1606 End:
1607 */
1608 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :