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 gchar c[256];
531 font->Family(c, 256);
532 sp_repr_css_set_property (css, "font-family", c);
534 font->Attribute( "weight", c, 256);
535 sp_repr_css_set_property (css, "font-weight", c);
537 font->Attribute("style", c, 256);
538 sp_repr_css_set_property (css, "font-style", c);
540 font->Attribute("stretch", c, 256);
541 sp_repr_css_set_property (css, "font-stretch", c);
543 font->Attribute("variant", c, 256);
544 sp_repr_css_set_property (css, "font-variant", c);
546 Inkscape::CSSOStringStream os;
547 os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel)) << "px"; // must specify px, see inkscape bug 1221626 and 1610103
548 sp_repr_css_set_property (css, "font-size", os.str().c_str());
550 font->Unref();
551 font=NULL;
552 }
554 /* Layout */
555 GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
557 // Align Left
558 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
559 sp_repr_css_set_property (css, "text-anchor", "start");
560 sp_repr_css_set_property (css, "text-align", "start");
561 } else {
562 // Align Center
563 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
564 "text_anchor_middle");
565 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
566 sp_repr_css_set_property (css, "text-anchor", "middle");
567 sp_repr_css_set_property (css, "text-align", "center");
568 } else {
569 // Align Right
570 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
571 "text_anchor_end");
572 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
573 sp_repr_css_set_property (css, "text-anchor", "end");
574 sp_repr_css_set_property (css, "text-align", "end");
575 } else {
576 // Align Justify
577 sp_repr_css_set_property (css, "text-anchor", "start");
578 sp_repr_css_set_property (css, "text-align", "justify");
579 }
580 }
581 }
583 b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
585 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
586 sp_repr_css_set_property (css, "writing-mode", "lr");
587 } else {
588 sp_repr_css_set_property (css, "writing-mode", "tb");
589 }
591 // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
592 // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
593 GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
594 const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
595 sp_repr_css_set_property (css, "line-height", sstr);
597 return css;
598 }
601 static void
602 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
603 {
604 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
606 SPCSSAttr *css = sp_get_text_dialog_style ();
608 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
609 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
610 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
612 sp_repr_css_attr_unref (css);
614 gtk_widget_set_sensitive (def, FALSE);
615 }
619 static void
620 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
621 {
622 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
624 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
625 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
626 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
628 unsigned items = 0;
629 const GSList *item_list = sp_desktop_selection(desktop)->itemList();
630 SPCSSAttr *css = sp_get_text_dialog_style ();
631 sp_desktop_set_style(desktop, css, true);
633 for (; item_list != NULL; item_list = item_list->next) {
634 // apply style to the reprs of all text objects in the selection
635 if (SP_IS_TEXT (item_list->data)) {
637 // backwards compatibility:
638 SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
640 ++items;
641 }
642 else if (SP_IS_FLOWTEXT (item_list->data))
643 // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
644 ++items;
645 }
647 if (items == 0) {
648 // no text objects; apply style to prefs for new objects
649 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
650 gtk_widget_set_sensitive (def, FALSE);
651 } else if (items == 1) {
652 /* exactly one text object; now set its text, too */
653 SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
654 if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
655 sp_text_edit_dialog_update_object_text (item);
656 }
657 }
659 // complete the transaction
660 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
661 _("Set text style"));
662 gtk_widget_set_sensitive (apply, FALSE);
663 sp_repr_css_attr_unref (css);
664 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
665 }
667 static void
668 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
669 {
670 gtk_widget_destroy (GTK_WIDGET (dlg));
671 }
673 static void
674 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
675 gboolean dostyle,
676 gboolean docontent )
677 {
678 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
679 return;
681 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
683 GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
684 GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
685 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
686 GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
687 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
688 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
690 GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
692 SPItem *text = sp_ted_get_selected_text_item ();
694 Inkscape::XML::Node *repr;
695 if (text)
696 {
697 guint items = sp_ted_get_selected_text_count ();
698 if (items == 1) {
699 gtk_widget_set_sensitive (textw, TRUE);
700 } else {
701 gtk_widget_set_sensitive (textw, FALSE);
702 }
703 gtk_widget_set_sensitive (apply, FALSE);
704 gtk_widget_set_sensitive (def, TRUE);
706 if (docontent) {
707 gchar *str;
708 str = sp_te_get_string_multiline (text);
710 if (str) {
711 int pos;
712 pos = 0;
714 if (items == 1) {
715 gtk_text_buffer_set_text (tb, str, strlen (str));
716 gtk_text_buffer_set_modified (tb, FALSE);
717 }
718 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
719 g_free (str);
721 } else {
722 gtk_text_buffer_set_text (tb, "", 0);
723 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
724 }
725 } // end of if (docontent)
726 repr = SP_OBJECT_REPR (text);
728 } else {
729 gtk_widget_set_sensitive (textw, FALSE);
730 gtk_widget_set_sensitive (apply, FALSE);
731 gtk_widget_set_sensitive (def, FALSE);
732 }
734 if (dostyle) {
736 // create temporary style
737 SPStyle *query = sp_style_new ();
738 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
739 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
740 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
741 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
743 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
744 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
745 repr = inkscape_get_repr (INKSCAPE, "tools.text");
746 if (repr) {
747 gtk_widget_set_sensitive (notebook, TRUE);
748 sp_style_read_from_repr (query, repr);
749 } else {
750 gtk_widget_set_sensitive (notebook, FALSE);
751 }
752 }
754 // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
755 font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
756 if (font) {
757 // the font is oversized, so we need to pass the true size separately
758 sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
759 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
760 font->Unref();
761 font=NULL;
762 }
764 GtkWidget *b;
765 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
766 if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY) {
767 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_justify" );
768 } else {
769 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
770 }
771 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
772 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
773 } else {
774 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
775 }
776 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
778 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
779 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
780 } else {
781 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
782 }
783 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
785 GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
786 double height;
787 if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
788 else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
789 height = query->line_height.value;
790 else height = query->line_height.computed;
791 gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
792 gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
793 g_free(sstr);
795 g_free (query);
796 }
798 g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
799 }
802 static void
803 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
804 {
805 GtkWidget *textw, *preview, *apply, *def;
806 GtkTextIter start, end;
807 gchar *str;
809 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
810 return;
812 SPItem *text = sp_ted_get_selected_text_item ();
814 textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
815 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
816 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
817 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
819 gtk_text_buffer_get_bounds (tb, &start, &end);
820 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
822 if (str && *str) {
823 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
824 } else {
825 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
826 }
827 g_free (str);
829 if (text) {
830 gtk_widget_set_sensitive (apply, TRUE);
831 }
832 gtk_widget_set_sensitive (def, TRUE);
834 } // end of sp_text_edit_dialog_text_changed()
836 void
837 sp_text_edit_dialog_default_set_insensitive ()
838 {
839 if (!dlg) return;
840 gpointer data = g_object_get_data (G_OBJECT (dlg), "default");
841 if (!data) return;
842 gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
843 }
845 static void
846 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
847 font_instance *font,
848 GtkWidget *dlg )
849 {
850 GtkWidget *preview, *apply, *def;
852 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
853 return;
855 SPItem *text = sp_ted_get_selected_text_item ();
857 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
858 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
859 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
861 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
863 if (text)
864 {
865 gtk_widget_set_sensitive (apply, TRUE);
866 }
867 gtk_widget_set_sensitive (def, TRUE);
869 } // end of sp_text_edit_dialog_font_changed()
873 static void
874 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
875 {
876 GtkWidget *apply, *def;
878 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
879 return;
881 SPItem *text = sp_ted_get_selected_text_item ();
883 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
884 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
886 if (text) {
887 gtk_widget_set_sensitive (apply, TRUE);
888 }
889 gtk_widget_set_sensitive (def, TRUE);
890 }
894 static void
895 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
896 {
897 GtkWidget *apply, *def;
899 if (g_object_get_data ((GObject *) dlg, "blocked"))
900 return;
902 SPItem *text = sp_ted_get_selected_text_item ();
904 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
905 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
907 if (text) {
908 gtk_widget_set_sensitive (apply, TRUE);
909 }
910 gtk_widget_set_sensitive (def, TRUE);
911 }
915 static SPItem *
916 sp_ted_get_selected_text_item (void)
917 {
918 if (!SP_ACTIVE_DESKTOP)
919 return NULL;
921 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
922 item != NULL;
923 item = item->next)
924 {
925 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
926 return SP_ITEM (item->data);
927 }
929 return NULL;
930 }
934 static unsigned
935 sp_ted_get_selected_text_count (void)
936 {
937 if (!SP_ACTIVE_DESKTOP)
938 return 0;
940 unsigned int items = 0;
942 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
943 item != NULL;
944 item = item->next)
945 {
946 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
947 ++items;
948 }
950 return items;
951 }
953 /*
954 Local Variables:
955 mode:c++
956 c-file-style:"stroustrup"
957 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
958 indent-tabs-mode:nil
959 fill-column:99
960 End:
961 */
962 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :