1 #define __CONTEXT_MENU_C__
3 /*
4 * Unser-interface related object extension
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@kaplinski.com>
8 *
9 * This code is in public domain
10 */
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
16 #include "context-menu.h"
17 #include "../xml/repr.h"
18 #include "desktop.h"
19 #include "document.h"
20 #include "message-stack.h"
21 #include "preferences.h"
22 #include "ui/dialog/dialog-manager.h"
24 static void sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu);
26 /* Append object-specific part to context menu */
28 void
29 sp_object_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu)
30 {
31 GObjectClass *klass;
32 klass = G_OBJECT_GET_CLASS(object);
33 while (G_TYPE_CHECK_CLASS_TYPE((klass), SP_TYPE_OBJECT)) {
34 GType type;
35 type = G_TYPE_FROM_CLASS(klass);
36 sp_object_type_menu(type, object, desktop, menu);
37 klass = (GObjectClass*)g_type_class_peek_parent(klass);
38 }
39 }
41 /* Implementation */
43 #include <gtk/gtkmenuitem.h>
45 #include <glibmm/i18n.h>
47 #include "sp-anchor.h"
48 #include "sp-image.h"
49 #include "sp-text.h"
51 #include "document.h"
52 #include "desktop-handles.h"
53 #include "selection.h"
54 #include "selection-chemistry.h"
55 #include "dialogs/item-properties.h"
56 #include "dialogs/object-attributes.h"
57 #include "dialogs/text-edit.h"
58 #include "dialogs/spellcheck.h"
60 #include "sp-path.h"
61 #include "sp-clippath.h"
62 #include "sp-mask.h"
65 static void sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
66 static void sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
67 static void sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
68 static void sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
69 static void sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
70 static void sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
72 static void
73 sp_object_type_menu(GType type, SPObject *object, SPDesktop *desktop, GtkMenu *menu)
74 {
75 static GHashTable *t2m = NULL;
76 void (* handler)(SPObject *object, SPDesktop *desktop, GtkMenu *menu);
77 if (!t2m) {
78 t2m = g_hash_table_new(NULL, NULL);
79 g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ITEM), (void*)sp_item_menu);
80 g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_GROUP), (void*)sp_group_menu);
81 g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_ANCHOR), (void*)sp_anchor_menu);
82 g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_IMAGE), (void*)sp_image_menu);
83 g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_SHAPE), (void*)sp_shape_menu);
84 g_hash_table_insert(t2m, GUINT_TO_POINTER(SP_TYPE_TEXT), (void*)sp_text_menu);
85 }
86 handler = (void (*)(SPObject*, SPDesktop*, GtkMenu*))g_hash_table_lookup(t2m, GUINT_TO_POINTER(type));
87 if (handler) handler(object, desktop, menu);
88 }
90 /* SPItem */
92 static void sp_item_properties(GtkMenuItem *menuitem, SPItem *item);
93 static void sp_item_select_this(GtkMenuItem *menuitem, SPItem *item);
94 static void sp_item_create_link(GtkMenuItem *menuitem, SPItem *item);
95 static void sp_set_mask(GtkMenuItem *menuitem, SPItem *item);
96 static void sp_release_mask(GtkMenuItem *menuitem, SPItem *item);
97 static void sp_set_clip(GtkMenuItem *menuitem, SPItem *item);
98 static void sp_release_clip(GtkMenuItem *menuitem, SPItem *item);
99 /* Generate context menu item section */
101 static void
102 sp_item_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
103 {
104 SPItem *item;
105 GtkWidget *w;
107 item = (SPItem *) object;
109 /* Item dialog */
110 w = gtk_menu_item_new_with_mnemonic(_("_Object Properties..."));
111 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
112 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_item_properties), item);
113 gtk_widget_show(w);
114 gtk_menu_append(GTK_MENU(m), w);
115 /* Separator */
116 w = gtk_menu_item_new();
117 gtk_widget_show(w);
118 gtk_menu_append(GTK_MENU(m), w);
119 /* Select item */
120 w = gtk_menu_item_new_with_mnemonic(_("_Select This"));
121 if (sp_desktop_selection(desktop)->includes(item)) {
122 gtk_widget_set_sensitive(w, FALSE);
123 } else {
124 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
125 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_item_select_this), item);
126 }
127 gtk_widget_show(w);
128 gtk_menu_append(GTK_MENU(m), w);
129 /* Create link */
130 w = gtk_menu_item_new_with_mnemonic(_("_Create Link"));
131 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
132 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_item_create_link), item);
133 gtk_widget_set_sensitive(w, !SP_IS_ANCHOR(item));
134 gtk_widget_show(w);
135 gtk_menu_append(GTK_MENU(m), w);
136 /* Set mask */
137 w = gtk_menu_item_new_with_mnemonic(_("Set Mask"));
138 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
139 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_set_mask), item);
140 if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) {
141 gtk_widget_set_sensitive(w, FALSE);
142 } else {
143 gtk_widget_set_sensitive(w, TRUE);
144 }
145 gtk_widget_show(w);
146 gtk_menu_append(GTK_MENU(m), w);
147 /* Release mask */
148 w = gtk_menu_item_new_with_mnemonic(_("Release Mask"));
149 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
150 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_release_mask), item);
151 if (item && item->mask_ref && item->mask_ref->getObject()) {
152 gtk_widget_set_sensitive(w, TRUE);
153 } else {
154 gtk_widget_set_sensitive(w, FALSE);
155 }
156 gtk_widget_show(w);
157 gtk_menu_append(GTK_MENU(m), w);
158 /* Set Clip */
159 w = gtk_menu_item_new_with_mnemonic(_("Set _Clip"));
160 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
161 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_set_clip), item);
162 if ((item && item->mask_ref && item->mask_ref->getObject()) || (item->clip_ref && item->clip_ref->getObject())) {
163 gtk_widget_set_sensitive(w, FALSE);
164 } else {
165 gtk_widget_set_sensitive(w, TRUE);
166 }
167 gtk_widget_show(w);
168 gtk_menu_append(GTK_MENU(m), w);
169 /* Release Clip */
170 w = gtk_menu_item_new_with_mnemonic(_("Release C_lip"));
171 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
172 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_release_clip), item);
173 if (item && item->clip_ref && item->clip_ref->getObject()) {
174 gtk_widget_set_sensitive(w, TRUE);
175 } else {
176 gtk_widget_set_sensitive(w, FALSE);
177 }
178 gtk_widget_show(w);
179 gtk_menu_append(GTK_MENU(m), w);
181 }
183 static void
184 sp_item_properties(GtkMenuItem *menuitem, SPItem *item)
185 {
186 SPDesktop *desktop;
188 g_assert(SP_IS_ITEM(item));
190 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
191 g_return_if_fail(desktop != NULL);
193 sp_desktop_selection(desktop)->set(item);
195 sp_item_dialog();
196 }
199 static void
200 sp_set_mask(GtkMenuItem *menuitem, SPItem *item)
201 {
202 SPDesktop *desktop;
204 g_assert(SP_IS_ITEM(item));
206 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
207 g_return_if_fail(desktop != NULL);
209 sp_selection_set_mask(desktop, false, false);
210 }
213 static void
214 sp_release_mask(GtkMenuItem *menuitem, SPItem *item)
215 {
216 SPDesktop *desktop;
218 g_assert(SP_IS_ITEM(item));
220 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
221 g_return_if_fail(desktop != NULL);
223 sp_selection_unset_mask(desktop, false);
224 }
227 static void
228 sp_set_clip(GtkMenuItem *menuitem, SPItem *item)
229 {
230 SPDesktop *desktop;
232 g_assert(SP_IS_ITEM(item));
234 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
235 g_return_if_fail(desktop != NULL);
237 sp_selection_set_mask(desktop, true, false);
238 }
241 static void
242 sp_release_clip(GtkMenuItem *menuitem, SPItem *item)
243 {
244 SPDesktop *desktop;
246 g_assert(SP_IS_ITEM(item));
248 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
249 g_return_if_fail(desktop != NULL);
251 sp_selection_unset_mask(desktop, true);
252 }
255 static void
256 sp_item_select_this(GtkMenuItem *menuitem, SPItem *item)
257 {
258 SPDesktop *desktop;
260 g_assert(SP_IS_ITEM(item));
262 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
263 g_return_if_fail(desktop != NULL);
265 sp_desktop_selection(desktop)->set(item);
266 }
268 static void
269 sp_item_create_link(GtkMenuItem *menuitem, SPItem *item)
270 {
271 g_assert(SP_IS_ITEM(item));
272 g_assert(!SP_IS_ANCHOR(item));
274 SPDesktop *desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
275 g_return_if_fail(desktop != NULL);
277 Inkscape::XML::Document *xml_doc = sp_document_repr_doc(desktop->doc());
278 Inkscape::XML::Node *repr = xml_doc->createElement("svg:a");
279 SP_OBJECT_REPR(SP_OBJECT_PARENT(item))->addChild(repr, SP_OBJECT_REPR(item));
280 SPObject *object = SP_OBJECT_DOCUMENT(item)->getObjectByRepr(repr);
281 g_return_if_fail(SP_IS_ANCHOR(object));
283 const char *id = SP_OBJECT_REPR(item)->attribute("id");
284 Inkscape::XML::Node *child = SP_OBJECT_REPR(item)->duplicate(xml_doc);
285 SP_OBJECT(item)->deleteObject(false);
286 repr->addChild(child, NULL);
287 child->setAttribute("id", id);
289 Inkscape::GC::release(repr);
290 Inkscape::GC::release(child);
292 sp_document_done(SP_OBJECT_DOCUMENT(object), SP_VERB_NONE,
293 _("Create link"));
295 sp_object_attributes_dialog(object, "SPAnchor");
297 sp_desktop_selection(desktop)->set(SP_ITEM(object));
298 }
300 /* SPGroup */
302 static void sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group);
304 static void
305 sp_group_menu(SPObject *object, SPDesktop *desktop, GtkMenu *menu)
306 {
307 SPItem *item=SP_ITEM(object);
308 GtkWidget *w;
310 /* "Ungroup" */
311 w = gtk_menu_item_new_with_mnemonic(_("_Ungroup"));
312 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
313 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_item_group_ungroup_activate), item);
314 gtk_widget_show(w);
315 gtk_menu_append(GTK_MENU(menu), w);
316 }
318 static void
319 sp_item_group_ungroup_activate(GtkMenuItem *menuitem, SPGroup *group)
320 {
321 SPDesktop *desktop;
322 GSList *children;
324 g_assert(SP_IS_GROUP(group));
326 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
327 g_return_if_fail(desktop != NULL);
329 children = NULL;
330 sp_item_group_ungroup(group, &children);
332 sp_desktop_selection(desktop)->setList(children);
333 g_slist_free(children);
334 }
336 /* SPAnchor */
338 static void sp_anchor_link_properties(GtkMenuItem *menuitem, SPAnchor *anchor);
339 static void sp_anchor_link_follow(GtkMenuItem *menuitem, SPAnchor *anchor);
340 static void sp_anchor_link_remove(GtkMenuItem *menuitem, SPAnchor *anchor);
342 static void
343 sp_anchor_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
344 {
345 SPItem *item;
346 GtkWidget *w;
348 item = (SPItem *) object;
350 /* Link dialog */
351 w = gtk_menu_item_new_with_mnemonic(_("Link _Properties..."));
352 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
353 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_anchor_link_properties), item);
354 gtk_widget_show(w);
355 gtk_menu_append(GTK_MENU(m), w);
356 /* Select item */
357 w = gtk_menu_item_new_with_mnemonic(_("_Follow Link"));
358 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_anchor_link_follow), item);
359 gtk_widget_show(w);
360 gtk_menu_append(GTK_MENU(m), w);
361 /* Reset transformations */
362 w = gtk_menu_item_new_with_mnemonic(_("_Remove Link"));
363 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
364 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_anchor_link_remove), item);
365 gtk_widget_show(w);
366 gtk_menu_append(GTK_MENU(m), w);
367 }
369 static void
370 sp_anchor_link_properties(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
371 {
372 sp_object_attributes_dialog(SP_OBJECT(anchor), "Link");
373 }
375 static void
376 sp_anchor_link_follow(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
377 {
378 g_return_if_fail(anchor != NULL);
379 g_return_if_fail(SP_IS_ANCHOR(anchor));
381 /* shell out to an external browser here */
382 }
384 static void
385 sp_anchor_link_remove(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
386 {
387 GSList *children;
389 g_return_if_fail(anchor != NULL);
390 g_return_if_fail(SP_IS_ANCHOR(anchor));
392 children = NULL;
393 sp_item_group_ungroup(SP_GROUP(anchor), &children);
395 g_slist_free(children);
396 }
398 /* Image */
400 static void sp_image_image_properties(GtkMenuItem *menuitem, SPAnchor *anchor);
401 static void sp_image_image_edit(GtkMenuItem *menuitem, SPAnchor *anchor);
403 static void
404 sp_image_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
405 {
406 SPItem *item = SP_ITEM(object);
407 GtkWidget *w;
409 /* Link dialog */
410 w = gtk_menu_item_new_with_mnemonic(_("Image _Properties..."));
411 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
412 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_image_image_properties), item);
413 gtk_widget_show(w);
414 gtk_menu_append(GTK_MENU(m), w);
416 w = gtk_menu_item_new_with_mnemonic(_("Edit Externally..."));
417 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
418 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_image_image_edit), item);
419 gtk_widget_show(w);
420 gtk_menu_append(GTK_MENU(m), w);
421 Inkscape::XML::Node *ir = SP_OBJECT_REPR(object);
422 const gchar *href = ir->attribute("xlink:href");
423 if ( (!href) || ((strncmp(href, "data:", 5) == 0)) ) {
424 gtk_widget_set_sensitive( w, FALSE );
425 }
426 }
428 static void
429 sp_image_image_properties(GtkMenuItem */*menuitem*/, SPAnchor *anchor)
430 {
431 sp_object_attributes_dialog(SP_OBJECT(anchor), "Image");
432 }
434 static gchar* getImageEditorName() {
435 Inkscape::Preferences *prefs = Inkscape::Preferences::get();
436 gchar* value = 0;
437 Glib::ustring choices = prefs->getString("/options/bitmapeditor/choices");
438 if (!choices.empty()) {
439 gchar** splits = g_strsplit(choices.data(), ",", 0);
440 gint numIems = g_strv_length(splits);
442 int setting = prefs->getIntLimited("/options/bitmapeditor/value", 0, 0, numIems);
443 value = g_strdup(splits[setting]);
445 g_strfreev(splits);
446 }
448 if (!value) {
449 value = g_strdup("gimp");
450 }
451 return value;
452 }
454 static void sp_image_image_edit(GtkMenuItem *menuitem, SPAnchor *anchor)
455 {
456 SPObject* obj = SP_OBJECT(anchor);
457 Inkscape::XML::Node *ir = SP_OBJECT_REPR(obj);
458 const gchar *href = ir->attribute("xlink:href");
460 GError* errThing = 0;
461 gchar* editorBin = getImageEditorName();
462 gchar const* args[] = {editorBin, href, 0};
463 g_spawn_async(0, // working dir
464 const_cast<gchar **>(args),
465 0, //envp
466 G_SPAWN_SEARCH_PATH,
467 0, // child_setup
468 0, // user_data
469 0, //GPid *child_pid
470 &errThing);
471 if ( errThing ) {
472 g_warning("Problem launching editor (%d). %s", errThing->code, errThing->message);
473 SPDesktop *desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
474 desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, errThing->message);
475 g_error_free(errThing);
476 errThing = 0;
477 }
478 g_free(editorBin);
479 }
481 /* Fill and Stroke entry */
483 static void
484 sp_fill_settings(GtkMenuItem *menuitem, SPItem *item)
485 {
486 SPDesktop *desktop;
488 g_assert(SP_IS_ITEM(item));
490 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
491 g_return_if_fail(desktop != NULL);
493 if (sp_desktop_selection(desktop)->isEmpty()) {
494 sp_desktop_selection(desktop)->set(item);
495 }
497 desktop->_dlg_mgr->showDialog("FillAndStroke");
498 }
500 /* SPShape */
502 static void
503 sp_shape_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
504 {
505 SPItem *item;
506 GtkWidget *w;
508 item = (SPItem *) object;
510 /* Item dialog */
511 w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke..."));
512 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
513 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_fill_settings), item);
514 gtk_widget_show(w);
515 gtk_menu_append(GTK_MENU(m), w);
516 }
518 /* Edit Text entry */
520 static void
521 sp_text_settings(GtkMenuItem *menuitem, SPItem *item)
522 {
523 SPDesktop *desktop;
525 g_assert(SP_IS_ITEM(item));
527 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
528 g_return_if_fail(desktop != NULL);
530 if (sp_desktop_selection(desktop)->isEmpty()) {
531 sp_desktop_selection(desktop)->set(item);
532 }
534 sp_text_edit_dialog();
535 }
537 /* Spellcheck entry */
539 static void
540 sp_spellcheck_settings(GtkMenuItem *menuitem, SPItem *item)
541 {
542 SPDesktop *desktop;
544 g_assert(SP_IS_ITEM(item));
546 desktop = (SPDesktop*)gtk_object_get_data(GTK_OBJECT(menuitem), "desktop");
547 g_return_if_fail(desktop != NULL);
549 if (sp_desktop_selection(desktop)->isEmpty()) {
550 sp_desktop_selection(desktop)->set(item);
551 }
553 sp_spellcheck_dialog();
554 }
556 /* SPText */
558 static void
559 sp_text_menu(SPObject *object, SPDesktop *desktop, GtkMenu *m)
560 {
561 SPItem *item;
562 GtkWidget *w;
564 item = (SPItem *) object;
566 /* Fill and Stroke dialog */
567 w = gtk_menu_item_new_with_mnemonic(_("_Fill and Stroke..."));
568 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
569 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_fill_settings), item);
570 gtk_widget_show(w);
571 gtk_menu_append(GTK_MENU(m), w);
573 /* Edit Text dialog */
574 w = gtk_menu_item_new_with_mnemonic(_("_Text and Font..."));
575 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
576 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_text_settings), item);
577 gtk_widget_show(w);
578 gtk_menu_append(GTK_MENU(m), w);
580 /* Spellcheck dialog */
581 w = gtk_menu_item_new_with_mnemonic(_("Check Spellin_g..."));
582 gtk_object_set_data(GTK_OBJECT(w), "desktop", desktop);
583 gtk_signal_connect(GTK_OBJECT(w), "activate", GTK_SIGNAL_FUNC(sp_spellcheck_settings), item);
584 gtk_widget_show(w);
585 gtk_menu_append(GTK_MENU(m), w);
586 }
587 /*
588 Local Variables:
589 mode:c++
590 c-file-style:"stroustrup"
591 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
592 indent-tabs-mode:nil
593 fill-column:99
594 End:
595 */
596 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :