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 #ifndef GTK_STOCK_DISCARD
1677 #define GTK_STOCK_DISCARD GTK_STOCK_NO
1680 gboolean ok_to_close(void)
1683 GtkResponseType response;
1685 if (ui.saved) return TRUE;
1686 dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1687 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("Save changes to '%s'?"),
1688 (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1689 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_DISCARD, GTK_RESPONSE_NO);
1690 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
1691 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1692 gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1693 response = gtk_dialog_run(GTK_DIALOG (dialog));
1694 gtk_widget_destroy(dialog);
1695 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1696 return FALSE; // aborted
1697 if (response == GTK_RESPONSE_YES) {
1698 on_fileSave_activate(NULL, NULL);
1699 if (!ui.saved) return FALSE; // if save failed, then we abort
1704 // send the focus back to the appropriate widget
1706 void reset_focus(void)
1708 if (ui.cur_item_type == ITEM_TEXT)
1709 gtk_widget_grab_focus(ui.cur_item->widget);
1711 gtk_widget_grab_focus(GTK_WIDGET(canvas));
1714 // selection / clipboard stuff
1716 void reset_selection(void)
1718 if (ui.selection == NULL) return;
1719 if (ui.selection->canvas_item != NULL)
1720 gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1721 g_list_free(ui.selection->items);
1722 g_free(ui.selection);
1723 ui.selection = NULL;
1724 update_copy_paste_enabled();
1725 update_color_menu();
1726 update_thickness_buttons();
1727 update_color_buttons();
1728 update_font_button();
1732 void move_journal_items_by(GList *itemlist, double dx, double dy,
1733 struct Layer *l1, struct Layer *l2, GList *depths)
1736 GnomeCanvasItem *refitem;
1741 while (itemlist!=NULL) {
1742 item = (struct Item *)itemlist->data;
1743 if (item->type == ITEM_STROKE)
1744 for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1745 { pt[0] += dx; pt[1] += dy; }
1746 if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1747 item->bbox.left += dx;
1748 item->bbox.right += dx;
1749 item->bbox.top += dy;
1750 item->bbox.bottom += dy;
1753 // find out where to insert
1754 if (depths != NULL) {
1755 if (depths->data == NULL) link = l2->items;
1757 link = g_list_find(l2->items, depths->data);
1758 if (link != NULL) link = link->next;
1761 l2->items = g_list_insert_before(l2->items, link, item);
1763 l1->items = g_list_remove(l1->items, item);
1766 if (depths != NULL) { // also raise/lower the canvas items
1767 if (item->canvas_item!=NULL) {
1768 if (depths->data == NULL) link = NULL;
1769 else link = g_list_find(l2->items, depths->data);
1770 if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1771 else refitem = NULL;
1772 lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1774 depths = depths->next;
1776 itemlist = itemlist->next;
1780 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1781 double offset_x, double offset_y)
1785 double mean_scaling, temp;
1787 GnomeCanvasGroup *group;
1790 /* geometric mean of x and y scalings = rescaling for stroke widths
1791 and for text font sizes */
1792 mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1794 for (list = itemlist; list != NULL; list = list->next) {
1795 item = (struct Item *)list->data;
1796 if (item->type == ITEM_STROKE) {
1797 item->brush.thickness = item->brush.thickness * mean_scaling;
1798 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1799 pt[0] = pt[0]*scaling_x + offset_x;
1800 pt[1] = pt[1]*scaling_y + offset_y;
1802 if (item->brush.variable_width)
1803 for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1804 *wid = *wid * mean_scaling;
1806 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1807 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1808 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1809 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1810 if (item->bbox.left > item->bbox.right) {
1811 temp = item->bbox.left;
1812 item->bbox.left = item->bbox.right;
1813 item->bbox.right = temp;
1815 if (item->bbox.top > item->bbox.bottom) {
1816 temp = item->bbox.top;
1817 item->bbox.top = item->bbox.bottom;
1818 item->bbox.bottom = temp;
1821 if (item->type == ITEM_TEXT) {
1822 /* must scale about NW corner -- all other points of the text box
1823 are font- and zoom-dependent, so scaling about center of text box
1824 couldn't be undone properly. FIXME? */
1825 item->font_size *= mean_scaling;
1826 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1827 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1830 if (item->canvas_item!=NULL) {
1831 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1832 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1833 make_canvas_item_one(group, item);
1838 // Switch between button mappings
1840 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1841 click event is being processed ... or if ui.button_switch_mapping is
1842 enabled and mappings are switched (but even then, canvas should have
1843 a pointer grab from the initial click that switched the mapping) */
1845 void switch_mapping(int m)
1847 if (ui.cur_mapping == m) return;
1850 if (ui.toolno[m] < NUM_STROKE_TOOLS)
1851 ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1852 if (ui.toolno[m] == TOOL_TEXT)
1853 ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1854 if (m==0) ui.which_unswitch_button = 0;
1856 update_tool_buttons();
1857 update_color_menu();
1861 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1863 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1864 if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1865 if (ui.toolno[m] == tool) return;
1869 ui.toolno[m] = tool;
1870 if (ui.linked_brush[m] == BRUSH_COPIED) {
1871 ui.linked_brush[m] = BRUSH_STATIC;
1872 update_mappings_menu_linkings();
1876 // update the ordering of components in the main vbox
1878 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1879 {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1881 void update_vbox_order(int *order)
1885 GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1886 gboolean present[VBOX_MAIN_NITEMS];
1888 for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1890 for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1891 if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1892 present[order[i]] = TRUE;
1893 child = GET_COMPONENT(vbox_component_names[order[i]]);
1894 gtk_box_reorder_child(vboxMain, child, j++);
1895 gtk_widget_show(child);
1897 for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1898 if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1901 gchar *make_cur_font_name(void)
1906 if (ui.cur_item_type == ITEM_TEXT)
1907 str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1908 else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1909 ui.selection->items->next==NULL &&
1910 (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1911 str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1913 str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1917 void update_font_button(void)
1921 str = make_cur_font_name();
1922 gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1926 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1928 return GTK_WIDGET_SENSITIVE(widget);
1931 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1933 if (ui.cur_item_type == ITEM_TEXT) {
1934 g_signal_stop_emission_by_name(widget, "can-activate-accel");
1937 return GTK_WIDGET_SENSITIVE(widget);
1940 void allow_all_accels(void)
1942 g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1943 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1944 g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1945 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1946 g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1947 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1948 g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1949 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1950 g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1951 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1952 g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1953 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1954 g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1955 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1956 g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1957 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1958 g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1959 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1960 g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1961 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1962 g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1963 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1964 g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1965 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1966 g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1967 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1968 g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1969 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1970 g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1971 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1972 g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1973 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1974 g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1975 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1976 g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1977 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1978 g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1979 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1980 g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1981 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1982 g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1983 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1984 g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1985 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1986 g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1987 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1988 g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1989 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1990 g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1991 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1992 /* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1993 "can-activate-accel", G_CALLBACK(can_accel), NULL); */
1994 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1995 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1996 g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1997 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1998 g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1999 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2000 g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
2001 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2002 g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
2003 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2004 g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
2005 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2008 void add_scroll_bindings(void)
2010 GtkBindingSet *binding_set;
2012 binding_set = gtk_binding_set_by_class(
2013 G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
2014 gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
2015 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2016 G_TYPE_BOOLEAN, FALSE);
2017 gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
2018 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2019 G_TYPE_BOOLEAN, FALSE);
2020 gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
2021 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2022 G_TYPE_BOOLEAN, FALSE);
2023 gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
2024 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2025 G_TYPE_BOOLEAN, FALSE);
2026 gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
2027 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2028 G_TYPE_BOOLEAN, TRUE);
2029 gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
2030 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2031 G_TYPE_BOOLEAN, TRUE);
2032 gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
2033 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2034 G_TYPE_BOOLEAN, TRUE);
2035 gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
2036 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2037 G_TYPE_BOOLEAN, TRUE);
2040 gboolean is_event_within_textview(GdkEventButton *event)
2044 if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2045 get_pointer_coords((GdkEvent *)event, pt);
2046 if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2047 if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2051 void hide_unimplemented(void)
2053 gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2054 gtk_widget_hide(GET_COMPONENT("journalFlatten"));
2055 gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2056 gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2057 gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2058 gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2059 gtk_widget_hide(GET_COMPONENT("helpIndex"));
2061 /* config file only works with glib 2.6 and beyond */
2062 if (glib_minor_version<6) {
2063 gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2064 gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2066 /* gtkprint only works with gtk+ 2.10 and beyond */
2067 if (gtk_check_version(2, 10, 0)) {
2068 gtk_widget_hide(GET_COMPONENT("filePrint"));
2071 /* screenshot feature doesn't work yet in Win32 */
2073 gtk_widget_hide(GET_COMPONENT("journalScreenshot"));
2077 // toggle fullscreen mode
2078 void do_fullscreen(gboolean active)
2081 ui.fullscreen = active;
2082 gtk_check_menu_item_set_active(
2083 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2084 gtk_toggle_tool_button_set_active(
2085 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2087 if (ui.fullscreen) {
2089 gtk_window_get_size(GTK_WINDOW(winMain), &ui.pre_fullscreen_width, &ui.pre_fullscreen_height);
2090 gtk_widget_set_size_request(GTK_WIDGET(winMain), gdk_screen_width(),
2091 gdk_screen_height());
2093 gtk_window_fullscreen(GTK_WINDOW(winMain));
2097 gtk_widget_set_size_request(GTK_WIDGET(winMain), -1, -1);
2098 gtk_window_resize(GTK_WINDOW(winMain), ui.pre_fullscreen_width,
2099 ui.pre_fullscreen_height);
2101 gtk_window_unfullscreen(GTK_WINDOW(winMain));
2104 update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2107 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2108 elements receive XInput events that they can't handle properly */
2110 // prevent interface items from getting bogus XInput events
2112 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2115 if (event->type == GDK_MOTION_NOTIFY &&
2116 event->motion.device != gdk_device_get_core_pointer())
2118 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2119 event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2120 event->button.device != gdk_device_get_core_pointer())
2125 /* Code to turn an extended input event into a core event and send it to
2126 a different GdkWindow -- e.g. could be used when a click in a text edit box
2127 gets sent to the canvas instead due to incorrect event translation.
2128 We now turn off xinput altogether while editing text under GTK+ 2.17, so
2129 this isn't needed any more... but could become useful again someday!
2133 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2139 if (user_data) window = (GdkWindow *)user_data;
2140 else window = widget->window;
2142 if (event->type == GDK_MOTION_NOTIFY &&
2143 event->motion.device != gdk_device_get_core_pointer()) {
2144 // printf("fixing motion\n");
2145 gdk_window_get_pointer(window, &ix, &iy, NULL);
2146 event->motion.x = ix; event->motion.y = iy;
2147 event->motion.device = gdk_device_get_core_pointer();
2148 g_object_unref(event->motion.window);
2149 event->motion.window = g_object_ref(window);
2150 gtk_widget_event(widget, event);
2153 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2154 event->button.device != gdk_device_get_core_pointer()) {
2155 // printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2156 gdk_window_get_pointer(window, &ix, &iy, NULL);
2157 event->button.x = ix; event->button.y = iy;
2158 event->button.device = gdk_device_get_core_pointer();
2159 g_object_unref(event->button.window);
2160 event->button.window = g_object_ref(window);
2161 // printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2162 gtk_widget_event(widget, event);
2170 /* When enter is pressed into page spinbox, send focus back to canvas. */
2172 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2178 /* recursively unset widget flags */
2180 void unset_flags(GtkWidget *w, gpointer flag)
2182 GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2183 if(GTK_IS_CONTAINER(w))
2184 gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2187 /* reset focus when a key or button press event reaches someone, or when the
2188 page-number spin button should relinquish control... */
2190 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2192 if (w == GET_COMPONENT("hbox1")) {
2193 /* the event won't be processed since the hbox1 doesn't know what to do with it,
2194 so we might as well kill it and avoid confusing ourselves when it gets
2195 propagated further ... */
2198 if (w == GET_COMPONENT("spinPageNo")) {
2199 /* we let the spin button take care of itself, and don't steal its focus,
2200 unless the user presses Esc or Tab (in those cases we intervene) */
2201 if (ev->type != GDK_KEY_PRESS) return FALSE;
2202 if (ev->key.keyval == GDK_Escape)
2203 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2204 else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2205 return FALSE; // let the spin button process it
2208 // otherwise, we want to make sure the canvas or text item gets focus back...
2213 void install_focus_hooks(GtkWidget *w, gpointer data)
2215 if (w == NULL) return;
2216 g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2217 g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2218 if (GTK_IS_MENU_ITEM(w)) {
2219 g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2220 install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2222 if(GTK_IS_CONTAINER(w))
2223 gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);
2226 // wrapper for missing poppler functions (defunct poppler-gdk api)
2229 wrapper_copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
2232 int cairo_width, cairo_height, cairo_rowstride;
2233 unsigned char *pixbuf_data, *dst, *cairo_data;
2234 int pixbuf_rowstride, pixbuf_n_channels;
2238 cairo_width = cairo_image_surface_get_width (surface);
2239 cairo_height = cairo_image_surface_get_height (surface);
2240 cairo_rowstride = cairo_image_surface_get_stride (surface);
2241 cairo_data = cairo_image_surface_get_data (surface);
2243 pixbuf_data = gdk_pixbuf_get_pixels (pixbuf);
2244 pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
2245 pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
2247 if (cairo_width > gdk_pixbuf_get_width (pixbuf))
2248 cairo_width = gdk_pixbuf_get_width (pixbuf);
2249 if (cairo_height > gdk_pixbuf_get_height (pixbuf))
2250 cairo_height = gdk_pixbuf_get_height (pixbuf);
2251 for (y = 0; y < cairo_height; y++)
2253 src = (unsigned int *) (cairo_data + y * cairo_rowstride);
2254 dst = pixbuf_data + y * pixbuf_rowstride;
2255 for (x = 0; x < cairo_width; x++)
2257 dst[0] = (*src >> 16) & 0xff;
2258 dst[1] = (*src >> 8) & 0xff;
2259 dst[2] = (*src >> 0) & 0xff;
2260 if (pixbuf_n_channels == 4)
2261 dst[3] = (*src >> 24) & 0xff;
2262 dst += pixbuf_n_channels;
2269 wrapper_poppler_page_render_to_pixbuf (PopplerPage *page,
2270 int src_x, int src_y,
2271 int src_width, int src_height,
2277 cairo_surface_t *surface;
2279 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2280 src_width, src_height);
2281 cr = cairo_create (surface);
2285 cairo_translate (cr, src_x + src_width, -src_y);
2288 cairo_translate (cr, src_x + src_width, src_y + src_height);
2291 cairo_translate (cr, -src_x, src_y + src_height);
2294 cairo_translate (cr, -src_x, -src_y);
2298 cairo_scale (cr, scale, scale);
2301 cairo_rotate (cr, rotation * G_PI / 180.0);
2303 poppler_page_render (page, cr);
2306 cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
2307 cairo_set_source_rgb (cr, 1., 1., 1.);
2312 wrapper_copy_cairo_surface_to_pixbuf (surface, pixbuf);
2313 cairo_surface_destroy (surface);