Code

Decoupling from direct use of GtkIconSize to allow for smaller custom ones.
[inkscape.git] / src / dialogs / text-edit.cpp
1 #define __SP_TEXT_EDIT_C__
3 /**
4  * \brief Text editing dialog
5  *
6  * Author:
7  *   Lauris Kaplinski <lauris@ximian.com>
8  *   bulia byak <buliabyak@users.sf.net>
9  *
10  * Copyright (C) 1999-2002 Lauris Kaplinski
11  * Copyright (C) 2000-2001 Ximian, Inc.
12  *
13  * Released under GNU GPL, read the file 'COPYING' for more information
14  */
16 #ifdef HAVE_CONFIG_H
17 # include "config.h"
18 #endif
20 #include <libnrtype/font-instance.h>
22 #include <gtk/gtk.h>
24 #ifdef WITH_GTKSPELL
25 extern "C" {
26 # include <gtkspell/gtkspell.h>
27 }
28 #endif
30 #include "macros.h"
31 #include <glibmm/i18n.h>
32 #include "helper/window.h"
33 #include "../widgets/font-selector.h"
34 #include "../inkscape.h"
35 #include "../document.h"
36 #include "../desktop-style.h"
37 #include "../desktop-handles.h"
38 #include "../selection.h"
39 #include "../style.h"
40 #include "../sp-text.h"
41 #include "../sp-flowtext.h"
42 #include "../text-editing.h"
43 #include "../inkscape-stock.h"
44 #include <libnrtype/font-style-to-pos.h>
46 #include "dialog-events.h"
47 #include "../prefs-utils.h"
48 #include "../verbs.h"
49 #include "../interface.h"
50 #include "svg/css-ostringstream.h"
51 #include "widgets/icon.h"
52 #include <xml/repr.h>
55 #define VB_MARGIN 4
57 static void sp_text_edit_dialog_selection_modified (Inkscape::Application *inkscape, Inkscape::Selection *sel, guint flags, GtkWidget *dlg);
58 static void sp_text_edit_dialog_selection_changed (Inkscape::Application *inkscape, Inkscape::Selection *sel, GtkWidget *dlg);
59 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg);
61 static void sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg);
62 static void sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg);
63 static void sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg);
65 static void sp_text_edit_dialog_read_selection (GtkWidget *dlg, gboolean style, gboolean content);
67 static void sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg);
68 static void sp_text_edit_dialog_font_changed (SPFontSelector *fontsel, font_instance *font, GtkWidget *dlg);
69 static void sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg);
70 static void sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg);
72 static SPItem *sp_ted_get_selected_text_item (void);
73 static unsigned sp_ted_get_selected_text_count (void);
76 static const gchar *spacings[] = {"50%", "80%", "90%", "100%", "110%", "120%", "130%", "140%", "150%", "200%", "300%", NULL};
78 static GtkWidget *dlg = NULL;
79 static win_data wd;
80 // impossible original values to make sure they are read from prefs
81 static gint x = -1000, y = -1000, w = 0, h = 0;
82 static gchar const *prefs_path = "dialogs.textandfont";
87 static void
88 sp_text_edit_dialog_destroy (GtkObject *object, gpointer data)
89 {
90     sp_signal_disconnect_by_data (INKSCAPE, dlg);
91     wd.win = dlg = NULL;
92     wd.stop = 0;
93 }
97 static gboolean
98 sp_text_edit_dialog_delete (GtkObject *object, GdkEvent *event, gpointer data)
99 {
100     gtk_window_get_position ((GtkWindow *) dlg, &x, &y);
101     gtk_window_get_size ((GtkWindow *) dlg, &w, &h);
103     if (x<0) x=0;
104     if (y<0) y=0;
106     prefs_set_int_attribute (prefs_path, "x", x);
107     prefs_set_int_attribute (prefs_path, "y", y);
108     prefs_set_int_attribute (prefs_path, "w", w);
109     prefs_set_int_attribute (prefs_path, "h", h);
111     return FALSE; // which means, go ahead and destroy it
115 /**
116     These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left.
117     This flag is used to prevent passing keys from the dialog to canvas, so that the text editor
118     can handle keys like Esc and Ctrl+Z itself.
119  */
120 gboolean
121 text_view_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data)
123     GObject *dlg = (GObject *) data;
124     g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE));
125     return FALSE;
128 gboolean
129 text_view_focus_out (GtkWidget *w, GdkEventKey *event, gpointer data)
131     GObject *dlg = (GObject *) data;
132     g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE));
133     return FALSE;
137 void
138 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", 0);
149             y = prefs_get_int_attribute (prefs_path, "y", 0);
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 (x != 0 || y != 0) {
161             gtk_window_move ((GtkWindow *) dlg, x, y);
162         } else {
163             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
164         }
166         if (w && h)
167             gtk_window_resize ((GtkWindow *) dlg, w, h);
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 );
212             gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
213             g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
215             // Layout
216             {
217                 GtkWidget *f = gtk_frame_new (_("Layout"));
218                 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
219                 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
220                 gtk_container_add (GTK_CONTAINER (f), l_vb);
222                 {
223                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
224                     GtkWidget *group;
226                     // align left
227                     {
228                         // TODO - replace with Inkscape-specific call
229                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
230                         GtkWidget *b = group = gtk_radio_button_new (NULL);
231                         gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
232                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
233                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
234                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
235                         gtk_container_add (GTK_CONTAINER (b), px);
236                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
237                         g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
238                     }
240                     // align center
241                     {
242                         // TODO - replace with Inkscape-specific call
243                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
244                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
245                         /* TRANSLATORS: `Center' here is a verb. */
246                         gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
247                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
248                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
249                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
250                         gtk_container_add (GTK_CONTAINER (b), px);
251                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
252                         g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
253                     }
255                     // align right
256                     {
257                         // TODO - replace with Inkscape-specific call
258                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
259                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
260                         gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
261                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
262                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
263                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
264                         gtk_container_add (GTK_CONTAINER (b), px);
265                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
266                         g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
267                     }
269                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
270                 }
273                 {
274                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
275                     GtkWidget *group;
277                     // horizontal
278                     {
279                         GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
280                                                       INKSCAPE_STOCK_WRITING_MODE_LR );
281                         GtkWidget *b = group = gtk_radio_button_new (NULL);
282                         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
283                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
284                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
285                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
286                         gtk_container_add (GTK_CONTAINER (b), px);
287                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
288                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
289                     }
291                     // vertical
292                     {
293                         GtkWidget *px = sp_icon_new( Inkscape::ICON_SIZE_LARGE_TOOLBAR,
294                                                       INKSCAPE_STOCK_WRITING_MODE_TB );
295                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
296                         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
297                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
298                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
299                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
300                         gtk_container_add (GTK_CONTAINER (b), px);
301                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
302                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
303                     }
305                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
306                 }
308                 {
309                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
311                     l = gtk_label_new (_("Line spacing:"));
312                     gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
313                     gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
315                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
316                 }
318                 {
319                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
321                     GtkWidget *c = gtk_combo_new ();
322                     gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
323                     gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
324                     gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
325                     gtk_widget_set_size_request (c, 90, -1);
327                     { /* Setup strings */
328                         GList *sl = NULL;
329                         for (int i = 0; spacings[i]; i++) {
330                             sl = g_list_prepend (sl, (void *) spacings[i]);
331                         }
332                         sl = g_list_reverse (sl);
333                         gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
334                         g_list_free (sl);
335                     }
337                     g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry,
338                                        "changed",
339                                        (GCallback) sp_text_edit_dialog_line_spacing_changed,
340                                        dlg );
341                     gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
342                     g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
344                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
345                 }
346             }
348             /* Font preview */
349             GtkWidget *preview = sp_font_preview_new ();
350             gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
351             g_object_set_data (G_OBJECT (dlg), "preview", preview);
352         }
355         // Text tab
356         {
357             GtkWidget *l = gtk_label_new (_("Text"));
358             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
359             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
360             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
362             GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
363             gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
364                                              GTK_POLICY_AUTOMATIC,
365                                              GTK_POLICY_AUTOMATIC );
366             gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
367             gtk_widget_show (scroller);
369             GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
370             GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
371             gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
372 #ifdef WITH_GTKSPELL
373             GError *error = NULL;
374             char *errortext = NULL;
375             /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
376                language (either as 2nd arg of gtkspell_new_attach, or with explicit
377                gtkspell_set_language call in; see advanced.c example in gtkspell docs).
378                sp_text_edit_dialog_read_selection looks like a suitable place. */
379             if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
380                 g_print("gtkspell error: %s\n", error->message);
381                 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
382                                             "%s", error->message);
383                 g_error_free(error);
384             }
385 #endif
386             gtk_widget_set_size_request (txt, -1, 64);
387             gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
388             gtk_container_add (GTK_CONTAINER (scroller), txt);
389             gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
390             g_signal_connect ( G_OBJECT (tb), "changed",
391                                G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
392             g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
393             g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
394             g_object_set_data (G_OBJECT (dlg), "text", tb);
395             g_object_set_data (G_OBJECT (dlg), "textw", txt);
396         }
398         /* Buttons */
399         GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
400         gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
401         gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
403         {
404             GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
405             g_signal_connect ( G_OBJECT (b), "clicked",
406                                G_CALLBACK (sp_text_edit_dialog_set_default),
407                                dlg );
408             gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
409             g_object_set_data (G_OBJECT (dlg), "default", b);
410         }
412         {
413             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
414             g_signal_connect ( G_OBJECT (b), "clicked", 
415                                G_CALLBACK (sp_text_edit_dialog_close), dlg );
416             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
417         }
419         {
420             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
421             g_signal_connect ( G_OBJECT (b), "clicked", 
422                                G_CALLBACK (sp_text_edit_dialog_apply), dlg );
423             gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
424             g_object_set_data (G_OBJECT (dlg), "apply", b);
425         }
427         g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
428                            G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
429         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
430                            G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
431         g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
433         gtk_widget_show_all (dlg);
435         sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
436     }
438     gtk_window_present ((GtkWindow *) dlg);
440 } // end of sp_text_edit_dialog()
444 static void
445 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape, 
446                                        Inkscape::Selection *sel, 
447                                        guint flags, 
448                                        GtkWidget *dlg )
450     gboolean style, content;
452     style = 
453         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
454                     SP_OBJECT_STYLE_MODIFIED_FLAG  )) != 0 );
455     
456     content = 
457         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
458                     SP_TEXT_CONTENT_MODIFIED_FLAG  )) != 0 );
459     
460     sp_text_edit_dialog_read_selection (dlg, style, content);
462
466 static void
467 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape, 
468                                        Inkscape::Selection *sel, 
469                                        GtkWidget *dlg )
471     sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
474 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
476     sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
479 static void
480 sp_text_edit_dialog_update_object_text ( SPItem *text )
482         GtkTextBuffer *tb;
483         GtkTextIter start, end;
484         gchar *str;
486         tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
488         /* write text */
489         if (gtk_text_buffer_get_modified (tb)) {
490             gtk_text_buffer_get_bounds (tb, &start, &end);
491             str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
492             sp_te_set_repr_text_multiline (text, str);
493             g_free (str);
494             gtk_text_buffer_set_modified (tb, FALSE);
495         }
498 SPCSSAttr *
499 sp_get_text_dialog_style ()
501         GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
503         SPCSSAttr *css = sp_repr_css_attr_new ();
505         /* font */
506         font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
508                                 if ( font ) {
509                                         gchar c[256];
510                                         font->Family(c, 256);
511                                         sp_repr_css_set_property (css, "font-family", c);
512                                         
513                                         font->Attribute( "weight", c, 256);
514                                         sp_repr_css_set_property (css, "font-weight", c);
515                                         
516                                         font->Attribute("style", c, 256);
517                                         sp_repr_css_set_property (css, "font-style", c);
518                                         
519                                         font->Attribute("stretch", c, 256);
520                                         sp_repr_css_set_property (css, "font-stretch", c);
521                                         
522                                         font->Attribute("variant", c, 256);
523                                         sp_repr_css_set_property (css, "font-variant", c);
524                                         
525                                         Inkscape::CSSOStringStream os;
526                                         os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
527                                         sp_repr_css_set_property (css, "font-size", os.str().c_str());
528                                         
529                                         font->Unref();
530                                         font=NULL;
531                                 }
532                                 
533         /* Layout */
534         GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
535         
536         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
537             sp_repr_css_set_property (css, "text-anchor", "start");
538             sp_repr_css_set_property (css, "text-align", "start");
539         } else {
540             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), 
541                                                 "text_anchor_middle");
542             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
543                 sp_repr_css_set_property (css, "text-anchor", "middle");
544                 sp_repr_css_set_property (css, "text-align", "center");
545             } else {
546                 sp_repr_css_set_property (css, "text-anchor", "end");
547                 sp_repr_css_set_property (css, "text-align", "end");
548             }
549         }
551         b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
553         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
554             sp_repr_css_set_property (css, "writing-mode", "lr");
555         } else {
556             sp_repr_css_set_property (css, "writing-mode", "tb");
557         }
559         // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
560         // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
561         GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
562         const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
563         sp_repr_css_set_property (css, "line-height", sstr);
565         return css;
569 static void
570 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
572     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
574     SPCSSAttr *css = sp_get_text_dialog_style ();
576     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
577     sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
578     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
580     sp_repr_css_attr_unref (css);
582     gtk_widget_set_sensitive (def, FALSE);
587 static void
588 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
590     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
592     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
593     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
594     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
596     unsigned items = 0;
597     const GSList *item_list = SP_DT_SELECTION(desktop)->itemList();
599     SPCSSAttr *css = sp_get_text_dialog_style ();
601     sp_desktop_set_style(desktop, css, true);
603     for (; item_list != NULL; item_list = item_list->next) {
604         // apply style to the reprs of all text objects in the selection
605         if (SP_IS_TEXT (item_list->data)) {
607             // backwards compatibility:
608             SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
610             ++items;
611         }
612         else if (SP_IS_FLOWTEXT (item_list->data))
613             // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
614             ++items;
615     }
617     if (items == 0) {
618         // no text objects; apply style to prefs for new objects
619         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
620         gtk_widget_set_sensitive (def, FALSE);
621     } else if (items == 1) {
622         /* exactly one text object; now set its text, too */
623         SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem();
624         if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
625             sp_text_edit_dialog_update_object_text (item);
626         }
627     }
629     // complete the transaction
630     sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP));
632     gtk_widget_set_sensitive (apply, FALSE);
634     sp_repr_css_attr_unref (css);
636     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
641 static void
642 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
644     gtk_widget_destroy (GTK_WIDGET (dlg));
647 static void
648 sp_text_edit_dialog_read_selection ( GtkWidget *dlg,
649                                      gboolean dostyle,
650                                      gboolean docontent )
652     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
653         return;
655     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
657     GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
658     GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
659     GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
660     GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
661     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
662     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
664     GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
666     SPItem *text = sp_ted_get_selected_text_item ();
668     Inkscape::XML::Node *repr;
669     if (text)
670     {
671         guint items = sp_ted_get_selected_text_count ();
672         if (items == 1) {
673             gtk_widget_set_sensitive (textw, TRUE);
674         } else {
675             gtk_widget_set_sensitive (textw, FALSE);
676         }
677         gtk_widget_set_sensitive (apply, FALSE);
678         gtk_widget_set_sensitive (def, TRUE);
680         if (docontent) {
681             gchar *str;
682             str = sp_te_get_string_multiline (text);
684             if (str) {
685                 int pos;
686                 pos = 0;
688                 if (items == 1) {
689                     gtk_text_buffer_set_text (tb, str, strlen (str));
690                     gtk_text_buffer_set_modified (tb, FALSE);
691                 }
692                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
693                 g_free (str);
695             } else {
696                 gtk_text_buffer_set_text (tb, "", 0);
697                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
698             }
699         } // end of if (docontent)
700         repr = SP_OBJECT_REPR (text);
702     } else {
703         gtk_widget_set_sensitive (textw, FALSE);
704         gtk_widget_set_sensitive (apply, FALSE);
705         gtk_widget_set_sensitive (def, FALSE);
706     }
708     if (dostyle) {
710         // create temporary style
711         SPStyle *query = sp_style_new ();
712         // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
713         int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); 
714         int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); 
715         int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); 
717         // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
718         if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
719             repr = inkscape_get_repr (INKSCAPE, "tools.text");
720             if (repr) {
721                 gtk_widget_set_sensitive (notebook, TRUE);
722                 sp_style_read_from_repr (query, repr);
723             } else {
724                 gtk_widget_set_sensitive (notebook, FALSE);
725             }
726         }
728         // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
729         font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
730         if (font) {
731             // the font is oversized, so we need to pass the true size separately
732             sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
733             sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
734                                                 font->Unref();
735                                                 font=NULL;
736         }
738         GtkWidget *b;
739         if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
740             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
741         } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
742             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
743         } else {
744             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
745         }
746         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
747        
748         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
749             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
750         } else {
751             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
752         }
753         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
755         GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
756         double height;
757         if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
758         else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
759             height = query->line_height.value;
760         else height = query->line_height.computed;
761         gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
762         gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
763         g_free(sstr);
765         g_free (query);
766     }
768     g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
772 static void
773 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
775     GtkWidget *textw, *preview, *apply, *def;
776     GtkTextIter start, end;
777     gchar *str;
779     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
780         return;
782     SPItem *text = sp_ted_get_selected_text_item ();
784     textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
785     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
786     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
787     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
789     gtk_text_buffer_get_bounds (tb, &start, &end);
790     str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
792     if (str && *str) {
793         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
794     } else {
795         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
796     }
797     g_free (str);
799     if (text) {
800         gtk_widget_set_sensitive (apply, TRUE);
801     }
802     gtk_widget_set_sensitive (def, TRUE);
804 } // end of sp_text_edit_dialog_text_changed()
808 static void
809 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel,
810                                    font_instance *font,
811                                    GtkWidget *dlg )
813     GtkWidget *preview, *apply, *def;
815     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
816         return;
818     SPItem *text = sp_ted_get_selected_text_item ();
820     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
821     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
822     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
824     sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
826     if (text) {
827         gtk_widget_set_sensitive (apply, TRUE);
828     }
829     gtk_widget_set_sensitive (def, TRUE);
831 } // end of sp_text_edit_dialog_font_changed()
835 static void
836 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
838     GtkWidget *apply, *def;
840     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
841         return;
843     SPItem *text = sp_ted_get_selected_text_item ();
845     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
846     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
848     if (text) {
849         gtk_widget_set_sensitive (apply, TRUE);
850     }
851     gtk_widget_set_sensitive (def, TRUE);
856 static void
857 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
859     GtkWidget *apply, *def;
861     if (g_object_get_data ((GObject *) dlg, "blocked"))
862         return;
864     SPItem *text = sp_ted_get_selected_text_item ();
866     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
867     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
869     if (text) {
870         gtk_widget_set_sensitive (apply, TRUE);
871     }
872     gtk_widget_set_sensitive (def, TRUE);
877 static SPItem *
878 sp_ted_get_selected_text_item (void)
880     if (!SP_ACTIVE_DESKTOP)
881         return NULL;
883     for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
884          item != NULL;
885          item = item->next)
886     {
887         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
888             return SP_ITEM (item->data);
889     }
891     return NULL;
896 static unsigned
897 sp_ted_get_selected_text_count (void)
899     if (!SP_ACTIVE_DESKTOP)
900         return 0;
902     unsigned int items = 0;
904     for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
905          item != NULL;
906          item = item->next)
907     {
908         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
909             ++items;
910     }
912     return items;
915 /*
916   Local Variables:
917   mode:c++
918   c-file-style:"stroustrup"
919   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
920   indent-tabs-mode:nil
921   fill-column:99
922   End:
923 */
924 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :