1 #define __SP_TEXT_EDIT_C__
3 /**
4 * \brief Text editing dialog
5 *
6 * Authors:
7 * Lauris Kaplinski <lauris@ximian.com>
8 * bulia byak <buliabyak@users.sf.net>
9 * Johan Engelen <goejendaagh@zonnet.nl>
10 *
11 * Copyright (C) 1999-2007 Authors
12 * Copyright (C) 2000-2001 Ximian, Inc.
13 *
14 * Released under GNU GPL, read the file 'COPYING' for more information
15 */
17 #ifdef HAVE_CONFIG_H
18 # include "config.h"
19 #endif
21 #include <libnrtype/font-instance.h>
23 #include <gtk/gtk.h>
25 #ifdef WITH_GTKSPELL
26 extern "C" {
27 # include <gtkspell/gtkspell.h>
28 }
29 #endif
31 #include "macros.h"
32 #include <glibmm/i18n.h>
33 #include "helper/window.h"
34 #include "../widgets/font-selector.h"
35 #include "../inkscape.h"
36 #include "../document.h"
37 #include "../desktop-style.h"
38 #include "../desktop-handles.h"
39 #include "../selection.h"
40 #include "../style.h"
41 #include "../sp-text.h"
42 #include "../sp-flowtext.h"
43 #include "../text-editing.h"
44 #include "../inkscape-stock.h"
45 #include <libnrtype/font-style-to-pos.h>
47 #include "dialog-events.h"
48 #include "../prefs-utils.h"
49 #include "../verbs.h"
50 #include "../interface.h"
51 #include "svg/css-ostringstream.h"
52 #include "widgets/icon.h"
53 #include <xml/repr.h>
55 #define VB_MARGIN 4
56 #define MIN_ONSCREEN_DISTANCE 50
58 static void sp_text_edit_dialog_selection_modified (Inkscape::Application *inkscape, Inkscape::Selection *sel, guint flags, GtkWidget *dlg);
59 static void sp_text_edit_dialog_selection_changed (Inkscape::Application *inkscape, Inkscape::Selection *sel, GtkWidget *dlg);
60 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg);
62 static void sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg);
63 static void sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg);
64 static void sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg);
66 static void sp_text_edit_dialog_read_selection (GtkWidget *dlg, gboolean style, gboolean content);
68 static void sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg);
69 static void sp_text_edit_dialog_font_changed (SPFontSelector *fontsel, font_instance *font, GtkWidget *dlg);
70 static void sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg);
71 static void sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg);
73 static SPItem *sp_ted_get_selected_text_item (void);
74 static unsigned sp_ted_get_selected_text_count (void);
77 static const gchar *spacings[] = {"50%", "80%", "90%", "100%", "110%", "120%", "130%", "140%", "150%", "200%", "300%", NULL};
79 static GtkWidget *dlg = NULL;
80 static win_data wd;
81 // impossible original values to make sure they are read from prefs
82 static gint x = -1000, y = -1000, w = 0, h = 0;
83 static gchar const *prefs_path = "dialogs.textandfont";
88 static void
89 sp_text_edit_dialog_destroy (GtkObject *object, gpointer data)
90 {
91 sp_signal_disconnect_by_data (INKSCAPE, dlg);
92 wd.win = dlg = NULL;
93 wd.stop = 0;
94 }
98 static gboolean
99 sp_text_edit_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data)
100 {
101 gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
102 gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
104 if (x<0) x=0;
105 if (y<0) y=0;
107 prefs_set_int_attribute (prefs_path, "x", x);
108 prefs_set_int_attribute (prefs_path, "y", y);
109 prefs_set_int_attribute (prefs_path, "w", w);
110 prefs_set_int_attribute (prefs_path, "h", h);
112 return FALSE; // which means, go ahead and destroy it
113 }
116 /**
117 These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left.
118 This flag is used to prevent passing keys from the dialog to canvas, so that the text editor
119 can handle keys like Esc and Ctrl+Z itself.
120 */
121 gboolean
122 text_view_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data)
123 {
124 GObject *dlg = (GObject *) data;
125 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE));
126 return FALSE;
127 }
129 gboolean
130 text_view_focus_out (GtkWidget *w, GdkEventKey *event, gpointer data)
131 {
132 GObject *dlg = (GObject *) data;
133 g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE));
134 return FALSE;
135 }
138 void
139 sp_text_edit_dialog (void)
140 {
141 if (!dlg) {
143 gchar title[500];
144 sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), title);
146 dlg = sp_window_new (title, TRUE);
147 if (x == -1000 || y == -1000) {
148 x = prefs_get_int_attribute (prefs_path, "x", -1000);
149 y = prefs_get_int_attribute (prefs_path, "y", -1000);
150 }
152 if (w ==0 || h == 0) {
153 w = prefs_get_int_attribute (prefs_path, "w", 0);
154 h = prefs_get_int_attribute (prefs_path, "h", 0);
155 }
157 // if (x<0) x=0;
158 // if (y<0) y=0;
160 if (w && h)
161 gtk_window_resize ((GtkWindow *) dlg, w, h);
162 if (x >= 0 && y >= 0 && (x < (gdk_screen_width()-MIN_ONSCREEN_DISTANCE)) && (y < (gdk_screen_height()-MIN_ONSCREEN_DISTANCE))) {
163 gtk_window_move ((GtkWindow *) dlg, x, y);
164 } else {
165 gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
166 }
169 sp_transientize (dlg);
170 wd.win = dlg;
171 wd.stop = 0;
172 g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
174 gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
176 gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
177 gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
178 g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
180 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
181 g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
183 gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
185 GtkTooltips *tt = gtk_tooltips_new();
187 // box containing the notebook and the bottom buttons
188 GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
189 gtk_container_add (GTK_CONTAINER (dlg), mainvb);
191 // notebook
192 GtkWidget *nb = gtk_notebook_new ();
193 gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
194 g_object_set_data (G_OBJECT (dlg), "notebook", nb);
198 // Font tab
199 {
200 GtkWidget *l = gtk_label_new (_("Font"));
201 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
202 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
203 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
205 /* HBox containing font selection and layout */
206 GtkWidget *hb = gtk_hbox_new (FALSE, 0);
207 gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
209 // font and style selector
210 GtkWidget *fontsel = sp_font_selector_new ();
211 g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
213 g_signal_connect_swapped ( G_OBJECT (g_object_get_data (G_OBJECT(fontsel), "family-treeview")),
214 "row-activated",
215 G_CALLBACK (gtk_window_activate_default),
216 dlg);
218 gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
219 g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
221 // Layout
222 {
223 GtkWidget *f = gtk_frame_new (_("Layout"));
224 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
225 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
226 gtk_container_add (GTK_CONTAINER (f), l_vb);
228 {
229 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
230 GtkWidget *group;
232 // align left
233 {
234 // TODO - replace with Inkscape-specific call
235 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
236 GtkWidget *b = group = gtk_radio_button_new (NULL);
237 gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
238 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
239 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
240 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
241 gtk_container_add (GTK_CONTAINER (b), px);
242 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
243 g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
244 }
246 // align center
247 {
248 // TODO - replace with Inkscape-specific call
249 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
250 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
251 /* TRANSLATORS: `Center' here is a verb. */
252 gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
253 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
254 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
255 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
256 gtk_container_add (GTK_CONTAINER (b), px);
257 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
258 g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
259 }
261 // align right
262 {
263 // TODO - replace with Inkscape-specific call
264 GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
265 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
266 gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
267 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
268 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
269 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
270 gtk_container_add (GTK_CONTAINER (b), px);
271 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
272 g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
273 }
275 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
276 }
279 {
280 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
281 GtkWidget *group;
283 // horizontal
284 {
285 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
286 INKSCAPE_STOCK_WRITING_MODE_LR );
287 GtkWidget *b = group = gtk_radio_button_new (NULL);
288 gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
289 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
290 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
291 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
292 gtk_container_add (GTK_CONTAINER (b), px);
293 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
294 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
295 }
297 // vertical
298 {
299 GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
300 INKSCAPE_STOCK_WRITING_MODE_TB );
301 GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
302 gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
303 gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
304 g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
305 gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
306 gtk_container_add (GTK_CONTAINER (b), px);
307 gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
308 g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
309 }
311 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
312 }
314 {
315 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
317 l = gtk_label_new (_("Line spacing:"));
318 gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
319 gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
321 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
322 }
324 {
325 GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
327 GtkWidget *c = gtk_combo_new ();
328 gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
329 gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
330 gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
331 gtk_widget_set_size_request (c, 90, -1);
333 { /* Setup strings */
334 GList *sl = NULL;
335 for (int i = 0; spacings[i]; i++) {
336 sl = g_list_prepend (sl, (void *) spacings[i]);
337 }
338 sl = g_list_reverse (sl);
339 gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
340 g_list_free (sl);
341 }
343 g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
344 "changed",
345 (GCallback) sp_text_edit_dialog_line_spacing_changed,
346 dlg );
347 gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
348 g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
350 gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
351 }
352 }
354 /* Font preview */
355 GtkWidget *preview = sp_font_preview_new ();
356 gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
357 g_object_set_data (G_OBJECT (dlg), "preview", preview);
358 }
361 // Text tab
362 {
363 GtkWidget *l = gtk_label_new (_("Text"));
364 GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
365 gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
366 gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
368 GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
369 gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
370 GTK_POLICY_AUTOMATIC,
371 GTK_POLICY_AUTOMATIC );
372 gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
373 gtk_widget_show (scroller);
375 GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
376 GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
377 gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
378 #ifdef WITH_GTKSPELL
379 GError *error = NULL;
380 char *errortext = NULL;
381 /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
382 language (either as 2nd arg of gtkspell_new_attach, or with explicit
383 gtkspell_set_language call in; see advanced.c example in gtkspell docs).
384 sp_text_edit_dialog_read_selection looks like a suitable place. */
385 if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
386 g_print("gtkspell error: %s\n", error->message);
387 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
388 "%s", error->message);
389 g_error_free(error);
390 }
391 #endif
392 gtk_widget_set_size_request (txt, -1, 64);
393 gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
394 gtk_container_add (GTK_CONTAINER (scroller), txt);
395 gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
396 g_signal_connect ( G_OBJECT (tb), "changed",
397 G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
398 g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
399 g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
400 g_object_set_data (G_OBJECT (dlg), "text", tb);
401 g_object_set_data (G_OBJECT (dlg), "textw", txt);
402 }
404 /* Buttons */
405 GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
406 gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
407 gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
409 {
410 GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
411 g_signal_connect ( G_OBJECT (b), "clicked",
412 G_CALLBACK (sp_text_edit_dialog_set_default),
413 dlg );
414 gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
415 g_object_set_data (G_OBJECT (dlg), "default", b);
416 }
418 {
419 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
420 g_signal_connect ( G_OBJECT (b), "clicked",
421 G_CALLBACK (sp_text_edit_dialog_close), dlg );
422 gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
423 }
425 {
426 GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
427 GTK_WIDGET_SET_FLAGS (b, GTK_CAN_DEFAULT | GTK_HAS_DEFAULT);
428 g_signal_connect ( G_OBJECT (b), "clicked",
429 G_CALLBACK (sp_text_edit_dialog_apply), dlg );
430 gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
431 g_object_set_data (G_OBJECT (dlg), "apply", b);
432 }
434 g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
435 G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
436 g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
437 G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
438 g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
440 gtk_widget_show_all (dlg);
442 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
443 }
445 gtk_window_present ((GtkWindow *) dlg);
447 } // end of sp_text_edit_dialog()
451 static void
452 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape,
453 Inkscape::Selection *sel,
454 guint flags,
455 GtkWidget *dlg )
456 {
457 gboolean style, content;
459 style =
460 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
461 SP_OBJECT_STYLE_MODIFIED_FLAG )) != 0 );
463 content =
464 ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
465 SP_TEXT_CONTENT_MODIFIED_FLAG )) != 0 );
467 sp_text_edit_dialog_read_selection (dlg, style, content);
469 }
473 static void
474 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape,
475 Inkscape::Selection *sel,
476 GtkWidget *dlg )
477 {
478 sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
479 }
481 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
482 {
483 sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
484 }
486 static void
487 sp_text_edit_dialog_update_object_text ( SPItem *text )
488 {
489 GtkTextBuffer *tb;
490 GtkTextIter start, end;
491 gchar *str;
493 tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
495 /* write text */
496 if (gtk_text_buffer_get_modified (tb)) {
497 gtk_text_buffer_get_bounds (tb, &start, &end);
498 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
499 sp_te_set_repr_text_multiline (text, str);
500 g_free (str);
501 gtk_text_buffer_set_modified (tb, FALSE);
502 }
503 }
505 SPCSSAttr *
506 sp_get_text_dialog_style ()
507 {
508 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
510 SPCSSAttr *css = sp_repr_css_attr_new ();
512 /* font */
513 font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
515 if ( font ) {
516 gchar c[256];
517 font->Family(c, 256);
518 sp_repr_css_set_property (css, "font-family", c);
520 font->Attribute( "weight", c, 256);
521 sp_repr_css_set_property (css, "font-weight", c);
523 font->Attribute("style", c, 256);
524 sp_repr_css_set_property (css, "font-style", c);
526 font->Attribute("stretch", c, 256);
527 sp_repr_css_set_property (css, "font-stretch", c);
529 font->Attribute("variant", c, 256);
530 sp_repr_css_set_property (css, "font-variant", c);
532 Inkscape::CSSOStringStream os;
533 os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel)) << "px"; // must specify px, see inkscape bug 1221626 and 1610103
534 sp_repr_css_set_property (css, "font-size", os.str().c_str());
536 font->Unref();
537 font=NULL;
538 }
540 /* Layout */
541 GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
543 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
544 sp_repr_css_set_property (css, "text-anchor", "start");
545 sp_repr_css_set_property (css, "text-align", "start");
546 } else {
547 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
548 "text_anchor_middle");
549 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
550 sp_repr_css_set_property (css, "text-anchor", "middle");
551 sp_repr_css_set_property (css, "text-align", "center");
552 } else {
553 sp_repr_css_set_property (css, "text-anchor", "end");
554 sp_repr_css_set_property (css, "text-align", "end");
555 }
556 }
558 b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
560 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
561 sp_repr_css_set_property (css, "writing-mode", "lr");
562 } else {
563 sp_repr_css_set_property (css, "writing-mode", "tb");
564 }
566 // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
567 // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
568 GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
569 const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
570 sp_repr_css_set_property (css, "line-height", sstr);
572 return css;
573 }
576 static void
577 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
578 {
579 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
581 SPCSSAttr *css = sp_get_text_dialog_style ();
583 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
584 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
585 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
587 sp_repr_css_attr_unref (css);
589 gtk_widget_set_sensitive (def, FALSE);
590 }
594 static void
595 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
596 {
597 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
599 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
600 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
601 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
603 unsigned items = 0;
604 const GSList *item_list = sp_desktop_selection(desktop)->itemList();
605 SPCSSAttr *css = sp_get_text_dialog_style ();
606 sp_desktop_set_style(desktop, css, true);
608 for (; item_list != NULL; item_list = item_list->next) {
609 // apply style to the reprs of all text objects in the selection
610 if (SP_IS_TEXT (item_list->data)) {
612 // backwards compatibility:
613 SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
615 ++items;
616 }
617 else if (SP_IS_FLOWTEXT (item_list->data))
618 // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
619 ++items;
620 }
622 if (items == 0) {
623 // no text objects; apply style to prefs for new objects
624 sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
625 gtk_widget_set_sensitive (def, FALSE);
626 } else if (items == 1) {
627 /* exactly one text object; now set its text, too */
628 SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
629 if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
630 sp_text_edit_dialog_update_object_text (item);
631 }
632 }
634 // complete the transaction
635 sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
636 _("Set text style"));
637 gtk_widget_set_sensitive (apply, FALSE);
638 sp_repr_css_attr_unref (css);
639 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
640 }
642 static void
643 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
644 {
645 gtk_widget_destroy (GTK_WIDGET (dlg));
646 }
648 static void
649 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
650 gboolean dostyle,
651 gboolean docontent )
652 {
653 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
654 return;
656 g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
658 GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
659 GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
660 GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
661 GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
662 GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
663 GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
665 GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
667 SPItem *text = sp_ted_get_selected_text_item ();
669 Inkscape::XML::Node *repr;
670 if (text)
671 {
672 guint items = sp_ted_get_selected_text_count ();
673 if (items == 1) {
674 gtk_widget_set_sensitive (textw, TRUE);
675 } else {
676 gtk_widget_set_sensitive (textw, FALSE);
677 }
678 gtk_widget_set_sensitive (apply, FALSE);
679 gtk_widget_set_sensitive (def, TRUE);
681 if (docontent) {
682 gchar *str;
683 str = sp_te_get_string_multiline (text);
685 if (str) {
686 int pos;
687 pos = 0;
689 if (items == 1) {
690 gtk_text_buffer_set_text (tb, str, strlen (str));
691 gtk_text_buffer_set_modified (tb, FALSE);
692 }
693 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
694 g_free (str);
696 } else {
697 gtk_text_buffer_set_text (tb, "", 0);
698 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
699 }
700 } // end of if (docontent)
701 repr = SP_OBJECT_REPR (text);
703 } else {
704 gtk_widget_set_sensitive (textw, FALSE);
705 gtk_widget_set_sensitive (apply, FALSE);
706 gtk_widget_set_sensitive (def, FALSE);
707 }
709 if (dostyle) {
711 // create temporary style
712 SPStyle *query = sp_style_new ();
713 // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
714 int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
715 int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
716 int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
718 // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
719 if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
720 repr = inkscape_get_repr (INKSCAPE, "tools.text");
721 if (repr) {
722 gtk_widget_set_sensitive (notebook, TRUE);
723 sp_style_read_from_repr (query, repr);
724 } else {
725 gtk_widget_set_sensitive (notebook, FALSE);
726 }
727 }
729 // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
730 font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
731 if (font) {
732 // the font is oversized, so we need to pass the true size separately
733 sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
734 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
735 font->Unref();
736 font=NULL;
737 }
739 GtkWidget *b;
740 if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
741 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
742 } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
743 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
744 } else {
745 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
746 }
747 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
749 if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
750 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
751 } else {
752 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
753 }
754 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
756 GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
757 double height;
758 if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
759 else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
760 height = query->line_height.value;
761 else height = query->line_height.computed;
762 gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
763 gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
764 g_free(sstr);
766 g_free (query);
767 }
769 g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
770 }
773 static void
774 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
775 {
776 GtkWidget *textw, *preview, *apply, *def;
777 GtkTextIter start, end;
778 gchar *str;
780 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
781 return;
783 SPItem *text = sp_ted_get_selected_text_item ();
785 textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
786 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
787 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
788 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
790 gtk_text_buffer_get_bounds (tb, &start, &end);
791 str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
793 if (str && *str) {
794 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
795 } else {
796 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
797 }
798 g_free (str);
800 if (text) {
801 gtk_widget_set_sensitive (apply, TRUE);
802 }
803 gtk_widget_set_sensitive (def, TRUE);
805 } // end of sp_text_edit_dialog_text_changed()
807 void
808 sp_text_edit_dialog_default_set_insensitive ()
809 {
810 if (!dlg) return;
811 gpointer data = g_object_get_data (G_OBJECT (dlg), "default");
812 if (!data) return;
813 gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
814 }
816 static void
817 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
818 font_instance *font,
819 GtkWidget *dlg )
820 {
821 GtkWidget *preview, *apply, *def;
823 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
824 return;
826 SPItem *text = sp_ted_get_selected_text_item ();
828 preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
829 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
830 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
832 sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
834 if (text)
835 {
836 gtk_widget_set_sensitive (apply, TRUE);
837 }
838 gtk_widget_set_sensitive (def, TRUE);
840 } // end of sp_text_edit_dialog_font_changed()
844 static void
845 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
846 {
847 GtkWidget *apply, *def;
849 if (g_object_get_data (G_OBJECT (dlg), "blocked"))
850 return;
852 SPItem *text = sp_ted_get_selected_text_item ();
854 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
855 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
857 if (text) {
858 gtk_widget_set_sensitive (apply, TRUE);
859 }
860 gtk_widget_set_sensitive (def, TRUE);
861 }
865 static void
866 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
867 {
868 GtkWidget *apply, *def;
870 if (g_object_get_data ((GObject *) dlg, "blocked"))
871 return;
873 SPItem *text = sp_ted_get_selected_text_item ();
875 apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
876 def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
878 if (text) {
879 gtk_widget_set_sensitive (apply, TRUE);
880 }
881 gtk_widget_set_sensitive (def, TRUE);
882 }
886 static SPItem *
887 sp_ted_get_selected_text_item (void)
888 {
889 if (!SP_ACTIVE_DESKTOP)
890 return NULL;
892 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
893 item != NULL;
894 item = item->next)
895 {
896 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
897 return SP_ITEM (item->data);
898 }
900 return NULL;
901 }
905 static unsigned
906 sp_ted_get_selected_text_count (void)
907 {
908 if (!SP_ACTIVE_DESKTOP)
909 return 0;
911 unsigned int items = 0;
913 for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
914 item != NULL;
915 item = item->next)
916 {
917 if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
918 ++items;
919 }
921 return items;
922 }
924 /*
925 Local Variables:
926 mode:c++
927 c-file-style:"stroustrup"
928 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
929 indent-tabs-mode:nil
930 fill-column:99
931 End:
932 */
933 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :