8 #include <libgnomecanvas/libgnomecanvas.h>
10 #include <libart_lgpl/art_vpath_dash.h>
13 #include "xo-callbacks.h"
14 #include "xo-interface.h"
15 #include "xo-support.h"
19 /************** drawing nice cursors *********/
21 static char cursor_pen_bits[] = {
22 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
23 0xc0, 0x01, 0xc0, 0x01, 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
24 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
26 static char cursor_eraser_bits[] = {
27 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0x08, 0x08, 0x08, 0x08,
28 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xf8, 0x0f,
29 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
31 static char cursor_eraser_mask[] = {
32 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f,
33 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f, 0xf8, 0x0f,
34 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
36 void set_cursor_busy(gboolean busy)
41 cursor = gdk_cursor_new(GDK_WATCH);
42 gdk_window_set_cursor(GTK_WIDGET(winMain)->window, cursor);
43 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, cursor);
44 gdk_cursor_unref(cursor);
47 gdk_window_set_cursor(GTK_WIDGET(winMain)->window, NULL);
50 gdk_display_sync(gdk_display_get_default());
53 void update_cursor(void)
55 GdkPixmap *source, *mask;
56 GdkColor fg = {0, 0, 0, 0}, bg = {0, 65535, 65535, 65535};
58 ui.is_sel_cursor = FALSE;
59 if (GTK_WIDGET(canvas)->window == NULL) return;
61 if (ui.cursor!=NULL) {
62 gdk_cursor_unref(ui.cursor);
65 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
66 ui.cursor = gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW);
67 else if (ui.cur_item_type == ITEM_MOVESEL)
68 ui.cursor = gdk_cursor_new(GDK_FLEUR);
69 else if (ui.toolno[ui.cur_mapping] == TOOL_PEN) {
70 fg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
71 fg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
72 fg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
73 source = gdk_bitmap_create_from_data(NULL, cursor_pen_bits, 16, 16);
74 ui.cursor = gdk_cursor_new_from_pixmap(source, source, &fg, &bg, 7, 7);
75 gdk_bitmap_unref(source);
77 else if (ui.toolno[ui.cur_mapping] == TOOL_ERASER) {
78 source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
79 mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
80 ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
81 gdk_bitmap_unref(source);
82 gdk_bitmap_unref(mask);
84 else if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
85 source = gdk_bitmap_create_from_data(NULL, cursor_eraser_bits, 16, 16);
86 mask = gdk_bitmap_create_from_data(NULL, cursor_eraser_mask, 16, 16);
87 bg.red = (ui.cur_brush->color_rgba >> 16) & 0xff00;
88 bg.green = (ui.cur_brush->color_rgba >> 8) & 0xff00;
89 bg.blue = (ui.cur_brush->color_rgba >> 0) & 0xff00;
90 ui.cursor = gdk_cursor_new_from_pixmap(source, mask, &fg, &bg, 7, 7);
91 gdk_bitmap_unref(source);
92 gdk_bitmap_unref(mask);
94 else if (ui.cur_item_type == ITEM_SELECTRECT) {
95 ui.cursor = gdk_cursor_new(GDK_TCROSS);
97 else if (ui.toolno[ui.cur_mapping] == TOOL_HAND) {
98 ui.cursor = gdk_cursor_new(GDK_HAND1);
100 else if (ui.toolno[ui.cur_mapping] == TOOL_TEXT) {
101 ui.cursor = gdk_cursor_new(GDK_XTERM);
104 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
107 /* adjust the cursor shape if it hovers near a selection box */
109 void update_cursor_for_resize(double *pt)
111 gboolean in_range_x, in_range_y;
112 gboolean can_resize_left, can_resize_right, can_resize_bottom, can_resize_top;
113 gdouble resize_margin;
114 GdkCursorType newcursor;
116 // if we're not even close to the box in some direction, return immediately
117 resize_margin = RESIZE_MARGIN / ui.zoom;
118 if (pt[0]<ui.selection->bbox.left-resize_margin || pt[0]>ui.selection->bbox.right+resize_margin
119 || pt[1]<ui.selection->bbox.top-resize_margin || pt[1]>ui.selection->bbox.bottom+resize_margin)
121 if (ui.is_sel_cursor) update_cursor();
125 ui.is_sel_cursor = TRUE;
126 can_resize_left = (pt[0] < ui.selection->bbox.left+resize_margin);
127 can_resize_right = (pt[0] > ui.selection->bbox.right-resize_margin);
128 can_resize_top = (pt[1] < ui.selection->bbox.top+resize_margin);
129 can_resize_bottom = (pt[1] > ui.selection->bbox.bottom-resize_margin);
131 if (can_resize_left) {
132 if (can_resize_top) newcursor = GDK_TOP_LEFT_CORNER;
133 else if (can_resize_bottom) newcursor = GDK_BOTTOM_LEFT_CORNER;
134 else newcursor = GDK_LEFT_SIDE;
136 else if (can_resize_right) {
137 if (can_resize_top) newcursor = GDK_TOP_RIGHT_CORNER;
138 else if (can_resize_bottom) newcursor = GDK_BOTTOM_RIGHT_CORNER;
139 else newcursor = GDK_RIGHT_SIDE;
141 else if (can_resize_top) newcursor = GDK_TOP_SIDE;
142 else if (can_resize_bottom) newcursor = GDK_BOTTOM_SIDE;
143 else newcursor = GDK_FLEUR;
145 if (ui.cursor!=NULL && ui.cursor->type == newcursor) return;
146 if (ui.cursor!=NULL) gdk_cursor_unref(ui.cursor);
147 ui.cursor = gdk_cursor_new(newcursor);
148 gdk_window_set_cursor(GTK_WIDGET(canvas)->window, ui.cursor);
151 /************** painting strokes *************/
153 #define SUBDIVIDE_MAXDIST 5.0
155 void subdivide_cur_path(null)
159 double x0, y0, x1, y1;
161 for (n=0, p=ui.cur_path.coords; n<ui.cur_path.num_points-1; n++, p+=2) {
162 pieces = (int)floor(hypot(p[2]-p[0], p[3]-p[1])/SUBDIVIDE_MAXDIST);
164 x0 = p[0]; y0 = p[1];
165 x1 = p[2]; y1 = p[3];
166 realloc_cur_path(ui.cur_path.num_points+pieces-1);
167 g_memmove(ui.cur_path.coords+2*(n+pieces), ui.cur_path.coords+2*(n+1),
168 2*(ui.cur_path.num_points-n-1)*sizeof(double));
169 p = ui.cur_path.coords+2*n;
170 ui.cur_path.num_points += pieces-1;
172 for (k=1; k<pieces; k++) {
174 p[0] = x0 + k*(x1-x0)/pieces;
175 p[1] = y0 + k*(y1-y0)/pieces;
181 void create_new_stroke(GdkEvent *event)
183 ui.cur_item_type = ITEM_STROKE;
184 ui.cur_item = g_new(struct Item, 1);
185 ui.cur_item->type = ITEM_STROKE;
186 g_memmove(&(ui.cur_item->brush), ui.cur_brush, sizeof(struct Brush));
187 ui.cur_item->path = &ui.cur_path;
189 ui.cur_path.num_points = 1;
190 get_pointer_coords(event, ui.cur_path.coords);
192 if (ui.cur_brush->ruler) {
193 ui.cur_item->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
194 gnome_canvas_line_get_type(),
195 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
196 "fill-color-rgba", ui.cur_item->brush.color_rgba,
197 "width-units", ui.cur_item->brush.thickness, NULL);
198 ui.cur_item->brush.variable_width = FALSE;
200 ui.cur_item->canvas_item = gnome_canvas_item_new(
201 ui.cur_layer->group, gnome_canvas_group_get_type(), NULL);
204 void continue_stroke(GdkEvent *event)
206 GnomeCanvasPoints seg;
207 double *pt, current_width;
209 if (ui.cur_brush->ruler) {
210 pt = ui.cur_path.coords;
212 realloc_cur_path(ui.cur_path.num_points+1);
213 pt = ui.cur_path.coords + 2*(ui.cur_path.num_points-1);
216 get_pointer_coords(event, pt+2);
218 if (ui.cur_item->brush.variable_width) {
219 realloc_cur_widths(ui.cur_path.num_points);
220 current_width = ui.cur_item->brush.thickness*get_pressure_multiplier(event);
221 ui.cur_widths[ui.cur_path.num_points-1] = current_width;
223 else current_width = ui.cur_item->brush.thickness;
225 if (ui.cur_brush->ruler)
226 ui.cur_path.num_points = 2;
228 if (hypot(pt[0]-pt[2], pt[1]-pt[3]) < PIXEL_MOTION_THRESHOLD/ui.zoom)
229 return; // not a meaningful motion
230 ui.cur_path.num_points++;
237 /* note: we're using a piece of the cur_path array. This is ok because
238 upon creation the line just copies the contents of the GnomeCanvasPoints
239 into an internal structure */
241 if (ui.cur_brush->ruler)
242 gnome_canvas_item_set(ui.cur_item->canvas_item, "points", &seg, NULL);
244 gnome_canvas_item_new((GnomeCanvasGroup *)ui.cur_item->canvas_item,
245 gnome_canvas_line_get_type(), "points", &seg,
246 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
247 "fill-color-rgba", ui.cur_item->brush.color_rgba,
248 "width-units", current_width, NULL);
251 void finalize_stroke(void)
253 if (ui.cur_path.num_points == 1) { // GnomeCanvas doesn't like num_points=1
254 ui.cur_path.coords[2] = ui.cur_path.coords[0]+0.1;
255 ui.cur_path.coords[3] = ui.cur_path.coords[1];
256 ui.cur_path.num_points = 2;
257 ui.cur_item->brush.variable_width = FALSE;
260 if (!ui.cur_item->brush.variable_width)
261 subdivide_cur_path(); // split the segment so eraser will work
263 ui.cur_item->path = gnome_canvas_points_new(ui.cur_path.num_points);
264 g_memmove(ui.cur_item->path->coords, ui.cur_path.coords,
265 2*ui.cur_path.num_points*sizeof(double));
266 if (ui.cur_item->brush.variable_width)
267 ui.cur_item->widths = (gdouble *)g_memdup(ui.cur_widths,
268 (ui.cur_path.num_points-1)*sizeof(gdouble));
269 else ui.cur_item->widths = NULL;
270 update_item_bbox(ui.cur_item);
271 ui.cur_path.num_points = 0;
273 if (!ui.cur_item->brush.variable_width) {
274 // destroy the entire group of temporary line segments
275 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
276 // make a new line item to replace it
277 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
280 // add undo information
282 undo->type = ITEM_STROKE;
283 undo->item = ui.cur_item;
284 undo->layer = ui.cur_layer;
286 // store the item on top of the layer stack
287 ui.cur_layer->items = g_list_append(ui.cur_layer->items, ui.cur_item);
288 ui.cur_layer->nitems++;
290 ui.cur_item_type = ITEM_NONE;
293 /************** eraser tool *************/
295 void erase_stroke_portions(struct Item *item, double x, double y, double radius,
296 gboolean whole_strokes, struct UndoErasureData *erasure)
300 struct Item *newhead, *newtail;
301 gboolean need_recalc = FALSE;
303 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
304 if (hypot(pt[0]-x, pt[1]-y) <= radius) { // found an intersection
305 // FIXME: need to test if line SEGMENT hits the circle
306 // hide the canvas item, and create erasure data if needed
307 if (erasure == NULL) {
308 item->type = ITEM_TEMP_STROKE;
309 gnome_canvas_item_hide(item->canvas_item);
310 /* we'll use this hidden item as an insertion point later */
311 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
312 item->erasure = erasure;
313 erasure->item = item;
314 erasure->npos = g_list_index(ui.cur_layer->items, item);
316 erasure->replacement_items = NULL;
319 newhead = newtail = NULL;
320 if (!whole_strokes) {
322 newhead = (struct Item *)g_malloc(sizeof(struct Item));
323 newhead->type = ITEM_STROKE;
324 g_memmove(&newhead->brush, &item->brush, sizeof(struct Brush));
325 newhead->path = gnome_canvas_points_new(i);
326 g_memmove(newhead->path->coords, item->path->coords, 2*i*sizeof(double));
327 if (newhead->brush.variable_width)
328 newhead->widths = (gdouble *)g_memdup(item->widths, (i-1)*sizeof(gdouble));
329 else newhead->widths = NULL;
331 while (++i < item->path->num_points) {
333 if (hypot(pt[0]-x, pt[1]-y) > radius) break;
335 if (i<item->path->num_points-1) {
336 newtail = (struct Item *)g_malloc(sizeof(struct Item));
337 newtail->type = ITEM_STROKE;
338 g_memmove(&newtail->brush, &item->brush, sizeof(struct Brush));
339 newtail->path = gnome_canvas_points_new(item->path->num_points-i);
340 g_memmove(newtail->path->coords, item->path->coords+2*i,
341 2*(item->path->num_points-i)*sizeof(double));
342 if (newtail->brush.variable_width)
343 newtail->widths = (gdouble *)g_memdup(item->widths+i,
344 (item->path->num_points-i-1)*sizeof(gdouble));
345 else newtail->widths = NULL;
346 newtail->canvas_item = NULL;
349 if (item->type == ITEM_STROKE) {
350 // it's inside an erasure list - we destroy it
351 gnome_canvas_points_free(item->path);
352 if (item->brush.variable_width) g_free(item->widths);
353 if (item->canvas_item != NULL)
354 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
356 erasure->replacement_items = g_list_remove(erasure->replacement_items, item);
360 if (newhead != NULL) {
361 update_item_bbox(newhead);
362 make_canvas_item_one(ui.cur_layer->group, newhead);
363 lower_canvas_item_to(ui.cur_layer->group,
364 newhead->canvas_item, erasure->item->canvas_item);
365 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newhead);
367 // prepending ensures it won't get processed twice
369 // recurse into the new tail
370 need_recalc = (newtail!=NULL);
371 if (newtail == NULL) break;
373 erasure->replacement_items = g_list_prepend(erasure->replacement_items, newtail);
375 i=0; pt=item->path->coords;
378 // add the tail if needed
379 if (!need_recalc) return;
380 update_item_bbox(item);
381 make_canvas_item_one(ui.cur_layer->group, item);
382 lower_canvas_item_to(ui.cur_layer->group, item->canvas_item,
383 erasure->item->canvas_item);
387 void do_eraser(GdkEvent *event, double radius, gboolean whole_strokes)
389 struct Item *item, *repl;
390 GList *itemlist, *repllist;
392 struct BBox eraserbox;
394 get_pointer_coords(event, pos);
395 eraserbox.left = pos[0]-radius;
396 eraserbox.right = pos[0]+radius;
397 eraserbox.top = pos[1]-radius;
398 eraserbox.bottom = pos[1]+radius;
399 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
400 item = (struct Item *)itemlist->data;
401 if (item->type == ITEM_STROKE) {
402 if (!have_intersect(&(item->bbox), &eraserbox)) continue;
403 erase_stroke_portions(item, pos[0], pos[1], radius, whole_strokes, NULL);
404 } else if (item->type == ITEM_TEMP_STROKE) {
405 repllist = item->erasure->replacement_items;
406 while (repllist!=NULL) {
407 repl = (struct Item *)repllist->data;
408 // we may delete the item soon! so advance now in the list
409 repllist = repllist->next;
410 if (have_intersect(&(repl->bbox), &eraserbox))
411 erase_stroke_portions(repl, pos[0], pos[1], radius, whole_strokes, item->erasure);
417 void finalize_erasure(void)
419 GList *itemlist, *partlist;
423 undo->type = ITEM_ERASURE;
424 undo->layer = ui.cur_layer;
425 undo->erasurelist = NULL;
427 itemlist = ui.cur_layer->items;
428 while (itemlist!=NULL) {
429 item = (struct Item *)itemlist->data;
430 itemlist = itemlist->next;
431 if (item->type != ITEM_TEMP_STROKE) continue;
432 item->type = ITEM_STROKE;
433 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, item);
434 // the item has an invisible canvas item, which used to act as anchor
435 if (item->canvas_item!=NULL) {
436 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
437 item->canvas_item = NULL;
439 undo->erasurelist = g_list_append(undo->erasurelist, item->erasure);
440 // add the new strokes into the current layer
441 for (partlist = item->erasure->replacement_items; partlist!=NULL; partlist = partlist->next)
442 ui.cur_layer->items = g_list_insert_before(
443 ui.cur_layer->items, itemlist, partlist->data);
444 ui.cur_layer->nitems += item->erasure->nrepl-1;
448 ui.cur_item_type = ITEM_NONE;
450 /* NOTE: the list of erasures goes in the depth order of the layer;
451 this guarantees that, upon undo, the erasure->npos fields give the
452 correct position where each item should be reinserted as the list
453 is traversed in the forward direction */
456 /************ selection tools ***********/
458 void make_dashed(GnomeCanvasItem *item)
466 dashlen[0] = dashlen[1] = 6.0;
467 gnome_canvas_item_set(item, "dash", &dash, NULL);
471 void start_selectrect(GdkEvent *event)
476 ui.cur_item_type = ITEM_SELECTRECT;
477 ui.selection = g_new(struct Selection, 1);
478 ui.selection->type = ITEM_SELECTRECT;
479 ui.selection->items = NULL;
480 ui.selection->layer = ui.cur_layer;
482 get_pointer_coords(event, pt);
483 ui.selection->bbox.left = ui.selection->bbox.right = pt[0];
484 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
486 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
487 gnome_canvas_rect_get_type(), "width-pixels", 1,
488 "outline-color-rgba", 0x000000ff,
489 "fill-color-rgba", 0x80808040,
490 "x1", pt[0], "x2", pt[0], "y1", pt[1], "y2", pt[1], NULL);
494 void finalize_selectrect(void)
496 double x1, x2, y1, y2;
501 ui.cur_item_type = ITEM_NONE;
503 if (ui.selection->bbox.left > ui.selection->bbox.right) {
504 x1 = ui.selection->bbox.right; x2 = ui.selection->bbox.left;
505 ui.selection->bbox.left = x1; ui.selection->bbox.right = x2;
507 x1 = ui.selection->bbox.left; x2 = ui.selection->bbox.right;
510 if (ui.selection->bbox.top > ui.selection->bbox.bottom) {
511 y1 = ui.selection->bbox.bottom; y2 = ui.selection->bbox.top;
512 ui.selection->bbox.top = y1; ui.selection->bbox.bottom = y2;
514 y1 = ui.selection->bbox.top; y2 = ui.selection->bbox.bottom;
517 for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) {
518 item = (struct Item *)itemlist->data;
519 if (item->bbox.left >= x1 && item->bbox.right <= x2 &&
520 item->bbox.top >= y1 && item->bbox.bottom <= y2) {
521 ui.selection->items = g_list_append(ui.selection->items, item);
525 if (ui.selection->items == NULL) {
526 // if we clicked inside a text zone ?
527 item = click_is_in_text(ui.selection->layer, x1, y1);
528 if (item!=NULL && item==click_is_in_text(ui.selection->layer, x2, y2)) {
529 ui.selection->items = g_list_append(ui.selection->items, item);
530 g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox));
531 gnome_canvas_item_set(ui.selection->canvas_item,
532 "x1", item->bbox.left, "x2", item->bbox.right,
533 "y1", item->bbox.top, "y2", item->bbox.bottom, NULL);
537 if (ui.selection->items == NULL) reset_selection();
538 else make_dashed(ui.selection->canvas_item);
540 update_copy_paste_enabled();
541 update_font_button();
544 gboolean start_movesel(GdkEvent *event)
548 if (ui.selection==NULL) return FALSE;
549 if (ui.cur_layer != ui.selection->layer) return FALSE;
551 get_pointer_coords(event, pt);
552 if (ui.selection->type == ITEM_SELECTRECT) {
553 if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right ||
554 pt[1]<ui.selection->bbox.top || pt[1]>ui.selection->bbox.bottom)
556 ui.cur_item_type = ITEM_MOVESEL;
557 ui.selection->anchor_x = ui.selection->last_x = pt[0];
558 ui.selection->anchor_y = ui.selection->last_y = pt[1];
559 ui.selection->orig_pageno = ui.pageno;
560 ui.selection->move_pageno = ui.pageno;
561 ui.selection->move_layer = ui.selection->layer;
562 ui.selection->move_pagedelta = 0.;
563 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
570 gboolean start_resizesel(GdkEvent *event)
572 double pt[2], resize_margin, hmargin, vmargin;
574 if (ui.selection==NULL) return FALSE;
575 if (ui.cur_layer != ui.selection->layer) return FALSE;
577 get_pointer_coords(event, pt);
579 if (ui.selection->type == ITEM_SELECTRECT) {
580 resize_margin = RESIZE_MARGIN/ui.zoom;
581 hmargin = (ui.selection->bbox.right-ui.selection->bbox.left)*0.3;
582 if (hmargin>resize_margin) hmargin = resize_margin;
583 vmargin = (ui.selection->bbox.bottom-ui.selection->bbox.top)*0.3;
584 if (vmargin>resize_margin) vmargin = resize_margin;
586 // make sure the click is within a box slightly bigger than the selection rectangle
587 if (pt[0]<ui.selection->bbox.left-resize_margin ||
588 pt[0]>ui.selection->bbox.right+resize_margin ||
589 pt[1]<ui.selection->bbox.top-resize_margin ||
590 pt[1]>ui.selection->bbox.bottom+resize_margin)
593 // now, if the click is near the edge, it's a resize operation
594 // keep track of which edges we're close to, since those are the ones which should move
595 ui.selection->resizing_left = (pt[0]<ui.selection->bbox.left+hmargin);
596 ui.selection->resizing_right = (pt[0]>ui.selection->bbox.right-hmargin);
597 ui.selection->resizing_top = (pt[1]<ui.selection->bbox.top+vmargin);
598 ui.selection->resizing_bottom = (pt[1]>ui.selection->bbox.bottom-vmargin);
600 // we're not near any edge, give up
601 if (!(ui.selection->resizing_left || ui.selection->resizing_right ||
602 ui.selection->resizing_top || ui.selection->resizing_bottom))
605 ui.cur_item_type = ITEM_RESIZESEL;
606 ui.selection->new_y1 = ui.selection->bbox.top;
607 ui.selection->new_y2 = ui.selection->bbox.bottom;
608 ui.selection->new_x1 = ui.selection->bbox.left;
609 ui.selection->new_x2 = ui.selection->bbox.right;
610 gnome_canvas_item_set(ui.selection->canvas_item, "dash", NULL, NULL);
617 void start_vertspace(GdkEvent *event)
624 ui.cur_item_type = ITEM_MOVESEL_VERT;
625 ui.selection = g_new(struct Selection, 1);
626 ui.selection->type = ITEM_MOVESEL_VERT;
627 ui.selection->items = NULL;
628 ui.selection->layer = ui.cur_layer;
630 get_pointer_coords(event, pt);
631 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
632 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
633 item = (struct Item *)itemlist->data;
634 if (item->bbox.top >= pt[1]) {
635 ui.selection->items = g_list_append(ui.selection->items, item);
636 if (item->bbox.bottom > ui.selection->bbox.bottom)
637 ui.selection->bbox.bottom = item->bbox.bottom;
641 ui.selection->anchor_x = ui.selection->last_x = 0;
642 ui.selection->anchor_y = ui.selection->last_y = pt[1];
643 ui.selection->orig_pageno = ui.pageno;
644 ui.selection->move_pageno = ui.pageno;
645 ui.selection->move_layer = ui.selection->layer;
646 ui.selection->move_pagedelta = 0.;
647 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
648 gnome_canvas_rect_get_type(), "width-pixels", 1,
649 "outline-color-rgba", 0x000000ff,
650 "fill-color-rgba", 0x80808040,
651 "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
655 void continue_movesel(GdkEvent *event)
657 double pt[2], dx, dy, upmargin;
661 struct Page *tmppage;
663 get_pointer_coords(event, pt);
664 if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
665 pt[1] += ui.selection->move_pagedelta;
667 // check for page jumps
668 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
669 upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
670 else upmargin = VIEW_CONTINUOUS_SKIP;
671 tmppageno = ui.selection->move_pageno;
672 tmppage = g_list_nth_data(journal.pages, tmppageno);
673 while (ui.view_continuous && (pt[1] < - upmargin)) {
674 if (tmppageno == 0) break;
676 tmppage = g_list_nth_data(journal.pages, tmppageno);
677 pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
678 ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
680 while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
681 if (tmppageno == journal.npages-1) break;
682 pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
683 ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
685 tmppage = g_list_nth_data(journal.pages, tmppageno);
688 if (tmppageno != ui.selection->move_pageno) {
689 // move to a new page !
690 ui.selection->move_pageno = tmppageno;
691 if (tmppageno == ui.selection->orig_pageno)
692 ui.selection->move_layer = ui.selection->layer;
694 ui.selection->move_layer = (struct Layer *)(g_list_last(
695 ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
696 gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
697 for (list = ui.selection->items; list!=NULL; list = list->next) {
698 item = (struct Item *)list->data;
699 if (item->canvas_item!=NULL)
700 gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
702 // avoid a refresh bug
703 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
704 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
705 gnome_canvas_item_set(ui.selection->canvas_item,
706 "x2", tmppage->width+100,
707 "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
710 // now, process things normally
712 dx = pt[0] - ui.selection->last_x;
713 dy = pt[1] - ui.selection->last_y;
714 if (hypot(dx,dy) < 1) return; // don't move subpixel
715 ui.selection->last_x = pt[0];
716 ui.selection->last_y = pt[1];
718 // move the canvas items
719 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
720 gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
722 gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
724 for (list = ui.selection->items; list != NULL; list = list->next) {
725 item = (struct Item *)list->data;
726 if (item->canvas_item != NULL)
727 gnome_canvas_item_move(item->canvas_item, dx, dy);
731 void continue_resizesel(GdkEvent *event)
735 get_pointer_coords(event, pt);
737 if (ui.selection->resizing_top) ui.selection->new_y1 = pt[1];
738 if (ui.selection->resizing_bottom) ui.selection->new_y2 = pt[1];
739 if (ui.selection->resizing_left) ui.selection->new_x1 = pt[0];
740 if (ui.selection->resizing_right) ui.selection->new_x2 = pt[0];
742 gnome_canvas_item_set(ui.selection->canvas_item,
743 "x1", ui.selection->new_x1, "x2", ui.selection->new_x2,
744 "y1", ui.selection->new_y1, "y2", ui.selection->new_y2, NULL);
747 void finalize_movesel(void)
751 if (ui.selection->items != NULL) {
753 undo->type = ITEM_MOVESEL;
754 undo->itemlist = g_list_copy(ui.selection->items);
755 undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
756 undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
757 undo->layer = ui.selection->layer;
758 undo->layer2 = ui.selection->move_layer;
759 undo->auxlist = NULL;
760 // build auxlist = pointers to Item's just before ours (for depths)
761 for (list = ui.selection->items; list!=NULL; list = list->next) {
762 link = g_list_find(ui.selection->layer->items, list->data);
763 if (link!=NULL) link = link->prev;
764 undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
766 ui.selection->layer = ui.selection->move_layer;
767 move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
768 undo->layer, undo->layer2,
769 (undo->layer == undo->layer2)?undo->auxlist:NULL);
772 if (ui.selection->move_pageno!=ui.selection->orig_pageno)
773 do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
775 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
778 ui.selection->bbox.left += undo->val_x;
779 ui.selection->bbox.right += undo->val_x;
780 ui.selection->bbox.top += undo->val_y;
781 ui.selection->bbox.bottom += undo->val_y;
782 make_dashed(ui.selection->canvas_item);
783 /* update selection box object's offset to be trivial, and its internal
784 coordinates to agree with those of the bbox; need this since resize
785 operations will modify the box by setting its coordinates directly */
786 gnome_canvas_item_affine_absolute(ui.selection->canvas_item, NULL);
787 gnome_canvas_item_set(ui.selection->canvas_item,
788 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
789 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
791 ui.cur_item_type = ITEM_NONE;
795 #define SCALING_EPSILON 0.001
797 void finalize_resizesel(void)
801 // build the affine transformation
802 double offset_x, offset_y, scaling_x, scaling_y;
803 scaling_x = (ui.selection->new_x2 - ui.selection->new_x1) /
804 (ui.selection->bbox.right - ui.selection->bbox.left);
805 scaling_y = (ui.selection->new_y2 - ui.selection->new_y1) /
806 (ui.selection->bbox.bottom - ui.selection->bbox.top);
807 // couldn't undo a resize-by-zero...
808 if (fabs(scaling_x)<SCALING_EPSILON) scaling_x = SCALING_EPSILON;
809 if (fabs(scaling_y)<SCALING_EPSILON) scaling_y = SCALING_EPSILON;
810 offset_x = ui.selection->new_x1 - ui.selection->bbox.left * scaling_x;
811 offset_y = ui.selection->new_y1 - ui.selection->bbox.top * scaling_y;
813 if (ui.selection->items != NULL) {
814 // create the undo information
816 undo->type = ITEM_RESIZESEL;
817 undo->itemlist = g_list_copy(ui.selection->items);
818 undo->auxlist = NULL;
820 undo->scaling_x = scaling_x;
821 undo->scaling_y = scaling_y;
822 undo->val_x = offset_x;
823 undo->val_y = offset_y;
825 // actually do the resize operation
826 resize_journal_items_by(ui.selection->items, scaling_x, scaling_y, offset_x, offset_y);
830 ui.selection->bbox.left = ui.selection->new_x1;
831 ui.selection->bbox.right = ui.selection->new_x2;
833 ui.selection->bbox.left = ui.selection->new_x2;
834 ui.selection->bbox.right = ui.selection->new_x1;
837 ui.selection->bbox.top = ui.selection->new_y1;
838 ui.selection->bbox.bottom = ui.selection->new_y2;
840 ui.selection->bbox.top = ui.selection->new_y2;
841 ui.selection->bbox.bottom = ui.selection->new_y1;
843 make_dashed(ui.selection->canvas_item);
845 ui.cur_item_type = ITEM_NONE;
849 void selection_delete(void)
851 struct UndoErasureData *erasure;
855 if (ui.selection == NULL) return;
857 undo->type = ITEM_ERASURE;
858 undo->layer = ui.selection->layer;
859 undo->erasurelist = NULL;
860 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
861 item = (struct Item *)itemlist->data;
862 if (item->canvas_item!=NULL)
863 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
864 erasure = g_new(struct UndoErasureData, 1);
865 erasure->item = item;
866 erasure->npos = g_list_index(ui.selection->layer->items, item);
868 erasure->replacement_items = NULL;
869 ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
870 ui.selection->layer->nitems--;
871 undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
875 /* NOTE: the erasurelist is built backwards; this guarantees that,
876 upon undo, the erasure->npos fields give the correct position
877 where each item should be reinserted as the list is traversed in
878 the forward direction */
881 void callback_clipboard_get(GtkClipboard *clipboard,
882 GtkSelectionData *selection_data,
883 guint info, gpointer user_data)
887 g_memmove(&length, user_data, sizeof(int));
888 gtk_selection_data_set(selection_data,
889 gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
892 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
897 void selection_to_clip(void)
899 int bufsz, nitems, val;
903 GtkTargetEntry target;
905 if (ui.selection == NULL) return;
906 bufsz = 2*sizeof(int) // bufsz, nitems
907 + sizeof(struct BBox); // bbox
909 for (list = ui.selection->items; list != NULL; list = list->next) {
910 item = (struct Item *)list->data;
912 if (item->type == ITEM_STROKE) {
913 bufsz+= sizeof(int) // type
914 + sizeof(struct Brush) // brush
915 + sizeof(int) // num_points
916 + 2*item->path->num_points*sizeof(double); // the points
917 if (item->brush.variable_width)
918 bufsz += (item->path->num_points-1)*sizeof(double); // the widths
920 else if (item->type == ITEM_TEXT) {
921 bufsz+= sizeof(int) // type
922 + sizeof(struct Brush) // brush
923 + 2*sizeof(double) // bbox upper-left
924 + sizeof(int) // text len
925 + strlen(item->text)+1 // text
926 + sizeof(int) // font_name len
927 + strlen(item->font_name)+1 // font_name
928 + sizeof(double); // font_size
930 else bufsz+= sizeof(int); // type
932 p = buf = g_malloc(bufsz);
933 g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
934 g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
935 g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
936 for (list = ui.selection->items; list != NULL; list = list->next) {
937 item = (struct Item *)list->data;
938 g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
939 if (item->type == ITEM_STROKE) {
940 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
941 g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
942 g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
943 p+= 2*item->path->num_points*sizeof(double);
944 if (item->brush.variable_width) {
945 g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
946 p+= (item->path->num_points-1)*sizeof(double);
949 if (item->type == ITEM_TEXT) {
950 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
951 g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
952 g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
953 val = strlen(item->text);
954 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
955 g_memmove(p, item->text, val+1); p+= val+1;
956 val = strlen(item->font_name);
957 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
958 g_memmove(p, item->font_name, val+1); p+= val+1;
959 g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
963 target.target = "_XOURNAL";
967 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
969 callback_clipboard_get, callback_clipboard_clear, buf);
973 void clipboard_paste(void)
975 GtkSelectionData *sel_data;
977 int nitems, npts, i, len;
979 double hoffset, voffset, cx, cy;
983 if (ui.cur_layer == NULL) return;
985 ui.cur_item_type = ITEM_PASTE;
986 sel_data = gtk_clipboard_wait_for_contents(
987 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
988 gdk_atom_intern("_XOURNAL", FALSE));
989 ui.cur_item_type = ITEM_NONE;
990 if (sel_data == NULL) return; // paste failed
994 ui.selection = g_new(struct Selection, 1);
995 p = sel_data->data + sizeof(int);
996 g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
997 ui.selection->type = ITEM_SELECTRECT;
998 ui.selection->layer = ui.cur_layer;
999 g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
1000 ui.selection->items = NULL;
1002 // find by how much we translate the pasted selection
1003 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
1004 gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
1005 gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
1006 cx -= ui.cur_page->hoffset;
1007 cy -= ui.cur_page->voffset;
1008 if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
1009 cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1010 if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
1011 cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1012 if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
1013 cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1014 if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
1015 cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1016 hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
1017 voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
1018 ui.selection->bbox.left += hoffset;
1019 ui.selection->bbox.right += hoffset;
1020 ui.selection->bbox.top += voffset;
1021 ui.selection->bbox.bottom += voffset;
1023 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1024 gnome_canvas_rect_get_type(), "width-pixels", 1,
1025 "outline-color-rgba", 0x000000ff,
1026 "fill-color-rgba", 0x80808040,
1027 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
1028 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
1029 make_dashed(ui.selection->canvas_item);
1031 while (nitems-- > 0) {
1032 item = g_new(struct Item, 1);
1033 ui.selection->items = g_list_append(ui.selection->items, item);
1034 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1035 ui.cur_layer->nitems++;
1036 g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
1037 if (item->type == ITEM_STROKE) {
1038 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1039 g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
1040 item->path = gnome_canvas_points_new(npts);
1042 for (i=0; i<npts; i++) {
1043 item->path->coords[2*i] = pf[2*i] + hoffset;
1044 item->path->coords[2*i+1] = pf[2*i+1] + voffset;
1046 p+= 2*item->path->num_points*sizeof(double);
1047 if (item->brush.variable_width) {
1048 g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
1049 p+= (item->path->num_points-1)*sizeof(double);
1051 else item->widths = NULL;
1052 update_item_bbox(item);
1053 make_canvas_item_one(ui.cur_layer->group, item);
1055 if (item->type == ITEM_TEXT) {
1056 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1057 g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
1058 g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
1059 item->bbox.left += hoffset;
1060 item->bbox.top += voffset;
1061 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1062 item->text = g_malloc(len+1);
1063 g_memmove(item->text, p, len+1); p+= len+1;
1064 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1065 item->font_name = g_malloc(len+1);
1066 g_memmove(item->font_name, p, len+1); p+= len+1;
1067 g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
1068 make_canvas_item_one(ui.cur_layer->group, item);
1073 undo->type = ITEM_PASTE;
1074 undo->layer = ui.cur_layer;
1075 undo->itemlist = g_list_copy(ui.selection->items);
1077 gtk_selection_data_free(sel_data);
1078 update_copy_paste_enabled();
1081 // modify the color or thickness of pen strokes in a selection
1083 void recolor_selection(int color)
1087 struct Brush *brush;
1088 GnomeCanvasGroup *group;
1090 if (ui.selection == NULL) return;
1092 undo->type = ITEM_REPAINTSEL;
1093 undo->itemlist = NULL;
1094 undo->auxlist = NULL;
1095 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1096 item = (struct Item *)itemlist->data;
1097 if (item->type != ITEM_STROKE && item->type != ITEM_TEXT) continue;
1098 if (item->type == ITEM_STROKE && item->brush.tool_type!=TOOL_PEN) continue;
1099 // store info for undo
1100 undo->itemlist = g_list_append(undo->itemlist, item);
1101 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1102 g_memmove(brush, &(item->brush), sizeof(struct Brush));
1103 undo->auxlist = g_list_append(undo->auxlist, brush);
1104 // repaint the stroke
1105 item->brush.color_no = color;
1106 item->brush.color_rgba = predef_colors_rgba[color];
1107 if (item->canvas_item!=NULL) {
1108 if (!item->brush.variable_width)
1109 gnome_canvas_item_set(item->canvas_item,
1110 "fill-color-rgba", item->brush.color_rgba, NULL);
1112 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1113 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1114 make_canvas_item_one(group, item);
1120 void rethicken_selection(int val)
1124 struct Brush *brush;
1125 GnomeCanvasGroup *group;
1127 if (ui.selection == NULL) return;
1129 undo->type = ITEM_REPAINTSEL;
1130 undo->itemlist = NULL;
1131 undo->auxlist = NULL;
1132 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1133 item = (struct Item *)itemlist->data;
1134 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
1135 // store info for undo
1136 undo->itemlist = g_list_append(undo->itemlist, item);
1137 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1138 g_memmove(brush, &(item->brush), sizeof(struct Brush));
1139 undo->auxlist = g_list_append(undo->auxlist, brush);
1140 // repaint the stroke
1141 item->brush.thickness_no = val;
1142 item->brush.thickness = predef_thickness[TOOL_PEN][val];
1143 if (item->canvas_item!=NULL) {
1144 if (!item->brush.variable_width)
1145 gnome_canvas_item_set(item->canvas_item,
1146 "width-units", item->brush.thickness, NULL);
1148 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1149 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1150 item->brush.variable_width = FALSE;
1151 make_canvas_item_one(group, item);
1157 gboolean do_hand_scrollto(gpointer data)
1159 ui.hand_scrollto_pending = FALSE;
1160 gnome_canvas_scroll_to(canvas, ui.hand_scrollto_cx, ui.hand_scrollto_cy);
1164 void do_hand(GdkEvent *event)
1169 get_pointer_coords(event, pt);
1170 pt[0] += ui.cur_page->hoffset;
1171 pt[1] += ui.cur_page->voffset;
1172 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1173 ui.hand_scrollto_cx = cx - (pt[0]-ui.hand_refpt[0])*ui.zoom;
1174 ui.hand_scrollto_cy = cy - (pt[1]-ui.hand_refpt[1])*ui.zoom;
1175 if (!ui.hand_scrollto_pending) g_idle_add(do_hand_scrollto, NULL);
1176 ui.hand_scrollto_pending = TRUE;
1179 /************ TEXT FUNCTIONS **************/
1181 // to make it easier to copy/paste at end of text box
1182 #define WIDGET_RIGHT_MARGIN 10
1184 void resize_textview(gpointer *toplevel, gpointer *data)
1189 /* when the text changes, resize the GtkTextView accordingly */
1190 if (ui.cur_item_type!=ITEM_TEXT) return;
1191 w = GTK_TEXT_VIEW(ui.cur_item->widget);
1192 width = w->width + WIDGET_RIGHT_MARGIN;
1194 gnome_canvas_item_set(ui.cur_item->canvas_item,
1195 "size-pixels", TRUE,
1196 "width", (gdouble)width, "height", (gdouble)height, NULL);
1197 ui.cur_item->bbox.right = ui.cur_item->bbox.left + width/ui.zoom;
1198 ui.cur_item->bbox.bottom = ui.cur_item->bbox.top + height/ui.zoom;
1201 void start_text(GdkEvent *event, struct Item *item)
1204 GtkTextBuffer *buffer;
1205 GnomeCanvasItem *canvas_item;
1206 PangoFontDescription *font_desc;
1209 get_pointer_coords(event, pt);
1210 ui.cur_item_type = ITEM_TEXT;
1213 item = g_new(struct Item, 1);
1215 item->canvas_item = NULL;
1216 item->bbox.left = pt[0];
1217 item->bbox.top = pt[1];
1218 item->bbox.right = ui.cur_page->width;
1219 item->bbox.bottom = pt[1]+100.;
1220 item->font_name = g_strdup(ui.font_name);
1221 item->font_size = ui.font_size;
1222 g_memmove(&(item->brush), ui.cur_brush, sizeof(struct Brush));
1223 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1224 ui.cur_layer->nitems++;
1227 item->type = ITEM_TEMP_TEXT;
1230 font_desc = pango_font_description_from_string(item->font_name);
1231 pango_font_description_set_absolute_size(font_desc,
1232 item->font_size*ui.zoom*PANGO_SCALE);
1233 item->widget = gtk_text_view_new();
1234 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(item->widget));
1235 if (item->text!=NULL)
1236 gtk_text_buffer_set_text(buffer, item->text, -1);
1237 gtk_widget_modify_font(item->widget, font_desc);
1238 rgb_to_gdkcolor(item->brush.color_rgba, &color);
1239 gtk_widget_modify_text(item->widget, GTK_STATE_NORMAL, &color);
1240 pango_font_description_free(font_desc);
1242 canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1243 gnome_canvas_widget_get_type(),
1244 "x", item->bbox.left, "y", item->bbox.top,
1245 "width", item->bbox.right-item->bbox.left,
1246 "height", item->bbox.bottom-item->bbox.top,
1247 "widget", item->widget, NULL);
1248 // TODO: width/height?
1249 if (item->canvas_item!=NULL) {
1250 lower_canvas_item_to(ui.cur_layer->group, canvas_item, item->canvas_item);
1251 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1253 item->canvas_item = canvas_item;
1255 gtk_widget_show(item->widget);
1256 gtk_widget_grab_focus(item->widget);
1257 ui.resize_signal_handler =
1258 g_signal_connect((gpointer) winMain, "check_resize",
1259 G_CALLBACK(resize_textview), NULL);
1260 update_font_button();
1261 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), FALSE);
1262 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), FALSE);
1267 GtkTextBuffer *buffer;
1268 GtkTextIter start, end;
1270 struct UndoErasureData *erasure;
1271 GnomeCanvasItem *tmpitem;
1273 if (ui.cur_item_type!=ITEM_TEXT) return; // nothing for us to do!
1275 // finalize the text that's been edited...
1276 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.cur_item->widget));
1277 gtk_text_buffer_get_bounds(buffer, &start, &end);
1278 ui.cur_item->type = ITEM_TEXT;
1279 new_text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
1280 ui.cur_item_type = ITEM_NONE;
1281 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), TRUE);
1282 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), TRUE);
1284 if (strlen(new_text)==0) { // erase object and cancel
1286 g_signal_handler_disconnect(winMain, ui.resize_signal_handler);
1287 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
1288 ui.cur_item->canvas_item = NULL;
1289 if (ui.cur_item->text == NULL) // nothing happened
1290 g_free(ui.cur_item->font_name);
1291 else { // treat this as an erasure
1293 undo->type = ITEM_ERASURE;
1294 undo->layer = ui.cur_layer;
1295 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
1296 erasure->item = ui.cur_item;
1297 erasure->npos = g_list_index(ui.cur_layer->items, ui.cur_item);
1299 erasure->replacement_items = NULL;
1300 undo->erasurelist = g_list_append(NULL, erasure);
1302 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, ui.cur_item);
1303 ui.cur_layer->nitems--;
1309 if (ui.cur_item->text==NULL || strcmp(ui.cur_item->text, new_text)) {
1311 if (ui.cur_item->text == NULL) undo->type = ITEM_TEXT;
1312 else undo->type = ITEM_TEXT_EDIT;
1313 undo->layer = ui.cur_layer;
1314 undo->item = ui.cur_item;
1315 undo->str = ui.cur_item->text;
1317 else g_free(ui.cur_item->text);
1319 ui.cur_item->text = new_text;
1320 ui.cur_item->widget = NULL;
1321 // replace the canvas item
1322 tmpitem = ui.cur_item->canvas_item;
1323 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
1324 update_item_bbox(ui.cur_item);
1325 lower_canvas_item_to(ui.cur_layer->group, ui.cur_item->canvas_item, tmpitem);
1326 gtk_object_destroy(GTK_OBJECT(tmpitem));
1329 /* update the items in the canvas so they're of the right font size */
1331 void update_text_item_displayfont(struct Item *item)
1333 PangoFontDescription *font_desc;
1335 if (item->type != ITEM_TEXT && item->type != ITEM_TEMP_TEXT) return;
1336 if (item->canvas_item==NULL) return;
1337 font_desc = pango_font_description_from_string(item->font_name);
1338 pango_font_description_set_absolute_size(font_desc,
1339 item->font_size*ui.zoom*PANGO_SCALE);
1340 if (item->type == ITEM_TEMP_TEXT)
1341 gtk_widget_modify_font(item->widget, font_desc);
1343 gnome_canvas_item_set(item->canvas_item, "font-desc", font_desc, NULL);
1344 update_item_bbox(item);
1346 pango_font_description_free(font_desc);
1349 void rescale_text_items(void)
1351 GList *pagelist, *layerlist, *itemlist;
1353 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next)
1354 for (layerlist = ((struct Page *)pagelist->data)->layers; layerlist!=NULL; layerlist = layerlist->next)
1355 for (itemlist = ((struct Layer *)layerlist->data)->items; itemlist!=NULL; itemlist = itemlist->next)
1356 update_text_item_displayfont((struct Item *)itemlist->data);
1359 struct Item *click_is_in_text(struct Layer *layer, double x, double y)
1362 struct Item *item, *val;
1365 for (itemlist = layer->items; itemlist!=NULL; itemlist = itemlist->next) {
1366 item = (struct Item *)itemlist->data;
1367 if (item->type != ITEM_TEXT) continue;
1368 if (x<item->bbox.left || x>item->bbox.right) continue;
1369 if (y<item->bbox.top || y>item->bbox.bottom) continue;
1375 void refont_text_item(struct Item *item, gchar *font_name, double font_size)
1377 if (!strcmp(font_name, item->font_name) && font_size==item->font_size) return;
1378 if (item->text!=NULL) {
1380 undo->type = ITEM_TEXT_ATTRIB;
1382 undo->str = item->font_name;
1383 undo->val_x = item->font_size;
1384 undo->brush = (struct Brush *)g_memdup(&(item->brush), sizeof(struct Brush));
1386 else g_free(item->font_name);
1387 item->font_name = g_strdup(font_name);
1388 if (font_size>0.) item->font_size = font_size;
1389 update_text_item_displayfont(item);
1392 void process_font_sel(gchar *str)
1400 p = strrchr(str, ' ');
1402 size = g_strtod(p+1, &q);
1403 if (*q!=0 || size<1.) size=0.;
1408 g_free(ui.font_name);
1410 if (size>0.) ui.font_size = size;
1412 // if there's a current text item, re-font it
1413 if (ui.cur_item_type == ITEM_TEXT) {
1414 refont_text_item(ui.cur_item, str, size);
1415 undo_cont = (ui.cur_item->text!=NULL);
1417 // if there's a current selection, re-font it
1418 if (ui.selection!=NULL)
1419 for (list=ui.selection->items; list!=NULL; list=list->next) {
1420 it = (struct Item *)list->data;
1421 if (it->type == ITEM_TEXT) {
1422 if (undo_cont) undo->multiop |= MULTIOP_CONT_REDO;
1423 refont_text_item(it, str, size);
1424 if (undo_cont) undo->multiop |= MULTIOP_CONT_UNDO;
1428 update_font_button();