2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) any later version.
7 * This software is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <libgnomecanvas/libgnomecanvas.h>
25 #include <libart_lgpl/art_vpath_dash.h>
28 #include "xo-callbacks.h"
29 #include "xo-interface.h"
30 #include "xo-support.h"
34 /***** Win32 fix for gdk_cursor_new_from_pixmap() by Dirk Gerrits ****/
37 gboolean colors_too_similar(const GdkColor *colora, const GdkColor *colorb)
39 return (abs(colora->red - colorb->red) < 256 &&
40 abs(colora->green - colorb->green) < 256 &&
41 abs(colora->blue - colorb->blue) < 256);
44 /* gdk_cursor_new_from_pixmap is broken on Windows.
45 this is a workaround using gdk_cursor_new_from_pixbuf. */
46 GdkCursor* fixed_gdk_cursor_new_from_pixmap(GdkPixmap *source, GdkPixmap *mask,
47 const GdkColor *fg, const GdkColor *bg,
50 GdkPixmap *rgb_pixmap;
52 GdkPixbuf *rgb_pixbuf, *rgba_pixbuf;
56 /* HACK! It seems impossible to work with RGBA pixmaps directly in
57 GDK-Win32. Instead we pick some third color, different from fg
58 and bg, and use that as the 'transparent color'. We do this using
59 colors_too_similar (see above) because two colors could be
60 unequal in GdkColor's 16-bit/sample, but equal in GdkPixbuf's
62 GdkColor candidates[3] = {{0,65535,0,0}, {0,0,65535,0}, {0,0,0,65535}};
63 GdkColor *trans = &candidates[0];
64 if (colors_too_similar(trans, fg) || colors_too_similar(trans, bg)) {
65 trans = &candidates[1];
66 if (colors_too_similar(trans, fg) || colors_too_similar(trans, bg)) {
67 trans = &candidates[2];
69 } /* trans is now guaranteed to be unique from fg and bg */
71 /* create an empty pixmap to hold the cursor image */
72 gdk_drawable_get_size(source, &width, &height);
73 rgb_pixmap = gdk_pixmap_new(NULL, width, height, 24);
75 /* blit the bitmaps defining the cursor onto a transparent background */
76 gc = gdk_gc_new(rgb_pixmap);
77 gdk_gc_set_fill(gc, GDK_SOLID);
78 gdk_gc_set_rgb_fg_color(gc, trans);
79 gdk_draw_rectangle(rgb_pixmap, gc, TRUE, 0, 0, width, height);
80 gdk_gc_set_fill(gc, GDK_OPAQUE_STIPPLED);
81 gdk_gc_set_stipple(gc, source);
82 gdk_gc_set_clip_mask(gc, mask);
83 gdk_gc_set_rgb_fg_color(gc, fg);
84 gdk_gc_set_rgb_bg_color(gc, bg);
85 gdk_draw_rectangle(rgb_pixmap, gc, TRUE, 0, 0, width, height);
88 /* create a cursor out of the created pixmap */
89 rgb_pixbuf = gdk_pixbuf_get_from_drawable(
90 NULL, rgb_pixmap, gdk_colormap_get_system(), 0, 0, 0, 0, width, height);
91 gdk_pixmap_unref(rgb_pixmap);
92 rgba_pixbuf = gdk_pixbuf_add_alpha(
93 rgb_pixbuf, TRUE, trans->red, trans->green, trans->blue);
94 gdk_pixbuf_unref(rgb_pixbuf);
95 cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(), rgba_pixbuf, x, y);
96 gdk_pixbuf_unref(rgba_pixbuf);
100 #define gdk_cursor_new_from_pixmap fixed_gdk_cursor_new_from_pixmap
104 /************** drawing nice cursors *********/
106 static char cursor_pen_bits[] = {
107 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
108 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
109 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
111 static char cursor_eraser_bits[] = {
112 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x08, 0x08, 0x08, 0x08,
113 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x0f,
114 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
116 static char cursor_eraser_mask[] = {
117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f,
118 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f,
119 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
121 void set_cursor_busy(gboolean busy)
126 cursor = gdk_cursor_new(GDK_WATCH);
127 gdk_window_set_cursor(GTK_WIDGET(winMain)->window, cursor);
128 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, cursor);
129 gdk_cursor_unref(cursor);
132 gdk_window_set_cursor(GTK_WIDGET(winMain)->window, NULL);
135 gdk_display_sync(gdk_display_get_default());
138 void update_cursor(void)
140 GdkPixmap *source, *mask;
141 GdkColor fg = {0, 0, 0, 0}, bg = {0, 65535, 65535, 65535};
143 ui.is_sel_cursor = FALSE;
144 if (GTK_WIDGET(canvas)->window == NULL) return;
146 if (ui.cursor!=NULL) {
147 gdk_cursor_unref(ui.cursor);
150 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
151 ui.cursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
152 else if (ui.cur_item_type == ITEM_MOVESEL)
153 ui.cursor = gdk_cursor_new(GDK_FLEUR);
154 else if (ui.toolno[ui.cur_mapping] == TOOL_PEN) {
155 fg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
156 fg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
157 fg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
158 source = gdk_bitmap_create_from_data(NULL, cursor_pen_bits, 16, 16);
159 ui.cursor = gdk_cursor_new_from_pixmap(source, source, &fg, &bg, 7, 7);
160 gdk_bitmap_unref(source);
162 else if (ui.toolno[ui.cur_mapping] == TOOL_ERASER) {
163 source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
164 mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
165 ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
166 gdk_bitmap_unref(source);
167 gdk_bitmap_unref(mask);
169 else if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
170 source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
171 mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
172 bg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
173 bg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
174 bg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
175 ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
176 gdk_bitmap_unref(source);
177 gdk_bitmap_unref(mask);
179 else if (ui.cur_item_type == ITEM_SELECTRECT) {
180 ui.cursor = gdk_cursor_new(GDK_TCROSS);
182 else if (ui.toolno[ui.cur_mapping] == TOOL_HAND) {
183 ui.cursor = gdk_cursor_new(GDK_HAND1);
185 else if (ui.toolno[ui.cur_mapping] == TOOL_TEXT) {
186 ui.cursor = gdk_cursor_new(GDK_XTERM);
189 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
192 /* adjust the cursor shape if it hovers near a selection box */
194 void update_cursor_for_resize(double *pt)
196 gboolean in_range_x, in_range_y;
197 gboolean can_resize_left, can_resize_right, can_resize_bottom, can_resize_top;
198 gdouble resize_margin;
199 GdkCursorType newcursor;
201 // if we're not even close to the box in some direction, return immediately
202 resize_margin = RESIZE_MARGIN / ui.zoom;
203 if (pt[0]<ui.selection->bbox.left-resize_margin || pt[0]>ui.selection->bbox.right+resize_margin
204 || pt[1]<ui.selection->bbox.top-resize_margin || pt[1]>ui.selection->bbox.bottom+resize_margin)
206 if (ui.is_sel_cursor) update_cursor();
210 ui.is_sel_cursor = TRUE;
211 can_resize_left = (pt[0] < ui.selection->bbox.left+resize_margin);
212 can_resize_right = (pt[0] > ui.selection->bbox.right-resize_margin);
213 can_resize_top = (pt[1] < ui.selection->bbox.top+resize_margin);
214 can_resize_bottom = (pt[1] > ui.selection->bbox.bottom-resize_margin);
216 if (can_resize_left) {
217 if (can_resize_top) newcursor = GDK_TOP_LEFT_CORNER;
218 else if (can_resize_bottom) newcursor = GDK_BOTTOM_LEFT_CORNER;
219 else newcursor = GDK_LEFT_SIDE;
221 else if (can_resize_right) {
222 if (can_resize_top) newcursor = GDK_TOP_RIGHT_CORNER;
223 else if (can_resize_bottom) newcursor = GDK_BOTTOM_RIGHT_CORNER;
224 else newcursor = GDK_RIGHT_SIDE;
226 else if (can_resize_top) newcursor = GDK_TOP_SIDE;
227 else if (can_resize_bottom) newcursor = GDK_BOTTOM_SIDE;
228 else newcursor = GDK_FLEUR;
230 if (ui.cursor!=NULL && ui.cursor->type == newcursor) return;
231 if (ui.cursor!=NULL) gdk_cursor_unref(ui.cursor);
232 ui.cursor = gdk_cursor_new(newcursor);
233 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
236 /************** painting strokes *************/
238 #define SUBDIVIDE_MAXDIST 5.0
240 void subdivide_cur_path(null)
244 double x0, y0, x1, y1;
246 for (n=0, p=ui.cur_path.coords; n<ui.cur_path.num_points-1; n++, p+=2) {
247 pieces = (int)floor(hypot(p[2]-p[0], p[3]-p[1])/SUBDIVIDE_MAXDIST);
249 x0 = p[0]; y0 = p[1];
250 x1 = p[2]; y1 = p[3];
251 realloc_cur_path(ui.cur_path.num_points+pieces-1);
252 g_memmove(ui.cur_path.coords+2*(n+pieces), ui.cur_path.coords+2*(n+1),
253 2*(ui.cur_path.num_points-n-1)*sizeof(double));
254 p = ui.cur_path.coords+2*n;
255 ui.cur_path.num_points += pieces-1;
257 for (k=1; k<pieces; k++) {
259 p[0] = x0 + k*(x1-x0)/pieces;
260 p[1] = y0 + k*(y1-y0)/pieces;
266 void create_new_stroke(GdkEvent *event)
268 ui.cur_item_type = ITEM_STROKE;
269 ui.cur_item = g_new(struct Item, 1);
270 ui.cur_item->type = ITEM_STROKE;
271 g_memmove(&(ui.cur_item->brush), ui.cur_brush, sizeof(struct Brush));
272 ui.cur_item->path = &ui.cur_path;
274 ui.cur_path.num_points = 1;
275 get_pointer_coords(event, ui.cur_path.coords);
277 if (ui.cur_brush->ruler) {
278 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
279 gnome_canvas_line_get_type(),
280 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
281 "fill-color-rgba", ui.cur_item->brush.color_rgba,
282 "width-units", ui.cur_item->brush.thickness, NULL);
283 ui.cur_item->brush.variable_width = FALSE;
285 ui.cur_item->canvas_item = gnome_canvas_item_new(
286 ui.cur_layer->group, gnome_canvas_group_get_type(), NULL);
289 void continue_stroke(GdkEvent *event)
291 GnomeCanvasPoints seg;
292 double *pt, current_width;
294 if (ui.cur_brush->ruler) {
295 pt = ui.cur_path.coords;
297 realloc_cur_path(ui.cur_path.num_points+1);
298 pt = ui.cur_path.coords + 2*(ui.cur_path.num_points-1);
301 get_pointer_coords(event, pt+2);
303 if (ui.cur_item->brush.variable_width) {
304 realloc_cur_widths(ui.cur_path.num_points);
305 current_width = ui.cur_item->brush.thickness*get_pressure_multiplier(event);
306 ui.cur_widths[ui.cur_path.num_points-1] = current_width;
308 else current_width = ui.cur_item->brush.thickness;
310 if (ui.cur_brush->ruler)
311 ui.cur_path.num_points = 2;
313 if (hypot(pt[0]-pt[2], pt[1]-pt[3]) < PIXEL_MOTION_THRESHOLD/ui.zoom)
314 return; // not a meaningful motion
315 ui.cur_path.num_points++;
322 /* note: we're using a piece of the cur_path array. This is ok because
323 upon creation the line just copies the contents of the GnomeCanvasPoints
324 into an internal structure */
326 if (ui.cur_brush->ruler)
327 gnome_canvas_item_set(ui.cur_item->canvas_item, "points", &seg, NULL);
329 gnome_canvas_item_new((GnomeCanvasGroup *)ui.cur_item->canvas_item,
330 gnome_canvas_line_get_type(), "points", &seg,
331 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
332 "fill-color-rgba", ui.cur_item->brush.color_rgba,
333 "width-units", current_width, NULL);
336 void finalize_stroke(void)
338 if (ui.cur_path.num_points == 1) { // GnomeCanvas doesn't like num_points=1
339 ui.cur_path.coords[2] = ui.cur_path.coords[0]+0.1;
340 ui.cur_path.coords[3] = ui.cur_path.coords[1];
341 ui.cur_path.num_points = 2;
342 ui.cur_item->brush.variable_width = FALSE;
345 if (!ui.cur_item->brush.variable_width)
346 subdivide_cur_path(); // split the segment so eraser will work
348 ui.cur_item->path = gnome_canvas_points_new(ui.cur_path.num_points);
349 g_memmove(ui.cur_item->path->coords, ui.cur_path.coords,
350 2*ui.cur_path.num_points*sizeof(double));
351 if (ui.cur_item->brush.variable_width)
352 ui.cur_item->widths = (gdouble *)g_memdup(ui.cur_widths,
353 (ui.cur_path.num_points-1)*sizeof(gdouble));
354 else ui.cur_item->widths = NULL;
355 update_item_bbox(ui.cur_item);
356 ui.cur_path.num_points = 0;
358 if (!ui.cur_item->brush.variable_width) {
359 // destroy the entire group of temporary line segments
360 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
361 // make a new line item to replace it
362 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
365 // add undo information
367 undo->type = ITEM_STROKE;
368 undo->item = ui.cur_item;
369 undo->layer = ui.cur_layer;
371 // store the item on top of the layer stack
372 ui.cur_layer->items = g_list_append(ui.cur_layer->items, ui.cur_item);
373 ui.cur_layer->nitems++;
375 ui.cur_item_type = ITEM_NONE;
378 /************** eraser tool *************/
380 void erase_stroke_portions(struct Item *item, double x, double y, double radius,
381 gboolean whole_strokes, struct UndoErasureData *erasure)
385 struct Item *newhead, *newtail;
386 gboolean need_recalc = FALSE;
388 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
389 if (hypot(pt[0]-x, pt[1]-y) <= radius) { // found an intersection
390 // FIXME: need to test if line SEGMENT hits the circle
391 // hide the canvas item, and create erasure data if needed
392 if (erasure == NULL) {
393 item->type = ITEM_TEMP_STROKE;
394 gnome_canvas_item_hide(item->canvas_item);
395 /* we'll use this hidden item as an insertion point later */
396 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
397 item->erasure = erasure;
398 erasure->item = item;
399 erasure->npos = g_list_index(ui.cur_layer->items, item);
401 erasure->replacement_items = NULL;
404 newhead = newtail = NULL;
405 if (!whole_strokes) {
407 newhead = (struct Item *)g_malloc(sizeof(struct Item));
408 newhead->type = ITEM_STROKE;
409 g_memmove(&newhead->brush, &item->brush, sizeof(struct Brush));
410 newhead->path = gnome_canvas_points_new(i);
411 g_memmove(newhead->path->coords, item->path->coords, 2*i*sizeof(double));
412 if (newhead->brush.variable_width)
413 newhead->widths = (gdouble *)g_memdup(item->widths, (i-1)*sizeof(gdouble));
414 else newhead->widths = NULL;
416 while (++i < item->path->num_points) {
418 if (hypot(pt[0]-x, pt[1]-y) > radius) break;
420 if (i<item->path->num_points-1) {
421 newtail = (struct Item *)g_malloc(sizeof(struct Item));
422 newtail->type = ITEM_STROKE;
423 g_memmove(&newtail->brush, &item->brush, sizeof(struct Brush));
424 newtail->path = gnome_canvas_points_new(item->path->num_points-i);
425 g_memmove(newtail->path->coords, item->path->coords+2*i,
426 2*(item->path->num_points-i)*sizeof(double));
427 if (newtail->brush.variable_width)
428 newtail->widths = (gdouble *)g_memdup(item->widths+i,
429 (item->path->num_points-i-1)*sizeof(gdouble));
430 else newtail->widths = NULL;
431 newtail->canvas_item = NULL;
434 if (item->type == ITEM_STROKE) {
435 // it's inside an erasure list - we destroy it
436 gnome_canvas_points_free(item->path);
437 if (item->brush.variable_width) g_free(item->widths);
438 if (item->canvas_item != NULL)
439 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
441 erasure->replacement_items = g_list_remove(erasure->replacement_items, item);
445 if (newhead != NULL) {
446 update_item_bbox(newhead);
447 make_canvas_item_one(ui.cur_layer->group, newhead);
448 lower_canvas_item_to(ui.cur_layer->group,
449 newhead->canvas_item, erasure->item->canvas_item);
450 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newhead);
452 // prepending ensures it won't get processed twice
454 // recurse into the new tail
455 need_recalc = (newtail!=NULL);
456 if (newtail == NULL) break;
458 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newtail);
460 i=0; pt=item->path->coords;
463 // add the tail if needed
464 if (!need_recalc) return;
465 update_item_bbox(item);
466 make_canvas_item_one(ui.cur_layer->group, item);
467 lower_canvas_item_to(ui.cur_layer->group, item->canvas_item,
468 erasure->item->canvas_item);
472 void do_eraser(GdkEvent *event, double radius, gboolean whole_strokes)
474 struct Item *item, *repl;
475 GList *itemlist, *repllist;
477 struct BBox eraserbox;
479 get_pointer_coords(event, pos);
480 eraserbox.left = pos[0]-radius;
481 eraserbox.right = pos[0]+radius;
482 eraserbox.top = pos[1]-radius;
483 eraserbox.bottom = pos[1]+radius;
484 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
485 item = (struct Item *)itemlist->data;
486 if (item->type == ITEM_STROKE) {
487 if (!have_intersect(&(item->bbox), &eraserbox)) continue;
488 erase_stroke_portions(item, pos[0], pos[1], radius, whole_strokes, NULL);
489 } else if (item->type == ITEM_TEMP_STROKE) {
490 repllist = item->erasure->replacement_items;
491 while (repllist!=NULL) {
492 repl = (struct Item *)repllist->data;
493 // we may delete the item soon! so advance now in the list
494 repllist = repllist->next;
495 if (have_intersect(&(repl->bbox), &eraserbox))
496 erase_stroke_portions(repl, pos[0], pos[1], radius, whole_strokes, item->erasure);
502 void finalize_erasure(void)
504 GList *itemlist, *partlist;
508 undo->type = ITEM_ERASURE;
509 undo->layer = ui.cur_layer;
510 undo->erasurelist = NULL;
512 itemlist = ui.cur_layer->items;
513 while (itemlist!=NULL) {
514 item = (struct Item *)itemlist->data;
515 itemlist = itemlist->next;
516 if (item->type != ITEM_TEMP_STROKE) continue;
517 item->type = ITEM_STROKE;
518 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, item);
519 // the item has an invisible canvas item, which used to act as anchor
520 if (item->canvas_item!=NULL) {
521 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
522 item->canvas_item = NULL;
524 undo->erasurelist = g_list_append(undo->erasurelist, item->erasure);
525 // add the new strokes into the current layer
526 for (partlist = item->erasure->replacement_items; partlist!=NULL; partlist = partlist->next)
527 ui.cur_layer->items = g_list_insert_before(
528 ui.cur_layer->items, itemlist, partlist->data);
529 ui.cur_layer->nitems += item->erasure->nrepl-1;
533 ui.cur_item_type = ITEM_NONE;
535 /* NOTE: the list of erasures goes in the depth order of the layer;
536 this guarantees that, upon undo, the erasure->npos fields give the
537 correct position where each item should be reinserted as the list
538 is traversed in the forward direction */
541 /************ selection tools ***********/
543 void make_dashed(GnomeCanvasItem *item)
551 dashlen[0] = dashlen[1] = 6.0;
552 gnome_canvas_item_set(item, "dash", &dash, NULL);
556 void start_selectrect(GdkEvent *event)
561 ui.cur_item_type = ITEM_SELECTRECT;
562 ui.selection = g_new(struct Selection, 1);
563 ui.selection->type = ITEM_SELECTRECT;
564 ui.selection->items = NULL;
565 ui.selection->layer = ui.cur_layer;
567 get_pointer_coords(event, pt);
568 ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
569 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
571 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
572 gnome_canvas_rect_get_type(), "width-pixels", 1,
573 "outline-color-rgba", 0x000000ff,
574 "fill-color-rgba", 0x80808040,
575 "x1", pt[0], "x2", pt[0], "y1", pt[1], "y2", pt[1], NULL);
579 void finalize_selectrect(void)
581 double x1, x2, y1, y2;
586 ui.cur_item_type = ITEM_NONE;
588 if (ui.selection->bbox.left > ui.selection->bbox.right) {
589 x1 = ui.selection->bbox.right; x2 = ui.selection->bbox.left;
590 ui.selection->bbox.left = x1; ui.selection->bbox.right = x2;
592 x1 = ui.selection->bbox.left; x2 = ui.selection->bbox.right;
595 if (ui.selection->bbox.top > ui.selection->bbox.bottom) {
596 y1 = ui.selection->bbox.bottom; y2 = ui.selection->bbox.top;
597 ui.selection->bbox.top = y1; ui.selection->bbox.bottom = y2;
599 y1 = ui.selection->bbox.top; y2 = ui.selection->bbox.bottom;
602 for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
603 item = (struct Item *)itemlist->data;
604 if (item->bbox.left >= x1 && item->bbox.right <= x2 &&
605 item->bbox.top >= y1 && item->bbox.bottom <= y2) {
606 ui.selection->items = g_list_append(ui.selection->items, item);
610 if (ui.selection->items == NULL) {
611 // if we clicked inside a text zone ?
612 item = click_is_in_text(ui.selection->layer, x1, y1);
613 if (item!=NULL && item==click_is_in_text(ui.selection->layer, x2, y2)) {
614 ui.selection->items = g_list_append(ui.selection->items, item);
615 g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox));
616 gnome_canvas_item_set(ui.selection->canvas_item,
617 "x1", item->bbox.left, "x2", item->bbox.right,
618 "y1", item->bbox.top, "y2", item->bbox.bottom, NULL);
622 if (ui.selection->items == NULL) reset_selection();
623 else make_dashed(ui.selection->canvas_item);
625 update_copy_paste_enabled();
626 update_font_button();
629 gboolean start_movesel(GdkEvent *event)
633 if (ui.selection==NULL) return FALSE;
634 if (ui.cur_layer != ui.selection->layer) return FALSE;
636 get_pointer_coords(event, pt);
637 if (ui.selection->type == ITEM_SELECTRECT) {
638 if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
639 pt[1]<ui.selection->bbox.top || pt[1]>ui.selection->bbox.bottom)
641 ui.cur_item_type = ITEM_MOVESEL;
642 ui.selection->anchor_x = ui.selection->last_x = pt[0];
643 ui.selection->anchor_y = ui.selection->last_y = pt[1];
644 ui.selection->orig_pageno = ui.pageno;
645 ui.selection->move_pageno = ui.pageno;
646 ui.selection->move_layer = ui.selection->layer;
647 ui.selection->move_pagedelta = 0.;
648 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
655 gboolean start_resizesel(GdkEvent *event)
657 double pt[2], resize_margin, hmargin, vmargin;
659 if (ui.selection==NULL) return FALSE;
660 if (ui.cur_layer != ui.selection->layer) return FALSE;
662 get_pointer_coords(event, pt);
664 if (ui.selection->type == ITEM_SELECTRECT) {
665 resize_margin = RESIZE_MARGIN/ui.zoom;
666 hmargin = (ui.selection->bbox.right-ui.selection->bbox.left)*0.3;
667 if (hmargin>resize_margin) hmargin = resize_margin;
668 vmargin = (ui.selection->bbox.bottom-ui.selection->bbox.top)*0.3;
669 if (vmargin>resize_margin) vmargin = resize_margin;
671 // make sure the click is within a box slightly bigger than the selection rectangle
672 if (pt[0]<ui.selection->bbox.left-resize_margin ||
673 pt[0]>ui.selection->bbox.right+resize_margin ||
674 pt[1]<ui.selection->bbox.top-resize_margin ||
675 pt[1]>ui.selection->bbox.bottom+resize_margin)
678 // now, if the click is near the edge, it's a resize operation
679 // keep track of which edges we're close to, since those are the ones which should move
680 ui.selection->resizing_left = (pt[0]<ui.selection->bbox.left+hmargin);
681 ui.selection->resizing_right = (pt[0]>ui.selection->bbox.right-hmargin);
682 ui.selection->resizing_top = (pt[1]<ui.selection->bbox.top+vmargin);
683 ui.selection->resizing_bottom = (pt[1]>ui.selection->bbox.bottom-vmargin);
685 // we're not near any edge, give up
686 if (!(ui.selection->resizing_left || ui.selection->resizing_right ||
687 ui.selection->resizing_top || ui.selection->resizing_bottom))
690 ui.cur_item_type = ITEM_RESIZESEL;
691 ui.selection->new_y1 = ui.selection->bbox.top;
692 ui.selection->new_y2 = ui.selection->bbox.bottom;
693 ui.selection->new_x1 = ui.selection->bbox.left;
694 ui.selection->new_x2 = ui.selection->bbox.right;
695 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
696 update_cursor_for_resize(pt);
703 void start_vertspace(GdkEvent *event)
710 ui.cur_item_type = ITEM_MOVESEL_VERT;
711 ui.selection = g_new(struct Selection, 1);
712 ui.selection->type = ITEM_MOVESEL_VERT;
713 ui.selection->items = NULL;
714 ui.selection->layer = ui.cur_layer;
716 get_pointer_coords(event, pt);
717 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
718 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
719 item = (struct Item *)itemlist->data;
720 if (item->bbox.top >= pt[1]) {
721 ui.selection->items = g_list_append(ui.selection->items, item);
722 if (item->bbox.bottom > ui.selection->bbox.bottom)
723 ui.selection->bbox.bottom = item->bbox.bottom;
727 ui.selection->anchor_x = ui.selection->last_x = 0;
728 ui.selection->anchor_y = ui.selection->last_y = pt[1];
729 ui.selection->orig_pageno = ui.pageno;
730 ui.selection->move_pageno = ui.pageno;
731 ui.selection->move_layer = ui.selection->layer;
732 ui.selection->move_pagedelta = 0.;
733 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
734 gnome_canvas_rect_get_type(), "width-pixels", 1,
735 "outline-color-rgba", 0x000000ff,
736 "fill-color-rgba", 0x80808040,
737 "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
741 void continue_movesel(GdkEvent *event)
743 double pt[2], dx, dy, upmargin;
747 struct Page *tmppage;
749 get_pointer_coords(event, pt);
750 if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
751 pt[1] += ui.selection->move_pagedelta;
753 // check for page jumps
754 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
755 upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
756 else upmargin = VIEW_CONTINUOUS_SKIP;
757 tmppageno = ui.selection->move_pageno;
758 tmppage = g_list_nth_data(journal.pages, tmppageno);
759 while (ui.view_continuous && (pt[1] < - upmargin)) {
760 if (tmppageno == 0) break;
762 tmppage = g_list_nth_data(journal.pages, tmppageno);
763 pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
764 ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
766 while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
767 if (tmppageno == journal.npages-1) break;
768 pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
769 ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
771 tmppage = g_list_nth_data(journal.pages, tmppageno);
774 if (tmppageno != ui.selection->move_pageno) {
775 // move to a new page !
776 ui.selection->move_pageno = tmppageno;
777 if (tmppageno == ui.selection->orig_pageno)
778 ui.selection->move_layer = ui.selection->layer;
780 ui.selection->move_layer = (struct Layer *)(g_list_last(
781 ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
782 gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
783 for (list = ui.selection->items; list!=NULL; list = list->next) {
784 item = (struct Item *)list->data;
785 if (item->canvas_item!=NULL)
786 gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
788 // avoid a refresh bug
789 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
790 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
791 gnome_canvas_item_set(ui.selection->canvas_item,
792 "x2", tmppage->width+100,
793 "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
796 // now, process things normally
798 dx = pt[0] - ui.selection->last_x;
799 dy = pt[1] - ui.selection->last_y;
800 if (hypot(dx,dy) < 1) return; // don't move subpixel
801 ui.selection->last_x = pt[0];
802 ui.selection->last_y = pt[1];
804 // move the canvas items
805 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
806 gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
808 gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
810 for (list = ui.selection->items; list != NULL; list = list->next) {
811 item = (struct Item *)list->data;
812 if (item->canvas_item != NULL)
813 gnome_canvas_item_move(item->canvas_item, dx, dy);
817 void continue_resizesel(GdkEvent *event)
821 get_pointer_coords(event, pt);
823 if (ui.selection->resizing_top) ui.selection->new_y1 = pt[1];
824 if (ui.selection->resizing_bottom) ui.selection->new_y2 = pt[1];
825 if (ui.selection->resizing_left) ui.selection->new_x1 = pt[0];
826 if (ui.selection->resizing_right) ui.selection->new_x2 = pt[0];
828 gnome_canvas_item_set(ui.selection->canvas_item,
829 "x1", ui.selection->new_x1, "x2", ui.selection->new_x2,
830 "y1", ui.selection->new_y1, "y2", ui.selection->new_y2, NULL);
833 void finalize_movesel(void)
837 if (ui.selection->items != NULL) {
839 undo->type = ITEM_MOVESEL;
840 undo->itemlist = g_list_copy(ui.selection->items);
841 undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
842 undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
843 undo->layer = ui.selection->layer;
844 undo->layer2 = ui.selection->move_layer;
845 undo->auxlist = NULL;
846 // build auxlist = pointers to Item's just before ours (for depths)
847 for (list = ui.selection->items; list!=NULL; list = list->next) {
848 link = g_list_find(ui.selection->layer->items, list->data);
849 if (link!=NULL) link = link->prev;
850 undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
852 ui.selection->layer = ui.selection->move_layer;
853 move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
854 undo->layer, undo->layer2,
855 (undo->layer == undo->layer2)?undo->auxlist:NULL);
858 if (ui.selection->move_pageno!=ui.selection->orig_pageno)
859 do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
861 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
864 ui.selection->bbox.left += undo->val_x;
865 ui.selection->bbox.right += undo->val_x;
866 ui.selection->bbox.top += undo->val_y;
867 ui.selection->bbox.bottom += undo->val_y;
868 make_dashed(ui.selection->canvas_item);
869 /* update selection box object's offset to be trivial, and its internal
870 coordinates to agree with those of the bbox; need this since resize
871 operations will modify the box by setting its coordinates directly */
872 gnome_canvas_item_affine_absolute(ui.selection->canvas_item, NULL);
873 gnome_canvas_item_set(ui.selection->canvas_item,
874 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
875 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
877 ui.cur_item_type = ITEM_NONE;
881 #define SCALING_EPSILON 0.001
883 void finalize_resizesel(void)
887 // build the affine transformation
888 double offset_x, offset_y, scaling_x, scaling_y;
889 scaling_x = (ui.selection->new_x2 - ui.selection->new_x1) /
890 (ui.selection->bbox.right - ui.selection->bbox.left);
891 scaling_y = (ui.selection->new_y2 - ui.selection->new_y1) /
892 (ui.selection->bbox.bottom - ui.selection->bbox.top);
893 // couldn't undo a resize-by-zero...
894 if (fabs(scaling_x)<SCALING_EPSILON) scaling_x = SCALING_EPSILON;
895 if (fabs(scaling_y)<SCALING_EPSILON) scaling_y = SCALING_EPSILON;
896 offset_x = ui.selection->new_x1 - ui.selection->bbox.left * scaling_x;
897 offset_y = ui.selection->new_y1 - ui.selection->bbox.top * scaling_y;
899 if (ui.selection->items != NULL) {
900 // create the undo information
902 undo->type = ITEM_RESIZESEL;
903 undo->itemlist = g_list_copy(ui.selection->items);
904 undo->auxlist = NULL;
906 undo->scaling_x = scaling_x;
907 undo->scaling_y = scaling_y;
908 undo->val_x = offset_x;
909 undo->val_y = offset_y;
911 // actually do the resize operation
912 resize_journal_items_by(ui.selection->items, scaling_x, scaling_y, offset_x, offset_y);
916 ui.selection->bbox.left = ui.selection->new_x1;
917 ui.selection->bbox.right = ui.selection->new_x2;
919 ui.selection->bbox.left = ui.selection->new_x2;
920 ui.selection->bbox.right = ui.selection->new_x1;
923 ui.selection->bbox.top = ui.selection->new_y1;
924 ui.selection->bbox.bottom = ui.selection->new_y2;
926 ui.selection->bbox.top = ui.selection->new_y2;
927 ui.selection->bbox.bottom = ui.selection->new_y1;
929 make_dashed(ui.selection->canvas_item);
931 ui.cur_item_type = ITEM_NONE;
935 void selection_delete(void)
937 struct UndoErasureData *erasure;
941 if (ui.selection == NULL) return;
943 undo->type = ITEM_ERASURE;
944 undo->layer = ui.selection->layer;
945 undo->erasurelist = NULL;
946 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
947 item = (struct Item *)itemlist->data;
948 if (item->canvas_item!=NULL)
949 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
950 erasure = g_new(struct UndoErasureData, 1);
951 erasure->item = item;
952 erasure->npos = g_list_index(ui.selection->layer->items, item);
954 erasure->replacement_items = NULL;
955 ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
956 ui.selection->layer->nitems--;
957 undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
961 /* NOTE: the erasurelist is built backwards; this guarantees that,
962 upon undo, the erasure->npos fields give the correct position
963 where each item should be reinserted as the list is traversed in
964 the forward direction */
967 void callback_clipboard_get(GtkClipboard *clipboard,
968 GtkSelectionData *selection_data,
969 guint info, gpointer user_data)
973 g_memmove(&length, user_data, sizeof(int));
974 gtk_selection_data_set(selection_data,
975 gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
978 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
983 void selection_to_clip(void)
985 int bufsz, nitems, val;
989 GtkTargetEntry target;
991 if (ui.selection == NULL) return;
992 bufsz = 2*sizeof(int) // bufsz, nitems
993 + sizeof(struct BBox); // bbox
995 for (list = ui.selection->items; list != NULL; list = list->next) {
996 item = (struct Item *)list->data;
998 if (item->type == ITEM_STROKE) {
999 bufsz+= sizeof(int) // type
1000 + sizeof(struct Brush) // brush
1001 + sizeof(int) // num_points
1002 + 2*item->path->num_points*sizeof(double); // the points
1003 if (item->brush.variable_width)
1004 bufsz += (item->path->num_points-1)*sizeof(double); // the widths
1006 else if (item->type == ITEM_TEXT) {
1007 bufsz+= sizeof(int) // type
1008 + sizeof(struct Brush) // brush
1009 + 2*sizeof(double) // bbox upper-left
1010 + sizeof(int) // text len
1011 + strlen(item->text)+1 // text
1012 + sizeof(int) // font_name len
1013 + strlen(item->font_name)+1 // font_name
1014 + sizeof(double); // font_size
1016 else bufsz+= sizeof(int); // type
1018 p = buf = g_malloc(bufsz);
1019 g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
1020 g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
1021 g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
1022 for (list = ui.selection->items; list != NULL; list = list->next) {
1023 item = (struct Item *)list->data;
1024 g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
1025 if (item->type == ITEM_STROKE) {
1026 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
1027 g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
1028 g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
1029 p+= 2*item->path->num_points*sizeof(double);
1030 if (item->brush.variable_width) {
1031 g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
1032 p+= (item->path->num_points-1)*sizeof(double);
1035 if (item->type == ITEM_TEXT) {
1036 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
1037 g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
1038 g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
1039 val = strlen(item->text);
1040 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
1041 g_memmove(p, item->text, val+1); p+= val+1;
1042 val = strlen(item->font_name);
1043 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
1044 g_memmove(p, item->font_name, val+1); p+= val+1;
1045 g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
1049 target.target = "_XOURNAL";
1053 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
1055 callback_clipboard_get, callback_clipboard_clear, buf);
1059 void clipboard_paste(void)
1061 GtkSelectionData *sel_data;
1063 int nitems, npts, i, len;
1065 double hoffset, voffset, cx, cy;
1069 if (ui.cur_layer == NULL) return;
1071 ui.cur_item_type = ITEM_PASTE;
1072 sel_data = gtk_clipboard_wait_for_contents(
1073 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
1074 gdk_atom_intern("_XOURNAL", FALSE));
1075 ui.cur_item_type = ITEM_NONE;
1076 if (sel_data == NULL) return; // paste failed
1080 ui.selection = g_new(struct Selection, 1);
1081 p = sel_data->data + sizeof(int);
1082 g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
1083 ui.selection->type = ITEM_SELECTRECT;
1084 ui.selection->layer = ui.cur_layer;
1085 g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
1086 ui.selection->items = NULL;
1088 // find by how much we translate the pasted selection
1089 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
1090 gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
1091 gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
1092 cx -= ui.cur_page->hoffset;
1093 cy -= ui.cur_page->voffset;
1094 if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
1095 cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1096 if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
1097 cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1098 if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
1099 cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1100 if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
1101 cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1102 hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
1103 voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
1104 ui.selection->bbox.left += hoffset;
1105 ui.selection->bbox.right += hoffset;
1106 ui.selection->bbox.top += voffset;
1107 ui.selection->bbox.bottom += voffset;
1109 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1110 gnome_canvas_rect_get_type(), "width-pixels", 1,
1111 "outline-color-rgba", 0x000000ff,
1112 "fill-color-rgba", 0x80808040,
1113 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
1114 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
1115 make_dashed(ui.selection->canvas_item);
1117 while (nitems-- > 0) {
1118 item = g_new(struct Item, 1);
1119 ui.selection->items = g_list_append(ui.selection->items, item);
1120 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1121 ui.cur_layer->nitems++;
1122 g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
1123 if (item->type == ITEM_STROKE) {
1124 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1125 g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
1126 item->path = gnome_canvas_points_new(npts);
1128 for (i=0; i<npts; i++) {
1129 item->path->coords[2*i] = pf[2*i] + hoffset;
1130 item->path->coords[2*i+1] = pf[2*i+1] + voffset;
1132 p+= 2*item->path->num_points*sizeof(double);
1133 if (item->brush.variable_width) {
1134 item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
1135 p+= (item->path->num_points-1)*sizeof(double);
1137 else item->widths = NULL;
1138 update_item_bbox(item);
1139 make_canvas_item_one(ui.cur_layer->group, item);
1141 if (item->type == ITEM_TEXT) {
1142 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1143 g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
1144 g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
1145 item->bbox.left += hoffset;
1146 item->bbox.top += voffset;
1147 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1148 item->text = g_malloc(len+1);
1149 g_memmove(item->text, p, len+1); p+= len+1;
1150 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1151 item->font_name = g_malloc(len+1);
1152 g_memmove(item->font_name, p, len+1); p+= len+1;
1153 g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
1154 make_canvas_item_one(ui.cur_layer->group, item);
1159 undo->type = ITEM_PASTE;
1160 undo->layer = ui.cur_layer;
1161 undo->itemlist = g_list_copy(ui.selection->items);
1163 gtk_selection_data_free(sel_data);
1164 update_copy_paste_enabled();
1165 update_color_menu();
1166 update_thickness_buttons();
1167 update_color_buttons();
1168 update_font_button();
1169 update_cursor(); // FIXME: can't know if pointer is within selection!
1172 // modify the color or thickness of pen strokes in a selection
1174 void recolor_selection(int color_no, guint color_rgba)
1178 struct Brush *brush;
1179 GnomeCanvasGroup *group;
1181 if (ui.selection == NULL) return;
1183 undo->type = ITEM_REPAINTSEL;
1184 undo->itemlist = NULL;
1185 undo->auxlist = NULL;
1186 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1187 item = (struct Item *)itemlist->data;
1188 if (item->type != ITEM_STROKE && item->type != ITEM_TEXT) continue;
1189 if (item->type == ITEM_STROKE && item->brush.tool_type!=TOOL_PEN) continue;
1190 // store info for undo
1191 undo->itemlist = g_list_append(undo->itemlist, item);
1192 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1193 g_memmove(brush, &(item->brush), sizeof(struct Brush));
1194 undo->auxlist = g_list_append(undo->auxlist, brush);
1195 // repaint the stroke
1196 item->brush.color_no = color_no;
1197 item->brush.color_rgba = color_rgba | 0xff; // no alpha
1198 if (item->canvas_item!=NULL) {
1199 if (!item->brush.variable_width)
1200 gnome_canvas_item_set(item->canvas_item,
1201 "fill-color-rgba", item->brush.color_rgba, NULL);
1203 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1204 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1205 make_canvas_item_one(group, item);
1211 void rethicken_selection(int val)
1215 struct Brush *brush;
1216 GnomeCanvasGroup *group;
1218 if (ui.selection == NULL) return;
1220 undo->type = ITEM_REPAINTSEL;
1221 undo->itemlist = NULL;
1222 undo->auxlist = NULL;
1223 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1224 item = (struct Item *)itemlist->data;
1225 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
1226 // store info for undo
1227 undo->itemlist = g_list_append(undo->itemlist, item);
1228 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1229 g_memmove(brush, &(item->brush), sizeof(struct Brush));
1230 undo->auxlist = g_list_append(undo->auxlist, brush);
1231 // repaint the stroke
1232 item->brush.thickness_no = val;
1233 item->brush.thickness = predef_thickness[TOOL_PEN][val];
1234 if (item->canvas_item!=NULL) {
1235 if (!item->brush.variable_width)
1236 gnome_canvas_item_set(item->canvas_item,
1237 "width-units", item->brush.thickness, NULL);
1239 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1240 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1241 item->brush.variable_width = FALSE;
1242 make_canvas_item_one(group, item);
1248 gboolean do_hand_scrollto(gpointer data)
1250 ui.hand_scrollto_pending = FALSE;
1251 gnome_canvas_scroll_to(canvas, ui.hand_scrollto_cx, ui.hand_scrollto_cy);
1255 void do_hand(GdkEvent *event)
1260 get_pointer_coords(event, pt);
1261 pt[0] += ui.cur_page->hoffset;
1262 pt[1] += ui.cur_page->voffset;
1263 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1264 ui.hand_scrollto_cx = cx - (pt[0]-ui.hand_refpt[0])*ui.zoom;
1265 ui.hand_scrollto_cy = cy - (pt[1]-ui.hand_refpt[1])*ui.zoom;
1266 if (!ui.hand_scrollto_pending) g_idle_add(do_hand_scrollto, NULL);
1267 ui.hand_scrollto_pending = TRUE;
1270 /************ TEXT FUNCTIONS **************/
1272 // to make it easier to copy/paste at end of text box
1273 #define WIDGET_RIGHT_MARGIN 10
1275 void resize_textview(gpointer *toplevel, gpointer *data)
1280 /* when the text changes, resize the GtkTextView accordingly */
1281 if (ui.cur_item_type!=ITEM_TEXT) return;
1282 w = GTK_TEXT_VIEW(ui.cur_item->widget);
1283 width = w->width + WIDGET_RIGHT_MARGIN;
1285 gnome_canvas_item_set(ui.cur_item->canvas_item,
1286 "size-pixels", TRUE,
1287 "width", (gdouble)width, "height", (gdouble)height, NULL);
1288 ui.cur_item->bbox.right = ui.cur_item->bbox.left + width/ui.zoom;
1289 ui.cur_item->bbox.bottom = ui.cur_item->bbox.top + height/ui.zoom;
1292 void start_text(GdkEvent *event, struct Item *item)
1295 GtkTextBuffer *buffer;
1296 GnomeCanvasItem *canvas_item;
1297 PangoFontDescription *font_desc;
1300 get_pointer_coords(event, pt);
1302 ui.cur_item_type = ITEM_TEXT;
1305 item = g_new(struct Item, 1);
1307 item->canvas_item = NULL;
1308 item->bbox.left = pt[0];
1309 item->bbox.top = pt[1];
1310 item->bbox.right = ui.cur_page->width;
1311 item->bbox.bottom = pt[1]+100.;
1312 item->font_name = g_strdup(ui.font_name);
1313 item->font_size = ui.font_size;
1314 g_memmove(&(item->brush), ui.cur_brush, sizeof(struct Brush));
1315 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1316 ui.cur_layer->nitems++;
1319 item->type = ITEM_TEMP_TEXT;
1322 font_desc = pango_font_description_from_string(item->font_name);
1323 pango_font_description_set_absolute_size(font_desc,
1324 item->font_size*ui.zoom*PANGO_SCALE);
1325 item->widget = gtk_text_view_new();
1326 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(item->widget));
1327 if (item->text!=NULL)
1328 gtk_text_buffer_set_text(buffer, item->text, -1);
1329 gtk_widget_modify_font(item->widget, font_desc);
1330 rgb_to_gdkcolor(item->brush.color_rgba, &color);
1331 gtk_widget_modify_text(item->widget, GTK_STATE_NORMAL, &color);
1332 pango_font_description_free(font_desc);
1334 canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1335 gnome_canvas_widget_get_type(),
1336 "x", item->bbox.left, "y", item->bbox.top,
1337 "width", item->bbox.right-item->bbox.left,
1338 "height", item->bbox.bottom-item->bbox.top,
1339 "widget", item->widget, NULL);
1340 // TODO: width/height?
1341 if (item->canvas_item!=NULL) {
1342 lower_canvas_item_to(ui.cur_layer->group, canvas_item, item->canvas_item);
1343 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1345 item->canvas_item = canvas_item;
1347 gtk_widget_show(item->widget);
1348 ui.resize_signal_handler =
1349 g_signal_connect((gpointer) winMain, "check_resize",
1350 G_CALLBACK(resize_textview), NULL);
1351 update_font_button();
1352 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), FALSE);
1353 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), FALSE);
1354 gtk_widget_grab_focus(item->widget);
1359 GtkTextBuffer *buffer;
1360 GtkTextIter start, end;
1362 struct UndoErasureData *erasure;
1363 GnomeCanvasItem *tmpitem;
1365 if (ui.cur_item_type!=ITEM_TEXT) return; // nothing for us to do!
1367 // finalize the text that's been edited...
1368 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.cur_item->widget));
1369 gtk_text_buffer_get_bounds(buffer, &start, &end);
1370 ui.cur_item->type = ITEM_TEXT;
1371 new_text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
1372 ui.cur_item_type = ITEM_NONE;
1373 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), TRUE);
1374 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), TRUE);
1376 if (strlen(new_text)==0) { // erase object and cancel
1378 g_signal_handler_disconnect(winMain, ui.resize_signal_handler);
1379 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
1380 ui.cur_item->canvas_item = NULL;
1381 if (ui.cur_item->text == NULL) // nothing happened
1382 g_free(ui.cur_item->font_name);
1383 else { // treat this as an erasure
1385 undo->type = ITEM_ERASURE;
1386 undo->layer = ui.cur_layer;
1387 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
1388 erasure->item = ui.cur_item;
1389 erasure->npos = g_list_index(ui.cur_layer->items, ui.cur_item);
1391 erasure->replacement_items = NULL;
1392 undo->erasurelist = g_list_append(NULL, erasure);
1394 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, ui.cur_item);
1395 ui.cur_layer->nitems--;
1401 if (ui.cur_item->text==NULL || strcmp(ui.cur_item->text, new_text)) {
1403 if (ui.cur_item->text == NULL) undo->type = ITEM_TEXT;
1404 else undo->type = ITEM_TEXT_EDIT;
1405 undo->layer = ui.cur_layer;
1406 undo->item = ui.cur_item;
1407 undo->str = ui.cur_item->text;
1409 else g_free(ui.cur_item->text);
1411 ui.cur_item->text = new_text;
1412 ui.cur_item->widget = NULL;
1413 // replace the canvas item
1414 tmpitem = ui.cur_item->canvas_item;
1415 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
1416 update_item_bbox(ui.cur_item);
1417 lower_canvas_item_to(ui.cur_layer->group, ui.cur_item->canvas_item, tmpitem);
1418 gtk_object_destroy(GTK_OBJECT(tmpitem));
1421 /* update the items in the canvas so they're of the right font size */
1423 void update_text_item_displayfont(struct Item *item)
1425 PangoFontDescription *font_desc;
1427 if (item->type != ITEM_TEXT && item->type != ITEM_TEMP_TEXT) return;
1428 if (item->canvas_item==NULL) return;
1429 font_desc = pango_font_description_from_string(item->font_name);
1430 pango_font_description_set_absolute_size(font_desc,
1431 item->font_size*ui.zoom*PANGO_SCALE);
1432 if (item->type == ITEM_TEMP_TEXT)
1433 gtk_widget_modify_font(item->widget, font_desc);
1435 gnome_canvas_item_set(item->canvas_item, "font-desc", font_desc, NULL);
1436 update_item_bbox(item);
1438 pango_font_description_free(font_desc);
1441 void rescale_text_items(void)
1443 GList *pagelist, *layerlist, *itemlist;
1445 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next)
1446 for (layerlist = ((struct Page *)pagelist->data)->layers; layerlist!=NULL; layerlist = layerlist->next)
1447 for (itemlist = ((struct Layer *)layerlist->data)->items; itemlist!=NULL; itemlist = itemlist->next)
1448 update_text_item_displayfont((struct Item *)itemlist->data);
1451 struct Item *click_is_in_text(struct Layer *layer, double x, double y)
1454 struct Item *item, *val;
1457 for (itemlist = layer->items; itemlist!=NULL; itemlist = itemlist->next) {
1458 item = (struct Item *)itemlist->data;
1459 if (item->type != ITEM_TEXT) continue;
1460 if (x<item->bbox.left || x>item->bbox.right) continue;
1461 if (y<item->bbox.top || y>item->bbox.bottom) continue;
1467 void refont_text_item(struct Item *item, gchar *font_name, double font_size)
1469 if (!strcmp(font_name, item->font_name) && font_size==item->font_size) return;
1470 if (item->text!=NULL) {
1472 undo->type = ITEM_TEXT_ATTRIB;
1474 undo->str = item->font_name;
1475 undo->val_x = item->font_size;
1476 undo->brush = (struct Brush *)g_memdup(&(item->brush), sizeof(struct Brush));
1478 else g_free(item->font_name);
1479 item->font_name = g_strdup(font_name);
1480 if (font_size>0.) item->font_size = font_size;
1481 update_text_item_displayfont(item);
1484 void process_font_sel(gchar *str)
1492 p = strrchr(str, ' ');
1494 size = g_strtod(p+1, &q);
1495 if (*q!=0 || size<1.) size=0.;
1499 g_free(ui.font_name);
1501 if (size>0.) ui.font_size = size;
1503 // if there's a current text item, re-font it
1504 if (ui.cur_item_type == ITEM_TEXT) {
1505 refont_text_item(ui.cur_item, str, size);
1506 undo_cont = (ui.cur_item->text!=NULL);
1508 // if there's a current selection, re-font it
1509 if (ui.selection!=NULL)
1510 for (list=ui.selection->items; list!=NULL; list=list->next) {
1511 it = (struct Item *)list->data;
1512 if (it->type == ITEM_TEXT) {
1513 if (undo_cont) undo->multiop |= MULTIOP_CONT_REDO;
1514 refont_text_item(it, str, size);
1515 if (undo_cont) undo->multiop |= MULTIOP_CONT_UNDO;
1519 update_font_button();