Public release 0.2.2.
[xournal.git] / src / xo-paint.c
blob5d19c1b9a03718ca858804b0602611791454c5b8
1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
5 #include <math.h>
6 #include <string.h>
7 #include <gtk/gtk.h>
8 #include <libgnomecanvas/libgnomecanvas.h>
10 #include <libart_lgpl/art_vpath_dash.h>
12 #include "xournal.h"
13 #include "xo-callbacks.h"
14 #include "xo-interface.h"
15 #include "xo-support.h"
16 #include "xo-misc.h"
17 #include "xo-paint.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)
38 GdkCursor *cursor;
40 if (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);
46 else {
47 gdk_window_set_cursor(GTK_WIDGET(winMain)->window, NULL);
48 update_cursor();
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);
62 ui.cursor = NULL;
64 if (ui.cur_item_type == ITEM_MOVESEL) {
65 if (ui.toolno[ui.cur_mapping] == TOOL_VERTSPACE)
66 ui.cursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
67 else
68 ui.cursor = gdk_cursor_new(GDK_FLEUR);
70 else if (ui.toolno[ui.cur_mapping] == TOOL_PEN) {
71 fg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
72 fg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
73 fg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
74 source = gdk_bitmap_create_from_data(NULL, cursor_pen_bits, 16, 16);
75 ui.cursor = gdk_cursor_new_from_pixmap(source, source, &fg, &bg, 7, 7);
76 gdk_bitmap_unref(source);
78 else if (ui.toolno[ui.cur_mapping] == TOOL_ERASER) {
79 source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
80 mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
81 ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
82 gdk_bitmap_unref(source);
83 gdk_bitmap_unref(mask);
85 else if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
86 source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
87 mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
88 bg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
89 bg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
90 bg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
91 ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
92 gdk_bitmap_unref(source);
93 gdk_bitmap_unref(mask);
95 else if (ui.cur_item_type == ITEM_SELECTRECT) {
96 ui.cursor = gdk_cursor_new(GDK_TCROSS);
99 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
103 /************** painting strokes *************/
105 #define SUBDIVIDE_MAXDIST 5.0
107 void subdivide_cur_path(null)
109 int n, pieces, k;
110 double *p;
111 double x0, y0, x1, y1;
113 for (n=0, p=ui.cur_path.coords; n<ui.cur_path.num_points-1; n++, p+=2) {
114 pieces = (int)floor(hypot(p[2]-p[0], p[3]-p[1])/SUBDIVIDE_MAXDIST);
115 if (pieces>1) {
116 x0 = p[0]; y0 = p[1];
117 x1 = p[2]; y1 = p[3];
118 realloc_cur_path(ui.cur_path.num_points+pieces-1);
119 g_memmove(ui.cur_path.coords+2*(n+pieces), ui.cur_path.coords+2*(n+1),
120 2*(ui.cur_path.num_points-n-1)*sizeof(double));
121 p = ui.cur_path.coords+2*n;
122 ui.cur_path.num_points += pieces-1;
123 n += (pieces-1);
124 for (k=1; k<pieces; k++) {
125 p+=2;
126 p[0] = x0 + k*(x1-x0)/pieces;
127 p[1] = y0 + k*(y1-y0)/pieces;
133 void create_new_stroke(GdkEvent *event)
135 ui.cur_item_type = ITEM_STROKE;
136 ui.cur_item = g_new(struct Item, 1);
137 ui.cur_item->type = ITEM_STROKE;
138 g_memmove(&(ui.cur_item->brush), ui.cur_brush, sizeof(struct Brush));
139 ui.cur_item->path = &ui.cur_path;
140 realloc_cur_path(2);
141 ui.cur_path.num_points = 1;
142 get_pointer_coords(event, ui.cur_path.coords);
144 if (ui.ruler[ui.cur_mapping])
145 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
146 gnome_canvas_line_get_type(),
147 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
148 "fill-color-rgba", ui.cur_item->brush.color_rgba,
149 "width-units", ui.cur_item->brush.thickness, NULL);
150 else
151 ui.cur_item->canvas_item = gnome_canvas_item_new(
152 ui.cur_layer->group, gnome_canvas_group_get_type(), NULL);
155 void continue_stroke(GdkEvent *event)
157 GnomeCanvasPoints seg;
158 double *pt;
160 if (ui.ruler[ui.cur_mapping]) {
161 pt = ui.cur_path.coords;
162 } else {
163 realloc_cur_path(ui.cur_path.num_points+1);
164 pt = ui.cur_path.coords + 2*(ui.cur_path.num_points-1);
167 get_pointer_coords(event, pt+2);
169 if (ui.ruler[ui.cur_mapping])
170 ui.cur_path.num_points = 2;
171 else {
172 if (hypot(pt[0]-pt[2], pt[1]-pt[3]) < PIXEL_MOTION_THRESHOLD/ui.zoom)
173 return; // not a meaningful motion
174 ui.cur_path.num_points++;
177 seg.coords = pt;
178 seg.num_points = 2;
179 seg.ref_count = 1;
181 /* note: we're using a piece of the cur_path array. This is ok because
182 upon creation the line just copies the contents of the GnomeCanvasPoints
183 into an internal structure */
185 if (ui.ruler[ui.cur_mapping])
186 gnome_canvas_item_set(ui.cur_item->canvas_item, "points", &seg, NULL);
187 else
188 gnome_canvas_item_new((GnomeCanvasGroup *)ui.cur_item->canvas_item,
189 gnome_canvas_line_get_type(), "points", &seg,
190 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
191 "fill-color-rgba", ui.cur_item->brush.color_rgba,
192 "width-units", ui.cur_item->brush.thickness, NULL);
195 void finalize_stroke(void)
197 if (ui.cur_path.num_points == 1) { // GnomeCanvas doesn't like num_points=1
198 ui.cur_path.coords[2] = ui.cur_path.coords[0]+0.1;
199 ui.cur_path.coords[3] = ui.cur_path.coords[1];
200 ui.cur_path.num_points = 2;
203 subdivide_cur_path(); // split the segment so eraser will work
205 ui.cur_item->path = gnome_canvas_points_new(ui.cur_path.num_points);
206 g_memmove(ui.cur_item->path->coords, ui.cur_path.coords,
207 2*ui.cur_path.num_points*sizeof(double));
208 update_item_bbox(ui.cur_item);
209 ui.cur_path.num_points = 0;
211 // destroy the entire group of temporary line segments
212 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
213 // make a new line item to replace it
214 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
215 gnome_canvas_line_get_type(), "points", ui.cur_item->path,
216 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
217 "fill-color-rgba", ui.cur_item->brush.color_rgba,
218 "width-units", ui.cur_item->brush.thickness, NULL);
220 // add undo information
221 prepare_new_undo();
222 undo->type = ITEM_STROKE;
223 undo->item = ui.cur_item;
224 undo->layer = ui.cur_layer;
226 // store the item on top of the layer stack
227 ui.cur_layer->items = g_list_append(ui.cur_layer->items, ui.cur_item);
228 ui.cur_layer->nitems++;
229 ui.cur_item = NULL;
230 ui.cur_item_type = ITEM_NONE;
233 /************** eraser tool *************/
235 void erase_stroke_portions(struct Item *item, double x, double y, double radius,
236 gboolean whole_strokes, struct UndoErasureData *erasure)
238 int i;
239 double *pt;
240 struct Item *newhead, *newtail;
241 gboolean need_recalc = FALSE;
243 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
244 if (hypot(pt[0]-x, pt[1]-y) <= radius) { // found an intersection
245 // FIXME: need to test if line SEGMENT hits the circle
246 // hide the canvas item, and create erasure data if needed
247 if (erasure == NULL) {
248 item->type = ITEM_TEMP_STROKE;
249 gnome_canvas_item_hide(item->canvas_item);
250 /* we'll use this hidden item as an insertion point later */
251 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
252 item->erasure = erasure;
253 erasure->item = item;
254 erasure->npos = g_list_index(ui.cur_layer->items, item);
255 erasure->nrepl = 0;
256 erasure->replacement_items = NULL;
258 // split the stroke
259 newhead = newtail = NULL;
260 if (!whole_strokes) {
261 if (i>=2) {
262 newhead = (struct Item *)g_malloc(sizeof(struct Item));
263 newhead->type = ITEM_STROKE;
264 g_memmove(&newhead->brush, &item->brush, sizeof(struct Brush));
265 newhead->path = gnome_canvas_points_new(i);
266 g_memmove(newhead->path->coords, item->path->coords, 2*i*sizeof(double));
268 while (++i < item->path->num_points) {
269 pt+=2;
270 if (hypot(pt[0]-x, pt[1]-y) > radius) break;
272 if (i<item->path->num_points-1) {
273 newtail = (struct Item *)g_malloc(sizeof(struct Item));
274 newtail->type = ITEM_STROKE;
275 g_memmove(&newtail->brush, &item->brush, sizeof(struct Brush));
276 newtail->path = gnome_canvas_points_new(item->path->num_points-i);
277 g_memmove(newtail->path->coords, item->path->coords+2*i,
278 2*(item->path->num_points-i)*sizeof(double));
279 newtail->canvas_item = NULL;
282 if (item->type == ITEM_STROKE) {
283 // it's inside an erasure list - we destroy it
284 gnome_canvas_points_free(item->path);
285 if (item->canvas_item != NULL)
286 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
287 erasure->nrepl--;
288 erasure->replacement_items = g_list_remove(erasure->replacement_items, item);
289 g_free(item);
291 // add the new head
292 if (newhead != NULL) {
293 update_item_bbox(newhead);
294 newhead->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
295 gnome_canvas_line_get_type(), "points", newhead->path,
296 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
297 "fill-color-rgba", newhead->brush.color_rgba,
298 "width-units", newhead->brush.thickness, NULL);
299 lower_canvas_item_to(ui.cur_layer->group,
300 newhead->canvas_item, erasure->item->canvas_item);
301 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newhead);
302 erasure->nrepl++;
303 // prepending ensures it won't get processed twice
305 // recurse into the new tail
306 need_recalc = (newtail!=NULL);
307 if (newtail == NULL) break;
308 item = newtail;
309 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newtail);
310 erasure->nrepl++;
311 i=0; pt=item->path->coords;
314 // add the tail if needed
315 if (!need_recalc) return;
316 update_item_bbox(item);
317 item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
318 gnome_canvas_line_get_type(), "points", item->path,
319 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
320 "fill-color-rgba", item->brush.color_rgba,
321 "width-units", item->brush.thickness, NULL);
322 lower_canvas_item_to(ui.cur_layer->group, item->canvas_item,
323 erasure->item->canvas_item);
327 void do_eraser(GdkEvent *event, double radius, gboolean whole_strokes)
329 struct Item *item, *repl;
330 GList *itemlist, *repllist;
331 double pos[2];
332 struct BBox eraserbox;
334 get_pointer_coords(event, pos);
335 eraserbox.left = pos[0]-radius;
336 eraserbox.right = pos[0]+radius;
337 eraserbox.top = pos[1]-radius;
338 eraserbox.bottom = pos[1]+radius;
339 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
340 item = (struct Item *)itemlist->data;
341 if (item->type == ITEM_STROKE) {
342 if (!have_intersect(&(item->bbox), &eraserbox)) continue;
343 erase_stroke_portions(item, pos[0], pos[1], radius, whole_strokes, NULL);
344 } else if (item->type == ITEM_TEMP_STROKE) {
345 repllist = item->erasure->replacement_items;
346 while (repllist!=NULL) {
347 repl = (struct Item *)repllist->data;
348 // we may delete the item soon! so advance now in the list
349 repllist = repllist->next;
350 if (have_intersect(&(repl->bbox), &eraserbox))
351 erase_stroke_portions(repl, pos[0], pos[1], radius, whole_strokes, item->erasure);
357 void finalize_erasure(void)
359 GList *itemlist, *partlist;
360 struct Item *item, *part;
362 prepare_new_undo();
363 undo->type = ITEM_ERASURE;
364 undo->layer = ui.cur_layer;
365 undo->erasurelist = NULL;
367 itemlist = ui.cur_layer->items;
368 while (itemlist!=NULL) {
369 item = (struct Item *)itemlist->data;
370 itemlist = itemlist->next;
371 if (item->type != ITEM_TEMP_STROKE) continue;
372 item->type = ITEM_STROKE;
373 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, item);
374 // the item has an invisible canvas item, which used to act as anchor
375 if (item->canvas_item!=NULL) {
376 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
377 item->canvas_item = NULL;
379 undo->erasurelist = g_list_append(undo->erasurelist, item->erasure);
380 // add the new strokes into the current layer
381 for (partlist = item->erasure->replacement_items; partlist!=NULL; partlist = partlist->next)
382 ui.cur_layer->items = g_list_insert_before(
383 ui.cur_layer->items, itemlist, partlist->data);
384 ui.cur_layer->nitems += item->erasure->nrepl-1;
387 ui.cur_item = NULL;
388 ui.cur_item_type = ITEM_NONE;
390 /* NOTE: the list of erasures goes in the depth order of the layer;
391 this guarantees that, upon undo, the erasure->npos fields give the
392 correct position where each item should be reinserted as the list
393 is traversed in the forward direction */
396 /************ selection tools ***********/
398 void make_dashed(GnomeCanvasItem *item)
400 double dashlen[2];
401 ArtVpathDash dash;
403 dash.n_dash = 2;
404 dash.offset = 3.0;
405 dash.dash = dashlen;
406 dashlen[0] = dashlen[1] = 6.0;
407 gnome_canvas_item_set(item, "dash", &dash, NULL);
411 void start_selectrect(GdkEvent *event)
413 double pt[2];
414 reset_selection();
416 ui.cur_item_type = ITEM_SELECTRECT;
417 ui.selection = g_new(struct Selection, 1);
418 ui.selection->type = ITEM_SELECTRECT;
419 ui.selection->items = NULL;
420 ui.selection->layer = ui.cur_layer;
422 get_pointer_coords(event, pt);
423 ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
424 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
426 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
427 gnome_canvas_rect_get_type(), "width-pixels", 1,
428 "outline-color-rgba", 0x000000ff,
429 "fill-color-rgba", 0x80808040,
430 "x1", pt[0], "x2", pt[0], "y1", pt[1], "y2", pt[1], NULL);
431 update_cursor();
434 void finalize_selectrect(void)
436 double x1, x2, y1, y2;
437 GList *itemlist;
438 struct Item *item;
441 ui.cur_item_type = ITEM_NONE;
443 if (ui.selection->bbox.left > ui.selection->bbox.right) {
444 x1 = ui.selection->bbox.right; x2 = ui.selection->bbox.left;
445 ui.selection->bbox.left = x1; ui.selection->bbox.right = x2;
446 } else {
447 x1 = ui.selection->bbox.left; x2 = ui.selection->bbox.right;
450 if (ui.selection->bbox.top > ui.selection->bbox.bottom) {
451 y1 = ui.selection->bbox.bottom; y2 = ui.selection->bbox.top;
452 ui.selection->bbox.top = y1; ui.selection->bbox.bottom = y2;
453 } else {
454 y1 = ui.selection->bbox.top; y2 = ui.selection->bbox.bottom;
457 for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
458 item = (struct Item *)itemlist->data;
459 if (item->bbox.left >= x1 && item->bbox.right <= x2 &&
460 item->bbox.top >= y1 && item->bbox.bottom <= y2) {
461 ui.selection->items = g_list_append(ui.selection->items, item);
465 if (ui.selection->items == NULL) reset_selection();
466 else make_dashed(ui.selection->canvas_item);
467 update_cursor();
468 update_copy_paste_enabled();
471 gboolean start_movesel(GdkEvent *event)
473 double pt[2];
475 if (ui.selection==NULL) return FALSE;
476 if (ui.cur_layer != ui.selection->layer) return FALSE;
478 get_pointer_coords(event, pt);
479 if (ui.selection->type == ITEM_SELECTRECT) {
480 if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
481 pt[1]<ui.selection->bbox.top || pt[1]>ui.selection->bbox.bottom)
482 return FALSE;
483 ui.cur_item_type = ITEM_MOVESEL;
484 ui.selection->anchor_x = ui.selection->last_x = pt[0];
485 ui.selection->anchor_y = ui.selection->last_y = pt[1];
486 ui.selection->orig_pageno = ui.pageno;
487 ui.selection->move_pageno = ui.pageno;
488 ui.selection->move_layer = ui.selection->layer;
489 ui.selection->move_pagedelta = 0.;
490 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
491 update_cursor();
492 return TRUE;
494 return FALSE;
497 void start_vertspace(GdkEvent *event)
499 double pt[2];
500 GList *itemlist;
501 struct Item *item;
503 reset_selection();
504 ui.cur_item_type = ITEM_MOVESEL_VERT;
505 ui.selection = g_new(struct Selection, 1);
506 ui.selection->type = ITEM_MOVESEL_VERT;
507 ui.selection->items = NULL;
508 ui.selection->layer = ui.cur_layer;
510 get_pointer_coords(event, pt);
511 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
512 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
513 item = (struct Item *)itemlist->data;
514 if (item->bbox.top >= pt[1]) {
515 ui.selection->items = g_list_append(ui.selection->items, item);
516 if (item->bbox.bottom > ui.selection->bbox.bottom)
517 ui.selection->bbox.bottom = item->bbox.bottom;
521 ui.selection->anchor_x = ui.selection->last_x = 0;
522 ui.selection->anchor_y = ui.selection->last_y = pt[1];
523 ui.selection->orig_pageno = ui.pageno;
524 ui.selection->move_pageno = ui.pageno;
525 ui.selection->move_layer = ui.selection->layer;
526 ui.selection->move_pagedelta = 0.;
527 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
528 gnome_canvas_rect_get_type(), "width-pixels", 1,
529 "outline-color-rgba", 0x000000ff,
530 "fill-color-rgba", 0x80808040,
531 "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
532 update_cursor();
535 void continue_movesel(GdkEvent *event)
537 double pt[2], dx, dy, upmargin;
538 GList *list;
539 struct Item *item;
540 int tmppageno;
541 struct Page *tmppage;
543 get_pointer_coords(event, pt);
544 if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
545 pt[1] += ui.selection->move_pagedelta;
547 // check for page jumps
548 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
549 upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
550 else upmargin = VIEW_CONTINUOUS_SKIP;
551 tmppageno = ui.selection->move_pageno;
552 tmppage = g_list_nth_data(journal.pages, tmppageno);
553 while (ui.view_continuous && (pt[1] < - upmargin)) {
554 if (tmppageno == 0) break;
555 tmppageno--;
556 tmppage = g_list_nth_data(journal.pages, tmppageno);
557 pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
558 ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
560 while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
561 if (tmppageno == journal.npages-1) break;
562 pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
563 ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
564 tmppageno++;
565 tmppage = g_list_nth_data(journal.pages, tmppageno);
568 if (tmppageno != ui.selection->move_pageno) {
569 // move to a new page !
570 ui.selection->move_pageno = tmppageno;
571 if (tmppageno == ui.selection->orig_pageno)
572 ui.selection->move_layer = ui.selection->layer;
573 else
574 ui.selection->move_layer = (struct Layer *)(g_list_last(
575 ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
576 gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
577 for (list = ui.selection->items; list!=NULL; list = list->next) {
578 item = (struct Item *)list->data;
579 if (item->canvas_item!=NULL)
580 gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
582 // avoid a refresh bug
583 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
584 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
585 gnome_canvas_item_set(ui.selection->canvas_item,
586 "x2", tmppage->width+100,
587 "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
590 // now, process things normally
592 dx = pt[0] - ui.selection->last_x;
593 dy = pt[1] - ui.selection->last_y;
594 if (hypot(dx,dy) < 1) return; // don't move subpixel
595 ui.selection->last_x = pt[0];
596 ui.selection->last_y = pt[1];
598 // move the canvas items
599 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
600 gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
601 else
602 gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
604 for (list = ui.selection->items; list != NULL; list = list->next) {
605 item = (struct Item *)list->data;
606 if (item->canvas_item != NULL)
607 gnome_canvas_item_move(item->canvas_item, dx, dy);
611 void finalize_movesel(void)
613 GList *list, *link;
614 struct Item *item;
616 if (ui.selection->items != NULL) {
617 prepare_new_undo();
618 undo->type = ITEM_MOVESEL;
619 undo->itemlist = g_list_copy(ui.selection->items);
620 undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
621 undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
622 undo->layer = ui.selection->layer;
623 undo->layer2 = ui.selection->move_layer;
624 undo->auxlist = NULL;
625 // build auxlist = pointers to Item's just before ours (for depths)
626 for (list = ui.selection->items; list!=NULL; list = list->next) {
627 link = g_list_find(ui.selection->layer->items, list->data);
628 if (link!=NULL) link = link->prev;
629 undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
631 ui.selection->layer = ui.selection->move_layer;
632 move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
633 undo->layer, undo->layer2,
634 (undo->layer == undo->layer2)?undo->auxlist:NULL);
637 if (ui.selection->move_pageno!=ui.selection->orig_pageno)
638 do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
640 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
641 reset_selection();
642 else {
643 ui.selection->bbox.left += undo->val_x;
644 ui.selection->bbox.right += undo->val_x;
645 ui.selection->bbox.top += undo->val_y;
646 ui.selection->bbox.bottom += undo->val_y;
647 make_dashed(ui.selection->canvas_item);
649 ui.cur_item_type = ITEM_NONE;
650 update_cursor();
654 void selection_delete(void)
656 struct UndoErasureData *erasure;
657 GList *itemlist;
658 struct Item *item;
660 if (ui.selection == NULL) return;
661 prepare_new_undo();
662 undo->type = ITEM_ERASURE;
663 undo->layer = ui.selection->layer;
664 undo->erasurelist = NULL;
665 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
666 item = (struct Item *)itemlist->data;
667 if (item->canvas_item!=NULL)
668 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
669 erasure = g_new(struct UndoErasureData, 1);
670 erasure->item = item;
671 erasure->npos = g_list_index(ui.selection->layer->items, item);
672 erasure->nrepl = 0;
673 erasure->replacement_items = NULL;
674 ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
675 ui.selection->layer->nitems--;
676 undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
678 reset_selection();
680 /* NOTE: the erasurelist is built backwards; this guarantees that,
681 upon undo, the erasure->npos fields give the correct position
682 where each item should be reinserted as the list is traversed in
683 the forward direction */
686 void callback_clipboard_get(GtkClipboard *clipboard,
687 GtkSelectionData *selection_data,
688 guint info, gpointer user_data)
690 int length;
692 g_memmove(&length, user_data, sizeof(int));
693 gtk_selection_data_set(selection_data,
694 gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
697 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
699 g_free(user_data);
702 void selection_to_clip(void)
704 int bufsz, nitems;
705 char *buf, *p;
706 GList *list;
707 struct Item *item;
708 GtkTargetEntry target;
710 if (ui.selection == NULL) return;
711 bufsz = 2*sizeof(int) // bufsz, nitems
712 + sizeof(struct BBox); // bbox
713 nitems = 0;
714 for (list = ui.selection->items; list != NULL; list = list->next) {
715 item = (struct Item *)list->data;
716 nitems++;
717 if (item->type == ITEM_STROKE) {
718 bufsz+= sizeof(int) // type
719 + sizeof(struct Brush) // brush
720 + sizeof(int) // num_points
721 + 2*item->path->num_points*sizeof(double); // the points
723 else bufsz+= sizeof(int); // type
725 p = buf = g_malloc(bufsz);
726 g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
727 g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
728 g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
729 for (list = ui.selection->items; list != NULL; list = list->next) {
730 item = (struct Item *)list->data;
731 g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
732 if (item->type == ITEM_STROKE) {
733 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
734 g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
735 g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
736 p+= 2*item->path->num_points*sizeof(double);
740 target.target = "_XOURNAL";
741 target.flags = 0;
742 target.info = 0;
744 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
745 &target, 1,
746 callback_clipboard_get, callback_clipboard_clear, buf);
750 void clipboard_paste(void)
752 GtkSelectionData *sel_data;
753 unsigned char *p;
754 int nitems, npts, i;
755 GList *list;
756 struct Item *item;
757 double hoffset, voffset, cx, cy;
758 double *pf;
759 int sx, sy, wx, wy;
761 if (ui.cur_layer == NULL) return;
763 ui.cur_item_type = ITEM_PASTE;
764 sel_data = gtk_clipboard_wait_for_contents(
765 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
766 gdk_atom_intern("_XOURNAL", FALSE));
767 ui.cur_item_type = ITEM_NONE;
768 if (sel_data == NULL) return; // paste failed
770 reset_selection();
772 ui.selection = g_new(struct Selection, 1);
773 p = sel_data->data + sizeof(int);
774 g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
775 ui.selection->type = ITEM_SELECTRECT;
776 ui.selection->layer = ui.cur_layer;
777 g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
778 ui.selection->items = NULL;
780 // find by how much we translate the pasted selection
781 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
782 gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
783 gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
784 cx -= ui.cur_page->hoffset;
785 cy -= ui.cur_page->voffset;
786 if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
787 cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
788 if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
789 cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
790 if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
791 cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
792 if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
793 cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
794 hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
795 voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
796 ui.selection->bbox.left += hoffset;
797 ui.selection->bbox.right += hoffset;
798 ui.selection->bbox.top += voffset;
799 ui.selection->bbox.bottom += voffset;
801 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
802 gnome_canvas_rect_get_type(), "width-pixels", 1,
803 "outline-color-rgba", 0x000000ff,
804 "fill-color-rgba", 0x80808040,
805 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
806 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
807 make_dashed(ui.selection->canvas_item);
809 while (nitems-- > 0) {
810 item = g_new(struct Item, 1);
811 ui.selection->items = g_list_append(ui.selection->items, item);
812 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
813 ui.cur_layer->nitems++;
814 g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
815 if (item->type == ITEM_STROKE) {
816 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
817 g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
818 item->path = gnome_canvas_points_new(npts);
819 pf = (double *)p;
820 for (i=0; i<npts; i++) {
821 item->path->coords[2*i] = pf[2*i] + hoffset;
822 item->path->coords[2*i+1] = pf[2*i+1] + voffset;
824 p+= 2*item->path->num_points*sizeof(double);
825 update_item_bbox(item);
826 item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
827 gnome_canvas_line_get_type(), "points", item->path,
828 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
829 "fill-color-rgba", item->brush.color_rgba,
830 "width-units", item->brush.thickness, NULL);
834 prepare_new_undo();
835 undo->type = ITEM_PASTE;
836 undo->layer = ui.cur_layer;
837 undo->itemlist = g_list_copy(ui.selection->items);
839 gtk_selection_data_free(sel_data);
840 update_copy_paste_enabled();
843 // modify the color or thickness of pen strokes in a selection
845 void recolor_selection(int color)
847 GList *itemlist;
848 struct Item *item;
849 struct Brush *brush;
851 if (ui.selection == NULL) return;
852 prepare_new_undo();
853 undo->type = ITEM_REPAINTSEL;
854 undo->itemlist = NULL;
855 undo->auxlist = NULL;
856 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
857 item = (struct Item *)itemlist->data;
858 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
859 // store info for undo
860 undo->itemlist = g_list_append(undo->itemlist, item);
861 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
862 g_memmove(brush, &(item->brush), sizeof(struct Brush));
863 undo->auxlist = g_list_append(undo->auxlist, brush);
864 // repaint the stroke
865 item->brush.color_no = color;
866 item->brush.color_rgba = predef_colors_rgba[color];
867 if (item->canvas_item!=NULL)
868 gnome_canvas_item_set(item->canvas_item,
869 "fill-color-rgba", item->brush.color_rgba, NULL);
873 void rethicken_selection(int val)
875 GList *itemlist;
876 struct Item *item;
877 struct Brush *brush;
879 if (ui.selection == NULL) return;
880 prepare_new_undo();
881 undo->type = ITEM_REPAINTSEL;
882 undo->itemlist = NULL;
883 undo->auxlist = NULL;
884 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
885 item = (struct Item *)itemlist->data;
886 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
887 // store info for undo
888 undo->itemlist = g_list_append(undo->itemlist, item);
889 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
890 g_memmove(brush, &(item->brush), sizeof(struct Brush));
891 undo->auxlist = g_list_append(undo->auxlist, brush);
892 // repaint the stroke
893 item->brush.thickness_no = val;
894 item->brush.thickness = predef_thickness[TOOL_PEN][val];
895 if (item->canvas_item!=NULL)
896 gnome_canvas_item_set(item->canvas_item,
897 "width-units", item->brush.thickness, NULL);