27304bb1f79e90e8b4a0c4783c5c908604507150
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 {
140 if (!dlg) {
142 gchar title[500];
143 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), title);
145 dlg = sp_window_new (title, TRUE);
146 if (x == -1000 || y == -1000) {
147 x = prefs_get_int_attribute (prefs_path, "x", 0);
148 y = prefs_get_int_attribute (prefs_path, "y", 0);
149 }
151 if (w ==0 || h == 0) {
152 w = prefs_get_int_attribute (prefs_path, "w", 0);
153 h = prefs_get_int_attribute (prefs_path, "h", 0);
154 }
156 if (x<0) x=0;
157 if (y<0) y=0;
159 if (x != 0 || y != 0) {
160 gtk_window_move ((GtkWindow *) dlg, x, y);
161 } else {
162 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
163 }
165 if (w && h)
166 gtk_window_resize ((GtkWindow *) dlg, w, h);
168 sp_transientize (dlg);
169 wd.win = dlg;
170 wd.stop = 0;
171 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
173 gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
175 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
176 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
177 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
179 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
180 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
182 gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
184 GtkTooltips *tt = gtk_tooltips_new();
186 // box containing the notebook and the bottom buttons
187 GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
188 gtk_container_add (GTK_CONTAINER (dlg), mainvb);
190 // notebook
191 GtkWidget *nb = gtk_notebook_new ();
192 gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
193 g_object_set_data (G_OBJECT (dlg), "notebook", nb);
197 // Font tab
198 {
199 GtkWidget *l = gtk_label_new (_("Font"));
200 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
201 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
202 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
204 /* HBox containing font selection and layout */
205 GtkWidget *hb = gtk_hbox_new (FALSE, 0);
206 gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
208 // font and style selector
209 GtkWidget *fontsel = sp_font_selector_new ();
210 g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
212 g_signal_connect_swapped ( G_OBJECT (g_object_get_data (G_OBJECT(fontsel), "family-treeview")),
213 "row-activated",
214 G_CALLBACK (gtk_window_activate_default),
215 dlg);
217 gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
218 g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
220 // Layout
221 {
222 GtkWidget *f = gtk_frame_new (_("Layout"));
223 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
224 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
225 gtk_container_add (GTK_CONTAINER (f), l_vb);
227 {
228 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
229 GtkWidget *group;
231 // align left
232 {
233 // TODO - replace with Inkscape-specific call
234 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
235 GtkWidget *b = group = gtk_radio_button_new (NULL);
236 gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
237 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
238 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
239 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
240 gtk_container_add (GTK_CONTAINER (b), px);
241 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
242 g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
243 }
245 // align center
246 {
247 // TODO - replace with Inkscape-specific call
248 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
249 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
250 /* TRANSLATORS: `Center' here is a verb. */
251 gtk_tooltips_set_tip (tt, b, _("Center lines"), 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_middle", b);
258 }
260 // align right
261 {
262 // TODO - replace with Inkscape-specific call
263 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
264 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
265 gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
266 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
267 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
268 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
269 gtk_container_add (GTK_CONTAINER (b), px);
270 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
271 g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
272 }
274 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
275 }
278 {
279 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
280 GtkWidget *group;
282 // horizontal
283 {
284 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
285 INKSCAPE_STOCK_WRITING_MODE_LR );
286 GtkWidget *b = group = gtk_radio_button_new (NULL);
287 gtk_tooltips_set_tip (tt, b, _("Horizontal 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_LR, b);
294 }
296 // vertical
297 {
298 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
299 INKSCAPE_STOCK_WRITING_MODE_TB );
300 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
301 gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
302 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
303 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
304 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
305 gtk_container_add (GTK_CONTAINER (b), px);
306 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
307 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
308 }
310 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
311 }
313 {
314 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
316 l = gtk_label_new (_("Line spacing:"));
317 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
318 gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
320 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
321 }
323 {
324 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
326 GtkWidget *c = gtk_combo_new ();
327 gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
328 gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
329 gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
330 gtk_widget_set_size_request (c, 90, -1);
332 { /* Setup strings */
333 GList *sl = NULL;
334 for (int i = 0; spacings[i]; i++) {
335 sl = g_list_prepend (sl, (void *) spacings[i]);
336 }
337 sl = g_list_reverse (sl);
338 gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
339 g_list_free (sl);
340 }
342 g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
343 "changed",
344 (GCallback) sp_text_edit_dialog_line_spacing_changed,
345 dlg );
346 gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
347 g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
349 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
350 }
351 }
353 /* Font preview */
354 GtkWidget *preview = sp_font_preview_new ();
355 gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
356 g_object_set_data (G_OBJECT (dlg), "preview", preview);
357 }
360 // Text tab
361 {
362 GtkWidget *l = gtk_label_new (_("Text"));
363 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
364 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
365 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
367 GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
368 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
369 GTK_POLICY_AUTOMATIC,
370 GTK_POLICY_AUTOMATIC );
371 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
372 gtk_widget_show (scroller);
374 GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
375 GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
376 gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
377 #ifdef WITH_GTKSPELL
378 GError *error = NULL;
379 char *errortext = NULL;
380 /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
381 language (either as 2nd arg of gtkspell_new_attach, or with explicit
382 gtkspell_set_language call in; see advanced.c example in gtkspell docs).
383 sp_text_edit_dialog_read_selection looks like a suitable place. */
384 if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
385 g_print("gtkspell error: %s\n", error->message);
386 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
387 "%s", error->message);
388 g_error_free(error);
389 }
390 #endif
391 gtk_widget_set_size_request (txt, -1, 64);
392 gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
393 gtk_container_add (GTK_CONTAINER (scroller), txt);
394 gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
395 g_signal_connect ( G_OBJECT (tb), "changed",
396 G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
397 g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
398 g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
399 g_object_set_data (G_OBJECT (dlg), "text", tb);
400 g_object_set_data (G_OBJECT (dlg), "textw", txt);
401 }
403 /* Buttons */
404 GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
405 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
406 gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
408 {
409 GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
410 g_signal_connect ( G_OBJECT (b), "clicked",
411 G_CALLBACK (sp_text_edit_dialog_set_default),
412 dlg );
413 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
414 g_object_set_data (G_OBJECT (dlg), "default", b);
415 }
417 {
418 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
419 g_signal_connect ( G_OBJECT (b), "clicked",
420 G_CALLBACK (sp_text_edit_dialog_close), dlg );
421 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
422 }
424 {
425 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
426 GTK_WIDGET_SET_FLAGS (b, GTK_CAN_DEFAULT | GTK_HAS_DEFAULT);
427 g_signal_connect ( G_OBJECT (b), "clicked",
428 G_CALLBACK (sp_text_edit_dialog_apply), dlg );
429 gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
430 g_object_set_data (G_OBJECT (dlg), "apply", b);
431 }
433 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
434 G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
435 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
436 G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
437 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
439 gtk_widget_show_all (dlg);
441 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
442 }
444 gtk_window_present ((GtkWindow *) dlg);
446 } // end of sp_text_edit_dialog()
450 static void
451 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape,
452 Inkscape::Selection *sel,
453 guint flags,
454 GtkWidget *dlg )
455 {
456 gboolean style, content;
458 style =
459 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
460 SP_OBJECT_STYLE_MODIFIED_FLAG )) != 0 );
462 content =
463 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
464 SP_TEXT_CONTENT_MODIFIED_FLAG )) != 0 );
466 sp_text_edit_dialog_read_selection (dlg, style, content);
468 }
472 static void
473 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape,
474 Inkscape::Selection *sel,
475 GtkWidget *dlg )
476 {
477 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
478 }
480 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
481 {
482 sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
483 }
485 static void
486 sp_text_edit_dialog_update_object_text ( SPItem *text )
487 {
488 GtkTextBuffer *tb;
489 GtkTextIter start, end;
490 gchar *str;
492 tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
494 /* write text */
495 if (gtk_text_buffer_get_modified (tb)) {
496 gtk_text_buffer_get_bounds (tb, &start, &end);
497 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
498 sp_te_set_repr_text_multiline (text, str);
499 g_free (str);
500 gtk_text_buffer_set_modified (tb, FALSE);
501 }
502 }
504 SPCSSAttr *
505 sp_get_text_dialog_style ()
506 {
507 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
509 SPCSSAttr *css = sp_repr_css_attr_new ();
511 /* font */
512 font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
514 if ( font ) {
515 gchar c[256];
516 font->Family(c, 256);
517 sp_repr_css_set_property (css, "font-family", c);
519 font->Attribute( "weight", c, 256);
520 sp_repr_css_set_property (css, "font-weight", c);
522 font->Attribute("style", c, 256);
523 sp_repr_css_set_property (css, "font-style", c);
525 font->Attribute("stretch", c, 256);
526 sp_repr_css_set_property (css, "font-stretch", c);
528 font->Attribute("variant", c, 256);
529 sp_repr_css_set_property (css, "font-variant", c);
531 Inkscape::CSSOStringStream os;
532 os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
533 sp_repr_css_set_property (css, "font-size", os.str().c_str());
535 font->Unref();
536 font=NULL;
537 }
539 /* Layout */
540 GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
542 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
543 sp_repr_css_set_property (css, "text-anchor", "start");
544 sp_repr_css_set_property (css, "text-align", "start");
545 } else {
546 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
547 "text_anchor_middle");
548 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
549 sp_repr_css_set_property (css, "text-anchor", "middle");
550 sp_repr_css_set_property (css, "text-align", "center");
551 } else {
552 sp_repr_css_set_property (css, "text-anchor", "end");
553 sp_repr_css_set_property (css, "text-align", "end");
554 }
555 }
557 b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
559 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
560 sp_repr_css_set_property (css, "writing-mode", "lr");
561 } else {
562 sp_repr_css_set_property (css, "writing-mode", "tb");
563 }
565 // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
566 // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
567 GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
568 const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
569 sp_repr_css_set_property (css, "line-height", sstr);
571 return css;
572 }
575 static void
576 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
577 {
578 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
580 SPCSSAttr *css = sp_get_text_dialog_style ();
582 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
583 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
584 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
586 sp_repr_css_attr_unref (css);
588 gtk_widget_set_sensitive (def, FALSE);
589 }
593 static void
594 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
595 {
596 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
598 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
599 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
600 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
602 unsigned items = 0;
603 const GSList *item_list = sp_desktop_selection(desktop)->itemList();
604 SPCSSAttr *css = sp_get_text_dialog_style ();
605 sp_desktop_set_style(desktop, css, true);
607 for (; item_list != NULL; item_list = item_list->next) {
608 // apply style to the reprs of all text objects in the selection
609 if (SP_IS_TEXT (item_list->data)) {
611 // backwards compatibility:
612 SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
614 ++items;
615 }
616 else if (SP_IS_FLOWTEXT (item_list->data))
617 // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
618 ++items;
619 }
621 if (items == 0) {
622 // no text objects; apply style to prefs for new objects
623 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
624 gtk_widget_set_sensitive (def, FALSE);
625 } else if (items == 1) {
626 /* exactly one text object; now set its text, too */
627 SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
628 if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
629 sp_text_edit_dialog_update_object_text (item);
630 }
631 }
633 // complete the transaction
634 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP));
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()
807 static void
808 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
809 font_instance *font,
810 GtkWidget *dlg )
811 {
812 GtkWidget *preview, *apply, *def;
814 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
815 return;
817 SPItem *text = sp_ted_get_selected_text_item ();
819 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
820 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
821 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
823 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
825 if (text)
826 {
827 gtk_widget_set_sensitive (apply, TRUE);
828 }
829 gtk_widget_set_sensitive (def, TRUE);
831 } // end of sp_text_edit_dialog_font_changed()
835 static void
836 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
837 {
838 GtkWidget *apply, *def;
840 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
841 return;
843 SPItem *text = sp_ted_get_selected_text_item ();
845 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
846 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
848 if (text) {
849 gtk_widget_set_sensitive (apply, TRUE);
850 }
851 gtk_widget_set_sensitive (def, TRUE);
852 }
856 static void
857 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
858 {
859 GtkWidget *apply, *def;
861 if (g_object_get_data ((GObject *) dlg, "blocked"))
862 return;
864 SPItem *text = sp_ted_get_selected_text_item ();
866 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
867 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
869 if (text) {
870 gtk_widget_set_sensitive (apply, TRUE);
871 }
872 gtk_widget_set_sensitive (def, TRUE);
873 }
877 static SPItem *
878 sp_ted_get_selected_text_item (void)
879 {
880 if (!SP_ACTIVE_DESKTOP)
881 return NULL;
883 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
884 item != NULL;
885 item = item->next)
886 {
887 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
888 return SP_ITEM (item->data);
889 }
891 return NULL;
892 }
896 static unsigned
897 sp_ted_get_selected_text_count (void)
898 {
899 if (!SP_ACTIVE_DESKTOP)
900 return 0;
902 unsigned int items = 0;
904 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
905 item != NULL;
906 item = item->next)
907 {
908 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
909 ++items;
910 }
912 return items;
913 }
915 /*
916 Local Variables:
917 mode:c++
918 c-file-style:"stroustrup"
919 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
920 indent-tabs-mode:nil
921 fill-column:99
922 End:
923 */
924 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :