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);
96 else if (ui.toolno[ui.cur_mapping] == TOOL_HAND) {
97 ui.cursor = gdk_cursor_new(GDK_HAND1);
100 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
104 /************** painting strokes *************/
106 #define SUBDIVIDE_MAXDIST 5.0
108 void subdivide_cur_path(null)
112 double x0, y0, x1, y1;
114 for (n=0, p=ui.cur_path.coords; n<ui.cur_path.num_points-1; n++, p+=2) {
115 pieces = (int)floor(hypot(p[2]-p[0], p[3]-p[1])/SUBDIVIDE_MAXDIST);
117 x0 = p[0]; y0 = p[1];
118 x1 = p[2]; y1 = p[3];
119 realloc_cur_path(ui.cur_path.num_points+pieces-1);
120 g_memmove(ui.cur_path.coords+2*(n+pieces), ui.cur_path.coords+2*(n+1),
121 2*(ui.cur_path.num_points-n-1)*sizeof(double));
122 p = ui.cur_path.coords+2*n;
123 ui.cur_path.num_points += pieces-1;
125 for (k=1; k<pieces; k++) {
127 p[0] = x0 + k*(x1-x0)/pieces;
128 p[1] = y0 + k*(y1-y0)/pieces;
134 void create_new_stroke(GdkEvent *event)
136 ui.cur_item_type = ITEM_STROKE;
137 ui.cur_item = g_new(struct Item, 1);
138 ui.cur_item->type = ITEM_STROKE;
139 g_memmove(&(ui.cur_item->brush), ui.cur_brush, sizeof(struct Brush));
140 ui.cur_item->path = &ui.cur_path;
142 ui.cur_path.num_points = 1;
143 get_pointer_coords(event, ui.cur_path.coords);
145 if (ui.ruler[ui.cur_mapping])
146 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
147 gnome_canvas_line_get_type(),
148 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
149 "fill-color-rgba", ui.cur_item->brush.color_rgba,
150 "width-units", ui.cur_item->brush.thickness, NULL);
152 ui.cur_item->canvas_item = gnome_canvas_item_new(
153 ui.cur_layer->group, gnome_canvas_group_get_type(), NULL);
156 void continue_stroke(GdkEvent *event)
158 GnomeCanvasPoints seg;
161 if (ui.ruler[ui.cur_mapping]) {
162 pt = ui.cur_path.coords;
164 realloc_cur_path(ui.cur_path.num_points+1);
165 pt = ui.cur_path.coords + 2*(ui.cur_path.num_points-1);
168 get_pointer_coords(event, pt+2);
170 if (ui.ruler[ui.cur_mapping])
171 ui.cur_path.num_points = 2;
173 if (hypot(pt[0]-pt[2], pt[1]-pt[3]) < PIXEL_MOTION_THRESHOLD/ui.zoom)
174 return; // not a meaningful motion
175 ui.cur_path.num_points++;
182 /* note: we're using a piece of the cur_path array. This is ok because
183 upon creation the line just copies the contents of the GnomeCanvasPoints
184 into an internal structure */
186 if (ui.ruler[ui.cur_mapping])
187 gnome_canvas_item_set(ui.cur_item->canvas_item, "points", &seg, NULL);
189 gnome_canvas_item_new((GnomeCanvasGroup *)ui.cur_item->canvas_item,
190 gnome_canvas_line_get_type(), "points", &seg,
191 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
192 "fill-color-rgba", ui.cur_item->brush.color_rgba,
193 "width-units", ui.cur_item->brush.thickness, NULL);
196 void finalize_stroke(void)
198 if (ui.cur_path.num_points == 1) { // GnomeCanvas doesn't like num_points=1
199 ui.cur_path.coords[2] = ui.cur_path.coords[0]+0.1;
200 ui.cur_path.coords[3] = ui.cur_path.coords[1];
201 ui.cur_path.num_points = 2;
204 subdivide_cur_path(); // split the segment so eraser will work
206 ui.cur_item->path = gnome_canvas_points_new(ui.cur_path.num_points);
207 g_memmove(ui.cur_item->path->coords, ui.cur_path.coords,
208 2*ui.cur_path.num_points*sizeof(double));
209 update_item_bbox(ui.cur_item);
210 ui.cur_path.num_points = 0;
212 // destroy the entire group of temporary line segments
213 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
214 // make a new line item to replace it
215 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
216 gnome_canvas_line_get_type(), "points", ui.cur_item->path,
217 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
218 "fill-color-rgba", ui.cur_item->brush.color_rgba,
219 "width-units", ui.cur_item->brush.thickness, NULL);
221 // add undo information
223 undo->type = ITEM_STROKE;
224 undo->item = ui.cur_item;
225 undo->layer = ui.cur_layer;
227 // store the item on top of the layer stack
228 ui.cur_layer->items = g_list_append(ui.cur_layer->items, ui.cur_item);
229 ui.cur_layer->nitems++;
231 ui.cur_item_type = ITEM_NONE;
234 /************** eraser tool *************/
236 void erase_stroke_portions(struct Item *item, double x, double y, double radius,
237 gboolean whole_strokes, struct UndoErasureData *erasure)
241 struct Item *newhead, *newtail;
242 gboolean need_recalc = FALSE;
244 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
245 if (hypot(pt[0]-x, pt[1]-y) <= radius) { // found an intersection
246 // FIXME: need to test if line SEGMENT hits the circle
247 // hide the canvas item, and create erasure data if needed
248 if (erasure == NULL) {
249 item->type = ITEM_TEMP_STROKE;
250 gnome_canvas_item_hide(item->canvas_item);
251 /* we'll use this hidden item as an insertion point later */
252 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
253 item->erasure = erasure;
254 erasure->item = item;
255 erasure->npos = g_list_index(ui.cur_layer->items, item);
257 erasure->replacement_items = NULL;
260 newhead = newtail = NULL;
261 if (!whole_strokes) {
263 newhead = (struct Item *)g_malloc(sizeof(struct Item));
264 newhead->type = ITEM_STROKE;
265 g_memmove(&newhead->brush, &item->brush, sizeof(struct Brush));
266 newhead->path = gnome_canvas_points_new(i);
267 g_memmove(newhead->path->coords, item->path->coords, 2*i*sizeof(double));
269 while (++i < item->path->num_points) {
271 if (hypot(pt[0]-x, pt[1]-y) > radius) break;
273 if (i<item->path->num_points-1) {
274 newtail = (struct Item *)g_malloc(sizeof(struct Item));
275 newtail->type = ITEM_STROKE;
276 g_memmove(&newtail->brush, &item->brush, sizeof(struct Brush));
277 newtail->path = gnome_canvas_points_new(item->path->num_points-i);
278 g_memmove(newtail->path->coords, item->path->coords+2*i,
279 2*(item->path->num_points-i)*sizeof(double));
280 newtail->canvas_item = NULL;
283 if (item->type == ITEM_STROKE) {
284 // it's inside an erasure list - we destroy it
285 gnome_canvas_points_free(item->path);
286 if (item->canvas_item != NULL)
287 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
289 erasure->replacement_items = g_list_remove(erasure->replacement_items, item);
293 if (newhead != NULL) {
294 update_item_bbox(newhead);
295 newhead->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
296 gnome_canvas_line_get_type(), "points", newhead->path,
297 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
298 "fill-color-rgba", newhead->brush.color_rgba,
299 "width-units", newhead->brush.thickness, NULL);
300 lower_canvas_item_to(ui.cur_layer->group,
301 newhead->canvas_item, erasure->item->canvas_item);
302 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newhead);
304 // prepending ensures it won't get processed twice
306 // recurse into the new tail
307 need_recalc = (newtail!=NULL);
308 if (newtail == NULL) break;
310 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newtail);
312 i=0; pt=item->path->coords;
315 // add the tail if needed
316 if (!need_recalc) return;
317 update_item_bbox(item);
318 item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
319 gnome_canvas_line_get_type(), "points", item->path,
320 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
321 "fill-color-rgba", item->brush.color_rgba,
322 "width-units", item->brush.thickness, NULL);
323 lower_canvas_item_to(ui.cur_layer->group, item->canvas_item,
324 erasure->item->canvas_item);
328 void do_eraser(GdkEvent *event, double radius, gboolean whole_strokes)
330 struct Item *item, *repl;
331 GList *itemlist, *repllist;
333 struct BBox eraserbox;
335 get_pointer_coords(event, pos);
336 eraserbox.left = pos[0]-radius;
337 eraserbox.right = pos[0]+radius;
338 eraserbox.top = pos[1]-radius;
339 eraserbox.bottom = pos[1]+radius;
340 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
341 item = (struct Item *)itemlist->data;
342 if (item->type == ITEM_STROKE) {
343 if (!have_intersect(&(item->bbox), &eraserbox)) continue;
344 erase_stroke_portions(item, pos[0], pos[1], radius, whole_strokes, NULL);
345 } else if (item->type == ITEM_TEMP_STROKE) {
346 repllist = item->erasure->replacement_items;
347 while (repllist!=NULL) {
348 repl = (struct Item *)repllist->data;
349 // we may delete the item soon! so advance now in the list
350 repllist = repllist->next;
351 if (have_intersect(&(repl->bbox), &eraserbox))
352 erase_stroke_portions(repl, pos[0], pos[1], radius, whole_strokes, item->erasure);
358 void finalize_erasure(void)
360 GList *itemlist, *partlist;
361 struct Item *item, *part;
364 undo->type = ITEM_ERASURE;
365 undo->layer = ui.cur_layer;
366 undo->erasurelist = NULL;
368 itemlist = ui.cur_layer->items;
369 while (itemlist!=NULL) {
370 item = (struct Item *)itemlist->data;
371 itemlist = itemlist->next;
372 if (item->type != ITEM_TEMP_STROKE) continue;
373 item->type = ITEM_STROKE;
374 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, item);
375 // the item has an invisible canvas item, which used to act as anchor
376 if (item->canvas_item!=NULL) {
377 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
378 item->canvas_item = NULL;
380 undo->erasurelist = g_list_append(undo->erasurelist, item->erasure);
381 // add the new strokes into the current layer
382 for (partlist = item->erasure->replacement_items; partlist!=NULL; partlist = partlist->next)
383 ui.cur_layer->items = g_list_insert_before(
384 ui.cur_layer->items, itemlist, partlist->data);
385 ui.cur_layer->nitems += item->erasure->nrepl-1;
389 ui.cur_item_type = ITEM_NONE;
391 /* NOTE: the list of erasures goes in the depth order of the layer;
392 this guarantees that, upon undo, the erasure->npos fields give the
393 correct position where each item should be reinserted as the list
394 is traversed in the forward direction */
397 /************ selection tools ***********/
399 void make_dashed(GnomeCanvasItem *item)
407 dashlen[0] = dashlen[1] = 6.0;
408 gnome_canvas_item_set(item, "dash", &dash, NULL);
412 void start_selectrect(GdkEvent *event)
417 ui.cur_item_type = ITEM_SELECTRECT;
418 ui.selection = g_new(struct Selection, 1);
419 ui.selection->type = ITEM_SELECTRECT;
420 ui.selection->items = NULL;
421 ui.selection->layer = ui.cur_layer;
423 get_pointer_coords(event, pt);
424 ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
425 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
427 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
428 gnome_canvas_rect_get_type(), "width-pixels", 1,
429 "outline-color-rgba", 0x000000ff,
430 "fill-color-rgba", 0x80808040,
431 "x1", pt[0], "x2", pt[0], "y1", pt[1], "y2", pt[1], NULL);
435 void finalize_selectrect(void)
437 double x1, x2, y1, y2;
442 ui.cur_item_type = ITEM_NONE;
444 if (ui.selection->bbox.left > ui.selection->bbox.right) {
445 x1 = ui.selection->bbox.right; x2 = ui.selection->bbox.left;
446 ui.selection->bbox.left = x1; ui.selection->bbox.right = x2;
448 x1 = ui.selection->bbox.left; x2 = ui.selection->bbox.right;
451 if (ui.selection->bbox.top > ui.selection->bbox.bottom) {
452 y1 = ui.selection->bbox.bottom; y2 = ui.selection->bbox.top;
453 ui.selection->bbox.top = y1; ui.selection->bbox.bottom = y2;
455 y1 = ui.selection->bbox.top; y2 = ui.selection->bbox.bottom;
458 for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
459 item = (struct Item *)itemlist->data;
460 if (item->bbox.left >= x1 && item->bbox.right <= x2 &&
461 item->bbox.top >= y1 && item->bbox.bottom <= y2) {
462 ui.selection->items = g_list_append(ui.selection->items, item);
466 if (ui.selection->items == NULL) reset_selection();
467 else make_dashed(ui.selection->canvas_item);
469 update_copy_paste_enabled();
472 gboolean start_movesel(GdkEvent *event)
476 if (ui.selection==NULL) return FALSE;
477 if (ui.cur_layer != ui.selection->layer) return FALSE;
479 get_pointer_coords(event, pt);
480 if (ui.selection->type == ITEM_SELECTRECT) {
481 if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
482 pt[1]<ui.selection->bbox.top || pt[1]>ui.selection->bbox.bottom)
484 ui.cur_item_type = ITEM_MOVESEL;
485 ui.selection->anchor_x = ui.selection->last_x = pt[0];
486 ui.selection->anchor_y = ui.selection->last_y = pt[1];
487 ui.selection->orig_pageno = ui.pageno;
488 ui.selection->move_pageno = ui.pageno;
489 ui.selection->move_layer = ui.selection->layer;
490 ui.selection->move_pagedelta = 0.;
491 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
498 void start_vertspace(GdkEvent *event)
505 ui.cur_item_type = ITEM_MOVESEL_VERT;
506 ui.selection = g_new(struct Selection, 1);
507 ui.selection->type = ITEM_MOVESEL_VERT;
508 ui.selection->items = NULL;
509 ui.selection->layer = ui.cur_layer;
511 get_pointer_coords(event, pt);
512 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
513 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
514 item = (struct Item *)itemlist->data;
515 if (item->bbox.top >= pt[1]) {
516 ui.selection->items = g_list_append(ui.selection->items, item);
517 if (item->bbox.bottom > ui.selection->bbox.bottom)
518 ui.selection->bbox.bottom = item->bbox.bottom;
522 ui.selection->anchor_x = ui.selection->last_x = 0;
523 ui.selection->anchor_y = ui.selection->last_y = pt[1];
524 ui.selection->orig_pageno = ui.pageno;
525 ui.selection->move_pageno = ui.pageno;
526 ui.selection->move_layer = ui.selection->layer;
527 ui.selection->move_pagedelta = 0.;
528 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
529 gnome_canvas_rect_get_type(), "width-pixels", 1,
530 "outline-color-rgba", 0x000000ff,
531 "fill-color-rgba", 0x80808040,
532 "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
536 void continue_movesel(GdkEvent *event)
538 double pt[2], dx, dy, upmargin;
542 struct Page *tmppage;
544 get_pointer_coords(event, pt);
545 if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
546 pt[1] += ui.selection->move_pagedelta;
548 // check for page jumps
549 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
550 upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
551 else upmargin = VIEW_CONTINUOUS_SKIP;
552 tmppageno = ui.selection->move_pageno;
553 tmppage = g_list_nth_data(journal.pages, tmppageno);
554 while (ui.view_continuous && (pt[1] < - upmargin)) {
555 if (tmppageno == 0) break;
557 tmppage = g_list_nth_data(journal.pages, tmppageno);
558 pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
559 ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
561 while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
562 if (tmppageno == journal.npages-1) break;
563 pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
564 ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
566 tmppage = g_list_nth_data(journal.pages, tmppageno);
569 if (tmppageno != ui.selection->move_pageno) {
570 // move to a new page !
571 ui.selection->move_pageno = tmppageno;
572 if (tmppageno == ui.selection->orig_pageno)
573 ui.selection->move_layer = ui.selection->layer;
575 ui.selection->move_layer = (struct Layer *)(g_list_last(
576 ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
577 gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
578 for (list = ui.selection->items; list!=NULL; list = list->next) {
579 item = (struct Item *)list->data;
580 if (item->canvas_item!=NULL)
581 gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
583 // avoid a refresh bug
584 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
585 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
586 gnome_canvas_item_set(ui.selection->canvas_item,
587 "x2", tmppage->width+100,
588 "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
591 // now, process things normally
593 dx = pt[0] - ui.selection->last_x;
594 dy = pt[1] - ui.selection->last_y;
595 if (hypot(dx,dy) < 1) return; // don't move subpixel
596 ui.selection->last_x = pt[0];
597 ui.selection->last_y = pt[1];
599 // move the canvas items
600 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
601 gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
603 gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
605 for (list = ui.selection->items; list != NULL; list = list->next) {
606 item = (struct Item *)list->data;
607 if (item->canvas_item != NULL)
608 gnome_canvas_item_move(item->canvas_item, dx, dy);
612 void finalize_movesel(void)
617 if (ui.selection->items != NULL) {
619 undo->type = ITEM_MOVESEL;
620 undo->itemlist = g_list_copy(ui.selection->items);
621 undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
622 undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
623 undo->layer = ui.selection->layer;
624 undo->layer2 = ui.selection->move_layer;
625 undo->auxlist = NULL;
626 // build auxlist = pointers to Item's just before ours (for depths)
627 for (list = ui.selection->items; list!=NULL; list = list->next) {
628 link = g_list_find(ui.selection->layer->items, list->data);
629 if (link!=NULL) link = link->prev;
630 undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
632 ui.selection->layer = ui.selection->move_layer;
633 move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
634 undo->layer, undo->layer2,
635 (undo->layer == undo->layer2)?undo->auxlist:NULL);
638 if (ui.selection->move_pageno!=ui.selection->orig_pageno)
639 do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
641 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
644 ui.selection->bbox.left += undo->val_x;
645 ui.selection->bbox.right += undo->val_x;
646 ui.selection->bbox.top += undo->val_y;
647 ui.selection->bbox.bottom += undo->val_y;
648 make_dashed(ui.selection->canvas_item);
650 ui.cur_item_type = ITEM_NONE;
655 void selection_delete(void)
657 struct UndoErasureData *erasure;
661 if (ui.selection == NULL) return;
663 undo->type = ITEM_ERASURE;
664 undo->layer = ui.selection->layer;
665 undo->erasurelist = NULL;
666 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
667 item = (struct Item *)itemlist->data;
668 if (item->canvas_item!=NULL)
669 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
670 erasure = g_new(struct UndoErasureData, 1);
671 erasure->item = item;
672 erasure->npos = g_list_index(ui.selection->layer->items, item);
674 erasure->replacement_items = NULL;
675 ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
676 ui.selection->layer->nitems--;
677 undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
681 /* NOTE: the erasurelist is built backwards; this guarantees that,
682 upon undo, the erasure->npos fields give the correct position
683 where each item should be reinserted as the list is traversed in
684 the forward direction */
687 void callback_clipboard_get(GtkClipboard *clipboard,
688 GtkSelectionData *selection_data,
689 guint info, gpointer user_data)
693 g_memmove(&length, user_data, sizeof(int));
694 gtk_selection_data_set(selection_data,
695 gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
698 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
703 void selection_to_clip(void)
709 GtkTargetEntry target;
711 if (ui.selection == NULL) return;
712 bufsz = 2*sizeof(int) // bufsz, nitems
713 + sizeof(struct BBox); // bbox
715 for (list = ui.selection->items; list != NULL; list = list->next) {
716 item = (struct Item *)list->data;
718 if (item->type == ITEM_STROKE) {
719 bufsz+= sizeof(int) // type
720 + sizeof(struct Brush) // brush
721 + sizeof(int) // num_points
722 + 2*item->path->num_points*sizeof(double); // the points
724 else bufsz+= sizeof(int); // type
726 p = buf = g_malloc(bufsz);
727 g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
728 g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
729 g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
730 for (list = ui.selection->items; list != NULL; list = list->next) {
731 item = (struct Item *)list->data;
732 g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
733 if (item->type == ITEM_STROKE) {
734 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
735 g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
736 g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
737 p+= 2*item->path->num_points*sizeof(double);
741 target.target = "_XOURNAL";
745 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
747 callback_clipboard_get, callback_clipboard_clear, buf);
751 void clipboard_paste(void)
753 GtkSelectionData *sel_data;
758 double hoffset, voffset, cx, cy;
762 if (ui.cur_layer == NULL) return;
764 ui.cur_item_type = ITEM_PASTE;
765 sel_data = gtk_clipboard_wait_for_contents(
766 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
767 gdk_atom_intern("_XOURNAL", FALSE));
768 ui.cur_item_type = ITEM_NONE;
769 if (sel_data == NULL) return; // paste failed
773 ui.selection = g_new(struct Selection, 1);
774 p = sel_data->data + sizeof(int);
775 g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
776 ui.selection->type = ITEM_SELECTRECT;
777 ui.selection->layer = ui.cur_layer;
778 g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
779 ui.selection->items = NULL;
781 // find by how much we translate the pasted selection
782 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
783 gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
784 gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
785 cx -= ui.cur_page->hoffset;
786 cy -= ui.cur_page->voffset;
787 if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
788 cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
789 if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
790 cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
791 if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
792 cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
793 if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
794 cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
795 hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
796 voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
797 ui.selection->bbox.left += hoffset;
798 ui.selection->bbox.right += hoffset;
799 ui.selection->bbox.top += voffset;
800 ui.selection->bbox.bottom += voffset;
802 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
803 gnome_canvas_rect_get_type(), "width-pixels", 1,
804 "outline-color-rgba", 0x000000ff,
805 "fill-color-rgba", 0x80808040,
806 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
807 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
808 make_dashed(ui.selection->canvas_item);
810 while (nitems-- > 0) {
811 item = g_new(struct Item, 1);
812 ui.selection->items = g_list_append(ui.selection->items, item);
813 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
814 ui.cur_layer->nitems++;
815 g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
816 if (item->type == ITEM_STROKE) {
817 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
818 g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
819 item->path = gnome_canvas_points_new(npts);
821 for (i=0; i<npts; i++) {
822 item->path->coords[2*i] = pf[2*i] + hoffset;
823 item->path->coords[2*i+1] = pf[2*i+1] + voffset;
825 p+= 2*item->path->num_points*sizeof(double);
826 update_item_bbox(item);
827 item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
828 gnome_canvas_line_get_type(), "points", item->path,
829 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
830 "fill-color-rgba", item->brush.color_rgba,
831 "width-units", item->brush.thickness, NULL);
836 undo->type = ITEM_PASTE;
837 undo->layer = ui.cur_layer;
838 undo->itemlist = g_list_copy(ui.selection->items);
840 gtk_selection_data_free(sel_data);
841 update_copy_paste_enabled();
844 // modify the color or thickness of pen strokes in a selection
846 void recolor_selection(int color)
852 if (ui.selection == NULL) return;
854 undo->type = ITEM_REPAINTSEL;
855 undo->itemlist = NULL;
856 undo->auxlist = NULL;
857 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
858 item = (struct Item *)itemlist->data;
859 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
860 // store info for undo
861 undo->itemlist = g_list_append(undo->itemlist, item);
862 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
863 g_memmove(brush, &(item->brush), sizeof(struct Brush));
864 undo->auxlist = g_list_append(undo->auxlist, brush);
865 // repaint the stroke
866 item->brush.color_no = color;
867 item->brush.color_rgba = predef_colors_rgba[color];
868 if (item->canvas_item!=NULL)
869 gnome_canvas_item_set(item->canvas_item,
870 "fill-color-rgba", item->brush.color_rgba, NULL);
874 void rethicken_selection(int val)
880 if (ui.selection == NULL) return;
882 undo->type = ITEM_REPAINTSEL;
883 undo->itemlist = NULL;
884 undo->auxlist = NULL;
885 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
886 item = (struct Item *)itemlist->data;
887 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
888 // store info for undo
889 undo->itemlist = g_list_append(undo->itemlist, item);
890 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
891 g_memmove(brush, &(item->brush), sizeof(struct Brush));
892 undo->auxlist = g_list_append(undo->auxlist, brush);
893 // repaint the stroke
894 item->brush.thickness_no = val;
895 item->brush.thickness = predef_thickness[TOOL_PEN][val];
896 if (item->canvas_item!=NULL)
897 gnome_canvas_item_set(item->canvas_item,
898 "width-units", item->brush.thickness, NULL);
902 void do_hand(GdkEvent *event)
907 get_pointer_coords(event, pt);
908 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
909 cx -= (pt[0]-ui.hand_refpt[0])*ui.zoom;
910 cy -= (pt[1]-ui.hand_refpt[1])*ui.zoom;
911 gnome_canvas_scroll_to(canvas, cx, cy);