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