Code

* on-canvas clip and mask editing :) in the object menu you can find how to edit...
[inkscape.git] / src / widgets / toolbox.cpp
index 85001866295994233b09c9fdf26607cda7b68afb..fcf3e0ce6bd874917ee2b0730179a33b326adf6e 100644 (file)
@@ -27,6 +27,9 @@
 # include "config.h"
 #endif
 
+#include <cstring>
+#include <string>
+
 #include <gtkmm.h>
 #include <gtk/gtk.h>
 #include <iostream>
@@ -37,6 +40,7 @@
 #include "widgets/spw-utilities.h"
 #include "widgets/spinbutton-events.h"
 #include "dialogs/text-edit.h"
+#include "dialogs/dialog-events.h"
 
 #include "ui/widget/style-swatch.h"
 
@@ -95,6 +99,8 @@
 #include "ege-select-one-action.h"
 #include "helper/unit-tracker.h"
 
+#include "svg/css-ostringstream.h"
+
 using Inkscape::UnitTracker;
 
 typedef void (*SetupFunction)(GtkWidget *toolbox, SPDesktop *desktop);
@@ -244,6 +250,7 @@ static gchar const * ui_descr =
         "    <toolitem action='StrokeToPath' />"
         "    <separator />"
         "    <toolitem action='NodesShowHandlesAction' />"
+        "    <toolitem action='NodesShowHelperpath' />"
         "    <separator />"
         "    <toolitem action='EditNextLPEParameterAction' />"
         "    <separator />"
@@ -501,7 +508,7 @@ static GtkAction* create_action_for_verb( Inkscape::Verb* verb, Inkscape::UI::Vi
     GtkAction* act = 0;
 
     SPAction* targetAction = verb->get_action(view);
-    InkAction* inky = ink_action_new( verb->get_id(), verb->get_name(), verb->get_tip(), verb->get_image(), size  );
+    InkAction* inky = ink_action_new( verb->get_id(), _(verb->get_name()), verb->get_tip(), verb->get_image(), size  );
     act = GTK_ACTION(inky);
     gtk_action_set_sensitive( act, targetAction->sensitive );
 
@@ -630,14 +637,9 @@ sp_aux_toolbox_new()
 {
     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
 
-    GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
-    GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
-    gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
-    gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
 
     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
-    g_object_set_data(G_OBJECT(tb), "top_spacer", tb_s);
 
     gtk_widget_set_sensitive(tb, FALSE);
 
@@ -667,11 +669,7 @@ sp_commands_toolbox_new()
 {
     GtkWidget *tb = gtk_vbox_new(FALSE, 0);
 
-    GtkWidget *tb_s = gtk_vbox_new(FALSE, 0);
-    GtkWidget *tb_e = gtk_vbox_new(FALSE, 0);
     gtk_box_set_spacing(GTK_BOX(tb), AUX_SPACING);
-    gtk_box_pack_start(GTK_BOX(tb), GTK_WIDGET(tb_s), FALSE, FALSE, 0);
-    gtk_box_pack_end(GTK_BOX(tb), GTK_WIDGET(tb_e), FALSE, FALSE, 0);
 
     g_object_set_data(G_OBJECT(tb), "desktop", NULL);
     gtk_widget_set_sensitive(tb, FALSE);
@@ -848,6 +846,13 @@ static void toggle_show_handles (GtkToggleAction *act, gpointer /*data*/) {
     if (shape_editor) shape_editor->show_handles(show);
 }
 
+static void toggle_show_helperpath (GtkToggleAction *act, gpointer /*data*/) {
+    bool show = gtk_toggle_action_get_active( act );
+    prefs_set_int_attribute ("tools.nodes", "show_helperpath",  show ? 1 : 0);
+    ShapeEditor *shape_editor = get_current_shape_editor();
+    if (shape_editor) shape_editor->show_helperpath(show);
+}
+
 void sp_node_path_edit_nextLPEparam (GtkAction */*act*/, gpointer data) {
     sp_selection_next_patheffect_param( reinterpret_cast<SPDesktop*>(data) );
 }
@@ -1098,6 +1103,17 @@ static void sp_node_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions
         gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_handles", 1 ) );
     }
 
+    {
+        InkToggleAction* act = ink_toggle_action_new( "NodesShowHelperpath",
+                                                      _("Show Outline"),
+                                                      _("Show the outline of the path"),
+                                                      "nodes_show_helperpath",
+                                                      Inkscape::ICON_SIZE_DECORATION );
+        gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
+        g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(toggle_show_helperpath), desktop );
+        gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.nodes", "show_helperpath", 0 ) );
+    }
+
     {
         InkAction* inky = ink_action_new( "EditNextLPEParameterAction",
                                           _("Next Path Effect Parameter"),
@@ -1439,10 +1455,6 @@ void show_aux_toolbox(GtkWidget *toolbox_toplevel)
     }
     gtk_widget_show(toolbox);
 
-    // need to show the spacer, or the padding will be off
-    GtkWidget *spacer = GTK_WIDGET(g_object_get_data(G_OBJECT(toolbox), "top_spacer"));
-    gtk_widget_show(spacer);
-
     gtk_widget_show_all(shown_toolbox);
 }
 
@@ -1725,6 +1737,9 @@ static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *n
 
     GtkAdjustment *adj = 0;
 
+    gchar const *flatsidedstr = prefs_get_string_attribute( "tools.shapes.star", "isflatsided" );
+    bool isFlatSided = flatsidedstr ? (strcmp(flatsidedstr, "false") != 0) : true;
+
     if (!strcmp(name, "inkscape:randomized")) {
         adj = GTK_ADJUSTMENT( gtk_object_get_data(GTK_OBJECT(tbl), "randomized") );
         gtk_adjustment_set_value(adj, sp_repr_get_double_attribute(repr, "inkscape:randomized", 0.0));
@@ -1742,7 +1757,7 @@ static void star_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *n
             ege_select_one_action_set_active( flat_action, 0 );
             gtk_action_set_sensitive( prop_action, FALSE );
         }
-    } else if (!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) {
+    } else if ((!strcmp(name, "sodipodi:r1") || !strcmp(name, "sodipodi:r2")) && (!isFlatSided) ) {
         adj = (GtkAdjustment*)gtk_object_get_data(GTK_OBJECT(tbl), "proportion");
         gdouble r1 = sp_repr_get_double_attribute(repr, "sodipodi:r1", 1.0);
         gdouble r2 = sp_repr_get_double_attribute(repr, "sodipodi:r2", 1.0);
@@ -3962,7 +3977,8 @@ sp_text_letter_rotation_changed(GtkAdjustment *adj, GtkWidget *tbl)
 
 namespace {
 
-bool visible = false;
+bool popdown_visible = false;
+bool popdown_hasfocus = false;
 
 void
 sp_text_toolbox_selection_changed (Inkscape::Selection */*selection*/, GObject *tbl)
@@ -4151,13 +4167,10 @@ sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
 {
     SPDesktop    *desktop = SP_ACTIVE_DESKTOP;
     GtkTreeModel *model = 0;
-    GtkWidget    *popdown = GTK_WIDGET (g_object_get_data (tbl, "family-popdown-window"));
     GtkWidget    *entry = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
     GtkTreeIter   iter;
     char         *family = 0;
 
-    (void)popdown;
-
     gdk_pointer_ungrab (GDK_CURRENT_TIME);
     gdk_keyboard_ungrab (GDK_CURRENT_TIME);
 
@@ -4180,10 +4193,22 @@ sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
 
     int result_fontspec =
         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
+
+    font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
     
     SPCSSAttr *css = sp_repr_css_attr_new ();
     
-    std::string fontSpec = query->text->font_specification.value;
+    
+    // First try to get the font spec from the stored value
+    Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
+
+    if (fontSpec.empty()) {
+        // Construct a new font specification if it does not yet exist
+        font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
+        fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
+        fontFromStyle->Unref();
+    }
+    
     if (!fontSpec.empty()) {
         Glib::ustring newFontSpec = font_factory::Default()->ReplaceFontSpecificationFamily(fontSpec, family);
         if (!newFontSpec.empty() && fontSpec != newFontSpec) {
@@ -4216,7 +4241,7 @@ sp_text_toolbox_family_changed (GtkTreeSelection    *selection,
         }
     }
 
-    // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
+    // If querying returned nothing, set the default style of the tool (for new texts)
     if (result_fontspec == QUERY_STYLE_NOTHING)
     {
         sp_repr_css_change (inkscape_get_repr (INKSCAPE, "tools.text"), css, "style");
@@ -4334,12 +4359,29 @@ sp_text_toolbox_style_toggled (GtkToggleButton  *button,
 
     SPStyle *query =
         sp_style_new (SP_ACTIVE_DOCUMENT);
+    
     int result_fontspec =
         sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONT_SPECIFICATION);
-    
-    Glib::ustring fontSpec = query->text->font_specification.value;
-    Glib::ustring newFontSpec;
 
+    int result_family =
+        sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTFAMILY);
+
+    int result_style =
+        sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTSTYLE);
+
+    int result_numbers =
+        sp_desktop_query_style (SP_ACTIVE_DESKTOP, query, QUERY_STYLE_PROPERTY_FONTNUMBERS);
+    
+    Glib::ustring fontSpec = query->text->font_specification.set ?  query->text->font_specification.value : "";
+    Glib::ustring newFontSpec = "";
+    
+    if (fontSpec.empty()) {
+        // Construct a new font specification if it does not yet exist
+        font_instance * fontFromStyle = font_factory::Default()->FaceFromStyle(query);
+        fontSpec = font_factory::Default()->ConstructFontSpecification(fontFromStyle);
+        fontFromStyle->Unref();
+    }
+    
     switch (prop)
     {
         case 0:
@@ -4348,6 +4390,7 @@ sp_text_toolbox_style_toggled (GtkToggleButton  *button,
                 newFontSpec = font_factory::Default()->FontSpecificationSetBold(fontSpec, active);
             }
             if (fontSpec != newFontSpec) {
+                // Don't even set the bold if the font didn't exist on the system
                 sp_repr_css_set_property (css, "font-weight", active ? "bold" : "normal" );
             }
             break;
@@ -4359,14 +4402,15 @@ sp_text_toolbox_style_toggled (GtkToggleButton  *button,
                 newFontSpec = font_factory::Default()->FontSpecificationSetItalic(fontSpec, active);
             }
             if (fontSpec != newFontSpec) {
+                // Don't even set the italic if the font didn't exist on the system
                 sp_repr_css_set_property (css, "font-style", active ? "italic" : "normal");
             }
             break;
         }
     }
 
-    if (!fontSpec.empty()) {
-        sp_repr_css_set_property (css, "-inkscape-font-specification", fontSpec.c_str()); 
+    if (!newFontSpec.empty()) {
+        sp_repr_css_set_property (css, "-inkscape-font-specification", newFontSpec.c_str()); 
     }
 
     // If querying returned nothing, read the style from the text tool prefs (default style for new texts)
@@ -4432,26 +4476,6 @@ sp_text_toolbox_orientation_toggled (GtkRadioButton  *button,
     gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
 }
 
-gboolean
-sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, gpointer /*data*/)
-{
-    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
-    if (!desktop) return FALSE;
-
-    switch (get_group0_keyval (event)) {
-        case GDK_Escape: // defocus
-            gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
-            return TRUE; // I consumed the event
-            break;
-        case GDK_Return: // defocus
-        case GDK_KP_Enter:
-            gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
-            return TRUE; // I consumed the event
-            break;
-    }
-    return FALSE;
-}
-
 gboolean
 sp_text_toolbox_family_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
 {
@@ -4479,10 +4503,19 @@ sp_text_toolbox_family_list_keypress (GtkWidget *w, GdkEventKey *event, GObject
         case GDK_Return:
         case GDK_Escape: // defocus
             gtk_widget_hide (w);
-            visible = false;
+            popdown_visible = false;
             gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
             return TRUE; // I consumed the event
             break;
+        case GDK_w: 
+        case GDK_W: 
+            if (event->state & GDK_CONTROL_MASK) {
+                gtk_widget_hide (w);
+                popdown_visible = false;
+                gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
+                return TRUE; // I consumed the event
+            }
+            break;
     }
     return FALSE;
 }
@@ -4496,11 +4529,30 @@ sp_text_toolbox_size_changed  (GtkComboBox *cbox,
 
     if (g_object_get_data (tbl, "size-block")) return;
 
+    // If this is not from selecting a size in the list (in which case get_active will give the
+    // index of the selected item, otherwise -1) and not from user pressing Enter/Return, do not
+    // process this event. This fixes GTK's stupid insistence on sending an activate change every
+    // time any character gets typed or deleted, which made this control nearly unusable in 0.45.
+    if (gtk_combo_box_get_active (cbox) < 0 && !g_object_get_data (tbl, "enter-pressed"))
+        return;
+
+    gchar *endptr;
+    gdouble value = -1;
     char *text = gtk_combo_box_get_active_text (cbox);
+    if (text) {
+        value = g_strtod (text, &endptr);
+        if (endptr == text) // conversion failed, non-numeric input
+            value = -1;
+        free (text);
+    }
+    if (value <= 0) {
+        return; // could not parse value 
+    }
 
     SPCSSAttr *css = sp_repr_css_attr_new ();
-    sp_repr_css_set_property (css, "font-size", text);
-    free (text);
+    Inkscape::CSSOStringStream osfs;
+    osfs << value;
+    sp_repr_css_set_property (css, "font-size", osfs.str().c_str());
 
     SPStyle *query =
         sp_style_new (SP_ACTIVE_DOCUMENT);
@@ -4520,9 +4572,49 @@ sp_text_toolbox_size_changed  (GtkComboBox *cbox,
                                    _("Text: Change font size"));
     sp_repr_css_attr_unref (css);
 
+    gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
+}
 
-    if (gtk_combo_box_get_active (cbox) > 0) // if this was from drop-down (as opposed to type-in), defocus
-        gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
+gboolean
+sp_text_toolbox_size_focusout (GtkWidget */*w*/, GdkEventFocus *event, GObject *tbl)
+{
+    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+    if (!desktop) return FALSE;
+
+    if (!g_object_get_data (tbl, "esc-pressed")) {
+        g_object_set_data (tbl, "enter-pressed", gpointer(1));
+        GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
+        sp_text_toolbox_size_changed (cbox, tbl);
+        g_object_set_data (tbl, "enter-pressed", gpointer(0));
+    }
+    return FALSE; // I consumed the event
+}
+
+
+gboolean
+sp_text_toolbox_size_keypress (GtkWidget */*w*/, GdkEventKey *event, GObject *tbl)
+{
+    SPDesktop *desktop = SP_ACTIVE_DESKTOP;
+    if (!desktop) return FALSE;
+
+    switch (get_group0_keyval (event)) {
+        case GDK_Escape: // defocus
+            g_object_set_data (tbl, "esc-pressed", gpointer(1));
+            gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
+            g_object_set_data (tbl, "esc-pressed", gpointer(0));
+            return TRUE; // I consumed the event
+            break;
+        case GDK_Return: // defocus
+        case GDK_KP_Enter:
+            g_object_set_data (tbl, "enter-pressed", gpointer(1));
+            GtkComboBox *cbox = GTK_COMBO_BOX(g_object_get_data (G_OBJECT (tbl), "combo-box-size"));
+            sp_text_toolbox_size_changed (cbox, tbl);
+            gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
+            g_object_set_data (tbl, "enter-pressed", gpointer(0));
+            return TRUE; // I consumed the event
+            break;
+    }
+    return FALSE;
 }
 
 void
@@ -4533,11 +4625,12 @@ sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
     GtkWidget *widget = GTK_WIDGET (g_object_get_data (tbl, "family-entry"));
     int x, y;
 
-    if (!visible)
+    if (!popdown_visible)
     {
         gdk_window_get_origin (widget->window, &x, &y);
         gtk_window_move (GTK_WINDOW (popdown), x, y + widget->allocation.height + 2); //2px of grace space
         gtk_widget_show_all (popdown);
+        //sp_transientize (popdown);
 
         gdk_pointer_grab (widget->window, TRUE,
                           GdkEventMask (GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
@@ -4547,14 +4640,16 @@ sp_text_toolbox_text_popdown_clicked    (GtkButton          */*button*/,
 
         gdk_keyboard_grab (widget->window, TRUE, GDK_CURRENT_TIME);
 
-        visible = true;
+        popdown_visible = true;
     }
     else
     {
+        SPDesktop *desktop = SP_ACTIVE_DESKTOP;
         gdk_pointer_ungrab (GDK_CURRENT_TIME);
         gdk_keyboard_ungrab (GDK_CURRENT_TIME);
+        gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
         gtk_widget_hide (popdown);
-        visible = false;
+        popdown_visible = false;
     }
 }
 
@@ -4574,12 +4669,26 @@ sp_text_toolbox_popdown_focus_out (GtkWidget        *popdown,
 {
     SPDesktop *desktop = SP_ACTIVE_DESKTOP;
 
-    gtk_widget_hide (popdown);
-    visible = false;
-    gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
+    if (popdown_hasfocus) {
+        gtk_widget_hide (popdown);
+        popdown_hasfocus = false;
+        popdown_visible = false;
+        gtk_widget_grab_focus (GTK_WIDGET(desktop->canvas));
+        return TRUE;
+    }
+    return FALSE;
+}
+
+gboolean
+sp_text_toolbox_popdown_focus_in (GtkWidget        *popdown,
+                                   GdkEventFocus    */*event*/,
+                                   GObject          */*tbl*/)
+{
+    popdown_hasfocus = true;
     return TRUE;
 }
 
+
 void
 cell_data_func  (GtkTreeViewColumn */*column*/,
                  GtkCellRenderer   *cell,
@@ -4680,6 +4789,7 @@ sp_text_toolbox_new (SPDesktop *desktop)
     g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (sp_text_toolbox_text_popdown_clicked), tbl);
 
     g_signal_connect (G_OBJECT (window), "focus-out-event", G_CALLBACK (sp_text_toolbox_popdown_focus_out), tbl);
+    g_signal_connect (G_OBJECT (window), "focus-in-event", G_CALLBACK (sp_text_toolbox_popdown_focus_in), tbl);
     g_signal_connect (G_OBJECT (window), "key-press-event", G_CALLBACK(sp_text_toolbox_family_list_keypress), tbl);
 
     GtkTreeSelection *tselection = gtk_tree_view_get_selection (GTK_TREE_VIEW (treeview));
@@ -4716,7 +4826,8 @@ sp_text_toolbox_new (SPDesktop *desktop)
     gtk_box_pack_start (GTK_BOX (tbl), cbox, FALSE, FALSE, 0);
     g_object_set_data (G_OBJECT (tbl), "combo-box-size", cbox);
     g_signal_connect (G_OBJECT (cbox), "changed", G_CALLBACK (sp_text_toolbox_size_changed), tbl);
-    gtk_signal_connect(GTK_OBJECT(cbox), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), NULL);
+    gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "key-press-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_keypress), tbl);
+    gtk_signal_connect(GTK_OBJECT(gtk_bin_get_child(GTK_BIN(cbox))), "focus-out-event", GTK_SIGNAL_FUNC(sp_text_toolbox_size_focusout), tbl);
 
     //spacer
     aux_toolbox_space (tbl, 4);
@@ -5214,7 +5325,9 @@ static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* main
 
     // Create the units menu.
     UnitTracker* tracker = new UnitTracker( SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE );
-    tracker->setActiveUnit(sp_unit_get_by_abbreviation(prefs_get_string_attribute("tools.paintbucket", "offsetunits")));
+    const gchar *stored_unit = prefs_get_string_attribute("tools.paintbucket", "offsetunits");
+    if (stored_unit)
+        tracker->setActiveUnit(sp_unit_get_by_abbreviation(stored_unit));
     g_object_set_data( holder, "tracker", tracker );
     {
         GtkAction* act = tracker->createAction( "PaintbucketUnitsAction", _("Units"), ("") );
@@ -5286,3 +5399,4 @@ static void sp_paintbucket_toolbox_prep(SPDesktop *desktop, GtkActionGroup* main
 
 
 
+