Code

removed leftover debug messages. Toggling new grids now works. Almost ready to commen...
[inkscape.git] / src / display / canvas-grid.cpp
1 #define INKSCAPE_CANVAS_GRID_C
3 /*
4  *
5  * Copyright (C) Johan Engelen 2006-2007 <johan@shouraizou.nl>
6  * Copyright (C) Lauris Kaplinski 2000
7  *
8  */
11 #include "sp-canvas-util.h"
12 #include "display-forward.h"
13 #include <libnr/nr-pixops.h>
14 #include "desktop-handles.h"
15 #include "helper/units.h"
16 #include "svg/svg-color.h"
17 #include "xml/node-event-vector.h"
18 #include "sp-object.h"
20 #include "sp-namedview.h"
21 #include "inkscape.h"
22 #include "desktop.h"
24 #include "../document.h"
26 #include "canvas-grid.h"
27 #include "canvas-axonomgrid.h"
29 namespace Inkscape {
31 static void grid_canvasitem_class_init (GridCanvasItemClass *klass);
32 static void grid_canvasitem_init (GridCanvasItem *grid);
33 static void grid_canvasitem_destroy (GtkObject *object);
35 static void grid_canvasitem_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
36 static void grid_canvasitem_render (SPCanvasItem *item, SPCanvasBuf *buf);
38 static SPCanvasItemClass * parent_class;
40 GtkType
41 grid_canvasitem_get_type (void)
42 {
43     static GtkType grid_canvasitem_type = 0;
45     if (!grid_canvasitem_type) {
46         GtkTypeInfo grid_canvasitem_info = {
47             "GridCanvasItem",
48             sizeof (GridCanvasItem),
49             sizeof (GridCanvasItemClass),
50             (GtkClassInitFunc) grid_canvasitem_class_init,
51             (GtkObjectInitFunc) grid_canvasitem_init,
52             NULL, NULL,
53             (GtkClassInitFunc) NULL
54         };
55         grid_canvasitem_type = gtk_type_unique (sp_canvas_item_get_type (), &grid_canvasitem_info);
56     }
57     return grid_canvasitem_type;
58 }
60 static void
61 grid_canvasitem_class_init (GridCanvasItemClass *klass)
62 {
63     GtkObjectClass *object_class;
64     SPCanvasItemClass *item_class;
66     object_class = (GtkObjectClass *) klass;
67     item_class = (SPCanvasItemClass *) klass;
69     parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
71     object_class->destroy = grid_canvasitem_destroy;
73     item_class->update = grid_canvasitem_update;
74     item_class->render = grid_canvasitem_render;
75 }
77 static void
78 grid_canvasitem_init (GridCanvasItem *griditem)
79 {
80     griditem->grid = NULL;
81 }
83 static void
84 grid_canvasitem_destroy (GtkObject *object)
85 {
86     g_return_if_fail (object != NULL);
87     g_return_if_fail (INKSCAPE_IS_GRID_CANVASITEM (object));
89     if (GTK_OBJECT_CLASS (parent_class)->destroy)
90         (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
91 }
93 /**
94 */
95 static void
96 grid_canvasitem_render (SPCanvasItem * item, SPCanvasBuf * buf)
97 {
98     GridCanvasItem *gridcanvasitem = INKSCAPE_GRID_CANVASITEM (item);
100     sp_canvas_prepare_buffer (buf);
102     if (gridcanvasitem->grid) gridcanvasitem->grid->Render(buf);
105 static void
106 grid_canvasitem_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
108     GridCanvasItem *gridcanvasitem = INKSCAPE_GRID_CANVASITEM (item);
110     if (parent_class->update)
111         (* parent_class->update) (item, affine, flags);
113     if (gridcanvasitem->grid) {
114         gridcanvasitem->grid->Update(affine, flags);
116         sp_canvas_request_redraw (item->canvas,
117                          -1000000, -1000000,
118                          1000000, 1000000);
120         item->x1 = item->y1 = -1000000;
121         item->x2 = item->y2 = 1000000;
122     }
127 // ##########################################################
128 //   CanvasGrid
130     static Inkscape::XML::NodeEventVector const _repr_events = {
131         NULL, /* child_added */
132         NULL, /* child_removed */
133         CanvasGrid::on_repr_attr_changed,
134         NULL, /* content_changed */
135         NULL  /* order_changed */
136     };
138 CanvasGrid::CanvasGrid(SPDesktop *desktop, Inkscape::XML::Node * in_repr)
140     //create canvasitem
141     // FIXME: probably this creation has to be done on demand. I think for multiple desktops it is best if each has their own canvasitem, but share the same CanvasGrid object.
142     canvasitem = INKSCAPE_GRID_CANVASITEM( sp_canvas_item_new(sp_desktop_grid(desktop), INKSCAPE_TYPE_GRID_CANVASITEM, NULL) );
143     gtk_object_ref(GTK_OBJECT(canvasitem));    // since we're keeping a copy, we need to bump up the ref count
144     canvasitem->grid = this;
146     snapenabled = false;
147     visible = false;
149 //    sp_canvas_item_hide(canvasitem);
151     repr = in_repr;
152     if (repr) {
153         repr->addListener (&_repr_events, this);
154     }
155     
156     namedview = sp_desktop_namedview(desktop);
159 CanvasGrid::~CanvasGrid()
161     if (repr) {
162         repr->removeListenerByData (this);
163     }
165     sp_canvas_item_hide(canvasitem);
166    // deref canvasitem
167    gtk_object_unref(GTK_OBJECT(canvasitem));
168    g_free(canvasitem);
171 /*
172 *  writes an <inkscape:grid> child to repr. 
173 */
174 void 
175 CanvasGrid::writeNewGridToRepr(Inkscape::XML::Node * repr, const char * gridtype)
177     if (!repr) return;
178     if (!gridtype) return;
179     
180     // first create the child xml node, then hook it to repr. This order is important, to not set off listeners to repr before the new node is complete.
181     
182     Inkscape::XML::Document *xml_doc = sp_document_repr_doc(sp_desktop_document(SP_ACTIVE_DESKTOP));
183     Inkscape::XML::Node *newnode;
184     newnode = xml_doc->createElement("inkscape:grid");
185     newnode->setAttribute("type",gridtype);
186     
187     repr->appendChild(newnode);
189     // FIXME: add this to history?
190 //    sp_document_done(current_document, SP_VERB_DIALOG_XML_EDITOR,
191 //                     _("Create new element node"));
194 /*
195 * Creates a new CanvasGrid object of type gridtype 
196 */
197 CanvasGrid* 
198 CanvasGrid::NewGrid(SPDesktop *desktop, Inkscape::XML::Node * in_repr, const char * gridtype)
200     if (!desktop) return NULL;
201     if (!in_repr) return NULL;
202     if (!gridtype) return NULL;
203     
204     if (!strcmp(gridtype,"xygrid")) {
205         return (CanvasGrid*) new CanvasXYGrid(desktop, in_repr);
206     } else if (!strcmp(gridtype,"axonometric")) {
207         return (CanvasGrid*) new CanvasAxonomGrid(desktop, in_repr);
208     }
209     
210     return NULL;
214 void
215 CanvasGrid::hide()
217     sp_canvas_item_hide(canvasitem);
218     visible = false;
219     disable_snapping(); // temporary hack, because at the moment visibilty and snapping are linked
222 void
223 CanvasGrid::show()
225     sp_canvas_item_show(canvasitem);
226     visible = true;
227     enable_snapping(); // temporary hack, because at the moment visibilty and snapping are linked
230 void 
231 CanvasGrid::set_visibility(bool visible)
233     this->visible = visible;
234     if(visible) {
235         show();
236     } else {
237         hide();
238     }
241 void
242 CanvasGrid::toggle_visibility()
244     visible = !visible;
245     set_visibility(visible);
248 void
249 CanvasGrid::on_repr_attr_changed (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive, void * data)
251     if (!data)
252         return;
254     ((CanvasGrid*) data)->onReprAttrChanged(repr, key, oldval, newval, is_interactive);
259 // ##########################################################
260 //   CanvasXYGrid
262 static void grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba);
263 static void grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba);
266 /**
267 * A DIRECT COPY-PASTE FROM DOCUMENT-PROPERTIES.CPP  TO QUICKLY GET RESULTS
269  * Helper function that attachs widgets in a 3xn table. The widgets come in an
270  * array that has two entries per table row. The two entries code for four
271  * possible cases: (0,0) means insert space in first column; (0, non-0) means
272  * widget in columns 2-3; (non-0, 0) means label in columns 1-3; and
273  * (non-0, non-0) means two widgets in columns 2 and 3.
274 **/
275 #define SPACE_SIZE_X 15
276 #define SPACE_SIZE_Y 10
277 static inline void
278 attach_all (Gtk::Table &table, const Gtk::Widget *arr[], unsigned size, int start = 0)
280     for (unsigned i=0, r=start; i<size/sizeof(Gtk::Widget*); i+=2)
281     {
282         if (arr[i] && arr[i+1])
283         {
284             table.attach (const_cast<Gtk::Widget&>(*arr[i]),   1, 2, r, r+1,
285                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
286             table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 2, 3, r, r+1,
287                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
288         }
289         else
290         {
291             if (arr[i+1])
292                 table.attach (const_cast<Gtk::Widget&>(*arr[i+1]), 1, 3, r, r+1,
293                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
294             else if (arr[i])
295             {
296                 Gtk::Label& label = reinterpret_cast<Gtk::Label&> (const_cast<Gtk::Widget&>(*arr[i]));
297                 label.set_alignment (0.0);
298                 table.attach (label, 0, 3, r, r+1,
299                       Gtk::FILL|Gtk::EXPAND, (Gtk::AttachOptions)0,0,0);
300             }
301             else
302             {
303                 Gtk::HBox *space = manage (new Gtk::HBox);
304                 space->set_size_request (SPACE_SIZE_X, SPACE_SIZE_Y);
305                 table.attach (*space, 0, 1, r, r+1,
306                       (Gtk::AttachOptions)0, (Gtk::AttachOptions)0,0,0);
307             }
308         }
309         ++r;
310     }
313 CanvasXYGrid::CanvasXYGrid (SPDesktop *desktop, Inkscape::XML::Node * in_repr)
314     : CanvasGrid(desktop, in_repr), table(1, 1)
316     origin[NR::X] = origin[NR::Y] = 0.0;
317 //            nv->gridcolor = (nv->gridcolor & 0xff) | (DEFAULTGRIDCOLOR & 0xffffff00);
318 //      case SP_ATTR_GRIDOPACITY:
319 //            nv->gridcolor = (nv->gridcolor & 0xffffff00) | (DEFAULTGRIDCOLOR & 0xff);
320     color = 0xff3f3f20;
321     empcolor = 0xFF3F3F40;
322     empspacing = 5;
323     spacing[NR::X] = spacing[NR::Y] = 8.0;
324     gridunit = &sp_unit_get_by_id(SP_UNIT_PX);
325     
326     snapper = new CanvasXYGridSnapper(this, namedview, 0);
327     
328     // initialize widgets:
329     vbox.set_border_width(2);
330     table.set_spacings(2);
331     vbox.pack_start(table, false, false, 0);
333     _rumg.init (_("Grid _units:"), "units", _wr, repr);
334     _rsu_ox.init (_("_Origin X:"), _("X coordinate of grid origin"),
335                   "originx", _rumg, _wr, repr);
336     _rsu_oy.init (_("O_rigin Y:"), _("Y coordinate of grid origin"),
337                   "originy", _rumg, _wr, repr);
338     _rsu_sx.init (_("Spacing _X:"), _("Distance between vertical grid lines"),
339                   "spacingx", _rumg, _wr, repr);
340     _rsu_sy.init (_("Spacing _Y:"), _("Distance between horizontal grid lines"),
341                   "spacingy", _rumg, _wr, repr);
342     _rcp_gcol.init (_("Grid line _color:"), _("Grid line color"),
343                     _("Color of grid lines"), "color", "opacity", _wr, repr);
344     _rcp_gmcol.init (_("Ma_jor grid line color:"), _("Major grid line color"),
345                      _("Color of the major (highlighted) grid lines"),
346                      "empcolor", "empopacity", _wr, repr);
347     _rsi.init (_("_Major grid line every:"), _("lines"), "empspacing", _wr, repr);
349     const Gtk::Widget* widget_array[] =
350     {
351         0,                  _rcbgrid._button,
352         _rumg._label,       _rumg._sel,
353         0,                  _rsu_ox.getSU(),
354         0,                  _rsu_oy.getSU(),
355         0,                  _rsu_sx.getSU(),
356         0,                  _rsu_sy.getSU(),
357         _rcp_gcol._label,   _rcp_gcol._cp,
358         0,                  0,
359         _rcp_gmcol._label,  _rcp_gmcol._cp,
360         _rsi._label,        &_rsi._hbox,
361     };
363     attach_all (table, widget_array, sizeof(widget_array));
365     vbox.show();
367     if (repr) readRepr();
368     updateWidgets();
371 CanvasXYGrid::~CanvasXYGrid ()
373    if (snapper) delete snapper;
377 /* fixme: Collect all these length parsing methods and think common sane API */
379 static gboolean sp_nv_read_length(const gchar *str, guint base, gdouble *val, const SPUnit **unit)
381     if (!str) {
382         return FALSE;
383     }
385     gchar *u;
386     gdouble v = g_ascii_strtod(str, &u);
387     if (!u) {
388         return FALSE;
389     }
390     while (isspace(*u)) {
391         u += 1;
392     }
394     if (!*u) {
395         /* No unit specified - keep default */
396         *val = v;
397         return TRUE;
398     }
400     if (base & SP_UNIT_DEVICE) {
401         if (u[0] && u[1] && !isalnum(u[2]) && !strncmp(u, "px", 2)) {
402             *unit = &sp_unit_get_by_id(SP_UNIT_PX);
403             *val = v;
404             return TRUE;
405         }
406     }
408     if (base & SP_UNIT_ABSOLUTE) {
409         if (!strncmp(u, "pt", 2)) {
410             *unit = &sp_unit_get_by_id(SP_UNIT_PT);
411         } else if (!strncmp(u, "mm", 2)) {
412             *unit = &sp_unit_get_by_id(SP_UNIT_MM);
413         } else if (!strncmp(u, "cm", 2)) {
414             *unit = &sp_unit_get_by_id(SP_UNIT_CM);
415         } else if (!strncmp(u, "m", 1)) {
416             *unit = &sp_unit_get_by_id(SP_UNIT_M);
417         } else if (!strncmp(u, "in", 2)) {
418             *unit = &sp_unit_get_by_id(SP_UNIT_IN);
419         } else {
420             return FALSE;
421         }
422         *val = v;
423         return TRUE;
424     }
426     return FALSE;
429 static gboolean sp_nv_read_opacity(const gchar *str, guint32 *color)
431     if (!str) {
432         return FALSE;
433     }
435     gchar *u;
436     gdouble v = g_ascii_strtod(str, &u);
437     if (!u) {
438         return FALSE;
439     }
440     v = CLAMP(v, 0.0, 1.0);
442     *color = (*color & 0xffffff00) | (guint32) floor(v * 255.9999);
444     return TRUE;
449 void
450 CanvasXYGrid::readRepr()
452     gchar const* value;
453     if ( (value = repr->attribute("originx")) ) {
454         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[NR::X], &gridunit);
455         origin[NR::X] = sp_units_get_pixels(origin[NR::X], *(gridunit));
456     }
457     if ( (value = repr->attribute("originy")) ) {
458         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &origin[NR::Y], &gridunit);
459         origin[NR::Y] = sp_units_get_pixels(origin[NR::Y], *(gridunit));
460     }
462     if ( (value = repr->attribute("spacingx")) ) {
463         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[NR::X], &gridunit);
464         spacing[NR::X] = sp_units_get_pixels(spacing[NR::X], *(gridunit));
465     }
466     if ( (value = repr->attribute("spacingy")) ) {
467         sp_nv_read_length(value, SP_UNIT_ABSOLUTE | SP_UNIT_DEVICE, &spacing[NR::Y], &gridunit);
468         spacing[NR::Y] = sp_units_get_pixels(spacing[NR::Y], *(gridunit));
469     }
471     if ( (value = repr->attribute("color")) ) {
472         color = (color & 0xff) | sp_svg_read_color(value, color);
473     }
475     if ( (value = repr->attribute("empcolor")) ) {
476         empcolor = (empcolor & 0xff) | sp_svg_read_color(value, empcolor);
477     }
479     if ( (value = repr->attribute("opacity")) ) {
480         sp_nv_read_opacity(value, &color);
481     }
482     if ( (value = repr->attribute("empopacity")) ) {
483         sp_nv_read_opacity(value, &empcolor);
484     }
486     if ( (value = repr->attribute("empspacing")) ) {
487         empspacing = atoi(value);
488     }
490     sp_canvas_item_request_update (canvasitem);
492     return;
495 /**
496  * Called when XML node attribute changed; updates dialog widgets if change was not done by widgets themselves.
497  */
498 void
499 CanvasXYGrid::onReprAttrChanged (Inkscape::XML::Node * repr, const gchar *key, const gchar *oldval, const gchar *newval, bool is_interactive)
501     readRepr();
503     if ( ! (_wr.isUpdating()) )
504         updateWidgets();
510 Gtk::Widget &
511 CanvasXYGrid::getWidget()
513     return vbox;
517 /**
518  * Update dialog widgets from object's values.
519  */
520 void
521 CanvasXYGrid::updateWidgets()
523     if (_wr.isUpdating()) return;
525     _wr.setUpdating (true);
527 //    _rrb_gridtype.setValue (nv->gridtype);
528     _rumg.setUnit (gridunit);
530     gdouble val;
531     val = origin[NR::X];
532     val = sp_pixels_get_units (val, *(gridunit));
533     _rsu_ox.setValue (val);
534     val = origin[NR::Y];
535     val = sp_pixels_get_units (val, *(gridunit));
536     _rsu_oy.setValue (val);
537     val = spacing[NR::X];
538     double gridx = sp_pixels_get_units (val, *(gridunit));
539     _rsu_sx.setValue (gridx);
540     val = spacing[NR::Y];
541     double gridy = sp_pixels_get_units (val, *(gridunit));
542     _rsu_sy.setValue (gridy);
544     _rcp_gcol.setRgba32 (color);
545     _rcp_gmcol.setRgba32 (empcolor);
546     _rsi.setValue (empspacing);
548     _wr.setUpdating (false);
549     
550     return;
555 void
556 CanvasXYGrid::Update (NR::Matrix const &affine, unsigned int flags)
558     ow = origin * affine;
559     sw = spacing * affine;
560     sw -= NR::Point(affine[4], affine[5]);
562     for(int dim = 0; dim < 2; dim++) {
563         gint scaling_factor = 5; //empspacing;
565         if (scaling_factor <= 1)
566             scaling_factor = 5;
568         scaled[dim] = FALSE;
569         sw[dim] = fabs (sw[dim]);
570         while (sw[dim] < 8.0) {
571             scaled[dim] = TRUE;
572             sw[dim] *= scaling_factor;
573             /* First pass, go up to the major line spacing, then
574                keep increasing by two. */
575             scaling_factor = 2;
576         }
577     }
580 void
581 CanvasXYGrid::Render (SPCanvasBuf *buf)
583     const gdouble sxg = floor ((buf->rect.x0 - ow[NR::X]) / sw[NR::X]) * sw[NR::X] + ow[NR::X];
584     const gint  xlinestart = (gint) Inkscape::round((sxg - ow[NR::X]) / sw[NR::X]);
585     const gdouble syg = floor ((buf->rect.y0 - ow[NR::Y]) / sw[NR::Y]) * sw[NR::Y] + ow[NR::Y];
586     const gint  ylinestart = (gint) Inkscape::round((syg - ow[NR::Y]) / sw[NR::Y]);
588     gint ylinenum;
589     gdouble y;
590     for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += sw[NR::Y], ylinenum++) {
591         const gint y0 = (gint) Inkscape::round(y);
593         if (!scaled[NR::Y] && (ylinenum % 5 /*empspacing*/) == 0) {
594             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, empcolor);
595         } else {
596             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, color);
597         }
598     }
600     gint xlinenum;
601     gdouble x;
602     for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += sw[NR::X], xlinenum++) {
603         const gint ix = (gint) Inkscape::round(x);
604         if (!scaled[NR::X] && (xlinenum % 5 /*empspacing*/) == 0) {
605             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, empcolor);
606         } else {
607             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, color);
608         }
609     }
623 /**
624  * \return x rounded to the nearest multiple of c1 plus c0.
625  *
626  * \note
627  * If c1==0 (and c0 is finite), then returns +/-inf.  This makes grid spacing of zero
628  * mean "ignore the grid in this dimention".  We're currently discussing "good" semantics
629  * for guide/grid snapping.
630  */
632 /* FIXME: move this somewhere else, perhaps */
633 static double round_to_nearest_multiple_plus(double x, double const c1, double const c0)
635     return floor((x - c0) / c1 + .5) * c1 + c0;
638 CanvasXYGridSnapper::CanvasXYGridSnapper(CanvasXYGrid *grid, SPNamedView const *nv, NR::Coord const d) : LineSnapper(nv, d)
640     this->grid = grid;
643 LineSnapper::LineList 
644 CanvasXYGridSnapper::_getSnapLines(NR::Point const &p) const
646     LineList s;
648     if ( grid == NULL ) {
649         return s;
650     }
652     for (unsigned int i = 0; i < 2; ++i) {
654         /* This is to make sure we snap to only visible grid lines */
655         double scaled_spacing = grid->sw[i]; // this is spacing of visible lines if screen pixels
657         // convert screen pixels to px
658         // FIXME: after we switch to snapping dist in screen pixels, this will be unnecessary
659         if (SP_ACTIVE_DESKTOP) {
660             scaled_spacing /= SP_ACTIVE_DESKTOP->current_zoom();
661         }
663         NR::Coord const rounded = round_to_nearest_multiple_plus(p[i],
664                                                                  scaled_spacing,
665                                                                  grid->origin[i]);
667         s.push_back(std::make_pair(NR::Dim2(i), rounded));
668     }
670     return s;
703 enum {
704     ARG_0,
705     ARG_ORIGINX,
706     ARG_ORIGINY,
707     ARG_SPACINGX,
708     ARG_SPACINGY,
709     ARG_COLOR,
710     ARG_EMPCOLOR,
711     ARG_EMPSPACING
712 };
715 static void cxygrid_class_init (CXYGridClass *klass);
716 static void cxygrid_init (CXYGrid *grid);
717 static void cxygrid_destroy (GtkObject *object);
718 static void cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id);
720 static void cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags);
721 static void cxygrid_render (SPCanvasItem *item, SPCanvasBuf *buf);
723 //static SPCanvasItemClass * parent_class;
725 GtkType
726 cxygrid_get_type (void)
728     static GtkType cxygrid_type = 0;
730     if (!cxygrid_type) {
731         GtkTypeInfo cxygrid_info = {
732             "CXYGrid",
733             sizeof (CXYGrid),
734             sizeof (CXYGridClass),
735             (GtkClassInitFunc) cxygrid_class_init,
736             (GtkObjectInitFunc) cxygrid_init,
737             NULL, NULL,
738             (GtkClassInitFunc) NULL
739         };
740         cxygrid_type = gtk_type_unique (sp_canvas_item_get_type (), &cxygrid_info);
741     }
742     return cxygrid_type;
745 static void
746 cxygrid_class_init (CXYGridClass *klass)
748     GtkObjectClass *object_class;
749     SPCanvasItemClass *item_class;
751     object_class = (GtkObjectClass *) klass;
752     item_class = (SPCanvasItemClass *) klass;
754     parent_class = (SPCanvasItemClass*)gtk_type_class (sp_canvas_item_get_type ());
756     gtk_object_add_arg_type ("CXYGrid::originx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINX);
757     gtk_object_add_arg_type ("CXYGrid::originy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_ORIGINY);
758     gtk_object_add_arg_type ("CXYGrid::spacingx", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGX);
759     gtk_object_add_arg_type ("CXYGrid::spacingy", GTK_TYPE_DOUBLE, GTK_ARG_WRITABLE, ARG_SPACINGY);
760     gtk_object_add_arg_type ("CXYGrid::color", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_COLOR);
761     gtk_object_add_arg_type ("CXYGrid::empcolor", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPCOLOR);
762     gtk_object_add_arg_type ("CXYGrid::empspacing", GTK_TYPE_INT, GTK_ARG_WRITABLE, ARG_EMPSPACING);
764     object_class->destroy = cxygrid_destroy;
765     object_class->set_arg = cxygrid_set_arg;
767     item_class->update = cxygrid_update;
768     item_class->render = cxygrid_render;
771 static void
772 cxygrid_init (CXYGrid *grid)
774     grid->origin[NR::X] = grid->origin[NR::Y] = 0.0;
775     grid->spacing[NR::X] = grid->spacing[NR::Y] = 8.0;
776     grid->color = 0x0000ff7f;
777     grid->empcolor = 0x3F3FFF40;
778     grid->empspacing = 5;
781 static void
782 cxygrid_destroy (GtkObject *object)
784     g_return_if_fail (object != NULL);
785     g_return_if_fail (INKSCAPE_IS_CXYGRID (object));
787     if (GTK_OBJECT_CLASS (parent_class)->destroy)
788         (* GTK_OBJECT_CLASS (parent_class)->destroy) (object);
791 static void
792 cxygrid_set_arg (GtkObject *object, GtkArg *arg, guint arg_id)
794     SPCanvasItem *item = SP_CANVAS_ITEM (object);
795     CXYGrid *grid = INKSCAPE_CXYGRID (object);
797     switch (arg_id) {
798     case ARG_ORIGINX:
799         grid->origin[NR::X] = GTK_VALUE_DOUBLE (* arg);
800         sp_canvas_item_request_update (item);
801         break;
802     case ARG_ORIGINY:
803         grid->origin[NR::Y] = GTK_VALUE_DOUBLE (* arg);
804         sp_canvas_item_request_update (item);
805         break;
806     case ARG_SPACINGX:
807         grid->spacing[NR::X] = GTK_VALUE_DOUBLE (* arg);
808         if (grid->spacing[NR::X] < 0.01) grid->spacing[NR::X] = 0.01;
809         sp_canvas_item_request_update (item);
810         break;
811     case ARG_SPACINGY:
812         grid->spacing[NR::Y] = GTK_VALUE_DOUBLE (* arg);
813         if (grid->spacing[NR::Y] < 0.01) grid->spacing[NR::Y] = 0.01;
814         sp_canvas_item_request_update (item);
815         break;
816     case ARG_COLOR:
817         grid->color = GTK_VALUE_INT (* arg);
818         sp_canvas_item_request_update (item);
819         break;
820     case ARG_EMPCOLOR:
821         grid->empcolor = GTK_VALUE_INT (* arg);
822         sp_canvas_item_request_update (item);
823         break;
824     case ARG_EMPSPACING:
825         grid->empspacing = GTK_VALUE_INT (* arg);
826         // std::cout << "Emphasis Spacing: " << grid->empspacing << std::endl;
827         sp_canvas_item_request_update (item);
828         break;
829     default:
830         break;
831     }
834 static void
835 grid_hline (SPCanvasBuf *buf, gint y, gint xs, gint xe, guint32 rgba)
837     if ((y >= buf->rect.y0) && (y < buf->rect.y1)) {
838         guint r, g, b, a;
839         gint x0, x1, x;
840         guchar *p;
841         r = NR_RGBA32_R (rgba);
842         g = NR_RGBA32_G (rgba);
843         b = NR_RGBA32_B (rgba);
844         a = NR_RGBA32_A (rgba);
845         x0 = MAX (buf->rect.x0, xs);
846         x1 = MIN (buf->rect.x1, xe + 1);
847         p = buf->buf + (y - buf->rect.y0) * buf->buf_rowstride + (x0 - buf->rect.x0) * 3;
848         for (x = x0; x < x1; x++) {
849             p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
850             p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
851             p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
852             p += 3;
853         }
854     }
857 static void
858 grid_vline (SPCanvasBuf *buf, gint x, gint ys, gint ye, guint32 rgba)
860     if ((x >= buf->rect.x0) && (x < buf->rect.x1)) {
861         guint r, g, b, a;
862         gint y0, y1, y;
863         guchar *p;
864         r = NR_RGBA32_R(rgba);
865         g = NR_RGBA32_G (rgba);
866         b = NR_RGBA32_B (rgba);
867         a = NR_RGBA32_A (rgba);
868         y0 = MAX (buf->rect.y0, ys);
869         y1 = MIN (buf->rect.y1, ye + 1);
870         p = buf->buf + (y0 - buf->rect.y0) * buf->buf_rowstride + (x - buf->rect.x0) * 3;
871         for (y = y0; y < y1; y++) {
872             p[0] = NR_COMPOSEN11_1111 (r, a, p[0]);
873             p[1] = NR_COMPOSEN11_1111 (g, a, p[1]);
874             p[2] = NR_COMPOSEN11_1111 (b, a, p[2]);
875             p += buf->buf_rowstride;
876         }
877     }
880 /**
881     \brief  This function renders the grid on a particular canvas buffer
882     \param  item  The grid to render on the buffer
883     \param  buf   The buffer to render the grid on
885     This function gets called a touch more than you might believe,
886     about once per tile.  This means that it could probably be optimized
887     and help things out.
889     Basically this function has to determine where in the canvas it is,
890     and how that associates with the grid.  It does this first by looking
891     at the bounding box of the buffer, and then calculates where the grid
892     starts in that buffer.  It will then step through grid lines until
893     it is outside of the buffer.
895     For each grid line it is drawn using the function \c sp_grid_hline
896     or \c sp_grid_vline.  These are convience functions for the sake
897     of making the function easier to read.
899     Also, there are emphisized lines on the grid.  While the \c syg and
900     \c sxg variable track grid positioning, the \c xlinestart and \c
901     ylinestart variables track the 'count' of what lines they are.  If
902     that count is a multiple of the line seperation between emphisis
903     lines, then that line is drawn in the emphisis color.
904 */
905 static void
906 cxygrid_render (SPCanvasItem * item, SPCanvasBuf * buf)
908     CXYGrid *grid = INKSCAPE_CXYGRID (item);
910     sp_canvas_prepare_buffer (buf);
912     const gdouble sxg = floor ((buf->rect.x0 - grid->ow[NR::X]) / grid->sw[NR::X]) * grid->sw[NR::X] + grid->ow[NR::X];
913     const gint  xlinestart = (gint) Inkscape::round((sxg - grid->ow[NR::X]) / grid->sw[NR::X]);
914     const gdouble syg = floor ((buf->rect.y0 - grid->ow[NR::Y]) / grid->sw[NR::Y]) * grid->sw[NR::Y] + grid->ow[NR::Y];
915     const gint  ylinestart = (gint) Inkscape::round((syg - grid->ow[NR::Y]) / grid->sw[NR::Y]);
917     gint ylinenum;
918     gdouble y;
919     for (y = syg, ylinenum = ylinestart; y < buf->rect.y1; y += grid->sw[NR::Y], ylinenum++) {
920         const gint y0 = (gint) Inkscape::round(y);
922         if (!grid->scaled[NR::Y] && (ylinenum % grid->empspacing) == 0) {
923             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->empcolor);
924         } else {
925             grid_hline (buf, y0, buf->rect.x0, buf->rect.x1 - 1, grid->color);
926         }
927     }
929     gint xlinenum;
930     gdouble x;
931     for (x = sxg, xlinenum = xlinestart; x < buf->rect.x1; x += grid->sw[NR::X], xlinenum++) {
932         const gint ix = (gint) Inkscape::round(x);
933         if (!grid->scaled[NR::X] && (xlinenum % grid->empspacing) == 0) {
934             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->empcolor);
935         } else {
936             grid_vline (buf, ix, buf->rect.y0, buf->rect.y1, grid->color);
937         }
938     }
941 static void
942 cxygrid_update (SPCanvasItem *item, NR::Matrix const &affine, unsigned int flags)
944     CXYGrid *grid = INKSCAPE_CXYGRID (item);
946     if (parent_class->update)
947         (* parent_class->update) (item, affine, flags);
949     grid->ow = grid->origin * affine;
950     grid->sw = grid->spacing * affine;
951     grid->sw -= NR::Point(affine[4], affine[5]);
953     for(int dim = 0; dim < 2; dim++) {
954         gint scaling_factor = grid->empspacing;
956         if (scaling_factor <= 1)
957             scaling_factor = 5;
959         grid->scaled[dim] = FALSE;
960         grid->sw[dim] = fabs (grid->sw[dim]);
961         while (grid->sw[dim] < 8.0) {
962             grid->scaled[dim] = TRUE;
963             grid->sw[dim] *= scaling_factor;
964             /* First pass, go up to the major line spacing, then
965                keep increasing by two. */
966             scaling_factor = 2;
967         }
968     }
970     if (grid->empspacing == 0) {
971         grid->scaled[NR::Y] = TRUE;
972         grid->scaled[NR::X] = TRUE;
973     }
975     sp_canvas_request_redraw (item->canvas,
976                      -1000000, -1000000,
977                      1000000, 1000000);
979     item->x1 = item->y1 = -1000000;
980     item->x2 = item->y2 = 1000000;
984 }; /* namespace Inkscape */
986 /*
987   Local Variables:
988   mode:c++
989   c-file-style:"stroustrup"
990   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
991   indent-tabs-mode:nil
992   fill-column:99
993   End:
994 */
995 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :