Code

Enable 3D box toolbar
authorcilix42 <cilix42@users.sourceforge.net>
Tue, 8 Jan 2008 22:39:39 +0000 (22:39 +0000)
committercilix42 <cilix42@users.sourceforge.net>
Tue, 8 Jan 2008 22:39:39 +0000 (22:39 +0000)
src/axis-manip.h
src/box3d-context.cpp
src/persp3d.cpp
src/persp3d.h
src/widgets/toolbox.cpp

index 8fb8fdc8e972ce5f4a556d2106eff77816a02c6a..e5cc963ba2614d95f406dad307a24c34c9dfb706 100644 (file)
 #ifndef SEEN_AXIS_MANIP_H
 #define SEEN_AXIS_MANIP_H
 
-#include <glib.h> // g_assert()
 #include <gtk/gtk.h>
 #include "libnr/nr-point.h"
 
 namespace Proj {
 
 enum VPState {
-    VP_FINITE = 0,
-    VP_INFINITE
+    FINITE = 0,
+    INFINITE
 };
 
 // The X-/Y-/Z-axis corresponds to the first/second/third digit
index 83952d5ef6583999080f3a19fb2273592a7a0144..78e80be45825f6719b3201d8fc2b399563239197 100644 (file)
@@ -455,37 +455,51 @@ static gint sp_box3d_context_root_handler(SPEventContext *event_context, GdkEven
 
         case GDK_bracketright:
             persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::X, -180/snaps, MOD__ALT);
+            sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
+                             _("Change perspective (angle of PLs)"));
             ret = true;
             break;
 
         case GDK_bracketleft:
             persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::X, 180/snaps, MOD__ALT);
+            sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
+                             _("Change perspective (angle of PLs)"));
             ret = true;
             break;
 
         case GDK_parenright:
             persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Y, -180/snaps, MOD__ALT);
+            sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
+                             _("Change perspective (angle of PLs)"));
             ret = true;
             break;
 
         case GDK_parenleft:
             persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Y, 180/snaps, MOD__ALT);
+            sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
+                             _("Change perspective (angle of PLs)"));
             ret = true;
             break;
 
         case GDK_braceright:
             persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Z, -180/snaps, MOD__ALT);
+            sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
+                             _("Change perspective (angle of PLs)"));
             ret = true;
             break;
 
         case GDK_braceleft:
             persp3d_rotate_VP (inkscape_active_document()->current_persp3d, Proj::Z, 180/snaps, MOD__ALT);
+            sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,
+                             _("Change perspective (angle of PLs)"));
             ret = true;
             break;
 
         case GDK_O:
-            Box3D::create_canvas_point(persp3d_get_VP(inkscape_active_document()->current_persp3d, Proj::W).affine(),
-                                       6, 0xff00ff00);
+            if (MOD__CTRL && MOD__SHIFT) {
+                Box3D::create_canvas_point(persp3d_get_VP(inkscape_active_document()->current_persp3d, Proj::W).affine(),
+                                           6, 0xff00ff00);
+            }
             ret = true;
             break;
 
index 27701c80deff7fd642a531997ba4f10881238b5e..c2bbb5b46984328873dcf7f4990fdf3b628fd7f5 100644 (file)
@@ -309,7 +309,7 @@ persp3d_toggle_VP (Persp3D *persp, Proj::Axis axis, bool set_undo) {
     //        On the other hand, vp_drag_sel_modified() would update all boxes;
     //        here we can confine ourselves to the boxes of this particular perspective.
     persp3d_update_box_reprs (persp);
-    persp3d_update_z_orders (persp);
+    //persp3d_update_z_orders (persp);
     SP_OBJECT(persp)->updateRepr(SP_OBJECT_WRITE_EXT);
     if (set_undo) {
         sp_document_done(sp_desktop_document(inkscape_active_desktop()), SP_VERB_CONTEXT_3DBOX,
@@ -329,7 +329,7 @@ persp3d_toggle_VPs (std::set<Persp3D *> p, Proj::Axis axis) {
 
 void
 persp3d_set_VP_state (Persp3D *persp, Proj::Axis axis, Proj::VPState state) {
-    if (persp3d_VP_is_finite(persp, axis) != (state == Proj::VP_FINITE)) {
+    if (persp3d_VP_is_finite(persp, axis) != (state == Proj::FINITE)) {
         persp3d_toggle_VP(persp, axis);
     }
 }
@@ -348,7 +348,7 @@ persp3d_rotate_VP (Persp3D *persp, Proj::Axis axis, double angle, bool alt_press
     persp->tmat.set_infinite_direction (axis, a);
 
     persp3d_update_box_reprs (persp);
-    persp3d_update_z_orders (persp);
+    //persp3d_update_z_orders (persp);
     SP_OBJECT(persp)->updateRepr(SP_OBJECT_WRITE_EXT);
 }
 
@@ -488,6 +488,7 @@ persp3d_update_box_reprs (Persp3D *persp) {
     //g_print ("Requesting repr update for %d boxes in the perspective.\n", persp->boxes.size());
     for (std::vector<SPBox3D *>::iterator i = persp->boxes.begin(); i != persp->boxes.end(); ++i) {
         SP_OBJECT(*i)->updateRepr(SP_OBJECT_WRITE_EXT);
+        box3d_set_z_orders(*i);
     }
 }
 
index c74af2e29cdbcd8b50a72875f7d94ff082f81fe1..ce4587f62f4f8eef556e1036b4f5401d7b8e32ec 100644 (file)
@@ -23,8 +23,9 @@
 #include <map>
 #include "sp-item.h"
 #include "transf_mat_3x4.h"
+#include "document.h"
+#include "inkscape.h"
 
-class SPDocument;
 class SPBox3D;
 class Box3DContext;
 
@@ -49,7 +50,12 @@ struct Persp3DClass {
 GType persp3d_get_type (void);
 
 // FIXME: Make more of these inline!
-inline Proj::Pt2 persp3d_get_VP (Persp3D *persp, Proj::Axis axis) { return persp->tmat.column(axis); }
+inline Persp3D * persp3d_get_from_repr (Inkscape::XML::Node *repr) {
+    return SP_PERSP3D(SP_ACTIVE_DOCUMENT->getObjectByRepr(repr));
+}
+inline Proj::Pt2 persp3d_get_VP (Persp3D *persp, Proj::Axis axis) {
+    return persp->tmat.column(axis);
+}
 NR::Point persp3d_get_PL_dir_from_pt (Persp3D *persp, NR::Point const &pt, Proj::Axis axis); // convenience wrapper around the following two
 NR::Point persp3d_get_finite_dir (Persp3D *persp, NR::Point const &pt, Proj::Axis axis);
 NR::Point persp3d_get_infinite_dir (Persp3D *persp, Proj::Axis axis);
index 3c35d0cfecc767aff62edda8e5ee61dabac172bc..37f122ca7d82718493d4c74faa6f2463b9f35e65 100644 (file)
@@ -312,15 +312,14 @@ static gchar const * ui_descr =
         "  </toolbar>"
 
         "  <toolbar name='3DBoxToolbar'>"
-        "    <toolitem action='3DBoxPosAngleXAction' />"
-        "    <toolitem action='3DBoxVPXAction' />"
+        "    <toolitem action='3DBoxAngleXAction' />"
+        "    <toolitem action='3DBoxVPXStateAction' />"
         "    <separator />"
-        "    <toolitem action='3DBoxPosAngleYAction' />"
-        "    <toolitem action='3DBoxVPYAction' />"
-        "    <separator />"
-        "    <toolitem action='3DBoxPosAngleZAction' />"
-        "    <toolitem action='3DBoxVPZAction' />"
+        "    <toolitem action='3DBoxAngleYAction' />"
+        "    <toolitem action='3DBoxVPYStateAction' />"
         "    <separator />"
+        "    <toolitem action='3DBoxAngleZAction' />"
+        "    <toolitem action='3DBoxVPZStateAction' />"
         "  </toolbar>"
 
         "  <toolbar name='SpiralToolbar'>"
@@ -2343,228 +2342,224 @@ static void sp_rect_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions
 //##       3D Box       ##
 //########################
 
-static void box3d_toggle_vp_changed (GtkToggleAction *act, GObject *dataKludge, Proj::Axis axis)
-{
-    SPDesktop *desktop = (SPDesktop *) g_object_get_data (dataKludge, "desktop");
-    SPDocument *document = sp_desktop_document (desktop);
-    // FIXME: Make sure document->current_persp3d is set correctly!
-    Persp3D *persp = document->current_persp3d;
+// normalize angle so that it lies in the interval [0,360]
+static double box3d_normalize_angle (double a) {
+    double angle = a + ((int) (a/360.0))*360;
+    if (angle < 0) {
+        angle += 360.0;
+    }
+    return angle;
+}
 
-    g_return_if_fail (persp);
+static void
+box3d_set_button_and_adjustment(Persp3D *persp, Proj::Axis axis,
+                                GtkAdjustment *adj, GtkAction *act, GtkToggleAction *tact) {
+    // TODO: Take all selected perspectives into account but don't touch the state button if not all of them
+    //       have the same state (otherwise a call to box3d_vp_z_state_changed() is triggered and the states
+    //       are reset).
+    bool is_infinite = !persp3d_VP_is_finite(persp, axis);
+
+    if (is_infinite) {
+        gtk_toggle_action_set_active(tact, TRUE);
+        gtk_action_set_sensitive(act, TRUE);
+
+        double angle = persp3d_get_infinite_angle(persp, axis);
+        if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
+            gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
+        }
+    } else {
+        gtk_toggle_action_set_active(tact, FALSE);
+        gtk_action_set_sensitive(act, FALSE);
+    }
+}
 
-    // quit if run by the attr_changed listener
-    if (g_object_get_data(dataKludge, "freeze")) {
+static void
+box3d_resync_toolbar(Inkscape::XML::Node *persp_repr, GObject *data) {
+    if (!persp_repr) {
+        g_print ("No perspective given to box3d_resync_toolbar().\n");
         return;
     }
 
-    // in turn, prevent listener from responding
-    g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
-
-    persp3d_set_VP_state(persp, axis, (gtk_toggle_action_get_active(act) ? Proj::VP_INFINITE : Proj::VP_FINITE));
+    GtkWidget *tbl = GTK_WIDGET(data);
+    GtkAdjustment *adj = 0;
+    GtkAction *act = 0;
+    GtkToggleAction *tact = 0;
+    Persp3D *persp = persp3d_get_from_repr(persp_repr);
+    {
+        adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_x"));
+        act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_x_action"));
+        tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_x_state_action"))->action;
 
-    // FIXME: Can we merge this functionality with the one in box3d_persp_tb_event_attr_changed()?
-    gchar *str;    
-    switch (axis) {
-        case Proj::X:
-            str = g_strdup ("box3d_angle_x_action");
-            break;
-        case Proj::Y:
-            str = g_strdup ("box3d_angle_y_action");
-            break;
-        case Proj::Z:
-            str = g_strdup ("box3d_angle_z_action");
-            break;
-        default:
-            return;
-    }
-    GtkAction* angle_action = GTK_ACTION (g_object_get_data (dataKludge, str));
-    if (angle_action) {
-        gtk_action_set_sensitive (angle_action, !persp3d_VP_is_finite(persp, axis));
+        box3d_set_button_and_adjustment(persp, Proj::X, adj, act, tact);
     }
+    {
+        adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_y"));
+        act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_y_action"));
+        tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_y_state_action"))->action;
 
-    sp_document_maybe_done(sp_desktop_document(desktop), "toggle_vp", SP_VERB_CONTEXT_3DBOX,
-                           _("3D Box: Toggle VP"));
-    //sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_3DBOX,_("3D Box: Toggle VP"));
+        box3d_set_button_and_adjustment(persp, Proj::Y, adj, act, tact);
+    }
+    {
+        adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "box3d_angle_z"));
+        act = GTK_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_angle_z_action"));
+        tact = &INK_TOGGLE_ACTION(g_object_get_data(G_OBJECT(tbl), "box3d_vp_z_state_action"))->action;
 
-    g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
+        box3d_set_button_and_adjustment(persp, Proj::Z, adj, act, tact);
+    }
 }
 
-static void box3d_toggle_vp_x_changed(GtkToggleAction *act, GObject *dataKludge)
+static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
+                                                  gchar const */*old_value*/, gchar const */*new_value*/,
+                                                  bool /*is_interactive*/, gpointer data)
 {
-    box3d_toggle_vp_changed (act, dataKludge, Proj::X);
+    GtkWidget *tbl = GTK_WIDGET(data);
+
+    // quit if run by the attr_changed listener
+    // note: it used to work without the differently called freeze_ attributes (here and in
+    //       box3d_angle_value_changed()) but I now it doesn't so I'm leaving them in for now
+    if (g_object_get_data(G_OBJECT(tbl), "freeze_angle")) {
+        return;
+    }
+
+    // set freeze so that it can be caught in box3d_angle_z_value_changed() (to avoid calling
+    // sp_document_maybe_done() when the document is undo insensitive)
+    g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(TRUE));
+
+    // TODO: Only update the appropriate part of the toolbar
+//    if (!strcmp(name, "inkscape:vp_z")) {
+        box3d_resync_toolbar(repr, G_OBJECT(tbl));
+//    }
+
+    Persp3D *persp = persp3d_get_from_repr(repr);
+    persp3d_update_box_reprs(persp);
+
+    g_object_set_data(G_OBJECT(tbl), "freeze_attr", GINT_TO_POINTER(FALSE));
 }
 
-static void box3d_toggle_vp_y_changed(GtkToggleAction *act, GObject *dataKludge)
+static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
 {
-    box3d_toggle_vp_changed (act, dataKludge, Proj::Y);
-}
+    NULL, /* child_added */
+    NULL, /* child_removed */
+    box3d_persp_tb_event_attr_changed,
+    NULL, /* content_changed */
+    NULL  /* order_changed */
+};
 
-static void box3d_toggle_vp_z_changed(GtkToggleAction *act, GObject *dataKludge)
+/**
+ *  \param selection Should not be NULL.
+ */
+// FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
+//        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
+static void
+box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
 {
-    box3d_toggle_vp_changed (act, dataKludge, Proj::Z);
+    // Here the following should be done: If all selected boxes have finite VPs in a certain direction,
+    // disable the angle entry fields for this direction (otherwise entering a value in them should only
+    // update the perspectives with infinite VPs and leave the other ones untouched).
+
+    Inkscape::XML::Node *persp_repr = NULL;
+    purge_repr_listener(tbl, tbl);
+
+    SPItem *item = selection->singleItem();
+    if (item && SP_IS_BOX3D(item)) {
+        // FIXME: Also deal with multiple selected boxes
+        SPBox3D *box = SP_BOX3D(item);
+        Persp3D *persp = box3d_get_perspective(box);
+        persp_repr = SP_OBJECT_REPR(persp);
+        if (persp_repr) {
+            g_object_set_data(tbl, "repr", persp_repr);
+            Inkscape::GC::anchor(persp_repr);
+            sp_repr_add_listener(persp_repr, &box3d_persp_tb_repr_events, tbl);
+            sp_repr_synthesize_events(persp_repr, &box3d_persp_tb_repr_events, tbl);
+        }
+
+        inkscape_active_document()->current_persp3d = persp3d_get_from_repr(persp_repr);
+        prefs_set_string_attribute("tools.shapes.3dbox", "persp", persp_repr->attribute("id"));
+    
+        box3d_resync_toolbar(persp_repr, tbl);
+    }
 }
 
-static void box3d_vp_angle_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis )
+static void
+box3d_angle_value_changed(GtkAdjustment *adj, GObject *dataKludge, Proj::Axis axis)
 {
-    SPDesktop *desktop = (SPDesktop *) g_object_get_data(dataKludge, "desktop");
-    Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
+    SPDesktop *desktop = (SPDesktop *) g_object_get_data( dataKludge, "desktop" );
+    SPDocument *document = sp_desktop_document(desktop);
 
     // quit if run by the attr_changed listener
-    if (g_object_get_data(dataKludge, "freeze")) {
+    // note: it used to work without the differently called freeze_ attributes (here and in
+    //       box3d_persp_tb_event_attr_changed()) but I now it doesn't so I'm leaving them in for now
+    if (g_object_get_data( dataKludge, "freeze_attr" )) {
         return;
     }
 
     // in turn, prevent listener from responding
-    g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(TRUE));
+    g_object_set_data(dataKludge, "freeze_angle", GINT_TO_POINTER(TRUE));
 
-    if (persp) {
-        double angle = adj->value;
-        // FIXME: Shouldn't we set the angle via the SVG attributes of the perspective instead of directly?
-        if (persp3d_VP_is_finite(persp, axis)) {
-            return; 
-        }
-        persp->tmat.set_infinite_direction (axis, angle);
-        persp3d_update_box_reprs (persp);
-
-        sp_document_maybe_done(sp_desktop_document(desktop), "perspectiveangle", SP_VERB_CONTEXT_3DBOX,
-                               _("3D Box: Change perspective"));
+    //Persp3D *persp = document->current_persp3d;
+    std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
+    if (sel_persps.empty()) {
+        // this can happen when the document is created; we silently ignore it
+        return;
     }
+    Persp3D *persp = *(sel_persps.begin());
 
-    g_object_set_data(dataKludge, "freeze", GINT_TO_POINTER(FALSE));
+    persp->tmat.set_infinite_direction (axis, adj->value);
+    SP_OBJECT(persp)->updateRepr();
+
+    // TODO: use the correct axis here, too
+    sp_document_maybe_done(document, "perspangle", SP_VERB_CONTEXT_3DBOX, _("3D Box: Change perspective (angle of infinite axis)"));
+
+    g_object_set_data( dataKludge, "freeze_angle", GINT_TO_POINTER(FALSE) );
 }
 
-static void box3d_vpx_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
+
+static void
+box3d_angle_x_value_changed(GtkAdjustment *adj, GObject *dataKludge)
 {
-    box3d_vp_angle_changed (adj, dataKludge, Proj::X);
+    box3d_angle_value_changed(adj, dataKludge, Proj::X);
 }
 
-static void box3d_vpy_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
+static void
+box3d_angle_y_value_changed(GtkAdjustment *adj, GObject *dataKludge)
 {
-    box3d_vp_angle_changed (adj, dataKludge, Proj::Y);
+    box3d_angle_value_changed(adj, dataKludge, Proj::Y);
 }
 
-static void box3d_vpz_angle_changed(GtkAdjustment *adj, GObject *dataKludge )
+static void
+box3d_angle_z_value_changed(GtkAdjustment *adj, GObject *dataKludge)
 {
-    box3d_vp_angle_changed (adj, dataKludge, Proj::Z);
+    box3d_angle_value_changed(adj, dataKludge, Proj::Z);
 }
 
-// normalize angle so that it lies in the interval [0,360]
-static double box3d_normalize_angle (double a) {
-    double angle = a + ((int) (a/360.0))*360;
-    if (angle < 0) {
-        angle += 360.0;
-    }
-    return angle;
-}
 
-static void box3d_persp_tb_event_attr_changed(Inkscape::XML::Node *repr, gchar const *name,
-                                              gchar const *old_value, gchar const *new_value,
-                                              bool is_interactive, gpointer data)
+static void box3d_vp_state_changed( GtkToggleAction *act, GtkAction *box3d_angle, Proj::Axis axis )
 {
-    GtkWidget *tbl = GTK_WIDGET(data);
-
-    // FIXME: if we check for "freeze" as in other tools, no action is performed at all ...
-    // quit if run by the _changed callbacks
-    if (g_object_get_data(G_OBJECT(tbl), "freeze")) {
-        //return;
-    }
-
-    // in turn, prevent callbacks from responding
-    //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(TRUE));
-
-    GtkAdjustment *adj = 0;
-    double angle;
-    SPDesktop *desktop = (SPDesktop *) g_object_get_data(G_OBJECT(tbl), "desktop");
-    // FIXME: Get the persp from the box (should be the same, but ...)
-    Persp3D *persp = sp_desktop_document (desktop)->current_persp3d;
-    if (!strcmp(name, "inkscape:vp_x")) {
-        GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_x_action"));
-        GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_x_action"));
-        if (!persp3d_VP_is_finite(persp, Proj::X)) {
-            gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
-            gtk_toggle_action_set_active(tact, TRUE);
-        } else {
-            gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
-            gtk_toggle_action_set_active(tact, FALSE);
-        }
-
-        adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_x"));
-        angle = persp3d_get_infinite_angle(persp, Proj::X);
-        if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
-            gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
-        }
-    }
-
-    if (!strcmp(name, "inkscape:vp_y")) {
-        GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_y_action"));
-        GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_y_action"));
-        if (!persp3d_VP_is_finite(persp, Proj::Y)) {
-            gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
-            gtk_toggle_action_set_active(tact, TRUE);
-        } else {
-            gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
-            gtk_toggle_action_set_active(tact, FALSE);
-        }
-
-        adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_y"));
-        angle = persp3d_get_infinite_angle(persp, Proj::Y);
-        if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
-            gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
-        }
+    // TODO: Take all selected perspectives into account
+    std::set<Persp3D *> sel_persps = persp3d_currently_selected_persps (inkscape_active_event_context());
+    if (sel_persps.empty()) {
+        // this can happen when the document is created; we silently ignore it
+        return;
     }
+    Persp3D *persp = *(sel_persps.begin());
 
-    if (!strcmp(name, "inkscape:vp_z")) {
-        GtkAction* act = GTK_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "box3d_angle_z_action"));
-        GtkToggleAction* tact = GTK_TOGGLE_ACTION (gtk_object_get_data (GTK_OBJECT(tbl), "toggle_vp_z_action"));
-        if (!persp3d_VP_is_finite(persp, Proj::Z)) {
-            gtk_action_set_sensitive(GTK_ACTION(act), TRUE);
-            gtk_toggle_action_set_active(tact, TRUE);
-        } else {
-            gtk_action_set_sensitive(GTK_ACTION(act), FALSE);
-            gtk_toggle_action_set_active(tact, FALSE);
-        }
-
-        adj = GTK_ADJUSTMENT(gtk_object_get_data(GTK_OBJECT(tbl), "dir_vp_z"));
-        angle = persp3d_get_infinite_angle(persp, Proj::Z);
-        if (angle != NR_HUGE) { // FIXME: We should catch this error earlier (don't show the spinbutton at all)
-            gtk_adjustment_set_value(adj, box3d_normalize_angle(angle));
-        }
-    }
-    
-    //g_object_set_data(G_OBJECT(tbl), "freeze", GINT_TO_POINTER(FALSE));
+    bool set_infinite = gtk_toggle_action_get_active(act);
+    persp3d_set_VP_state (persp, axis, set_infinite ? Proj::INFINITE : Proj::FINITE);
 }
 
-static Inkscape::XML::NodeEventVector box3d_persp_tb_repr_events =
+static void box3d_vp_x_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
 {
-    NULL, /* child_added */
-    NULL, /* child_removed */
-    box3d_persp_tb_event_attr_changed,
-    NULL, /* content_changed */
-    NULL  /* order_changed */
-};
+    box3d_vp_state_changed(act, box3d_angle, Proj::X);
+}
 
-/**
- *  \param selection Should not be NULL.
- */
-// FIXME: This should rather be put into persp3d-reference.cpp or something similar so that it reacts upon each
-//        Change of the perspective, and not of the current selection (but how to refer to the toolbar then?)
-static void
-box3d_toolbox_selection_changed(Inkscape::Selection *selection, GObject *tbl)
+static void box3d_vp_y_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
 {
-    Inkscape::XML::Node *repr = NULL;
-    purge_repr_listener(tbl, tbl);
+    box3d_vp_state_changed(act, box3d_angle, Proj::Y);
+}
 
-    SPItem *item = selection->singleItem();
-    if (item && SP_IS_BOX3D(item)) {
-        //repr = SP_OBJECT_REPR(item);
-        repr = SP_OBJECT_REPR(SP_BOX3D(item)->persp_ref->getObject());
-        if (repr) {
-            g_object_set_data(tbl, "repr", repr);
-            Inkscape::GC::anchor(repr);
-            sp_repr_add_listener(repr, &box3d_persp_tb_repr_events, tbl);
-            sp_repr_synthesize_events(repr, &box3d_persp_tb_repr_events, tbl);
-        }
-    }
+static void box3d_vp_z_state_changed( GtkToggleAction *act, GtkAction *box3d_angle )
+{
+    box3d_vp_state_changed(act, box3d_angle, Proj::Z);
 }
 
 static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions, GObject* holder)
@@ -2572,101 +2567,127 @@ static void box3d_toolbox_prep(SPDesktop *desktop, GtkActionGroup* mainActions,
     EgeAdjustmentAction* eact = 0;
     SPDocument *document = sp_desktop_document (desktop);
     Persp3D *persp = document->current_persp3d;
-    bool toggled = false;
-
-    /* angle of VP in X direction */
-    eact = create_adjustment_action("3DBoxPosAngleXAction",
-                                    _("Angle X"), _("Angle X:"), _("Angle of infinite vanishing point in X direction"),
-                                    "tools.shapes.3dbox", "dir_vp_x", persp3d_get_infinite_angle(persp, Proj::X),
-                                    GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
-                                    -360.0, 360.0, 1.0, 10.0,
-                                    0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
-                                    box3d_vpx_angle_changed,
-                                    0.1, 1);
-    gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
-    g_object_set_data(holder, "box3d_angle_x_action", eact);
+
+    EgeAdjustmentAction* box3d_angle_x = 0;
+    EgeAdjustmentAction* box3d_angle_y = 0;
+    EgeAdjustmentAction* box3d_angle_z = 0;
+
+    /* Angle X */
+    {
+        gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
+        gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
+        eact = create_adjustment_action( "3DBoxAngleXAction",
+                                         _("Angle in X direction"), _("Angle X:"),
+                                         // Translators: PL is short for 'perspective line'
+                                         _("Angle of PLs in X direction"),
+                                         "tools.shapes.3dbox", "box3d_angle_x", 30,
+                                         GTK_WIDGET(desktop->canvas), NULL, holder, TRUE, "altx-box3d",
+                                         -360.0, 360.0, 1.0, 10.0,
+                                         labels, values, G_N_ELEMENTS(labels),
+                                         box3d_angle_x_value_changed );
+        gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
+        g_object_set_data( holder, "box3d_angle_x_action", eact );
+        box3d_angle_x = eact;
+    }
+
     if (!persp3d_VP_is_finite(persp, Proj::X)) {
-        gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
+        gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
     } else {
-        gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
+        gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
+    }
+
+
+    /* VP X state */
+    {
+        InkToggleAction* act = ink_toggle_action_new( "3DBoxVPXStateAction",
+                                                      // Translators: VP is short for 'vanishing point'
+                                                      _("State of VP in X direction"),
+                                                      _("Toggle VP in X direction between 'finite' and 'infinite' (=parallel)"),
+                                                      "toggle_vp_x",
+                                                      Inkscape::ICON_SIZE_DECORATION );
+        gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
+        g_object_set_data( holder, "box3d_vp_x_state_action", act );
+        g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_x_state_changed), box3d_angle_x );
+        gtk_action_set_sensitive( GTK_ACTION(box3d_angle_x), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
+        gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_x_state", 1 ) );
     }
 
-    /* toggle VP in X direction */
+    /* Angle Y */
     {
-    InkToggleAction* act = ink_toggle_action_new("3DBoxVPXAction",
-                                                  _("Toggle VP in X direction"),
-                                                  _("Toggle VP in X direction between 'finite' and 'infinite' (= parallel)"),
-                                                  "toggle_vp_x",
-                                                  Inkscape::ICON_SIZE_DECORATION);
-    gtk_action_group_add_action(mainActions, GTK_ACTION(act));
-    g_object_set_data(holder, "toggle_vp_x_action", act);
-    gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::X));
-    /* we connect the signal after setting the state to avoid switching the state again */
-    g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_x_changed), holder);
-    }
-
-    /* angle of VP in Y direction */
-    eact = create_adjustment_action("3DBoxPosAngleYAction",
-                                    _("Angle Y"), _("Angle Y:"), _("Angle of infinite vanishing point in Y direction"),
-                                    "tools.shapes.3dbox", "dir_vp_y", persp3d_get_infinite_angle(persp, Proj::Y),
-                                    GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
-                                    -360.0, 360.0, 1.0, 10.0,
-                                    0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
-                                    box3d_vpy_angle_changed,
-                                    0.1, 1);
-    gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
-    g_object_set_data(holder, "box3d_angle_y_action", eact);
+        gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
+        gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
+        eact = create_adjustment_action( "3DBoxAngleYAction",
+                                         _("Angle in Y direction"), _("Angle Y:"),
+                                         // Translators: PL is short for 'perspective line'
+                                         _("Angle of PLs in Y direction"),
+                                         "tools.shapes.3dbox", "box3d_angle_y", 30,
+                                         GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
+                                         -360.0, 360.0, 1.0, 10.0,
+                                         labels, values, G_N_ELEMENTS(labels),
+                                         box3d_angle_y_value_changed );
+        gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
+        g_object_set_data( holder, "box3d_angle_y_action", eact );
+        box3d_angle_y = eact;
+    }
+
     if (!persp3d_VP_is_finite(persp, Proj::Y)) {
-        gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
+        gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
     } else {
-        gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
+        gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
+    }
+
+    /* VP Y state */
+    {
+        InkToggleAction* act = ink_toggle_action_new( "3DBoxVPYStateAction",
+                                                      // Translators: VP is short for 'vanishing point'
+                                                      _("State of VP in Y direction"),
+                                                      _("Toggle VP in Y direction between 'finite' and 'infinite' (=parallel)"),
+                                                      "toggle_vp_y",
+                                                      Inkscape::ICON_SIZE_DECORATION );
+        gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
+        g_object_set_data( holder, "box3d_vp_y_state_action", act );
+        g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_y_state_changed), box3d_angle_y );
+        gtk_action_set_sensitive( GTK_ACTION(box3d_angle_y), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
+        gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_y_state", 1 ) );
     }
 
-    /* toggle VP in Y direction */
+    /* Angle Z */
     {
-    InkToggleAction* act = ink_toggle_action_new("3DBoxVPYAction",
-                                                 _("Toggle VP in Y direction"),
-                                                 _("Toggle VP in Y direction between 'finite' and 'infinite' (= parallel)"),
-                                                 "toggle_vp_y",
-                                                 Inkscape::ICON_SIZE_DECORATION);
-    gtk_action_group_add_action(mainActions, GTK_ACTION(act));
-    gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Y));
-    g_object_set_data(holder, "toggle_vp_y_action", act);
-    /* we connect the signal after setting the state to avoid switching the state again */
-    g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_y_changed), holder);
-    }
-
-    /* angle of VP in Z direction */
-    eact = create_adjustment_action("3DBoxPosAngleZAction",
-                                    _("Angle Z"), _("Angle Z:"), _("Angle of infinite vanishing point in Z direction"),
-                                    "tools.shapes.3dbox", "dir_vp_z", persp3d_get_infinite_angle(persp, Proj::Z),
-                                    GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
-                                    -360.0, 360.0, 1.0, 10.0,
-                                    0, 0, 0, // labels, values, G_N_ELEMENTS(labels),
-                                    box3d_vpz_angle_changed,
-                                    0.1, 1);
-
-    gtk_action_group_add_action(mainActions, GTK_ACTION(eact));
-    g_object_set_data(holder, "box3d_angle_z_action", eact);
+        gchar const* labels[] = { 0, 0, 0, 0, 0, 0, 0 };
+        gdouble values[] = {-90, -60, -30, 0, 30, 60, 90};
+        eact = create_adjustment_action( "3DBoxAngleZAction",
+                                         _("Angle in Z direction"), _("Angle Z:"),
+                                         // Translators: PL is short for 'perspective line'
+                                         _("Angle of PLs in Z direction"),
+                                         "tools.shapes.3dbox", "box3d_angle_z", 30,
+                                         GTK_WIDGET(desktop->canvas), NULL, holder, FALSE, NULL,
+                                         -360.0, 360.0, 1.0, 10.0,
+                                         labels, values, G_N_ELEMENTS(labels),
+                                         box3d_angle_z_value_changed );
+        gtk_action_group_add_action( mainActions, GTK_ACTION(eact) );
+        g_object_set_data( holder, "box3d_angle_z_action", eact );
+        box3d_angle_z = eact;
+    }
+
     if (!persp3d_VP_is_finite(persp, Proj::Z)) {
-        gtk_action_set_sensitive(GTK_ACTION(eact), TRUE);
+        gtk_action_set_sensitive( GTK_ACTION(eact), TRUE );
     } else {
-        gtk_action_set_sensitive(GTK_ACTION(eact), FALSE);
+        gtk_action_set_sensitive( GTK_ACTION(eact), FALSE );
     }
 
-    /* toggle VP in Z direction */
+    /* VP Z state */
     {
-    InkToggleAction* act = ink_toggle_action_new("3DBoxVPZAction",
-                                                 _("Toggle VP in Z direction"),
-                                                 _("Toggle VP in Z direction between 'finite' and 'infinite' (= parallel)"),
-                                                 "toggle_vp_z",
-                                                 Inkscape::ICON_SIZE_DECORATION);
-    gtk_action_group_add_action(mainActions, GTK_ACTION(act));
-
-    gtk_toggle_action_set_active(GTK_TOGGLE_ACTION(act), !persp3d_VP_is_finite(persp, Proj::Z));
-    g_object_set_data(holder, "toggle_vp_z_action", act);
-    /* we connect the signal after setting the state to avoid switching the state again */
-    g_signal_connect_after(G_OBJECT(act), "toggled", G_CALLBACK(box3d_toggle_vp_z_changed), holder);
+        InkToggleAction* act = ink_toggle_action_new( "3DBoxVPZStateAction",
+                                                      // Translators: VP is short for 'vanishing point'
+                                                      _("State of VP in Z direction"),
+                                                      _("Toggle VP in Z direction between 'finite' and 'infinite' (=parallel)"),
+                                                      "toggle_vp_z",
+                                                      Inkscape::ICON_SIZE_DECORATION );
+        gtk_action_group_add_action( mainActions, GTK_ACTION( act ) );
+        g_object_set_data( holder, "box3d_vp_z_state_action", act );
+        g_signal_connect_after( G_OBJECT(act), "toggled", G_CALLBACK(box3d_vp_z_state_changed), box3d_angle_z );
+        gtk_action_set_sensitive( GTK_ACTION(box3d_angle_z), !prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
+        gtk_toggle_action_set_active( GTK_TOGGLE_ACTION(act), prefs_get_int_attribute( "tools.shapes.3dbox", "vp_z_state", 1 ) );
     }
 
     sigc::connection *connection = new sigc::connection(