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);
611 update_cursor_for_resize(pt);
618 void start_vertspace(GdkEvent *event)
625 ui.cur_item_type = ITEM_MOVESEL_VERT;
626 ui.selection = g_new(struct Selection, 1);
627 ui.selection->type = ITEM_MOVESEL_VERT;
628 ui.selection->items = NULL;
629 ui.selection->layer = ui.cur_layer;
631 get_pointer_coords(event, pt);
632 ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1];
633 for (itemlist = ui.cur_layer->items; itemlist!=NULL; itemlist = itemlist->next) {
634 item = (struct Item *)itemlist->data;
635 if (item->bbox.top >= pt[1]) {
636 ui.selection->items = g_list_append(ui.selection->items, item);
637 if (item->bbox.bottom > ui.selection->bbox.bottom)
638 ui.selection->bbox.bottom = item->bbox.bottom;
642 ui.selection->anchor_x = ui.selection->last_x = 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 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
649 gnome_canvas_rect_get_type(), "width-pixels", 1,
650 "outline-color-rgba", 0x000000ff,
651 "fill-color-rgba", 0x80808040,
652 "x1", -100.0, "x2", ui.cur_page->width+100, "y1", pt[1], "y2", pt[1], NULL);
656 void continue_movesel(GdkEvent *event)
658 double pt[2], dx, dy, upmargin;
662 struct Page *tmppage;
664 get_pointer_coords(event, pt);
665 if (ui.cur_item_type == ITEM_MOVESEL_VERT) pt[0] = 0;
666 pt[1] += ui.selection->move_pagedelta;
668 // check for page jumps
669 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
670 upmargin = ui.selection->bbox.bottom - ui.selection->bbox.top;
671 else upmargin = VIEW_CONTINUOUS_SKIP;
672 tmppageno = ui.selection->move_pageno;
673 tmppage = g_list_nth_data(journal.pages, tmppageno);
674 while (ui.view_continuous && (pt[1] < - upmargin)) {
675 if (tmppageno == 0) break;
677 tmppage = g_list_nth_data(journal.pages, tmppageno);
678 pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
679 ui.selection->move_pagedelta += tmppage->height + VIEW_CONTINUOUS_SKIP;
681 while (ui.view_continuous && (pt[1] > tmppage->height+VIEW_CONTINUOUS_SKIP)) {
682 if (tmppageno == journal.npages-1) break;
683 pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
684 ui.selection->move_pagedelta -= tmppage->height + VIEW_CONTINUOUS_SKIP;
686 tmppage = g_list_nth_data(journal.pages, tmppageno);
689 if (tmppageno != ui.selection->move_pageno) {
690 // move to a new page !
691 ui.selection->move_pageno = tmppageno;
692 if (tmppageno == ui.selection->orig_pageno)
693 ui.selection->move_layer = ui.selection->layer;
695 ui.selection->move_layer = (struct Layer *)(g_list_last(
696 ((struct Page *)g_list_nth_data(journal.pages, tmppageno))->layers)->data);
697 gnome_canvas_item_reparent(ui.selection->canvas_item, ui.selection->move_layer->group);
698 for (list = ui.selection->items; list!=NULL; list = list->next) {
699 item = (struct Item *)list->data;
700 if (item->canvas_item!=NULL)
701 gnome_canvas_item_reparent(item->canvas_item, ui.selection->move_layer->group);
703 // avoid a refresh bug
704 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.selection->move_layer->group), 0., 0.);
705 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
706 gnome_canvas_item_set(ui.selection->canvas_item,
707 "x2", tmppage->width+100,
708 "y1", ui.selection->anchor_y+ui.selection->move_pagedelta, NULL);
711 // now, process things normally
713 dx = pt[0] - ui.selection->last_x;
714 dy = pt[1] - ui.selection->last_y;
715 if (hypot(dx,dy) < 1) return; // don't move subpixel
716 ui.selection->last_x = pt[0];
717 ui.selection->last_y = pt[1];
719 // move the canvas items
720 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
721 gnome_canvas_item_set(ui.selection->canvas_item, "y2", pt[1], NULL);
723 gnome_canvas_item_move(ui.selection->canvas_item, dx, dy);
725 for (list = ui.selection->items; list != NULL; list = list->next) {
726 item = (struct Item *)list->data;
727 if (item->canvas_item != NULL)
728 gnome_canvas_item_move(item->canvas_item, dx, dy);
732 void continue_resizesel(GdkEvent *event)
736 get_pointer_coords(event, pt);
738 if (ui.selection->resizing_top) ui.selection->new_y1 = pt[1];
739 if (ui.selection->resizing_bottom) ui.selection->new_y2 = pt[1];
740 if (ui.selection->resizing_left) ui.selection->new_x1 = pt[0];
741 if (ui.selection->resizing_right) ui.selection->new_x2 = pt[0];
743 gnome_canvas_item_set(ui.selection->canvas_item,
744 "x1", ui.selection->new_x1, "x2", ui.selection->new_x2,
745 "y1", ui.selection->new_y1, "y2", ui.selection->new_y2, NULL);
748 void finalize_movesel(void)
752 if (ui.selection->items != NULL) {
754 undo->type = ITEM_MOVESEL;
755 undo->itemlist = g_list_copy(ui.selection->items);
756 undo->val_x = ui.selection->last_x - ui.selection->anchor_x;
757 undo->val_y = ui.selection->last_y - ui.selection->anchor_y;
758 undo->layer = ui.selection->layer;
759 undo->layer2 = ui.selection->move_layer;
760 undo->auxlist = NULL;
761 // build auxlist = pointers to Item's just before ours (for depths)
762 for (list = ui.selection->items; list!=NULL; list = list->next) {
763 link = g_list_find(ui.selection->layer->items, list->data);
764 if (link!=NULL) link = link->prev;
765 undo->auxlist = g_list_append(undo->auxlist, ((link!=NULL) ? link->data : NULL));
767 ui.selection->layer = ui.selection->move_layer;
768 move_journal_items_by(undo->itemlist, undo->val_x, undo->val_y,
769 undo->layer, undo->layer2,
770 (undo->layer == undo->layer2)?undo->auxlist:NULL);
773 if (ui.selection->move_pageno!=ui.selection->orig_pageno)
774 do_switch_page(ui.selection->move_pageno, FALSE, FALSE);
776 if (ui.cur_item_type == ITEM_MOVESEL_VERT)
779 ui.selection->bbox.left += undo->val_x;
780 ui.selection->bbox.right += undo->val_x;
781 ui.selection->bbox.top += undo->val_y;
782 ui.selection->bbox.bottom += undo->val_y;
783 make_dashed(ui.selection->canvas_item);
784 /* update selection box object's offset to be trivial, and its internal
785 coordinates to agree with those of the bbox; need this since resize
786 operations will modify the box by setting its coordinates directly */
787 gnome_canvas_item_affine_absolute(ui.selection->canvas_item, NULL);
788 gnome_canvas_item_set(ui.selection->canvas_item,
789 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
790 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
792 ui.cur_item_type = ITEM_NONE;
796 #define SCALING_EPSILON 0.001
798 void finalize_resizesel(void)
802 // build the affine transformation
803 double offset_x, offset_y, scaling_x, scaling_y;
804 scaling_x = (ui.selection->new_x2 - ui.selection->new_x1) /
805 (ui.selection->bbox.right - ui.selection->bbox.left);
806 scaling_y = (ui.selection->new_y2 - ui.selection->new_y1) /
807 (ui.selection->bbox.bottom - ui.selection->bbox.top);
808 // couldn't undo a resize-by-zero...
809 if (fabs(scaling_x)<SCALING_EPSILON) scaling_x = SCALING_EPSILON;
810 if (fabs(scaling_y)<SCALING_EPSILON) scaling_y = SCALING_EPSILON;
811 offset_x = ui.selection->new_x1 - ui.selection->bbox.left * scaling_x;
812 offset_y = ui.selection->new_y1 - ui.selection->bbox.top * scaling_y;
814 if (ui.selection->items != NULL) {
815 // create the undo information
817 undo->type = ITEM_RESIZESEL;
818 undo->itemlist = g_list_copy(ui.selection->items);
819 undo->auxlist = NULL;
821 undo->scaling_x = scaling_x;
822 undo->scaling_y = scaling_y;
823 undo->val_x = offset_x;
824 undo->val_y = offset_y;
826 // actually do the resize operation
827 resize_journal_items_by(ui.selection->items, scaling_x, scaling_y, offset_x, offset_y);
831 ui.selection->bbox.left = ui.selection->new_x1;
832 ui.selection->bbox.right = ui.selection->new_x2;
834 ui.selection->bbox.left = ui.selection->new_x2;
835 ui.selection->bbox.right = ui.selection->new_x1;
838 ui.selection->bbox.top = ui.selection->new_y1;
839 ui.selection->bbox.bottom = ui.selection->new_y2;
841 ui.selection->bbox.top = ui.selection->new_y2;
842 ui.selection->bbox.bottom = ui.selection->new_y1;
844 make_dashed(ui.selection->canvas_item);
846 ui.cur_item_type = ITEM_NONE;
850 void selection_delete(void)
852 struct UndoErasureData *erasure;
856 if (ui.selection == NULL) return;
858 undo->type = ITEM_ERASURE;
859 undo->layer = ui.selection->layer;
860 undo->erasurelist = NULL;
861 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
862 item = (struct Item *)itemlist->data;
863 if (item->canvas_item!=NULL)
864 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
865 erasure = g_new(struct UndoErasureData, 1);
866 erasure->item = item;
867 erasure->npos = g_list_index(ui.selection->layer->items, item);
869 erasure->replacement_items = NULL;
870 ui.selection->layer->items = g_list_remove(ui.selection->layer->items, item);
871 ui.selection->layer->nitems--;
872 undo->erasurelist = g_list_prepend(undo->erasurelist, erasure);
876 /* NOTE: the erasurelist is built backwards; this guarantees that,
877 upon undo, the erasure->npos fields give the correct position
878 where each item should be reinserted as the list is traversed in
879 the forward direction */
882 void callback_clipboard_get(GtkClipboard *clipboard,
883 GtkSelectionData *selection_data,
884 guint info, gpointer user_data)
888 g_memmove(&length, user_data, sizeof(int));
889 gtk_selection_data_set(selection_data,
890 gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
893 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
898 void selection_to_clip(void)
900 int bufsz, nitems, val;
904 GtkTargetEntry target;
906 if (ui.selection == NULL) return;
907 bufsz = 2*sizeof(int) // bufsz, nitems
908 + sizeof(struct BBox); // bbox
910 for (list = ui.selection->items; list != NULL; list = list->next) {
911 item = (struct Item *)list->data;
913 if (item->type == ITEM_STROKE) {
914 bufsz+= sizeof(int) // type
915 + sizeof(struct Brush) // brush
916 + sizeof(int) // num_points
917 + 2*item->path->num_points*sizeof(double); // the points
918 if (item->brush.variable_width)
919 bufsz += (item->path->num_points-1)*sizeof(double); // the widths
921 else if (item->type == ITEM_TEXT) {
922 bufsz+= sizeof(int) // type
923 + sizeof(struct Brush) // brush
924 + 2*sizeof(double) // bbox upper-left
925 + sizeof(int) // text len
926 + strlen(item->text)+1 // text
927 + sizeof(int) // font_name len
928 + strlen(item->font_name)+1 // font_name
929 + sizeof(double); // font_size
931 else bufsz+= sizeof(int); // type
933 p = buf = g_malloc(bufsz);
934 g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
935 g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
936 g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
937 for (list = ui.selection->items; list != NULL; list = list->next) {
938 item = (struct Item *)list->data;
939 g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
940 if (item->type == ITEM_STROKE) {
941 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
942 g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
943 g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
944 p+= 2*item->path->num_points*sizeof(double);
945 if (item->brush.variable_width) {
946 g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
947 p+= (item->path->num_points-1)*sizeof(double);
950 if (item->type == ITEM_TEXT) {
951 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
952 g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
953 g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
954 val = strlen(item->text);
955 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
956 g_memmove(p, item->text, val+1); p+= val+1;
957 val = strlen(item->font_name);
958 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
959 g_memmove(p, item->font_name, val+1); p+= val+1;
960 g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
964 target.target = "_XOURNAL";
968 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
970 callback_clipboard_get, callback_clipboard_clear, buf);
974 void clipboard_paste(void)
976 GtkSelectionData *sel_data;
978 int nitems, npts, i, len;
980 double hoffset, voffset, cx, cy;
984 if (ui.cur_layer == NULL) return;
986 ui.cur_item_type = ITEM_PASTE;
987 sel_data = gtk_clipboard_wait_for_contents(
988 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
989 gdk_atom_intern("_XOURNAL", FALSE));
990 ui.cur_item_type = ITEM_NONE;
991 if (sel_data == NULL) return; // paste failed
995 ui.selection = g_new(struct Selection, 1);
996 p = sel_data->data + sizeof(int);
997 g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
998 ui.selection->type = ITEM_SELECTRECT;
999 ui.selection->layer = ui.cur_layer;
1000 g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
1001 ui.selection->items = NULL;
1003 // find by how much we translate the pasted selection
1004 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
1005 gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
1006 gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
1007 cx -= ui.cur_page->hoffset;
1008 cy -= ui.cur_page->voffset;
1009 if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
1010 cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1011 if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
1012 cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
1013 if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
1014 cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1015 if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
1016 cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
1017 hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
1018 voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
1019 ui.selection->bbox.left += hoffset;
1020 ui.selection->bbox.right += hoffset;
1021 ui.selection->bbox.top += voffset;
1022 ui.selection->bbox.bottom += voffset;
1024 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1025 gnome_canvas_rect_get_type(), "width-pixels", 1,
1026 "outline-color-rgba", 0x000000ff,
1027 "fill-color-rgba", 0x80808040,
1028 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
1029 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
1030 make_dashed(ui.selection->canvas_item);
1032 while (nitems-- > 0) {
1033 item = g_new(struct Item, 1);
1034 ui.selection->items = g_list_append(ui.selection->items, item);
1035 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1036 ui.cur_layer->nitems++;
1037 g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
1038 if (item->type == ITEM_STROKE) {
1039 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1040 g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
1041 item->path = gnome_canvas_points_new(npts);
1043 for (i=0; i<npts; i++) {
1044 item->path->coords[2*i] = pf[2*i] + hoffset;
1045 item->path->coords[2*i+1] = pf[2*i+1] + voffset;
1047 p+= 2*item->path->num_points*sizeof(double);
1048 if (item->brush.variable_width) {
1049 item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
1050 p+= (item->path->num_points-1)*sizeof(double);
1052 else item->widths = NULL;
1053 update_item_bbox(item);
1054 make_canvas_item_one(ui.cur_layer->group, item);
1056 if (item->type == ITEM_TEXT) {
1057 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
1058 g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
1059 g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
1060 item->bbox.left += hoffset;
1061 item->bbox.top += voffset;
1062 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1063 item->text = g_malloc(len+1);
1064 g_memmove(item->text, p, len+1); p+= len+1;
1065 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
1066 item->font_name = g_malloc(len+1);
1067 g_memmove(item->font_name, p, len+1); p+= len+1;
1068 g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
1069 make_canvas_item_one(ui.cur_layer->group, item);
1074 undo->type = ITEM_PASTE;
1075 undo->layer = ui.cur_layer;
1076 undo->itemlist = g_list_copy(ui.selection->items);
1078 gtk_selection_data_free(sel_data);
1079 update_copy_paste_enabled();
1080 update_color_menu();
1081 update_thickness_buttons();
1082 update_color_buttons();
1083 update_font_button();
1084 update_cursor(); // FIXME: can't know if pointer is within selection!
1087 // modify the color or thickness of pen strokes in a selection
1089 void recolor_selection(int color_no, guint color_rgba)
1093 struct Brush *brush;
1094 GnomeCanvasGroup *group;
1096 if (ui.selection == NULL) return;
1098 undo->type = ITEM_REPAINTSEL;
1099 undo->itemlist = NULL;
1100 undo->auxlist = NULL;
1101 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1102 item = (struct Item *)itemlist->data;
1103 if (item->type != ITEM_STROKE && item->type != ITEM_TEXT) continue;
1104 if (item->type == ITEM_STROKE && item->brush.tool_type!=TOOL_PEN) continue;
1105 // store info for undo
1106 undo->itemlist = g_list_append(undo->itemlist, item);
1107 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1108 g_memmove(brush, &(item->brush), sizeof(struct Brush));
1109 undo->auxlist = g_list_append(undo->auxlist, brush);
1110 // repaint the stroke
1111 item->brush.color_no = color_no;
1112 item->brush.color_rgba = color_rgba;
1113 if (item->canvas_item!=NULL) {
1114 if (!item->brush.variable_width)
1115 gnome_canvas_item_set(item->canvas_item,
1116 "fill-color-rgba", item->brush.color_rgba, NULL);
1118 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1119 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1120 make_canvas_item_one(group, item);
1126 void rethicken_selection(int val)
1130 struct Brush *brush;
1131 GnomeCanvasGroup *group;
1133 if (ui.selection == NULL) return;
1135 undo->type = ITEM_REPAINTSEL;
1136 undo->itemlist = NULL;
1137 undo->auxlist = NULL;
1138 for (itemlist = ui.selection->items; itemlist!=NULL; itemlist = itemlist->next) {
1139 item = (struct Item *)itemlist->data;
1140 if (item->type != ITEM_STROKE || item->brush.tool_type!=TOOL_PEN) continue;
1141 // store info for undo
1142 undo->itemlist = g_list_append(undo->itemlist, item);
1143 brush = (struct Brush *)g_malloc(sizeof(struct Brush));
1144 g_memmove(brush, &(item->brush), sizeof(struct Brush));
1145 undo->auxlist = g_list_append(undo->auxlist, brush);
1146 // repaint the stroke
1147 item->brush.thickness_no = val;
1148 item->brush.thickness = predef_thickness[TOOL_PEN][val];
1149 if (item->canvas_item!=NULL) {
1150 if (!item->brush.variable_width)
1151 gnome_canvas_item_set(item->canvas_item,
1152 "width-units", item->brush.thickness, NULL);
1154 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1155 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1156 item->brush.variable_width = FALSE;
1157 make_canvas_item_one(group, item);
1163 gboolean do_hand_scrollto(gpointer data)
1165 ui.hand_scrollto_pending = FALSE;
1166 gnome_canvas_scroll_to(canvas, ui.hand_scrollto_cx, ui.hand_scrollto_cy);
1170 void do_hand(GdkEvent *event)
1175 get_pointer_coords(event, pt);
1176 pt[0] += ui.cur_page->hoffset;
1177 pt[1] += ui.cur_page->voffset;
1178 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1179 ui.hand_scrollto_cx = cx - (pt[0]-ui.hand_refpt[0])*ui.zoom;
1180 ui.hand_scrollto_cy = cy - (pt[1]-ui.hand_refpt[1])*ui.zoom;
1181 if (!ui.hand_scrollto_pending) g_idle_add(do_hand_scrollto, NULL);
1182 ui.hand_scrollto_pending = TRUE;
1185 /************ TEXT FUNCTIONS **************/
1187 // to make it easier to copy/paste at end of text box
1188 #define WIDGET_RIGHT_MARGIN 10
1190 void resize_textview(gpointer *toplevel, gpointer *data)
1195 /* when the text changes, resize the GtkTextView accordingly */
1196 if (ui.cur_item_type!=ITEM_TEXT) return;
1197 w = GTK_TEXT_VIEW(ui.cur_item->widget);
1198 width = w->width + WIDGET_RIGHT_MARGIN;
1200 gnome_canvas_item_set(ui.cur_item->canvas_item,
1201 "size-pixels", TRUE,
1202 "width", (gdouble)width, "height", (gdouble)height, NULL);
1203 ui.cur_item->bbox.right = ui.cur_item->bbox.left + width/ui.zoom;
1204 ui.cur_item->bbox.bottom = ui.cur_item->bbox.top + height/ui.zoom;
1207 void start_text(GdkEvent *event, struct Item *item)
1210 GtkTextBuffer *buffer;
1211 GnomeCanvasItem *canvas_item;
1212 PangoFontDescription *font_desc;
1215 get_pointer_coords(event, pt);
1216 ui.cur_item_type = ITEM_TEXT;
1218 // HACK TO BYPASS GTK+ 2.17 issues
1219 if (!gtk_check_version(2, 17, 0)) {
1220 /* first we make *all* canvas subwindows become XInput aware,
1221 so that it'll be safe to disable XInput later on... (!!!) */
1222 gtk_widget_set_extension_events(GTK_WIDGET (canvas),
1223 ui.use_xinput?GDK_EXTENSION_EVENTS_ALL:GDK_EXTENSION_EVENTS_NONE);
1224 /* then we ask the canvas's leave-notify handler to disable
1225 xinput when it's safe to do so... */
1226 ui.need_emergency_disable_xinput = TRUE;
1230 item = g_new(struct Item, 1);
1232 item->canvas_item = NULL;
1233 item->bbox.left = pt[0];
1234 item->bbox.top = pt[1];
1235 item->bbox.right = ui.cur_page->width;
1236 item->bbox.bottom = pt[1]+100.;
1237 item->font_name = g_strdup(ui.font_name);
1238 item->font_size = ui.font_size;
1239 g_memmove(&(item->brush), ui.cur_brush, sizeof(struct Brush));
1240 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
1241 ui.cur_layer->nitems++;
1244 item->type = ITEM_TEMP_TEXT;
1247 font_desc = pango_font_description_from_string(item->font_name);
1248 pango_font_description_set_absolute_size(font_desc,
1249 item->font_size*ui.zoom*PANGO_SCALE);
1250 item->widget = gtk_text_view_new();
1251 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(item->widget));
1252 if (item->text!=NULL)
1253 gtk_text_buffer_set_text(buffer, item->text, -1);
1254 gtk_widget_modify_font(item->widget, font_desc);
1255 rgb_to_gdkcolor(item->brush.color_rgba, &color);
1256 gtk_widget_modify_text(item->widget, GTK_STATE_NORMAL, &color);
1257 pango_font_description_free(font_desc);
1259 canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
1260 gnome_canvas_widget_get_type(),
1261 "x", item->bbox.left, "y", item->bbox.top,
1262 "width", item->bbox.right-item->bbox.left,
1263 "height", item->bbox.bottom-item->bbox.top,
1264 "widget", item->widget, NULL);
1265 // TODO: width/height?
1266 if (item->canvas_item!=NULL) {
1267 lower_canvas_item_to(ui.cur_layer->group, canvas_item, item->canvas_item);
1268 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1270 item->canvas_item = canvas_item;
1272 gtk_widget_show(item->widget);
1273 gtk_widget_grab_focus(item->widget);
1274 ui.resize_signal_handler =
1275 g_signal_connect((gpointer) winMain, "check_resize",
1276 G_CALLBACK(resize_textview), NULL);
1277 update_font_button();
1278 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), FALSE);
1279 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), FALSE);
1284 GtkTextBuffer *buffer;
1285 GtkTextIter start, end;
1287 struct UndoErasureData *erasure;
1288 GnomeCanvasItem *tmpitem;
1290 if (ui.cur_item_type!=ITEM_TEXT) return; // nothing for us to do!
1292 // HACK TO BYPASS GTK+ 2.17 issues
1293 if (!gtk_check_version(2, 17, 0)) {
1294 // re-enable XInput if needed (we disabled it during text edition)
1295 gdk_input_set_extension_events(GTK_WIDGET(canvas)->window,
1296 GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK,
1297 ui.use_xinput?GDK_EXTENSION_EVENTS_ALL:GDK_EXTENSION_EVENTS_NONE);
1300 // finalize the text that's been edited...
1301 buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(ui.cur_item->widget));
1302 gtk_text_buffer_get_bounds(buffer, &start, &end);
1303 ui.cur_item->type = ITEM_TEXT;
1304 new_text = gtk_text_buffer_get_text(buffer, &start, &end, TRUE);
1305 ui.cur_item_type = ITEM_NONE;
1306 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), TRUE);
1307 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), TRUE);
1309 if (strlen(new_text)==0) { // erase object and cancel
1311 g_signal_handler_disconnect(winMain, ui.resize_signal_handler);
1312 gtk_object_destroy(GTK_OBJECT(ui.cur_item->canvas_item));
1313 ui.cur_item->canvas_item = NULL;
1314 if (ui.cur_item->text == NULL) // nothing happened
1315 g_free(ui.cur_item->font_name);
1316 else { // treat this as an erasure
1318 undo->type = ITEM_ERASURE;
1319 undo->layer = ui.cur_layer;
1320 erasure = (struct UndoErasureData *)g_malloc(sizeof(struct UndoErasureData));
1321 erasure->item = ui.cur_item;
1322 erasure->npos = g_list_index(ui.cur_layer->items, ui.cur_item);
1324 erasure->replacement_items = NULL;
1325 undo->erasurelist = g_list_append(NULL, erasure);
1327 ui.cur_layer->items = g_list_remove(ui.cur_layer->items, ui.cur_item);
1328 ui.cur_layer->nitems--;
1334 if (ui.cur_item->text==NULL || strcmp(ui.cur_item->text, new_text)) {
1336 if (ui.cur_item->text == NULL) undo->type = ITEM_TEXT;
1337 else undo->type = ITEM_TEXT_EDIT;
1338 undo->layer = ui.cur_layer;
1339 undo->item = ui.cur_item;
1340 undo->str = ui.cur_item->text;
1342 else g_free(ui.cur_item->text);
1344 ui.cur_item->text = new_text;
1345 ui.cur_item->widget = NULL;
1346 // replace the canvas item
1347 tmpitem = ui.cur_item->canvas_item;
1348 make_canvas_item_one(ui.cur_layer->group, ui.cur_item);
1349 update_item_bbox(ui.cur_item);
1350 lower_canvas_item_to(ui.cur_layer->group, ui.cur_item->canvas_item, tmpitem);
1351 gtk_object_destroy(GTK_OBJECT(tmpitem));
1354 /* update the items in the canvas so they're of the right font size */
1356 void update_text_item_displayfont(struct Item *item)
1358 PangoFontDescription *font_desc;
1360 if (item->type != ITEM_TEXT && item->type != ITEM_TEMP_TEXT) return;
1361 if (item->canvas_item==NULL) return;
1362 font_desc = pango_font_description_from_string(item->font_name);
1363 pango_font_description_set_absolute_size(font_desc,
1364 item->font_size*ui.zoom*PANGO_SCALE);
1365 if (item->type == ITEM_TEMP_TEXT)
1366 gtk_widget_modify_font(item->widget, font_desc);
1368 gnome_canvas_item_set(item->canvas_item, "font-desc", font_desc, NULL);
1369 update_item_bbox(item);
1371 pango_font_description_free(font_desc);
1374 void rescale_text_items(void)
1376 GList *pagelist, *layerlist, *itemlist;
1378 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next)
1379 for (layerlist = ((struct Page *)pagelist->data)->layers; layerlist!=NULL; layerlist = layerlist->next)
1380 for (itemlist = ((struct Layer *)layerlist->data)->items; itemlist!=NULL; itemlist = itemlist->next)
1381 update_text_item_displayfont((struct Item *)itemlist->data);
1384 struct Item *click_is_in_text(struct Layer *layer, double x, double y)
1387 struct Item *item, *val;
1390 for (itemlist = layer->items; itemlist!=NULL; itemlist = itemlist->next) {
1391 item = (struct Item *)itemlist->data;
1392 if (item->type != ITEM_TEXT) continue;
1393 if (x<item->bbox.left || x>item->bbox.right) continue;
1394 if (y<item->bbox.top || y>item->bbox.bottom) continue;
1400 void refont_text_item(struct Item *item, gchar *font_name, double font_size)
1402 if (!strcmp(font_name, item->font_name) && font_size==item->font_size) return;
1403 if (item->text!=NULL) {
1405 undo->type = ITEM_TEXT_ATTRIB;
1407 undo->str = item->font_name;
1408 undo->val_x = item->font_size;
1409 undo->brush = (struct Brush *)g_memdup(&(item->brush), sizeof(struct Brush));
1411 else g_free(item->font_name);
1412 item->font_name = g_strdup(font_name);
1413 if (font_size>0.) item->font_size = font_size;
1414 update_text_item_displayfont(item);
1417 void process_font_sel(gchar *str)
1425 p = strrchr(str, ' ');
1427 size = g_strtod(p+1, &q);
1428 if (*q!=0 || size<1.) size=0.;
1433 g_free(ui.font_name);
1435 if (size>0.) ui.font_size = size;
1437 // if there's a current text item, re-font it
1438 if (ui.cur_item_type == ITEM_TEXT) {
1439 refont_text_item(ui.cur_item, str, size);
1440 undo_cont = (ui.cur_item->text!=NULL);
1442 // if there's a current selection, re-font it
1443 if (ui.selection!=NULL)
1444 for (list=ui.selection->items; list!=NULL; list=list->next) {
1445 it = (struct Item *)list->data;
1446 if (it->type == ITEM_TEXT) {
1447 if (undo_cont) undo->multiop |= MULTIOP_CONT_REDO;
1448 refont_text_item(it, str, size);
1449 if (undo_cont) undo->multiop |= MULTIOP_CONT_UNDO;
1453 update_font_button();