Code

switch SPStyle to using SPFilterReference for filters; sp_style_new now requires...
[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                     }
274                     
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 );
476     
477     content = 
478         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
479                     SP_TEXT_CONTENT_MODIFIED_FLAG  )) != 0 );
480     
481     sp_text_edit_dialog_read_selection (dlg, style, content);
483
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                                         gchar c[256];
531                                         font->Family(c, 256);
532                                         sp_repr_css_set_property (css, "font-family", c);
533                                         
534                                         font->Attribute( "weight", c, 256);
535                                         sp_repr_css_set_property (css, "font-weight", c);
536                                         
537                                         font->Attribute("style", c, 256);
538                                         sp_repr_css_set_property (css, "font-style", c);
539                                         
540                                         font->Attribute("stretch", c, 256);
541                                         sp_repr_css_set_property (css, "font-stretch", c);
542                                         
543                                         font->Attribute("variant", c, 256);
544                                         sp_repr_css_set_property (css, "font-variant", c);
545                                         
546                                         Inkscape::CSSOStringStream os;
547                                         os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel)) << "px"; // must specify px, see inkscape bug 1221626 and 1610103
548                                         sp_repr_css_set_property (css, "font-size", os.str().c_str());
549                                         
550                                         font->Unref();
551                                         font=NULL;
552                                 }
553                                 
554         /* Layout */
555         GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
556         
557         // Align Left
558         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
559             sp_repr_css_set_property (css, "text-anchor", "start");
560             sp_repr_css_set_property (css, "text-align", "start");
561         } else {
562             // Align Center
563             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), 
564                                                 "text_anchor_middle");
565             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
566                 sp_repr_css_set_property (css, "text-anchor", "middle");
567                 sp_repr_css_set_property (css, "text-align", "center");
568             } else {
569                 // Align Right
570                 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), 
571                                                     "text_anchor_end");
572                 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
573                     sp_repr_css_set_property (css, "text-anchor", "end");
574                     sp_repr_css_set_property (css, "text-align", "end");
575                 } else {
576                     // Align Justify
577                     sp_repr_css_set_property (css, "text-anchor", "start");
578                     sp_repr_css_set_property (css, "text-align", "justify");
579                 }
580             }
581         }
583         b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
585         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
586             sp_repr_css_set_property (css, "writing-mode", "lr");
587         } else {
588             sp_repr_css_set_property (css, "writing-mode", "tb");
589         }
591         // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
592         // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
593         GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
594         const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
595         sp_repr_css_set_property (css, "line-height", sstr);
597         return css;
601 static void
602 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
604     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
606     SPCSSAttr *css = sp_get_text_dialog_style ();
608     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
609     sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
610     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
612     sp_repr_css_attr_unref (css);
614     gtk_widget_set_sensitive (def, FALSE);
619 static void
620 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
622     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
624     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
625     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
626     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
628     unsigned items = 0;
629     const GSList *item_list = sp_desktop_selection(desktop)->itemList();
630     SPCSSAttr *css = sp_get_text_dialog_style ();
631     sp_desktop_set_style(desktop, css, true);
633     for (; item_list != NULL; item_list = item_list->next) {
634         // apply style to the reprs of all text objects in the selection
635         if (SP_IS_TEXT (item_list->data)) {
637             // backwards compatibility:
638             SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
640             ++items;
641         }
642         else if (SP_IS_FLOWTEXT (item_list->data))
643             // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
644             ++items;
645     }
647     if (items == 0) {
648         // no text objects; apply style to prefs for new objects
649         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
650         gtk_widget_set_sensitive (def, FALSE);
651     } else if (items == 1) {
652         /* exactly one text object; now set its text, too */
653         SPItem *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->singleItem();
654         if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
655             sp_text_edit_dialog_update_object_text (item);
656         }
657     }
659     // complete the transaction
660     sp_document_done (sp_desktop_document (SP_ACTIVE_DESKTOP), SP_VERB_CONTEXT_TEXT, 
661                       _("Set text style"));
662     gtk_widget_set_sensitive (apply, FALSE);
663     sp_repr_css_attr_unref (css);
664     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
667 static void
668 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
670     gtk_widget_destroy (GTK_WIDGET (dlg));
673 static void
674 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
675                                      gboolean dostyle,
676                                      gboolean docontent )
678     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
679         return;
681     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
683     GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
684     GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
685     GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
686     GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
687     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
688     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
690     GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
692     SPItem *text = sp_ted_get_selected_text_item ();
694     Inkscape::XML::Node *repr;
695     if (text)
696     {
697         guint items = sp_ted_get_selected_text_count ();
698         if (items == 1) {
699             gtk_widget_set_sensitive (textw, TRUE);
700         } else {
701             gtk_widget_set_sensitive (textw, FALSE);
702         }
703         gtk_widget_set_sensitive (apply, FALSE);
704         gtk_widget_set_sensitive (def, TRUE);
706         if (docontent) {
707             gchar *str;
708             str = sp_te_get_string_multiline (text);
710             if (str) {
711                 int pos;
712                 pos = 0;
714                 if (items == 1) {
715                     gtk_text_buffer_set_text (tb, str, strlen (str));
716                     gtk_text_buffer_set_modified (tb, FALSE);
717                 }
718                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
719                 g_free (str);
721             } else {
722                 gtk_text_buffer_set_text (tb, "", 0);
723                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
724             }
725         } // end of if (docontent)
726         repr = SP_OBJECT_REPR (text);
728     } else {
729         gtk_widget_set_sensitive (textw, FALSE);
730         gtk_widget_set_sensitive (apply, FALSE);
731         gtk_widget_set_sensitive (def, FALSE);
732     }
734     if (dostyle) {
736         // create temporary style
737         SPStyle *query = sp_style_new (SP_ACTIVE_DOCUMENT);
738         // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
739         int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); 
740         int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); 
741         int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); 
743         // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
744         if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
745             repr = inkscape_get_repr (INKSCAPE, "tools.text");
746             if (repr) {
747                 gtk_widget_set_sensitive (notebook, TRUE);
748                 sp_style_read_from_repr (query, repr);
749             } else {
750                 gtk_widget_set_sensitive (notebook, FALSE);
751             }
752         }
754         // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
755         font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
756         if (font) {
757             // the font is oversized, so we need to pass the true size separately
758             sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
759             sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
760                                                 font->Unref();
761                                                 font=NULL;
762         }
764         GtkWidget *b;
765         if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
766             if (query->text_align.computed == SP_CSS_TEXT_ALIGN_JUSTIFY) {
767                 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_justify" );
768             } else {
769                 b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
770             }
771         } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
772             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
773         } else {
774             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
775         }
776         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
777        
778         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
779             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
780         } else {
781             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
782         }
783         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
785         GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
786         double height;
787         if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
788         else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
789             height = query->line_height.value;
790         else height = query->line_height.computed;
791         gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
792         gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
793         g_free(sstr);
795         sp_style_unref(query);
796     }
798     g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
802 static void
803 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
805     GtkWidget *textw, *preview, *apply, *def;
806     GtkTextIter start, end;
807     gchar *str;
809     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
810         return;
812     SPItem *text = sp_ted_get_selected_text_item ();
814     textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
815     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
816     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
817     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
819     gtk_text_buffer_get_bounds (tb, &start, &end);
820     str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
822     if (str && *str) {
823         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
824     } else {
825         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
826     }
827     g_free (str);
829     if (text) {
830         gtk_widget_set_sensitive (apply, TRUE);
831     }
832     gtk_widget_set_sensitive (def, TRUE);
834 } // end of sp_text_edit_dialog_text_changed()
836 void
837 sp_text_edit_dialog_default_set_insensitive ()
839     if (!dlg) return;
840     gpointer data = g_object_get_data (G_OBJECT (dlg), "default");
841     if (!data) return;
842     gtk_widget_set_sensitive (GTK_WIDGET (data), FALSE);
845 static void
846 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
847                                    font_instance *font,
848                                    GtkWidget *dlg )
850     GtkWidget *preview, *apply, *def;
852     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
853         return;
855     SPItem *text = sp_ted_get_selected_text_item ();
857     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
858     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
859     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
861     sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
863     if (text)
864     {
865         gtk_widget_set_sensitive (apply, TRUE);
866     }
867     gtk_widget_set_sensitive (def, TRUE);
869 } // end of sp_text_edit_dialog_font_changed()
873 static void
874 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
876     GtkWidget *apply, *def;
878     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
879         return;
881     SPItem *text = sp_ted_get_selected_text_item ();
883     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
884     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
886     if (text) {
887         gtk_widget_set_sensitive (apply, TRUE);
888     }
889     gtk_widget_set_sensitive (def, TRUE);
894 static void
895 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
897     GtkWidget *apply, *def;
899     if (g_object_get_data ((GObject *) dlg, "blocked"))
900         return;
902     SPItem *text = sp_ted_get_selected_text_item ();
904     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
905     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
907     if (text) {
908         gtk_widget_set_sensitive (apply, TRUE);
909     }
910     gtk_widget_set_sensitive (def, TRUE);
915 static SPItem *
916 sp_ted_get_selected_text_item (void)
918     if (!SP_ACTIVE_DESKTOP)
919         return NULL;
921     for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
922          item != NULL;
923          item = item->next)
924     {
925         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
926             return SP_ITEM (item->data);
927     }
929     return NULL;
934 static unsigned
935 sp_ted_get_selected_text_count (void)
937     if (!SP_ACTIVE_DESKTOP)
938         return 0;
940     unsigned int items = 0;
942     for (const GSList *item = sp_desktop_selection(SP_ACTIVE_DESKTOP)->itemList();
943          item != NULL;
944          item = item->next)
945     {
946         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
947             ++items;
948     }
950     return items;
953 /*
954   Local Variables:
955   mode:c++
956   c-file-style:"stroustrup"
957   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
958   indent-tabs-mode:nil
959   fill-column:99
960   End:
961 */
962 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :