Code

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