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