Code

136cb823d8f7238fcf3262c2c4ac24e005893b55
[inkscape.git] / src / dialogs / text-edit.cpp
1 #define __SP_TEXT_EDIT_C__
3 /**
4  * \brief Text editing dialog
5  *
6  * Author:
7  *   Lauris Kaplinski <lauris@ximian.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Copyright (C) 1999-2002 Lauris Kaplinski
11  * Copyright (C) 2000-2001 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include <libnrtype/font-instance.h>
22 #include <gtk/gtk.h>
24 #ifdef WITH_GTKSPELL
25 extern "C" {
26 # include <gtkspell/gtkspell.h>
27 }
28 #endif
30 #include "macros.h"
31 #include <glibmm/i18n.h>
32 #include "helper/window.h"
33 #include "../widgets/font-selector.h"
34 #include "../inkscape.h"
35 #include "../document.h"
36 #include "../desktop-style.h"
37 #include "../desktop-handles.h"
38 #include "../selection.h"
39 #include "../style.h"
40 #include "../sp-text.h"
41 #include "../sp-flowtext.h"
42 #include "../text-editing.h"
43 #include "../inkscape-stock.h"
44 #include <libnrtype/font-style-to-pos.h>
46 #include "dialog-events.h"
47 #include "../prefs-utils.h"
48 #include "../verbs.h"
49 #include "../interface.h"
50 #include "svg/css-ostringstream.h"
51 #include "widgets/icon.h"
52 #include <xml/repr.h>
55 #define VB_MARGIN 4
57 static void sp_text_edit_dialog_selection_modified (Inkscape::Application *inkscape, Inkscape::Selection *sel, guint flags, GtkWidget *dlg);
58 static void sp_text_edit_dialog_selection_changed (Inkscape::Application *inkscape, Inkscape::Selection *sel, GtkWidget *dlg);
59 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg);
61 static void sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg);
62 static void sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg);
63 static void sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg);
65 static void sp_text_edit_dialog_read_selection (GtkWidget *dlg, gboolean style, gboolean content);
67 static void sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg);
68 static void sp_text_edit_dialog_font_changed (SPFontSelector *fontsel, font_instance *font, GtkWidget *dlg);
69 static void sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg);
70 static void sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg);
72 static SPItem *sp_ted_get_selected_text_item (void);
73 static unsigned sp_ted_get_selected_text_count (void);
76 static const gchar *spacings[] = {"50%", "80%", "90%", "100%", "110%", "120%", "130%", "140%", "150%", "200%", "300%", NULL};
78 static GtkWidget *dlg = NULL;
79 static win_data wd;
80 // impossible original values to make sure they are read from prefs
81 static gint x = -1000, y = -1000, w = 0, h = 0;
82 static gchar const *prefs_path = "dialogs.textandfont";
87 static void
88 sp_text_edit_dialog_destroy (GtkObject *object, gpointer data)
89 {
90     sp_signal_disconnect_by_data (INKSCAPE, dlg);
91     wd.win = dlg = NULL;
92     wd.stop = 0;
93 }
97 static gboolean
98 sp_text_edit_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data)
99 {
100     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
101     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
103     if (x<0) x=0;
104     if (y<0) y=0;
106     prefs_set_int_attribute (prefs_path, "x", x);
107     prefs_set_int_attribute (prefs_path, "y", y);
108     prefs_set_int_attribute (prefs_path, "w", w);
109     prefs_set_int_attribute (prefs_path, "h", h);
111     return FALSE; // which means, go ahead and destroy it
115 /**
116     These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left.
117     This flag is used to prevent passing keys from the dialog to canvas, so that the text editor
118     can handle keys like Esc and Ctrl+Z itself.
119  */
120 gboolean
121 text_view_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data)
123     GObject *dlg = (GObject *) data;
124     g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE));
125     return FALSE;
128 gboolean
129 text_view_focus_out (GtkWidget *w, GdkEventKey *event, gpointer data)
131     GObject *dlg = (GObject *) data;
132     g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE));
133     return FALSE;
137 void
138 sp_text_edit_dialog (void)
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", 0);
149             y = prefs_get_int_attribute (prefs_path, "y", 0);
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 || y != 0) {
158             gtk_window_move ((GtkWindow *) dlg, x, y);
159         } else {
160             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
161         }
163         if (w && h)
164             gtk_window_resize ((GtkWindow *) dlg, w, h);
166         sp_transientize (dlg);
167         wd.win = dlg;
168         wd.stop = 0;
169         g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
171         gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
173         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
174         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
175         g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
177         g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
178         g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
180         gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
182         GtkTooltips *tt = gtk_tooltips_new();
184         // box containing the notebook and the bottom buttons
185         GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
186         gtk_container_add (GTK_CONTAINER (dlg), mainvb);
188         // notebook
189         GtkWidget *nb = gtk_notebook_new ();
190         gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
191         g_object_set_data (G_OBJECT (dlg), "notebook", nb);
195         // Font tab
196         {
197             GtkWidget *l = gtk_label_new (_("Font"));
198             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
199             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
200             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
202             /* HBox containing font selection and layout */
203             GtkWidget *hb = gtk_hbox_new (FALSE, 0);
204             gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
206             // font and style selector
207             GtkWidget *fontsel = sp_font_selector_new ();
208             g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
209             gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
210             g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
212             // Layout
213             {
214                 GtkWidget *f = gtk_frame_new (_("Layout"));
215                 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
216                 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
217                 gtk_container_add (GTK_CONTAINER (f), l_vb);
219                 {
220                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
221                     GtkWidget *group;
223                     // align left
224                     {
225                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
226                         GtkWidget *b = group = gtk_radio_button_new (NULL);
227                         gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
228                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
229                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
230                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
231                         gtk_container_add (GTK_CONTAINER (b), px);
232                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
233                         g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
234                     }
236                     // align center
237                     {
238                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
239                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
240                         /* TRANSLATORS: `Center' here is a verb. */
241                         gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
242                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
243                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
244                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
245                         gtk_container_add (GTK_CONTAINER (b), px);
246                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
247                         g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
248                     }
250                     // align right
251                     {
252                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
253                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
254                         gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
255                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
256                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
257                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
258                         gtk_container_add (GTK_CONTAINER (b), px);
259                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
260                         g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
261                     }
263                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
264                 }
267                 {
268                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
269                     GtkWidget *group;
271                     // horizontal
272                     {
273                         GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
274                                                       INKSCAPE_STOCK_WRITING_MODE_LR );
275                         GtkWidget *b = group = gtk_radio_button_new (NULL);
276                         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
277                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
278                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
279                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
280                         gtk_container_add (GTK_CONTAINER (b), px);
281                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
282                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
283                     }
285                     // vertical
286                     {
287                         GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
288                                                       INKSCAPE_STOCK_WRITING_MODE_TB );
289                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
290                         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
291                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
292                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
293                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
294                         gtk_container_add (GTK_CONTAINER (b), px);
295                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
296                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
297                     }
299                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
300                 }
302                 {
303                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
305                     l = gtk_label_new (_("Line spacing:"));
306                     gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
307                     gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
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                     GtkWidget *c = gtk_combo_new ();
316                     gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
317                     gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
318                     gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
319                     gtk_widget_set_size_request (c, 90, -1);
321                     { /* Setup strings */
322                         GList *sl = NULL;
323                         for (int i = 0; spacings[i]; i++) {
324                             sl = g_list_prepend (sl, (void *) spacings[i]);
325                         }
326                         sl = g_list_reverse (sl);
327                         gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
328                         g_list_free (sl);
329                     }
331                     g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
332                                        "changed",
333                                        (GCallback) sp_text_edit_dialog_line_spacing_changed,
334                                        dlg );
335                     gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
336                     g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
338                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
339                 }
340             }
342             /* Font preview */
343             GtkWidget *preview = sp_font_preview_new ();
344             gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
345             g_object_set_data (G_OBJECT (dlg), "preview", preview);
346         }
349         // Text tab
350         {
351             GtkWidget *l = gtk_label_new (_("Text"));
352             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
353             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
354             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
356             GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
357             gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
358                                              GTK_POLICY_AUTOMATIC,
359                                              GTK_POLICY_AUTOMATIC );
360             gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
361             gtk_widget_show (scroller);
363             GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
364             GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
365             gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
366 #ifdef WITH_GTKSPELL
367             GError *error = NULL;
368             char *errortext = NULL;
369             /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
370                language (either as 2nd arg of gtkspell_new_attach, or with explicit
371                gtkspell_set_language call in; see advanced.c example in gtkspell docs).
372                sp_text_edit_dialog_read_selection looks like a suitable place. */
373             if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
374                 g_print("gtkspell error: %s\n", error->message);
375                 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
376                                             "%s", error->message);
377                 g_error_free(error);
378             }
379 #endif
380             gtk_widget_set_size_request (txt, -1, 64);
381             gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
382             gtk_container_add (GTK_CONTAINER (scroller), txt);
383             gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
384             g_signal_connect ( G_OBJECT (tb), "changed",
385                                G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
386             g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
387             g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
388             g_object_set_data (G_OBJECT (dlg), "text", tb);
389             g_object_set_data (G_OBJECT (dlg), "textw", txt);
390         }
392         /* Buttons */
393         GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
394         gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
395         gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
397         {
398             GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
399             g_signal_connect ( G_OBJECT (b), "clicked",
400                                G_CALLBACK (sp_text_edit_dialog_set_default),
401                                dlg );
402             gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
403             g_object_set_data (G_OBJECT (dlg), "default", b);
404         }
406         {
407             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
408             g_signal_connect ( G_OBJECT (b), "clicked", 
409                                G_CALLBACK (sp_text_edit_dialog_close), dlg );
410             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
411         }
413         {
414             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
415             g_signal_connect ( G_OBJECT (b), "clicked", 
416                                G_CALLBACK (sp_text_edit_dialog_apply), dlg );
417             gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
418             g_object_set_data (G_OBJECT (dlg), "apply", b);
419         }
421         g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
422                            G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
423         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
424                            G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
425         g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
427         gtk_widget_show_all (dlg);
429         sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
430     }
432     gtk_window_present ((GtkWindow *) dlg);
434 } // end of sp_text_edit_dialog()
438 static void
439 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape, 
440                                        Inkscape::Selection *sel, 
441                                        guint flags, 
442                                        GtkWidget *dlg )
444     gboolean style, content;
446     style = 
447         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
448                     SP_OBJECT_STYLE_MODIFIED_FLAG  )) != 0 );
449     
450     content = 
451         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
452                     SP_TEXT_CONTENT_MODIFIED_FLAG  )) != 0 );
453     
454     sp_text_edit_dialog_read_selection (dlg, style, content);
456
460 static void
461 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape, 
462                                        Inkscape::Selection *sel, 
463                                        GtkWidget *dlg )
465     sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
468 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
470     sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
473 static void
474 sp_text_edit_dialog_update_object_text ( SPItem *text )
476         GtkTextBuffer *tb;
477         GtkTextIter start, end;
478         gchar *str;
480         tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
482         /* write text */
483         if (gtk_text_buffer_get_modified (tb)) {
484             gtk_text_buffer_get_bounds (tb, &start, &end);
485             str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
486             sp_te_set_repr_text_multiline (text, str);
487             g_free (str);
488             gtk_text_buffer_set_modified (tb, FALSE);
489         }
492 SPCSSAttr *
493 sp_get_text_dialog_style ()
495         GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
497         SPCSSAttr *css = sp_repr_css_attr_new ();
499         /* font */
500         font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
502                                 if ( font ) {
503                                         gchar c[256];
504                                         font->Family(c, 256);
505                                         sp_repr_css_set_property (css, "font-family", c);
506                                         
507                                         font->Attribute( "weight", c, 256);
508                                         sp_repr_css_set_property (css, "font-weight", c);
509                                         
510                                         font->Attribute("style", c, 256);
511                                         sp_repr_css_set_property (css, "font-style", c);
512                                         
513                                         font->Attribute("stretch", c, 256);
514                                         sp_repr_css_set_property (css, "font-stretch", c);
515                                         
516                                         font->Attribute("variant", c, 256);
517                                         sp_repr_css_set_property (css, "font-variant", c);
518                                         
519                                         Inkscape::CSSOStringStream os;
520                                         os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
521                                         sp_repr_css_set_property (css, "font-size", os.str().c_str());
522                                         
523                                         font->Unref();
524                                         font=NULL;
525                                 }
526                                 
527         /* Layout */
528         GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
529         
530         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
531             sp_repr_css_set_property (css, "text-anchor", "start");
532             sp_repr_css_set_property (css, "text-align", "start");
533         } else {
534             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), 
535                                                 "text_anchor_middle");
536             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
537                 sp_repr_css_set_property (css, "text-anchor", "middle");
538                 sp_repr_css_set_property (css, "text-align", "center");
539             } else {
540                 sp_repr_css_set_property (css, "text-anchor", "end");
541                 sp_repr_css_set_property (css, "text-align", "end");
542             }
543         }
545         b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
547         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
548             sp_repr_css_set_property (css, "writing-mode", "lr");
549         } else {
550             sp_repr_css_set_property (css, "writing-mode", "tb");
551         }
553         // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
554         // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
555         GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
556         const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
557         sp_repr_css_set_property (css, "line-height", sstr);
559         return css;
563 static void
564 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
566     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
568     SPCSSAttr *css = sp_get_text_dialog_style ();
570     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
571     sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
572     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
574     sp_repr_css_attr_unref (css);
576     gtk_widget_set_sensitive (def, FALSE);
581 static void
582 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
584     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
586     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
587     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
588     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
590     unsigned items = 0;
591     const GSList *item_list = SP_DT_SELECTION(desktop)->itemList();
593     SPCSSAttr *css = sp_get_text_dialog_style ();
595     sp_desktop_set_style(desktop, css, true);
597     for (; item_list != NULL; item_list = item_list->next) {
598         // apply style to the reprs of all text objects in the selection
599         if (SP_IS_TEXT (item_list->data)) {
601             // backwards compatibility:
602             SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
604             ++items;
605         }
606         else if (SP_IS_FLOWTEXT (item_list->data))
607             // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
608             ++items;
609     }
611     if (items == 0) {
612         // no text objects; apply style to prefs for new objects
613         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
614         gtk_widget_set_sensitive (def, FALSE);
615     } else if (items == 1) {
616         /* exactly one text object; now set its text, too */
617         SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem();
618         if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
619             sp_text_edit_dialog_update_object_text (item);
620         }
621     }
623     // complete the transaction
624     sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP));
626     gtk_widget_set_sensitive (apply, FALSE);
628     sp_repr_css_attr_unref (css);
630     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
635 static void
636 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
638     gtk_widget_destroy (GTK_WIDGET (dlg));
641 static void
642 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
643                                      gboolean dostyle,
644                                      gboolean docontent )
646     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
647         return;
649     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
651     GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
652     GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
653     GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
654     GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
655     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
656     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
658     GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
660     SPItem *text = sp_ted_get_selected_text_item ();
662     Inkscape::XML::Node *repr;
663     if (text)
664     {
665         guint items = sp_ted_get_selected_text_count ();
666         if (items == 1) {
667             gtk_widget_set_sensitive (textw, TRUE);
668         } else {
669             gtk_widget_set_sensitive (textw, FALSE);
670         }
671         gtk_widget_set_sensitive (apply, FALSE);
672         gtk_widget_set_sensitive (def, TRUE);
674         if (docontent) {
675             gchar *str;
676             str = sp_te_get_string_multiline (text);
678             if (str) {
679                 int pos;
680                 pos = 0;
682                 if (items == 1) {
683                     gtk_text_buffer_set_text (tb, str, strlen (str));
684                     gtk_text_buffer_set_modified (tb, FALSE);
685                 }
686                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
687                 g_free (str);
689             } else {
690                 gtk_text_buffer_set_text (tb, "", 0);
691                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
692             }
693         } // end of if (docontent)
694         repr = SP_OBJECT_REPR (text);
696     } else {
697         gtk_widget_set_sensitive (textw, FALSE);
698         gtk_widget_set_sensitive (apply, FALSE);
699         gtk_widget_set_sensitive (def, FALSE);
700     }
702     if (dostyle) {
704         // create temporary style
705         SPStyle *query = sp_style_new ();
706         // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
707         int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); 
708         int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); 
709         int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); 
711         // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
712         if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
713             repr = inkscape_get_repr (INKSCAPE, "tools.text");
714             if (repr) {
715                 gtk_widget_set_sensitive (notebook, TRUE);
716                 sp_style_read_from_repr (query, repr);
717             } else {
718                 gtk_widget_set_sensitive (notebook, FALSE);
719             }
720         }
722         // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
723         font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
724         if (font) {
725             // the font is oversized, so we need to pass the true size separately
726             sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
727             sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
728                                                 font->Unref();
729                                                 font=NULL;
730         }
732         GtkWidget *b;
733         if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
734             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
735         } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
736             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
737         } else {
738             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
739         }
740         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
741        
742         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
743             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
744         } else {
745             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
746         }
747         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
749         GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
750         double height;
751         if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
752         else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
753             height = query->line_height.value;
754         else height = query->line_height.computed;
755         gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
756         gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
757         g_free(sstr);
759         g_free (query);
760     }
762     g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
766 static void
767 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
769     GtkWidget *textw, *preview, *apply, *def;
770     GtkTextIter start, end;
771     gchar *str;
773     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
774         return;
776     SPItem *text = sp_ted_get_selected_text_item ();
778     textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
779     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
780     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
781     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
783     gtk_text_buffer_get_bounds (tb, &start, &end);
784     str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
786     if (str && *str) {
787         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
788     } else {
789         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
790     }
791     g_free (str);
793     if (text) {
794         gtk_widget_set_sensitive (apply, TRUE);
795     }
796     gtk_widget_set_sensitive (def, TRUE);
798 } // end of sp_text_edit_dialog_text_changed()
802 static void
803 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
804                                    font_instance *font,
805                                    GtkWidget *dlg )
807     GtkWidget *preview, *apply, *def;
809     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
810         return;
812     SPItem *text = sp_ted_get_selected_text_item ();
814     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
815     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
816     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
818     sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
820     if (text) {
821         gtk_widget_set_sensitive (apply, TRUE);
822     }
823     gtk_widget_set_sensitive (def, TRUE);
825 } // end of sp_text_edit_dialog_font_changed()
829 static void
830 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
832     GtkWidget *apply, *def;
834     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
835         return;
837     SPItem *text = sp_ted_get_selected_text_item ();
839     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
840     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
842     if (text) {
843         gtk_widget_set_sensitive (apply, TRUE);
844     }
845     gtk_widget_set_sensitive (def, TRUE);
850 static void
851 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
853     GtkWidget *apply, *def;
855     if (g_object_get_data ((GObject *) dlg, "blocked"))
856         return;
858     SPItem *text = sp_ted_get_selected_text_item ();
860     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
861     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
863     if (text) {
864         gtk_widget_set_sensitive (apply, TRUE);
865     }
866     gtk_widget_set_sensitive (def, TRUE);
871 static SPItem *
872 sp_ted_get_selected_text_item (void)
874     if (!SP_ACTIVE_DESKTOP)
875         return NULL;
877     for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
878          item != NULL;
879          item = item->next)
880     {
881         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
882             return SP_ITEM (item->data);
883     }
885     return NULL;
890 static unsigned
891 sp_ted_get_selected_text_count (void)
893     if (!SP_ACTIVE_DESKTOP)
894         return 0;
896     unsigned int items = 0;
898     for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
899          item != NULL;
900          item = item->next)
901     {
902         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
903             ++items;
904     }
906     return items;
909 /*
910   Local Variables:
911   mode:c++
912   c-file-style:"stroustrup"
913   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
914   indent-tabs-mode:nil
915   fill-column:99
916   End:
917 */
918 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :