Code

Split SPCanvasItem and SPCanvasGroup to individual .h files. Removed forward header.
[inkscape.git] / src / display / sp-canvas.cpp
index e5ef2c80d5bc92c4b9a92784cb69e6e4890ac271..fc68bcfc00a3c6ac304033832b183ab925738eab 100644 (file)
@@ -1,5 +1,3 @@
-#define __SP_CANVAS_C__
-
 /** \file
  * Port of GnomeCanvas for Inkscape needs
  *
 
 #include "helper/sp-marshal.h"
 #include <helper/recthull.h>
-#include <display/sp-canvas.h>
-#include "display-forward.h"
+#include "display/sp-canvas.h"
+#include "display/sp-canvas-group.h"
 #include <2geom/matrix.h>
-#include <libnr/nr-convex-hull.h>
+#include "libnr/nr-convex-hull.h"
 #include "preferences.h"
 #include "inkscape.h"
 #include "sodipodi-ctrlrect.h"
@@ -103,6 +101,7 @@ static void group_remove (SPCanvasGroup *group, SPCanvasItem *item);
 /* SPCanvasItem */
 
 enum {ITEM_EVENT, ITEM_LAST_SIGNAL};
+enum {PROP_0, PROP_VISIBLE};
 
 
 static void sp_canvas_request_update (SPCanvas *canvas);
@@ -114,11 +113,10 @@ static void sp_canvas_item_dispose (GObject *object);
 static void sp_canvas_item_construct (SPCanvasItem *item, SPCanvasGroup *parent, gchar const *first_arg_name, va_list args);
 
 static int emit_event (SPCanvas *canvas, GdkEvent *event);
+static int pick_current_item (SPCanvas *canvas, GdkEvent *event);
 
 static guint item_signals[ITEM_LAST_SIGNAL] = { 0 };
 
-static GtkObjectClass *item_parent_class;
-
 /**
  * Registers the SPCanvasItem class with Glib and returns its type number.
  */
@@ -151,9 +149,6 @@ sp_canvas_item_class_init (SPCanvasItemClass *klass)
 {
     GObjectClass *object_class = (GObjectClass *) klass;
 
-    /* fixme: Derive from GObject */
-    item_parent_class = (GtkObjectClass*)gtk_type_class (GTK_TYPE_OBJECT);
-
     item_signals[ITEM_EVENT] = g_signal_new ("event",
                                              G_TYPE_FROM_CLASS (klass),
                                              G_SIGNAL_RUN_LAST,
@@ -172,6 +167,9 @@ sp_canvas_item_class_init (SPCanvasItemClass *klass)
 static void
 sp_canvas_item_init (SPCanvasItem *item)
 {
+    // TODO items should not be visible on creation - this causes kludges with items
+    // that should be initially invisible; examples of such items: node handles, the CtrlRect
+    // used for rubberbanding, path outline, etc.
     item->flags |= SP_CANVAS_ITEM_VISIBLE;
     item->xform = Geom::Matrix(Geom::identity());
 }
@@ -277,7 +275,7 @@ sp_canvas_item_dispose (GObject *object)
         group_remove (SP_CANVAS_GROUP (item->parent), item);
     }
 
-    G_OBJECT_CLASS (item_parent_class)->dispose (object);
+    G_OBJECT_CLASS (g_type_class_peek(g_type_parent(sp_canvas_item_get_type())))->dispose (object);
 }
 
 /**
@@ -477,6 +475,13 @@ sp_canvas_item_lower (SPCanvasItem *item, int positions)
     item->canvas->need_repick = TRUE;
 }
 
+bool
+sp_canvas_item_is_visible (SPCanvasItem *item)
+{
+    return item->flags & SP_CANVAS_ITEM_VISIBLE;
+}
+
+
 /**
  * Sets visible flag on item and requests a redraw.
  */
@@ -542,8 +547,13 @@ sp_canvas_item_grab (SPCanvasItem *item, guint event_mask, GdkCursor *cursor, gu
     if (item->canvas->grabbed_item)
         return -1;
 
-    if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
-        return -1;
+    // This test disallows grabbing events by an invisible item, which may be useful
+    // sometimes. An example is the hidden control point used for the selector component,
+    // where it is used for object selection and rubberbanding. There seems to be nothing
+    // preventing this except this test, so I removed it.
+    // -- Krzysztof KosiƄski, 2009.08.12
+    //if (!(item->flags & SP_CANVAS_ITEM_VISIBLE))
+    //    return -1;
 
     if (HAS_BROKEN_MOTION_HINTS) {
         event_mask &= ~GDK_POINTER_MOTION_HINT_MASK;
@@ -946,10 +956,6 @@ static void sp_canvas_dirty_rect(SPCanvas* canvas, int nl, int nt, int nr, int n
 static void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8_t val);
 static int do_update (SPCanvas *canvas);
 
-static gboolean sp_canvas_snap_watchdog_callback(gpointer data); 
-static void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event); 
-static void sp_canvas_snap_watchdog_kill(SPCanvas *canvas);
-
 /**
  * Registers the SPCanvas class if necessary, and returns the type ID
  * associated to it.
@@ -1032,6 +1038,8 @@ sp_canvas_init (SPCanvas *canvas)
 
     // See comment at in sp-canvas.h.
     canvas->gen_all_enter_events = false;
+    
+    canvas->drawing_disabled = false;
 
     canvas->tiles=NULL;
     canvas->tLeft=canvas->tTop=canvas->tRight=canvas->tBottom=0;
@@ -1046,9 +1054,6 @@ sp_canvas_init (SPCanvas *canvas)
 #endif // ENABLE_LCMS
 
     canvas->is_scrolling = false;
-
-    canvas->watchdog_id = 0;
-    canvas->watchdog_event = NULL;
 }
 
 /**
@@ -1234,7 +1239,7 @@ sp_canvas_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
     }
 
     widget->allocation = *allocation;
-    
+
     if (GTK_WIDGET_REALIZED (widget)) {
         gdk_window_move_resize (widget->window,
                                 widget->allocation.x, widget->allocation.y,
@@ -1287,7 +1292,7 @@ emit_event (SPCanvas *canvas, GdkEvent *event)
         if (!(mask & canvas->grabbed_event_mask)) return FALSE;
     }
 
-    /* Convert to world coordinates -- we have two cases because of diferent
+    /* Convert to world coordinates -- we have two cases because of different
      * offsets of the fields in the event structures.
      */
 
@@ -1321,6 +1326,20 @@ emit_event (SPCanvas *canvas, GdkEvent *event)
     if (canvas->grabbed_item && !is_descendant (canvas->current_item, canvas->grabbed_item)) {
         item = canvas->grabbed_item;
     } else {
+        // Make sure that current_item is up-to-date. If a snap indicator was just deleted, then
+        // sp_canvas_item_dispose has been called and there is no current_item specified. We need
+        // that though because otherwise we don't know where to send this event to, leading to a
+        // lost event. We can't wait for idle events to have current_item updated, we need it now!
+        // Otherwise, scrolling when hovering above a pre-snap indicator won't work (for example)
+        // See this bug report: https://bugs.launchpad.net/inkscape/+bug/522335/comments/8
+        if (canvas->need_repick && !canvas->in_repick && event->type == GDK_SCROLL) {
+            // To avoid side effects, we'll only do this for scroll events, because this is the
+            // only thing we want to fix here. An example of a reported side effect is that
+            // otherwise selection of nodes in the node editor by dragging a rectangle using a
+            // tablet will break
+            canvas->need_repick = FALSE;
+            pick_current_item (canvas, (GdkEvent *) event);
+        }
         item = canvas->current_item;
     }
 
@@ -1486,6 +1505,8 @@ pick_current_item (SPCanvas *canvas, GdkEvent *event)
         retval = emit_event (canvas, &new_event);
     }
 
+
+
     return retval;
 }
 
@@ -1496,11 +1517,10 @@ static gint
 sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
 {
     SPCanvas *canvas = SP_CANVAS (widget);
-    SPDesktop *dt = SP_ACTIVE_DESKTOP;
 
     int retval = FALSE;
 
-    /* dispatch normally regardless of the event's window if an item has
+    /* dispatch normally regardless of the event's window if an item
        has a pointer grab in effect */
     if (!canvas->grabbed_item &&
         event->window != SP_CANVAS_WINDOW (canvas))
@@ -1531,12 +1551,6 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
     case GDK_BUTTON_PRESS:
     case GDK_2BUTTON_PRESS:
     case GDK_3BUTTON_PRESS:
-       if (dt) { 
-                       // Snapping will be on hold if we're moving the mouse at high speeds. When starting
-               // drawing a new shape we really should snap though. 
-               dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
-               }
-       
        /* Pick the current item as if the button were not pressed, and
          * then process the event.
          */
@@ -1547,9 +1561,7 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
         break;
 
     case GDK_BUTTON_RELEASE:
-       sp_canvas_snap_watchdog_callback(canvas); // If we have any pending snapping action, then invoke it now 
-        
-       /* Process the event as if the button were pressed, then repick
+               /* Process the event as if the button were pressed, then repick
          * after the button has been released
          */
         canvas->state = event->state;
@@ -1558,7 +1570,7 @@ sp_canvas_button (GtkWidget *widget, GdkEventButton *event)
         canvas->state = event->state;
         pick_current_item (canvas, (GdkEvent *) event);
         event->state ^= mask;
-        
+
         break;
 
     default:
@@ -1592,10 +1604,7 @@ static inline void request_motions(GdkWindow *w, GdkEventMotion *event) {
 static int
 sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
 {
-       static guint32 prev_time;
-       static boost::optional<Geom::Point> prev_pos;
-       
-       int status;
+    int status;
     SPCanvas *canvas = SP_CANVAS (widget);
 
     track_latency((GdkEvent *)event);
@@ -1605,66 +1614,10 @@ sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
 
     if (canvas->pixmap_gc == NULL) // canvas being deleted
         return FALSE;
-    
-    SPDesktop *dt = SP_ACTIVE_DESKTOP;
-    
-    // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occured for some period.
-       // i.e. snap when we're at stand still. A speed threshold enforces snapping for tablets, which might never
-       // be fully at stand still and might keep spitting out motion events.
-    if (dt) {
-           bool const c1 = event->type == GDK_MOTION_NOTIFY;
-           bool const c21 = event->state & GDK_BUTTON1_MASK; // Snapping only occurs when dragging with the left mouse button down
-           bool const c22 = event->state & GDK_BUTTON2_MASK; // We shouldn't hold back any events when other mouse buttons have been
-           bool const c23 = event->state & GDK_BUTTON3_MASK; // pressed, e.g. when scrolling with the middle mouse button; if we do then
-                                                                                                         // Inkscape will get stuck in an unresponsive state
-           bool const c3 = dt->namedview->snap_manager.snapprefs.getSnapEnabledGlobally();
-           if (c1 && c21 && (!c22) && (!c23) && c3) {          
-               Geom::Point event_pos(event->x, event->y);
-               guint32 event_t = gdk_event_get_time ( (GdkEvent *) event );
-                       
-               dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold
-                       
-               if (prev_pos) {
-                       Geom::Coord dist = Geom::L2(event_pos - *prev_pos);
-                       guint32 delta_t = event_t - prev_time;
-                       gdouble speed = delta_t > 0 ? dist/delta_t : 1000;
-                       // std::cout << "speed = " << speed << " px/msec " << "| time passed = " << delta_t << " msec" << std::endl;
-                       if (speed > 0.02) { // Jitter threshold, might be needed for tablets 
-                                       // We're moving fast, so postpone any snapping until the next GDK_MOTION_NOTIFY event. We
-                               // will keep on postponing the snapping as long as the speed is high.
-                                       // We must snap at some point in time though, so set a watchdog timer at some time from
-                                       // now, just in case there's no future motion event that drops under the speed limit (when 
-                               // stoppping abruptly)
-                               sp_canvas_snap_watchdog_kill(canvas);
-                               sp_canvas_snap_watchdog_set(canvas, event); // watchdog is reset, i.e. pushed forward in time
-                               // If the watchdog expires before a new motion event is received, we will snap (as explained
-                               // above). This means however that when the timer is too short, we will always snap and that the
-                               // speed threshold is ineffective. In the extreme case the delay is set to zero, and snapping will
-                               // be immediate, as it used to be in the old days ;-). 
-                               } else { // Speed is very low, so we're virtually at stand still
-                                       // But if we're really standing still, then we should snap now. We could use some low-pass filtering, 
-                                       // otherwise snapping occurs for each jitter movement. For this filtering we'll leave the watchdog to expire,
-                                       // snap, and set a new watchdog again. 
-                                       if (canvas->watchdog_id == 0) { // no watchdog has been set 
-                                               // it might have already expired, so we'll set a new one; the snapping frequency will be limited by this
-                                               sp_canvas_snap_watchdog_set(canvas, event);
-                                       } // else: watchdog has been set before and we'll wait for it to expire
-                               }
-                       } else {
-                               // This is the first GDK_MOTION_NOTIFY event, so postpone snapping and set the watchdog
-                               sp_canvas_snap_watchdog_set(canvas, event);
-                       }
-                               
-               prev_pos = event_pos;
-               prev_time = event_t;
-           }
-       }
-    
+
     canvas->state = event->state;
     pick_current_item (canvas, (GdkEvent *) event);
-
     status = emit_event (canvas, (GdkEvent *) event);
-
     if (event->is_hint) {
         request_motions(widget->window, event);
     }
@@ -1672,50 +1625,6 @@ sp_canvas_motion (GtkWidget *widget, GdkEventMotion *event)
     return status;
 }
 
-gboolean sp_canvas_snap_watchdog_callback(gpointer data) 
-{
-       // Snap NOW! For this the "postponed" flag will be reset and an the last motion event will be repeated  
-       SPCanvas *canvas = reinterpret_cast<SPCanvas *>(data);
-       if (!canvas->watchdog_event) {
-               return FALSE;
-       }       
-       
-       SPDesktop *dt = SP_ACTIVE_DESKTOP;
-       if (dt) {
-               dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
-       }
-       
-       emit_event(canvas, canvas->watchdog_event);
-       gdk_event_free(canvas->watchdog_event);
-       canvas->watchdog_event = NULL;
-       canvas->watchdog_id = 0;
-    
-       return FALSE;
-}
-
-void sp_canvas_snap_watchdog_set(SPCanvas *canvas, GdkEventMotion *event) 
-{
-       Inkscape::Preferences *prefs = Inkscape::Preferences::get();
-       double value = prefs->getDoubleLimited("/options/snapdelay/value", 0, 0, 1000);
-       g_assert(canvas->watchdog_id == 0);
-       canvas->watchdog_id = g_timeout_add(value, &sp_canvas_snap_watchdog_callback, canvas);
-       g_assert(canvas->watchdog_event == NULL);
-       canvas->watchdog_event = gdk_event_copy( (GdkEvent *) event); 
-}
-
-void sp_canvas_snap_watchdog_kill(SPCanvas *canvas) 
-{
-       if (canvas->watchdog_id) { 
-        g_source_remove(canvas->watchdog_id); // Kill the watchdog
-        canvas->watchdog_id = 0;
-    }
-       
-       if (canvas->watchdog_event) {
-               gdk_event_free(canvas->watchdog_event);
-               canvas->watchdog_event = NULL;
-       }
-}
-       
 static void
 sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1, int draw_x1, int draw_y1, int draw_x2, int draw_y2, int sw)
 {
@@ -1731,7 +1640,7 @@ sp_canvas_paint_single_buffer (SPCanvas *canvas, int x0, int y0, int x1, int y1,
     // Mark the region clean
     sp_canvas_mark_rect(canvas, x0, y0, x1, y1, 0);
 
-    buf.buf_rowstride = sw * 4; 
+    buf.buf_rowstride = sw * 4;
     buf.rect.x0 = x0;
     buf.rect.y0 = y0;
     buf.rect.x1 = x1;
@@ -2020,7 +1929,7 @@ sp_canvas_paint_rect (SPCanvas *canvas, int xx0, int yy0, int xx1, int yy1)
     } else {
         // paths only, so 1M works faster
         // 1M is the cached buffer and we need 4 channels
-        setup.max_pixels = 262144; 
+        setup.max_pixels = 262144;
     }
 
     // Start the clock
@@ -2202,7 +2111,10 @@ paint (SPCanvas *canvas)
 static int
 do_update (SPCanvas *canvas)
 {
-    if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop durring interrupted display!
+    if (!canvas->root || !canvas->pixmap_gc) // canvas may have already be destroyed by closing desktop during interrupted display!
+        return TRUE;
+        
+    if (canvas->drawing_disabled)
         return TRUE;
 
     /* Cause the update if necessary */
@@ -2282,7 +2194,7 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear,
 
     int ix = (int) round(cx); // ix and iy are the new canvas coordinates (integer screen pixels)
     int iy = (int) round(cy); // cx might be negative, so (int)(cx + 0.5) will not do!
-    int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the 
+    int dx = ix - canvas->x0; // dx and dy specify the displacement (scroll) of the
     int dy = iy - canvas->y0; // canvas w.r.t its previous position
 
     canvas->dx0 = cx; // here the 'd' stands for double, not delta!
@@ -2303,6 +2215,7 @@ sp_canvas_scroll_to (SPCanvas *canvas, double cx, double cy, unsigned int clear,
     } else {
         // scrolling as part of zoom; do nothing here - the next do_update will perform full redraw
     }
+
 }
 
 /**
@@ -2537,4 +2450,4 @@ void sp_canvas_mark_rect(SPCanvas* canvas, int nl, int nt, int nr, int nb, uint8
   fill-column:99
   End:
 */
-// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :
+// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :