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 prefs_set_int_attribute (prefs_path, "x", x);
104 prefs_set_int_attribute (prefs_path, "y", y);
105 prefs_set_int_attribute (prefs_path, "w", w);
106 prefs_set_int_attribute (prefs_path, "h", h);
108 return FALSE; // which means, go ahead and destroy it
109 }
112 /**
113 These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left.
114 This flag is used to prevent passing keys from the dialog to canvas, so that the text editor
115 can handle keys like Esc and Ctrl+Z itself.
116 */
117 gboolean
118 text_view_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data)
119 {
120 GObject *dlg = (GObject *) data;
121 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE));
122 return FALSE;
123 }
125 gboolean
126 text_view_focus_out (GtkWidget *w, GdkEventKey *event, gpointer data)
127 {
128 GObject *dlg = (GObject *) data;
129 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE));
130 return FALSE;
131 }
134 void
135 sp_text_edit_dialog (void)
136 {
138 if (!dlg) {
140 gchar title[500];
141 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), title);
143 dlg = sp_window_new (title, TRUE);
144 if (x == -1000 || y == -1000) {
145 x = prefs_get_int_attribute (prefs_path, "x", 0);
146 y = prefs_get_int_attribute (prefs_path, "y", 0);
147 }
149 if (w ==0 || h == 0) {
150 w = prefs_get_int_attribute (prefs_path, "w", 0);
151 h = prefs_get_int_attribute (prefs_path, "h", 0);
152 }
154 if (x != 0 || y != 0) {
155 gtk_window_move ((GtkWindow *) dlg, x, y);
156 } else {
157 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
158 }
160 if (w && h)
161 gtk_window_resize ((GtkWindow *) dlg, w, h);
163 sp_transientize (dlg);
164 wd.win = dlg;
165 wd.stop = 0;
166 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
168 gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
170 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
171 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
172 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
174 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
175 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
177 gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
179 GtkTooltips *tt = gtk_tooltips_new();
181 // box containing the notebook and the bottom buttons
182 GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
183 gtk_container_add (GTK_CONTAINER (dlg), mainvb);
185 // notebook
186 GtkWidget *nb = gtk_notebook_new ();
187 gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
188 g_object_set_data (G_OBJECT (dlg), "notebook", nb);
192 // Font tab
193 {
194 GtkWidget *l = gtk_label_new (_("Font"));
195 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
196 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
197 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
199 /* HBox containing font selection and layout */
200 GtkWidget *hb = gtk_hbox_new (FALSE, 0);
201 gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
203 // font and style selector
204 GtkWidget *fontsel = sp_font_selector_new ();
205 g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
206 gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
207 g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
209 // Layout
210 {
211 GtkWidget *f = gtk_frame_new (_("Layout"));
212 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
213 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
214 gtk_container_add (GTK_CONTAINER (f), l_vb);
216 {
217 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
218 GtkWidget *group;
220 // align left
221 {
222 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
223 GtkWidget *b = group = gtk_radio_button_new (NULL);
224 gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
225 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
226 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
227 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
228 gtk_container_add (GTK_CONTAINER (b), px);
229 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
230 g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
231 }
233 // align center
234 {
235 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
236 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
237 /* TRANSLATORS: `Center' here is a verb. */
238 gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
239 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
240 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
241 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
242 gtk_container_add (GTK_CONTAINER (b), px);
243 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
244 g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
245 }
247 // align right
248 {
249 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
250 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
251 gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
252 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
253 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
254 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
255 gtk_container_add (GTK_CONTAINER (b), px);
256 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
257 g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
258 }
260 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
261 }
264 {
265 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
266 GtkWidget *group;
268 // horizontal
269 {
270 GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
271 INKSCAPE_STOCK_WRITING_MODE_LR );
272 GtkWidget *b = group = gtk_radio_button_new (NULL);
273 gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
274 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
275 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
276 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
277 gtk_container_add (GTK_CONTAINER (b), px);
278 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
279 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
280 }
282 // vertical
283 {
284 GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
285 INKSCAPE_STOCK_WRITING_MODE_TB );
286 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
287 gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
288 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
289 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
290 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
291 gtk_container_add (GTK_CONTAINER (b), px);
292 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
293 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
294 }
296 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
297 }
299 {
300 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
302 l = gtk_label_new (_("Line spacing:"));
303 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
304 gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
306 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
307 }
309 {
310 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
312 GtkWidget *c = gtk_combo_new ();
313 gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
314 gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
315 gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
316 gtk_widget_set_size_request (c, 90, -1);
318 { /* Setup strings */
319 GList *sl = NULL;
320 for (int i = 0; spacings[i]; i++) {
321 sl = g_list_prepend (sl, (void *) spacings[i]);
322 }
323 sl = g_list_reverse (sl);
324 gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
325 g_list_free (sl);
326 }
328 g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
329 "changed",
330 (GCallback) sp_text_edit_dialog_line_spacing_changed,
331 dlg );
332 gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
333 g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
335 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
336 }
337 }
339 /* Font preview */
340 GtkWidget *preview = sp_font_preview_new ();
341 gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
342 g_object_set_data (G_OBJECT (dlg), "preview", preview);
343 }
346 // Text tab
347 {
348 GtkWidget *l = gtk_label_new (_("Text"));
349 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
350 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
351 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
353 GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
354 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
355 GTK_POLICY_AUTOMATIC,
356 GTK_POLICY_AUTOMATIC );
357 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
358 gtk_widget_show (scroller);
360 GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
361 GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
362 gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
363 #ifdef WITH_GTKSPELL
364 GError *error = NULL;
365 char *errortext = NULL;
366 /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
367 language (either as 2nd arg of gtkspell_new_attach, or with explicit
368 gtkspell_set_language call in; see advanced.c example in gtkspell docs).
369 sp_text_edit_dialog_read_selection looks like a suitable place. */
370 if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
371 g_print("gtkspell error: %s\n", error->message);
372 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
373 "%s", error->message);
374 g_error_free(error);
375 }
376 #endif
377 gtk_widget_set_size_request (txt, -1, 64);
378 gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
379 gtk_container_add (GTK_CONTAINER (scroller), txt);
380 gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
381 g_signal_connect ( G_OBJECT (tb), "changed",
382 G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
383 g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
384 g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
385 g_object_set_data (G_OBJECT (dlg), "text", tb);
386 g_object_set_data (G_OBJECT (dlg), "textw", txt);
387 }
389 /* Buttons */
390 GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
391 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
392 gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
394 {
395 GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
396 g_signal_connect ( G_OBJECT (b), "clicked",
397 G_CALLBACK (sp_text_edit_dialog_set_default),
398 dlg );
399 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
400 g_object_set_data (G_OBJECT (dlg), "default", b);
401 }
403 {
404 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
405 g_signal_connect ( G_OBJECT (b), "clicked",
406 G_CALLBACK (sp_text_edit_dialog_close), dlg );
407 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
408 }
410 {
411 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
412 g_signal_connect ( G_OBJECT (b), "clicked",
413 G_CALLBACK (sp_text_edit_dialog_apply), dlg );
414 gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
415 g_object_set_data (G_OBJECT (dlg), "apply", b);
416 }
418 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
419 G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
420 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
421 G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
422 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
424 gtk_widget_show_all (dlg);
426 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
427 }
429 gtk_window_present ((GtkWindow *) dlg);
431 } // end of sp_text_edit_dialog()
435 static void
436 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape,
437 Inkscape::Selection *sel,
438 guint flags,
439 GtkWidget *dlg )
440 {
441 gboolean style, content;
443 style =
444 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
445 SP_OBJECT_STYLE_MODIFIED_FLAG )) != 0 );
447 content =
448 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
449 SP_TEXT_CONTENT_MODIFIED_FLAG )) != 0 );
451 sp_text_edit_dialog_read_selection (dlg, style, content);
453 }
457 static void
458 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape,
459 Inkscape::Selection *sel,
460 GtkWidget *dlg )
461 {
462 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
463 }
465 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
466 {
467 sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
468 }
470 static void
471 sp_text_edit_dialog_update_object_text ( SPItem *text )
472 {
473 GtkTextBuffer *tb;
474 GtkTextIter start, end;
475 gchar *str;
477 tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
479 /* write text */
480 if (gtk_text_buffer_get_modified (tb)) {
481 gtk_text_buffer_get_bounds (tb, &start, &end);
482 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
483 sp_te_set_repr_text_multiline (text, str);
484 g_free (str);
485 gtk_text_buffer_set_modified (tb, FALSE);
486 }
487 }
489 SPCSSAttr *
490 sp_get_text_dialog_style ()
491 {
492 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
494 SPCSSAttr *css = sp_repr_css_attr_new ();
496 /* font */
497 font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
499 if ( font ) {
500 gchar c[256];
501 font->Family(c, 256);
502 sp_repr_css_set_property (css, "font-family", c);
504 font->Attribute( "weight", c, 256);
505 sp_repr_css_set_property (css, "font-weight", c);
507 font->Attribute("style", c, 256);
508 sp_repr_css_set_property (css, "font-style", c);
510 font->Attribute("stretch", c, 256);
511 sp_repr_css_set_property (css, "font-stretch", c);
513 font->Attribute("variant", c, 256);
514 sp_repr_css_set_property (css, "font-variant", c);
516 Inkscape::CSSOStringStream os;
517 os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
518 sp_repr_css_set_property (css, "font-size", os.str().c_str());
520 font->Unref();
521 font=NULL;
522 }
524 /* Layout */
525 GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
527 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
528 sp_repr_css_set_property (css, "text-anchor", "start");
529 sp_repr_css_set_property (css, "text-align", "start");
530 } else {
531 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
532 "text_anchor_middle");
533 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
534 sp_repr_css_set_property (css, "text-anchor", "middle");
535 sp_repr_css_set_property (css, "text-align", "center");
536 } else {
537 sp_repr_css_set_property (css, "text-anchor", "end");
538 sp_repr_css_set_property (css, "text-align", "end");
539 }
540 }
542 b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
544 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
545 sp_repr_css_set_property (css, "writing-mode", "lr");
546 } else {
547 sp_repr_css_set_property (css, "writing-mode", "tb");
548 }
550 // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
551 // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
552 GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
553 const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
554 sp_repr_css_set_property (css, "line-height", sstr);
556 return css;
557 }
560 static void
561 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
562 {
563 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
565 SPCSSAttr *css = sp_get_text_dialog_style ();
567 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
568 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
569 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
571 sp_repr_css_attr_unref (css);
573 gtk_widget_set_sensitive (def, FALSE);
574 }
578 static void
579 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
580 {
581 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
583 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
584 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
585 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
587 unsigned items = 0;
588 const GSList *item_list = SP_DT_SELECTION(desktop)->itemList();
590 SPCSSAttr *css = sp_get_text_dialog_style ();
592 sp_desktop_set_style(desktop, css, true);
594 for (; item_list != NULL; item_list = item_list->next) {
595 // apply style to the reprs of all text objects in the selection
596 if (SP_IS_TEXT (item_list->data)) {
598 // backwards compatibility:
599 SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
601 ++items;
602 }
603 else if (SP_IS_FLOWTEXT (item_list->data))
604 // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
605 ++items;
606 }
608 if (items == 0) {
609 // no text objects; apply style to prefs for new objects
610 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
611 gtk_widget_set_sensitive (def, FALSE);
612 } else if (items == 1) {
613 /* exactly one text object; now set its text, too */
614 SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem();
615 if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
616 sp_text_edit_dialog_update_object_text (item);
617 }
618 }
620 // complete the transaction
621 sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP));
623 gtk_widget_set_sensitive (apply, FALSE);
625 sp_repr_css_attr_unref (css);
627 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
628 }
632 static void
633 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
634 {
635 gtk_widget_destroy (GTK_WIDGET (dlg));
636 }
638 static void
639 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
640 gboolean dostyle,
641 gboolean docontent )
642 {
643 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
644 return;
646 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
648 GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
649 GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
650 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
651 GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
652 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
653 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
655 GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
657 SPItem *text = sp_ted_get_selected_text_item ();
659 Inkscape::XML::Node *repr;
660 if (text)
661 {
662 guint items = sp_ted_get_selected_text_count ();
663 if (items == 1) {
664 gtk_widget_set_sensitive (textw, TRUE);
665 } else {
666 gtk_widget_set_sensitive (textw, FALSE);
667 }
668 gtk_widget_set_sensitive (apply, FALSE);
669 gtk_widget_set_sensitive (def, TRUE);
671 if (docontent) {
672 gchar *str;
673 str = sp_te_get_string_multiline (text);
675 if (str) {
676 int pos;
677 pos = 0;
679 if (items == 1) {
680 gtk_text_buffer_set_text (tb, str, strlen (str));
681 gtk_text_buffer_set_modified (tb, FALSE);
682 }
683 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
684 g_free (str);
686 } else {
687 gtk_text_buffer_set_text (tb, "", 0);
688 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
689 }
690 } // end of if (docontent)
691 repr = SP_OBJECT_REPR (text);
693 } else {
694 gtk_widget_set_sensitive (textw, FALSE);
695 gtk_widget_set_sensitive (apply, FALSE);
696 gtk_widget_set_sensitive (def, FALSE);
697 }
699 if (dostyle) {
701 // create temporary style
702 SPStyle *query = sp_style_new ();
703 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
704 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
705 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
706 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
708 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
709 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
710 repr = inkscape_get_repr (INKSCAPE, "tools.text");
711 if (repr) {
712 gtk_widget_set_sensitive (notebook, TRUE);
713 sp_style_read_from_repr (query, repr);
714 } else {
715 gtk_widget_set_sensitive (notebook, FALSE);
716 }
717 }
719 // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
720 font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
721 if (font) {
722 // the font is oversized, so we need to pass the true size separately
723 sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
724 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
725 font->Unref();
726 font=NULL;
727 }
729 GtkWidget *b;
730 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
731 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
732 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
733 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
734 } else {
735 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
736 }
737 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
739 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
740 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
741 } else {
742 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
743 }
744 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
746 GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
747 double height;
748 if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
749 else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
750 height = query->line_height.value;
751 else height = query->line_height.computed;
752 gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
753 gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
754 g_free(sstr);
756 g_free (query);
757 }
759 g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
760 }
763 static void
764 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
765 {
766 GtkWidget *textw, *preview, *apply, *def;
767 GtkTextIter start, end;
768 gchar *str;
770 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
771 return;
773 SPItem *text = sp_ted_get_selected_text_item ();
775 textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
776 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
777 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
778 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
780 gtk_text_buffer_get_bounds (tb, &start, &end);
781 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
783 if (str && *str) {
784 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
785 } else {
786 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
787 }
788 g_free (str);
790 if (text) {
791 gtk_widget_set_sensitive (apply, TRUE);
792 }
793 gtk_widget_set_sensitive (def, TRUE);
795 } // end of sp_text_edit_dialog_text_changed()
799 static void
800 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
801 font_instance *font,
802 GtkWidget *dlg )
803 {
804 GtkWidget *preview, *apply, *def;
806 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
807 return;
809 SPItem *text = sp_ted_get_selected_text_item ();
811 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
812 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
813 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
815 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
817 if (text) {
818 gtk_widget_set_sensitive (apply, TRUE);
819 }
820 gtk_widget_set_sensitive (def, TRUE);
822 } // end of sp_text_edit_dialog_font_changed()
826 static void
827 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
828 {
829 GtkWidget *apply, *def;
831 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
832 return;
834 SPItem *text = sp_ted_get_selected_text_item ();
836 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
837 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
839 if (text) {
840 gtk_widget_set_sensitive (apply, TRUE);
841 }
842 gtk_widget_set_sensitive (def, TRUE);
843 }
847 static void
848 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
849 {
850 GtkWidget *apply, *def;
852 if (g_object_get_data ((GObject *) dlg, "blocked"))
853 return;
855 SPItem *text = sp_ted_get_selected_text_item ();
857 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
858 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
860 if (text) {
861 gtk_widget_set_sensitive (apply, TRUE);
862 }
863 gtk_widget_set_sensitive (def, TRUE);
864 }
868 static SPItem *
869 sp_ted_get_selected_text_item (void)
870 {
871 if (!SP_ACTIVE_DESKTOP)
872 return NULL;
874 for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
875 item != NULL;
876 item = item->next)
877 {
878 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
879 return SP_ITEM (item->data);
880 }
882 return NULL;
883 }
887 static unsigned
888 sp_ted_get_selected_text_count (void)
889 {
890 if (!SP_ACTIVE_DESKTOP)
891 return 0;
893 unsigned int items = 0;
895 for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
896 item != NULL;
897 item = item->next)
898 {
899 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
900 ++items;
901 }
903 return items;
904 }
906 /*
907 Local Variables:
908 mode:c++
909 c-file-style:"stroustrup"
910 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
911 indent-tabs-mode:nil
912 fill-column:99
913 End:
914 */
915 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :