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) {
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 if (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("papercolorNA")), 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, guint rgba)
1570 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1571 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1575 if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages || color == COLOR_OTHER)
1576 gtk_check_menu_item_set_active(
1577 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1581 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1582 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1583 if (pg->bg->type == BG_SOLID && pg->bg->color_rgba != rgba) {
1585 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1586 undo->multiop |= MULTIOP_CONT_REDO;
1588 undo->type = ITEM_NEW_BG_ONE;
1590 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1591 undo->bg->canvas_item = NULL;
1593 pg->bg->color_no = color;
1594 pg->bg->color_rgba = rgba;
1595 update_canvas_bg(pg);
1597 if (!ui.bg_apply_all_pages) break;
1599 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1602 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1606 gboolean hasdone, must_upd;
1608 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1611 if (ui.bg_apply_all_pages)
1612 gtk_check_menu_item_set_active(
1613 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1618 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1619 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1620 if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1622 undo->type = ITEM_NEW_BG_ONE;
1623 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1624 undo->multiop |= MULTIOP_CONT_REDO;
1627 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1628 undo->bg->canvas_item = NULL;
1630 if (pg->bg->type != BG_SOLID) {
1631 pg->bg->type = BG_SOLID;
1632 pg->bg->color_no = COLOR_WHITE;
1633 pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1634 pg->bg->filename = NULL;
1635 pg->bg->pixbuf = NULL;
1638 pg->bg->ruling = style;
1639 update_canvas_bg(pg);
1641 if (!ui.bg_apply_all_pages) break;
1643 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1644 if (must_upd) update_page_stuff();
1647 gboolean ok_to_close(void)
1650 GtkResponseType response;
1652 if (ui.saved) return TRUE;
1653 dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1654 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Save changes to '%s'?"),
1655 (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1656 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1657 response = gtk_dialog_run(GTK_DIALOG (dialog));
1658 gtk_widget_destroy(dialog);
1659 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1660 return FALSE; // aborted
1661 if (response == GTK_RESPONSE_YES) {
1662 on_fileSave_activate(NULL, NULL);
1663 if (!ui.saved) return FALSE; // if save failed, then we abort
1668 // send the focus back to the appropriate widget
1670 void reset_focus(void)
1672 if (ui.cur_item_type == ITEM_TEXT)
1673 gtk_widget_grab_focus(ui.cur_item->widget);
1675 gtk_widget_grab_focus(GTK_WIDGET(canvas));
1678 // selection / clipboard stuff
1680 void reset_selection(void)
1682 if (ui.selection == NULL) return;
1683 if (ui.selection->canvas_item != NULL)
1684 gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1685 g_list_free(ui.selection->items);
1686 g_free(ui.selection);
1687 ui.selection = NULL;
1688 update_copy_paste_enabled();
1689 update_color_menu();
1690 update_thickness_buttons();
1691 update_color_buttons();
1692 update_font_button();
1696 void move_journal_items_by(GList *itemlist, double dx, double dy,
1697 struct Layer *l1, struct Layer *l2, GList *depths)
1700 GnomeCanvasItem *refitem;
1705 while (itemlist!=NULL) {
1706 item = (struct Item *)itemlist->data;
1707 if (item->type == ITEM_STROKE)
1708 for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1709 { pt[0] += dx; pt[1] += dy; }
1710 if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1711 item->bbox.left += dx;
1712 item->bbox.right += dx;
1713 item->bbox.top += dy;
1714 item->bbox.bottom += dy;
1717 // find out where to insert
1718 if (depths != NULL) {
1719 if (depths->data == NULL) link = l2->items;
1721 link = g_list_find(l2->items, depths->data);
1722 if (link != NULL) link = link->next;
1725 l2->items = g_list_insert_before(l2->items, link, item);
1727 l1->items = g_list_remove(l1->items, item);
1730 if (depths != NULL) { // also raise/lower the canvas items
1731 if (item->canvas_item!=NULL) {
1732 if (depths->data == NULL) link = NULL;
1733 else link = g_list_find(l2->items, depths->data);
1734 if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1735 else refitem = NULL;
1736 lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1738 depths = depths->next;
1740 itemlist = itemlist->next;
1744 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1745 double offset_x, double offset_y)
1749 double mean_scaling, temp;
1751 GnomeCanvasGroup *group;
1754 /* geometric mean of x and y scalings = rescaling for stroke widths
1755 and for text font sizes */
1756 mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1758 for (list = itemlist; list != NULL; list = list->next) {
1759 item = (struct Item *)list->data;
1760 if (item->type == ITEM_STROKE) {
1761 item->brush.thickness = item->brush.thickness * mean_scaling;
1762 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1763 pt[0] = pt[0]*scaling_x + offset_x;
1764 pt[1] = pt[1]*scaling_y + offset_y;
1766 if (item->brush.variable_width)
1767 for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1768 *wid = *wid * mean_scaling;
1770 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1771 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1772 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1773 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1774 if (item->bbox.left > item->bbox.right) {
1775 temp = item->bbox.left;
1776 item->bbox.left = item->bbox.right;
1777 item->bbox.right = temp;
1779 if (item->bbox.top > item->bbox.bottom) {
1780 temp = item->bbox.top;
1781 item->bbox.top = item->bbox.bottom;
1782 item->bbox.bottom = temp;
1785 if (item->type == ITEM_TEXT) {
1786 /* must scale about NW corner -- all other points of the text box
1787 are font- and zoom-dependent, so scaling about center of text box
1788 couldn't be undone properly. FIXME? */
1789 item->font_size *= mean_scaling;
1790 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1791 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1794 if (item->canvas_item!=NULL) {
1795 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1796 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1797 make_canvas_item_one(group, item);
1802 // Switch between button mappings
1804 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1805 click event is being processed ... or if ui.button_switch_mapping is
1806 enabled and mappings are switched (but even then, canvas should have
1807 a pointer grab from the initial click that switched the mapping) */
1809 void switch_mapping(int m)
1811 if (ui.cur_mapping == m) return;
1814 if (ui.toolno[m] < NUM_STROKE_TOOLS)
1815 ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1816 if (ui.toolno[m] == TOOL_TEXT)
1817 ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1818 if (m==0) ui.which_unswitch_button = 0;
1820 update_tool_buttons();
1821 update_color_menu();
1825 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1827 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1828 if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1829 if (ui.toolno[m] == tool) return;
1833 ui.toolno[m] = tool;
1834 if (ui.linked_brush[m] == BRUSH_COPIED) {
1835 ui.linked_brush[m] = BRUSH_STATIC;
1836 update_mappings_menu_linkings();
1840 // update the ordering of components in the main vbox
1842 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1843 {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1845 void update_vbox_order(int *order)
1849 GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1850 gboolean present[VBOX_MAIN_NITEMS];
1852 for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1854 for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1855 if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1856 present[order[i]] = TRUE;
1857 child = GET_COMPONENT(vbox_component_names[order[i]]);
1858 gtk_box_reorder_child(vboxMain, child, j++);
1859 gtk_widget_show(child);
1861 for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1862 if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1865 gchar *make_cur_font_name(void)
1870 if (ui.cur_item_type == ITEM_TEXT)
1871 str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1872 else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1873 ui.selection->items->next==NULL &&
1874 (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1875 str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1877 str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1881 void update_font_button(void)
1885 str = make_cur_font_name();
1886 gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1890 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1892 return GTK_WIDGET_SENSITIVE(widget);
1895 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1897 if (ui.cur_item_type == ITEM_TEXT) {
1898 g_signal_stop_emission_by_name(widget, "can-activate-accel");
1901 return GTK_WIDGET_SENSITIVE(widget);
1904 void allow_all_accels(void)
1906 g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1907 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1908 g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1909 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1910 g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1911 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1912 g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1913 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1914 g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1915 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1916 g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1917 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1918 g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1919 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1920 g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1921 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1922 g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1923 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1924 g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1925 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1926 g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1927 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1928 g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1929 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1930 g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1931 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1932 g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1933 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1934 g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1935 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1936 g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1937 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1938 g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1939 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1940 g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1941 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1942 g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1943 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1944 g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1945 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1946 g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1947 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1948 g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1949 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1950 g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1951 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1952 g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1953 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1954 g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1955 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1956 /* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1957 "can-activate-accel", G_CALLBACK(can_accel), NULL); */
1958 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1959 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1960 g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1961 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1962 g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1963 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1964 g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1965 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1966 g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1967 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1968 g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
1969 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1972 void add_scroll_bindings(void)
1974 GtkBindingSet *binding_set;
1976 binding_set = gtk_binding_set_by_class(
1977 G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
1978 gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
1979 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1980 G_TYPE_BOOLEAN, FALSE);
1981 gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
1982 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1983 G_TYPE_BOOLEAN, FALSE);
1984 gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
1985 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1986 G_TYPE_BOOLEAN, FALSE);
1987 gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
1988 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1989 G_TYPE_BOOLEAN, FALSE);
1990 gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
1991 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1992 G_TYPE_BOOLEAN, TRUE);
1993 gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
1994 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1995 G_TYPE_BOOLEAN, TRUE);
1996 gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
1997 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1998 G_TYPE_BOOLEAN, TRUE);
1999 gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
2000 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2001 G_TYPE_BOOLEAN, TRUE);
2004 gboolean is_event_within_textview(GdkEventButton *event)
2008 if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2009 get_pointer_coords((GdkEvent *)event, pt);
2010 if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2011 if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2015 void hide_unimplemented(void)
2017 gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2018 gtk_widget_hide(GET_COMPONENT("journalFlatten"));
2019 gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2020 gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2021 gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2022 gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2023 gtk_widget_hide(GET_COMPONENT("helpIndex"));
2025 /* config file only works with glib 2.6 and beyond */
2026 if (glib_minor_version<6) {
2027 gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2028 gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2030 /* gtkprint only works with gtk+ 2.10 and beyond */
2031 if (gtk_check_version(2, 10, 0)) {
2032 gtk_widget_hide(GET_COMPONENT("filePrint"));
2036 // toggle fullscreen mode
2037 void do_fullscreen(gboolean active)
2040 ui.fullscreen = active;
2041 gtk_check_menu_item_set_active(
2042 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2043 gtk_toggle_tool_button_set_active(
2044 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2046 if (ui.fullscreen) gtk_window_fullscreen(GTK_WINDOW(winMain));
2047 else gtk_window_unfullscreen(GTK_WINDOW(winMain));
2049 update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2052 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2053 elements receive XInput events that they can't handle properly */
2055 // prevent interface items from getting bogus XInput events
2057 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2060 if (event->type == GDK_MOTION_NOTIFY &&
2061 event->motion.device != gdk_device_get_core_pointer())
2063 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2064 event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2065 event->button.device != gdk_device_get_core_pointer())
2070 /* Code to turn an extended input event into a core event and send it to
2071 a different GdkWindow -- e.g. could be used when a click in a text edit box
2072 gets sent to the canvas instead due to incorrect event translation.
2073 We now turn off xinput altogether while editing text under GTK+ 2.17, so
2074 this isn't needed any more... but could become useful again someday!
2078 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2084 if (user_data) window = (GdkWindow *)user_data;
2085 else window = widget->window;
2087 if (event->type == GDK_MOTION_NOTIFY &&
2088 event->motion.device != gdk_device_get_core_pointer()) {
2089 // printf("fixing motion\n");
2090 gdk_window_get_pointer(window, &ix, &iy, NULL);
2091 event->motion.x = ix; event->motion.y = iy;
2092 event->motion.device = gdk_device_get_core_pointer();
2093 g_object_unref(event->motion.window);
2094 event->motion.window = g_object_ref(window);
2095 gtk_widget_event(widget, event);
2098 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2099 event->button.device != gdk_device_get_core_pointer()) {
2100 // printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2101 gdk_window_get_pointer(window, &ix, &iy, NULL);
2102 event->button.x = ix; event->button.y = iy;
2103 event->button.device = gdk_device_get_core_pointer();
2104 g_object_unref(event->button.window);
2105 event->button.window = g_object_ref(window);
2106 // printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2107 gtk_widget_event(widget, event);
2115 /* When enter is pressed into page spinbox, send focus back to canvas. */
2117 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2123 /* recursively unset widget flags */
2125 void unset_flags(GtkWidget *w, gpointer flag)
2127 GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2128 if(GTK_IS_CONTAINER(w))
2129 gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2132 /* reset focus when a key or button press event reaches someone, or when the
2133 page-number spin button should relinquish control... */
2135 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2137 if (w == GET_COMPONENT("hbox1")) {
2138 /* the event won't be processed since the hbox1 doesn't know what to do with it,
2139 so we might as well kill it and avoid confusing ourselves when it gets
2140 propagated further ... */
2143 if (w == GET_COMPONENT("spinPageNo")) {
2144 /* we let the spin button take care of itself, and don't steal its focus,
2145 unless the user presses Esc or Tab (in those cases we intervene) */
2146 if (ev->type != GDK_KEY_PRESS) return FALSE;
2147 if (ev->key.keyval == GDK_Escape)
2148 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2149 else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2150 return FALSE; // let the spin button process it
2153 // otherwise, we want to make sure the canvas or text item gets focus back...
2158 void install_focus_hooks(GtkWidget *w, gpointer data)
2160 if (w == NULL) return;
2161 g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2162 g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2163 if (GTK_IS_MENU_ITEM(w)) {
2164 g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2165 install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2167 if(GTK_IS_CONTAINER(w))
2168 gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);