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);
99 else if (ui.toolno[ui.cur_mapping] == TOOL_TEXT) {
100 ui.cursor = gdk_cursor_new(GDK_XTERM);
103 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
107 /************** painting strokes *************/
109 #define SUBDIVIDE_MAXDIST 5.0
111 void subdivide_cur_path(null)
115 double x0, y0, x1, y1;
117 for (n=0, p=ui.cur_path.coords; n<ui.cur_path.num_points-1; n++, p+=2) {
118 pieces = (int)floor(hypot(p[2]-p[0], p[3]-p[1])/SUBDIVIDE_MAXDIST);
120 x0 = p[0]; y0 = p[1];
121 x1 = p[2]; y1 = p[3];
122 realloc_cur_path(ui.cur_path.num_points+pieces-1);
123 g_memmove(ui.cur_path.coords+2*(n+pieces), ui.cur_path.coords+2*(n+1),
124 2*(ui.cur_path.num_points-n-1)*sizeof(double));
125 p = ui.cur_path.coords+2*n;
126 ui.cur_path.num_points += pieces-1;
128 for (k=1; k<pieces; k++) {
130 p[0] = x0 + k*(x1-x0)/pieces;
131 p[1] = y0 + k*(y1-y0)/pieces;
137 void create_new_stroke(GdkEvent *event)
139 ui.cur_item_type = ITEM_STROKE;
140 ui.cur_item = g_new(struct Item, 1);
141 ui.cur_item->type = ITEM_STROKE;
142 g_memmove(&(ui.cur_item->brush), ui.cur_brush, sizeof(struct Brush));
143 ui.cur_item->path = &ui.cur_path;
145 ui.cur_path.num_points = 1;
146 get_pointer_coords(event, ui.cur_path.coords);
148 if (ui.ruler[ui.cur_mapping])
149 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
150 gnome_canvas_line_get_type(),
151 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
152 "fill-color-rgba", ui.cur_item->brush.color_rgba,
153 "width-units", ui.cur_item->brush.thickness, NULL);
155 ui.cur_item->canvas_item = gnome_canvas_item_new(
156 ui.cur_layer->group, gnome_canvas_group_get_type(), NULL);
159 void continue_stroke(GdkEvent *event)
161 GnomeCanvasPoints seg;
164 if (ui.ruler[ui.cur_mapping]) {
165 pt = ui.cur_path.coords;
167 realloc_cur_path(ui.cur_path.num_points+1);
168 pt = ui.cur_path.coords + 2*(ui.cur_path.num_points-1);
171 get_pointer_coords(event, pt+2);
173 if (ui.ruler[ui.cur_mapping])
174 ui.cur_path.num_points = 2;
176 if (hypot(pt[0]-pt[2], pt[1]-pt[3]) < PIXEL_MOTION_THRESHOLD/ui.zoom)
177 return; // not a meaningful motion
178 ui.cur_path.num_points++;
185 /* note: we're using a piece of the cur_path array. This is ok because
186 upon creation the line just copies the contents of the GnomeCanvasPoints
187 into an internal structure */
189 if (ui.ruler[ui.cur_mapping])
190 gnome_canvas_item_set(ui.cur_item->canvas_item, "points", &seg, NULL);
192 gnome_canvas_item_new((GnomeCanvasGroup *)ui.cur_item->canvas_item,
193 gnome_canvas_line_get_type(), "points", &seg,
194 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
195 "fill-color-rgba", ui.cur_item->brush.color_rgba,
196 "width-units", ui.cur_item->brush.thickness, NULL);
199 void finalize_stroke(void)
201 if (ui.cur_path.num_points == 1) { // GnomeCanvas doesn't like num_points=1
202 ui.cur_path.coords[2] = ui.cur_path.coords[0]+0.1;
203 ui.cur_path.coords[3] = ui.cur_path.coords[1];
204 ui.cur_path.num_points = 2;
207 subdivide_cur_path(); // split the segment so eraser will work
209 ui.cur_item->path = gnome_canvas_points_new(ui.cur_path.num_points);
210 g_memmove(ui.cur_item->path->coords, ui.cur_path.coords,
211 2*ui.cur_path.num_points*sizeof(double));
212 update_item_bbox(ui.cur_item);
213 ui.cur_path.num_points = 0;
215 // destroy the entire group of temporary line segments
216 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
217 // make a new line item to replace it
218 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
220 // add undo information
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++;
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)
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);
256 erasure->replacement_items = NULL;
259 newhead = newtail = NULL;
260 if (!whole_strokes) {
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) {
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));
288 erasure->replacement_items = g_list_remove(erasure->replacement_items, item);
292 if (newhead != NULL) {
293 update_item_bbox(newhead);
294 make_canvas_item_one(ui.cur_layer->group, newhead);
295 lower_canvas_item_to(ui.cur_layer->group,
296 newhead->canvas_item, erasure->item->canvas_item);
297 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newhead);
299 // prepending ensures it won't get processed twice
301 // recurse into the new tail
302 need_recalc = (newtail!=NULL);
303 if (newtail == NULL) break;
305 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newtail);
307 i=0; pt=item->path->coords;
310 // add the tail if needed
311 if (!need_recalc) return;
312 update_item_bbox(item);
313 make_canvas_item_one(ui.cur_layer->group, item);
314 lower_canvas_item_to(ui.cur_layer->group, item->canvas_item,
315 erasure->item->canvas_item);
319 void do_eraser(GdkEvent *event, double radius, gboolean whole_strokes)
321 struct Item *item, *repl;
322 GList *itemlist, *repllist;
324 struct BBox eraserbox;
326 get_pointer_coords(event, pos);
327 eraserbox.left = pos[0]-radius;
328 eraserbox.right = pos[0]+radius;
329 eraserbox.top = pos[1]-radius;
330 eraserbox.bottom = pos[1]+radius;
331 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
332 item = (struct Item *)itemlist->data;
333 if (item->type == ITEM_STROKE) {
334 if (!have_intersect(&(item->bbox), &eraserbox)) continue;
335 erase_stroke_portions(item, pos[0], pos[1], radius, whole_strokes, NULL);
336 } else if (item->type == ITEM_TEMP_STROKE) {
337 repllist = item->erasure->replacement_items;
338 while (repllist!=NULL) {
339 repl = (struct Item *)repllist->data;
340 // we may delete the item soon! so advance now in the list
341 repllist = repllist->next;
342 if (have_intersect(&(repl->bbox), &eraserbox))
343 erase_stroke_portions(repl, pos[0], pos[1], radius, whole_strokes, item->erasure);
349 void finalize_erasure(void)
351 GList *itemlist, *partlist;
352 struct Item *item, *part;
355 undo->type = ITEM_ERASURE;
356 undo->layer = ui.cur_layer;
357 undo->erasurelist = NULL;
359 itemlist = ui.cur_layer->items;
360 while (itemlist!=NULL) {
361 item = (struct Item *)itemlist->data;
362 itemlist = itemlist->next;
363 if (item->type != ITEM_TEMP_STROKE) continue;
364 item->type = ITEM_STROKE;
365 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, item);
366 // the item has an invisible canvas item, which used to act as anchor
367 if (item->canvas_item!=NULL) {
368 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
369 item->canvas_item = NULL;
371 undo->erasurelist = g_list_append(undo->erasurelist, item->erasure);
372 // add the new strokes into the current layer
373 for (partlist = item->erasure->replacement_items; partlist!=NULL; partlist = partlist->next)
374 ui.cur_layer->items = g_list_insert_before(
375 ui.cur_layer->items, itemlist, partlist->data);
376 ui.cur_layer->nitems += item->erasure->nrepl-1;
380 ui.cur_item_type = ITEM_NONE;
382 /* NOTE: the list of erasures goes in the depth order of the layer;
383 this guarantees that, upon undo, the erasure->npos fields give the
384 correct position where each item should be reinserted as the list
385 is traversed in the forward direction */
388 /************ selection tools ***********/
390 void make_dashed(GnomeCanvasItem *item)
398 dashlen[0] = dashlen[1] = 6.0;
399 gnome_canvas_item_set(item, "dash", &dash, NULL);
403 void start_selectrect(GdkEvent *event)
408 ui.cur_item_type = ITEM_SELECTRECT;
409 ui.selection = g_new(struct Selection, 1);
410 ui.selection->type = ITEM_SELECTRECT;
411 ui.selection->items = NULL;
412 ui.selection->layer = ui.cur_layer;
414 get_pointer_coords(event, pt);
415 ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
416 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
418 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
419 gnome_canvas_rect_get_type(), "width-pixels", 1,
420 "outline-color-rgba", 0x000000ff,
421 "fill-color-rgba", 0x80808040,
422 "x1", pt[0], "x2", pt[0], "y1", pt[1], "y2", pt[1], NULL);
426 void finalize_selectrect(void)
428 double x1, x2, y1, y2;
433 ui.cur_item_type = ITEM_NONE;
435 if (ui.selection->bbox.left > ui.selection->bbox.right) {
436 x1 = ui.selection->bbox.right; x2 = ui.selection->bbox.left;
437 ui.selection->bbox.left = x1; ui.selection->bbox.right = x2;
439 x1 = ui.selection->bbox.left; x2 = ui.selection->bbox.right;
442 if (ui.selection->bbox.top > ui.selection->bbox.bottom) {
443 y1 = ui.selection->bbox.bottom; y2 = ui.selection->bbox.top;
444 ui.selection->bbox.top = y1; ui.selection->bbox.bottom = y2;
446 y1 = ui.selection->bbox.top; y2 = ui.selection->bbox.bottom;
449 for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
450 item = (struct Item *)itemlist->data;
451 if (item->bbox.left >= x1 && item->bbox.right <= x2 &&
452 item->bbox.top >= y1 && item->bbox.bottom <= y2) {
453 ui.selection->items = g_list_append(ui.selection->items, item);
457 if (ui.selection->items == NULL) {
458 // if we clicked inside a text zone ?
459 item = click_is_in_text(ui.selection->layer, x1, y1);
460 if (item!=NULL && item==click_is_in_text(ui.selection->layer, x2, y2)) {
461 ui.selection->items = g_list_append(ui.selection->items, item);
462 g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox));
463 gnome_canvas_item_set(ui.selection->canvas_item,
464 "x1", item->bbox.left, "x2", item->bbox.right,
465 "y1", item->bbox.top, "y2", item->bbox.bottom, NULL);
469 if (ui.selection->items == NULL) reset_selection();
470 else make_dashed(ui.selection->canvas_item);
472 update_copy_paste_enabled();
473 update_font_button();
476 gboolean start_movesel(GdkEvent *event)
480 if (ui.selection==NULL) return FALSE;
481 if (ui.cur_layer != ui.selection->layer) return FALSE;
483 get_pointer_coords(event, pt);
484 if (ui.selection->type == ITEM_SELECTRECT) {
485 if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
486 pt[1]<ui.selection->bbox.top || pt[1]>ui.selection->bbox.bottom)
488 ui.cur_item_type = ITEM_MOVESEL;
489 ui.selection->anchor_x = ui.selection->last_x = pt[0];
490 ui.selection->anchor_y = ui.selection->last_y = pt[1];
491 ui.selection->orig_pageno = ui.pageno;
492 ui.selection->move_pageno = ui.pageno;
493 ui.selection->move_layer = ui.selection->layer;
494 ui.selection->move_pagedelta = 0.;
495 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
502 void start_vertspace(GdkEvent *event)
509 ui.cur_item_type = ITEM_MOVESEL_VERT;
510 ui.selection = g_new(struct Selection, 1);
511 ui.selection->type = ITEM_MOVESEL_VERT;
512 ui.selection->items = NULL;
513 ui.selection->layer = ui.cur_layer;
515 get_pointer_coords(event, pt);
516 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
517 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
518 item = (struct Item *)itemlist->data;
519 if (item->bbox.top >= pt[1]) {
520 ui.selection->items = g_list_append(ui.selection->items, item);
521 if (item->bbox.bottom > ui.selection->bbox.bottom)
522 ui.selection->bbox.bottom = item->bbox.bottom;
526 ui.selection->anchor_x = ui.selection->last_x = 0;
527 ui.selection->anchor_y = ui.selection->last_y = pt[1];
528 ui.selection->orig_pageno = ui.pageno;
529 ui.selection->move_pageno = ui.pageno;
530 ui.selection->move_layer = ui.selection->layer;
531 ui.selection->move_pagedelta = 0.;
532 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
533 gnome_canvas_rect_get_type(), "width-pixels", 1,
534 "outline-color-rgba", 0x000000ff,
535 "fill-color-rgba", 0x80808040,
536 "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
540 void continue_movesel(GdkEvent *event)
542 double pt[2], dx, dy, upmargin;
546 struct Page *tmppage;
548 get_pointer_coords(event, pt);
549 if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
550 pt[1] += ui.selection->move_pagedelta;
552 // check for page jumps
553 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
554 upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
555 else upmargin = VIEW_CONTINUOUS_SKIP;
556 tmppageno = ui.selection->move_pageno;
557 tmppage = g_list_nth_data(journal.pages, tmppageno);
558 while (ui.view_continuous && (pt[1] < - upmargin)) {
559 if (tmppageno == 0) break;
561 tmppage = g_list_nth_data(journal.pages, tmppageno);
562 pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
563 ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
565 while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
566 if (tmppageno == journal.npages-1) break;
567 pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
568 ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
570 tmppage = g_list_nth_data(journal.pages, tmppageno);
573 if (tmppageno != ui.selection->move_pageno) {
574 // move to a new page !
575 ui.selection->move_pageno = tmppageno;
576 if (tmppageno == ui.selection->orig_pageno)
577 ui.selection->move_layer = ui.selection->layer;
579 ui.selection->move_layer = (struct Layer *)(g_list_last(
580 ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
581 gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
582 for (list = ui.selection->items; list!=NULL; list = list->next) {
583 item = (struct Item *)list->data;
584 if (item->canvas_item!=NULL)
585 gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
587 // avoid a refresh bug
588 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
589 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
590 gnome_canvas_item_set(ui.selection->canvas_item,
591 "x2", tmppage->width+100,
592 "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
595 // now, process things normally
597 dx = pt[0] - ui.selection->last_x;
598 dy = pt[1] - ui.selection->last_y;
599 if (hypot(dx,dy) < 1) return; // don't move subpixel
600 ui.selection->last_x = pt[0];
601 ui.selection->last_y = pt[1];
603 // move the canvas items
604 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
605 gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
607 gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
609 for (list = ui.selection->items; list != NULL; list = list->next) {
610 item = (struct Item *)list->data;
611 if (item->canvas_item != NULL)
612 gnome_canvas_item_move(item->canvas_item, dx, dy);
616 void finalize_movesel(void)
621 if (ui.selection->items != NULL) {
623 undo->type = ITEM_MOVESEL;
624 undo->itemlist = g_list_copy(ui.selection->items);
625 undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
626 undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
627 undo->layer = ui.selection->layer;
628 undo->layer2 = ui.selection->move_layer;
629 undo->auxlist = NULL;
630 // build auxlist = pointers to Item's just before ours (for depths)
631 for (list = ui.selection->items; list!=NULL; list = list->next) {
632 link = g_list_find(ui.selection->layer->items, list->data);
633 if (link!=NULL) link = link->prev;
634 undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
636 ui.selection->layer = ui.selection->move_layer;
637 move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
638 undo->layer, undo->layer2,
639 (undo->layer == undo->layer2)?undo->auxlist:NULL);
642 if (ui.selection->move_pageno!=ui.selection->orig_pageno)
643 do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
645 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
648 ui.selection->bbox.left += undo->val_x;
649 ui.selection->bbox.right += undo->val_x;
650 ui.selection->bbox.top += undo->val_y;
651 ui.selection->bbox.bottom += undo->val_y;
652 make_dashed(ui.selection->canvas_item);
654 ui.cur_item_type = ITEM_NONE;
659 void selection_delete(void)
661 struct UndoErasureData *erasure;
665 if (ui.selection == NULL) return;
667 undo->type = ITEM_ERASURE;
668 undo->layer = ui.selection->layer;
669 undo->erasurelist = NULL;
670 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
671 item = (struct Item *)itemlist->data;
672 if (item->canvas_item!=NULL)
673 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
674 erasure = g_new(struct UndoErasureData, 1);
675 erasure->item = item;
676 erasure->npos = g_list_index(ui.selection->layer->items, item);
678 erasure->replacement_items = NULL;
679 ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
680 ui.selection->layer->nitems--;
681 undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
685 /* NOTE: the erasurelist is built backwards; this guarantees that,
686 upon undo, the erasure->npos fields give the correct position
687 where each item should be reinserted as the list is traversed in
688 the forward direction */
691 void callback_clipboard_get(GtkClipboard *clipboard,
692 GtkSelectionData *selection_data,
693 guint info, gpointer user_data)
697 g_memmove(&length, user_data, sizeof(int));
698 gtk_selection_data_set(selection_data,
699 gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
702 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
707 void selection_to_clip(void)
709 int bufsz, nitems, val;
713 GtkTargetEntry target;
715 if (ui.selection == NULL) return;
716 bufsz = 2*sizeof(int) // bufsz, nitems
717 + sizeof(struct BBox); // bbox
719 for (list = ui.selection->items; list != NULL; list = list->next) {
720 item = (struct Item *)list->data;
722 if (item->type == ITEM_STROKE) {
723 bufsz+= sizeof(int) // type
724 + sizeof(struct Brush) // brush
725 + sizeof(int) // num_points
726 + 2*item->path->num_points*sizeof(double); // the points
728 else if (item->type == ITEM_TEXT) {
729 bufsz+= sizeof(int) // type
730 + sizeof(struct Brush) // brush
731 + 2*sizeof(double) // bbox upper-left
732 + sizeof(int) // text len
733 + strlen(item->text)+1 // text
734 + sizeof(int) // font_name len
735 + strlen(item->font_name)+1 // font_name
736 + sizeof(double); // font_size
738 else bufsz+= sizeof(int); // type
740 p = buf = g_malloc(bufsz);
741 g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
742 g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
743 g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
744 for (list = ui.selection->items; list != NULL; list = list->next) {
745 item = (struct Item *)list->data;
746 g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
747 if (item->type == ITEM_STROKE) {
748 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
749 g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
750 g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
751 p+= 2*item->path->num_points*sizeof(double);
753 if (item->type == ITEM_TEXT) {
754 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
755 g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
756 g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
757 val = strlen(item->text);
758 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
759 g_memmove(p, item->text, val+1); p+= val+1;
760 val = strlen(item->font_name);
761 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
762 g_memmove(p, item->font_name, val+1); p+= val+1;
763 g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
767 target.target = "_XOURNAL";
771 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
773 callback_clipboard_get, callback_clipboard_clear, buf);
777 void clipboard_paste(void)
779 GtkSelectionData *sel_data;
781 int nitems, npts, i, len;
784 double hoffset, voffset, cx, cy;
788 if (ui.cur_layer == NULL) return;
790 ui.cur_item_type = ITEM_PASTE;
791 sel_data = gtk_clipboard_wait_for_contents(
792 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
793 gdk_atom_intern("_XOURNAL", FALSE));
794 ui.cur_item_type = ITEM_NONE;
795 if (sel_data == NULL) return; // paste failed
799 ui.selection = g_new(struct Selection, 1);
800 p = sel_data->data + sizeof(int);
801 g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
802 ui.selection->type = ITEM_SELECTRECT;
803 ui.selection->layer = ui.cur_layer;
804 g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
805 ui.selection->items = NULL;
807 // find by how much we translate the pasted selection
808 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
809 gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
810 gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
811 cx -= ui.cur_page->hoffset;
812 cy -= ui.cur_page->voffset;
813 if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
814 cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
815 if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
816 cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
817 if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
818 cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
819 if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
820 cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
821 hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
822 voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
823 ui.selection->bbox.left += hoffset;
824 ui.selection->bbox.right += hoffset;
825 ui.selection->bbox.top += voffset;
826 ui.selection->bbox.bottom += voffset;
828 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
829 gnome_canvas_rect_get_type(), "width-pixels", 1,
830 "outline-color-rgba", 0x000000ff,
831 "fill-color-rgba", 0x80808040,
832 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
833 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
834 make_dashed(ui.selection->canvas_item);
836 while (nitems-- > 0) {
837 item = g_new(struct Item, 1);
838 ui.selection->items = g_list_append(ui.selection->items, item);
839 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
840 ui.cur_layer->nitems++;
841 g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
842 if (item->type == ITEM_STROKE) {
843 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
844 g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
845 item->path = gnome_canvas_points_new(npts);
847 for (i=0; i<npts; i++) {
848 item->path->coords[2*i] = pf[2*i] + hoffset;
849 item->path->coords[2*i+1] = pf[2*i+1] + voffset;
851 p+= 2*item->path->num_points*sizeof(double);
852 update_item_bbox(item);
853 make_canvas_item_one(ui.cur_layer->group, item);
855 if (item->type == ITEM_TEXT) {
856 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
857 g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
858 g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
859 item->bbox.left += hoffset;
860 item->bbox.top += voffset;
861 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
862 item->text = g_malloc(len+1);
863 g_memmove(item->text, p, len+1); p+= len+1;
864 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
865 item->font_name = g_malloc(len+1);
866 g_memmove(item->font_name, p, len+1); p+= len+1;
867 g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
868 make_canvas_item_one(ui.cur_layer->group, item);
873 undo->type = ITEM_PASTE;
874 undo->layer = ui.cur_layer;
875 undo->itemlist = g_list_copy(ui.selection->items);
877 gtk_selection_data_free(sel_data);
878 update_copy_paste_enabled();
881 // modify the color or thickness of pen strokes in a selection
883 void recolor_selection(int color)
889 if (ui.selection == NULL) return;
891 undo->type = ITEM_REPAINTSEL;
892 undo->itemlist = NULL;
893 undo->auxlist = NULL;
894 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
895 item = (struct Item *)itemlist->data;
896 if (item->type != ITEM_STROKE && item->type != ITEM_TEXT) continue;
897 if (item->type == ITEM_STROKE && item->brush.tool_type!=TOOL_PEN) continue;
898 // store info for undo
899 undo->itemlist = g_list_append(undo->itemlist, item);
900 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
901 g_memmove(brush, &(item->brush), sizeof(struct Brush));
902 undo->auxlist = g_list_append(undo->auxlist, brush);
903 // repaint the stroke
904 item->brush.color_no = color;
905 item->brush.color_rgba = predef_colors_rgba[color];
906 if (item->canvas_item!=NULL)
907 gnome_canvas_item_set(item->canvas_item,
908 "fill-color-rgba", item->brush.color_rgba, NULL);
912 void rethicken_selection(int val)
918 if (ui.selection == NULL) return;
920 undo->type = ITEM_REPAINTSEL;
921 undo->itemlist = NULL;
922 undo->auxlist = NULL;
923 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
924 item = (struct Item *)itemlist->data;
925 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
926 // store info for undo
927 undo->itemlist = g_list_append(undo->itemlist, item);
928 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
929 g_memmove(brush, &(item->brush), sizeof(struct Brush));
930 undo->auxlist = g_list_append(undo->auxlist, brush);
931 // repaint the stroke
932 item->brush.thickness_no = val;
933 item->brush.thickness = predef_thickness[TOOL_PEN][val];
934 if (item->canvas_item!=NULL)
935 gnome_canvas_item_set(item->canvas_item,
936 "width-units", item->brush.thickness, NULL);
940 gboolean do_hand_scrollto(gpointer data)
942 ui.hand_scrollto_pending = FALSE;
943 gnome_canvas_scroll_to(canvas, ui.hand_scrollto_cx, ui.hand_scrollto_cy);
947 void do_hand(GdkEvent *event)
952 get_pointer_coords(event, pt);
953 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
954 ui.hand_scrollto_cx = cx - (pt[0]-ui.hand_refpt[0])*ui.zoom;
955 ui.hand_scrollto_cy = cy - (pt[1]-ui.hand_refpt[1])*ui.zoom;
956 if (!ui.hand_scrollto_pending) g_idle_add(do_hand_scrollto, NULL);
957 ui.hand_scrollto_pending = TRUE;
960 /************ TEXT FUNCTIONS **************/
962 // to make it easier to copy/paste at end of text box
963 #define WIDGET_RIGHT_MARGIN 10
965 void resize_textview(gpointer *toplevel, gpointer *data)
970 /* when the text changes, resize the GtkTextView accordingly */
971 if (ui.cur_item_type!=ITEM_TEXT) return;
972 w = GTK_TEXT_VIEW(ui.cur_item->widget);
973 width = w->width + WIDGET_RIGHT_MARGIN;
975 gnome_canvas_item_set(ui.cur_item->canvas_item,
977 "width", (gdouble)width, "height", (gdouble)height, NULL);
978 ui.cur_item->bbox.right = ui.cur_item->bbox.left + width/ui.zoom;
979 ui.cur_item->bbox.bottom = ui.cur_item->bbox.top + height/ui.zoom;
982 void start_text(GdkEvent *event, struct Item *item)
986 GtkTextBuffer *buffer;
987 GnomeCanvasItem *canvas_item;
988 PangoFontDescription *font_desc;
991 get_pointer_coords(event, pt);
992 ui.cur_item_type = ITEM_TEXT;
995 item = g_new(struct Item, 1);
997 item->canvas_item = NULL;
998 item->bbox.left = pt[0];
999 item->bbox.top = pt[1];
1000 item->bbox.right = ui.cur_page->width;
1001 item->bbox.bottom = pt[1]+100.;
1002 item->font_name = g_strdup(ui.font_name);
1003 item->font_size = ui.font_size;
1004 g_memmove(&(item->brush), ui.cur_brush, sizeof(struct Brush));
1005 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1006 ui.cur_layer->nitems++;
1009 item->type = ITEM_TEMP_TEXT;
1012 font_desc = pango_font_description_from_string(item->font_name);
1013 pango_font_description_set_absolute_size(font_desc,
1014 item->font_size*ui.zoom*PANGO_SCALE);
1015 item->widget = gtk_text_view_new();
1016 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(item->widget));
1017 if (item->text!=NULL)
1018 gtk_text_buffer_set_text(buffer, item->text, -1);
1019 gtk_widget_modify_font(item->widget, font_desc);
1020 rgb_to_gdkcolor(item->brush.color_rgba, &color);
1021 gtk_widget_modify_text(item->widget, GTK_STATE_NORMAL, &color);
1022 pango_font_description_free(font_desc);
1024 canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1025 gnome_canvas_widget_get_type(),
1026 "x", item->bbox.left, "y", item->bbox.top,
1027 "width", item->bbox.right-item->bbox.left,
1028 "height", item->bbox.bottom-item->bbox.top,
1029 "widget", item->widget, NULL);
1030 // TODO: width/height?
1031 if (item->canvas_item!=NULL) {
1032 lower_canvas_item_to(ui.cur_layer->group, canvas_item, item->canvas_item);
1033 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1035 item->canvas_item = canvas_item;
1037 gtk_widget_show(item->widget);
1038 gtk_widget_grab_focus(item->widget);
1039 ui.resize_signal_handler =
1040 g_signal_connect((gpointer) winMain, "check_resize",
1041 G_CALLBACK(resize_textview), NULL);
1042 update_font_button();
1043 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), FALSE);
1044 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), FALSE);
1049 GtkTextBuffer *buffer;
1050 GtkTextIter start, end;
1052 struct UndoErasureData *erasure;
1053 GnomeCanvasItem *tmpitem;
1055 if (ui.cur_item_type!=ITEM_TEXT) return; // nothing for us to do!
1057 // finalize the text that's been edited...
1058 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.cur_item->widget));
1059 gtk_text_buffer_get_bounds(buffer, &start, &end);
1060 ui.cur_item->type = ITEM_TEXT;
1061 new_text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
1062 ui.cur_item_type = ITEM_NONE;
1063 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), TRUE);
1064 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), TRUE);
1066 if (strlen(new_text)==0) { // erase object and cancel
1068 g_signal_handler_disconnect(winMain, ui.resize_signal_handler);
1069 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
1070 ui.cur_item->canvas_item = NULL;
1071 if (ui.cur_item->text == NULL) // nothing happened
1072 g_free(ui.cur_item->font_name);
1073 else { // treat this as an erasure
1075 undo->type = ITEM_ERASURE;
1076 undo->layer = ui.cur_layer;
1077 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
1078 erasure->item = ui.cur_item;
1079 erasure->npos = g_list_index(ui.cur_layer->items, ui.cur_item);
1081 erasure->replacement_items = NULL;
1082 undo->erasurelist = g_list_append(NULL, erasure);
1084 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, ui.cur_item);
1085 ui.cur_layer->nitems--;
1091 if (ui.cur_item->text==NULL || strcmp(ui.cur_item->text, new_text)) {
1093 if (ui.cur_item->text == NULL) undo->type = ITEM_TEXT;
1094 else undo->type = ITEM_TEXT_EDIT;
1095 undo->layer = ui.cur_layer;
1096 undo->item = ui.cur_item;
1097 undo->str = ui.cur_item->text;
1099 else g_free(ui.cur_item->text);
1101 ui.cur_item->text = new_text;
1102 ui.cur_item->widget = NULL;
1103 // replace the canvas item
1104 tmpitem = ui.cur_item->canvas_item;
1105 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
1106 update_item_bbox(ui.cur_item);
1107 lower_canvas_item_to(ui.cur_layer->group, ui.cur_item->canvas_item, tmpitem);
1108 gtk_object_destroy(GTK_OBJECT(tmpitem));
1111 /* update the items in the canvas so they're of the right font size */
1113 void update_text_item_displayfont(struct Item *item)
1115 PangoFontDescription *font_desc;
1117 if (item->type != ITEM_TEXT && item->type != ITEM_TEMP_TEXT) return;
1118 if (item->canvas_item==NULL) return;
1119 font_desc = pango_font_description_from_string(item->font_name);
1120 pango_font_description_set_absolute_size(font_desc,
1121 item->font_size*ui.zoom*PANGO_SCALE);
1122 if (item->type == ITEM_TEMP_TEXT)
1123 gtk_widget_modify_font(item->widget, font_desc);
1125 gnome_canvas_item_set(item->canvas_item, "font-desc", font_desc, NULL);
1126 update_item_bbox(item);
1128 pango_font_description_free(font_desc);
1131 void rescale_text_items(void)
1133 GList *pagelist, *layerlist, *itemlist;
1136 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next)
1137 for (layerlist = ((struct Page *)pagelist->data)->layers; layerlist!=NULL; layerlist = layerlist->next)
1138 for (itemlist = ((struct Layer *)layerlist->data)->items; itemlist!=NULL; itemlist = itemlist->next)
1139 update_text_item_displayfont((struct Item *)itemlist->data);
1142 struct Item *click_is_in_text(struct Layer *layer, double x, double y)
1145 struct Item *item, *val;
1148 for (itemlist = layer->items; itemlist!=NULL; itemlist = itemlist->next) {
1149 item = (struct Item *)itemlist->data;
1150 if (item->type != ITEM_TEXT) continue;
1151 if (x<item->bbox.left || x>item->bbox.right) continue;
1152 if (y<item->bbox.top || y>item->bbox.bottom) continue;
1158 void refont_text_item(struct Item *item, gchar *font_name, double font_size)
1160 if (!strcmp(font_name, item->font_name) && font_size==item->font_size) return;
1161 if (item->text!=NULL) {
1163 undo->type = ITEM_TEXT_ATTRIB;
1165 undo->str = item->font_name;
1166 undo->val_x = item->font_size;
1167 undo->brush = (struct Brush *)g_memdup(&(item->brush), sizeof(struct Brush));
1169 else g_free(item->font_name);
1170 item->font_name = g_strdup(font_name);
1171 if (font_size>0.) item->font_size = font_size;
1172 update_text_item_displayfont(item);
1175 void process_font_sel(gchar *str)
1183 p = strrchr(str, ' ');
1185 size = g_strtod(p+1, &q);
1186 if (*q!=0 || size<1.) size=0.;
1191 g_free(ui.font_name);
1193 if (size>0.) ui.font_size = size;
1195 // if there's a current text item, re-font it
1196 if (ui.cur_item_type == ITEM_TEXT) {
1197 refont_text_item(ui.cur_item, str, size);
1198 undo_cont = (ui.cur_item->text!=NULL);
1200 // if there's a current selection, re-font it
1201 if (ui.selection!=NULL)
1202 for (list=ui.selection->items; list!=NULL; list=list->next) {
1203 it = (struct Item *)list->data;
1204 if (it->type == ITEM_TEXT) {
1205 if (undo_cont) undo->multiop |= MULTIOP_CONT_REDO;
1206 refont_text_item(it, str, size);
1207 if (undo_cont) undo->multiop |= MULTIOP_CONT_UNDO;
1211 update_font_button();