2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public
4 * License as published by the Free Software Foundation; either
5 * version 2 of the License, or (at your option) any later version.
7 * This software is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
10 * General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <libgnomecanvas/libgnomecanvas.h>
24 #include <gdk/gdkkeysyms.h>
27 #include "xo-interface.h"
28 #include "xo-support.h"
29 #include "xo-callbacks.h"
33 #include "xo-shapes.h"
35 // some global constants
37 guint predef_colors_rgba[COLOR_MAX] =
38 { 0x000000ff, 0x3333ccff, 0xff0000ff, 0x008000ff,
39 0x808080ff, 0x00c0ffff, 0x00ff00ff, 0xff00ffff,
40 0xff8000ff, 0xffff00ff, 0xffffffff };
42 guint predef_bgcolors_rgba[COLOR_MAX] = // meaningless ones set to white
43 { 0xffffffff, 0xa0e8ffff, 0xffc0d4ff, 0x80ffc0ff,
44 0xffffffff, 0xa0e8ffff, 0x80ffc0ff, 0xffc0d4ff,
45 0xffc080ff, 0xffff80ff, 0xffffffff };
47 double predef_thickness[NUM_STROKE_TOOLS][THICKNESS_MAX] =
48 { { 0.42, 0.85, 1.41, 2.26, 5.67 }, // pen thicknesses = 0.15, 0.3, 0.5, 0.8, 2 mm
49 { 2.83, 2.83, 8.50, 19.84, 19.84 }, // eraser thicknesses = 1, 3, 7 mm
50 { 2.83, 2.83, 8.50, 19.84, 19.84 }, // highlighter thicknesses = 1, 3, 7 mm
53 // some manipulation functions
55 struct Page *new_page(struct Page *template)
57 struct Page *pg = (struct Page *) g_memdup(template, sizeof(struct Page));
58 struct Layer *l = g_new(struct Layer, 1);
62 pg->layers = g_list_append(NULL, l);
64 pg->bg = (struct Background *)g_memdup(template->bg, sizeof(struct Background));
65 pg->bg->canvas_item = NULL;
66 if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
67 gdk_pixbuf_ref(pg->bg->pixbuf);
68 refstring_ref(pg->bg->filename);
70 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
71 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
72 make_page_clipbox(pg);
74 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
75 pg->group, gnome_canvas_group_get_type(), NULL);
80 /* Create a page from a background.
81 Note: bg should be an UNREFERENCED background.
82 If needed, first duplicate it and increase the refcount of the pixbuf.
84 struct Page *new_page_with_bg(struct Background *bg, double width, double height)
86 struct Page *pg = g_new(struct Page, 1);
87 struct Layer *l = g_new(struct Layer, 1);
91 pg->layers = g_list_append(NULL, l);
94 pg->bg->canvas_item = NULL;
97 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
98 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
99 make_page_clipbox(pg);
100 update_canvas_bg(pg);
101 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
102 pg->group, gnome_canvas_group_get_type(), NULL);
107 void realloc_cur_path(int n)
109 if (n <= ui.cur_path_storage_alloc) return;
110 ui.cur_path_storage_alloc = n+100;
111 ui.cur_path.coords = g_realloc(ui.cur_path.coords, 2*(n+100)*sizeof(double));
114 void realloc_cur_widths(int n)
116 if (n <= ui.cur_widths_storage_alloc) return;
117 ui.cur_widths_storage_alloc = n+100;
118 ui.cur_widths = g_realloc(ui.cur_widths, (n+100)*sizeof(double));
121 // undo utility functions
123 void prepare_new_undo(void)
126 // add a new UndoItem on the stack
127 u = (struct UndoItem *)g_malloc(sizeof(struct UndoItem));
135 void clear_redo_stack(void)
139 struct UndoErasureData *erasure;
142 /* Warning: the redo items might reference items from past redo entries,
143 which have been destroyed before them. Be careful! As a rule, it's
144 safe to destroy data which has been created at the current history step,
145 it's unsafe to refer to any data from previous history steps */
148 if (redo->type == ITEM_STROKE) {
149 gnome_canvas_points_free(redo->item->path);
150 if (redo->item->brush.variable_width) g_free(redo->item->widths);
152 /* the strokes are unmapped, so there are no associated canvas items */
154 else if (redo->type == ITEM_TEXT) {
155 g_free(redo->item->text);
156 g_free(redo->item->font_name);
159 else if (redo->type == ITEM_ERASURE || redo->type == ITEM_RECOGNIZER) {
160 for (list = redo->erasurelist; list!=NULL; list=list->next) {
161 erasure = (struct UndoErasureData *)list->data;
162 for (repl = erasure->replacement_items; repl!=NULL; repl=repl->next) {
163 it = (struct Item *)repl->data;
164 gnome_canvas_points_free(it->path);
165 if (it->brush.variable_width) g_free(it->widths);
168 g_list_free(erasure->replacement_items);
171 g_list_free(redo->erasurelist);
173 else if (redo->type == ITEM_NEW_BG_ONE || redo->type == ITEM_NEW_BG_RESIZE
174 || redo->type == ITEM_NEW_DEFAULT_BG) {
175 if (redo->bg->type == BG_PIXMAP || redo->bg->type == BG_PDF) {
176 if (redo->bg->pixbuf!=NULL) gdk_pixbuf_unref(redo->bg->pixbuf);
177 refstring_unref(redo->bg->filename);
181 else if (redo->type == ITEM_NEW_PAGE) {
182 redo->page->group = NULL;
183 delete_page(redo->page);
185 else if (redo->type == ITEM_MOVESEL || redo->type == ITEM_REPAINTSEL) {
186 g_list_free(redo->itemlist); g_list_free(redo->auxlist);
188 else if (redo->type == ITEM_RESIZESEL) {
189 g_list_free(redo->itemlist);
191 else if (redo->type == ITEM_PASTE) {
192 for (list = redo->itemlist; list!=NULL; list=list->next) {
193 it = (struct Item *)list->data;
194 if (it->type == ITEM_STROKE) {
195 gnome_canvas_points_free(it->path);
196 if (it->brush.variable_width) g_free(it->widths);
200 g_list_free(redo->itemlist);
202 else if (redo->type == ITEM_NEW_LAYER) {
205 else if (redo->type == ITEM_TEXT_EDIT || redo->type == ITEM_TEXT_ATTRIB) {
207 if (redo->type == ITEM_TEXT_ATTRIB) g_free(redo->brush);
214 update_undo_redo_enabled();
217 void clear_undo_stack(void)
221 struct UndoErasureData *erasure;
224 // for strokes, items are already in the journal, so we don't free them
225 // for erasures, we need to free the dead items
226 if (undo->type == ITEM_ERASURE || undo->type == ITEM_RECOGNIZER) {
227 for (list = undo->erasurelist; list!=NULL; list=list->next) {
228 erasure = (struct UndoErasureData *)list->data;
229 if (erasure->item->type == ITEM_STROKE) {
230 gnome_canvas_points_free(erasure->item->path);
231 if (erasure->item->brush.variable_width) g_free(erasure->item->widths);
233 if (erasure->item->type == ITEM_TEXT)
234 { g_free(erasure->item->text); g_free(erasure->item->font_name); }
235 g_free(erasure->item);
236 g_list_free(erasure->replacement_items);
239 g_list_free(undo->erasurelist);
241 else if (undo->type == ITEM_NEW_BG_ONE || undo->type == ITEM_NEW_BG_RESIZE
242 || undo->type == ITEM_NEW_DEFAULT_BG) {
243 if (undo->bg->type == BG_PIXMAP || undo->bg->type == BG_PDF) {
244 if (undo->bg->pixbuf!=NULL) gdk_pixbuf_unref(undo->bg->pixbuf);
245 refstring_unref(undo->bg->filename);
249 else if (undo->type == ITEM_MOVESEL || undo->type == ITEM_REPAINTSEL) {
250 g_list_free(undo->itemlist); g_list_free(undo->auxlist);
252 else if (undo->type == ITEM_RESIZESEL) {
253 g_list_free(undo->itemlist);
255 else if (undo->type == ITEM_PASTE) {
256 g_list_free(undo->itemlist);
258 else if (undo->type == ITEM_DELETE_LAYER) {
259 undo->layer->group = NULL;
260 delete_layer(undo->layer);
262 else if (undo->type == ITEM_DELETE_PAGE) {
263 undo->page->group = NULL;
264 delete_page(undo->page);
266 else if (undo->type == ITEM_TEXT_EDIT || undo->type == ITEM_TEXT_ATTRIB) {
268 if (undo->type == ITEM_TEXT_ATTRIB) g_free(undo->brush);
275 update_undo_redo_enabled();
278 // free data structures
280 void delete_journal(struct Journal *j)
282 while (j->pages!=NULL) {
283 delete_page((struct Page *)j->pages->data);
284 j->pages = g_list_delete_link(j->pages, j->pages);
288 void delete_page(struct Page *pg)
292 while (pg->layers!=NULL) {
293 l = (struct Layer *)pg->layers->data;
296 pg->layers = g_list_delete_link(pg->layers, pg->layers);
298 if (pg->group!=NULL) gtk_object_destroy(GTK_OBJECT(pg->group));
299 // this also destroys the background's canvas items
300 if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
301 if (pg->bg->pixbuf != NULL) gdk_pixbuf_unref(pg->bg->pixbuf);
302 if (pg->bg->filename != NULL) refstring_unref(pg->bg->filename);
308 void delete_layer(struct Layer *l)
312 while (l->items!=NULL) {
313 item = (struct Item *)l->items->data;
314 if (item->type == ITEM_STROKE && item->path != NULL)
315 gnome_canvas_points_free(item->path);
316 if (item->type == ITEM_TEXT) {
317 g_free(item->font_name); g_free(item->text);
319 // don't need to delete the canvas_item, as it's part of the group destroyed below
321 l->items = g_list_delete_link(l->items, l->items);
323 if (l->group!= NULL) gtk_object_destroy(GTK_OBJECT(l->group));
327 // referenced strings
329 struct Refstring *new_refstring(const char *s)
331 struct Refstring *rs = g_new(struct Refstring, 1);
333 if (s!=NULL) rs->s = g_strdup(s);
339 struct Refstring *refstring_ref(struct Refstring *rs)
345 void refstring_unref(struct Refstring *rs)
349 if (rs->s!=NULL) g_free(rs->s);
350 if (rs->aux!=NULL) g_free(rs->aux);
356 // some helper functions
358 int finite_sized(double x) // detect unrealistic coordinate values
360 return (finite(x) && x<1E6 && x>-1E6);
364 void get_pointer_coords(GdkEvent *event, gdouble *ret)
367 gdk_event_get_coords(event, &x, &y);
368 gnome_canvas_window_to_world(canvas, x, y, ret, ret+1);
369 ret[0] -= ui.cur_page->hoffset;
370 ret[1] -= ui.cur_page->voffset;
373 void fix_xinput_coords(GdkEvent *event)
375 double *axes, *px, *py, axis_width;
377 int wx, wy, sx, sy, ix, iy;
379 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
380 axes = event->button.axes;
381 px = &(event->button.x);
382 py = &(event->button.y);
383 device = event->button.device;
385 else if (event->type == GDK_MOTION_NOTIFY) {
386 axes = event->motion.axes;
387 px = &(event->motion.x);
388 py = &(event->motion.y);
389 device = event->motion.device;
391 else return; // nothing we know how to do
393 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
395 #ifdef ENABLE_XINPUT_BUGFIX
396 // fix broken events with the core pointer's location
397 if (!finite_sized(axes[0]) || !finite_sized(axes[1]) || axes[0]==0. || axes[1]==0.) {
398 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
403 gdk_window_get_origin(GTK_WIDGET(canvas)->window, &wx, &wy);
404 axis_width = device->axes[0].max - device->axes[0].min;
405 if (axis_width>EPSILON)
406 *px = (axes[0]/axis_width)*ui.screen_width + sx - wx;
407 axis_width = device->axes[1].max - device->axes[1].min;
408 if (axis_width>EPSILON)
409 *py = (axes[1]/axis_width)*ui.screen_height + sy - wy;
412 if (!finite_sized(*px) || !finite_sized(*py) || *px==0. || *py==0.) {
413 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
418 /* with GTK+ 2.16 or earlier, the event comes from the parent gdkwindow
419 and so needs to be adjusted for scrolling */
420 if (gtk_major_version == 2 && gtk_minor_version <= 16) {
424 /* with GTK+ 2.17, events come improperly translated, and the event's
425 GdkWindow isn't even the same for ButtonDown as for MotionNotify... */
426 if (gtk_major_version == 2 && gtk_minor_version == 17) { // GTK+ 2.17 issues !!
427 gdk_window_get_position(GTK_WIDGET(canvas)->window, &wx, &wy);
435 double get_pressure_multiplier(GdkEvent *event)
441 if (event->type == GDK_MOTION_NOTIFY) {
442 axes = event->motion.axes;
443 device = event->motion.device;
446 axes = event->button.axes;
447 device = event->button.device;
450 if (device == gdk_device_get_core_pointer()
451 || device->num_axes <= 2) return 1.0;
453 rawpressure = axes[2]/(device->axes[2].max - device->axes[2].min);
454 if (!finite_sized(rawpressure)) return 1.0;
456 return ((1-rawpressure)*ui.width_minimum_multiplier + rawpressure*ui.width_maximum_multiplier);
459 void update_item_bbox(struct Item *item)
464 if (item->type == ITEM_STROKE) {
465 item->bbox.left = item->bbox.right = item->path->coords[0];
466 item->bbox.top = item->bbox.bottom = item->path->coords[1];
467 for (i=1, p=item->path->coords+2; i<item->path->num_points; i++, p+=2)
469 if (p[0] < item->bbox.left) item->bbox.left = p[0];
470 if (p[0] > item->bbox.right) item->bbox.right = p[0];
471 if (p[1] < item->bbox.top) item->bbox.top = p[1];
472 if (p[1] > item->bbox.bottom) item->bbox.bottom = p[1];
475 if (item->type == ITEM_TEXT && item->canvas_item!=NULL) {
477 g_object_get(item->canvas_item, "text_width", &w, "text_height", &h, NULL);
478 item->bbox.right = item->bbox.left + w;
479 item->bbox.bottom = item->bbox.top + h;
483 void make_page_clipbox(struct Page *pg)
485 GnomeCanvasPathDef *pg_clip;
487 pg_clip = gnome_canvas_path_def_new_sized(4);
488 gnome_canvas_path_def_moveto(pg_clip, 0., 0.);
489 gnome_canvas_path_def_lineto(pg_clip, 0., pg->height);
490 gnome_canvas_path_def_lineto(pg_clip, pg->width, pg->height);
491 gnome_canvas_path_def_lineto(pg_clip, pg->width, 0.);
492 gnome_canvas_path_def_closepath(pg_clip);
493 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), "path", pg_clip, NULL);
494 gnome_canvas_path_def_unref(pg_clip);
497 void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
499 PangoFontDescription *font_desc;
500 GnomeCanvasPoints points;
503 if (item->type == ITEM_STROKE) {
504 if (!item->brush.variable_width)
505 item->canvas_item = gnome_canvas_item_new(group,
506 gnome_canvas_line_get_type(), "points", item->path,
507 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
508 "fill-color-rgba", item->brush.color_rgba,
509 "width-units", item->brush.thickness, NULL);
511 item->canvas_item = gnome_canvas_item_new(group,
512 gnome_canvas_group_get_type(), NULL);
513 points.num_points = 2;
514 points.ref_count = 1;
515 for (j = 0; j < item->path->num_points-1; j++) {
516 points.coords = item->path->coords+2*j;
517 gnome_canvas_item_new((GnomeCanvasGroup *) item->canvas_item,
518 gnome_canvas_line_get_type(), "points", &points,
519 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
520 "fill-color-rgba", item->brush.color_rgba,
521 "width-units", item->widths[j], NULL);
525 if (item->type == ITEM_TEXT) {
526 font_desc = pango_font_description_from_string(item->font_name);
527 pango_font_description_set_absolute_size(font_desc,
528 item->font_size*ui.zoom*PANGO_SCALE);
529 item->canvas_item = gnome_canvas_item_new(group,
530 gnome_canvas_text_get_type(),
531 "x", item->bbox.left, "y", item->bbox.top, "anchor", GTK_ANCHOR_NW,
532 "font-desc", font_desc, "fill-color-rgba", item->brush.color_rgba,
533 "text", item->text, NULL);
534 update_item_bbox(item);
538 void make_canvas_items(void)
543 GList *pagelist, *layerlist, *itemlist;
545 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next) {
546 pg = (struct Page *)pagelist->data;
547 if (pg->group == NULL) {
548 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
549 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
550 make_page_clipbox(pg);
552 if (pg->bg->canvas_item == NULL) update_canvas_bg(pg);
553 for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
554 l = (struct Layer *)layerlist->data;
555 if (l->group == NULL)
556 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
557 pg->group, gnome_canvas_group_get_type(), NULL);
558 for (itemlist = l->items; itemlist!=NULL; itemlist = itemlist->next) {
559 item = (struct Item *)itemlist->data;
560 if (item->canvas_item == NULL)
561 make_canvas_item_one(l->group, item);
567 void update_canvas_bg(struct Page *pg)
569 GnomeCanvasGroup *group;
570 GnomeCanvasPoints *seg;
571 GdkPixbuf *scaled_pix;
575 gboolean is_well_scaled;
577 if (pg->bg->canvas_item != NULL)
578 gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
579 pg->bg->canvas_item = NULL;
581 if (pg->bg->type == BG_SOLID)
583 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
584 gnome_canvas_group_get_type(), NULL);
585 group = GNOME_CANVAS_GROUP(pg->bg->canvas_item);
586 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
587 gnome_canvas_item_new(group, gnome_canvas_rect_get_type(),
588 "x1", 0., "x2", pg->width, "y1", 0., "y2", pg->height,
589 "fill-color-rgba", pg->bg->color_rgba, NULL);
590 if (pg->bg->ruling == RULING_NONE) return;
591 seg = gnome_canvas_points_new(2);
593 if (pg->bg->ruling == RULING_GRAPH) {
594 pt[1] = 0; pt[3] = pg->height;
595 for (x=RULING_GRAPHSPACING; x<pg->width-1; x+=RULING_GRAPHSPACING) {
597 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
598 "points", seg, "fill-color-rgba", RULING_COLOR,
599 "width-units", RULING_THICKNESS, NULL);
601 pt[0] = 0; pt[2] = pg->width;
602 for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
604 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
605 "points", seg, "fill-color-rgba", RULING_COLOR,
606 "width-units", RULING_THICKNESS, NULL);
608 gnome_canvas_points_free(seg);
611 pt[0] = 0; pt[2] = pg->width;
612 for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
614 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
615 "points", seg, "fill-color-rgba", RULING_COLOR,
616 "width-units", RULING_THICKNESS, NULL);
618 if (pg->bg->ruling == RULING_LINED) {
619 pt[0] = pt[2] = RULING_LEFTMARGIN;
620 pt[1] = 0; pt[3] = pg->height;
621 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
622 "points", seg, "fill-color-rgba", RULING_MARGIN_COLOR,
623 "width-units", RULING_THICKNESS, NULL);
625 gnome_canvas_points_free(seg);
629 if (pg->bg->type == BG_PIXMAP)
631 pg->bg->pixbuf_scale = 0;
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);
641 if (pg->bg->type == BG_PDF)
643 if (pg->bg->pixbuf == NULL) return;
644 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
645 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
647 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
648 gnome_canvas_pixbuf_get_type(),
649 "pixbuf", pg->bg->pixbuf,
650 "width-in-pixels", TRUE, "height-in-pixels", TRUE,
653 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
654 gnome_canvas_pixbuf_get_type(),
655 "pixbuf", pg->bg->pixbuf,
656 "width", pg->width, "height", pg->height,
657 "width-set", TRUE, "height-set", TRUE,
659 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
663 gboolean is_visible(struct Page *pg)
665 GtkAdjustment *v_adj;
668 if (!ui.view_continuous) return (pg == ui.cur_page);
669 v_adj = gtk_layout_get_vadjustment(GTK_LAYOUT(canvas));
670 ytop = v_adj->value/ui.zoom;
671 ybot = (v_adj->value + v_adj->page_size) / ui.zoom;
672 return (MAX(ytop, pg->voffset) < MIN(ybot, pg->voffset+pg->height));
675 void rescale_bg_pixmaps(void)
680 gboolean is_well_scaled;
681 gdouble zoom_to_request;
683 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
684 pg = (struct Page *)pglist->data;
685 // in progressive mode we scale only visible pages
686 if (ui.progressive_bg && !is_visible(pg)) continue;
688 if (pg->bg->type == BG_PIXMAP && pg->bg->canvas_item!=NULL) {
689 g_object_get(G_OBJECT(pg->bg->canvas_item), "pixbuf", &pix, NULL);
690 if (pix!=pg->bg->pixbuf)
691 gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pg->bg->pixbuf, NULL);
692 pg->bg->pixbuf_scale = 0;
694 if (pg->bg->type == BG_PDF) {
695 // make pixmap scale to correct size if current one is wrong
696 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
697 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
698 if (pg->bg->canvas_item != NULL && !is_well_scaled) {
699 g_object_get(pg->bg->canvas_item, "width-in-pixels", &is_well_scaled, NULL);
701 gnome_canvas_item_set(pg->bg->canvas_item,
702 "width", pg->width, "height", pg->height,
703 "width-in-pixels", FALSE, "height-in-pixels", FALSE,
704 "width-set", TRUE, "height-set", TRUE,
707 // request an asynchronous update to a better pixmap if needed
708 zoom_to_request = MIN(ui.zoom, MAX_SAFE_RENDER_DPI/72.0);
709 if (pg->bg->pixbuf_scale == zoom_to_request) continue;
710 if (add_bgpdf_request(pg->bg->file_page_seq, zoom_to_request))
711 pg->bg->pixbuf_scale = zoom_to_request;
716 gboolean have_intersect(struct BBox *a, struct BBox *b)
718 return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
719 (MAX(a->left, b->left) <= MIN(a->right, b->right));
722 /* In libgnomecanvas 2.10.0, the lower/raise functions fail to update
723 correctly the end of the group's item list. We try to work around this.
724 DON'T USE gnome_canvas_item_raise/lower directly !! */
726 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
730 i1 = g_list_index(g->item_list, item);
731 if (i1 == -1) return;
733 if (after == NULL) i2 = -1;
734 else i2 = g_list_index(g->item_list, after);
736 if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
737 if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
739 // BUGFIX for libgnomecanvas
740 g->item_list_end = g_list_last(g->item_list);
743 void rgb_to_gdkcolor(guint rgba, GdkColor *color)
746 color->red = ((rgba>>24)&0xff)*0x101;
747 color->green = ((rgba>>16)&0xff)*0x101;
748 color->blue = ((rgba>>8)&0xff)*0x101;
751 guint32 gdkcolor_to_rgba(GdkColor gdkcolor, guint16 alpha)
753 guint32 rgba = ((gdkcolor.red & 0xff00) << 16) |
754 ((gdkcolor.green & 0xff00) << 8) |
755 ((gdkcolor.blue & 0xff00) ) |
756 ((alpha & 0xff00) >> 8);
761 // some interface functions
763 void update_thickness_buttons(void)
765 if (ui.selection!=NULL || ui.toolno[ui.cur_mapping] >= NUM_STROKE_TOOLS) {
766 gtk_toggle_tool_button_set_active(
767 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
769 switch (ui.cur_brush->thickness_no) {
771 gtk_toggle_tool_button_set_active(
772 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
774 case THICKNESS_MEDIUM:
775 gtk_toggle_tool_button_set_active(
776 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
778 case THICKNESS_THICK:
779 gtk_toggle_tool_button_set_active(
780 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
783 gtk_toggle_tool_button_set_active(
784 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
788 void update_color_buttons(void)
791 GtkColorButton *colorbutton;
793 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
794 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
795 gtk_toggle_tool_button_set_active(
796 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
798 switch (ui.cur_brush->color_no) {
800 gtk_toggle_tool_button_set_active(
801 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
804 gtk_toggle_tool_button_set_active(
805 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
808 gtk_toggle_tool_button_set_active(
809 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
812 gtk_toggle_tool_button_set_active(
813 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
816 gtk_toggle_tool_button_set_active(
817 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
819 case COLOR_LIGHTBLUE:
820 gtk_toggle_tool_button_set_active(
821 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
823 case COLOR_LIGHTGREEN:
824 gtk_toggle_tool_button_set_active(
825 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
828 gtk_toggle_tool_button_set_active(
829 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
832 gtk_toggle_tool_button_set_active(
833 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
836 gtk_toggle_tool_button_set_active(
837 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
840 gtk_toggle_tool_button_set_active(
841 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
844 gtk_toggle_tool_button_set_active(
845 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
848 colorbutton = GTK_COLOR_BUTTON(GET_COMPONENT("buttonColorChooser"));
849 if ((ui.toolno[ui.cur_mapping] != TOOL_PEN &&
850 ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER &&
851 ui.toolno[ui.cur_mapping] != TOOL_TEXT))
852 gdkcolor.red = gdkcolor.blue = gdkcolor.green = 0;
853 else rgb_to_gdkcolor(ui.cur_brush->color_rgba, &gdkcolor);
854 gtk_color_button_set_color(colorbutton, &gdkcolor);
855 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
856 gtk_color_button_set_alpha(colorbutton,
857 (ui.cur_brush->color_rgba&0xff)*0x101);
858 gtk_color_button_set_use_alpha(colorbutton, TRUE);
860 gtk_color_button_set_alpha(colorbutton, 0xffff);
861 gtk_color_button_set_use_alpha(colorbutton, FALSE);
865 void update_tool_buttons(void)
867 switch(ui.toolno[ui.cur_mapping]) {
869 gtk_toggle_tool_button_set_active(
870 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
873 gtk_toggle_tool_button_set_active(
874 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
876 case TOOL_HIGHLIGHTER:
877 gtk_toggle_tool_button_set_active(
878 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
881 gtk_toggle_tool_button_set_active(
882 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
884 case TOOL_SELECTREGION:
885 gtk_toggle_tool_button_set_active(
886 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
888 case TOOL_SELECTRECT:
889 gtk_toggle_tool_button_set_active(
890 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
893 gtk_toggle_tool_button_set_active(
894 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
897 gtk_toggle_tool_button_set_active(
898 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
902 gtk_toggle_tool_button_set_active(
903 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
904 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
905 gtk_toggle_tool_button_set_active(
906 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
907 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
909 update_thickness_buttons();
910 update_color_buttons();
913 void update_tool_menu(void)
915 switch(ui.toolno[0]) {
917 gtk_check_menu_item_set_active(
918 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
921 gtk_check_menu_item_set_active(
922 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
924 case TOOL_HIGHLIGHTER:
925 gtk_check_menu_item_set_active(
926 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
929 gtk_check_menu_item_set_active(
930 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
932 case TOOL_SELECTREGION:
933 gtk_check_menu_item_set_active(
934 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
936 case TOOL_SELECTRECT:
937 gtk_check_menu_item_set_active(
938 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
941 gtk_check_menu_item_set_active(
942 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
945 gtk_check_menu_item_set_active(
946 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
950 gtk_check_menu_item_set_active(
951 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
952 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
953 gtk_check_menu_item_set_active(
954 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
955 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
958 void update_ruler_indicator(void)
960 gtk_toggle_tool_button_set_active(
961 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
962 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
963 gtk_toggle_tool_button_set_active(
964 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
965 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
966 gtk_check_menu_item_set_active(
967 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
968 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
969 gtk_check_menu_item_set_active(
970 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
971 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
974 void update_color_menu(void)
976 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
977 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
978 gtk_check_menu_item_set_active(
979 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
981 switch (ui.cur_brush->color_no) {
983 gtk_check_menu_item_set_active(
984 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
987 gtk_check_menu_item_set_active(
988 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
991 gtk_check_menu_item_set_active(
992 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
995 gtk_check_menu_item_set_active(
996 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
999 gtk_check_menu_item_set_active(
1000 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
1002 case COLOR_LIGHTBLUE:
1003 gtk_check_menu_item_set_active(
1004 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
1006 case COLOR_LIGHTGREEN:
1007 gtk_check_menu_item_set_active(
1008 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
1011 gtk_check_menu_item_set_active(
1012 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
1015 gtk_check_menu_item_set_active(
1016 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
1019 gtk_check_menu_item_set_active(
1020 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
1023 gtk_check_menu_item_set_active(
1024 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
1027 gtk_check_menu_item_set_active(
1028 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
1032 void update_pen_props_menu(void)
1034 switch(ui.brushes[0][TOOL_PEN].thickness_no) {
1035 case THICKNESS_VERYFINE:
1036 gtk_check_menu_item_set_active(
1037 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
1039 case THICKNESS_FINE:
1040 gtk_check_menu_item_set_active(
1041 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
1043 case THICKNESS_MEDIUM:
1044 gtk_check_menu_item_set_active(
1045 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
1047 case THICKNESS_THICK:
1048 gtk_check_menu_item_set_active(
1049 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
1051 case THICKNESS_VERYTHICK:
1052 gtk_check_menu_item_set_active(
1053 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
1058 void update_eraser_props_menu(void)
1060 switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
1061 case THICKNESS_FINE:
1062 gtk_check_menu_item_set_active(
1063 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
1065 case THICKNESS_MEDIUM:
1066 gtk_check_menu_item_set_active(
1067 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
1069 case THICKNESS_THICK:
1070 gtk_check_menu_item_set_active(
1071 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
1075 gtk_check_menu_item_set_active(
1076 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
1077 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
1078 gtk_check_menu_item_set_active(
1079 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
1080 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
1081 gtk_check_menu_item_set_active(
1082 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
1083 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
1086 void update_highlighter_props_menu(void)
1088 switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
1089 case THICKNESS_FINE:
1090 gtk_check_menu_item_set_active(
1091 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
1093 case THICKNESS_MEDIUM:
1094 gtk_check_menu_item_set_active(
1095 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
1097 case THICKNESS_THICK:
1098 gtk_check_menu_item_set_active(
1099 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
1104 void update_mappings_menu_linkings(void)
1106 switch (ui.linked_brush[1]) {
1108 gtk_check_menu_item_set_active(
1109 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
1112 gtk_check_menu_item_set_active(
1113 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
1116 gtk_check_menu_item_set_active(
1117 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
1120 switch (ui.linked_brush[2]) {
1122 gtk_check_menu_item_set_active(
1123 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
1126 gtk_check_menu_item_set_active(
1127 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
1130 gtk_check_menu_item_set_active(
1131 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
1136 void update_mappings_menu(void)
1138 gtk_widget_set_sensitive(GET_COMPONENT("optionsButtonMappings"), ui.use_xinput);
1139 gtk_widget_set_sensitive(GET_COMPONENT("optionsPressureSensitive"), ui.use_xinput);
1140 gtk_check_menu_item_set_active(
1141 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
1142 gtk_check_menu_item_set_active(
1143 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsPressureSensitive")), ui.pressure_sensitivity);
1145 switch(ui.toolno[1]) {
1147 gtk_check_menu_item_set_active(
1148 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
1151 gtk_check_menu_item_set_active(
1152 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
1154 case TOOL_HIGHLIGHTER:
1155 gtk_check_menu_item_set_active(
1156 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
1159 gtk_check_menu_item_set_active(
1160 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
1162 case TOOL_SELECTREGION:
1163 gtk_check_menu_item_set_active(
1164 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
1166 case TOOL_SELECTRECT:
1167 gtk_check_menu_item_set_active(
1168 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
1170 case TOOL_VERTSPACE:
1171 gtk_check_menu_item_set_active(
1172 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
1175 switch(ui.toolno[2]) {
1177 gtk_check_menu_item_set_active(
1178 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
1181 gtk_check_menu_item_set_active(
1182 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
1184 case TOOL_HIGHLIGHTER:
1185 gtk_check_menu_item_set_active(
1186 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
1189 gtk_check_menu_item_set_active(
1190 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
1192 case TOOL_SELECTREGION:
1193 gtk_check_menu_item_set_active(
1194 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
1196 case TOOL_SELECTRECT:
1197 gtk_check_menu_item_set_active(
1198 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
1200 case TOOL_VERTSPACE:
1201 gtk_check_menu_item_set_active(
1202 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1205 update_mappings_menu_linkings();
1208 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1211 struct Layer *layer;
1216 /* re-show all the layers of the old page */
1217 if (ui.cur_page != NULL)
1218 for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1219 layer = (struct Layer *)list->data;
1220 if (layer->group!=NULL)
1221 gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1224 ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1225 ui.layerno = ui.cur_page->nlayers-1;
1226 ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1227 update_page_stuff();
1228 if (ui.progressive_bg) rescale_bg_pixmaps();
1230 if (rescroll) { // scroll and force a refresh
1231 /* -- this seems to cause some display bugs ??
1232 gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1233 ui.cur_page->voffset*ui.zoom); */
1234 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1235 cy = ui.cur_page->voffset*ui.zoom;
1236 gnome_canvas_scroll_to(canvas, cx, cy);
1239 gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1240 else if (!ui.view_continuous)
1241 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1245 void update_page_stuff(void)
1248 GtkComboBox *layerbox;
1251 GtkSpinButton *spin;
1253 double vertpos, maxwidth;
1255 // move the page groups to their rightful locations or hide them
1256 if (ui.view_continuous) {
1259 for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1260 pg = (struct Page *)pglist->data;
1261 if (pg->group!=NULL) {
1262 pg->hoffset = 0.; pg->voffset = vertpos;
1263 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1264 "x", pg->hoffset, "y", pg->voffset, NULL);
1265 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1267 vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1268 if (pg->width > maxwidth) maxwidth = pg->width;
1270 vertpos -= VIEW_CONTINUOUS_SKIP;
1271 gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1273 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1274 pg = (struct Page *)pglist->data;
1275 if (pg == ui.cur_page && pg->group!=NULL) {
1276 pg->hoffset = 0.; pg->voffset = 0.;
1277 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1278 "x", pg->hoffset, "y", pg->voffset, NULL);
1279 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1281 if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1284 gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1287 // update the page / layer info at bottom of screen
1289 spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1290 ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1291 gtk_spin_button_set_range(spin, 1, journal.npages+1);
1292 /* npages+1 will be used to create a new page at end */
1293 gtk_spin_button_set_value(spin, ui.pageno+1);
1294 g_snprintf(tmp, 10, _(" of %d"), journal.npages);
1295 gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1297 layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1298 if (ui.layerbox_length == 0) {
1299 gtk_combo_box_prepend_text(layerbox, _("Background"));
1300 ui.layerbox_length++;
1302 while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1303 gtk_combo_box_remove_text(layerbox, 0);
1304 ui.layerbox_length--;
1306 while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1307 g_snprintf(tmp, 10, _("Layer %d"), ui.layerbox_length++);
1308 gtk_combo_box_prepend_text(layerbox, tmp);
1310 gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1311 ui.in_update_page_stuff = FALSE;
1313 gtk_container_forall(GTK_CONTAINER(layerbox), unset_flags, (gpointer)GTK_CAN_FOCUS);
1315 // update the paper-style menu radio buttons
1317 if (ui.view_continuous)
1318 gtk_check_menu_item_set_active(
1319 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1321 gtk_check_menu_item_set_active(
1322 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1324 if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1325 switch (ui.cur_page->bg->color_no) {
1327 gtk_check_menu_item_set_active(
1328 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1331 gtk_check_menu_item_set_active(
1332 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1335 gtk_check_menu_item_set_active(
1336 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1339 gtk_check_menu_item_set_active(
1340 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1343 gtk_check_menu_item_set_active(
1344 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1347 gtk_check_menu_item_set_active(
1348 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1351 gtk_check_menu_item_set_active(
1352 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1355 switch (ui.cur_page->bg->ruling) {
1357 gtk_check_menu_item_set_active(
1358 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1361 gtk_check_menu_item_set_active(
1362 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1365 gtk_check_menu_item_set_active(
1366 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1369 gtk_check_menu_item_set_active(
1370 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1374 gtk_check_menu_item_set_active(
1375 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1376 gtk_check_menu_item_set_active(
1377 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1380 // enable/disable the page/layer menu items and toolbar buttons
1382 gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"),
1383 ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1384 gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1385 ui.cur_page->bg->type == BG_SOLID);
1387 gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1388 gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1389 gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1390 gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1391 gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1392 gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1393 gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1394 gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1396 gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1397 gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1399 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1400 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1403 void update_toolbar_and_menu(void)
1405 update_tool_buttons(); // takes care of other toolbar buttons as well
1407 update_color_menu();
1408 update_pen_props_menu();
1409 update_eraser_props_menu();
1410 update_highlighter_props_menu();
1411 update_mappings_menu();
1413 gtk_toggle_tool_button_set_active(
1414 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1415 gtk_check_menu_item_set_active(
1416 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1419 void update_file_name(char *filename)
1422 if (ui.filename != NULL) g_free(ui.filename);
1423 ui.filename = filename;
1424 if (filename == NULL) {
1425 gtk_window_set_title(GTK_WINDOW (winMain), _("Xournal"));
1428 p = g_utf8_strrchr(filename, -1, '/');
1429 if (p == NULL) p = filename;
1430 else p = g_utf8_next_char(p);
1431 g_snprintf(tmp, 100, _("Xournal - %s"), p);
1432 gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1433 new_mru_entry(filename);
1435 if (filename[0]=='/') {
1436 if (ui.default_path!=NULL) g_free(ui.default_path);
1437 ui.default_path = g_path_get_dirname(filename);
1441 void update_undo_redo_enabled(void)
1443 gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1444 gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1445 gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1446 gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1449 void update_copy_paste_enabled(void)
1451 gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1452 gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1453 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_item_type!=ITEM_TEXT);
1454 gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1455 gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1456 gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1457 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_item_type!=ITEM_TEXT);
1460 void update_mapping_linkings(int toolno)
1464 for (i = 1; i<=NUM_BUTTONS; i++) {
1465 if (ui.linked_brush[i] == BRUSH_LINKED) {
1466 if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1467 g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1469 if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1470 ui.linked_brush[i] = BRUSH_STATIC;
1471 if (i==1 || i==2) update_mappings_menu_linkings();
1476 void set_cur_color(int color_no, guint color_rgba)
1478 int which_mapping, tool;
1480 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) tool = TOOL_HIGHLIGHTER;
1481 else tool = TOOL_PEN;
1482 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1483 which_mapping = ui.cur_mapping;
1484 else which_mapping = 0;
1486 ui.brushes[which_mapping][tool].color_no = color_no;
1487 if (tool == TOOL_HIGHLIGHTER && (color_rgba & 0xff) == 0xff)
1488 ui.brushes[which_mapping][tool].color_rgba = color_rgba & ui.hiliter_alpha_mask;
1490 ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1491 update_mapping_linkings(tool);
1494 void recolor_temp_text(int color_no, guint color_rgba)
1498 if (ui.cur_item_type!=ITEM_TEXT) return;
1499 if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1501 undo->type = ITEM_TEXT_ATTRIB;
1502 undo->item = ui.cur_item;
1503 undo->str = g_strdup(ui.cur_item->font_name);
1504 undo->val_x = ui.cur_item->font_size;
1505 undo->brush = (struct Brush *)g_memdup(&(ui.cur_item->brush), sizeof(struct Brush));
1507 ui.cur_item->brush.color_no = color_no;
1508 ui.cur_item->brush.color_rgba = color_rgba;
1509 rgb_to_gdkcolor(color_rgba, &gdkcolor);
1510 gtk_widget_modify_text(ui.cur_item->widget, GTK_STATE_NORMAL, &gdkcolor);
1511 gtk_widget_grab_focus(ui.cur_item->widget);
1514 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1516 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1517 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1520 else if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_TOOL_BUTTON) {
1521 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1525 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1527 if (ui.cur_item_type == ITEM_TEXT)
1528 recolor_temp_text(color_no, color_rgba);
1530 if (ui.selection != NULL) {
1531 recolor_selection(color_no, color_rgba);
1532 update_color_buttons();
1533 update_color_menu();
1536 if (ui.toolno[ui.cur_mapping] != TOOL_PEN && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER
1537 && ui.toolno[ui.cur_mapping] != TOOL_TEXT) {
1538 if (ui.selection != NULL) return;
1541 ui.toolno[ui.cur_mapping] = TOOL_PEN;
1542 ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1543 update_tool_buttons();
1547 set_cur_color(color_no, color_rgba);
1548 update_color_buttons();
1549 update_color_menu();
1553 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1557 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1558 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1561 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1565 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1567 if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1568 rethicken_selection(val);
1569 update_thickness_buttons();
1572 if (tool >= NUM_STROKE_TOOLS) {
1573 update_thickness_buttons(); // undo illegal button selection
1577 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1578 which_mapping = ui.cur_mapping;
1579 else which_mapping = 0;
1580 if (ui.brushes[which_mapping][tool].thickness_no == val) return;
1582 ui.brushes[which_mapping][tool].thickness_no = val;
1583 ui.brushes[which_mapping][tool].thickness = predef_thickness[tool][val];
1584 update_mapping_linkings(tool);
1586 update_thickness_buttons();
1587 if (tool == TOOL_PEN) update_pen_props_menu();
1588 if (tool == TOOL_ERASER) update_eraser_props_menu();
1589 if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1593 void process_papercolor_activate(GtkMenuItem *menuitem, int color, guint rgba)
1599 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1600 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1604 if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages || color == COLOR_OTHER)
1605 gtk_check_menu_item_set_active(
1606 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1610 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1611 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1612 if (pg->bg->type == BG_SOLID && pg->bg->color_rgba != rgba) {
1614 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1615 undo->multiop |= MULTIOP_CONT_REDO;
1617 undo->type = ITEM_NEW_BG_ONE;
1619 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1620 undo->bg->canvas_item = NULL;
1622 pg->bg->color_no = color;
1623 pg->bg->color_rgba = rgba;
1624 update_canvas_bg(pg);
1626 if (!ui.bg_apply_all_pages) break;
1628 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1631 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1635 gboolean hasdone, must_upd;
1637 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1640 if (ui.bg_apply_all_pages)
1641 gtk_check_menu_item_set_active(
1642 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1647 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1648 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1649 if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1651 undo->type = ITEM_NEW_BG_ONE;
1652 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1653 undo->multiop |= MULTIOP_CONT_REDO;
1656 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1657 undo->bg->canvas_item = NULL;
1659 if (pg->bg->type != BG_SOLID) {
1660 pg->bg->type = BG_SOLID;
1661 pg->bg->color_no = COLOR_WHITE;
1662 pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1663 pg->bg->filename = NULL;
1664 pg->bg->pixbuf = NULL;
1667 pg->bg->ruling = style;
1668 update_canvas_bg(pg);
1670 if (!ui.bg_apply_all_pages) break;
1672 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1673 if (must_upd) update_page_stuff();
1676 gboolean ok_to_close(void)
1679 GtkResponseType response;
1681 if (ui.saved) return TRUE;
1682 dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1683 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("Save changes to '%s'?"),
1684 (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1685 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_DISCARD, GTK_RESPONSE_NO);
1686 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
1687 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1688 gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1689 response = gtk_dialog_run(GTK_DIALOG (dialog));
1690 gtk_widget_destroy(dialog);
1691 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1692 return FALSE; // aborted
1693 if (response == GTK_RESPONSE_YES) {
1694 on_fileSave_activate(NULL, NULL);
1695 if (!ui.saved) return FALSE; // if save failed, then we abort
1700 // send the focus back to the appropriate widget
1702 void reset_focus(void)
1704 if (ui.cur_item_type == ITEM_TEXT)
1705 gtk_widget_grab_focus(ui.cur_item->widget);
1707 gtk_widget_grab_focus(GTK_WIDGET(canvas));
1710 // selection / clipboard stuff
1712 void reset_selection(void)
1714 if (ui.selection == NULL) return;
1715 if (ui.selection->canvas_item != NULL)
1716 gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1717 g_list_free(ui.selection->items);
1718 g_free(ui.selection);
1719 ui.selection = NULL;
1720 update_copy_paste_enabled();
1721 update_color_menu();
1722 update_thickness_buttons();
1723 update_color_buttons();
1724 update_font_button();
1728 void move_journal_items_by(GList *itemlist, double dx, double dy,
1729 struct Layer *l1, struct Layer *l2, GList *depths)
1732 GnomeCanvasItem *refitem;
1737 while (itemlist!=NULL) {
1738 item = (struct Item *)itemlist->data;
1739 if (item->type == ITEM_STROKE)
1740 for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1741 { pt[0] += dx; pt[1] += dy; }
1742 if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1743 item->bbox.left += dx;
1744 item->bbox.right += dx;
1745 item->bbox.top += dy;
1746 item->bbox.bottom += dy;
1749 // find out where to insert
1750 if (depths != NULL) {
1751 if (depths->data == NULL) link = l2->items;
1753 link = g_list_find(l2->items, depths->data);
1754 if (link != NULL) link = link->next;
1757 l2->items = g_list_insert_before(l2->items, link, item);
1759 l1->items = g_list_remove(l1->items, item);
1762 if (depths != NULL) { // also raise/lower the canvas items
1763 if (item->canvas_item!=NULL) {
1764 if (depths->data == NULL) link = NULL;
1765 else link = g_list_find(l2->items, depths->data);
1766 if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1767 else refitem = NULL;
1768 lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1770 depths = depths->next;
1772 itemlist = itemlist->next;
1776 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1777 double offset_x, double offset_y)
1781 double mean_scaling, temp;
1783 GnomeCanvasGroup *group;
1786 /* geometric mean of x and y scalings = rescaling for stroke widths
1787 and for text font sizes */
1788 mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1790 for (list = itemlist; list != NULL; list = list->next) {
1791 item = (struct Item *)list->data;
1792 if (item->type == ITEM_STROKE) {
1793 item->brush.thickness = item->brush.thickness * mean_scaling;
1794 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1795 pt[0] = pt[0]*scaling_x + offset_x;
1796 pt[1] = pt[1]*scaling_y + offset_y;
1798 if (item->brush.variable_width)
1799 for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1800 *wid = *wid * mean_scaling;
1802 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1803 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1804 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1805 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1806 if (item->bbox.left > item->bbox.right) {
1807 temp = item->bbox.left;
1808 item->bbox.left = item->bbox.right;
1809 item->bbox.right = temp;
1811 if (item->bbox.top > item->bbox.bottom) {
1812 temp = item->bbox.top;
1813 item->bbox.top = item->bbox.bottom;
1814 item->bbox.bottom = temp;
1817 if (item->type == ITEM_TEXT) {
1818 /* must scale about NW corner -- all other points of the text box
1819 are font- and zoom-dependent, so scaling about center of text box
1820 couldn't be undone properly. FIXME? */
1821 item->font_size *= mean_scaling;
1822 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1823 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1826 if (item->canvas_item!=NULL) {
1827 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1828 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1829 make_canvas_item_one(group, item);
1834 // Switch between button mappings
1836 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1837 click event is being processed ... or if ui.button_switch_mapping is
1838 enabled and mappings are switched (but even then, canvas should have
1839 a pointer grab from the initial click that switched the mapping) */
1841 void switch_mapping(int m)
1843 if (ui.cur_mapping == m) return;
1846 if (ui.toolno[m] < NUM_STROKE_TOOLS)
1847 ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1848 if (ui.toolno[m] == TOOL_TEXT)
1849 ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1850 if (m==0) ui.which_unswitch_button = 0;
1852 update_tool_buttons();
1853 update_color_menu();
1857 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1859 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1860 if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1861 if (ui.toolno[m] == tool) return;
1865 ui.toolno[m] = tool;
1866 if (ui.linked_brush[m] == BRUSH_COPIED) {
1867 ui.linked_brush[m] = BRUSH_STATIC;
1868 update_mappings_menu_linkings();
1872 // update the ordering of components in the main vbox
1874 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1875 {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1877 void update_vbox_order(int *order)
1881 GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1882 gboolean present[VBOX_MAIN_NITEMS];
1884 for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1886 for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1887 if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1888 present[order[i]] = TRUE;
1889 child = GET_COMPONENT(vbox_component_names[order[i]]);
1890 gtk_box_reorder_child(vboxMain, child, j++);
1891 gtk_widget_show(child);
1893 for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1894 if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1897 gchar *make_cur_font_name(void)
1902 if (ui.cur_item_type == ITEM_TEXT)
1903 str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1904 else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1905 ui.selection->items->next==NULL &&
1906 (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1907 str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1909 str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1913 void update_font_button(void)
1917 str = make_cur_font_name();
1918 gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1922 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1924 return GTK_WIDGET_SENSITIVE(widget);
1927 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1929 if (ui.cur_item_type == ITEM_TEXT) {
1930 g_signal_stop_emission_by_name(widget, "can-activate-accel");
1933 return GTK_WIDGET_SENSITIVE(widget);
1936 void allow_all_accels(void)
1938 g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1939 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1940 g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1941 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1942 g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1943 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1944 g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1945 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1946 g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1947 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1948 g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1949 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1950 g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1951 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1952 g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1953 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1954 g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1955 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1956 g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1957 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1958 g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1959 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1960 g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1961 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1962 g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1963 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1964 g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1965 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1966 g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1967 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1968 g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1969 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1970 g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1971 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1972 g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1973 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1974 g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1975 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1976 g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1977 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1978 g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1979 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1980 g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1981 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1982 g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1983 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1984 g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1985 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1986 g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1987 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1988 /* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1989 "can-activate-accel", G_CALLBACK(can_accel), NULL); */
1990 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1991 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1992 g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1993 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1994 g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1995 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1996 g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1997 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1998 g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1999 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2000 g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
2001 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2004 void add_scroll_bindings(void)
2006 GtkBindingSet *binding_set;
2008 binding_set = gtk_binding_set_by_class(
2009 G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
2010 gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
2011 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2012 G_TYPE_BOOLEAN, FALSE);
2013 gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
2014 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2015 G_TYPE_BOOLEAN, FALSE);
2016 gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
2017 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2018 G_TYPE_BOOLEAN, FALSE);
2019 gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
2020 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2021 G_TYPE_BOOLEAN, FALSE);
2022 gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
2023 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2024 G_TYPE_BOOLEAN, TRUE);
2025 gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
2026 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2027 G_TYPE_BOOLEAN, TRUE);
2028 gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
2029 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2030 G_TYPE_BOOLEAN, TRUE);
2031 gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
2032 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2033 G_TYPE_BOOLEAN, TRUE);
2036 gboolean is_event_within_textview(GdkEventButton *event)
2040 if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2041 get_pointer_coords((GdkEvent *)event, pt);
2042 if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2043 if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2047 void hide_unimplemented(void)
2049 gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2050 gtk_widget_hide(GET_COMPONENT("journalFlatten"));
2051 gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2052 gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2053 gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2054 gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2055 gtk_widget_hide(GET_COMPONENT("helpIndex"));
2057 /* config file only works with glib 2.6 and beyond */
2058 if (glib_minor_version<6) {
2059 gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2060 gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2062 /* gtkprint only works with gtk+ 2.10 and beyond */
2063 if (gtk_check_version(2, 10, 0)) {
2064 gtk_widget_hide(GET_COMPONENT("filePrint"));
2067 /* screenshot feature doesn't work yet in Win32 */
2069 gtk_widget_hide(GET_COMPONENT("journalScreenshot"));
2073 // toggle fullscreen mode
2074 void do_fullscreen(gboolean active)
2077 ui.fullscreen = active;
2078 gtk_check_menu_item_set_active(
2079 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2080 gtk_toggle_tool_button_set_active(
2081 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2083 if (ui.fullscreen) {
2085 gtk_window_get_size(GTK_WINDOW(winMain), &ui.pre_fullscreen_width, &ui.pre_fullscreen_height);
2086 gtk_widget_set_size_request(GTK_WIDGET(winMain), gdk_screen_width(),
2087 gdk_screen_height());
2089 gtk_window_fullscreen(GTK_WINDOW(winMain));
2093 gtk_widget_set_size_request(GTK_WIDGET(winMain), -1, -1);
2094 gtk_window_resize(GTK_WINDOW(winMain), ui.pre_fullscreen_width,
2095 ui.pre_fullscreen_height);
2097 gtk_window_unfullscreen(GTK_WINDOW(winMain));
2100 update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2103 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2104 elements receive XInput events that they can't handle properly */
2106 // prevent interface items from getting bogus XInput events
2108 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2111 if (event->type == GDK_MOTION_NOTIFY &&
2112 event->motion.device != gdk_device_get_core_pointer())
2114 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2115 event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2116 event->button.device != gdk_device_get_core_pointer())
2121 /* Code to turn an extended input event into a core event and send it to
2122 a different GdkWindow -- e.g. could be used when a click in a text edit box
2123 gets sent to the canvas instead due to incorrect event translation.
2124 We now turn off xinput altogether while editing text under GTK+ 2.17, so
2125 this isn't needed any more... but could become useful again someday!
2129 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2135 if (user_data) window = (GdkWindow *)user_data;
2136 else window = widget->window;
2138 if (event->type == GDK_MOTION_NOTIFY &&
2139 event->motion.device != gdk_device_get_core_pointer()) {
2140 // printf("fixing motion\n");
2141 gdk_window_get_pointer(window, &ix, &iy, NULL);
2142 event->motion.x = ix; event->motion.y = iy;
2143 event->motion.device = gdk_device_get_core_pointer();
2144 g_object_unref(event->motion.window);
2145 event->motion.window = g_object_ref(window);
2146 gtk_widget_event(widget, event);
2149 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2150 event->button.device != gdk_device_get_core_pointer()) {
2151 // printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2152 gdk_window_get_pointer(window, &ix, &iy, NULL);
2153 event->button.x = ix; event->button.y = iy;
2154 event->button.device = gdk_device_get_core_pointer();
2155 g_object_unref(event->button.window);
2156 event->button.window = g_object_ref(window);
2157 // printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2158 gtk_widget_event(widget, event);
2166 /* When enter is pressed into page spinbox, send focus back to canvas. */
2168 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2174 /* recursively unset widget flags */
2176 void unset_flags(GtkWidget *w, gpointer flag)
2178 GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2179 if(GTK_IS_CONTAINER(w))
2180 gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2183 /* reset focus when a key or button press event reaches someone, or when the
2184 page-number spin button should relinquish control... */
2186 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2188 if (w == GET_COMPONENT("hbox1")) {
2189 /* the event won't be processed since the hbox1 doesn't know what to do with it,
2190 so we might as well kill it and avoid confusing ourselves when it gets
2191 propagated further ... */
2194 if (w == GET_COMPONENT("spinPageNo")) {
2195 /* we let the spin button take care of itself, and don't steal its focus,
2196 unless the user presses Esc or Tab (in those cases we intervene) */
2197 if (ev->type != GDK_KEY_PRESS) return FALSE;
2198 if (ev->key.keyval == GDK_Escape)
2199 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2200 else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2201 return FALSE; // let the spin button process it
2204 // otherwise, we want to make sure the canvas or text item gets focus back...
2209 void install_focus_hooks(GtkWidget *w, gpointer data)
2211 if (w == NULL) return;
2212 g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2213 g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2214 if (GTK_IS_MENU_ITEM(w)) {
2215 g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2216 install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2218 if(GTK_IS_CONTAINER(w))
2219 gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);
2222 // wrapper for missing poppler functions (defunct poppler-gdk api)
2225 wrapper_copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
2228 int cairo_width, cairo_height, cairo_rowstride;
2229 unsigned char *pixbuf_data, *dst, *cairo_data;
2230 int pixbuf_rowstride, pixbuf_n_channels;
2234 cairo_width = cairo_image_surface_get_width (surface);
2235 cairo_height = cairo_image_surface_get_height (surface);
2236 cairo_rowstride = cairo_image_surface_get_stride (surface);
2237 cairo_data = cairo_image_surface_get_data (surface);
2239 pixbuf_data = gdk_pixbuf_get_pixels (pixbuf);
2240 pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
2241 pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
2243 if (cairo_width > gdk_pixbuf_get_width (pixbuf))
2244 cairo_width = gdk_pixbuf_get_width (pixbuf);
2245 if (cairo_height > gdk_pixbuf_get_height (pixbuf))
2246 cairo_height = gdk_pixbuf_get_height (pixbuf);
2247 for (y = 0; y < cairo_height; y++)
2249 src = (unsigned int *) (cairo_data + y * cairo_rowstride);
2250 dst = pixbuf_data + y * pixbuf_rowstride;
2251 for (x = 0; x < cairo_width; x++)
2253 dst[0] = (*src >> 16) & 0xff;
2254 dst[1] = (*src >> 8) & 0xff;
2255 dst[2] = (*src >> 0) & 0xff;
2256 if (pixbuf_n_channels == 4)
2257 dst[3] = (*src >> 24) & 0xff;
2258 dst += pixbuf_n_channels;
2265 wrapper_poppler_page_render_to_pixbuf (PopplerPage *page,
2266 int src_x, int src_y,
2267 int src_width, int src_height,
2273 cairo_surface_t *surface;
2275 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2276 src_width, src_height);
2277 cr = cairo_create (surface);
2281 cairo_translate (cr, src_x + src_width, -src_y);
2284 cairo_translate (cr, src_x + src_width, src_y + src_height);
2287 cairo_translate (cr, -src_x, src_y + src_height);
2290 cairo_translate (cr, -src_x, -src_y);
2294 cairo_scale (cr, scale, scale);
2297 cairo_rotate (cr, rotation * G_PI / 180.0);
2299 poppler_page_render (page, cr);
2302 cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
2303 cairo_set_source_rgb (cr, 1., 1., 1.);
2308 wrapper_copy_cairo_surface_to_pixbuf (surface, pixbuf);
2309 cairo_surface_destroy (surface);