Code

warning cleanup
[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                     // align justify
276                     {
277                         // TODO - replace with Inkscape-specific call
278                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_FILL, GTK_ICON_SIZE_LARGE_TOOLBAR );
279                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
280                         gtk_tooltips_set_tip (tt, b, _("Justify lines"), NULL);
281                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
282                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
283                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
284                         gtk_container_add (GTK_CONTAINER (b), px);
285                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
286                         g_object_set_data (G_OBJECT (dlg), "text_anchor_justify", b);
287                     }
289                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
290                 }
293                 {
294                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
295                     GtkWidget *group;
297                     // horizontal
298                     {
299                         GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
300                                                       INKSCAPE_STOCK_WRITING_MODE_LR );
301                         GtkWidget *b = group = gtk_radio_button_new (NULL);
302                         gtk_tooltips_set_tip (tt, b, _("Horizontal 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_LR, b);
309                     }
311                     // vertical
312                     {
313                         GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
314                                                       INKSCAPE_STOCK_WRITING_MODE_TB );
315                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
316                         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
317                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
318                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
319                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
320                         gtk_container_add (GTK_CONTAINER (b), px);
321                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
322                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
323                     }
325                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
326                 }
328                 {
329                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
331                     l = gtk_label_new (_("Line spacing:"));
332                     gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
333                     gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
335                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
336                 }
338                 {
339                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
341                     GtkWidget *c = gtk_combo_new ();
342                     gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
343                     gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
344                     gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
345                     gtk_widget_set_size_request (c, 90, -1);
347                     { /* Setup strings */
348                         GList *sl = NULL;
349                         for (int i = 0; spacings[i]; i++) {
350                             sl = g_list_prepend (sl, (void *) spacings[i]);
351                         }
352                         sl = g_list_reverse (sl);
353                         gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
354                         g_list_free (sl);
355                     }
357                     g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
358                                        "changed",
359                                        (GCallback) sp_text_edit_dialog_line_spacing_changed,
360                                        dlg );
361                     gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
362                     g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
364                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
365                 }
366             }
368             /* Font preview */
369             GtkWidget *preview = sp_font_preview_new ();
370             gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
371             g_object_set_data (G_OBJECT (dlg), "preview", preview);
372         }
375         // Text tab
376         {
377             GtkWidget *l = gtk_label_new (_("Text"));
378             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
379             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
380             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
382             GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
383             gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
384                                              GTK_POLICY_AUTOMATIC,
385                                              GTK_POLICY_AUTOMATIC );
386             gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
387             gtk_widget_show (scroller);
389             GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
390             GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
391             gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
392 #ifdef WITH_GTKSPELL
393             GError *error = NULL;
394             char *errortext = NULL;
395             /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
396                language (either as 2nd arg of gtkspell_new_attach, or with explicit
397                gtkspell_set_language call in; see advanced.c example in gtkspell docs).
398                sp_text_edit_dialog_read_selection looks like a suitable place. */
399             if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
400                 g_print("gtkspell error: %s\n", error->message);
401                 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
402                                             "%s", error->message);
403                 g_error_free(error);
404             }
405 #endif
406             gtk_widget_set_size_request (txt, -1, 64);
407             gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
408             gtk_container_add (GTK_CONTAINER (scroller), txt);
409             gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
410             g_signal_connect ( G_OBJECT (tb), "changed",
411                                G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
412             g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
413             g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
414             g_object_set_data (G_OBJECT (dlg), "text", tb);
415             g_object_set_data (G_OBJECT (dlg), "textw", txt);
416         }
418         /* Buttons */
419         GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
420         gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
421         gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
423         {
424             GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
425             g_signal_connect ( G_OBJECT (b), "clicked",
426                                G_CALLBACK (sp_text_edit_dialog_set_default),
427                                dlg );
428             gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
429             g_object_set_data (G_OBJECT (dlg), "default", b);
430         }
432         {
433             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
434             g_signal_connect ( G_OBJECT (b), "clicked",
435                                G_CALLBACK (sp_text_edit_dialog_close), dlg );
436             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
437         }
439         {
440             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
441             GTK_WIDGET_SET_FLAGS (b, GTK_CAN_DEFAULT | GTK_HAS_DEFAULT);
442             g_signal_connect ( G_OBJECT (b), "clicked",
443                                G_CALLBACK (sp_text_edit_dialog_apply), dlg );
444             gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
445             g_object_set_data (G_OBJECT (dlg), "apply", b);
446         }
448         g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection",
449                            G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
450         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection",
451                            G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
452         g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
454         gtk_widget_show_all (dlg);
456         sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
457     }
459     gtk_window_present ((GtkWindow *) dlg);
461 } // end of sp_text_edit_dialog()
465 static void
466 sp_text_edit_dialog_selection_modified( Inkscape::Application */*inkscape*/,
467                                         Inkscape::Selection */*sel*/,
468                                         guint flags,
469                                         GtkWidget *dlg )
471     gboolean style, content;
473     style =
474         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
475                     SP_OBJECT_STYLE_MODIFIED_FLAG  )) != 0 );
477     content =
478         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG |
479                     SP_TEXT_CONTENT_MODIFIED_FLAG  )) != 0 );
481     sp_text_edit_dialog_read_selection (dlg, style, content);
487 static void
488 sp_text_edit_dialog_selection_changed( Inkscape::Application */*inkscape*/,
489                                        Inkscape::Selection */*sel*/,
490                                        GtkWidget *dlg )
492     sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
495 static void sp_text_edit_dialog_subselection_changed( Inkscape::Application */*inkscape*/, SPDesktop */*desktop*/, GtkWidget *dlg )
497     sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
500 static void
501 sp_text_edit_dialog_update_object_text ( SPItem *text )
503         GtkTextBuffer *tb;
504         GtkTextIter start, end;
505         gchar *str;
507         tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
509         /* write text */
510         if (gtk_text_buffer_get_modified (tb)) {
511             gtk_text_buffer_get_bounds (tb, &start, &end);
512             str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
513             sp_te_set_repr_text_multiline (text, str);
514             g_free (str);
515             gtk_text_buffer_set_modified (tb, FALSE);
516         }
519 SPCSSAttr *
520 sp_get_text_dialog_style ()
522         GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
524         SPCSSAttr *css = sp_repr_css_attr_new ();
526         /* font */
527         font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
529         if ( font ) {
530             Glib::ustring fontName = font_factory::Default()->ConstructFontSpecification(font);
531             sp_repr_css_set_property (css, "-inkscape-font-specification", fontName.c_str());
533             gchar c[256];
535             font->Family(c, 256);
536             sp_repr_css_set_property (css, "font-family", c);
538             font->Attribute( "weight", c, 256);
539             sp_repr_css_set_property (css, "font-weight", c);
541             font->Attribute("style", c, 256);
542             sp_repr_css_set_property (css, "font-style", c);
544             font->Attribute("stretch", c, 256);
545             sp_repr_css_set_property (css, "font-stretch", c);
547             font->Attribute("variant", c, 256);
548             sp_repr_css_set_property (css, "font-variant", c);
550             Inkscape::CSSOStringStream os;
551             os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel)) << "px"; // must specify px, see inkscape bug 1221626 and 1610103
552             sp_repr_css_set_property (css, "font-size", os.str().c_str());
554             font->Unref();
555             font=NULL;
556         }
558         /* Layout */
559         GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
561         // Align Left
562         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
563             sp_repr_css_set_property (css, "text-anchor", "start");
564             sp_repr_css_set_property (css, "text-align", "start");
565         } else {
566             // Align Center
567             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
568                                                 "text_anchor_middle");
569             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
570                 sp_repr_css_set_property (css, "text-anchor", "middle");
571                 sp_repr_css_set_property (css, "text-align", "center");
572             } else {
573                 // Align Right
574                 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg),
575                                                     "text_anchor_end");
576                 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
577                     sp_repr_css_set_property (css, "text-anchor", "end");
578                     sp_repr_css_set_property (css, "text-align", "end");
579                 } else {
580                     // Align Justify
581                     sp_repr_css_set_property (css, "text-anchor", "start");
582                     sp_repr_css_set_property (css, "text-align", "justify");
583                 }
584             }
585         }
587         b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
589         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
590             sp_repr_css_set_property (css, "writing-mode", "lr");
591         } else {
592             sp_repr_css_set_property (css, "writing-mode", "tb");
593         }
595         // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
596         // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
597         GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
598         const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
599         sp_repr_css_set_property (css, "line-height", sstr);
601         return css;
605 static void
606 sp_text_edit_dialog_set_default( GtkButton */*button*/, GtkWidget *dlg )
608     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
610     SPCSSAttr *css = sp_get_text_dialog_style ();
612     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
613     sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
614     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
616     sp_repr_css_attr_unref (css);
618     gtk_widget_set_sensitive (def, FALSE);
623 static void
624 sp_text_edit_dialog_apply( GtkButton */*button*/, GtkWidget *dlg )
626     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
628     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
629     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
630     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
632     unsigned items = 0;
633     const GSList *item_list = sp_desktop_selection(desktop)->itemList();
634     SPCSSAttr *css = sp_get_text_dialog_style ();
635     sp_desktop_set_style(desktop, css, true);
637     for (; item_list != NULL; item_list = item_list->next) {
638         // apply style to the reprs of all text objects in the selection
639         if (SP_IS_TEXT (item_list->data)) {
641             // backwards compatibility:
642             SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
644             ++items;
645         }
646         else if (SP_IS_FLOWTEXT (item_list->data))
647             // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
648             ++items;
649     }
651     if (items == 0) {
652         // no text objects; apply style to prefs for new objects
653         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
654         gtk_widget_set_sensitive (def, FALSE);
655     } else if (items == 1) {
656         /* exactly one text object; now set its text, too */
657         SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
658         if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
659             sp_text_edit_dialog_update_object_text (item);
660         }
661     }
663     // complete the transaction
664     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT,
665                       _("Set text style"));
666     gtk_widget_set_sensitive (apply, FALSE);
667     sp_repr_css_attr_unref (css);
668     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
671 static void
672 sp_text_edit_dialog_close( GtkButton */*button*/, GtkWidget *dlg )
674     gtk_widget_destroy (GTK_WIDGET (dlg));
677 static void
678 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
679                                      gboolean dostyle,
680                                      gboolean docontent )
682     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
683         return;
685     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
687     GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
688     GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
689     GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
690     GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
691     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
692     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
694     GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
696     SPItem *text = sp_ted_get_selected_text_item ();
698     Inkscape::XML::Node *repr;
699     if (text)
700     {
701         guint items = sp_ted_get_selected_text_count ();
702         if (items == 1) {
703             gtk_widget_set_sensitive (textw, TRUE);
704         } else {
705             gtk_widget_set_sensitive (textw, FALSE);
706         }
707         gtk_widget_set_sensitive (apply, FALSE);
708         gtk_widget_set_sensitive (def, TRUE);
710         if (docontent) {
711             gchar *str;
712             str = sp_te_get_string_multiline (text);
714             if (str) {
715                 int pos;
716                 pos = 0;
718                 if (items == 1) {
719                     gtk_text_buffer_set_text (tb, str, strlen (str));
720                     gtk_text_buffer_set_modified (tb, FALSE);
721                 }
722                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
723                 g_free (str);
725             } else {
726                 gtk_text_buffer_set_text (tb, "", 0);
727                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
728             }
729         } // end of if (docontent)
730         repr = SP_OBJECT_REPR (text);
732     } else {
733         gtk_widget_set_sensitive (textw, FALSE);
734         gtk_widget_set_sensitive (apply, FALSE);
735         gtk_widget_set_sensitive (def, FALSE);
736     }
738     if (dostyle) {
740         // create temporary style
741         SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
742         // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
743         //int result_fontspec = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
744         int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
745         int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
746         int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
748         // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
749         // (Ok to not get a font specification - must just rely on the family and style in that case)
750         if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING
751                 || result_numbers == QUERY_STYLE_NOTHING) {
752             repr = inkscape_get_repr (INKSCAPE, "tools.text");
753             if (repr) {
754                 gtk_widget_set_sensitive (notebook, TRUE);
755                 sp_style_read_from_repr (query, repr);
756             } else {
757                 gtk_widget_set_sensitive (notebook, FALSE);
758             }
759         }
761         // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
763         // Get a font_instance using the font-specification attribute stored in SPStyle if available
764         font_instance *font = font_factory::Default()->FaceFromStyle(query);
767         if (font) {
768             // the font is oversized, so we need to pass the true size separately
769             sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
770             sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
771             font->Unref();
772             font=NULL;
773         }
775         GtkWidget *b;
776         if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
777             if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY) {
778                 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_justify" );
779             } else {
780                 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
781             }
782         } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
783             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
784         } else {
785             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
786         }
787         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
789         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
790             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
791         } else {
792             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
793         }
794         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
796         GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
797         double height;
798         if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
799         else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
800             height = query->line_height.value;
801         else height = query->line_height.computed;
802         gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
803         gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
804         g_free(sstr);
806         sp_style_unref(query);
807     }
809     g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
813 static void
814 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
816     GtkWidget *textw, *preview, *apply, *def;
817     GtkTextIter start, end;
818     gchar *str;
820     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
821         return;
823     SPItem *text = sp_ted_get_selected_text_item ();
825     textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
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     gtk_text_buffer_get_bounds (tb, &start, &end);
831     str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
833     if (str && *str) {
834         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
835     } else {
836         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
837     }
838     g_free (str);
840     if (text) {
841         gtk_widget_set_sensitive (apply, TRUE);
842     }
843     gtk_widget_set_sensitive (def, TRUE);
845 } // end of sp_text_edit_dialog_text_changed()
847 void
848 sp_text_edit_dialog_default_set_insensitive ()
850     if (!dlg) return;
851     gpointer data = g_object_get_data (G_OBJECT (dlg), "default");
852     if (!data) return;
853     gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
856 static void
857 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
858                                    font_instance *font,
859                                    GtkWidget *dlg )
861     GtkWidget *preview, *apply, *def;
863     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
864         return;
866     SPItem *text = sp_ted_get_selected_text_item ();
868     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
869     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
870     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
872     sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
874     if (text)
875     {
876         gtk_widget_set_sensitive (apply, TRUE);
877     }
878     gtk_widget_set_sensitive (def, TRUE);
880 } // end of sp_text_edit_dialog_font_changed()
884 static void
885 sp_text_edit_dialog_any_toggled( GtkToggleButton */*tb*/, GtkWidget *dlg )
887     GtkWidget *apply, *def;
889     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
890         return;
892     SPItem *text = sp_ted_get_selected_text_item ();
894     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
895     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
897     if (text) {
898         gtk_widget_set_sensitive (apply, TRUE);
899     }
900     gtk_widget_set_sensitive (def, TRUE);
905 static void
906 sp_text_edit_dialog_line_spacing_changed( GtkEditable */*editable*/, GtkWidget *dlg )
908     GtkWidget *apply, *def;
910     if (g_object_get_data ((GObject *) dlg, "blocked"))
911         return;
913     SPItem *text = sp_ted_get_selected_text_item ();
915     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
916     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
918     if (text) {
919         gtk_widget_set_sensitive (apply, TRUE);
920     }
921     gtk_widget_set_sensitive (def, TRUE);
926 static SPItem *
927 sp_ted_get_selected_text_item (void)
929     if (!SP_ACTIVE_DESKTOP)
930         return NULL;
932     for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
933          item != NULL;
934          item = item->next)
935     {
936         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
937             return SP_ITEM (item->data);
938     }
940     return NULL;
945 static unsigned
946 sp_ted_get_selected_text_count (void)
948     if (!SP_ACTIVE_DESKTOP)
949         return 0;
951     unsigned int items = 0;
953     for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
954          item != NULL;
955          item = item->next)
956     {
957         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
958             ++items;
959     }
961     return items;
964 /*
965   Local Variables:
966   mode:c++
967   c-file-style:"stroustrup"
968   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
969   indent-tabs-mode:nil
970   fill-column:99
971   End:
972 */
973 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :