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