Code

85237c7cb6cd681507d6236ec64b44e3e47830b0
[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)
139     if (!dlg) {
141         gchar title[500];
142         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), title);
144         dlg = sp_window_new (title, TRUE);
145         if (x == -1000 || y == -1000) {
146             x = prefs_get_int_attribute (prefs_path, "x", 0);
147             y = prefs_get_int_attribute (prefs_path, "y", 0);
148         }
150         if (w ==0 || h == 0) {
151             w = prefs_get_int_attribute (prefs_path, "w", 0);
152             h = prefs_get_int_attribute (prefs_path, "h", 0);
153         }
155         if (x<0) x=0;
156         if (y<0) y=0;
158         if (x != 0 || y != 0) {
159             gtk_window_move ((GtkWindow *) dlg, x, y);
160         } else {
161             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
162         }
164         if (w && h)
165             gtk_window_resize ((GtkWindow *) dlg, w, h);
167         sp_transientize (dlg);
168         wd.win = dlg;
169         wd.stop = 0;
170         g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
172         gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
174         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
175         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
176         g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
178         g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
179         g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
181         gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
183         GtkTooltips *tt = gtk_tooltips_new();
185         // box containing the notebook and the bottom buttons
186         GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
187         gtk_container_add (GTK_CONTAINER (dlg), mainvb);
189         // notebook
190         GtkWidget *nb = gtk_notebook_new ();
191         gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
192         g_object_set_data (G_OBJECT (dlg), "notebook", nb);
196         // Font tab
197         {
198             GtkWidget *l = gtk_label_new (_("Font"));
199             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
200             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
201             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
203             /* HBox containing font selection and layout */
204             GtkWidget *hb = gtk_hbox_new (FALSE, 0);
205             gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
207             // font and style selector
208             GtkWidget *fontsel = sp_font_selector_new ();
209             g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
211             g_signal_connect_swapped ( G_OBJECT (g_object_get_data (G_OBJECT(fontsel), "family-treeview")),
212                                       "row-activated",
213                                       G_CALLBACK (gtk_window_activate_default),
214                                       dlg);
216             gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
217             g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
219             // Layout
220             {
221                 GtkWidget *f = gtk_frame_new (_("Layout"));
222                 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
223                 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
224                 gtk_container_add (GTK_CONTAINER (f), l_vb);
226                 {
227                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
228                     GtkWidget *group;
230                     // align left
231                     {
232                         // TODO - replace with Inkscape-specific call
233                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
234                         GtkWidget *b = group = gtk_radio_button_new (NULL);
235                         gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
236                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
237                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
238                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
239                         gtk_container_add (GTK_CONTAINER (b), px);
240                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
241                         g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
242                     }
244                     // align center
245                     {
246                         // TODO - replace with Inkscape-specific call
247                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
248                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
249                         /* TRANSLATORS: `Center' here is a verb. */
250                         gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
251                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
252                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
253                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
254                         gtk_container_add (GTK_CONTAINER (b), px);
255                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
256                         g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
257                     }
259                     // align right
260                     {
261                         // TODO - replace with Inkscape-specific call
262                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
263                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
264                         gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
265                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
266                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
267                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
268                         gtk_container_add (GTK_CONTAINER (b), px);
269                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
270                         g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
271                     }
273                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
274                 }
277                 {
278                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
279                     GtkWidget *group;
281                     // horizontal
282                     {
283                         GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
284                                                       INKSCAPE_STOCK_WRITING_MODE_LR );
285                         GtkWidget *b = group = gtk_radio_button_new (NULL);
286                         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
287                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
288                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
289                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
290                         gtk_container_add (GTK_CONTAINER (b), px);
291                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
292                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
293                     }
295                     // vertical
296                     {
297                         GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
298                                                       INKSCAPE_STOCK_WRITING_MODE_TB );
299                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
300                         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
301                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
302                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
303                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
304                         gtk_container_add (GTK_CONTAINER (b), px);
305                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
306                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
307                     }
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                     l = gtk_label_new (_("Line spacing:"));
316                     gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
317                     gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
319                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
320                 }
322                 {
323                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
325                     GtkWidget *c = gtk_combo_new ();
326                     gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
327                     gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
328                     gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
329                     gtk_widget_set_size_request (c, 90, -1);
331                     { /* Setup strings */
332                         GList *sl = NULL;
333                         for (int i = 0; spacings[i]; i++) {
334                             sl = g_list_prepend (sl, (void *) spacings[i]);
335                         }
336                         sl = g_list_reverse (sl);
337                         gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
338                         g_list_free (sl);
339                     }
341                     g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
342                                        "changed",
343                                        (GCallback) sp_text_edit_dialog_line_spacing_changed,
344                                        dlg );
345                     gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
346                     g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
348                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
349                 }
350             }
352             /* Font preview */
353             GtkWidget *preview = sp_font_preview_new ();
354             gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
355             g_object_set_data (G_OBJECT (dlg), "preview", preview);
356         }
359         // Text tab
360         {
361             GtkWidget *l = gtk_label_new (_("Text"));
362             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
363             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
364             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
366             GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
367             gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
368                                              GTK_POLICY_AUTOMATIC,
369                                              GTK_POLICY_AUTOMATIC );
370             gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
371             gtk_widget_show (scroller);
373             GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
374             GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
375             gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
376 #ifdef WITH_GTKSPELL
377             GError *error = NULL;
378             char *errortext = NULL;
379             /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
380                language (either as 2nd arg of gtkspell_new_attach, or with explicit
381                gtkspell_set_language call in; see advanced.c example in gtkspell docs).
382                sp_text_edit_dialog_read_selection looks like a suitable place. */
383             if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
384                 g_print("gtkspell error: %s\n", error->message);
385                 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
386                                             "%s", error->message);
387                 g_error_free(error);
388             }
389 #endif
390             gtk_widget_set_size_request (txt, -1, 64);
391             gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
392             gtk_container_add (GTK_CONTAINER (scroller), txt);
393             gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
394             g_signal_connect ( G_OBJECT (tb), "changed",
395                                G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
396             g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
397             g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
398             g_object_set_data (G_OBJECT (dlg), "text", tb);
399             g_object_set_data (G_OBJECT (dlg), "textw", txt);
400         }
402         /* Buttons */
403         GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
404         gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
405         gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
407         {
408             GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
409             g_signal_connect ( G_OBJECT (b), "clicked",
410                                G_CALLBACK (sp_text_edit_dialog_set_default),
411                                dlg );
412             gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
413             g_object_set_data (G_OBJECT (dlg), "default", b);
414         }
416         {
417             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
418             g_signal_connect ( G_OBJECT (b), "clicked", 
419                                G_CALLBACK (sp_text_edit_dialog_close), dlg );
420             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
421         }
423         {
424             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
425             GTK_WIDGET_SET_FLAGS (b, GTK_CAN_DEFAULT | GTK_HAS_DEFAULT);
426             g_signal_connect ( G_OBJECT (b), "clicked", 
427                                G_CALLBACK (sp_text_edit_dialog_apply), dlg );
428             gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
429             g_object_set_data (G_OBJECT (dlg), "apply", b);
430         }
432         g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
433                            G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
434         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
435                            G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
436         g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
438         gtk_widget_show_all (dlg);
440         sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
441     }
443     gtk_window_present ((GtkWindow *) dlg);
445 } // end of sp_text_edit_dialog()
449 static void
450 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape, 
451                                        Inkscape::Selection *sel, 
452                                        guint flags, 
453                                        GtkWidget *dlg )
455     gboolean style, content;
457     style = 
458         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
459                     SP_OBJECT_STYLE_MODIFIED_FLAG  )) != 0 );
460     
461     content = 
462         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
463                     SP_TEXT_CONTENT_MODIFIED_FLAG  )) != 0 );
464     
465     sp_text_edit_dialog_read_selection (dlg, style, content);
467
471 static void
472 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape, 
473                                        Inkscape::Selection *sel, 
474                                        GtkWidget *dlg )
476     sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
479 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
481     sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
484 static void
485 sp_text_edit_dialog_update_object_text ( SPItem *text )
487         GtkTextBuffer *tb;
488         GtkTextIter start, end;
489         gchar *str;
491         tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
493         /* write text */
494         if (gtk_text_buffer_get_modified (tb)) {
495             gtk_text_buffer_get_bounds (tb, &start, &end);
496             str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
497             sp_te_set_repr_text_multiline (text, str);
498             g_free (str);
499             gtk_text_buffer_set_modified (tb, FALSE);
500         }
503 SPCSSAttr *
504 sp_get_text_dialog_style ()
506         GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
508         SPCSSAttr *css = sp_repr_css_attr_new ();
510         /* font */
511         font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
513                                 if ( font ) {
514                                         gchar c[256];
515                                         font->Family(c, 256);
516                                         sp_repr_css_set_property (css, "font-family", c);
517                                         
518                                         font->Attribute( "weight", c, 256);
519                                         sp_repr_css_set_property (css, "font-weight", c);
520                                         
521                                         font->Attribute("style", c, 256);
522                                         sp_repr_css_set_property (css, "font-style", c);
523                                         
524                                         font->Attribute("stretch", c, 256);
525                                         sp_repr_css_set_property (css, "font-stretch", c);
526                                         
527                                         font->Attribute("variant", c, 256);
528                                         sp_repr_css_set_property (css, "font-variant", c);
529                                         
530                                         Inkscape::CSSOStringStream os;
531                                         os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
532                                         sp_repr_css_set_property (css, "font-size", os.str().c_str());
533                                         
534                                         font->Unref();
535                                         font=NULL;
536                                 }
537                                 
538         /* Layout */
539         GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
540         
541         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
542             sp_repr_css_set_property (css, "text-anchor", "start");
543             sp_repr_css_set_property (css, "text-align", "start");
544         } else {
545             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), 
546                                                 "text_anchor_middle");
547             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
548                 sp_repr_css_set_property (css, "text-anchor", "middle");
549                 sp_repr_css_set_property (css, "text-align", "center");
550             } else {
551                 sp_repr_css_set_property (css, "text-anchor", "end");
552                 sp_repr_css_set_property (css, "text-align", "end");
553             }
554         }
556         b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
558         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
559             sp_repr_css_set_property (css, "writing-mode", "lr");
560         } else {
561             sp_repr_css_set_property (css, "writing-mode", "tb");
562         }
564         // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
565         // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
566         GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
567         const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
568         sp_repr_css_set_property (css, "line-height", sstr);
570         return css;
574 static void
575 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
577     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
579     SPCSSAttr *css = sp_get_text_dialog_style ();
581     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
582     sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
583     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
585     sp_repr_css_attr_unref (css);
587     gtk_widget_set_sensitive (def, FALSE);
592 static void
593 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
595     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
597     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
598     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
599     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
601     unsigned items = 0;
602     const GSList *item_list = sp_desktop_selection(desktop)->itemList();
603     SPCSSAttr *css = sp_get_text_dialog_style ();
604     sp_desktop_set_style(desktop, css, true);
606     for (; item_list != NULL; item_list = item_list->next) {
607         // apply style to the reprs of all text objects in the selection
608         if (SP_IS_TEXT (item_list->data)) {
610             // backwards compatibility:
611             SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
613             ++items;
614         }
615         else if (SP_IS_FLOWTEXT (item_list->data))
616             // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
617             ++items;
618     }
620     if (items == 0) {
621         // no text objects; apply style to prefs for new objects
622         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
623         gtk_widget_set_sensitive (def, FALSE);
624     } else if (items == 1) {
625         /* exactly one text object; now set its text, too */
626         SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
627         if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
628             sp_text_edit_dialog_update_object_text (item);
629         }
630     }
632     // complete the transaction
633     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT, 
634                       _("Set text style"));
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()
805 void
806 sp_text_edit_dialog_default_set_insensitive ()
808     if (!dlg) return;
809     gpointer data = g_object_get_data (G_OBJECT (dlg), "default");
810     if (!data) return;
811     gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
814 static void
815 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
816                                    font_instance *font,
817                                    GtkWidget *dlg )
819     GtkWidget *preview, *apply, *def;
821     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
822         return;
824     SPItem *text = sp_ted_get_selected_text_item ();
826     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
827     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
828     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
830     sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
832     if (text)
833     {
834         gtk_widget_set_sensitive (apply, TRUE);
835     }
836     gtk_widget_set_sensitive (def, TRUE);
838 } // end of sp_text_edit_dialog_font_changed()
842 static void
843 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
845     GtkWidget *apply, *def;
847     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
848         return;
850     SPItem *text = sp_ted_get_selected_text_item ();
852     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
853     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
855     if (text) {
856         gtk_widget_set_sensitive (apply, TRUE);
857     }
858     gtk_widget_set_sensitive (def, TRUE);
863 static void
864 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
866     GtkWidget *apply, *def;
868     if (g_object_get_data ((GObject *) dlg, "blocked"))
869         return;
871     SPItem *text = sp_ted_get_selected_text_item ();
873     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
874     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
876     if (text) {
877         gtk_widget_set_sensitive (apply, TRUE);
878     }
879     gtk_widget_set_sensitive (def, TRUE);
884 static SPItem *
885 sp_ted_get_selected_text_item (void)
887     if (!SP_ACTIVE_DESKTOP)
888         return NULL;
890     for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
891          item != NULL;
892          item = item->next)
893     {
894         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
895             return SP_ITEM (item->data);
896     }
898     return NULL;
903 static unsigned
904 sp_ted_get_selected_text_count (void)
906     if (!SP_ACTIVE_DESKTOP)
907         return 0;
909     unsigned int items = 0;
911     for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
912          item != NULL;
913          item = item->next)
914     {
915         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
916             ++items;
917     }
919     return items;
922 /*
923   Local Variables:
924   mode:c++
925   c-file-style:"stroustrup"
926   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
927   indent-tabs-mode:nil
928   fill-column:99
929   End:
930 */
931 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :