Code

moving trunk for module inkscape
[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     prefs_set_int_attribute (prefs_path, "x", x);
104     prefs_set_int_attribute (prefs_path, "y", y);
105     prefs_set_int_attribute (prefs_path, "w", w);
106     prefs_set_int_attribute (prefs_path, "h", h);
108     return FALSE; // which means, go ahead and destroy it
112 /**
113     These callbacks set the eatkeys flag when the text editor is entered and cancel it when it's left.
114     This flag is used to prevent passing keys from the dialog to canvas, so that the text editor
115     can handle keys like Esc and Ctrl+Z itself.
116  */
117 gboolean
118 text_view_focus_in (GtkWidget *w, GdkEventKey *event, gpointer data)
120     GObject *dlg = (GObject *) data;
121     g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (TRUE));
122     return FALSE;
125 gboolean
126 text_view_focus_out (GtkWidget *w, GdkEventKey *event, gpointer data)
128     GObject *dlg = (GObject *) data;
129     g_object_set_data (dlg, "eatkeys", GINT_TO_POINTER (FALSE));
130     return FALSE;
134 void
135 sp_text_edit_dialog (void)
138     if (!dlg) {
139     
140         gchar title[500];
141         sp_ui_dialog_title_string (Inkscape::Verb::get(SP_VERB_DIALOG_TEXT), title);
143         dlg = sp_window_new (title, TRUE);
144         if (x == -1000 || y == -1000) {
145             x = prefs_get_int_attribute (prefs_path, "x", 0);
146             y = prefs_get_int_attribute (prefs_path, "y", 0);
147         }
148         
149         if (w ==0 || h == 0) {
150             w = prefs_get_int_attribute (prefs_path, "w", 0);
151             h = prefs_get_int_attribute (prefs_path, "h", 0);
152         }
153         
154         if (x != 0 || y != 0) {
155             gtk_window_move ((GtkWindow *) dlg, x, y);
156         } else {
157             gtk_window_set_position(GTK_WINDOW(dlg), GTK_WIN_POS_CENTER);
158         }
159         
160         if (w && h) 
161             gtk_window_resize ((GtkWindow *) dlg, w, h);
162         
163         sp_transientize (dlg);
164         wd.win = dlg;
165         wd.stop = 0;
166         g_signal_connect ( G_OBJECT (INKSCAPE), "activate_desktop", G_CALLBACK (sp_transientize_callback), &wd );
167                            
168         gtk_signal_connect ( GTK_OBJECT (dlg), "event", GTK_SIGNAL_FUNC (sp_dialog_event_handler), dlg );
169                              
170         gtk_signal_connect ( GTK_OBJECT (dlg), "destroy", G_CALLBACK (sp_text_edit_dialog_destroy), dlg );
171         gtk_signal_connect ( GTK_OBJECT (dlg), "delete_event", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
172         g_signal_connect ( G_OBJECT (INKSCAPE), "shut_down", G_CALLBACK (sp_text_edit_dialog_delete), dlg );
173                            
174         g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_hide", G_CALLBACK (sp_dialog_hide), dlg );
175         g_signal_connect ( G_OBJECT (INKSCAPE), "dialogs_unhide", G_CALLBACK (sp_dialog_unhide), dlg );
177         gtk_window_set_policy (GTK_WINDOW (dlg), TRUE, TRUE, FALSE);
179         GtkTooltips *tt = gtk_tooltips_new();
181         // box containing the notebook and the bottom buttons
182         GtkWidget *mainvb = gtk_vbox_new (FALSE, 0);
183         gtk_container_add (GTK_CONTAINER (dlg), mainvb);
185         // notebook
186         GtkWidget *nb = gtk_notebook_new ();
187         gtk_box_pack_start (GTK_BOX (mainvb), nb, TRUE, TRUE, 0);
188         g_object_set_data (G_OBJECT (dlg), "notebook", nb);
192         // Font tab
193         {
194             GtkWidget *l = gtk_label_new (_("Font"));
195             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
196             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
197             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
199             /* HBox containing font selection and layout */
200             GtkWidget *hb = gtk_hbox_new (FALSE, 0);
201             gtk_box_pack_start (GTK_BOX (vb), hb, TRUE, TRUE, 0);
203             // font and style selector
204             GtkWidget *fontsel = sp_font_selector_new ();
205             g_signal_connect ( G_OBJECT (fontsel), "font_set", G_CALLBACK (sp_text_edit_dialog_font_changed), dlg );
206             gtk_box_pack_start (GTK_BOX (hb), fontsel, TRUE, TRUE, 0);
207             g_object_set_data (G_OBJECT (dlg), "fontsel", fontsel);
209             // Layout
210             {
211                 GtkWidget *f = gtk_frame_new (_("Layout"));
212                 gtk_box_pack_start (GTK_BOX (hb), f, FALSE, FALSE, 4);
213                 GtkWidget *l_vb = gtk_vbox_new (FALSE, VB_MARGIN);
214                 gtk_container_add (GTK_CONTAINER (f), l_vb);
216                 {
217                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
218                     GtkWidget *group;
220                     // align left
221                     {
222                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_LEFT, GTK_ICON_SIZE_LARGE_TOOLBAR );
223                         GtkWidget *b = group = gtk_radio_button_new (NULL);
224                         gtk_tooltips_set_tip (tt, b, _("Align lines left"), NULL);
225                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
226                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg);
227                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE );
228                         gtk_container_add (GTK_CONTAINER (b), px);
229                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
230                         g_object_set_data (G_OBJECT (dlg), "text_anchor_start", b);
231                     }
233                     // align center
234                     {
235                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_CENTER, GTK_ICON_SIZE_LARGE_TOOLBAR );
236                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
237                         /* TRANSLATORS: `Center' here is a verb. */
238                         gtk_tooltips_set_tip (tt, b, _("Center lines"), NULL);
239                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
240                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
241                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
242                         gtk_container_add (GTK_CONTAINER (b), px);
243                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
244                         g_object_set_data (G_OBJECT (dlg), "text_anchor_middle", b);
245                     }
247                     // align right
248                     {
249                         GtkWidget *px = gtk_image_new_from_stock ( GTK_STOCK_JUSTIFY_RIGHT, GTK_ICON_SIZE_LARGE_TOOLBAR );
250                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
251                         gtk_tooltips_set_tip (tt, b, _("Align lines right"), NULL);
252                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
253                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
254                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
255                         gtk_container_add (GTK_CONTAINER (b), px);
256                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
257                         g_object_set_data (G_OBJECT (dlg), "text_anchor_end", b);
258                     }
260                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
261                 }
264                 {
265                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
266                     GtkWidget *group;
268                     // horizontal
269                     {
270                         GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
271                                                       INKSCAPE_STOCK_WRITING_MODE_LR );
272                         GtkWidget *b = group = gtk_radio_button_new (NULL);
273                         gtk_tooltips_set_tip (tt, b, _("Horizontal text"), NULL);
274                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
275                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
276                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
277                         gtk_container_add (GTK_CONTAINER (b), px);
278                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
279                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR, b);
280                     }
282                     // vertical
283                     {
284                         GtkWidget *px = sp_icon_new( GTK_ICON_SIZE_LARGE_TOOLBAR,
285                                                       INKSCAPE_STOCK_WRITING_MODE_TB );
286                         GtkWidget *b = gtk_radio_button_new (gtk_radio_button_group (GTK_RADIO_BUTTON (group)));
287                         gtk_tooltips_set_tip (tt, b, _("Vertical text"), NULL);
288                         gtk_button_set_relief (GTK_BUTTON (b), GTK_RELIEF_NONE);
289                         g_signal_connect ( G_OBJECT (b), "toggled", G_CALLBACK (sp_text_edit_dialog_any_toggled), dlg );
290                         gtk_toggle_button_set_mode (GTK_TOGGLE_BUTTON (b), FALSE);
291                         gtk_container_add (GTK_CONTAINER (b), px);
292                         gtk_box_pack_start (GTK_BOX (row), b, FALSE, FALSE, 0);
293                         g_object_set_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB, b);
294                     }
296                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
297                 }
299                 {
300                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
302                     l = gtk_label_new (_("Line spacing:"));
303                     gtk_misc_set_alignment (GTK_MISC (l), 1.0, 0.5);
304                     gtk_box_pack_start (GTK_BOX (row), l, FALSE, FALSE, VB_MARGIN);
306                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, 0);
307                 }
309                 {
310                     GtkWidget *row = gtk_hbox_new (FALSE, VB_MARGIN);
312                     GtkWidget *c = gtk_combo_new ();
313                     gtk_combo_set_value_in_list ((GtkCombo *) c, FALSE, FALSE);
314                     gtk_combo_set_use_arrows ((GtkCombo *) c, TRUE);
315                     gtk_combo_set_use_arrows_always ((GtkCombo *) c, TRUE);
316                     gtk_widget_set_size_request (c, 90, -1);
318                     { /* Setup strings */
319                         GList *sl = NULL;
320                         for (int i = 0; spacings[i]; i++) {
321                             sl = g_list_prepend (sl, (void *) spacings[i]);
322                         }
323                         sl = g_list_reverse (sl);
324                         gtk_combo_set_popdown_strings ((GtkCombo *) c, sl);
325                         g_list_free (sl);
326                     }
328                     g_signal_connect ( (GObject *) ((GtkCombo *) c)->entry, 
329                                        "changed", 
330                                        (GCallback) sp_text_edit_dialog_line_spacing_changed,
331                                        dlg );
332                     gtk_box_pack_start (GTK_BOX (row), c, FALSE, FALSE, VB_MARGIN);
333                     g_object_set_data (G_OBJECT (dlg), "line_spacing", c);
335                     gtk_box_pack_start (GTK_BOX (l_vb), row, FALSE, FALSE, VB_MARGIN);
336                 }
337             }
339             /* Font preview */
340             GtkWidget *preview = sp_font_preview_new ();
341             gtk_box_pack_start (GTK_BOX (vb), preview, TRUE, TRUE, 4);
342             g_object_set_data (G_OBJECT (dlg), "preview", preview);
343         }
346         // Text tab
347         {
348             GtkWidget *l = gtk_label_new (_("Text"));
349             GtkWidget *vb = gtk_vbox_new (FALSE, VB_MARGIN);
350             gtk_container_set_border_width (GTK_CONTAINER (vb), VB_MARGIN);
351             gtk_notebook_append_page (GTK_NOTEBOOK (nb), vb, l);
353             GtkWidget *scroller = gtk_scrolled_window_new ( NULL, NULL );
354             gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scroller),
355                                              GTK_POLICY_AUTOMATIC,
356                                              GTK_POLICY_AUTOMATIC );
357             gtk_scrolled_window_set_shadow_type ( GTK_SCROLLED_WINDOW(scroller), GTK_SHADOW_IN );
358             gtk_widget_show (scroller);
360             GtkTextBuffer *tb = gtk_text_buffer_new (NULL);
361             GtkWidget *txt = gtk_text_view_new_with_buffer (tb);
362             gtk_text_view_set_wrap_mode ((GtkTextView *) txt, GTK_WRAP_WORD);
363 #ifdef WITH_GTKSPELL
364             GError *error = NULL;
365             char *errortext = NULL;
366             /* todo: Use computed xml:lang attribute of relevant element, if present, to specify the
367                language (either as 2nd arg of gtkspell_new_attach, or with explicit
368                gtkspell_set_language call in; see advanced.c example in gtkspell docs).
369                sp_text_edit_dialog_read_selection looks like a suitable place. */
370             if (gtkspell_new_attach(GTK_TEXT_VIEW(txt), NULL, &error) == NULL) {
371                 g_print("gtkspell error: %s\n", error->message);
372                 errortext = g_strdup_printf("GtkSpell was unable to initialize.\n"
373                                             "%s", error->message);
374                 g_error_free(error);
375             }
376 #endif
377             gtk_widget_set_size_request (txt, -1, 64);
378             gtk_text_view_set_editable (GTK_TEXT_VIEW (txt), TRUE);
379             gtk_container_add (GTK_CONTAINER (scroller), txt);
380             gtk_box_pack_start (GTK_BOX (vb), scroller, TRUE, TRUE, 0);
381             g_signal_connect ( G_OBJECT (tb), "changed", 
382                                G_CALLBACK (sp_text_edit_dialog_text_changed), dlg );
383             g_signal_connect (G_OBJECT (txt), "focus-in-event", G_CALLBACK (text_view_focus_in), dlg);
384             g_signal_connect (G_OBJECT (txt), "focus-out-event", G_CALLBACK (text_view_focus_out), dlg);
385             g_object_set_data (G_OBJECT (dlg), "text", tb);
386             g_object_set_data (G_OBJECT (dlg), "textw", txt);
387         }
389         /* Buttons */
390         GtkWidget *hb = gtk_hbox_new (FALSE, VB_MARGIN);
391         gtk_container_set_border_width (GTK_CONTAINER (hb), 4);
392         gtk_box_pack_start (GTK_BOX (mainvb), hb, FALSE, FALSE, 0);
394         {
395             GtkWidget *b = gtk_button_new_with_label (_("Set as default"));
396             g_signal_connect ( G_OBJECT (b), "clicked", 
397                                G_CALLBACK (sp_text_edit_dialog_set_default), 
398                                dlg );
399             gtk_box_pack_start (GTK_BOX (hb), b, FALSE, FALSE, 0);
400             g_object_set_data (G_OBJECT (dlg), "default", b);
401         }
403         {
404             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_CLOSE);
405             g_signal_connect ( G_OBJECT (b), "clicked", 
406                                G_CALLBACK (sp_text_edit_dialog_close), dlg );
407             gtk_box_pack_end (GTK_BOX (hb), b, FALSE, FALSE, 0);
408         }
410         {
411             GtkWidget *b = gtk_button_new_from_stock (GTK_STOCK_APPLY);
412             g_signal_connect ( G_OBJECT (b), "clicked", 
413                                G_CALLBACK (sp_text_edit_dialog_apply), dlg );
414             gtk_box_pack_end ( GTK_BOX (hb), b, FALSE, FALSE, 0 );
415             g_object_set_data (G_OBJECT (dlg), "apply", b);
416         }
418         g_signal_connect ( G_OBJECT (INKSCAPE), "modify_selection", 
419                            G_CALLBACK (sp_text_edit_dialog_selection_modified), dlg);
420         g_signal_connect ( G_OBJECT (INKSCAPE), "change_selection", 
421                            G_CALLBACK (sp_text_edit_dialog_selection_changed), dlg);
422         g_signal_connect (INKSCAPE, "change_subselection", G_CALLBACK (sp_text_edit_dialog_subselection_changed), dlg);
424         gtk_widget_show_all (dlg);
426         sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
427     }
429     gtk_window_present ((GtkWindow *) dlg);
431 } // end of sp_text_edit_dialog()
435 static void
436 sp_text_edit_dialog_selection_modified ( Inkscape::Application *inkscape, 
437                                        Inkscape::Selection *sel, 
438                                        guint flags, 
439                                        GtkWidget *dlg )
441     gboolean style, content;
443     style = 
444         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
445                     SP_OBJECT_STYLE_MODIFIED_FLAG  )) != 0 );
446     
447     content = 
448         ((flags & ( SP_OBJECT_CHILD_MODIFIED_FLAG | 
449                     SP_TEXT_CONTENT_MODIFIED_FLAG  )) != 0 );
450     
451     sp_text_edit_dialog_read_selection (dlg, style, content);
453
457 static void
458 sp_text_edit_dialog_selection_changed ( Inkscape::Application *inkscape, 
459                                        Inkscape::Selection *sel, 
460                                        GtkWidget *dlg )
462     sp_text_edit_dialog_read_selection (dlg, TRUE, TRUE);
465 static void sp_text_edit_dialog_subselection_changed ( Inkscape::Application *inkscape, SPDesktop *desktop, GtkWidget *dlg )
467     sp_text_edit_dialog_read_selection (dlg, TRUE, FALSE);
470 static void
471 sp_text_edit_dialog_update_object_text ( SPItem *text )
473         GtkTextBuffer *tb;
474         GtkTextIter start, end;
475         gchar *str;
477         tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
479         /* write text */
480         if (gtk_text_buffer_get_modified (tb)) {
481             gtk_text_buffer_get_bounds (tb, &start, &end);
482             str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
483             sp_te_set_repr_text_multiline (text, str);
484             g_free (str);    
485             gtk_text_buffer_set_modified (tb, FALSE);
486         }
489 SPCSSAttr *
490 sp_get_text_dialog_style ()
492         GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
494         SPCSSAttr *css = sp_repr_css_attr_new ();
496         /* font */
497         font_instance *font = sp_font_selector_get_font (SP_FONT_SELECTOR (fontsel));
499                                 if ( font ) {
500                                         gchar c[256];
501                                         font->Family(c, 256);
502                                         sp_repr_css_set_property (css, "font-family", c);
503                                         
504                                         font->Attribute( "weight", c, 256);
505                                         sp_repr_css_set_property (css, "font-weight", c);
506                                         
507                                         font->Attribute("style", c, 256);
508                                         sp_repr_css_set_property (css, "font-style", c);
509                                         
510                                         font->Attribute("stretch", c, 256);
511                                         sp_repr_css_set_property (css, "font-stretch", c);
512                                         
513                                         font->Attribute("variant", c, 256);
514                                         sp_repr_css_set_property (css, "font-variant", c);
515                                         
516                                         Inkscape::CSSOStringStream os;
517                                         os << sp_font_selector_get_size (SP_FONT_SELECTOR (fontsel));
518                                         sp_repr_css_set_property (css, "font-size", os.str().c_str());
519                                         
520                                         font->Unref();
521                                         font=NULL;
522                                 }
523                                 
524         /* Layout */
525         GtkWidget *b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "text_anchor_start");
526         
527         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
528             sp_repr_css_set_property (css, "text-anchor", "start");
529             sp_repr_css_set_property (css, "text-align", "start");
530         } else {
531             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), 
532                                                 "text_anchor_middle");
533             if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
534                 sp_repr_css_set_property (css, "text-anchor", "middle");
535                 sp_repr_css_set_property (css, "text-align", "center");
536             } else {
537                 sp_repr_css_set_property (css, "text-anchor", "end");
538                 sp_repr_css_set_property (css, "text-align", "end");
539             }
540         }
541         
542         b = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
543         
544         if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (b))) {
545             sp_repr_css_set_property (css, "writing-mode", "lr");
546         } else {
547             sp_repr_css_set_property (css, "writing-mode", "tb");
548         }
550         // Note that CSS 1.1 does not support line-height; we set it for consistency, but also set
551         // sodipodi:linespacing for backwards compatibility; in 1.2 we use line-height for flowtext
552         GtkWidget *combo = (GtkWidget*)g_object_get_data ((GObject *) dlg, "line_spacing");
553         const char *sstr = gtk_entry_get_text ((GtkEntry *) ((GtkCombo *) (combo))->entry);
554         sp_repr_css_set_property (css, "line-height", sstr);
556         return css;
560 static void
561 sp_text_edit_dialog_set_default (GtkButton *button, GtkWidget *dlg)
563     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
565     SPCSSAttr *css = sp_get_text_dialog_style ();
567     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
568     sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
569     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
571     sp_repr_css_attr_unref (css);
573     gtk_widget_set_sensitive (def, FALSE);
578 static void
579 sp_text_edit_dialog_apply (GtkButton *button, GtkWidget *dlg)
581     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
583     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
584     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
585     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
587     unsigned items = 0;
588     const GSList *item_list = SP_DT_SELECTION(desktop)->itemList();
590     SPCSSAttr *css = sp_get_text_dialog_style ();
592     sp_desktop_set_style(desktop, css, true);
594     for (; item_list != NULL; item_list = item_list->next) { 
595         // apply style to the reprs of all text objects in the selection
596         if (SP_IS_TEXT (item_list->data)) {
598             // backwards compatibility:
599             SP_OBJECT_REPR(item_list->data)->setAttribute("sodipodi:linespacing", sp_repr_css_property (css, "line-height", NULL));
601             ++items;
602         }
603         else if (SP_IS_FLOWTEXT (item_list->data))
604             // no need to set sodipodi:linespacing, because Inkscape never supported it on flowtext
605             ++items;
606     }
607     
608     if (items == 0) { 
609         // no text objects; apply style to prefs for new objects
610         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
611         gtk_widget_set_sensitive (def, FALSE);
612     } else if (items == 1) {
613         /* exactly one text object; now set its text, too */
614         SPItem *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->singleItem();
615         if (SP_IS_TEXT (item) || SP_IS_FLOWTEXT(item)) {
616             sp_text_edit_dialog_update_object_text (item);
617         }
618     } 
620     // complete the transaction
621     sp_document_done (SP_DT_DOCUMENT (SP_ACTIVE_DESKTOP));
623     gtk_widget_set_sensitive (apply, FALSE);
625     sp_repr_css_attr_unref (css);
627     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (FALSE));
632 static void
633 sp_text_edit_dialog_close (GtkButton *button, GtkWidget *dlg)
635     gtk_widget_destroy (GTK_WIDGET (dlg));
638 static void
639 sp_text_edit_dialog_read_selection ( GtkWidget *dlg, 
640                                      gboolean dostyle, 
641                                      gboolean docontent )
643     if (g_object_get_data (G_OBJECT (dlg), "blocked")) 
644         return;
645     
646     g_object_set_data (G_OBJECT (dlg), "blocked", GINT_TO_POINTER (TRUE));
648     GtkWidget *notebook = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "notebook");
649     GtkWidget *textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
650     GtkWidget *fontsel = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "fontsel");
651     GtkWidget *preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
652     GtkWidget *apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
653     GtkWidget *def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
655     GtkTextBuffer *tb = (GtkTextBuffer*)g_object_get_data (G_OBJECT (dlg), "text");
657     SPItem *text = sp_ted_get_selected_text_item ();
659     Inkscape::XML::Node *repr;
660     if (text)
661     {
662         guint items = sp_ted_get_selected_text_count ();
663         if (items == 1) {
664             gtk_widget_set_sensitive (textw, TRUE);
665         } else {
666             gtk_widget_set_sensitive (textw, FALSE);
667         }
668         gtk_widget_set_sensitive (apply, FALSE);
669         gtk_widget_set_sensitive (def, TRUE);
671         if (docontent) {
672             gchar *str;
673             str = sp_te_get_string_multiline (text);
675             if (str) {
676                 int pos;
677                 pos = 0;
679                 if (items == 1) {
680                     gtk_text_buffer_set_text (tb, str, strlen (str));
681                     gtk_text_buffer_set_modified (tb, FALSE);
682                 }
683                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
684                 g_free (str);
686             } else {
687                 gtk_text_buffer_set_text (tb, "", 0);
688                 sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
689             }
690         } // end of if (docontent)
691         repr = SP_OBJECT_REPR (text);
692         
693     } else {
694         gtk_widget_set_sensitive (textw, FALSE);
695         gtk_widget_set_sensitive (apply, FALSE);
696         gtk_widget_set_sensitive (def, FALSE);
697     }
699     if (dostyle) {
701         // create temporary style
702         SPStyle *query = sp_style_new ();
703         // query style from desktop into it. This returns a result flag and fills query with the style of subselection, if any, or selection
704         int result_family = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY); 
705         int result_style = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE); 
706         int result_numbers = sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS); 
708         // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
709         if (result_family == QUERY_STYLE_NOTHING || result_style == QUERY_STYLE_NOTHING || result_numbers == QUERY_STYLE_NOTHING) {
710             repr = inkscape_get_repr (INKSCAPE, "tools.text");
711             if (repr) {
712                 gtk_widget_set_sensitive (notebook, TRUE);
713                 sp_style_read_from_repr (query, repr);
714             } else {
715                 gtk_widget_set_sensitive (notebook, FALSE);
716             }
717         }
719         // FIXME: process result_family/style == QUERY_STYLE_MULTIPLE_DIFFERENT by showing "Many" in the lists
720         font_instance *font = (font_factory::Default())->Face ( query->text->font_family.value, font_style_to_pos(*query) );
721         if (font) {
722             // the font is oversized, so we need to pass the true size separately
723             sp_font_selector_set_font (SP_FONT_SELECTOR (fontsel), font, query->font_size.computed);
724             sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fontsel));
725                                                 font->Unref();
726                                                 font=NULL;
727         }
729         GtkWidget *b;
730         if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START) {
731             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_start" );
732         } else if (query->text_anchor.computed == SP_CSS_TEXT_ANCHOR_MIDDLE) {
733             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_middle" );
734         } else {
735             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "text_anchor_end" );
736         }
737         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
738        
739         if (query->writing_mode.computed == SP_CSS_WRITING_MODE_LR_TB) {
740             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_LR );
741         } else {
742             b = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), INKSCAPE_STOCK_WRITING_MODE_TB );
743         }
744         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (b), TRUE);
746         GtkWidget *combo = (GtkWidget*)g_object_get_data ( G_OBJECT (dlg), "line_spacing" );
747         double height;
748         if (query->line_height.normal) height = Inkscape::Text::Layout::LINE_HEIGHT_NORMAL;
749         else if (query->line_height.unit == SP_CSS_UNIT_PERCENT)
750             height = query->line_height.value;
751         else height = query->line_height.computed;
752         gchar *sstr = g_strdup_printf ("%d%%", (int) floor(height * 100 + 0.5));
753         gtk_entry_set_text ((GtkEntry *) ((GtkCombo *) (combo))->entry, sstr);
754         g_free(sstr);
756         g_free (query);
757     }
759     g_object_set_data (G_OBJECT (dlg), "blocked", NULL);
763 static void
764 sp_text_edit_dialog_text_changed (GtkTextBuffer *tb, GtkWidget *dlg)
766     GtkWidget *textw, *preview, *apply, *def;
767     GtkTextIter start, end;
768     gchar *str;
770     if (g_object_get_data (G_OBJECT (dlg), "blocked")) 
771         return;
773     SPItem *text = sp_ted_get_selected_text_item ();
775     textw = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "textw");
776     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
777     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
778     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
780     gtk_text_buffer_get_bounds (tb, &start, &end);
781     str = gtk_text_buffer_get_text (tb, &start, &end, TRUE);
783     if (str && *str) {
784         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), str);
785     } else {
786         sp_font_preview_set_phrase (SP_FONT_PREVIEW (preview), NULL);
787     }
788     g_free (str);
790     if (text) {
791         gtk_widget_set_sensitive (apply, TRUE);
792     }
793     gtk_widget_set_sensitive (def, TRUE);
795 } // end of sp_text_edit_dialog_text_changed()
799 static void
800 sp_text_edit_dialog_font_changed ( SPFontSelector *fsel, 
801                                    font_instance *font, 
802                                    GtkWidget *dlg )
804     GtkWidget *preview, *apply, *def;
806     if (g_object_get_data (G_OBJECT (dlg), "blocked")) 
807         return;
809     SPItem *text = sp_ted_get_selected_text_item ();
811     preview = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "preview");
812     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
813     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
815     sp_font_preview_set_font (SP_FONT_PREVIEW (preview), font, SP_FONT_SELECTOR(fsel));
817     if (text) {
818         gtk_widget_set_sensitive (apply, TRUE);
819     }
820     gtk_widget_set_sensitive (def, TRUE);
822 } // end of sp_text_edit_dialog_font_changed()
826 static void
827 sp_text_edit_dialog_any_toggled (GtkToggleButton *tb, GtkWidget *dlg)
829     GtkWidget *apply, *def;
831     if (g_object_get_data (G_OBJECT (dlg), "blocked"))
832         return;
834     SPItem *text = sp_ted_get_selected_text_item ();
836     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
837     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
839     if (text) {
840         gtk_widget_set_sensitive (apply, TRUE);
841     }
842     gtk_widget_set_sensitive (def, TRUE);
847 static void
848 sp_text_edit_dialog_line_spacing_changed (GtkEditable *editable, GtkWidget *dlg)
850     GtkWidget *apply, *def;
852     if (g_object_get_data ((GObject *) dlg, "blocked")) 
853         return;
855     SPItem *text = sp_ted_get_selected_text_item ();
857     apply = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "apply");
858     def = (GtkWidget*)g_object_get_data (G_OBJECT (dlg), "default");
860     if (text) {
861         gtk_widget_set_sensitive (apply, TRUE);
862     }
863     gtk_widget_set_sensitive (def, TRUE);
868 static SPItem *
869 sp_ted_get_selected_text_item (void)
871     if (!SP_ACTIVE_DESKTOP)
872         return NULL;
874     for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
875          item != NULL;
876          item = item->next)
877     {
878         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
879             return SP_ITEM (item->data);
880     }
882     return NULL;
887 static unsigned
888 sp_ted_get_selected_text_count (void)
890     if (!SP_ACTIVE_DESKTOP) 
891         return 0;
893     unsigned int items = 0;
895     for (const GSList *item = SP_DT_SELECTION(SP_ACTIVE_DESKTOP)->itemList();
896          item != NULL;
897          item = item->next)
898     {
899         if (SP_IS_TEXT(item->data) || SP_IS_FLOWTEXT(item->data))
900             ++items;
901     }
903     return items;
906 /*
907   Local Variables:
908   mode:c++
909   c-file-style:"stroustrup"
910   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
911   indent-tabs-mode:nil
912   fill-column:99
913   End:
914 */
915 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :