1 /** @file
2 * @brief Main UI stuff
3 */
4 /* Authors:
5 * Lauris Kaplinski <lauris@kaplinski.com>
6 * Frank Felfe <innerspace@iname.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Jon A. Cruz <jon@joncruz.org>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 2010 authors
12 * Copyright (C) 1999-2005 authors
13 * Copyright (C) 2001-2002 Ximian, Inc.
14 * Copyright (C) 2004 David Turner
15 *
16 * Released under GNU GPL, read the file 'COPYING' for more information
17 */
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #endif
23 #include <gtk/gtk.h>
24 #include <glib.h>
26 #include "inkscape-private.h"
27 #include "extension/db.h"
28 #include "extension/effect.h"
29 #include "extension/input.h"
30 #include "widgets/icon.h"
31 #include "preferences.h"
32 #include "path-prefix.h"
33 #include "shortcuts.h"
34 #include "document.h"
35 #include "desktop-handles.h"
36 #include "file.h"
37 #include "interface.h"
38 #include "desktop.h"
39 #include "ui/context-menu.h"
40 #include "selection.h"
41 #include "selection-chemistry.h"
42 #include "svg-view-widget.h"
43 #include "widgets/desktop-widget.h"
44 #include "sp-item-group.h"
45 #include "sp-text.h"
46 #include "sp-gradient-fns.h"
47 #include "sp-gradient.h"
48 #include "sp-flowtext.h"
49 #include "sp-namedview.h"
50 #include "ui/view/view.h"
51 #include "helper/action.h"
52 #include "helper/gnome-utils.h"
53 #include "helper/window.h"
54 #include "io/sys.h"
55 #include "dialogs/dialog-events.h"
56 #include "message-context.h"
57 #include "ui/uxmanager.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"
70 #include "widgets/ege-paint-def.h"
72 // Include Mac OS X menu synchronization on native OSX build
73 #ifdef GDK_WINDOWING_QUARTZ
74 #include "ige-mac-menu.h"
75 #endif
77 using Inkscape::DocumentUndo;
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 APP_OSWB_COLOR,
90 } ui_drop_target_info;
92 static GtkTargetEntry ui_drop_target_entries [] = {
93 {(gchar *)"text/uri-list", 0, URI_LIST },
94 {(gchar *)"image/svg+xml", 0, SVG_XML_DATA },
95 {(gchar *)"image/svg", 0, SVG_DATA },
96 {(gchar *)"image/png", 0, PNG_DATA },
97 {(gchar *)"image/jpeg", 0, JPEG_DATA },
98 #if ENABLE_MAGIC_COLORS
99 {(gchar *)"application/x-inkscape-color", 0, APP_X_INKY_COLOR},
100 #endif // ENABLE_MAGIC_COLORS
101 {(gchar *)"application/x-oswb-color", 0, APP_OSWB_COLOR },
102 {(gchar *)"application/x-color", 0, APP_X_COLOR }
103 };
105 static GtkTargetEntry *completeDropTargets = 0;
106 static int completeDropTargetsCount = 0;
107 static bool temporarily_block_actions = false;
109 #define ENTRIES_SIZE(n) sizeof(n)/sizeof(n[0])
110 static guint nui_drop_target_entries = ENTRIES_SIZE(ui_drop_target_entries);
111 static void sp_ui_import_files(gchar *buffer);
112 static void sp_ui_import_one_file(char const *filename);
113 static void sp_ui_import_one_file_with_check(gpointer filename, gpointer unused);
114 static void sp_ui_drag_data_received(GtkWidget *widget,
115 GdkDragContext *drag_context,
116 gint x, gint y,
117 GtkSelectionData *data,
118 guint info,
119 guint event_time,
120 gpointer user_data);
121 static void sp_ui_drag_motion( GtkWidget *widget,
122 GdkDragContext *drag_context,
123 gint x, gint y,
124 GtkSelectionData *data,
125 guint info,
126 guint event_time,
127 gpointer user_data );
128 static void sp_ui_drag_leave( GtkWidget *widget,
129 GdkDragContext *drag_context,
130 guint event_time,
131 gpointer user_data );
132 static void sp_ui_menu_item_set_sensitive(SPAction *action,
133 unsigned int sensitive,
134 void *data);
135 static void sp_ui_menu_item_set_name(SPAction *action,
136 Glib::ustring name,
137 void *data);
138 static void sp_recent_open(GtkRecentChooser *, gpointer);
140 static void injectRenamedIcons();
142 SPActionEventVector menu_item_event_vector = {
143 {NULL},
144 NULL,
145 NULL, /* set_active */
146 sp_ui_menu_item_set_sensitive, /* set_sensitive */
147 NULL, /* set_shortcut */
148 sp_ui_menu_item_set_name /* set_name */
149 };
151 static const int MIN_ONSCREEN_DISTANCE = 50;
153 void
154 sp_create_window(SPViewWidget *vw, gboolean editable)
155 {
156 g_return_if_fail(vw != NULL);
157 g_return_if_fail(SP_IS_VIEW_WIDGET(vw));
159 Gtk::Window *win = Inkscape::UI::window_new("", TRUE);
161 gtk_container_add(GTK_CONTAINER(win->gobj()), GTK_WIDGET(vw));
162 gtk_widget_show(GTK_WIDGET(vw));
164 if (editable) {
165 g_object_set_data(G_OBJECT(vw), "window", win);
167 SPDesktopWidget *desktop_widget = reinterpret_cast<SPDesktopWidget*>(vw);
168 SPDesktop* desktop = desktop_widget->desktop;
170 desktop_widget->window = win;
172 win->set_data("desktop", desktop);
173 win->set_data("desktopwidget", desktop_widget);
175 win->signal_delete_event().connect(sigc::mem_fun(*(SPDesktop*)vw->view, &SPDesktop::onDeleteUI));
176 win->signal_window_state_event().connect(sigc::mem_fun(*desktop, &SPDesktop::onWindowStateEvent));
177 win->signal_focus_in_event().connect(sigc::mem_fun(*desktop_widget, &SPDesktopWidget::onFocusInEvent));
179 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
180 gint prefs_geometry =
181 (2==prefs->getInt("/options/savewindowgeometry/value", 0));
182 if (prefs_geometry) {
183 gint pw = prefs->getInt("/desktop/geometry/width", -1);
184 gint ph = prefs->getInt("/desktop/geometry/height", -1);
185 gint px = prefs->getInt("/desktop/geometry/x", -1);
186 gint py = prefs->getInt("/desktop/geometry/y", -1);
187 gint full = prefs->getBool("/desktop/geometry/fullscreen");
188 gint maxed = prefs->getBool("/desktop/geometry/maximized");
189 if (pw>0 && ph>0) {
190 gint w = MIN(gdk_screen_width(), pw);
191 gint h = MIN(gdk_screen_height(), ph);
192 gint x = MIN(gdk_screen_width() - MIN_ONSCREEN_DISTANCE, px);
193 gint y = MIN(gdk_screen_height() - MIN_ONSCREEN_DISTANCE, py);
194 if (w>0 && h>0) {
195 x = MIN(gdk_screen_width() - w, x);
196 y = MIN(gdk_screen_height() - h, y);
197 desktop->setWindowSize(w, h);
198 }
200 // Only restore xy for the first window so subsequent windows don't overlap exactly
201 // with first. (Maybe rule should be only restore xy if it's different from xy of
202 // other desktops?)
204 // Empirically it seems that active_desktop==this desktop only the first time a
205 // desktop is created.
206 SPDesktop *active_desktop = SP_ACTIVE_DESKTOP;
207 if (active_desktop == desktop || active_desktop==NULL) {
208 desktop->setWindowPosition(Geom::Point(x, y));
209 }
210 }
211 if (maxed) {
212 win->maximize();
213 }
214 if (full) {
215 win->fullscreen();
216 }
217 }
219 } else {
220 gtk_window_set_policy(GTK_WINDOW(win->gobj()), TRUE, TRUE, TRUE);
221 }
223 if ( completeDropTargets == 0 || completeDropTargetsCount == 0 )
224 {
225 std::vector<gchar*> types;
227 GSList *list = gdk_pixbuf_get_formats();
228 while ( list ) {
229 int i = 0;
230 GdkPixbufFormat *one = (GdkPixbufFormat*)list->data;
231 gchar** typesXX = gdk_pixbuf_format_get_mime_types(one);
232 for ( i = 0; typesXX[i]; i++ ) {
233 types.push_back(g_strdup(typesXX[i]));
234 }
235 g_strfreev(typesXX);
237 list = g_slist_next(list);
238 }
239 completeDropTargetsCount = nui_drop_target_entries + types.size();
240 completeDropTargets = new GtkTargetEntry[completeDropTargetsCount];
241 for ( int i = 0; i < (int)nui_drop_target_entries; i++ ) {
242 completeDropTargets[i] = ui_drop_target_entries[i];
243 }
244 int pos = nui_drop_target_entries;
246 for (std::vector<gchar*>::iterator it = types.begin() ; it != types.end() ; it++) {
247 completeDropTargets[pos].target = *it;
248 completeDropTargets[pos].flags = 0;
249 completeDropTargets[pos].info = IMAGE_DATA;
250 pos++;
251 }
252 }
254 gtk_drag_dest_set((GtkWidget*)win->gobj(),
255 GTK_DEST_DEFAULT_ALL,
256 completeDropTargets,
257 completeDropTargetsCount,
258 GdkDragAction(GDK_ACTION_COPY | GDK_ACTION_MOVE));
261 g_signal_connect(G_OBJECT(win->gobj()),
262 "drag_data_received",
263 G_CALLBACK(sp_ui_drag_data_received),
264 NULL);
265 g_signal_connect(G_OBJECT(win->gobj()),
266 "drag_motion",
267 G_CALLBACK(sp_ui_drag_motion),
268 NULL);
269 g_signal_connect(G_OBJECT(win->gobj()),
270 "drag_leave",
271 G_CALLBACK(sp_ui_drag_leave),
272 NULL);
273 win->show();
275 // needed because the first ACTIVATE_DESKTOP was sent when there was no window yet
276 if ( SP_IS_DESKTOP_WIDGET(vw) ) {
277 inkscape_reactivate_desktop(SP_DESKTOP_WIDGET(vw)->desktop);
278 }
279 }
281 void
282 sp_ui_new_view()
283 {
284 SPDocument *document;
285 SPViewWidget *dtw;
287 document = SP_ACTIVE_DOCUMENT;
288 if (!document) return;
290 dtw = sp_desktop_widget_new(sp_document_namedview(document, NULL));
291 g_return_if_fail(dtw != NULL);
293 sp_create_window(dtw, TRUE);
294 sp_namedview_window_from_document(static_cast<SPDesktop*>(dtw->view));
295 sp_namedview_update_layers_from_document(static_cast<SPDesktop*>(dtw->view));
296 }
298 /* TODO: not yet working */
299 /* To be re-enabled (by adding to menu) once it works. */
300 void sp_ui_new_view_preview()
301 {
302 SPDocument *document = SP_ACTIVE_DOCUMENT;
303 if ( document ) {
304 SPViewWidget *dtw = reinterpret_cast<SPViewWidget *>(sp_svg_view_widget_new(document));
305 g_return_if_fail(dtw != NULL);
306 SP_SVG_VIEW_WIDGET(dtw)->setResize(true, 400.0, 400.0);
308 sp_create_window(dtw, FALSE);
309 }
310 }
312 /**
313 * \param widget unused
314 */
315 void
316 sp_ui_close_view(GtkWidget */*widget*/)
317 {
318 SPDesktop *dt = SP_ACTIVE_DESKTOP;
320 if (dt == NULL) {
321 return;
322 }
324 if (dt->shutdown()) {
325 return; // Shutdown operation has been canceled, so do nothing
326 }
328 // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
329 // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
330 dt->destroyWidget();
331 }
334 /**
335 * sp_ui_close_all
336 *
337 * This function is called to exit the program, and iterates through all
338 * open document view windows, attempting to close each in turn. If the
339 * view has unsaved information, the user will be prompted to save,
340 * discard, or cancel.
341 *
342 * Returns FALSE if the user cancels the close_all operation, TRUE
343 * otherwise.
344 */
345 unsigned int
346 sp_ui_close_all(void)
347 {
348 /* Iterate through all the windows, destroying each in the order they
349 become active */
350 while (SP_ACTIVE_DESKTOP) {
351 SPDesktop *dt = SP_ACTIVE_DESKTOP;
352 if (dt->shutdown()) {
353 /* The user canceled the operation, so end doing the close */
354 return FALSE;
355 }
356 // Shutdown can proceed; use the stored reference to the desktop here instead of the current SP_ACTIVE_DESKTOP,
357 // because the user might have changed the focus in the meantime (see bug #381357 on Launchpad)
358 dt->destroyWidget();
359 }
361 return TRUE;
362 }
364 /*
365 * Some day when the right-click menus are ready to start working
366 * smarter with the verbs, we'll need to change this NULL being
367 * sent to sp_action_perform to something useful, or set some kind
368 * of global "right-clicked position" variable for actions to
369 * investigate when they're called.
370 */
371 static void
372 sp_ui_menu_activate(void */*object*/, SPAction *action)
373 {
374 if (!temporarily_block_actions) {
375 sp_action_perform(action, NULL);
376 }
377 }
379 static void
380 sp_ui_menu_select_action(void */*object*/, SPAction *action)
381 {
382 action->view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, action->tip);
383 }
385 static void
386 sp_ui_menu_deselect_action(void */*object*/, SPAction *action)
387 {
388 action->view->tipsMessageContext()->clear();
389 }
391 static void
392 sp_ui_menu_select(gpointer object, gpointer tip)
393 {
394 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
395 view->tipsMessageContext()->set(Inkscape::NORMAL_MESSAGE, (gchar *)tip);
396 }
398 static void
399 sp_ui_menu_deselect(gpointer object)
400 {
401 Inkscape::UI::View::View *view = static_cast<Inkscape::UI::View::View*> (g_object_get_data(G_OBJECT(object), "view"));
402 view->tipsMessageContext()->clear();
403 }
405 /**
406 * sp_ui_menuitem_add_icon
407 *
408 * Creates and attaches a scaled icon to the given menu item.
409 *
410 */
411 void
412 sp_ui_menuitem_add_icon( GtkWidget *item, gchar *icon_name )
413 {
414 static bool iconsInjected = false;
415 if ( !iconsInjected ) {
416 iconsInjected = true;
417 injectRenamedIcons();
418 }
419 GtkWidget *icon;
421 icon = sp_icon_new( Inkscape::ICON_SIZE_MENU, icon_name );
422 gtk_widget_show(icon);
423 gtk_image_menu_item_set_image((GtkImageMenuItem *) item, icon);
424 } // end of sp_ui_menu_add_icon
426 /**
427 * sp_ui_menu_append_item
428 *
429 * Appends a UI item with specific info for Inkscape/Sodipodi.
430 *
431 */
432 static GtkWidget *
433 sp_ui_menu_append_item( GtkMenu *menu, gchar const *stock,
434 gchar const *label, gchar const *tip, Inkscape::UI::View::View *view, GCallback callback,
435 gpointer data, gboolean with_mnemonic = TRUE )
436 {
437 GtkWidget *item;
439 if (stock) {
440 item = gtk_image_menu_item_new_from_stock(stock, NULL);
441 } else if (label) {
442 item = (with_mnemonic)
443 ? gtk_image_menu_item_new_with_mnemonic(label) :
444 gtk_image_menu_item_new_with_label(label);
445 } else {
446 item = gtk_separator_menu_item_new();
447 }
449 gtk_widget_show(item);
451 if (callback) {
452 g_signal_connect(G_OBJECT(item), "activate", callback, data);
453 }
455 if (tip && view) {
456 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
457 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) tip );
458 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
459 }
461 gtk_menu_append(GTK_MENU(menu), item);
463 return item;
465 } // end of sp_ui_menu_append_item()
467 void
468 sp_ui_dialog_title_string(Inkscape::Verb *verb, gchar *c)
469 {
470 SPAction *action;
471 unsigned int shortcut;
472 gchar *s;
473 gchar *atitle;
475 action = verb->get_action(NULL);
476 if (!action)
477 return;
479 atitle = sp_action_get_title(action);
481 s = g_stpcpy(c, atitle);
483 g_free(atitle);
485 shortcut = sp_shortcut_get_primary(verb);
486 if (shortcut!=GDK_VoidSymbol) {
487 gchar* key = sp_shortcut_get_label(shortcut);
488 s = g_stpcpy(s, " (");
489 s = g_stpcpy(s, key);
490 s = g_stpcpy(s, ")");
491 g_free(key);
492 }
493 }
496 /**
497 * sp_ui_menu_append_item_from_verb
498 *
499 * Appends a custom menu UI from a verb.
500 *
501 */
503 static GtkWidget *
504 sp_ui_menu_append_item_from_verb(GtkMenu *menu, Inkscape::Verb *verb, Inkscape::UI::View::View *view, bool radio = false, GSList *group = NULL)
505 {
506 SPAction *action;
507 GtkWidget *item;
509 if (verb->get_code() == SP_VERB_NONE) {
511 item = gtk_separator_menu_item_new();
513 } else {
514 unsigned int shortcut;
516 action = verb->get_action(view);
518 if (!action) return NULL;
520 shortcut = sp_shortcut_get_primary(verb);
521 if (shortcut!=GDK_VoidSymbol) {
522 gchar* c = sp_shortcut_get_label(shortcut);
523 GtkWidget *const hb = gtk_hbox_new(FALSE, 16);
524 GtkWidget *const name_lbl = gtk_label_new("");
525 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
526 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
527 gtk_box_pack_start((GtkBox *) hb, name_lbl, TRUE, TRUE, 0);
528 GtkWidget *const accel_lbl = gtk_label_new(c);
529 gtk_misc_set_alignment((GtkMisc *) accel_lbl, 1.0, 0.5);
530 gtk_box_pack_end((GtkBox *) hb, accel_lbl, FALSE, FALSE, 0);
531 gtk_widget_show_all(hb);
532 if (radio) {
533 item = gtk_radio_menu_item_new (group);
534 } else {
535 item = gtk_image_menu_item_new();
536 }
537 gtk_container_add((GtkContainer *) item, hb);
538 g_free(c);
539 } else {
540 if (radio) {
541 item = gtk_radio_menu_item_new (group);
542 } else {
543 item = gtk_image_menu_item_new ();
544 }
545 GtkWidget *const name_lbl = gtk_label_new("");
546 gtk_label_set_markup_with_mnemonic(GTK_LABEL(name_lbl), action->name);
547 gtk_misc_set_alignment((GtkMisc *) name_lbl, 0.0, 0.5);
548 gtk_container_add((GtkContainer *) item, name_lbl);
549 }
551 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
552 if (!action->sensitive) {
553 gtk_widget_set_sensitive(item, FALSE);
554 }
556 if (action->image) {
557 sp_ui_menuitem_add_icon(item, action->image);
558 }
559 gtk_widget_set_events(item, GDK_KEY_PRESS_MASK);
560 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
561 g_signal_connect( G_OBJECT(item), "activate", G_CALLBACK(sp_ui_menu_activate), action );
562 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select_action), action );
563 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect_action), action );
564 }
566 gtk_widget_show(item);
567 gtk_menu_append(GTK_MENU(menu), item);
569 return item;
571 } // end of sp_ui_menu_append_item_from_verb
574 static Glib::ustring getLayoutPrefPath( Inkscape::UI::View::View *view )
575 {
576 Glib::ustring prefPath;
578 if (reinterpret_cast<SPDesktop*>(view)->is_focusMode()) {
579 prefPath = "/focus/";
580 } else if (reinterpret_cast<SPDesktop*>(view)->is_fullscreen()) {
581 prefPath = "/fullscreen/";
582 } else {
583 prefPath = "/window/";
584 }
586 return prefPath;
587 }
589 static void
590 checkitem_toggled(GtkCheckMenuItem *menuitem, gpointer user_data)
591 {
592 gchar const *pref = (gchar const *) user_data;
593 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
595 Glib::ustring pref_path = getLayoutPrefPath( view );
596 pref_path += pref;
597 pref_path += "/state";
599 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
600 gboolean checked = gtk_check_menu_item_get_active(menuitem);
601 prefs->setBool(pref_path, checked);
603 reinterpret_cast<SPDesktop*>(view)->layoutWidget();
604 }
606 static gboolean
607 checkitem_update(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
608 {
609 GtkCheckMenuItem *menuitem=GTK_CHECK_MENU_ITEM(widget);
611 gchar const *pref = (gchar const *) user_data;
612 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
614 Glib::ustring pref_path = getLayoutPrefPath( view );
615 pref_path += pref;
616 pref_path += "/state";
618 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
619 bool ison = prefs->getBool(pref_path, true);
621 g_signal_handlers_block_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
622 gtk_check_menu_item_set_active(menuitem, ison);
623 g_signal_handlers_unblock_by_func(G_OBJECT(menuitem), (gpointer)(GCallback)checkitem_toggled, user_data);
625 return FALSE;
626 }
628 static void taskToggled(GtkCheckMenuItem *menuitem, gpointer userData)
629 {
630 if ( gtk_check_menu_item_get_active(menuitem) ) {
631 gint taskNum = GPOINTER_TO_INT(userData);
632 taskNum = (taskNum < 0) ? 0 : (taskNum > 2) ? 2 : taskNum;
634 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(menuitem), "view");
636 // note: this will change once more options are in the task set support:
637 Inkscape::UI::UXManager::getInstance()->setTask( dynamic_cast<SPDesktop*>(view), taskNum );
638 }
639 }
642 /**
643 * \brief Callback function to update the status of the radio buttons in the View -> Display mode menu (Normal, No Filters, Outline)
644 */
646 static gboolean
647 update_view_menu(GtkWidget *widget, GdkEventExpose */*event*/, gpointer user_data)
648 {
649 SPAction *action = (SPAction *) user_data;
650 g_assert(action->id != NULL);
652 Inkscape::UI::View::View *view = (Inkscape::UI::View::View *) g_object_get_data(G_OBJECT(widget), "view");
653 SPDesktop *dt = static_cast<SPDesktop*>(view);
654 Inkscape::RenderMode mode = dt->getMode();
656 bool new_state = false;
657 if (!strcmp(action->id, "ViewModeNormal")) {
658 new_state = mode == Inkscape::RENDERMODE_NORMAL;
659 } else if (!strcmp(action->id, "ViewModeNoFilters")) {
660 new_state = mode == Inkscape::RENDERMODE_NO_FILTERS;
661 } else if (!strcmp(action->id, "ViewModeOutline")) {
662 new_state = mode == Inkscape::RENDERMODE_OUTLINE;
663 } else if (!strcmp(action->id, "ViewModePrintColorsPreview")) {
664 new_state = mode == Inkscape::RENDERMODE_PRINT_COLORS_PREVIEW;
665 } else {
666 g_warning("update_view_menu does not handle this verb");
667 }
669 if (new_state) { //only one of the radio buttons has to be activated; the others will automatically be deactivated
670 if (!gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (widget))) {
671 // When the GtkMenuItem version of the "activate" signal has been emitted by a GtkRadioMenuItem, there is a second
672 // emission as the most recently active item is toggled to inactive. This is dealt with before the original signal is handled.
673 // This emission however should not invoke any actions, hence we block it here:
674 temporarily_block_actions = true;
675 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (widget), TRUE);
676 temporarily_block_actions = false;
677 }
678 }
680 return FALSE;
681 }
683 void
684 sp_ui_menu_append_check_item_from_verb(GtkMenu *menu, Inkscape::UI::View::View *view, gchar const *label, gchar const *tip, gchar const *pref,
685 void (*callback_toggle)(GtkCheckMenuItem *, gpointer user_data),
686 gboolean (*callback_update)(GtkWidget *widget, GdkEventExpose *event, gpointer user_data),
687 Inkscape::Verb *verb)
688 {
689 unsigned int shortcut = (verb) ? sp_shortcut_get_primary(verb) : 0;
690 SPAction *action = (verb) ? verb->get_action(view) : 0;
691 GtkWidget *item = gtk_check_menu_item_new();
693 if (verb && shortcut!=GDK_VoidSymbol) {
694 gchar* c = sp_shortcut_get_label(shortcut);
696 GtkWidget *hb = gtk_hbox_new(FALSE, 16);
698 {
699 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
700 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
701 gtk_box_pack_start((GtkBox *) hb, l, TRUE, TRUE, 0);
702 }
704 {
705 GtkWidget *l = gtk_label_new(c);
706 gtk_misc_set_alignment((GtkMisc *) l, 1.0, 0.5);
707 gtk_box_pack_end((GtkBox *) hb, l, FALSE, FALSE, 0);
708 }
710 gtk_widget_show_all(hb);
712 gtk_container_add((GtkContainer *) item, hb);
713 g_free(c);
714 } else {
715 GtkWidget *l = gtk_label_new_with_mnemonic(action ? action->name : label);
716 gtk_misc_set_alignment((GtkMisc *) l, 0.0, 0.5);
717 gtk_container_add((GtkContainer *) item, l);
718 }
719 #if 0
720 nr_active_object_add_listener((NRActiveObject *)action, (NRObjectEventVector *)&menu_item_event_vector, sizeof(SPActionEventVector), item);
721 if (!action->sensitive) {
722 gtk_widget_set_sensitive(item, FALSE);
723 }
724 #endif
725 gtk_widget_show(item);
727 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
729 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
731 g_signal_connect( G_OBJECT(item), "toggled", (GCallback) callback_toggle, (void *) pref);
732 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) callback_update, (void *) pref);
734 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) (action ? action->tip : tip));
735 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
736 }
738 static void
739 sp_recent_open(GtkRecentChooser *recent_menu, gpointer /*user_data*/)
740 {
741 // dealing with the bizarre filename convention in Inkscape for now
742 gchar *uri = gtk_recent_chooser_get_current_uri(GTK_RECENT_CHOOSER(recent_menu));
743 gchar *local_fn = g_filename_from_uri(uri, NULL, NULL);
744 gchar *utf8_fn = g_filename_to_utf8(local_fn, -1, NULL, NULL, NULL);
745 sp_file_open(utf8_fn, NULL);
746 g_free(utf8_fn);
747 g_free(local_fn);
748 g_free(uri);
749 }
751 static void
752 sp_file_new_from_template(GtkWidget */*widget*/, gchar const *uri)
753 {
754 sp_file_new(uri);
755 }
757 void
758 sp_menu_append_new_templates(GtkWidget *menu, Inkscape::UI::View::View *view)
759 {
760 std::list<gchar *> sources;
761 sources.push_back( profile_path("templates") ); // first try user's local dir
762 sources.push_back( g_strdup(INKSCAPE_TEMPLATESDIR) ); // then the system templates dir
764 // Use this loop to iterate through a list of possible document locations.
765 while (!sources.empty()) {
766 gchar *dirname = sources.front();
768 if ( Inkscape::IO::file_test( dirname, (GFileTest)(G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) ) ) {
769 GError *err = 0;
770 GDir *dir = g_dir_open(dirname, 0, &err);
772 if (dir) {
773 for (gchar const *file = g_dir_read_name(dir); file != NULL; file = g_dir_read_name(dir)) {
774 if (!g_str_has_suffix(file, ".svg") && !g_str_has_suffix(file, ".svgz"))
775 continue; // skip non-svg files
777 gchar *basename = g_path_get_basename(file);
778 if (g_str_has_suffix(basename, ".svg") && g_str_has_prefix(basename, "default."))
779 continue; // skip default.*.svg (i.e. default.svg and translations) - it's in the menu already
781 gchar const *filepath = g_build_filename(dirname, file, NULL);
782 gchar *dupfile = g_strndup(file, strlen(file) - 4);
783 gchar *filename = g_filename_to_utf8(dupfile, -1, NULL, NULL, NULL);
784 g_free(dupfile);
785 GtkWidget *item = gtk_menu_item_new_with_label(filename);
786 g_free(filename);
788 gtk_widget_show(item);
789 // how does "filepath" ever get freed?
790 g_signal_connect(G_OBJECT(item),
791 "activate",
792 G_CALLBACK(sp_file_new_from_template),
793 (gpointer) filepath);
795 if (view) {
796 // set null tip for now; later use a description from the template file
797 g_object_set_data(G_OBJECT(item), "view", (gpointer) view);
798 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), (gpointer) NULL );
799 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), NULL);
800 }
802 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
803 }
804 g_dir_close(dir);
805 }
806 }
808 // toss the dirname
809 g_free(dirname);
810 sources.pop_front();
811 }
812 }
814 void
815 sp_ui_checkboxes_menus(GtkMenu *m, Inkscape::UI::View::View *view)
816 {
817 //sp_ui_menu_append_check_item_from_verb(m, view, _("_Menu"), _("Show or hide the menu bar"), "menu",
818 // checkitem_toggled, checkitem_update, 0);
819 sp_ui_menu_append_check_item_from_verb(m, view, _("Commands Bar"), _("Show or hide the Commands bar (under the menu)"), "commands",
820 checkitem_toggled, checkitem_update, 0);
821 sp_ui_menu_append_check_item_from_verb(m, view, _("Snap Controls Bar"), _("Show or hide the snapping controls"), "snaptoolbox",
822 checkitem_toggled, checkitem_update, 0);
823 sp_ui_menu_append_check_item_from_verb(m, view, _("Tool Controls Bar"), _("Show or hide the Tool Controls bar"), "toppanel",
824 checkitem_toggled, checkitem_update, 0);
825 sp_ui_menu_append_check_item_from_verb(m, view, _("_Toolbox"), _("Show or hide the main toolbox (on the left)"), "toolbox",
826 checkitem_toggled, checkitem_update, 0);
827 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "rulers",
828 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_RULERS));
829 sp_ui_menu_append_check_item_from_verb(m, view, NULL, NULL, "scrollbars",
830 checkitem_toggled, checkitem_update, Inkscape::Verb::get(SP_VERB_TOGGLE_SCROLLBARS));
831 sp_ui_menu_append_check_item_from_verb(m, view, _("_Palette"), _("Show or hide the color palette"), "panels",
832 checkitem_toggled, checkitem_update, 0);
833 sp_ui_menu_append_check_item_from_verb(m, view, _("_Statusbar"), _("Show or hide the statusbar (at the bottom of the window)"), "statusbar",
834 checkitem_toggled, checkitem_update, 0);
835 }
838 void addTaskMenuItems(GtkMenu *menu, Inkscape::UI::View::View *view)
839 {
840 gchar const* data[] = {
841 _("Default"), _("Default interface setup"),
842 _("Custom"), _("Set the custom task"),
843 _("Wide"), _("Setup for widescreen work"),
844 0, 0
845 };
847 GSList *group = 0;
848 int count = 0;
849 gint active = Inkscape::UI::UXManager::getInstance()->getDefaultTask( dynamic_cast<SPDesktop*>(view) );
850 for (gchar const **strs = data; strs[0]; strs += 2, count++)
851 {
852 GtkWidget *item = gtk_radio_menu_item_new_with_label( group, strs[0] );
853 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item) );
854 if ( count == active )
855 {
856 gtk_check_menu_item_set_active( GTK_CHECK_MENU_ITEM(item), TRUE );
857 }
859 g_object_set_data( G_OBJECT(item), "view", view );
860 g_signal_connect( G_OBJECT(item), "toggled", reinterpret_cast<GCallback>(taskToggled), GINT_TO_POINTER(count) );
861 g_signal_connect( G_OBJECT(item), "select", G_CALLBACK(sp_ui_menu_select), const_cast<gchar*>(strs[1]) );
862 g_signal_connect( G_OBJECT(item), "deselect", G_CALLBACK(sp_ui_menu_deselect), 0 );
864 gtk_widget_show( item );
865 gtk_menu_shell_append( GTK_MENU_SHELL(menu), item );
866 }
867 }
870 /** @brief Observer that updates the recent list's max document count */
871 class MaxRecentObserver : public Inkscape::Preferences::Observer {
872 public:
873 MaxRecentObserver(GtkWidget *recent_menu) :
874 Observer("/options/maxrecentdocuments/value"),
875 _rm(recent_menu)
876 {}
877 virtual void notify(Inkscape::Preferences::Entry const &e) {
878 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(_rm), e.getInt());
879 // hack: the recent menu doesn't repopulate after changing the limit, so we force it
880 g_signal_emit_by_name((gpointer) gtk_recent_manager_get_default(), "changed");
881 }
882 private:
883 GtkWidget *_rm;
884 };
886 /** \brief This function turns XML into a menu
887 \param menus This is the XML that defines the menu
888 \param menu Menu to be added to
889 \param view The View that this menu is being built for
891 This function is realitively simple as it just goes through the XML
892 and parses the individual elements. In the case of a submenu, it
893 just calls itself recursively. Because it is only reasonable to have
894 a couple of submenus, it is unlikely this will go more than two or
895 three times.
897 In the case of an unrecognized verb, a menu item is made to identify
898 the verb that is missing, and display that. The menu item is also made
899 insensitive.
900 */
901 void
902 sp_ui_build_dyn_menus(Inkscape::XML::Node *menus, GtkWidget *menu, Inkscape::UI::View::View *view)
903 {
904 if (menus == NULL) return;
905 if (menu == NULL) return;
906 GSList *group = NULL;
908 for (Inkscape::XML::Node *menu_pntr = menus;
909 menu_pntr != NULL;
910 menu_pntr = menu_pntr->next()) {
911 if (!strcmp(menu_pntr->name(), "submenu")) {
912 GtkWidget *mitem = gtk_menu_item_new_with_mnemonic(_(menu_pntr->attribute("name")));
913 GtkWidget *submenu = gtk_menu_new();
914 sp_ui_build_dyn_menus(menu_pntr->firstChild(), submenu, view);
915 gtk_menu_item_set_submenu(GTK_MENU_ITEM(mitem), GTK_WIDGET(submenu));
916 gtk_menu_shell_append(GTK_MENU_SHELL(menu), mitem);
917 continue;
918 }
919 if (!strcmp(menu_pntr->name(), "verb")) {
920 gchar const *verb_name = menu_pntr->attribute("verb-id");
921 Inkscape::Verb *verb = Inkscape::Verb::getbyid(verb_name);
923 if (verb != NULL) {
924 if (menu_pntr->attribute("radio") != NULL) {
925 GtkWidget *item = sp_ui_menu_append_item_from_verb (GTK_MENU(menu), verb, view, true, group);
926 group = gtk_radio_menu_item_get_group( GTK_RADIO_MENU_ITEM(item));
927 if (menu_pntr->attribute("default") != NULL) {
928 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (item), TRUE);
929 }
930 if (verb->get_code() != SP_VERB_NONE) {
931 SPAction *action = verb->get_action(view);
932 g_signal_connect( G_OBJECT(item), "expose_event", (GCallback) update_view_menu, (void *) action);
933 }
934 } else {
935 sp_ui_menu_append_item_from_verb(GTK_MENU(menu), verb, view);
936 group = NULL;
937 }
938 } else {
939 gchar string[120];
940 g_snprintf(string, 120, _("Verb \"%s\" Unknown"), verb_name);
941 string[119] = '\0'; /* may not be terminated */
942 GtkWidget *item = gtk_menu_item_new_with_label(string);
943 gtk_widget_set_sensitive(item, false);
944 gtk_widget_show(item);
945 gtk_menu_append(GTK_MENU(menu), item);
946 }
947 continue;
948 }
949 if (!strcmp(menu_pntr->name(), "separator")
950 // This was spelt wrong in the original version
951 // and so this is for backward compatibility. It can
952 // probably be dropped after the 0.44 release.
953 || !strcmp(menu_pntr->name(), "seperator")) {
954 GtkWidget *item = gtk_separator_menu_item_new();
955 gtk_widget_show(item);
956 gtk_menu_append(GTK_MENU(menu), item);
957 continue;
958 }
959 if (!strcmp(menu_pntr->name(), "template-list")) {
960 sp_menu_append_new_templates(menu, view);
961 continue;
962 }
963 if (!strcmp(menu_pntr->name(), "recent-file-list")) {
964 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
966 // create recent files menu
967 int max_recent = prefs->getInt("/options/maxrecentdocuments/value");
968 GtkWidget *recent_menu = gtk_recent_chooser_menu_new_for_manager(gtk_recent_manager_get_default());
969 gtk_recent_chooser_set_limit(GTK_RECENT_CHOOSER(recent_menu), max_recent);
970 // sort most recently used documents first to preserve previous behavior
971 gtk_recent_chooser_set_sort_type(GTK_RECENT_CHOOSER(recent_menu), GTK_RECENT_SORT_MRU);
972 g_signal_connect(G_OBJECT(recent_menu), "item-activated", G_CALLBACK(sp_recent_open), (gpointer) NULL);
974 // add filter to only open files added by Inkscape
975 GtkRecentFilter *inkscape_only_filter = gtk_recent_filter_new();
976 gtk_recent_filter_add_application(inkscape_only_filter, g_get_prgname());
977 gtk_recent_chooser_add_filter(GTK_RECENT_CHOOSER(recent_menu), inkscape_only_filter);
979 gtk_recent_chooser_set_show_tips (GTK_RECENT_CHOOSER(recent_menu), TRUE);
980 gtk_recent_chooser_set_show_not_found (GTK_RECENT_CHOOSER(recent_menu), FALSE);
982 GtkWidget *recent_item = gtk_menu_item_new_with_mnemonic(_("Open _Recent"));
983 gtk_menu_item_set_submenu(GTK_MENU_ITEM(recent_item), recent_menu);
985 gtk_menu_append(GTK_MENU(menu), GTK_WIDGET(recent_item));
986 // this will just sit and update the list's item count
987 static MaxRecentObserver *mro = new MaxRecentObserver(recent_menu);
988 prefs->addObserver(*mro);
989 continue;
990 }
991 if (!strcmp(menu_pntr->name(), "objects-checkboxes")) {
992 sp_ui_checkboxes_menus(GTK_MENU(menu), view);
993 continue;
994 }
995 if (!strcmp(menu_pntr->name(), "task-checkboxes")) {
996 addTaskMenuItems(GTK_MENU(menu), view);
997 continue;
998 }
999 }
1000 }
1002 /** \brief Build the main tool bar
1003 \param view View to build the bar for
1005 Currently the main tool bar is built as a dynamic XML menu using
1006 \c sp_ui_build_dyn_menus. This function builds the bar, and then
1007 pass it to get items attached to it.
1008 */
1009 GtkWidget *
1010 sp_ui_main_menubar(Inkscape::UI::View::View *view)
1011 {
1012 GtkWidget *mbar = gtk_menu_bar_new();
1014 #ifdef GDK_WINDOWING_QUARTZ
1015 ige_mac_menu_set_menu_bar(GTK_MENU_SHELL(mbar));
1016 #endif
1018 sp_ui_build_dyn_menus(inkscape_get_menus(INKSCAPE), mbar, view);
1020 #ifdef GDK_WINDOWING_QUARTZ
1021 return NULL;
1022 #else
1023 return mbar;
1024 #endif
1025 }
1027 static void leave_group(GtkMenuItem *, SPDesktop *desktop) {
1028 desktop->setCurrentLayer(SP_OBJECT_PARENT(desktop->currentLayer()));
1029 }
1031 static void enter_group(GtkMenuItem *mi, SPDesktop *desktop) {
1032 desktop->setCurrentLayer(reinterpret_cast<SPObject *>(g_object_get_data(G_OBJECT(mi), "group")));
1033 sp_desktop_selection(desktop)->clear();
1034 }
1036 GtkWidget *
1037 sp_ui_context_menu(Inkscape::UI::View::View *view, SPItem *item)
1038 {
1039 GtkWidget *m;
1040 SPDesktop *dt;
1042 dt = static_cast<SPDesktop*>(view);
1044 m = gtk_menu_new();
1046 /* Undo and Redo */
1047 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_UNDO), view);
1048 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_REDO), view);
1050 /* Separator */
1051 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1053 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_CUT), view);
1054 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_COPY), view);
1055 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_PASTE), view);
1057 /* Separator */
1058 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1060 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DUPLICATE), view);
1061 sp_ui_menu_append_item_from_verb(GTK_MENU(m), Inkscape::Verb::get(SP_VERB_EDIT_DELETE), view);
1063 /* Item menu */
1064 if (item) {
1065 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1066 sp_object_menu((SPObject *) item, dt, GTK_MENU(m));
1067 }
1069 /* layer menu */
1070 SPGroup *group=NULL;
1071 if (item) {
1072 if (SP_IS_GROUP(item)) {
1073 group = SP_GROUP(item);
1074 } else if ( item != dt->currentRoot() && SP_IS_GROUP(SP_OBJECT_PARENT(item)) ) {
1075 group = SP_GROUP(SP_OBJECT_PARENT(item));
1076 }
1077 }
1079 if (( group && group != dt->currentLayer() ) ||
1080 ( dt->currentLayer() != dt->currentRoot() && SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) ) {
1081 /* Separator */
1082 sp_ui_menu_append_item(GTK_MENU(m), NULL, NULL, NULL, NULL, NULL, NULL);
1083 }
1085 if ( group && group != dt->currentLayer() ) {
1086 /* TRANSLATORS: #%s is the id of the group e.g. <g id="#g7">, not a number. */
1087 gchar *label=g_strdup_printf(_("Enter group #%s"), group->getId());
1088 GtkWidget *w = gtk_menu_item_new_with_label(label);
1089 g_free(label);
1090 g_object_set_data(G_OBJECT(w), "group", group);
1091 g_signal_connect(G_OBJECT(w), "activate", GCallback(enter_group), dt);
1092 gtk_widget_show(w);
1093 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1094 }
1096 if ( dt->currentLayer() != dt->currentRoot() ) {
1097 if ( SP_OBJECT_PARENT(dt->currentLayer()) != dt->currentRoot() ) {
1098 GtkWidget *w = gtk_menu_item_new_with_label(_("Go to parent"));
1099 g_signal_connect(G_OBJECT(w), "activate", GCallback(leave_group), dt);
1100 gtk_widget_show(w);
1101 gtk_menu_shell_append(GTK_MENU_SHELL(m), w);
1103 }
1104 }
1106 return m;
1107 }
1109 /* Drag and Drop */
1110 void
1111 sp_ui_drag_data_received(GtkWidget *widget,
1112 GdkDragContext *drag_context,
1113 gint x, gint y,
1114 GtkSelectionData *data,
1115 guint info,
1116 guint /*event_time*/,
1117 gpointer /*user_data*/)
1118 {
1119 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1120 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1122 switch (info) {
1123 #if ENABLE_MAGIC_COLORS
1124 case APP_X_INKY_COLOR:
1125 {
1126 int destX = 0;
1127 int destY = 0;
1128 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1129 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1131 SPItem *item = desktop->getItemAtPoint( where, true );
1132 if ( item )
1133 {
1134 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1136 if ( data->length >= 8 ) {
1137 cmsHPROFILE srgbProf = cmsCreate_sRGBProfile();
1139 gchar c[64] = {0};
1140 // Careful about endian issues.
1141 guint16* dataVals = (guint16*)data->data;
1142 sp_svg_write_color( c, sizeof(c),
1143 SP_RGBA32_U_COMPOSE(
1144 0x0ff & (dataVals[0] >> 8),
1145 0x0ff & (dataVals[1] >> 8),
1146 0x0ff & (dataVals[2] >> 8),
1147 0xff // can't have transparency in the color itself
1148 //0x0ff & (data->data[3] >> 8),
1149 ));
1150 SPCSSAttr *css = sp_repr_css_attr_new();
1151 bool updatePerformed = false;
1153 if ( data->length > 14 ) {
1154 int flags = dataVals[4];
1156 // piggie-backed palette entry info
1157 int index = dataVals[5];
1158 Glib::ustring palName;
1159 for ( int i = 0; i < dataVals[6]; i++ ) {
1160 palName += (gunichar)dataVals[7+i];
1161 }
1163 // Now hook in a magic tag of some sort.
1164 if ( !palName.empty() && (flags & 1) ) {
1165 gchar* str = g_strdup_printf("%d|", index);
1166 palName.insert( 0, str );
1167 g_free(str);
1168 str = 0;
1170 SP_OBJECT(item)->setAttribute(
1171 fillnotstroke ? "inkscape:x-fill-tag":"inkscape:x-stroke-tag",
1172 palName.c_str(),
1173 false );
1174 item->updateRepr();
1176 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1177 updatePerformed = true;
1178 }
1179 }
1181 if ( !updatePerformed ) {
1182 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", c );
1183 }
1185 sp_desktop_apply_css_recursive( item, css, true );
1186 item->updateRepr();
1188 SPDocumentUndo::done( doc , SP_VERB_NONE,
1189 _("Drop color"));
1191 if ( srgbProf ) {
1192 cmsCloseProfile( srgbProf );
1193 }
1194 }
1195 }
1196 }
1197 break;
1198 #endif // ENABLE_MAGIC_COLORS
1200 case APP_X_COLOR:
1201 {
1202 int destX = 0;
1203 int destY = 0;
1204 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1205 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1206 Geom::Point const button_dt(desktop->w2d(where));
1207 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1209 if ( data->length == 8 ) {
1210 gchar colorspec[64] = {0};
1211 // Careful about endian issues.
1212 guint16* dataVals = (guint16*)data->data;
1213 sp_svg_write_color( colorspec, sizeof(colorspec),
1214 SP_RGBA32_U_COMPOSE(
1215 0x0ff & (dataVals[0] >> 8),
1216 0x0ff & (dataVals[1] >> 8),
1217 0x0ff & (dataVals[2] >> 8),
1218 0xff // can't have transparency in the color itself
1219 //0x0ff & (data->data[3] >> 8),
1220 ));
1222 SPItem *item = desktop->getItemAtPoint( where, true );
1224 bool consumed = false;
1225 if (desktop->event_context && desktop->event_context->get_drag()) {
1226 consumed = desktop->event_context->get_drag()->dropColor(item, colorspec, button_dt);
1227 if (consumed) {
1228 DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") );
1229 desktop->event_context->get_drag()->updateDraggers();
1230 }
1231 }
1233 //if (!consumed && tools_active(desktop, TOOLS_TEXT)) {
1234 // consumed = sp_text_context_drop_color(c, button_doc);
1235 // if (consumed) {
1236 // SPDocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient stop"));
1237 // }
1238 //}
1240 if (!consumed && item) {
1241 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1242 if (fillnotstroke &&
1243 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1244 Path *livarot_path = Path_for_item(item, true, true);
1245 livarot_path->ConvertWithBackData(0.04);
1247 boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1248 if (position) {
1249 Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1250 Geom::Point delta = nearest - button_doc;
1251 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1252 delta = desktop->d2w(delta);
1253 double stroke_tolerance =
1254 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1255 desktop->current_zoom() *
1256 SP_OBJECT_STYLE (item)->stroke_width.computed *
1257 to_2geom(item->i2d_affine()).descrim() * 0.5
1258 : 0.0)
1259 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1261 if (Geom::L2 (delta) < stroke_tolerance) {
1262 fillnotstroke = false;
1263 }
1264 }
1265 delete livarot_path;
1266 }
1268 SPCSSAttr *css = sp_repr_css_attr_new();
1269 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec );
1271 sp_desktop_apply_css_recursive( item, css, true );
1272 item->updateRepr();
1274 DocumentUndo::done( doc , SP_VERB_NONE,
1275 _("Drop color") );
1276 }
1277 }
1278 }
1279 break;
1281 case APP_OSWB_COLOR:
1282 {
1283 bool worked = false;
1284 Glib::ustring colorspec;
1285 if ( data->format == 8 ) {
1286 ege::PaintDef color;
1287 worked = color.fromMIMEData("application/x-oswb-color",
1288 reinterpret_cast<char*>(data->data),
1289 data->length,
1290 data->format);
1291 if ( worked ) {
1292 if ( color.getType() == ege::PaintDef::CLEAR ) {
1293 colorspec = ""; // TODO check if this is sufficient
1294 } else if ( color.getType() == ege::PaintDef::NONE ) {
1295 colorspec = "none";
1296 } else {
1297 unsigned int r = color.getR();
1298 unsigned int g = color.getG();
1299 unsigned int b = color.getB();
1301 SPGradient* matches = 0;
1302 const GSList *gradients = doc->getResourceList("gradient");
1303 for (const GSList *item = gradients; item; item = item->next) {
1304 SPGradient* grad = SP_GRADIENT(item->data);
1305 if ( color.descr == grad->getId() ) {
1306 if ( grad->hasStops() ) {
1307 matches = grad;
1308 break;
1309 }
1310 }
1311 }
1312 if (matches) {
1313 colorspec = "url(#";
1314 colorspec += matches->getId();
1315 colorspec += ")";
1316 } else {
1317 gchar* tmp = g_strdup_printf("#%02x%02x%02x", r, g, b);
1318 colorspec = tmp;
1319 g_free(tmp);
1320 }
1321 }
1322 }
1323 }
1324 if ( worked ) {
1325 int destX = 0;
1326 int destY = 0;
1327 gtk_widget_translate_coordinates( widget, &(desktop->canvas->widget), x, y, &destX, &destY );
1328 Geom::Point where( sp_canvas_window_to_world( desktop->canvas, Geom::Point( destX, destY ) ) );
1329 Geom::Point const button_dt(desktop->w2d(where));
1330 Geom::Point const button_doc(desktop->dt2doc(button_dt));
1332 SPItem *item = desktop->getItemAtPoint( where, true );
1334 bool consumed = false;
1335 if (desktop->event_context && desktop->event_context->get_drag()) {
1336 consumed = desktop->event_context->get_drag()->dropColor(item, colorspec.c_str(), button_dt);
1337 if (consumed) {
1338 DocumentUndo::done( doc , SP_VERB_NONE, _("Drop color on gradient") );
1339 desktop->event_context->get_drag()->updateDraggers();
1340 }
1341 }
1343 if (!consumed && item) {
1344 bool fillnotstroke = (drag_context->action != GDK_ACTION_MOVE);
1345 if (fillnotstroke &&
1346 (SP_IS_SHAPE(item) || SP_IS_TEXT(item) || SP_IS_FLOWTEXT(item))) {
1347 Path *livarot_path = Path_for_item(item, true, true);
1348 livarot_path->ConvertWithBackData(0.04);
1350 boost::optional<Path::cut_position> position = get_nearest_position_on_Path(livarot_path, button_doc);
1351 if (position) {
1352 Geom::Point nearest = get_point_on_Path(livarot_path, position->piece, position->t);
1353 Geom::Point delta = nearest - button_doc;
1354 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
1355 delta = desktop->d2w(delta);
1356 double stroke_tolerance =
1357 ( !SP_OBJECT_STYLE(item)->stroke.isNone() ?
1358 desktop->current_zoom() *
1359 SP_OBJECT_STYLE (item)->stroke_width.computed *
1360 to_2geom(item->i2d_affine()).descrim() * 0.5
1361 : 0.0)
1362 + prefs->getIntLimited("/options/dragtolerance/value", 0, 0, 100);
1364 if (Geom::L2 (delta) < stroke_tolerance) {
1365 fillnotstroke = false;
1366 }
1367 }
1368 delete livarot_path;
1369 }
1371 SPCSSAttr *css = sp_repr_css_attr_new();
1372 sp_repr_css_set_property( css, fillnotstroke ? "fill":"stroke", colorspec.c_str() );
1374 sp_desktop_apply_css_recursive( item, css, true );
1375 item->updateRepr();
1377 DocumentUndo::done( doc , SP_VERB_NONE,
1378 _("Drop color") );
1379 }
1380 }
1381 }
1382 break;
1384 case SVG_DATA:
1385 case SVG_XML_DATA: {
1386 gchar *svgdata = (gchar *)data->data;
1388 Inkscape::XML::Document *rnewdoc = sp_repr_read_mem(svgdata, data->length, SP_SVG_NS_URI);
1390 if (rnewdoc == NULL) {
1391 sp_ui_error_dialog(_("Could not parse SVG data"));
1392 return;
1393 }
1395 Inkscape::XML::Node *repr = rnewdoc->root();
1396 gchar const *style = repr->attribute("style");
1398 Inkscape::XML::Node *newgroup = rnewdoc->createElement("svg:g");
1399 newgroup->setAttribute("style", style);
1401 Inkscape::XML::Document * xml_doc = doc->getReprDoc();
1402 for (Inkscape::XML::Node *child = repr->firstChild(); child != NULL; child = child->next()) {
1403 Inkscape::XML::Node *newchild = child->duplicate(xml_doc);
1404 newgroup->appendChild(newchild);
1405 }
1407 Inkscape::GC::release(rnewdoc);
1409 // Add it to the current layer
1411 // Greg's edits to add intelligent positioning of svg drops
1412 SPObject *new_obj = NULL;
1413 new_obj = desktop->currentLayer()->appendChildRepr(newgroup);
1415 Inkscape::Selection *selection = sp_desktop_selection(desktop);
1416 selection->set(SP_ITEM(new_obj));
1418 // move to mouse pointer
1419 {
1420 sp_desktop_document(desktop)->ensureUpToDate();
1421 Geom::OptRect sel_bbox = selection->bounds();
1422 if (sel_bbox) {
1423 Geom::Point m( desktop->point() - sel_bbox->midpoint() );
1424 sp_selection_move_relative(selection, m, false);
1425 }
1426 }
1428 Inkscape::GC::release(newgroup);
1429 DocumentUndo::done( doc, SP_VERB_NONE,
1430 _("Drop SVG") );
1431 break;
1432 }
1434 case URI_LIST: {
1435 gchar *uri = (gchar *)data->data;
1436 sp_ui_import_files(uri);
1437 break;
1438 }
1440 case PNG_DATA:
1441 case JPEG_DATA:
1442 case IMAGE_DATA: {
1443 const char *mime = (info == JPEG_DATA ? "image/jpeg" : "image/png");
1445 Inkscape::Extension::DB::InputList o;
1446 Inkscape::Extension::db.get_input_list(o);
1447 Inkscape::Extension::DB::InputList::const_iterator i = o.begin();
1448 while (i != o.end() && strcmp( (*i)->get_mimetype(), mime ) != 0) {
1449 ++i;
1450 }
1451 Inkscape::Extension::Extension *ext = *i;
1452 bool save = (strcmp(ext->get_param_optiongroup("link"), "embed") == 0);
1453 ext->set_param_optiongroup("link", "embed");
1454 ext->set_gui(false);
1456 gchar *filename = g_build_filename( g_get_tmp_dir(), "inkscape-dnd-import", NULL );
1457 g_file_set_contents(filename, reinterpret_cast<gchar const *>(data->data), data->length, NULL);
1458 file_import(doc, filename, ext);
1459 g_free(filename);
1461 ext->set_param_optiongroup("link", save ? "embed" : "link");
1462 ext->set_gui(true);
1463 DocumentUndo::done( doc , SP_VERB_NONE,
1464 _("Drop bitmap image") );
1465 break;
1466 }
1467 }
1468 }
1470 #include "gradient-context.h"
1472 void sp_ui_drag_motion( GtkWidget */*widget*/,
1473 GdkDragContext */*drag_context*/,
1474 gint /*x*/, gint /*y*/,
1475 GtkSelectionData */*data*/,
1476 guint /*info*/,
1477 guint /*event_time*/,
1478 gpointer /*user_data*/)
1479 {
1480 // SPDocument *doc = SP_ACTIVE_DOCUMENT;
1481 // SPDesktop *desktop = SP_ACTIVE_DESKTOP;
1484 // g_message("drag-n-drop motion (%4d, %4d) at %d", x, y, event_time);
1485 }
1487 static void sp_ui_drag_leave( GtkWidget */*widget*/,
1488 GdkDragContext */*drag_context*/,
1489 guint /*event_time*/,
1490 gpointer /*user_data*/ )
1491 {
1492 // g_message("drag-n-drop leave at %d", event_time);
1493 }
1495 static void
1496 sp_ui_import_files(gchar *buffer)
1497 {
1498 GList *list = gnome_uri_list_extract_filenames(buffer);
1499 if (!list)
1500 return;
1501 g_list_foreach(list, sp_ui_import_one_file_with_check, NULL);
1502 g_list_foreach(list, (GFunc) g_free, NULL);
1503 g_list_free(list);
1504 }
1506 static void
1507 sp_ui_import_one_file_with_check(gpointer filename, gpointer /*unused*/)
1508 {
1509 if (filename) {
1510 if (strlen((char const *)filename) > 2)
1511 sp_ui_import_one_file((char const *)filename);
1512 }
1513 }
1515 static void
1516 sp_ui_import_one_file(char const *filename)
1517 {
1518 SPDocument *doc = SP_ACTIVE_DOCUMENT;
1519 if (!doc) return;
1521 if (filename == NULL) return;
1523 // Pass off to common implementation
1524 // TODO might need to get the proper type of Inkscape::Extension::Extension
1525 file_import( doc, filename, NULL );
1526 }
1528 void
1529 sp_ui_error_dialog(gchar const *message)
1530 {
1531 GtkWidget *dlg;
1532 gchar *safeMsg = Inkscape::IO::sanitizeString(message);
1534 dlg = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR,
1535 GTK_BUTTONS_CLOSE, "%s", safeMsg);
1536 sp_transientize(dlg);
1537 gtk_window_set_resizable(GTK_WINDOW(dlg), FALSE);
1538 gtk_dialog_run(GTK_DIALOG(dlg));
1539 gtk_widget_destroy(dlg);
1540 g_free(safeMsg);
1541 }
1543 bool
1544 sp_ui_overwrite_file(gchar const *filename)
1545 {
1546 bool return_value = FALSE;
1548 if (Inkscape::IO::file_test(filename, G_FILE_TEST_EXISTS)) {
1549 Gtk::Window *window = SP_ACTIVE_DESKTOP->getToplevel();
1550 gchar* baseName = g_path_get_basename( filename );
1551 gchar* dirName = g_path_get_dirname( filename );
1552 GtkWidget* dialog = gtk_message_dialog_new_with_markup( window->gobj(),
1553 (GtkDialogFlags)(GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT),
1554 GTK_MESSAGE_QUESTION,
1555 GTK_BUTTONS_NONE,
1556 _( "<span weight=\"bold\" size=\"larger\">A file named \"%s\" already exists. Do you want to replace it?</span>\n\n"
1557 "The file already exists in \"%s\". Replacing it will overwrite its contents." ),
1558 baseName,
1559 dirName
1560 );
1561 gtk_dialog_add_buttons( GTK_DIALOG(dialog),
1562 GTK_STOCK_CANCEL, GTK_RESPONSE_NO,
1563 _("Replace"), GTK_RESPONSE_YES,
1564 NULL );
1565 gtk_dialog_set_default_response( GTK_DIALOG(dialog), GTK_RESPONSE_YES );
1567 if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_YES ) {
1568 return_value = TRUE;
1569 } else {
1570 return_value = FALSE;
1571 }
1572 gtk_widget_destroy(dialog);
1573 g_free( baseName );
1574 g_free( dirName );
1575 } else {
1576 return_value = TRUE;
1577 }
1579 return return_value;
1580 }
1582 static void
1583 sp_ui_menu_item_set_sensitive(SPAction */*action*/, unsigned int sensitive, void *data)
1584 {
1585 return gtk_widget_set_sensitive(GTK_WIDGET(data), sensitive);
1586 }
1588 static void
1589 sp_ui_menu_item_set_name(SPAction */*action*/, Glib::ustring name, void *data)
1590 {
1591 void *child = GTK_BIN (data)->child;
1592 //child is either
1593 //- a GtkHBox, whose first child is a label displaying name if the menu
1594 //item has an accel key
1595 //- a GtkLabel if the menu has no accel key
1596 if (GTK_IS_LABEL(child)) {
1597 gtk_label_set_markup_with_mnemonic(GTK_LABEL (child), name.c_str());
1598 } else if (GTK_IS_HBOX(child)) {
1599 gtk_label_set_markup_with_mnemonic(
1600 GTK_LABEL (gtk_container_get_children(GTK_CONTAINER (child))->data),
1601 name.c_str());
1602 }//else sp_ui_menu_append_item_from_verb has been modified and can set
1603 //a menu item in yet another way...
1604 }
1606 void injectRenamedIcons()
1607 {
1608 Glib::RefPtr<Gtk::IconTheme> iconTheme = Gtk::IconTheme::get_default();
1610 std::vector< std::pair<Glib::ustring, Glib::ustring> > renamed;
1611 renamed.push_back(std::make_pair("gtk-file", "document-x-generic"));
1612 renamed.push_back(std::make_pair("gtk-directory", "folder"));
1614 for ( std::vector< std::pair<Glib::ustring, Glib::ustring> >::iterator it = renamed.begin(); it < renamed.end(); ++it ) {
1615 bool hasIcon = iconTheme->has_icon(it->first);
1616 bool hasSecondIcon = iconTheme->has_icon(it->second);
1618 if ( !hasIcon && hasSecondIcon ) {
1619 Glib::ArrayHandle<int> sizes = iconTheme->get_icon_sizes(it->second);
1620 for ( Glib::ArrayHandle<int>::iterator it2 = sizes.begin(); it2 < sizes.end(); ++it2 ) {
1621 Glib::RefPtr<Gdk::Pixbuf> pb = iconTheme->load_icon( it->second, *it2 );
1622 if ( pb ) {
1623 // install a private copy of the pixbuf to avoid pinning a theme
1624 Glib::RefPtr<Gdk::Pixbuf> pbCopy = pb->copy();
1625 Gtk::IconTheme::add_builtin_icon( it->first, *it2, pbCopy );
1626 }
1627 }
1628 }
1629 }
1630 }
1633 /*
1634 Local Variables:
1635 mode:c++
1636 c-file-style:"stroustrup"
1637 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1638 indent-tabs-mode:nil
1639 fill-column:99
1640 End:
1641 */
1642 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :