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 void get_pointer_coords(GdkEvent *event, gdouble *ret)
361 gdk_event_get_coords(event, &x, &y);
362 gnome_canvas_window_to_world(canvas, x, y, ret, ret+1);
363 ret[0] -= ui.cur_page->hoffset;
364 ret[1] -= ui.cur_page->voffset;
367 void fix_xinput_coords(GdkEvent *event)
369 double *axes, *px, *py, axis_width;
371 int wx, wy, sx, sy, ix, iy;
373 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
374 axes = event->button.axes;
375 px = &(event->button.x);
376 py = &(event->button.y);
377 device = event->button.device;
379 else if (event->type == GDK_MOTION_NOTIFY) {
380 axes = event->motion.axes;
381 px = &(event->motion.x);
382 py = &(event->motion.y);
383 device = event->motion.device;
385 else return; // nothing we know how to do
387 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
389 #ifdef ENABLE_XINPUT_BUGFIX
390 // fix broken events with the core pointer's location
391 if (!finite(axes[0]) || !finite(axes[1]) || axes[0]==0. || axes[1]==0.) {
392 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
397 gdk_window_get_origin(GTK_WIDGET(canvas)->window, &wx, &wy);
398 axis_width = device->axes[0].max - device->axes[0].min;
399 if (axis_width>EPSILON)
400 *px = (axes[0]/axis_width)*ui.screen_width + sx - wx;
401 axis_width = device->axes[1].max - device->axes[1].min;
402 if (axis_width>EPSILON)
403 *py = (axes[1]/axis_width)*ui.screen_height + sy - wy;
406 if (!finite(*px) || !finite(*py) || (*px==0. && *py==0.)) {
407 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
412 /* with GTK+ 2.16 or earlier, the event comes from the parent gdkwindow
413 and so needs to be adjusted for scrolling */
414 if (gtk_major_version == 2 && gtk_minor_version <= 16) {
418 /* with GTK+ 2.17, events come improperly translated, and the event's
419 GdkWindow isn't even the same for ButtonDown as for MotionNotify... */
420 if (gtk_major_version == 2 && gtk_minor_version == 17) { // GTK+ 2.17 issues !!
421 gdk_window_get_position(GTK_WIDGET(canvas)->window, &wx, &wy);
429 double get_pressure_multiplier(GdkEvent *event)
435 if (event->type == GDK_MOTION_NOTIFY) {
436 axes = event->motion.axes;
437 device = event->motion.device;
440 axes = event->button.axes;
441 device = event->button.device;
444 if (device == gdk_device_get_core_pointer()
445 || device->num_axes <= 2) return 1.0;
447 rawpressure = axes[2]/(device->axes[2].max - device->axes[2].min);
448 if (!finite(rawpressure)) return 1.0;
450 return ((1-rawpressure)*ui.width_minimum_multiplier + rawpressure*ui.width_maximum_multiplier);
453 void update_item_bbox(struct Item *item)
458 if (item->type == ITEM_STROKE) {
459 item->bbox.left = item->bbox.right = item->path->coords[0];
460 item->bbox.top = item->bbox.bottom = item->path->coords[1];
461 for (i=1, p=item->path->coords+2; i<item->path->num_points; i++, p+=2)
463 if (p[0] < item->bbox.left) item->bbox.left = p[0];
464 if (p[0] > item->bbox.right) item->bbox.right = p[0];
465 if (p[1] < item->bbox.top) item->bbox.top = p[1];
466 if (p[1] > item->bbox.bottom) item->bbox.bottom = p[1];
469 if (item->type == ITEM_TEXT && item->canvas_item!=NULL) {
471 g_object_get(item->canvas_item, "text_width", &w, "text_height", &h, NULL);
472 item->bbox.right = item->bbox.left + w;
473 item->bbox.bottom = item->bbox.top + h;
477 void make_page_clipbox(struct Page *pg)
479 GnomeCanvasPathDef *pg_clip;
481 pg_clip = gnome_canvas_path_def_new_sized(4);
482 gnome_canvas_path_def_moveto(pg_clip, 0., 0.);
483 gnome_canvas_path_def_lineto(pg_clip, 0., pg->height);
484 gnome_canvas_path_def_lineto(pg_clip, pg->width, pg->height);
485 gnome_canvas_path_def_lineto(pg_clip, pg->width, 0.);
486 gnome_canvas_path_def_closepath(pg_clip);
487 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), "path", pg_clip, NULL);
488 gnome_canvas_path_def_unref(pg_clip);
491 void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
493 PangoFontDescription *font_desc;
494 GnomeCanvasPoints points;
497 if (item->type == ITEM_STROKE) {
498 if (!item->brush.variable_width)
499 item->canvas_item = gnome_canvas_item_new(group,
500 gnome_canvas_line_get_type(), "points", item->path,
501 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
502 "fill-color-rgba", item->brush.color_rgba,
503 "width-units", item->brush.thickness, NULL);
505 item->canvas_item = gnome_canvas_item_new(group,
506 gnome_canvas_group_get_type(), NULL);
507 points.num_points = 2;
508 points.ref_count = 1;
509 for (j = 0; j < item->path->num_points-1; j++) {
510 points.coords = item->path->coords+2*j;
511 gnome_canvas_item_new((GnomeCanvasGroup *) item->canvas_item,
512 gnome_canvas_line_get_type(), "points", &points,
513 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
514 "fill-color-rgba", item->brush.color_rgba,
515 "width-units", item->widths[j], NULL);
519 if (item->type == ITEM_TEXT) {
520 font_desc = pango_font_description_from_string(item->font_name);
521 pango_font_description_set_absolute_size(font_desc,
522 item->font_size*ui.zoom*PANGO_SCALE);
523 item->canvas_item = gnome_canvas_item_new(group,
524 gnome_canvas_text_get_type(),
525 "x", item->bbox.left, "y", item->bbox.top, "anchor", GTK_ANCHOR_NW,
526 "font-desc", font_desc, "fill-color-rgba", item->brush.color_rgba,
527 "text", item->text, NULL);
528 update_item_bbox(item);
532 void make_canvas_items(void)
537 GList *pagelist, *layerlist, *itemlist;
539 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next) {
540 pg = (struct Page *)pagelist->data;
541 if (pg->group == NULL) {
542 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
543 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
544 make_page_clipbox(pg);
546 if (pg->bg->canvas_item == NULL) update_canvas_bg(pg);
547 for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
548 l = (struct Layer *)layerlist->data;
549 if (l->group == NULL)
550 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
551 pg->group, gnome_canvas_group_get_type(), NULL);
552 for (itemlist = l->items; itemlist!=NULL; itemlist = itemlist->next) {
553 item = (struct Item *)itemlist->data;
554 if (item->canvas_item == NULL)
555 make_canvas_item_one(l->group, item);
561 void update_canvas_bg(struct Page *pg)
563 GnomeCanvasGroup *group;
564 GnomeCanvasPoints *seg;
565 GdkPixbuf *scaled_pix;
569 gboolean is_well_scaled;
571 if (pg->bg->canvas_item != NULL)
572 gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
573 pg->bg->canvas_item = NULL;
575 if (pg->bg->type == BG_SOLID)
577 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
578 gnome_canvas_group_get_type(), NULL);
579 group = GNOME_CANVAS_GROUP(pg->bg->canvas_item);
580 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
581 gnome_canvas_item_new(group, gnome_canvas_rect_get_type(),
582 "x1", 0., "x2", pg->width, "y1", 0., "y2", pg->height,
583 "fill-color-rgba", pg->bg->color_rgba, NULL);
584 if (pg->bg->ruling == RULING_NONE) return;
585 seg = gnome_canvas_points_new(2);
587 if (pg->bg->ruling == RULING_GRAPH) {
588 pt[1] = 0; pt[3] = pg->height;
589 for (x=RULING_GRAPHSPACING; x<pg->width-1; x+=RULING_GRAPHSPACING) {
591 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
592 "points", seg, "fill-color-rgba", RULING_COLOR,
593 "width-units", RULING_THICKNESS, NULL);
595 pt[0] = 0; pt[2] = pg->width;
596 for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
598 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
599 "points", seg, "fill-color-rgba", RULING_COLOR,
600 "width-units", RULING_THICKNESS, NULL);
602 gnome_canvas_points_free(seg);
605 pt[0] = 0; pt[2] = pg->width;
606 for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
608 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
609 "points", seg, "fill-color-rgba", RULING_COLOR,
610 "width-units", RULING_THICKNESS, NULL);
612 if (pg->bg->ruling == RULING_LINED) {
613 pt[0] = pt[2] = RULING_LEFTMARGIN;
614 pt[1] = 0; pt[3] = pg->height;
615 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
616 "points", seg, "fill-color-rgba", RULING_MARGIN_COLOR,
617 "width-units", RULING_THICKNESS, NULL);
619 gnome_canvas_points_free(seg);
623 if (pg->bg->type == BG_PIXMAP)
625 pg->bg->pixbuf_scale = 0;
626 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
627 gnome_canvas_pixbuf_get_type(),
628 "pixbuf", pg->bg->pixbuf,
629 "width", pg->width, "height", pg->height,
630 "width-set", TRUE, "height-set", TRUE,
632 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
635 if (pg->bg->type == BG_PDF)
637 if (pg->bg->pixbuf == NULL) return;
638 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
639 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
641 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
642 gnome_canvas_pixbuf_get_type(),
643 "pixbuf", pg->bg->pixbuf,
644 "width-in-pixels", TRUE, "height-in-pixels", TRUE,
647 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
648 gnome_canvas_pixbuf_get_type(),
649 "pixbuf", pg->bg->pixbuf,
650 "width", pg->width, "height", pg->height,
651 "width-set", TRUE, "height-set", TRUE,
653 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
657 gboolean is_visible(struct Page *pg)
659 GtkAdjustment *v_adj;
662 if (!ui.view_continuous) return (pg == ui.cur_page);
663 v_adj = gtk_layout_get_vadjustment(GTK_LAYOUT(canvas));
664 ytop = v_adj->value/ui.zoom;
665 ybot = (v_adj->value + v_adj->page_size) / ui.zoom;
666 return (MAX(ytop, pg->voffset) < MIN(ybot, pg->voffset+pg->height));
669 void rescale_bg_pixmaps(void)
674 gboolean is_well_scaled;
675 gdouble zoom_to_request;
677 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
678 pg = (struct Page *)pglist->data;
679 // in progressive mode we scale only visible pages
680 if (ui.progressive_bg && !is_visible(pg)) continue;
682 if (pg->bg->type == BG_PIXMAP && pg->bg->canvas_item!=NULL) {
683 g_object_get(G_OBJECT(pg->bg->canvas_item), "pixbuf", &pix, NULL);
684 if (pix!=pg->bg->pixbuf)
685 gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pg->bg->pixbuf, NULL);
686 pg->bg->pixbuf_scale = 0;
688 if (pg->bg->type == BG_PDF) {
689 // make pixmap scale to correct size if current one is wrong
690 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
691 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
692 if (pg->bg->canvas_item != NULL && !is_well_scaled) {
693 g_object_get(pg->bg->canvas_item, "width-in-pixels", &is_well_scaled, NULL);
695 gnome_canvas_item_set(pg->bg->canvas_item,
696 "width", pg->width, "height", pg->height,
697 "width-in-pixels", FALSE, "height-in-pixels", FALSE,
698 "width-set", TRUE, "height-set", TRUE,
701 // request an asynchronous update to a better pixmap if needed
702 zoom_to_request = MIN(ui.zoom, MAX_SAFE_RENDER_DPI/72.0);
703 if (pg->bg->pixbuf_scale == zoom_to_request) continue;
704 if (add_bgpdf_request(pg->bg->file_page_seq, zoom_to_request))
705 pg->bg->pixbuf_scale = zoom_to_request;
710 gboolean have_intersect(struct BBox *a, struct BBox *b)
712 return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
713 (MAX(a->left, b->left) <= MIN(a->right, b->right));
716 /* In libgnomecanvas 2.10.0, the lower/raise functions fail to update
717 correctly the end of the group's item list. We try to work around this.
718 DON'T USE gnome_canvas_item_raise/lower directly !! */
720 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
724 i1 = g_list_index(g->item_list, item);
725 if (i1 == -1) return;
727 if (after == NULL) i2 = -1;
728 else i2 = g_list_index(g->item_list, after);
730 if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
731 if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
733 // BUGFIX for libgnomecanvas
734 g->item_list_end = g_list_last(g->item_list);
737 void rgb_to_gdkcolor(guint rgba, GdkColor *color)
740 color->red = ((rgba>>24)&0xff)*0x101;
741 color->green = ((rgba>>16)&0xff)*0x101;
742 color->blue = ((rgba>>8)&0xff)*0x101;
745 guint32 gdkcolor_to_rgba(GdkColor gdkcolor, guint16 alpha)
747 guint32 rgba = ((gdkcolor.red & 0xff00) << 16) |
748 ((gdkcolor.green & 0xff00) << 8) |
749 ((gdkcolor.blue & 0xff00) ) |
750 ((alpha & 0xff00) >> 8);
755 // some interface functions
757 void update_thickness_buttons(void)
759 if (ui.selection!=NULL || ui.toolno[ui.cur_mapping] >= NUM_STROKE_TOOLS) {
760 gtk_toggle_tool_button_set_active(
761 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
763 switch (ui.cur_brush->thickness_no) {
765 gtk_toggle_tool_button_set_active(
766 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
768 case THICKNESS_MEDIUM:
769 gtk_toggle_tool_button_set_active(
770 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
772 case THICKNESS_THICK:
773 gtk_toggle_tool_button_set_active(
774 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
777 gtk_toggle_tool_button_set_active(
778 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
782 void update_color_buttons(void)
785 GtkColorButton *colorbutton;
787 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
788 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
789 gtk_toggle_tool_button_set_active(
790 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
792 switch (ui.cur_brush->color_no) {
794 gtk_toggle_tool_button_set_active(
795 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
798 gtk_toggle_tool_button_set_active(
799 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
802 gtk_toggle_tool_button_set_active(
803 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
806 gtk_toggle_tool_button_set_active(
807 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
810 gtk_toggle_tool_button_set_active(
811 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
813 case COLOR_LIGHTBLUE:
814 gtk_toggle_tool_button_set_active(
815 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
817 case COLOR_LIGHTGREEN:
818 gtk_toggle_tool_button_set_active(
819 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
822 gtk_toggle_tool_button_set_active(
823 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
826 gtk_toggle_tool_button_set_active(
827 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
830 gtk_toggle_tool_button_set_active(
831 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
834 gtk_toggle_tool_button_set_active(
835 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
838 gtk_toggle_tool_button_set_active(
839 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
842 colorbutton = GTK_COLOR_BUTTON(GET_COMPONENT("buttonColorChooser"));
843 if ((ui.toolno[ui.cur_mapping] != TOOL_PEN &&
844 ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER &&
845 ui.toolno[ui.cur_mapping] != TOOL_TEXT))
846 gdkcolor.red = gdkcolor.blue = gdkcolor.green = 0;
847 else rgb_to_gdkcolor(ui.cur_brush->color_rgba, &gdkcolor);
848 gtk_color_button_set_color(colorbutton, &gdkcolor);
849 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
850 gtk_color_button_set_alpha(colorbutton,
851 (ui.cur_brush->color_rgba&0xff)*0x101);
852 gtk_color_button_set_use_alpha(colorbutton, TRUE);
854 gtk_color_button_set_alpha(colorbutton, 0xffff);
855 gtk_color_button_set_use_alpha(colorbutton, FALSE);
859 void update_tool_buttons(void)
861 switch(ui.toolno[ui.cur_mapping]) {
863 gtk_toggle_tool_button_set_active(
864 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
867 gtk_toggle_tool_button_set_active(
868 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
870 case TOOL_HIGHLIGHTER:
871 gtk_toggle_tool_button_set_active(
872 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
875 gtk_toggle_tool_button_set_active(
876 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
878 case TOOL_SELECTREGION:
879 gtk_toggle_tool_button_set_active(
880 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
882 case TOOL_SELECTRECT:
883 gtk_toggle_tool_button_set_active(
884 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
887 gtk_toggle_tool_button_set_active(
888 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
891 gtk_toggle_tool_button_set_active(
892 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
896 gtk_toggle_tool_button_set_active(
897 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
898 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
899 gtk_toggle_tool_button_set_active(
900 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
901 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
903 update_thickness_buttons();
904 update_color_buttons();
907 void update_tool_menu(void)
909 switch(ui.toolno[0]) {
911 gtk_check_menu_item_set_active(
912 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
915 gtk_check_menu_item_set_active(
916 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
918 case TOOL_HIGHLIGHTER:
919 gtk_check_menu_item_set_active(
920 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
923 gtk_check_menu_item_set_active(
924 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
926 case TOOL_SELECTREGION:
927 gtk_check_menu_item_set_active(
928 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
930 case TOOL_SELECTRECT:
931 gtk_check_menu_item_set_active(
932 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
935 gtk_check_menu_item_set_active(
936 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
939 gtk_check_menu_item_set_active(
940 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
944 gtk_check_menu_item_set_active(
945 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
946 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
947 gtk_check_menu_item_set_active(
948 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
949 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
952 void update_ruler_indicator(void)
954 gtk_toggle_tool_button_set_active(
955 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
956 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
957 gtk_toggle_tool_button_set_active(
958 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
959 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
960 gtk_check_menu_item_set_active(
961 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
962 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
963 gtk_check_menu_item_set_active(
964 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
965 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
968 void update_color_menu(void)
970 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
971 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
972 gtk_check_menu_item_set_active(
973 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
975 switch (ui.cur_brush->color_no) {
977 gtk_check_menu_item_set_active(
978 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
981 gtk_check_menu_item_set_active(
982 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
985 gtk_check_menu_item_set_active(
986 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
989 gtk_check_menu_item_set_active(
990 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
993 gtk_check_menu_item_set_active(
994 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
996 case COLOR_LIGHTBLUE:
997 gtk_check_menu_item_set_active(
998 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
1000 case COLOR_LIGHTGREEN:
1001 gtk_check_menu_item_set_active(
1002 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
1005 gtk_check_menu_item_set_active(
1006 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
1009 gtk_check_menu_item_set_active(
1010 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
1013 gtk_check_menu_item_set_active(
1014 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
1017 gtk_check_menu_item_set_active(
1018 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
1021 gtk_check_menu_item_set_active(
1022 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
1026 void update_pen_props_menu(void)
1028 switch(ui.brushes[0][TOOL_PEN].thickness_no) {
1029 case THICKNESS_VERYFINE:
1030 gtk_check_menu_item_set_active(
1031 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
1033 case THICKNESS_FINE:
1034 gtk_check_menu_item_set_active(
1035 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
1037 case THICKNESS_MEDIUM:
1038 gtk_check_menu_item_set_active(
1039 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
1041 case THICKNESS_THICK:
1042 gtk_check_menu_item_set_active(
1043 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
1045 case THICKNESS_VERYTHICK:
1046 gtk_check_menu_item_set_active(
1047 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
1052 void update_eraser_props_menu(void)
1054 switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
1055 case THICKNESS_FINE:
1056 gtk_check_menu_item_set_active(
1057 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
1059 case THICKNESS_MEDIUM:
1060 gtk_check_menu_item_set_active(
1061 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
1063 case THICKNESS_THICK:
1064 gtk_check_menu_item_set_active(
1065 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
1069 gtk_check_menu_item_set_active(
1070 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
1071 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
1072 gtk_check_menu_item_set_active(
1073 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
1074 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
1075 gtk_check_menu_item_set_active(
1076 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
1077 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
1080 void update_highlighter_props_menu(void)
1082 switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
1083 case THICKNESS_FINE:
1084 gtk_check_menu_item_set_active(
1085 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
1087 case THICKNESS_MEDIUM:
1088 gtk_check_menu_item_set_active(
1089 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
1091 case THICKNESS_THICK:
1092 gtk_check_menu_item_set_active(
1093 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
1098 void update_mappings_menu_linkings(void)
1100 switch (ui.linked_brush[1]) {
1102 gtk_check_menu_item_set_active(
1103 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
1106 gtk_check_menu_item_set_active(
1107 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
1110 gtk_check_menu_item_set_active(
1111 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
1114 switch (ui.linked_brush[2]) {
1116 gtk_check_menu_item_set_active(
1117 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
1120 gtk_check_menu_item_set_active(
1121 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
1124 gtk_check_menu_item_set_active(
1125 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
1130 void update_mappings_menu(void)
1132 gtk_widget_set_sensitive(GET_COMPONENT("optionsButtonMappings"), ui.use_xinput);
1133 gtk_widget_set_sensitive(GET_COMPONENT("optionsPressureSensitive"), ui.use_xinput);
1134 gtk_check_menu_item_set_active(
1135 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
1136 gtk_check_menu_item_set_active(
1137 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsPressureSensitive")), ui.pressure_sensitivity);
1139 switch(ui.toolno[1]) {
1141 gtk_check_menu_item_set_active(
1142 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
1145 gtk_check_menu_item_set_active(
1146 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
1148 case TOOL_HIGHLIGHTER:
1149 gtk_check_menu_item_set_active(
1150 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
1153 gtk_check_menu_item_set_active(
1154 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
1156 case TOOL_SELECTREGION:
1157 gtk_check_menu_item_set_active(
1158 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
1160 case TOOL_SELECTRECT:
1161 gtk_check_menu_item_set_active(
1162 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
1164 case TOOL_VERTSPACE:
1165 gtk_check_menu_item_set_active(
1166 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
1169 switch(ui.toolno[2]) {
1171 gtk_check_menu_item_set_active(
1172 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
1175 gtk_check_menu_item_set_active(
1176 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
1178 case TOOL_HIGHLIGHTER:
1179 gtk_check_menu_item_set_active(
1180 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
1183 gtk_check_menu_item_set_active(
1184 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
1186 case TOOL_SELECTREGION:
1187 gtk_check_menu_item_set_active(
1188 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
1190 case TOOL_SELECTRECT:
1191 gtk_check_menu_item_set_active(
1192 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
1194 case TOOL_VERTSPACE:
1195 gtk_check_menu_item_set_active(
1196 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1199 update_mappings_menu_linkings();
1202 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1205 struct Layer *layer;
1210 /* re-show all the layers of the old page */
1211 if (ui.cur_page != NULL)
1212 for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1213 layer = (struct Layer *)list->data;
1214 if (layer->group!=NULL)
1215 gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1218 ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1219 ui.layerno = ui.cur_page->nlayers-1;
1220 ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1221 update_page_stuff();
1222 if (ui.progressive_bg) rescale_bg_pixmaps();
1224 if (rescroll) { // scroll and force a refresh
1225 /* -- this seems to cause some display bugs ??
1226 gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1227 ui.cur_page->voffset*ui.zoom); */
1228 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1229 cy = ui.cur_page->voffset*ui.zoom;
1230 gnome_canvas_scroll_to(canvas, cx, cy);
1233 gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1234 else if (!ui.view_continuous)
1235 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1239 void update_page_stuff(void)
1242 GtkComboBox *layerbox;
1245 GtkSpinButton *spin;
1247 double vertpos, maxwidth;
1249 // move the page groups to their rightful locations or hide them
1250 if (ui.view_continuous) {
1253 for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1254 pg = (struct Page *)pglist->data;
1255 if (pg->group!=NULL) {
1256 pg->hoffset = 0.; pg->voffset = vertpos;
1257 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1258 "x", pg->hoffset, "y", pg->voffset, NULL);
1259 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1261 vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1262 if (pg->width > maxwidth) maxwidth = pg->width;
1264 vertpos -= VIEW_CONTINUOUS_SKIP;
1265 gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1267 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1268 pg = (struct Page *)pglist->data;
1269 if (pg == ui.cur_page && pg->group!=NULL) {
1270 pg->hoffset = 0.; pg->voffset = 0.;
1271 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1272 "x", pg->hoffset, "y", pg->voffset, NULL);
1273 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1275 if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1278 gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1281 // update the page / layer info at bottom of screen
1283 spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1284 ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1285 gtk_spin_button_set_range(spin, 1, journal.npages+1);
1286 /* npages+1 will be used to create a new page at end */
1287 gtk_spin_button_set_value(spin, ui.pageno+1);
1288 g_snprintf(tmp, 10, _(" of %d"), journal.npages);
1289 gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1291 layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1292 if (ui.layerbox_length == 0) {
1293 gtk_combo_box_prepend_text(layerbox, _("Background"));
1294 ui.layerbox_length++;
1296 while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1297 gtk_combo_box_remove_text(layerbox, 0);
1298 ui.layerbox_length--;
1300 while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1301 g_snprintf(tmp, 10, _("Layer %d"), ui.layerbox_length++);
1302 gtk_combo_box_prepend_text(layerbox, tmp);
1304 gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1305 ui.in_update_page_stuff = FALSE;
1307 gtk_container_forall(GTK_CONTAINER(layerbox), unset_flags, (gpointer)GTK_CAN_FOCUS);
1309 // update the paper-style menu radio buttons
1311 if (ui.view_continuous)
1312 gtk_check_menu_item_set_active(
1313 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1315 gtk_check_menu_item_set_active(
1316 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1318 if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1319 switch (ui.cur_page->bg->color_no) {
1321 gtk_check_menu_item_set_active(
1322 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1325 gtk_check_menu_item_set_active(
1326 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1329 gtk_check_menu_item_set_active(
1330 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1333 gtk_check_menu_item_set_active(
1334 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1337 gtk_check_menu_item_set_active(
1338 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1341 gtk_check_menu_item_set_active(
1342 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1345 gtk_check_menu_item_set_active(
1346 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1349 switch (ui.cur_page->bg->ruling) {
1351 gtk_check_menu_item_set_active(
1352 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1355 gtk_check_menu_item_set_active(
1356 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1359 gtk_check_menu_item_set_active(
1360 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1363 gtk_check_menu_item_set_active(
1364 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1368 gtk_check_menu_item_set_active(
1369 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1370 gtk_check_menu_item_set_active(
1371 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1374 // enable/disable the page/layer menu items and toolbar buttons
1376 gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"),
1377 ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1378 gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1379 ui.cur_page->bg->type == BG_SOLID);
1381 gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1382 gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1383 gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1384 gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1385 gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1386 gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1387 gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1388 gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1390 gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1391 gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1393 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1394 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1397 void update_toolbar_and_menu(void)
1399 update_tool_buttons(); // takes care of other toolbar buttons as well
1401 update_color_menu();
1402 update_pen_props_menu();
1403 update_eraser_props_menu();
1404 update_highlighter_props_menu();
1405 update_mappings_menu();
1407 gtk_toggle_tool_button_set_active(
1408 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1409 gtk_check_menu_item_set_active(
1410 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1413 void update_file_name(char *filename)
1416 if (ui.filename != NULL) g_free(ui.filename);
1417 ui.filename = filename;
1418 if (filename == NULL) {
1419 gtk_window_set_title(GTK_WINDOW (winMain), _("Xournal"));
1422 p = g_utf8_strrchr(filename, -1, '/');
1423 if (p == NULL) p = filename;
1424 else p = g_utf8_next_char(p);
1425 g_snprintf(tmp, 100, _("Xournal - %s"), p);
1426 gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1427 new_mru_entry(filename);
1429 if (filename[0]=='/') {
1430 if (ui.default_path!=NULL) g_free(ui.default_path);
1431 ui.default_path = g_path_get_dirname(filename);
1435 void update_undo_redo_enabled(void)
1437 gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1438 gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1439 gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1440 gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1443 void update_copy_paste_enabled(void)
1445 gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1446 gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1447 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_item_type!=ITEM_TEXT);
1448 gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1449 gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1450 gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1451 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_item_type!=ITEM_TEXT);
1454 void update_mapping_linkings(int toolno)
1458 for (i = 1; i<=NUM_BUTTONS; i++) {
1459 if (ui.linked_brush[i] == BRUSH_LINKED) {
1460 if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1461 g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1463 if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1464 ui.linked_brush[i] = BRUSH_STATIC;
1465 if (i==1 || i==2) update_mappings_menu_linkings();
1470 void set_cur_color(int color_no, guint color_rgba)
1472 int which_mapping, tool;
1474 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) tool = TOOL_HIGHLIGHTER;
1475 else tool = TOOL_PEN;
1476 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1477 which_mapping = ui.cur_mapping;
1478 else which_mapping = 0;
1480 ui.brushes[which_mapping][tool].color_no = color_no;
1481 if (tool == TOOL_HIGHLIGHTER && (color_rgba & 0xff) == 0xff)
1482 ui.brushes[which_mapping][tool].color_rgba = color_rgba & ui.hiliter_alpha_mask;
1484 ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1485 update_mapping_linkings(tool);
1488 void recolor_temp_text(int color_no, guint color_rgba)
1492 if (ui.cur_item_type!=ITEM_TEXT) return;
1493 if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1495 undo->type = ITEM_TEXT_ATTRIB;
1496 undo->item = ui.cur_item;
1497 undo->str = g_strdup(ui.cur_item->font_name);
1498 undo->val_x = ui.cur_item->font_size;
1499 undo->brush = (struct Brush *)g_memdup(&(ui.cur_item->brush), sizeof(struct Brush));
1501 ui.cur_item->brush.color_no = color_no;
1502 ui.cur_item->brush.color_rgba = color_rgba;
1503 rgb_to_gdkcolor(color_rgba, &gdkcolor);
1504 gtk_widget_modify_text(ui.cur_item->widget, GTK_STATE_NORMAL, &gdkcolor);
1505 gtk_widget_grab_focus(ui.cur_item->widget);
1508 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1510 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1511 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1514 else if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_TOOL_BUTTON) {
1515 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1519 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1521 if (ui.cur_item_type == ITEM_TEXT)
1522 recolor_temp_text(color_no, color_rgba);
1524 if (ui.selection != NULL) {
1525 recolor_selection(color_no, color_rgba);
1526 update_color_buttons();
1527 update_color_menu();
1530 if (ui.toolno[ui.cur_mapping] != TOOL_PEN && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER
1531 && ui.toolno[ui.cur_mapping] != TOOL_TEXT) {
1532 if (ui.selection != NULL) return;
1535 ui.toolno[ui.cur_mapping] = TOOL_PEN;
1536 ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1537 update_tool_buttons();
1541 set_cur_color(color_no, color_rgba);
1542 update_color_buttons();
1543 update_color_menu();
1547 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1551 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1552 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1555 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1559 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1561 if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1562 rethicken_selection(val);
1563 update_thickness_buttons();
1566 if (tool >= NUM_STROKE_TOOLS) {
1567 update_thickness_buttons(); // undo illegal button selection
1571 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1572 which_mapping = ui.cur_mapping;
1573 else which_mapping = 0;
1574 if (ui.brushes[which_mapping][tool].thickness_no == val) return;
1576 ui.brushes[which_mapping][tool].thickness_no = val;
1577 ui.brushes[which_mapping][tool].thickness = predef_thickness[tool][val];
1578 update_mapping_linkings(tool);
1580 update_thickness_buttons();
1581 if (tool == TOOL_PEN) update_pen_props_menu();
1582 if (tool == TOOL_ERASER) update_eraser_props_menu();
1583 if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1587 void process_papercolor_activate(GtkMenuItem *menuitem, int color, guint rgba)
1593 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1594 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1598 if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages || color == COLOR_OTHER)
1599 gtk_check_menu_item_set_active(
1600 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1604 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1605 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1606 if (pg->bg->type == BG_SOLID && pg->bg->color_rgba != rgba) {
1608 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1609 undo->multiop |= MULTIOP_CONT_REDO;
1611 undo->type = ITEM_NEW_BG_ONE;
1613 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1614 undo->bg->canvas_item = NULL;
1616 pg->bg->color_no = color;
1617 pg->bg->color_rgba = rgba;
1618 update_canvas_bg(pg);
1620 if (!ui.bg_apply_all_pages) break;
1622 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1625 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1629 gboolean hasdone, must_upd;
1631 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1634 if (ui.bg_apply_all_pages)
1635 gtk_check_menu_item_set_active(
1636 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1641 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1642 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1643 if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1645 undo->type = ITEM_NEW_BG_ONE;
1646 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1647 undo->multiop |= MULTIOP_CONT_REDO;
1650 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1651 undo->bg->canvas_item = NULL;
1653 if (pg->bg->type != BG_SOLID) {
1654 pg->bg->type = BG_SOLID;
1655 pg->bg->color_no = COLOR_WHITE;
1656 pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1657 pg->bg->filename = NULL;
1658 pg->bg->pixbuf = NULL;
1661 pg->bg->ruling = style;
1662 update_canvas_bg(pg);
1664 if (!ui.bg_apply_all_pages) break;
1666 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1667 if (must_upd) update_page_stuff();
1670 gboolean ok_to_close(void)
1673 GtkResponseType response;
1675 if (ui.saved) return TRUE;
1676 dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1677 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("Save changes to '%s'?"),
1678 (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1679 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_DISCARD, GTK_RESPONSE_NO);
1680 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
1681 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1682 gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1683 response = gtk_dialog_run(GTK_DIALOG (dialog));
1684 gtk_widget_destroy(dialog);
1685 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1686 return FALSE; // aborted
1687 if (response == GTK_RESPONSE_YES) {
1688 on_fileSave_activate(NULL, NULL);
1689 if (!ui.saved) return FALSE; // if save failed, then we abort
1694 // send the focus back to the appropriate widget
1696 void reset_focus(void)
1698 if (ui.cur_item_type == ITEM_TEXT)
1699 gtk_widget_grab_focus(ui.cur_item->widget);
1701 gtk_widget_grab_focus(GTK_WIDGET(canvas));
1704 // selection / clipboard stuff
1706 void reset_selection(void)
1708 if (ui.selection == NULL) return;
1709 if (ui.selection->canvas_item != NULL)
1710 gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1711 g_list_free(ui.selection->items);
1712 g_free(ui.selection);
1713 ui.selection = NULL;
1714 update_copy_paste_enabled();
1715 update_color_menu();
1716 update_thickness_buttons();
1717 update_color_buttons();
1718 update_font_button();
1722 void move_journal_items_by(GList *itemlist, double dx, double dy,
1723 struct Layer *l1, struct Layer *l2, GList *depths)
1726 GnomeCanvasItem *refitem;
1731 while (itemlist!=NULL) {
1732 item = (struct Item *)itemlist->data;
1733 if (item->type == ITEM_STROKE)
1734 for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1735 { pt[0] += dx; pt[1] += dy; }
1736 if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1737 item->bbox.left += dx;
1738 item->bbox.right += dx;
1739 item->bbox.top += dy;
1740 item->bbox.bottom += dy;
1743 // find out where to insert
1744 if (depths != NULL) {
1745 if (depths->data == NULL) link = l2->items;
1747 link = g_list_find(l2->items, depths->data);
1748 if (link != NULL) link = link->next;
1751 l2->items = g_list_insert_before(l2->items, link, item);
1753 l1->items = g_list_remove(l1->items, item);
1756 if (depths != NULL) { // also raise/lower the canvas items
1757 if (item->canvas_item!=NULL) {
1758 if (depths->data == NULL) link = NULL;
1759 else link = g_list_find(l2->items, depths->data);
1760 if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1761 else refitem = NULL;
1762 lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1764 depths = depths->next;
1766 itemlist = itemlist->next;
1770 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1771 double offset_x, double offset_y)
1775 double mean_scaling, temp;
1777 GnomeCanvasGroup *group;
1780 /* geometric mean of x and y scalings = rescaling for stroke widths
1781 and for text font sizes */
1782 mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1784 for (list = itemlist; list != NULL; list = list->next) {
1785 item = (struct Item *)list->data;
1786 if (item->type == ITEM_STROKE) {
1787 item->brush.thickness = item->brush.thickness * mean_scaling;
1788 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1789 pt[0] = pt[0]*scaling_x + offset_x;
1790 pt[1] = pt[1]*scaling_y + offset_y;
1792 if (item->brush.variable_width)
1793 for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1794 *wid = *wid * mean_scaling;
1796 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1797 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1798 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1799 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1800 if (item->bbox.left > item->bbox.right) {
1801 temp = item->bbox.left;
1802 item->bbox.left = item->bbox.right;
1803 item->bbox.right = temp;
1805 if (item->bbox.top > item->bbox.bottom) {
1806 temp = item->bbox.top;
1807 item->bbox.top = item->bbox.bottom;
1808 item->bbox.bottom = temp;
1811 if (item->type == ITEM_TEXT) {
1812 /* must scale about NW corner -- all other points of the text box
1813 are font- and zoom-dependent, so scaling about center of text box
1814 couldn't be undone properly. FIXME? */
1815 item->font_size *= mean_scaling;
1816 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1817 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1820 if (item->canvas_item!=NULL) {
1821 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1822 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1823 make_canvas_item_one(group, item);
1828 // Switch between button mappings
1830 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1831 click event is being processed ... or if ui.button_switch_mapping is
1832 enabled and mappings are switched (but even then, canvas should have
1833 a pointer grab from the initial click that switched the mapping) */
1835 void switch_mapping(int m)
1837 if (ui.cur_mapping == m) return;
1840 if (ui.toolno[m] < NUM_STROKE_TOOLS)
1841 ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1842 if (ui.toolno[m] == TOOL_TEXT)
1843 ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1844 if (m==0) ui.which_unswitch_button = 0;
1846 update_tool_buttons();
1847 update_color_menu();
1851 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1853 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1854 if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1855 if (ui.toolno[m] == tool) return;
1859 ui.toolno[m] = tool;
1860 if (ui.linked_brush[m] == BRUSH_COPIED) {
1861 ui.linked_brush[m] = BRUSH_STATIC;
1862 update_mappings_menu_linkings();
1866 // update the ordering of components in the main vbox
1868 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1869 {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1871 void update_vbox_order(int *order)
1875 GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1876 gboolean present[VBOX_MAIN_NITEMS];
1878 for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1880 for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1881 if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1882 present[order[i]] = TRUE;
1883 child = GET_COMPONENT(vbox_component_names[order[i]]);
1884 gtk_box_reorder_child(vboxMain, child, j++);
1885 gtk_widget_show(child);
1887 for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1888 if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1891 gchar *make_cur_font_name(void)
1896 if (ui.cur_item_type == ITEM_TEXT)
1897 str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1898 else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1899 ui.selection->items->next==NULL &&
1900 (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1901 str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1903 str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1907 void update_font_button(void)
1911 str = make_cur_font_name();
1912 gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1916 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1918 return GTK_WIDGET_SENSITIVE(widget);
1921 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1923 if (ui.cur_item_type == ITEM_TEXT) {
1924 g_signal_stop_emission_by_name(widget, "can-activate-accel");
1927 return GTK_WIDGET_SENSITIVE(widget);
1930 void allow_all_accels(void)
1932 g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1933 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1934 g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1935 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1936 g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1937 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1938 g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1939 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1940 g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1941 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1942 g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1943 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1944 g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1945 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1946 g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1947 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1948 g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1949 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1950 g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1951 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1952 g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1953 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1954 g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1955 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1956 g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1957 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1958 g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1959 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1960 g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1961 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1962 g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1963 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1964 g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1965 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1966 g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1967 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1968 g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1969 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1970 g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1971 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1972 g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1973 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1974 g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1975 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1976 g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1977 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1978 g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1979 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1980 g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1981 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1982 /* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1983 "can-activate-accel", G_CALLBACK(can_accel), NULL); */
1984 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1985 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1986 g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1987 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1988 g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1989 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1990 g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1991 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1992 g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1993 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1994 g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
1995 "can-activate-accel", G_CALLBACK(can_accel), NULL);
1998 void add_scroll_bindings(void)
2000 GtkBindingSet *binding_set;
2002 binding_set = gtk_binding_set_by_class(
2003 G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
2004 gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
2005 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2006 G_TYPE_BOOLEAN, FALSE);
2007 gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
2008 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2009 G_TYPE_BOOLEAN, FALSE);
2010 gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
2011 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2012 G_TYPE_BOOLEAN, FALSE);
2013 gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
2014 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2015 G_TYPE_BOOLEAN, FALSE);
2016 gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
2017 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2018 G_TYPE_BOOLEAN, TRUE);
2019 gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
2020 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2021 G_TYPE_BOOLEAN, TRUE);
2022 gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
2023 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2024 G_TYPE_BOOLEAN, TRUE);
2025 gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
2026 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2027 G_TYPE_BOOLEAN, TRUE);
2030 gboolean is_event_within_textview(GdkEventButton *event)
2034 if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2035 get_pointer_coords((GdkEvent *)event, pt);
2036 if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2037 if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2041 void hide_unimplemented(void)
2043 gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2044 gtk_widget_hide(GET_COMPONENT("journalFlatten"));
2045 gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2046 gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2047 gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2048 gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2049 gtk_widget_hide(GET_COMPONENT("helpIndex"));
2051 /* config file only works with glib 2.6 and beyond */
2052 if (glib_minor_version<6) {
2053 gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2054 gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2056 /* gtkprint only works with gtk+ 2.10 and beyond */
2057 if (gtk_check_version(2, 10, 0)) {
2058 gtk_widget_hide(GET_COMPONENT("filePrint"));
2061 /* screenshot feature doesn't work yet in Win32 */
2063 gtk_widget_hide(GET_COMPONENT("journalScreenshot"));
2067 // toggle fullscreen mode
2068 void do_fullscreen(gboolean active)
2071 ui.fullscreen = active;
2072 gtk_check_menu_item_set_active(
2073 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2074 gtk_toggle_tool_button_set_active(
2075 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2077 if (ui.fullscreen) {
2079 gtk_window_get_size(GTK_WINDOW(winMain), &ui.pre_fullscreen_width, &ui.pre_fullscreen_height);
2080 gtk_widget_set_size_request(GTK_WIDGET(winMain), gdk_screen_width(),
2081 gdk_screen_height());
2083 gtk_window_fullscreen(GTK_WINDOW(winMain));
2087 gtk_widget_set_size_request(GTK_WIDGET(winMain), -1, -1);
2088 gtk_window_resize(GTK_WINDOW(winMain), ui.pre_fullscreen_width,
2089 ui.pre_fullscreen_height);
2091 gtk_window_unfullscreen(GTK_WINDOW(winMain));
2094 update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2097 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2098 elements receive XInput events that they can't handle properly */
2100 // prevent interface items from getting bogus XInput events
2102 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2105 if (event->type == GDK_MOTION_NOTIFY &&
2106 event->motion.device != gdk_device_get_core_pointer())
2108 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2109 event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2110 event->button.device != gdk_device_get_core_pointer())
2115 /* Code to turn an extended input event into a core event and send it to
2116 a different GdkWindow -- e.g. could be used when a click in a text edit box
2117 gets sent to the canvas instead due to incorrect event translation.
2118 We now turn off xinput altogether while editing text under GTK+ 2.17, so
2119 this isn't needed any more... but could become useful again someday!
2123 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2129 if (user_data) window = (GdkWindow *)user_data;
2130 else window = widget->window;
2132 if (event->type == GDK_MOTION_NOTIFY &&
2133 event->motion.device != gdk_device_get_core_pointer()) {
2134 // printf("fixing motion\n");
2135 gdk_window_get_pointer(window, &ix, &iy, NULL);
2136 event->motion.x = ix; event->motion.y = iy;
2137 event->motion.device = gdk_device_get_core_pointer();
2138 g_object_unref(event->motion.window);
2139 event->motion.window = g_object_ref(window);
2140 gtk_widget_event(widget, event);
2143 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2144 event->button.device != gdk_device_get_core_pointer()) {
2145 // printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2146 gdk_window_get_pointer(window, &ix, &iy, NULL);
2147 event->button.x = ix; event->button.y = iy;
2148 event->button.device = gdk_device_get_core_pointer();
2149 g_object_unref(event->button.window);
2150 event->button.window = g_object_ref(window);
2151 // printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2152 gtk_widget_event(widget, event);
2160 /* When enter is pressed into page spinbox, send focus back to canvas. */
2162 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2168 /* recursively unset widget flags */
2170 void unset_flags(GtkWidget *w, gpointer flag)
2172 GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2173 if(GTK_IS_CONTAINER(w))
2174 gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2177 /* reset focus when a key or button press event reaches someone, or when the
2178 page-number spin button should relinquish control... */
2180 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2182 if (w == GET_COMPONENT("hbox1")) {
2183 /* the event won't be processed since the hbox1 doesn't know what to do with it,
2184 so we might as well kill it and avoid confusing ourselves when it gets
2185 propagated further ... */
2188 if (w == GET_COMPONENT("spinPageNo")) {
2189 /* we let the spin button take care of itself, and don't steal its focus,
2190 unless the user presses Esc or Tab (in those cases we intervene) */
2191 if (ev->type != GDK_KEY_PRESS) return FALSE;
2192 if (ev->key.keyval == GDK_Escape)
2193 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2194 else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2195 return FALSE; // let the spin button process it
2198 // otherwise, we want to make sure the canvas or text item gets focus back...
2203 void install_focus_hooks(GtkWidget *w, gpointer data)
2205 if (w == NULL) return;
2206 g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2207 g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2208 if (GTK_IS_MENU_ITEM(w)) {
2209 g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2210 install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2212 if(GTK_IS_CONTAINER(w))
2213 gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);
2216 // wrapper for missing poppler functions (defunct poppler-gdk api)
2219 wrapper_copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
2222 int cairo_width, cairo_height, cairo_rowstride;
2223 unsigned char *pixbuf_data, *dst, *cairo_data;
2224 int pixbuf_rowstride, pixbuf_n_channels;
2228 cairo_width = cairo_image_surface_get_width (surface);
2229 cairo_height = cairo_image_surface_get_height (surface);
2230 cairo_rowstride = cairo_image_surface_get_stride (surface);
2231 cairo_data = cairo_image_surface_get_data (surface);
2233 pixbuf_data = gdk_pixbuf_get_pixels (pixbuf);
2234 pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
2235 pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
2237 if (cairo_width > gdk_pixbuf_get_width (pixbuf))
2238 cairo_width = gdk_pixbuf_get_width (pixbuf);
2239 if (cairo_height > gdk_pixbuf_get_height (pixbuf))
2240 cairo_height = gdk_pixbuf_get_height (pixbuf);
2241 for (y = 0; y < cairo_height; y++)
2243 src = (unsigned int *) (cairo_data + y * cairo_rowstride);
2244 dst = pixbuf_data + y * pixbuf_rowstride;
2245 for (x = 0; x < cairo_width; x++)
2247 dst[0] = (*src >> 16) & 0xff;
2248 dst[1] = (*src >> 8) & 0xff;
2249 dst[2] = (*src >> 0) & 0xff;
2250 if (pixbuf_n_channels == 4)
2251 dst[3] = (*src >> 24) & 0xff;
2252 dst += pixbuf_n_channels;
2259 wrapper_poppler_page_render_to_pixbuf (PopplerPage *page,
2260 int src_x, int src_y,
2261 int src_width, int src_height,
2267 cairo_surface_t *surface;
2269 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2270 src_width, src_height);
2271 cr = cairo_create (surface);
2275 cairo_translate (cr, src_x + src_width, -src_y);
2278 cairo_translate (cr, src_x + src_width, src_y + src_height);
2281 cairo_translate (cr, -src_x, src_y + src_height);
2284 cairo_translate (cr, -src_x, -src_y);
2288 cairo_scale (cr, scale, scale);
2291 cairo_rotate (cr, rotation * G_PI / 180.0);
2293 poppler_page_render (page, cr);
2296 cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
2297 cairo_set_source_rgb (cr, 1., 1., 1.);
2302 wrapper_copy_cairo_surface_to_pixbuf (surface, pixbuf);
2303 cairo_surface_destroy (surface);