136cb823d8f7238fcf3262c2c4ac24e005893b55
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 || y != 0) {
158 gtk_window_move ((GtkWindow *) dlg, x, y);
159 } else {
160 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
161 }
163 if (w && h)
164 gtk_window_resize ((GtkWindow *) dlg, w, h);
166 sp_transientize (dlg);
167 wd.win = dlg;
168 wd.stop = 0;
169 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
171 gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
173 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
174 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
175 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
177 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
178 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
180 gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
182 GtkTooltips *tt = gtk_tooltips_new();
184 // box containing the notebook and the bottom buttons
185 GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
186 gtk_container_add (GTK_CONTAINER (dlg), mainvb);
188 // notebook
189 GtkWidget *nb = gtk_notebook_new ();
190 gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
191 g_object_set_data (G_OBJECT (dlg), "notebook", nb);
195 // Font tab
196 {
197 GtkWidget *l = gtk_label_new (_("Font"));
198 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
199 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
200 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
202 /* HBox containing font selection and layout */
203 GtkWidget *hb = gtk_hbox_new (FALSE, 0);
204 gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
206 // font and style selector
207 GtkWidget *fontsel = sp_font_selector_new ();
208 g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
209 gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
210 g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
212 // Layout
213 {
214 GtkWidget *f = gtk_frame_new (_("Layout"));
215 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
216 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
217 gtk_container_add (GTK_CONTAINER (f), l_vb);
219 {
220 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
221 GtkWidget *group;
223 // align left
224 {
225 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
226 GtkWidget *b = group = gtk_radio_button_new (NULL);
227 gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
228 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
229 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
230 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
231 gtk_container_add (GTK_CONTAINER (b), px);
232 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
233 g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
234 }
236 // align center
237 {
238 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
239 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
240 /* TRANSLATORS: `Center' here is a verb. */
241 gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
242 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
243 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
244 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
245 gtk_container_add (GTK_CONTAINER (b), px);
246 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
247 g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
248 }
250 // align right
251 {
252 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
253 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
254 gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
255 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
256 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
257 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
258 gtk_container_add (GTK_CONTAINER (b), px);
259 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
260 g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
261 }
263 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
264 }
267 {
268 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
269 GtkWidget *group;
271 // horizontal
272 {
273 GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
274 INKSCAPE_STOCK_WRITING_MODE_LR );
275 GtkWidget *b = group = gtk_radio_button_new (NULL);
276 gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
277 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
278 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
279 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
280 gtk_container_add (GTK_CONTAINER (b), px);
281 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
282 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
283 }
285 // vertical
286 {
287 GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
288 INKSCAPE_STOCK_WRITING_MODE_TB );
289 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
290 gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
291 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
292 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
293 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
294 gtk_container_add (GTK_CONTAINER (b), px);
295 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
296 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
297 }
299 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
300 }
302 {
303 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
305 l = gtk_label_new (_("Line spacing:"));
306 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
307 gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
309 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
310 }
312 {
313 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
315 GtkWidget *c = gtk_combo_new ();
316 gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
317 gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
318 gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
319 gtk_widget_set_size_request (c, 90, -1);
321 { /* Setup strings */
322 GList *sl = NULL;
323 for (int i = 0; spacings[i]; i++) {
324 sl = g_list_prepend (sl, (void *) spacings[i]);
325 }
326 sl = g_list_reverse (sl);
327 gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
328 g_list_free (sl);
329 }
331 g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
332 "changed",
333 (GCallback) sp_text_edit_dialog_line_spacing_changed,
334 dlg );
335 gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
336 g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
338 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
339 }
340 }
342 /* Font preview */
343 GtkWidget *preview = sp_font_preview_new ();
344 gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
345 g_object_set_data (G_OBJECT (dlg), "preview", preview);
346 }
349 // Text tab
350 {
351 GtkWidget *l = gtk_label_new (_("Text"));
352 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
353 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
354 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
356 GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
357 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
358 GTK_POLICY_AUTOMATIC,
359 GTK_POLICY_AUTOMATIC );
360 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
361 gtk_widget_show (scroller);
363 GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
364 GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
365 gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
366 #ifdef WITH_GTKSPELL
367 GError *error = NULL;
368 char *errortext = NULL;
369 /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
370 language (either as 2nd arg of gtkspell_new_attach, or with explicit
371 gtkspell_set_language call in; see advanced.c example in gtkspell docs).
372 sp_text_edit_dialog_read_selection looks like a suitable place. */
373 if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
374 g_print("gtkspell error: %s\n", error->message);
375 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
376 "%s", error->message);
377 g_error_free(error);
378 }
379 #endif
380 gtk_widget_set_size_request (txt, -1, 64);
381 gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
382 gtk_container_add (GTK_CONTAINER (scroller), txt);
383 gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
384 g_signal_connect ( G_OBJECT (tb), "changed",
385 G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
386 g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
387 g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
388 g_object_set_data (G_OBJECT (dlg), "text", tb);
389 g_object_set_data (G_OBJECT (dlg), "textw", txt);
390 }
392 /* Buttons */
393 GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
394 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
395 gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
397 {
398 GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
399 g_signal_connect ( G_OBJECT (b), "clicked",
400 G_CALLBACK (sp_text_edit_dialog_set_default),
401 dlg );
402 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
403 g_object_set_data (G_OBJECT (dlg), "default", b);
404 }
406 {
407 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
408 g_signal_connect ( G_OBJECT (b), "clicked",
409 G_CALLBACK (sp_text_edit_dialog_close), dlg );
410 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
411 }
413 {
414 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
415 g_signal_connect ( G_OBJECT (b), "clicked",
416 G_CALLBACK (sp_text_edit_dialog_apply), dlg );
417 gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
418 g_object_set_data (G_OBJECT (dlg), "apply", b);
419 }
421 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
422 G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
423 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
424 G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
425 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
427 gtk_widget_show_all (dlg);
429 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
430 }
432 gtk_window_present ((GtkWindow *) dlg);
434 } // end of sp_text_edit_dialog()
438 static void
439 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape,
440 Inkscape::Selection *sel,
441 guint flags,
442 GtkWidget *dlg )
443 {
444 gboolean style, content;
446 style =
447 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
448 SP_OBJECT_STYLE_MODIFIED_FLAG )) != 0 );
450 content =
451 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
452 SP_TEXT_CONTENT_MODIFIED_FLAG )) != 0 );
454 sp_text_edit_dialog_read_selection (dlg, style, content);
456 }
460 static void
461 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape,
462 Inkscape::Selection *sel,
463 GtkWidget *dlg )
464 {
465 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
466 }
468 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
469 {
470 sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
471 }
473 static void
474 sp_text_edit_dialog_update_object_text ( SPItem *text )
475 {
476 GtkTextBuffer *tb;
477 GtkTextIter start, end;
478 gchar *str;
480 tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
482 /* write text */
483 if (gtk_text_buffer_get_modified (tb)) {
484 gtk_text_buffer_get_bounds (tb, &start, &end);
485 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
486 sp_te_set_repr_text_multiline (text, str);
487 g_free (str);
488 gtk_text_buffer_set_modified (tb, FALSE);
489 }
490 }
492 SPCSSAttr *
493 sp_get_text_dialog_style ()
494 {
495 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
497 SPCSSAttr *css = sp_repr_css_attr_new ();
499 /* font */
500 font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
502 if ( font ) {
503 gchar c[256];
504 font->Family(c, 256);
505 sp_repr_css_set_property (css, "font-family", c);
507 font->Attribute( "weight", c, 256);
508 sp_repr_css_set_property (css, "font-weight", c);
510 font->Attribute("style", c, 256);
511 sp_repr_css_set_property (css, "font-style", c);
513 font->Attribute("stretch", c, 256);
514 sp_repr_css_set_property (css, "font-stretch", c);
516 font->Attribute("variant", c, 256);
517 sp_repr_css_set_property (css, "font-variant", c);
519 Inkscape::CSSOStringStream os;
520 os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
521 sp_repr_css_set_property (css, "font-size", os.str().c_str());
523 font->Unref();
524 font=NULL;
525 }
527 /* Layout */
528 GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
530 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
531 sp_repr_css_set_property (css, "text-anchor", "start");
532 sp_repr_css_set_property (css, "text-align", "start");
533 } else {
534 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
535 "text_anchor_middle");
536 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
537 sp_repr_css_set_property (css, "text-anchor", "middle");
538 sp_repr_css_set_property (css, "text-align", "center");
539 } else {
540 sp_repr_css_set_property (css, "text-anchor", "end");
541 sp_repr_css_set_property (css, "text-align", "end");
542 }
543 }
545 b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
547 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
548 sp_repr_css_set_property (css, "writing-mode", "lr");
549 } else {
550 sp_repr_css_set_property (css, "writing-mode", "tb");
551 }
553 // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
554 // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
555 GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
556 const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
557 sp_repr_css_set_property (css, "line-height", sstr);
559 return css;
560 }
563 static void
564 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
565 {
566 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
568 SPCSSAttr *css = sp_get_text_dialog_style ();
570 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
571 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
572 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
574 sp_repr_css_attr_unref (css);
576 gtk_widget_set_sensitive (def, FALSE);
577 }
581 static void
582 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
583 {
584 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
586 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
587 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
588 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
590 unsigned items = 0;
591 const GSList *item_list = SP_DT_SELECTION(desktop)->itemList();
593 SPCSSAttr *css = sp_get_text_dialog_style ();
595 sp_desktop_set_style(desktop, css, true);
597 for (; item_list != NULL; item_list = item_list->next) {
598 // apply style to the reprs of all text objects in the selection
599 if (SP_IS_TEXT (item_list->data)) {
601 // backwards compatibility:
602 SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
604 ++items;
605 }
606 else if (SP_IS_FLOWTEXT (item_list->data))
607 // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
608 ++items;
609 }
611 if (items == 0) {
612 // no text objects; apply style to prefs for new objects
613 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
614 gtk_widget_set_sensitive (def, FALSE);
615 } else if (items == 1) {
616 /* exactly one text object; now set its text, too */
617 SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem();
618 if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
619 sp_text_edit_dialog_update_object_text (item);
620 }
621 }
623 // complete the transaction
624 sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP));
626 gtk_widget_set_sensitive (apply, FALSE);
628 sp_repr_css_attr_unref (css);
630 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
631 }
635 static void
636 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
637 {
638 gtk_widget_destroy (GTK_WIDGET (dlg));
639 }
641 static void
642 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
643 gboolean dostyle,
644 gboolean docontent )
645 {
646 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
647 return;
649 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
651 GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
652 GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
653 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
654 GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
655 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
656 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
658 GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
660 SPItem *text = sp_ted_get_selected_text_item ();
662 Inkscape::XML::Node *repr;
663 if (text)
664 {
665 guint items = sp_ted_get_selected_text_count ();
666 if (items == 1) {
667 gtk_widget_set_sensitive (textw, TRUE);
668 } else {
669 gtk_widget_set_sensitive (textw, FALSE);
670 }
671 gtk_widget_set_sensitive (apply, FALSE);
672 gtk_widget_set_sensitive (def, TRUE);
674 if (docontent) {
675 gchar *str;
676 str = sp_te_get_string_multiline (text);
678 if (str) {
679 int pos;
680 pos = 0;
682 if (items == 1) {
683 gtk_text_buffer_set_text (tb, str, strlen (str));
684 gtk_text_buffer_set_modified (tb, FALSE);
685 }
686 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
687 g_free (str);
689 } else {
690 gtk_text_buffer_set_text (tb, "", 0);
691 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
692 }
693 } // end of if (docontent)
694 repr = SP_OBJECT_REPR (text);
696 } else {
697 gtk_widget_set_sensitive (textw, FALSE);
698 gtk_widget_set_sensitive (apply, FALSE);
699 gtk_widget_set_sensitive (def, FALSE);
700 }
702 if (dostyle) {
704 // create temporary style
705 SPStyle *query = sp_style_new ();
706 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
707 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
708 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
709 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
711 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
712 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
713 repr = inkscape_get_repr (INKSCAPE, "tools.text");
714 if (repr) {
715 gtk_widget_set_sensitive (notebook, TRUE);
716 sp_style_read_from_repr (query, repr);
717 } else {
718 gtk_widget_set_sensitive (notebook, FALSE);
719 }
720 }
722 // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
723 font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
724 if (font) {
725 // the font is oversized, so we need to pass the true size separately
726 sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
727 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
728 font->Unref();
729 font=NULL;
730 }
732 GtkWidget *b;
733 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
734 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
735 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
736 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
737 } else {
738 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
739 }
740 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
742 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
743 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
744 } else {
745 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
746 }
747 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
749 GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
750 double height;
751 if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
752 else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
753 height = query->line_height.value;
754 else height = query->line_height.computed;
755 gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
756 gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
757 g_free(sstr);
759 g_free (query);
760 }
762 g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
763 }
766 static void
767 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
768 {
769 GtkWidget *textw, *preview, *apply, *def;
770 GtkTextIter start, end;
771 gchar *str;
773 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
774 return;
776 SPItem *text = sp_ted_get_selected_text_item ();
778 textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
779 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
780 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
781 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
783 gtk_text_buffer_get_bounds (tb, &start, &end);
784 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
786 if (str && *str) {
787 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
788 } else {
789 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
790 }
791 g_free (str);
793 if (text) {
794 gtk_widget_set_sensitive (apply, TRUE);
795 }
796 gtk_widget_set_sensitive (def, TRUE);
798 } // end of sp_text_edit_dialog_text_changed()
802 static void
803 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
804 font_instance *font,
805 GtkWidget *dlg )
806 {
807 GtkWidget *preview, *apply, *def;
809 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
810 return;
812 SPItem *text = sp_ted_get_selected_text_item ();
814 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
815 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
816 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
818 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
820 if (text) {
821 gtk_widget_set_sensitive (apply, TRUE);
822 }
823 gtk_widget_set_sensitive (def, TRUE);
825 } // end of sp_text_edit_dialog_font_changed()
829 static void
830 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
831 {
832 GtkWidget *apply, *def;
834 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
835 return;
837 SPItem *text = sp_ted_get_selected_text_item ();
839 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
840 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
842 if (text) {
843 gtk_widget_set_sensitive (apply, TRUE);
844 }
845 gtk_widget_set_sensitive (def, TRUE);
846 }
850 static void
851 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
852 {
853 GtkWidget *apply, *def;
855 if (g_object_get_data ((GObject *) dlg, "blocked"))
856 return;
858 SPItem *text = sp_ted_get_selected_text_item ();
860 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
861 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
863 if (text) {
864 gtk_widget_set_sensitive (apply, TRUE);
865 }
866 gtk_widget_set_sensitive (def, TRUE);
867 }
871 static SPItem *
872 sp_ted_get_selected_text_item (void)
873 {
874 if (!SP_ACTIVE_DESKTOP)
875 return NULL;
877 for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
878 item != NULL;
879 item = item->next)
880 {
881 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
882 return SP_ITEM (item->data);
883 }
885 return NULL;
886 }
890 static unsigned
891 sp_ted_get_selected_text_count (void)
892 {
893 if (!SP_ACTIVE_DESKTOP)
894 return 0;
896 unsigned int items = 0;
898 for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
899 item != NULL;
900 item = item->next)
901 {
902 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
903 ++items;
904 }
906 return items;
907 }
909 /*
910 Local Variables:
911 mode:c++
912 c-file-style:"stroustrup"
913 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
914 indent-tabs-mode:nil
915 fill-column:99
916 End:
917 */
918 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :