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)
421 if (event->type == GDK_MOTION_NOTIFY) {
422 axes = event->motion.axes;
423 device = event->motion.device;
426 axes = event->button.axes;
427 device = event->button.device;
430 if (device == gdk_device_get_core_pointer()
431 || device->num_axes <= 2) return 1.0;
433 rawpressure = axes[2]/(device->axes[2].max - device->axes[2].min);
434 if (!finite(rawpressure)) return 1.0;
436 return ((1-rawpressure)*ui.width_minimum_multiplier + rawpressure*ui.width_maximum_multiplier);
439 void update_item_bbox(struct Item *item)
444 if (item->type == ITEM_STROKE) {
445 item->bbox.left = item->bbox.right = item->path->coords[0];
446 item->bbox.top = item->bbox.bottom = item->path->coords[1];
447 for (i=1, p=item->path->coords+2; i<item->path->num_points; i++, p+=2)
449 if (p[0] < item->bbox.left) item->bbox.left = p[0];
450 if (p[0] > item->bbox.right) item->bbox.right = p[0];
451 if (p[1] < item->bbox.top) item->bbox.top = p[1];
452 if (p[1] > item->bbox.bottom) item->bbox.bottom = p[1];
455 if (item->type == ITEM_TEXT && item->canvas_item!=NULL) {
457 g_object_get(item->canvas_item, "text_width", &w, "text_height", &h, NULL);
458 item->bbox.right = item->bbox.left + w;
459 item->bbox.bottom = item->bbox.top + h;
463 void make_page_clipbox(struct Page *pg)
465 GnomeCanvasPathDef *pg_clip;
467 pg_clip = gnome_canvas_path_def_new_sized(4);
468 gnome_canvas_path_def_moveto(pg_clip, 0., 0.);
469 gnome_canvas_path_def_lineto(pg_clip, 0., pg->height);
470 gnome_canvas_path_def_lineto(pg_clip, pg->width, pg->height);
471 gnome_canvas_path_def_lineto(pg_clip, pg->width, 0.);
472 gnome_canvas_path_def_closepath(pg_clip);
473 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), "path", pg_clip, NULL);
474 gnome_canvas_path_def_unref(pg_clip);
477 void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
479 PangoFontDescription *font_desc;
480 GnomeCanvasPoints points;
483 if (item->type == ITEM_STROKE) {
484 if (!item->brush.variable_width)
485 item->canvas_item = gnome_canvas_item_new(group,
486 gnome_canvas_line_get_type(), "points", item->path,
487 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
488 "fill-color-rgba", item->brush.color_rgba,
489 "width-units", item->brush.thickness, NULL);
491 item->canvas_item = gnome_canvas_item_new(group,
492 gnome_canvas_group_get_type(), NULL);
493 points.num_points = 2;
494 points.ref_count = 1;
495 for (j = 0; j < item->path->num_points-1; j++) {
496 points.coords = item->path->coords+2*j;
497 gnome_canvas_item_new((GnomeCanvasGroup *) item->canvas_item,
498 gnome_canvas_line_get_type(), "points", &points,
499 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
500 "fill-color-rgba", item->brush.color_rgba,
501 "width-units", item->widths[j], NULL);
505 if (item->type == ITEM_TEXT) {
506 font_desc = pango_font_description_from_string(item->font_name);
507 pango_font_description_set_absolute_size(font_desc,
508 item->font_size*ui.zoom*PANGO_SCALE);
509 item->canvas_item = gnome_canvas_item_new(group,
510 gnome_canvas_text_get_type(),
511 "x", item->bbox.left, "y", item->bbox.top, "anchor", GTK_ANCHOR_NW,
512 "font-desc", font_desc, "fill-color-rgba", item->brush.color_rgba,
513 "text", item->text, NULL);
514 update_item_bbox(item);
518 void make_canvas_items(void)
523 GList *pagelist, *layerlist, *itemlist;
525 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next) {
526 pg = (struct Page *)pagelist->data;
527 if (pg->group == NULL) {
528 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
529 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
530 make_page_clipbox(pg);
532 if (pg->bg->canvas_item == NULL) update_canvas_bg(pg);
533 for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
534 l = (struct Layer *)layerlist->data;
535 if (l->group == NULL)
536 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
537 pg->group, gnome_canvas_group_get_type(), NULL);
538 for (itemlist = l->items; itemlist!=NULL; itemlist = itemlist->next) {
539 item = (struct Item *)itemlist->data;
540 if (item->canvas_item == NULL)
541 make_canvas_item_one(l->group, item);
547 void update_canvas_bg(struct Page *pg)
549 GnomeCanvasGroup *group;
550 GnomeCanvasPoints *seg;
551 GdkPixbuf *scaled_pix;
555 gboolean is_well_scaled;
557 if (pg->bg->canvas_item != NULL)
558 gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
559 pg->bg->canvas_item = NULL;
561 if (pg->bg->type == BG_SOLID)
563 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
564 gnome_canvas_group_get_type(), NULL);
565 group = GNOME_CANVAS_GROUP(pg->bg->canvas_item);
566 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
567 gnome_canvas_item_new(group, gnome_canvas_rect_get_type(),
568 "x1", 0., "x2", pg->width, "y1", 0., "y2", pg->height,
569 "fill-color-rgba", pg->bg->color_rgba, NULL);
570 if (pg->bg->ruling == RULING_NONE) return;
571 seg = gnome_canvas_points_new(2);
573 if (pg->bg->ruling == RULING_GRAPH) {
574 pt[1] = 0; pt[3] = pg->height;
575 for (x=RULING_GRAPHSPACING; x<pg->width-1; x+=RULING_GRAPHSPACING) {
577 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
578 "points", seg, "fill-color-rgba", RULING_COLOR,
579 "width-units", RULING_THICKNESS, NULL);
581 pt[0] = 0; pt[2] = pg->width;
582 for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
584 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
585 "points", seg, "fill-color-rgba", RULING_COLOR,
586 "width-units", RULING_THICKNESS, NULL);
588 gnome_canvas_points_free(seg);
591 pt[0] = 0; pt[2] = pg->width;
592 for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
594 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
595 "points", seg, "fill-color-rgba", RULING_COLOR,
596 "width-units", RULING_THICKNESS, NULL);
598 if (pg->bg->ruling == RULING_LINED) {
599 pt[0] = pt[2] = RULING_LEFTMARGIN;
600 pt[1] = 0; pt[3] = pg->height;
601 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
602 "points", seg, "fill-color-rgba", RULING_MARGIN_COLOR,
603 "width-units", RULING_THICKNESS, NULL);
605 gnome_canvas_points_free(seg);
609 if (pg->bg->type == BG_PIXMAP)
611 pg->bg->pixbuf_scale = 0;
612 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
613 gnome_canvas_pixbuf_get_type(),
614 "pixbuf", pg->bg->pixbuf,
615 "width", pg->width, "height", pg->height,
616 "width-set", TRUE, "height-set", TRUE,
618 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
621 if (pg->bg->type == BG_PDF)
623 if (pg->bg->pixbuf == NULL) return;
624 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
625 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
627 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
628 gnome_canvas_pixbuf_get_type(),
629 "pixbuf", pg->bg->pixbuf,
630 "width-in-pixels", TRUE, "height-in-pixels", TRUE,
633 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
634 gnome_canvas_pixbuf_get_type(),
635 "pixbuf", pg->bg->pixbuf,
636 "width", pg->width, "height", pg->height,
637 "width-set", TRUE, "height-set", TRUE,
639 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
643 gboolean is_visible(struct Page *pg)
645 GtkAdjustment *v_adj;
648 if (!ui.view_continuous) return (pg == ui.cur_page);
649 v_adj = gtk_layout_get_vadjustment(GTK_LAYOUT(canvas));
650 ytop = v_adj->value/ui.zoom;
651 ybot = (v_adj->value + v_adj->page_size) / ui.zoom;
652 return (MAX(ytop, pg->voffset) < MIN(ybot, pg->voffset+pg->height));
655 void rescale_bg_pixmaps(void)
660 gboolean is_well_scaled;
661 gdouble zoom_to_request;
663 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
664 pg = (struct Page *)pglist->data;
665 // in progressive mode we scale only visible pages
666 if (ui.progressive_bg && !is_visible(pg)) continue;
668 if (pg->bg->type == BG_PIXMAP && pg->bg->canvas_item!=NULL) {
669 g_object_get(G_OBJECT(pg->bg->canvas_item), "pixbuf", &pix, NULL);
670 if (pix!=pg->bg->pixbuf)
671 gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pg->bg->pixbuf, NULL);
672 pg->bg->pixbuf_scale = 0;
674 if (pg->bg->type == BG_PDF) {
675 // make pixmap scale to correct size if current one is wrong
676 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
677 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
678 if (pg->bg->canvas_item != NULL && !is_well_scaled) {
679 g_object_get(pg->bg->canvas_item, "width-in-pixels", &is_well_scaled, NULL);
681 gnome_canvas_item_set(pg->bg->canvas_item,
682 "width", pg->width, "height", pg->height,
683 "width-in-pixels", FALSE, "height-in-pixels", FALSE,
684 "width-set", TRUE, "height-set", TRUE,
687 // request an asynchronous update to a better pixmap if needed
688 zoom_to_request = MIN(ui.zoom, MAX_SAFE_RENDER_DPI/72.0);
689 if (pg->bg->pixbuf_scale == zoom_to_request) continue;
690 if (add_bgpdf_request(pg->bg->file_page_seq, zoom_to_request))
691 pg->bg->pixbuf_scale = zoom_to_request;
696 gboolean have_intersect(struct BBox *a, struct BBox *b)
698 return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
699 (MAX(a->left, b->left) <= MIN(a->right, b->right));
702 /* In libgnomecanvas 2.10.0, the lower/raise functions fail to update
703 correctly the end of the group's item list. We try to work around this.
704 DON'T USE gnome_canvas_item_raise/lower directly !! */
706 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
710 i1 = g_list_index(g->item_list, item);
711 if (i1 == -1) return;
713 if (after == NULL) i2 = -1;
714 else i2 = g_list_index(g->item_list, after);
716 if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
717 if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
719 // BUGFIX for libgnomecanvas
720 g->item_list_end = g_list_last(g->item_list);
723 void rgb_to_gdkcolor(guint rgba, GdkColor *color)
726 color->red = ((rgba>>24)&0xff)*0x101;
727 color->green = ((rgba>>16)&0xff)*0x101;
728 color->blue = ((rgba>>8)&0xff)*0x101;
731 guint32 gdkcolor_to_rgba(GdkColor gdkcolor, guint16 alpha)
733 guint32 rgba = ((gdkcolor.red & 0xff00) << 16) |
734 ((gdkcolor.green & 0xff00) << 8) |
735 ((gdkcolor.blue & 0xff00) ) |
736 ((alpha & 0xff00) >> 8);
741 // some interface functions
743 void update_thickness_buttons(void)
745 if (ui.selection!=NULL || ui.toolno[ui.cur_mapping] >= NUM_STROKE_TOOLS) {
746 gtk_toggle_tool_button_set_active(
747 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
749 switch (ui.cur_brush->thickness_no) {
751 gtk_toggle_tool_button_set_active(
752 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
754 case THICKNESS_MEDIUM:
755 gtk_toggle_tool_button_set_active(
756 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
758 case THICKNESS_THICK:
759 gtk_toggle_tool_button_set_active(
760 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
763 gtk_toggle_tool_button_set_active(
764 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
768 void update_color_buttons(void)
771 GtkColorButton *colorbutton;
773 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
774 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
775 gtk_toggle_tool_button_set_active(
776 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
778 switch (ui.cur_brush->color_no) {
780 gtk_toggle_tool_button_set_active(
781 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
784 gtk_toggle_tool_button_set_active(
785 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
788 gtk_toggle_tool_button_set_active(
789 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
792 gtk_toggle_tool_button_set_active(
793 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
796 gtk_toggle_tool_button_set_active(
797 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
799 case COLOR_LIGHTBLUE:
800 gtk_toggle_tool_button_set_active(
801 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
803 case COLOR_LIGHTGREEN:
804 gtk_toggle_tool_button_set_active(
805 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
808 gtk_toggle_tool_button_set_active(
809 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
812 gtk_toggle_tool_button_set_active(
813 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
816 gtk_toggle_tool_button_set_active(
817 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
820 gtk_toggle_tool_button_set_active(
821 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
824 gtk_toggle_tool_button_set_active(
825 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
828 colorbutton = GTK_COLOR_BUTTON(GET_COMPONENT("buttonColorChooser"));
829 if ((ui.toolno[ui.cur_mapping] != TOOL_PEN &&
830 ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER &&
831 ui.toolno[ui.cur_mapping] != TOOL_TEXT))
832 gdkcolor.red = gdkcolor.blue = gdkcolor.green = 0;
833 else rgb_to_gdkcolor(ui.cur_brush->color_rgba, &gdkcolor);
834 gtk_color_button_set_color(colorbutton, &gdkcolor);
835 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
836 gtk_color_button_set_alpha(colorbutton,
837 (ui.cur_brush->color_rgba&0xff)*0x101);
838 gtk_color_button_set_use_alpha(colorbutton, TRUE);
840 gtk_color_button_set_alpha(colorbutton, 0xffff);
841 gtk_color_button_set_use_alpha(colorbutton, FALSE);
845 void update_tool_buttons(void)
847 switch(ui.toolno[ui.cur_mapping]) {
849 gtk_toggle_tool_button_set_active(
850 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
853 gtk_toggle_tool_button_set_active(
854 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
856 case TOOL_HIGHLIGHTER:
857 gtk_toggle_tool_button_set_active(
858 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
861 gtk_toggle_tool_button_set_active(
862 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
864 case TOOL_SELECTREGION:
865 gtk_toggle_tool_button_set_active(
866 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
868 case TOOL_SELECTRECT:
869 gtk_toggle_tool_button_set_active(
870 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
873 gtk_toggle_tool_button_set_active(
874 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
877 gtk_toggle_tool_button_set_active(
878 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
882 gtk_toggle_tool_button_set_active(
883 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
884 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
885 gtk_toggle_tool_button_set_active(
886 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
887 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
889 update_thickness_buttons();
890 update_color_buttons();
893 void update_tool_menu(void)
895 switch(ui.toolno[0]) {
897 gtk_check_menu_item_set_active(
898 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
901 gtk_check_menu_item_set_active(
902 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
904 case TOOL_HIGHLIGHTER:
905 gtk_check_menu_item_set_active(
906 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
909 gtk_check_menu_item_set_active(
910 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
912 case TOOL_SELECTREGION:
913 gtk_check_menu_item_set_active(
914 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
916 case TOOL_SELECTRECT:
917 gtk_check_menu_item_set_active(
918 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
921 gtk_check_menu_item_set_active(
922 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
925 gtk_check_menu_item_set_active(
926 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
930 gtk_check_menu_item_set_active(
931 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
932 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
933 gtk_check_menu_item_set_active(
934 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
935 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
938 void update_ruler_indicator(void)
940 gtk_toggle_tool_button_set_active(
941 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
942 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
943 gtk_toggle_tool_button_set_active(
944 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
945 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
946 gtk_check_menu_item_set_active(
947 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
948 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
949 gtk_check_menu_item_set_active(
950 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
951 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
954 void update_color_menu(void)
956 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
957 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
958 gtk_check_menu_item_set_active(
959 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
961 switch (ui.cur_brush->color_no) {
963 gtk_check_menu_item_set_active(
964 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
967 gtk_check_menu_item_set_active(
968 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
971 gtk_check_menu_item_set_active(
972 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
975 gtk_check_menu_item_set_active(
976 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
979 gtk_check_menu_item_set_active(
980 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
982 case COLOR_LIGHTBLUE:
983 gtk_check_menu_item_set_active(
984 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
986 case COLOR_LIGHTGREEN:
987 gtk_check_menu_item_set_active(
988 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
991 gtk_check_menu_item_set_active(
992 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
995 gtk_check_menu_item_set_active(
996 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
999 gtk_check_menu_item_set_active(
1000 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
1003 gtk_check_menu_item_set_active(
1004 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
1007 gtk_check_menu_item_set_active(
1008 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
1012 void update_pen_props_menu(void)
1014 switch(ui.brushes[0][TOOL_PEN].thickness_no) {
1015 case THICKNESS_VERYFINE:
1016 gtk_check_menu_item_set_active(
1017 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
1019 case THICKNESS_FINE:
1020 gtk_check_menu_item_set_active(
1021 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
1023 case THICKNESS_MEDIUM:
1024 gtk_check_menu_item_set_active(
1025 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
1027 case THICKNESS_THICK:
1028 gtk_check_menu_item_set_active(
1029 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
1031 case THICKNESS_VERYTHICK:
1032 gtk_check_menu_item_set_active(
1033 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
1038 void update_eraser_props_menu(void)
1040 switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
1041 case THICKNESS_FINE:
1042 gtk_check_menu_item_set_active(
1043 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
1045 case THICKNESS_MEDIUM:
1046 gtk_check_menu_item_set_active(
1047 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
1049 case THICKNESS_THICK:
1050 gtk_check_menu_item_set_active(
1051 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
1055 gtk_check_menu_item_set_active(
1056 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
1057 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
1058 gtk_check_menu_item_set_active(
1059 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
1060 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
1061 gtk_check_menu_item_set_active(
1062 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
1063 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
1066 void update_highlighter_props_menu(void)
1068 switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
1069 case THICKNESS_FINE:
1070 gtk_check_menu_item_set_active(
1071 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
1073 case THICKNESS_MEDIUM:
1074 gtk_check_menu_item_set_active(
1075 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
1077 case THICKNESS_THICK:
1078 gtk_check_menu_item_set_active(
1079 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
1084 void update_mappings_menu_linkings(void)
1086 switch (ui.linked_brush[1]) {
1088 gtk_check_menu_item_set_active(
1089 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
1092 gtk_check_menu_item_set_active(
1093 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
1096 gtk_check_menu_item_set_active(
1097 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
1100 switch (ui.linked_brush[2]) {
1102 gtk_check_menu_item_set_active(
1103 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
1106 gtk_check_menu_item_set_active(
1107 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
1110 gtk_check_menu_item_set_active(
1111 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
1116 void update_mappings_menu(void)
1118 gtk_widget_set_sensitive(GET_COMPONENT("optionsButtonMappings"), ui.use_xinput);
1119 gtk_widget_set_sensitive(GET_COMPONENT("optionsPressureSensitive"), ui.use_xinput);
1120 gtk_check_menu_item_set_active(
1121 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
1122 gtk_check_menu_item_set_active(
1123 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsPressureSensitive")), ui.pressure_sensitivity);
1125 switch(ui.toolno[1]) {
1127 gtk_check_menu_item_set_active(
1128 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
1131 gtk_check_menu_item_set_active(
1132 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
1134 case TOOL_HIGHLIGHTER:
1135 gtk_check_menu_item_set_active(
1136 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
1139 gtk_check_menu_item_set_active(
1140 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
1142 case TOOL_SELECTREGION:
1143 gtk_check_menu_item_set_active(
1144 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
1146 case TOOL_SELECTRECT:
1147 gtk_check_menu_item_set_active(
1148 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
1150 case TOOL_VERTSPACE:
1151 gtk_check_menu_item_set_active(
1152 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
1155 switch(ui.toolno[2]) {
1157 gtk_check_menu_item_set_active(
1158 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
1161 gtk_check_menu_item_set_active(
1162 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
1164 case TOOL_HIGHLIGHTER:
1165 gtk_check_menu_item_set_active(
1166 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
1169 gtk_check_menu_item_set_active(
1170 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
1172 case TOOL_SELECTREGION:
1173 gtk_check_menu_item_set_active(
1174 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
1176 case TOOL_SELECTRECT:
1177 gtk_check_menu_item_set_active(
1178 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
1180 case TOOL_VERTSPACE:
1181 gtk_check_menu_item_set_active(
1182 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1185 update_mappings_menu_linkings();
1188 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1191 struct Layer *layer;
1196 /* re-show all the layers of the old page */
1197 if (ui.cur_page != NULL)
1198 for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1199 layer = (struct Layer *)list->data;
1200 if (layer->group!=NULL)
1201 gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1204 ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1205 ui.layerno = ui.cur_page->nlayers-1;
1206 ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1207 update_page_stuff();
1208 if (ui.progressive_bg) rescale_bg_pixmaps();
1210 if (rescroll) { // scroll and force a refresh
1211 /* -- this seems to cause some display bugs ??
1212 gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1213 ui.cur_page->voffset*ui.zoom); */
1214 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1215 cy = ui.cur_page->voffset*ui.zoom;
1216 gnome_canvas_scroll_to(canvas, cx, cy);
1219 gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1220 else if (!ui.view_continuous)
1221 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1225 void update_page_stuff(void)
1228 GtkComboBox *layerbox;
1231 GtkSpinButton *spin;
1233 double vertpos, maxwidth;
1235 // move the page groups to their rightful locations or hide them
1236 if (ui.view_continuous) {
1239 for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1240 pg = (struct Page *)pglist->data;
1241 if (pg->group!=NULL) {
1242 pg->hoffset = 0.; pg->voffset = vertpos;
1243 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1244 "x", pg->hoffset, "y", pg->voffset, NULL);
1245 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1247 vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1248 if (pg->width > maxwidth) maxwidth = pg->width;
1250 vertpos -= VIEW_CONTINUOUS_SKIP;
1251 gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1253 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1254 pg = (struct Page *)pglist->data;
1255 if (pg == ui.cur_page && pg->group!=NULL) {
1256 pg->hoffset = 0.; pg->voffset = 0.;
1257 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1258 "x", pg->hoffset, "y", pg->voffset, NULL);
1259 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1261 if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1264 gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1267 // update the page / layer info at bottom of screen
1269 spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1270 ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1271 gtk_spin_button_set_range(spin, 1, journal.npages+1);
1272 /* npages+1 will be used to create a new page at end */
1273 gtk_spin_button_set_value(spin, ui.pageno+1);
1274 g_snprintf(tmp, 10, _(" of %d"), journal.npages);
1275 gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1277 layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1278 if (ui.layerbox_length == 0) {
1279 gtk_combo_box_prepend_text(layerbox, _("Background"));
1280 ui.layerbox_length++;
1282 while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1283 gtk_combo_box_remove_text(layerbox, 0);
1284 ui.layerbox_length--;
1286 while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1287 g_snprintf(tmp, 10, _("Layer %d"), ui.layerbox_length++);
1288 gtk_combo_box_prepend_text(layerbox, tmp);
1290 gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1291 ui.in_update_page_stuff = FALSE;
1293 gtk_container_forall(GTK_CONTAINER(layerbox), unset_flags, (gpointer)GTK_CAN_FOCUS);
1295 // update the paper-style menu radio buttons
1297 if (ui.view_continuous)
1298 gtk_check_menu_item_set_active(
1299 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1301 gtk_check_menu_item_set_active(
1302 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1304 if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1305 switch (ui.cur_page->bg->color_no) {
1307 gtk_check_menu_item_set_active(
1308 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1311 gtk_check_menu_item_set_active(
1312 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1315 gtk_check_menu_item_set_active(
1316 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1319 gtk_check_menu_item_set_active(
1320 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1323 gtk_check_menu_item_set_active(
1324 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1327 gtk_check_menu_item_set_active(
1328 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1331 gtk_check_menu_item_set_active(
1332 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1335 switch (ui.cur_page->bg->ruling) {
1337 gtk_check_menu_item_set_active(
1338 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1341 gtk_check_menu_item_set_active(
1342 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1345 gtk_check_menu_item_set_active(
1346 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1349 gtk_check_menu_item_set_active(
1350 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1354 gtk_check_menu_item_set_active(
1355 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1356 gtk_check_menu_item_set_active(
1357 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1360 // enable/disable the page/layer menu items and toolbar buttons
1362 gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"),
1363 ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1364 gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1365 ui.cur_page->bg->type == BG_SOLID);
1367 gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1368 gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1369 gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1370 gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1371 gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1372 gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1373 gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1374 gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1376 gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1377 gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1379 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1380 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1383 void update_toolbar_and_menu(void)
1385 update_tool_buttons(); // takes care of other toolbar buttons as well
1387 update_color_menu();
1388 update_pen_props_menu();
1389 update_eraser_props_menu();
1390 update_highlighter_props_menu();
1391 update_mappings_menu();
1393 gtk_toggle_tool_button_set_active(
1394 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1395 gtk_check_menu_item_set_active(
1396 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1399 void update_file_name(char *filename)
1402 if (ui.filename != NULL) g_free(ui.filename);
1403 ui.filename = filename;
1404 if (filename == NULL) {
1405 gtk_window_set_title(GTK_WINDOW (winMain), _("Xournal"));
1408 p = g_utf8_strrchr(filename, -1, '/');
1409 if (p == NULL) p = filename;
1410 else p = g_utf8_next_char(p);
1411 g_snprintf(tmp, 100, _("Xournal - %s"), p);
1412 gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1413 new_mru_entry(filename);
1415 if (filename[0]=='/') {
1416 if (ui.default_path!=NULL) g_free(ui.default_path);
1417 ui.default_path = g_path_get_dirname(filename);
1421 void update_undo_redo_enabled(void)
1423 gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1424 gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1425 gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1426 gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1429 void update_copy_paste_enabled(void)
1431 gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1432 gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1433 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_item_type!=ITEM_TEXT);
1434 gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1435 gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1436 gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1437 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_item_type!=ITEM_TEXT);
1440 void update_mapping_linkings(int toolno)
1444 for (i = 1; i<=NUM_BUTTONS; i++) {
1445 if (ui.linked_brush[i] == BRUSH_LINKED) {
1446 if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1447 g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1449 if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1450 ui.linked_brush[i] = BRUSH_STATIC;
1451 if (i==1 || i==2) update_mappings_menu_linkings();
1456 void set_cur_color(int color_no, guint color_rgba)
1458 int which_mapping, tool;
1460 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) tool = TOOL_HIGHLIGHTER;
1461 else tool = TOOL_PEN;
1462 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1463 which_mapping = ui.cur_mapping;
1464 else which_mapping = 0;
1466 ui.brushes[which_mapping][tool].color_no = color_no;
1467 if (tool == TOOL_HIGHLIGHTER && (color_rgba & 0xff) == 0xff)
1468 ui.brushes[which_mapping][tool].color_rgba = color_rgba & ui.hiliter_alpha_mask;
1470 ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1471 update_mapping_linkings(tool);
1474 void recolor_temp_text(int color_no, guint color_rgba)
1478 if (ui.cur_item_type!=ITEM_TEXT) return;
1479 if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1481 undo->type = ITEM_TEXT_ATTRIB;
1482 undo->item = ui.cur_item;
1483 undo->str = g_strdup(ui.cur_item->font_name);
1484 undo->val_x = ui.cur_item->font_size;
1485 undo->brush = (struct Brush *)g_memdup(&(ui.cur_item->brush), sizeof(struct Brush));
1487 ui.cur_item->brush.color_no = color_no;
1488 ui.cur_item->brush.color_rgba = color_rgba;
1489 rgb_to_gdkcolor(color_rgba, &gdkcolor);
1490 gtk_widget_modify_text(ui.cur_item->widget, GTK_STATE_NORMAL, &gdkcolor);
1491 gtk_widget_grab_focus(ui.cur_item->widget);
1494 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1496 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1497 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1500 else if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_TOOL_BUTTON) {
1501 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1505 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1507 if (ui.cur_item_type == ITEM_TEXT)
1508 recolor_temp_text(color_no, color_rgba);
1510 if (ui.selection != NULL) {
1511 recolor_selection(color_no, color_rgba);
1512 update_color_buttons();
1513 update_color_menu();
1516 if (ui.toolno[ui.cur_mapping] != TOOL_PEN && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER
1517 && ui.toolno[ui.cur_mapping] != TOOL_TEXT) {
1518 if (ui.selection != NULL) return;
1521 ui.toolno[ui.cur_mapping] = TOOL_PEN;
1522 ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1523 update_tool_buttons();
1527 set_cur_color(color_no, color_rgba);
1528 update_color_buttons();
1529 update_color_menu();
1533 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1537 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1538 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1541 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1545 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1547 if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1548 rethicken_selection(val);
1549 update_thickness_buttons();
1552 if (tool >= NUM_STROKE_TOOLS) {
1553 update_thickness_buttons(); // undo illegal button selection
1557 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1558 which_mapping = ui.cur_mapping;
1559 else which_mapping = 0;
1560 if (ui.brushes[which_mapping][tool].thickness_no == val) return;
1562 ui.brushes[which_mapping][tool].thickness_no = val;
1563 ui.brushes[which_mapping][tool].thickness = predef_thickness[tool][val];
1564 update_mapping_linkings(tool);
1566 update_thickness_buttons();
1567 if (tool == TOOL_PEN) update_pen_props_menu();
1568 if (tool == TOOL_ERASER) update_eraser_props_menu();
1569 if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1573 void process_papercolor_activate(GtkMenuItem *menuitem, int color, guint rgba)
1579 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1580 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1584 if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages || color == COLOR_OTHER)
1585 gtk_check_menu_item_set_active(
1586 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1590 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1591 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1592 if (pg->bg->type == BG_SOLID && pg->bg->color_rgba != rgba) {
1594 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1595 undo->multiop |= MULTIOP_CONT_REDO;
1597 undo->type = ITEM_NEW_BG_ONE;
1599 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1600 undo->bg->canvas_item = NULL;
1602 pg->bg->color_no = color;
1603 pg->bg->color_rgba = rgba;
1604 update_canvas_bg(pg);
1606 if (!ui.bg_apply_all_pages) break;
1608 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1611 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1615 gboolean hasdone, must_upd;
1617 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1620 if (ui.bg_apply_all_pages)
1621 gtk_check_menu_item_set_active(
1622 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1627 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1628 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1629 if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1631 undo->type = ITEM_NEW_BG_ONE;
1632 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1633 undo->multiop |= MULTIOP_CONT_REDO;
1636 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1637 undo->bg->canvas_item = NULL;
1639 if (pg->bg->type != BG_SOLID) {
1640 pg->bg->type = BG_SOLID;
1641 pg->bg->color_no = COLOR_WHITE;
1642 pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1643 pg->bg->filename = NULL;
1644 pg->bg->pixbuf = NULL;
1647 pg->bg->ruling = style;
1648 update_canvas_bg(pg);
1650 if (!ui.bg_apply_all_pages) break;
1652 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1653 if (must_upd) update_page_stuff();
1656 gboolean ok_to_close(void)
1659 GtkResponseType response;
1661 if (ui.saved) return TRUE;
1662 dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1663 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Save changes to '%s'?"),
1664 (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1665 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1666 response = gtk_dialog_run(GTK_DIALOG (dialog));
1667 gtk_widget_destroy(dialog);
1668 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1669 return FALSE; // aborted
1670 if (response == GTK_RESPONSE_YES) {
1671 on_fileSave_activate(NULL, NULL);
1672 if (!ui.saved) return FALSE; // if save failed, then we abort
1677 // send the focus back to the appropriate widget
1679 void reset_focus(void)
1681 if (ui.cur_item_type == ITEM_TEXT)
1682 gtk_widget_grab_focus(ui.cur_item->widget);
1684 gtk_widget_grab_focus(GTK_WIDGET(canvas));
1687 // selection / clipboard stuff
1689 void reset_selection(void)
1691 if (ui.selection == NULL) return;
1692 if (ui.selection->canvas_item != NULL)
1693 gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1694 g_list_free(ui.selection->items);
1695 g_free(ui.selection);
1696 ui.selection = NULL;
1697 update_copy_paste_enabled();
1698 update_color_menu();
1699 update_thickness_buttons();
1700 update_color_buttons();
1701 update_font_button();
1705 void move_journal_items_by(GList *itemlist, double dx, double dy,
1706 struct Layer *l1, struct Layer *l2, GList *depths)
1709 GnomeCanvasItem *refitem;
1714 while (itemlist!=NULL) {
1715 item = (struct Item *)itemlist->data;
1716 if (item->type == ITEM_STROKE)
1717 for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1718 { pt[0] += dx; pt[1] += dy; }
1719 if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1720 item->bbox.left += dx;
1721 item->bbox.right += dx;
1722 item->bbox.top += dy;
1723 item->bbox.bottom += dy;
1726 // find out where to insert
1727 if (depths != NULL) {
1728 if (depths->data == NULL) link = l2->items;
1730 link = g_list_find(l2->items, depths->data);
1731 if (link != NULL) link = link->next;
1734 l2->items = g_list_insert_before(l2->items, link, item);
1736 l1->items = g_list_remove(l1->items, item);
1739 if (depths != NULL) { // also raise/lower the canvas items
1740 if (item->canvas_item!=NULL) {
1741 if (depths->data == NULL) link = NULL;
1742 else link = g_list_find(l2->items, depths->data);
1743 if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1744 else refitem = NULL;
1745 lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1747 depths = depths->next;
1749 itemlist = itemlist->next;
1753 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1754 double offset_x, double offset_y)
1758 double mean_scaling, temp;
1760 GnomeCanvasGroup *group;
1763 /* geometric mean of x and y scalings = rescaling for stroke widths
1764 and for text font sizes */
1765 mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1767 for (list = itemlist; list != NULL; list = list->next) {
1768 item = (struct Item *)list->data;
1769 if (item->type == ITEM_STROKE) {
1770 item->brush.thickness = item->brush.thickness * mean_scaling;
1771 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1772 pt[0] = pt[0]*scaling_x + offset_x;
1773 pt[1] = pt[1]*scaling_y + offset_y;
1775 if (item->brush.variable_width)
1776 for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1777 *wid = *wid * mean_scaling;
1779 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1780 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1781 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1782 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1783 if (item->bbox.left > item->bbox.right) {
1784 temp = item->bbox.left;
1785 item->bbox.left = item->bbox.right;
1786 item->bbox.right = temp;
1788 if (item->bbox.top > item->bbox.bottom) {
1789 temp = item->bbox.top;
1790 item->bbox.top = item->bbox.bottom;
1791 item->bbox.bottom = temp;
1794 if (item->type == ITEM_TEXT) {
1795 /* must scale about NW corner -- all other points of the text box
1796 are font- and zoom-dependent, so scaling about center of text box
1797 couldn't be undone properly. FIXME? */
1798 item->font_size *= mean_scaling;
1799 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1800 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1803 if (item->canvas_item!=NULL) {
1804 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1805 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1806 make_canvas_item_one(group, item);
1811 // Switch between button mappings
1813 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1814 click event is being processed ... or if ui.button_switch_mapping is
1815 enabled and mappings are switched (but even then, canvas should have
1816 a pointer grab from the initial click that switched the mapping) */
1818 void switch_mapping(int m)
1820 if (ui.cur_mapping == m) return;
1823 if (ui.toolno[m] < NUM_STROKE_TOOLS)
1824 ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1825 if (ui.toolno[m] == TOOL_TEXT)
1826 ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1827 if (m==0) ui.which_unswitch_button = 0;
1829 update_tool_buttons();
1830 update_color_menu();
1834 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1836 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1837 if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1838 if (ui.toolno[m] == tool) return;
1842 ui.toolno[m] = tool;
1843 if (ui.linked_brush[m] == BRUSH_COPIED) {
1844 ui.linked_brush[m] = BRUSH_STATIC;
1845 update_mappings_menu_linkings();
1849 // update the ordering of components in the main vbox
1851 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1852 {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1854 void update_vbox_order(int *order)
1858 GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1859 gboolean present[VBOX_MAIN_NITEMS];
1861 for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1863 for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1864 if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1865 present[order[i]] = TRUE;
1866 child = GET_COMPONENT(vbox_component_names[order[i]]);
1867 gtk_box_reorder_child(vboxMain, child, j++);
1868 gtk_widget_show(child);
1870 for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1871 if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1874 gchar *make_cur_font_name(void)
1879 if (ui.cur_item_type == ITEM_TEXT)
1880 str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1881 else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1882 ui.selection->items->next==NULL &&
1883 (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1884 str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1886 str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1890 void update_font_button(void)
1894 str = make_cur_font_name();
1895 gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1899 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1901 return GTK_WIDGET_SENSITIVE(widget);
1904 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1906 if (ui.cur_item_type == ITEM_TEXT) {
1907 g_signal_stop_emission_by_name(widget, "can-activate-accel");
1910 return GTK_WIDGET_SENSITIVE(widget);
1913 void allow_all_accels(void)
1915 g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1916 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1917 g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1918 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1919 g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1920 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1921 g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1922 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1923 g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1924 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1925 g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1926 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1927 g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1928 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1929 g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1930 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1931 g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1932 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1933 g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1934 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1935 g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1936 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1937 g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1938 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1939 g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1940 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1941 g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1942 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1943 g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1944 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1945 g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1946 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1947 g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1948 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1949 g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1950 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1951 g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1952 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1953 g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1954 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1955 g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1956 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1957 g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1958 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1959 g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1960 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1961 g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1962 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1963 g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1964 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1965 /* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1966 "can-activate-accel", G_CALLBACK(can_accel), NULL); */
1967 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1968 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1969 g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1970 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1971 g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1972 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1973 g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1974 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1975 g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1976 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1977 g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
1978 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1981 void add_scroll_bindings(void)
1983 GtkBindingSet *binding_set;
1985 binding_set = gtk_binding_set_by_class(
1986 G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
1987 gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
1988 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1989 G_TYPE_BOOLEAN, FALSE);
1990 gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
1991 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1992 G_TYPE_BOOLEAN, FALSE);
1993 gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
1994 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1995 G_TYPE_BOOLEAN, FALSE);
1996 gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
1997 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1998 G_TYPE_BOOLEAN, FALSE);
1999 gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
2000 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2001 G_TYPE_BOOLEAN, TRUE);
2002 gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
2003 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2004 G_TYPE_BOOLEAN, TRUE);
2005 gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
2006 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2007 G_TYPE_BOOLEAN, TRUE);
2008 gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
2009 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2010 G_TYPE_BOOLEAN, TRUE);
2013 gboolean is_event_within_textview(GdkEventButton *event)
2017 if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2018 get_pointer_coords((GdkEvent *)event, pt);
2019 if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2020 if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2024 void hide_unimplemented(void)
2026 gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2027 gtk_widget_hide(GET_COMPONENT("journalFlatten"));
2028 gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2029 gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2030 gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2031 gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2032 gtk_widget_hide(GET_COMPONENT("helpIndex"));
2034 /* config file only works with glib 2.6 and beyond */
2035 if (glib_minor_version<6) {
2036 gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2037 gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2039 /* gtkprint only works with gtk+ 2.10 and beyond */
2040 if (gtk_check_version(2, 10, 0)) {
2041 gtk_widget_hide(GET_COMPONENT("filePrint"));
2045 // toggle fullscreen mode
2046 void do_fullscreen(gboolean active)
2049 ui.fullscreen = active;
2050 gtk_check_menu_item_set_active(
2051 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2052 gtk_toggle_tool_button_set_active(
2053 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2055 if (ui.fullscreen) gtk_window_fullscreen(GTK_WINDOW(winMain));
2056 else gtk_window_unfullscreen(GTK_WINDOW(winMain));
2058 update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2061 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2062 elements receive XInput events that they can't handle properly */
2064 // prevent interface items from getting bogus XInput events
2066 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2069 if (event->type == GDK_MOTION_NOTIFY &&
2070 event->motion.device != gdk_device_get_core_pointer())
2072 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2073 event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2074 event->button.device != gdk_device_get_core_pointer())
2079 /* Code to turn an extended input event into a core event and send it to
2080 a different GdkWindow -- e.g. could be used when a click in a text edit box
2081 gets sent to the canvas instead due to incorrect event translation.
2082 We now turn off xinput altogether while editing text under GTK+ 2.17, so
2083 this isn't needed any more... but could become useful again someday!
2087 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2093 if (user_data) window = (GdkWindow *)user_data;
2094 else window = widget->window;
2096 if (event->type == GDK_MOTION_NOTIFY &&
2097 event->motion.device != gdk_device_get_core_pointer()) {
2098 // printf("fixing motion\n");
2099 gdk_window_get_pointer(window, &ix, &iy, NULL);
2100 event->motion.x = ix; event->motion.y = iy;
2101 event->motion.device = gdk_device_get_core_pointer();
2102 g_object_unref(event->motion.window);
2103 event->motion.window = g_object_ref(window);
2104 gtk_widget_event(widget, event);
2107 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2108 event->button.device != gdk_device_get_core_pointer()) {
2109 // printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2110 gdk_window_get_pointer(window, &ix, &iy, NULL);
2111 event->button.x = ix; event->button.y = iy;
2112 event->button.device = gdk_device_get_core_pointer();
2113 g_object_unref(event->button.window);
2114 event->button.window = g_object_ref(window);
2115 // printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2116 gtk_widget_event(widget, event);
2124 /* When enter is pressed into page spinbox, send focus back to canvas. */
2126 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2132 /* recursively unset widget flags */
2134 void unset_flags(GtkWidget *w, gpointer flag)
2136 GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2137 if(GTK_IS_CONTAINER(w))
2138 gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2141 /* reset focus when a key or button press event reaches someone, or when the
2142 page-number spin button should relinquish control... */
2144 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2146 if (w == GET_COMPONENT("hbox1")) {
2147 /* the event won't be processed since the hbox1 doesn't know what to do with it,
2148 so we might as well kill it and avoid confusing ourselves when it gets
2149 propagated further ... */
2152 if (w == GET_COMPONENT("spinPageNo")) {
2153 /* we let the spin button take care of itself, and don't steal its focus,
2154 unless the user presses Esc or Tab (in those cases we intervene) */
2155 if (ev->type != GDK_KEY_PRESS) return FALSE;
2156 if (ev->key.keyval == GDK_Escape)
2157 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2158 else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2159 return FALSE; // let the spin button process it
2162 // otherwise, we want to make sure the canvas or text item gets focus back...
2167 void install_focus_hooks(GtkWidget *w, gpointer data)
2169 if (w == NULL) return;
2170 g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2171 g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2172 if (GTK_IS_MENU_ITEM(w)) {
2173 g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2174 install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2176 if(GTK_IS_CONTAINER(w))
2177 gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);