Re: is_item_at



On Tue, May 24, 2011 at 12:34 PM, Damon Chaplin <damonachaplin gmail com> wrote:
> On 23 May 2011 14:28, control H <control h gmail com> wrote:
>
>> I'm not using large coordinates. The x&y coordinates look very reasonable.
>> The only place where I'm using a transformation matrix (I think) is in
>> the constructor:
>>
>> goo_line_item_new (GooCanvasItem      *parent,
>>                   double x, double y,
>>                   gdouble dx,
>>                   gdouble dy,
>>                   ...)
>> {
>>  GooCanvasItem *item;
>>  GooLineItem *line_item;
>>  const char *first_property;
>>  va_list var_args;
>>
>>  item = g_object_new (GOO_TYPE_LINE_ITEM, NULL);
>>
>>  line_item = (GooLineItem*) item;
>>  line_item->dx = dx;
>>  line_item->dy = dy;
>>
>>  goo_canvas_item_translate(item,x,y);
>>
>>  va_start (var_args, dy);
>>  first_property = va_arg (var_args, char*);
>>  if (first_property)
>>    g_object_set_valist ((GObject*) item, first_property, var_args);
>>  va_end (var_args);
>>
>>  if (parent)
>>    {
>>      goo_canvas_item_add_child (parent, item, -1);
>>      g_object_unref (item);
>>    }
>>
>>  g_object_set(item,"can-focus", TRUE,NULL);
>>
>>  return item;
>> }
>>
>> Example line showing problems:
>>
>> goo_line_item_new (root, 50,50,140, 30,
>>                             "stroke-color", "blue",
>>                             "line-width", 3.0,
>>                             NULL);
>>
>> In addition it seems that the line-width + 4.0 in the is_item_at()
>> code doesn't seem to have any influence.
>
> I can't understand the problem either.
>
> If you can produce a simple test app I'll have a look at it.
>
> Damon
>

Well, luckily I'm not the only one who doesn't understand. ;)
I have checked my code over and over again to make sure I didn't miss
something elementary. Can't find anything though.

Attached you'll find the class line-item.[ch] and an example program lijn.c.

regards,
CH
/*
 * GooCanvas Demo. Copyright (C) 2006 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * demo-item.c - a simple demo item.
 */
#include "goocanvas.h"
#include "line-item.h"

#define DRAG_BOX_WIDTH 8

static double drag_x,drag_y;


static gboolean leave_notify (GooCanvasItem *item,
			      GooCanvasItem *target,
			      GdkEventCrossing *event)
{
  GooLineItem *line=GOO_LINE_ITEM(item);

  printf("line_item leave event\n");
  goo_canvas_item_simple_changed(GOO_CANVAS_ITEM_SIMPLE(line),TRUE);
  return FALSE;
}

static gboolean enter_notify(GooCanvasItem    *item,
			     GooCanvasItem    *target_item,
			     GdkEventCrossing *event)
{
  GooLineItem *line=GOO_LINE_ITEM(item);

  printf("line_item enter event\n");
  goo_canvas_item_simple_changed(GOO_CANVAS_ITEM_SIMPLE(line),TRUE);

  return FALSE;
}


static gboolean motion_notify(GooCanvasItem *item,
			      GooCanvasItem *target,
			      GdkEventMotion *event)
{
  double x,y;
  GooLineItem *line=GOO_LINE_ITEM(item);

  x=event->x;
  y=event->y;
  printf("line_item motion event (%.2f,%.2f)\n",x,y);

  if(line->dragging){
    printf("in drag\n");
    goo_canvas_item_translate(item,x-drag_x,y-drag_y);
  }else{
    printf("not in drag\n");
  }
 
  //  return TRUE;
  return FALSE;
}

static gboolean button_press(GooCanvasItem  *item,
			     GooCanvasItem  *target_item,
			     GdkEventButton *event)
{
  GooLineItem *line=GOO_LINE_ITEM(item);
  GooCanvasItemSimple *simple=GOO_CANVAS_ITEM_SIMPLE(item);
  GdkCursor *fleur;
  GooCanvas *canvas;

  printf("line button press (%.2f,%.2f)\n",event->x,event->y);

  drag_x = event->x;
  drag_y = event->y;
  fleur = gdk_cursor_new (GDK_FLEUR);
  goo_canvas_pointer_grab (canvas, item,
			   GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
			   fleur,
			   event->time);
  gdk_cursor_unref (fleur);

  line->dragging=TRUE;

  printf("current bounds: %.2f, %.2f, %.2f, %.2f\n",
	 simple->bounds.x1,simple->bounds.y1,simple->bounds.x2,
	 simple->bounds.y2);
  
  return TRUE;
}


static gboolean button_release(GooCanvasItem *item,
			       GooCanvasItem *target,
			       GdkEventButton *event)
{
  GooLineItem *line=GOO_LINE_ITEM(item);
  GooCanvas *canvas;

  canvas = goo_canvas_item_get_canvas (item);

  line->dragging=FALSE;

  return TRUE;
}


static void canvas_item_interface_init (GooCanvasItemIface *iface)
{
  printf("line_item interface init\n");
  iface->motion_notify_event = motion_notify;
  iface->button_press_event = button_press;
  iface->button_release_event = button_release;
}

/* Use the GLib convenience macro to define the type. GooLineItem is the
   class struct, goo_line_item is the function prefix, and our class is a
   subclass of GOO_TYPE_CANVAS_ITEM_SIMPLE. */
//G_DEFINE_TYPE (GooLineItem, goo_line_item, GOO_TYPE_CANVAS_ITEM_SIMPLE)

G_DEFINE_TYPE_WITH_CODE (GooLineItem, goo_line_item,
			 GOO_TYPE_CANVAS_ITEM_SIMPLE,
			 G_IMPLEMENT_INTERFACE(GOO_TYPE_CANVAS_ITEM,
					       canvas_item_interface_init))

/* The standard object initialization function. */
static void
goo_line_item_init (GooLineItem *line_item)
{
  printf("line_item init\n");
  line_item->dx = 80.0;
  line_item->dy = 30.0;
  line_item->dragging=FALSE;
}


/* The convenience function to create new items. This should start with a 
   parent argument and end with a variable list of object properties to fit
   in with the standard canvas items. */
GooCanvasItem*
goo_line_item_new (GooCanvasItem      *parent,
		   double x, double y,
		   gdouble dx,
		   gdouble dy,
		   ...)
{
  GooCanvasItem *item;
  GooLineItem *line_item;
  const char *first_property;
  va_list var_args;

  item = g_object_new (GOO_TYPE_LINE_ITEM, NULL);

  line_item = (GooLineItem*) item;
  line_item->dx = dx;
  line_item->dy = dy;

  goo_canvas_item_translate(item,x,y);

  va_start (var_args, dy);
  first_property = va_arg (var_args, char*);
  if (first_property)
    g_object_set_valist ((GObject*) item, first_property, var_args);
  va_end (var_args);

  if (parent)
    {
      goo_canvas_item_add_child (parent, item, -1);
      g_object_unref (item);
    }

  g_object_set(item,"can-focus", TRUE,NULL);

  return item;
}


/* The update method. This is called when the canvas is initially shown and
   also whenever the object is updated and needs to change its size and/or
   shape. It should calculate its new bounds in its own coordinate space,
   storing them in simple->bounds. */
static void
goo_line_item_update  (GooCanvasItemSimple *simple,
		       cairo_t             *cr)
{
  GooLineItem *line = (GooLineItem*) simple;
  gdouble lw;
  GooCanvas *canvas;
  GooCanvasItem *item=GOO_CANVAS_ITEM(simple);
  canvas=goo_canvas_item_get_canvas(item);

  printf("line_item update\n");
  g_object_get (G_OBJECT(simple),"line-width", &lw,NULL);

  /* Compute the new bounds. */
  simple->bounds.x1 = MIN(0,line->dx)-lw;
  simple->bounds.y1 = MIN(0,line->dy)-lw;
  simple->bounds.x2 = MAX(0,line->dx)+lw;
  simple->bounds.y2 = MAX(0,line->dy)+lw;

  printf("new bounds user: %.2f, %.2f, %.2f, %.2f\n",
	 simple->bounds.x1,simple->bounds.y1,simple->bounds.x2,
	 simple->bounds.y2);
}


/* The paint method. This should draw the item on the given cairo_t, using
   the item's own coordinate space. */
static void
goo_line_item_paint (GooCanvasItemSimple   *simple,
		     cairo_t               *cr,
		     const GooCanvasBounds *bounds)
{
  //GooCanvasItemSimple *group,*handle;
  GooLineItem *line_item;
  cairo_pattern_t *pattern;
  gdouble w;

  line_item = (GooLineItem*) simple;
  printf("line_item paint\n");
  printf("(%.2f,%.2f)\n",line_item->dx,line_item->dy);

  g_object_get (G_OBJECT(line_item),"line-width", &w,NULL);
  //printf("line width: %.2f\n",w);

  cairo_move_to(cr,0,0);
  cairo_line_to(cr,line_item->dx,line_item->dy);
  goo_canvas_style_set_stroke_options(simple->simple_data->style, cr);
  goo_canvas_style_set_fill_options (simple->simple_data->style, cr);
  cairo_stroke (cr);
}


/* Hit detection. This should check if the given coordinate (in the item's
   coordinate space) is within the item. If it is it should return TRUE,
   otherwise it should return FALSE. */
static gboolean
goo_line_item_is_item_at (GooCanvasItemSimple *simple,
			  gdouble              x,
			  gdouble              y,
			  cairo_t             *cr,
			  gboolean             is_pointer_event)
{
  GooLineItem *line=(GooLineItem*)simple;
  gboolean r,r2;
  GooCanvasPointerEvents pointer_events = GOO_CANVAS_EVENTS_ALL;
  gdouble width;

  printf("line_item is_item_at (%.2f,%.2f), pointer event: %d",
	 x,y,is_pointer_event);

  g_object_get (G_OBJECT(line),"line-width", &width,NULL);

  cairo_move_to(cr,0,0);
  cairo_line_to(cr,line->dx,line->dy);
  cairo_set_line_width(cr,width+4.0); // to make it easier to pick up
				      // thin lines

  cairo_set_source_rgb(cr,1,0,0);

  r2=goo_canvas_item_simple_check_in_path(simple,x,y,cr,pointer_events);
  r=cairo_in_stroke (cr, x, y);
  printf(", r: %d, r2: %d\n",r,r2);

  return r;
}


/* The class initialization function. Here we set the class' update(), paint()
   and is_item_at() methods. */
static void
goo_line_item_class_init (GooLineItemClass *klass)
{
  GooCanvasItemSimpleClass *simple_class = (GooCanvasItemSimpleClass*) klass;

  printf("line_item class init\n");
  simple_class->simple_update        = goo_line_item_update;
  simple_class->simple_paint         = goo_line_item_paint;
  simple_class->simple_is_item_at    = goo_line_item_is_item_at;
}


/*
 * GooCanvas Demo. Copyright (C) 2006 Damon Chaplin.
 * Released under the GNU LGPL license. See COPYING for details.
 *
 * demo-item.c - a simple demo item.
 */
#ifndef __GOO_LINE_ITEM_H__
#define __GOO_LINE_ITEM_H__

#include <gtk/gtk.h>
#include "goocanvasitemsimple.h"

G_BEGIN_DECLS


#define GOO_TYPE_LINE_ITEM            (goo_line_item_get_type ())
#define GOO_LINE_ITEM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GOO_TYPE_LINE_ITEM, GooLineItem))
#define GOO_LINE_ITEM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GOO_TYPE_LINE_ITEM, GooLineItemClass))
#define GOO_IS_LINE_ITEM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GOO_TYPE_LINE_ITEM))
#define GOO_IS_LINE_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GOO_TYPE_LINE_ITEM))
#define GOO_LINE_ITEM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), GOO_TYPE_LINE_ITEM, GooLineItemClass))


typedef struct _GooLineItem       GooLineItem;
typedef struct _GooLineItemClass  GooLineItemClass;

struct _GooLineItem
{
  GooCanvasItemSimple parent_object;

  gdouble dx,dy;
  gboolean dragging;
};

struct _GooLineItemClass
{
  GooCanvasItemSimpleClass parent_class;
};


GType               goo_line_item_get_type  (void) G_GNUC_CONST;

GooCanvasItem*      goo_line_item_new       (GooCanvasItem      *parent,
					     double x,double y,
					     gdouble             dx,
					     gdouble             dy,
					     ...);


G_END_DECLS

#endif /* __GOO_LINE_ITEM_H__ */
#include <stdlib.h>
#include <goocanvas.h>
#include "line-item.h"

static gboolean dragging = FALSE;
static double drag_x, drag_y;

gboolean  button_event(GooCanvasItem  *item,
		       GooCanvasItem  *target_item,
		       GdkEventButton *event,
		       gpointer        user_data)       
{
  GooCanvasBounds bounds;

  printf("line instance: button pressed\n");

  g_print ("%p received 'button-press' signal at %g, %g (root: %g, %g)\n",
	   item, event->x, event->y, event->x_root, event->y_root);

  goo_canvas_item_get_bounds(item,&bounds);
  printf("bounds: %.2f, %.2f, %.2f, %.2f\n",bounds.x1,bounds.y1,
	 bounds.x2,bounds.y2);

  return FALSE;
  //return TRUE;
}


static gboolean
on_motion_notify (GooCanvasItem *item,
		  GooCanvasItem *target,
		  GdkEventMotion *event,
		  gpointer data)
{
#if 0
  g_print ("received 'motion-notify' signal at %g, %g\n",
	   event->x, event->y);
#endif

  if (dragging && (event->state & GDK_BUTTON1_MASK))
    {
      double new_x = event->x;
      double new_y = event->y;

      goo_canvas_item_translate (item, new_x - drag_x, new_y - drag_y);
    }

  return TRUE;
}


static void
output_items_in_area (GooCanvas *canvas,
		      gdouble x,
		      gdouble y)
{
  static gdouble last_x = 0.0, last_y = 0.0;
  GooCanvasBounds area;
  GList *items, *elem;

  /* Print out the items from the last point to this one. */
  area.x1 = MIN (x, last_x);
  area.x2 = MAX (x, last_x);
  area.y1 = MIN (y, last_y);
  area.y2 = MAX (y, last_y);
  items = goo_canvas_get_items_in_area (canvas, &area, TRUE, FALSE, FALSE);
  for (elem = items; elem; elem = elem->next)
    {
      g_print ("  found items in area (%g, %g - %g, %g): %p\n",
	       area.x1, area.y1, area.x2, area.y2, elem->data);
    }
  g_list_free (items);
  last_x = x;
  last_y = y;
}


static gboolean
on_button_press (GooCanvasItem *item,
		 GooCanvasItem *target,
		 GdkEventButton *event,
		 gpointer data)
{
  GooCanvas *canvas;
  GdkCursor *fleur;
  GList *items, *elem;
  GooCanvasBounds bounds;

  g_print ("%p received 'button-press' signal at %g, %g (root: %g, %g)\n",
	   item, event->x, event->y, event->x_root, event->y_root);

  goo_canvas_item_get_bounds(item,&bounds);
  printf("bounds: %.2f, %.2f, %.2f, %.2f\n",bounds.x1,bounds.y1,
	 bounds.x2,bounds.y2);

  canvas = goo_canvas_item_get_canvas (item);
  items = goo_canvas_get_items_at (canvas, event->x_root, event->y_root,
				   TRUE);
  for (elem = items; elem; elem = elem->next)
    g_print ("  found items: %p\n", elem->data);
  g_list_free (items);

  output_items_in_area (canvas, event->x_root, event->y_root);

  switch (event->button)
    {
    case 1:
      if (event->state & GDK_SHIFT_MASK)
	{
	  goo_canvas_item_remove (item);
	}
      else
	{
	  drag_x = event->x;
	  drag_y = event->y;

	  fleur = gdk_cursor_new (GDK_FLEUR);
	  goo_canvas_pointer_grab (canvas, item,
				   GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
				   fleur,
				   event->time);
	  gdk_cursor_unref (fleur);
	  dragging = TRUE;
	}
      break;

    case 2:
      goo_canvas_item_lower (item, NULL);
      break;

    case 3:
      goo_canvas_item_raise (item, NULL);
      break;

    default:
      break;
    }

  //  return TRUE;
  return FALSE;
}


static gboolean
on_button_release (GooCanvasItem *item,
		   GooCanvasItem *target,
		   GdkEventButton *event,
		   gpointer data)
{
  GooCanvas *canvas;

#if 0
  g_print ("received 'button-release' signal\n");
#endif

  canvas = goo_canvas_item_get_canvas (item);
  goo_canvas_pointer_ungrab (canvas, item, event->time);
  dragging = FALSE;

  return TRUE;
}




/*
*/
static gboolean on_rect_button_press (GooCanvasItem  *view,
				      GooCanvasItem  *target,
				      GdkEventButton *event,
				      gpointer        data);
static gboolean on_delete_event      (GtkWidget      *window,
				      GdkEvent       *event,
				      gpointer        unused_data);

static void
setup_item_signals (GooCanvasItem *item)
{
  g_signal_connect (item, "motion_notify_event",
		    G_CALLBACK(on_motion_notify), NULL);
  g_signal_connect (item, "button_press_event",
		    G_CALLBACK(on_button_press), NULL);
  g_signal_connect (item, "button_release_event",
		    G_CALLBACK(on_button_release), NULL);
}

int
main (int argc, char *argv[])
{
  GtkWidget *window, *scrolled_win, *canvas;
  GooCanvasItem *root, *rect_item, *text_item;
  
  /* Initialize GTK+. */
  gtk_init (&argc, &argv);
  
  /* Create the window and widgets. */
  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  gtk_window_set_default_size (GTK_WINDOW (window), 640, 600);
  gtk_widget_show (window);
  g_signal_connect (window, "delete_event", G_CALLBACK(on_delete_event),
		    NULL);
  
  scrolled_win = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_win),
				       GTK_SHADOW_IN);
  gtk_widget_show (scrolled_win);
  gtk_container_add (GTK_CONTAINER (window), scrolled_win);
  
  canvas = goo_canvas_new ();
  gtk_widget_set_size_request (canvas, 600, 450);
  goo_canvas_set_bounds (GOO_CANVAS (canvas), 0, 0, 1000, 1000);
  gtk_widget_show (canvas);
  gtk_container_add (GTK_CONTAINER (scrolled_win), canvas);
  
  root = goo_canvas_get_root_item (GOO_CANVAS (canvas));
  
  /* Add a few simple items. */
  rect_item = goo_canvas_rect_new (root, 100, 100, 400, 400,
				   "line-width", 10.0,
				   "radius-x", 20.0,
				   "radius-y", 10.0,
				   "stroke-color", "yellow",
				   "fill-color", "red",
				   NULL);
  
  text_item = goo_canvas_text_new (root, "Hello World", 300, 300, -1,
				   GOO_CANVAS_ANCHOR_CENTER,
				   "font", "Sans 24",
				   NULL);
  goo_canvas_item_rotate (text_item, 45, 300, 300);
  
  setup_item_signals(rect_item);


  GooCanvasItem *ellipse1;
  ellipse1 = goo_canvas_ellipse_new (root, 245, 45, 25, 15,
				     "stroke-color", "goldenrod",
				     "line-width", 8.0,
				     NULL);
  setup_item_signals (ellipse1);

  GooCanvasItem *lijn1;
  lijn1 = goo_line_item_new (root, 50,50,140, 30,
			     "stroke-color", "blue",
			     "line-width", 3.0,
			     NULL);

  lijn1 = goo_line_item_new (root, 250,150,130, 30,
			     "stroke-color", "green",
			     NULL);

  GooCanvasLineDash *dash;
  dash = goo_canvas_line_dash_new (2, 5.0, 5.0);

  lijn1 = goo_line_item_new (root, 250,20,100, 140,
			     "stroke-color", "brown",
			     "line-width", 3.0,
			     "line-dash",dash,
			     NULL);
  
  goo_canvas_line_dash_unref(dash);


  /* Pass control to the GTK+ main event loop. */
  gtk_main ();
  
  return 0;
}


/* This handles button presses in item views. We simply output a message to
   the console. */
  static gboolean
  on_rect_button_press (GooCanvasItem  *item,
                        GooCanvasItem  *target,
                        GdkEventButton *event,
                        gpointer        data)
  {
    g_print ("rect item received button press event\n");
    return TRUE;
  }


/* This is our handler for the "delete-event" signal of the window, which
   is emitted when the 'x' close button is clicked. We just exit here. */
static gboolean
on_delete_event (GtkWidget *window,
		 GdkEvent  *event,
		 gpointer   unused_data)
{
  exit (0);
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]