]> git.donarmstrong.com Git - xournal.git/blob - src/xo-selection.c
add Polish translation
[xournal.git] / src / xo-selection.c
1 /*
2  *  This program is free software; you can redistribute it and/or
3  *  modify it under the terms of the GNU General Public
4  *  License as published by the Free Software Foundation; either
5  *  version 2 of the License, or (at your option) any later version.
6  *
7  *  This software is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of  
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
14  */
15
16 #ifdef HAVE_CONFIG_H
17 #  include <config.h>
18 #endif
19
20 #include <math.h>
21 #include <string.h>
22 #include <gtk/gtk.h>
23 #include <libgnomecanvas/libgnomecanvas.h>
24 #include <libart_lgpl/art_vpath_dash.h>
25 #include <libart_lgpl/art_svp_point.h>
26 #include <libart_lgpl/art_svp_vpath.h>
27
28 #include "xournal.h"
29 #include "xo-callbacks.h"
30 #include "xo-interface.h"
31 #include "xo-support.h"
32 #include "xo-misc.h"
33 #include "xo-paint.h"
34 #include "xo-selection.h"
35
36 /************ selection tools ***********/
37
38 void make_dashed(GnomeCanvasItem *item)
39 {
40   double dashlen[2];
41   ArtVpathDash dash;
42   
43   dash.n_dash = 2;
44   dash.offset = 3.0;
45   dash.dash = dashlen;
46   dashlen[0] = dashlen[1] = 6.0;
47   gnome_canvas_item_set(item, "dash", &dash, NULL);
48 }
49
50
51 void start_selectrect(GdkEvent *event)
52 {
53   double pt[2];
54   reset_selection();
55   
56   ui.cur_item_type = ITEM_SELECTRECT;
57   ui.selection = g_new(struct Selection, 1);
58   ui.selection->type = ITEM_SELECTRECT;
59   ui.selection->items = NULL;
60   ui.selection->layer = ui.cur_layer;
61
62   get_pointer_coords(event, pt);
63   ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
64   ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
65  
66   ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
67       gnome_canvas_rect_get_type(), "width-pixels", 1, 
68       "outline-color-rgba", 0x000000ff,
69       "fill-color-rgba", 0x80808040,
70       "x1", pt[0], "x2", pt[0], "y1", pt[1], "y2", pt[1], NULL);
71   update_cursor();
72 }
73
74 void finalize_selectrect(void)
75 {
76   double x1, x2, y1, y2;
77   GList *itemlist;
78   struct Item *item;
79   
80   ui.cur_item_type = ITEM_NONE;
81
82   if (ui.selection->bbox.left > ui.selection->bbox.right) {
83     x1 = ui.selection->bbox.right;  x2 = ui.selection->bbox.left;
84     ui.selection->bbox.left = x1;   ui.selection->bbox.right = x2;
85   } else {
86     x1 = ui.selection->bbox.left;  x2 = ui.selection->bbox.right;
87   }
88
89   if (ui.selection->bbox.top > ui.selection->bbox.bottom) {
90     y1 = ui.selection->bbox.bottom;  y2 = ui.selection->bbox.top;
91     ui.selection->bbox.top = y1;   ui.selection->bbox.bottom = y2;
92   } else {
93     y1 = ui.selection->bbox.top;  y2 = ui.selection->bbox.bottom;
94   }
95   
96   for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
97     item = (struct Item *)itemlist->data;
98     if (item->bbox.left >= x1 && item->bbox.right <= x2 &&
99           item->bbox.top >= y1 && item->bbox.bottom <= y2) {
100       ui.selection->items = g_list_append(ui.selection->items, item); 
101     }
102   }
103   
104   if (ui.selection->items == NULL) {
105     // if we clicked inside a text zone or image?  
106     item = click_is_in_text_or_image(ui.selection->layer, x1, y1);
107     if (item!=NULL && item==click_is_in_text_or_image(ui.selection->layer, x2, y2)) {
108       ui.selection->items = g_list_append(ui.selection->items, item);
109       g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox));
110       gnome_canvas_item_set(ui.selection->canvas_item,
111         "x1", item->bbox.left, "x2", item->bbox.right, 
112         "y1", item->bbox.top, "y2", item->bbox.bottom, NULL);
113     }
114   }
115   
116   if (ui.selection->items == NULL) reset_selection();
117   else make_dashed(ui.selection->canvas_item);
118   update_cursor();
119   update_copy_paste_enabled();
120   update_font_button();
121 }
122
123
124 void start_selectregion(GdkEvent *event)
125 {
126   double pt[2];
127   reset_selection();
128   
129   ui.cur_item_type = ITEM_SELECTREGION;
130   ui.selection = g_new(struct Selection, 1);
131   ui.selection->type = ITEM_SELECTREGION;
132   ui.selection->items = NULL;
133   ui.selection->layer = ui.cur_layer;
134
135   get_pointer_coords(event, pt);
136   ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
137   ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
138   
139   realloc_cur_path(1);
140   ui.cur_path.num_points = 1;
141   ui.cur_path.coords[0] = ui.cur_path.coords[2] = pt[0];
142   ui.cur_path.coords[1] = ui.cur_path.coords[3] = pt[1];
143  
144   ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
145       gnome_canvas_polygon_get_type(), "width-pixels", 1, 
146       "outline-color-rgba", 0x000000ff,
147       "fill-color-rgba", 0x80808040,
148       NULL);
149   make_dashed(ui.selection->canvas_item);
150   update_cursor();
151 }
152
153 void continue_selectregion(GdkEvent *event)
154 {
155   double *pt;
156   
157   realloc_cur_path(ui.cur_path.num_points+1);
158   pt = ui.cur_path.coords + 2*ui.cur_path.num_points;
159   get_pointer_coords(event, pt);
160   if (hypot(pt[0]-pt[-2], pt[1]-pt[-1]) < PIXEL_MOTION_THRESHOLD/ui.zoom)
161     return; // not a meaningful motion
162   ui.cur_path.num_points++;
163   if (ui.cur_path.num_points>2)
164     gnome_canvas_item_set(ui.selection->canvas_item, 
165      "points", &ui.cur_path, NULL);
166 }
167
168 /* check whether a point, resp. an item, is inside a lasso selection */
169
170 gboolean hittest_point(ArtSVP *lassosvp, double x, double y)
171 {
172   return art_svp_point_wind(lassosvp, x, y)%2;
173 }
174
175 gboolean hittest_item(ArtSVP *lassosvp, struct Item *item)
176 {
177   int i;
178   
179   if (item->type == ITEM_STROKE) {
180     for (i=0; i<item->path->num_points; i++)
181       if (!hittest_point(lassosvp, item->path->coords[2*i], item->path->coords[2*i+1])) 
182         return FALSE;
183     return TRUE;
184   }
185   else 
186     return (hittest_point(lassosvp, item->bbox.left, item->bbox.top) &&
187             hittest_point(lassosvp, item->bbox.right, item->bbox.top) &&
188             hittest_point(lassosvp, item->bbox.left, item->bbox.bottom) &&
189             hittest_point(lassosvp, item->bbox.right, item->bbox.bottom));
190 }
191
192 void finalize_selectregion(void)
193 {
194   GList *itemlist;
195   struct Item *item;
196   ArtVpath *vpath;
197   ArtSVP *lassosvp;
198   int i, n;
199   double *pt;
200   
201   ui.cur_item_type = ITEM_NONE;
202   
203   // build SVP for the lasso path
204   n = ui.cur_path.num_points;
205   vpath = g_malloc((n+2)*sizeof(ArtVpath));
206   for (i=0; i<n; i++) { 
207     vpath[i].x = ui.cur_path.coords[2*i];
208     vpath[i].y = ui.cur_path.coords[2*i+1];
209   }
210   vpath[n].x = vpath[0].x; vpath[n].y = vpath[0].y;
211   vpath[0].code = ART_MOVETO;
212   for (i=1; i<=n; i++) vpath[i].code = ART_LINETO;
213   vpath[n+1].code = ART_END;
214   lassosvp = art_svp_from_vpath(vpath);
215   g_free(vpath);
216
217   // see which items we selected
218   for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
219     item = (struct Item *)itemlist->data;
220     if (hittest_item(lassosvp, item)) {
221       // update the selection bbox
222       if (ui.selection->items==NULL || ui.selection->bbox.left>item->bbox.left)
223         ui.selection->bbox.left = item->bbox.left;
224       if (ui.selection->items==NULL || ui.selection->bbox.right<item->bbox.right)
225         ui.selection->bbox.right = item->bbox.right;
226       if (ui.selection->items==NULL || ui.selection->bbox.top>item->bbox.top)
227         ui.selection->bbox.top = item->bbox.top;
228       if (ui.selection->items==NULL || ui.selection->bbox.bottom<item->bbox.bottom)
229         ui.selection->bbox.bottom = item->bbox.bottom;
230       // add the item
231       ui.selection->items = g_list_append(ui.selection->items, item); 
232     }
233   }
234   art_svp_free(lassosvp);
235   
236   if (ui.selection->items == NULL) {
237     // if we clicked inside a text zone or image?
238     pt = ui.cur_path.coords; 
239     item = click_is_in_text_or_image(ui.selection->layer, pt[0], pt[1]);
240     if (item!=NULL) {
241       for (i=0; i<n; i++, pt+=2) {
242         if (pt[0]<item->bbox.left || pt[0]>item->bbox.right || pt[1]<item->bbox.top || pt[1]>item->bbox.bottom)
243           { item = NULL; break; }
244       }
245     }
246     if (item!=NULL) {
247       ui.selection->items = g_list_append(ui.selection->items, item);
248       g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox));
249     }
250   }
251
252   if (ui.selection->items == NULL) reset_selection();
253   else { // make a selection rectangle instead of the lasso shape
254     gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
255     ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
256       gnome_canvas_rect_get_type(), "width-pixels", 1, 
257       "outline-color-rgba", 0x000000ff,
258       "fill-color-rgba", 0x80808040,
259       "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right, 
260       "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
261     make_dashed(ui.selection->canvas_item);
262     ui.selection->type = ITEM_SELECTRECT;
263   }
264
265   update_cursor();
266   update_copy_paste_enabled();
267   update_font_button();
268 }
269
270
271 /*** moving/resizing the selection ***/
272
273 gboolean start_movesel(GdkEvent *event)
274 {
275   double pt[2];
276   
277   if (ui.selection==NULL) return FALSE;
278   if (ui.cur_layer != ui.selection->layer) return FALSE;
279   
280   get_pointer_coords(event, pt);
281   if (ui.selection->type == ITEM_SELECTRECT || ui.selection->type == ITEM_SELECTREGION) {
282     if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
283         pt[1]<ui.selection->bbox.top  || pt[1]>ui.selection->bbox.bottom)
284       return FALSE;
285     ui.cur_item_type = ITEM_MOVESEL;
286     ui.selection->anchor_x = ui.selection->last_x = pt[0];
287     ui.selection->anchor_y = ui.selection->last_y = pt[1];
288     ui.selection->orig_pageno = ui.pageno;
289     ui.selection->move_pageno = ui.pageno;
290     ui.selection->move_layer = ui.selection->layer;
291     ui.selection->move_pagedelta = 0.;
292     gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
293     update_cursor();
294     return TRUE;
295   }
296   return FALSE;
297 }
298
299 gboolean start_resizesel(GdkEvent *event)
300 {
301   double pt[2], resize_margin, hmargin, vmargin;
302
303   if (ui.selection==NULL) return FALSE;
304   if (ui.cur_layer != ui.selection->layer) return FALSE;
305
306   get_pointer_coords(event, pt);
307
308   if (ui.selection->type == ITEM_SELECTRECT || ui.selection->type == ITEM_SELECTREGION) {
309     resize_margin = RESIZE_MARGIN/ui.zoom;
310     hmargin = (ui.selection->bbox.right-ui.selection->bbox.left)*0.3;
311     if (hmargin>resize_margin) hmargin = resize_margin;
312     vmargin = (ui.selection->bbox.bottom-ui.selection->bbox.top)*0.3;
313     if (vmargin>resize_margin) vmargin = resize_margin;
314
315     // make sure the click is within a box slightly bigger than the selection rectangle
316     if (pt[0]<ui.selection->bbox.left-resize_margin || 
317         pt[0]>ui.selection->bbox.right+resize_margin ||
318         pt[1]<ui.selection->bbox.top-resize_margin || 
319         pt[1]>ui.selection->bbox.bottom+resize_margin)
320       return FALSE;
321
322     // now, if the click is near the edge, it's a resize operation
323     // keep track of which edges we're close to, since those are the ones which should move
324     ui.selection->resizing_left = (pt[0]<ui.selection->bbox.left+hmargin);
325     ui.selection->resizing_right = (pt[0]>ui.selection->bbox.right-hmargin);
326     ui.selection->resizing_top = (pt[1]<ui.selection->bbox.top+vmargin);
327     ui.selection->resizing_bottom = (pt[1]>ui.selection->bbox.bottom-vmargin);
328
329     // we're not near any edge, give up
330     if (!(ui.selection->resizing_left || ui.selection->resizing_right ||
331           ui.selection->resizing_top  || ui.selection->resizing_bottom)) 
332       return FALSE;
333
334     ui.cur_item_type = ITEM_RESIZESEL;
335     ui.selection->new_y1 = ui.selection->bbox.top;
336     ui.selection->new_y2 = ui.selection->bbox.bottom;
337     ui.selection->new_x1 = ui.selection->bbox.left;
338     ui.selection->new_x2 = ui.selection->bbox.right;
339     gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
340     update_cursor_for_resize(pt);
341     return TRUE;
342   }
343   return FALSE;
344 }
345
346
347 void start_vertspace(GdkEvent *event)
348 {
349   double pt[2];
350   GList *itemlist;
351   struct Item *item;
352
353   reset_selection();
354   ui.cur_item_type = ITEM_MOVESEL_VERT;
355   ui.selection = g_new(struct Selection, 1);
356   ui.selection->type = ITEM_MOVESEL_VERT;
357   ui.selection->items = NULL;
358   ui.selection->layer = ui.cur_layer;
359
360   get_pointer_coords(event, pt);
361   ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
362   for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
363     item = (struct Item *)itemlist->data;
364     if (item->bbox.top >= pt[1]) {
365       ui.selection->items = g_list_append(ui.selection->items, item); 
366       if (item->bbox.bottom > ui.selection->bbox.bottom)
367         ui.selection->bbox.bottom = item->bbox.bottom;
368     }
369   }
370
371   ui.selection->anchor_x = ui.selection->last_x = 0;
372   ui.selection->anchor_y = ui.selection->last_y = pt[1];
373   ui.selection->orig_pageno = ui.pageno;
374   ui.selection->move_pageno = ui.pageno;
375   ui.selection->move_layer = ui.selection->layer;
376   ui.selection->move_pagedelta = 0.;
377   ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
378       gnome_canvas_rect_get_type(), "width-pixels", 1, 
379       "outline-color-rgba", 0x000000ff,
380       "fill-color-rgba", 0x80808040,
381       "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
382   update_cursor();
383 }
384
385 void continue_movesel(GdkEvent *event)
386 {
387   double pt[2], dx, dy, upmargin;
388   GList *list;
389   struct Item *item;
390   int tmppageno;
391   struct Page *tmppage;
392   
393   get_pointer_coords(event, pt);
394   if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
395   pt[1] += ui.selection->move_pagedelta;
396
397   // check for page jumps
398   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
399     upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
400   else upmargin = VIEW_CONTINUOUS_SKIP;
401   tmppageno = ui.selection->move_pageno;
402   tmppage = g_list_nth_data(journal.pages, tmppageno);
403   while (ui.view_continuous && (pt[1] < - upmargin)) {
404     if (tmppageno == 0) break;
405     tmppageno--;
406     tmppage = g_list_nth_data(journal.pages, tmppageno);
407     pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
408     ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
409   }
410   while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
411     if (tmppageno == journal.npages-1) break;
412     pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
413     ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
414     tmppageno++;
415     tmppage = g_list_nth_data(journal.pages, tmppageno);
416   }
417   
418   if (tmppageno != ui.selection->move_pageno) {
419     // move to a new page !
420     ui.selection->move_pageno = tmppageno;
421     if (tmppageno == ui.selection->orig_pageno)
422       ui.selection->move_layer = ui.selection->layer;
423     else
424       ui.selection->move_layer = (struct Layer *)(g_list_last(
425         ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
426     gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
427     for (list = ui.selection->items; list!=NULL; list = list->next) {
428       item = (struct Item *)list->data;
429       if (item->canvas_item!=NULL)
430         gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
431     }
432     // avoid a refresh bug
433     gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
434     if (ui.cur_item_type == ITEM_MOVESEL_VERT)
435       gnome_canvas_item_set(ui.selection->canvas_item,
436         "x2", tmppage->width+100, 
437         "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
438   }
439   
440   // now, process things normally
441
442   dx = pt[0] - ui.selection->last_x;
443   dy = pt[1] - ui.selection->last_y;
444   if (hypot(dx,dy) < 1) return; // don't move subpixel
445   ui.selection->last_x = pt[0];
446   ui.selection->last_y = pt[1];
447
448   // move the canvas items
449   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
450     gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
451   else 
452     gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
453   
454   for (list = ui.selection->items; list != NULL; list = list->next) {
455     item = (struct Item *)list->data;
456     if (item->canvas_item != NULL)
457       gnome_canvas_item_move(item->canvas_item, dx, dy);
458   }
459 }
460
461 void continue_resizesel(GdkEvent *event)
462 {
463   double pt[2];
464
465   get_pointer_coords(event, pt);
466
467   if (ui.selection->resizing_top) ui.selection->new_y1 = pt[1];
468   if (ui.selection->resizing_bottom) ui.selection->new_y2 = pt[1];
469   if (ui.selection->resizing_left) ui.selection->new_x1 = pt[0];
470   if (ui.selection->resizing_right) ui.selection->new_x2 = pt[0];
471
472   gnome_canvas_item_set(ui.selection->canvas_item, 
473     "x1", ui.selection->new_x1, "x2", ui.selection->new_x2,
474     "y1", ui.selection->new_y1, "y2", ui.selection->new_y2, NULL);
475 }
476
477 void finalize_movesel(void)
478 {
479   GList *list, *link;
480   
481   if (ui.selection->items != NULL) {
482     prepare_new_undo();
483     undo->type = ITEM_MOVESEL;
484     undo->itemlist = g_list_copy(ui.selection->items);
485     undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
486     undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
487     undo->layer = ui.selection->layer;
488     undo->layer2 = ui.selection->move_layer;
489     undo->auxlist = NULL;
490     // build auxlist = pointers to Item's just before ours (for depths)
491     for (list = ui.selection->items; list!=NULL; list = list->next) {
492       link = g_list_find(ui.selection->layer->items, list->data);
493       if (link!=NULL) link = link->prev;
494       undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
495     }
496     ui.selection->layer = ui.selection->move_layer;
497     move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
498                           undo->layer, undo->layer2, 
499                           (undo->layer == undo->layer2)?undo->auxlist:NULL);
500   }
501
502   if (ui.selection->move_pageno!=ui.selection->orig_pageno) 
503     do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
504     
505   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
506     reset_selection();
507   else {
508     ui.selection->bbox.left += undo->val_x;
509     ui.selection->bbox.right += undo->val_x;
510     ui.selection->bbox.top += undo->val_y;
511     ui.selection->bbox.bottom += undo->val_y;
512     make_dashed(ui.selection->canvas_item);
513     /* update selection box object's offset to be trivial, and its internal 
514        coordinates to agree with those of the bbox; need this since resize
515        operations will modify the box by setting its coordinates directly */
516     gnome_canvas_item_affine_absolute(ui.selection->canvas_item, NULL);
517     gnome_canvas_item_set(ui.selection->canvas_item, 
518       "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
519       "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
520   }
521   ui.cur_item_type = ITEM_NONE;
522   update_cursor();
523 }
524
525 #define SCALING_EPSILON 0.001
526
527 void finalize_resizesel(void)
528 {
529   struct Item *item;
530
531   // build the affine transformation
532   double offset_x, offset_y, scaling_x, scaling_y;
533   scaling_x = (ui.selection->new_x2 - ui.selection->new_x1) / 
534               (ui.selection->bbox.right - ui.selection->bbox.left);
535   scaling_y = (ui.selection->new_y2 - ui.selection->new_y1) /
536               (ui.selection->bbox.bottom - ui.selection->bbox.top);
537   // couldn't undo a resize-by-zero...
538   if (fabs(scaling_x)<SCALING_EPSILON) scaling_x = SCALING_EPSILON;
539   if (fabs(scaling_y)<SCALING_EPSILON) scaling_y = SCALING_EPSILON;
540   offset_x = ui.selection->new_x1 - ui.selection->bbox.left * scaling_x;
541   offset_y = ui.selection->new_y1 - ui.selection->bbox.top * scaling_y;
542
543   if (ui.selection->items != NULL) {
544     // create the undo information
545     prepare_new_undo();
546     undo->type = ITEM_RESIZESEL;
547     undo->itemlist = g_list_copy(ui.selection->items);
548     undo->auxlist = NULL;
549
550     undo->scaling_x = scaling_x;
551     undo->scaling_y = scaling_y;
552     undo->val_x = offset_x;
553     undo->val_y = offset_y;
554
555     // actually do the resize operation
556     resize_journal_items_by(ui.selection->items, scaling_x, scaling_y, offset_x, offset_y);
557   }
558
559   if (scaling_x>0) {
560     ui.selection->bbox.left = ui.selection->new_x1;
561     ui.selection->bbox.right = ui.selection->new_x2;
562   } else {
563     ui.selection->bbox.left = ui.selection->new_x2;
564     ui.selection->bbox.right = ui.selection->new_x1;
565   }
566   if (scaling_y>0) {
567     ui.selection->bbox.top = ui.selection->new_y1;
568     ui.selection->bbox.bottom = ui.selection->new_y2;
569   } else {
570     ui.selection->bbox.top = ui.selection->new_y2;
571     ui.selection->bbox.bottom = ui.selection->new_y1;
572   }
573   make_dashed(ui.selection->canvas_item);
574
575   ui.cur_item_type = ITEM_NONE;
576   update_cursor();
577 }
578
579 void selection_delete(void)
580 {
581   struct UndoErasureData *erasure;
582   GList *itemlist;
583   struct Item *item;
584   
585   if (ui.selection == NULL) return;
586   prepare_new_undo();
587   undo->type = ITEM_ERASURE;
588   undo->layer = ui.selection->layer;
589   undo->erasurelist = NULL;
590   for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
591     item = (struct Item *)itemlist->data;
592     if (item->canvas_item!=NULL)
593       gtk_object_destroy(GTK_OBJECT(item->canvas_item));
594     erasure = g_new(struct UndoErasureData, 1);
595     erasure->item = item;
596     erasure->npos = g_list_index(ui.selection->layer->items, item);
597     erasure->nrepl = 0;
598     erasure->replacement_items = NULL;
599     ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
600     ui.selection->layer->nitems--;
601     undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
602   }
603   reset_selection();
604
605   /* NOTE: the erasurelist is built backwards; this guarantees that,
606      upon undo, the erasure->npos fields give the correct position
607      where each item should be reinserted as the list is traversed in
608      the forward direction */
609 }
610
611 // modify the color or thickness of pen strokes in a selection
612
613 void recolor_selection(int color_no, guint color_rgba)
614 {
615   GList *itemlist;
616   struct Item *item;
617   struct Brush *brush;
618   GnomeCanvasGroup *group;
619   
620   if (ui.selection == NULL) return;
621   prepare_new_undo();
622   undo->type = ITEM_REPAINTSEL;
623   undo->itemlist = NULL;
624   undo->auxlist = NULL;
625   for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
626     item = (struct Item *)itemlist->data;
627     if (item->type != ITEM_STROKE && item->type != ITEM_TEXT) continue;
628     if (item->type == ITEM_STROKE && item->brush.tool_type!=TOOL_PEN) continue;
629     // store info for undo
630     undo->itemlist = g_list_append(undo->itemlist, item);
631     brush = (struct Brush *)g_malloc(sizeof(struct Brush));
632     g_memmove(brush, &(item->brush), sizeof(struct Brush));
633     undo->auxlist = g_list_append(undo->auxlist, brush);
634     // repaint the stroke
635     item->brush.color_no = color_no;
636     item->brush.color_rgba = color_rgba | 0xff; // no alpha
637     if (item->canvas_item!=NULL) {
638       if (!item->brush.variable_width)
639         gnome_canvas_item_set(item->canvas_item, 
640            "fill-color-rgba", item->brush.color_rgba, NULL);
641       else {
642         group = (GnomeCanvasGroup *) item->canvas_item->parent;
643         gtk_object_destroy(GTK_OBJECT(item->canvas_item));
644         make_canvas_item_one(group, item);
645       }
646     }
647   }
648 }
649
650 void rethicken_selection(int val)
651 {
652   GList *itemlist;
653   struct Item *item;
654   struct Brush *brush;
655   GnomeCanvasGroup *group;
656   
657   if (ui.selection == NULL) return;
658   prepare_new_undo();
659   undo->type = ITEM_REPAINTSEL;
660   undo->itemlist = NULL;
661   undo->auxlist = NULL;
662   for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
663     item = (struct Item *)itemlist->data;
664     if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
665     // store info for undo
666     undo->itemlist = g_list_append(undo->itemlist, item);
667     brush = (struct Brush *)g_malloc(sizeof(struct Brush));
668     g_memmove(brush, &(item->brush), sizeof(struct Brush));
669     undo->auxlist = g_list_append(undo->auxlist, brush);
670     // repaint the stroke
671     item->brush.thickness_no = val;
672     item->brush.thickness = predef_thickness[TOOL_PEN][val];
673     if (item->canvas_item!=NULL) {
674       if (!item->brush.variable_width)
675         gnome_canvas_item_set(item->canvas_item, 
676            "width-units", item->brush.thickness, NULL);
677       else {
678         group = (GnomeCanvasGroup *) item->canvas_item->parent;
679         gtk_object_destroy(GTK_OBJECT(item->canvas_item));
680         item->brush.variable_width = FALSE;
681         make_canvas_item_one(group, item);
682       }
683     }
684   }
685 }
686