8 #include <libgnomecanvas/libgnomecanvas.h>
9 #include <gdk/gdkkeysyms.h>
12 #include "xo-interface.h"
13 #include "xo-support.h"
14 #include "xo-callbacks.h"
18 #include "xo-shapes.h"
20 // some global constants
22 guint predef_colors_rgba[COLOR_MAX] =
23 { 0x000000ff, 0x3333ccff, 0xff0000ff, 0x008000ff,
24 0x808080ff, 0x00c0ffff, 0x00ff00ff, 0xff00ffff,
25 0xff8000ff, 0xffff00ff, 0xffffffff };
27 guint predef_bgcolors_rgba[COLOR_MAX] = // meaningless ones set to white
28 { 0xffffffff, 0xa0e8ffff, 0xffc0d4ff, 0x80ffc0ff,
29 0xffffffff, 0xa0e8ffff, 0x80ffc0ff, 0xffc0d4ff,
30 0xffc080ff, 0xffff80ff, 0xffffffff };
32 double predef_thickness[NUM_STROKE_TOOLS][THICKNESS_MAX] =
33 { { 0.42, 0.85, 1.41, 2.26, 5.67 }, // pen thicknesses = 0.15, 0.3, 0.5, 0.8, 2 mm
34 { 2.83, 2.83, 8.50, 19.84, 19.84 }, // eraser thicknesses = 1, 3, 7 mm
35 { 2.83, 2.83, 8.50, 19.84, 19.84 }, // highlighter thicknesses = 1, 3, 7 mm
38 // some manipulation functions
40 struct Page *new_page(struct Page *template)
42 struct Page *pg = (struct Page *) g_memdup(template, sizeof(struct Page));
43 struct Layer *l = g_new(struct Layer, 1);
47 pg->layers = g_list_append(NULL, l);
49 pg->bg = (struct Background *)g_memdup(template->bg, sizeof(struct Background));
50 pg->bg->canvas_item = NULL;
51 if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
52 gdk_pixbuf_ref(pg->bg->pixbuf);
53 refstring_ref(pg->bg->filename);
55 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
56 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
57 make_page_clipbox(pg);
59 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
60 pg->group, gnome_canvas_group_get_type(), NULL);
65 /* Create a page from a background.
66 Note: bg should be an UNREFERENCED background.
67 If needed, first duplicate it and increase the refcount of the pixbuf.
69 struct Page *new_page_with_bg(struct Background *bg, double width, double height)
71 struct Page *pg = g_new(struct Page, 1);
72 struct Layer *l = g_new(struct Layer, 1);
76 pg->layers = g_list_append(NULL, l);
79 pg->bg->canvas_item = NULL;
82 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
83 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
84 make_page_clipbox(pg);
86 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
87 pg->group, gnome_canvas_group_get_type(), NULL);
92 void realloc_cur_path(int n)
94 if (n <= ui.cur_path_storage_alloc) return;
95 ui.cur_path_storage_alloc = n+100;
96 ui.cur_path.coords = g_realloc(ui.cur_path.coords, 2*(n+100)*sizeof(double));
99 void realloc_cur_widths(int n)
101 if (n <= ui.cur_widths_storage_alloc) return;
102 ui.cur_widths_storage_alloc = n+100;
103 ui.cur_widths = g_realloc(ui.cur_widths, (n+100)*sizeof(double));
106 // undo utility functions
108 void prepare_new_undo(void)
111 // add a new UndoItem on the stack
112 u = (struct UndoItem *)g_malloc(sizeof(struct UndoItem));
120 void clear_redo_stack(void)
124 struct UndoErasureData *erasure;
127 /* Warning: the redo items might reference items from past redo entries,
128 which have been destroyed before them. Be careful! As a rule, it's
129 safe to destroy data which has been created at the current history step,
130 it's unsafe to refer to any data from previous history steps */
133 if (redo->type == ITEM_STROKE) {
134 gnome_canvas_points_free(redo->item->path);
135 if (redo->item->brush.variable_width) g_free(redo->item->widths);
137 /* the strokes are unmapped, so there are no associated canvas items */
139 else if (redo->type == ITEM_TEXT) {
140 g_free(redo->item->text);
141 g_free(redo->item->font_name);
144 else if (redo->type == ITEM_ERASURE || redo->type == ITEM_RECOGNIZER) {
145 for (list = redo->erasurelist; list!=NULL; list=list->next) {
146 erasure = (struct UndoErasureData *)list->data;
147 for (repl = erasure->replacement_items; repl!=NULL; repl=repl->next) {
148 it = (struct Item *)repl->data;
149 gnome_canvas_points_free(it->path);
150 if (it->brush.variable_width) g_free(it->widths);
153 g_list_free(erasure->replacement_items);
156 g_list_free(redo->erasurelist);
158 else if (redo->type == ITEM_NEW_BG_ONE || redo->type == ITEM_NEW_BG_RESIZE
159 || redo->type == ITEM_NEW_DEFAULT_BG) {
160 if (redo->bg->type == BG_PIXMAP || redo->bg->type == BG_PDF) {
161 if (redo->bg->pixbuf!=NULL) gdk_pixbuf_unref(redo->bg->pixbuf);
162 refstring_unref(redo->bg->filename);
166 else if (redo->type == ITEM_NEW_PAGE) {
167 redo->page->group = NULL;
168 delete_page(redo->page);
170 else if (redo->type == ITEM_MOVESEL || redo->type == ITEM_REPAINTSEL) {
171 g_list_free(redo->itemlist); g_list_free(redo->auxlist);
173 else if (redo->type == ITEM_RESIZESEL) {
174 g_list_free(redo->itemlist);
176 else if (redo->type == ITEM_PASTE) {
177 for (list = redo->itemlist; list!=NULL; list=list->next) {
178 it = (struct Item *)list->data;
179 if (it->type == ITEM_STROKE) {
180 gnome_canvas_points_free(it->path);
181 if (it->brush.variable_width) g_free(it->widths);
185 g_list_free(redo->itemlist);
187 else if (redo->type == ITEM_NEW_LAYER) {
190 else if (redo->type == ITEM_TEXT_EDIT || redo->type == ITEM_TEXT_ATTRIB) {
192 if (redo->type == ITEM_TEXT_ATTRIB) g_free(redo->brush);
199 update_undo_redo_enabled();
202 void clear_undo_stack(void)
206 struct UndoErasureData *erasure;
209 // for strokes, items are already in the journal, so we don't free them
210 // for erasures, we need to free the dead items
211 if (undo->type == ITEM_ERASURE || undo->type == ITEM_RECOGNIZER) {
212 for (list = undo->erasurelist; list!=NULL; list=list->next) {
213 erasure = (struct UndoErasureData *)list->data;
214 if (erasure->item->type == ITEM_STROKE) {
215 gnome_canvas_points_free(erasure->item->path);
216 if (erasure->item->brush.variable_width) g_free(erasure->item->widths);
218 if (erasure->item->type == ITEM_TEXT)
219 { g_free(erasure->item->text); g_free(erasure->item->font_name); }
220 g_free(erasure->item);
221 g_list_free(erasure->replacement_items);
224 g_list_free(undo->erasurelist);
226 else if (undo->type == ITEM_NEW_BG_ONE || undo->type == ITEM_NEW_BG_RESIZE
227 || undo->type == ITEM_NEW_DEFAULT_BG) {
228 if (undo->bg->type == BG_PIXMAP || undo->bg->type == BG_PDF) {
229 if (undo->bg->pixbuf!=NULL) gdk_pixbuf_unref(undo->bg->pixbuf);
230 refstring_unref(undo->bg->filename);
234 else if (undo->type == ITEM_MOVESEL || undo->type == ITEM_REPAINTSEL) {
235 g_list_free(undo->itemlist); g_list_free(undo->auxlist);
237 else if (undo->type == ITEM_RESIZESEL) {
238 g_list_free(undo->itemlist);
240 else if (undo->type == ITEM_PASTE) {
241 g_list_free(undo->itemlist);
243 else if (undo->type == ITEM_DELETE_LAYER) {
244 undo->layer->group = NULL;
245 delete_layer(undo->layer);
247 else if (undo->type == ITEM_DELETE_PAGE) {
248 undo->page->group = NULL;
249 delete_page(undo->page);
251 else if (undo->type == ITEM_TEXT_EDIT || undo->type == ITEM_TEXT_ATTRIB) {
253 if (undo->type == ITEM_TEXT_ATTRIB) g_free(undo->brush);
260 update_undo_redo_enabled();
263 // free data structures
265 void delete_journal(struct Journal *j)
267 while (j->pages!=NULL) {
268 delete_page((struct Page *)j->pages->data);
269 j->pages = g_list_delete_link(j->pages, j->pages);
273 void delete_page(struct Page *pg)
277 while (pg->layers!=NULL) {
278 l = (struct Layer *)pg->layers->data;
281 pg->layers = g_list_delete_link(pg->layers, pg->layers);
283 if (pg->group!=NULL) gtk_object_destroy(GTK_OBJECT(pg->group));
284 // this also destroys the background's canvas items
285 if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
286 if (pg->bg->pixbuf != NULL) gdk_pixbuf_unref(pg->bg->pixbuf);
287 if (pg->bg->filename != NULL) refstring_unref(pg->bg->filename);
293 void delete_layer(struct Layer *l)
297 while (l->items!=NULL) {
298 item = (struct Item *)l->items->data;
299 if (item->type == ITEM_STROKE && item->path != NULL)
300 gnome_canvas_points_free(item->path);
301 if (item->type == ITEM_TEXT) {
302 g_free(item->font_name); g_free(item->text);
304 // don't need to delete the canvas_item, as it's part of the group destroyed below
306 l->items = g_list_delete_link(l->items, l->items);
308 if (l->group!= NULL) gtk_object_destroy(GTK_OBJECT(l->group));
312 // referenced strings
314 struct Refstring *new_refstring(const char *s)
316 struct Refstring *rs = g_new(struct Refstring, 1);
318 if (s!=NULL) rs->s = g_strdup(s);
324 struct Refstring *refstring_ref(struct Refstring *rs)
330 void refstring_unref(struct Refstring *rs)
334 if (rs->s!=NULL) g_free(rs->s);
335 if (rs->aux!=NULL) g_free(rs->aux);
341 // some helper functions
343 void get_pointer_coords(GdkEvent *event, gdouble *ret)
346 gdk_event_get_coords(event, &x, &y);
347 gnome_canvas_window_to_world(canvas, x, y, ret, ret+1);
348 ret[0] -= ui.cur_page->hoffset;
349 ret[1] -= ui.cur_page->voffset;
352 void fix_xinput_coords(GdkEvent *event)
354 double *axes, *px, *py, axis_width;
356 int wx, wy, sx, sy, ix, iy;
358 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
359 axes = event->button.axes;
360 px = &(event->button.x);
361 py = &(event->button.y);
362 device = event->button.device;
364 else if (event->type == GDK_MOTION_NOTIFY) {
365 axes = event->motion.axes;
366 px = &(event->motion.x);
367 py = &(event->motion.y);
368 device = event->motion.device;
370 else return; // nothing we know how to do
372 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
374 #ifdef ENABLE_XINPUT_BUGFIX
375 // fix broken events with the core pointer's location
376 if (!finite(axes[0]) || !finite(axes[1]) || (axes[0]==0. && axes[1]==0.)) {
377 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
382 gdk_window_get_origin(GTK_WIDGET(canvas)->window, &wx, &wy);
383 axis_width = device->axes[0].max - device->axes[0].min;
384 if (axis_width>EPSILON)
385 *px = (axes[0]/axis_width)*ui.screen_width + sx - wx;
386 axis_width = device->axes[1].max - device->axes[1].min;
387 if (axis_width>EPSILON)
388 *py = (axes[1]/axis_width)*ui.screen_height + sy - wy;
391 if (!finite(*px) || !finite(*py) || (*px==0. && *py==0.)) {
392 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
397 /* with GTK+ 2.16 or earlier, the event comes from the parent gdkwindow
398 and so needs to be adjusted for scrolling */
399 if (gtk_major_version == 2 && gtk_minor_version <= 16) {
403 /* with GTK+ 2.17, events come improperly translated, and the event's
404 GdkWindow isn't even the same for ButtonDown as for MotionNotify... */
405 if (gtk_major_version == 2 && gtk_minor_version == 17) { // GTK+ 2.17 issues !!
406 gdk_window_get_position(GTK_WIDGET(canvas)->window, &wx, &wy);
414 double get_pressure_multiplier(GdkEvent *event)
420 if (event->type == GDK_MOTION_NOTIFY) {
421 axes = event->motion.axes;
422 device = event->motion.device;
425 axes = event->button.axes;
426 device = event->button.device;
429 if (device == gdk_device_get_core_pointer()
430 || device->num_axes <= 2) return 1.0;
432 rawpressure = axes[2]/(device->axes[2].max - device->axes[2].min);
433 if (!finite(rawpressure)) return 1.0;
435 return ((1-rawpressure)*ui.width_minimum_multiplier + rawpressure*ui.width_maximum_multiplier);
438 void update_item_bbox(struct Item *item)
443 if (item->type == ITEM_STROKE) {
444 item->bbox.left = item->bbox.right = item->path->coords[0];
445 item->bbox.top = item->bbox.bottom = item->path->coords[1];
446 for (i=1, p=item->path->coords+2; i<item->path->num_points; i++, p+=2)
448 if (p[0] < item->bbox.left) item->bbox.left = p[0];
449 if (p[0] > item->bbox.right) item->bbox.right = p[0];
450 if (p[1] < item->bbox.top) item->bbox.top = p[1];
451 if (p[1] > item->bbox.bottom) item->bbox.bottom = p[1];
454 if (item->type == ITEM_TEXT && item->canvas_item!=NULL) {
456 g_object_get(item->canvas_item, "text_width", &w, "text_height", &h, NULL);
457 item->bbox.right = item->bbox.left + w;
458 item->bbox.bottom = item->bbox.top + h;
462 void make_page_clipbox(struct Page *pg)
464 GnomeCanvasPathDef *pg_clip;
466 pg_clip = gnome_canvas_path_def_new_sized(4);
467 gnome_canvas_path_def_moveto(pg_clip, 0., 0.);
468 gnome_canvas_path_def_lineto(pg_clip, 0., pg->height);
469 gnome_canvas_path_def_lineto(pg_clip, pg->width, pg->height);
470 gnome_canvas_path_def_lineto(pg_clip, pg->width, 0.);
471 gnome_canvas_path_def_closepath(pg_clip);
472 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), "path", pg_clip, NULL);
473 gnome_canvas_path_def_unref(pg_clip);
476 void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
478 PangoFontDescription *font_desc;
479 GnomeCanvasPoints points;
482 if (item->type == ITEM_STROKE) {
483 if (!item->brush.variable_width)
484 item->canvas_item = gnome_canvas_item_new(group,
485 gnome_canvas_line_get_type(), "points", item->path,
486 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
487 "fill-color-rgba", item->brush.color_rgba,
488 "width-units", item->brush.thickness, NULL);
490 item->canvas_item = gnome_canvas_item_new(group,
491 gnome_canvas_group_get_type(), NULL);
492 points.num_points = 2;
493 points.ref_count = 1;
494 for (j = 0; j < item->path->num_points-1; j++) {
495 points.coords = item->path->coords+2*j;
496 gnome_canvas_item_new((GnomeCanvasGroup *) item->canvas_item,
497 gnome_canvas_line_get_type(), "points", &points,
498 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
499 "fill-color-rgba", item->brush.color_rgba,
500 "width-units", item->widths[j], NULL);
504 if (item->type == ITEM_TEXT) {
505 font_desc = pango_font_description_from_string(item->font_name);
506 pango_font_description_set_absolute_size(font_desc,
507 item->font_size*ui.zoom*PANGO_SCALE);
508 item->canvas_item = gnome_canvas_item_new(group,
509 gnome_canvas_text_get_type(),
510 "x", item->bbox.left, "y", item->bbox.top, "anchor", GTK_ANCHOR_NW,
511 "font-desc", font_desc, "fill-color-rgba", item->brush.color_rgba,
512 "text", item->text, NULL);
513 update_item_bbox(item);
517 void make_canvas_items(void)
522 GList *pagelist, *layerlist, *itemlist;
524 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next) {
525 pg = (struct Page *)pagelist->data;
526 if (pg->group == NULL) {
527 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
528 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
529 make_page_clipbox(pg);
531 if (pg->bg->canvas_item == NULL) update_canvas_bg(pg);
532 for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
533 l = (struct Layer *)layerlist->data;
534 if (l->group == NULL)
535 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
536 pg->group, gnome_canvas_group_get_type(), NULL);
537 for (itemlist = l->items; itemlist!=NULL; itemlist = itemlist->next) {
538 item = (struct Item *)itemlist->data;
539 if (item->canvas_item == NULL)
540 make_canvas_item_one(l->group, item);
546 void update_canvas_bg(struct Page *pg)
548 GnomeCanvasGroup *group;
549 GnomeCanvasPoints *seg;
550 GdkPixbuf *scaled_pix;
554 gboolean is_well_scaled;
556 if (pg->bg->canvas_item != NULL)
557 gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
558 pg->bg->canvas_item = NULL;
560 if (pg->bg->type == BG_SOLID)
562 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
563 gnome_canvas_group_get_type(), NULL);
564 group = GNOME_CANVAS_GROUP(pg->bg->canvas_item);
565 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
566 gnome_canvas_item_new(group, gnome_canvas_rect_get_type(),
567 "x1", 0., "x2", pg->width, "y1", 0., "y2", pg->height,
568 "fill-color-rgba", pg->bg->color_rgba, NULL);
569 if (pg->bg->ruling == RULING_NONE) return;
570 seg = gnome_canvas_points_new(2);
572 if (pg->bg->ruling == RULING_GRAPH) {
573 pt[1] = 0; pt[3] = pg->height;
574 for (x=RULING_GRAPHSPACING; x<pg->width-1; x+=RULING_GRAPHSPACING) {
576 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
577 "points", seg, "fill-color-rgba", RULING_COLOR,
578 "width-units", RULING_THICKNESS, NULL);
580 pt[0] = 0; pt[2] = pg->width;
581 for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
583 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
584 "points", seg, "fill-color-rgba", RULING_COLOR,
585 "width-units", RULING_THICKNESS, NULL);
587 gnome_canvas_points_free(seg);
590 pt[0] = 0; pt[2] = pg->width;
591 for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
593 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
594 "points", seg, "fill-color-rgba", RULING_COLOR,
595 "width-units", RULING_THICKNESS, NULL);
597 if (pg->bg->ruling == RULING_LINED) {
598 pt[0] = pt[2] = RULING_LEFTMARGIN;
599 pt[1] = 0; pt[3] = pg->height;
600 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
601 "points", seg, "fill-color-rgba", RULING_MARGIN_COLOR,
602 "width-units", RULING_THICKNESS, NULL);
604 gnome_canvas_points_free(seg);
608 if (pg->bg->type == BG_PIXMAP)
610 pg->bg->pixbuf_scale = 0;
611 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
612 gnome_canvas_pixbuf_get_type(),
613 "pixbuf", pg->bg->pixbuf,
614 "width", pg->width, "height", pg->height,
615 "width-set", TRUE, "height-set", TRUE,
617 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
620 if (pg->bg->type == BG_PDF)
622 if (pg->bg->pixbuf == NULL) return;
623 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
624 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
626 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
627 gnome_canvas_pixbuf_get_type(),
628 "pixbuf", pg->bg->pixbuf,
629 "width-in-pixels", TRUE, "height-in-pixels", TRUE,
632 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
633 gnome_canvas_pixbuf_get_type(),
634 "pixbuf", pg->bg->pixbuf,
635 "width", pg->width, "height", pg->height,
636 "width-set", TRUE, "height-set", TRUE,
638 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
642 gboolean is_visible(struct Page *pg)
644 GtkAdjustment *v_adj;
647 if (!ui.view_continuous) return (pg == ui.cur_page);
648 v_adj = gtk_layout_get_vadjustment(GTK_LAYOUT(canvas));
649 ytop = v_adj->value/ui.zoom;
650 ybot = (v_adj->value + v_adj->page_size) / ui.zoom;
651 return (MAX(ytop, pg->voffset) < MIN(ybot, pg->voffset+pg->height));
654 void rescale_bg_pixmaps(void)
659 gboolean is_well_scaled;
660 gdouble zoom_to_request;
662 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
663 pg = (struct Page *)pglist->data;
664 // in progressive mode we scale only visible pages
665 if (ui.progressive_bg && !is_visible(pg)) continue;
667 if (pg->bg->type == BG_PIXMAP && pg->bg->canvas_item!=NULL) {
668 g_object_get(G_OBJECT(pg->bg->canvas_item), "pixbuf", &pix, NULL);
669 if (pix!=pg->bg->pixbuf)
670 gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pg->bg->pixbuf, NULL);
671 pg->bg->pixbuf_scale = 0;
673 if (pg->bg->type == BG_PDF) {
674 // make pixmap scale to correct size if current one is wrong
675 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
676 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
677 if (pg->bg->canvas_item != NULL && !is_well_scaled) {
678 g_object_get(pg->bg->canvas_item, "width-in-pixels", &is_well_scaled, NULL);
680 gnome_canvas_item_set(pg->bg->canvas_item,
681 "width", pg->width, "height", pg->height,
682 "width-in-pixels", FALSE, "height-in-pixels", FALSE,
683 "width-set", TRUE, "height-set", TRUE,
686 // request an asynchronous update to a better pixmap if needed
687 zoom_to_request = MIN(ui.zoom, MAX_SAFE_RENDER_DPI/72.0);
688 if (pg->bg->pixbuf_scale == zoom_to_request) continue;
689 if (add_bgpdf_request(pg->bg->file_page_seq, zoom_to_request))
690 pg->bg->pixbuf_scale = zoom_to_request;
695 gboolean have_intersect(struct BBox *a, struct BBox *b)
697 return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
698 (MAX(a->left, b->left) <= MIN(a->right, b->right));
701 /* In libgnomecanvas 2.10.0, the lower/raise functions fail to update
702 correctly the end of the group's item list. We try to work around this.
703 DON'T USE gnome_canvas_item_raise/lower directly !! */
705 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
709 i1 = g_list_index(g->item_list, item);
710 if (i1 == -1) return;
712 if (after == NULL) i2 = -1;
713 else i2 = g_list_index(g->item_list, after);
715 if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
716 if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
718 // BUGFIX for libgnomecanvas
719 g->item_list_end = g_list_last(g->item_list);
722 void rgb_to_gdkcolor(guint rgba, GdkColor *color)
725 color->red = ((rgba>>24)&0xff)*0x101;
726 color->green = ((rgba>>16)&0xff)*0x101;
727 color->blue = ((rgba>>8)&0xff)*0x101;
730 guint32 gdkcolor_to_rgba(GdkColor gdkcolor, guint16 alpha)
732 guint32 rgba = ((gdkcolor.red & 0xff00) << 16) |
733 ((gdkcolor.green & 0xff00) << 8) |
734 ((gdkcolor.blue & 0xff00) ) |
735 ((alpha & 0xff00) >> 8);
740 // some interface functions
742 void update_thickness_buttons(void)
744 if (ui.selection!=NULL || ui.toolno[ui.cur_mapping] >= NUM_STROKE_TOOLS) {
745 gtk_toggle_tool_button_set_active(
746 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
748 switch (ui.cur_brush->thickness_no) {
750 gtk_toggle_tool_button_set_active(
751 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
753 case THICKNESS_MEDIUM:
754 gtk_toggle_tool_button_set_active(
755 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
757 case THICKNESS_THICK:
758 gtk_toggle_tool_button_set_active(
759 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
762 gtk_toggle_tool_button_set_active(
763 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
767 void update_color_buttons(void)
770 GtkColorButton *colorbutton;
772 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
773 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
774 gtk_toggle_tool_button_set_active(
775 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
777 switch (ui.cur_brush->color_no) {
779 gtk_toggle_tool_button_set_active(
780 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
783 gtk_toggle_tool_button_set_active(
784 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
787 gtk_toggle_tool_button_set_active(
788 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
791 gtk_toggle_tool_button_set_active(
792 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
795 gtk_toggle_tool_button_set_active(
796 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
798 case COLOR_LIGHTBLUE:
799 gtk_toggle_tool_button_set_active(
800 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
802 case COLOR_LIGHTGREEN:
803 gtk_toggle_tool_button_set_active(
804 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
807 gtk_toggle_tool_button_set_active(
808 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
811 gtk_toggle_tool_button_set_active(
812 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
815 gtk_toggle_tool_button_set_active(
816 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
819 gtk_toggle_tool_button_set_active(
820 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
823 gtk_toggle_tool_button_set_active(
824 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
827 colorbutton = GTK_COLOR_BUTTON(GET_COMPONENT("buttonColorChooser"));
828 if ((ui.toolno[ui.cur_mapping] != TOOL_PEN &&
829 ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER &&
830 ui.toolno[ui.cur_mapping] != TOOL_TEXT))
831 gdkcolor.red = gdkcolor.blue = gdkcolor.green = 0;
832 else rgb_to_gdkcolor(ui.cur_brush->color_rgba, &gdkcolor);
833 gtk_color_button_set_color(colorbutton, &gdkcolor);
834 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
835 gtk_color_button_set_alpha(colorbutton,
836 (ui.cur_brush->color_rgba&0xff)*0x101);
837 gtk_color_button_set_use_alpha(colorbutton, TRUE);
839 gtk_color_button_set_alpha(colorbutton, 0xffff);
840 gtk_color_button_set_use_alpha(colorbutton, FALSE);
844 void update_tool_buttons(void)
846 switch(ui.toolno[ui.cur_mapping]) {
848 gtk_toggle_tool_button_set_active(
849 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
852 gtk_toggle_tool_button_set_active(
853 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
855 case TOOL_HIGHLIGHTER:
856 gtk_toggle_tool_button_set_active(
857 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
860 gtk_toggle_tool_button_set_active(
861 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
863 case TOOL_SELECTREGION:
864 gtk_toggle_tool_button_set_active(
865 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
867 case TOOL_SELECTRECT:
868 gtk_toggle_tool_button_set_active(
869 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
872 gtk_toggle_tool_button_set_active(
873 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
876 gtk_toggle_tool_button_set_active(
877 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
881 gtk_toggle_tool_button_set_active(
882 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
883 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
884 gtk_toggle_tool_button_set_active(
885 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
886 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
888 update_thickness_buttons();
889 update_color_buttons();
892 void update_tool_menu(void)
894 switch(ui.toolno[0]) {
896 gtk_check_menu_item_set_active(
897 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
900 gtk_check_menu_item_set_active(
901 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
903 case TOOL_HIGHLIGHTER:
904 gtk_check_menu_item_set_active(
905 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
908 gtk_check_menu_item_set_active(
909 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
911 case TOOL_SELECTREGION:
912 gtk_check_menu_item_set_active(
913 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
915 case TOOL_SELECTRECT:
916 gtk_check_menu_item_set_active(
917 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
920 gtk_check_menu_item_set_active(
921 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
924 gtk_check_menu_item_set_active(
925 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
929 gtk_check_menu_item_set_active(
930 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
931 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
932 gtk_check_menu_item_set_active(
933 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
934 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
937 void update_ruler_indicator(void)
939 gtk_toggle_tool_button_set_active(
940 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
941 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
942 gtk_toggle_tool_button_set_active(
943 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
944 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
945 gtk_check_menu_item_set_active(
946 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
947 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
948 gtk_check_menu_item_set_active(
949 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
950 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
953 void update_color_menu(void)
955 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
956 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
957 gtk_check_menu_item_set_active(
958 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
960 switch (ui.cur_brush->color_no) {
962 gtk_check_menu_item_set_active(
963 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
966 gtk_check_menu_item_set_active(
967 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
970 gtk_check_menu_item_set_active(
971 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
974 gtk_check_menu_item_set_active(
975 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
978 gtk_check_menu_item_set_active(
979 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
981 case COLOR_LIGHTBLUE:
982 gtk_check_menu_item_set_active(
983 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
985 case COLOR_LIGHTGREEN:
986 gtk_check_menu_item_set_active(
987 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
990 gtk_check_menu_item_set_active(
991 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
994 gtk_check_menu_item_set_active(
995 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
998 gtk_check_menu_item_set_active(
999 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
1002 gtk_check_menu_item_set_active(
1003 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
1006 gtk_check_menu_item_set_active(
1007 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
1011 void update_pen_props_menu(void)
1013 switch(ui.brushes[0][TOOL_PEN].thickness_no) {
1014 case THICKNESS_VERYFINE:
1015 gtk_check_menu_item_set_active(
1016 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
1018 case THICKNESS_FINE:
1019 gtk_check_menu_item_set_active(
1020 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
1022 case THICKNESS_MEDIUM:
1023 gtk_check_menu_item_set_active(
1024 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
1026 case THICKNESS_THICK:
1027 gtk_check_menu_item_set_active(
1028 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
1030 case THICKNESS_VERYTHICK:
1031 gtk_check_menu_item_set_active(
1032 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
1037 void update_eraser_props_menu(void)
1039 switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
1040 case THICKNESS_FINE:
1041 gtk_check_menu_item_set_active(
1042 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
1044 case THICKNESS_MEDIUM:
1045 gtk_check_menu_item_set_active(
1046 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
1048 case THICKNESS_THICK:
1049 gtk_check_menu_item_set_active(
1050 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
1054 gtk_check_menu_item_set_active(
1055 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
1056 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
1057 gtk_check_menu_item_set_active(
1058 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
1059 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
1060 gtk_check_menu_item_set_active(
1061 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
1062 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
1065 void update_highlighter_props_menu(void)
1067 switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
1068 case THICKNESS_FINE:
1069 gtk_check_menu_item_set_active(
1070 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
1072 case THICKNESS_MEDIUM:
1073 gtk_check_menu_item_set_active(
1074 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
1076 case THICKNESS_THICK:
1077 gtk_check_menu_item_set_active(
1078 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
1083 void update_mappings_menu_linkings(void)
1085 switch (ui.linked_brush[1]) {
1087 gtk_check_menu_item_set_active(
1088 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
1091 gtk_check_menu_item_set_active(
1092 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
1095 gtk_check_menu_item_set_active(
1096 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
1099 switch (ui.linked_brush[2]) {
1101 gtk_check_menu_item_set_active(
1102 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
1105 gtk_check_menu_item_set_active(
1106 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
1109 gtk_check_menu_item_set_active(
1110 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
1115 void update_mappings_menu(void)
1117 gtk_widget_set_sensitive(GET_COMPONENT("optionsButtonMappings"), ui.use_xinput);
1118 gtk_widget_set_sensitive(GET_COMPONENT("optionsPressureSensitive"), ui.use_xinput);
1119 gtk_check_menu_item_set_active(
1120 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
1121 gtk_check_menu_item_set_active(
1122 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsPressureSensitive")), ui.pressure_sensitivity);
1124 switch(ui.toolno[1]) {
1126 gtk_check_menu_item_set_active(
1127 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
1130 gtk_check_menu_item_set_active(
1131 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
1133 case TOOL_HIGHLIGHTER:
1134 gtk_check_menu_item_set_active(
1135 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
1138 gtk_check_menu_item_set_active(
1139 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
1141 case TOOL_SELECTREGION:
1142 gtk_check_menu_item_set_active(
1143 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
1145 case TOOL_SELECTRECT:
1146 gtk_check_menu_item_set_active(
1147 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
1149 case TOOL_VERTSPACE:
1150 gtk_check_menu_item_set_active(
1151 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
1154 switch(ui.toolno[2]) {
1156 gtk_check_menu_item_set_active(
1157 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
1160 gtk_check_menu_item_set_active(
1161 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
1163 case TOOL_HIGHLIGHTER:
1164 gtk_check_menu_item_set_active(
1165 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
1168 gtk_check_menu_item_set_active(
1169 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
1171 case TOOL_SELECTREGION:
1172 gtk_check_menu_item_set_active(
1173 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
1175 case TOOL_SELECTRECT:
1176 gtk_check_menu_item_set_active(
1177 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
1179 case TOOL_VERTSPACE:
1180 gtk_check_menu_item_set_active(
1181 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1184 update_mappings_menu_linkings();
1187 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1190 struct Layer *layer;
1195 /* re-show all the layers of the old page */
1196 if (ui.cur_page != NULL)
1197 for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1198 layer = (struct Layer *)list->data;
1199 if (layer->group!=NULL)
1200 gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1203 ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1204 ui.layerno = ui.cur_page->nlayers-1;
1205 ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1206 update_page_stuff();
1207 if (ui.progressive_bg) rescale_bg_pixmaps();
1209 if (rescroll) { // scroll and force a refresh
1210 /* -- this seems to cause some display bugs ??
1211 gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1212 ui.cur_page->voffset*ui.zoom); */
1213 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1214 cy = ui.cur_page->voffset*ui.zoom;
1215 gnome_canvas_scroll_to(canvas, cx, cy);
1218 gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1219 else if (!ui.view_continuous)
1220 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1224 void update_page_stuff(void)
1227 GtkComboBox *layerbox;
1230 GtkSpinButton *spin;
1232 double vertpos, maxwidth;
1234 // move the page groups to their rightful locations or hide them
1235 if (ui.view_continuous) {
1238 for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1239 pg = (struct Page *)pglist->data;
1240 if (pg->group!=NULL) {
1241 pg->hoffset = 0.; pg->voffset = vertpos;
1242 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1243 "x", pg->hoffset, "y", pg->voffset, NULL);
1244 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1246 vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1247 if (pg->width > maxwidth) maxwidth = pg->width;
1249 vertpos -= VIEW_CONTINUOUS_SKIP;
1250 gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1252 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1253 pg = (struct Page *)pglist->data;
1254 if (pg == ui.cur_page && pg->group!=NULL) {
1255 pg->hoffset = 0.; pg->voffset = 0.;
1256 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1257 "x", pg->hoffset, "y", pg->voffset, NULL);
1258 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1260 if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1263 gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1266 // update the page / layer info at bottom of screen
1268 spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1269 ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1270 gtk_spin_button_set_range(spin, 1, journal.npages+1);
1271 /* npages+1 will be used to create a new page at end */
1272 gtk_spin_button_set_value(spin, ui.pageno+1);
1273 g_snprintf(tmp, 10, _(" of %d"), journal.npages);
1274 gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1276 layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1277 if (ui.layerbox_length == 0) {
1278 gtk_combo_box_prepend_text(layerbox, _("Background"));
1279 ui.layerbox_length++;
1281 while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1282 gtk_combo_box_remove_text(layerbox, 0);
1283 ui.layerbox_length--;
1285 while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1286 g_snprintf(tmp, 10, _("Layer %d"), ui.layerbox_length++);
1287 gtk_combo_box_prepend_text(layerbox, tmp);
1289 gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1290 ui.in_update_page_stuff = FALSE;
1292 gtk_container_forall(GTK_CONTAINER(layerbox), unset_flags, (gpointer)GTK_CAN_FOCUS);
1294 // update the paper-style menu radio buttons
1296 if (ui.view_continuous)
1297 gtk_check_menu_item_set_active(
1298 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1300 gtk_check_menu_item_set_active(
1301 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1303 if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1304 switch (ui.cur_page->bg->color_no) {
1306 gtk_check_menu_item_set_active(
1307 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1310 gtk_check_menu_item_set_active(
1311 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1314 gtk_check_menu_item_set_active(
1315 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1318 gtk_check_menu_item_set_active(
1319 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1322 gtk_check_menu_item_set_active(
1323 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1326 gtk_check_menu_item_set_active(
1327 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1330 gtk_check_menu_item_set_active(
1331 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1334 switch (ui.cur_page->bg->ruling) {
1336 gtk_check_menu_item_set_active(
1337 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1340 gtk_check_menu_item_set_active(
1341 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1344 gtk_check_menu_item_set_active(
1345 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1348 gtk_check_menu_item_set_active(
1349 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1353 gtk_check_menu_item_set_active(
1354 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1355 gtk_check_menu_item_set_active(
1356 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1359 // enable/disable the page/layer menu items and toolbar buttons
1361 gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"),
1362 ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1363 gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1364 ui.cur_page->bg->type == BG_SOLID);
1366 gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1367 gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1368 gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1369 gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1370 gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1371 gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1372 gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1373 gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1375 gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1376 gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1378 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1379 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1382 void update_toolbar_and_menu(void)
1384 update_tool_buttons(); // takes care of other toolbar buttons as well
1386 update_color_menu();
1387 update_pen_props_menu();
1388 update_eraser_props_menu();
1389 update_highlighter_props_menu();
1390 update_mappings_menu();
1392 gtk_toggle_tool_button_set_active(
1393 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1394 gtk_check_menu_item_set_active(
1395 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1398 void update_file_name(char *filename)
1401 if (ui.filename != NULL) g_free(ui.filename);
1402 ui.filename = filename;
1403 if (filename == NULL) {
1404 gtk_window_set_title(GTK_WINDOW (winMain), _("Xournal"));
1407 p = g_utf8_strrchr(filename, -1, '/');
1408 if (p == NULL) p = filename;
1409 else p = g_utf8_next_char(p);
1410 g_snprintf(tmp, 100, _("Xournal - %s"), p);
1411 gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1412 new_mru_entry(filename);
1414 if (filename[0]=='/') {
1415 if (ui.default_path!=NULL) g_free(ui.default_path);
1416 ui.default_path = g_path_get_dirname(filename);
1420 void update_undo_redo_enabled(void)
1422 gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1423 gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1424 gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1425 gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1428 void update_copy_paste_enabled(void)
1430 gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1431 gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1432 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_item_type!=ITEM_TEXT);
1433 gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1434 gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1435 gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1436 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_item_type!=ITEM_TEXT);
1439 void update_mapping_linkings(int toolno)
1443 for (i = 1; i<=NUM_BUTTONS; i++) {
1444 if (ui.linked_brush[i] == BRUSH_LINKED) {
1445 if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1446 g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1448 if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1449 ui.linked_brush[i] = BRUSH_STATIC;
1450 if (i==1 || i==2) update_mappings_menu_linkings();
1455 void set_cur_color(int color_no, guint color_rgba)
1457 int which_mapping, tool;
1459 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) tool = TOOL_HIGHLIGHTER;
1460 else tool = TOOL_PEN;
1461 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1462 which_mapping = ui.cur_mapping;
1463 else which_mapping = 0;
1465 ui.brushes[which_mapping][tool].color_no = color_no;
1466 if (tool == TOOL_HIGHLIGHTER && (color_rgba & 0xff) == 0xff)
1467 ui.brushes[which_mapping][tool].color_rgba = color_rgba & ui.hiliter_alpha_mask;
1469 ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1470 update_mapping_linkings(tool);
1473 void recolor_temp_text(int color_no, guint color_rgba)
1477 if (ui.cur_item_type!=ITEM_TEXT) return;
1478 if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1480 undo->type = ITEM_TEXT_ATTRIB;
1481 undo->item = ui.cur_item;
1482 undo->str = g_strdup(ui.cur_item->font_name);
1483 undo->val_x = ui.cur_item->font_size;
1484 undo->brush = (struct Brush *)g_memdup(&(ui.cur_item->brush), sizeof(struct Brush));
1486 ui.cur_item->brush.color_no = color_no;
1487 ui.cur_item->brush.color_rgba = color_rgba;
1488 rgb_to_gdkcolor(color_rgba, &gdkcolor);
1489 gtk_widget_modify_text(ui.cur_item->widget, GTK_STATE_NORMAL, &gdkcolor);
1490 gtk_widget_grab_focus(ui.cur_item->widget);
1493 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1495 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1496 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1499 else if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_TOOL_BUTTON) {
1500 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1504 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1506 if (ui.cur_item_type == ITEM_TEXT)
1507 recolor_temp_text(color_no, color_rgba);
1509 if (ui.selection != NULL) {
1510 recolor_selection(color_no, color_rgba);
1511 update_color_buttons();
1512 update_color_menu();
1515 if (ui.toolno[ui.cur_mapping] != TOOL_PEN && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER
1516 && ui.toolno[ui.cur_mapping] != TOOL_TEXT) {
1517 if (ui.selection != NULL) return;
1520 ui.toolno[ui.cur_mapping] = TOOL_PEN;
1521 ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1522 update_tool_buttons();
1526 set_cur_color(color_no, color_rgba);
1527 update_color_buttons();
1528 update_color_menu();
1532 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1536 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1537 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1540 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1544 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1546 if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1547 rethicken_selection(val);
1548 update_thickness_buttons();
1551 if (tool >= NUM_STROKE_TOOLS) {
1552 update_thickness_buttons(); // undo illegal button selection
1556 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1557 which_mapping = ui.cur_mapping;
1558 else which_mapping = 0;
1559 if (ui.brushes[which_mapping][tool].thickness_no == val) return;
1561 ui.brushes[which_mapping][tool].thickness_no = val;
1562 ui.brushes[which_mapping][tool].thickness = predef_thickness[tool][val];
1563 update_mapping_linkings(tool);
1565 update_thickness_buttons();
1566 if (tool == TOOL_PEN) update_pen_props_menu();
1567 if (tool == TOOL_ERASER) update_eraser_props_menu();
1568 if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1572 void process_papercolor_activate(GtkMenuItem *menuitem, int color, guint rgba)
1578 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1579 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1583 if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages || color == COLOR_OTHER)
1584 gtk_check_menu_item_set_active(
1585 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1589 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1590 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1591 if (pg->bg->type == BG_SOLID && pg->bg->color_rgba != rgba) {
1593 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1594 undo->multiop |= MULTIOP_CONT_REDO;
1596 undo->type = ITEM_NEW_BG_ONE;
1598 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1599 undo->bg->canvas_item = NULL;
1601 pg->bg->color_no = color;
1602 pg->bg->color_rgba = rgba;
1603 update_canvas_bg(pg);
1605 if (!ui.bg_apply_all_pages) break;
1607 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1610 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1614 gboolean hasdone, must_upd;
1616 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1619 if (ui.bg_apply_all_pages)
1620 gtk_check_menu_item_set_active(
1621 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1626 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1627 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1628 if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1630 undo->type = ITEM_NEW_BG_ONE;
1631 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1632 undo->multiop |= MULTIOP_CONT_REDO;
1635 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1636 undo->bg->canvas_item = NULL;
1638 if (pg->bg->type != BG_SOLID) {
1639 pg->bg->type = BG_SOLID;
1640 pg->bg->color_no = COLOR_WHITE;
1641 pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1642 pg->bg->filename = NULL;
1643 pg->bg->pixbuf = NULL;
1646 pg->bg->ruling = style;
1647 update_canvas_bg(pg);
1649 if (!ui.bg_apply_all_pages) break;
1651 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1652 if (must_upd) update_page_stuff();
1655 gboolean ok_to_close(void)
1658 GtkResponseType response;
1660 if (ui.saved) return TRUE;
1661 dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1662 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("Save changes to '%s'?"),
1663 (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1664 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_DISCARD, GTK_RESPONSE_NO);
1665 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
1666 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1667 gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1668 response = gtk_dialog_run(GTK_DIALOG (dialog));
1669 gtk_widget_destroy(dialog);
1670 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1671 return FALSE; // aborted
1672 if (response == GTK_RESPONSE_YES) {
1673 on_fileSave_activate(NULL, NULL);
1674 if (!ui.saved) return FALSE; // if save failed, then we abort
1679 // send the focus back to the appropriate widget
1681 void reset_focus(void)
1683 if (ui.cur_item_type == ITEM_TEXT)
1684 gtk_widget_grab_focus(ui.cur_item->widget);
1686 gtk_widget_grab_focus(GTK_WIDGET(canvas));
1689 // selection / clipboard stuff
1691 void reset_selection(void)
1693 if (ui.selection == NULL) return;
1694 if (ui.selection->canvas_item != NULL)
1695 gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1696 g_list_free(ui.selection->items);
1697 g_free(ui.selection);
1698 ui.selection = NULL;
1699 update_copy_paste_enabled();
1700 update_color_menu();
1701 update_thickness_buttons();
1702 update_color_buttons();
1703 update_font_button();
1707 void move_journal_items_by(GList *itemlist, double dx, double dy,
1708 struct Layer *l1, struct Layer *l2, GList *depths)
1711 GnomeCanvasItem *refitem;
1716 while (itemlist!=NULL) {
1717 item = (struct Item *)itemlist->data;
1718 if (item->type == ITEM_STROKE)
1719 for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1720 { pt[0] += dx; pt[1] += dy; }
1721 if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1722 item->bbox.left += dx;
1723 item->bbox.right += dx;
1724 item->bbox.top += dy;
1725 item->bbox.bottom += dy;
1728 // find out where to insert
1729 if (depths != NULL) {
1730 if (depths->data == NULL) link = l2->items;
1732 link = g_list_find(l2->items, depths->data);
1733 if (link != NULL) link = link->next;
1736 l2->items = g_list_insert_before(l2->items, link, item);
1738 l1->items = g_list_remove(l1->items, item);
1741 if (depths != NULL) { // also raise/lower the canvas items
1742 if (item->canvas_item!=NULL) {
1743 if (depths->data == NULL) link = NULL;
1744 else link = g_list_find(l2->items, depths->data);
1745 if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1746 else refitem = NULL;
1747 lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1749 depths = depths->next;
1751 itemlist = itemlist->next;
1755 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1756 double offset_x, double offset_y)
1760 double mean_scaling, temp;
1762 GnomeCanvasGroup *group;
1765 /* geometric mean of x and y scalings = rescaling for stroke widths
1766 and for text font sizes */
1767 mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1769 for (list = itemlist; list != NULL; list = list->next) {
1770 item = (struct Item *)list->data;
1771 if (item->type == ITEM_STROKE) {
1772 item->brush.thickness = item->brush.thickness * mean_scaling;
1773 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1774 pt[0] = pt[0]*scaling_x + offset_x;
1775 pt[1] = pt[1]*scaling_y + offset_y;
1777 if (item->brush.variable_width)
1778 for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1779 *wid = *wid * mean_scaling;
1781 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1782 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1783 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1784 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1785 if (item->bbox.left > item->bbox.right) {
1786 temp = item->bbox.left;
1787 item->bbox.left = item->bbox.right;
1788 item->bbox.right = temp;
1790 if (item->bbox.top > item->bbox.bottom) {
1791 temp = item->bbox.top;
1792 item->bbox.top = item->bbox.bottom;
1793 item->bbox.bottom = temp;
1796 if (item->type == ITEM_TEXT) {
1797 /* must scale about NW corner -- all other points of the text box
1798 are font- and zoom-dependent, so scaling about center of text box
1799 couldn't be undone properly. FIXME? */
1800 item->font_size *= mean_scaling;
1801 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1802 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1805 if (item->canvas_item!=NULL) {
1806 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1807 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1808 make_canvas_item_one(group, item);
1813 // Switch between button mappings
1815 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1816 click event is being processed ... or if ui.button_switch_mapping is
1817 enabled and mappings are switched (but even then, canvas should have
1818 a pointer grab from the initial click that switched the mapping) */
1820 void switch_mapping(int m)
1822 if (ui.cur_mapping == m) return;
1825 if (ui.toolno[m] < NUM_STROKE_TOOLS)
1826 ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1827 if (ui.toolno[m] == TOOL_TEXT)
1828 ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1829 if (m==0) ui.which_unswitch_button = 0;
1831 update_tool_buttons();
1832 update_color_menu();
1836 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1838 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1839 if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1840 if (ui.toolno[m] == tool) return;
1844 ui.toolno[m] = tool;
1845 if (ui.linked_brush[m] == BRUSH_COPIED) {
1846 ui.linked_brush[m] = BRUSH_STATIC;
1847 update_mappings_menu_linkings();
1851 // update the ordering of components in the main vbox
1853 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1854 {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1856 void update_vbox_order(int *order)
1860 GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1861 gboolean present[VBOX_MAIN_NITEMS];
1863 for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1865 for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1866 if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1867 present[order[i]] = TRUE;
1868 child = GET_COMPONENT(vbox_component_names[order[i]]);
1869 gtk_box_reorder_child(vboxMain, child, j++);
1870 gtk_widget_show(child);
1872 for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1873 if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1876 gchar *make_cur_font_name(void)
1881 if (ui.cur_item_type == ITEM_TEXT)
1882 str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1883 else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1884 ui.selection->items->next==NULL &&
1885 (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1886 str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1888 str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1892 void update_font_button(void)
1896 str = make_cur_font_name();
1897 gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1901 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1903 return GTK_WIDGET_SENSITIVE(widget);
1906 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1908 if (ui.cur_item_type == ITEM_TEXT) {
1909 g_signal_stop_emission_by_name(widget, "can-activate-accel");
1912 return GTK_WIDGET_SENSITIVE(widget);
1915 void allow_all_accels(void)
1917 g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1918 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1919 g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1920 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1921 g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1922 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1923 g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1924 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1925 g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1926 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1927 g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1928 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1929 g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1930 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1931 g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1932 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1933 g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1934 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1935 g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1936 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1937 g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1938 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1939 g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1940 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1941 g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1942 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1943 g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1944 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1945 g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1946 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1947 g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1948 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1949 g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1950 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1951 g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1952 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1953 g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1954 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1955 g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1956 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1957 g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1958 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1959 g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1960 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1961 g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1962 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1963 g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1964 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1965 g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1966 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1967 /* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1968 "can-activate-accel", G_CALLBACK(can_accel), NULL); */
1969 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1970 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1971 g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1972 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1973 g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1974 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1975 g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1976 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1977 g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1978 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1979 g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
1980 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1983 void add_scroll_bindings(void)
1985 GtkBindingSet *binding_set;
1987 binding_set = gtk_binding_set_by_class(
1988 G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
1989 gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
1990 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1991 G_TYPE_BOOLEAN, FALSE);
1992 gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
1993 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1994 G_TYPE_BOOLEAN, FALSE);
1995 gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
1996 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1997 G_TYPE_BOOLEAN, FALSE);
1998 gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
1999 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2000 G_TYPE_BOOLEAN, FALSE);
2001 gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
2002 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2003 G_TYPE_BOOLEAN, TRUE);
2004 gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
2005 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2006 G_TYPE_BOOLEAN, TRUE);
2007 gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
2008 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2009 G_TYPE_BOOLEAN, TRUE);
2010 gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
2011 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2012 G_TYPE_BOOLEAN, TRUE);
2015 gboolean is_event_within_textview(GdkEventButton *event)
2019 if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2020 get_pointer_coords((GdkEvent *)event, pt);
2021 if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2022 if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2026 void hide_unimplemented(void)
2028 gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2029 gtk_widget_hide(GET_COMPONENT("journalFlatten"));
2030 gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2031 gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2032 gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2033 gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2034 gtk_widget_hide(GET_COMPONENT("helpIndex"));
2036 /* config file only works with glib 2.6 and beyond */
2037 if (glib_minor_version<6) {
2038 gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2039 gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2041 /* gtkprint only works with gtk+ 2.10 and beyond */
2042 if (gtk_check_version(2, 10, 0)) {
2043 gtk_widget_hide(GET_COMPONENT("filePrint"));
2046 /* screenshot feature doesn't work yet in Win32 */
2048 gtk_widget_hide(GET_COMPONENT("journalScreenshot"));
2052 // toggle fullscreen mode
2053 void do_fullscreen(gboolean active)
2056 ui.fullscreen = active;
2057 gtk_check_menu_item_set_active(
2058 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2059 gtk_toggle_tool_button_set_active(
2060 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2062 if (ui.fullscreen) {
2064 gtk_window_get_size(GTK_WINDOW(winMain), &ui.pre_fullscreen_width, &ui.pre_fullscreen_height);
2065 gtk_widget_set_size_request(GTK_WIDGET(winMain), gdk_screen_width(),
2066 gdk_screen_height());
2068 gtk_window_fullscreen(GTK_WINDOW(winMain));
2072 gtk_widget_set_size_request(GTK_WIDGET(winMain), -1, -1);
2073 gtk_window_resize(GTK_WINDOW(winMain), ui.pre_fullscreen_width,
2074 ui.pre_fullscreen_height);
2076 gtk_window_unfullscreen(GTK_WINDOW(winMain));
2079 update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2082 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2083 elements receive XInput events that they can't handle properly */
2085 // prevent interface items from getting bogus XInput events
2087 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2090 if (event->type == GDK_MOTION_NOTIFY &&
2091 event->motion.device != gdk_device_get_core_pointer())
2093 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2094 event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2095 event->button.device != gdk_device_get_core_pointer())
2100 /* Code to turn an extended input event into a core event and send it to
2101 a different GdkWindow -- e.g. could be used when a click in a text edit box
2102 gets sent to the canvas instead due to incorrect event translation.
2103 We now turn off xinput altogether while editing text under GTK+ 2.17, so
2104 this isn't needed any more... but could become useful again someday!
2108 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2114 if (user_data) window = (GdkWindow *)user_data;
2115 else window = widget->window;
2117 if (event->type == GDK_MOTION_NOTIFY &&
2118 event->motion.device != gdk_device_get_core_pointer()) {
2119 // printf("fixing motion\n");
2120 gdk_window_get_pointer(window, &ix, &iy, NULL);
2121 event->motion.x = ix; event->motion.y = iy;
2122 event->motion.device = gdk_device_get_core_pointer();
2123 g_object_unref(event->motion.window);
2124 event->motion.window = g_object_ref(window);
2125 gtk_widget_event(widget, event);
2128 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2129 event->button.device != gdk_device_get_core_pointer()) {
2130 // printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2131 gdk_window_get_pointer(window, &ix, &iy, NULL);
2132 event->button.x = ix; event->button.y = iy;
2133 event->button.device = gdk_device_get_core_pointer();
2134 g_object_unref(event->button.window);
2135 event->button.window = g_object_ref(window);
2136 // printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2137 gtk_widget_event(widget, event);
2145 /* When enter is pressed into page spinbox, send focus back to canvas. */
2147 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2153 /* recursively unset widget flags */
2155 void unset_flags(GtkWidget *w, gpointer flag)
2157 GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2158 if(GTK_IS_CONTAINER(w))
2159 gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2162 /* reset focus when a key or button press event reaches someone, or when the
2163 page-number spin button should relinquish control... */
2165 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2167 if (w == GET_COMPONENT("hbox1")) {
2168 /* the event won't be processed since the hbox1 doesn't know what to do with it,
2169 so we might as well kill it and avoid confusing ourselves when it gets
2170 propagated further ... */
2173 if (w == GET_COMPONENT("spinPageNo")) {
2174 /* we let the spin button take care of itself, and don't steal its focus,
2175 unless the user presses Esc or Tab (in those cases we intervene) */
2176 if (ev->type != GDK_KEY_PRESS) return FALSE;
2177 if (ev->key.keyval == GDK_Escape)
2178 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2179 else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2180 return FALSE; // let the spin button process it
2183 // otherwise, we want to make sure the canvas or text item gets focus back...
2188 void install_focus_hooks(GtkWidget *w, gpointer data)
2190 if (w == NULL) return;
2191 g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2192 g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2193 if (GTK_IS_MENU_ITEM(w)) {
2194 g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2195 install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2197 if(GTK_IS_CONTAINER(w))
2198 gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);