8 #include <libgnomecanvas/libgnomecanvas.h>
9 #include <gdk/gdkkeysyms.h>
13 #include "xo-interface.h"
14 #include "xo-support.h"
15 #include "xo-callbacks.h"
19 #include "xo-shapes.h"
21 // some global constants
23 guint predef_colors_rgba[COLOR_MAX] =
24 { 0x000000ff, 0x3333ccff, 0xff0000ff, 0x008000ff,
25 0x808080ff, 0x00c0ffff, 0x00ff00ff, 0xff00ffff,
26 0xff8000ff, 0xffff00ff, 0xffffffff };
28 guint predef_bgcolors_rgba[COLOR_MAX] = // meaningless ones set to white
29 { 0xffffffff, 0xa0e8ffff, 0xffc0d4ff, 0x80ffc0ff,
30 0xffffffff, 0xa0e8ffff, 0x80ffc0ff, 0xffc0d4ff,
31 0xffc080ff, 0xffff80ff, 0xffffffff };
33 double predef_thickness[NUM_STROKE_TOOLS][THICKNESS_MAX] =
34 { { 0.42, 0.85, 1.41, 2.26, 5.67 }, // pen thicknesses = 0.15, 0.3, 0.5, 0.8, 2 mm
35 { 2.83, 2.83, 8.50, 19.84, 19.84 }, // eraser thicknesses = 1, 3, 7 mm
36 { 2.83, 2.83, 8.50, 19.84, 19.84 }, // highlighter thicknesses = 1, 3, 7 mm
39 // some manipulation functions
41 struct Page *new_page(struct Page *template)
43 struct Page *pg = (struct Page *) g_memdup(template, sizeof(struct Page));
44 struct Layer *l = g_new(struct Layer, 1);
48 pg->layers = g_list_append(NULL, l);
50 pg->bg = (struct Background *)g_memdup(template->bg, sizeof(struct Background));
51 pg->bg->canvas_item = NULL;
52 if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
53 gdk_pixbuf_ref(pg->bg->pixbuf);
54 refstring_ref(pg->bg->filename);
56 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
57 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
58 make_page_clipbox(pg);
60 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
61 pg->group, gnome_canvas_group_get_type(), NULL);
66 /* Create a page from a background.
67 Note: bg should be an UNREFERENCED background.
68 If needed, first duplicate it and increase the refcount of the pixbuf.
70 struct Page *new_page_with_bg(struct Background *bg, double width, double height)
72 struct Page *pg = g_new(struct Page, 1);
73 struct Layer *l = g_new(struct Layer, 1);
77 pg->layers = g_list_append(NULL, l);
80 pg->bg->canvas_item = NULL;
83 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
84 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
85 make_page_clipbox(pg);
87 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
88 pg->group, gnome_canvas_group_get_type(), NULL);
93 void realloc_cur_path(int n)
95 if (n <= ui.cur_path_storage_alloc) return;
96 ui.cur_path_storage_alloc = n+100;
97 ui.cur_path.coords = g_realloc(ui.cur_path.coords, 2*(n+100)*sizeof(double));
100 void realloc_cur_widths(int n)
102 if (n <= ui.cur_widths_storage_alloc) return;
103 ui.cur_widths_storage_alloc = n+100;
104 ui.cur_widths = g_realloc(ui.cur_widths, (n+100)*sizeof(double));
107 // undo utility functions
109 void prepare_new_undo(void)
112 // add a new UndoItem on the stack
113 u = (struct UndoItem *)g_malloc(sizeof(struct UndoItem));
121 void clear_redo_stack(void)
125 struct UndoErasureData *erasure;
128 /* Warning: the redo items might reference items from past redo entries,
129 which have been destroyed before them. Be careful! As a rule, it's
130 safe to destroy data which has been created at the current history step,
131 it's unsafe to refer to any data from previous history steps */
134 if (redo->type == ITEM_STROKE) {
135 gnome_canvas_points_free(redo->item->path);
136 if (redo->item->brush.variable_width) g_free(redo->item->widths);
138 /* the strokes are unmapped, so there are no associated canvas items */
140 else if (redo->type == ITEM_TEXT) {
141 g_free(redo->item->text);
142 g_free(redo->item->font_name);
145 else if (redo->type == ITEM_ERASURE || redo->type == ITEM_RECOGNIZER) {
146 for (list = redo->erasurelist; list!=NULL; list=list->next) {
147 erasure = (struct UndoErasureData *)list->data;
148 for (repl = erasure->replacement_items; repl!=NULL; repl=repl->next) {
149 it = (struct Item *)repl->data;
150 gnome_canvas_points_free(it->path);
151 if (it->brush.variable_width) g_free(it->widths);
154 g_list_free(erasure->replacement_items);
157 g_list_free(redo->erasurelist);
159 else if (redo->type == ITEM_NEW_BG_ONE || redo->type == ITEM_NEW_BG_RESIZE
160 || redo->type == ITEM_NEW_DEFAULT_BG) {
161 if (redo->bg->type == BG_PIXMAP || redo->bg->type == BG_PDF) {
162 if (redo->bg->pixbuf!=NULL) gdk_pixbuf_unref(redo->bg->pixbuf);
163 refstring_unref(redo->bg->filename);
167 else if (redo->type == ITEM_NEW_PAGE) {
168 redo->page->group = NULL;
169 delete_page(redo->page);
171 else if (redo->type == ITEM_MOVESEL || redo->type == ITEM_REPAINTSEL) {
172 g_list_free(redo->itemlist); g_list_free(redo->auxlist);
174 else if (redo->type == ITEM_RESIZESEL) {
175 g_list_free(redo->itemlist);
177 else if (redo->type == ITEM_PASTE) {
178 for (list = redo->itemlist; list!=NULL; list=list->next) {
179 it = (struct Item *)list->data;
180 if (it->type == ITEM_STROKE) {
181 gnome_canvas_points_free(it->path);
182 if (it->brush.variable_width) g_free(it->widths);
186 g_list_free(redo->itemlist);
188 else if (redo->type == ITEM_NEW_LAYER) {
191 else if (redo->type == ITEM_TEXT_EDIT || redo->type == ITEM_TEXT_ATTRIB) {
193 if (redo->type == ITEM_TEXT_ATTRIB) g_free(redo->brush);
200 update_undo_redo_enabled();
203 void clear_undo_stack(void)
207 struct UndoErasureData *erasure;
210 // for strokes, items are already in the journal, so we don't free them
211 // for erasures, we need to free the dead items
212 if (undo->type == ITEM_ERASURE || undo->type == ITEM_RECOGNIZER) {
213 for (list = undo->erasurelist; list!=NULL; list=list->next) {
214 erasure = (struct UndoErasureData *)list->data;
215 if (erasure->item->type == ITEM_STROKE) {
216 gnome_canvas_points_free(erasure->item->path);
217 if (erasure->item->brush.variable_width) g_free(erasure->item->widths);
219 if (erasure->item->type == ITEM_TEXT)
220 { g_free(erasure->item->text); g_free(erasure->item->font_name); }
221 g_free(erasure->item);
222 g_list_free(erasure->replacement_items);
225 g_list_free(undo->erasurelist);
227 else if (undo->type == ITEM_NEW_BG_ONE || undo->type == ITEM_NEW_BG_RESIZE
228 || undo->type == ITEM_NEW_DEFAULT_BG) {
229 if (undo->bg->type == BG_PIXMAP || undo->bg->type == BG_PDF) {
230 if (undo->bg->pixbuf!=NULL) gdk_pixbuf_unref(undo->bg->pixbuf);
231 refstring_unref(undo->bg->filename);
235 else if (undo->type == ITEM_MOVESEL || undo->type == ITEM_REPAINTSEL) {
236 g_list_free(undo->itemlist); g_list_free(undo->auxlist);
238 else if (undo->type == ITEM_RESIZESEL) {
239 g_list_free(undo->itemlist);
241 else if (undo->type == ITEM_PASTE) {
242 g_list_free(undo->itemlist);
244 else if (undo->type == ITEM_DELETE_LAYER) {
245 undo->layer->group = NULL;
246 delete_layer(undo->layer);
248 else if (undo->type == ITEM_DELETE_PAGE) {
249 undo->page->group = NULL;
250 delete_page(undo->page);
252 else if (undo->type == ITEM_TEXT_EDIT || undo->type == ITEM_TEXT_ATTRIB) {
254 if (undo->type == ITEM_TEXT_ATTRIB) g_free(undo->brush);
261 update_undo_redo_enabled();
264 // free data structures
266 void delete_journal(struct Journal *j)
268 while (j->pages!=NULL) {
269 delete_page((struct Page *)j->pages->data);
270 j->pages = g_list_delete_link(j->pages, j->pages);
274 void delete_page(struct Page *pg)
278 while (pg->layers!=NULL) {
279 l = (struct Layer *)pg->layers->data;
282 pg->layers = g_list_delete_link(pg->layers, pg->layers);
284 if (pg->group!=NULL) gtk_object_destroy(GTK_OBJECT(pg->group));
285 // this also destroys the background's canvas items
286 if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
287 if (pg->bg->pixbuf != NULL) gdk_pixbuf_unref(pg->bg->pixbuf);
288 if (pg->bg->filename != NULL) refstring_unref(pg->bg->filename);
294 void delete_layer(struct Layer *l)
298 while (l->items!=NULL) {
299 item = (struct Item *)l->items->data;
300 if (item->type == ITEM_STROKE && item->path != NULL)
301 gnome_canvas_points_free(item->path);
302 if (item->type == ITEM_TEXT) {
303 g_free(item->font_name); g_free(item->text);
305 // don't need to delete the canvas_item, as it's part of the group destroyed below
307 l->items = g_list_delete_link(l->items, l->items);
309 if (l->group!= NULL) gtk_object_destroy(GTK_OBJECT(l->group));
313 // referenced strings
315 struct Refstring *new_refstring(const char *s)
317 struct Refstring *rs = g_new(struct Refstring, 1);
319 if (s!=NULL) rs->s = g_strdup(s);
325 struct Refstring *refstring_ref(struct Refstring *rs)
331 void refstring_unref(struct Refstring *rs)
335 if (rs->s!=NULL) g_free(rs->s);
336 if (rs->aux!=NULL) g_free(rs->aux);
342 // some helper functions
344 void get_pointer_coords(GdkEvent *event, gdouble *ret)
347 gdk_event_get_coords(event, &x, &y);
348 gnome_canvas_window_to_world(canvas, x, y, ret, ret+1);
349 ret[0] -= ui.cur_page->hoffset;
350 ret[1] -= ui.cur_page->voffset;
353 void fix_xinput_coords(GdkEvent *event)
355 double *axes, *px, *py, axis_width;
357 int wx, wy, sx, sy, ix, iy;
359 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
360 axes = event->button.axes;
361 px = &(event->button.x);
362 py = &(event->button.y);
363 device = event->button.device;
365 else if (event->type == GDK_MOTION_NOTIFY) {
366 axes = event->motion.axes;
367 px = &(event->motion.x);
368 py = &(event->motion.y);
369 device = event->motion.device;
371 else return; // nothing we know how to do
373 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
375 #ifdef ENABLE_XINPUT_BUGFIX
376 // fix broken events with the core pointer's location
377 if (!finite(axes[0]) || !finite(axes[1]) || (axes[0]==0. && axes[1]==0.)) {
378 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
383 gdk_window_get_origin(GTK_WIDGET(canvas)->window, &wx, &wy);
384 axis_width = device->axes[0].max - device->axes[0].min;
385 if (axis_width>EPSILON)
386 *px = (axes[0]/axis_width)*ui.screen_width + sx - wx;
387 axis_width = device->axes[1].max - device->axes[1].min;
388 if (axis_width>EPSILON)
389 *py = (axes[1]/axis_width)*ui.screen_height + sy - wy;
392 if (!finite(*px) || !finite(*py) || (*px==0. && *py==0.)) {
393 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
398 /* with GTK+ 2.16 or earlier, the event comes from the parent gdkwindow
399 and so needs to be adjusted for scrolling */
400 if (gtk_major_version == 2 && gtk_minor_version <= 16) {
404 /* with GTK+ 2.17, events come improperly translated, and the event's
405 GdkWindow isn't even the same for ButtonDown as for MotionNotify... */
406 if (gtk_major_version == 2 && gtk_minor_version == 17) { // GTK+ 2.17 issues !!
407 gdk_window_get_position(GTK_WIDGET(canvas)->window, &wx, &wy);
415 double get_pressure_multiplier(GdkEvent *event)
419 if (event->button.device == gdk_device_get_core_pointer()
420 || event->button.device->num_axes <= 2) return 1.0;
422 rawpressure = event->button.axes[2]/(event->button.device->axes[2].max - event->button.device->axes[2].min);
424 return ((1-rawpressure)*ui.width_minimum_multiplier + rawpressure*ui.width_maximum_multiplier);
427 void update_item_bbox(struct Item *item)
432 if (item->type == ITEM_STROKE) {
433 item->bbox.left = item->bbox.right = item->path->coords[0];
434 item->bbox.top = item->bbox.bottom = item->path->coords[1];
435 for (i=1, p=item->path->coords+2; i<item->path->num_points; i++, p+=2)
437 if (p[0] < item->bbox.left) item->bbox.left = p[0];
438 if (p[0] > item->bbox.right) item->bbox.right = p[0];
439 if (p[1] < item->bbox.top) item->bbox.top = p[1];
440 if (p[1] > item->bbox.bottom) item->bbox.bottom = p[1];
443 if (item->type == ITEM_TEXT && item->canvas_item!=NULL) {
445 g_object_get(item->canvas_item, "text_width", &w, "text_height", &h, NULL);
446 item->bbox.right = item->bbox.left + w;
447 item->bbox.bottom = item->bbox.top + h;
451 void make_page_clipbox(struct Page *pg)
453 GnomeCanvasPathDef *pg_clip;
455 pg_clip = gnome_canvas_path_def_new_sized(4);
456 gnome_canvas_path_def_moveto(pg_clip, 0., 0.);
457 gnome_canvas_path_def_lineto(pg_clip, 0., pg->height);
458 gnome_canvas_path_def_lineto(pg_clip, pg->width, pg->height);
459 gnome_canvas_path_def_lineto(pg_clip, pg->width, 0.);
460 gnome_canvas_path_def_closepath(pg_clip);
461 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), "path", pg_clip, NULL);
462 gnome_canvas_path_def_unref(pg_clip);
465 void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
467 PangoFontDescription *font_desc;
468 GnomeCanvasPoints points;
471 if (item->type == ITEM_STROKE) {
472 if (!item->brush.variable_width)
473 item->canvas_item = gnome_canvas_item_new(group,
474 gnome_canvas_line_get_type(), "points", item->path,
475 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
476 "fill-color-rgba", item->brush.color_rgba,
477 "width-units", item->brush.thickness, NULL);
479 item->canvas_item = gnome_canvas_item_new(group,
480 gnome_canvas_group_get_type(), NULL);
481 points.num_points = 2;
482 points.ref_count = 1;
483 for (j = 0; j < item->path->num_points-1; j++) {
484 points.coords = item->path->coords+2*j;
485 gnome_canvas_item_new((GnomeCanvasGroup *) item->canvas_item,
486 gnome_canvas_line_get_type(), "points", &points,
487 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
488 "fill-color-rgba", item->brush.color_rgba,
489 "width-units", item->widths[j], NULL);
493 if (item->type == ITEM_TEXT) {
494 font_desc = pango_font_description_from_string(item->font_name);
495 pango_font_description_set_absolute_size(font_desc,
496 item->font_size*ui.zoom*PANGO_SCALE);
497 item->canvas_item = gnome_canvas_item_new(group,
498 gnome_canvas_text_get_type(),
499 "x", item->bbox.left, "y", item->bbox.top, "anchor", GTK_ANCHOR_NW,
500 "font-desc", font_desc, "fill-color-rgba", item->brush.color_rgba,
501 "text", item->text, NULL);
502 update_item_bbox(item);
506 void make_canvas_items(void)
511 GList *pagelist, *layerlist, *itemlist;
513 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next) {
514 pg = (struct Page *)pagelist->data;
515 if (pg->group == NULL) {
516 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
517 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
518 make_page_clipbox(pg);
520 if (pg->bg->canvas_item == NULL) update_canvas_bg(pg);
521 for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
522 l = (struct Layer *)layerlist->data;
523 if (l->group == NULL)
524 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
525 pg->group, gnome_canvas_group_get_type(), NULL);
526 for (itemlist = l->items; itemlist!=NULL; itemlist = itemlist->next) {
527 item = (struct Item *)itemlist->data;
528 if (item->canvas_item == NULL)
529 make_canvas_item_one(l->group, item);
535 void update_canvas_bg(struct Page *pg)
537 GnomeCanvasGroup *group;
538 GnomeCanvasPoints *seg;
539 GdkPixbuf *scaled_pix;
543 gboolean is_well_scaled;
545 if (pg->bg->canvas_item != NULL)
546 gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
547 pg->bg->canvas_item = NULL;
549 if (pg->bg->type == BG_SOLID)
551 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
552 gnome_canvas_group_get_type(), NULL);
553 group = GNOME_CANVAS_GROUP(pg->bg->canvas_item);
554 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
555 gnome_canvas_item_new(group, gnome_canvas_rect_get_type(),
556 "x1", 0., "x2", pg->width, "y1", 0., "y2", pg->height,
557 "fill-color-rgba", pg->bg->color_rgba, NULL);
558 if (pg->bg->ruling == RULING_NONE) return;
559 seg = gnome_canvas_points_new(2);
561 if (pg->bg->ruling == RULING_GRAPH) {
562 pt[1] = 0; pt[3] = pg->height;
563 for (x=RULING_GRAPHSPACING; x<pg->width-1; x+=RULING_GRAPHSPACING) {
565 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
566 "points", seg, "fill-color-rgba", RULING_COLOR,
567 "width-units", RULING_THICKNESS, NULL);
569 pt[0] = 0; pt[2] = pg->width;
570 for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
572 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
573 "points", seg, "fill-color-rgba", RULING_COLOR,
574 "width-units", RULING_THICKNESS, NULL);
576 gnome_canvas_points_free(seg);
579 pt[0] = 0; pt[2] = pg->width;
580 for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
582 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
583 "points", seg, "fill-color-rgba", RULING_COLOR,
584 "width-units", RULING_THICKNESS, NULL);
586 if (pg->bg->ruling == RULING_LINED) {
587 pt[0] = pt[2] = RULING_LEFTMARGIN;
588 pt[1] = 0; pt[3] = pg->height;
589 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
590 "points", seg, "fill-color-rgba", RULING_MARGIN_COLOR,
591 "width-units", RULING_THICKNESS, NULL);
593 gnome_canvas_points_free(seg);
597 if (pg->bg->type == BG_PIXMAP)
599 pg->bg->pixbuf_scale = 0;
600 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
601 gnome_canvas_pixbuf_get_type(),
602 "pixbuf", pg->bg->pixbuf,
603 "width", pg->width, "height", pg->height,
604 "width-set", TRUE, "height-set", TRUE,
606 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
609 if (pg->bg->type == BG_PDF)
611 if (pg->bg->pixbuf == NULL) return;
612 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
613 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
615 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
616 gnome_canvas_pixbuf_get_type(),
617 "pixbuf", pg->bg->pixbuf,
618 "width-in-pixels", TRUE, "height-in-pixels", TRUE,
621 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
622 gnome_canvas_pixbuf_get_type(),
623 "pixbuf", pg->bg->pixbuf,
624 "width", pg->width, "height", pg->height,
625 "width-set", TRUE, "height-set", TRUE,
627 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
631 gboolean is_visible(struct Page *pg)
633 GtkAdjustment *v_adj;
636 if (!ui.view_continuous) return (pg == ui.cur_page);
637 v_adj = gtk_layout_get_vadjustment(GTK_LAYOUT(canvas));
638 ytop = v_adj->value/ui.zoom;
639 ybot = (v_adj->value + v_adj->page_size) / ui.zoom;
640 return (MAX(ytop, pg->voffset) < MIN(ybot, pg->voffset+pg->height));
643 void rescale_bg_pixmaps(void)
648 gboolean is_well_scaled;
649 gdouble zoom_to_request;
651 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
652 pg = (struct Page *)pglist->data;
653 // in progressive mode we scale only visible pages
654 if (ui.progressive_bg && !is_visible(pg)) continue;
656 if (pg->bg->type == BG_PIXMAP && pg->bg->canvas_item!=NULL) { // do the rescaling ourselves
657 g_object_get(G_OBJECT(pg->bg->canvas_item), "pixbuf", &pix, NULL);
658 if (pix!=pg->bg->pixbuf)
659 gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pg->bg->pixbuf, NULL);
660 pg->bg->pixbuf_scale = 0;
662 if (pg->bg->type == BG_PDF) {
663 // make pixmap scale to correct size if current one is wrong
664 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
665 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
666 if (pg->bg->canvas_item != NULL && !is_well_scaled) {
667 g_object_get(pg->bg->canvas_item, "width-in-pixels", &is_well_scaled, NULL);
669 gnome_canvas_item_set(pg->bg->canvas_item,
670 "width", pg->width, "height", pg->height,
671 "width-in-pixels", FALSE, "height-in-pixels", FALSE,
672 "width-set", TRUE, "height-set", TRUE,
675 // request an asynchronous update to a better pixmap if needed
676 zoom_to_request = MIN(ui.zoom, MAX_SAFE_RENDER_DPI/72.0);
677 if (pg->bg->pixbuf_scale == zoom_to_request) continue;
678 add_bgpdf_request(pg->bg->file_page_seq, zoom_to_request);
679 pg->bg->pixbuf_scale = zoom_to_request;
684 gboolean have_intersect(struct BBox *a, struct BBox *b)
686 return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
687 (MAX(a->left, b->left) <= MIN(a->right, b->right));
690 /* In libgnomecanvas 2.10.0, the lower/raise functions fail to update
691 correctly the end of the group's item list. We try to work around this.
692 DON'T USE gnome_canvas_item_raise/lower directly !! */
694 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
698 i1 = g_list_index(g->item_list, item);
699 if (i1 == -1) return;
701 if (after == NULL) i2 = -1;
702 else i2 = g_list_index(g->item_list, after);
704 if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
705 if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
707 // BUGFIX for libgnomecanvas
708 g->item_list_end = g_list_last(g->item_list);
711 void rgb_to_gdkcolor(guint rgba, GdkColor *color)
714 color->red = ((rgba>>24)&0xff)*0x101;
715 color->green = ((rgba>>16)&0xff)*0x101;
716 color->blue = ((rgba>>8)&0xff)*0x101;
719 guint32 gdkcolor_to_rgba(GdkColor gdkcolor, guint16 alpha)
721 guint32 rgba = ((gdkcolor.red & 0xff00) << 16) |
722 ((gdkcolor.green & 0xff00) << 8) |
723 ((gdkcolor.blue & 0xff00) ) |
724 ((alpha & 0xff00) >> 8);
729 // some interface functions
731 void update_thickness_buttons(void)
733 if (ui.selection!=NULL || ui.toolno[ui.cur_mapping] >= NUM_STROKE_TOOLS) {
734 gtk_toggle_tool_button_set_active(
735 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
737 switch (ui.cur_brush->thickness_no) {
739 gtk_toggle_tool_button_set_active(
740 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
742 case THICKNESS_MEDIUM:
743 gtk_toggle_tool_button_set_active(
744 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
746 case THICKNESS_THICK:
747 gtk_toggle_tool_button_set_active(
748 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
751 gtk_toggle_tool_button_set_active(
752 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
756 void update_color_buttons(void)
759 GtkColorButton *colorbutton;
761 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
762 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
763 gtk_toggle_tool_button_set_active(
764 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
766 switch (ui.cur_brush->color_no) {
768 gtk_toggle_tool_button_set_active(
769 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
772 gtk_toggle_tool_button_set_active(
773 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
776 gtk_toggle_tool_button_set_active(
777 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
780 gtk_toggle_tool_button_set_active(
781 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
784 gtk_toggle_tool_button_set_active(
785 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
787 case COLOR_LIGHTBLUE:
788 gtk_toggle_tool_button_set_active(
789 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
791 case COLOR_LIGHTGREEN:
792 gtk_toggle_tool_button_set_active(
793 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
796 gtk_toggle_tool_button_set_active(
797 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
800 gtk_toggle_tool_button_set_active(
801 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
804 gtk_toggle_tool_button_set_active(
805 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
808 gtk_toggle_tool_button_set_active(
809 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
812 gtk_toggle_tool_button_set_active(
813 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
816 colorbutton = GTK_COLOR_BUTTON(GET_COMPONENT("buttonColorChooser"));
817 if ((ui.toolno[ui.cur_mapping] != TOOL_PEN &&
818 ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER &&
819 ui.toolno[ui.cur_mapping] != TOOL_TEXT))
820 gdkcolor.red = gdkcolor.blue = gdkcolor.green = 0;
821 else rgb_to_gdkcolor(ui.cur_brush->color_rgba, &gdkcolor);
822 gtk_color_button_set_color(colorbutton, &gdkcolor);
823 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
824 gtk_color_button_set_alpha(colorbutton,
825 (ui.cur_brush->color_rgba&0xff)*0x101);
826 gtk_color_button_set_use_alpha(colorbutton, TRUE);
828 gtk_color_button_set_alpha(colorbutton, 0xffff);
829 gtk_color_button_set_use_alpha(colorbutton, FALSE);
833 void update_tool_buttons(void)
835 switch(ui.toolno[ui.cur_mapping]) {
837 gtk_toggle_tool_button_set_active(
838 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
841 gtk_toggle_tool_button_set_active(
842 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
844 case TOOL_HIGHLIGHTER:
845 gtk_toggle_tool_button_set_active(
846 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
849 gtk_toggle_tool_button_set_active(
850 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
852 case TOOL_SELECTREGION:
853 gtk_toggle_tool_button_set_active(
854 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
856 case TOOL_SELECTRECT:
857 gtk_toggle_tool_button_set_active(
858 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
861 gtk_toggle_tool_button_set_active(
862 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
865 gtk_toggle_tool_button_set_active(
866 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
870 gtk_toggle_tool_button_set_active(
871 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
872 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
873 gtk_toggle_tool_button_set_active(
874 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
875 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
877 update_thickness_buttons();
878 update_color_buttons();
881 void update_tool_menu(void)
883 switch(ui.toolno[0]) {
885 gtk_check_menu_item_set_active(
886 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
889 gtk_check_menu_item_set_active(
890 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
892 case TOOL_HIGHLIGHTER:
893 gtk_check_menu_item_set_active(
894 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
897 gtk_check_menu_item_set_active(
898 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
900 case TOOL_SELECTREGION:
901 gtk_check_menu_item_set_active(
902 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
904 case TOOL_SELECTRECT:
905 gtk_check_menu_item_set_active(
906 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
909 gtk_check_menu_item_set_active(
910 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
913 gtk_check_menu_item_set_active(
914 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
918 gtk_check_menu_item_set_active(
919 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
920 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
921 gtk_check_menu_item_set_active(
922 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
923 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
926 void update_ruler_indicator(void)
928 gtk_toggle_tool_button_set_active(
929 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
930 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
931 gtk_toggle_tool_button_set_active(
932 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
933 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
934 gtk_check_menu_item_set_active(
935 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
936 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
937 gtk_check_menu_item_set_active(
938 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
939 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
942 void update_color_menu(void)
944 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
945 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
946 gtk_check_menu_item_set_active(
947 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
949 switch (ui.cur_brush->color_no) {
951 gtk_check_menu_item_set_active(
952 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
955 gtk_check_menu_item_set_active(
956 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
959 gtk_check_menu_item_set_active(
960 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
963 gtk_check_menu_item_set_active(
964 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
967 gtk_check_menu_item_set_active(
968 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
970 case COLOR_LIGHTBLUE:
971 gtk_check_menu_item_set_active(
972 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
974 case COLOR_LIGHTGREEN:
975 gtk_check_menu_item_set_active(
976 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
979 gtk_check_menu_item_set_active(
980 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
983 gtk_check_menu_item_set_active(
984 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
987 gtk_check_menu_item_set_active(
988 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
991 gtk_check_menu_item_set_active(
992 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
995 gtk_check_menu_item_set_active(
996 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
1000 void update_pen_props_menu(void)
1002 switch(ui.brushes[0][TOOL_PEN].thickness_no) {
1003 case THICKNESS_VERYFINE:
1004 gtk_check_menu_item_set_active(
1005 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
1007 case THICKNESS_FINE:
1008 gtk_check_menu_item_set_active(
1009 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
1011 case THICKNESS_MEDIUM:
1012 gtk_check_menu_item_set_active(
1013 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
1015 case THICKNESS_THICK:
1016 gtk_check_menu_item_set_active(
1017 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
1019 case THICKNESS_VERYTHICK:
1020 gtk_check_menu_item_set_active(
1021 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
1026 void update_eraser_props_menu(void)
1028 switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
1029 case THICKNESS_FINE:
1030 gtk_check_menu_item_set_active(
1031 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
1033 case THICKNESS_MEDIUM:
1034 gtk_check_menu_item_set_active(
1035 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
1037 case THICKNESS_THICK:
1038 gtk_check_menu_item_set_active(
1039 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
1043 gtk_check_menu_item_set_active(
1044 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
1045 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
1046 gtk_check_menu_item_set_active(
1047 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
1048 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
1049 gtk_check_menu_item_set_active(
1050 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
1051 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
1054 void update_highlighter_props_menu(void)
1056 switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
1057 case THICKNESS_FINE:
1058 gtk_check_menu_item_set_active(
1059 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
1061 case THICKNESS_MEDIUM:
1062 gtk_check_menu_item_set_active(
1063 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
1065 case THICKNESS_THICK:
1066 gtk_check_menu_item_set_active(
1067 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
1072 void update_mappings_menu_linkings(void)
1074 switch (ui.linked_brush[1]) {
1076 gtk_check_menu_item_set_active(
1077 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
1080 gtk_check_menu_item_set_active(
1081 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
1084 gtk_check_menu_item_set_active(
1085 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
1088 switch (ui.linked_brush[2]) {
1090 gtk_check_menu_item_set_active(
1091 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
1094 gtk_check_menu_item_set_active(
1095 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
1098 gtk_check_menu_item_set_active(
1099 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
1104 void update_mappings_menu(void)
1106 gtk_widget_set_sensitive(GET_COMPONENT("optionsButtonMappings"), ui.use_xinput);
1107 gtk_widget_set_sensitive(GET_COMPONENT("optionsDiscardCoreEvents"), ui.use_xinput);
1108 gtk_widget_set_sensitive(GET_COMPONENT("optionsPressureSensitive"), ui.use_xinput);
1109 gtk_check_menu_item_set_active(
1110 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
1111 gtk_check_menu_item_set_active(
1112 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsDiscardCoreEvents")), ui.discard_corepointer);
1113 gtk_check_menu_item_set_active(
1114 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsPressureSensitive")), ui.pressure_sensitivity);
1116 switch(ui.toolno[1]) {
1118 gtk_check_menu_item_set_active(
1119 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
1122 gtk_check_menu_item_set_active(
1123 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
1125 case TOOL_HIGHLIGHTER:
1126 gtk_check_menu_item_set_active(
1127 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
1130 gtk_check_menu_item_set_active(
1131 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
1133 case TOOL_SELECTREGION:
1134 gtk_check_menu_item_set_active(
1135 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
1137 case TOOL_SELECTRECT:
1138 gtk_check_menu_item_set_active(
1139 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
1141 case TOOL_VERTSPACE:
1142 gtk_check_menu_item_set_active(
1143 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
1146 switch(ui.toolno[2]) {
1148 gtk_check_menu_item_set_active(
1149 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
1152 gtk_check_menu_item_set_active(
1153 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
1155 case TOOL_HIGHLIGHTER:
1156 gtk_check_menu_item_set_active(
1157 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
1160 gtk_check_menu_item_set_active(
1161 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
1163 case TOOL_SELECTREGION:
1164 gtk_check_menu_item_set_active(
1165 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
1167 case TOOL_SELECTRECT:
1168 gtk_check_menu_item_set_active(
1169 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
1171 case TOOL_VERTSPACE:
1172 gtk_check_menu_item_set_active(
1173 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1176 update_mappings_menu_linkings();
1179 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1182 struct Layer *layer;
1187 /* re-show all the layers of the old page */
1188 if (ui.cur_page != NULL)
1189 for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1190 layer = (struct Layer *)list->data;
1191 if (layer->group!=NULL)
1192 gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1195 ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1196 ui.layerno = ui.cur_page->nlayers-1;
1197 ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1198 update_page_stuff();
1199 if (ui.progressive_bg) rescale_bg_pixmaps();
1201 if (rescroll) { // scroll and force a refresh
1202 /* -- this seems to cause some display bugs ??
1203 gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1204 ui.cur_page->voffset*ui.zoom); */
1205 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1206 cy = ui.cur_page->voffset*ui.zoom;
1207 gnome_canvas_scroll_to(canvas, cx, cy);
1210 gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1211 else if (!ui.view_continuous)
1212 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1216 void update_page_stuff(void)
1219 GtkComboBox *layerbox;
1222 GtkSpinButton *spin;
1224 double vertpos, maxwidth;
1226 // move the page groups to their rightful locations or hide them
1227 if (ui.view_continuous) {
1230 for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1231 pg = (struct Page *)pglist->data;
1232 if (pg->group!=NULL) {
1233 pg->hoffset = 0.; pg->voffset = vertpos;
1234 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1235 "x", pg->hoffset, "y", pg->voffset, NULL);
1236 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1238 vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1239 if (pg->width > maxwidth) maxwidth = pg->width;
1241 vertpos -= VIEW_CONTINUOUS_SKIP;
1242 gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1244 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1245 pg = (struct Page *)pglist->data;
1246 if (pg == ui.cur_page && pg->group!=NULL) {
1247 pg->hoffset = 0.; pg->voffset = 0.;
1248 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1249 "x", pg->hoffset, "y", pg->voffset, NULL);
1250 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1252 if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1255 gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1258 // update the page / layer info at bottom of screen
1260 spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1261 ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1262 gtk_spin_button_set_range(spin, 1, journal.npages+1);
1263 /* npages+1 will be used to create a new page at end */
1264 gtk_spin_button_set_value(spin, ui.pageno+1);
1265 g_snprintf(tmp, 10, _(" of %d"), journal.npages);
1266 gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1268 layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1269 if (ui.layerbox_length == 0) {
1270 gtk_combo_box_prepend_text(layerbox, _("Background"));
1271 ui.layerbox_length++;
1273 while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1274 gtk_combo_box_remove_text(layerbox, 0);
1275 ui.layerbox_length--;
1277 while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1278 g_snprintf(tmp, 10, _("Layer %d"), ui.layerbox_length++);
1279 gtk_combo_box_prepend_text(layerbox, tmp);
1281 gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1282 ui.in_update_page_stuff = FALSE;
1284 gtk_container_forall(GTK_CONTAINER(layerbox), unset_flags, (gpointer)GTK_CAN_FOCUS);
1286 // update the paper-style menu radio buttons
1288 if (ui.view_continuous)
1289 gtk_check_menu_item_set_active(
1290 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1292 gtk_check_menu_item_set_active(
1293 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1295 if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1296 switch (ui.cur_page->bg->color_no) {
1298 gtk_check_menu_item_set_active(
1299 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1302 gtk_check_menu_item_set_active(
1303 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1306 gtk_check_menu_item_set_active(
1307 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1310 gtk_check_menu_item_set_active(
1311 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1314 gtk_check_menu_item_set_active(
1315 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1318 gtk_check_menu_item_set_active(
1319 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1322 gtk_check_menu_item_set_active(
1323 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOther")), TRUE);
1326 switch (ui.cur_page->bg->ruling) {
1328 gtk_check_menu_item_set_active(
1329 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1332 gtk_check_menu_item_set_active(
1333 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1336 gtk_check_menu_item_set_active(
1337 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1340 gtk_check_menu_item_set_active(
1341 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1345 gtk_check_menu_item_set_active(
1346 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1347 gtk_check_menu_item_set_active(
1348 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1351 // enable/disable the page/layer menu items and toolbar buttons
1353 gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"),
1354 ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1355 gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1356 ui.cur_page->bg->type == BG_SOLID);
1358 gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1359 gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1360 gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1361 gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1362 gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1363 gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1364 gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1365 gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1367 gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1368 gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1370 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1371 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1374 void update_toolbar_and_menu(void)
1376 update_tool_buttons(); // takes care of other toolbar buttons as well
1378 update_color_menu();
1379 update_pen_props_menu();
1380 update_eraser_props_menu();
1381 update_highlighter_props_menu();
1382 update_mappings_menu();
1384 gtk_toggle_tool_button_set_active(
1385 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1386 gtk_check_menu_item_set_active(
1387 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1390 void update_file_name(char *filename)
1393 if (ui.filename != NULL) g_free(ui.filename);
1394 ui.filename = filename;
1395 if (filename == NULL) {
1396 gtk_window_set_title(GTK_WINDOW (winMain), _("Xournal"));
1399 p = g_utf8_strrchr(filename, -1, '/');
1400 if (p == NULL) p = filename;
1401 else p = g_utf8_next_char(p);
1402 g_snprintf(tmp, 100, _("Xournal - %s"), p);
1403 gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1404 new_mru_entry(filename);
1406 if (filename[0]=='/') {
1407 if (ui.default_path!=NULL) g_free(ui.default_path);
1408 ui.default_path = g_path_get_dirname(filename);
1412 void update_undo_redo_enabled(void)
1414 gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1415 gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1416 gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1417 gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1420 void update_copy_paste_enabled(void)
1422 gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1423 gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1424 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_item_type!=ITEM_TEXT);
1425 gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1426 gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1427 gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1428 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_item_type!=ITEM_TEXT);
1431 void update_mapping_linkings(int toolno)
1435 for (i = 1; i<=NUM_BUTTONS; i++) {
1436 if (ui.linked_brush[i] == BRUSH_LINKED) {
1437 if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1438 g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1440 if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1441 ui.linked_brush[i] = BRUSH_STATIC;
1442 if (i==1 || i==2) update_mappings_menu_linkings();
1447 void set_cur_color(int color_no, guint color_rgba)
1449 int which_mapping, tool;
1451 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) tool = TOOL_HIGHLIGHTER;
1452 else tool = TOOL_PEN;
1453 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1454 which_mapping = ui.cur_mapping;
1455 else which_mapping = 0;
1457 ui.brushes[which_mapping][tool].color_no = color_no;
1458 if (tool == TOOL_HIGHLIGHTER && (color_rgba & 0xff) == 0xff)
1459 ui.brushes[which_mapping][tool].color_rgba = color_rgba & ui.hiliter_alpha_mask;
1461 ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1462 update_mapping_linkings(tool);
1465 void recolor_temp_text(int color_no, guint color_rgba)
1469 if (ui.cur_item_type!=ITEM_TEXT) return;
1470 if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1472 undo->type = ITEM_TEXT_ATTRIB;
1473 undo->item = ui.cur_item;
1474 undo->str = g_strdup(ui.cur_item->font_name);
1475 undo->val_x = ui.cur_item->font_size;
1476 undo->brush = (struct Brush *)g_memdup(&(ui.cur_item->brush), sizeof(struct Brush));
1478 ui.cur_item->brush.color_no = color_no;
1479 ui.cur_item->brush.color_rgba = color_rgba;
1480 rgb_to_gdkcolor(color_rgba, &gdkcolor);
1481 gtk_widget_modify_text(ui.cur_item->widget, GTK_STATE_NORMAL, &gdkcolor);
1482 gtk_widget_grab_focus(ui.cur_item->widget);
1485 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1487 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1488 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1491 else if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_TOOL_BUTTON) {
1492 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1496 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1498 if (ui.cur_item_type == ITEM_TEXT)
1499 recolor_temp_text(color_no, color_rgba);
1501 if (ui.selection != NULL) {
1502 recolor_selection(color_no, color_rgba);
1503 update_color_buttons();
1504 update_color_menu();
1507 if (ui.toolno[ui.cur_mapping] != TOOL_PEN && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER
1508 && ui.toolno[ui.cur_mapping] != TOOL_TEXT) {
1509 if (ui.selection != NULL) return;
1512 ui.toolno[ui.cur_mapping] = TOOL_PEN;
1513 ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1514 update_tool_buttons();
1518 set_cur_color(color_no, color_rgba);
1519 update_color_buttons();
1520 update_color_menu();
1524 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1528 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1529 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1532 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1536 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1538 if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1539 rethicken_selection(val);
1540 update_thickness_buttons();
1543 if (tool >= NUM_STROKE_TOOLS) {
1544 update_thickness_buttons(); // undo illegal button selection
1548 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1549 which_mapping = ui.cur_mapping;
1550 else which_mapping = 0;
1551 if (ui.brushes[which_mapping][tool].thickness_no == val) return;
1553 ui.brushes[which_mapping][tool].thickness_no = val;
1554 ui.brushes[which_mapping][tool].thickness = predef_thickness[tool][val];
1555 update_mapping_linkings(tool);
1557 update_thickness_buttons();
1558 if (tool == TOOL_PEN) update_pen_props_menu();
1559 if (tool == TOOL_ERASER) update_eraser_props_menu();
1560 if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1564 void process_papercolor_activate(GtkMenuItem *menuitem, int color)
1570 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1573 if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages)
1574 gtk_check_menu_item_set_active(
1575 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1579 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1580 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1581 if (pg->bg->type == BG_SOLID && pg->bg->color_no != color) {
1583 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1584 undo->multiop |= MULTIOP_CONT_REDO;
1586 undo->type = ITEM_NEW_BG_ONE;
1588 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1589 undo->bg->canvas_item = NULL;
1591 pg->bg->color_no = color;
1592 pg->bg->color_rgba = predef_bgcolors_rgba[color];
1593 update_canvas_bg(pg);
1595 if (!ui.bg_apply_all_pages) break;
1597 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1600 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1604 gboolean hasdone, must_upd;
1606 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1609 if (ui.bg_apply_all_pages)
1610 gtk_check_menu_item_set_active(
1611 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1616 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1617 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1618 if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1620 undo->type = ITEM_NEW_BG_ONE;
1621 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1622 undo->multiop |= MULTIOP_CONT_REDO;
1625 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1626 undo->bg->canvas_item = NULL;
1628 if (pg->bg->type != BG_SOLID) {
1629 pg->bg->type = BG_SOLID;
1630 pg->bg->color_no = COLOR_WHITE;
1631 pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1632 pg->bg->filename = NULL;
1633 pg->bg->pixbuf = NULL;
1636 pg->bg->ruling = style;
1637 update_canvas_bg(pg);
1639 if (!ui.bg_apply_all_pages) break;
1641 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1642 if (must_upd) update_page_stuff();
1645 gboolean ok_to_close(void)
1648 GtkResponseType response;
1650 if (ui.saved) return TRUE;
1651 dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1652 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Save changes to '%s'?"),
1653 (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1654 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1655 response = gtk_dialog_run(GTK_DIALOG (dialog));
1656 gtk_widget_destroy(dialog);
1657 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1658 return FALSE; // aborted
1659 if (response == GTK_RESPONSE_YES) {
1660 on_fileSave_activate(NULL, NULL);
1661 if (!ui.saved) return FALSE; // if save failed, then we abort
1666 // send the focus back to the appropriate widget
1668 void reset_focus(void)
1670 if (ui.cur_item_type == ITEM_TEXT)
1671 gtk_widget_grab_focus(ui.cur_item->widget);
1673 gtk_widget_grab_focus(GTK_WIDGET(canvas));
1676 // selection / clipboard stuff
1678 void reset_selection(void)
1680 if (ui.selection == NULL) return;
1681 if (ui.selection->canvas_item != NULL)
1682 gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1683 g_list_free(ui.selection->items);
1684 g_free(ui.selection);
1685 ui.selection = NULL;
1686 update_copy_paste_enabled();
1687 update_color_menu();
1688 update_thickness_buttons();
1689 update_color_buttons();
1690 update_font_button();
1694 void move_journal_items_by(GList *itemlist, double dx, double dy,
1695 struct Layer *l1, struct Layer *l2, GList *depths)
1698 GnomeCanvasItem *refitem;
1703 while (itemlist!=NULL) {
1704 item = (struct Item *)itemlist->data;
1705 if (item->type == ITEM_STROKE)
1706 for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1707 { pt[0] += dx; pt[1] += dy; }
1708 if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1709 item->bbox.left += dx;
1710 item->bbox.right += dx;
1711 item->bbox.top += dy;
1712 item->bbox.bottom += dy;
1715 // find out where to insert
1716 if (depths != NULL) {
1717 if (depths->data == NULL) link = l2->items;
1719 link = g_list_find(l2->items, depths->data);
1720 if (link != NULL) link = link->next;
1723 l2->items = g_list_insert_before(l2->items, link, item);
1725 l1->items = g_list_remove(l1->items, item);
1728 if (depths != NULL) { // also raise/lower the canvas items
1729 if (item->canvas_item!=NULL) {
1730 if (depths->data == NULL) link = NULL;
1731 else link = g_list_find(l2->items, depths->data);
1732 if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1733 else refitem = NULL;
1734 lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1736 depths = depths->next;
1738 itemlist = itemlist->next;
1742 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1743 double offset_x, double offset_y)
1747 double mean_scaling, temp;
1749 GnomeCanvasGroup *group;
1752 /* geometric mean of x and y scalings = rescaling for stroke widths
1753 and for text font sizes */
1754 mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1756 for (list = itemlist; list != NULL; list = list->next) {
1757 item = (struct Item *)list->data;
1758 if (item->type == ITEM_STROKE) {
1759 item->brush.thickness = item->brush.thickness * mean_scaling;
1760 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1761 pt[0] = pt[0]*scaling_x + offset_x;
1762 pt[1] = pt[1]*scaling_y + offset_y;
1764 if (item->brush.variable_width)
1765 for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1766 *wid = *wid * mean_scaling;
1768 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1769 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1770 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1771 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1772 if (item->bbox.left > item->bbox.right) {
1773 temp = item->bbox.left;
1774 item->bbox.left = item->bbox.right;
1775 item->bbox.right = temp;
1777 if (item->bbox.top > item->bbox.bottom) {
1778 temp = item->bbox.top;
1779 item->bbox.top = item->bbox.bottom;
1780 item->bbox.bottom = temp;
1783 if (item->type == ITEM_TEXT) {
1784 /* must scale about NW corner -- all other points of the text box
1785 are font- and zoom-dependent, so scaling about center of text box
1786 couldn't be undone properly. FIXME? */
1787 item->font_size *= mean_scaling;
1788 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1789 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1792 if (item->canvas_item!=NULL) {
1793 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1794 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1795 make_canvas_item_one(group, item);
1800 // Switch between button mappings
1802 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1803 click event is being processed ... or if ui.button_switch_mapping is
1804 enabled and mappings are switched (but even then, canvas should have
1805 a pointer grab from the initial click that switched the mapping) */
1807 void switch_mapping(int m)
1809 if (ui.cur_mapping == m) return;
1812 if (ui.toolno[m] < NUM_STROKE_TOOLS)
1813 ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1814 if (ui.toolno[m] == TOOL_TEXT)
1815 ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1816 if (m==0) ui.which_unswitch_button = 0;
1818 update_tool_buttons();
1819 update_color_menu();
1823 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1825 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1826 if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1827 if (ui.toolno[m] == tool) return;
1831 ui.toolno[m] = tool;
1832 if (ui.linked_brush[m] == BRUSH_COPIED) {
1833 ui.linked_brush[m] = BRUSH_STATIC;
1834 update_mappings_menu_linkings();
1838 // update the ordering of components in the main vbox
1840 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1841 {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1843 void update_vbox_order(int *order)
1847 GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1848 gboolean present[VBOX_MAIN_NITEMS];
1850 for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1852 for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1853 if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1854 present[order[i]] = TRUE;
1855 child = GET_COMPONENT(vbox_component_names[order[i]]);
1856 gtk_box_reorder_child(vboxMain, child, j++);
1857 gtk_widget_show(child);
1859 for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1860 if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1863 gchar *make_cur_font_name(void)
1868 if (ui.cur_item_type == ITEM_TEXT)
1869 str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1870 else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1871 ui.selection->items->next==NULL &&
1872 (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1873 str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1875 str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1879 void update_font_button(void)
1883 str = make_cur_font_name();
1884 gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1888 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1890 return GTK_WIDGET_SENSITIVE(widget);
1893 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1895 if (ui.cur_item_type == ITEM_TEXT) {
1896 g_signal_stop_emission_by_name(widget, "can-activate-accel");
1899 return GTK_WIDGET_SENSITIVE(widget);
1902 void allow_all_accels(void)
1904 g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1905 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1906 g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1907 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1908 g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1909 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1910 g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1911 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1912 g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1913 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1914 g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1915 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1916 g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1917 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1918 g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1919 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1920 g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1921 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1922 g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1923 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1924 g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1925 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1926 g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1927 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1928 g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1929 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1930 g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1931 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1932 g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1933 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1934 g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1935 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1936 g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1937 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1938 g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1939 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1940 g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1941 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1942 g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1943 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1944 g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1945 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1946 g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1947 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1948 g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1949 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1950 g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1951 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1952 g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1953 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1954 /* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1955 "can-activate-accel", G_CALLBACK(can_accel), NULL); */
1956 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1957 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1958 g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1959 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1960 g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1961 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1962 g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1963 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1964 g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1965 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1966 g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
1967 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1970 void add_scroll_bindings(void)
1972 GtkBindingSet *binding_set;
1974 binding_set = gtk_binding_set_by_class(
1975 G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
1976 gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
1977 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1978 G_TYPE_BOOLEAN, FALSE);
1979 gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
1980 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1981 G_TYPE_BOOLEAN, FALSE);
1982 gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
1983 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1984 G_TYPE_BOOLEAN, FALSE);
1985 gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
1986 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1987 G_TYPE_BOOLEAN, FALSE);
1988 gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
1989 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1990 G_TYPE_BOOLEAN, TRUE);
1991 gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
1992 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1993 G_TYPE_BOOLEAN, TRUE);
1994 gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
1995 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1996 G_TYPE_BOOLEAN, TRUE);
1997 gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
1998 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1999 G_TYPE_BOOLEAN, TRUE);
2002 gboolean is_event_within_textview(GdkEventButton *event)
2006 if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2007 get_pointer_coords((GdkEvent *)event, pt);
2008 if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2009 if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2013 void hide_unimplemented(void)
2015 gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2016 gtk_widget_hide(GET_COMPONENT("journalFlatten"));
2017 gtk_widget_hide(GET_COMPONENT("papercolorOther"));
2018 gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2019 gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2020 gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2021 gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2022 gtk_widget_hide(GET_COMPONENT("helpIndex"));
2024 /* config file only works with glib 2.6 and beyond */
2025 if (glib_minor_version<6) {
2026 gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2027 gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2029 /* gtkprint only works with gtk+ 2.10 and beyond */
2030 if (gtk_check_version(2, 10, 0)) {
2031 gtk_widget_hide(GET_COMPONENT("filePrint"));
2035 // toggle fullscreen mode
2036 void do_fullscreen(gboolean active)
2039 ui.fullscreen = active;
2040 gtk_check_menu_item_set_active(
2041 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2042 gtk_toggle_tool_button_set_active(
2043 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2045 if (ui.fullscreen) gtk_window_fullscreen(GTK_WINDOW(winMain));
2046 else gtk_window_unfullscreen(GTK_WINDOW(winMain));
2048 update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2051 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2052 elements receive XInput events that they can't handle properly */
2054 // prevent interface items from getting bogus XInput events
2056 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2059 if (event->type == GDK_MOTION_NOTIFY &&
2060 event->motion.device != gdk_device_get_core_pointer())
2062 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2063 event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2064 event->button.device != gdk_device_get_core_pointer())
2069 /* Code to turn an extended input event into a core event and send it to
2070 a different GdkWindow -- e.g. could be used when a click in a text edit box
2071 gets sent to the canvas instead due to incorrect event translation.
2072 We now turn off xinput altogether while editing text under GTK+ 2.17, so
2073 this isn't needed any more... but could become useful again someday!
2077 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2083 if (user_data) window = (GdkWindow *)user_data;
2084 else window = widget->window;
2086 if (event->type == GDK_MOTION_NOTIFY &&
2087 event->motion.device != gdk_device_get_core_pointer()) {
2088 // printf("fixing motion\n");
2089 gdk_window_get_pointer(window, &ix, &iy, NULL);
2090 event->motion.x = ix; event->motion.y = iy;
2091 event->motion.device = gdk_device_get_core_pointer();
2092 g_object_unref(event->motion.window);
2093 event->motion.window = g_object_ref(window);
2094 gtk_widget_event(widget, event);
2097 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2098 event->button.device != gdk_device_get_core_pointer()) {
2099 // printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2100 gdk_window_get_pointer(window, &ix, &iy, NULL);
2101 event->button.x = ix; event->button.y = iy;
2102 event->button.device = gdk_device_get_core_pointer();
2103 g_object_unref(event->button.window);
2104 event->button.window = g_object_ref(window);
2105 // printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2106 gtk_widget_event(widget, event);
2113 // disable xinput when layer combo box is popped up, to avoid GTK+ 2.17 crash
2115 gboolean combobox_popup_disable_xinput (GtkWidget *widget, GdkEvent *event,
2120 g_object_get(G_OBJECT(widget), "popup-shown", &is_shown, NULL);
2121 gtk_widget_set_extension_events(GTK_WIDGET (canvas),
2122 (ui.use_xinput && !is_shown)?GDK_EXTENSION_EVENTS_ALL:GDK_EXTENSION_EVENTS_NONE);
2125 /* When enter is pressed into page spinbox, send focus back to canvas. */
2127 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2133 /* recursively unset widget flags */
2135 void unset_flags(GtkWidget *w, gpointer flag)
2137 GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2138 if(GTK_IS_CONTAINER(w))
2139 gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2142 /* reset focus when a key or button press event reaches someone, or when the
2143 page-number spin button should relinquish control... */
2145 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2147 if (w == GET_COMPONENT("hbox1")) {
2148 /* the event won't be processed since the hbox1 doesn't know what to do with it,
2149 so we might as well kill it and avoid confusing ourselves when it gets
2150 propagated further ... */
2153 if (w == GET_COMPONENT("spinPageNo")) {
2154 /* we let the spin button take care of itself, and don't steal its focus,
2155 unless the user presses Esc or Tab (in those cases we intervene) */
2156 if (ev->type != GDK_KEY_PRESS) return FALSE;
2157 if (ev->key.keyval == GDK_Escape)
2158 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2159 else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2160 return FALSE; // let the spin button process it
2163 // otherwise, we want to make sure the canvas or text item gets focus back...
2168 void install_focus_hooks(GtkWidget *w, gpointer data)
2170 if (w == NULL) return;
2171 g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2172 g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2173 if (GTK_IS_MENU_ITEM(w)) {
2174 g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2175 install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2177 if(GTK_IS_CONTAINER(w))
2178 gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);