]> git.donarmstrong.com Git - xournal.git/blob - src/xo-misc.c
3d866cf37da202002da89588a5711ceeb5d1f6e3
[xournal.git] / src / xo-misc.c
1 /*
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.
6  *
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.
11  *
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/>.
14  */
15
16 #ifdef HAVE_CONFIG_H
17 #  include <config.h>
18 #endif
19
20 #include <math.h>
21 #include <string.h>
22 #include <gtk/gtk.h>
23 #include <libgnomecanvas/libgnomecanvas.h>
24 #include <gdk/gdkkeysyms.h>
25
26 #include "xournal.h"
27 #include "xo-interface.h"
28 #include "xo-support.h"
29 #include "xo-callbacks.h"
30 #include "xo-misc.h"
31 #include "xo-file.h"
32 #include "xo-paint.h"
33 #include "xo-shapes.h"
34 #include "xo-image.h"
35
36 // some global constants
37
38 guint predef_colors_rgba[COLOR_MAX] =
39   { 0x000000ff, 0x3333ccff, 0xff0000ff, 0x008000ff,
40     0x808080ff, 0x00c0ffff, 0x00ff00ff, 0xff00ffff,
41     0xff8000ff, 0xffff00ff, 0xffffffff };
42
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 };
47
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
52   };
53
54 // some manipulation functions
55
56 struct Page *new_page(struct Page *template)
57 {
58   struct Page *pg = (struct Page *) g_memdup(template, sizeof(struct Page));
59   struct Layer *l = g_new(struct Layer, 1);
60   
61   l->items = NULL;
62   l->nitems = 0;
63   pg->layers = g_list_append(NULL, l);
64   pg->nlayers = 1;
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);
70   }
71   pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
72       gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
73   make_page_clipbox(pg);
74   update_canvas_bg(pg);
75   l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
76       pg->group, gnome_canvas_group_get_type(), NULL);
77   
78   return pg;
79 }
80
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.
84 */
85 struct Page *new_page_with_bg(struct Background *bg, double width, double height)
86 {
87   struct Page *pg = g_new(struct Page, 1);
88   struct Layer *l = g_new(struct Layer, 1);
89   
90   l->items = NULL;
91   l->nitems = 0;
92   pg->layers = g_list_append(NULL, l);
93   pg->nlayers = 1;
94   pg->bg = bg;
95   pg->bg->canvas_item = NULL;
96   pg->height = height;
97   pg->width = width;
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);
104   
105   return pg;
106 }
107
108 // change the current page if necessary for pointer at pt
109 void set_current_page(gdouble *pt)
110 {
111   gboolean page_change;
112   struct Page *tmppage;
113
114   page_change = FALSE;
115   tmppage = ui.cur_page;
116   while (ui.view_continuous && (pt[1] < - VIEW_CONTINUOUS_SKIP)) {
117     if (ui.pageno == 0) break;
118     page_change = TRUE;
119     ui.pageno--;
120     tmppage = g_list_nth_data(journal.pages, ui.pageno);
121     pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
122   }
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;
126     page_change = TRUE;
127     ui.pageno++;
128     tmppage = g_list_nth_data(journal.pages, ui.pageno);
129   }
130   if (page_change) do_switch_page(ui.pageno, FALSE, FALSE);
131 }
132
133 void realloc_cur_path(int n)
134 {
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));
138 }
139
140 void realloc_cur_widths(int n)
141 {
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));
145 }
146
147 // undo utility functions
148
149 void prepare_new_undo(void)
150 {
151   struct UndoItem *u;
152   // add a new UndoItem on the stack  
153   u = (struct UndoItem *)g_malloc(sizeof(struct UndoItem));
154   u->next = undo;
155   u->multiop = 0;
156   undo = u;
157   ui.saved = FALSE;
158   clear_redo_stack();
159 }
160
161 void clear_redo_stack(void)
162 {
163   struct UndoItem *u;  
164   GList *list, *repl;
165   struct UndoErasureData *erasure;
166   struct Item *it;
167
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 */
172   
173   while (redo!=NULL) {
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);
177       g_free(redo->item);
178       /* the strokes are unmapped, so there are no associated canvas items */
179     }
180     else if (redo->type == ITEM_TEXT) {
181       g_free(redo->item->text);
182       g_free(redo->item->font_name);
183       g_free(redo->item);
184     }
185     else if (redo->type == ITEM_IMAGE) {
186       g_object_unref(redo->item->image);
187       g_free(redo->item->image_png);
188       g_free(redo->item);
189     }
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);
197           g_free(it);
198         }
199         g_list_free(erasure->replacement_items);
200         g_free(erasure);
201       }
202       g_list_free(redo->erasurelist);
203     }
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);
209       }
210       g_free(redo->bg);
211     }
212     else if (redo->type == ITEM_NEW_PAGE) {
213       redo->page->group = NULL;
214       delete_page(redo->page);
215     }
216     else if (redo->type == ITEM_MOVESEL || redo->type == ITEM_REPAINTSEL) {
217       g_list_free(redo->itemlist); g_list_free(redo->auxlist);
218     }
219     else if (redo->type == ITEM_RESIZESEL) {
220       g_list_free(redo->itemlist);
221     }
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);
228         }
229         g_free(it);
230       }
231       g_list_free(redo->itemlist);
232     }
233     else if (redo->type == ITEM_NEW_LAYER) {
234       g_free(redo->layer);
235     }
236     else if (redo->type == ITEM_TEXT_EDIT || redo->type == ITEM_TEXT_ATTRIB) {
237       g_free(redo->str);
238       if (redo->type == ITEM_TEXT_ATTRIB) g_free(redo->brush);
239     }
240
241     u = redo;
242     redo = redo->next;
243     g_free(u);
244   }
245   update_undo_redo_enabled();
246 }
247
248 void clear_undo_stack(void)
249 {
250   struct UndoItem *u;
251   GList *list;
252   struct UndoErasureData *erasure;
253   
254   while (undo!=NULL) {
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);
263         }
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);
269         }
270         g_free(erasure->item);
271         g_list_free(erasure->replacement_items);
272         g_free(erasure);
273       }
274       g_list_free(undo->erasurelist);
275     }
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);
281       }
282       g_free(undo->bg);
283     }
284     else if (undo->type == ITEM_MOVESEL || undo->type == ITEM_REPAINTSEL) {
285       g_list_free(undo->itemlist); g_list_free(undo->auxlist);
286     }
287     else if (undo->type == ITEM_RESIZESEL) {
288       g_list_free(undo->itemlist);
289     }
290     else if (undo->type == ITEM_PASTE) {
291       g_list_free(undo->itemlist);
292     }
293     else if (undo->type == ITEM_DELETE_LAYER) {
294       undo->layer->group = NULL;
295       delete_layer(undo->layer);
296     }
297     else if (undo->type == ITEM_DELETE_PAGE) {
298       undo->page->group = NULL;
299       delete_page(undo->page);
300     }
301     else if (undo->type == ITEM_TEXT_EDIT || undo->type == ITEM_TEXT_ATTRIB) {
302       g_free(undo->str);
303       if (undo->type == ITEM_TEXT_ATTRIB) g_free(undo->brush);
304     }
305
306     u = undo;
307     undo = undo->next;
308     g_free(u);
309   }
310   update_undo_redo_enabled();
311 }
312
313 // free data structures 
314
315 void delete_journal(struct Journal *j)
316 {
317   while (j->pages!=NULL) {
318     delete_page((struct Page *)j->pages->data);
319     j->pages = g_list_delete_link(j->pages, j->pages);
320   }
321 }
322
323 void delete_page(struct Page *pg)
324 {
325   struct Layer *l;
326   
327   while (pg->layers!=NULL) {
328     l = (struct Layer *)pg->layers->data;
329     l->group = NULL;
330     delete_layer(l);
331     pg->layers = g_list_delete_link(pg->layers, pg->layers);
332   }
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);
338   }
339   g_free(pg->bg);
340   g_free(pg);
341 }
342
343 void delete_layer(struct Layer *l)
344 {
345   struct Item *item;
346   
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);
353     }
354     if (item->type == ITEM_IMAGE) {
355       g_object_unref(item->image);
356       g_free(item->image_png);
357     }
358     // don't need to delete the canvas_item, as it's part of the group destroyed below
359     g_free(item);
360     l->items = g_list_delete_link(l->items, l->items);
361   }
362   if (l->group!= NULL) gtk_object_destroy(GTK_OBJECT(l->group));
363   g_free(l);
364 }
365
366 // referenced strings
367
368 struct Refstring *new_refstring(const char *s)
369 {
370   struct Refstring *rs = g_new(struct Refstring, 1);
371   rs->nref = 1;
372   if (s!=NULL) rs->s = g_strdup(s);
373   else rs->s = NULL;
374   rs->aux = NULL;
375   return rs;
376 }
377
378 struct Refstring *refstring_ref(struct Refstring *rs)
379 {
380   rs->nref++;
381   return rs;
382 }
383
384 void refstring_unref(struct Refstring *rs)
385 {
386   rs->nref--;
387   if (rs->nref == 0) {
388     if (rs->s!=NULL) g_free(rs->s);
389     if (rs->aux!=NULL) g_free(rs->aux);
390     g_free(rs);
391   }
392 }
393
394
395 // some helper functions
396
397 int finite_sized(double x) // detect unrealistic coordinate values
398 {
399   return (finite(x) && x<1E6 && x>-1E6);
400 }
401
402
403 void get_pointer_coords(GdkEvent *event, gdouble *ret)
404 {
405   double x, y;
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;
410 }
411
412 void get_current_pointer_coords(gdouble *ret)
413 {
414   gint wx, wy, sx, sy;
415
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;
421 }
422
423 void fix_xinput_coords(GdkEvent *event)
424 {
425   double *axes, *px, *py, axis_width;
426   GdkDevice *device;
427   int wx, wy, sx, sy, ix, iy;
428
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;
434   }
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;
440   }
441   else return; // nothing we know how to do
442
443   gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
444
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);
449     *px = ix + sx; 
450     *py = iy + sy;
451   }
452   else {
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;
460   }
461 #else
462   if (!finite_sized(*px) || !finite_sized(*py) || *px==0. || *py==0.) {
463     gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
464     *px = ix + sx; 
465     *py = iy + sy;
466   }
467   else {
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) {
471       *px += sx;
472       *py += sy;
473     }
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);
478       *px += sx - wx;
479       *py += sy - wy;
480     }
481   }
482 #endif
483 }
484
485 double get_pressure_multiplier(GdkEvent *event)
486 {
487   double *axes;
488   double rawpressure;
489   GdkDevice *device;
490
491   if (event->type == GDK_MOTION_NOTIFY) {
492     axes = event->motion.axes;
493     device = event->motion.device;
494   }
495   else {
496     axes = event->button.axes;
497     device = event->button.device;
498   }
499   
500   if (device == gdk_device_get_core_pointer()
501       || device->num_axes <= 2) return 1.0;
502
503   rawpressure = axes[2]/(device->axes[2].max - device->axes[2].min);
504   if (!finite_sized(rawpressure)) return 1.0;
505
506   return ((1-rawpressure)*ui.width_minimum_multiplier + rawpressure*ui.width_maximum_multiplier);
507 }
508
509 void update_item_bbox(struct Item *item)
510 {
511   int i;
512   gdouble *p, h, w;
513   
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)
518     {
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];
523     }
524   }
525   if (item->type == ITEM_TEXT && item->canvas_item!=NULL) {
526     h=0.; w=0.;
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;
530   }
531 }
532
533 void make_page_clipbox(struct Page *pg)
534 {
535   GnomeCanvasPathDef *pg_clip;
536   
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);
545 }
546
547 void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
548 {
549   PangoFontDescription *font_desc;
550   GnomeCanvasPoints points;
551   int j;
552
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);
560     else {
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);
572       }
573     }
574   }
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);
585   }
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,
594           NULL);
595   }
596 }
597
598 void make_canvas_items(void)
599 {
600   struct Page *pg;
601   struct Layer *l;
602   struct Item *item;
603   GList *pagelist, *layerlist, *itemlist;
604   
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);
611     }
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);
622       }
623     }
624   }
625 }
626
627 void update_canvas_bg(struct Page *pg)
628 {
629   GnomeCanvasGroup *group;
630   GnomeCanvasPoints *seg;
631   GdkPixbuf *scaled_pix;
632   double *pt;
633   double x, y;
634   int w, h;
635   gboolean is_well_scaled;
636   
637   if (pg->bg->canvas_item != NULL)
638     gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
639   pg->bg->canvas_item = NULL;
640   
641   if (pg->bg->type == BG_SOLID)
642   {
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);
652     pt = seg->coords;
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) {
656         pt[0] = pt[2] = x;
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);
660       }      
661       pt[0] = 0; pt[2] = pg->width;
662       for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
663         pt[1] = pt[3] = y;
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);
667       }      
668       gnome_canvas_points_free(seg);
669       return;
670     }
671     pt[0] = 0; pt[2] = pg->width;
672     for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
673       pt[1] = pt[3] = y;
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);
677     }      
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);
684     }
685     gnome_canvas_points_free(seg);
686     return;
687   }
688   
689   if (pg->bg->type == BG_PIXMAP)
690   {
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, 
697         NULL);
698     lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
699   }
700
701   if (pg->bg->type == BG_PDF)
702   {
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.);
706     if (is_well_scaled)
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, 
711           NULL);
712     else
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, 
718           NULL);
719     lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
720   }
721 }
722
723 gboolean is_visible(struct Page *pg)
724 {
725   GtkAdjustment *v_adj;
726   double ytop, ybot;
727   
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));
733 }
734
735 void rescale_bg_pixmaps(void)
736 {
737   GList *pglist;
738   struct Page *pg;
739   GdkPixbuf *pix;
740   gboolean is_well_scaled;
741   gdouble zoom_to_request;
742   
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;
747
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;
753     }
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);
760         if (is_well_scaled)
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, 
765             NULL);
766       }
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;
772     }
773   }
774 }
775
776 gboolean have_intersect(struct BBox *a, struct BBox *b)
777 {
778   return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
779          (MAX(a->left, b->left) <= MIN(a->right, b->right));
780 }
781
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 !! */
785
786 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
787 {
788   int i1, i2;
789   
790   i1 = g_list_index(g->item_list, item);
791   if (i1 == -1) return;
792   
793   if (after == NULL) i2 = -1;
794   else i2 = g_list_index(g->item_list, after);
795
796   if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
797   if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
798   
799   // BUGFIX for libgnomecanvas
800   g->item_list_end = g_list_last(g->item_list);
801 }
802
803 void rgb_to_gdkcolor(guint rgba, GdkColor *color)
804 {
805   color->pixel = 0;
806   color->red = ((rgba>>24)&0xff)*0x101;
807   color->green = ((rgba>>16)&0xff)*0x101;
808   color->blue = ((rgba>>8)&0xff)*0x101;
809 }
810
811 guint32 gdkcolor_to_rgba(GdkColor gdkcolor, guint16 alpha) 
812 {
813   guint32 rgba =  ((gdkcolor.red   & 0xff00) << 16) |
814                   ((gdkcolor.green & 0xff00) << 8)  |
815                   ((gdkcolor.blue  & 0xff00) )      |
816                   ((alpha & 0xff00) >> 8);
817
818   return rgba;
819 }
820
821 // some interface functions
822
823 void update_thickness_buttons(void)
824 {
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);
828   } else 
829   switch (ui.cur_brush->thickness_no) {
830     case THICKNESS_FINE:
831       gtk_toggle_tool_button_set_active(
832         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
833       break;
834     case THICKNESS_MEDIUM:
835       gtk_toggle_tool_button_set_active(
836         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
837       break;
838     case THICKNESS_THICK:
839       gtk_toggle_tool_button_set_active(
840         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
841       break;
842     default:
843       gtk_toggle_tool_button_set_active(
844         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
845   }
846 }
847
848 void update_color_buttons(void)
849 {
850   GdkColor gdkcolor;
851   GtkColorButton *colorbutton;
852   
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);
857   } else
858   switch (ui.cur_brush->color_no) {
859     case COLOR_BLACK:
860       gtk_toggle_tool_button_set_active(
861         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
862       break;
863     case COLOR_BLUE:
864       gtk_toggle_tool_button_set_active(
865         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
866       break;
867     case COLOR_RED:
868       gtk_toggle_tool_button_set_active(
869         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
870       break;
871     case COLOR_GREEN:
872       gtk_toggle_tool_button_set_active(
873         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
874       break;
875     case COLOR_GRAY:
876       gtk_toggle_tool_button_set_active(
877         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
878       break;
879     case COLOR_LIGHTBLUE:
880       gtk_toggle_tool_button_set_active(
881         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
882       break;
883     case COLOR_LIGHTGREEN:
884       gtk_toggle_tool_button_set_active(
885         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
886       break;
887     case COLOR_MAGENTA:
888       gtk_toggle_tool_button_set_active(
889         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
890       break;
891     case COLOR_ORANGE:
892       gtk_toggle_tool_button_set_active(
893         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
894       break;
895     case COLOR_YELLOW:
896       gtk_toggle_tool_button_set_active(
897         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
898       break;
899     case COLOR_WHITE:
900       gtk_toggle_tool_button_set_active(
901         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
902       break;
903     default:
904       gtk_toggle_tool_button_set_active(
905         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
906   }
907
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);
919   } else {
920     gtk_color_button_set_alpha(colorbutton, 0xffff);
921     gtk_color_button_set_use_alpha(colorbutton, FALSE);
922   }
923 }
924
925 void update_tool_buttons(void)
926 {
927   switch(ui.toolno[ui.cur_mapping]) {
928     case TOOL_PEN:
929       gtk_toggle_tool_button_set_active(
930         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
931       break;
932     case TOOL_ERASER:
933       gtk_toggle_tool_button_set_active(
934         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
935       break;
936     case TOOL_HIGHLIGHTER:
937       gtk_toggle_tool_button_set_active(
938         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
939       break;
940     case TOOL_TEXT:
941       gtk_toggle_tool_button_set_active(
942         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
943       break;
944     case TOOL_IMAGE:
945       gtk_toggle_tool_button_set_active(
946         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonImage")), TRUE);
947       break;
948     case TOOL_SELECTREGION:
949       gtk_toggle_tool_button_set_active(
950         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
951       break;
952     case TOOL_SELECTRECT:
953       gtk_toggle_tool_button_set_active(
954         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
955       break;
956     case TOOL_VERTSPACE:
957       gtk_toggle_tool_button_set_active(
958         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
959       break;
960     case TOOL_HAND:
961       gtk_toggle_tool_button_set_active(
962         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
963       break;
964   }
965     
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);
972
973   update_thickness_buttons();
974   update_color_buttons();
975 }
976
977 void update_tool_menu(void)
978 {
979   switch(ui.toolno[0]) {
980     case TOOL_PEN:
981       gtk_check_menu_item_set_active(
982         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
983       break;
984     case TOOL_ERASER:
985       gtk_check_menu_item_set_active(
986         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
987       break;
988     case TOOL_HIGHLIGHTER:
989       gtk_check_menu_item_set_active(
990         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
991       break;
992     case TOOL_TEXT:
993       gtk_check_menu_item_set_active(
994         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
995       break;
996     case TOOL_IMAGE:
997       gtk_check_menu_item_set_active(
998         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsImage")), TRUE);
999       break;
1000     case TOOL_SELECTREGION:
1001       gtk_check_menu_item_set_active(
1002         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
1003       break;
1004     case TOOL_SELECTRECT:
1005       gtk_check_menu_item_set_active(
1006         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
1007       break;
1008     case TOOL_VERTSPACE:
1009       gtk_check_menu_item_set_active(
1010         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
1011       break;
1012     case TOOL_HAND:
1013       gtk_check_menu_item_set_active(
1014         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
1015       break;
1016   }
1017
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);
1024 }
1025
1026 void update_ruler_indicator(void)
1027 {
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);
1040 }
1041
1042 void update_color_menu(void)
1043 {
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);
1048   } else
1049   switch (ui.cur_brush->color_no) {
1050     case COLOR_BLACK:
1051       gtk_check_menu_item_set_active(
1052         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
1053       break;
1054     case COLOR_BLUE:
1055       gtk_check_menu_item_set_active(
1056         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
1057       break;
1058     case COLOR_RED:
1059       gtk_check_menu_item_set_active(
1060         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
1061       break;
1062     case COLOR_GREEN:
1063       gtk_check_menu_item_set_active(
1064         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
1065       break;
1066     case COLOR_GRAY:
1067       gtk_check_menu_item_set_active(
1068         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
1069       break;
1070     case COLOR_LIGHTBLUE:
1071       gtk_check_menu_item_set_active(
1072         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
1073       break;
1074     case COLOR_LIGHTGREEN:
1075       gtk_check_menu_item_set_active(
1076         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
1077       break;
1078     case COLOR_MAGENTA:
1079       gtk_check_menu_item_set_active(
1080         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
1081       break;
1082     case COLOR_ORANGE:
1083       gtk_check_menu_item_set_active(
1084         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
1085       break;
1086     case COLOR_YELLOW:
1087       gtk_check_menu_item_set_active(
1088         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
1089       break;
1090     case COLOR_WHITE:
1091       gtk_check_menu_item_set_active(
1092         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
1093       break;
1094     default:
1095       gtk_check_menu_item_set_active(
1096         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
1097   }
1098 }
1099
1100 void update_pen_props_menu(void)
1101 {
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);
1106       break;
1107     case THICKNESS_FINE:
1108       gtk_check_menu_item_set_active(
1109         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
1110       break;
1111     case THICKNESS_MEDIUM:
1112       gtk_check_menu_item_set_active(
1113         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
1114       break;
1115     case THICKNESS_THICK:
1116       gtk_check_menu_item_set_active(
1117         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
1118       break;
1119     case THICKNESS_VERYTHICK:
1120       gtk_check_menu_item_set_active(
1121         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
1122       break;
1123   }
1124 }
1125
1126 void update_eraser_props_menu(void)
1127 {
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);
1132       break;
1133     case THICKNESS_MEDIUM:
1134       gtk_check_menu_item_set_active(
1135         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
1136       break;
1137     case THICKNESS_THICK:
1138       gtk_check_menu_item_set_active(
1139         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
1140       break;
1141   }
1142   
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);
1152 }
1153
1154 void update_highlighter_props_menu(void)
1155 {
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);
1160       break;
1161     case THICKNESS_MEDIUM:
1162       gtk_check_menu_item_set_active(
1163         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
1164       break;
1165     case THICKNESS_THICK:
1166       gtk_check_menu_item_set_active(
1167         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
1168       break;
1169   }
1170 }
1171
1172 void update_mappings_menu_linkings(void)
1173 {
1174   switch (ui.linked_brush[1]) {
1175     case BRUSH_LINKED:
1176       gtk_check_menu_item_set_active(
1177         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
1178       break;
1179     case BRUSH_COPIED:
1180       gtk_check_menu_item_set_active(
1181         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
1182       break;
1183     case BRUSH_STATIC:
1184       gtk_check_menu_item_set_active(
1185         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
1186       break;
1187   }
1188   switch (ui.linked_brush[2]) {
1189     case BRUSH_LINKED:
1190       gtk_check_menu_item_set_active(
1191         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
1192       break;
1193     case BRUSH_COPIED:
1194       gtk_check_menu_item_set_active(
1195         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
1196       break;
1197     case BRUSH_STATIC:
1198       gtk_check_menu_item_set_active(
1199         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
1200       break;
1201   }
1202 }
1203
1204 void update_mappings_menu(void)
1205 {
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);
1212
1213   switch(ui.toolno[1]) {
1214     case TOOL_PEN:
1215       gtk_check_menu_item_set_active(
1216         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
1217       break;
1218     case TOOL_ERASER:
1219       gtk_check_menu_item_set_active(
1220         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
1221       break;
1222     case TOOL_HIGHLIGHTER:
1223       gtk_check_menu_item_set_active(
1224         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
1225       break;
1226     case TOOL_TEXT:
1227       gtk_check_menu_item_set_active(
1228         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
1229       break;
1230     case TOOL_IMAGE:
1231       gtk_check_menu_item_set_active(
1232         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Image")), TRUE);
1233       break;
1234     case TOOL_SELECTREGION:
1235       gtk_check_menu_item_set_active(
1236         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
1237       break;
1238     case TOOL_SELECTRECT:
1239       gtk_check_menu_item_set_active(
1240         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
1241       break;
1242     case TOOL_VERTSPACE:
1243       gtk_check_menu_item_set_active(
1244         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
1245       break;
1246   }
1247   switch(ui.toolno[2]) {
1248     case TOOL_PEN:
1249       gtk_check_menu_item_set_active(
1250         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
1251       break;
1252     case TOOL_ERASER:
1253       gtk_check_menu_item_set_active(
1254         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
1255       break;
1256     case TOOL_HIGHLIGHTER:
1257       gtk_check_menu_item_set_active(
1258         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
1259       break;
1260     case TOOL_TEXT:
1261       gtk_check_menu_item_set_active(
1262         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
1263       break;
1264     case TOOL_IMAGE:
1265       gtk_check_menu_item_set_active(
1266         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Image")), TRUE);
1267       break;
1268     case TOOL_SELECTREGION:
1269       gtk_check_menu_item_set_active(
1270         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
1271       break;
1272     case TOOL_SELECTRECT:
1273       gtk_check_menu_item_set_active(
1274         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
1275       break;
1276     case TOOL_VERTSPACE:
1277       gtk_check_menu_item_set_active(
1278         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1279       break;
1280   }
1281   update_mappings_menu_linkings();
1282 }
1283
1284 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1285 {
1286   int i, cx, cy;
1287   struct Layer *layer;
1288   GList *list;
1289   
1290   ui.pageno = pg;
1291
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));
1298     }
1299   
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();
1305  
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);
1313     
1314     if (refresh_all) 
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.);
1318   }
1319 }
1320
1321 void update_page_stuff(void)
1322 {
1323   gchar tmp[10];
1324   GtkComboBox *layerbox;
1325   int i;
1326   GList *pglist;
1327   GtkSpinButton *spin;
1328   struct Page *pg;
1329   double vertpos, maxwidth;
1330
1331   // move the page groups to their rightful locations or hide them
1332   if (ui.view_continuous) {
1333     vertpos = 0.; 
1334     maxwidth = 0.;
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));
1342       }
1343       vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1344       if (pg->width > maxwidth) maxwidth = pg->width;
1345     }
1346     vertpos -= VIEW_CONTINUOUS_SKIP;
1347     gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1348   } else {
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));
1356       } else {
1357         if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1358       }
1359     }
1360     gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1361   }
1362
1363   // update the page / layer info at bottom of screen
1364
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);
1372
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++;
1377   }
1378   while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1379     gtk_combo_box_remove_text(layerbox, 0);
1380     ui.layerbox_length--;
1381   }
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);
1385   }
1386   gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1387   ui.in_update_page_stuff = FALSE;
1388   
1389   gtk_container_forall(GTK_CONTAINER(layerbox), unset_flags, (gpointer)GTK_CAN_FOCUS);
1390   
1391   // update the paper-style menu radio buttons
1392   
1393   if (ui.view_continuous)
1394     gtk_check_menu_item_set_active(
1395        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1396   else
1397     gtk_check_menu_item_set_active(
1398        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1399
1400   if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1401     switch (ui.cur_page->bg->color_no) {
1402       case COLOR_WHITE:
1403         gtk_check_menu_item_set_active(
1404           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1405         break;
1406       case COLOR_YELLOW:
1407         gtk_check_menu_item_set_active(
1408           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1409         break;
1410       case COLOR_RED:
1411         gtk_check_menu_item_set_active(
1412           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1413         break;
1414       case COLOR_ORANGE:
1415         gtk_check_menu_item_set_active(
1416           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1417         break;
1418       case COLOR_BLUE:
1419         gtk_check_menu_item_set_active(
1420           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1421         break;
1422       case COLOR_GREEN:
1423         gtk_check_menu_item_set_active(
1424           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1425         break;
1426       default:
1427         gtk_check_menu_item_set_active(
1428           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1429         break;
1430     }
1431     switch (ui.cur_page->bg->ruling) {
1432       case RULING_NONE:
1433         gtk_check_menu_item_set_active(
1434           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1435         break;
1436       case RULING_LINED:
1437         gtk_check_menu_item_set_active(
1438           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1439         break;
1440       case RULING_RULED:
1441         gtk_check_menu_item_set_active(
1442           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1443         break;
1444       case RULING_GRAPH:
1445         gtk_check_menu_item_set_active(
1446           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1447         break;
1448     }
1449   } else {
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);
1454   }
1455   
1456   // enable/disable the page/layer menu items and toolbar buttons
1457
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);
1462   
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);
1471   
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);
1474
1475   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1476   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1477 }
1478
1479 void update_toolbar_and_menu(void)
1480 {
1481   update_tool_buttons(); // takes care of other toolbar buttons as well  
1482   update_tool_menu();
1483   update_color_menu();
1484   update_pen_props_menu();
1485   update_eraser_props_menu();
1486   update_highlighter_props_menu();
1487   update_mappings_menu();
1488
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);
1493 }
1494
1495 void update_file_name(char *filename)
1496 {
1497   gchar tmp[100], *p;
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"));
1502     return;
1503   }
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);
1510
1511   if (filename[0]=='/') {
1512     if (ui.default_path!=NULL) g_free(ui.default_path);
1513     ui.default_path = g_path_get_dirname(filename);
1514   }
1515 }
1516
1517 void update_undo_redo_enabled(void)
1518 {
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);
1523 }
1524
1525 void update_copy_paste_enabled(void)
1526 {
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);
1534 }
1535
1536 void update_mapping_linkings(int toolno)
1537 {
1538   int i;
1539   
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));
1544     }
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();
1548     }
1549   }
1550 }
1551
1552 void set_cur_color(int color_no, guint color_rgba)
1553 {
1554   int which_mapping, tool;
1555   
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;
1561
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;
1565   else
1566     ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1567   update_mapping_linkings(tool);
1568 }
1569
1570 void recolor_temp_text(int color_no, guint color_rgba)
1571 {
1572   GdkColor gdkcolor;
1573   
1574   if (ui.cur_item_type!=ITEM_TEXT) return;
1575   if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1576     prepare_new_undo();
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));
1582   }
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);
1588 }
1589
1590 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1591 {
1592   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1593     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1594       return;
1595   } 
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)))
1598       return;
1599   }
1600
1601   if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1602
1603   if (ui.cur_item_type == ITEM_TEXT)
1604     recolor_temp_text(color_no, color_rgba);
1605
1606   if (ui.selection != NULL) {
1607     recolor_selection(color_no, color_rgba);
1608     update_color_buttons();
1609     update_color_menu();
1610   }
1611   
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;
1615     ui.cur_mapping = 0;
1616     end_text();
1617     ui.toolno[ui.cur_mapping] = TOOL_PEN;
1618     ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1619     update_tool_buttons();
1620     update_tool_menu();
1621   }
1622   
1623   set_cur_color(color_no, color_rgba);
1624   update_color_buttons();
1625   update_color_menu();
1626   update_cursor();
1627 }
1628
1629 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1630 {
1631   int which_mapping;
1632   
1633   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1634     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1635       return;
1636   } else {
1637     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1638       return;
1639   }
1640
1641   if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1642
1643   if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1644     rethicken_selection(val);
1645     update_thickness_buttons();
1646   }
1647
1648   if (tool >= NUM_STROKE_TOOLS) {
1649     update_thickness_buttons(); // undo illegal button selection
1650     return;
1651   }
1652
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;
1657   end_text();
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);
1661   
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();
1666   update_cursor();
1667 }
1668
1669 void process_papercolor_activate(GtkMenuItem *menuitem, int color, guint rgba)
1670 {
1671   struct Page *pg;
1672   GList *pglist;
1673   gboolean hasdone;
1674
1675   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1676     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1677       return;
1678   }
1679
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);
1683
1684   pg = ui.cur_page;
1685   hasdone = FALSE;
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) {
1689       prepare_new_undo();
1690       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1691       undo->multiop |= MULTIOP_CONT_REDO;
1692       hasdone = TRUE;
1693       undo->type = ITEM_NEW_BG_ONE;
1694       undo->page = pg;
1695       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1696       undo->bg->canvas_item = NULL;
1697
1698       pg->bg->color_no = color;
1699       pg->bg->color_rgba = rgba;
1700       update_canvas_bg(pg);
1701     }
1702     if (!ui.bg_apply_all_pages) break;
1703   }
1704   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1705 }
1706
1707 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1708 {
1709   struct Page *pg;
1710   GList *pglist;
1711   gboolean hasdone, must_upd;
1712
1713   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1714     return;
1715
1716   if (ui.bg_apply_all_pages)
1717     gtk_check_menu_item_set_active(
1718       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1719
1720   pg = ui.cur_page;
1721   hasdone = FALSE;
1722   must_upd = FALSE;
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) {
1726       prepare_new_undo();
1727       undo->type = ITEM_NEW_BG_ONE;
1728       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1729       undo->multiop |= MULTIOP_CONT_REDO;
1730       hasdone = TRUE;
1731       undo->page = pg;
1732       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1733       undo->bg->canvas_item = NULL;
1734
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;
1741         must_upd = TRUE;
1742       }
1743       pg->bg->ruling = style;
1744       update_canvas_bg(pg);
1745     }
1746     if (!ui.bg_apply_all_pages) break;
1747   }
1748   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1749   if (must_upd) update_page_stuff();
1750 }
1751
1752 #ifndef GTK_STOCK_DISCARD
1753 #define GTK_STOCK_DISCARD GTK_STOCK_NO
1754 #endif
1755
1756 gboolean ok_to_close(void)
1757 {
1758   GtkWidget *dialog;
1759   GtkResponseType response;
1760
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
1776   }
1777   return TRUE;
1778 }
1779
1780 // send the focus back to the appropriate widget
1781
1782 void reset_focus(void)
1783 {
1784   if (ui.cur_item_type == ITEM_TEXT)
1785     gtk_widget_grab_focus(ui.cur_item->widget);
1786   else
1787     gtk_widget_grab_focus(GTK_WIDGET(canvas));
1788 }
1789
1790 // selection / clipboard stuff
1791
1792 void reset_selection(void)
1793 {
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();
1805   update_cursor();
1806 }
1807
1808 void move_journal_items_by(GList *itemlist, double dx, double dy,
1809                               struct Layer *l1, struct Layer *l2, GList *depths)
1810 {
1811   struct Item *item;
1812   GnomeCanvasItem *refitem;
1813   GList *link;
1814   int i;
1815   double *pt;
1816   
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;
1828     }
1829     if (l1 != l2) {
1830       // find out where to insert
1831       if (depths != NULL) {
1832         if (depths->data == NULL) link = l2->items;
1833         else {
1834           link = g_list_find(l2->items, depths->data);
1835           if (link != NULL) link = link->next;
1836         }
1837       } else link = NULL;
1838       l2->items = g_list_insert_before(l2->items, link, item);
1839       l2->nitems++;
1840       l1->items = g_list_remove(l1->items, item);
1841       l1->nitems--;
1842     }
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);
1850       }
1851       depths = depths->next;
1852     }
1853     itemlist = itemlist->next;
1854   }
1855 }
1856
1857 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1858                              double offset_x, double offset_y)
1859 {
1860   struct Item *item;
1861   GList *list;
1862   double mean_scaling, temp;
1863   double *pt, *wid;
1864   GnomeCanvasGroup *group;
1865   int i; 
1866   
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));
1870
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;
1878       }
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;
1882
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;
1891       }
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;
1896       }
1897     }
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;
1905     }
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;
1915       }
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;
1920       }
1921     }
1922     // redraw the item
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);
1927     }
1928   }
1929 }
1930
1931 // Switch between button mappings
1932
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) */
1937
1938 void switch_mapping(int m)
1939 {
1940   if (ui.cur_mapping == m) return;
1941
1942   ui.cur_mapping = m;
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;
1948   
1949   update_tool_buttons();
1950   update_color_menu();
1951   update_cursor();
1952 }
1953
1954 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1955 {
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;
1959   switch_mapping(0);
1960   end_text();
1961     
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();
1966   }
1967 }
1968
1969 // update the ordering of components in the main vbox
1970
1971 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1972  {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1973
1974 void update_vbox_order(int *order)
1975 {
1976   int i, j;
1977   GtkWidget *child;
1978   GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1979   gboolean present[VBOX_MAIN_NITEMS];
1980   
1981   for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1982   j=0;
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);
1989   }
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]));
1992 }
1993
1994 gchar *make_cur_font_name(void)
1995 {
1996   gchar *str;
1997   struct Item *it;
1998
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);
2005   else
2006     str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
2007   return str;
2008 }
2009
2010 void update_font_button(void)
2011 {
2012   gchar *str;
2013
2014   str = make_cur_font_name();
2015   gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
2016   g_free(str);
2017 }
2018
2019 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
2020 {
2021   return GTK_WIDGET_SENSITIVE(widget);
2022 }
2023
2024 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
2025 {
2026   if (ui.cur_item_type == ITEM_TEXT) {
2027     g_signal_stop_emission_by_name(widget, "can-activate-accel");
2028     return FALSE;
2029   }
2030   return GTK_WIDGET_SENSITIVE(widget);
2031 }
2032
2033 void allow_all_accels(void)
2034 {
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);
2099 }
2100
2101 void add_scroll_bindings(void)
2102 {
2103   GtkBindingSet *binding_set;
2104   
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);  
2131 }
2132
2133 gboolean is_event_within_textview(GdkEventButton *event)
2134 {
2135   double pt[2];
2136   
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;
2141   return TRUE;
2142 }
2143
2144 void hide_unimplemented(void)
2145 {
2146   gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2147   gtk_widget_hide(GET_COMPONENT("journalFlatten"));  
2148   gtk_widget_hide(GET_COMPONENT("helpIndex")); 
2149
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"));
2154   }
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"));
2158   }  
2159   
2160   /* screenshot feature doesn't work yet in Win32 */
2161 #ifdef WIN32
2162   gtk_widget_hide(GET_COMPONENT("journalScreenshot"));
2163 #endif
2164 }  
2165
2166 // toggle fullscreen mode
2167 void do_fullscreen(gboolean active)
2168 {
2169   end_text();
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);
2175
2176   if (ui.fullscreen) {
2177 #ifdef WIN32
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());
2181 #endif
2182     gtk_window_fullscreen(GTK_WINDOW(winMain));
2183   }
2184   else {
2185 #ifdef WIN32
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);
2189 #endif
2190     gtk_window_unfullscreen(GTK_WINDOW(winMain));
2191   }
2192
2193   update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2194 }
2195
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    */
2198
2199 // prevent interface items from getting bogus XInput events
2200
2201 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2202                                    gpointer user_data)
2203 {
2204   if (event->type == GDK_MOTION_NOTIFY &&
2205       event->motion.device != gdk_device_get_core_pointer())
2206     return TRUE;
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())
2210     return TRUE;
2211   return FALSE;
2212 }
2213
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!
2219 */
2220
2221 /*  
2222 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2223                                    gpointer user_data)
2224 {
2225   int ix, iy;
2226   GdkWindow *window;
2227
2228   if (user_data) window = (GdkWindow *)user_data;
2229   else window = widget->window;
2230
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);
2240     return TRUE;
2241   }
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);
2252     return TRUE;
2253   }
2254   return FALSE;
2255 }
2256 */
2257
2258
2259 /* When enter is pressed into page spinbox, send focus back to canvas. */
2260
2261 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2262 {
2263   reset_focus();
2264   return FALSE;
2265 }
2266
2267 /* recursively unset widget flags */
2268
2269 void unset_flags(GtkWidget *w, gpointer flag)
2270 {
2271   GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2272   if(GTK_IS_CONTAINER(w))
2273     gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2274 }
2275
2276 /* reset focus when a key or button press event reaches someone, or when the
2277    page-number spin button should relinquish control... */
2278
2279 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2280 {
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 ... */
2285     return TRUE;
2286   }
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
2295   }
2296
2297   // otherwise, we want to make sure the canvas or text item gets focus back...
2298   reset_focus();  
2299   return FALSE;
2300 }
2301
2302 void install_focus_hooks(GtkWidget *w, gpointer data)
2303 {
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);
2310   }
2311   if(GTK_IS_CONTAINER(w))
2312     gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);
2313 }
2314
2315 // wrapper for missing poppler functions (defunct poppler-gdk api)
2316
2317 static void
2318 wrapper_copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
2319                               GdkPixbuf       *pixbuf)
2320 {
2321   int cairo_width, cairo_height, cairo_rowstride;
2322   unsigned char *pixbuf_data, *dst, *cairo_data;
2323   int pixbuf_rowstride, pixbuf_n_channels;
2324   unsigned int *src;
2325   int x, y;
2326
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);
2331
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);
2335
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++)
2341     {
2342       src = (unsigned int *) (cairo_data + y * cairo_rowstride);
2343       dst = pixbuf_data + y * pixbuf_rowstride;
2344       for (x = 0; x < cairo_width; x++) 
2345         {
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;
2352           src++;
2353         }
2354     }
2355 }       
2356
2357 void
2358 wrapper_poppler_page_render_to_pixbuf (PopplerPage *page,
2359                                int src_x, int src_y,
2360                                int src_width, int src_height,
2361                                double scale,
2362                                int rotation,
2363                                GdkPixbuf *pixbuf)
2364 {
2365   cairo_t *cr;
2366   cairo_surface_t *surface;
2367
2368   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2369                                         src_width, src_height);
2370   cr = cairo_create (surface);
2371   cairo_save (cr);
2372   switch (rotation) {
2373   case 90:
2374           cairo_translate (cr, src_x + src_width, -src_y);
2375           break;
2376   case 180:
2377           cairo_translate (cr, src_x + src_width, src_y + src_height);
2378           break;
2379   case 270:
2380           cairo_translate (cr, -src_x, src_y + src_height);
2381           break;
2382   default:
2383           cairo_translate (cr, -src_x, -src_y);
2384   }
2385
2386   if (scale != 1.0)
2387           cairo_scale (cr, scale, scale);
2388
2389   if (rotation != 0)
2390           cairo_rotate (cr, rotation * G_PI / 180.0);
2391
2392   poppler_page_render (page, cr);
2393   cairo_restore (cr);
2394
2395   cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
2396   cairo_set_source_rgb (cr, 1., 1., 1.);
2397   cairo_paint (cr);
2398
2399   cairo_destroy (cr);
2400
2401   wrapper_copy_cairo_surface_to_pixbuf (surface, pixbuf);
2402   cairo_surface_destroy (surface);
2403 }