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 if (GTK_WIDGET(canvas
)->window
== NULL
) return;
60 if (ui
.cursor
!=NULL
) {
61 gdk_cursor_unref(ui
.cursor
);
64 if (ui
.cur_item_type
== ITEM_MOVESEL
) {
65 if (ui
.toolno
[ui
.cur_mapping
] == TOOL_VERTSPACE
)
66 ui
.cursor
= gdk_cursor_new(GDK_SB_V_DOUBLE_ARROW
);
68 ui
.cursor
= gdk_cursor_new(GDK_FLEUR
);
70 else if (ui
.toolno
[ui
.cur_mapping
] == TOOL_PEN
) {
71 fg
.red
= (ui
.cur_brush
->color_rgba
>> 16) & 0xff00;
72 fg
.green
= (ui
.cur_brush
->color_rgba
>> 8) & 0xff00;
73 fg
.blue
= (ui
.cur_brush
->color_rgba
>> 0) & 0xff00;
74 source
= gdk_bitmap_create_from_data(NULL
, cursor_pen_bits
, 16, 16);
75 ui
.cursor
= gdk_cursor_new_from_pixmap(source
, source
, &fg
, &bg
, 7, 7);
76 gdk_bitmap_unref(source
);
78 else if (ui
.toolno
[ui
.cur_mapping
] == TOOL_ERASER
) {
79 source
= gdk_bitmap_create_from_data(NULL
, cursor_eraser_bits
, 16, 16);
80 mask
= gdk_bitmap_create_from_data(NULL
, cursor_eraser_mask
, 16, 16);
81 ui
.cursor
= gdk_cursor_new_from_pixmap(source
, mask
, &fg
, &bg
, 7, 7);
82 gdk_bitmap_unref(source
);
83 gdk_bitmap_unref(mask
);
85 else if (ui
.toolno
[ui
.cur_mapping
] == TOOL_HIGHLIGHTER
) {
86 source
= gdk_bitmap_create_from_data(NULL
, cursor_eraser_bits
, 16, 16);
87 mask
= gdk_bitmap_create_from_data(NULL
, cursor_eraser_mask
, 16, 16);
88 bg
.red
= (ui
.cur_brush
->color_rgba
>> 16) & 0xff00;
89 bg
.green
= (ui
.cur_brush
->color_rgba
>> 8) & 0xff00;
90 bg
.blue
= (ui
.cur_brush
->color_rgba
>> 0) & 0xff00;
91 ui
.cursor
= gdk_cursor_new_from_pixmap(source
, mask
, &fg
, &bg
, 7, 7);
92 gdk_bitmap_unref(source
);
93 gdk_bitmap_unref(mask
);
95 else if (ui
.cur_item_type
== ITEM_SELECTRECT
) {
96 ui
.cursor
= gdk_cursor_new(GDK_TCROSS
);
99 gdk_window_set_cursor(GTK_WIDGET(canvas
)->window
, ui
.cursor
);
103 /************** painting strokes *************/
105 #define SUBDIVIDE_MAXDIST 5.0
107 void subdivide_cur_path(null
)
111 double x0
, y0
, x1
, y1
;
113 for (n
=0, p
=ui
.cur_path
.coords
; n
<ui
.cur_path
.num_points
-1; n
++, p
+=2) {
114 pieces
= (int)floor(hypot(p
[2]-p
[0], p
[3]-p
[1])/SUBDIVIDE_MAXDIST
);
116 x0
= p
[0]; y0
= p
[1];
117 x1
= p
[2]; y1
= p
[3];
118 realloc_cur_path(ui
.cur_path
.num_points
+pieces
-1);
119 g_memmove(ui
.cur_path
.coords
+2*(n
+pieces
), ui
.cur_path
.coords
+2*(n
+1),
120 2*(ui
.cur_path
.num_points
-n
-1)*sizeof(double));
121 p
= ui
.cur_path
.coords
+2*n
;
122 ui
.cur_path
.num_points
+= pieces
-1;
124 for (k
=1; k
<pieces
; k
++) {
126 p
[0] = x0
+ k
*(x1
-x0
)/pieces
;
127 p
[1] = y0
+ k
*(y1
-y0
)/pieces
;
133 void create_new_stroke(GdkEvent
*event
)
135 ui
.cur_item_type
= ITEM_STROKE
;
136 ui
.cur_item
= g_new(struct Item
, 1);
137 ui
.cur_item
->type
= ITEM_STROKE
;
138 g_memmove(&(ui
.cur_item
->brush
), ui
.cur_brush
, sizeof(struct Brush
));
139 ui
.cur_item
->path
= &ui
.cur_path
;
141 ui
.cur_path
.num_points
= 1;
142 get_pointer_coords(event
, ui
.cur_path
.coords
);
144 if (ui
.ruler
[ui
.cur_mapping
])
145 ui
.cur_item
->canvas_item
= gnome_canvas_item_new(ui
.cur_layer
->group
,
146 gnome_canvas_line_get_type(),
147 "cap-style", GDK_CAP_ROUND
, "join-style", GDK_JOIN_ROUND
,
148 "fill-color-rgba", ui
.cur_item
->brush
.color_rgba
,
149 "width-units", ui
.cur_item
->brush
.thickness
, NULL
);
151 ui
.cur_item
->canvas_item
= gnome_canvas_item_new(
152 ui
.cur_layer
->group
, gnome_canvas_group_get_type(), NULL
);
155 void continue_stroke(GdkEvent
*event
)
157 GnomeCanvasPoints seg
;
160 if (ui
.ruler
[ui
.cur_mapping
]) {
161 pt
= ui
.cur_path
.coords
;
163 realloc_cur_path(ui
.cur_path
.num_points
+1);
164 pt
= ui
.cur_path
.coords
+ 2*(ui
.cur_path
.num_points
-1);
167 get_pointer_coords(event
, pt
+2);
169 if (ui
.ruler
[ui
.cur_mapping
])
170 ui
.cur_path
.num_points
= 2;
172 if (hypot(pt
[0]-pt
[2], pt
[1]-pt
[3]) < PIXEL_MOTION_THRESHOLD
/ui
.zoom
)
173 return; // not a meaningful motion
174 ui
.cur_path
.num_points
++;
181 /* note: we're using a piece of the cur_path array. This is ok because
182 upon creation the line just copies the contents of the GnomeCanvasPoints
183 into an internal structure */
185 if (ui
.ruler
[ui
.cur_mapping
])
186 gnome_canvas_item_set(ui
.cur_item
->canvas_item
, "points", &seg
, NULL
);
188 gnome_canvas_item_new((GnomeCanvasGroup
*)ui
.cur_item
->canvas_item
,
189 gnome_canvas_line_get_type(), "points", &seg
,
190 "cap-style", GDK_CAP_ROUND
, "join-style", GDK_JOIN_ROUND
,
191 "fill-color-rgba", ui
.cur_item
->brush
.color_rgba
,
192 "width-units", ui
.cur_item
->brush
.thickness
, NULL
);
195 void finalize_stroke(void)
197 if (ui
.cur_path
.num_points
== 1) { // GnomeCanvas doesn't like num_points=1
198 ui
.cur_path
.coords
[2] = ui
.cur_path
.coords
[0]+0.1;
199 ui
.cur_path
.coords
[3] = ui
.cur_path
.coords
[1];
200 ui
.cur_path
.num_points
= 2;
203 subdivide_cur_path(); // split the segment so eraser will work
205 ui
.cur_item
->path
= gnome_canvas_points_new(ui
.cur_path
.num_points
);
206 g_memmove(ui
.cur_item
->path
->coords
, ui
.cur_path
.coords
,
207 2*ui
.cur_path
.num_points
*sizeof(double));
208 update_item_bbox(ui
.cur_item
);
209 ui
.cur_path
.num_points
= 0;
211 // destroy the entire group of temporary line segments
212 gtk_object_destroy(GTK_OBJECT(ui
.cur_item
->canvas_item
));
213 // make a new line item to replace it
214 ui
.cur_item
->canvas_item
= gnome_canvas_item_new(ui
.cur_layer
->group
,
215 gnome_canvas_line_get_type(), "points", ui
.cur_item
->path
,
216 "cap-style", GDK_CAP_ROUND
, "join-style", GDK_JOIN_ROUND
,
217 "fill-color-rgba", ui
.cur_item
->brush
.color_rgba
,
218 "width-units", ui
.cur_item
->brush
.thickness
, NULL
);
220 // add undo information
222 undo
->type
= ITEM_STROKE
;
223 undo
->item
= ui
.cur_item
;
224 undo
->layer
= ui
.cur_layer
;
226 // store the item on top of the layer stack
227 ui
.cur_layer
->items
= g_list_append(ui
.cur_layer
->items
, ui
.cur_item
);
228 ui
.cur_layer
->nitems
++;
230 ui
.cur_item_type
= ITEM_NONE
;
233 /************** eraser tool *************/
235 void erase_stroke_portions(struct Item
*item
, double x
, double y
, double radius
,
236 gboolean whole_strokes
, struct UndoErasureData
*erasure
)
240 struct Item
*newhead
, *newtail
;
241 gboolean need_recalc
= FALSE
;
243 for (i
=0, pt
=item
->path
->coords
; i
<item
->path
->num_points
; i
++, pt
+=2) {
244 if (hypot(pt
[0]-x
, pt
[1]-y
) <= radius
) { // found an intersection
245 // FIXME: need to test if line SEGMENT hits the circle
246 // hide the canvas item, and create erasure data if needed
247 if (erasure
== NULL
) {
248 item
->type
= ITEM_TEMP_STROKE
;
249 gnome_canvas_item_hide(item
->canvas_item
);
250 /* we'll use this hidden item as an insertion point later */
251 erasure
= (struct UndoErasureData
*)g_malloc(sizeof(struct UndoErasureData
));
252 item
->erasure
= erasure
;
253 erasure
->item
= item
;
254 erasure
->npos
= g_list_index(ui
.cur_layer
->items
, item
);
256 erasure
->replacement_items
= NULL
;
259 newhead
= newtail
= NULL
;
260 if (!whole_strokes
) {
262 newhead
= (struct Item
*)g_malloc(sizeof(struct Item
));
263 newhead
->type
= ITEM_STROKE
;
264 g_memmove(&newhead
->brush
, &item
->brush
, sizeof(struct Brush
));
265 newhead
->path
= gnome_canvas_points_new(i
);
266 g_memmove(newhead
->path
->coords
, item
->path
->coords
, 2*i
*sizeof(double));
268 while (++i
< item
->path
->num_points
) {
270 if (hypot(pt
[0]-x
, pt
[1]-y
) > radius
) break;
272 if (i
<item
->path
->num_points
-1) {
273 newtail
= (struct Item
*)g_malloc(sizeof(struct Item
));
274 newtail
->type
= ITEM_STROKE
;
275 g_memmove(&newtail
->brush
, &item
->brush
, sizeof(struct Brush
));
276 newtail
->path
= gnome_canvas_points_new(item
->path
->num_points
-i
);
277 g_memmove(newtail
->path
->coords
, item
->path
->coords
+2*i
,
278 2*(item
->path
->num_points
-i
)*sizeof(double));
279 newtail
->canvas_item
= NULL
;
282 if (item
->type
== ITEM_STROKE
) {
283 // it's inside an erasure list - we destroy it
284 gnome_canvas_points_free(item
->path
);
285 if (item
->canvas_item
!= NULL
)
286 gtk_object_destroy(GTK_OBJECT(item
->canvas_item
));
288 erasure
->replacement_items
= g_list_remove(erasure
->replacement_items
, item
);
292 if (newhead
!= NULL
) {
293 update_item_bbox(newhead
);
294 newhead
->canvas_item
= gnome_canvas_item_new(ui
.cur_layer
->group
,
295 gnome_canvas_line_get_type(), "points", newhead
->path
,
296 "cap-style", GDK_CAP_ROUND
, "join-style", GDK_JOIN_ROUND
,
297 "fill-color-rgba", newhead
->brush
.color_rgba
,
298 "width-units", newhead
->brush
.thickness
, NULL
);
299 lower_canvas_item_to(ui
.cur_layer
->group
,
300 newhead
->canvas_item
, erasure
->item
->canvas_item
);
301 erasure
->replacement_items
= g_list_prepend(erasure
->replacement_items
, newhead
);
303 // prepending ensures it won't get processed twice
305 // recurse into the new tail
306 need_recalc
= (newtail
!=NULL
);
307 if (newtail
== NULL
) break;
309 erasure
->replacement_items
= g_list_prepend(erasure
->replacement_items
, newtail
);
311 i
=0; pt
=item
->path
->coords
;
314 // add the tail if needed
315 if (!need_recalc
) return;
316 update_item_bbox(item
);
317 item
->canvas_item
= gnome_canvas_item_new(ui
.cur_layer
->group
,
318 gnome_canvas_line_get_type(), "points", item
->path
,
319 "cap-style", GDK_CAP_ROUND
, "join-style", GDK_JOIN_ROUND
,
320 "fill-color-rgba", item
->brush
.color_rgba
,
321 "width-units", item
->brush
.thickness
, NULL
);
322 lower_canvas_item_to(ui
.cur_layer
->group
, item
->canvas_item
,
323 erasure
->item
->canvas_item
);
327 void do_eraser(GdkEvent
*event
, double radius
, gboolean whole_strokes
)
329 struct Item
*item
, *repl
;
330 GList
*itemlist
, *repllist
;
332 struct BBox eraserbox
;
334 get_pointer_coords(event
, pos
);
335 eraserbox
.left
= pos
[0]-radius
;
336 eraserbox
.right
= pos
[0]+radius
;
337 eraserbox
.top
= pos
[1]-radius
;
338 eraserbox
.bottom
= pos
[1]+radius
;
339 for (itemlist
= ui
.cur_layer
->items
; itemlist
!=NULL
; itemlist
= itemlist
->next
) {
340 item
= (struct Item
*)itemlist
->data
;
341 if (item
->type
== ITEM_STROKE
) {
342 if (!have_intersect(&(item
->bbox
), &eraserbox
)) continue;
343 erase_stroke_portions(item
, pos
[0], pos
[1], radius
, whole_strokes
, NULL
);
344 } else if (item
->type
== ITEM_TEMP_STROKE
) {
345 repllist
= item
->erasure
->replacement_items
;
346 while (repllist
!=NULL
) {
347 repl
= (struct Item
*)repllist
->data
;
348 // we may delete the item soon! so advance now in the list
349 repllist
= repllist
->next
;
350 if (have_intersect(&(repl
->bbox
), &eraserbox
))
351 erase_stroke_portions(repl
, pos
[0], pos
[1], radius
, whole_strokes
, item
->erasure
);
357 void finalize_erasure(void)
359 GList
*itemlist
, *partlist
;
360 struct Item
*item
, *part
;
363 undo
->type
= ITEM_ERASURE
;
364 undo
->layer
= ui
.cur_layer
;
365 undo
->erasurelist
= NULL
;
367 itemlist
= ui
.cur_layer
->items
;
368 while (itemlist
!=NULL
) {
369 item
= (struct Item
*)itemlist
->data
;
370 itemlist
= itemlist
->next
;
371 if (item
->type
!= ITEM_TEMP_STROKE
) continue;
372 item
->type
= ITEM_STROKE
;
373 ui
.cur_layer
->items
= g_list_remove(ui
.cur_layer
->items
, item
);
374 // the item has an invisible canvas item, which used to act as anchor
375 if (item
->canvas_item
!=NULL
) {
376 gtk_object_destroy(GTK_OBJECT(item
->canvas_item
));
377 item
->canvas_item
= NULL
;
379 undo
->erasurelist
= g_list_append(undo
->erasurelist
, item
->erasure
);
380 // add the new strokes into the current layer
381 for (partlist
= item
->erasure
->replacement_items
; partlist
!=NULL
; partlist
= partlist
->next
)
382 ui
.cur_layer
->items
= g_list_insert_before(
383 ui
.cur_layer
->items
, itemlist
, partlist
->data
);
384 ui
.cur_layer
->nitems
+= item
->erasure
->nrepl
-1;
388 ui
.cur_item_type
= ITEM_NONE
;
390 /* NOTE: the list of erasures goes in the depth order of the layer;
391 this guarantees that, upon undo, the erasure->npos fields give the
392 correct position where each item should be reinserted as the list
393 is traversed in the forward direction */
396 /************ selection tools ***********/
398 void make_dashed(GnomeCanvasItem
*item
)
406 dashlen
[0] = dashlen
[1] = 6.0;
407 gnome_canvas_item_set(item
, "dash", &dash
, NULL
);
411 void start_selectrect(GdkEvent
*event
)
416 ui
.cur_item_type
= ITEM_SELECTRECT
;
417 ui
.selection
= g_new(struct Selection
, 1);
418 ui
.selection
->type
= ITEM_SELECTRECT
;
419 ui
.selection
->items
= NULL
;
420 ui
.selection
->layer
= ui
.cur_layer
;
422 get_pointer_coords(event
, pt
);
423 ui
.selection
->bbox
.left
= ui
.selection
->bbox
.right
= pt
[0];
424 ui
.selection
->bbox
.top
= ui
.selection
->bbox
.bottom
= pt
[1];
426 ui
.selection
->canvas_item
= gnome_canvas_item_new(ui
.cur_layer
->group
,
427 gnome_canvas_rect_get_type(), "width-pixels", 1,
428 "outline-color-rgba", 0x000000ff,
429 "fill-color-rgba", 0x80808040,
430 "x1", pt
[0], "x2", pt
[0], "y1", pt
[1], "y2", pt
[1], NULL
);
434 void finalize_selectrect(void)
436 double x1
, x2
, y1
, y2
;
441 ui
.cur_item_type
= ITEM_NONE
;
443 if (ui
.selection
->bbox
.left
> ui
.selection
->bbox
.right
) {
444 x1
= ui
.selection
->bbox
.right
; x2
= ui
.selection
->bbox
.left
;
445 ui
.selection
->bbox
.left
= x1
; ui
.selection
->bbox
.right
= x2
;
447 x1
= ui
.selection
->bbox
.left
; x2
= ui
.selection
->bbox
.right
;
450 if (ui
.selection
->bbox
.top
> ui
.selection
->bbox
.bottom
) {
451 y1
= ui
.selection
->bbox
.bottom
; y2
= ui
.selection
->bbox
.top
;
452 ui
.selection
->bbox
.top
= y1
; ui
.selection
->bbox
.bottom
= y2
;
454 y1
= ui
.selection
->bbox
.top
; y2
= ui
.selection
->bbox
.bottom
;
457 for (itemlist
= ui
.selection
->layer
->items
; itemlist
!=NULL
; itemlist
= itemlist
->next
) {
458 item
= (struct Item
*)itemlist
->data
;
459 if (item
->bbox
.left
>= x1
&& item
->bbox
.right
<= x2
&&
460 item
->bbox
.top
>= y1
&& item
->bbox
.bottom
<= y2
) {
461 ui
.selection
->items
= g_list_append(ui
.selection
->items
, item
);
465 if (ui
.selection
->items
== NULL
) reset_selection();
466 else make_dashed(ui
.selection
->canvas_item
);
468 update_copy_paste_enabled();
471 gboolean
start_movesel(GdkEvent
*event
)
475 if (ui
.selection
==NULL
) return FALSE
;
476 if (ui
.cur_layer
!= ui
.selection
->layer
) return FALSE
;
478 get_pointer_coords(event
, pt
);
479 if (ui
.selection
->type
== ITEM_SELECTRECT
) {
480 if (pt
[0]<ui
.selection
->bbox
.left
|| pt
[0]>ui
.selection
->bbox
.right
||
481 pt
[1]<ui
.selection
->bbox
.top
|| pt
[1]>ui
.selection
->bbox
.bottom
)
483 ui
.cur_item_type
= ITEM_MOVESEL
;
484 ui
.selection
->anchor_x
= ui
.selection
->last_x
= pt
[0];
485 ui
.selection
->anchor_y
= ui
.selection
->last_y
= pt
[1];
486 ui
.selection
->orig_pageno
= ui
.pageno
;
487 ui
.selection
->move_pageno
= ui
.pageno
;
488 ui
.selection
->move_layer
= ui
.selection
->layer
;
489 ui
.selection
->move_pagedelta
= 0.;
490 gnome_canvas_item_set(ui
.selection
->canvas_item
, "dash", NULL
, NULL
);
497 void start_vertspace(GdkEvent
*event
)
504 ui
.cur_item_type
= ITEM_MOVESEL_VERT
;
505 ui
.selection
= g_new(struct Selection
, 1);
506 ui
.selection
->type
= ITEM_MOVESEL_VERT
;
507 ui
.selection
->items
= NULL
;
508 ui
.selection
->layer
= ui
.cur_layer
;
510 get_pointer_coords(event
, pt
);
511 ui
.selection
->bbox
.top
= ui
.selection
->bbox
.bottom
= pt
[1];
512 for (itemlist
= ui
.cur_layer
->items
; itemlist
!=NULL
; itemlist
= itemlist
->next
) {
513 item
= (struct Item
*)itemlist
->data
;
514 if (item
->bbox
.top
>= pt
[1]) {
515 ui
.selection
->items
= g_list_append(ui
.selection
->items
, item
);
516 if (item
->bbox
.bottom
> ui
.selection
->bbox
.bottom
)
517 ui
.selection
->bbox
.bottom
= item
->bbox
.bottom
;
521 ui
.selection
->anchor_x
= ui
.selection
->last_x
= 0;
522 ui
.selection
->anchor_y
= ui
.selection
->last_y
= pt
[1];
523 ui
.selection
->orig_pageno
= ui
.pageno
;
524 ui
.selection
->move_pageno
= ui
.pageno
;
525 ui
.selection
->move_layer
= ui
.selection
->layer
;
526 ui
.selection
->move_pagedelta
= 0.;
527 ui
.selection
->canvas_item
= gnome_canvas_item_new(ui
.cur_layer
->group
,
528 gnome_canvas_rect_get_type(), "width-pixels", 1,
529 "outline-color-rgba", 0x000000ff,
530 "fill-color-rgba", 0x80808040,
531 "x1", -100.0, "x2", ui
.cur_page
->width
+100, "y1", pt
[1], "y2", pt
[1], NULL
);
535 void continue_movesel(GdkEvent
*event
)
537 double pt
[2], dx
, dy
, upmargin
;
541 struct Page
*tmppage
;
543 get_pointer_coords(event
, pt
);
544 if (ui
.cur_item_type
== ITEM_MOVESEL_VERT
) pt
[0] = 0;
545 pt
[1] += ui
.selection
->move_pagedelta
;
547 // check for page jumps
548 if (ui
.cur_item_type
== ITEM_MOVESEL_VERT
)
549 upmargin
= ui
.selection
->bbox
.bottom
- ui
.selection
->bbox
.top
;
550 else upmargin
= VIEW_CONTINUOUS_SKIP
;
551 tmppageno
= ui
.selection
->move_pageno
;
552 tmppage
= g_list_nth_data(journal
.pages
, tmppageno
);
553 while (ui
.view_continuous
&& (pt
[1] < - upmargin
)) {
554 if (tmppageno
== 0) break;
556 tmppage
= g_list_nth_data(journal
.pages
, tmppageno
);
557 pt
[1] += tmppage
->height
+ VIEW_CONTINUOUS_SKIP
;
558 ui
.selection
->move_pagedelta
+= tmppage
->height
+ VIEW_CONTINUOUS_SKIP
;
560 while (ui
.view_continuous
&& (pt
[1] > tmppage
->height
+VIEW_CONTINUOUS_SKIP
)) {
561 if (tmppageno
== journal
.npages
-1) break;
562 pt
[1] -= tmppage
->height
+ VIEW_CONTINUOUS_SKIP
;
563 ui
.selection
->move_pagedelta
-= tmppage
->height
+ VIEW_CONTINUOUS_SKIP
;
565 tmppage
= g_list_nth_data(journal
.pages
, tmppageno
);
568 if (tmppageno
!= ui
.selection
->move_pageno
) {
569 // move to a new page !
570 ui
.selection
->move_pageno
= tmppageno
;
571 if (tmppageno
== ui
.selection
->orig_pageno
)
572 ui
.selection
->move_layer
= ui
.selection
->layer
;
574 ui
.selection
->move_layer
= (struct Layer
*)(g_list_last(
575 ((struct Page
*)g_list_nth_data(journal
.pages
, tmppageno
))->layers
)->data
);
576 gnome_canvas_item_reparent(ui
.selection
->canvas_item
, ui
.selection
->move_layer
->group
);
577 for (list
= ui
.selection
->items
; list
!=NULL
; list
= list
->next
) {
578 item
= (struct Item
*)list
->data
;
579 if (item
->canvas_item
!=NULL
)
580 gnome_canvas_item_reparent(item
->canvas_item
, ui
.selection
->move_layer
->group
);
582 // avoid a refresh bug
583 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui
.selection
->move_layer
->group
), 0., 0.);
584 if (ui
.cur_item_type
== ITEM_MOVESEL_VERT
)
585 gnome_canvas_item_set(ui
.selection
->canvas_item
,
586 "x2", tmppage
->width
+100,
587 "y1", ui
.selection
->anchor_y
+ui
.selection
->move_pagedelta
, NULL
);
590 // now, process things normally
592 dx
= pt
[0] - ui
.selection
->last_x
;
593 dy
= pt
[1] - ui
.selection
->last_y
;
594 if (hypot(dx
,dy
) < 1) return; // don't move subpixel
595 ui
.selection
->last_x
= pt
[0];
596 ui
.selection
->last_y
= pt
[1];
598 // move the canvas items
599 if (ui
.cur_item_type
== ITEM_MOVESEL_VERT
)
600 gnome_canvas_item_set(ui
.selection
->canvas_item
, "y2", pt
[1], NULL
);
602 gnome_canvas_item_move(ui
.selection
->canvas_item
, dx
, dy
);
604 for (list
= ui
.selection
->items
; list
!= NULL
; list
= list
->next
) {
605 item
= (struct Item
*)list
->data
;
606 if (item
->canvas_item
!= NULL
)
607 gnome_canvas_item_move(item
->canvas_item
, dx
, dy
);
611 void finalize_movesel(void)
616 if (ui
.selection
->items
!= NULL
) {
618 undo
->type
= ITEM_MOVESEL
;
619 undo
->itemlist
= g_list_copy(ui
.selection
->items
);
620 undo
->val_x
= ui
.selection
->last_x
- ui
.selection
->anchor_x
;
621 undo
->val_y
= ui
.selection
->last_y
- ui
.selection
->anchor_y
;
622 undo
->layer
= ui
.selection
->layer
;
623 undo
->layer2
= ui
.selection
->move_layer
;
624 undo
->auxlist
= NULL
;
625 // build auxlist = pointers to Item's just before ours (for depths)
626 for (list
= ui
.selection
->items
; list
!=NULL
; list
= list
->next
) {
627 link
= g_list_find(ui
.selection
->layer
->items
, list
->data
);
628 if (link
!=NULL
) link
= link
->prev
;
629 undo
->auxlist
= g_list_append(undo
->auxlist
, ((link
!=NULL
) ? link
->data
: NULL
));
631 ui
.selection
->layer
= ui
.selection
->move_layer
;
632 move_journal_items_by(undo
->itemlist
, undo
->val_x
, undo
->val_y
,
633 undo
->layer
, undo
->layer2
,
634 (undo
->layer
== undo
->layer2
)?undo
->auxlist
:NULL
);
637 if (ui
.selection
->move_pageno
!=ui
.selection
->orig_pageno
)
638 do_switch_page(ui
.selection
->move_pageno
, FALSE
, FALSE
);
640 if (ui
.cur_item_type
== ITEM_MOVESEL_VERT
)
643 ui
.selection
->bbox
.left
+= undo
->val_x
;
644 ui
.selection
->bbox
.right
+= undo
->val_x
;
645 ui
.selection
->bbox
.top
+= undo
->val_y
;
646 ui
.selection
->bbox
.bottom
+= undo
->val_y
;
647 make_dashed(ui
.selection
->canvas_item
);
649 ui
.cur_item_type
= ITEM_NONE
;
654 void selection_delete(void)
656 struct UndoErasureData
*erasure
;
660 if (ui
.selection
== NULL
) return;
662 undo
->type
= ITEM_ERASURE
;
663 undo
->layer
= ui
.selection
->layer
;
664 undo
->erasurelist
= NULL
;
665 for (itemlist
= ui
.selection
->items
; itemlist
!=NULL
; itemlist
= itemlist
->next
) {
666 item
= (struct Item
*)itemlist
->data
;
667 if (item
->canvas_item
!=NULL
)
668 gtk_object_destroy(GTK_OBJECT(item
->canvas_item
));
669 erasure
= g_new(struct UndoErasureData
, 1);
670 erasure
->item
= item
;
671 erasure
->npos
= g_list_index(ui
.selection
->layer
->items
, item
);
673 erasure
->replacement_items
= NULL
;
674 ui
.selection
->layer
->items
= g_list_remove(ui
.selection
->layer
->items
, item
);
675 ui
.selection
->layer
->nitems
--;
676 undo
->erasurelist
= g_list_prepend(undo
->erasurelist
, erasure
);
680 /* NOTE: the erasurelist is built backwards; this guarantees that,
681 upon undo, the erasure->npos fields give the correct position
682 where each item should be reinserted as the list is traversed in
683 the forward direction */
686 void callback_clipboard_get(GtkClipboard
*clipboard
,
687 GtkSelectionData
*selection_data
,
688 guint info
, gpointer user_data
)
692 g_memmove(&length
, user_data
, sizeof(int));
693 gtk_selection_data_set(selection_data
,
694 gdk_atom_intern("_XOURNAL", FALSE
), 8, user_data
, length
);
697 void callback_clipboard_clear(GtkClipboard
*clipboard
, gpointer user_data
)
702 void selection_to_clip(void)
708 GtkTargetEntry target
;
710 if (ui
.selection
== NULL
) return;
711 bufsz
= 2*sizeof(int) // bufsz, nitems
712 + sizeof(struct BBox
); // bbox
714 for (list
= ui
.selection
->items
; list
!= NULL
; list
= list
->next
) {
715 item
= (struct Item
*)list
->data
;
717 if (item
->type
== ITEM_STROKE
) {
718 bufsz
+= sizeof(int) // type
719 + sizeof(struct Brush
) // brush
720 + sizeof(int) // num_points
721 + 2*item
->path
->num_points
*sizeof(double); // the points
723 else bufsz
+= sizeof(int); // type
725 p
= buf
= g_malloc(bufsz
);
726 g_memmove(p
, &bufsz
, sizeof(int)); p
+= sizeof(int);
727 g_memmove(p
, &nitems
, sizeof(int)); p
+= sizeof(int);
728 g_memmove(p
, &ui
.selection
->bbox
, sizeof(struct BBox
)); p
+= sizeof(struct BBox
);
729 for (list
= ui
.selection
->items
; list
!= NULL
; list
= list
->next
) {
730 item
= (struct Item
*)list
->data
;
731 g_memmove(p
, &item
->type
, sizeof(int)); p
+= sizeof(int);
732 if (item
->type
== ITEM_STROKE
) {
733 g_memmove(p
, &item
->brush
, sizeof(struct Brush
)); p
+= sizeof(struct Brush
);
734 g_memmove(p
, &item
->path
->num_points
, sizeof(int)); p
+= sizeof(int);
735 g_memmove(p
, item
->path
->coords
, 2*item
->path
->num_points
*sizeof(double));
736 p
+= 2*item
->path
->num_points
*sizeof(double);
740 target
.target
= "_XOURNAL";
744 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
746 callback_clipboard_get
, callback_clipboard_clear
, buf
);
750 void clipboard_paste(void)
752 GtkSelectionData
*sel_data
;
757 double hoffset
, voffset
, cx
, cy
;
761 if (ui
.cur_layer
== NULL
) return;
763 ui
.cur_item_type
= ITEM_PASTE
;
764 sel_data
= gtk_clipboard_wait_for_contents(
765 gtk_clipboard_get(GDK_SELECTION_CLIPBOARD
),
766 gdk_atom_intern("_XOURNAL", FALSE
));
767 ui
.cur_item_type
= ITEM_NONE
;
768 if (sel_data
== NULL
) return; // paste failed
772 ui
.selection
= g_new(struct Selection
, 1);
773 p
= sel_data
->data
+ sizeof(int);
774 g_memmove(&nitems
, p
, sizeof(int)); p
+= sizeof(int);
775 ui
.selection
->type
= ITEM_SELECTRECT
;
776 ui
.selection
->layer
= ui
.cur_layer
;
777 g_memmove(&ui
.selection
->bbox
, p
, sizeof(struct BBox
)); p
+= sizeof(struct BBox
);
778 ui
.selection
->items
= NULL
;
780 // find by how much we translate the pasted selection
781 gnome_canvas_get_scroll_offsets(canvas
, &sx
, &sy
);
782 gdk_window_get_geometry(GTK_WIDGET(canvas
)->window
, NULL
, NULL
, &wx
, &wy
, NULL
);
783 gnome_canvas_window_to_world(canvas
, sx
+ wx
/2, sy
+ wy
/2, &cx
, &cy
);
784 cx
-= ui
.cur_page
->hoffset
;
785 cy
-= ui
.cur_page
->voffset
;
786 if (cx
+ (ui
.selection
->bbox
.right
-ui
.selection
->bbox
.left
)/2 > ui
.cur_page
->width
)
787 cx
= ui
.cur_page
->width
- (ui
.selection
->bbox
.right
-ui
.selection
->bbox
.left
)/2;
788 if (cx
- (ui
.selection
->bbox
.right
-ui
.selection
->bbox
.left
)/2 < 0)
789 cx
= (ui
.selection
->bbox
.right
-ui
.selection
->bbox
.left
)/2;
790 if (cy
+ (ui
.selection
->bbox
.bottom
-ui
.selection
->bbox
.top
)/2 > ui
.cur_page
->height
)
791 cy
= ui
.cur_page
->height
- (ui
.selection
->bbox
.bottom
-ui
.selection
->bbox
.top
)/2;
792 if (cy
- (ui
.selection
->bbox
.bottom
-ui
.selection
->bbox
.top
)/2 < 0)
793 cy
= (ui
.selection
->bbox
.bottom
-ui
.selection
->bbox
.top
)/2;
794 hoffset
= cx
- (ui
.selection
->bbox
.right
+ui
.selection
->bbox
.left
)/2;
795 voffset
= cy
- (ui
.selection
->bbox
.top
+ui
.selection
->bbox
.bottom
)/2;
796 ui
.selection
->bbox
.left
+= hoffset
;
797 ui
.selection
->bbox
.right
+= hoffset
;
798 ui
.selection
->bbox
.top
+= voffset
;
799 ui
.selection
->bbox
.bottom
+= voffset
;
801 ui
.selection
->canvas_item
= gnome_canvas_item_new(ui
.cur_layer
->group
,
802 gnome_canvas_rect_get_type(), "width-pixels", 1,
803 "outline-color-rgba", 0x000000ff,
804 "fill-color-rgba", 0x80808040,
805 "x1", ui
.selection
->bbox
.left
, "x2", ui
.selection
->bbox
.right
,
806 "y1", ui
.selection
->bbox
.top
, "y2", ui
.selection
->bbox
.bottom
, NULL
);
807 make_dashed(ui
.selection
->canvas_item
);
809 while (nitems
-- > 0) {
810 item
= g_new(struct Item
, 1);
811 ui
.selection
->items
= g_list_append(ui
.selection
->items
, item
);
812 ui
.cur_layer
->items
= g_list_append(ui
.cur_layer
->items
, item
);
813 ui
.cur_layer
->nitems
++;
814 g_memmove(&item
->type
, p
, sizeof(int)); p
+= sizeof(int);
815 if (item
->type
== ITEM_STROKE
) {
816 g_memmove(&item
->brush
, p
, sizeof(struct Brush
)); p
+= sizeof(struct Brush
);
817 g_memmove(&npts
, p
, sizeof(int)); p
+= sizeof(int);
818 item
->path
= gnome_canvas_points_new(npts
);
820 for (i
=0; i
<npts
; i
++) {
821 item
->path
->coords
[2*i
] = pf
[2*i
] + hoffset
;
822 item
->path
->coords
[2*i
+1] = pf
[2*i
+1] + voffset
;
824 p
+= 2*item
->path
->num_points
*sizeof(double);
825 update_item_bbox(item
);
826 item
->canvas_item
= gnome_canvas_item_new(ui
.cur_layer
->group
,
827 gnome_canvas_line_get_type(), "points", item
->path
,
828 "cap-style", GDK_CAP_ROUND
, "join-style", GDK_JOIN_ROUND
,
829 "fill-color-rgba", item
->brush
.color_rgba
,
830 "width-units", item
->brush
.thickness
, NULL
);
835 undo
->type
= ITEM_PASTE
;
836 undo
->layer
= ui
.cur_layer
;
837 undo
->itemlist
= g_list_copy(ui
.selection
->items
);
839 gtk_selection_data_free(sel_data
);
840 update_copy_paste_enabled();
843 // modify the color or thickness of pen strokes in a selection
845 void recolor_selection(int color
)
851 if (ui
.selection
== NULL
) return;
853 undo
->type
= ITEM_REPAINTSEL
;
854 undo
->itemlist
= NULL
;
855 undo
->auxlist
= NULL
;
856 for (itemlist
= ui
.selection
->items
; itemlist
!=NULL
; itemlist
= itemlist
->next
) {
857 item
= (struct Item
*)itemlist
->data
;
858 if (item
->type
!= ITEM_STROKE
|| item
->brush
.tool_type
!=TOOL_PEN
) continue;
859 // store info for undo
860 undo
->itemlist
= g_list_append(undo
->itemlist
, item
);
861 brush
= (struct Brush
*)g_malloc(sizeof(struct Brush
));
862 g_memmove(brush
, &(item
->brush
), sizeof(struct Brush
));
863 undo
->auxlist
= g_list_append(undo
->auxlist
, brush
);
864 // repaint the stroke
865 item
->brush
.color_no
= color
;
866 item
->brush
.color_rgba
= predef_colors_rgba
[color
];
867 if (item
->canvas_item
!=NULL
)
868 gnome_canvas_item_set(item
->canvas_item
,
869 "fill-color-rgba", item
->brush
.color_rgba
, NULL
);
873 void rethicken_selection(int val
)
879 if (ui
.selection
== NULL
) return;
881 undo
->type
= ITEM_REPAINTSEL
;
882 undo
->itemlist
= NULL
;
883 undo
->auxlist
= NULL
;
884 for (itemlist
= ui
.selection
->items
; itemlist
!=NULL
; itemlist
= itemlist
->next
) {
885 item
= (struct Item
*)itemlist
->data
;
886 if (item
->type
!= ITEM_STROKE
|| item
->brush
.tool_type
!=TOOL_PEN
) continue;
887 // store info for undo
888 undo
->itemlist
= g_list_append(undo
->itemlist
, item
);
889 brush
= (struct Brush
*)g_malloc(sizeof(struct Brush
));
890 g_memmove(brush
, &(item
->brush
), sizeof(struct Brush
));
891 undo
->auxlist
= g_list_append(undo
->auxlist
, brush
);
892 // repaint the stroke
893 item
->brush
.thickness_no
= val
;
894 item
->brush
.thickness
= predef_thickness
[TOOL_PEN
][val
];
895 if (item
->canvas_item
!=NULL
)
896 gnome_canvas_item_set(item
->canvas_item
,
897 "width-units", item
->brush
.thickness
, NULL
);