85237c7cb6cd681507d6236ec64b44e3e47830b0
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), SP_VERB_CONTEXT_TEXT,
634 _("Set text style"));
635 gtk_widget_set_sensitive (apply, FALSE);
636 sp_repr_css_attr_unref (css);
637 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
638 }
640 static void
641 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
642 {
643 gtk_widget_destroy (GTK_WIDGET (dlg));
644 }
646 static void
647 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
648 gboolean dostyle,
649 gboolean docontent )
650 {
651 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
652 return;
654 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
656 GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
657 GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
658 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
659 GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
660 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
661 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
663 GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
665 SPItem *text = sp_ted_get_selected_text_item ();
667 Inkscape::XML::Node *repr;
668 if (text)
669 {
670 guint items = sp_ted_get_selected_text_count ();
671 if (items == 1) {
672 gtk_widget_set_sensitive (textw, TRUE);
673 } else {
674 gtk_widget_set_sensitive (textw, FALSE);
675 }
676 gtk_widget_set_sensitive (apply, FALSE);
677 gtk_widget_set_sensitive (def, TRUE);
679 if (docontent) {
680 gchar *str;
681 str = sp_te_get_string_multiline (text);
683 if (str) {
684 int pos;
685 pos = 0;
687 if (items == 1) {
688 gtk_text_buffer_set_text (tb, str, strlen (str));
689 gtk_text_buffer_set_modified (tb, FALSE);
690 }
691 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
692 g_free (str);
694 } else {
695 gtk_text_buffer_set_text (tb, "", 0);
696 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
697 }
698 } // end of if (docontent)
699 repr = SP_OBJECT_REPR (text);
701 } else {
702 gtk_widget_set_sensitive (textw, FALSE);
703 gtk_widget_set_sensitive (apply, FALSE);
704 gtk_widget_set_sensitive (def, FALSE);
705 }
707 if (dostyle) {
709 // create temporary style
710 SPStyle *query = sp_style_new ();
711 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
712 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
713 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
714 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
716 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
717 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
718 repr = inkscape_get_repr (INKSCAPE, "tools.text");
719 if (repr) {
720 gtk_widget_set_sensitive (notebook, TRUE);
721 sp_style_read_from_repr (query, repr);
722 } else {
723 gtk_widget_set_sensitive (notebook, FALSE);
724 }
725 }
727 // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
728 font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
729 if (font) {
730 // the font is oversized, so we need to pass the true size separately
731 sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
732 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
733 font->Unref();
734 font=NULL;
735 }
737 GtkWidget *b;
738 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
739 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
740 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
741 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
742 } else {
743 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
744 }
745 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
747 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
748 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
749 } else {
750 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
751 }
752 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
754 GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
755 double height;
756 if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
757 else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
758 height = query->line_height.value;
759 else height = query->line_height.computed;
760 gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
761 gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
762 g_free(sstr);
764 g_free (query);
765 }
767 g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
768 }
771 static void
772 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
773 {
774 GtkWidget *textw, *preview, *apply, *def;
775 GtkTextIter start, end;
776 gchar *str;
778 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
779 return;
781 SPItem *text = sp_ted_get_selected_text_item ();
783 textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
784 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
785 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
786 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
788 gtk_text_buffer_get_bounds (tb, &start, &end);
789 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
791 if (str && *str) {
792 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
793 } else {
794 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
795 }
796 g_free (str);
798 if (text) {
799 gtk_widget_set_sensitive (apply, TRUE);
800 }
801 gtk_widget_set_sensitive (def, TRUE);
803 } // end of sp_text_edit_dialog_text_changed()
805 void
806 sp_text_edit_dialog_default_set_insensitive ()
807 {
808 if (!dlg) return;
809 gpointer data = g_object_get_data (G_OBJECT (dlg), "default");
810 if (!data) return;
811 gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
812 }
814 static void
815 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
816 font_instance *font,
817 GtkWidget *dlg )
818 {
819 GtkWidget *preview, *apply, *def;
821 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
822 return;
824 SPItem *text = sp_ted_get_selected_text_item ();
826 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
827 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
828 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
830 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
832 if (text)
833 {
834 gtk_widget_set_sensitive (apply, TRUE);
835 }
836 gtk_widget_set_sensitive (def, TRUE);
838 } // end of sp_text_edit_dialog_font_changed()
842 static void
843 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
844 {
845 GtkWidget *apply, *def;
847 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
848 return;
850 SPItem *text = sp_ted_get_selected_text_item ();
852 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
853 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
855 if (text) {
856 gtk_widget_set_sensitive (apply, TRUE);
857 }
858 gtk_widget_set_sensitive (def, TRUE);
859 }
863 static void
864 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
865 {
866 GtkWidget *apply, *def;
868 if (g_object_get_data ((GObject *) dlg, "blocked"))
869 return;
871 SPItem *text = sp_ted_get_selected_text_item ();
873 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
874 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
876 if (text) {
877 gtk_widget_set_sensitive (apply, TRUE);
878 }
879 gtk_widget_set_sensitive (def, TRUE);
880 }
884 static SPItem *
885 sp_ted_get_selected_text_item (void)
886 {
887 if (!SP_ACTIVE_DESKTOP)
888 return NULL;
890 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
891 item != NULL;
892 item = item->next)
893 {
894 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
895 return SP_ITEM (item->data);
896 }
898 return NULL;
899 }
903 static unsigned
904 sp_ted_get_selected_text_count (void)
905 {
906 if (!SP_ACTIVE_DESKTOP)
907 return 0;
909 unsigned int items = 0;
911 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
912 item != NULL;
913 item = item->next)
914 {
915 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
916 ++items;
917 }
919 return items;
920 }
922 /*
923 Local Variables:
924 mode:c++
925 c-file-style:"stroustrup"
926 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
927 indent-tabs-mode:nil
928 fill-column:99
929 End:
930 */
931 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :