Code

Use subdirectories with icon sizes.
[inkscape.git] / src / event-context.cpp
index 100e6b1430123126dfbf94c6466a90c4776feb58..828ce3d5b1d02ab950dc31ab76eb789603ca24d3 100644 (file)
@@ -1,5 +1,3 @@
-#define __SP_EVENT_CONTEXT_C__
-
 /** \file
  * Main event handling, and related helper functions.
  *
@@ -7,8 +5,9 @@
  *   Lauris Kaplinski <lauris@kaplinski.com>
  *   Frank Felfe <innerspace@iname.com>
  *   bulia byak <buliabyak@users.sf.net>
+ *   Jon A. Cruz <jon@joncruz.org>
  *
- * Copyright (C) 1999-2005 authors
+ * Copyright (C) 1999-2010 authors
  * Copyright (C) 2001-2002 Ximian, Inc.
  *
  * Released under GNU GPL, read the file 'COPYING' for more information
@@ -43,6 +42,9 @@
 #include "shortcuts.h"
 #include "desktop.h"
 #include "desktop-handles.h"
+#include "desktop-events.h"
+#include "desktop-style.h"
+#include "widgets/desktop-widget.h"
 #include "sp-namedview.h"
 #include "selection.h"
 #include "file.h"
@@ -58,6 +60,9 @@
 #include "selcue.h"
 #include "lpe-tool-context.h"
 #include "ui/tool/control-point.h"
+#include "shape-editor.h"
+#include "sp-guide.h"
+#include "color.h"
 
 static void sp_event_context_class_init(SPEventContextClass *klass);
 static void sp_event_context_init(SPEventContext *event_context);
@@ -136,6 +141,8 @@ static void sp_event_context_init(SPEventContext *event_context) {
     event_context->space_panning = false;
     event_context->shape_editor = NULL;
     event_context->_delayed_snap_event = NULL;
+    event_context->_dse_callback_in_process = false;
+    event_context->tool_url = NULL;
 }
 
 /**
@@ -178,17 +185,38 @@ void sp_event_context_update_cursor(SPEventContext *ec) {
     if (w->window) {
         /* fixme: */
         if (ec->cursor_shape) {
-            GdkBitmap *bitmap = NULL;
-            GdkBitmap *mask = NULL;
-            sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, ec->cursor_shape);
-            if ((bitmap != NULL) && (mask != NULL)) {
-                if (ec->cursor)
-                    gdk_cursor_unref(ec->cursor);
-                ec->cursor = gdk_cursor_new_from_pixmap(bitmap, mask,
-                        &w->style->black, &w->style->white, ec->hot_x,
-                        ec->hot_y);
-                g_object_unref(bitmap);
-                g_object_unref(mask);
+            GdkDisplay *display = gdk_display_get_default();
+            if (ec->tool_url && gdk_display_supports_cursor_alpha(display) && gdk_display_supports_cursor_color(display)) {
+                bool fillHasColor=false, strokeHasColor=false;
+                guint32 fillColor = sp_desktop_get_color_tool(ec->desktop, ec->tool_url, true, &fillHasColor);
+                guint32 strokeColor = sp_desktop_get_color_tool(ec->desktop, ec->tool_url, false, &strokeHasColor);
+                double fillOpacity = fillHasColor ? sp_desktop_get_opacity_tool(ec->desktop, ec->tool_url, true) : 0;
+                double strokeOpacity = strokeHasColor ? sp_desktop_get_opacity_tool(ec->desktop, ec->tool_url, false) : 0;
+                GdkPixbuf *pixbuf = sp_cursor_pixbuf_from_xpm(
+                    ec->cursor_shape,
+                    w->style->black, w->style->white,
+                    SP_RGBA32_U_COMPOSE(SP_RGBA32_R_U(fillColor),SP_RGBA32_G_U(fillColor),SP_RGBA32_B_U(fillColor),SP_COLOR_F_TO_U(fillOpacity)),
+                    SP_RGBA32_U_COMPOSE(SP_RGBA32_R_U(strokeColor),SP_RGBA32_G_U(strokeColor),SP_RGBA32_B_U(strokeColor),SP_COLOR_F_TO_U(strokeOpacity))
+                    );
+                if (pixbuf != NULL) {
+                    if (ec->cursor)
+                        gdk_cursor_unref(ec->cursor);
+                    ec->cursor = gdk_cursor_new_from_pixbuf(display, pixbuf, ec->hot_x, ec->hot_y);
+                    g_object_unref(pixbuf);
+                }
+            } else {
+                GdkBitmap *bitmap = NULL;
+                GdkBitmap *mask = NULL;
+                sp_cursor_bitmap_and_mask_from_xpm(&bitmap, &mask, ec->cursor_shape);
+                if ((bitmap != NULL) && (mask != NULL)) {
+                    if (ec->cursor)
+                        gdk_cursor_unref(ec->cursor);
+                    ec->cursor = gdk_cursor_new_from_pixmap(bitmap, mask,
+                            &w->style->black, &w->style->white, ec->hot_x,
+                            ec->hot_y);
+                    g_object_unref(bitmap);
+                    g_object_unref(mask);
+                }
             }
         }
         gdk_window_set_cursor(w->window, ec->cursor);
@@ -743,6 +771,19 @@ gint sp_event_context_private_item_handler(SPEventContext *ec, SPItem *item,
     return ret;
 }
 
+/**
+ * @brief: Returns true if we're hovering above a knot (needed because we don't want to pre-snap in that case)
+ */
+
+bool sp_event_context_knot_mouseover(SPEventContext *ec)
+{
+    if (ec->shape_editor) {
+        return ec->shape_editor->knot_mouseover();
+    }
+
+    return false;
+}
+
 /**
  * @brief An observer that relays pref changes to the derived classes
  */
@@ -888,7 +929,8 @@ void sp_event_context_deactivate(SPEventContext *ec) {
  * Calls virtual root_handler(), the main event handling function.
  */
 gint sp_event_context_root_handler(SPEventContext * event_context,
-        GdkEvent * event) {
+        GdkEvent * event)
+{
     switch (event->type) {
     case GDK_MOTION_NOTIFY:
         sp_event_context_snap_delay_handler(event_context, NULL, NULL,
@@ -917,13 +959,13 @@ gint sp_event_context_root_handler(SPEventContext * event_context,
     return sp_event_context_virtual_root_handler(event_context, event);
 }
 
-gint sp_event_context_virtual_root_handler(SPEventContext * event_context,
-        GdkEvent * event) {
-    gint
-            ret =
-                    ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->root_handler(
-                            event_context, event);
-    set_event_location(event_context->desktop, event);
+gint sp_event_context_virtual_root_handler(SPEventContext * event_context, GdkEvent * event) {
+    gint ret = false;
+    if (event_context) {    // If no event-context is available then do nothing, otherwise Inkscape would crash
+                            // (see the comment in SPDesktop::set_event_context, and bug LP #622350)
+        ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->root_handler(event_context, event);
+        set_event_location(event_context->desktop, event);
+    }
     return ret;
 }
 
@@ -934,25 +976,21 @@ gint sp_event_context_item_handler(SPEventContext * event_context,
         SPItem * item, GdkEvent * event) {
     switch (event->type) {
     case GDK_MOTION_NOTIFY:
-        sp_event_context_snap_delay_handler(event_context, item, NULL,
-                (GdkEventMotion *) event,
-                DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER);
+        sp_event_context_snap_delay_handler(event_context, (gpointer) item, NULL, (GdkEventMotion *) event, DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER);
         break;
     case GDK_BUTTON_RELEASE:
         if (event_context->_delayed_snap_event) {
             // If we have any pending snapping action, then invoke it now
-            sp_event_context_snap_watchdog_callback(
-                    event_context->_delayed_snap_event);
+            sp_event_context_snap_watchdog_callback(event_context->_delayed_snap_event);
         }
         break;
-        /*case GDK_BUTTON_PRESS:
-         case GDK_2BUTTON_PRESS:
-         case GDK_3BUTTON_PRESS:
-         // 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.
-         event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
-         break;
-         */
+    case GDK_BUTTON_PRESS:
+    case GDK_2BUTTON_PRESS:
+    case GDK_3BUTTON_PRESS:
+        // 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.
+        event_context->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
+        break;
     default:
         break;
     }
@@ -960,17 +998,16 @@ gint sp_event_context_item_handler(SPEventContext * event_context,
     return sp_event_context_virtual_item_handler(event_context, item, event);
 }
 
-gint sp_event_context_virtual_item_handler(SPEventContext * event_context,
-        SPItem * item, GdkEvent * event) {
-    gint
-            ret =
-                    ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->item_handler(
-                            event_context, item, event);
-
-    if (!ret) {
-        ret = sp_event_context_virtual_root_handler(event_context, event);
-    } else {
-        set_event_location(event_context->desktop, event);
+gint sp_event_context_virtual_item_handler(SPEventContext * event_context, SPItem * item, GdkEvent * event) {
+    gint ret = false;
+    if (event_context) {    // If no event-context is available then do nothing, otherwise Inkscape would crash
+                            // (see the comment in SPDesktop::set_event_context, and bug LP #622350)
+        ret = ((SPEventContextClass *) G_OBJECT_GET_CLASS(event_context))->item_handler(event_context, item, event);
+        if (!ret) {
+            ret = sp_event_context_virtual_root_handler(event_context, event);
+        } else {
+            set_event_location(event_context->desktop, event);
+        }
     }
 
     return ret;
@@ -1065,20 +1102,21 @@ guint get_group0_keyval(GdkEventKey *event) {
  * If state includes alt key mask, cyclically selects under; honors
  * into_groups.
  */
-SPItem *
-sp_event_context_find_item(SPDesktop *desktop, Geom::Point const &p,
-        bool select_under, bool into_groups) {
-    SPItem *item;
+SPItem *sp_event_context_find_item(SPDesktop *desktop, Geom::Point const &p,
+                                   bool select_under, bool into_groups)
+{
+    SPItem *item = 0;
 
     if (select_under) {
-        SPItem *selected_at_point = desktop->item_from_list_at_point_bottom(
+        SPItem *selected_at_point = desktop->getItemFromListAtPointBottom(
                 desktop->selection->itemList(), p);
-        item = desktop->item_at_point(p, into_groups, selected_at_point);
+        item = desktop->getItemAtPoint(p, into_groups, selected_at_point);
         if (item == NULL) { // we may have reached bottom, flip over to the top
-            item = desktop->item_at_point(p, into_groups, NULL);
+            item = desktop->getItemAtPoint(p, into_groups, NULL);
         }
-    } else
-        item = desktop->item_at_point(p, into_groups, NULL);
+    } else {
+        item = desktop->getItemAtPoint(p, into_groups, NULL);
+    }
 
     return item;
 }
@@ -1093,7 +1131,7 @@ sp_event_context_over_item(SPDesktop *desktop, SPItem *item,
         Geom::Point const &p) {
     GSList *temp = NULL;
     temp = g_slist_prepend(temp, item);
-    SPItem *item_at_point = desktop->item_from_list_at_point_bottom(temp, p);
+    SPItem *item_at_point = desktop->getItemFromListAtPointBottom(temp, p);
     g_slist_free(temp);
 
     return item_at_point;
@@ -1146,12 +1184,28 @@ void event_context_print_event_info(GdkEvent *event, bool print_return) {
     }
 }
 
+/**
+ * \brief Analyses the current event, calculates the mouse speed, turns snapping off (temporarily) if the
+ * mouse speed is above a threshold, and stores the current event such that it can be re-triggered when needed
+ * (re-triggering is controlled by a watchdog timer)
+ *
+ * \param ec Pointer to the event context
+ * \param dse_item Pointer that store a reference to a canvas or to an item
+ * \param dse_item2 Another pointer, storing a reference to a knot or controlpoint
+ * \param event Pointer to the motion event
+ * \param origin Identifier (enum) specifying where the delay (and the call to this method) were initiated
+ */
 void sp_event_context_snap_delay_handler(SPEventContext *ec,
-        SPItem* const item, SPKnot* const knot, GdkEventMotion *event,
-        DelayedSnapEvent::DelayedSnapEventOrigin origin) {
+        gpointer const dse_item, gpointer const dse_item2, GdkEventMotion *event,
+        DelayedSnapEvent::DelayedSnapEventOrigin origin)
+{
     static guint32 prev_time;
     static boost::optional<Geom::Point> prev_pos;
 
+    if (ec->_dse_callback_in_process) {
+        return;
+    }
+
     // Snapping occurs when dragging with the left mouse button down, or when hovering e.g. in the pen tool with left mouse button up
     bool const c1 = event->state & GDK_BUTTON2_MASK; // We shouldn't hold back any events when other mouse buttons have been
     bool const c2 = event->state & GDK_BUTTON3_MASK; // pressed, e.g. when scrolling with the middle mouse button; if we do then
@@ -1171,8 +1225,7 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec,
         // Snap when speed drops below e.g. 0.02 px/msec, or when no motion events have occurred 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.
-        ec->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(
-                true); // put snapping on hold
+        ec->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(true); // put snapping on hold
 
         Geom::Point event_pos(event->x, event->y);
         guint32 event_t = gdk_event_get_time((GdkEvent *) event);
@@ -1189,7 +1242,7 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec,
                 // now, just in case there's no future motion event that drops under the speed limit (when
                 // stopping abruptly)
                 delete ec->_delayed_snap_event;
-                ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot,
+                ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2,
                         event, origin); // 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
@@ -1201,14 +1254,14 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec,
                 // snap, and set a new watchdog again.
                 if (ec->_delayed_snap_event == NULL) { // no watchdog has been set
                     // it might have already expired, so we'll set a new one; the snapping frequency will be limited this way
-                    ec->_delayed_snap_event = new DelayedSnapEvent(ec, item,
-                            knot, event, origin);
+                    ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item,
+                            dse_item2, event, origin);
                 } // 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
             g_assert(ec->_delayed_snap_event == NULL);
-            ec->_delayed_snap_event = new DelayedSnapEvent(ec, item, knot,
+            ec->_delayed_snap_event = new DelayedSnapEvent(ec, dse_item, dse_item2,
                     event, origin);
         }
 
@@ -1217,6 +1270,10 @@ void sp_event_context_snap_delay_handler(SPEventContext *ec,
     }
 }
 
+/**
+ * \brief When the snap delay watchdog timer barks, this method will be called and will re-inject the last motion
+ * event in an appropriate place, with snapping being turned on again
+ */
 gboolean sp_event_context_snap_watchdog_callback(gpointer data) {
     // Snap NOW! For this the "postponed" flag will be reset and the last motion event will be repeated
     DelayedSnapEvent *dse = reinterpret_cast<DelayedSnapEvent*> (data);
@@ -1231,24 +1288,27 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data) {
     if (ec == NULL || ec->desktop == NULL) {
         return false;
     }
+    ec->_dse_callback_in_process = true;
 
     SPDesktop *dt = ec->desktop;
     dt->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
 
+    // Depending on where the delayed snap event originated from, we will inject it back at it's origin
+    // The switch below takes care of that and prepares the relevant parameters
     switch (dse->getOrigin()) {
     case DelayedSnapEvent::EVENTCONTEXT_ROOT_HANDLER:
         sp_event_context_virtual_root_handler(ec, dse->getEvent());
         break;
     case DelayedSnapEvent::EVENTCONTEXT_ITEM_HANDLER: {
         SPItem* item = NULL;
-        item = dse->getItem();
+        item = SP_ITEM(dse->getItem());
         if (item && SP_IS_ITEM(item)) {
             sp_event_context_virtual_item_handler(ec, item, dse->getEvent());
         }
     }
         break;
     case DelayedSnapEvent::KNOT_HANDLER: {
-        SPKnot* knot = dse->getKnot();
+        SPKnot* knot = SP_KNOT(dse->getItem2());
         if (knot && SP_IS_KNOT(knot)) {
             sp_knot_handler_request_position(dse->getEvent(), knot);
         }
@@ -1256,10 +1316,35 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data) {
         break;
     case DelayedSnapEvent::CONTROL_POINT_HANDLER: {
         using Inkscape::UI::ControlPoint;
-        ControlPoint *point = reinterpret_cast<ControlPoint*> (dse->getKnot());
+        ControlPoint *point = reinterpret_cast<ControlPoint*> (dse->getItem2());
         point->_eventHandler(dse->getEvent());
     }
         break;
+    case DelayedSnapEvent::GUIDE_HANDLER: {
+        gpointer item = dse->getItem();
+        gpointer item2 = dse->getItem2();
+        if (item && item2) {
+            g_assert(SP_IS_CANVAS_ITEM(item));
+            g_assert(SP_IS_GUIDE(item2));
+            sp_dt_guide_event(SP_CANVAS_ITEM(item), dse->getEvent(), item2);
+        }
+    }
+        break;
+    case DelayedSnapEvent::GUIDE_HRULER:
+    case DelayedSnapEvent::GUIDE_VRULER: {
+        gpointer item = dse->getItem();
+        gpointer item2 = dse->getItem2();
+        if (item && item2) {
+            g_assert(GTK_IS_WIDGET(item));
+            g_assert(SP_IS_DESKTOP_WIDGET(item2));
+            if (dse->getOrigin() == DelayedSnapEvent::GUIDE_HRULER) {
+                sp_dt_hruler_event(GTK_WIDGET(item), dse->getEvent(), SP_DESKTOP_WIDGET(item2));
+            } else {
+                sp_dt_vruler_event(GTK_WIDGET(item), dse->getEvent(), SP_DESKTOP_WIDGET(item2));
+            }
+        }
+    }
+        break;
     default:
         g_warning("Origin of snap-delay event has not been defined!;");
         break;
@@ -1268,12 +1353,15 @@ gboolean sp_event_context_snap_watchdog_callback(gpointer data) {
     ec->_delayed_snap_event = NULL;
     delete dse;
 
+    ec->_dse_callback_in_process = false;
+
     return FALSE; //Kills the timer and stops it from executing this callback over and over again.
 }
 
 void sp_event_context_discard_delayed_snap_event(SPEventContext *ec) {
     delete ec->_delayed_snap_event;
     ec->_delayed_snap_event = NULL;
+    ec->desktop->namedview->snap_manager.snapprefs.setSnapPostponedGlobally(false);
 }
 
 /*
@@ -1285,4 +1373,4 @@ void sp_event_context_discard_delayed_snap_event(SPEventContext *ec) {
  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 :