1 #define __SP_TEXT_EDIT_C__
3 /**
4 * \brief Text editing dialog
5 *
6 * Author:
7 * Lauris Kaplinski <lauris@ximian.com>
8 * bulia byak <buliabyak@users.sf.net>
9 *
10 * Copyright (C) 1999-2002 Lauris Kaplinski
11 * Copyright (C) 2000-2001 Ximian, Inc.
12 *
13 * Released under GNU GPL, read the file 'COPYING' for more information
14 */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include <libnrtype/font-instance.h>
22 #include <gtk/gtk.h>
24 #ifdef WITH_GTKSPELL
25 extern "C" {
26 # include <gtkspell/gtkspell.h>
27 }
28 #endif
30 #include "macros.h"
31 #include <glibmm/i18n.h>
32 #include "helper/window.h"
33 #include "../widgets/font-selector.h"
34 #include "../inkscape.h"
35 #include "../document.h"
36 #include "../desktop-style.h"
37 #include "../desktop-handles.h"
38 #include "../selection.h"
39 #include "../style.h"
40 #include "../sp-text.h"
41 #include "../sp-flowtext.h"
42 #include "../text-editing.h"
43 #include "../inkscape-stock.h"
44 #include <libnrtype/font-style-to-pos.h>
46 #include "dialog-events.h"
47 #include "../prefs-utils.h"
48 #include "../verbs.h"
49 #include "../interface.h"
50 #include "svg/css-ostringstream.h"
51 #include "widgets/icon.h"
52 #include <xml/repr.h>
55 #define VB_MARGIN 4
57 static void sp_text_edit_dialog_selection_modified (Inkscape::Application *inkscape, Inkscape::Selection *sel, guint flags, GtkWidget *dlg);
58 static void sp_text_edit_dialog_selection_changed (Inkscape::Application *inkscape, Inkscape::Selection *sel, GtkWidget *dlg);
59 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg);
61 static void sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg);
62 static void sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg);
63 static void sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg);
65 static void sp_text_edit_dialog_read_selection (GtkWidget *dlg, gboolean style, gboolean content);
67 static void sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg);
68 static void sp_text_edit_dialog_font_changed (SPFontSelector *fontsel, font_instance *font, GtkWidget *dlg);
69 static void sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg);
70 static void sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg);
72 static SPItem *sp_ted_get_selected_text_item (void);
73 static unsigned sp_ted_get_selected_text_count (void);
76 static const gchar *spacings[] = {"50%", "80%", "90%", "100%", "110%", "120%", "130%", "140%", "150%", "200%", "300%", NULL};
78 static GtkWidget *dlg = NULL;
79 static win_data wd;
80 // impossible original values to make sure they are read from prefs
81 static gint x = -1000, y = -1000, w = 0, h = 0;
82 static gchar const *prefs_path = "dialogs.textandfont";
87 static void
88 sp_text_edit_dialog_destroy (GtkObject *object, gpointer data)
89 {
90 sp_signal_disconnect_by_data (INKSCAPE, dlg);
91 wd.win = dlg = NULL;
92 wd.stop = 0;
93 }
97 static gboolean
98 sp_text_edit_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data)
99 {
100 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
101 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
103 if (x<0) x=0;
104 if (y<0) y=0;
106 prefs_set_int_attribute (prefs_path, "x", x);
107 prefs_set_int_attribute (prefs_path, "y", y);
108 prefs_set_int_attribute (prefs_path, "w", w);
109 prefs_set_int_attribute (prefs_path, "h", h);
111 return FALSE; // which means, go ahead and destroy it
112 }
115 /**
116 These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left.
117 This flag is used to prevent passing keys from the dialog to canvas, so that the text editor
118 can handle keys like Esc and Ctrl+Z itself.
119 */
120 gboolean
121 text_view_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data)
122 {
123 GObject *dlg = (GObject *) data;
124 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE));
125 return FALSE;
126 }
128 gboolean
129 text_view_focus_out (GtkWidget *w, GdkEventKey *event, gpointer data)
130 {
131 GObject *dlg = (GObject *) data;
132 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE));
133 return FALSE;
134 }
137 void
138 sp_text_edit_dialog (void)
139 {
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", 0);
149 y = prefs_get_int_attribute (prefs_path, "y", 0);
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 (x != 0 || y != 0) {
161 gtk_window_move ((GtkWindow *) dlg, x, y);
162 } else {
163 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
164 }
166 if (w && h)
167 gtk_window_resize ((GtkWindow *) dlg, w, h);
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 );
212 gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
213 g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
215 // Layout
216 {
217 GtkWidget *f = gtk_frame_new (_("Layout"));
218 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
219 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
220 gtk_container_add (GTK_CONTAINER (f), l_vb);
222 {
223 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
224 GtkWidget *group;
226 // align left
227 {
228 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
229 GtkWidget *b = group = gtk_radio_button_new (NULL);
230 gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
231 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
232 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
233 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
234 gtk_container_add (GTK_CONTAINER (b), px);
235 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
236 g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
237 }
239 // align center
240 {
241 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
242 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
243 /* TRANSLATORS: `Center' here is a verb. */
244 gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
245 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
246 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
247 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
248 gtk_container_add (GTK_CONTAINER (b), px);
249 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
250 g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
251 }
253 // align right
254 {
255 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
256 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
257 gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
258 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
259 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
260 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
261 gtk_container_add (GTK_CONTAINER (b), px);
262 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
263 g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
264 }
266 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
267 }
270 {
271 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
272 GtkWidget *group;
274 // horizontal
275 {
276 GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
277 INKSCAPE_STOCK_WRITING_MODE_LR );
278 GtkWidget *b = group = gtk_radio_button_new (NULL);
279 gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
280 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
281 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
282 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
283 gtk_container_add (GTK_CONTAINER (b), px);
284 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
285 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
286 }
288 // vertical
289 {
290 GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
291 INKSCAPE_STOCK_WRITING_MODE_TB );
292 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
293 gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
294 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
295 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
296 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
297 gtk_container_add (GTK_CONTAINER (b), px);
298 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
299 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
300 }
302 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
303 }
305 {
306 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
308 l = gtk_label_new (_("Line spacing:"));
309 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
310 gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
312 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
313 }
315 {
316 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
318 GtkWidget *c = gtk_combo_new ();
319 gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
320 gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
321 gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
322 gtk_widget_set_size_request (c, 90, -1);
324 { /* Setup strings */
325 GList *sl = NULL;
326 for (int i = 0; spacings[i]; i++) {
327 sl = g_list_prepend (sl, (void *) spacings[i]);
328 }
329 sl = g_list_reverse (sl);
330 gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
331 g_list_free (sl);
332 }
334 g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
335 "changed",
336 (GCallback) sp_text_edit_dialog_line_spacing_changed,
337 dlg );
338 gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
339 g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
341 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
342 }
343 }
345 /* Font preview */
346 GtkWidget *preview = sp_font_preview_new ();
347 gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
348 g_object_set_data (G_OBJECT (dlg), "preview", preview);
349 }
352 // Text tab
353 {
354 GtkWidget *l = gtk_label_new (_("Text"));
355 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
356 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
357 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
359 GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
360 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
361 GTK_POLICY_AUTOMATIC,
362 GTK_POLICY_AUTOMATIC );
363 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
364 gtk_widget_show (scroller);
366 GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
367 GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
368 gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
369 #ifdef WITH_GTKSPELL
370 GError *error = NULL;
371 char *errortext = NULL;
372 /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
373 language (either as 2nd arg of gtkspell_new_attach, or with explicit
374 gtkspell_set_language call in; see advanced.c example in gtkspell docs).
375 sp_text_edit_dialog_read_selection looks like a suitable place. */
376 if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
377 g_print("gtkspell error: %s\n", error->message);
378 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
379 "%s", error->message);
380 g_error_free(error);
381 }
382 #endif
383 gtk_widget_set_size_request (txt, -1, 64);
384 gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
385 gtk_container_add (GTK_CONTAINER (scroller), txt);
386 gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
387 g_signal_connect ( G_OBJECT (tb), "changed",
388 G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
389 g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
390 g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
391 g_object_set_data (G_OBJECT (dlg), "text", tb);
392 g_object_set_data (G_OBJECT (dlg), "textw", txt);
393 }
395 /* Buttons */
396 GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
397 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
398 gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
400 {
401 GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
402 g_signal_connect ( G_OBJECT (b), "clicked",
403 G_CALLBACK (sp_text_edit_dialog_set_default),
404 dlg );
405 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
406 g_object_set_data (G_OBJECT (dlg), "default", b);
407 }
409 {
410 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
411 g_signal_connect ( G_OBJECT (b), "clicked",
412 G_CALLBACK (sp_text_edit_dialog_close), dlg );
413 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
414 }
416 {
417 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
418 g_signal_connect ( G_OBJECT (b), "clicked",
419 G_CALLBACK (sp_text_edit_dialog_apply), dlg );
420 gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
421 g_object_set_data (G_OBJECT (dlg), "apply", b);
422 }
424 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
425 G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
426 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
427 G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
428 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
430 gtk_widget_show_all (dlg);
432 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
433 }
435 gtk_window_present ((GtkWindow *) dlg);
437 } // end of sp_text_edit_dialog()
441 static void
442 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape,
443 Inkscape::Selection *sel,
444 guint flags,
445 GtkWidget *dlg )
446 {
447 gboolean style, content;
449 style =
450 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
451 SP_OBJECT_STYLE_MODIFIED_FLAG )) != 0 );
453 content =
454 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
455 SP_TEXT_CONTENT_MODIFIED_FLAG )) != 0 );
457 sp_text_edit_dialog_read_selection (dlg, style, content);
459 }
463 static void
464 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape,
465 Inkscape::Selection *sel,
466 GtkWidget *dlg )
467 {
468 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
469 }
471 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
472 {
473 sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
474 }
476 static void
477 sp_text_edit_dialog_update_object_text ( SPItem *text )
478 {
479 GtkTextBuffer *tb;
480 GtkTextIter start, end;
481 gchar *str;
483 tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
485 /* write text */
486 if (gtk_text_buffer_get_modified (tb)) {
487 gtk_text_buffer_get_bounds (tb, &start, &end);
488 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
489 sp_te_set_repr_text_multiline (text, str);
490 g_free (str);
491 gtk_text_buffer_set_modified (tb, FALSE);
492 }
493 }
495 SPCSSAttr *
496 sp_get_text_dialog_style ()
497 {
498 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
500 SPCSSAttr *css = sp_repr_css_attr_new ();
502 /* font */
503 font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
505 if ( font ) {
506 gchar c[256];
507 font->Family(c, 256);
508 sp_repr_css_set_property (css, "font-family", c);
510 font->Attribute( "weight", c, 256);
511 sp_repr_css_set_property (css, "font-weight", c);
513 font->Attribute("style", c, 256);
514 sp_repr_css_set_property (css, "font-style", c);
516 font->Attribute("stretch", c, 256);
517 sp_repr_css_set_property (css, "font-stretch", c);
519 font->Attribute("variant", c, 256);
520 sp_repr_css_set_property (css, "font-variant", c);
522 Inkscape::CSSOStringStream os;
523 os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
524 sp_repr_css_set_property (css, "font-size", os.str().c_str());
526 font->Unref();
527 font=NULL;
528 }
530 /* Layout */
531 GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
533 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
534 sp_repr_css_set_property (css, "text-anchor", "start");
535 sp_repr_css_set_property (css, "text-align", "start");
536 } else {
537 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
538 "text_anchor_middle");
539 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
540 sp_repr_css_set_property (css, "text-anchor", "middle");
541 sp_repr_css_set_property (css, "text-align", "center");
542 } else {
543 sp_repr_css_set_property (css, "text-anchor", "end");
544 sp_repr_css_set_property (css, "text-align", "end");
545 }
546 }
548 b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
550 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
551 sp_repr_css_set_property (css, "writing-mode", "lr");
552 } else {
553 sp_repr_css_set_property (css, "writing-mode", "tb");
554 }
556 // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
557 // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
558 GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
559 const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
560 sp_repr_css_set_property (css, "line-height", sstr);
562 return css;
563 }
566 static void
567 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
568 {
569 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
571 SPCSSAttr *css = sp_get_text_dialog_style ();
573 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
574 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
575 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
577 sp_repr_css_attr_unref (css);
579 gtk_widget_set_sensitive (def, FALSE);
580 }
584 static void
585 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
586 {
587 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
589 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
590 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
591 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
593 unsigned items = 0;
594 const GSList *item_list = SP_DT_SELECTION(desktop)->itemList();
596 SPCSSAttr *css = sp_get_text_dialog_style ();
598 sp_desktop_set_style(desktop, css, true);
600 for (; item_list != NULL; item_list = item_list->next) {
601 // apply style to the reprs of all text objects in the selection
602 if (SP_IS_TEXT (item_list->data)) {
604 // backwards compatibility:
605 SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
607 ++items;
608 }
609 else if (SP_IS_FLOWTEXT (item_list->data))
610 // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
611 ++items;
612 }
614 if (items == 0) {
615 // no text objects; apply style to prefs for new objects
616 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
617 gtk_widget_set_sensitive (def, FALSE);
618 } else if (items == 1) {
619 /* exactly one text object; now set its text, too */
620 SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem();
621 if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
622 sp_text_edit_dialog_update_object_text (item);
623 }
624 }
626 // complete the transaction
627 sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP));
629 gtk_widget_set_sensitive (apply, FALSE);
631 sp_repr_css_attr_unref (css);
633 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
634 }
638 static void
639 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
640 {
641 gtk_widget_destroy (GTK_WIDGET (dlg));
642 }
644 static void
645 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
646 gboolean dostyle,
647 gboolean docontent )
648 {
649 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
650 return;
652 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
654 GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
655 GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
656 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
657 GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
658 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
659 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
661 GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
663 SPItem *text = sp_ted_get_selected_text_item ();
665 Inkscape::XML::Node *repr;
666 if (text)
667 {
668 guint items = sp_ted_get_selected_text_count ();
669 if (items == 1) {
670 gtk_widget_set_sensitive (textw, TRUE);
671 } else {
672 gtk_widget_set_sensitive (textw, FALSE);
673 }
674 gtk_widget_set_sensitive (apply, FALSE);
675 gtk_widget_set_sensitive (def, TRUE);
677 if (docontent) {
678 gchar *str;
679 str = sp_te_get_string_multiline (text);
681 if (str) {
682 int pos;
683 pos = 0;
685 if (items == 1) {
686 gtk_text_buffer_set_text (tb, str, strlen (str));
687 gtk_text_buffer_set_modified (tb, FALSE);
688 }
689 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
690 g_free (str);
692 } else {
693 gtk_text_buffer_set_text (tb, "", 0);
694 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
695 }
696 } // end of if (docontent)
697 repr = SP_OBJECT_REPR (text);
699 } else {
700 gtk_widget_set_sensitive (textw, FALSE);
701 gtk_widget_set_sensitive (apply, FALSE);
702 gtk_widget_set_sensitive (def, FALSE);
703 }
705 if (dostyle) {
707 // create temporary style
708 SPStyle *query = sp_style_new ();
709 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
710 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
711 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
712 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
714 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
715 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
716 repr = inkscape_get_repr (INKSCAPE, "tools.text");
717 if (repr) {
718 gtk_widget_set_sensitive (notebook, TRUE);
719 sp_style_read_from_repr (query, repr);
720 } else {
721 gtk_widget_set_sensitive (notebook, FALSE);
722 }
723 }
725 // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
726 font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
727 if (font) {
728 // the font is oversized, so we need to pass the true size separately
729 sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
730 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
731 font->Unref();
732 font=NULL;
733 }
735 GtkWidget *b;
736 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
737 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
738 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
739 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
740 } else {
741 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
742 }
743 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
745 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
746 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
747 } else {
748 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
749 }
750 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
752 GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
753 double height;
754 if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
755 else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
756 height = query->line_height.value;
757 else height = query->line_height.computed;
758 gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
759 gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
760 g_free(sstr);
762 g_free (query);
763 }
765 g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
766 }
769 static void
770 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
771 {
772 GtkWidget *textw, *preview, *apply, *def;
773 GtkTextIter start, end;
774 gchar *str;
776 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
777 return;
779 SPItem *text = sp_ted_get_selected_text_item ();
781 textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
782 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
783 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
784 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
786 gtk_text_buffer_get_bounds (tb, &start, &end);
787 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
789 if (str && *str) {
790 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
791 } else {
792 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
793 }
794 g_free (str);
796 if (text) {
797 gtk_widget_set_sensitive (apply, TRUE);
798 }
799 gtk_widget_set_sensitive (def, TRUE);
801 } // end of sp_text_edit_dialog_text_changed()
805 static void
806 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
807 font_instance *font,
808 GtkWidget *dlg )
809 {
810 GtkWidget *preview, *apply, *def;
812 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
813 return;
815 SPItem *text = sp_ted_get_selected_text_item ();
817 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
818 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
819 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
821 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
823 if (text) {
824 gtk_widget_set_sensitive (apply, TRUE);
825 }
826 gtk_widget_set_sensitive (def, TRUE);
828 } // end of sp_text_edit_dialog_font_changed()
832 static void
833 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
834 {
835 GtkWidget *apply, *def;
837 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
838 return;
840 SPItem *text = sp_ted_get_selected_text_item ();
842 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
843 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
845 if (text) {
846 gtk_widget_set_sensitive (apply, TRUE);
847 }
848 gtk_widget_set_sensitive (def, TRUE);
849 }
853 static void
854 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
855 {
856 GtkWidget *apply, *def;
858 if (g_object_get_data ((GObject *) dlg, "blocked"))
859 return;
861 SPItem *text = sp_ted_get_selected_text_item ();
863 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
864 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
866 if (text) {
867 gtk_widget_set_sensitive (apply, TRUE);
868 }
869 gtk_widget_set_sensitive (def, TRUE);
870 }
874 static SPItem *
875 sp_ted_get_selected_text_item (void)
876 {
877 if (!SP_ACTIVE_DESKTOP)
878 return NULL;
880 for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
881 item != NULL;
882 item = item->next)
883 {
884 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
885 return SP_ITEM (item->data);
886 }
888 return NULL;
889 }
893 static unsigned
894 sp_ted_get_selected_text_count (void)
895 {
896 if (!SP_ACTIVE_DESKTOP)
897 return 0;
899 unsigned int items = 0;
901 for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
902 item != NULL;
903 item = item->next)
904 {
905 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
906 ++items;
907 }
909 return items;
910 }
912 /*
913 Local Variables:
914 mode:c++
915 c-file-style:"stroustrup"
916 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
917 indent-tabs-mode:nil
918 fill-column:99
919 End:
920 */
921 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :