]> git.donarmstrong.com Git - xournal.git/blob - src/xo-paint.c
6361af72e2d30cdc83dba3b189f89859330c1a2d
[xournal.git] / src / xo-paint.c
1 #ifdef HAVE_CONFIG_H
2 #  include <config.h>
3 #endif
4
5 #include <math.h>
6 #include <string.h>
7 #include <gtk/gtk.h>
8 #include <libgnomecanvas/libgnomecanvas.h>
9
10 #include <libart_lgpl/art_vpath_dash.h>
11
12 #include "xournal.h"
13 #include "xo-callbacks.h"
14 #include "xo-interface.h"
15 #include "xo-support.h"
16 #include "xo-misc.h"
17 #include "xo-paint.h"
18
19 /***** Win32 fix for gdk_cursor_new_from_pixmap() by Dirk Gerrits ****/
20
21 #ifdef WIN32
22 gboolean colors_too_similar(const GdkColor *colora, const GdkColor *colorb)
23 {
24   return (abs(colora->red - colorb->red) < 256 &&
25           abs(colora->green - colorb->green) < 256 &&
26           abs(colora->blue - colorb->blue) < 256);
27 }
28
29 /* gdk_cursor_new_from_pixmap is broken on Windows.
30    this is a workaround using gdk_cursor_new_from_pixbuf. */
31 GdkCursor* fixed_gdk_cursor_new_from_pixmap(GdkPixmap *source, GdkPixmap *mask,
32                                             const GdkColor *fg, const GdkColor *bg,
33                                             gint x, gint y)
34 {
35   GdkPixmap *rgb_pixmap;
36   GdkGC *gc;
37   GdkPixbuf *rgb_pixbuf, *rgba_pixbuf;
38   GdkCursor *cursor;
39   int width, height;
40
41   /* HACK!  It seems impossible to work with RGBA pixmaps directly in
42      GDK-Win32.  Instead we pick some third color, different from fg
43      and bg, and use that as the 'transparent color'.  We do this using
44      colors_too_similar (see above) because two colors could be
45      unequal in GdkColor's 16-bit/sample, but equal in GdkPixbuf's
46      8-bit/sample. */
47   GdkColor candidates[3] = {{0,65535,0,0}, {0,0,65535,0}, {0,0,0,65535}};
48   GdkColor *trans = &candidates[0];
49   if (colors_too_similar(trans, fg) || colors_too_similar(trans, bg)) {
50     trans = &candidates[1];
51     if (colors_too_similar(trans, fg) || colors_too_similar(trans, bg)) {
52       trans = &candidates[2];
53     }
54   } /* trans is now guaranteed to be unique from fg and bg */
55
56   /* create an empty pixmap to hold the cursor image */
57   gdk_drawable_get_size(source, &width, &height);
58   rgb_pixmap = gdk_pixmap_new(NULL, width, height, 24);
59
60   /* blit the bitmaps defining the cursor onto a transparent background */
61   gc = gdk_gc_new(rgb_pixmap);
62   gdk_gc_set_fill(gc, GDK_SOLID);
63   gdk_gc_set_rgb_fg_color(gc, trans);
64   gdk_draw_rectangle(rgb_pixmap, gc, TRUE, 0, 0, width, height);
65   gdk_gc_set_fill(gc, GDK_OPAQUE_STIPPLED);
66   gdk_gc_set_stipple(gc, source);
67   gdk_gc_set_clip_mask(gc, mask);
68   gdk_gc_set_rgb_fg_color(gc, fg);
69   gdk_gc_set_rgb_bg_color(gc, bg);
70   gdk_draw_rectangle(rgb_pixmap, gc, TRUE, 0, 0, width, height);
71   gdk_gc_unref(gc);
72
73   /* create a cursor out of the created pixmap */
74   rgb_pixbuf = gdk_pixbuf_get_from_drawable(
75     NULL, rgb_pixmap, gdk_colormap_get_system(), 0, 0, 0, 0, width, height);
76   gdk_pixmap_unref(rgb_pixmap);
77   rgba_pixbuf = gdk_pixbuf_add_alpha(
78     rgb_pixbuf, TRUE, trans->red, trans->green, trans->blue);
79   gdk_pixbuf_unref(rgb_pixbuf);
80   cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), rgba_pixbuf, x, y);
81   gdk_pixbuf_unref(rgba_pixbuf);
82
83   return cursor;
84 }
85 #define gdk_cursor_new_from_pixmap fixed_gdk_cursor_new_from_pixmap
86 #endif
87
88
89 /************** drawing nice cursors *********/
90
91 static char cursor_pen_bits[] = {
92    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
93    0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
95
96 static char cursor_eraser_bits[] = {
97    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x08, 0x08, 0x08, 0x08,
98    0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x0f,
99    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
100
101 static char cursor_eraser_mask[] = {
102    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f,
103    0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f,
104    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
105
106 void set_cursor_busy(gboolean busy)
107 {
108   GdkCursor *cursor;
109   
110   if (busy) {
111     cursor = gdk_cursor_new(GDK_WATCH);
112     gdk_window_set_cursor(GTK_WIDGET(winMain)->window, cursor);
113     gdk_window_set_cursor(GTK_WIDGET(canvas)->window, cursor);
114     gdk_cursor_unref(cursor);
115   }
116   else {
117     gdk_window_set_cursor(GTK_WIDGET(winMain)->window, NULL);
118     update_cursor();
119   }
120   gdk_display_sync(gdk_display_get_default());
121 }
122
123 void update_cursor(void)
124 {
125   GdkPixmap *source, *mask;
126   GdkColor fg = {0, 0, 0, 0}, bg = {0, 65535, 65535, 65535};
127
128   ui.is_sel_cursor = FALSE;
129   if (GTK_WIDGET(canvas)->window == NULL) return;
130   
131   if (ui.cursor!=NULL) { 
132     gdk_cursor_unref(ui.cursor);
133     ui.cursor = NULL;
134   }
135   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
136     ui.cursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
137   else if (ui.cur_item_type == ITEM_MOVESEL)
138     ui.cursor = gdk_cursor_new(GDK_FLEUR);
139   else if (ui.toolno[ui.cur_mapping] == TOOL_PEN) {
140     fg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
141     fg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
142     fg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
143     source = gdk_bitmap_create_from_data(NULL, cursor_pen_bits, 16, 16);
144     ui.cursor = gdk_cursor_new_from_pixmap(source, source, &fg, &bg, 7, 7);
145     gdk_bitmap_unref(source);
146   }
147   else if (ui.toolno[ui.cur_mapping] == TOOL_ERASER) {
148     source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
149     mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
150     ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
151     gdk_bitmap_unref(source);
152     gdk_bitmap_unref(mask);
153   }
154   else if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
155     source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
156     mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
157     bg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
158     bg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
159     bg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
160     ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
161     gdk_bitmap_unref(source);
162     gdk_bitmap_unref(mask);
163   }
164   else if (ui.cur_item_type == ITEM_SELECTRECT) {
165     ui.cursor = gdk_cursor_new(GDK_TCROSS);
166   }
167   else if (ui.toolno[ui.cur_mapping] == TOOL_HAND) {
168     ui.cursor = gdk_cursor_new(GDK_HAND1);
169   }
170   else if (ui.toolno[ui.cur_mapping] == TOOL_TEXT) {
171     ui.cursor = gdk_cursor_new(GDK_XTERM);
172   }
173   
174   gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
175 }
176
177 /* adjust the cursor shape if it hovers near a selection box */
178
179 void update_cursor_for_resize(double *pt)
180 {
181   gboolean in_range_x, in_range_y;
182   gboolean can_resize_left, can_resize_right, can_resize_bottom, can_resize_top;
183   gdouble resize_margin;
184   GdkCursorType newcursor;
185
186   // if we're not even close to the box in some direction, return immediately
187   resize_margin = RESIZE_MARGIN / ui.zoom;
188   if (pt[0]<ui.selection->bbox.left-resize_margin || pt[0]>ui.selection->bbox.right+resize_margin
189    || pt[1]<ui.selection->bbox.top-resize_margin || pt[1]>ui.selection->bbox.bottom+resize_margin)
190   {
191     if (ui.is_sel_cursor) update_cursor();
192     return;
193   }
194
195   ui.is_sel_cursor = TRUE;
196   can_resize_left = (pt[0] < ui.selection->bbox.left+resize_margin);
197   can_resize_right = (pt[0] > ui.selection->bbox.right-resize_margin);
198   can_resize_top = (pt[1] < ui.selection->bbox.top+resize_margin);
199   can_resize_bottom = (pt[1] > ui.selection->bbox.bottom-resize_margin);
200
201   if (can_resize_left) {
202     if (can_resize_top) newcursor = GDK_TOP_LEFT_CORNER;
203     else if (can_resize_bottom) newcursor = GDK_BOTTOM_LEFT_CORNER;
204     else newcursor = GDK_LEFT_SIDE;
205   }
206   else if (can_resize_right) {
207     if (can_resize_top) newcursor = GDK_TOP_RIGHT_CORNER;
208     else if (can_resize_bottom) newcursor = GDK_BOTTOM_RIGHT_CORNER;
209     else newcursor = GDK_RIGHT_SIDE;
210   }
211   else if (can_resize_top) newcursor = GDK_TOP_SIDE;
212   else if (can_resize_bottom) newcursor = GDK_BOTTOM_SIDE;
213   else newcursor = GDK_FLEUR;
214
215   if (ui.cursor!=NULL && ui.cursor->type == newcursor) return;
216   if (ui.cursor!=NULL) gdk_cursor_unref(ui.cursor);
217   ui.cursor = gdk_cursor_new(newcursor);
218   gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
219 }
220
221 /************** painting strokes *************/
222
223 #define SUBDIVIDE_MAXDIST 5.0
224
225 void subdivide_cur_path(null)
226 {
227   int n, pieces, k;
228   double *p;
229   double x0, y0, x1, y1;
230
231   for (n=0, p=ui.cur_path.coords; n<ui.cur_path.num_points-1; n++, p+=2) {
232     pieces = (int)floor(hypot(p[2]-p[0], p[3]-p[1])/SUBDIVIDE_MAXDIST);
233     if (pieces>1) {
234       x0 = p[0]; y0 = p[1];
235       x1 = p[2]; y1 = p[3];
236       realloc_cur_path(ui.cur_path.num_points+pieces-1);
237       g_memmove(ui.cur_path.coords+2*(n+pieces), ui.cur_path.coords+2*(n+1),
238                     2*(ui.cur_path.num_points-n-1)*sizeof(double));
239       p = ui.cur_path.coords+2*n;
240       ui.cur_path.num_points += pieces-1;
241       n += (pieces-1);
242       for (k=1; k<pieces; k++) {
243         p+=2;
244         p[0] = x0 + k*(x1-x0)/pieces;
245         p[1] = y0 + k*(y1-y0)/pieces;
246       } 
247     }
248   }
249 }
250
251 void create_new_stroke(GdkEvent *event)
252 {
253   ui.cur_item_type = ITEM_STROKE;
254   ui.cur_item = g_new(struct Item, 1);
255   ui.cur_item->type = ITEM_STROKE;
256   g_memmove(&(ui.cur_item->brush), ui.cur_brush, sizeof(struct Brush));
257   ui.cur_item->path = &ui.cur_path;
258   realloc_cur_path(2);
259   ui.cur_path.num_points = 1;
260   get_pointer_coords(event, ui.cur_path.coords);
261   
262   if (ui.cur_brush->ruler) {
263     ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
264       gnome_canvas_line_get_type(),
265       "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
266       "fill-color-rgba", ui.cur_item->brush.color_rgba,
267       "width-units", ui.cur_item->brush.thickness, NULL);
268     ui.cur_item->brush.variable_width = FALSE;
269   } else
270     ui.cur_item->canvas_item = gnome_canvas_item_new(
271       ui.cur_layer->group, gnome_canvas_group_get_type(), NULL);
272 }
273
274 void continue_stroke(GdkEvent *event)
275 {
276   GnomeCanvasPoints seg;
277   double *pt, current_width;
278
279   if (ui.cur_brush->ruler) {
280     pt = ui.cur_path.coords;
281   } else {
282     realloc_cur_path(ui.cur_path.num_points+1);
283     pt = ui.cur_path.coords + 2*(ui.cur_path.num_points-1);
284   } 
285   
286   get_pointer_coords(event, pt+2);
287   
288   if (ui.cur_item->brush.variable_width) {
289     realloc_cur_widths(ui.cur_path.num_points);
290     current_width = ui.cur_item->brush.thickness*get_pressure_multiplier(event);
291     ui.cur_widths[ui.cur_path.num_points-1] = current_width;
292   }
293   else current_width = ui.cur_item->brush.thickness;
294   
295   if (ui.cur_brush->ruler)
296     ui.cur_path.num_points = 2;
297   else {
298     if (hypot(pt[0]-pt[2], pt[1]-pt[3]) < PIXEL_MOTION_THRESHOLD/ui.zoom)
299       return;  // not a meaningful motion
300     ui.cur_path.num_points++;
301   }
302
303   seg.coords = pt; 
304   seg.num_points = 2;
305   seg.ref_count = 1;
306   
307   /* note: we're using a piece of the cur_path array. This is ok because
308      upon creation the line just copies the contents of the GnomeCanvasPoints
309      into an internal structure */
310
311   if (ui.cur_brush->ruler)
312     gnome_canvas_item_set(ui.cur_item->canvas_item, "points", &seg, NULL);
313   else
314     gnome_canvas_item_new((GnomeCanvasGroup *)ui.cur_item->canvas_item,
315        gnome_canvas_line_get_type(), "points", &seg,
316        "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
317        "fill-color-rgba", ui.cur_item->brush.color_rgba,
318        "width-units", current_width, NULL);
319 }
320
321 void finalize_stroke(void)
322 {
323   if (ui.cur_path.num_points == 1) { // GnomeCanvas doesn't like num_points=1
324     ui.cur_path.coords[2] = ui.cur_path.coords[0]+0.1;
325     ui.cur_path.coords[3] = ui.cur_path.coords[1];
326     ui.cur_path.num_points = 2;
327     ui.cur_item->brush.variable_width = FALSE;
328   }
329   
330   if (!ui.cur_item->brush.variable_width)
331     subdivide_cur_path(); // split the segment so eraser will work
332
333   ui.cur_item->path = gnome_canvas_points_new(ui.cur_path.num_points);
334   g_memmove(ui.cur_item->path->coords, ui.cur_path.coords, 
335       2*ui.cur_path.num_points*sizeof(double));
336   if (ui.cur_item->brush.variable_width)
337     ui.cur_item->widths = (gdouble *)g_memdup(ui.cur_widths, 
338                             (ui.cur_path.num_points-1)*sizeof(gdouble));
339   else ui.cur_item->widths = NULL;
340   update_item_bbox(ui.cur_item);
341   ui.cur_path.num_points = 0;
342
343   if (!ui.cur_item->brush.variable_width) {
344     // destroy the entire group of temporary line segments
345     gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
346     // make a new line item to replace it
347     make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
348   }
349
350   // add undo information
351   prepare_new_undo();
352   undo->type = ITEM_STROKE;
353   undo->item = ui.cur_item;
354   undo->layer = ui.cur_layer;
355
356   // store the item on top of the layer stack
357   ui.cur_layer->items = g_list_append(ui.cur_layer->items, ui.cur_item);
358   ui.cur_layer->nitems++;
359   ui.cur_item = NULL;
360   ui.cur_item_type = ITEM_NONE;
361 }
362
363 /************** eraser tool *************/
364
365 void erase_stroke_portions(struct Item *item, double x, double y, double radius,
366                    gboolean whole_strokes, struct UndoErasureData *erasure)
367 {
368   int i;
369   double *pt;
370   struct Item *newhead, *newtail;
371   gboolean need_recalc = FALSE;
372
373   for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
374     if (hypot(pt[0]-x, pt[1]-y) <= radius) { // found an intersection
375       // FIXME: need to test if line SEGMENT hits the circle
376       // hide the canvas item, and create erasure data if needed
377       if (erasure == NULL) {
378         item->type = ITEM_TEMP_STROKE;
379         gnome_canvas_item_hide(item->canvas_item);  
380             /*  we'll use this hidden item as an insertion point later */
381         erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
382         item->erasure = erasure;
383         erasure->item = item;
384         erasure->npos = g_list_index(ui.cur_layer->items, item);
385         erasure->nrepl = 0;
386         erasure->replacement_items = NULL;
387       }
388       // split the stroke
389       newhead = newtail = NULL;
390       if (!whole_strokes) {
391         if (i>=2) {
392           newhead = (struct Item *)g_malloc(sizeof(struct Item));
393           newhead->type = ITEM_STROKE;
394           g_memmove(&newhead->brush, &item->brush, sizeof(struct Brush));
395           newhead->path = gnome_canvas_points_new(i);
396           g_memmove(newhead->path->coords, item->path->coords, 2*i*sizeof(double));
397           if (newhead->brush.variable_width)
398             newhead->widths = (gdouble *)g_memdup(item->widths, (i-1)*sizeof(gdouble));
399           else newhead->widths = NULL;
400         }
401         while (++i < item->path->num_points) {
402           pt+=2;
403           if (hypot(pt[0]-x, pt[1]-y) > radius) break;
404         }
405         if (i<item->path->num_points-1) {
406           newtail = (struct Item *)g_malloc(sizeof(struct Item));
407           newtail->type = ITEM_STROKE;
408           g_memmove(&newtail->brush, &item->brush, sizeof(struct Brush));
409           newtail->path = gnome_canvas_points_new(item->path->num_points-i);
410           g_memmove(newtail->path->coords, item->path->coords+2*i, 
411                            2*(item->path->num_points-i)*sizeof(double));
412           if (newtail->brush.variable_width)
413             newtail->widths = (gdouble *)g_memdup(item->widths+i, 
414               (item->path->num_points-i-1)*sizeof(gdouble));
415           else newtail->widths = NULL;
416           newtail->canvas_item = NULL;
417         }
418       }
419       if (item->type == ITEM_STROKE) { 
420         // it's inside an erasure list - we destroy it
421         gnome_canvas_points_free(item->path);
422         if (item->brush.variable_width) g_free(item->widths);
423         if (item->canvas_item != NULL) 
424           gtk_object_destroy(GTK_OBJECT(item->canvas_item));
425         erasure->nrepl--;
426         erasure->replacement_items = g_list_remove(erasure->replacement_items, item);
427         g_free(item);
428       }
429       // add the new head
430       if (newhead != NULL) {
431         update_item_bbox(newhead);
432         make_canvas_item_one(ui.cur_layer->group, newhead);
433         lower_canvas_item_to(ui.cur_layer->group,
434                   newhead->canvas_item, erasure->item->canvas_item);
435         erasure->replacement_items = g_list_prepend(erasure->replacement_items, newhead);
436         erasure->nrepl++;
437         // prepending ensures it won't get processed twice
438       }
439       // recurse into the new tail
440       need_recalc = (newtail!=NULL);
441       if (newtail == NULL) break;
442       item = newtail;
443       erasure->replacement_items = g_list_prepend(erasure->replacement_items, newtail);
444       erasure->nrepl++;
445       i=0; pt=item->path->coords;
446     }
447   }
448   // add the tail if needed
449   if (!need_recalc) return;
450   update_item_bbox(item);
451   make_canvas_item_one(ui.cur_layer->group, item);
452   lower_canvas_item_to(ui.cur_layer->group, item->canvas_item, 
453                                       erasure->item->canvas_item);
454 }
455
456
457 void do_eraser(GdkEvent *event, double radius, gboolean whole_strokes)
458 {
459   struct Item *item, *repl;
460   GList *itemlist, *repllist;
461   double pos[2];
462   struct BBox eraserbox;
463   
464   get_pointer_coords(event, pos);
465   eraserbox.left = pos[0]-radius;
466   eraserbox.right = pos[0]+radius;
467   eraserbox.top = pos[1]-radius;
468   eraserbox.bottom = pos[1]+radius;
469   for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
470     item = (struct Item *)itemlist->data;
471     if (item->type == ITEM_STROKE) {
472       if (!have_intersect(&(item->bbox), &eraserbox)) continue;
473       erase_stroke_portions(item, pos[0], pos[1], radius, whole_strokes, NULL);
474     } else if (item->type == ITEM_TEMP_STROKE) {
475       repllist = item->erasure->replacement_items;
476       while (repllist!=NULL) {
477         repl = (struct Item *)repllist->data;
478           // we may delete the item soon! so advance now in the list
479         repllist = repllist->next; 
480         if (have_intersect(&(repl->bbox), &eraserbox))
481           erase_stroke_portions(repl, pos[0], pos[1], radius, whole_strokes, item->erasure);
482       }
483     }
484   }
485 }
486
487 void finalize_erasure(void)
488 {
489   GList *itemlist, *partlist;
490   struct Item *item;
491   
492   prepare_new_undo();
493   undo->type = ITEM_ERASURE;
494   undo->layer = ui.cur_layer;
495   undo->erasurelist = NULL;
496   
497   itemlist = ui.cur_layer->items;
498   while (itemlist!=NULL) {
499     item = (struct Item *)itemlist->data;
500     itemlist = itemlist->next;
501     if (item->type != ITEM_TEMP_STROKE) continue;
502     item->type = ITEM_STROKE;
503     ui.cur_layer->items = g_list_remove(ui.cur_layer->items, item);
504     // the item has an invisible canvas item, which used to act as anchor
505     if (item->canvas_item!=NULL) {
506       gtk_object_destroy(GTK_OBJECT(item->canvas_item));
507       item->canvas_item = NULL;
508     }
509     undo->erasurelist = g_list_append(undo->erasurelist, item->erasure);
510     // add the new strokes into the current layer
511     for (partlist = item->erasure->replacement_items; partlist!=NULL; partlist = partlist->next)
512       ui.cur_layer->items = g_list_insert_before(
513                       ui.cur_layer->items, itemlist, partlist->data);
514     ui.cur_layer->nitems += item->erasure->nrepl-1;
515   }
516     
517   ui.cur_item = NULL;
518   ui.cur_item_type = ITEM_NONE;
519   
520   /* NOTE: the list of erasures goes in the depth order of the layer;
521      this guarantees that, upon undo, the erasure->npos fields give the
522      correct position where each item should be reinserted as the list
523      is traversed in the forward direction */
524 }
525
526 /************ selection tools ***********/
527
528 void make_dashed(GnomeCanvasItem *item)
529 {
530   double dashlen[2];
531   ArtVpathDash dash;
532   
533   dash.n_dash = 2;
534   dash.offset = 3.0;
535   dash.dash = dashlen;
536   dashlen[0] = dashlen[1] = 6.0;
537   gnome_canvas_item_set(item, "dash", &dash, NULL);
538 }
539
540
541 void start_selectrect(GdkEvent *event)
542 {
543   double pt[2];
544   reset_selection();
545   
546   ui.cur_item_type = ITEM_SELECTRECT;
547   ui.selection = g_new(struct Selection, 1);
548   ui.selection->type = ITEM_SELECTRECT;
549   ui.selection->items = NULL;
550   ui.selection->layer = ui.cur_layer;
551
552   get_pointer_coords(event, pt);
553   ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
554   ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
555  
556   ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
557       gnome_canvas_rect_get_type(), "width-pixels", 1, 
558       "outline-color-rgba", 0x000000ff,
559       "fill-color-rgba", 0x80808040,
560       "x1", pt[0], "x2", pt[0], "y1", pt[1], "y2", pt[1], NULL);
561   update_cursor();
562 }
563
564 void finalize_selectrect(void)
565 {
566   double x1, x2, y1, y2;
567   GList *itemlist;
568   struct Item *item;
569
570   
571   ui.cur_item_type = ITEM_NONE;
572
573   if (ui.selection->bbox.left > ui.selection->bbox.right) {
574     x1 = ui.selection->bbox.right;  x2 = ui.selection->bbox.left;
575     ui.selection->bbox.left = x1;   ui.selection->bbox.right = x2;
576   } else {
577     x1 = ui.selection->bbox.left;  x2 = ui.selection->bbox.right;
578   }
579
580   if (ui.selection->bbox.top > ui.selection->bbox.bottom) {
581     y1 = ui.selection->bbox.bottom;  y2 = ui.selection->bbox.top;
582     ui.selection->bbox.top = y1;   ui.selection->bbox.bottom = y2;
583   } else {
584     y1 = ui.selection->bbox.top;  y2 = ui.selection->bbox.bottom;
585   }
586   
587   for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
588     item = (struct Item *)itemlist->data;
589     if (item->bbox.left >= x1 && item->bbox.right <= x2 &&
590           item->bbox.top >= y1 && item->bbox.bottom <= y2) {
591       ui.selection->items = g_list_append(ui.selection->items, item); 
592     }
593   }
594   
595   if (ui.selection->items == NULL) {
596     // if we clicked inside a text zone ?  
597     item = click_is_in_text(ui.selection->layer, x1, y1);
598     if (item!=NULL && item==click_is_in_text(ui.selection->layer, x2, y2)) {
599       ui.selection->items = g_list_append(ui.selection->items, item);
600       g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox));
601       gnome_canvas_item_set(ui.selection->canvas_item,
602         "x1", item->bbox.left, "x2", item->bbox.right, 
603         "y1", item->bbox.top, "y2", item->bbox.bottom, NULL);
604     }
605   }
606   
607   if (ui.selection->items == NULL) reset_selection();
608   else make_dashed(ui.selection->canvas_item);
609   update_cursor();
610   update_copy_paste_enabled();
611   update_font_button();
612 }
613
614 gboolean start_movesel(GdkEvent *event)
615 {
616   double pt[2];
617   
618   if (ui.selection==NULL) return FALSE;
619   if (ui.cur_layer != ui.selection->layer) return FALSE;
620   
621   get_pointer_coords(event, pt);
622   if (ui.selection->type == ITEM_SELECTRECT) {
623     if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
624         pt[1]<ui.selection->bbox.top  || pt[1]>ui.selection->bbox.bottom)
625       return FALSE;
626     ui.cur_item_type = ITEM_MOVESEL;
627     ui.selection->anchor_x = ui.selection->last_x = pt[0];
628     ui.selection->anchor_y = ui.selection->last_y = pt[1];
629     ui.selection->orig_pageno = ui.pageno;
630     ui.selection->move_pageno = ui.pageno;
631     ui.selection->move_layer = ui.selection->layer;
632     ui.selection->move_pagedelta = 0.;
633     gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
634     update_cursor();
635     return TRUE;
636   }
637   return FALSE;
638 }
639
640 gboolean start_resizesel(GdkEvent *event)
641 {
642   double pt[2], resize_margin, hmargin, vmargin;
643
644   if (ui.selection==NULL) return FALSE;
645   if (ui.cur_layer != ui.selection->layer) return FALSE;
646
647   get_pointer_coords(event, pt);
648
649   if (ui.selection->type == ITEM_SELECTRECT) {
650     resize_margin = RESIZE_MARGIN/ui.zoom;
651     hmargin = (ui.selection->bbox.right-ui.selection->bbox.left)*0.3;
652     if (hmargin>resize_margin) hmargin = resize_margin;
653     vmargin = (ui.selection->bbox.bottom-ui.selection->bbox.top)*0.3;
654     if (vmargin>resize_margin) vmargin = resize_margin;
655
656     // make sure the click is within a box slightly bigger than the selection rectangle
657     if (pt[0]<ui.selection->bbox.left-resize_margin || 
658         pt[0]>ui.selection->bbox.right+resize_margin ||
659         pt[1]<ui.selection->bbox.top-resize_margin || 
660         pt[1]>ui.selection->bbox.bottom+resize_margin)
661       return FALSE;
662
663     // now, if the click is near the edge, it's a resize operation
664     // keep track of which edges we're close to, since those are the ones which should move
665     ui.selection->resizing_left = (pt[0]<ui.selection->bbox.left+hmargin);
666     ui.selection->resizing_right = (pt[0]>ui.selection->bbox.right-hmargin);
667     ui.selection->resizing_top = (pt[1]<ui.selection->bbox.top+vmargin);
668     ui.selection->resizing_bottom = (pt[1]>ui.selection->bbox.bottom-vmargin);
669
670     // we're not near any edge, give up
671     if (!(ui.selection->resizing_left || ui.selection->resizing_right ||
672           ui.selection->resizing_top  || ui.selection->resizing_bottom)) 
673       return FALSE;
674
675     ui.cur_item_type = ITEM_RESIZESEL;
676     ui.selection->new_y1 = ui.selection->bbox.top;
677     ui.selection->new_y2 = ui.selection->bbox.bottom;
678     ui.selection->new_x1 = ui.selection->bbox.left;
679     ui.selection->new_x2 = ui.selection->bbox.right;
680     gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
681     update_cursor_for_resize(pt);
682     return TRUE;
683   }
684   return FALSE;
685 }
686
687
688 void start_vertspace(GdkEvent *event)
689 {
690   double pt[2];
691   GList *itemlist;
692   struct Item *item;
693
694   reset_selection();
695   ui.cur_item_type = ITEM_MOVESEL_VERT;
696   ui.selection = g_new(struct Selection, 1);
697   ui.selection->type = ITEM_MOVESEL_VERT;
698   ui.selection->items = NULL;
699   ui.selection->layer = ui.cur_layer;
700
701   get_pointer_coords(event, pt);
702   ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
703   for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
704     item = (struct Item *)itemlist->data;
705     if (item->bbox.top >= pt[1]) {
706       ui.selection->items = g_list_append(ui.selection->items, item); 
707       if (item->bbox.bottom > ui.selection->bbox.bottom)
708         ui.selection->bbox.bottom = item->bbox.bottom;
709     }
710   }
711
712   ui.selection->anchor_x = ui.selection->last_x = 0;
713   ui.selection->anchor_y = ui.selection->last_y = pt[1];
714   ui.selection->orig_pageno = ui.pageno;
715   ui.selection->move_pageno = ui.pageno;
716   ui.selection->move_layer = ui.selection->layer;
717   ui.selection->move_pagedelta = 0.;
718   ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
719       gnome_canvas_rect_get_type(), "width-pixels", 1, 
720       "outline-color-rgba", 0x000000ff,
721       "fill-color-rgba", 0x80808040,
722       "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
723   update_cursor();
724 }
725
726 void continue_movesel(GdkEvent *event)
727 {
728   double pt[2], dx, dy, upmargin;
729   GList *list;
730   struct Item *item;
731   int tmppageno;
732   struct Page *tmppage;
733   
734   get_pointer_coords(event, pt);
735   if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
736   pt[1] += ui.selection->move_pagedelta;
737
738   // check for page jumps
739   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
740     upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
741   else upmargin = VIEW_CONTINUOUS_SKIP;
742   tmppageno = ui.selection->move_pageno;
743   tmppage = g_list_nth_data(journal.pages, tmppageno);
744   while (ui.view_continuous && (pt[1] < - upmargin)) {
745     if (tmppageno == 0) break;
746     tmppageno--;
747     tmppage = g_list_nth_data(journal.pages, tmppageno);
748     pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
749     ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
750   }
751   while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
752     if (tmppageno == journal.npages-1) break;
753     pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
754     ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
755     tmppageno++;
756     tmppage = g_list_nth_data(journal.pages, tmppageno);
757   }
758   
759   if (tmppageno != ui.selection->move_pageno) {
760     // move to a new page !
761     ui.selection->move_pageno = tmppageno;
762     if (tmppageno == ui.selection->orig_pageno)
763       ui.selection->move_layer = ui.selection->layer;
764     else
765       ui.selection->move_layer = (struct Layer *)(g_list_last(
766         ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
767     gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
768     for (list = ui.selection->items; list!=NULL; list = list->next) {
769       item = (struct Item *)list->data;
770       if (item->canvas_item!=NULL)
771         gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
772     }
773     // avoid a refresh bug
774     gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
775     if (ui.cur_item_type == ITEM_MOVESEL_VERT)
776       gnome_canvas_item_set(ui.selection->canvas_item,
777         "x2", tmppage->width+100, 
778         "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
779   }
780   
781   // now, process things normally
782
783   dx = pt[0] - ui.selection->last_x;
784   dy = pt[1] - ui.selection->last_y;
785   if (hypot(dx,dy) < 1) return; // don't move subpixel
786   ui.selection->last_x = pt[0];
787   ui.selection->last_y = pt[1];
788
789   // move the canvas items
790   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
791     gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
792   else 
793     gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
794   
795   for (list = ui.selection->items; list != NULL; list = list->next) {
796     item = (struct Item *)list->data;
797     if (item->canvas_item != NULL)
798       gnome_canvas_item_move(item->canvas_item, dx, dy);
799   }
800 }
801
802 void continue_resizesel(GdkEvent *event)
803 {
804   double pt[2];
805
806   get_pointer_coords(event, pt);
807
808   if (ui.selection->resizing_top) ui.selection->new_y1 = pt[1];
809   if (ui.selection->resizing_bottom) ui.selection->new_y2 = pt[1];
810   if (ui.selection->resizing_left) ui.selection->new_x1 = pt[0];
811   if (ui.selection->resizing_right) ui.selection->new_x2 = pt[0];
812
813   gnome_canvas_item_set(ui.selection->canvas_item, 
814     "x1", ui.selection->new_x1, "x2", ui.selection->new_x2,
815     "y1", ui.selection->new_y1, "y2", ui.selection->new_y2, NULL);
816 }
817
818 void finalize_movesel(void)
819 {
820   GList *list, *link;
821   
822   if (ui.selection->items != NULL) {
823     prepare_new_undo();
824     undo->type = ITEM_MOVESEL;
825     undo->itemlist = g_list_copy(ui.selection->items);
826     undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
827     undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
828     undo->layer = ui.selection->layer;
829     undo->layer2 = ui.selection->move_layer;
830     undo->auxlist = NULL;
831     // build auxlist = pointers to Item's just before ours (for depths)
832     for (list = ui.selection->items; list!=NULL; list = list->next) {
833       link = g_list_find(ui.selection->layer->items, list->data);
834       if (link!=NULL) link = link->prev;
835       undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
836     }
837     ui.selection->layer = ui.selection->move_layer;
838     move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
839                           undo->layer, undo->layer2, 
840                           (undo->layer == undo->layer2)?undo->auxlist:NULL);
841   }
842
843   if (ui.selection->move_pageno!=ui.selection->orig_pageno) 
844     do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
845     
846   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
847     reset_selection();
848   else {
849     ui.selection->bbox.left += undo->val_x;
850     ui.selection->bbox.right += undo->val_x;
851     ui.selection->bbox.top += undo->val_y;
852     ui.selection->bbox.bottom += undo->val_y;
853     make_dashed(ui.selection->canvas_item);
854     /* update selection box object's offset to be trivial, and its internal 
855        coordinates to agree with those of the bbox; need this since resize
856        operations will modify the box by setting its coordinates directly */
857     gnome_canvas_item_affine_absolute(ui.selection->canvas_item, NULL);
858     gnome_canvas_item_set(ui.selection->canvas_item, 
859       "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
860       "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
861   }
862   ui.cur_item_type = ITEM_NONE;
863   update_cursor();
864 }
865
866 #define SCALING_EPSILON 0.001
867
868 void finalize_resizesel(void)
869 {
870   struct Item *item;
871
872   // build the affine transformation
873   double offset_x, offset_y, scaling_x, scaling_y;
874   scaling_x = (ui.selection->new_x2 - ui.selection->new_x1) / 
875               (ui.selection->bbox.right - ui.selection->bbox.left);
876   scaling_y = (ui.selection->new_y2 - ui.selection->new_y1) /
877               (ui.selection->bbox.bottom - ui.selection->bbox.top);
878   // couldn't undo a resize-by-zero...
879   if (fabs(scaling_x)<SCALING_EPSILON) scaling_x = SCALING_EPSILON;
880   if (fabs(scaling_y)<SCALING_EPSILON) scaling_y = SCALING_EPSILON;
881   offset_x = ui.selection->new_x1 - ui.selection->bbox.left * scaling_x;
882   offset_y = ui.selection->new_y1 - ui.selection->bbox.top * scaling_y;
883
884   if (ui.selection->items != NULL) {
885     // create the undo information
886     prepare_new_undo();
887     undo->type = ITEM_RESIZESEL;
888     undo->itemlist = g_list_copy(ui.selection->items);
889     undo->auxlist = NULL;
890
891     undo->scaling_x = scaling_x;
892     undo->scaling_y = scaling_y;
893     undo->val_x = offset_x;
894     undo->val_y = offset_y;
895
896     // actually do the resize operation
897     resize_journal_items_by(ui.selection->items, scaling_x, scaling_y, offset_x, offset_y);
898   }
899
900   if (scaling_x>0) {
901     ui.selection->bbox.left = ui.selection->new_x1;
902     ui.selection->bbox.right = ui.selection->new_x2;
903   } else {
904     ui.selection->bbox.left = ui.selection->new_x2;
905     ui.selection->bbox.right = ui.selection->new_x1;
906   }
907   if (scaling_y>0) {
908     ui.selection->bbox.top = ui.selection->new_y1;
909     ui.selection->bbox.bottom = ui.selection->new_y2;
910   } else {
911     ui.selection->bbox.top = ui.selection->new_y2;
912     ui.selection->bbox.bottom = ui.selection->new_y1;
913   }
914   make_dashed(ui.selection->canvas_item);
915
916   ui.cur_item_type = ITEM_NONE;
917   update_cursor();
918 }
919
920 void selection_delete(void)
921 {
922   struct UndoErasureData *erasure;
923   GList *itemlist;
924   struct Item *item;
925   
926   if (ui.selection == NULL) return;
927   prepare_new_undo();
928   undo->type = ITEM_ERASURE;
929   undo->layer = ui.selection->layer;
930   undo->erasurelist = NULL;
931   for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
932     item = (struct Item *)itemlist->data;
933     if (item->canvas_item!=NULL)
934       gtk_object_destroy(GTK_OBJECT(item->canvas_item));
935     erasure = g_new(struct UndoErasureData, 1);
936     erasure->item = item;
937     erasure->npos = g_list_index(ui.selection->layer->items, item);
938     erasure->nrepl = 0;
939     erasure->replacement_items = NULL;
940     ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
941     ui.selection->layer->nitems--;
942     undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
943   }
944   reset_selection();
945
946   /* NOTE: the erasurelist is built backwards; this guarantees that,
947      upon undo, the erasure->npos fields give the correct position
948      where each item should be reinserted as the list is traversed in
949      the forward direction */
950 }
951
952 void callback_clipboard_get(GtkClipboard *clipboard,
953                             GtkSelectionData *selection_data,
954                             guint info, gpointer user_data)
955 {
956   int length;
957   
958   g_memmove(&length, user_data, sizeof(int));
959   gtk_selection_data_set(selection_data,
960      gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
961 }
962
963 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
964 {
965   g_free(user_data);
966 }
967
968 void selection_to_clip(void)
969 {
970   int bufsz, nitems, val;
971   char *buf, *p;
972   GList *list;
973   struct Item *item;
974   GtkTargetEntry target;
975   
976   if (ui.selection == NULL) return;
977   bufsz = 2*sizeof(int) // bufsz, nitems
978         + sizeof(struct BBox); // bbox
979   nitems = 0;
980   for (list = ui.selection->items; list != NULL; list = list->next) {
981     item = (struct Item *)list->data;
982     nitems++;
983     if (item->type == ITEM_STROKE) {
984       bufsz+= sizeof(int) // type
985             + sizeof(struct Brush) // brush
986             + sizeof(int) // num_points
987             + 2*item->path->num_points*sizeof(double); // the points
988       if (item->brush.variable_width)
989         bufsz += (item->path->num_points-1)*sizeof(double); // the widths
990     }
991     else if (item->type == ITEM_TEXT) {
992       bufsz+= sizeof(int) // type
993             + sizeof(struct Brush) // brush
994             + 2*sizeof(double) // bbox upper-left
995             + sizeof(int) // text len
996             + strlen(item->text)+1 // text
997             + sizeof(int) // font_name len
998             + strlen(item->font_name)+1 // font_name
999             + sizeof(double); // font_size
1000     }
1001     else bufsz+= sizeof(int); // type
1002   }
1003   p = buf = g_malloc(bufsz);
1004   g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
1005   g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
1006   g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
1007   for (list = ui.selection->items; list != NULL; list = list->next) {
1008     item = (struct Item *)list->data;
1009     g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
1010     if (item->type == ITEM_STROKE) {
1011       g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
1012       g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
1013       g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
1014       p+= 2*item->path->num_points*sizeof(double);
1015       if (item->brush.variable_width) {
1016         g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
1017         p+= (item->path->num_points-1)*sizeof(double);
1018       }
1019     }
1020     if (item->type == ITEM_TEXT) {
1021       g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
1022       g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
1023       g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
1024       val = strlen(item->text);
1025       g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
1026       g_memmove(p, item->text, val+1); p+= val+1;
1027       val = strlen(item->font_name);
1028       g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
1029       g_memmove(p, item->font_name, val+1); p+= val+1;
1030       g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
1031     }
1032   }
1033   
1034   target.target = "_XOURNAL";
1035   target.flags = 0;
1036   target.info = 0;
1037   
1038   gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), 
1039        &target, 1,
1040        callback_clipboard_get, callback_clipboard_clear, buf);
1041 }
1042
1043
1044 void clipboard_paste(void)
1045 {
1046   GtkSelectionData *sel_data;
1047   unsigned char *p;
1048   int nitems, npts, i, len;
1049   struct Item *item;
1050   double hoffset, voffset, cx, cy;
1051   double *pf;
1052   int sx, sy, wx, wy;
1053   
1054   if (ui.cur_layer == NULL) return;
1055   
1056   ui.cur_item_type = ITEM_PASTE;
1057   sel_data = gtk_clipboard_wait_for_contents(
1058       gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
1059       gdk_atom_intern("_XOURNAL", FALSE));
1060   ui.cur_item_type = ITEM_NONE;
1061   if (sel_data == NULL) return; // paste failed
1062   
1063   reset_selection();
1064   
1065   ui.selection = g_new(struct Selection, 1);
1066   p = sel_data->data + sizeof(int);
1067   g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
1068   ui.selection->type = ITEM_SELECTRECT;
1069   ui.selection->layer = ui.cur_layer;
1070   g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
1071   ui.selection->items = NULL;
1072   
1073   // find by how much we translate the pasted selection
1074   gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
1075   gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
1076   gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
1077   cx -= ui.cur_page->hoffset;
1078   cy -= ui.cur_page->voffset;
1079   if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
1080     cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1081   if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
1082     cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1083   if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
1084     cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1085   if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
1086     cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1087   hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
1088   voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
1089   ui.selection->bbox.left += hoffset;
1090   ui.selection->bbox.right += hoffset;
1091   ui.selection->bbox.top += voffset;
1092   ui.selection->bbox.bottom += voffset;
1093
1094   ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1095       gnome_canvas_rect_get_type(), "width-pixels", 1,
1096       "outline-color-rgba", 0x000000ff,
1097       "fill-color-rgba", 0x80808040,
1098       "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right, 
1099       "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
1100   make_dashed(ui.selection->canvas_item);
1101
1102   while (nitems-- > 0) {
1103     item = g_new(struct Item, 1);
1104     ui.selection->items = g_list_append(ui.selection->items, item);
1105     ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1106     ui.cur_layer->nitems++;
1107     g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
1108     if (item->type == ITEM_STROKE) {
1109       g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1110       g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
1111       item->path = gnome_canvas_points_new(npts);
1112       pf = (double *)p;
1113       for (i=0; i<npts; i++) {
1114         item->path->coords[2*i] = pf[2*i] + hoffset;
1115         item->path->coords[2*i+1] = pf[2*i+1] + voffset;
1116       }
1117       p+= 2*item->path->num_points*sizeof(double);
1118       if (item->brush.variable_width) {
1119         item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
1120         p+= (item->path->num_points-1)*sizeof(double);
1121       }
1122       else item->widths = NULL;
1123       update_item_bbox(item);
1124       make_canvas_item_one(ui.cur_layer->group, item);
1125     }
1126     if (item->type == ITEM_TEXT) {
1127       g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1128       g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
1129       g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
1130       item->bbox.left += hoffset;
1131       item->bbox.top += voffset;
1132       g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1133       item->text = g_malloc(len+1);
1134       g_memmove(item->text, p, len+1); p+= len+1;
1135       g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1136       item->font_name = g_malloc(len+1);
1137       g_memmove(item->font_name, p, len+1); p+= len+1;
1138       g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
1139       make_canvas_item_one(ui.cur_layer->group, item);
1140     }
1141   }
1142
1143   prepare_new_undo();
1144   undo->type = ITEM_PASTE;
1145   undo->layer = ui.cur_layer;
1146   undo->itemlist = g_list_copy(ui.selection->items);  
1147   
1148   gtk_selection_data_free(sel_data);
1149   update_copy_paste_enabled();
1150   update_color_menu();
1151   update_thickness_buttons();
1152   update_color_buttons();
1153   update_font_button();  
1154   update_cursor(); // FIXME: can't know if pointer is within selection!
1155 }
1156
1157 // modify the color or thickness of pen strokes in a selection
1158
1159 void recolor_selection(int color_no, guint color_rgba)
1160 {
1161   GList *itemlist;
1162   struct Item *item;
1163   struct Brush *brush;
1164   GnomeCanvasGroup *group;
1165   
1166   if (ui.selection == NULL) return;
1167   prepare_new_undo();
1168   undo->type = ITEM_REPAINTSEL;
1169   undo->itemlist = NULL;
1170   undo->auxlist = NULL;
1171   for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1172     item = (struct Item *)itemlist->data;
1173     if (item->type != ITEM_STROKE && item->type != ITEM_TEXT) continue;
1174     if (item->type == ITEM_STROKE && item->brush.tool_type!=TOOL_PEN) continue;
1175     // store info for undo
1176     undo->itemlist = g_list_append(undo->itemlist, item);
1177     brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1178     g_memmove(brush, &(item->brush), sizeof(struct Brush));
1179     undo->auxlist = g_list_append(undo->auxlist, brush);
1180     // repaint the stroke
1181     item->brush.color_no = color_no;
1182     item->brush.color_rgba = color_rgba | 0xff; // no alpha
1183     if (item->canvas_item!=NULL) {
1184       if (!item->brush.variable_width)
1185         gnome_canvas_item_set(item->canvas_item, 
1186            "fill-color-rgba", item->brush.color_rgba, NULL);
1187       else {
1188         group = (GnomeCanvasGroup *) item->canvas_item->parent;
1189         gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1190         make_canvas_item_one(group, item);
1191       }
1192     }
1193   }
1194 }
1195
1196 void rethicken_selection(int val)
1197 {
1198   GList *itemlist;
1199   struct Item *item;
1200   struct Brush *brush;
1201   GnomeCanvasGroup *group;
1202   
1203   if (ui.selection == NULL) return;
1204   prepare_new_undo();
1205   undo->type = ITEM_REPAINTSEL;
1206   undo->itemlist = NULL;
1207   undo->auxlist = NULL;
1208   for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1209     item = (struct Item *)itemlist->data;
1210     if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
1211     // store info for undo
1212     undo->itemlist = g_list_append(undo->itemlist, item);
1213     brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1214     g_memmove(brush, &(item->brush), sizeof(struct Brush));
1215     undo->auxlist = g_list_append(undo->auxlist, brush);
1216     // repaint the stroke
1217     item->brush.thickness_no = val;
1218     item->brush.thickness = predef_thickness[TOOL_PEN][val];
1219     if (item->canvas_item!=NULL) {
1220       if (!item->brush.variable_width)
1221         gnome_canvas_item_set(item->canvas_item, 
1222            "width-units", item->brush.thickness, NULL);
1223       else {
1224         group = (GnomeCanvasGroup *) item->canvas_item->parent;
1225         gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1226         item->brush.variable_width = FALSE;
1227         make_canvas_item_one(group, item);
1228       }
1229     }
1230   }
1231 }
1232
1233 gboolean do_hand_scrollto(gpointer data)
1234 {
1235   ui.hand_scrollto_pending = FALSE;
1236   gnome_canvas_scroll_to(canvas, ui.hand_scrollto_cx, ui.hand_scrollto_cy);
1237   return FALSE;
1238 }
1239
1240 void do_hand(GdkEvent *event)
1241 {
1242   double pt[2];
1243   int cx, cy;
1244   
1245   get_pointer_coords(event, pt);
1246   pt[0] += ui.cur_page->hoffset;
1247   pt[1] += ui.cur_page->voffset;
1248   gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1249   ui.hand_scrollto_cx = cx - (pt[0]-ui.hand_refpt[0])*ui.zoom;
1250   ui.hand_scrollto_cy = cy - (pt[1]-ui.hand_refpt[1])*ui.zoom;
1251   if (!ui.hand_scrollto_pending) g_idle_add(do_hand_scrollto, NULL);
1252   ui.hand_scrollto_pending = TRUE;
1253 }
1254
1255 /************ TEXT FUNCTIONS **************/
1256
1257 // to make it easier to copy/paste at end of text box
1258 #define WIDGET_RIGHT_MARGIN 10
1259
1260 void resize_textview(gpointer *toplevel, gpointer *data)
1261 {
1262   GtkTextView *w;
1263   int width, height;
1264   
1265   /* when the text changes, resize the GtkTextView accordingly */
1266   if (ui.cur_item_type!=ITEM_TEXT) return;
1267   w = GTK_TEXT_VIEW(ui.cur_item->widget);
1268   width = w->width + WIDGET_RIGHT_MARGIN;
1269   height = w->height;
1270   gnome_canvas_item_set(ui.cur_item->canvas_item, 
1271     "size-pixels", TRUE, 
1272     "width", (gdouble)width, "height", (gdouble)height, NULL);
1273   ui.cur_item->bbox.right = ui.cur_item->bbox.left + width/ui.zoom;
1274   ui.cur_item->bbox.bottom = ui.cur_item->bbox.top + height/ui.zoom;
1275 }
1276
1277 void start_text(GdkEvent *event, struct Item *item)
1278 {
1279   double pt[2];
1280   GtkTextBuffer *buffer;
1281   GnomeCanvasItem *canvas_item;
1282   PangoFontDescription *font_desc;
1283   GdkColor color;
1284
1285   get_pointer_coords(event, pt);
1286
1287   ui.cur_item_type = ITEM_TEXT;
1288
1289   if (item==NULL) {
1290     item = g_new(struct Item, 1);
1291     item->text = NULL;
1292     item->canvas_item = NULL;
1293     item->bbox.left = pt[0];
1294     item->bbox.top = pt[1];
1295     item->bbox.right = ui.cur_page->width;
1296     item->bbox.bottom = pt[1]+100.;
1297     item->font_name = g_strdup(ui.font_name);
1298     item->font_size = ui.font_size;
1299     g_memmove(&(item->brush), ui.cur_brush, sizeof(struct Brush));
1300     ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1301     ui.cur_layer->nitems++;
1302   }
1303   
1304   item->type = ITEM_TEMP_TEXT;
1305   ui.cur_item = item;
1306   
1307   font_desc = pango_font_description_from_string(item->font_name);
1308   pango_font_description_set_absolute_size(font_desc, 
1309       item->font_size*ui.zoom*PANGO_SCALE);
1310   item->widget = gtk_text_view_new();
1311   buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(item->widget));
1312   if (item->text!=NULL)
1313     gtk_text_buffer_set_text(buffer, item->text, -1);
1314   gtk_widget_modify_font(item->widget, font_desc);
1315   rgb_to_gdkcolor(item->brush.color_rgba, &color);
1316   gtk_widget_modify_text(item->widget, GTK_STATE_NORMAL, &color);
1317   pango_font_description_free(font_desc);
1318
1319   canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1320     gnome_canvas_widget_get_type(),
1321     "x", item->bbox.left, "y", item->bbox.top, 
1322     "width", item->bbox.right-item->bbox.left, 
1323     "height", item->bbox.bottom-item->bbox.top,
1324     "widget", item->widget, NULL);
1325   // TODO: width/height?
1326   if (item->canvas_item!=NULL) {
1327     lower_canvas_item_to(ui.cur_layer->group, canvas_item, item->canvas_item);
1328     gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1329   }
1330   item->canvas_item = canvas_item;
1331
1332   gtk_widget_show(item->widget);
1333   ui.resize_signal_handler = 
1334     g_signal_connect((gpointer) winMain, "check_resize",
1335        G_CALLBACK(resize_textview), NULL);
1336   update_font_button();
1337   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), FALSE);
1338   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), FALSE);
1339   gtk_widget_grab_focus(item->widget); 
1340 }
1341
1342 void end_text(void)
1343 {
1344   GtkTextBuffer *buffer;
1345   GtkTextIter start, end;
1346   gchar *new_text;
1347   struct UndoErasureData *erasure;
1348   GnomeCanvasItem *tmpitem;
1349
1350   if (ui.cur_item_type!=ITEM_TEXT) return; // nothing for us to do!
1351
1352   // finalize the text that's been edited... 
1353   buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.cur_item->widget));
1354   gtk_text_buffer_get_bounds(buffer, &start, &end);
1355   ui.cur_item->type = ITEM_TEXT;
1356   new_text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
1357   ui.cur_item_type = ITEM_NONE;
1358   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), TRUE);
1359   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), TRUE);
1360   
1361   if (strlen(new_text)==0) { // erase object and cancel
1362     g_free(new_text);
1363     g_signal_handler_disconnect(winMain, ui.resize_signal_handler);
1364     gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
1365     ui.cur_item->canvas_item = NULL;
1366     if (ui.cur_item->text == NULL) // nothing happened
1367       g_free(ui.cur_item->font_name);
1368     else { // treat this as an erasure
1369       prepare_new_undo();
1370       undo->type = ITEM_ERASURE;
1371       undo->layer = ui.cur_layer;
1372       erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
1373       erasure->item = ui.cur_item;
1374       erasure->npos = g_list_index(ui.cur_layer->items, ui.cur_item);
1375       erasure->nrepl = 0;
1376       erasure->replacement_items = NULL;
1377       undo->erasurelist = g_list_append(NULL, erasure);
1378     }
1379     ui.cur_layer->items = g_list_remove(ui.cur_layer->items, ui.cur_item);
1380     ui.cur_layer->nitems--;
1381     ui.cur_item = NULL;
1382     return;
1383   }
1384
1385   // store undo data
1386   if (ui.cur_item->text==NULL || strcmp(ui.cur_item->text, new_text)) {
1387     prepare_new_undo();
1388     if (ui.cur_item->text == NULL) undo->type = ITEM_TEXT; 
1389     else undo->type = ITEM_TEXT_EDIT;
1390     undo->layer = ui.cur_layer;
1391     undo->item = ui.cur_item;
1392     undo->str = ui.cur_item->text;
1393   }
1394   else g_free(ui.cur_item->text);
1395
1396   ui.cur_item->text = new_text;
1397   ui.cur_item->widget = NULL;
1398   // replace the canvas item
1399   tmpitem = ui.cur_item->canvas_item;
1400   make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
1401   update_item_bbox(ui.cur_item);
1402   lower_canvas_item_to(ui.cur_layer->group, ui.cur_item->canvas_item, tmpitem);
1403   gtk_object_destroy(GTK_OBJECT(tmpitem));
1404 }
1405
1406 /* update the items in the canvas so they're of the right font size */
1407
1408 void update_text_item_displayfont(struct Item *item)
1409 {
1410   PangoFontDescription *font_desc;
1411
1412   if (item->type != ITEM_TEXT && item->type != ITEM_TEMP_TEXT) return;
1413   if (item->canvas_item==NULL) return;
1414   font_desc = pango_font_description_from_string(item->font_name);
1415   pango_font_description_set_absolute_size(font_desc, 
1416         item->font_size*ui.zoom*PANGO_SCALE);
1417   if (item->type == ITEM_TEMP_TEXT)
1418     gtk_widget_modify_font(item->widget, font_desc);
1419   else {
1420     gnome_canvas_item_set(item->canvas_item, "font-desc", font_desc, NULL);
1421     update_item_bbox(item);
1422   }
1423   pango_font_description_free(font_desc);
1424 }
1425
1426 void rescale_text_items(void)
1427 {
1428   GList *pagelist, *layerlist, *itemlist;
1429   
1430   for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next)
1431     for (layerlist = ((struct Page *)pagelist->data)->layers; layerlist!=NULL; layerlist = layerlist->next)
1432       for (itemlist = ((struct Layer *)layerlist->data)->items; itemlist!=NULL; itemlist = itemlist->next)
1433         update_text_item_displayfont((struct Item *)itemlist->data);
1434 }
1435
1436 struct Item *click_is_in_text(struct Layer *layer, double x, double y)
1437 {
1438   GList *itemlist;
1439   struct Item *item, *val;
1440   
1441   val = NULL;
1442   for (itemlist = layer->items; itemlist!=NULL; itemlist = itemlist->next) {
1443     item = (struct Item *)itemlist->data;
1444     if (item->type != ITEM_TEXT) continue;
1445     if (x<item->bbox.left || x>item->bbox.right) continue;
1446     if (y<item->bbox.top || y>item->bbox.bottom) continue;
1447     val = item;
1448   }
1449   return val;
1450 }
1451
1452 void refont_text_item(struct Item *item, gchar *font_name, double font_size)
1453 {
1454   if (!strcmp(font_name, item->font_name) && font_size==item->font_size) return;
1455   if (item->text!=NULL) {
1456     prepare_new_undo();
1457     undo->type = ITEM_TEXT_ATTRIB;
1458     undo->item = item;
1459     undo->str = item->font_name;
1460     undo->val_x = item->font_size;
1461     undo->brush = (struct Brush *)g_memdup(&(item->brush), sizeof(struct Brush));
1462   }
1463   else g_free(item->font_name);
1464   item->font_name = g_strdup(font_name);
1465   if (font_size>0.) item->font_size = font_size;
1466   update_text_item_displayfont(item);
1467 }
1468
1469 void process_font_sel(gchar *str)
1470 {
1471   gchar *p, *q;
1472   struct Item *it;
1473   gdouble size;
1474   GList *list;
1475   gboolean undo_cont;
1476
1477   p = strrchr(str, ' ');
1478   if (p!=NULL) { 
1479     size = g_strtod(p+1, &q);
1480     if (*q!=0 || size<1.) size=0.;
1481     else *p=0;
1482   }
1483   else size=0.;
1484   g_free(ui.font_name);
1485   ui.font_name = str;  
1486   if (size>0.) ui.font_size = size;
1487   undo_cont = FALSE;   
1488   // if there's a current text item, re-font it
1489   if (ui.cur_item_type == ITEM_TEXT) {
1490     refont_text_item(ui.cur_item, str, size);
1491     undo_cont = (ui.cur_item->text!=NULL);   
1492   }
1493   // if there's a current selection, re-font it
1494   if (ui.selection!=NULL) 
1495     for (list=ui.selection->items; list!=NULL; list=list->next) {
1496       it = (struct Item *)list->data;
1497       if (it->type == ITEM_TEXT) {   
1498         if (undo_cont) undo->multiop |= MULTIOP_CONT_REDO;
1499         refont_text_item(it, str, size);
1500         if (undo_cont) undo->multiop |= MULTIOP_CONT_UNDO;
1501         undo_cont = TRUE;
1502       }
1503     }  
1504   update_font_button();
1505 }