Implement more of LdCanvas.
1. Add methods for coordinate translation between cairo/GtkWidget coordinates and LdDocument coordinates. 2. Draw the grid correctly (also make it significantly faster). 3. Place the canvas into a GtkScrolledWindow and register the set_scroll_adjustments signal in the GtkWidget base class. 4. Allow scrolling inside of an area of 200 x 200 units (for now). In the future, it should be possible to go to infinity.
This commit is contained in:
		
							
								
								
									
										368
									
								
								src/ld-canvas.c
									
									
									
									
									
								
							
							
						
						
									
										368
									
								
								src/ld-canvas.c
									
									
									
									
									
								
							@@ -8,6 +8,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <math.h>
 | 
			
		||||
#include <gtk/gtk.h>
 | 
			
		||||
 | 
			
		||||
#include "config.h"
 | 
			
		||||
@@ -28,14 +29,30 @@
 | 
			
		||||
 * #LdCanvas is used for displaying #LdDocument objects.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Milimetres per inch. */
 | 
			
		||||
#define MM_PER_INCH 25.4
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * LdCanvasPrivate:
 | 
			
		||||
 * @document: A document object assigned to this canvas as a model.
 | 
			
		||||
 * @library: A library object assigned to this canvas as a model.
 | 
			
		||||
 * @adjustment_h: An adjustment object for the horizontal axis, if any.
 | 
			
		||||
 * @adjustment_v: An adjustment object for the vertical axis, if any.
 | 
			
		||||
 * @x: The X coordinate of the center of view.
 | 
			
		||||
 * @y: The Y coordinate of the center of view.
 | 
			
		||||
 * @zoom: The current zoom of the canvas.
 | 
			
		||||
 */
 | 
			
		||||
struct _LdCanvasPrivate
 | 
			
		||||
{
 | 
			
		||||
	LdDocument *document;
 | 
			
		||||
	LdLibrary *library;
 | 
			
		||||
 | 
			
		||||
	GtkAdjustment *adjustment_h;
 | 
			
		||||
	GtkAdjustment *adjustment_v;
 | 
			
		||||
 | 
			
		||||
	gdouble x;
 | 
			
		||||
	gdouble y;
 | 
			
		||||
	gdouble zoom;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
G_DEFINE_TYPE (LdCanvas, ld_canvas, GTK_TYPE_DRAWING_AREA);
 | 
			
		||||
@@ -47,26 +64,43 @@ enum
 | 
			
		||||
	PROP_LIBRARY
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ld_canvas_get_property (GObject *object, guint property_id,
 | 
			
		||||
typedef struct _DrawData DrawData;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * DrawData:
 | 
			
		||||
 * @self: Our #LdCanvas.
 | 
			
		||||
 * @cr: A cairo context to draw on.
 | 
			
		||||
 * @exposed_rect: The area that is to be redrawn.
 | 
			
		||||
 * @scale: Computed size of one document unit in pixels.
 | 
			
		||||
 */
 | 
			
		||||
struct _DrawData
 | 
			
		||||
{
 | 
			
		||||
	LdCanvas *self;
 | 
			
		||||
	cairo_t *cr;
 | 
			
		||||
	cairo_rectangle_t exposed_rect;
 | 
			
		||||
	gdouble scale;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static void ld_canvas_get_property (GObject *object, guint property_id,
 | 
			
		||||
	GValue *value, GParamSpec *pspec);
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ld_canvas_set_property (GObject *object, guint property_id,
 | 
			
		||||
static void ld_canvas_set_property (GObject *object, guint property_id,
 | 
			
		||||
	const GValue *value, GParamSpec *pspec);
 | 
			
		||||
static void ld_canvas_finalize (GObject *gobject);
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ld_canvas_finalize (GObject *gobject);
 | 
			
		||||
static void on_adjustment_value_changed
 | 
			
		||||
	(GtkAdjustment *adjustment, LdCanvas *self);
 | 
			
		||||
static void ld_canvas_set_scroll_adjustments
 | 
			
		||||
	(LdCanvas *self, GtkAdjustment *horizontal, GtkAdjustment *vertical);
 | 
			
		||||
 | 
			
		||||
static gdouble ld_canvas_get_base_unit_in_px (GtkWidget *self);
 | 
			
		||||
static gdouble ld_canvas_get_scale_in_px (LdCanvas *self);
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
on_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data);
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
draw_grid (GtkWidget *widget, cairo_t *cr);
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
canvas_paint (GtkWidget *widget, cairo_t *cr);
 | 
			
		||||
static void on_size_allocate (GtkWidget *widget, GtkAllocation *allocation,
 | 
			
		||||
	gpointer user_data);
 | 
			
		||||
static gboolean on_expose_event (GtkWidget *widget, GdkEventExpose *event,
 | 
			
		||||
	gpointer user_data);
 | 
			
		||||
static void draw_grid (GtkWidget *widget, DrawData *data);
 | 
			
		||||
static void draw_document (GtkWidget *widget, DrawData *data);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -76,6 +110,8 @@ ld_canvas_class_init (LdCanvasClass *klass)
 | 
			
		||||
	GtkWidgetClass *widget_class;
 | 
			
		||||
	GParamSpec *pspec;
 | 
			
		||||
 | 
			
		||||
	widget_class = GTK_WIDGET_CLASS (klass);
 | 
			
		||||
 | 
			
		||||
	object_class = G_OBJECT_CLASS (klass);
 | 
			
		||||
	object_class->get_property = ld_canvas_get_property;
 | 
			
		||||
	object_class->set_property = ld_canvas_set_property;
 | 
			
		||||
@@ -101,16 +137,15 @@ ld_canvas_class_init (LdCanvasClass *klass)
 | 
			
		||||
		LD_TYPE_LIBRARY, G_PARAM_READWRITE);
 | 
			
		||||
	g_object_class_install_property (object_class, PROP_DOCUMENT, pspec);
 | 
			
		||||
 | 
			
		||||
	widget_class = GTK_WIDGET_CLASS (klass);
 | 
			
		||||
	klass->set_scroll_adjustments = ld_canvas_set_scroll_adjustments;
 | 
			
		||||
 | 
			
		||||
/* TODO: Scrolling support; make the comment bellow a gtk-doc comment then. */
 | 
			
		||||
/*
 | 
			
		||||
/**
 | 
			
		||||
 * LdCanvas::set-scroll-adjustments:
 | 
			
		||||
 * @canvas: The canvas object.
 | 
			
		||||
 * @horizontal: The horizontal #GtkAdjustment.
 | 
			
		||||
 * @vertical: The vertical #GtkAdjustment.
 | 
			
		||||
 *
 | 
			
		||||
 * Contents of the library have changed.
 | 
			
		||||
 * Set scroll adjustments for the canvas.
 | 
			
		||||
 */
 | 
			
		||||
/*
 | 
			
		||||
	widget_class->set_scroll_adjustments_signal = g_signal_new
 | 
			
		||||
		("set-scroll-adjustments", G_TYPE_FROM_CLASS (widget_class),
 | 
			
		||||
		G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
 | 
			
		||||
@@ -118,7 +153,7 @@ ld_canvas_class_init (LdCanvasClass *klass)
 | 
			
		||||
		NULL, NULL,
 | 
			
		||||
		g_cclosure_user_marshal_VOID__OBJECT_OBJECT,
 | 
			
		||||
		G_TYPE_NONE, 2, GTK_TYPE_ADJUSTMENT, GTK_TYPE_ADJUSTMENT);
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
	g_type_class_add_private (klass, sizeof (LdCanvasPrivate));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -128,7 +163,14 @@ ld_canvas_init (LdCanvas *self)
 | 
			
		||||
	self->priv = G_TYPE_INSTANCE_GET_PRIVATE
 | 
			
		||||
		(self, LD_TYPE_CANVAS, LdCanvasPrivate);
 | 
			
		||||
 | 
			
		||||
	g_signal_connect (self, "expose-event", G_CALLBACK (on_expose_event), NULL);
 | 
			
		||||
	self->priv->x = 0;
 | 
			
		||||
	self->priv->y = 0;
 | 
			
		||||
	self->priv->zoom = 1;
 | 
			
		||||
 | 
			
		||||
	g_signal_connect (self, "expose-event",
 | 
			
		||||
		G_CALLBACK (on_expose_event), NULL);
 | 
			
		||||
	g_signal_connect (self, "size-allocate",
 | 
			
		||||
		G_CALLBACK (on_size_allocate), NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
@@ -138,6 +180,8 @@ ld_canvas_finalize (GObject *gobject)
 | 
			
		||||
 | 
			
		||||
	self = LD_CANVAS (gobject);
 | 
			
		||||
 | 
			
		||||
	ld_canvas_set_scroll_adjustments (self, NULL, NULL);
 | 
			
		||||
 | 
			
		||||
	if (self->priv->document)
 | 
			
		||||
		g_object_unref (self->priv->document);
 | 
			
		||||
	if (self->priv->library)
 | 
			
		||||
@@ -187,6 +231,128 @@ ld_canvas_set_property (GObject *object, guint property_id,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
ld_canvas_set_scroll_adjustments (LdCanvas *self,
 | 
			
		||||
	GtkAdjustment *horizontal, GtkAdjustment *vertical)
 | 
			
		||||
{
 | 
			
		||||
	/* TODO: Infinite canvas. */
 | 
			
		||||
	GtkWidget *widget;
 | 
			
		||||
	gdouble scale;
 | 
			
		||||
 | 
			
		||||
	widget = GTK_WIDGET (self);
 | 
			
		||||
	scale = ld_canvas_get_scale_in_px (self);
 | 
			
		||||
 | 
			
		||||
	if (horizontal != self->priv->adjustment_h)
 | 
			
		||||
	{
 | 
			
		||||
		if (self->priv->adjustment_h)
 | 
			
		||||
		{
 | 
			
		||||
			g_signal_handlers_disconnect_by_func (self->priv->adjustment_h,
 | 
			
		||||
				on_adjustment_value_changed, self);
 | 
			
		||||
			g_object_unref (self->priv->adjustment_h);
 | 
			
		||||
 | 
			
		||||
			self->priv->adjustment_h = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		if (horizontal)
 | 
			
		||||
		{
 | 
			
		||||
			g_object_ref (horizontal);
 | 
			
		||||
			g_signal_connect (horizontal, "value-changed",
 | 
			
		||||
				G_CALLBACK (on_adjustment_value_changed), self);
 | 
			
		||||
 | 
			
		||||
			horizontal->upper = 100;
 | 
			
		||||
			horizontal->lower = -100;
 | 
			
		||||
			horizontal->step_increment = 0.5;
 | 
			
		||||
			horizontal->page_increment = 5;
 | 
			
		||||
			horizontal->page_size = widget->allocation.width / scale;
 | 
			
		||||
			horizontal->value = -horizontal->page_size / 2;
 | 
			
		||||
 | 
			
		||||
			self->priv->adjustment_h = horizontal;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (vertical != self->priv->adjustment_v)
 | 
			
		||||
	{
 | 
			
		||||
		if (self->priv->adjustment_v)
 | 
			
		||||
		{
 | 
			
		||||
			g_signal_handlers_disconnect_by_func (self->priv->adjustment_v,
 | 
			
		||||
				on_adjustment_value_changed, self);
 | 
			
		||||
			g_object_unref (self->priv->adjustment_v);
 | 
			
		||||
 | 
			
		||||
			self->priv->adjustment_v = NULL;
 | 
			
		||||
		}
 | 
			
		||||
		if (vertical)
 | 
			
		||||
		{
 | 
			
		||||
			g_object_ref (vertical);
 | 
			
		||||
			g_signal_connect (vertical, "value-changed",
 | 
			
		||||
				G_CALLBACK (on_adjustment_value_changed), self);
 | 
			
		||||
 | 
			
		||||
			vertical->upper = 100;
 | 
			
		||||
			vertical->lower = -100;
 | 
			
		||||
			vertical->step_increment = 0.5;
 | 
			
		||||
			vertical->page_increment = 5;
 | 
			
		||||
			vertical->page_size = widget->allocation.height / scale;
 | 
			
		||||
			vertical->value = -vertical->page_size / 2;
 | 
			
		||||
 | 
			
		||||
			self->priv->adjustment_v = vertical;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_adjustment_value_changed (GtkAdjustment *adjustment, LdCanvas *self)
 | 
			
		||||
{
 | 
			
		||||
	GtkWidget *widget;
 | 
			
		||||
	gdouble scale;
 | 
			
		||||
 | 
			
		||||
	widget = GTK_WIDGET (self);
 | 
			
		||||
	scale = ld_canvas_get_scale_in_px (self);
 | 
			
		||||
 | 
			
		||||
	if (adjustment == self->priv->adjustment_h)
 | 
			
		||||
	{
 | 
			
		||||
		self->priv->x = adjustment->value
 | 
			
		||||
			+ widget->allocation.width / scale / 2;
 | 
			
		||||
		gtk_widget_queue_draw (widget);
 | 
			
		||||
	}
 | 
			
		||||
	else if (adjustment == self->priv->adjustment_v)
 | 
			
		||||
	{
 | 
			
		||||
		self->priv->y = adjustment->value
 | 
			
		||||
			+ widget->allocation.height / scale / 2;
 | 
			
		||||
		gtk_widget_queue_draw (widget);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
on_size_allocate (GtkWidget *widget, GtkAllocation *allocation,
 | 
			
		||||
	gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	LdCanvas *self;
 | 
			
		||||
	gdouble scale;
 | 
			
		||||
 | 
			
		||||
	self = LD_CANVAS (widget);
 | 
			
		||||
	scale = ld_canvas_get_scale_in_px (self);
 | 
			
		||||
 | 
			
		||||
	/* FIXME: If the new allocation is bigger, we may see more than
 | 
			
		||||
	 *        what we're supposed to be able to see -> adjust X and Y.
 | 
			
		||||
	 *
 | 
			
		||||
	 *        If the visible area is just so large that we must see more,
 | 
			
		||||
	 *        let's disable the scrollbars in question.
 | 
			
		||||
	 */
 | 
			
		||||
	if (self->priv->adjustment_h)
 | 
			
		||||
	{
 | 
			
		||||
		self->priv->adjustment_h->page_size = allocation->width / scale;
 | 
			
		||||
		self->priv->adjustment_h->value
 | 
			
		||||
			= self->priv->x - self->priv->adjustment_h->page_size / 2;
 | 
			
		||||
		gtk_adjustment_changed (self->priv->adjustment_h);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (self->priv->adjustment_v)
 | 
			
		||||
	{
 | 
			
		||||
		self->priv->adjustment_v->page_size = allocation->height / scale;
 | 
			
		||||
		self->priv->adjustment_v->value
 | 
			
		||||
			= self->priv->y - self->priv->adjustment_v->page_size / 2;
 | 
			
		||||
		gtk_adjustment_changed (self->priv->adjustment_v);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ld_canvas_new:
 | 
			
		||||
 *
 | 
			
		||||
@@ -258,58 +424,154 @@ ld_canvas_get_library (LdCanvas *self)
 | 
			
		||||
	return self->priv->library;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ld_canvas_get_base_unit_in_px:
 | 
			
		||||
 * @self: A #GtkWidget object to retrieve DPI from (indirectly).
 | 
			
		||||
 *
 | 
			
		||||
 * Return value: The length of the base unit in pixels.
 | 
			
		||||
 */
 | 
			
		||||
static gdouble
 | 
			
		||||
ld_canvas_get_base_unit_in_px (GtkWidget *self)
 | 
			
		||||
{
 | 
			
		||||
	g_return_val_if_fail (GTK_IS_WIDGET (self), 1);
 | 
			
		||||
 | 
			
		||||
	/* XXX: It might look better if the unit was rounded to a whole number. */
 | 
			
		||||
	return gdk_screen_get_resolution (gtk_widget_get_screen (self))
 | 
			
		||||
		/ MM_PER_INCH * LD_CANVAS_BASE_UNIT_LENGTH;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * ld_canvas_get_scale_in_px:
 | 
			
		||||
 * @self: An #LdCanvas object.
 | 
			
		||||
 *
 | 
			
		||||
 * Return value: The displayed length of the base unit in pixels.
 | 
			
		||||
 */
 | 
			
		||||
static gdouble
 | 
			
		||||
ld_canvas_get_scale_in_px (LdCanvas *self)
 | 
			
		||||
{
 | 
			
		||||
	g_return_val_if_fail (LD_IS_CANVAS (self), 1);
 | 
			
		||||
 | 
			
		||||
	return ld_canvas_get_base_unit_in_px (GTK_WIDGET (self))
 | 
			
		||||
		* self->priv->zoom;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ld_canvas_translate_canvas_coordinates:
 | 
			
		||||
 * @self: An #LdCanvas object.
 | 
			
		||||
 * @x: The X coordinate to be translated.
 | 
			
		||||
 * @y: The Y coordinate to be translated.
 | 
			
		||||
 *
 | 
			
		||||
 * Translate coordinates located inside the canvas window
 | 
			
		||||
 * into document coordinates.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
ld_canvas_translate_canvas_coordinates (LdCanvas *self, gdouble *x, gdouble *y)
 | 
			
		||||
{
 | 
			
		||||
	GtkWidget *widget;
 | 
			
		||||
	gdouble scale;
 | 
			
		||||
 | 
			
		||||
	g_return_if_fail (LD_IS_CANVAS (self));
 | 
			
		||||
 | 
			
		||||
	widget = GTK_WIDGET (self);
 | 
			
		||||
	scale = ld_canvas_get_scale_in_px (self);
 | 
			
		||||
 | 
			
		||||
	/* We know document coordinates of the center of the canvas, so we may
 | 
			
		||||
	 * translate the given X and Y coordinates to this center and then scale
 | 
			
		||||
	 * them by dividing them by the length of the base unit in pixels
 | 
			
		||||
	 * times zoom of the canvas.
 | 
			
		||||
	 */
 | 
			
		||||
	*x = self->priv->x + (*x - (widget->allocation.width  * 0.5)) / scale;
 | 
			
		||||
	*y = self->priv->y + (*y - (widget->allocation.height * 0.5)) / scale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * ld_canvas_translate_document_coordinates:
 | 
			
		||||
 * @self: An #LdCanvas object.
 | 
			
		||||
 * @x: The X coordinate to be translated.
 | 
			
		||||
 * @y: The Y coordinate to be translated.
 | 
			
		||||
 *
 | 
			
		||||
 * Translate document coordinates into canvas coordinates.
 | 
			
		||||
 */
 | 
			
		||||
void
 | 
			
		||||
ld_canvas_translate_document_coordinates (LdCanvas *self,
 | 
			
		||||
	gdouble *x, gdouble *y)
 | 
			
		||||
{
 | 
			
		||||
	GtkWidget *widget;
 | 
			
		||||
	gdouble scale;
 | 
			
		||||
 | 
			
		||||
	g_return_if_fail (LD_IS_CANVAS (self));
 | 
			
		||||
 | 
			
		||||
	widget = GTK_WIDGET (self);
 | 
			
		||||
	scale = ld_canvas_get_scale_in_px (self);
 | 
			
		||||
 | 
			
		||||
	/* Just the reversal of ld_canvas_translate_canvas_coordinates(). */
 | 
			
		||||
	*x = scale * (*x - self->priv->x) + 0.5 * widget->allocation.width;
 | 
			
		||||
	*y = scale * (*y - self->priv->y) + 0.5 * widget->allocation.height;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static gboolean
 | 
			
		||||
on_expose_event (GtkWidget *widget, GdkEventExpose *event, gpointer user_data)
 | 
			
		||||
{
 | 
			
		||||
	cairo_t *cr;
 | 
			
		||||
	DrawData data;
 | 
			
		||||
 | 
			
		||||
	cr = gdk_cairo_create (widget->window);
 | 
			
		||||
	data.cr = gdk_cairo_create (widget->window);
 | 
			
		||||
	data.self = LD_CANVAS (widget);
 | 
			
		||||
	data.scale = ld_canvas_get_scale_in_px (data.self);
 | 
			
		||||
	data.exposed_rect.x = event->area.x;
 | 
			
		||||
	data.exposed_rect.y = event->area.y;
 | 
			
		||||
	data.exposed_rect.width = event->area.width;
 | 
			
		||||
	data.exposed_rect.height = event->area.height;
 | 
			
		||||
 | 
			
		||||
	cairo_rectangle (cr, event->area.x, event->area.y,
 | 
			
		||||
		event->area.width, event->area.height);
 | 
			
		||||
	cairo_clip (cr);
 | 
			
		||||
	cairo_rectangle (data.cr, data.exposed_rect.x, data.exposed_rect.y,
 | 
			
		||||
		data.exposed_rect.width, data.exposed_rect.height);
 | 
			
		||||
	cairo_clip (data.cr);
 | 
			
		||||
 | 
			
		||||
	canvas_paint (widget, cr);
 | 
			
		||||
	/* Paint the background white. */
 | 
			
		||||
	cairo_set_source_rgb (data.cr, 1, 1, 1);
 | 
			
		||||
	cairo_paint (data.cr);
 | 
			
		||||
 | 
			
		||||
	cairo_destroy (cr);
 | 
			
		||||
	draw_grid (widget, &data);
 | 
			
		||||
	draw_document (widget, &data);
 | 
			
		||||
 | 
			
		||||
	cairo_destroy (data.cr);
 | 
			
		||||
	return FALSE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
draw_grid (GtkWidget *widget, cairo_t *cr)
 | 
			
		||||
draw_grid (GtkWidget *widget, DrawData *data)
 | 
			
		||||
{
 | 
			
		||||
	int x, y;
 | 
			
		||||
	gdouble x_top, y_top;
 | 
			
		||||
	gdouble x, y;
 | 
			
		||||
 | 
			
		||||
	/* Drawing points:
 | 
			
		||||
	 * http://lists.freedesktop.org/archives/cairo/2009-June/017459.html
 | 
			
		||||
	 */
 | 
			
		||||
	cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
 | 
			
		||||
	cairo_set_line_width (cr, 1);
 | 
			
		||||
	cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND);
 | 
			
		||||
	cairo_set_source_rgb (data->cr, 0.5, 0.5, 0.5);
 | 
			
		||||
	cairo_set_line_width (data->cr, 1);
 | 
			
		||||
	cairo_set_line_cap (data->cr, CAIRO_LINE_CAP_ROUND);
 | 
			
		||||
 | 
			
		||||
	/* FIXME: Invariable grid size; it's slow because it draws whole area. */
 | 
			
		||||
	for (x = 0; x < widget->allocation.width; x += 20)
 | 
			
		||||
	/* Get coordinates of the most top-left grid point. */
 | 
			
		||||
	x_top = data->exposed_rect.x;
 | 
			
		||||
	y_top = data->exposed_rect.y;
 | 
			
		||||
	ld_canvas_translate_canvas_coordinates (data->self, &x_top, &y_top);
 | 
			
		||||
	x_top = ceil (x_top);
 | 
			
		||||
	y_top = ceil (y_top);
 | 
			
		||||
	ld_canvas_translate_document_coordinates (data->self, &x_top, &y_top);
 | 
			
		||||
 | 
			
		||||
	/* Iterate over all the points. */
 | 
			
		||||
	for     (x = x_top; x <= data->exposed_rect.x + data->exposed_rect.width;
 | 
			
		||||
	         x += data->scale)
 | 
			
		||||
	{
 | 
			
		||||
		for (y = 0; y < widget->allocation.height; y += 20)
 | 
			
		||||
		for (y = y_top; y <= data->exposed_rect.y + data->exposed_rect.height;
 | 
			
		||||
		     y += data->scale)
 | 
			
		||||
		{
 | 
			
		||||
			/* Drawing sharp lines (also applies to these dots):
 | 
			
		||||
			 * http://www.cairographics.org/FAQ/#sharp_lines
 | 
			
		||||
			 */
 | 
			
		||||
			cairo_move_to (cr, x + 0.5, y + 0.5);
 | 
			
		||||
			cairo_close_path (cr);
 | 
			
		||||
			cairo_stroke (cr);
 | 
			
		||||
			cairo_move_to (data->cr, x, y);
 | 
			
		||||
			cairo_line_to (data->cr, x, y);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	cairo_stroke (data->cr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void
 | 
			
		||||
canvas_paint (GtkWidget *widget, cairo_t *cr)
 | 
			
		||||
draw_document (GtkWidget *widget, DrawData *data)
 | 
			
		||||
{
 | 
			
		||||
	/* Paint a white background. */
 | 
			
		||||
	cairo_set_source_rgb (cr, 1, 1, 1);
 | 
			
		||||
	cairo_paint (cr);
 | 
			
		||||
 | 
			
		||||
	draw_grid (widget, cr);
 | 
			
		||||
	/* TODO: Draw symbols from the document. */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,22 +48,33 @@ struct _LdCanvasClass
 | 
			
		||||
/*< private >*/
 | 
			
		||||
	GtkDrawingAreaClass parent_class;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
	void (*set_scroll_adjustments) (GtkAdjustment *x, GtkAdjustment *y);
 | 
			
		||||
*/
 | 
			
		||||
	void (*set_scroll_adjustments) (LdCanvas *self,
 | 
			
		||||
		GtkAdjustment *horizontal, GtkAdjustment *vertical);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * LD_CANVAS_BASE_UNIT:
 | 
			
		||||
 *
 | 
			
		||||
 * The length of the base unit in milimetres.
 | 
			
		||||
 */
 | 
			
		||||
#define LD_CANVAS_BASE_UNIT_LENGTH 2.5
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
GType ld_canvas_get_type (void) G_GNUC_CONST;
 | 
			
		||||
 | 
			
		||||
LdCanvas *ld_canvas_new (void);
 | 
			
		||||
 | 
			
		||||
void ld_canvas_set_document (LdCanvas *self, LdDocument *document);
 | 
			
		||||
LdDocument *ld_canvas_get_document (LdCanvas *self);
 | 
			
		||||
 | 
			
		||||
void ld_canvas_set_library (LdCanvas *self, LdLibrary *library);
 | 
			
		||||
LdLibrary *ld_canvas_get_library (LdCanvas *self);
 | 
			
		||||
 | 
			
		||||
void ld_canvas_translate_canvas_coordinates (LdCanvas *self,
 | 
			
		||||
	gdouble *x, gdouble *y);
 | 
			
		||||
void ld_canvas_translate_document_coordinates (LdCanvas *self,
 | 
			
		||||
	gdouble *x, gdouble *y);
 | 
			
		||||
 | 
			
		||||
/* TODO: The rest of the interface. */
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -97,6 +97,8 @@ struct _LdWindowMainPrivate
 | 
			
		||||
	GtkWidget *toolbar;
 | 
			
		||||
 | 
			
		||||
	LdLibrary *library;
 | 
			
		||||
 | 
			
		||||
	GtkWidget *canvas_window;
 | 
			
		||||
	LdCanvas *canvas;
 | 
			
		||||
 | 
			
		||||
	GtkWidget *statusbar;
 | 
			
		||||
@@ -270,10 +272,9 @@ ld_window_main_init (LdWindowMain *self)
 | 
			
		||||
	/* TODO in the future: GtkHPaned */
 | 
			
		||||
 | 
			
		||||
	/* Canvas. */
 | 
			
		||||
	/* TODO: Put it into a GtkScrolledWindow. */
 | 
			
		||||
	priv->canvas = ld_canvas_new ();
 | 
			
		||||
 | 
			
		||||
	/* TODO: To be able to draw a symbol menu over the canvas, we may:
 | 
			
		||||
	/* XXX: To be able to draw a symbol menu over the canvas, we may:
 | 
			
		||||
	 *   1. Hook the expose-event and button-{press,release}-event signals.
 | 
			
		||||
	 *   2. Create a hook mechanism in the LdCanvas object.
 | 
			
		||||
	 *     + The cairo context would not have to be created twice.
 | 
			
		||||
@@ -297,7 +298,10 @@ ld_window_main_init (LdWindowMain *self)
 | 
			
		||||
	g_signal_handler_block (priv->canvas,
 | 
			
		||||
		priv->symbol_menu.button_release_handler);
 | 
			
		||||
 | 
			
		||||
	gtk_box_pack_start (GTK_BOX (priv->hbox), GTK_WIDGET (priv->canvas),
 | 
			
		||||
	priv->canvas_window = gtk_scrolled_window_new (NULL, NULL);
 | 
			
		||||
	gtk_container_add (GTK_CONTAINER (priv->canvas_window),
 | 
			
		||||
		GTK_WIDGET (priv->canvas));
 | 
			
		||||
	gtk_box_pack_start (GTK_BOX (priv->hbox), GTK_WIDGET (priv->canvas_window),
 | 
			
		||||
		TRUE, TRUE, 0);
 | 
			
		||||
 | 
			
		||||
	priv->statusbar = gtk_statusbar_new ();
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user