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 
make_dashed(GnomeCanvasItem * item)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 
start_selectrect(GdkEvent * event)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 
finalize_selectrect(void)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 
start_selectregion(GdkEvent * event)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 
continue_selectregion(GdkEvent * event)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 
hittest_point(ArtSVP * lassosvp,double x,double y)170 gboolean hittest_point(ArtSVP *lassosvp, double x, double y)
171 {
172   return art_svp_point_wind(lassosvp, x, y)%2;
173 }
174 
hittest_item(ArtSVP * lassosvp,struct Item * item)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 
finalize_selectregion(void)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    // expand the bounding box by some amount (medium highlighter, or 3 pixels)
237   if (ui.selection->items != NULL) {
238     double margin = MAX(0.6*predef_thickness[TOOL_HIGHLIGHTER][THICKNESS_MEDIUM], 3.0/ui.zoom);
239     ui.selection->bbox.top -= margin;
240     ui.selection->bbox.bottom += margin;
241     ui.selection->bbox.left -= margin;
242     ui.selection->bbox.right += margin;
243   }
244 
245   if (ui.selection->items == NULL) {
246     // if we clicked inside a text zone or image?
247     pt = ui.cur_path.coords;
248     item = click_is_in_text_or_image(ui.selection->layer, pt[0], pt[1]);
249     if (item!=NULL) {
250       for (i=0; i<n; i++, pt+=2) {
251         if (pt[0]<item->bbox.left || pt[0]>item->bbox.right || pt[1]<item->bbox.top || pt[1]>item->bbox.bottom)
252           { item = NULL; break; }
253       }
254     }
255     if (item!=NULL) {
256       ui.selection->items = g_list_append(ui.selection->items, item);
257       g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox));
258     }
259   }
260 
261   if (ui.selection->items == NULL) reset_selection();
262   else { // make a selection rectangle instead of the lasso shape
263     gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
264     ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
265       gnome_canvas_rect_get_type(), "width-pixels", 1,
266       "outline-color-rgba", 0x000000ff,
267       "fill-color-rgba", 0x80808040,
268       "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
269       "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
270     make_dashed(ui.selection->canvas_item);
271     ui.selection->type = ITEM_SELECTRECT;
272   }
273 
274   update_cursor();
275   update_copy_paste_enabled();
276   update_font_button();
277 }
278 
279 
280 /*** moving/resizing the selection ***/
281 
start_movesel(GdkEvent * event)282 gboolean start_movesel(GdkEvent *event)
283 {
284   double pt[2];
285 
286   if (ui.selection==NULL) return FALSE;
287   if (ui.cur_layer != ui.selection->layer) return FALSE;
288 
289   get_pointer_coords(event, pt);
290   if (ui.selection->type == ITEM_SELECTRECT || ui.selection->type == ITEM_SELECTREGION) {
291     if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
292         pt[1]<ui.selection->bbox.top  || pt[1]>ui.selection->bbox.bottom)
293       return FALSE;
294     ui.cur_item_type = ITEM_MOVESEL;
295     ui.selection->anchor_x = ui.selection->last_x = pt[0];
296     ui.selection->anchor_y = ui.selection->last_y = pt[1];
297     ui.selection->orig_pageno = ui.pageno;
298     ui.selection->move_pageno = ui.pageno;
299     ui.selection->move_layer = ui.selection->layer;
300     ui.selection->move_pagedelta = 0.;
301     gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
302     update_cursor();
303     return TRUE;
304   }
305   return FALSE;
306 }
307 
start_resizesel(GdkEvent * event)308 gboolean start_resizesel(GdkEvent *event)
309 {
310   double pt[2], resize_margin, hmargin, vmargin;
311 
312   if (ui.selection==NULL) return FALSE;
313   if (ui.cur_layer != ui.selection->layer) return FALSE;
314 
315   get_pointer_coords(event, pt);
316 
317   if (ui.selection->type == ITEM_SELECTRECT || ui.selection->type == ITEM_SELECTREGION) {
318     resize_margin = RESIZE_MARGIN/ui.zoom;
319     hmargin = (ui.selection->bbox.right-ui.selection->bbox.left)*0.3;
320     if (hmargin>resize_margin) hmargin = resize_margin;
321     vmargin = (ui.selection->bbox.bottom-ui.selection->bbox.top)*0.3;
322     if (vmargin>resize_margin) vmargin = resize_margin;
323 
324     // make sure the click is within a box slightly bigger than the selection rectangle
325     if (pt[0]<ui.selection->bbox.left-resize_margin ||
326         pt[0]>ui.selection->bbox.right+resize_margin ||
327         pt[1]<ui.selection->bbox.top-resize_margin ||
328         pt[1]>ui.selection->bbox.bottom+resize_margin)
329       return FALSE;
330 
331     // now, if the click is near the edge, it's a resize operation
332     // keep track of which edges we're close to, since those are the ones which should move
333     ui.selection->resizing_left = (pt[0]<ui.selection->bbox.left+hmargin);
334     ui.selection->resizing_right = (pt[0]>ui.selection->bbox.right-hmargin);
335     ui.selection->resizing_top = (pt[1]<ui.selection->bbox.top+vmargin);
336     ui.selection->resizing_bottom = (pt[1]>ui.selection->bbox.bottom-vmargin);
337 
338     // we're not near any edge, give up
339     if (!(ui.selection->resizing_left || ui.selection->resizing_right ||
340           ui.selection->resizing_top  || ui.selection->resizing_bottom))
341       return FALSE;
342 
343     ui.cur_item_type = ITEM_RESIZESEL;
344     ui.selection->new_y1 = ui.selection->bbox.top;
345     ui.selection->new_y2 = ui.selection->bbox.bottom;
346     ui.selection->new_x1 = ui.selection->bbox.left;
347     ui.selection->new_x2 = ui.selection->bbox.right;
348     gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
349     update_cursor_for_resize(pt);
350     return TRUE;
351   }
352   return FALSE;
353 }
354 
355 
start_vertspace(GdkEvent * event)356 void start_vertspace(GdkEvent *event)
357 {
358   double pt[2];
359   GList *itemlist;
360   struct Item *item;
361 
362   reset_selection();
363   ui.cur_item_type = ITEM_MOVESEL_VERT;
364   ui.selection = g_new(struct Selection, 1);
365   ui.selection->type = ITEM_MOVESEL_VERT;
366   ui.selection->items = NULL;
367   ui.selection->layer = ui.cur_layer;
368 
369   get_pointer_coords(event, pt);
370   ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
371   for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
372     item = (struct Item *)itemlist->data;
373     if (item->bbox.top >= pt[1]) {
374       ui.selection->items = g_list_append(ui.selection->items, item);
375       if (item->bbox.bottom > ui.selection->bbox.bottom)
376         ui.selection->bbox.bottom = item->bbox.bottom;
377     }
378   }
379 
380   ui.selection->anchor_x = ui.selection->last_x = 0;
381   ui.selection->anchor_y = ui.selection->last_y = pt[1];
382   ui.selection->orig_pageno = ui.pageno;
383   ui.selection->move_pageno = ui.pageno;
384   ui.selection->move_layer = ui.selection->layer;
385   ui.selection->move_pagedelta = 0.;
386   ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
387       gnome_canvas_rect_get_type(), "width-pixels", 1,
388       "outline-color-rgba", 0x000000ff,
389       "fill-color-rgba", 0x80808040,
390       "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
391   update_cursor();
392 }
393 
continue_movesel(GdkEvent * event)394 void continue_movesel(GdkEvent *event)
395 {
396   double pt[2], dx, dy, upmargin;
397   GList *list;
398   struct Item *item;
399   int tmppageno;
400   struct Page *tmppage;
401 
402   get_pointer_coords(event, pt);
403   if (ui.view_continuous == VIEW_MODE_CONTINUOUS) pt[1] += ui.selection->move_pagedelta;
404   if (ui.view_continuous == VIEW_MODE_HORIZONTAL) pt[0] += ui.selection->move_pagedelta;
405   if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
406 
407   // check for page jumps
408   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
409     upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
410   else upmargin = VIEW_CONTINUOUS_SKIP;
411   tmppageno = ui.selection->move_pageno;
412   tmppage = g_list_nth_data(journal.pages, tmppageno);
413   if (ui.view_continuous == VIEW_MODE_CONTINUOUS) {
414     while (pt[1] < - upmargin) {
415       if (tmppageno == 0) break;
416       tmppageno--;
417       tmppage = g_list_nth_data(journal.pages, tmppageno);
418       pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
419       ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
420     }
421     while (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP) {
422       if (tmppageno == journal.npages-1) break;
423       pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
424       ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
425       tmppageno++;
426       tmppage = g_list_nth_data(journal.pages, tmppageno);
427     }
428   }
429   if (ui.view_continuous == VIEW_MODE_HORIZONTAL) {
430     while (pt[0] < -VIEW_CONTINUOUS_SKIP) {
431       if (tmppageno == 0) break;
432       tmppageno--;
433       tmppage = g_list_nth_data(journal.pages, tmppageno);
434       pt[0] += tmppage->width + VIEW_CONTINUOUS_SKIP;
435       ui.selection->move_pagedelta += tmppage->width + VIEW_CONTINUOUS_SKIP;
436     }
437     while (pt[0] > tmppage->width+VIEW_CONTINUOUS_SKIP) {
438       if (tmppageno == journal.npages-1) break;
439       pt[0] -= tmppage->width + VIEW_CONTINUOUS_SKIP;
440       ui.selection->move_pagedelta -= tmppage->width + VIEW_CONTINUOUS_SKIP;
441       tmppageno++;
442       tmppage = g_list_nth_data(journal.pages, tmppageno);
443     }
444   }
445 
446   if (tmppageno != ui.selection->move_pageno) {
447     // move to a new page !
448     ui.selection->move_pageno = tmppageno;
449     if (tmppageno == ui.selection->orig_pageno)
450       ui.selection->move_layer = ui.selection->layer;
451     else
452       ui.selection->move_layer = (struct Layer *)(g_list_last(
453         ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
454     gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
455     for (list = ui.selection->items; list!=NULL; list = list->next) {
456       item = (struct Item *)list->data;
457       if (item->canvas_item!=NULL)
458         gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
459     }
460     // avoid a refresh bug
461     gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
462     if (ui.cur_item_type == ITEM_MOVESEL_VERT)
463       gnome_canvas_item_set(ui.selection->canvas_item,
464         "x2", tmppage->width+100,
465         "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
466             /* note: moving across pages for vert. space only works in vertical continuous mode */
467   }
468 
469   // now, process things normally
470 
471   dx = pt[0] - ui.selection->last_x;
472   dy = pt[1] - ui.selection->last_y;
473   if (hypot(dx,dy) < 1) return; // don't move subpixel
474   ui.selection->last_x = pt[0];
475   ui.selection->last_y = pt[1];
476 
477   // move the canvas items
478   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
479     gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
480   else
481     gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
482 
483   for (list = ui.selection->items; list != NULL; list = list->next) {
484     item = (struct Item *)list->data;
485     if (item->canvas_item != NULL)
486       gnome_canvas_item_move(item->canvas_item, dx, dy);
487   }
488 }
489 
continue_resizesel(GdkEvent * event)490 void continue_resizesel(GdkEvent *event)
491 {
492   double pt[2];
493 
494   get_pointer_coords(event, pt);
495 
496   if (ui.selection->resizing_top) ui.selection->new_y1 = pt[1];
497   if (ui.selection->resizing_bottom) ui.selection->new_y2 = pt[1];
498   if (ui.selection->resizing_left) ui.selection->new_x1 = pt[0];
499   if (ui.selection->resizing_right) ui.selection->new_x2 = pt[0];
500 
501   gnome_canvas_item_set(ui.selection->canvas_item,
502     "x1", ui.selection->new_x1, "x2", ui.selection->new_x2,
503     "y1", ui.selection->new_y1, "y2", ui.selection->new_y2, NULL);
504 }
505 
finalize_movesel(void)506 void finalize_movesel(void)
507 {
508   GList *list, *link;
509 
510   if (ui.selection->items != NULL) {
511     prepare_new_undo();
512     undo->type = ITEM_MOVESEL;
513     undo->itemlist = g_list_copy(ui.selection->items);
514     undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
515     undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
516     undo->layer = ui.selection->layer;
517     undo->layer2 = ui.selection->move_layer;
518     undo->auxlist = NULL;
519     // build auxlist = pointers to Item's just before ours (for depths)
520     for (list = ui.selection->items; list!=NULL; list = list->next) {
521       link = g_list_find(ui.selection->layer->items, list->data);
522       if (link!=NULL) link = link->prev;
523       undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
524     }
525     ui.selection->layer = ui.selection->move_layer;
526     move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
527                           undo->layer, undo->layer2,
528                           (undo->layer == undo->layer2)?undo->auxlist:NULL);
529   }
530 
531   if (ui.selection->move_pageno!=ui.selection->orig_pageno)
532     do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
533 
534   if (ui.cur_item_type == ITEM_MOVESEL_VERT)
535     reset_selection();
536   else {
537     ui.selection->bbox.left += undo->val_x;
538     ui.selection->bbox.right += undo->val_x;
539     ui.selection->bbox.top += undo->val_y;
540     ui.selection->bbox.bottom += undo->val_y;
541     make_dashed(ui.selection->canvas_item);
542     /* update selection box object's offset to be trivial, and its internal
543        coordinates to agree with those of the bbox; need this since resize
544        operations will modify the box by setting its coordinates directly */
545     gnome_canvas_item_affine_absolute(ui.selection->canvas_item, NULL);
546     gnome_canvas_item_set(ui.selection->canvas_item,
547       "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
548       "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
549   }
550   ui.cur_item_type = ITEM_NONE;
551   update_cursor();
552 }
553 
554 #define SCALING_EPSILON 0.001
555 
finalize_resizesel(void)556 void finalize_resizesel(void)
557 {
558   struct Item *item;
559 
560   // build the affine transformation
561   double offset_x, offset_y, scaling_x, scaling_y;
562   scaling_x = (ui.selection->new_x2 - ui.selection->new_x1) /
563               (ui.selection->bbox.right - ui.selection->bbox.left);
564   scaling_y = (ui.selection->new_y2 - ui.selection->new_y1) /
565               (ui.selection->bbox.bottom - ui.selection->bbox.top);
566   // couldn't undo a resize-by-zero...
567   if (fabs(scaling_x)<SCALING_EPSILON) scaling_x = SCALING_EPSILON;
568   if (fabs(scaling_y)<SCALING_EPSILON) scaling_y = SCALING_EPSILON;
569   offset_x = ui.selection->new_x1 - ui.selection->bbox.left * scaling_x;
570   offset_y = ui.selection->new_y1 - ui.selection->bbox.top * scaling_y;
571 
572   if (ui.selection->items != NULL) {
573     // create the undo information
574     prepare_new_undo();
575     undo->type = ITEM_RESIZESEL;
576     undo->itemlist = g_list_copy(ui.selection->items);
577     undo->auxlist = NULL;
578 
579     undo->scaling_x = scaling_x;
580     undo->scaling_y = scaling_y;
581     undo->val_x = offset_x;
582     undo->val_y = offset_y;
583 
584     // actually do the resize operation
585     resize_journal_items_by(ui.selection->items, scaling_x, scaling_y, offset_x, offset_y);
586   }
587 
588   if (scaling_x>0) {
589     ui.selection->bbox.left = ui.selection->new_x1;
590     ui.selection->bbox.right = ui.selection->new_x2;
591   } else {
592     ui.selection->bbox.left = ui.selection->new_x2;
593     ui.selection->bbox.right = ui.selection->new_x1;
594   }
595   if (scaling_y>0) {
596     ui.selection->bbox.top = ui.selection->new_y1;
597     ui.selection->bbox.bottom = ui.selection->new_y2;
598   } else {
599     ui.selection->bbox.top = ui.selection->new_y2;
600     ui.selection->bbox.bottom = ui.selection->new_y1;
601   }
602   make_dashed(ui.selection->canvas_item);
603 
604   ui.cur_item_type = ITEM_NONE;
605   update_cursor();
606 }
607 
selection_delete(void)608 void selection_delete(void)
609 {
610   struct UndoErasureData *erasure;
611   GList *itemlist;
612   struct Item *item;
613 
614   if (ui.selection == NULL) return;
615   prepare_new_undo();
616   undo->type = ITEM_ERASURE;
617   undo->layer = ui.selection->layer;
618   undo->erasurelist = NULL;
619   for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
620     item = (struct Item *)itemlist->data;
621     if (item->canvas_item!=NULL)
622       gtk_object_destroy(GTK_OBJECT(item->canvas_item));
623     erasure = g_new(struct UndoErasureData, 1);
624     erasure->item = item;
625     erasure->npos = g_list_index(ui.selection->layer->items, item);
626     erasure->nrepl = 0;
627     erasure->replacement_items = NULL;
628     ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
629     ui.selection->layer->nitems--;
630     undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
631   }
632   reset_selection();
633 
634   /* NOTE: the erasurelist is built backwards; this guarantees that,
635      upon undo, the erasure->npos fields give the correct position
636      where each item should be reinserted as the list is traversed in
637      the forward direction */
638 }
639 
640 // modify the color or thickness of pen strokes in a selection
641 
recolor_selection(int color_no,guint color_rgba)642 void recolor_selection(int color_no, guint color_rgba)
643 {
644   GList *itemlist;
645   struct Item *item;
646   struct Brush *brush;
647   GnomeCanvasGroup *group;
648 
649   if (ui.selection == NULL) return;
650   prepare_new_undo();
651   undo->type = ITEM_REPAINTSEL;
652   undo->itemlist = NULL;
653   undo->auxlist = NULL;
654   for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
655     item = (struct Item *)itemlist->data;
656     if (item->type != ITEM_STROKE && item->type != ITEM_TEXT) continue;
657     if (item->type == ITEM_STROKE && item->brush.tool_type!=TOOL_PEN) continue;
658     // store info for undo
659     undo->itemlist = g_list_append(undo->itemlist, item);
660     brush = (struct Brush *)g_malloc(sizeof(struct Brush));
661     g_memmove(brush, &(item->brush), sizeof(struct Brush));
662     undo->auxlist = g_list_append(undo->auxlist, brush);
663     // repaint the stroke
664     item->brush.color_no = color_no;
665     item->brush.color_rgba = color_rgba | 0xff; // no alpha
666     if (item->canvas_item!=NULL) {
667       if (!item->brush.variable_width)
668         gnome_canvas_item_set(item->canvas_item,
669            "fill-color-rgba", item->brush.color_rgba, NULL);
670       else {
671         group = (GnomeCanvasGroup *) item->canvas_item->parent;
672         gtk_object_destroy(GTK_OBJECT(item->canvas_item));
673         make_canvas_item_one(group, item);
674       }
675     }
676   }
677 }
678 
rethicken_selection(int val)679 void rethicken_selection(int val)
680 {
681   GList *itemlist;
682   struct Item *item;
683   struct Brush *brush;
684   GnomeCanvasGroup *group;
685 
686   if (ui.selection == NULL) return;
687   prepare_new_undo();
688   undo->type = ITEM_REPAINTSEL;
689   undo->itemlist = NULL;
690   undo->auxlist = NULL;
691   for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
692     item = (struct Item *)itemlist->data;
693     if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
694     // store info for undo
695     undo->itemlist = g_list_append(undo->itemlist, item);
696     brush = (struct Brush *)g_malloc(sizeof(struct Brush));
697     g_memmove(brush, &(item->brush), sizeof(struct Brush));
698     undo->auxlist = g_list_append(undo->auxlist, brush);
699     // repaint the stroke
700     item->brush.thickness_no = val;
701     item->brush.thickness = predef_thickness[TOOL_PEN][val];
702     if (item->canvas_item!=NULL) {
703       if (!item->brush.variable_width)
704         gnome_canvas_item_set(item->canvas_item,
705            "width-units", item->brush.thickness, NULL);
706       else {
707         group = (GnomeCanvasGroup *) item->canvas_item->parent;
708         gtk_object_destroy(GTK_OBJECT(item->canvas_item));
709         item->brush.variable_width = FALSE;
710         make_canvas_item_one(group, item);
711       }
712     }
713   }
714 }
715 
716