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>
54 #define VB_MARGIN 4
56 static void sp_text_edit_dialog_selection_modified (Inkscape::Application *inkscape, Inkscape::Selection *sel, guint flags, GtkWidget *dlg);
57 static void sp_text_edit_dialog_selection_changed (Inkscape::Application *inkscape, Inkscape::Selection *sel, GtkWidget *dlg);
58 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg);
60 static void sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg);
61 static void sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg);
62 static void sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg);
64 static void sp_text_edit_dialog_read_selection (GtkWidget *dlg, gboolean style, gboolean content);
66 static void sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg);
67 static void sp_text_edit_dialog_font_changed (SPFontSelector *fontsel, font_instance *font, GtkWidget *dlg);
68 static void sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg);
69 static void sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg);
71 static SPItem *sp_ted_get_selected_text_item (void);
72 static unsigned sp_ted_get_selected_text_count (void);
75 static const gchar *spacings[] = {"50%", "80%", "90%", "100%", "110%", "120%", "130%", "140%", "150%", "200%", "300%", NULL};
77 static GtkWidget *dlg = NULL;
78 static win_data wd;
79 // impossible original values to make sure they are read from prefs
80 static gint x = -1000, y = -1000, w = 0, h = 0;
81 static gchar const *prefs_path = "dialogs.textandfont";
86 static void
87 sp_text_edit_dialog_destroy (GtkObject *object, gpointer data)
88 {
89 sp_signal_disconnect_by_data (INKSCAPE, dlg);
90 wd.win = dlg = NULL;
91 wd.stop = 0;
92 }
96 static gboolean
97 sp_text_edit_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data)
98 {
99 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
100 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
102 if (x<0) x=0;
103 if (y<0) y=0;
105 prefs_set_int_attribute (prefs_path, "x", x);
106 prefs_set_int_attribute (prefs_path, "y", y);
107 prefs_set_int_attribute (prefs_path, "w", w);
108 prefs_set_int_attribute (prefs_path, "h", h);
110 return FALSE; // which means, go ahead and destroy it
111 }
114 /**
115 These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left.
116 This flag is used to prevent passing keys from the dialog to canvas, so that the text editor
117 can handle keys like Esc and Ctrl+Z itself.
118 */
119 gboolean
120 text_view_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data)
121 {
122 GObject *dlg = (GObject *) data;
123 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE));
124 return FALSE;
125 }
127 gboolean
128 text_view_focus_out (GtkWidget *w, GdkEventKey *event, gpointer data)
129 {
130 GObject *dlg = (GObject *) data;
131 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE));
132 return FALSE;
133 }
136 void
137 sp_text_edit_dialog (void)
138 {
139 if (!dlg) {
141 gchar title[500];
142 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), title);
144 dlg = sp_window_new (title, TRUE);
145 if (x == -1000 || y == -1000) {
146 x = prefs_get_int_attribute (prefs_path, "x", 0);
147 y = prefs_get_int_attribute (prefs_path, "y", 0);
148 }
150 if (w ==0 || h == 0) {
151 w = prefs_get_int_attribute (prefs_path, "w", 0);
152 h = prefs_get_int_attribute (prefs_path, "h", 0);
153 }
155 if (x<0) x=0;
156 if (y<0) y=0;
158 if (x != 0 || y != 0) {
159 gtk_window_move ((GtkWindow *) dlg, x, y);
160 } else {
161 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
162 }
164 if (w && h)
165 gtk_window_resize ((GtkWindow *) dlg, w, h);
167 sp_transientize (dlg);
168 wd.win = dlg;
169 wd.stop = 0;
170 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
172 gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
174 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
175 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
176 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
178 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
179 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
181 gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
183 GtkTooltips *tt = gtk_tooltips_new();
185 // box containing the notebook and the bottom buttons
186 GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
187 gtk_container_add (GTK_CONTAINER (dlg), mainvb);
189 // notebook
190 GtkWidget *nb = gtk_notebook_new ();
191 gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
192 g_object_set_data (G_OBJECT (dlg), "notebook", nb);
196 // Font tab
197 {
198 GtkWidget *l = gtk_label_new (_("Font"));
199 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
200 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
201 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
203 /* HBox containing font selection and layout */
204 GtkWidget *hb = gtk_hbox_new (FALSE, 0);
205 gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
207 // font and style selector
208 GtkWidget *fontsel = sp_font_selector_new ();
209 g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
211 g_signal_connect_swapped ( G_OBJECT (g_object_get_data (G_OBJECT(fontsel), "family-treeview")),
212 "row-activated",
213 G_CALLBACK (gtk_window_activate_default),
214 dlg);
216 gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
217 g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
219 // Layout
220 {
221 GtkWidget *f = gtk_frame_new (_("Layout"));
222 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
223 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
224 gtk_container_add (GTK_CONTAINER (f), l_vb);
226 {
227 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
228 GtkWidget *group;
230 // align left
231 {
232 // TODO - replace with Inkscape-specific call
233 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
234 GtkWidget *b = group = gtk_radio_button_new (NULL);
235 gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
236 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
237 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
238 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
239 gtk_container_add (GTK_CONTAINER (b), px);
240 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
241 g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
242 }
244 // align center
245 {
246 // TODO - replace with Inkscape-specific call
247 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
248 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
249 /* TRANSLATORS: `Center' here is a verb. */
250 gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
251 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
252 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
253 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
254 gtk_container_add (GTK_CONTAINER (b), px);
255 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
256 g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
257 }
259 // align right
260 {
261 // TODO - replace with Inkscape-specific call
262 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
263 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
264 gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
265 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
266 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
267 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
268 gtk_container_add (GTK_CONTAINER (b), px);
269 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
270 g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
271 }
273 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
274 }
277 {
278 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
279 GtkWidget *group;
281 // horizontal
282 {
283 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
284 INKSCAPE_STOCK_WRITING_MODE_LR );
285 GtkWidget *b = group = gtk_radio_button_new (NULL);
286 gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
287 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
288 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
289 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
290 gtk_container_add (GTK_CONTAINER (b), px);
291 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
292 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
293 }
295 // vertical
296 {
297 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
298 INKSCAPE_STOCK_WRITING_MODE_TB );
299 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
300 gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
301 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
302 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
303 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
304 gtk_container_add (GTK_CONTAINER (b), px);
305 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
306 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
307 }
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 l = gtk_label_new (_("Line spacing:"));
316 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
317 gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
319 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
320 }
322 {
323 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
325 GtkWidget *c = gtk_combo_new ();
326 gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
327 gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
328 gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
329 gtk_widget_set_size_request (c, 90, -1);
331 { /* Setup strings */
332 GList *sl = NULL;
333 for (int i = 0; spacings[i]; i++) {
334 sl = g_list_prepend (sl, (void *) spacings[i]);
335 }
336 sl = g_list_reverse (sl);
337 gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
338 g_list_free (sl);
339 }
341 g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
342 "changed",
343 (GCallback) sp_text_edit_dialog_line_spacing_changed,
344 dlg );
345 gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
346 g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
348 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
349 }
350 }
352 /* Font preview */
353 GtkWidget *preview = sp_font_preview_new ();
354 gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
355 g_object_set_data (G_OBJECT (dlg), "preview", preview);
356 }
359 // Text tab
360 {
361 GtkWidget *l = gtk_label_new (_("Text"));
362 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
363 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
364 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
366 GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
367 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
368 GTK_POLICY_AUTOMATIC,
369 GTK_POLICY_AUTOMATIC );
370 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
371 gtk_widget_show (scroller);
373 GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
374 GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
375 gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
376 #ifdef WITH_GTKSPELL
377 GError *error = NULL;
378 char *errortext = NULL;
379 /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
380 language (either as 2nd arg of gtkspell_new_attach, or with explicit
381 gtkspell_set_language call in; see advanced.c example in gtkspell docs).
382 sp_text_edit_dialog_read_selection looks like a suitable place. */
383 if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
384 g_print("gtkspell error: %s\n", error->message);
385 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
386 "%s", error->message);
387 g_error_free(error);
388 }
389 #endif
390 gtk_widget_set_size_request (txt, -1, 64);
391 gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
392 gtk_container_add (GTK_CONTAINER (scroller), txt);
393 gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
394 g_signal_connect ( G_OBJECT (tb), "changed",
395 G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
396 g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
397 g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
398 g_object_set_data (G_OBJECT (dlg), "text", tb);
399 g_object_set_data (G_OBJECT (dlg), "textw", txt);
400 }
402 /* Buttons */
403 GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
404 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
405 gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
407 {
408 GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
409 g_signal_connect ( G_OBJECT (b), "clicked",
410 G_CALLBACK (sp_text_edit_dialog_set_default),
411 dlg );
412 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
413 g_object_set_data (G_OBJECT (dlg), "default", b);
414 }
416 {
417 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
418 g_signal_connect ( G_OBJECT (b), "clicked",
419 G_CALLBACK (sp_text_edit_dialog_close), dlg );
420 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
421 }
423 {
424 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
425 GTK_WIDGET_SET_FLAGS (b, GTK_CAN_DEFAULT | GTK_HAS_DEFAULT);
426 g_signal_connect ( G_OBJECT (b), "clicked",
427 G_CALLBACK (sp_text_edit_dialog_apply), dlg );
428 gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
429 g_object_set_data (G_OBJECT (dlg), "apply", b);
430 }
432 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
433 G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
434 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
435 G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
436 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
438 gtk_widget_show_all (dlg);
440 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
441 }
443 gtk_window_present ((GtkWindow *) dlg);
445 } // end of sp_text_edit_dialog()
449 static void
450 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape,
451 Inkscape::Selection *sel,
452 guint flags,
453 GtkWidget *dlg )
454 {
455 gboolean style, content;
457 style =
458 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
459 SP_OBJECT_STYLE_MODIFIED_FLAG )) != 0 );
461 content =
462 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
463 SP_TEXT_CONTENT_MODIFIED_FLAG )) != 0 );
465 sp_text_edit_dialog_read_selection (dlg, style, content);
467 }
471 static void
472 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape,
473 Inkscape::Selection *sel,
474 GtkWidget *dlg )
475 {
476 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
477 }
479 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
480 {
481 sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
482 }
484 static void
485 sp_text_edit_dialog_update_object_text ( SPItem *text )
486 {
487 GtkTextBuffer *tb;
488 GtkTextIter start, end;
489 gchar *str;
491 tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
493 /* write text */
494 if (gtk_text_buffer_get_modified (tb)) {
495 gtk_text_buffer_get_bounds (tb, &start, &end);
496 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
497 sp_te_set_repr_text_multiline (text, str);
498 g_free (str);
499 gtk_text_buffer_set_modified (tb, FALSE);
500 }
501 }
503 SPCSSAttr *
504 sp_get_text_dialog_style ()
505 {
506 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
508 SPCSSAttr *css = sp_repr_css_attr_new ();
510 /* font */
511 font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
513 if ( font ) {
514 gchar c[256];
515 font->Family(c, 256);
516 sp_repr_css_set_property (css, "font-family", c);
518 font->Attribute( "weight", c, 256);
519 sp_repr_css_set_property (css, "font-weight", c);
521 font->Attribute("style", c, 256);
522 sp_repr_css_set_property (css, "font-style", c);
524 font->Attribute("stretch", c, 256);
525 sp_repr_css_set_property (css, "font-stretch", c);
527 font->Attribute("variant", c, 256);
528 sp_repr_css_set_property (css, "font-variant", c);
530 Inkscape::CSSOStringStream os;
531 os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
532 sp_repr_css_set_property (css, "font-size", os.str().c_str());
534 font->Unref();
535 font=NULL;
536 }
538 /* Layout */
539 GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
541 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
542 sp_repr_css_set_property (css, "text-anchor", "start");
543 sp_repr_css_set_property (css, "text-align", "start");
544 } else {
545 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
546 "text_anchor_middle");
547 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
548 sp_repr_css_set_property (css, "text-anchor", "middle");
549 sp_repr_css_set_property (css, "text-align", "center");
550 } else {
551 sp_repr_css_set_property (css, "text-anchor", "end");
552 sp_repr_css_set_property (css, "text-align", "end");
553 }
554 }
556 b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
558 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
559 sp_repr_css_set_property (css, "writing-mode", "lr");
560 } else {
561 sp_repr_css_set_property (css, "writing-mode", "tb");
562 }
564 // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
565 // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
566 GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
567 const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
568 sp_repr_css_set_property (css, "line-height", sstr);
570 return css;
571 }
574 static void
575 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
576 {
577 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
579 SPCSSAttr *css = sp_get_text_dialog_style ();
581 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
582 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
583 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
585 sp_repr_css_attr_unref (css);
587 gtk_widget_set_sensitive (def, FALSE);
588 }
592 static void
593 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
594 {
595 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
597 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
598 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
599 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
601 unsigned items = 0;
602 const GSList *item_list = sp_desktop_selection(desktop)->itemList();
603 SPCSSAttr *css = sp_get_text_dialog_style ();
604 sp_desktop_set_style(desktop, css, true);
606 for (; item_list != NULL; item_list = item_list->next) {
607 // apply style to the reprs of all text objects in the selection
608 if (SP_IS_TEXT (item_list->data)) {
610 // backwards compatibility:
611 SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
613 ++items;
614 }
615 else if (SP_IS_FLOWTEXT (item_list->data))
616 // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
617 ++items;
618 }
620 if (items == 0) {
621 // no text objects; apply style to prefs for new objects
622 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
623 gtk_widget_set_sensitive (def, FALSE);
624 } else if (items == 1) {
625 /* exactly one text object; now set its text, too */
626 SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
627 if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
628 sp_text_edit_dialog_update_object_text (item);
629 }
630 }
632 // complete the transaction
633 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP));
634 gtk_widget_set_sensitive (apply, FALSE);
635 sp_repr_css_attr_unref (css);
636 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
637 }
639 static void
640 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
641 {
642 gtk_widget_destroy (GTK_WIDGET (dlg));
643 }
645 static void
646 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
647 gboolean dostyle,
648 gboolean docontent )
649 {
650 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
651 return;
653 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
655 GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
656 GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
657 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
658 GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
659 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
660 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
662 GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
664 SPItem *text = sp_ted_get_selected_text_item ();
666 Inkscape::XML::Node *repr;
667 if (text)
668 {
669 guint items = sp_ted_get_selected_text_count ();
670 if (items == 1) {
671 gtk_widget_set_sensitive (textw, TRUE);
672 } else {
673 gtk_widget_set_sensitive (textw, FALSE);
674 }
675 gtk_widget_set_sensitive (apply, FALSE);
676 gtk_widget_set_sensitive (def, TRUE);
678 if (docontent) {
679 gchar *str;
680 str = sp_te_get_string_multiline (text);
682 if (str) {
683 int pos;
684 pos = 0;
686 if (items == 1) {
687 gtk_text_buffer_set_text (tb, str, strlen (str));
688 gtk_text_buffer_set_modified (tb, FALSE);
689 }
690 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
691 g_free (str);
693 } else {
694 gtk_text_buffer_set_text (tb, "", 0);
695 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
696 }
697 } // end of if (docontent)
698 repr = SP_OBJECT_REPR (text);
700 } else {
701 gtk_widget_set_sensitive (textw, FALSE);
702 gtk_widget_set_sensitive (apply, FALSE);
703 gtk_widget_set_sensitive (def, FALSE);
704 }
706 if (dostyle) {
708 // create temporary style
709 SPStyle *query = sp_style_new ();
710 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
711 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
712 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
713 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
715 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
716 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
717 repr = inkscape_get_repr (INKSCAPE, "tools.text");
718 if (repr) {
719 gtk_widget_set_sensitive (notebook, TRUE);
720 sp_style_read_from_repr (query, repr);
721 } else {
722 gtk_widget_set_sensitive (notebook, FALSE);
723 }
724 }
726 // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
727 font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
728 if (font) {
729 // the font is oversized, so we need to pass the true size separately
730 sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
731 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
732 font->Unref();
733 font=NULL;
734 }
736 GtkWidget *b;
737 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
738 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
739 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
740 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
741 } else {
742 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
743 }
744 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
746 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
747 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
748 } else {
749 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
750 }
751 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
753 GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
754 double height;
755 if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
756 else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
757 height = query->line_height.value;
758 else height = query->line_height.computed;
759 gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
760 gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
761 g_free(sstr);
763 g_free (query);
764 }
766 g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
767 }
770 static void
771 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
772 {
773 GtkWidget *textw, *preview, *apply, *def;
774 GtkTextIter start, end;
775 gchar *str;
777 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
778 return;
780 SPItem *text = sp_ted_get_selected_text_item ();
782 textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
783 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
784 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
785 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
787 gtk_text_buffer_get_bounds (tb, &start, &end);
788 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
790 if (str && *str) {
791 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
792 } else {
793 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
794 }
795 g_free (str);
797 if (text) {
798 gtk_widget_set_sensitive (apply, TRUE);
799 }
800 gtk_widget_set_sensitive (def, TRUE);
802 } // end of sp_text_edit_dialog_text_changed()
804 void
805 sp_text_edit_dialog_default_set_insensitive ()
806 {
807 if (!dlg) return;
808 gpointer data = g_object_get_data (G_OBJECT (dlg), "default");
809 if (!data) return;
810 gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
811 }
813 static void
814 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
815 font_instance *font,
816 GtkWidget *dlg )
817 {
818 GtkWidget *preview, *apply, *def;
820 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
821 return;
823 SPItem *text = sp_ted_get_selected_text_item ();
825 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
826 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
827 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
829 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
831 if (text)
832 {
833 gtk_widget_set_sensitive (apply, TRUE);
834 }
835 gtk_widget_set_sensitive (def, TRUE);
837 } // end of sp_text_edit_dialog_font_changed()
841 static void
842 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
843 {
844 GtkWidget *apply, *def;
846 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
847 return;
849 SPItem *text = sp_ted_get_selected_text_item ();
851 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
852 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
854 if (text) {
855 gtk_widget_set_sensitive (apply, TRUE);
856 }
857 gtk_widget_set_sensitive (def, TRUE);
858 }
862 static void
863 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
864 {
865 GtkWidget *apply, *def;
867 if (g_object_get_data ((GObject *) dlg, "blocked"))
868 return;
870 SPItem *text = sp_ted_get_selected_text_item ();
872 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
873 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
875 if (text) {
876 gtk_widget_set_sensitive (apply, TRUE);
877 }
878 gtk_widget_set_sensitive (def, TRUE);
879 }
883 static SPItem *
884 sp_ted_get_selected_text_item (void)
885 {
886 if (!SP_ACTIVE_DESKTOP)
887 return NULL;
889 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
890 item != NULL;
891 item = item->next)
892 {
893 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
894 return SP_ITEM (item->data);
895 }
897 return NULL;
898 }
902 static unsigned
903 sp_ted_get_selected_text_count (void)
904 {
905 if (!SP_ACTIVE_DESKTOP)
906 return 0;
908 unsigned int items = 0;
910 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
911 item != NULL;
912 item = item->next)
913 {
914 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
915 ++items;
916 }
918 return items;
919 }
921 /*
922 Local Variables:
923 mode:c++
924 c-file-style:"stroustrup"
925 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
926 indent-tabs-mode:nil
927 fill-column:99
928 End:
929 */
930 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :