Code

27304bb1f79e90e8b4a0c4783c5c908604507150
[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>
54 #define VB_MARGIN 4
56 static void sp_text_edit_dialog_selection_modified (Inkscape::Application *inkscape, Inkscape::Selection *sel, guint flags, GtkWidget *dlg);
57 static void sp_text_edit_dialog_selection_changed (Inkscape::Application *inkscape, Inkscape::Selection *sel, GtkWidget *dlg);
58 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg);
60 static void sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg);
61 static void sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg);
62 static void sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg);
64 static void sp_text_edit_dialog_read_selection (GtkWidget *dlg, gboolean style, gboolean content);
66 static void sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg);
67 static void sp_text_edit_dialog_font_changed (SPFontSelector *fontsel, font_instance *font, GtkWidget *dlg);
68 static void sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg);
69 static void sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg);
71 static SPItem *sp_ted_get_selected_text_item (void);
72 static unsigned sp_ted_get_selected_text_count (void);
75 static const gchar *spacings[] = {"50%", "80%", "90%", "100%", "110%", "120%", "130%", "140%", "150%", "200%", "300%", NULL};
77 static GtkWidget *dlg = NULL;
78 static win_data wd;
79 // impossible original values to make sure they are read from prefs
80 static gint x = -1000, y = -1000, w = 0, h = 0;
81 static gchar const *prefs_path = "dialogs.textandfont";
86 static void
87 sp_text_edit_dialog_destroy (GtkObject *object, gpointer data)
88 {
89     sp_signal_disconnect_by_data (INKSCAPE, dlg);
90     wd.win = dlg = NULL;
91     wd.stop = 0;
92 }
96 static gboolean
97 sp_text_edit_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data)
98 {
99     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
100     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
102     if (x<0) x=0;
103     if (y<0) y=0;
105     prefs_set_int_attribute (prefs_path, "x", x);
106     prefs_set_int_attribute (prefs_path, "y", y);
107     prefs_set_int_attribute (prefs_path, "w", w);
108     prefs_set_int_attribute (prefs_path, "h", h);
110     return FALSE; // which means, go ahead and destroy it
114 /**
115     These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left.
116     This flag is used to prevent passing keys from the dialog to canvas, so that the text editor
117     can handle keys like Esc and Ctrl+Z itself.
118  */
119 gboolean
120 text_view_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data)
122     GObject *dlg = (GObject *) data;
123     g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE));
124     return FALSE;
127 gboolean
128 text_view_focus_out (GtkWidget *w, GdkEventKey *event, gpointer data)
130     GObject *dlg = (GObject *) data;
131     g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE));
132     return FALSE;
136 void
137 sp_text_edit_dialog (void)
140     if (!dlg) {
142         gchar title[500];
143         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), title);
145         dlg = sp_window_new (title, TRUE);
146         if (x == -1000 || y == -1000) {
147             x = prefs_get_int_attribute (prefs_path, "x", 0);
148             y = prefs_get_int_attribute (prefs_path, "y", 0);
149         }
151         if (w ==0 || h == 0) {
152             w = prefs_get_int_attribute (prefs_path, "w", 0);
153             h = prefs_get_int_attribute (prefs_path, "h", 0);
154         }
156         if (x<0) x=0;
157         if (y<0) y=0;
159         if (x != 0 || y != 0) {
160             gtk_window_move ((GtkWindow *) dlg, x, y);
161         } else {
162             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
163         }
165         if (w && h)
166             gtk_window_resize ((GtkWindow *) dlg, w, h);
168         sp_transientize (dlg);
169         wd.win = dlg;
170         wd.stop = 0;
171         g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
173         gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
175         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
176         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
177         g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
179         g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
180         g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
182         gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
184         GtkTooltips *tt = gtk_tooltips_new();
186         // box containing the notebook and the bottom buttons
187         GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
188         gtk_container_add (GTK_CONTAINER (dlg), mainvb);
190         // notebook
191         GtkWidget *nb = gtk_notebook_new ();
192         gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
193         g_object_set_data (G_OBJECT (dlg), "notebook", nb);
197         // Font tab
198         {
199             GtkWidget *l = gtk_label_new (_("Font"));
200             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
201             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
202             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
204             /* HBox containing font selection and layout */
205             GtkWidget *hb = gtk_hbox_new (FALSE, 0);
206             gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
208             // font and style selector
209             GtkWidget *fontsel = sp_font_selector_new ();
210             g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
212             g_signal_connect_swapped ( G_OBJECT (g_object_get_data (G_OBJECT(fontsel), "family-treeview")),
213                                       "row-activated",
214                                       G_CALLBACK (gtk_window_activate_default),
215                                       dlg);
217             gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
218             g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
220             // Layout
221             {
222                 GtkWidget *f = gtk_frame_new (_("Layout"));
223                 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
224                 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
225                 gtk_container_add (GTK_CONTAINER (f), l_vb);
227                 {
228                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
229                     GtkWidget *group;
231                     // align left
232                     {
233                         // TODO - replace with Inkscape-specific call
234                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
235                         GtkWidget *b = group = gtk_radio_button_new (NULL);
236                         gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
237                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
238                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
239                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
240                         gtk_container_add (GTK_CONTAINER (b), px);
241                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
242                         g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
243                     }
245                     // align center
246                     {
247                         // TODO - replace with Inkscape-specific call
248                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
249                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
250                         /* TRANSLATORS: `Center' here is a verb. */
251                         gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
252                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
253                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
254                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
255                         gtk_container_add (GTK_CONTAINER (b), px);
256                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
257                         g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
258                     }
260                     // align right
261                     {
262                         // TODO - replace with Inkscape-specific call
263                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
264                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
265                         gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
266                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
267                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
268                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
269                         gtk_container_add (GTK_CONTAINER (b), px);
270                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
271                         g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
272                     }
274                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
275                 }
278                 {
279                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
280                     GtkWidget *group;
282                     // horizontal
283                     {
284                         GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
285                                                       INKSCAPE_STOCK_WRITING_MODE_LR );
286                         GtkWidget *b = group = gtk_radio_button_new (NULL);
287                         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
288                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
289                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
290                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
291                         gtk_container_add (GTK_CONTAINER (b), px);
292                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
293                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
294                     }
296                     // vertical
297                     {
298                         GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
299                                                       INKSCAPE_STOCK_WRITING_MODE_TB );
300                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
301                         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
302                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
303                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
304                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
305                         gtk_container_add (GTK_CONTAINER (b), px);
306                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
307                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
308                     }
310                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
311                 }
313                 {
314                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
316                     l = gtk_label_new (_("Line spacing:"));
317                     gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
318                     gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
320                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
321                 }
323                 {
324                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
326                     GtkWidget *c = gtk_combo_new ();
327                     gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
328                     gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
329                     gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
330                     gtk_widget_set_size_request (c, 90, -1);
332                     { /* Setup strings */
333                         GList *sl = NULL;
334                         for (int i = 0; spacings[i]; i++) {
335                             sl = g_list_prepend (sl, (void *) spacings[i]);
336                         }
337                         sl = g_list_reverse (sl);
338                         gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
339                         g_list_free (sl);
340                     }
342                     g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
343                                        "changed",
344                                        (GCallback) sp_text_edit_dialog_line_spacing_changed,
345                                        dlg );
346                     gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
347                     g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
349                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
350                 }
351             }
353             /* Font preview */
354             GtkWidget *preview = sp_font_preview_new ();
355             gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
356             g_object_set_data (G_OBJECT (dlg), "preview", preview);
357         }
360         // Text tab
361         {
362             GtkWidget *l = gtk_label_new (_("Text"));
363             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
364             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
365             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
367             GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
368             gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
369                                              GTK_POLICY_AUTOMATIC,
370                                              GTK_POLICY_AUTOMATIC );
371             gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
372             gtk_widget_show (scroller);
374             GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
375             GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
376             gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
377 #ifdef WITH_GTKSPELL
378             GError *error = NULL;
379             char *errortext = NULL;
380             /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
381                language (either as 2nd arg of gtkspell_new_attach, or with explicit
382                gtkspell_set_language call in; see advanced.c example in gtkspell docs).
383                sp_text_edit_dialog_read_selection looks like a suitable place. */
384             if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
385                 g_print("gtkspell error: %s\n", error->message);
386                 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
387                                             "%s", error->message);
388                 g_error_free(error);
389             }
390 #endif
391             gtk_widget_set_size_request (txt, -1, 64);
392             gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
393             gtk_container_add (GTK_CONTAINER (scroller), txt);
394             gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
395             g_signal_connect ( G_OBJECT (tb), "changed",
396                                G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
397             g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
398             g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
399             g_object_set_data (G_OBJECT (dlg), "text", tb);
400             g_object_set_data (G_OBJECT (dlg), "textw", txt);
401         }
403         /* Buttons */
404         GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
405         gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
406         gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
408         {
409             GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
410             g_signal_connect ( G_OBJECT (b), "clicked",
411                                G_CALLBACK (sp_text_edit_dialog_set_default),
412                                dlg );
413             gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
414             g_object_set_data (G_OBJECT (dlg), "default", b);
415         }
417         {
418             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
419             g_signal_connect ( G_OBJECT (b), "clicked", 
420                                G_CALLBACK (sp_text_edit_dialog_close), dlg );
421             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
422         }
424         {
425             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
426             GTK_WIDGET_SET_FLAGS (b, GTK_CAN_DEFAULT | GTK_HAS_DEFAULT);
427             g_signal_connect ( G_OBJECT (b), "clicked", 
428                                G_CALLBACK (sp_text_edit_dialog_apply), dlg );
429             gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
430             g_object_set_data (G_OBJECT (dlg), "apply", b);
431         }
433         g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
434                            G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
435         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
436                            G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
437         g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
439         gtk_widget_show_all (dlg);
441         sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
442     }
444     gtk_window_present ((GtkWindow *) dlg);
446 } // end of sp_text_edit_dialog()
450 static void
451 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape, 
452                                        Inkscape::Selection *sel, 
453                                        guint flags, 
454                                        GtkWidget *dlg )
456     gboolean style, content;
458     style = 
459         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
460                     SP_OBJECT_STYLE_MODIFIED_FLAG  )) != 0 );
461     
462     content = 
463         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
464                     SP_TEXT_CONTENT_MODIFIED_FLAG  )) != 0 );
465     
466     sp_text_edit_dialog_read_selection (dlg, style, content);
468
472 static void
473 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape, 
474                                        Inkscape::Selection *sel, 
475                                        GtkWidget *dlg )
477     sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
480 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
482     sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
485 static void
486 sp_text_edit_dialog_update_object_text ( SPItem *text )
488         GtkTextBuffer *tb;
489         GtkTextIter start, end;
490         gchar *str;
492         tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
494         /* write text */
495         if (gtk_text_buffer_get_modified (tb)) {
496             gtk_text_buffer_get_bounds (tb, &start, &end);
497             str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
498             sp_te_set_repr_text_multiline (text, str);
499             g_free (str);
500             gtk_text_buffer_set_modified (tb, FALSE);
501         }
504 SPCSSAttr *
505 sp_get_text_dialog_style ()
507         GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
509         SPCSSAttr *css = sp_repr_css_attr_new ();
511         /* font */
512         font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
514                                 if ( font ) {
515                                         gchar c[256];
516                                         font->Family(c, 256);
517                                         sp_repr_css_set_property (css, "font-family", c);
518                                         
519                                         font->Attribute( "weight", c, 256);
520                                         sp_repr_css_set_property (css, "font-weight", c);
521                                         
522                                         font->Attribute("style", c, 256);
523                                         sp_repr_css_set_property (css, "font-style", c);
524                                         
525                                         font->Attribute("stretch", c, 256);
526                                         sp_repr_css_set_property (css, "font-stretch", c);
527                                         
528                                         font->Attribute("variant", c, 256);
529                                         sp_repr_css_set_property (css, "font-variant", c);
530                                         
531                                         Inkscape::CSSOStringStream os;
532                                         os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
533                                         sp_repr_css_set_property (css, "font-size", os.str().c_str());
534                                         
535                                         font->Unref();
536                                         font=NULL;
537                                 }
538                                 
539         /* Layout */
540         GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
541         
542         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
543             sp_repr_css_set_property (css, "text-anchor", "start");
544             sp_repr_css_set_property (css, "text-align", "start");
545         } else {
546             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), 
547                                                 "text_anchor_middle");
548             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
549                 sp_repr_css_set_property (css, "text-anchor", "middle");
550                 sp_repr_css_set_property (css, "text-align", "center");
551             } else {
552                 sp_repr_css_set_property (css, "text-anchor", "end");
553                 sp_repr_css_set_property (css, "text-align", "end");
554             }
555         }
557         b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
559         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
560             sp_repr_css_set_property (css, "writing-mode", "lr");
561         } else {
562             sp_repr_css_set_property (css, "writing-mode", "tb");
563         }
565         // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
566         // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
567         GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
568         const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
569         sp_repr_css_set_property (css, "line-height", sstr);
571         return css;
575 static void
576 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
578     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
580     SPCSSAttr *css = sp_get_text_dialog_style ();
582     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
583     sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
584     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
586     sp_repr_css_attr_unref (css);
588     gtk_widget_set_sensitive (def, FALSE);
593 static void
594 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
596     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
598     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
599     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
600     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
602     unsigned items = 0;
603     const GSList *item_list = sp_desktop_selection(desktop)->itemList();
604     SPCSSAttr *css = sp_get_text_dialog_style ();
605     sp_desktop_set_style(desktop, css, true);
607     for (; item_list != NULL; item_list = item_list->next) {
608         // apply style to the reprs of all text objects in the selection
609         if (SP_IS_TEXT (item_list->data)) {
611             // backwards compatibility:
612             SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
614             ++items;
615         }
616         else if (SP_IS_FLOWTEXT (item_list->data))
617             // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
618             ++items;
619     }
621     if (items == 0) {
622         // no text objects; apply style to prefs for new objects
623         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
624         gtk_widget_set_sensitive (def, FALSE);
625     } else if (items == 1) {
626         /* exactly one text object; now set its text, too */
627         SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
628         if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
629             sp_text_edit_dialog_update_object_text (item);
630         }
631     }
633     // complete the transaction
634     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP));
635     gtk_widget_set_sensitive (apply, FALSE);
636     sp_repr_css_attr_unref (css);
637     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
640 static void
641 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
643     gtk_widget_destroy (GTK_WIDGET (dlg));
646 static void
647 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
648                                      gboolean dostyle,
649                                      gboolean docontent )
651     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
652         return;
654     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
656     GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
657     GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
658     GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
659     GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
660     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
661     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
663     GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
665     SPItem *text = sp_ted_get_selected_text_item ();
667     Inkscape::XML::Node *repr;
668     if (text)
669     {
670         guint items = sp_ted_get_selected_text_count ();
671         if (items == 1) {
672             gtk_widget_set_sensitive (textw, TRUE);
673         } else {
674             gtk_widget_set_sensitive (textw, FALSE);
675         }
676         gtk_widget_set_sensitive (apply, FALSE);
677         gtk_widget_set_sensitive (def, TRUE);
679         if (docontent) {
680             gchar *str;
681             str = sp_te_get_string_multiline (text);
683             if (str) {
684                 int pos;
685                 pos = 0;
687                 if (items == 1) {
688                     gtk_text_buffer_set_text (tb, str, strlen (str));
689                     gtk_text_buffer_set_modified (tb, FALSE);
690                 }
691                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
692                 g_free (str);
694             } else {
695                 gtk_text_buffer_set_text (tb, "", 0);
696                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
697             }
698         } // end of if (docontent)
699         repr = SP_OBJECT_REPR (text);
701     } else {
702         gtk_widget_set_sensitive (textw, FALSE);
703         gtk_widget_set_sensitive (apply, FALSE);
704         gtk_widget_set_sensitive (def, FALSE);
705     }
707     if (dostyle) {
709         // create temporary style
710         SPStyle *query = sp_style_new ();
711         // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
712         int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); 
713         int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); 
714         int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); 
716         // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
717         if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
718             repr = inkscape_get_repr (INKSCAPE, "tools.text");
719             if (repr) {
720                 gtk_widget_set_sensitive (notebook, TRUE);
721                 sp_style_read_from_repr (query, repr);
722             } else {
723                 gtk_widget_set_sensitive (notebook, FALSE);
724             }
725         }
727         // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
728         font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
729         if (font) {
730             // the font is oversized, so we need to pass the true size separately
731             sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
732             sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
733                                                 font->Unref();
734                                                 font=NULL;
735         }
737         GtkWidget *b;
738         if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
739             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
740         } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
741             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
742         } else {
743             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
744         }
745         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
746        
747         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
748             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
749         } else {
750             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
751         }
752         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
754         GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
755         double height;
756         if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
757         else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
758             height = query->line_height.value;
759         else height = query->line_height.computed;
760         gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
761         gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
762         g_free(sstr);
764         g_free (query);
765     }
767     g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
771 static void
772 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
774     GtkWidget *textw, *preview, *apply, *def;
775     GtkTextIter start, end;
776     gchar *str;
778     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
779         return;
781     SPItem *text = sp_ted_get_selected_text_item ();
783     textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
784     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
785     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
786     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
788     gtk_text_buffer_get_bounds (tb, &start, &end);
789     str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
791     if (str && *str) {
792         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
793     } else {
794         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
795     }
796     g_free (str);
798     if (text) {
799         gtk_widget_set_sensitive (apply, TRUE);
800     }
801     gtk_widget_set_sensitive (def, TRUE);
803 } // end of sp_text_edit_dialog_text_changed()
807 static void
808 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
809                                    font_instance *font,
810                                    GtkWidget *dlg )
812     GtkWidget *preview, *apply, *def;
814     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
815         return;
817     SPItem *text = sp_ted_get_selected_text_item ();
819     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
820     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
821     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
823     sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
825     if (text)
826     {
827         gtk_widget_set_sensitive (apply, TRUE);
828     }
829     gtk_widget_set_sensitive (def, TRUE);
831 } // end of sp_text_edit_dialog_font_changed()
835 static void
836 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
838     GtkWidget *apply, *def;
840     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
841         return;
843     SPItem *text = sp_ted_get_selected_text_item ();
845     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
846     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
848     if (text) {
849         gtk_widget_set_sensitive (apply, TRUE);
850     }
851     gtk_widget_set_sensitive (def, TRUE);
856 static void
857 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
859     GtkWidget *apply, *def;
861     if (g_object_get_data ((GObject *) dlg, "blocked"))
862         return;
864     SPItem *text = sp_ted_get_selected_text_item ();
866     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
867     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
869     if (text) {
870         gtk_widget_set_sensitive (apply, TRUE);
871     }
872     gtk_widget_set_sensitive (def, TRUE);
877 static SPItem *
878 sp_ted_get_selected_text_item (void)
880     if (!SP_ACTIVE_DESKTOP)
881         return NULL;
883     for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
884          item != NULL;
885          item = item->next)
886     {
887         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
888             return SP_ITEM (item->data);
889     }
891     return NULL;
896 static unsigned
897 sp_ted_get_selected_text_count (void)
899     if (!SP_ACTIVE_DESKTOP)
900         return 0;
902     unsigned int items = 0;
904     for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
905          item != NULL;
906          item = item->next)
907     {
908         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
909             ++items;
910     }
912     return items;
915 /*
916   Local Variables:
917   mode:c++
918   c-file-style:"stroustrup"
919   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
920   indent-tabs-mode:nil
921   fill-column:99
922   End:
923 */
924 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :