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+10;
97 ui.cur_path.coords = g_realloc(ui.cur_path.coords, 2*(n+10)*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+10;
104 ui.cur_widths = g_realloc(ui.cur_widths, (n+10)*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);
400 /* with GTK+ 2.17, events come improperly translated, and the event's
401 GdkWindow isn't even the same for ButtonDown as for MotionNotify... */
402 if (!gtk_check_version(2,17,0)) { // GTK+ 2.17 issues !!
403 gdk_window_get_position(GTK_WIDGET(canvas)->window, &wx, &wy);
411 double get_pressure_multiplier(GdkEvent *event)
415 if (event->button.device == gdk_device_get_core_pointer()
416 || event->button.device->num_axes <= 2) return 1.0;
418 rawpressure = event->button.axes[2]/(event->button.device->axes[2].max - event->button.device->axes[2].min);
420 return ((1-rawpressure)*ui.width_minimum_multiplier + rawpressure*ui.width_maximum_multiplier);
423 void update_item_bbox(struct Item *item)
428 if (item->type == ITEM_STROKE) {
429 item->bbox.left = item->bbox.right = item->path->coords[0];
430 item->bbox.top = item->bbox.bottom = item->path->coords[1];
431 for (i=1, p=item->path->coords+2; i<item->path->num_points; i++, p+=2)
433 if (p[0] < item->bbox.left) item->bbox.left = p[0];
434 if (p[0] > item->bbox.right) item->bbox.right = p[0];
435 if (p[1] < item->bbox.top) item->bbox.top = p[1];
436 if (p[1] > item->bbox.bottom) item->bbox.bottom = p[1];
439 if (item->type == ITEM_TEXT && item->canvas_item!=NULL) {
441 g_object_get(item->canvas_item, "text_width", &w, "text_height", &h, NULL);
442 item->bbox.right = item->bbox.left + w;
443 item->bbox.bottom = item->bbox.top + h;
447 void make_page_clipbox(struct Page *pg)
449 GnomeCanvasPathDef *pg_clip;
451 pg_clip = gnome_canvas_path_def_new_sized(4);
452 gnome_canvas_path_def_moveto(pg_clip, 0., 0.);
453 gnome_canvas_path_def_lineto(pg_clip, 0., pg->height);
454 gnome_canvas_path_def_lineto(pg_clip, pg->width, pg->height);
455 gnome_canvas_path_def_lineto(pg_clip, pg->width, 0.);
456 gnome_canvas_path_def_closepath(pg_clip);
457 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), "path", pg_clip, NULL);
458 gnome_canvas_path_def_unref(pg_clip);
461 void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
463 PangoFontDescription *font_desc;
464 GnomeCanvasPoints points;
467 if (item->type == ITEM_STROKE) {
468 if (!item->brush.variable_width)
469 item->canvas_item = gnome_canvas_item_new(group,
470 gnome_canvas_line_get_type(), "points", item->path,
471 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
472 "fill-color-rgba", item->brush.color_rgba,
473 "width-units", item->brush.thickness, NULL);
475 item->canvas_item = gnome_canvas_item_new(group,
476 gnome_canvas_group_get_type(), NULL);
477 points.num_points = 2;
478 points.ref_count = 1;
479 for (j = 0; j < item->path->num_points-1; j++) {
480 points.coords = item->path->coords+2*j;
481 gnome_canvas_item_new((GnomeCanvasGroup *) item->canvas_item,
482 gnome_canvas_line_get_type(), "points", &points,
483 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
484 "fill-color-rgba", item->brush.color_rgba,
485 "width-units", item->widths[j], NULL);
489 if (item->type == ITEM_TEXT) {
490 font_desc = pango_font_description_from_string(item->font_name);
491 pango_font_description_set_absolute_size(font_desc,
492 item->font_size*ui.zoom*PANGO_SCALE);
493 item->canvas_item = gnome_canvas_item_new(group,
494 gnome_canvas_text_get_type(),
495 "x", item->bbox.left, "y", item->bbox.top, "anchor", GTK_ANCHOR_NW,
496 "font-desc", font_desc, "fill-color-rgba", item->brush.color_rgba,
497 "text", item->text, NULL);
498 update_item_bbox(item);
502 void make_canvas_items(void)
507 GList *pagelist, *layerlist, *itemlist;
509 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next) {
510 pg = (struct Page *)pagelist->data;
511 if (pg->group == NULL) {
512 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
513 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
514 make_page_clipbox(pg);
516 if (pg->bg->canvas_item == NULL) update_canvas_bg(pg);
517 for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
518 l = (struct Layer *)layerlist->data;
519 if (l->group == NULL)
520 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
521 pg->group, gnome_canvas_group_get_type(), NULL);
522 for (itemlist = l->items; itemlist!=NULL; itemlist = itemlist->next) {
523 item = (struct Item *)itemlist->data;
524 if (item->canvas_item == NULL)
525 make_canvas_item_one(l->group, item);
531 void update_canvas_bg(struct Page *pg)
533 GnomeCanvasGroup *group;
534 GnomeCanvasPoints *seg;
535 GdkPixbuf *scaled_pix;
539 gboolean is_well_scaled;
541 if (pg->bg->canvas_item != NULL)
542 gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
543 pg->bg->canvas_item = NULL;
545 if (pg->bg->type == BG_SOLID)
547 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
548 gnome_canvas_group_get_type(), NULL);
549 group = GNOME_CANVAS_GROUP(pg->bg->canvas_item);
550 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
551 gnome_canvas_item_new(group, gnome_canvas_rect_get_type(),
552 "x1", 0., "x2", pg->width, "y1", 0., "y2", pg->height,
553 "fill-color-rgba", pg->bg->color_rgba, NULL);
554 if (pg->bg->ruling == RULING_NONE) return;
555 seg = gnome_canvas_points_new(2);
557 if (pg->bg->ruling == RULING_GRAPH) {
558 pt[1] = 0; pt[3] = pg->height;
559 for (x=RULING_GRAPHSPACING; x<pg->width-1; x+=RULING_GRAPHSPACING) {
561 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
562 "points", seg, "fill-color-rgba", RULING_COLOR,
563 "width-units", RULING_THICKNESS, NULL);
565 pt[0] = 0; pt[2] = pg->width;
566 for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
568 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
569 "points", seg, "fill-color-rgba", RULING_COLOR,
570 "width-units", RULING_THICKNESS, NULL);
572 gnome_canvas_points_free(seg);
575 pt[0] = 0; pt[2] = pg->width;
576 for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
578 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
579 "points", seg, "fill-color-rgba", RULING_COLOR,
580 "width-units", RULING_THICKNESS, NULL);
582 if (pg->bg->ruling == RULING_LINED) {
583 pt[0] = pt[2] = RULING_LEFTMARGIN;
584 pt[1] = 0; pt[3] = pg->height;
585 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
586 "points", seg, "fill-color-rgba", RULING_MARGIN_COLOR,
587 "width-units", RULING_THICKNESS, NULL);
589 gnome_canvas_points_free(seg);
593 if (pg->bg->type == BG_PIXMAP)
595 pg->bg->pixbuf_scale = 0;
596 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
597 gnome_canvas_pixbuf_get_type(),
598 "pixbuf", pg->bg->pixbuf,
599 "width", pg->width, "height", pg->height,
600 "width-set", TRUE, "height-set", TRUE,
602 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
605 if (pg->bg->type == BG_PDF)
607 if (pg->bg->pixbuf == NULL) return;
608 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
609 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
611 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
612 gnome_canvas_pixbuf_get_type(),
613 "pixbuf", pg->bg->pixbuf,
614 "width-in-pixels", TRUE, "height-in-pixels", TRUE,
617 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
618 gnome_canvas_pixbuf_get_type(),
619 "pixbuf", pg->bg->pixbuf,
620 "width", pg->width, "height", pg->height,
621 "width-set", TRUE, "height-set", TRUE,
623 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
627 gboolean is_visible(struct Page *pg)
629 GtkAdjustment *v_adj;
632 if (!ui.view_continuous) return (pg == ui.cur_page);
633 v_adj = gtk_layout_get_vadjustment(GTK_LAYOUT(canvas));
634 ytop = v_adj->value/ui.zoom;
635 ybot = (v_adj->value + v_adj->page_size) / ui.zoom;
636 return (MAX(ytop, pg->voffset) < MIN(ybot, pg->voffset+pg->height));
639 void rescale_bg_pixmaps(void)
644 gboolean is_well_scaled;
645 gdouble zoom_to_request;
647 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
648 pg = (struct Page *)pglist->data;
649 // in progressive mode we scale only visible pages
650 if (ui.progressive_bg && !is_visible(pg)) continue;
652 if (pg->bg->type == BG_PIXMAP && pg->bg->canvas_item!=NULL) { // do the rescaling ourselves
653 g_object_get(G_OBJECT(pg->bg->canvas_item), "pixbuf", &pix, NULL);
654 if (pix!=pg->bg->pixbuf)
655 gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pg->bg->pixbuf, NULL);
656 pg->bg->pixbuf_scale = 0;
658 if (pg->bg->type == BG_PDF) {
659 // make pixmap scale to correct size if current one is wrong
660 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
661 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
662 if (pg->bg->canvas_item != NULL && !is_well_scaled) {
663 g_object_get(pg->bg->canvas_item, "width-in-pixels", &is_well_scaled, NULL);
665 gnome_canvas_item_set(pg->bg->canvas_item,
666 "width", pg->width, "height", pg->height,
667 "width-in-pixels", FALSE, "height-in-pixels", FALSE,
668 "width-set", TRUE, "height-set", TRUE,
671 // request an asynchronous update to a better pixmap if needed
672 zoom_to_request = MIN(ui.zoom, MAX_SAFE_RENDER_DPI/72.0);
673 if (pg->bg->pixbuf_scale == zoom_to_request) continue;
674 add_bgpdf_request(pg->bg->file_page_seq, zoom_to_request);
675 pg->bg->pixbuf_scale = zoom_to_request;
680 gboolean have_intersect(struct BBox *a, struct BBox *b)
682 return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
683 (MAX(a->left, b->left) <= MIN(a->right, b->right));
686 /* In libgnomecanvas 2.10.0, the lower/raise functions fail to update
687 correctly the end of the group's item list. We try to work around this.
688 DON'T USE gnome_canvas_item_raise/lower directly !! */
690 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
694 i1 = g_list_index(g->item_list, item);
695 if (i1 == -1) return;
697 if (after == NULL) i2 = -1;
698 else i2 = g_list_index(g->item_list, after);
700 if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
701 if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
703 // BUGFIX for libgnomecanvas
704 g->item_list_end = g_list_last(g->item_list);
707 void rgb_to_gdkcolor(guint rgba, GdkColor *color)
710 color->red = ((rgba>>24)&0xff)*0x101;
711 color->green = ((rgba>>16)&0xff)*0x101;
712 color->blue = ((rgba>>8)&0xff)*0x101;
715 guint32 gdkcolor_to_rgba(GdkColor gdkcolor, guint16 alpha)
717 guint32 rgba = ((gdkcolor.red & 0xff00) << 16) |
718 ((gdkcolor.green & 0xff00) << 8) |
719 ((gdkcolor.blue & 0xff00) ) |
720 ((alpha & 0xff00) >> 8);
725 // some interface functions
727 void update_thickness_buttons(void)
729 if (ui.selection!=NULL || ui.toolno[ui.cur_mapping] >= NUM_STROKE_TOOLS) {
730 gtk_toggle_tool_button_set_active(
731 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
733 switch (ui.cur_brush->thickness_no) {
735 gtk_toggle_tool_button_set_active(
736 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
738 case THICKNESS_MEDIUM:
739 gtk_toggle_tool_button_set_active(
740 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
742 case THICKNESS_THICK:
743 gtk_toggle_tool_button_set_active(
744 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
747 gtk_toggle_tool_button_set_active(
748 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
752 void update_color_buttons(void)
755 GtkColorButton *colorbutton;
757 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
758 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
759 gtk_toggle_tool_button_set_active(
760 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
762 switch (ui.cur_brush->color_no) {
764 gtk_toggle_tool_button_set_active(
765 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
768 gtk_toggle_tool_button_set_active(
769 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
772 gtk_toggle_tool_button_set_active(
773 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
776 gtk_toggle_tool_button_set_active(
777 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
780 gtk_toggle_tool_button_set_active(
781 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
783 case COLOR_LIGHTBLUE:
784 gtk_toggle_tool_button_set_active(
785 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
787 case COLOR_LIGHTGREEN:
788 gtk_toggle_tool_button_set_active(
789 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
792 gtk_toggle_tool_button_set_active(
793 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
796 gtk_toggle_tool_button_set_active(
797 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
800 gtk_toggle_tool_button_set_active(
801 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
804 gtk_toggle_tool_button_set_active(
805 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
808 gtk_toggle_tool_button_set_active(
809 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
812 colorbutton = GTK_COLOR_BUTTON(GET_COMPONENT("buttonColorChooser"));
813 if ((ui.toolno[ui.cur_mapping] != TOOL_PEN &&
814 ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER &&
815 ui.toolno[ui.cur_mapping] != TOOL_TEXT))
816 gdkcolor.red = gdkcolor.blue = gdkcolor.green = 0;
817 else rgb_to_gdkcolor(ui.cur_brush->color_rgba, &gdkcolor);
818 gtk_color_button_set_color(colorbutton, &gdkcolor);
819 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
820 gtk_color_button_set_alpha(colorbutton,
821 (ui.cur_brush->color_rgba&0xff)*0x101);
822 gtk_color_button_set_use_alpha(colorbutton, TRUE);
824 gtk_color_button_set_alpha(colorbutton, 0xffff);
825 gtk_color_button_set_use_alpha(colorbutton, FALSE);
829 void update_tool_buttons(void)
831 switch(ui.toolno[ui.cur_mapping]) {
833 gtk_toggle_tool_button_set_active(
834 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
837 gtk_toggle_tool_button_set_active(
838 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
840 case TOOL_HIGHLIGHTER:
841 gtk_toggle_tool_button_set_active(
842 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
845 gtk_toggle_tool_button_set_active(
846 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
848 case TOOL_SELECTREGION:
849 gtk_toggle_tool_button_set_active(
850 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
852 case TOOL_SELECTRECT:
853 gtk_toggle_tool_button_set_active(
854 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
857 gtk_toggle_tool_button_set_active(
858 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
861 gtk_toggle_tool_button_set_active(
862 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
866 gtk_toggle_tool_button_set_active(
867 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
868 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
869 gtk_toggle_tool_button_set_active(
870 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
871 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
873 update_thickness_buttons();
874 update_color_buttons();
877 void update_tool_menu(void)
879 switch(ui.toolno[0]) {
881 gtk_check_menu_item_set_active(
882 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
885 gtk_check_menu_item_set_active(
886 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
888 case TOOL_HIGHLIGHTER:
889 gtk_check_menu_item_set_active(
890 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
893 gtk_check_menu_item_set_active(
894 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
896 case TOOL_SELECTREGION:
897 gtk_check_menu_item_set_active(
898 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
900 case TOOL_SELECTRECT:
901 gtk_check_menu_item_set_active(
902 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
905 gtk_check_menu_item_set_active(
906 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
909 gtk_check_menu_item_set_active(
910 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
914 gtk_check_menu_item_set_active(
915 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
916 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
917 gtk_check_menu_item_set_active(
918 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
919 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
922 void update_ruler_indicator(void)
924 gtk_toggle_tool_button_set_active(
925 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
926 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
927 gtk_toggle_tool_button_set_active(
928 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
929 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
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_color_menu(void)
940 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
941 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
942 gtk_check_menu_item_set_active(
943 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
945 switch (ui.cur_brush->color_no) {
947 gtk_check_menu_item_set_active(
948 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
951 gtk_check_menu_item_set_active(
952 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
955 gtk_check_menu_item_set_active(
956 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
959 gtk_check_menu_item_set_active(
960 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
963 gtk_check_menu_item_set_active(
964 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
966 case COLOR_LIGHTBLUE:
967 gtk_check_menu_item_set_active(
968 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
970 case COLOR_LIGHTGREEN:
971 gtk_check_menu_item_set_active(
972 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
975 gtk_check_menu_item_set_active(
976 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
979 gtk_check_menu_item_set_active(
980 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
983 gtk_check_menu_item_set_active(
984 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
987 gtk_check_menu_item_set_active(
988 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
991 gtk_check_menu_item_set_active(
992 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
996 void update_pen_props_menu(void)
998 switch(ui.brushes[0][TOOL_PEN].thickness_no) {
999 case THICKNESS_VERYFINE:
1000 gtk_check_menu_item_set_active(
1001 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
1003 case THICKNESS_FINE:
1004 gtk_check_menu_item_set_active(
1005 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
1007 case THICKNESS_MEDIUM:
1008 gtk_check_menu_item_set_active(
1009 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
1011 case THICKNESS_THICK:
1012 gtk_check_menu_item_set_active(
1013 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
1015 case THICKNESS_VERYTHICK:
1016 gtk_check_menu_item_set_active(
1017 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
1022 void update_eraser_props_menu(void)
1024 switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
1025 case THICKNESS_FINE:
1026 gtk_check_menu_item_set_active(
1027 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
1029 case THICKNESS_MEDIUM:
1030 gtk_check_menu_item_set_active(
1031 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
1033 case THICKNESS_THICK:
1034 gtk_check_menu_item_set_active(
1035 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
1039 gtk_check_menu_item_set_active(
1040 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
1041 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
1042 gtk_check_menu_item_set_active(
1043 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
1044 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
1045 gtk_check_menu_item_set_active(
1046 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
1047 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
1050 void update_highlighter_props_menu(void)
1052 switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
1053 case THICKNESS_FINE:
1054 gtk_check_menu_item_set_active(
1055 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
1057 case THICKNESS_MEDIUM:
1058 gtk_check_menu_item_set_active(
1059 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
1061 case THICKNESS_THICK:
1062 gtk_check_menu_item_set_active(
1063 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
1068 void update_mappings_menu_linkings(void)
1070 switch (ui.linked_brush[1]) {
1072 gtk_check_menu_item_set_active(
1073 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
1076 gtk_check_menu_item_set_active(
1077 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
1080 gtk_check_menu_item_set_active(
1081 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
1084 switch (ui.linked_brush[2]) {
1086 gtk_check_menu_item_set_active(
1087 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
1090 gtk_check_menu_item_set_active(
1091 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
1094 gtk_check_menu_item_set_active(
1095 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
1100 void update_mappings_menu(void)
1102 gtk_widget_set_sensitive(GET_COMPONENT("optionsButtonMappings"), ui.use_xinput);
1103 gtk_widget_set_sensitive(GET_COMPONENT("optionsDiscardCoreEvents"), ui.use_xinput);
1104 gtk_widget_set_sensitive(GET_COMPONENT("optionsPressureSensitive"), ui.use_xinput);
1105 gtk_check_menu_item_set_active(
1106 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
1107 gtk_check_menu_item_set_active(
1108 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsDiscardCoreEvents")), ui.discard_corepointer);
1109 gtk_check_menu_item_set_active(
1110 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsPressureSensitive")), ui.pressure_sensitivity);
1112 switch(ui.toolno[1]) {
1114 gtk_check_menu_item_set_active(
1115 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
1118 gtk_check_menu_item_set_active(
1119 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
1121 case TOOL_HIGHLIGHTER:
1122 gtk_check_menu_item_set_active(
1123 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
1126 gtk_check_menu_item_set_active(
1127 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
1129 case TOOL_SELECTREGION:
1130 gtk_check_menu_item_set_active(
1131 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
1133 case TOOL_SELECTRECT:
1134 gtk_check_menu_item_set_active(
1135 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
1137 case TOOL_VERTSPACE:
1138 gtk_check_menu_item_set_active(
1139 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
1142 switch(ui.toolno[2]) {
1144 gtk_check_menu_item_set_active(
1145 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
1148 gtk_check_menu_item_set_active(
1149 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
1151 case TOOL_HIGHLIGHTER:
1152 gtk_check_menu_item_set_active(
1153 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
1156 gtk_check_menu_item_set_active(
1157 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
1159 case TOOL_SELECTREGION:
1160 gtk_check_menu_item_set_active(
1161 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
1163 case TOOL_SELECTRECT:
1164 gtk_check_menu_item_set_active(
1165 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
1167 case TOOL_VERTSPACE:
1168 gtk_check_menu_item_set_active(
1169 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1172 update_mappings_menu_linkings();
1175 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1178 struct Layer *layer;
1183 /* re-show all the layers of the old page */
1184 if (ui.cur_page != NULL)
1185 for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1186 layer = (struct Layer *)list->data;
1187 if (layer->group!=NULL)
1188 gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1191 ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1192 ui.layerno = ui.cur_page->nlayers-1;
1193 ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1194 update_page_stuff();
1195 if (ui.progressive_bg) rescale_bg_pixmaps();
1197 if (rescroll) { // scroll and force a refresh
1198 /* -- this seems to cause some display bugs ??
1199 gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1200 ui.cur_page->voffset*ui.zoom); */
1201 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1202 cy = ui.cur_page->voffset*ui.zoom;
1203 gnome_canvas_scroll_to(canvas, cx, cy);
1206 gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1207 else if (!ui.view_continuous)
1208 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1212 void update_page_stuff(void)
1215 GtkComboBox *layerbox;
1218 GtkSpinButton *spin;
1220 double vertpos, maxwidth;
1222 // move the page groups to their rightful locations or hide them
1223 if (ui.view_continuous) {
1226 for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1227 pg = (struct Page *)pglist->data;
1228 if (pg->group!=NULL) {
1229 pg->hoffset = 0.; pg->voffset = vertpos;
1230 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1231 "x", pg->hoffset, "y", pg->voffset, NULL);
1232 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1234 vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1235 if (pg->width > maxwidth) maxwidth = pg->width;
1237 vertpos -= VIEW_CONTINUOUS_SKIP;
1238 gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1240 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1241 pg = (struct Page *)pglist->data;
1242 if (pg == ui.cur_page && pg->group!=NULL) {
1243 pg->hoffset = 0.; pg->voffset = 0.;
1244 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1245 "x", pg->hoffset, "y", pg->voffset, NULL);
1246 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1248 if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1251 gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1254 // update the page / layer info at bottom of screen
1256 spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1257 ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1258 gtk_spin_button_set_range(spin, 1, journal.npages+1);
1259 /* npages+1 will be used to create a new page at end */
1260 gtk_spin_button_set_value(spin, ui.pageno+1);
1261 g_snprintf(tmp, 10, _(" of %d"), journal.npages);
1262 gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1264 layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1265 if (ui.layerbox_length == 0) {
1266 gtk_combo_box_prepend_text(layerbox, _("Background"));
1267 ui.layerbox_length++;
1269 while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1270 gtk_combo_box_remove_text(layerbox, 0);
1271 ui.layerbox_length--;
1273 while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1274 g_snprintf(tmp, 10, _("Layer %d"), ui.layerbox_length++);
1275 gtk_combo_box_prepend_text(layerbox, tmp);
1277 gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1278 ui.in_update_page_stuff = FALSE;
1280 // update the paper-style menu radio buttons
1282 if (ui.view_continuous)
1283 gtk_check_menu_item_set_active(
1284 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1286 gtk_check_menu_item_set_active(
1287 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1289 if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1290 switch (ui.cur_page->bg->color_no) {
1292 gtk_check_menu_item_set_active(
1293 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1296 gtk_check_menu_item_set_active(
1297 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1300 gtk_check_menu_item_set_active(
1301 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1304 gtk_check_menu_item_set_active(
1305 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1308 gtk_check_menu_item_set_active(
1309 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1312 gtk_check_menu_item_set_active(
1313 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1316 gtk_check_menu_item_set_active(
1317 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOther")), TRUE);
1320 switch (ui.cur_page->bg->ruling) {
1322 gtk_check_menu_item_set_active(
1323 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1326 gtk_check_menu_item_set_active(
1327 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1330 gtk_check_menu_item_set_active(
1331 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1334 gtk_check_menu_item_set_active(
1335 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1339 gtk_check_menu_item_set_active(
1340 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1341 gtk_check_menu_item_set_active(
1342 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1345 // enable/disable the page/layer menu items and toolbar buttons
1347 gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"),
1348 ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1349 gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1350 ui.cur_page->bg->type == BG_SOLID);
1352 gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1353 gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1354 gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1355 gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1356 gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1357 gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1358 gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1359 gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1361 gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1362 gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1364 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1365 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1368 void update_toolbar_and_menu(void)
1370 update_tool_buttons(); // takes care of other toolbar buttons as well
1372 update_color_menu();
1373 update_pen_props_menu();
1374 update_eraser_props_menu();
1375 update_highlighter_props_menu();
1376 update_mappings_menu();
1378 gtk_toggle_tool_button_set_active(
1379 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1380 gtk_check_menu_item_set_active(
1381 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1384 void update_file_name(char *filename)
1387 if (ui.filename != NULL) g_free(ui.filename);
1388 ui.filename = filename;
1389 if (filename == NULL) {
1390 gtk_window_set_title(GTK_WINDOW (winMain), _("Xournal"));
1393 p = g_utf8_strrchr(filename, -1, '/');
1394 if (p == NULL) p = filename;
1395 else p = g_utf8_next_char(p);
1396 g_snprintf(tmp, 100, _("Xournal - %s"), p);
1397 gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1398 new_mru_entry(filename);
1400 if (filename[0]=='/') {
1401 if (ui.default_path!=NULL) g_free(ui.default_path);
1402 ui.default_path = g_path_get_dirname(filename);
1406 void update_undo_redo_enabled(void)
1408 gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1409 gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1410 gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1411 gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1414 void update_copy_paste_enabled(void)
1416 gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1417 gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1418 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_item_type!=ITEM_TEXT);
1419 gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1420 gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1421 gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1422 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_item_type!=ITEM_TEXT);
1425 void update_mapping_linkings(int toolno)
1429 for (i = 1; i<=NUM_BUTTONS; i++) {
1430 if (ui.linked_brush[i] == BRUSH_LINKED) {
1431 if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1432 g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1434 if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1435 ui.linked_brush[i] = BRUSH_STATIC;
1436 if (i==1 || i==2) update_mappings_menu_linkings();
1441 void set_cur_color(int color_no, guint color_rgba)
1443 int which_mapping, tool;
1445 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) tool = TOOL_HIGHLIGHTER;
1446 else tool = TOOL_PEN;
1447 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1448 which_mapping = ui.cur_mapping;
1449 else which_mapping = 0;
1451 ui.brushes[which_mapping][tool].color_no = color_no;
1452 if (tool == TOOL_HIGHLIGHTER && (color_rgba & 0xff) == 0xff)
1453 ui.brushes[which_mapping][tool].color_rgba = color_rgba & ui.hiliter_alpha_mask;
1455 ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1456 update_mapping_linkings(tool);
1459 void recolor_temp_text(int color_no, guint color_rgba)
1463 if (ui.cur_item_type!=ITEM_TEXT) return;
1464 if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1466 undo->type = ITEM_TEXT_ATTRIB;
1467 undo->item = ui.cur_item;
1468 undo->str = g_strdup(ui.cur_item->font_name);
1469 undo->val_x = ui.cur_item->font_size;
1470 undo->brush = (struct Brush *)g_memdup(&(ui.cur_item->brush), sizeof(struct Brush));
1472 ui.cur_item->brush.color_no = color_no;
1473 ui.cur_item->brush.color_rgba = color_rgba;
1474 rgb_to_gdkcolor(color_rgba, &gdkcolor);
1475 gtk_widget_modify_text(ui.cur_item->widget, GTK_STATE_NORMAL, &gdkcolor);
1476 gtk_widget_grab_focus(ui.cur_item->widget);
1479 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1481 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1482 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1485 else if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_TOOL_BUTTON) {
1486 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1490 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1493 if (ui.cur_item_type == ITEM_TEXT)
1494 recolor_temp_text(color_no, color_rgba);
1496 if (ui.selection != NULL) {
1497 recolor_selection(color_no, color_rgba);
1498 update_color_buttons();
1499 update_color_menu();
1502 if (ui.toolno[ui.cur_mapping] != TOOL_PEN && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER
1503 && ui.toolno[ui.cur_mapping] != TOOL_TEXT) {
1504 if (ui.selection != NULL) return;
1507 ui.toolno[ui.cur_mapping] = TOOL_PEN;
1508 ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1509 update_tool_buttons();
1513 set_cur_color(color_no, color_rgba);
1514 update_color_buttons();
1515 update_color_menu();
1519 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1523 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1524 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1527 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1531 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1533 if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1535 rethicken_selection(val);
1536 update_thickness_buttons();
1539 if (tool >= NUM_STROKE_TOOLS) {
1540 update_thickness_buttons(); // undo illegal button selection
1544 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1545 which_mapping = ui.cur_mapping;
1546 else which_mapping = 0;
1547 if (ui.brushes[which_mapping][tool].thickness_no == val) return;
1550 ui.brushes[which_mapping][tool].thickness_no = val;
1551 ui.brushes[which_mapping][tool].thickness = predef_thickness[tool][val];
1552 update_mapping_linkings(tool);
1554 update_thickness_buttons();
1555 if (tool == TOOL_PEN) update_pen_props_menu();
1556 if (tool == TOOL_ERASER) update_eraser_props_menu();
1557 if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1561 void process_papercolor_activate(GtkMenuItem *menuitem, int color)
1567 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1570 if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages)
1571 gtk_check_menu_item_set_active(
1572 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1576 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1577 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1578 if (pg->bg->type == BG_SOLID && pg->bg->color_no != color) {
1580 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1581 undo->multiop |= MULTIOP_CONT_REDO;
1583 undo->type = ITEM_NEW_BG_ONE;
1585 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1586 undo->bg->canvas_item = NULL;
1588 pg->bg->color_no = color;
1589 pg->bg->color_rgba = predef_bgcolors_rgba[color];
1590 update_canvas_bg(pg);
1592 if (!ui.bg_apply_all_pages) break;
1594 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1597 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1601 gboolean hasdone, must_upd;
1603 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1606 if (ui.bg_apply_all_pages)
1607 gtk_check_menu_item_set_active(
1608 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1613 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1614 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1615 if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1617 undo->type = ITEM_NEW_BG_ONE;
1618 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1619 undo->multiop |= MULTIOP_CONT_REDO;
1622 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1623 undo->bg->canvas_item = NULL;
1625 if (pg->bg->type != BG_SOLID) {
1626 pg->bg->type = BG_SOLID;
1627 pg->bg->color_no = COLOR_WHITE;
1628 pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1629 pg->bg->filename = NULL;
1630 pg->bg->pixbuf = NULL;
1633 pg->bg->ruling = style;
1634 update_canvas_bg(pg);
1636 if (!ui.bg_apply_all_pages) break;
1638 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1639 if (must_upd) update_page_stuff();
1642 gboolean ok_to_close(void)
1645 GtkResponseType response;
1647 if (ui.saved) return TRUE;
1648 dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1649 GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Save changes to '%s'?"),
1650 (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1651 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1652 response = gtk_dialog_run(GTK_DIALOG (dialog));
1653 gtk_widget_destroy(dialog);
1654 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1655 return FALSE; // aborted
1656 if (response == GTK_RESPONSE_YES) {
1657 on_fileSave_activate(NULL, NULL);
1658 if (!ui.saved) return FALSE; // if save failed, then we abort
1663 // send the focus back to the appropriate widget
1664 void reset_focus(void)
1666 if (ui.cur_item_type == ITEM_TEXT)
1667 gtk_widget_grab_focus(ui.cur_item->widget);
1669 gtk_widget_grab_focus(GTK_WIDGET(canvas));
1673 // selection / clipboard stuff
1675 void reset_selection(void)
1677 if (ui.selection == NULL) return;
1678 if (ui.selection->canvas_item != NULL)
1679 gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1680 g_list_free(ui.selection->items);
1681 g_free(ui.selection);
1682 ui.selection = NULL;
1683 update_copy_paste_enabled();
1684 update_color_menu();
1685 update_thickness_buttons();
1686 update_color_buttons();
1687 update_font_button();
1691 void move_journal_items_by(GList *itemlist, double dx, double dy,
1692 struct Layer *l1, struct Layer *l2, GList *depths)
1695 GnomeCanvasItem *refitem;
1700 while (itemlist!=NULL) {
1701 item = (struct Item *)itemlist->data;
1702 if (item->type == ITEM_STROKE)
1703 for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1704 { pt[0] += dx; pt[1] += dy; }
1705 if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1706 item->bbox.left += dx;
1707 item->bbox.right += dx;
1708 item->bbox.top += dy;
1709 item->bbox.bottom += dy;
1712 // find out where to insert
1713 if (depths != NULL) {
1714 if (depths->data == NULL) link = l2->items;
1716 link = g_list_find(l2->items, depths->data);
1717 if (link != NULL) link = link->next;
1720 l2->items = g_list_insert_before(l2->items, link, item);
1722 l1->items = g_list_remove(l1->items, item);
1725 if (depths != NULL) { // also raise/lower the canvas items
1726 if (item->canvas_item!=NULL) {
1727 if (depths->data == NULL) link = NULL;
1728 else link = g_list_find(l2->items, depths->data);
1729 if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1730 else refitem = NULL;
1731 lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1733 depths = depths->next;
1735 itemlist = itemlist->next;
1739 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1740 double offset_x, double offset_y)
1744 double mean_scaling, temp;
1746 GnomeCanvasGroup *group;
1749 /* geometric mean of x and y scalings = rescaling for stroke widths
1750 and for text font sizes */
1751 mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1753 for (list = itemlist; list != NULL; list = list->next) {
1754 item = (struct Item *)list->data;
1755 if (item->type == ITEM_STROKE) {
1756 item->brush.thickness = item->brush.thickness * mean_scaling;
1757 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1758 pt[0] = pt[0]*scaling_x + offset_x;
1759 pt[1] = pt[1]*scaling_y + offset_y;
1761 if (item->brush.variable_width)
1762 for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1763 *wid = *wid * mean_scaling;
1765 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1766 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1767 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1768 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1769 if (item->bbox.left > item->bbox.right) {
1770 temp = item->bbox.left;
1771 item->bbox.left = item->bbox.right;
1772 item->bbox.right = temp;
1774 if (item->bbox.top > item->bbox.bottom) {
1775 temp = item->bbox.top;
1776 item->bbox.top = item->bbox.bottom;
1777 item->bbox.bottom = temp;
1780 if (item->type == ITEM_TEXT) {
1781 /* must scale about NW corner -- all other points of the text box
1782 are font- and zoom-dependent, so scaling about center of text box
1783 couldn't be undone properly. FIXME? */
1784 item->font_size *= mean_scaling;
1785 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1786 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1789 if (item->canvas_item!=NULL) {
1790 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1791 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1792 make_canvas_item_one(group, item);
1797 // Switch between button mappings
1799 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1800 click event is being processed ... or if ui.button_switch_mapping is
1801 enabled and mappings are switched! */
1803 void switch_mapping(int m)
1805 if (ui.cur_mapping == m) return;
1808 if (ui.toolno[m] < NUM_STROKE_TOOLS)
1809 ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1810 if (ui.toolno[m] == TOOL_TEXT)
1811 ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1812 update_tool_buttons();
1813 update_color_menu();
1817 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1819 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1820 if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1821 if (ui.toolno[m] == tool) return;
1826 ui.toolno[m] = tool;
1827 if (ui.linked_brush[m] == BRUSH_COPIED) {
1828 ui.linked_brush[m] = BRUSH_STATIC;
1829 update_mappings_menu_linkings();
1833 // update the ordering of components in the main vbox
1835 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1836 {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1838 void update_vbox_order(int *order)
1842 GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1843 gboolean present[VBOX_MAIN_NITEMS];
1845 for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1847 for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1848 if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1849 present[order[i]] = TRUE;
1850 child = GET_COMPONENT(vbox_component_names[order[i]]);
1851 gtk_box_reorder_child(vboxMain, child, j++);
1852 gtk_widget_show(child);
1854 for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1855 if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1858 gchar *make_cur_font_name(void)
1863 if (ui.cur_item_type == ITEM_TEXT)
1864 str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1865 else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1866 ui.selection->items->next==NULL &&
1867 (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1868 str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1870 str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1874 void update_font_button(void)
1878 str = make_cur_font_name();
1879 gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1883 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1885 return GTK_WIDGET_SENSITIVE(widget);
1888 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1890 if (ui.cur_item_type == ITEM_TEXT) {
1891 g_signal_stop_emission_by_name(widget, "can-activate-accel");
1894 return GTK_WIDGET_SENSITIVE(widget);
1897 void allow_all_accels(void)
1899 g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1900 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1901 g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1902 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1903 g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1904 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1905 g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1906 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1907 g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1908 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1909 g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1910 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1911 g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1912 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1913 g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1914 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1915 g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1916 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1917 g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1918 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1919 g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1920 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1921 g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1922 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1923 g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1924 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1925 g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1926 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1927 g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1928 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1929 g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1930 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1931 g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1932 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1933 g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1934 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1935 g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1936 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1937 g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1938 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1939 g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1940 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1941 g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1942 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1943 g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1944 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1945 g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1946 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1947 g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1948 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1949 /* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1950 "can-activate-accel", G_CALLBACK(can_accel), NULL); */
1951 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1952 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1953 g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1954 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1955 g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1956 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1957 g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1958 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1959 g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1960 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1961 g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
1962 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1965 void add_scroll_bindings(void)
1967 GtkBindingSet *binding_set;
1969 binding_set = gtk_binding_set_by_class(
1970 G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
1971 gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
1972 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1973 G_TYPE_BOOLEAN, FALSE);
1974 gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
1975 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1976 G_TYPE_BOOLEAN, FALSE);
1977 gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
1978 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1979 G_TYPE_BOOLEAN, FALSE);
1980 gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
1981 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1982 G_TYPE_BOOLEAN, FALSE);
1983 gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
1984 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1985 G_TYPE_BOOLEAN, TRUE);
1986 gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
1987 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
1988 G_TYPE_BOOLEAN, TRUE);
1989 gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
1990 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1991 G_TYPE_BOOLEAN, TRUE);
1992 gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
1993 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
1994 G_TYPE_BOOLEAN, TRUE);
1997 gboolean is_event_within_textview(GdkEventButton *event)
2001 if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2002 get_pointer_coords((GdkEvent *)event, pt);
2003 if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2004 if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2008 void hide_unimplemented(void)
2010 gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2011 gtk_widget_hide(GET_COMPONENT("journalFlatten"));
2012 gtk_widget_hide(GET_COMPONENT("papercolorOther"));
2013 gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2014 gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2015 gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2016 gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2017 gtk_widget_hide(GET_COMPONENT("helpIndex"));
2019 /* config file only works with glib 2.6 and beyond */
2020 if (glib_minor_version<6) {
2021 gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2022 gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2024 /* gtkprint only works with gtk+ 2.10 and beyond */
2025 if (gtk_check_version(2, 10, 0)) {
2026 gtk_widget_hide(GET_COMPONENT("filePrint"));
2030 // toggle fullscreen mode
2031 void do_fullscreen(gboolean active)
2035 ui.fullscreen = active;
2036 gtk_check_menu_item_set_active(
2037 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2038 gtk_toggle_tool_button_set_active(
2039 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2041 if (ui.fullscreen) gtk_window_fullscreen(GTK_WINDOW(winMain));
2042 else gtk_window_unfullscreen(GTK_WINDOW(winMain));
2044 update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2047 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2048 elements receive XInput events that they can't handle properly */
2050 // prevent interface items from getting bogus XInput events
2052 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2055 if (event->type == GDK_MOTION_NOTIFY &&
2056 event->motion.device != gdk_device_get_core_pointer())
2058 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2059 event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2060 event->button.device != gdk_device_get_core_pointer())
2065 /* Code to turn an extended input event into a core event and send it to
2066 a different GdkWindow -- e.g. could be used when a click in a text edit box
2067 gets sent to the canvas instead due to incorrect event translation.
2068 We now turn off xinput altogether while editing text under GTK+ 2.17, so
2069 this isn't needed any more... but could become useful again someday!
2073 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2079 if (user_data) window = (GdkWindow *)user_data;
2080 else window = widget->window;
2082 if (event->type == GDK_MOTION_NOTIFY &&
2083 event->motion.device != gdk_device_get_core_pointer()) {
2084 // printf("fixing motion\n");
2085 gdk_window_get_pointer(window, &ix, &iy, NULL);
2086 event->motion.x = ix; event->motion.y = iy;
2087 event->motion.device = gdk_device_get_core_pointer();
2088 g_object_unref(event->motion.window);
2089 event->motion.window = g_object_ref(window);
2090 gtk_widget_event(widget, event);
2093 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2094 event->button.device != gdk_device_get_core_pointer()) {
2095 // printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2096 gdk_window_get_pointer(window, &ix, &iy, NULL);
2097 event->button.x = ix; event->button.y = iy;
2098 event->button.device = gdk_device_get_core_pointer();
2099 g_object_unref(event->button.window);
2100 event->button.window = g_object_ref(window);
2101 // printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2102 gtk_widget_event(widget, event);
2109 // disable xinput when layer combo box is popped up, to avoid GTK+ 2.17 crash
2111 gboolean combobox_popup_disable_xinput (GtkWidget *widget, GdkEvent *event,
2116 g_object_get(G_OBJECT(widget), "popup-shown", &is_shown, NULL);
2117 gdk_input_set_extension_events(GTK_WIDGET(canvas)->window,
2118 GDK_POINTER_MOTION_MASK | GDK_BUTTON_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK,
2119 (ui.use_xinput && !is_shown)?GDK_EXTENSION_EVENTS_ALL:GDK_EXTENSION_EVENTS_NONE);