8 #include <libgnomecanvas/libgnomecanvas.h>
10 #include <libart_lgpl/art_vpath_dash.h>
13 #include "xo-callbacks.h"
14 #include "xo-interface.h"
15 #include "xo-support.h"
19 /************** drawing nice cursors *********/
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};
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};
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};
36 void set_cursor_busy(gboolean 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);
47 gdk_window_set_cursor(GTK_WIDGET(winMain)->window, NULL);
50 gdk_display_sync(gdk_display_get_default());
53 void update_cursor(void)
55 GdkPixmap *source, *mask;
56 GdkColor fg = {0, 0, 0, 0}, bg = {0, 65535, 65535, 65535};
58 if (GTK_WIDGET(canvas)->window == NULL) return;
60 if (ui.cursor!=NULL) {
61 gdk_cursor_unref(ui.cursor);
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);
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);
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);
93 else if (ui.cur_item_type == ITEM_SELECTRECT) {
94 ui.cursor = gdk_cursor_new(GDK_TCROSS);
97 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
101 /************** painting strokes *************/
103 #define SUBDIVIDE_MAXDIST 5.0
105 void subdivide_cur_path(null)
109 double x0, y0, x1, y1;
111 for (n=0, p=ui.cur_path.coords; n<ui.cur_path.num_points-1; n++, p+=2) {
112 pieces = (int)floor(hypot(p[2]-p[0], p[3]-p[1])/SUBDIVIDE_MAXDIST);
114 x0 = p[0]; y0 = p[1];
115 x1 = p[2]; y1 = p[3];
116 realloc_cur_path(ui.cur_path.num_points+pieces-1);
117 g_memmove(ui.cur_path.coords+2*(n+pieces), ui.cur_path.coords+2*(n+1),
118 2*(ui.cur_path.num_points-n-1)*sizeof(double));
119 p = ui.cur_path.coords+2*n;
120 ui.cur_path.num_points += pieces-1;
122 for (k=1; k<pieces; k++) {
124 p[0] = x0 + k*(x1-x0)/pieces;
125 p[1] = y0 + k*(y1-y0)/pieces;
131 void create_new_stroke(GdkEvent *event)
133 ui.cur_item_type = ITEM_STROKE;
134 ui.cur_item = g_new(struct Item, 1);
135 ui.cur_item->type = ITEM_STROKE;
136 g_memmove(&(ui.cur_item->brush), ui.cur_brush, sizeof(struct Brush));
137 ui.cur_item->path = &ui.cur_path;
139 ui.cur_path.num_points = 1;
140 get_pointer_coords(event, ui.cur_path.coords);
142 if (ui.ruler[ui.cur_mapping])
143 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
144 gnome_canvas_line_get_type(),
145 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
146 "fill-color-rgba", ui.cur_item->brush.color_rgba,
147 "width-units", ui.cur_item->brush.thickness, NULL);
149 ui.cur_item->canvas_item = gnome_canvas_item_new(
150 ui.cur_layer->group, gnome_canvas_group_get_type(), NULL);
153 void continue_stroke(GdkEvent *event)
155 GnomeCanvasPoints seg;
158 if (ui.ruler[ui.cur_mapping]) {
159 pt = ui.cur_path.coords;
161 realloc_cur_path(ui.cur_path.num_points+1);
162 pt = ui.cur_path.coords + 2*(ui.cur_path.num_points-1);
165 get_pointer_coords(event, pt+2);
167 if (ui.ruler[ui.cur_mapping])
168 ui.cur_path.num_points = 2;
170 if (hypot(pt[0]-pt[2], pt[1]-pt[3]) < PIXEL_MOTION_THRESHOLD/ui.zoom)
171 return; // not a meaningful motion
172 ui.cur_path.num_points++;
179 /* note: we're using a piece of the cur_path array. This is ok because
180 upon creation the line just copies the contents of the GnomeCanvasPoints
181 into an internal structure */
183 if (ui.ruler[ui.cur_mapping])
184 gnome_canvas_item_set(ui.cur_item->canvas_item, "points", &seg, NULL);
186 gnome_canvas_item_new((GnomeCanvasGroup *)ui.cur_item->canvas_item,
187 gnome_canvas_line_get_type(), "points", &seg,
188 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
189 "fill-color-rgba", ui.cur_item->brush.color_rgba,
190 "width-units", ui.cur_item->brush.thickness, NULL);
193 void finalize_stroke(void)
195 if (ui.cur_path.num_points == 1) { // GnomeCanvas doesn't like num_points=1
196 ui.cur_path.coords[2] = ui.cur_path.coords[0]+0.1;
197 ui.cur_path.coords[3] = ui.cur_path.coords[1];
198 ui.cur_path.num_points = 2;
201 subdivide_cur_path(); // split the segment so eraser will work
203 ui.cur_item->path = gnome_canvas_points_new(ui.cur_path.num_points);
204 g_memmove(ui.cur_item->path->coords, ui.cur_path.coords,
205 2*ui.cur_path.num_points*sizeof(double));
206 update_item_bbox(ui.cur_item);
207 ui.cur_path.num_points = 0;
209 // destroy the entire group of temporary line segments
210 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
211 // make a new line item to replace it
212 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
213 gnome_canvas_line_get_type(), "points", ui.cur_item->path,
214 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
215 "fill-color-rgba", ui.cur_item->brush.color_rgba,
216 "width-units", ui.cur_item->brush.thickness, NULL);
218 // add undo information
220 undo->type = ITEM_STROKE;
221 undo->item = ui.cur_item;
222 undo->layer = ui.cur_layer;
224 // store the item on top of the layer stack
225 ui.cur_layer->items = g_list_append(ui.cur_layer->items, ui.cur_item);
226 ui.cur_layer->nitems++;
228 ui.cur_item_type = ITEM_NONE;
231 /************** eraser tool *************/
233 void erase_stroke_portions(struct Item *item, double x, double y, double radius,
234 gboolean whole_strokes, struct UndoErasureData *erasure)
238 struct Item *newhead, *newtail;
239 gboolean need_recalc = FALSE;
241 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
242 if (hypot(pt[0]-x, pt[1]-y) <= radius) { // found an intersection
243 // FIXME: need to test if line SEGMENT hits the circle
244 // hide the canvas item, and create erasure data if needed
245 if (erasure == NULL) {
246 item->type = ITEM_TEMP_STROKE;
247 gnome_canvas_item_hide(item->canvas_item);
248 /* we'll use this hidden item as an insertion point later */
249 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
250 item->erasure = erasure;
251 erasure->item = item;
252 erasure->npos = g_list_index(ui.cur_layer->items, item);
254 erasure->replacement_items = NULL;
257 newhead = newtail = NULL;
258 if (!whole_strokes) {
260 newhead = (struct Item *)g_malloc(sizeof(struct Item));
261 newhead->type = ITEM_STROKE;
262 g_memmove(&newhead->brush, &item->brush, sizeof(struct Brush));
263 newhead->path = gnome_canvas_points_new(i);
264 g_memmove(newhead->path->coords, item->path->coords, 2*i*sizeof(double));
266 while (++i < item->path->num_points) {
268 if (hypot(pt[0]-x, pt[1]-y) > radius) break;
270 if (i<item->path->num_points-1) {
271 newtail = (struct Item *)g_malloc(sizeof(struct Item));
272 newtail->type = ITEM_STROKE;
273 g_memmove(&newtail->brush, &item->brush, sizeof(struct Brush));
274 newtail->path = gnome_canvas_points_new(item->path->num_points-i);
275 g_memmove(newtail->path->coords, item->path->coords+2*i,
276 2*(item->path->num_points-i)*sizeof(double));
277 newtail->canvas_item = NULL;
280 if (item->type == ITEM_STROKE) {
281 // it's inside an erasure list - we destroy it
282 gnome_canvas_points_free(item->path);
283 if (item->canvas_item != NULL)
284 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
286 erasure->replacement_items = g_list_remove(erasure->replacement_items, item);
290 if (newhead != NULL) {
291 update_item_bbox(newhead);
292 newhead->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
293 gnome_canvas_line_get_type(), "points", newhead->path,
294 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
295 "fill-color-rgba", newhead->brush.color_rgba,
296 "width-units", newhead->brush.thickness, NULL);
297 lower_canvas_item_to(ui.cur_layer->group,
298 newhead->canvas_item, erasure->item->canvas_item);
299 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newhead);
301 // prepending ensures it won't get processed twice
303 // recurse into the new tail
304 need_recalc = (newtail!=NULL);
305 if (newtail == NULL) break;
307 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newtail);
309 i=0; pt=item->path->coords;
312 // add the tail if needed
313 if (!need_recalc) return;
314 update_item_bbox(item);
315 item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
316 gnome_canvas_line_get_type(), "points", item->path,
317 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
318 "fill-color-rgba", item->brush.color_rgba,
319 "width-units", item->brush.thickness, NULL);
320 lower_canvas_item_to(ui.cur_layer->group, item->canvas_item,
321 erasure->item->canvas_item);
325 void do_eraser(GdkEvent *event, double radius, gboolean whole_strokes)
327 struct Item *item, *repl;
328 GList *itemlist, *repllist;
330 struct BBox eraserbox;
332 get_pointer_coords(event, pos);
333 eraserbox.left = pos[0]-radius;
334 eraserbox.right = pos[0]+radius;
335 eraserbox.top = pos[1]-radius;
336 eraserbox.bottom = pos[1]+radius;
337 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
338 item = (struct Item *)itemlist->data;
339 if (item->type == ITEM_STROKE) {
340 if (!have_intersect(&(item->bbox), &eraserbox)) continue;
341 erase_stroke_portions(item, pos[0], pos[1], radius, whole_strokes, NULL);
342 } else if (item->type == ITEM_TEMP_STROKE) {
343 repllist = item->erasure->replacement_items;
344 while (repllist!=NULL) {
345 repl = (struct Item *)repllist->data;
346 // we may delete the item soon! so advance now in the list
347 repllist = repllist->next;
348 if (have_intersect(&(repl->bbox), &eraserbox))
349 erase_stroke_portions(repl, pos[0], pos[1], radius, whole_strokes, item->erasure);
355 void finalize_erasure(void)
357 GList *itemlist, *partlist;
358 struct Item *item, *part;
361 undo->type = ITEM_ERASURE;
362 undo->layer = ui.cur_layer;
363 undo->erasurelist = NULL;
365 itemlist = ui.cur_layer->items;
366 while (itemlist!=NULL) {
367 item = (struct Item *)itemlist->data;
368 itemlist = itemlist->next;
369 if (item->type != ITEM_TEMP_STROKE) continue;
370 item->type = ITEM_STROKE;
371 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, item);
372 // the item has an invisible canvas item, which used to act as anchor
373 if (item->canvas_item!=NULL) {
374 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
375 item->canvas_item = NULL;
377 undo->erasurelist = g_list_append(undo->erasurelist, item->erasure);
378 // add the new strokes into the current layer
379 for (partlist = item->erasure->replacement_items; partlist!=NULL; partlist = partlist->next)
380 ui.cur_layer->items = g_list_insert_before(
381 ui.cur_layer->items, itemlist, partlist->data);
382 ui.cur_layer->nitems += item->erasure->nrepl-1;
386 ui.cur_item_type = ITEM_NONE;
388 /* NOTE: the list of erasures goes in the depth order of the layer;
389 this guarantees that, upon undo, the erasure->npos fields give the
390 correct position where each item should be reinserted as the list
391 is traversed in the forward direction */
394 /************ selection tools ***********/
396 void make_dashed(GnomeCanvasItem *item)
404 dashlen[0] = dashlen[1] = 6.0;
405 gnome_canvas_item_set(item, "dash", &dash, NULL);
409 void start_selectrect(GdkEvent *event)
414 ui.cur_item_type = ITEM_SELECTRECT;
415 ui.selection = g_new(struct Selection, 1);
416 ui.selection->type = ITEM_SELECTRECT;
417 ui.selection->items = NULL;
418 ui.selection->layer = ui.cur_layer;
420 get_pointer_coords(event, pt);
421 ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
422 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
424 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
425 gnome_canvas_rect_get_type(), "width-pixels", 1,
426 "outline-color-rgba", 0x000000ff,
427 "fill-color-rgba", 0x80808040,
428 "x1", pt[0], "x2", pt[0], "y1", pt[1], "y2", pt[1], NULL);
432 void finalize_selectrect(void)
434 double x1, x2, y1, y2;
439 ui.cur_item_type = ITEM_NONE;
441 if (ui.selection->bbox.left > ui.selection->bbox.right) {
442 x1 = ui.selection->bbox.right; x2 = ui.selection->bbox.left;
443 ui.selection->bbox.left = x1; ui.selection->bbox.right = x2;
445 x1 = ui.selection->bbox.left; x2 = ui.selection->bbox.right;
448 if (ui.selection->bbox.top > ui.selection->bbox.bottom) {
449 y1 = ui.selection->bbox.bottom; y2 = ui.selection->bbox.top;
450 ui.selection->bbox.top = y1; ui.selection->bbox.bottom = y2;
452 y1 = ui.selection->bbox.top; y2 = ui.selection->bbox.bottom;
455 for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
456 item = (struct Item *)itemlist->data;
457 if (item->bbox.left >= x1 && item->bbox.right <= x2 &&
458 item->bbox.top >= y1 && item->bbox.bottom <= y2) {
459 ui.selection->items = g_list_append(ui.selection->items, item);
463 if (ui.selection->items == NULL) reset_selection();
464 else make_dashed(ui.selection->canvas_item);
466 update_copy_paste_enabled();
469 gboolean start_movesel(GdkEvent *event)
473 if (ui.selection==NULL) return FALSE;
474 if (ui.cur_layer != ui.selection->layer) return FALSE;
476 get_pointer_coords(event, pt);
477 if (ui.selection->type == ITEM_SELECTRECT) {
478 if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
479 pt[1]<ui.selection->bbox.top || pt[1]>ui.selection->bbox.bottom)
481 ui.cur_item_type = ITEM_MOVESEL;
482 ui.selection->anchor_x = ui.selection->last_x = pt[0];
483 ui.selection->anchor_y = ui.selection->last_y = pt[1];
484 ui.selection->orig_pageno = ui.pageno;
485 ui.selection->move_pageno = ui.pageno;
486 ui.selection->move_layer = ui.selection->layer;
487 ui.selection->move_pagedelta = 0.;
488 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
495 void start_vertspace(GdkEvent *event)
502 ui.cur_item_type = ITEM_MOVESEL_VERT;
503 ui.selection = g_new(struct Selection, 1);
504 ui.selection->type = ITEM_MOVESEL_VERT;
505 ui.selection->items = NULL;
506 ui.selection->layer = ui.cur_layer;
508 get_pointer_coords(event, pt);
509 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
510 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
511 item = (struct Item *)itemlist->data;
512 if (item->bbox.top >= pt[1]) {
513 ui.selection->items = g_list_append(ui.selection->items, item);
514 if (item->bbox.bottom > ui.selection->bbox.bottom)
515 ui.selection->bbox.bottom = item->bbox.bottom;
519 ui.selection->anchor_x = ui.selection->last_x = 0;
520 ui.selection->anchor_y = ui.selection->last_y = pt[1];
521 ui.selection->orig_pageno = ui.pageno;
522 ui.selection->move_pageno = ui.pageno;
523 ui.selection->move_layer = ui.selection->layer;
524 ui.selection->move_pagedelta = 0.;
525 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
526 gnome_canvas_rect_get_type(), "width-pixels", 1,
527 "outline-color-rgba", 0x000000ff,
528 "fill-color-rgba", 0x80808040,
529 "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
533 void continue_movesel(GdkEvent *event)
535 double pt[2], dx, dy, upmargin;
539 struct Page *tmppage;
541 get_pointer_coords(event, pt);
542 if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
543 pt[1] += ui.selection->move_pagedelta;
545 // check for page jumps
546 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
547 upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
548 else upmargin = VIEW_CONTINUOUS_SKIP;
549 tmppageno = ui.selection->move_pageno;
550 tmppage = g_list_nth_data(journal.pages, tmppageno);
551 while (ui.view_continuous && (pt[1] < - upmargin)) {
552 if (tmppageno == 0) break;
554 tmppage = g_list_nth_data(journal.pages, tmppageno);
555 pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
556 ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
558 while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
559 if (tmppageno == journal.npages-1) break;
560 pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
561 ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
563 tmppage = g_list_nth_data(journal.pages, tmppageno);
566 if (tmppageno != ui.selection->move_pageno) {
567 // move to a new page !
568 ui.selection->move_pageno = tmppageno;
569 if (tmppageno == ui.selection->orig_pageno)
570 ui.selection->move_layer = ui.selection->layer;
572 ui.selection->move_layer = (struct Layer *)(g_list_last(
573 ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
574 gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
575 for (list = ui.selection->items; list!=NULL; list = list->next) {
576 item = (struct Item *)list->data;
577 if (item->canvas_item!=NULL)
578 gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
580 // avoid a refresh bug
581 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
582 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
583 gnome_canvas_item_set(ui.selection->canvas_item,
584 "x2", tmppage->width+100,
585 "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
588 // now, process things normally
590 dx = pt[0] - ui.selection->last_x;
591 dy = pt[1] - ui.selection->last_y;
592 if (hypot(dx,dy) < 1) return; // don't move subpixel
593 ui.selection->last_x = pt[0];
594 ui.selection->last_y = pt[1];
596 // move the canvas items
597 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
598 gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
600 gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
602 for (list = ui.selection->items; list != NULL; list = list->next) {
603 item = (struct Item *)list->data;
604 if (item->canvas_item != NULL)
605 gnome_canvas_item_move(item->canvas_item, dx, dy);
609 void finalize_movesel(void)
614 if (ui.selection->items != NULL) {
616 undo->type = ITEM_MOVESEL;
617 undo->itemlist = g_list_copy(ui.selection->items);
618 undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
619 undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
620 undo->layer = ui.selection->layer;
621 undo->layer2 = ui.selection->move_layer;
622 undo->auxlist = NULL;
623 // build auxlist = pointers to Item's just before ours (for depths)
624 for (list = ui.selection->items; list!=NULL; list = list->next) {
625 link = g_list_find(ui.selection->layer->items, list->data);
626 if (link!=NULL) link = link->prev;
627 undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
629 ui.selection->layer = ui.selection->move_layer;
630 move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
631 undo->layer, undo->layer2,
632 (undo->layer == undo->layer2)?undo->auxlist:NULL);
635 if (ui.selection->move_pageno!=ui.selection->orig_pageno)
636 do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
638 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
641 ui.selection->bbox.left += undo->val_x;
642 ui.selection->bbox.right += undo->val_x;
643 ui.selection->bbox.top += undo->val_y;
644 ui.selection->bbox.bottom += undo->val_y;
645 make_dashed(ui.selection->canvas_item);
647 ui.cur_item_type = ITEM_NONE;
652 void selection_delete(void)
654 struct UndoErasureData *erasure;
658 if (ui.selection == NULL) return;
660 undo->type = ITEM_ERASURE;
661 undo->layer = ui.selection->layer;
662 undo->erasurelist = NULL;
663 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
664 item = (struct Item *)itemlist->data;
665 if (item->canvas_item!=NULL)
666 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
667 erasure = g_new(struct UndoErasureData, 1);
668 erasure->item = item;
669 erasure->npos = g_list_index(ui.selection->layer->items, item);
671 erasure->replacement_items = NULL;
672 ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
673 ui.selection->layer->nitems--;
674 undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
678 /* NOTE: the erasurelist is built backwards; this guarantees that,
679 upon undo, the erasure->npos fields give the correct position
680 where each item should be reinserted as the list is traversed in
681 the forward direction */
684 void callback_clipboard_get(GtkClipboard *clipboard,
685 GtkSelectionData *selection_data,
686 guint info, gpointer user_data)
690 g_memmove(&length, user_data, sizeof(int));
691 gtk_selection_data_set(selection_data,
692 gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
695 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
700 void selection_to_clip(void)
706 GtkTargetEntry target;
708 if (ui.selection == NULL) return;
709 bufsz = 2*sizeof(int) // bufsz, nitems
710 + sizeof(struct BBox); // bbox
712 for (list = ui.selection->items; list != NULL; list = list->next) {
713 item = (struct Item *)list->data;
715 if (item->type == ITEM_STROKE) {
716 bufsz+= sizeof(int) // type
717 + sizeof(struct Brush) // brush
718 + sizeof(int) // num_points
719 + 2*item->path->num_points*sizeof(double); // the points
721 else bufsz+= sizeof(int); // type
723 p = buf = g_malloc(bufsz);
724 g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
725 g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
726 g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
727 for (list = ui.selection->items; list != NULL; list = list->next) {
728 item = (struct Item *)list->data;
729 g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
730 if (item->type == ITEM_STROKE) {
731 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
732 g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
733 g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
734 p+= 2*item->path->num_points*sizeof(double);
738 target.target = "_XOURNAL";
742 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
744 callback_clipboard_get, callback_clipboard_clear, buf);
748 void clipboard_paste(void)
750 GtkSelectionData *sel_data;
755 double hoffset, voffset, cx, cy;
759 if (ui.cur_layer == NULL) return;
761 ui.cur_item_type = ITEM_PASTE;
762 sel_data = gtk_clipboard_wait_for_contents(
763 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
764 gdk_atom_intern("_XOURNAL", FALSE));
765 ui.cur_item_type = ITEM_NONE;
766 if (sel_data == NULL) return; // paste failed
770 ui.selection = g_new(struct Selection, 1);
771 p = sel_data->data + sizeof(int);
772 g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
773 ui.selection->type = ITEM_SELECTRECT;
774 ui.selection->layer = ui.cur_layer;
775 g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
776 ui.selection->items = NULL;
778 // find by how much we translate the pasted selection
779 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
780 gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
781 gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
782 cx -= ui.cur_page->hoffset;
783 cy -= ui.cur_page->voffset;
784 if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
785 cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
786 if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
787 cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
788 if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
789 cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
790 if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
791 cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
792 hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
793 voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
794 ui.selection->bbox.left += hoffset;
795 ui.selection->bbox.right += hoffset;
796 ui.selection->bbox.top += voffset;
797 ui.selection->bbox.bottom += voffset;
799 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
800 gnome_canvas_rect_get_type(), "width-pixels", 1,
801 "outline-color-rgba", 0x000000ff,
802 "fill-color-rgba", 0x80808040,
803 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
804 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
805 make_dashed(ui.selection->canvas_item);
807 while (nitems-- > 0) {
808 item = g_new(struct Item, 1);
809 ui.selection->items = g_list_append(ui.selection->items, item);
810 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
811 ui.cur_layer->nitems++;
812 g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
813 if (item->type == ITEM_STROKE) {
814 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
815 g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
816 item->path = gnome_canvas_points_new(npts);
818 for (i=0; i<npts; i++) {
819 item->path->coords[2*i] = pf[2*i] + hoffset;
820 item->path->coords[2*i+1] = pf[2*i+1] + voffset;
822 p+= 2*item->path->num_points*sizeof(double);
823 update_item_bbox(item);
824 item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
825 gnome_canvas_line_get_type(), "points", item->path,
826 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
827 "fill-color-rgba", item->brush.color_rgba,
828 "width-units", item->brush.thickness, NULL);
833 undo->type = ITEM_PASTE;
834 undo->layer = ui.cur_layer;
835 undo->itemlist = g_list_copy(ui.selection->items);
837 gtk_selection_data_free(sel_data);
838 update_copy_paste_enabled();
841 // modify the color or thickness of pen strokes in a selection
843 void recolor_selection(int color)
849 if (ui.selection == NULL) return;
851 undo->type = ITEM_REPAINTSEL;
852 undo->itemlist = NULL;
853 undo->auxlist = NULL;
854 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
855 item = (struct Item *)itemlist->data;
856 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
857 // store info for undo
858 undo->itemlist = g_list_append(undo->itemlist, item);
859 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
860 g_memmove(brush, &(item->brush), sizeof(struct Brush));
861 undo->auxlist = g_list_append(undo->auxlist, brush);
862 // repaint the stroke
863 item->brush.color_no = color;
864 item->brush.color_rgba = predef_colors_rgba[color];
865 if (item->canvas_item!=NULL)
866 gnome_canvas_item_set(item->canvas_item,
867 "fill-color-rgba", item->brush.color_rgba, NULL);
871 void rethicken_selection(int val)
877 if (ui.selection == NULL) return;
879 undo->type = ITEM_REPAINTSEL;
880 undo->itemlist = NULL;
881 undo->auxlist = NULL;
882 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
883 item = (struct Item *)itemlist->data;
884 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
885 // store info for undo
886 undo->itemlist = g_list_append(undo->itemlist, item);
887 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
888 g_memmove(brush, &(item->brush), sizeof(struct Brush));
889 undo->auxlist = g_list_append(undo->auxlist, brush);
890 // repaint the stroke
891 item->brush.thickness_no = val;
892 item->brush.thickness = predef_thickness[TOOL_PEN][val];
893 if (item->canvas_item!=NULL)
894 gnome_canvas_item_set(item->canvas_item,
895 "width-units", item->brush.thickness, NULL);