Code

excise never-used code and stale comments
[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) x=0;
158         if (y<0) y=0;
160         if (x != 0 || y != 0) {
161             gtk_window_move ((GtkWindow *) dlg, x, y);
162         } else {
163             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
164         }
166         if (w && h)
167             gtk_window_resize ((GtkWindow *) dlg, w, h);
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 );
212             gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
213             g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
215             // Layout
216             {
217                 GtkWidget *f = gtk_frame_new (_("Layout"));
218                 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
219                 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
220                 gtk_container_add (GTK_CONTAINER (f), l_vb);
222                 {
223                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
224                     GtkWidget *group;
226                     // align left
227                     {
228                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
229                         GtkWidget *b = group = gtk_radio_button_new (NULL);
230                         gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
231                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
232                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
233                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
234                         gtk_container_add (GTK_CONTAINER (b), px);
235                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
236                         g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
237                     }
239                     // align center
240                     {
241                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
242                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
243                         /* TRANSLATORS: `Center' here is a verb. */
244                         gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
245                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
246                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
247                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
248                         gtk_container_add (GTK_CONTAINER (b), px);
249                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
250                         g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
251                     }
253                     // align right
254                     {
255                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
256                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
257                         gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
258                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
259                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
260                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
261                         gtk_container_add (GTK_CONTAINER (b), px);
262                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
263                         g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
264                     }
266                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
267                 }
270                 {
271                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
272                     GtkWidget *group;
274                     // horizontal
275                     {
276                         GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
277                                                       INKSCAPE_STOCK_WRITING_MODE_LR );
278                         GtkWidget *b = group = gtk_radio_button_new (NULL);
279                         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
280                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
281                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
282                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
283                         gtk_container_add (GTK_CONTAINER (b), px);
284                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
285                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
286                     }
288                     // vertical
289                     {
290                         GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
291                                                       INKSCAPE_STOCK_WRITING_MODE_TB );
292                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
293                         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
294                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
295                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
296                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
297                         gtk_container_add (GTK_CONTAINER (b), px);
298                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
299                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
300                     }
302                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
303                 }
305                 {
306                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
308                     l = gtk_label_new (_("Line spacing:"));
309                     gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
310                     gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
312                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
313                 }
315                 {
316                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
318                     GtkWidget *c = gtk_combo_new ();
319                     gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
320                     gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
321                     gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
322                     gtk_widget_set_size_request (c, 90, -1);
324                     { /* Setup strings */
325                         GList *sl = NULL;
326                         for (int i = 0; spacings[i]; i++) {
327                             sl = g_list_prepend (sl, (void *) spacings[i]);
328                         }
329                         sl = g_list_reverse (sl);
330                         gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
331                         g_list_free (sl);
332                     }
334                     g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
335                                        "changed",
336                                        (GCallback) sp_text_edit_dialog_line_spacing_changed,
337                                        dlg );
338                     gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
339                     g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
341                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
342                 }
343             }
345             /* Font preview */
346             GtkWidget *preview = sp_font_preview_new ();
347             gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
348             g_object_set_data (G_OBJECT (dlg), "preview", preview);
349         }
352         // Text tab
353         {
354             GtkWidget *l = gtk_label_new (_("Text"));
355             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
356             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
357             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
359             GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
360             gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
361                                              GTK_POLICY_AUTOMATIC,
362                                              GTK_POLICY_AUTOMATIC );
363             gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
364             gtk_widget_show (scroller);
366             GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
367             GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
368             gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
369 #ifdef WITH_GTKSPELL
370             GError *error = NULL;
371             char *errortext = NULL;
372             /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
373                language (either as 2nd arg of gtkspell_new_attach, or with explicit
374                gtkspell_set_language call in; see advanced.c example in gtkspell docs).
375                sp_text_edit_dialog_read_selection looks like a suitable place. */
376             if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
377                 g_print("gtkspell error: %s\n", error->message);
378                 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
379                                             "%s", error->message);
380                 g_error_free(error);
381             }
382 #endif
383             gtk_widget_set_size_request (txt, -1, 64);
384             gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
385             gtk_container_add (GTK_CONTAINER (scroller), txt);
386             gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
387             g_signal_connect ( G_OBJECT (tb), "changed",
388                                G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
389             g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
390             g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
391             g_object_set_data (G_OBJECT (dlg), "text", tb);
392             g_object_set_data (G_OBJECT (dlg), "textw", txt);
393         }
395         /* Buttons */
396         GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
397         gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
398         gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
400         {
401             GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
402             g_signal_connect ( G_OBJECT (b), "clicked",
403                                G_CALLBACK (sp_text_edit_dialog_set_default),
404                                dlg );
405             gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
406             g_object_set_data (G_OBJECT (dlg), "default", b);
407         }
409         {
410             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
411             g_signal_connect ( G_OBJECT (b), "clicked", 
412                                G_CALLBACK (sp_text_edit_dialog_close), dlg );
413             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
414         }
416         {
417             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
418             g_signal_connect ( G_OBJECT (b), "clicked", 
419                                G_CALLBACK (sp_text_edit_dialog_apply), dlg );
420             gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
421             g_object_set_data (G_OBJECT (dlg), "apply", b);
422         }
424         g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
425                            G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
426         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
427                            G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
428         g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
430         gtk_widget_show_all (dlg);
432         sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
433     }
435     gtk_window_present ((GtkWindow *) dlg);
437 } // end of sp_text_edit_dialog()
441 static void
442 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape, 
443                                        Inkscape::Selection *sel, 
444                                        guint flags, 
445                                        GtkWidget *dlg )
447     gboolean style, content;
449     style = 
450         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
451                     SP_OBJECT_STYLE_MODIFIED_FLAG  )) != 0 );
452     
453     content = 
454         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
455                     SP_TEXT_CONTENT_MODIFIED_FLAG  )) != 0 );
456     
457     sp_text_edit_dialog_read_selection (dlg, style, content);
459
463 static void
464 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape, 
465                                        Inkscape::Selection *sel, 
466                                        GtkWidget *dlg )
468     sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
471 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
473     sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
476 static void
477 sp_text_edit_dialog_update_object_text ( SPItem *text )
479         GtkTextBuffer *tb;
480         GtkTextIter start, end;
481         gchar *str;
483         tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
485         /* write text */
486         if (gtk_text_buffer_get_modified (tb)) {
487             gtk_text_buffer_get_bounds (tb, &start, &end);
488             str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
489             sp_te_set_repr_text_multiline (text, str);
490             g_free (str);
491             gtk_text_buffer_set_modified (tb, FALSE);
492         }
495 SPCSSAttr *
496 sp_get_text_dialog_style ()
498         GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
500         SPCSSAttr *css = sp_repr_css_attr_new ();
502         /* font */
503         font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
505                                 if ( font ) {
506                                         gchar c[256];
507                                         font->Family(c, 256);
508                                         sp_repr_css_set_property (css, "font-family", c);
509                                         
510                                         font->Attribute( "weight", c, 256);
511                                         sp_repr_css_set_property (css, "font-weight", c);
512                                         
513                                         font->Attribute("style", c, 256);
514                                         sp_repr_css_set_property (css, "font-style", c);
515                                         
516                                         font->Attribute("stretch", c, 256);
517                                         sp_repr_css_set_property (css, "font-stretch", c);
518                                         
519                                         font->Attribute("variant", c, 256);
520                                         sp_repr_css_set_property (css, "font-variant", c);
521                                         
522                                         Inkscape::CSSOStringStream os;
523                                         os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
524                                         sp_repr_css_set_property (css, "font-size", os.str().c_str());
525                                         
526                                         font->Unref();
527                                         font=NULL;
528                                 }
529                                 
530         /* Layout */
531         GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
532         
533         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
534             sp_repr_css_set_property (css, "text-anchor", "start");
535             sp_repr_css_set_property (css, "text-align", "start");
536         } else {
537             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), 
538                                                 "text_anchor_middle");
539             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
540                 sp_repr_css_set_property (css, "text-anchor", "middle");
541                 sp_repr_css_set_property (css, "text-align", "center");
542             } else {
543                 sp_repr_css_set_property (css, "text-anchor", "end");
544                 sp_repr_css_set_property (css, "text-align", "end");
545             }
546         }
548         b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
550         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
551             sp_repr_css_set_property (css, "writing-mode", "lr");
552         } else {
553             sp_repr_css_set_property (css, "writing-mode", "tb");
554         }
556         // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
557         // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
558         GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
559         const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
560         sp_repr_css_set_property (css, "line-height", sstr);
562         return css;
566 static void
567 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
569     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
571     SPCSSAttr *css = sp_get_text_dialog_style ();
573     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
574     sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
575     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
577     sp_repr_css_attr_unref (css);
579     gtk_widget_set_sensitive (def, FALSE);
584 static void
585 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
587     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
589     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
590     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
591     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
593     unsigned items = 0;
594     const GSList *item_list = SP_DT_SELECTION(desktop)->itemList();
596     SPCSSAttr *css = sp_get_text_dialog_style ();
598     sp_desktop_set_style(desktop, css, true);
600     for (; item_list != NULL; item_list = item_list->next) {
601         // apply style to the reprs of all text objects in the selection
602         if (SP_IS_TEXT (item_list->data)) {
604             // backwards compatibility:
605             SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
607             ++items;
608         }
609         else if (SP_IS_FLOWTEXT (item_list->data))
610             // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
611             ++items;
612     }
614     if (items == 0) {
615         // no text objects; apply style to prefs for new objects
616         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
617         gtk_widget_set_sensitive (def, FALSE);
618     } else if (items == 1) {
619         /* exactly one text object; now set its text, too */
620         SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem();
621         if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
622             sp_text_edit_dialog_update_object_text (item);
623         }
624     }
626     // complete the transaction
627     sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP));
629     gtk_widget_set_sensitive (apply, FALSE);
631     sp_repr_css_attr_unref (css);
633     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
638 static void
639 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
641     gtk_widget_destroy (GTK_WIDGET (dlg));
644 static void
645 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
646                                      gboolean dostyle,
647                                      gboolean docontent )
649     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
650         return;
652     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
654     GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
655     GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
656     GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
657     GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
658     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
659     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
661     GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
663     SPItem *text = sp_ted_get_selected_text_item ();
665     Inkscape::XML::Node *repr;
666     if (text)
667     {
668         guint items = sp_ted_get_selected_text_count ();
669         if (items == 1) {
670             gtk_widget_set_sensitive (textw, TRUE);
671         } else {
672             gtk_widget_set_sensitive (textw, FALSE);
673         }
674         gtk_widget_set_sensitive (apply, FALSE);
675         gtk_widget_set_sensitive (def, TRUE);
677         if (docontent) {
678             gchar *str;
679             str = sp_te_get_string_multiline (text);
681             if (str) {
682                 int pos;
683                 pos = 0;
685                 if (items == 1) {
686                     gtk_text_buffer_set_text (tb, str, strlen (str));
687                     gtk_text_buffer_set_modified (tb, FALSE);
688                 }
689                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
690                 g_free (str);
692             } else {
693                 gtk_text_buffer_set_text (tb, "", 0);
694                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
695             }
696         } // end of if (docontent)
697         repr = SP_OBJECT_REPR (text);
699     } else {
700         gtk_widget_set_sensitive (textw, FALSE);
701         gtk_widget_set_sensitive (apply, FALSE);
702         gtk_widget_set_sensitive (def, FALSE);
703     }
705     if (dostyle) {
707         // create temporary style
708         SPStyle *query = sp_style_new ();
709         // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
710         int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); 
711         int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); 
712         int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); 
714         // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
715         if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
716             repr = inkscape_get_repr (INKSCAPE, "tools.text");
717             if (repr) {
718                 gtk_widget_set_sensitive (notebook, TRUE);
719                 sp_style_read_from_repr (query, repr);
720             } else {
721                 gtk_widget_set_sensitive (notebook, FALSE);
722             }
723         }
725         // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
726         font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
727         if (font) {
728             // the font is oversized, so we need to pass the true size separately
729             sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
730             sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
731                                                 font->Unref();
732                                                 font=NULL;
733         }
735         GtkWidget *b;
736         if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
737             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
738         } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
739             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
740         } else {
741             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
742         }
743         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
744        
745         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
746             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
747         } else {
748             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
749         }
750         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
752         GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
753         double height;
754         if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
755         else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
756             height = query->line_height.value;
757         else height = query->line_height.computed;
758         gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
759         gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
760         g_free(sstr);
762         g_free (query);
763     }
765     g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
769 static void
770 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
772     GtkWidget *textw, *preview, *apply, *def;
773     GtkTextIter start, end;
774     gchar *str;
776     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
777         return;
779     SPItem *text = sp_ted_get_selected_text_item ();
781     textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
782     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
783     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
784     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
786     gtk_text_buffer_get_bounds (tb, &start, &end);
787     str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
789     if (str && *str) {
790         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
791     } else {
792         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
793     }
794     g_free (str);
796     if (text) {
797         gtk_widget_set_sensitive (apply, TRUE);
798     }
799     gtk_widget_set_sensitive (def, TRUE);
801 } // end of sp_text_edit_dialog_text_changed()
805 static void
806 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
807                                    font_instance *font,
808                                    GtkWidget *dlg )
810     GtkWidget *preview, *apply, *def;
812     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
813         return;
815     SPItem *text = sp_ted_get_selected_text_item ();
817     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
818     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
819     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
821     sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
823     if (text) {
824         gtk_widget_set_sensitive (apply, TRUE);
825     }
826     gtk_widget_set_sensitive (def, TRUE);
828 } // end of sp_text_edit_dialog_font_changed()
832 static void
833 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
835     GtkWidget *apply, *def;
837     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
838         return;
840     SPItem *text = sp_ted_get_selected_text_item ();
842     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
843     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
845     if (text) {
846         gtk_widget_set_sensitive (apply, TRUE);
847     }
848     gtk_widget_set_sensitive (def, TRUE);
853 static void
854 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
856     GtkWidget *apply, *def;
858     if (g_object_get_data ((GObject *) dlg, "blocked"))
859         return;
861     SPItem *text = sp_ted_get_selected_text_item ();
863     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
864     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
866     if (text) {
867         gtk_widget_set_sensitive (apply, TRUE);
868     }
869     gtk_widget_set_sensitive (def, TRUE);
874 static SPItem *
875 sp_ted_get_selected_text_item (void)
877     if (!SP_ACTIVE_DESKTOP)
878         return NULL;
880     for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
881          item != NULL;
882          item = item->next)
883     {
884         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
885             return SP_ITEM (item->data);
886     }
888     return NULL;
893 static unsigned
894 sp_ted_get_selected_text_count (void)
896     if (!SP_ACTIVE_DESKTOP)
897         return 0;
899     unsigned int items = 0;
901     for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
902          item != NULL;
903          item = item->next)
904     {
905         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
906             ++items;
907     }
909     return items;
912 /*
913   Local Variables:
914   mode:c++
915   c-file-style:"stroustrup"
916   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
917   indent-tabs-mode:nil
918   fill-column:99
919   End:
920 */
921 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :