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 /***** Win32 fix for gdk_cursor_new_from_pixmap() by Dirk Gerrits ****/
22 gboolean colors_too_similar(const GdkColor *colora, const GdkColor *colorb)
24 return (abs(colora->red - colorb->red) < 256 &&
25 abs(colora->green - colorb->green) < 256 &&
26 abs(colora->blue - colorb->blue) < 256);
29 /* gdk_cursor_new_from_pixmap is broken on Windows.
30 this is a workaround using gdk_cursor_new_from_pixbuf. */
31 GdkCursor* fixed_gdk_cursor_new_from_pixmap(GdkPixmap *source, GdkPixmap *mask,
32 const GdkColor *fg, const GdkColor *bg,
35 GdkPixmap *rgb_pixmap;
37 GdkPixbuf *rgb_pixbuf, *rgba_pixbuf;
41 /* HACK! It seems impossible to work with RGBA pixmaps directly in
42 GDK-Win32. Instead we pick some third color, different from fg
43 and bg, and use that as the 'transparent color'. We do this using
44 colors_too_similar (see above) because two colors could be
45 unequal in GdkColor's 16-bit/sample, but equal in GdkPixbuf's
47 GdkColor candidates[3] = {{0,65535,0,0}, {0,0,65535,0}, {0,0,0,65535}};
48 GdkColor *trans = &candidates[0];
49 if (colors_too_similar(trans, fg) || colors_too_similar(trans, bg)) {
50 trans = &candidates[1];
51 if (colors_too_similar(trans, fg) || colors_too_similar(trans, bg)) {
52 trans = &candidates[2];
54 } /* trans is now guaranteed to be unique from fg and bg */
56 /* create an empty pixmap to hold the cursor image */
57 gdk_drawable_get_size(source, &width, &height);
58 rgb_pixmap = gdk_pixmap_new(NULL, width, height, 24);
60 /* blit the bitmaps defining the cursor onto a transparent background */
61 gc = gdk_gc_new(rgb_pixmap);
62 gdk_gc_set_fill(gc, GDK_SOLID);
63 gdk_gc_set_rgb_fg_color(gc, trans);
64 gdk_draw_rectangle(rgb_pixmap, gc, TRUE, 0, 0, width, height);
65 gdk_gc_set_fill(gc, GDK_OPAQUE_STIPPLED);
66 gdk_gc_set_stipple(gc, source);
67 gdk_gc_set_clip_mask(gc, mask);
68 gdk_gc_set_rgb_fg_color(gc, fg);
69 gdk_gc_set_rgb_bg_color(gc, bg);
70 gdk_draw_rectangle(rgb_pixmap, gc, TRUE, 0, 0, width, height);
73 /* create a cursor out of the created pixmap */
74 rgb_pixbuf = gdk_pixbuf_get_from_drawable(
75 NULL, rgb_pixmap, gdk_colormap_get_system(), 0, 0, 0, 0, width, height);
76 gdk_pixmap_unref(rgb_pixmap);
77 rgba_pixbuf = gdk_pixbuf_add_alpha(
78 rgb_pixbuf, TRUE, trans->red, trans->green, trans->blue);
79 gdk_pixbuf_unref(rgb_pixbuf);
80 cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), rgba_pixbuf, x, y);
81 gdk_pixbuf_unref(rgba_pixbuf);
85 #define gdk_cursor_new_from_pixmap fixed_gdk_cursor_new_from_pixmap
89 /************** drawing nice cursors *********/
91 static char cursor_pen_bits[] = {
92 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
93 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
94 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
96 static char cursor_eraser_bits[] = {
97 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x08, 0x08, 0x08, 0x08,
98 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x0f,
99 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
101 static char cursor_eraser_mask[] = {
102 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f,
103 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f,
104 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
106 void set_cursor_busy(gboolean busy)
111 cursor = gdk_cursor_new(GDK_WATCH);
112 gdk_window_set_cursor(GTK_WIDGET(winMain)->window, cursor);
113 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, cursor);
114 gdk_cursor_unref(cursor);
117 gdk_window_set_cursor(GTK_WIDGET(winMain)->window, NULL);
120 gdk_display_sync(gdk_display_get_default());
123 void update_cursor(void)
125 GdkPixmap *source, *mask;
126 GdkColor fg = {0, 0, 0, 0}, bg = {0, 65535, 65535, 65535};
128 ui.is_sel_cursor = FALSE;
129 if (GTK_WIDGET(canvas)->window == NULL) return;
131 if (ui.cursor!=NULL) {
132 gdk_cursor_unref(ui.cursor);
135 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
136 ui.cursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
137 else if (ui.cur_item_type == ITEM_MOVESEL)
138 ui.cursor = gdk_cursor_new(GDK_FLEUR);
139 else if (ui.toolno[ui.cur_mapping] == TOOL_PEN) {
140 fg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
141 fg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
142 fg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
143 source = gdk_bitmap_create_from_data(NULL, cursor_pen_bits, 16, 16);
144 ui.cursor = gdk_cursor_new_from_pixmap(source, source, &fg, &bg, 7, 7);
145 gdk_bitmap_unref(source);
147 else if (ui.toolno[ui.cur_mapping] == TOOL_ERASER) {
148 source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
149 mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
150 ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
151 gdk_bitmap_unref(source);
152 gdk_bitmap_unref(mask);
154 else if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
155 source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
156 mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
157 bg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
158 bg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
159 bg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
160 ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
161 gdk_bitmap_unref(source);
162 gdk_bitmap_unref(mask);
164 else if (ui.cur_item_type == ITEM_SELECTRECT) {
165 ui.cursor = gdk_cursor_new(GDK_TCROSS);
167 else if (ui.toolno[ui.cur_mapping] == TOOL_HAND) {
168 ui.cursor = gdk_cursor_new(GDK_HAND1);
170 else if (ui.toolno[ui.cur_mapping] == TOOL_TEXT) {
171 ui.cursor = gdk_cursor_new(GDK_XTERM);
174 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
177 /* adjust the cursor shape if it hovers near a selection box */
179 void update_cursor_for_resize(double *pt)
181 gboolean in_range_x, in_range_y;
182 gboolean can_resize_left, can_resize_right, can_resize_bottom, can_resize_top;
183 gdouble resize_margin;
184 GdkCursorType newcursor;
186 // if we're not even close to the box in some direction, return immediately
187 resize_margin = RESIZE_MARGIN / ui.zoom;
188 if (pt[0]<ui.selection->bbox.left-resize_margin || pt[0]>ui.selection->bbox.right+resize_margin
189 || pt[1]<ui.selection->bbox.top-resize_margin || pt[1]>ui.selection->bbox.bottom+resize_margin)
191 if (ui.is_sel_cursor) update_cursor();
195 ui.is_sel_cursor = TRUE;
196 can_resize_left = (pt[0] < ui.selection->bbox.left+resize_margin);
197 can_resize_right = (pt[0] > ui.selection->bbox.right-resize_margin);
198 can_resize_top = (pt[1] < ui.selection->bbox.top+resize_margin);
199 can_resize_bottom = (pt[1] > ui.selection->bbox.bottom-resize_margin);
201 if (can_resize_left) {
202 if (can_resize_top) newcursor = GDK_TOP_LEFT_CORNER;
203 else if (can_resize_bottom) newcursor = GDK_BOTTOM_LEFT_CORNER;
204 else newcursor = GDK_LEFT_SIDE;
206 else if (can_resize_right) {
207 if (can_resize_top) newcursor = GDK_TOP_RIGHT_CORNER;
208 else if (can_resize_bottom) newcursor = GDK_BOTTOM_RIGHT_CORNER;
209 else newcursor = GDK_RIGHT_SIDE;
211 else if (can_resize_top) newcursor = GDK_TOP_SIDE;
212 else if (can_resize_bottom) newcursor = GDK_BOTTOM_SIDE;
213 else newcursor = GDK_FLEUR;
215 if (ui.cursor!=NULL && ui.cursor->type == newcursor) return;
216 if (ui.cursor!=NULL) gdk_cursor_unref(ui.cursor);
217 ui.cursor = gdk_cursor_new(newcursor);
218 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
221 /************** painting strokes *************/
223 #define SUBDIVIDE_MAXDIST 5.0
225 void subdivide_cur_path(null)
229 double x0, y0, x1, y1;
231 for (n=0, p=ui.cur_path.coords; n<ui.cur_path.num_points-1; n++, p+=2) {
232 pieces = (int)floor(hypot(p[2]-p[0], p[3]-p[1])/SUBDIVIDE_MAXDIST);
234 x0 = p[0]; y0 = p[1];
235 x1 = p[2]; y1 = p[3];
236 realloc_cur_path(ui.cur_path.num_points+pieces-1);
237 g_memmove(ui.cur_path.coords+2*(n+pieces), ui.cur_path.coords+2*(n+1),
238 2*(ui.cur_path.num_points-n-1)*sizeof(double));
239 p = ui.cur_path.coords+2*n;
240 ui.cur_path.num_points += pieces-1;
242 for (k=1; k<pieces; k++) {
244 p[0] = x0 + k*(x1-x0)/pieces;
245 p[1] = y0 + k*(y1-y0)/pieces;
251 void create_new_stroke(GdkEvent *event)
253 ui.cur_item_type = ITEM_STROKE;
254 ui.cur_item = g_new(struct Item, 1);
255 ui.cur_item->type = ITEM_STROKE;
256 g_memmove(&(ui.cur_item->brush), ui.cur_brush, sizeof(struct Brush));
257 ui.cur_item->path = &ui.cur_path;
259 ui.cur_path.num_points = 1;
260 get_pointer_coords(event, ui.cur_path.coords);
262 if (ui.cur_brush->ruler) {
263 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
264 gnome_canvas_line_get_type(),
265 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
266 "fill-color-rgba", ui.cur_item->brush.color_rgba,
267 "width-units", ui.cur_item->brush.thickness, NULL);
268 ui.cur_item->brush.variable_width = FALSE;
270 ui.cur_item->canvas_item = gnome_canvas_item_new(
271 ui.cur_layer->group, gnome_canvas_group_get_type(), NULL);
274 void continue_stroke(GdkEvent *event)
276 GnomeCanvasPoints seg;
277 double *pt, current_width;
279 if (ui.cur_brush->ruler) {
280 pt = ui.cur_path.coords;
282 realloc_cur_path(ui.cur_path.num_points+1);
283 pt = ui.cur_path.coords + 2*(ui.cur_path.num_points-1);
286 get_pointer_coords(event, pt+2);
288 if (ui.cur_item->brush.variable_width) {
289 realloc_cur_widths(ui.cur_path.num_points);
290 current_width = ui.cur_item->brush.thickness*get_pressure_multiplier(event);
291 ui.cur_widths[ui.cur_path.num_points-1] = current_width;
293 else current_width = ui.cur_item->brush.thickness;
295 if (ui.cur_brush->ruler)
296 ui.cur_path.num_points = 2;
298 if (hypot(pt[0]-pt[2], pt[1]-pt[3]) < PIXEL_MOTION_THRESHOLD/ui.zoom)
299 return; // not a meaningful motion
300 ui.cur_path.num_points++;
307 /* note: we're using a piece of the cur_path array. This is ok because
308 upon creation the line just copies the contents of the GnomeCanvasPoints
309 into an internal structure */
311 if (ui.cur_brush->ruler)
312 gnome_canvas_item_set(ui.cur_item->canvas_item, "points", &seg, NULL);
314 gnome_canvas_item_new((GnomeCanvasGroup *)ui.cur_item->canvas_item,
315 gnome_canvas_line_get_type(), "points", &seg,
316 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
317 "fill-color-rgba", ui.cur_item->brush.color_rgba,
318 "width-units", current_width, NULL);
321 void finalize_stroke(void)
323 if (ui.cur_path.num_points == 1) { // GnomeCanvas doesn't like num_points=1
324 ui.cur_path.coords[2] = ui.cur_path.coords[0]+0.1;
325 ui.cur_path.coords[3] = ui.cur_path.coords[1];
326 ui.cur_path.num_points = 2;
327 ui.cur_item->brush.variable_width = FALSE;
330 if (!ui.cur_item->brush.variable_width)
331 subdivide_cur_path(); // split the segment so eraser will work
333 ui.cur_item->path = gnome_canvas_points_new(ui.cur_path.num_points);
334 g_memmove(ui.cur_item->path->coords, ui.cur_path.coords,
335 2*ui.cur_path.num_points*sizeof(double));
336 if (ui.cur_item->brush.variable_width)
337 ui.cur_item->widths = (gdouble *)g_memdup(ui.cur_widths,
338 (ui.cur_path.num_points-1)*sizeof(gdouble));
339 else ui.cur_item->widths = NULL;
340 update_item_bbox(ui.cur_item);
341 ui.cur_path.num_points = 0;
343 if (!ui.cur_item->brush.variable_width) {
344 // destroy the entire group of temporary line segments
345 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
346 // make a new line item to replace it
347 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
350 // add undo information
352 undo->type = ITEM_STROKE;
353 undo->item = ui.cur_item;
354 undo->layer = ui.cur_layer;
356 // store the item on top of the layer stack
357 ui.cur_layer->items = g_list_append(ui.cur_layer->items, ui.cur_item);
358 ui.cur_layer->nitems++;
360 ui.cur_item_type = ITEM_NONE;
363 /************** eraser tool *************/
365 void erase_stroke_portions(struct Item *item, double x, double y, double radius,
366 gboolean whole_strokes, struct UndoErasureData *erasure)
370 struct Item *newhead, *newtail;
371 gboolean need_recalc = FALSE;
373 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
374 if (hypot(pt[0]-x, pt[1]-y) <= radius) { // found an intersection
375 // FIXME: need to test if line SEGMENT hits the circle
376 // hide the canvas item, and create erasure data if needed
377 if (erasure == NULL) {
378 item->type = ITEM_TEMP_STROKE;
379 gnome_canvas_item_hide(item->canvas_item);
380 /* we'll use this hidden item as an insertion point later */
381 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
382 item->erasure = erasure;
383 erasure->item = item;
384 erasure->npos = g_list_index(ui.cur_layer->items, item);
386 erasure->replacement_items = NULL;
389 newhead = newtail = NULL;
390 if (!whole_strokes) {
392 newhead = (struct Item *)g_malloc(sizeof(struct Item));
393 newhead->type = ITEM_STROKE;
394 g_memmove(&newhead->brush, &item->brush, sizeof(struct Brush));
395 newhead->path = gnome_canvas_points_new(i);
396 g_memmove(newhead->path->coords, item->path->coords, 2*i*sizeof(double));
397 if (newhead->brush.variable_width)
398 newhead->widths = (gdouble *)g_memdup(item->widths, (i-1)*sizeof(gdouble));
399 else newhead->widths = NULL;
401 while (++i < item->path->num_points) {
403 if (hypot(pt[0]-x, pt[1]-y) > radius) break;
405 if (i<item->path->num_points-1) {
406 newtail = (struct Item *)g_malloc(sizeof(struct Item));
407 newtail->type = ITEM_STROKE;
408 g_memmove(&newtail->brush, &item->brush, sizeof(struct Brush));
409 newtail->path = gnome_canvas_points_new(item->path->num_points-i);
410 g_memmove(newtail->path->coords, item->path->coords+2*i,
411 2*(item->path->num_points-i)*sizeof(double));
412 if (newtail->brush.variable_width)
413 newtail->widths = (gdouble *)g_memdup(item->widths+i,
414 (item->path->num_points-i-1)*sizeof(gdouble));
415 else newtail->widths = NULL;
416 newtail->canvas_item = NULL;
419 if (item->type == ITEM_STROKE) {
420 // it's inside an erasure list - we destroy it
421 gnome_canvas_points_free(item->path);
422 if (item->brush.variable_width) g_free(item->widths);
423 if (item->canvas_item != NULL)
424 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
426 erasure->replacement_items = g_list_remove(erasure->replacement_items, item);
430 if (newhead != NULL) {
431 update_item_bbox(newhead);
432 make_canvas_item_one(ui.cur_layer->group, newhead);
433 lower_canvas_item_to(ui.cur_layer->group,
434 newhead->canvas_item, erasure->item->canvas_item);
435 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newhead);
437 // prepending ensures it won't get processed twice
439 // recurse into the new tail
440 need_recalc = (newtail!=NULL);
441 if (newtail == NULL) break;
443 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newtail);
445 i=0; pt=item->path->coords;
448 // add the tail if needed
449 if (!need_recalc) return;
450 update_item_bbox(item);
451 make_canvas_item_one(ui.cur_layer->group, item);
452 lower_canvas_item_to(ui.cur_layer->group, item->canvas_item,
453 erasure->item->canvas_item);
457 void do_eraser(GdkEvent *event, double radius, gboolean whole_strokes)
459 struct Item *item, *repl;
460 GList *itemlist, *repllist;
462 struct BBox eraserbox;
464 get_pointer_coords(event, pos);
465 eraserbox.left = pos[0]-radius;
466 eraserbox.right = pos[0]+radius;
467 eraserbox.top = pos[1]-radius;
468 eraserbox.bottom = pos[1]+radius;
469 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
470 item = (struct Item *)itemlist->data;
471 if (item->type == ITEM_STROKE) {
472 if (!have_intersect(&(item->bbox), &eraserbox)) continue;
473 erase_stroke_portions(item, pos[0], pos[1], radius, whole_strokes, NULL);
474 } else if (item->type == ITEM_TEMP_STROKE) {
475 repllist = item->erasure->replacement_items;
476 while (repllist!=NULL) {
477 repl = (struct Item *)repllist->data;
478 // we may delete the item soon! so advance now in the list
479 repllist = repllist->next;
480 if (have_intersect(&(repl->bbox), &eraserbox))
481 erase_stroke_portions(repl, pos[0], pos[1], radius, whole_strokes, item->erasure);
487 void finalize_erasure(void)
489 GList *itemlist, *partlist;
493 undo->type = ITEM_ERASURE;
494 undo->layer = ui.cur_layer;
495 undo->erasurelist = NULL;
497 itemlist = ui.cur_layer->items;
498 while (itemlist!=NULL) {
499 item = (struct Item *)itemlist->data;
500 itemlist = itemlist->next;
501 if (item->type != ITEM_TEMP_STROKE) continue;
502 item->type = ITEM_STROKE;
503 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, item);
504 // the item has an invisible canvas item, which used to act as anchor
505 if (item->canvas_item!=NULL) {
506 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
507 item->canvas_item = NULL;
509 undo->erasurelist = g_list_append(undo->erasurelist, item->erasure);
510 // add the new strokes into the current layer
511 for (partlist = item->erasure->replacement_items; partlist!=NULL; partlist = partlist->next)
512 ui.cur_layer->items = g_list_insert_before(
513 ui.cur_layer->items, itemlist, partlist->data);
514 ui.cur_layer->nitems += item->erasure->nrepl-1;
518 ui.cur_item_type = ITEM_NONE;
520 /* NOTE: the list of erasures goes in the depth order of the layer;
521 this guarantees that, upon undo, the erasure->npos fields give the
522 correct position where each item should be reinserted as the list
523 is traversed in the forward direction */
526 /************ selection tools ***********/
528 void make_dashed(GnomeCanvasItem *item)
536 dashlen[0] = dashlen[1] = 6.0;
537 gnome_canvas_item_set(item, "dash", &dash, NULL);
541 void start_selectrect(GdkEvent *event)
546 ui.cur_item_type = ITEM_SELECTRECT;
547 ui.selection = g_new(struct Selection, 1);
548 ui.selection->type = ITEM_SELECTRECT;
549 ui.selection->items = NULL;
550 ui.selection->layer = ui.cur_layer;
552 get_pointer_coords(event, pt);
553 ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
554 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
556 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
557 gnome_canvas_rect_get_type(), "width-pixels", 1,
558 "outline-color-rgba", 0x000000ff,
559 "fill-color-rgba", 0x80808040,
560 "x1", pt[0], "x2", pt[0], "y1", pt[1], "y2", pt[1], NULL);
564 void finalize_selectrect(void)
566 double x1, x2, y1, y2;
571 ui.cur_item_type = ITEM_NONE;
573 if (ui.selection->bbox.left > ui.selection->bbox.right) {
574 x1 = ui.selection->bbox.right; x2 = ui.selection->bbox.left;
575 ui.selection->bbox.left = x1; ui.selection->bbox.right = x2;
577 x1 = ui.selection->bbox.left; x2 = ui.selection->bbox.right;
580 if (ui.selection->bbox.top > ui.selection->bbox.bottom) {
581 y1 = ui.selection->bbox.bottom; y2 = ui.selection->bbox.top;
582 ui.selection->bbox.top = y1; ui.selection->bbox.bottom = y2;
584 y1 = ui.selection->bbox.top; y2 = ui.selection->bbox.bottom;
587 for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
588 item = (struct Item *)itemlist->data;
589 if (item->bbox.left >= x1 && item->bbox.right <= x2 &&
590 item->bbox.top >= y1 && item->bbox.bottom <= y2) {
591 ui.selection->items = g_list_append(ui.selection->items, item);
595 if (ui.selection->items == NULL) {
596 // if we clicked inside a text zone ?
597 item = click_is_in_text(ui.selection->layer, x1, y1);
598 if (item!=NULL && item==click_is_in_text(ui.selection->layer, x2, y2)) {
599 ui.selection->items = g_list_append(ui.selection->items, item);
600 g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox));
601 gnome_canvas_item_set(ui.selection->canvas_item,
602 "x1", item->bbox.left, "x2", item->bbox.right,
603 "y1", item->bbox.top, "y2", item->bbox.bottom, NULL);
607 if (ui.selection->items == NULL) reset_selection();
608 else make_dashed(ui.selection->canvas_item);
610 update_copy_paste_enabled();
611 update_font_button();
614 gboolean start_movesel(GdkEvent *event)
618 if (ui.selection==NULL) return FALSE;
619 if (ui.cur_layer != ui.selection->layer) return FALSE;
621 get_pointer_coords(event, pt);
622 if (ui.selection->type == ITEM_SELECTRECT) {
623 if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
624 pt[1]<ui.selection->bbox.top || pt[1]>ui.selection->bbox.bottom)
626 ui.cur_item_type = ITEM_MOVESEL;
627 ui.selection->anchor_x = ui.selection->last_x = pt[0];
628 ui.selection->anchor_y = ui.selection->last_y = pt[1];
629 ui.selection->orig_pageno = ui.pageno;
630 ui.selection->move_pageno = ui.pageno;
631 ui.selection->move_layer = ui.selection->layer;
632 ui.selection->move_pagedelta = 0.;
633 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
640 gboolean start_resizesel(GdkEvent *event)
642 double pt[2], resize_margin, hmargin, vmargin;
644 if (ui.selection==NULL) return FALSE;
645 if (ui.cur_layer != ui.selection->layer) return FALSE;
647 get_pointer_coords(event, pt);
649 if (ui.selection->type == ITEM_SELECTRECT) {
650 resize_margin = RESIZE_MARGIN/ui.zoom;
651 hmargin = (ui.selection->bbox.right-ui.selection->bbox.left)*0.3;
652 if (hmargin>resize_margin) hmargin = resize_margin;
653 vmargin = (ui.selection->bbox.bottom-ui.selection->bbox.top)*0.3;
654 if (vmargin>resize_margin) vmargin = resize_margin;
656 // make sure the click is within a box slightly bigger than the selection rectangle
657 if (pt[0]<ui.selection->bbox.left-resize_margin ||
658 pt[0]>ui.selection->bbox.right+resize_margin ||
659 pt[1]<ui.selection->bbox.top-resize_margin ||
660 pt[1]>ui.selection->bbox.bottom+resize_margin)
663 // now, if the click is near the edge, it's a resize operation
664 // keep track of which edges we're close to, since those are the ones which should move
665 ui.selection->resizing_left = (pt[0]<ui.selection->bbox.left+hmargin);
666 ui.selection->resizing_right = (pt[0]>ui.selection->bbox.right-hmargin);
667 ui.selection->resizing_top = (pt[1]<ui.selection->bbox.top+vmargin);
668 ui.selection->resizing_bottom = (pt[1]>ui.selection->bbox.bottom-vmargin);
670 // we're not near any edge, give up
671 if (!(ui.selection->resizing_left || ui.selection->resizing_right ||
672 ui.selection->resizing_top || ui.selection->resizing_bottom))
675 ui.cur_item_type = ITEM_RESIZESEL;
676 ui.selection->new_y1 = ui.selection->bbox.top;
677 ui.selection->new_y2 = ui.selection->bbox.bottom;
678 ui.selection->new_x1 = ui.selection->bbox.left;
679 ui.selection->new_x2 = ui.selection->bbox.right;
680 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
681 update_cursor_for_resize(pt);
688 void start_vertspace(GdkEvent *event)
695 ui.cur_item_type = ITEM_MOVESEL_VERT;
696 ui.selection = g_new(struct Selection, 1);
697 ui.selection->type = ITEM_MOVESEL_VERT;
698 ui.selection->items = NULL;
699 ui.selection->layer = ui.cur_layer;
701 get_pointer_coords(event, pt);
702 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
703 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
704 item = (struct Item *)itemlist->data;
705 if (item->bbox.top >= pt[1]) {
706 ui.selection->items = g_list_append(ui.selection->items, item);
707 if (item->bbox.bottom > ui.selection->bbox.bottom)
708 ui.selection->bbox.bottom = item->bbox.bottom;
712 ui.selection->anchor_x = ui.selection->last_x = 0;
713 ui.selection->anchor_y = ui.selection->last_y = pt[1];
714 ui.selection->orig_pageno = ui.pageno;
715 ui.selection->move_pageno = ui.pageno;
716 ui.selection->move_layer = ui.selection->layer;
717 ui.selection->move_pagedelta = 0.;
718 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
719 gnome_canvas_rect_get_type(), "width-pixels", 1,
720 "outline-color-rgba", 0x000000ff,
721 "fill-color-rgba", 0x80808040,
722 "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
726 void continue_movesel(GdkEvent *event)
728 double pt[2], dx, dy, upmargin;
732 struct Page *tmppage;
734 get_pointer_coords(event, pt);
735 if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
736 pt[1] += ui.selection->move_pagedelta;
738 // check for page jumps
739 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
740 upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
741 else upmargin = VIEW_CONTINUOUS_SKIP;
742 tmppageno = ui.selection->move_pageno;
743 tmppage = g_list_nth_data(journal.pages, tmppageno);
744 while (ui.view_continuous && (pt[1] < - upmargin)) {
745 if (tmppageno == 0) break;
747 tmppage = g_list_nth_data(journal.pages, tmppageno);
748 pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
749 ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
751 while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
752 if (tmppageno == journal.npages-1) break;
753 pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
754 ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
756 tmppage = g_list_nth_data(journal.pages, tmppageno);
759 if (tmppageno != ui.selection->move_pageno) {
760 // move to a new page !
761 ui.selection->move_pageno = tmppageno;
762 if (tmppageno == ui.selection->orig_pageno)
763 ui.selection->move_layer = ui.selection->layer;
765 ui.selection->move_layer = (struct Layer *)(g_list_last(
766 ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
767 gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
768 for (list = ui.selection->items; list!=NULL; list = list->next) {
769 item = (struct Item *)list->data;
770 if (item->canvas_item!=NULL)
771 gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
773 // avoid a refresh bug
774 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
775 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
776 gnome_canvas_item_set(ui.selection->canvas_item,
777 "x2", tmppage->width+100,
778 "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
781 // now, process things normally
783 dx = pt[0] - ui.selection->last_x;
784 dy = pt[1] - ui.selection->last_y;
785 if (hypot(dx,dy) < 1) return; // don't move subpixel
786 ui.selection->last_x = pt[0];
787 ui.selection->last_y = pt[1];
789 // move the canvas items
790 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
791 gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
793 gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
795 for (list = ui.selection->items; list != NULL; list = list->next) {
796 item = (struct Item *)list->data;
797 if (item->canvas_item != NULL)
798 gnome_canvas_item_move(item->canvas_item, dx, dy);
802 void continue_resizesel(GdkEvent *event)
806 get_pointer_coords(event, pt);
808 if (ui.selection->resizing_top) ui.selection->new_y1 = pt[1];
809 if (ui.selection->resizing_bottom) ui.selection->new_y2 = pt[1];
810 if (ui.selection->resizing_left) ui.selection->new_x1 = pt[0];
811 if (ui.selection->resizing_right) ui.selection->new_x2 = pt[0];
813 gnome_canvas_item_set(ui.selection->canvas_item,
814 "x1", ui.selection->new_x1, "x2", ui.selection->new_x2,
815 "y1", ui.selection->new_y1, "y2", ui.selection->new_y2, NULL);
818 void finalize_movesel(void)
822 if (ui.selection->items != NULL) {
824 undo->type = ITEM_MOVESEL;
825 undo->itemlist = g_list_copy(ui.selection->items);
826 undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
827 undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
828 undo->layer = ui.selection->layer;
829 undo->layer2 = ui.selection->move_layer;
830 undo->auxlist = NULL;
831 // build auxlist = pointers to Item's just before ours (for depths)
832 for (list = ui.selection->items; list!=NULL; list = list->next) {
833 link = g_list_find(ui.selection->layer->items, list->data);
834 if (link!=NULL) link = link->prev;
835 undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
837 ui.selection->layer = ui.selection->move_layer;
838 move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
839 undo->layer, undo->layer2,
840 (undo->layer == undo->layer2)?undo->auxlist:NULL);
843 if (ui.selection->move_pageno!=ui.selection->orig_pageno)
844 do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
846 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
849 ui.selection->bbox.left += undo->val_x;
850 ui.selection->bbox.right += undo->val_x;
851 ui.selection->bbox.top += undo->val_y;
852 ui.selection->bbox.bottom += undo->val_y;
853 make_dashed(ui.selection->canvas_item);
854 /* update selection box object's offset to be trivial, and its internal
855 coordinates to agree with those of the bbox; need this since resize
856 operations will modify the box by setting its coordinates directly */
857 gnome_canvas_item_affine_absolute(ui.selection->canvas_item, NULL);
858 gnome_canvas_item_set(ui.selection->canvas_item,
859 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
860 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
862 ui.cur_item_type = ITEM_NONE;
866 #define SCALING_EPSILON 0.001
868 void finalize_resizesel(void)
872 // build the affine transformation
873 double offset_x, offset_y, scaling_x, scaling_y;
874 scaling_x = (ui.selection->new_x2 - ui.selection->new_x1) /
875 (ui.selection->bbox.right - ui.selection->bbox.left);
876 scaling_y = (ui.selection->new_y2 - ui.selection->new_y1) /
877 (ui.selection->bbox.bottom - ui.selection->bbox.top);
878 // couldn't undo a resize-by-zero...
879 if (fabs(scaling_x)<SCALING_EPSILON) scaling_x = SCALING_EPSILON;
880 if (fabs(scaling_y)<SCALING_EPSILON) scaling_y = SCALING_EPSILON;
881 offset_x = ui.selection->new_x1 - ui.selection->bbox.left * scaling_x;
882 offset_y = ui.selection->new_y1 - ui.selection->bbox.top * scaling_y;
884 if (ui.selection->items != NULL) {
885 // create the undo information
887 undo->type = ITEM_RESIZESEL;
888 undo->itemlist = g_list_copy(ui.selection->items);
889 undo->auxlist = NULL;
891 undo->scaling_x = scaling_x;
892 undo->scaling_y = scaling_y;
893 undo->val_x = offset_x;
894 undo->val_y = offset_y;
896 // actually do the resize operation
897 resize_journal_items_by(ui.selection->items, scaling_x, scaling_y, offset_x, offset_y);
901 ui.selection->bbox.left = ui.selection->new_x1;
902 ui.selection->bbox.right = ui.selection->new_x2;
904 ui.selection->bbox.left = ui.selection->new_x2;
905 ui.selection->bbox.right = ui.selection->new_x1;
908 ui.selection->bbox.top = ui.selection->new_y1;
909 ui.selection->bbox.bottom = ui.selection->new_y2;
911 ui.selection->bbox.top = ui.selection->new_y2;
912 ui.selection->bbox.bottom = ui.selection->new_y1;
914 make_dashed(ui.selection->canvas_item);
916 ui.cur_item_type = ITEM_NONE;
920 void selection_delete(void)
922 struct UndoErasureData *erasure;
926 if (ui.selection == NULL) return;
928 undo->type = ITEM_ERASURE;
929 undo->layer = ui.selection->layer;
930 undo->erasurelist = NULL;
931 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
932 item = (struct Item *)itemlist->data;
933 if (item->canvas_item!=NULL)
934 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
935 erasure = g_new(struct UndoErasureData, 1);
936 erasure->item = item;
937 erasure->npos = g_list_index(ui.selection->layer->items, item);
939 erasure->replacement_items = NULL;
940 ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
941 ui.selection->layer->nitems--;
942 undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
946 /* NOTE: the erasurelist is built backwards; this guarantees that,
947 upon undo, the erasure->npos fields give the correct position
948 where each item should be reinserted as the list is traversed in
949 the forward direction */
952 void callback_clipboard_get(GtkClipboard *clipboard,
953 GtkSelectionData *selection_data,
954 guint info, gpointer user_data)
958 g_memmove(&length, user_data, sizeof(int));
959 gtk_selection_data_set(selection_data,
960 gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
963 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
968 void selection_to_clip(void)
970 int bufsz, nitems, val;
974 GtkTargetEntry target;
976 if (ui.selection == NULL) return;
977 bufsz = 2*sizeof(int) // bufsz, nitems
978 + sizeof(struct BBox); // bbox
980 for (list = ui.selection->items; list != NULL; list = list->next) {
981 item = (struct Item *)list->data;
983 if (item->type == ITEM_STROKE) {
984 bufsz+= sizeof(int) // type
985 + sizeof(struct Brush) // brush
986 + sizeof(int) // num_points
987 + 2*item->path->num_points*sizeof(double); // the points
988 if (item->brush.variable_width)
989 bufsz += (item->path->num_points-1)*sizeof(double); // the widths
991 else if (item->type == ITEM_TEXT) {
992 bufsz+= sizeof(int) // type
993 + sizeof(struct Brush) // brush
994 + 2*sizeof(double) // bbox upper-left
995 + sizeof(int) // text len
996 + strlen(item->text)+1 // text
997 + sizeof(int) // font_name len
998 + strlen(item->font_name)+1 // font_name
999 + sizeof(double); // font_size
1001 else bufsz+= sizeof(int); // type
1003 p = buf = g_malloc(bufsz);
1004 g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
1005 g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
1006 g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
1007 for (list = ui.selection->items; list != NULL; list = list->next) {
1008 item = (struct Item *)list->data;
1009 g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
1010 if (item->type == ITEM_STROKE) {
1011 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
1012 g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
1013 g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
1014 p+= 2*item->path->num_points*sizeof(double);
1015 if (item->brush.variable_width) {
1016 g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
1017 p+= (item->path->num_points-1)*sizeof(double);
1020 if (item->type == ITEM_TEXT) {
1021 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
1022 g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
1023 g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
1024 val = strlen(item->text);
1025 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
1026 g_memmove(p, item->text, val+1); p+= val+1;
1027 val = strlen(item->font_name);
1028 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
1029 g_memmove(p, item->font_name, val+1); p+= val+1;
1030 g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
1034 target.target = "_XOURNAL";
1038 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
1040 callback_clipboard_get, callback_clipboard_clear, buf);
1044 void clipboard_paste(void)
1046 GtkSelectionData *sel_data;
1048 int nitems, npts, i, len;
1050 double hoffset, voffset, cx, cy;
1054 if (ui.cur_layer == NULL) return;
1056 ui.cur_item_type = ITEM_PASTE;
1057 sel_data = gtk_clipboard_wait_for_contents(
1058 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
1059 gdk_atom_intern("_XOURNAL", FALSE));
1060 ui.cur_item_type = ITEM_NONE;
1061 if (sel_data == NULL) return; // paste failed
1065 ui.selection = g_new(struct Selection, 1);
1066 p = sel_data->data + sizeof(int);
1067 g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
1068 ui.selection->type = ITEM_SELECTRECT;
1069 ui.selection->layer = ui.cur_layer;
1070 g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
1071 ui.selection->items = NULL;
1073 // find by how much we translate the pasted selection
1074 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
1075 gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
1076 gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
1077 cx -= ui.cur_page->hoffset;
1078 cy -= ui.cur_page->voffset;
1079 if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
1080 cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1081 if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
1082 cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1083 if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
1084 cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1085 if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
1086 cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1087 hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
1088 voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
1089 ui.selection->bbox.left += hoffset;
1090 ui.selection->bbox.right += hoffset;
1091 ui.selection->bbox.top += voffset;
1092 ui.selection->bbox.bottom += voffset;
1094 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1095 gnome_canvas_rect_get_type(), "width-pixels", 1,
1096 "outline-color-rgba", 0x000000ff,
1097 "fill-color-rgba", 0x80808040,
1098 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
1099 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
1100 make_dashed(ui.selection->canvas_item);
1102 while (nitems-- > 0) {
1103 item = g_new(struct Item, 1);
1104 ui.selection->items = g_list_append(ui.selection->items, item);
1105 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1106 ui.cur_layer->nitems++;
1107 g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
1108 if (item->type == ITEM_STROKE) {
1109 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1110 g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
1111 item->path = gnome_canvas_points_new(npts);
1113 for (i=0; i<npts; i++) {
1114 item->path->coords[2*i] = pf[2*i] + hoffset;
1115 item->path->coords[2*i+1] = pf[2*i+1] + voffset;
1117 p+= 2*item->path->num_points*sizeof(double);
1118 if (item->brush.variable_width) {
1119 item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
1120 p+= (item->path->num_points-1)*sizeof(double);
1122 else item->widths = NULL;
1123 update_item_bbox(item);
1124 make_canvas_item_one(ui.cur_layer->group, item);
1126 if (item->type == ITEM_TEXT) {
1127 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1128 g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
1129 g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
1130 item->bbox.left += hoffset;
1131 item->bbox.top += voffset;
1132 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1133 item->text = g_malloc(len+1);
1134 g_memmove(item->text, p, len+1); p+= len+1;
1135 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1136 item->font_name = g_malloc(len+1);
1137 g_memmove(item->font_name, p, len+1); p+= len+1;
1138 g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
1139 make_canvas_item_one(ui.cur_layer->group, item);
1144 undo->type = ITEM_PASTE;
1145 undo->layer = ui.cur_layer;
1146 undo->itemlist = g_list_copy(ui.selection->items);
1148 gtk_selection_data_free(sel_data);
1149 update_copy_paste_enabled();
1150 update_color_menu();
1151 update_thickness_buttons();
1152 update_color_buttons();
1153 update_font_button();
1154 update_cursor(); // FIXME: can't know if pointer is within selection!
1157 // modify the color or thickness of pen strokes in a selection
1159 void recolor_selection(int color_no, guint color_rgba)
1163 struct Brush *brush;
1164 GnomeCanvasGroup *group;
1166 if (ui.selection == NULL) return;
1168 undo->type = ITEM_REPAINTSEL;
1169 undo->itemlist = NULL;
1170 undo->auxlist = NULL;
1171 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1172 item = (struct Item *)itemlist->data;
1173 if (item->type != ITEM_STROKE && item->type != ITEM_TEXT) continue;
1174 if (item->type == ITEM_STROKE && item->brush.tool_type!=TOOL_PEN) continue;
1175 // store info for undo
1176 undo->itemlist = g_list_append(undo->itemlist, item);
1177 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1178 g_memmove(brush, &(item->brush), sizeof(struct Brush));
1179 undo->auxlist = g_list_append(undo->auxlist, brush);
1180 // repaint the stroke
1181 item->brush.color_no = color_no;
1182 item->brush.color_rgba = color_rgba | 0xff; // no alpha
1183 if (item->canvas_item!=NULL) {
1184 if (!item->brush.variable_width)
1185 gnome_canvas_item_set(item->canvas_item,
1186 "fill-color-rgba", item->brush.color_rgba, NULL);
1188 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1189 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1190 make_canvas_item_one(group, item);
1196 void rethicken_selection(int val)
1200 struct Brush *brush;
1201 GnomeCanvasGroup *group;
1203 if (ui.selection == NULL) return;
1205 undo->type = ITEM_REPAINTSEL;
1206 undo->itemlist = NULL;
1207 undo->auxlist = NULL;
1208 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1209 item = (struct Item *)itemlist->data;
1210 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
1211 // store info for undo
1212 undo->itemlist = g_list_append(undo->itemlist, item);
1213 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1214 g_memmove(brush, &(item->brush), sizeof(struct Brush));
1215 undo->auxlist = g_list_append(undo->auxlist, brush);
1216 // repaint the stroke
1217 item->brush.thickness_no = val;
1218 item->brush.thickness = predef_thickness[TOOL_PEN][val];
1219 if (item->canvas_item!=NULL) {
1220 if (!item->brush.variable_width)
1221 gnome_canvas_item_set(item->canvas_item,
1222 "width-units", item->brush.thickness, NULL);
1224 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1225 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1226 item->brush.variable_width = FALSE;
1227 make_canvas_item_one(group, item);
1233 gboolean do_hand_scrollto(gpointer data)
1235 ui.hand_scrollto_pending = FALSE;
1236 gnome_canvas_scroll_to(canvas, ui.hand_scrollto_cx, ui.hand_scrollto_cy);
1240 void do_hand(GdkEvent *event)
1245 get_pointer_coords(event, pt);
1246 pt[0] += ui.cur_page->hoffset;
1247 pt[1] += ui.cur_page->voffset;
1248 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1249 ui.hand_scrollto_cx = cx - (pt[0]-ui.hand_refpt[0])*ui.zoom;
1250 ui.hand_scrollto_cy = cy - (pt[1]-ui.hand_refpt[1])*ui.zoom;
1251 if (!ui.hand_scrollto_pending) g_idle_add(do_hand_scrollto, NULL);
1252 ui.hand_scrollto_pending = TRUE;
1255 /************ TEXT FUNCTIONS **************/
1257 // to make it easier to copy/paste at end of text box
1258 #define WIDGET_RIGHT_MARGIN 10
1260 void resize_textview(gpointer *toplevel, gpointer *data)
1265 /* when the text changes, resize the GtkTextView accordingly */
1266 if (ui.cur_item_type!=ITEM_TEXT) return;
1267 w = GTK_TEXT_VIEW(ui.cur_item->widget);
1268 width = w->width + WIDGET_RIGHT_MARGIN;
1270 gnome_canvas_item_set(ui.cur_item->canvas_item,
1271 "size-pixels", TRUE,
1272 "width", (gdouble)width, "height", (gdouble)height, NULL);
1273 ui.cur_item->bbox.right = ui.cur_item->bbox.left + width/ui.zoom;
1274 ui.cur_item->bbox.bottom = ui.cur_item->bbox.top + height/ui.zoom;
1277 void start_text(GdkEvent *event, struct Item *item)
1280 GtkTextBuffer *buffer;
1281 GnomeCanvasItem *canvas_item;
1282 PangoFontDescription *font_desc;
1285 get_pointer_coords(event, pt);
1287 ui.cur_item_type = ITEM_TEXT;
1290 item = g_new(struct Item, 1);
1292 item->canvas_item = NULL;
1293 item->bbox.left = pt[0];
1294 item->bbox.top = pt[1];
1295 item->bbox.right = ui.cur_page->width;
1296 item->bbox.bottom = pt[1]+100.;
1297 item->font_name = g_strdup(ui.font_name);
1298 item->font_size = ui.font_size;
1299 g_memmove(&(item->brush), ui.cur_brush, sizeof(struct Brush));
1300 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1301 ui.cur_layer->nitems++;
1304 item->type = ITEM_TEMP_TEXT;
1307 font_desc = pango_font_description_from_string(item->font_name);
1308 pango_font_description_set_absolute_size(font_desc,
1309 item->font_size*ui.zoom*PANGO_SCALE);
1310 item->widget = gtk_text_view_new();
1311 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(item->widget));
1312 if (item->text!=NULL)
1313 gtk_text_buffer_set_text(buffer, item->text, -1);
1314 gtk_widget_modify_font(item->widget, font_desc);
1315 rgb_to_gdkcolor(item->brush.color_rgba, &color);
1316 gtk_widget_modify_text(item->widget, GTK_STATE_NORMAL, &color);
1317 pango_font_description_free(font_desc);
1319 canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1320 gnome_canvas_widget_get_type(),
1321 "x", item->bbox.left, "y", item->bbox.top,
1322 "width", item->bbox.right-item->bbox.left,
1323 "height", item->bbox.bottom-item->bbox.top,
1324 "widget", item->widget, NULL);
1325 // TODO: width/height?
1326 if (item->canvas_item!=NULL) {
1327 lower_canvas_item_to(ui.cur_layer->group, canvas_item, item->canvas_item);
1328 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1330 item->canvas_item = canvas_item;
1332 gtk_widget_show(item->widget);
1333 ui.resize_signal_handler =
1334 g_signal_connect((gpointer) winMain, "check_resize",
1335 G_CALLBACK(resize_textview), NULL);
1336 update_font_button();
1337 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), FALSE);
1338 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), FALSE);
1339 gtk_widget_grab_focus(item->widget);
1344 GtkTextBuffer *buffer;
1345 GtkTextIter start, end;
1347 struct UndoErasureData *erasure;
1348 GnomeCanvasItem *tmpitem;
1350 if (ui.cur_item_type!=ITEM_TEXT) return; // nothing for us to do!
1352 // finalize the text that's been edited...
1353 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.cur_item->widget));
1354 gtk_text_buffer_get_bounds(buffer, &start, &end);
1355 ui.cur_item->type = ITEM_TEXT;
1356 new_text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
1357 ui.cur_item_type = ITEM_NONE;
1358 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), TRUE);
1359 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), TRUE);
1361 if (strlen(new_text)==0) { // erase object and cancel
1363 g_signal_handler_disconnect(winMain, ui.resize_signal_handler);
1364 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
1365 ui.cur_item->canvas_item = NULL;
1366 if (ui.cur_item->text == NULL) // nothing happened
1367 g_free(ui.cur_item->font_name);
1368 else { // treat this as an erasure
1370 undo->type = ITEM_ERASURE;
1371 undo->layer = ui.cur_layer;
1372 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
1373 erasure->item = ui.cur_item;
1374 erasure->npos = g_list_index(ui.cur_layer->items, ui.cur_item);
1376 erasure->replacement_items = NULL;
1377 undo->erasurelist = g_list_append(NULL, erasure);
1379 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, ui.cur_item);
1380 ui.cur_layer->nitems--;
1386 if (ui.cur_item->text==NULL || strcmp(ui.cur_item->text, new_text)) {
1388 if (ui.cur_item->text == NULL) undo->type = ITEM_TEXT;
1389 else undo->type = ITEM_TEXT_EDIT;
1390 undo->layer = ui.cur_layer;
1391 undo->item = ui.cur_item;
1392 undo->str = ui.cur_item->text;
1394 else g_free(ui.cur_item->text);
1396 ui.cur_item->text = new_text;
1397 ui.cur_item->widget = NULL;
1398 // replace the canvas item
1399 tmpitem = ui.cur_item->canvas_item;
1400 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
1401 update_item_bbox(ui.cur_item);
1402 lower_canvas_item_to(ui.cur_layer->group, ui.cur_item->canvas_item, tmpitem);
1403 gtk_object_destroy(GTK_OBJECT(tmpitem));
1406 /* update the items in the canvas so they're of the right font size */
1408 void update_text_item_displayfont(struct Item *item)
1410 PangoFontDescription *font_desc;
1412 if (item->type != ITEM_TEXT && item->type != ITEM_TEMP_TEXT) return;
1413 if (item->canvas_item==NULL) return;
1414 font_desc = pango_font_description_from_string(item->font_name);
1415 pango_font_description_set_absolute_size(font_desc,
1416 item->font_size*ui.zoom*PANGO_SCALE);
1417 if (item->type == ITEM_TEMP_TEXT)
1418 gtk_widget_modify_font(item->widget, font_desc);
1420 gnome_canvas_item_set(item->canvas_item, "font-desc", font_desc, NULL);
1421 update_item_bbox(item);
1423 pango_font_description_free(font_desc);
1426 void rescale_text_items(void)
1428 GList *pagelist, *layerlist, *itemlist;
1430 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next)
1431 for (layerlist = ((struct Page *)pagelist->data)->layers; layerlist!=NULL; layerlist = layerlist->next)
1432 for (itemlist = ((struct Layer *)layerlist->data)->items; itemlist!=NULL; itemlist = itemlist->next)
1433 update_text_item_displayfont((struct Item *)itemlist->data);
1436 struct Item *click_is_in_text(struct Layer *layer, double x, double y)
1439 struct Item *item, *val;
1442 for (itemlist = layer->items; itemlist!=NULL; itemlist = itemlist->next) {
1443 item = (struct Item *)itemlist->data;
1444 if (item->type != ITEM_TEXT) continue;
1445 if (x<item->bbox.left || x>item->bbox.right) continue;
1446 if (y<item->bbox.top || y>item->bbox.bottom) continue;
1452 void refont_text_item(struct Item *item, gchar *font_name, double font_size)
1454 if (!strcmp(font_name, item->font_name) && font_size==item->font_size) return;
1455 if (item->text!=NULL) {
1457 undo->type = ITEM_TEXT_ATTRIB;
1459 undo->str = item->font_name;
1460 undo->val_x = item->font_size;
1461 undo->brush = (struct Brush *)g_memdup(&(item->brush), sizeof(struct Brush));
1463 else g_free(item->font_name);
1464 item->font_name = g_strdup(font_name);
1465 if (font_size>0.) item->font_size = font_size;
1466 update_text_item_displayfont(item);
1469 void process_font_sel(gchar *str)
1477 p = strrchr(str, ' ');
1479 size = g_strtod(p+1, &q);
1480 if (*q!=0 || size<1.) size=0.;
1484 g_free(ui.font_name);
1486 if (size>0.) ui.font_size = size;
1488 // if there's a current text item, re-font it
1489 if (ui.cur_item_type == ITEM_TEXT) {
1490 refont_text_item(ui.cur_item, str, size);
1491 undo_cont = (ui.cur_item->text!=NULL);
1493 // if there's a current selection, re-font it
1494 if (ui.selection!=NULL)
1495 for (list=ui.selection->items; list!=NULL; list=list->next) {
1496 it = (struct Item *)list->data;
1497 if (it->type == ITEM_TEXT) {
1498 if (undo_cont) undo->multiop |= MULTIOP_CONT_REDO;
1499 refont_text_item(it, str, size);
1500 if (undo_cont) undo->multiop |= MULTIOP_CONT_UNDO;
1504 update_font_button();