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"
36 // some global constants
38 guint predef_colors_rgba[COLOR_MAX] =
39 { 0x000000ff, 0x3333ccff, 0xff0000ff, 0x008000ff,
40 0x808080ff, 0x00c0ffff, 0x00ff00ff, 0xff00ffff,
41 0xff8000ff, 0xffff00ff, 0xffffffff };
43 guint predef_bgcolors_rgba[COLOR_MAX] = // meaningless ones set to white
44 { 0xffffffff, 0xa0e8ffff, 0xffc0d4ff, 0x80ffc0ff,
45 0xffffffff, 0xa0e8ffff, 0x80ffc0ff, 0xffc0d4ff,
46 0xffc080ff, 0xffff80ff, 0xffffffff };
48 double predef_thickness[NUM_STROKE_TOOLS][THICKNESS_MAX] =
49 { { 0.42, 0.85, 1.41, 2.26, 5.67 }, // pen thicknesses = 0.15, 0.3, 0.5, 0.8, 2 mm
50 { 2.83, 2.83, 8.50, 19.84, 19.84 }, // eraser thicknesses = 1, 3, 7 mm
51 { 2.83, 2.83, 8.50, 19.84, 19.84 }, // highlighter thicknesses = 1, 3, 7 mm
54 // some manipulation functions
56 struct Page *new_page(struct Page *template)
58 struct Page *pg = (struct Page *) g_memdup(template, sizeof(struct Page));
59 struct Layer *l = g_new(struct Layer, 1);
63 pg->layers = g_list_append(NULL, l);
65 pg->bg = (struct Background *)g_memdup(template->bg, sizeof(struct Background));
66 pg->bg->canvas_item = NULL;
67 if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
68 gdk_pixbuf_ref(pg->bg->pixbuf);
69 refstring_ref(pg->bg->filename);
71 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
72 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
73 make_page_clipbox(pg);
75 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
76 pg->group, gnome_canvas_group_get_type(), NULL);
81 /* Create a page from a background.
82 Note: bg should be an UNREFERENCED background.
83 If needed, first duplicate it and increase the refcount of the pixbuf.
85 struct Page *new_page_with_bg(struct Background *bg, double width, double height)
87 struct Page *pg = g_new(struct Page, 1);
88 struct Layer *l = g_new(struct Layer, 1);
92 pg->layers = g_list_append(NULL, l);
95 pg->bg->canvas_item = NULL;
98 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
99 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
100 make_page_clipbox(pg);
101 update_canvas_bg(pg);
102 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
103 pg->group, gnome_canvas_group_get_type(), NULL);
108 // change the current page if necessary for pointer at pt
109 void set_current_page(gdouble *pt)
111 gboolean page_change;
112 struct Page *tmppage;
115 tmppage = ui.cur_page;
116 while (ui.view_continuous && (pt[1] < - VIEW_CONTINUOUS_SKIP)) {
117 if (ui.pageno == 0) break;
120 tmppage = g_list_nth_data(journal.pages, ui.pageno);
121 pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
123 while (ui.view_continuous && (pt[1] > tmppage->height + VIEW_CONTINUOUS_SKIP)) {
124 if (ui.pageno == journal.npages-1) break;
125 pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
128 tmppage = g_list_nth_data(journal.pages, ui.pageno);
130 if (page_change) do_switch_page(ui.pageno, FALSE, FALSE);
133 void realloc_cur_path(int n)
135 if (n <= ui.cur_path_storage_alloc) return;
136 ui.cur_path_storage_alloc = n+100;
137 ui.cur_path.coords = g_realloc(ui.cur_path.coords, 2*(n+100)*sizeof(double));
140 void realloc_cur_widths(int n)
142 if (n <= ui.cur_widths_storage_alloc) return;
143 ui.cur_widths_storage_alloc = n+100;
144 ui.cur_widths = g_realloc(ui.cur_widths, (n+100)*sizeof(double));
147 // undo utility functions
149 void prepare_new_undo(void)
152 // add a new UndoItem on the stack
153 u = (struct UndoItem *)g_malloc(sizeof(struct UndoItem));
161 void clear_redo_stack(void)
165 struct UndoErasureData *erasure;
168 /* Warning: the redo items might reference items from past redo entries,
169 which have been destroyed before them. Be careful! As a rule, it's
170 safe to destroy data which has been created at the current history step,
171 it's unsafe to refer to any data from previous history steps */
174 if (redo->type == ITEM_STROKE) {
175 gnome_canvas_points_free(redo->item->path);
176 if (redo->item->brush.variable_width) g_free(redo->item->widths);
178 /* the strokes are unmapped, so there are no associated canvas items */
180 else if (redo->type == ITEM_TEXT) {
181 g_free(redo->item->text);
182 g_free(redo->item->font_name);
185 else if (redo->type == ITEM_IMAGE) {
186 g_object_unref(redo->item->image);
187 g_free(redo->item->image_png);
190 else if (redo->type == ITEM_ERASURE || redo->type == ITEM_RECOGNIZER) {
191 for (list = redo->erasurelist; list!=NULL; list=list->next) {
192 erasure = (struct UndoErasureData *)list->data;
193 for (repl = erasure->replacement_items; repl!=NULL; repl=repl->next) {
194 it = (struct Item *)repl->data;
195 gnome_canvas_points_free(it->path);
196 if (it->brush.variable_width) g_free(it->widths);
199 g_list_free(erasure->replacement_items);
202 g_list_free(redo->erasurelist);
204 else if (redo->type == ITEM_NEW_BG_ONE || redo->type == ITEM_NEW_BG_RESIZE
205 || redo->type == ITEM_NEW_DEFAULT_BG) {
206 if (redo->bg->type == BG_PIXMAP || redo->bg->type == BG_PDF) {
207 if (redo->bg->pixbuf!=NULL) gdk_pixbuf_unref(redo->bg->pixbuf);
208 refstring_unref(redo->bg->filename);
212 else if (redo->type == ITEM_NEW_PAGE) {
213 redo->page->group = NULL;
214 delete_page(redo->page);
216 else if (redo->type == ITEM_MOVESEL || redo->type == ITEM_REPAINTSEL) {
217 g_list_free(redo->itemlist); g_list_free(redo->auxlist);
219 else if (redo->type == ITEM_RESIZESEL) {
220 g_list_free(redo->itemlist);
222 else if (redo->type == ITEM_PASTE) {
223 for (list = redo->itemlist; list!=NULL; list=list->next) {
224 it = (struct Item *)list->data;
225 if (it->type == ITEM_STROKE) {
226 gnome_canvas_points_free(it->path);
227 if (it->brush.variable_width) g_free(it->widths);
231 g_list_free(redo->itemlist);
233 else if (redo->type == ITEM_NEW_LAYER) {
236 else if (redo->type == ITEM_TEXT_EDIT || redo->type == ITEM_TEXT_ATTRIB) {
238 if (redo->type == ITEM_TEXT_ATTRIB) g_free(redo->brush);
245 update_undo_redo_enabled();
248 void clear_undo_stack(void)
252 struct UndoErasureData *erasure;
255 // for strokes, items are already in the journal, so we don't free them
256 // for erasures, we need to free the dead items
257 if (undo->type == ITEM_ERASURE || undo->type == ITEM_RECOGNIZER) {
258 for (list = undo->erasurelist; list!=NULL; list=list->next) {
259 erasure = (struct UndoErasureData *)list->data;
260 if (erasure->item->type == ITEM_STROKE) {
261 gnome_canvas_points_free(erasure->item->path);
262 if (erasure->item->brush.variable_width) g_free(erasure->item->widths);
264 if (erasure->item->type == ITEM_TEXT)
265 { g_free(erasure->item->text); g_free(erasure->item->font_name); }
266 if (erasure->item->type == ITEM_IMAGE) {
267 g_object_unref(erasure->item->image);
268 g_free(erasure->item->image_png);
270 g_free(erasure->item);
271 g_list_free(erasure->replacement_items);
274 g_list_free(undo->erasurelist);
276 else if (undo->type == ITEM_NEW_BG_ONE || undo->type == ITEM_NEW_BG_RESIZE
277 || undo->type == ITEM_NEW_DEFAULT_BG) {
278 if (undo->bg->type == BG_PIXMAP || undo->bg->type == BG_PDF) {
279 if (undo->bg->pixbuf!=NULL) gdk_pixbuf_unref(undo->bg->pixbuf);
280 refstring_unref(undo->bg->filename);
284 else if (undo->type == ITEM_MOVESEL || undo->type == ITEM_REPAINTSEL) {
285 g_list_free(undo->itemlist); g_list_free(undo->auxlist);
287 else if (undo->type == ITEM_RESIZESEL) {
288 g_list_free(undo->itemlist);
290 else if (undo->type == ITEM_PASTE) {
291 g_list_free(undo->itemlist);
293 else if (undo->type == ITEM_DELETE_LAYER) {
294 undo->layer->group = NULL;
295 delete_layer(undo->layer);
297 else if (undo->type == ITEM_DELETE_PAGE) {
298 undo->page->group = NULL;
299 delete_page(undo->page);
301 else if (undo->type == ITEM_TEXT_EDIT || undo->type == ITEM_TEXT_ATTRIB) {
303 if (undo->type == ITEM_TEXT_ATTRIB) g_free(undo->brush);
310 update_undo_redo_enabled();
313 // free data structures
315 void delete_journal(struct Journal *j)
317 while (j->pages!=NULL) {
318 delete_page((struct Page *)j->pages->data);
319 j->pages = g_list_delete_link(j->pages, j->pages);
323 void delete_page(struct Page *pg)
327 while (pg->layers!=NULL) {
328 l = (struct Layer *)pg->layers->data;
331 pg->layers = g_list_delete_link(pg->layers, pg->layers);
333 if (pg->group!=NULL) gtk_object_destroy(GTK_OBJECT(pg->group));
334 // this also destroys the background's canvas items
335 if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
336 if (pg->bg->pixbuf != NULL) gdk_pixbuf_unref(pg->bg->pixbuf);
337 if (pg->bg->filename != NULL) refstring_unref(pg->bg->filename);
343 void delete_layer(struct Layer *l)
347 while (l->items!=NULL) {
348 item = (struct Item *)l->items->data;
349 if (item->type == ITEM_STROKE && item->path != NULL)
350 gnome_canvas_points_free(item->path);
351 if (item->type == ITEM_TEXT) {
352 g_free(item->font_name); g_free(item->text);
354 if (item->type == ITEM_IMAGE) {
355 g_object_unref(item->image);
356 g_free(item->image_png);
358 // don't need to delete the canvas_item, as it's part of the group destroyed below
360 l->items = g_list_delete_link(l->items, l->items);
362 if (l->group!= NULL) gtk_object_destroy(GTK_OBJECT(l->group));
366 // referenced strings
368 struct Refstring *new_refstring(const char *s)
370 struct Refstring *rs = g_new(struct Refstring, 1);
372 if (s!=NULL) rs->s = g_strdup(s);
378 struct Refstring *refstring_ref(struct Refstring *rs)
384 void refstring_unref(struct Refstring *rs)
388 if (rs->s!=NULL) g_free(rs->s);
389 if (rs->aux!=NULL) g_free(rs->aux);
395 // some helper functions
397 int finite_sized(double x) // detect unrealistic coordinate values
399 return (finite(x) && x<1E6 && x>-1E6);
403 void get_pointer_coords(GdkEvent *event, gdouble *ret)
406 gdk_event_get_coords(event, &x, &y);
407 gnome_canvas_window_to_world(canvas, x, y, ret, ret+1);
408 ret[0] -= ui.cur_page->hoffset;
409 ret[1] -= ui.cur_page->voffset;
412 void get_current_pointer_coords(gdouble *ret)
416 gtk_widget_get_pointer((GtkWidget *)canvas, &wx, &wy);
417 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
418 gnome_canvas_window_to_world(canvas, (double)(wx + sx), (double)(wy + sy), ret, ret+1);
419 ret[0] -= ui.cur_page->hoffset;
420 ret[1] -= ui.cur_page->voffset;
423 void fix_xinput_coords(GdkEvent *event)
425 double *axes, *px, *py, axis_width;
427 int wx, wy, sx, sy, ix, iy;
429 if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
430 axes = event->button.axes;
431 px = &(event->button.x);
432 py = &(event->button.y);
433 device = event->button.device;
435 else if (event->type == GDK_MOTION_NOTIFY) {
436 axes = event->motion.axes;
437 px = &(event->motion.x);
438 py = &(event->motion.y);
439 device = event->motion.device;
441 else return; // nothing we know how to do
443 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
445 #ifdef ENABLE_XINPUT_BUGFIX
446 // fix broken events with the core pointer's location
447 if (!finite_sized(axes[0]) || !finite_sized(axes[1]) || axes[0]==0. || axes[1]==0.) {
448 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
453 gdk_window_get_origin(GTK_WIDGET(canvas)->window, &wx, &wy);
454 axis_width = device->axes[0].max - device->axes[0].min;
455 if (axis_width>EPSILON)
456 *px = (axes[0]/axis_width)*ui.screen_width + sx - wx;
457 axis_width = device->axes[1].max - device->axes[1].min;
458 if (axis_width>EPSILON)
459 *py = (axes[1]/axis_width)*ui.screen_height + sy - wy;
462 if (!finite_sized(*px) || !finite_sized(*py) || *px==0. || *py==0.) {
463 gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
468 /* with GTK+ 2.16 or earlier, the event comes from the parent gdkwindow
469 and so needs to be adjusted for scrolling */
470 if (gtk_major_version == 2 && gtk_minor_version <= 16) {
474 /* with GTK+ 2.17, events come improperly translated, and the event's
475 GdkWindow isn't even the same for ButtonDown as for MotionNotify... */
476 if (gtk_major_version == 2 && gtk_minor_version == 17) { // GTK+ 2.17 issues !!
477 gdk_window_get_position(GTK_WIDGET(canvas)->window, &wx, &wy);
485 double get_pressure_multiplier(GdkEvent *event)
491 if (event->type == GDK_MOTION_NOTIFY) {
492 axes = event->motion.axes;
493 device = event->motion.device;
496 axes = event->button.axes;
497 device = event->button.device;
500 if (device == gdk_device_get_core_pointer()
501 || device->num_axes <= 2) return 1.0;
503 rawpressure = axes[2]/(device->axes[2].max - device->axes[2].min);
504 if (!finite_sized(rawpressure)) return 1.0;
506 return ((1-rawpressure)*ui.width_minimum_multiplier + rawpressure*ui.width_maximum_multiplier);
509 void update_item_bbox(struct Item *item)
514 if (item->type == ITEM_STROKE) {
515 item->bbox.left = item->bbox.right = item->path->coords[0];
516 item->bbox.top = item->bbox.bottom = item->path->coords[1];
517 for (i=1, p=item->path->coords+2; i<item->path->num_points; i++, p+=2)
519 if (p[0] < item->bbox.left) item->bbox.left = p[0];
520 if (p[0] > item->bbox.right) item->bbox.right = p[0];
521 if (p[1] < item->bbox.top) item->bbox.top = p[1];
522 if (p[1] > item->bbox.bottom) item->bbox.bottom = p[1];
525 if (item->type == ITEM_TEXT && item->canvas_item!=NULL) {
527 g_object_get(item->canvas_item, "text_width", &w, "text_height", &h, NULL);
528 item->bbox.right = item->bbox.left + w;
529 item->bbox.bottom = item->bbox.top + h;
533 void make_page_clipbox(struct Page *pg)
535 GnomeCanvasPathDef *pg_clip;
537 pg_clip = gnome_canvas_path_def_new_sized(4);
538 gnome_canvas_path_def_moveto(pg_clip, 0., 0.);
539 gnome_canvas_path_def_lineto(pg_clip, 0., pg->height);
540 gnome_canvas_path_def_lineto(pg_clip, pg->width, pg->height);
541 gnome_canvas_path_def_lineto(pg_clip, pg->width, 0.);
542 gnome_canvas_path_def_closepath(pg_clip);
543 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), "path", pg_clip, NULL);
544 gnome_canvas_path_def_unref(pg_clip);
547 void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
549 PangoFontDescription *font_desc;
550 GnomeCanvasPoints points;
553 if (item->type == ITEM_STROKE) {
554 if (!item->brush.variable_width)
555 item->canvas_item = gnome_canvas_item_new(group,
556 gnome_canvas_line_get_type(), "points", item->path,
557 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
558 "fill-color-rgba", item->brush.color_rgba,
559 "width-units", item->brush.thickness, NULL);
561 item->canvas_item = gnome_canvas_item_new(group,
562 gnome_canvas_group_get_type(), NULL);
563 points.num_points = 2;
564 points.ref_count = 1;
565 for (j = 0; j < item->path->num_points-1; j++) {
566 points.coords = item->path->coords+2*j;
567 gnome_canvas_item_new((GnomeCanvasGroup *) item->canvas_item,
568 gnome_canvas_line_get_type(), "points", &points,
569 "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
570 "fill-color-rgba", item->brush.color_rgba,
571 "width-units", item->widths[j], NULL);
575 if (item->type == ITEM_TEXT) {
576 font_desc = pango_font_description_from_string(item->font_name);
577 pango_font_description_set_absolute_size(font_desc,
578 item->font_size*ui.zoom*PANGO_SCALE);
579 item->canvas_item = gnome_canvas_item_new(group,
580 gnome_canvas_text_get_type(),
581 "x", item->bbox.left, "y", item->bbox.top, "anchor", GTK_ANCHOR_NW,
582 "font-desc", font_desc, "fill-color-rgba", item->brush.color_rgba,
583 "text", item->text, NULL);
584 update_item_bbox(item);
586 if (item->type == ITEM_IMAGE) {
587 item->canvas_item = gnome_canvas_item_new(group,
588 gnome_canvas_pixbuf_get_type(),
589 "pixbuf", item->image,
590 "x", item->bbox.left, "y", item->bbox.top,
591 "width", item->bbox.right - item->bbox.left,
592 "height", item->bbox.bottom - item->bbox.top,
593 "width-set", TRUE, "height-set", TRUE,
598 void make_canvas_items(void)
603 GList *pagelist, *layerlist, *itemlist;
605 for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next) {
606 pg = (struct Page *)pagelist->data;
607 if (pg->group == NULL) {
608 pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
609 gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
610 make_page_clipbox(pg);
612 if (pg->bg->canvas_item == NULL) update_canvas_bg(pg);
613 for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
614 l = (struct Layer *)layerlist->data;
615 if (l->group == NULL)
616 l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
617 pg->group, gnome_canvas_group_get_type(), NULL);
618 for (itemlist = l->items; itemlist!=NULL; itemlist = itemlist->next) {
619 item = (struct Item *)itemlist->data;
620 if (item->canvas_item == NULL)
621 make_canvas_item_one(l->group, item);
627 void update_canvas_bg(struct Page *pg)
629 GnomeCanvasGroup *group;
630 GnomeCanvasPoints *seg;
631 GdkPixbuf *scaled_pix;
635 gboolean is_well_scaled;
637 if (pg->bg->canvas_item != NULL)
638 gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
639 pg->bg->canvas_item = NULL;
641 if (pg->bg->type == BG_SOLID)
643 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
644 gnome_canvas_group_get_type(), NULL);
645 group = GNOME_CANVAS_GROUP(pg->bg->canvas_item);
646 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
647 gnome_canvas_item_new(group, gnome_canvas_rect_get_type(),
648 "x1", 0., "x2", pg->width, "y1", 0., "y2", pg->height,
649 "fill-color-rgba", pg->bg->color_rgba, NULL);
650 if (pg->bg->ruling == RULING_NONE) return;
651 seg = gnome_canvas_points_new(2);
653 if (pg->bg->ruling == RULING_GRAPH) {
654 pt[1] = 0; pt[3] = pg->height;
655 for (x=RULING_GRAPHSPACING; x<pg->width-1; x+=RULING_GRAPHSPACING) {
657 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
658 "points", seg, "fill-color-rgba", RULING_COLOR,
659 "width-units", RULING_THICKNESS, NULL);
661 pt[0] = 0; pt[2] = pg->width;
662 for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
664 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
665 "points", seg, "fill-color-rgba", RULING_COLOR,
666 "width-units", RULING_THICKNESS, NULL);
668 gnome_canvas_points_free(seg);
671 pt[0] = 0; pt[2] = pg->width;
672 for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
674 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
675 "points", seg, "fill-color-rgba", RULING_COLOR,
676 "width-units", RULING_THICKNESS, NULL);
678 if (pg->bg->ruling == RULING_LINED) {
679 pt[0] = pt[2] = RULING_LEFTMARGIN;
680 pt[1] = 0; pt[3] = pg->height;
681 gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
682 "points", seg, "fill-color-rgba", RULING_MARGIN_COLOR,
683 "width-units", RULING_THICKNESS, NULL);
685 gnome_canvas_points_free(seg);
689 if (pg->bg->type == BG_PIXMAP)
691 pg->bg->pixbuf_scale = 0;
692 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
693 gnome_canvas_pixbuf_get_type(),
694 "pixbuf", pg->bg->pixbuf,
695 "width", pg->width, "height", pg->height,
696 "width-set", TRUE, "height-set", TRUE,
698 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
701 if (pg->bg->type == BG_PDF)
703 if (pg->bg->pixbuf == NULL) return;
704 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
705 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
707 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
708 gnome_canvas_pixbuf_get_type(),
709 "pixbuf", pg->bg->pixbuf,
710 "width-in-pixels", TRUE, "height-in-pixels", TRUE,
713 pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
714 gnome_canvas_pixbuf_get_type(),
715 "pixbuf", pg->bg->pixbuf,
716 "width", pg->width, "height", pg->height,
717 "width-set", TRUE, "height-set", TRUE,
719 lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
723 gboolean is_visible(struct Page *pg)
725 GtkAdjustment *v_adj;
728 if (!ui.view_continuous) return (pg == ui.cur_page);
729 v_adj = gtk_layout_get_vadjustment(GTK_LAYOUT(canvas));
730 ytop = v_adj->value/ui.zoom;
731 ybot = (v_adj->value + v_adj->page_size) / ui.zoom;
732 return (MAX(ytop, pg->voffset) < MIN(ybot, pg->voffset+pg->height));
735 void rescale_bg_pixmaps(void)
740 gboolean is_well_scaled;
741 gdouble zoom_to_request;
743 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
744 pg = (struct Page *)pglist->data;
745 // in progressive mode we scale only visible pages
746 if (ui.progressive_bg && !is_visible(pg)) continue;
748 if (pg->bg->type == BG_PIXMAP && pg->bg->canvas_item!=NULL) {
749 g_object_get(G_OBJECT(pg->bg->canvas_item), "pixbuf", &pix, NULL);
750 if (pix!=pg->bg->pixbuf)
751 gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pg->bg->pixbuf, NULL);
752 pg->bg->pixbuf_scale = 0;
754 if (pg->bg->type == BG_PDF) {
755 // make pixmap scale to correct size if current one is wrong
756 is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
757 && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
758 if (pg->bg->canvas_item != NULL && !is_well_scaled) {
759 g_object_get(pg->bg->canvas_item, "width-in-pixels", &is_well_scaled, NULL);
761 gnome_canvas_item_set(pg->bg->canvas_item,
762 "width", pg->width, "height", pg->height,
763 "width-in-pixels", FALSE, "height-in-pixels", FALSE,
764 "width-set", TRUE, "height-set", TRUE,
767 // request an asynchronous update to a better pixmap if needed
768 zoom_to_request = MIN(ui.zoom, MAX_SAFE_RENDER_DPI/72.0);
769 if (pg->bg->pixbuf_scale == zoom_to_request) continue;
770 if (add_bgpdf_request(pg->bg->file_page_seq, zoom_to_request))
771 pg->bg->pixbuf_scale = zoom_to_request;
776 gboolean have_intersect(struct BBox *a, struct BBox *b)
778 return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
779 (MAX(a->left, b->left) <= MIN(a->right, b->right));
782 /* In libgnomecanvas 2.10.0, the lower/raise functions fail to update
783 correctly the end of the group's item list. We try to work around this.
784 DON'T USE gnome_canvas_item_raise/lower directly !! */
786 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
790 i1 = g_list_index(g->item_list, item);
791 if (i1 == -1) return;
793 if (after == NULL) i2 = -1;
794 else i2 = g_list_index(g->item_list, after);
796 if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
797 if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
799 // BUGFIX for libgnomecanvas
800 g->item_list_end = g_list_last(g->item_list);
803 void rgb_to_gdkcolor(guint rgba, GdkColor *color)
806 color->red = ((rgba>>24)&0xff)*0x101;
807 color->green = ((rgba>>16)&0xff)*0x101;
808 color->blue = ((rgba>>8)&0xff)*0x101;
811 guint32 gdkcolor_to_rgba(GdkColor gdkcolor, guint16 alpha)
813 guint32 rgba = ((gdkcolor.red & 0xff00) << 16) |
814 ((gdkcolor.green & 0xff00) << 8) |
815 ((gdkcolor.blue & 0xff00) ) |
816 ((alpha & 0xff00) >> 8);
821 // some interface functions
823 void update_thickness_buttons(void)
825 if (ui.selection!=NULL || ui.toolno[ui.cur_mapping] >= NUM_STROKE_TOOLS) {
826 gtk_toggle_tool_button_set_active(
827 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
829 switch (ui.cur_brush->thickness_no) {
831 gtk_toggle_tool_button_set_active(
832 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
834 case THICKNESS_MEDIUM:
835 gtk_toggle_tool_button_set_active(
836 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
838 case THICKNESS_THICK:
839 gtk_toggle_tool_button_set_active(
840 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
843 gtk_toggle_tool_button_set_active(
844 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
848 void update_color_buttons(void)
851 GtkColorButton *colorbutton;
853 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
854 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
855 gtk_toggle_tool_button_set_active(
856 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
858 switch (ui.cur_brush->color_no) {
860 gtk_toggle_tool_button_set_active(
861 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
864 gtk_toggle_tool_button_set_active(
865 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
868 gtk_toggle_tool_button_set_active(
869 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
872 gtk_toggle_tool_button_set_active(
873 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
876 gtk_toggle_tool_button_set_active(
877 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
879 case COLOR_LIGHTBLUE:
880 gtk_toggle_tool_button_set_active(
881 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
883 case COLOR_LIGHTGREEN:
884 gtk_toggle_tool_button_set_active(
885 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
888 gtk_toggle_tool_button_set_active(
889 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
892 gtk_toggle_tool_button_set_active(
893 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
896 gtk_toggle_tool_button_set_active(
897 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
900 gtk_toggle_tool_button_set_active(
901 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
904 gtk_toggle_tool_button_set_active(
905 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
908 colorbutton = GTK_COLOR_BUTTON(GET_COMPONENT("buttonColorChooser"));
909 if ((ui.toolno[ui.cur_mapping] != TOOL_PEN &&
910 ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER &&
911 ui.toolno[ui.cur_mapping] != TOOL_TEXT))
912 gdkcolor.red = gdkcolor.blue = gdkcolor.green = 0;
913 else rgb_to_gdkcolor(ui.cur_brush->color_rgba, &gdkcolor);
914 gtk_color_button_set_color(colorbutton, &gdkcolor);
915 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
916 gtk_color_button_set_alpha(colorbutton,
917 (ui.cur_brush->color_rgba&0xff)*0x101);
918 gtk_color_button_set_use_alpha(colorbutton, TRUE);
920 gtk_color_button_set_alpha(colorbutton, 0xffff);
921 gtk_color_button_set_use_alpha(colorbutton, FALSE);
925 void update_tool_buttons(void)
927 switch(ui.toolno[ui.cur_mapping]) {
929 gtk_toggle_tool_button_set_active(
930 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
933 gtk_toggle_tool_button_set_active(
934 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
936 case TOOL_HIGHLIGHTER:
937 gtk_toggle_tool_button_set_active(
938 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
941 gtk_toggle_tool_button_set_active(
942 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
945 gtk_toggle_tool_button_set_active(
946 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonImage")), TRUE);
948 case TOOL_SELECTREGION:
949 gtk_toggle_tool_button_set_active(
950 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
952 case TOOL_SELECTRECT:
953 gtk_toggle_tool_button_set_active(
954 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
957 gtk_toggle_tool_button_set_active(
958 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
961 gtk_toggle_tool_button_set_active(
962 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
966 gtk_toggle_tool_button_set_active(
967 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
968 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
969 gtk_toggle_tool_button_set_active(
970 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
971 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
973 update_thickness_buttons();
974 update_color_buttons();
977 void update_tool_menu(void)
979 switch(ui.toolno[0]) {
981 gtk_check_menu_item_set_active(
982 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
985 gtk_check_menu_item_set_active(
986 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
988 case TOOL_HIGHLIGHTER:
989 gtk_check_menu_item_set_active(
990 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
993 gtk_check_menu_item_set_active(
994 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
997 gtk_check_menu_item_set_active(
998 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsImage")), TRUE);
1000 case TOOL_SELECTREGION:
1001 gtk_check_menu_item_set_active(
1002 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
1004 case TOOL_SELECTRECT:
1005 gtk_check_menu_item_set_active(
1006 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
1008 case TOOL_VERTSPACE:
1009 gtk_check_menu_item_set_active(
1010 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
1013 gtk_check_menu_item_set_active(
1014 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
1018 gtk_check_menu_item_set_active(
1019 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
1020 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
1021 gtk_check_menu_item_set_active(
1022 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
1023 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
1026 void update_ruler_indicator(void)
1028 gtk_toggle_tool_button_set_active(
1029 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")),
1030 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
1031 gtk_toggle_tool_button_set_active(
1032 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")),
1033 ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
1034 gtk_check_menu_item_set_active(
1035 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")),
1036 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
1037 gtk_check_menu_item_set_active(
1038 GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")),
1039 ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
1042 void update_color_menu(void)
1044 if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN
1045 && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
1046 gtk_check_menu_item_set_active(
1047 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
1049 switch (ui.cur_brush->color_no) {
1051 gtk_check_menu_item_set_active(
1052 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
1055 gtk_check_menu_item_set_active(
1056 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
1059 gtk_check_menu_item_set_active(
1060 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
1063 gtk_check_menu_item_set_active(
1064 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
1067 gtk_check_menu_item_set_active(
1068 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
1070 case COLOR_LIGHTBLUE:
1071 gtk_check_menu_item_set_active(
1072 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
1074 case COLOR_LIGHTGREEN:
1075 gtk_check_menu_item_set_active(
1076 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
1079 gtk_check_menu_item_set_active(
1080 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
1083 gtk_check_menu_item_set_active(
1084 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
1087 gtk_check_menu_item_set_active(
1088 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
1091 gtk_check_menu_item_set_active(
1092 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
1095 gtk_check_menu_item_set_active(
1096 GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
1100 void update_pen_props_menu(void)
1102 switch(ui.brushes[0][TOOL_PEN].thickness_no) {
1103 case THICKNESS_VERYFINE:
1104 gtk_check_menu_item_set_active(
1105 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
1107 case THICKNESS_FINE:
1108 gtk_check_menu_item_set_active(
1109 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
1111 case THICKNESS_MEDIUM:
1112 gtk_check_menu_item_set_active(
1113 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
1115 case THICKNESS_THICK:
1116 gtk_check_menu_item_set_active(
1117 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
1119 case THICKNESS_VERYTHICK:
1120 gtk_check_menu_item_set_active(
1121 GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
1126 void update_eraser_props_menu(void)
1128 switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
1129 case THICKNESS_FINE:
1130 gtk_check_menu_item_set_active(
1131 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
1133 case THICKNESS_MEDIUM:
1134 gtk_check_menu_item_set_active(
1135 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
1137 case THICKNESS_THICK:
1138 gtk_check_menu_item_set_active(
1139 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
1143 gtk_check_menu_item_set_active(
1144 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
1145 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
1146 gtk_check_menu_item_set_active(
1147 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
1148 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
1149 gtk_check_menu_item_set_active(
1150 GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
1151 ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
1154 void update_highlighter_props_menu(void)
1156 switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
1157 case THICKNESS_FINE:
1158 gtk_check_menu_item_set_active(
1159 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
1161 case THICKNESS_MEDIUM:
1162 gtk_check_menu_item_set_active(
1163 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
1165 case THICKNESS_THICK:
1166 gtk_check_menu_item_set_active(
1167 GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
1172 void update_mappings_menu_linkings(void)
1174 switch (ui.linked_brush[1]) {
1176 gtk_check_menu_item_set_active(
1177 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
1180 gtk_check_menu_item_set_active(
1181 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
1184 gtk_check_menu_item_set_active(
1185 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
1188 switch (ui.linked_brush[2]) {
1190 gtk_check_menu_item_set_active(
1191 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
1194 gtk_check_menu_item_set_active(
1195 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
1198 gtk_check_menu_item_set_active(
1199 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
1204 void update_mappings_menu(void)
1206 gtk_widget_set_sensitive(GET_COMPONENT("optionsButtonMappings"), ui.use_xinput);
1207 gtk_widget_set_sensitive(GET_COMPONENT("optionsPressureSensitive"), ui.use_xinput);
1208 gtk_check_menu_item_set_active(
1209 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
1210 gtk_check_menu_item_set_active(
1211 GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsPressureSensitive")), ui.pressure_sensitivity);
1213 switch(ui.toolno[1]) {
1215 gtk_check_menu_item_set_active(
1216 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
1219 gtk_check_menu_item_set_active(
1220 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
1222 case TOOL_HIGHLIGHTER:
1223 gtk_check_menu_item_set_active(
1224 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
1227 gtk_check_menu_item_set_active(
1228 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
1231 gtk_check_menu_item_set_active(
1232 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Image")), TRUE);
1234 case TOOL_SELECTREGION:
1235 gtk_check_menu_item_set_active(
1236 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
1238 case TOOL_SELECTRECT:
1239 gtk_check_menu_item_set_active(
1240 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
1242 case TOOL_VERTSPACE:
1243 gtk_check_menu_item_set_active(
1244 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
1247 switch(ui.toolno[2]) {
1249 gtk_check_menu_item_set_active(
1250 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
1253 gtk_check_menu_item_set_active(
1254 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
1256 case TOOL_HIGHLIGHTER:
1257 gtk_check_menu_item_set_active(
1258 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
1261 gtk_check_menu_item_set_active(
1262 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
1265 gtk_check_menu_item_set_active(
1266 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Image")), TRUE);
1268 case TOOL_SELECTREGION:
1269 gtk_check_menu_item_set_active(
1270 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
1272 case TOOL_SELECTRECT:
1273 gtk_check_menu_item_set_active(
1274 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
1276 case TOOL_VERTSPACE:
1277 gtk_check_menu_item_set_active(
1278 GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1281 update_mappings_menu_linkings();
1284 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1287 struct Layer *layer;
1292 /* re-show all the layers of the old page */
1293 if (ui.cur_page != NULL)
1294 for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1295 layer = (struct Layer *)list->data;
1296 if (layer->group!=NULL)
1297 gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1300 ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1301 ui.layerno = ui.cur_page->nlayers-1;
1302 ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1303 update_page_stuff();
1304 if (ui.progressive_bg) rescale_bg_pixmaps();
1306 if (rescroll) { // scroll and force a refresh
1307 /* -- this seems to cause some display bugs ??
1308 gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1309 ui.cur_page->voffset*ui.zoom); */
1310 gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1311 cy = ui.cur_page->voffset*ui.zoom;
1312 gnome_canvas_scroll_to(canvas, cx, cy);
1315 gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1316 else if (!ui.view_continuous)
1317 gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1321 void update_page_stuff(void)
1324 GtkComboBox *layerbox;
1327 GtkSpinButton *spin;
1329 double vertpos, maxwidth;
1331 // move the page groups to their rightful locations or hide them
1332 if (ui.view_continuous) {
1335 for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1336 pg = (struct Page *)pglist->data;
1337 if (pg->group!=NULL) {
1338 pg->hoffset = 0.; pg->voffset = vertpos;
1339 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1340 "x", pg->hoffset, "y", pg->voffset, NULL);
1341 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1343 vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1344 if (pg->width > maxwidth) maxwidth = pg->width;
1346 vertpos -= VIEW_CONTINUOUS_SKIP;
1347 gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1349 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1350 pg = (struct Page *)pglist->data;
1351 if (pg == ui.cur_page && pg->group!=NULL) {
1352 pg->hoffset = 0.; pg->voffset = 0.;
1353 gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group),
1354 "x", pg->hoffset, "y", pg->voffset, NULL);
1355 gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1357 if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1360 gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1363 // update the page / layer info at bottom of screen
1365 spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1366 ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1367 gtk_spin_button_set_range(spin, 1, journal.npages+1);
1368 /* npages+1 will be used to create a new page at end */
1369 gtk_spin_button_set_value(spin, ui.pageno+1);
1370 g_snprintf(tmp, 10, _(" of %d"), journal.npages);
1371 gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1373 layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1374 if (ui.layerbox_length == 0) {
1375 gtk_combo_box_prepend_text(layerbox, _("Background"));
1376 ui.layerbox_length++;
1378 while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1379 gtk_combo_box_remove_text(layerbox, 0);
1380 ui.layerbox_length--;
1382 while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1383 g_snprintf(tmp, 10, _("Layer %d"), ui.layerbox_length++);
1384 gtk_combo_box_prepend_text(layerbox, tmp);
1386 gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1387 ui.in_update_page_stuff = FALSE;
1389 gtk_container_forall(GTK_CONTAINER(layerbox), unset_flags, (gpointer)GTK_CAN_FOCUS);
1391 // update the paper-style menu radio buttons
1393 if (ui.view_continuous)
1394 gtk_check_menu_item_set_active(
1395 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1397 gtk_check_menu_item_set_active(
1398 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1400 if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1401 switch (ui.cur_page->bg->color_no) {
1403 gtk_check_menu_item_set_active(
1404 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1407 gtk_check_menu_item_set_active(
1408 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1411 gtk_check_menu_item_set_active(
1412 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1415 gtk_check_menu_item_set_active(
1416 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1419 gtk_check_menu_item_set_active(
1420 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1423 gtk_check_menu_item_set_active(
1424 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1427 gtk_check_menu_item_set_active(
1428 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1431 switch (ui.cur_page->bg->ruling) {
1433 gtk_check_menu_item_set_active(
1434 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1437 gtk_check_menu_item_set_active(
1438 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1441 gtk_check_menu_item_set_active(
1442 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1445 gtk_check_menu_item_set_active(
1446 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1450 gtk_check_menu_item_set_active(
1451 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1452 gtk_check_menu_item_set_active(
1453 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1456 // enable/disable the page/layer menu items and toolbar buttons
1458 gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"),
1459 ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1460 gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1461 ui.cur_page->bg->type == BG_SOLID);
1463 gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1464 gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1465 gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1466 gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1467 gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1468 gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1469 gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1470 gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1472 gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1473 gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1475 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1476 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1479 void update_toolbar_and_menu(void)
1481 update_tool_buttons(); // takes care of other toolbar buttons as well
1483 update_color_menu();
1484 update_pen_props_menu();
1485 update_eraser_props_menu();
1486 update_highlighter_props_menu();
1487 update_mappings_menu();
1489 gtk_toggle_tool_button_set_active(
1490 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1491 gtk_check_menu_item_set_active(
1492 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1495 void update_file_name(char *filename)
1498 if (ui.filename != NULL) g_free(ui.filename);
1499 ui.filename = filename;
1500 if (filename == NULL) {
1501 gtk_window_set_title(GTK_WINDOW (winMain), _("Xournal"));
1504 p = g_utf8_strrchr(filename, -1, '/');
1505 if (p == NULL) p = filename;
1506 else p = g_utf8_next_char(p);
1507 g_snprintf(tmp, 100, _("Xournal - %s"), p);
1508 gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1509 new_mru_entry(filename);
1511 if (filename[0]=='/') {
1512 if (ui.default_path!=NULL) g_free(ui.default_path);
1513 ui.default_path = g_path_get_dirname(filename);
1517 void update_undo_redo_enabled(void)
1519 gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1520 gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1521 gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1522 gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1525 void update_copy_paste_enabled(void)
1527 gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1528 gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1529 gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_item_type!=ITEM_TEXT);
1530 gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1531 gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1532 gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1533 gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_item_type!=ITEM_TEXT);
1536 void update_mapping_linkings(int toolno)
1540 for (i = 1; i<=NUM_BUTTONS; i++) {
1541 if (ui.linked_brush[i] == BRUSH_LINKED) {
1542 if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1543 g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1545 if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1546 ui.linked_brush[i] = BRUSH_STATIC;
1547 if (i==1 || i==2) update_mappings_menu_linkings();
1552 void set_cur_color(int color_no, guint color_rgba)
1554 int which_mapping, tool;
1556 if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) tool = TOOL_HIGHLIGHTER;
1557 else tool = TOOL_PEN;
1558 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1559 which_mapping = ui.cur_mapping;
1560 else which_mapping = 0;
1562 ui.brushes[which_mapping][tool].color_no = color_no;
1563 if (tool == TOOL_HIGHLIGHTER && (color_rgba & 0xff) == 0xff)
1564 ui.brushes[which_mapping][tool].color_rgba = color_rgba & ui.hiliter_alpha_mask;
1566 ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1567 update_mapping_linkings(tool);
1570 void recolor_temp_text(int color_no, guint color_rgba)
1574 if (ui.cur_item_type!=ITEM_TEXT) return;
1575 if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1577 undo->type = ITEM_TEXT_ATTRIB;
1578 undo->item = ui.cur_item;
1579 undo->str = g_strdup(ui.cur_item->font_name);
1580 undo->val_x = ui.cur_item->font_size;
1581 undo->brush = (struct Brush *)g_memdup(&(ui.cur_item->brush), sizeof(struct Brush));
1583 ui.cur_item->brush.color_no = color_no;
1584 ui.cur_item->brush.color_rgba = color_rgba;
1585 rgb_to_gdkcolor(color_rgba, &gdkcolor);
1586 gtk_widget_modify_text(ui.cur_item->widget, GTK_STATE_NORMAL, &gdkcolor);
1587 gtk_widget_grab_focus(ui.cur_item->widget);
1590 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1592 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1593 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1596 else if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_TOOL_BUTTON) {
1597 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1601 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1603 if (ui.cur_item_type == ITEM_TEXT)
1604 recolor_temp_text(color_no, color_rgba);
1606 if (ui.selection != NULL) {
1607 recolor_selection(color_no, color_rgba);
1608 update_color_buttons();
1609 update_color_menu();
1612 if (ui.toolno[ui.cur_mapping] != TOOL_PEN && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER
1613 && ui.toolno[ui.cur_mapping] != TOOL_TEXT) {
1614 if (ui.selection != NULL) return;
1617 ui.toolno[ui.cur_mapping] = TOOL_PEN;
1618 ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1619 update_tool_buttons();
1623 set_cur_color(color_no, color_rgba);
1624 update_color_buttons();
1625 update_color_menu();
1629 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1633 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1634 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1637 if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1641 if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1643 if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1644 rethicken_selection(val);
1645 update_thickness_buttons();
1648 if (tool >= NUM_STROKE_TOOLS) {
1649 update_thickness_buttons(); // undo illegal button selection
1653 if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1654 which_mapping = ui.cur_mapping;
1655 else which_mapping = 0;
1656 if (ui.brushes[which_mapping][tool].thickness_no == val) return;
1658 ui.brushes[which_mapping][tool].thickness_no = val;
1659 ui.brushes[which_mapping][tool].thickness = predef_thickness[tool][val];
1660 update_mapping_linkings(tool);
1662 update_thickness_buttons();
1663 if (tool == TOOL_PEN) update_pen_props_menu();
1664 if (tool == TOOL_ERASER) update_eraser_props_menu();
1665 if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1669 void process_papercolor_activate(GtkMenuItem *menuitem, int color, guint rgba)
1675 if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1676 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1680 if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages || color == COLOR_OTHER)
1681 gtk_check_menu_item_set_active(
1682 GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1686 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1687 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1688 if (pg->bg->type == BG_SOLID && pg->bg->color_rgba != rgba) {
1690 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1691 undo->multiop |= MULTIOP_CONT_REDO;
1693 undo->type = ITEM_NEW_BG_ONE;
1695 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1696 undo->bg->canvas_item = NULL;
1698 pg->bg->color_no = color;
1699 pg->bg->color_rgba = rgba;
1700 update_canvas_bg(pg);
1702 if (!ui.bg_apply_all_pages) break;
1704 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1707 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1711 gboolean hasdone, must_upd;
1713 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1716 if (ui.bg_apply_all_pages)
1717 gtk_check_menu_item_set_active(
1718 GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1723 for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1724 if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1725 if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1727 undo->type = ITEM_NEW_BG_ONE;
1728 if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1729 undo->multiop |= MULTIOP_CONT_REDO;
1732 undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1733 undo->bg->canvas_item = NULL;
1735 if (pg->bg->type != BG_SOLID) {
1736 pg->bg->type = BG_SOLID;
1737 pg->bg->color_no = COLOR_WHITE;
1738 pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1739 pg->bg->filename = NULL;
1740 pg->bg->pixbuf = NULL;
1743 pg->bg->ruling = style;
1744 update_canvas_bg(pg);
1746 if (!ui.bg_apply_all_pages) break;
1748 if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1749 if (must_upd) update_page_stuff();
1752 #ifndef GTK_STOCK_DISCARD
1753 #define GTK_STOCK_DISCARD GTK_STOCK_NO
1756 gboolean ok_to_close(void)
1759 GtkResponseType response;
1761 if (ui.saved) return TRUE;
1762 dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1763 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("Save changes to '%s'?"),
1764 (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1765 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_DISCARD, GTK_RESPONSE_NO);
1766 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
1767 gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1768 gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1769 response = gtk_dialog_run(GTK_DIALOG (dialog));
1770 gtk_widget_destroy(dialog);
1771 if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT)
1772 return FALSE; // aborted
1773 if (response == GTK_RESPONSE_YES) {
1774 on_fileSave_activate(NULL, NULL);
1775 if (!ui.saved) return FALSE; // if save failed, then we abort
1780 // send the focus back to the appropriate widget
1782 void reset_focus(void)
1784 if (ui.cur_item_type == ITEM_TEXT)
1785 gtk_widget_grab_focus(ui.cur_item->widget);
1787 gtk_widget_grab_focus(GTK_WIDGET(canvas));
1790 // selection / clipboard stuff
1792 void reset_selection(void)
1794 if (ui.selection == NULL) return;
1795 if (ui.selection->canvas_item != NULL)
1796 gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1797 g_list_free(ui.selection->items);
1798 g_free(ui.selection);
1799 ui.selection = NULL;
1800 update_copy_paste_enabled();
1801 update_color_menu();
1802 update_thickness_buttons();
1803 update_color_buttons();
1804 update_font_button();
1808 void move_journal_items_by(GList *itemlist, double dx, double dy,
1809 struct Layer *l1, struct Layer *l2, GList *depths)
1812 GnomeCanvasItem *refitem;
1817 while (itemlist!=NULL) {
1818 item = (struct Item *)itemlist->data;
1819 if (item->type == ITEM_STROKE)
1820 for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1821 { pt[0] += dx; pt[1] += dy; }
1822 if (item->type == ITEM_STROKE || item->type == ITEM_TEXT ||
1823 item->type == ITEM_TEMP_TEXT || item->type == ITEM_IMAGE) {
1824 item->bbox.left += dx;
1825 item->bbox.right += dx;
1826 item->bbox.top += dy;
1827 item->bbox.bottom += dy;
1830 // find out where to insert
1831 if (depths != NULL) {
1832 if (depths->data == NULL) link = l2->items;
1834 link = g_list_find(l2->items, depths->data);
1835 if (link != NULL) link = link->next;
1838 l2->items = g_list_insert_before(l2->items, link, item);
1840 l1->items = g_list_remove(l1->items, item);
1843 if (depths != NULL) { // also raise/lower the canvas items
1844 if (item->canvas_item!=NULL) {
1845 if (depths->data == NULL) link = NULL;
1846 else link = g_list_find(l2->items, depths->data);
1847 if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1848 else refitem = NULL;
1849 lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1851 depths = depths->next;
1853 itemlist = itemlist->next;
1857 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1858 double offset_x, double offset_y)
1862 double mean_scaling, temp;
1864 GnomeCanvasGroup *group;
1867 /* geometric mean of x and y scalings = rescaling for stroke widths
1868 and for text font sizes */
1869 mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1871 for (list = itemlist; list != NULL; list = list->next) {
1872 item = (struct Item *)list->data;
1873 if (item->type == ITEM_STROKE) {
1874 item->brush.thickness = item->brush.thickness * mean_scaling;
1875 for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1876 pt[0] = pt[0]*scaling_x + offset_x;
1877 pt[1] = pt[1]*scaling_y + offset_y;
1879 if (item->brush.variable_width)
1880 for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1881 *wid = *wid * mean_scaling;
1883 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1884 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1885 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1886 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1887 if (item->bbox.left > item->bbox.right) {
1888 temp = item->bbox.left;
1889 item->bbox.left = item->bbox.right;
1890 item->bbox.right = temp;
1892 if (item->bbox.top > item->bbox.bottom) {
1893 temp = item->bbox.top;
1894 item->bbox.top = item->bbox.bottom;
1895 item->bbox.bottom = temp;
1898 if (item->type == ITEM_TEXT) {
1899 /* must scale about NW corner -- all other points of the text box
1900 are font- and zoom-dependent, so scaling about center of text box
1901 couldn't be undone properly. FIXME? */
1902 item->font_size *= mean_scaling;
1903 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1904 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1906 if (item->type == ITEM_IMAGE) {
1907 item->bbox.left = item->bbox.left*scaling_x + offset_x;
1908 item->bbox.right = item->bbox.right*scaling_x + offset_x;
1909 item->bbox.top = item->bbox.top*scaling_y + offset_y;
1910 item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1911 if (item->bbox.left > item->bbox.right) {
1912 temp = item->bbox.left;
1913 item->bbox.left = item->bbox.right;
1914 item->bbox.right = temp;
1916 if (item->bbox.top > item->bbox.bottom) {
1917 temp = item->bbox.top;
1918 item->bbox.top = item->bbox.bottom;
1919 item->bbox.bottom = temp;
1923 if (item->canvas_item!=NULL) {
1924 group = (GnomeCanvasGroup *) item->canvas_item->parent;
1925 gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1926 make_canvas_item_one(group, item);
1931 // Switch between button mappings
1933 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1934 click event is being processed ... or if ui.button_switch_mapping is
1935 enabled and mappings are switched (but even then, canvas should have
1936 a pointer grab from the initial click that switched the mapping) */
1938 void switch_mapping(int m)
1940 if (ui.cur_mapping == m) return;
1943 if (ui.toolno[m] < NUM_STROKE_TOOLS)
1944 ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1945 if (ui.toolno[m] == TOOL_TEXT)
1946 ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1947 if (m==0) ui.which_unswitch_button = 0;
1949 update_tool_buttons();
1950 update_color_menu();
1954 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1956 if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1957 if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1958 if (ui.toolno[m] == tool) return;
1962 ui.toolno[m] = tool;
1963 if (ui.linked_brush[m] == BRUSH_COPIED) {
1964 ui.linked_brush[m] = BRUSH_STATIC;
1965 update_mappings_menu_linkings();
1969 // update the ordering of components in the main vbox
1971 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1972 {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1974 void update_vbox_order(int *order)
1978 GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1979 gboolean present[VBOX_MAIN_NITEMS];
1981 for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1983 for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1984 if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1985 present[order[i]] = TRUE;
1986 child = GET_COMPONENT(vbox_component_names[order[i]]);
1987 gtk_box_reorder_child(vboxMain, child, j++);
1988 gtk_widget_show(child);
1990 for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1991 if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1994 gchar *make_cur_font_name(void)
1999 if (ui.cur_item_type == ITEM_TEXT)
2000 str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
2001 else if (ui.selection!=NULL && ui.selection->items!=NULL &&
2002 ui.selection->items->next==NULL &&
2003 (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
2004 str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
2006 str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
2010 void update_font_button(void)
2014 str = make_cur_font_name();
2015 gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
2019 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
2021 return GTK_WIDGET_SENSITIVE(widget);
2024 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
2026 if (ui.cur_item_type == ITEM_TEXT) {
2027 g_signal_stop_emission_by_name(widget, "can-activate-accel");
2030 return GTK_WIDGET_SENSITIVE(widget);
2033 void allow_all_accels(void)
2035 g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
2036 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2037 g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
2038 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2039 g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
2040 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2041 g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
2042 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2043 g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
2044 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2045 g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
2046 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2047 g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
2048 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2049 g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
2050 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2051 g_signal_connect((gpointer) GET_COMPONENT("editCut"),
2052 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2053 g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
2054 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2055 g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
2056 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2057 g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
2058 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2059 g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
2060 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2061 g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
2062 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2063 g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
2064 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2065 g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
2066 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2067 g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
2068 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2069 g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
2070 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2071 g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
2072 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
2073 g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
2074 "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
2075 g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
2076 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2077 g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
2078 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2079 g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
2080 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2081 g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
2082 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2083 g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
2084 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2085 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
2086 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2087 g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
2088 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2089 g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
2090 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2091 g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
2092 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2093 g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
2094 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2095 g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
2096 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2097 g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
2098 "can-activate-accel", G_CALLBACK(can_accel), NULL);
2101 void add_scroll_bindings(void)
2103 GtkBindingSet *binding_set;
2105 binding_set = gtk_binding_set_by_class(
2106 G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
2107 gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
2108 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2109 G_TYPE_BOOLEAN, FALSE);
2110 gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
2111 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2112 G_TYPE_BOOLEAN, FALSE);
2113 gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
2114 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2115 G_TYPE_BOOLEAN, FALSE);
2116 gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
2117 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2118 G_TYPE_BOOLEAN, FALSE);
2119 gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
2120 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2121 G_TYPE_BOOLEAN, TRUE);
2122 gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
2123 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD,
2124 G_TYPE_BOOLEAN, TRUE);
2125 gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
2126 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2127 G_TYPE_BOOLEAN, TRUE);
2128 gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
2129 "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD,
2130 G_TYPE_BOOLEAN, TRUE);
2133 gboolean is_event_within_textview(GdkEventButton *event)
2137 if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2138 get_pointer_coords((GdkEvent *)event, pt);
2139 if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2140 if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2144 void hide_unimplemented(void)
2146 gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2147 gtk_widget_hide(GET_COMPONENT("journalFlatten"));
2148 gtk_widget_hide(GET_COMPONENT("helpIndex"));
2150 /* config file only works with glib 2.6 and beyond */
2151 if (glib_minor_version<6) {
2152 gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2153 gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2155 /* gtkprint only works with gtk+ 2.10 and beyond */
2156 if (gtk_check_version(2, 10, 0)) {
2157 gtk_widget_hide(GET_COMPONENT("filePrint"));
2160 /* screenshot feature doesn't work yet in Win32 */
2162 gtk_widget_hide(GET_COMPONENT("journalScreenshot"));
2166 // toggle fullscreen mode
2167 void do_fullscreen(gboolean active)
2170 ui.fullscreen = active;
2171 gtk_check_menu_item_set_active(
2172 GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2173 gtk_toggle_tool_button_set_active(
2174 GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2176 if (ui.fullscreen) {
2178 gtk_window_get_size(GTK_WINDOW(winMain), &ui.pre_fullscreen_width, &ui.pre_fullscreen_height);
2179 gtk_widget_set_size_request(GTK_WIDGET(winMain), gdk_screen_width(),
2180 gdk_screen_height());
2182 gtk_window_fullscreen(GTK_WINDOW(winMain));
2186 gtk_widget_set_size_request(GTK_WIDGET(winMain), -1, -1);
2187 gtk_window_resize(GTK_WINDOW(winMain), ui.pre_fullscreen_width,
2188 ui.pre_fullscreen_height);
2190 gtk_window_unfullscreen(GTK_WINDOW(winMain));
2193 update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2196 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2197 elements receive XInput events that they can't handle properly */
2199 // prevent interface items from getting bogus XInput events
2201 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2204 if (event->type == GDK_MOTION_NOTIFY &&
2205 event->motion.device != gdk_device_get_core_pointer())
2207 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2208 event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2209 event->button.device != gdk_device_get_core_pointer())
2214 /* Code to turn an extended input event into a core event and send it to
2215 a different GdkWindow -- e.g. could be used when a click in a text edit box
2216 gets sent to the canvas instead due to incorrect event translation.
2217 We now turn off xinput altogether while editing text under GTK+ 2.17, so
2218 this isn't needed any more... but could become useful again someday!
2222 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2228 if (user_data) window = (GdkWindow *)user_data;
2229 else window = widget->window;
2231 if (event->type == GDK_MOTION_NOTIFY &&
2232 event->motion.device != gdk_device_get_core_pointer()) {
2233 // printf("fixing motion\n");
2234 gdk_window_get_pointer(window, &ix, &iy, NULL);
2235 event->motion.x = ix; event->motion.y = iy;
2236 event->motion.device = gdk_device_get_core_pointer();
2237 g_object_unref(event->motion.window);
2238 event->motion.window = g_object_ref(window);
2239 gtk_widget_event(widget, event);
2242 if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2243 event->button.device != gdk_device_get_core_pointer()) {
2244 // printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2245 gdk_window_get_pointer(window, &ix, &iy, NULL);
2246 event->button.x = ix; event->button.y = iy;
2247 event->button.device = gdk_device_get_core_pointer();
2248 g_object_unref(event->button.window);
2249 event->button.window = g_object_ref(window);
2250 // printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2251 gtk_widget_event(widget, event);
2259 /* When enter is pressed into page spinbox, send focus back to canvas. */
2261 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2267 /* recursively unset widget flags */
2269 void unset_flags(GtkWidget *w, gpointer flag)
2271 GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2272 if(GTK_IS_CONTAINER(w))
2273 gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2276 /* reset focus when a key or button press event reaches someone, or when the
2277 page-number spin button should relinquish control... */
2279 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2281 if (w == GET_COMPONENT("hbox1")) {
2282 /* the event won't be processed since the hbox1 doesn't know what to do with it,
2283 so we might as well kill it and avoid confusing ourselves when it gets
2284 propagated further ... */
2287 if (w == GET_COMPONENT("spinPageNo")) {
2288 /* we let the spin button take care of itself, and don't steal its focus,
2289 unless the user presses Esc or Tab (in those cases we intervene) */
2290 if (ev->type != GDK_KEY_PRESS) return FALSE;
2291 if (ev->key.keyval == GDK_Escape)
2292 gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2293 else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2294 return FALSE; // let the spin button process it
2297 // otherwise, we want to make sure the canvas or text item gets focus back...
2302 void install_focus_hooks(GtkWidget *w, gpointer data)
2304 if (w == NULL) return;
2305 g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2306 g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2307 if (GTK_IS_MENU_ITEM(w)) {
2308 g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2309 install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2311 if(GTK_IS_CONTAINER(w))
2312 gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);
2315 // wrapper for missing poppler functions (defunct poppler-gdk api)
2318 wrapper_copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
2321 int cairo_width, cairo_height, cairo_rowstride;
2322 unsigned char *pixbuf_data, *dst, *cairo_data;
2323 int pixbuf_rowstride, pixbuf_n_channels;
2327 cairo_width = cairo_image_surface_get_width (surface);
2328 cairo_height = cairo_image_surface_get_height (surface);
2329 cairo_rowstride = cairo_image_surface_get_stride (surface);
2330 cairo_data = cairo_image_surface_get_data (surface);
2332 pixbuf_data = gdk_pixbuf_get_pixels (pixbuf);
2333 pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
2334 pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
2336 if (cairo_width > gdk_pixbuf_get_width (pixbuf))
2337 cairo_width = gdk_pixbuf_get_width (pixbuf);
2338 if (cairo_height > gdk_pixbuf_get_height (pixbuf))
2339 cairo_height = gdk_pixbuf_get_height (pixbuf);
2340 for (y = 0; y < cairo_height; y++)
2342 src = (unsigned int *) (cairo_data + y * cairo_rowstride);
2343 dst = pixbuf_data + y * pixbuf_rowstride;
2344 for (x = 0; x < cairo_width; x++)
2346 dst[0] = (*src >> 16) & 0xff;
2347 dst[1] = (*src >> 8) & 0xff;
2348 dst[2] = (*src >> 0) & 0xff;
2349 if (pixbuf_n_channels == 4)
2350 dst[3] = (*src >> 24) & 0xff;
2351 dst += pixbuf_n_channels;
2358 wrapper_poppler_page_render_to_pixbuf (PopplerPage *page,
2359 int src_x, int src_y,
2360 int src_width, int src_height,
2366 cairo_surface_t *surface;
2368 surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2369 src_width, src_height);
2370 cr = cairo_create (surface);
2374 cairo_translate (cr, src_x + src_width, -src_y);
2377 cairo_translate (cr, src_x + src_width, src_y + src_height);
2380 cairo_translate (cr, -src_x, src_y + src_height);
2383 cairo_translate (cr, -src_x, -src_y);
2387 cairo_scale (cr, scale, scale);
2390 cairo_rotate (cr, rotation * G_PI / 180.0);
2392 poppler_page_render (page, cr);
2395 cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
2396 cairo_set_source_rgb (cr, 1., 1., 1.);
2401 wrapper_copy_cairo_surface_to_pixbuf (surface, pixbuf);
2402 cairo_surface_destroy (surface);