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 or image?
612 item = click_is_in_text_or_image(ui.selection->layer, x1, y1);
613 if (item!=NULL && item==click_is_in_text_or_image(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 // modify the color or thickness of pen strokes in a selection
969 void recolor_selection(int color_no, guint color_rgba)
974 GnomeCanvasGroup *group;
976 if (ui.selection == NULL) return;
978 undo->type = ITEM_REPAINTSEL;
979 undo->itemlist = NULL;
980 undo->auxlist = NULL;
981 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
982 item = (struct Item *)itemlist->data;
983 if (item->type != ITEM_STROKE && item->type != ITEM_TEXT) continue;
984 if (item->type == ITEM_STROKE && item->brush.tool_type!=TOOL_PEN) continue;
985 // store info for undo
986 undo->itemlist = g_list_append(undo->itemlist, item);
987 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
988 g_memmove(brush, &(item->brush), sizeof(struct Brush));
989 undo->auxlist = g_list_append(undo->auxlist, brush);
990 // repaint the stroke
991 item->brush.color_no = color_no;
992 item->brush.color_rgba = color_rgba | 0xff; // no alpha
993 if (item->canvas_item!=NULL) {
994 if (!item->brush.variable_width)
995 gnome_canvas_item_set(item->canvas_item,
996 "fill-color-rgba", item->brush.color_rgba, NULL);
998 group = (GnomeCanvasGroup *) item->canvas_item->parent;
999 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1000 make_canvas_item_one(group, item);
1006 void rethicken_selection(int val)
1010 struct Brush *brush;
1011 GnomeCanvasGroup *group;
1013 if (ui.selection == NULL) return;
1015 undo->type = ITEM_REPAINTSEL;
1016 undo->itemlist = NULL;
1017 undo->auxlist = NULL;
1018 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1019 item = (struct Item *)itemlist->data;
1020 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
1021 // store info for undo
1022 undo->itemlist = g_list_append(undo->itemlist, item);
1023 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1024 g_memmove(brush, &(item->brush), sizeof(struct Brush));
1025 undo->auxlist = g_list_append(undo->auxlist, brush);
1026 // repaint the stroke
1027 item->brush.thickness_no = val;
1028 item->brush.thickness = predef_thickness[TOOL_PEN][val];
1029 if (item->canvas_item!=NULL) {
1030 if (!item->brush.variable_width)
1031 gnome_canvas_item_set(item->canvas_item,
1032 "width-units", item->brush.thickness, NULL);
1034 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1035 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1036 item->brush.variable_width = FALSE;
1037 make_canvas_item_one(group, item);
1043 gboolean do_hand_scrollto(gpointer data)
1045 ui.hand_scrollto_pending = FALSE;
1046 gnome_canvas_scroll_to(canvas, ui.hand_scrollto_cx, ui.hand_scrollto_cy);
1050 void do_hand(GdkEvent *event)
1055 get_pointer_coords(event, pt);
1056 pt[0] += ui.cur_page->hoffset;
1057 pt[1] += ui.cur_page->voffset;
1058 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1059 ui.hand_scrollto_cx = cx - (pt[0]-ui.hand_refpt[0])*ui.zoom;
1060 ui.hand_scrollto_cy = cy - (pt[1]-ui.hand_refpt[1])*ui.zoom;
1061 if (!ui.hand_scrollto_pending) g_idle_add(do_hand_scrollto, NULL);
1062 ui.hand_scrollto_pending = TRUE;
1065 /************ TEXT FUNCTIONS **************/
1067 // to make it easier to copy/paste at end of text box
1068 #define WIDGET_RIGHT_MARGIN 10
1070 void resize_textview(gpointer *toplevel, gpointer *data)
1075 /* when the text changes, resize the GtkTextView accordingly */
1076 if (ui.cur_item_type!=ITEM_TEXT) return;
1077 w = GTK_TEXT_VIEW(ui.cur_item->widget);
1078 width = w->width + WIDGET_RIGHT_MARGIN;
1080 gnome_canvas_item_set(ui.cur_item->canvas_item,
1081 "size-pixels", TRUE,
1082 "width", (gdouble)width, "height", (gdouble)height, NULL);
1083 ui.cur_item->bbox.right = ui.cur_item->bbox.left + width/ui.zoom;
1084 ui.cur_item->bbox.bottom = ui.cur_item->bbox.top + height/ui.zoom;
1087 void start_text(GdkEvent *event, struct Item *item)
1090 GtkTextBuffer *buffer;
1091 GnomeCanvasItem *canvas_item;
1092 PangoFontDescription *font_desc;
1095 get_pointer_coords(event, pt);
1097 ui.cur_item_type = ITEM_TEXT;
1100 item = g_new(struct Item, 1);
1102 item->canvas_item = NULL;
1103 item->bbox.left = pt[0];
1104 item->bbox.top = pt[1];
1105 item->bbox.right = ui.cur_page->width;
1106 item->bbox.bottom = pt[1]+100.;
1107 item->font_name = g_strdup(ui.font_name);
1108 item->font_size = ui.font_size;
1109 g_memmove(&(item->brush), ui.cur_brush, sizeof(struct Brush));
1110 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1111 ui.cur_layer->nitems++;
1114 item->type = ITEM_TEMP_TEXT;
1117 font_desc = pango_font_description_from_string(item->font_name);
1118 pango_font_description_set_absolute_size(font_desc,
1119 item->font_size*ui.zoom*PANGO_SCALE);
1120 item->widget = gtk_text_view_new();
1121 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(item->widget));
1122 if (item->text!=NULL)
1123 gtk_text_buffer_set_text(buffer, item->text, -1);
1124 gtk_widget_modify_font(item->widget, font_desc);
1125 rgb_to_gdkcolor(item->brush.color_rgba, &color);
1126 gtk_widget_modify_text(item->widget, GTK_STATE_NORMAL, &color);
1127 pango_font_description_free(font_desc);
1129 canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1130 gnome_canvas_widget_get_type(),
1131 "x", item->bbox.left, "y", item->bbox.top,
1132 "width", item->bbox.right-item->bbox.left,
1133 "height", item->bbox.bottom-item->bbox.top,
1134 "widget", item->widget, NULL);
1135 // TODO: width/height?
1136 if (item->canvas_item!=NULL) {
1137 lower_canvas_item_to(ui.cur_layer->group, canvas_item, item->canvas_item);
1138 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1140 item->canvas_item = canvas_item;
1142 gtk_widget_show(item->widget);
1143 ui.resize_signal_handler =
1144 g_signal_connect((gpointer) winMain, "check_resize",
1145 G_CALLBACK(resize_textview), NULL);
1146 update_font_button();
1147 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), FALSE);
1148 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), FALSE);
1149 gtk_widget_grab_focus(item->widget);
1154 GtkTextBuffer *buffer;
1155 GtkTextIter start, end;
1157 struct UndoErasureData *erasure;
1158 GnomeCanvasItem *tmpitem;
1160 if (ui.cur_item_type!=ITEM_TEXT) return; // nothing for us to do!
1162 // finalize the text that's been edited...
1163 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.cur_item->widget));
1164 gtk_text_buffer_get_bounds(buffer, &start, &end);
1165 ui.cur_item->type = ITEM_TEXT;
1166 new_text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
1167 ui.cur_item_type = ITEM_NONE;
1168 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), TRUE);
1169 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), TRUE);
1171 if (strlen(new_text)==0) { // erase object and cancel
1173 g_signal_handler_disconnect(winMain, ui.resize_signal_handler);
1174 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
1175 ui.cur_item->canvas_item = NULL;
1176 if (ui.cur_item->text == NULL) // nothing happened
1177 g_free(ui.cur_item->font_name);
1178 else { // treat this as an erasure
1180 undo->type = ITEM_ERASURE;
1181 undo->layer = ui.cur_layer;
1182 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
1183 erasure->item = ui.cur_item;
1184 erasure->npos = g_list_index(ui.cur_layer->items, ui.cur_item);
1186 erasure->replacement_items = NULL;
1187 undo->erasurelist = g_list_append(NULL, erasure);
1189 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, ui.cur_item);
1190 ui.cur_layer->nitems--;
1196 if (ui.cur_item->text==NULL || strcmp(ui.cur_item->text, new_text)) {
1198 if (ui.cur_item->text == NULL) undo->type = ITEM_TEXT;
1199 else undo->type = ITEM_TEXT_EDIT;
1200 undo->layer = ui.cur_layer;
1201 undo->item = ui.cur_item;
1202 undo->str = ui.cur_item->text;
1204 else g_free(ui.cur_item->text);
1206 ui.cur_item->text = new_text;
1207 ui.cur_item->widget = NULL;
1208 // replace the canvas item
1209 tmpitem = ui.cur_item->canvas_item;
1210 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
1211 update_item_bbox(ui.cur_item);
1212 lower_canvas_item_to(ui.cur_layer->group, ui.cur_item->canvas_item, tmpitem);
1213 gtk_object_destroy(GTK_OBJECT(tmpitem));
1216 /* update the items in the canvas so they're of the right font size */
1218 void update_text_item_displayfont(struct Item *item)
1220 PangoFontDescription *font_desc;
1222 if (item->type != ITEM_TEXT && item->type != ITEM_TEMP_TEXT) return;
1223 if (item->canvas_item==NULL) return;
1224 font_desc = pango_font_description_from_string(item->font_name);
1225 pango_font_description_set_absolute_size(font_desc,
1226 item->font_size*ui.zoom*PANGO_SCALE);
1227 if (item->type == ITEM_TEMP_TEXT)
1228 gtk_widget_modify_font(item->widget, font_desc);
1230 gnome_canvas_item_set(item->canvas_item, "font-desc", font_desc, NULL);
1231 update_item_bbox(item);
1233 pango_font_description_free(font_desc);
1236 void rescale_text_items(void)
1238 GList *pagelist, *layerlist, *itemlist;
1240 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next)
1241 for (layerlist = ((struct Page *)pagelist->data)->layers; layerlist!=NULL; layerlist = layerlist->next)
1242 for (itemlist = ((struct Layer *)layerlist->data)->items; itemlist!=NULL; itemlist = itemlist->next)
1243 update_text_item_displayfont((struct Item *)itemlist->data);
1246 struct Item *click_is_in_text(struct Layer *layer, double x, double y)
1249 struct Item *item, *val;
1252 for (itemlist = layer->items; itemlist!=NULL; itemlist = itemlist->next) {
1253 item = (struct Item *)itemlist->data;
1254 if (item->type != ITEM_TEXT) continue;
1255 if (x<item->bbox.left || x>item->bbox.right) continue;
1256 if (y<item->bbox.top || y>item->bbox.bottom) continue;
1262 struct Item *click_is_in_text_or_image(struct Layer *layer, double x, double y)
1265 struct Item *item, *val;
1268 for (itemlist = layer->items; itemlist!=NULL; itemlist = itemlist->next) {
1269 item = (struct Item *)itemlist->data;
1270 if (item->type != ITEM_TEXT && item->type != ITEM_IMAGE) continue;
1271 if (x<item->bbox.left || x>item->bbox.right) continue;
1272 if (y<item->bbox.top || y>item->bbox.bottom) continue;
1278 void refont_text_item(struct Item *item, gchar *font_name, double font_size)
1280 if (!strcmp(font_name, item->font_name) && font_size==item->font_size) return;
1281 if (item->text!=NULL) {
1283 undo->type = ITEM_TEXT_ATTRIB;
1285 undo->str = item->font_name;
1286 undo->val_x = item->font_size;
1287 undo->brush = (struct Brush *)g_memdup(&(item->brush), sizeof(struct Brush));
1289 else g_free(item->font_name);
1290 item->font_name = g_strdup(font_name);
1291 if (font_size>0.) item->font_size = font_size;
1292 update_text_item_displayfont(item);
1295 void process_font_sel(gchar *str)
1303 p = strrchr(str, ' ');
1305 size = g_strtod(p+1, &q);
1306 if (*q!=0 || size<1.) size=0.;
1310 g_free(ui.font_name);
1312 if (size>0.) ui.font_size = size;
1314 // if there's a current text item, re-font it
1315 if (ui.cur_item_type == ITEM_TEXT) {
1316 refont_text_item(ui.cur_item, str, size);
1317 undo_cont = (ui.cur_item->text!=NULL);
1319 // if there's a current selection, re-font it
1320 if (ui.selection!=NULL)
1321 for (list=ui.selection->items; list!=NULL; list=list->next) {
1322 it = (struct Item *)list->data;
1323 if (it->type == ITEM_TEXT) {
1324 if (undo_cont) undo->multiop |= MULTIOP_CONT_REDO;
1325 refont_text_item(it, str, size);
1326 if (undo_cont) undo->multiop |= MULTIOP_CONT_UNDO;
1330 update_font_button();