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 // TODO - replace with Inkscape-specific call
229 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
230 GtkWidget *b = group = gtk_radio_button_new (NULL);
231 gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
232 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
233 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
234 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
235 gtk_container_add (GTK_CONTAINER (b), px);
236 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
237 g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
238 }
240 // align center
241 {
242 // TODO - replace with Inkscape-specific call
243 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
244 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
245 /* TRANSLATORS: `Center' here is a verb. */
246 gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
247 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
248 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
249 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
250 gtk_container_add (GTK_CONTAINER (b), px);
251 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
252 g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
253 }
255 // align right
256 {
257 // TODO - replace with Inkscape-specific call
258 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
259 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
260 gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
261 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
262 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
263 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
264 gtk_container_add (GTK_CONTAINER (b), px);
265 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
266 g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
267 }
269 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
270 }
273 {
274 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
275 GtkWidget *group;
277 // horizontal
278 {
279 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
280 INKSCAPE_STOCK_WRITING_MODE_LR );
281 GtkWidget *b = group = gtk_radio_button_new (NULL);
282 gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
283 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
284 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
285 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
286 gtk_container_add (GTK_CONTAINER (b), px);
287 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
288 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
289 }
291 // vertical
292 {
293 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
294 INKSCAPE_STOCK_WRITING_MODE_TB );
295 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
296 gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
297 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
298 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
299 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
300 gtk_container_add (GTK_CONTAINER (b), px);
301 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
302 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
303 }
305 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
306 }
308 {
309 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
311 l = gtk_label_new (_("Line spacing:"));
312 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
313 gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
315 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
316 }
318 {
319 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
321 GtkWidget *c = gtk_combo_new ();
322 gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
323 gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
324 gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
325 gtk_widget_set_size_request (c, 90, -1);
327 { /* Setup strings */
328 GList *sl = NULL;
329 for (int i = 0; spacings[i]; i++) {
330 sl = g_list_prepend (sl, (void *) spacings[i]);
331 }
332 sl = g_list_reverse (sl);
333 gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
334 g_list_free (sl);
335 }
337 g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
338 "changed",
339 (GCallback) sp_text_edit_dialog_line_spacing_changed,
340 dlg );
341 gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
342 g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
344 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
345 }
346 }
348 /* Font preview */
349 GtkWidget *preview = sp_font_preview_new ();
350 gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
351 g_object_set_data (G_OBJECT (dlg), "preview", preview);
352 }
355 // Text tab
356 {
357 GtkWidget *l = gtk_label_new (_("Text"));
358 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
359 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
360 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
362 GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
363 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
364 GTK_POLICY_AUTOMATIC,
365 GTK_POLICY_AUTOMATIC );
366 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
367 gtk_widget_show (scroller);
369 GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
370 GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
371 gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
372 #ifdef WITH_GTKSPELL
373 GError *error = NULL;
374 char *errortext = NULL;
375 /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
376 language (either as 2nd arg of gtkspell_new_attach, or with explicit
377 gtkspell_set_language call in; see advanced.c example in gtkspell docs).
378 sp_text_edit_dialog_read_selection looks like a suitable place. */
379 if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
380 g_print("gtkspell error: %s\n", error->message);
381 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
382 "%s", error->message);
383 g_error_free(error);
384 }
385 #endif
386 gtk_widget_set_size_request (txt, -1, 64);
387 gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
388 gtk_container_add (GTK_CONTAINER (scroller), txt);
389 gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
390 g_signal_connect ( G_OBJECT (tb), "changed",
391 G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
392 g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
393 g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
394 g_object_set_data (G_OBJECT (dlg), "text", tb);
395 g_object_set_data (G_OBJECT (dlg), "textw", txt);
396 }
398 /* Buttons */
399 GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
400 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
401 gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
403 {
404 GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
405 g_signal_connect ( G_OBJECT (b), "clicked",
406 G_CALLBACK (sp_text_edit_dialog_set_default),
407 dlg );
408 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
409 g_object_set_data (G_OBJECT (dlg), "default", b);
410 }
412 {
413 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
414 g_signal_connect ( G_OBJECT (b), "clicked",
415 G_CALLBACK (sp_text_edit_dialog_close), dlg );
416 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
417 }
419 {
420 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
421 g_signal_connect ( G_OBJECT (b), "clicked",
422 G_CALLBACK (sp_text_edit_dialog_apply), dlg );
423 gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
424 g_object_set_data (G_OBJECT (dlg), "apply", b);
425 }
427 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
428 G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
429 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
430 G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
431 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
433 gtk_widget_show_all (dlg);
435 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
436 }
438 gtk_window_present ((GtkWindow *) dlg);
440 } // end of sp_text_edit_dialog()
444 static void
445 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape,
446 Inkscape::Selection *sel,
447 guint flags,
448 GtkWidget *dlg )
449 {
450 gboolean style, content;
452 style =
453 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
454 SP_OBJECT_STYLE_MODIFIED_FLAG )) != 0 );
456 content =
457 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
458 SP_TEXT_CONTENT_MODIFIED_FLAG )) != 0 );
460 sp_text_edit_dialog_read_selection (dlg, style, content);
462 }
466 static void
467 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape,
468 Inkscape::Selection *sel,
469 GtkWidget *dlg )
470 {
471 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
472 }
474 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
475 {
476 sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
477 }
479 static void
480 sp_text_edit_dialog_update_object_text ( SPItem *text )
481 {
482 GtkTextBuffer *tb;
483 GtkTextIter start, end;
484 gchar *str;
486 tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
488 /* write text */
489 if (gtk_text_buffer_get_modified (tb)) {
490 gtk_text_buffer_get_bounds (tb, &start, &end);
491 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
492 sp_te_set_repr_text_multiline (text, str);
493 g_free (str);
494 gtk_text_buffer_set_modified (tb, FALSE);
495 }
496 }
498 SPCSSAttr *
499 sp_get_text_dialog_style ()
500 {
501 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
503 SPCSSAttr *css = sp_repr_css_attr_new ();
505 /* font */
506 font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
508 if ( font ) {
509 gchar c[256];
510 font->Family(c, 256);
511 sp_repr_css_set_property (css, "font-family", c);
513 font->Attribute( "weight", c, 256);
514 sp_repr_css_set_property (css, "font-weight", c);
516 font->Attribute("style", c, 256);
517 sp_repr_css_set_property (css, "font-style", c);
519 font->Attribute("stretch", c, 256);
520 sp_repr_css_set_property (css, "font-stretch", c);
522 font->Attribute("variant", c, 256);
523 sp_repr_css_set_property (css, "font-variant", c);
525 Inkscape::CSSOStringStream os;
526 os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
527 sp_repr_css_set_property (css, "font-size", os.str().c_str());
529 font->Unref();
530 font=NULL;
531 }
533 /* Layout */
534 GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
536 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
537 sp_repr_css_set_property (css, "text-anchor", "start");
538 sp_repr_css_set_property (css, "text-align", "start");
539 } else {
540 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
541 "text_anchor_middle");
542 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
543 sp_repr_css_set_property (css, "text-anchor", "middle");
544 sp_repr_css_set_property (css, "text-align", "center");
545 } else {
546 sp_repr_css_set_property (css, "text-anchor", "end");
547 sp_repr_css_set_property (css, "text-align", "end");
548 }
549 }
551 b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
553 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
554 sp_repr_css_set_property (css, "writing-mode", "lr");
555 } else {
556 sp_repr_css_set_property (css, "writing-mode", "tb");
557 }
559 // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
560 // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
561 GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
562 const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
563 sp_repr_css_set_property (css, "line-height", sstr);
565 return css;
566 }
569 static void
570 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
571 {
572 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
574 SPCSSAttr *css = sp_get_text_dialog_style ();
576 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
577 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
578 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
580 sp_repr_css_attr_unref (css);
582 gtk_widget_set_sensitive (def, FALSE);
583 }
587 static void
588 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
589 {
590 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
592 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
593 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
594 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
596 unsigned items = 0;
597 const GSList *item_list = sp_desktop_selection(desktop)->itemList();
599 SPCSSAttr *css = sp_get_text_dialog_style ();
601 sp_desktop_set_style(desktop, css, true);
603 for (; item_list != NULL; item_list = item_list->next) {
604 // apply style to the reprs of all text objects in the selection
605 if (SP_IS_TEXT (item_list->data)) {
607 // backwards compatibility:
608 SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
610 ++items;
611 }
612 else if (SP_IS_FLOWTEXT (item_list->data))
613 // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
614 ++items;
615 }
617 if (items == 0) {
618 // no text objects; apply style to prefs for new objects
619 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
620 gtk_widget_set_sensitive (def, FALSE);
621 } else if (items == 1) {
622 /* exactly one text object; now set its text, too */
623 SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
624 if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
625 sp_text_edit_dialog_update_object_text (item);
626 }
627 }
629 // complete the transaction
630 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP));
632 gtk_widget_set_sensitive (apply, FALSE);
634 sp_repr_css_attr_unref (css);
636 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
637 }
641 static void
642 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
643 {
644 gtk_widget_destroy (GTK_WIDGET (dlg));
645 }
647 static void
648 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
649 gboolean dostyle,
650 gboolean docontent )
651 {
652 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
653 return;
655 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
657 GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
658 GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
659 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
660 GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
661 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
662 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
664 GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
666 SPItem *text = sp_ted_get_selected_text_item ();
668 Inkscape::XML::Node *repr;
669 if (text)
670 {
671 guint items = sp_ted_get_selected_text_count ();
672 if (items == 1) {
673 gtk_widget_set_sensitive (textw, TRUE);
674 } else {
675 gtk_widget_set_sensitive (textw, FALSE);
676 }
677 gtk_widget_set_sensitive (apply, FALSE);
678 gtk_widget_set_sensitive (def, TRUE);
680 if (docontent) {
681 gchar *str;
682 str = sp_te_get_string_multiline (text);
684 if (str) {
685 int pos;
686 pos = 0;
688 if (items == 1) {
689 gtk_text_buffer_set_text (tb, str, strlen (str));
690 gtk_text_buffer_set_modified (tb, FALSE);
691 }
692 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
693 g_free (str);
695 } else {
696 gtk_text_buffer_set_text (tb, "", 0);
697 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
698 }
699 } // end of if (docontent)
700 repr = SP_OBJECT_REPR (text);
702 } else {
703 gtk_widget_set_sensitive (textw, FALSE);
704 gtk_widget_set_sensitive (apply, FALSE);
705 gtk_widget_set_sensitive (def, FALSE);
706 }
708 if (dostyle) {
710 // create temporary style
711 SPStyle *query = sp_style_new ();
712 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
713 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
714 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
715 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
717 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
718 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
719 repr = inkscape_get_repr (INKSCAPE, "tools.text");
720 if (repr) {
721 gtk_widget_set_sensitive (notebook, TRUE);
722 sp_style_read_from_repr (query, repr);
723 } else {
724 gtk_widget_set_sensitive (notebook, FALSE);
725 }
726 }
728 // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
729 font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
730 if (font) {
731 // the font is oversized, so we need to pass the true size separately
732 sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
733 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
734 font->Unref();
735 font=NULL;
736 }
738 GtkWidget *b;
739 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
740 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
741 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
742 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
743 } else {
744 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
745 }
746 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
748 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
749 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
750 } else {
751 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
752 }
753 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
755 GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
756 double height;
757 if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
758 else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
759 height = query->line_height.value;
760 else height = query->line_height.computed;
761 gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
762 gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
763 g_free(sstr);
765 g_free (query);
766 }
768 g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
769 }
772 static void
773 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
774 {
775 GtkWidget *textw, *preview, *apply, *def;
776 GtkTextIter start, end;
777 gchar *str;
779 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
780 return;
782 SPItem *text = sp_ted_get_selected_text_item ();
784 textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
785 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
786 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
787 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
789 gtk_text_buffer_get_bounds (tb, &start, &end);
790 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
792 if (str && *str) {
793 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
794 } else {
795 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
796 }
797 g_free (str);
799 if (text) {
800 gtk_widget_set_sensitive (apply, TRUE);
801 }
802 gtk_widget_set_sensitive (def, TRUE);
804 } // end of sp_text_edit_dialog_text_changed()
808 static void
809 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
810 font_instance *font,
811 GtkWidget *dlg )
812 {
813 GtkWidget *preview, *apply, *def;
815 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
816 return;
818 SPItem *text = sp_ted_get_selected_text_item ();
820 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
821 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
822 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
824 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
826 if (text) {
827 gtk_widget_set_sensitive (apply, TRUE);
828 }
829 gtk_widget_set_sensitive (def, TRUE);
831 } // end of sp_text_edit_dialog_font_changed()
835 static void
836 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
837 {
838 GtkWidget *apply, *def;
840 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
841 return;
843 SPItem *text = sp_ted_get_selected_text_item ();
845 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
846 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
848 if (text) {
849 gtk_widget_set_sensitive (apply, TRUE);
850 }
851 gtk_widget_set_sensitive (def, TRUE);
852 }
856 static void
857 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
858 {
859 GtkWidget *apply, *def;
861 if (g_object_get_data ((GObject *) dlg, "blocked"))
862 return;
864 SPItem *text = sp_ted_get_selected_text_item ();
866 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
867 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
869 if (text) {
870 gtk_widget_set_sensitive (apply, TRUE);
871 }
872 gtk_widget_set_sensitive (def, TRUE);
873 }
877 static SPItem *
878 sp_ted_get_selected_text_item (void)
879 {
880 if (!SP_ACTIVE_DESKTOP)
881 return NULL;
883 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
884 item != NULL;
885 item = item->next)
886 {
887 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
888 return SP_ITEM (item->data);
889 }
891 return NULL;
892 }
896 static unsigned
897 sp_ted_get_selected_text_count (void)
898 {
899 if (!SP_ACTIVE_DESKTOP)
900 return 0;
902 unsigned int items = 0;
904 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
905 item != NULL;
906 item = item->next)
907 {
908 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
909 ++items;
910 }
912 return items;
913 }
915 /*
916 Local Variables:
917 mode:c++
918 c-file-style:"stroustrup"
919 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
920 indent-tabs-mode:nil
921 fill-column:99
922 End:
923 */
924 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :