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