1 #define __SP_TEXT_EDIT_C__
3 /**
4 * \brief Text editing dialog
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@ximian.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Johan Engelen <goejendaagh@zonnet.nl>
10 *
11 * Copyright (C) 1999-2007 Authors
12 * Copyright (C) 2000-2001 Ximian, Inc.
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
21 #include <libnrtype/font-instance.h>
23 #include <gtk/gtk.h>
25 #ifdef WITH_GTKSPELL
26 extern "C" {
27 # include <gtkspell/gtkspell.h>
28 }
29 #endif
31 #include "macros.h"
32 #include <glibmm/i18n.h>
33 #include "helper/window.h"
34 #include "../widgets/font-selector.h"
35 #include "../inkscape.h"
36 #include "../document.h"
37 #include "../desktop-style.h"
38 #include "../desktop-handles.h"
39 #include "../selection.h"
40 #include "../style.h"
41 #include "../sp-text.h"
42 #include "../sp-flowtext.h"
43 #include "../text-editing.h"
44 #include "../inkscape-stock.h"
45 #include <libnrtype/font-style-to-pos.h>
47 #include "dialog-events.h"
48 #include "../prefs-utils.h"
49 #include "../verbs.h"
50 #include "../interface.h"
51 #include "svg/css-ostringstream.h"
52 #include "widgets/icon.h"
53 #include <xml/repr.h>
55 #define VB_MARGIN 4
56 #define MIN_ONSCREEN_DISTANCE 50
58 static void sp_text_edit_dialog_selection_modified (Inkscape::Application *inkscape, Inkscape::Selection *sel, guint flags, GtkWidget *dlg);
59 static void sp_text_edit_dialog_selection_changed (Inkscape::Application *inkscape, Inkscape::Selection *sel, GtkWidget *dlg);
60 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg);
62 static void sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg);
63 static void sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg);
64 static void sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg);
66 static void sp_text_edit_dialog_read_selection (GtkWidget *dlg, gboolean style, gboolean content);
68 static void sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg);
69 static void sp_text_edit_dialog_font_changed (SPFontSelector *fontsel, font_instance *font, GtkWidget *dlg);
70 static void sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg);
71 static void sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg);
73 static SPItem *sp_ted_get_selected_text_item (void);
74 static unsigned sp_ted_get_selected_text_count (void);
77 static const gchar *spacings[] = {"50%", "80%", "90%", "100%", "110%", "120%", "130%", "140%", "150%", "200%", "300%", NULL};
79 static GtkWidget *dlg = NULL;
80 static win_data wd;
81 // impossible original values to make sure they are read from prefs
82 static gint x = -1000, y = -1000, w = 0, h = 0;
83 static gchar const *prefs_path = "dialogs.textandfont";
88 static void
89 sp_text_edit_dialog_destroy( GtkObject */*object*/, gpointer /*data*/ )
90 {
91 sp_signal_disconnect_by_data (INKSCAPE, dlg);
92 wd.win = dlg = NULL;
93 wd.stop = 0;
94 }
98 static gboolean
99 sp_text_edit_dialog_delete( GtkObject */*object*/, GdkEvent */*event*/, gpointer /*data*/ )
100 {
101 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
102 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
104 if (x<0) x=0;
105 if (y<0) y=0;
107 prefs_set_int_attribute (prefs_path, "x", x);
108 prefs_set_int_attribute (prefs_path, "y", y);
109 prefs_set_int_attribute (prefs_path, "w", w);
110 prefs_set_int_attribute (prefs_path, "h", h);
112 return FALSE; // which means, go ahead and destroy it
113 }
116 /**
117 These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left.
118 This flag is used to prevent passing keys from the dialog to canvas, so that the text editor
119 can handle keys like Esc and Ctrl+Z itself.
120 */
121 gboolean
122 text_view_focus_in( GtkWidget */*w*/, GdkEventKey */*event*/, gpointer data )
123 {
124 GObject *dlg = (GObject *) data;
125 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE));
126 return FALSE;
127 }
129 gboolean
130 text_view_focus_out (GtkWidget */*w*/, GdkEventKey */*event*/, gpointer data)
131 {
132 GObject *dlg = (GObject *) data;
133 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE));
134 return FALSE;
135 }
138 void
139 sp_text_edit_dialog (void)
140 {
141 if (!dlg) {
143 gchar title[500];
144 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), title);
146 dlg = sp_window_new (title, TRUE);
147 if (x == -1000 || y == -1000) {
148 x = prefs_get_int_attribute (prefs_path, "x", -1000);
149 y = prefs_get_int_attribute (prefs_path, "y", -1000);
150 }
152 if (w ==0 || h == 0) {
153 w = prefs_get_int_attribute (prefs_path, "w", 0);
154 h = prefs_get_int_attribute (prefs_path, "h", 0);
155 }
157 // if (x<0) x=0;
158 // if (y<0) y=0;
160 if (w && h)
161 gtk_window_resize ((GtkWindow *) dlg, w, h);
162 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) {
163 gtk_window_move ((GtkWindow *) dlg, x, y);
164 } else {
165 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
166 }
169 sp_transientize (dlg);
170 wd.win = dlg;
171 wd.stop = 0;
172 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
174 gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
176 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
177 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
178 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
180 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
181 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
183 gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
185 GtkTooltips *tt = gtk_tooltips_new();
187 // box containing the notebook and the bottom buttons
188 GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
189 gtk_container_add (GTK_CONTAINER (dlg), mainvb);
191 // notebook
192 GtkWidget *nb = gtk_notebook_new ();
193 gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
194 g_object_set_data (G_OBJECT (dlg), "notebook", nb);
198 // Font tab
199 {
200 GtkWidget *l = gtk_label_new (_("Font"));
201 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
202 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
203 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
205 /* HBox containing font selection and layout */
206 GtkWidget *hb = gtk_hbox_new (FALSE, 0);
207 gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
209 // font and style selector
210 GtkWidget *fontsel = sp_font_selector_new ();
211 g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
213 g_signal_connect_swapped ( G_OBJECT (g_object_get_data (G_OBJECT(fontsel), "family-treeview")),
214 "row-activated",
215 G_CALLBACK (gtk_window_activate_default),
216 dlg);
218 gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
219 g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
221 // Layout
222 {
223 GtkWidget *f = gtk_frame_new (_("Layout"));
224 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
225 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
226 gtk_container_add (GTK_CONTAINER (f), l_vb);
228 {
229 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
230 GtkWidget *group;
232 // align left
233 {
234 // TODO - replace with Inkscape-specific call
235 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
236 GtkWidget *b = group = gtk_radio_button_new (NULL);
237 gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
238 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
239 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
240 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
241 gtk_container_add (GTK_CONTAINER (b), px);
242 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
243 g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
244 }
246 // align center
247 {
248 // TODO - replace with Inkscape-specific call
249 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
250 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
251 /* TRANSLATORS: `Center' here is a verb. */
252 gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
253 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
254 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
255 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
256 gtk_container_add (GTK_CONTAINER (b), px);
257 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
258 g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
259 }
261 // align right
262 {
263 // TODO - replace with Inkscape-specific call
264 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
265 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
266 gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
267 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
268 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
269 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
270 gtk_container_add (GTK_CONTAINER (b), px);
271 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
272 g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
273 }
275 // align justify
276 {
277 // TODO - replace with Inkscape-specific call
278 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_LARGE_TOOLBAR );
279 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
280 gtk_tooltips_set_tip (tt, b, _("Justify lines"), NULL);
281 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
282 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
283 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
284 gtk_container_add (GTK_CONTAINER (b), px);
285 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
286 g_object_set_data (G_OBJECT (dlg), "text_anchor_justify", b);
287 }
289 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
290 }
293 {
294 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
295 GtkWidget *group;
297 // horizontal
298 {
299 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
300 INKSCAPE_STOCK_WRITING_MODE_LR );
301 GtkWidget *b = group = gtk_radio_button_new (NULL);
302 gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
303 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
304 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
305 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
306 gtk_container_add (GTK_CONTAINER (b), px);
307 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
308 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
309 }
311 // vertical
312 {
313 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
314 INKSCAPE_STOCK_WRITING_MODE_TB );
315 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
316 gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
317 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
318 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
319 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
320 gtk_container_add (GTK_CONTAINER (b), px);
321 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
322 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
323 }
325 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
326 }
328 {
329 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
331 l = gtk_label_new (_("Line spacing:"));
332 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
333 gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
335 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
336 }
338 {
339 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
341 GtkWidget *c = gtk_combo_new ();
342 gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
343 gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
344 gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
345 gtk_widget_set_size_request (c, 90, -1);
347 { /* Setup strings */
348 GList *sl = NULL;
349 for (int i = 0; spacings[i]; i++) {
350 sl = g_list_prepend (sl, (void *) spacings[i]);
351 }
352 sl = g_list_reverse (sl);
353 gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
354 g_list_free (sl);
355 }
357 g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
358 "changed",
359 (GCallback) sp_text_edit_dialog_line_spacing_changed,
360 dlg );
361 gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
362 g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
364 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
365 }
366 }
368 /* Font preview */
369 GtkWidget *preview = sp_font_preview_new ();
370 gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
371 g_object_set_data (G_OBJECT (dlg), "preview", preview);
372 }
375 // Text tab
376 {
377 GtkWidget *l = gtk_label_new (_("Text"));
378 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
379 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
380 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
382 GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
383 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
384 GTK_POLICY_AUTOMATIC,
385 GTK_POLICY_AUTOMATIC );
386 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
387 gtk_widget_show (scroller);
389 GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
390 GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
391 gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
392 #ifdef WITH_GTKSPELL
393 GError *error = NULL;
394 char *errortext = NULL;
395 /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
396 language (either as 2nd arg of gtkspell_new_attach, or with explicit
397 gtkspell_set_language call in; see advanced.c example in gtkspell docs).
398 sp_text_edit_dialog_read_selection looks like a suitable place. */
399 if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
400 g_print("gtkspell error: %s\n", error->message);
401 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
402 "%s", error->message);
403 g_error_free(error);
404 }
405 #endif
406 gtk_widget_set_size_request (txt, -1, 64);
407 gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
408 gtk_container_add (GTK_CONTAINER (scroller), txt);
409 gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
410 g_signal_connect ( G_OBJECT (tb), "changed",
411 G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
412 g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
413 g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
414 g_object_set_data (G_OBJECT (dlg), "text", tb);
415 g_object_set_data (G_OBJECT (dlg), "textw", txt);
416 }
418 /* Buttons */
419 GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
420 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
421 gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
423 {
424 GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
425 g_signal_connect ( G_OBJECT (b), "clicked",
426 G_CALLBACK (sp_text_edit_dialog_set_default),
427 dlg );
428 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
429 g_object_set_data (G_OBJECT (dlg), "default", b);
430 }
432 {
433 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
434 g_signal_connect ( G_OBJECT (b), "clicked",
435 G_CALLBACK (sp_text_edit_dialog_close), dlg );
436 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
437 }
439 {
440 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
441 GTK_WIDGET_SET_FLAGS (b, GTK_CAN_DEFAULT | GTK_HAS_DEFAULT);
442 g_signal_connect ( G_OBJECT (b), "clicked",
443 G_CALLBACK (sp_text_edit_dialog_apply), dlg );
444 gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
445 g_object_set_data (G_OBJECT (dlg), "apply", b);
446 }
448 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
449 G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
450 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
451 G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
452 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
454 gtk_widget_show_all (dlg);
456 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
457 }
459 gtk_window_present ((GtkWindow *) dlg);
461 } // end of sp_text_edit_dialog()
465 static void
466 sp_text_edit_dialog_selection_modified( Inkscape::Application */*inkscape*/,
467 Inkscape::Selection */*sel*/,
468 guint flags,
469 GtkWidget *dlg )
470 {
471 gboolean style, content;
473 style =
474 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
475 SP_OBJECT_STYLE_MODIFIED_FLAG )) != 0 );
477 content =
478 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
479 SP_TEXT_CONTENT_MODIFIED_FLAG )) != 0 );
481 sp_text_edit_dialog_read_selection (dlg, style, content);
483 }
487 static void
488 sp_text_edit_dialog_selection_changed( Inkscape::Application */*inkscape*/,
489 Inkscape::Selection */*sel*/,
490 GtkWidget *dlg )
491 {
492 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
493 }
495 static void sp_text_edit_dialog_subselection_changed( Inkscape::Application */*inkscape*/, SPDesktop */*desktop*/, GtkWidget *dlg )
496 {
497 sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
498 }
500 static void
501 sp_text_edit_dialog_update_object_text ( SPItem *text )
502 {
503 GtkTextBuffer *tb;
504 GtkTextIter start, end;
505 gchar *str;
507 tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
509 /* write text */
510 if (gtk_text_buffer_get_modified (tb)) {
511 gtk_text_buffer_get_bounds (tb, &start, &end);
512 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
513 sp_te_set_repr_text_multiline (text, str);
514 g_free (str);
515 gtk_text_buffer_set_modified (tb, FALSE);
516 }
517 }
519 SPCSSAttr *
520 sp_get_text_dialog_style ()
521 {
522 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
524 SPCSSAttr *css = sp_repr_css_attr_new ();
526 /* font */
527 font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
529 if ( font ) {
530 Glib::ustring fontName = font_factory::Default()->ConstructFontSpecification(font);
531 sp_repr_css_set_property (css, "-inkscape-font-specification", fontName.c_str());
533 gchar c[256];
535 font->Family(c, 256);
536 sp_repr_css_set_property (css, "font-family", c);
538 font->Attribute( "weight", c, 256);
539 sp_repr_css_set_property (css, "font-weight", c);
541 font->Attribute("style", c, 256);
542 sp_repr_css_set_property (css, "font-style", c);
544 font->Attribute("stretch", c, 256);
545 sp_repr_css_set_property (css, "font-stretch", c);
547 font->Attribute("variant", c, 256);
548 sp_repr_css_set_property (css, "font-variant", c);
550 Inkscape::CSSOStringStream os;
551 os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel)) << "px"; // must specify px, see inkscape bug 1221626 and 1610103
552 sp_repr_css_set_property (css, "font-size", os.str().c_str());
554 font->Unref();
555 font=NULL;
556 }
558 /* Layout */
559 GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
561 // Align Left
562 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
563 sp_repr_css_set_property (css, "text-anchor", "start");
564 sp_repr_css_set_property (css, "text-align", "start");
565 } else {
566 // Align Center
567 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
568 "text_anchor_middle");
569 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
570 sp_repr_css_set_property (css, "text-anchor", "middle");
571 sp_repr_css_set_property (css, "text-align", "center");
572 } else {
573 // Align Right
574 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
575 "text_anchor_end");
576 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
577 sp_repr_css_set_property (css, "text-anchor", "end");
578 sp_repr_css_set_property (css, "text-align", "end");
579 } else {
580 // Align Justify
581 sp_repr_css_set_property (css, "text-anchor", "start");
582 sp_repr_css_set_property (css, "text-align", "justify");
583 }
584 }
585 }
587 b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
589 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
590 sp_repr_css_set_property (css, "writing-mode", "lr");
591 } else {
592 sp_repr_css_set_property (css, "writing-mode", "tb");
593 }
595 // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
596 // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
597 GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
598 const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
599 sp_repr_css_set_property (css, "line-height", sstr);
601 return css;
602 }
605 static void
606 sp_text_edit_dialog_set_default( GtkButton */*button*/, GtkWidget *dlg )
607 {
608 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
610 SPCSSAttr *css = sp_get_text_dialog_style ();
612 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
613 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
614 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
616 sp_repr_css_attr_unref (css);
618 gtk_widget_set_sensitive (def, FALSE);
619 }
623 static void
624 sp_text_edit_dialog_apply( GtkButton */*button*/, GtkWidget *dlg )
625 {
626 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
628 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
629 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
630 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
632 unsigned items = 0;
633 const GSList *item_list = sp_desktop_selection(desktop)->itemList();
634 SPCSSAttr *css = sp_get_text_dialog_style ();
635 sp_desktop_set_style(desktop, css, true);
637 for (; item_list != NULL; item_list = item_list->next) {
638 // apply style to the reprs of all text objects in the selection
639 if (SP_IS_TEXT (item_list->data)) {
641 // backwards compatibility:
642 SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
644 ++items;
645 }
646 else if (SP_IS_FLOWTEXT (item_list->data))
647 // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
648 ++items;
649 }
651 if (items == 0) {
652 // no text objects; apply style to prefs for new objects
653 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
654 gtk_widget_set_sensitive (def, FALSE);
655 } else if (items == 1) {
656 /* exactly one text object; now set its text, too */
657 SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
658 if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
659 sp_text_edit_dialog_update_object_text (item);
660 }
661 }
663 // complete the transaction
664 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
665 _("Set text style"));
666 gtk_widget_set_sensitive (apply, FALSE);
667 sp_repr_css_attr_unref (css);
668 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
669 }
671 static void
672 sp_text_edit_dialog_close( GtkButton */*button*/, GtkWidget *dlg )
673 {
674 gtk_widget_destroy (GTK_WIDGET (dlg));
675 }
677 static void
678 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
679 gboolean dostyle,
680 gboolean docontent )
681 {
682 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
683 return;
685 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
687 GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
688 GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
689 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
690 GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
691 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
692 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
694 GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
696 SPItem *text = sp_ted_get_selected_text_item ();
698 Inkscape::XML::Node *repr;
699 if (text)
700 {
701 guint items = sp_ted_get_selected_text_count ();
702 if (items == 1) {
703 gtk_widget_set_sensitive (textw, TRUE);
704 } else {
705 gtk_widget_set_sensitive (textw, FALSE);
706 }
707 gtk_widget_set_sensitive (apply, FALSE);
708 gtk_widget_set_sensitive (def, TRUE);
710 if (docontent) {
711 gchar *str;
712 str = sp_te_get_string_multiline (text);
714 if (str) {
715 int pos;
716 pos = 0;
718 if (items == 1) {
719 gtk_text_buffer_set_text (tb, str, strlen (str));
720 gtk_text_buffer_set_modified (tb, FALSE);
721 }
722 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
723 g_free (str);
725 } else {
726 gtk_text_buffer_set_text (tb, "", 0);
727 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
728 }
729 } // end of if (docontent)
730 repr = SP_OBJECT_REPR (text);
732 } else {
733 gtk_widget_set_sensitive (textw, FALSE);
734 gtk_widget_set_sensitive (apply, FALSE);
735 gtk_widget_set_sensitive (def, FALSE);
736 }
738 if (dostyle) {
740 // create temporary style
741 SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
742 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
743 //int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
744 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
745 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
746 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
748 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
749 // (Ok to not get a font specification - must just rely on the family and style in that case)
750 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING
751 || result_numbers == QUERY_STYLE_NOTHING) {
752 repr = inkscape_get_repr (INKSCAPE, "tools.text");
753 if (repr) {
754 gtk_widget_set_sensitive (notebook, TRUE);
755 sp_style_read_from_repr (query, repr);
756 } else {
757 gtk_widget_set_sensitive (notebook, FALSE);
758 }
759 }
761 // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
763 // Get a font_instance using the font-specification attribute stored in SPStyle if available
764 font_instance *font = font_factory::Default()->FaceFromStyle(query);
767 if (font) {
768 // the font is oversized, so we need to pass the true size separately
769 sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
770 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
771 font->Unref();
772 font=NULL;
773 }
775 GtkWidget *b;
776 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
777 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY) {
778 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_justify" );
779 } else {
780 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
781 }
782 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
783 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
784 } else {
785 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
786 }
787 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
789 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
790 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
791 } else {
792 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
793 }
794 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
796 GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
797 double height;
798 if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
799 else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
800 height = query->line_height.value;
801 else height = query->line_height.computed;
802 gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
803 gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
804 g_free(sstr);
806 sp_style_unref(query);
807 }
809 g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
810 }
813 static void
814 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
815 {
816 GtkWidget *textw, *preview, *apply, *def;
817 GtkTextIter start, end;
818 gchar *str;
820 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
821 return;
823 SPItem *text = sp_ted_get_selected_text_item ();
825 textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
826 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
827 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
828 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
830 gtk_text_buffer_get_bounds (tb, &start, &end);
831 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
833 if (str && *str) {
834 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
835 } else {
836 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
837 }
838 g_free (str);
840 if (text) {
841 gtk_widget_set_sensitive (apply, TRUE);
842 }
843 gtk_widget_set_sensitive (def, TRUE);
845 } // end of sp_text_edit_dialog_text_changed()
847 void
848 sp_text_edit_dialog_default_set_insensitive ()
849 {
850 if (!dlg) return;
851 gpointer data = g_object_get_data (G_OBJECT (dlg), "default");
852 if (!data) return;
853 gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
854 }
856 static void
857 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
858 font_instance *font,
859 GtkWidget *dlg )
860 {
861 GtkWidget *preview, *apply, *def;
863 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
864 return;
866 SPItem *text = sp_ted_get_selected_text_item ();
868 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
869 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
870 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
872 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
874 if (text)
875 {
876 gtk_widget_set_sensitive (apply, TRUE);
877 }
878 gtk_widget_set_sensitive (def, TRUE);
880 } // end of sp_text_edit_dialog_font_changed()
884 static void
885 sp_text_edit_dialog_any_toggled( GtkToggleButton */*tb*/, GtkWidget *dlg )
886 {
887 GtkWidget *apply, *def;
889 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
890 return;
892 SPItem *text = sp_ted_get_selected_text_item ();
894 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
895 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
897 if (text) {
898 gtk_widget_set_sensitive (apply, TRUE);
899 }
900 gtk_widget_set_sensitive (def, TRUE);
901 }
905 static void
906 sp_text_edit_dialog_line_spacing_changed( GtkEditable */*editable*/, GtkWidget *dlg )
907 {
908 GtkWidget *apply, *def;
910 if (g_object_get_data ((GObject *) dlg, "blocked"))
911 return;
913 SPItem *text = sp_ted_get_selected_text_item ();
915 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
916 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
918 if (text) {
919 gtk_widget_set_sensitive (apply, TRUE);
920 }
921 gtk_widget_set_sensitive (def, TRUE);
922 }
926 static SPItem *
927 sp_ted_get_selected_text_item (void)
928 {
929 if (!SP_ACTIVE_DESKTOP)
930 return NULL;
932 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
933 item != NULL;
934 item = item->next)
935 {
936 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
937 return SP_ITEM (item->data);
938 }
940 return NULL;
941 }
945 static unsigned
946 sp_ted_get_selected_text_count (void)
947 {
948 if (!SP_ACTIVE_DESKTOP)
949 return 0;
951 unsigned int items = 0;
953 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
954 item != NULL;
955 item = item->next)
956 {
957 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
958 ++items;
959 }
961 return items;
962 }
964 /*
965 Local Variables:
966 mode:c++
967 c-file-style:"stroustrup"
968 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
969 indent-tabs-mode:nil
970 fill-column:99
971 End:
972 */
973 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :