]> git.donarmstrong.com Git - xournal.git/blob - src/xo-misc.c
1ec4401725b2412c10cb530643a7f2238717e72d
[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
35 // some global constants
36
37 guint predef_colors_rgba[COLOR_MAX] =
38   { 0x000000ff, 0x3333ccff, 0xff0000ff, 0x008000ff,
39     0x808080ff, 0x00c0ffff, 0x00ff00ff, 0xff00ffff,
40     0xff8000ff, 0xffff00ff, 0xffffffff };
41
42 guint predef_bgcolors_rgba[COLOR_MAX] = // meaningless ones set to white
43   { 0xffffffff, 0xa0e8ffff, 0xffc0d4ff, 0x80ffc0ff,
44     0xffffffff, 0xa0e8ffff, 0x80ffc0ff, 0xffc0d4ff,
45     0xffc080ff, 0xffff80ff, 0xffffffff };
46
47 double predef_thickness[NUM_STROKE_TOOLS][THICKNESS_MAX] =
48   { { 0.42, 0.85, 1.41,  2.26, 5.67 }, // pen thicknesses = 0.15, 0.3, 0.5, 0.8, 2 mm
49     { 2.83, 2.83, 8.50, 19.84, 19.84 }, // eraser thicknesses = 1, 3, 7 mm
50     { 2.83, 2.83, 8.50, 19.84, 19.84 }, // highlighter thicknesses = 1, 3, 7 mm
51   };
52
53 // some manipulation functions
54
55 struct Page *new_page(struct Page *template)
56 {
57   struct Page *pg = (struct Page *) g_memdup(template, sizeof(struct Page));
58   struct Layer *l = g_new(struct Layer, 1);
59   
60   l->items = NULL;
61   l->nitems = 0;
62   pg->layers = g_list_append(NULL, l);
63   pg->nlayers = 1;
64   pg->bg = (struct Background *)g_memdup(template->bg, sizeof(struct Background));
65   pg->bg->canvas_item = NULL;
66   if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
67     gdk_pixbuf_ref(pg->bg->pixbuf);
68     refstring_ref(pg->bg->filename);
69   }
70   pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
71       gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
72   make_page_clipbox(pg);
73   update_canvas_bg(pg);
74   l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
75       pg->group, gnome_canvas_group_get_type(), NULL);
76   
77   return pg;
78 }
79
80 /* Create a page from a background. 
81    Note: bg should be an UNREFERENCED background.
82    If needed, first duplicate it and increase the refcount of the pixbuf.
83 */
84 struct Page *new_page_with_bg(struct Background *bg, double width, double height)
85 {
86   struct Page *pg = g_new(struct Page, 1);
87   struct Layer *l = g_new(struct Layer, 1);
88   
89   l->items = NULL;
90   l->nitems = 0;
91   pg->layers = g_list_append(NULL, l);
92   pg->nlayers = 1;
93   pg->bg = bg;
94   pg->bg->canvas_item = NULL;
95   pg->height = height;
96   pg->width = width;
97   pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
98       gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
99   make_page_clipbox(pg);
100   update_canvas_bg(pg);
101   l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
102       pg->group, gnome_canvas_group_get_type(), NULL);
103   
104   return pg;
105 }
106
107 void realloc_cur_path(int n)
108 {
109   if (n <= ui.cur_path_storage_alloc) return;
110   ui.cur_path_storage_alloc = n+100;
111   ui.cur_path.coords = g_realloc(ui.cur_path.coords, 2*(n+100)*sizeof(double));
112 }
113
114 void realloc_cur_widths(int n)
115 {
116   if (n <= ui.cur_widths_storage_alloc) return;
117   ui.cur_widths_storage_alloc = n+100;
118   ui.cur_widths = g_realloc(ui.cur_widths, (n+100)*sizeof(double));
119 }
120
121 // undo utility functions
122
123 void prepare_new_undo(void)
124 {
125   struct UndoItem *u;
126   // add a new UndoItem on the stack  
127   u = (struct UndoItem *)g_malloc(sizeof(struct UndoItem));
128   u->next = undo;
129   u->multiop = 0;
130   undo = u;
131   ui.saved = FALSE;
132   clear_redo_stack();
133 }
134
135 void clear_redo_stack(void)
136 {
137   struct UndoItem *u;  
138   GList *list, *repl;
139   struct UndoErasureData *erasure;
140   struct Item *it;
141
142   /* Warning: the redo items might reference items from past redo entries,
143      which have been destroyed before them. Be careful! As a rule, it's
144      safe to destroy data which has been created at the current history step,
145      it's unsafe to refer to any data from previous history steps */
146   
147   while (redo!=NULL) {
148     if (redo->type == ITEM_STROKE) {
149       gnome_canvas_points_free(redo->item->path);
150       if (redo->item->brush.variable_width) g_free(redo->item->widths);
151       g_free(redo->item);
152       /* the strokes are unmapped, so there are no associated canvas items */
153     }
154     else if (redo->type == ITEM_TEXT) {
155       g_free(redo->item->text);
156       g_free(redo->item->font_name);
157       g_free(redo->item);
158     }
159     else if (redo->type == ITEM_ERASURE || redo->type == ITEM_RECOGNIZER) {
160       for (list = redo->erasurelist; list!=NULL; list=list->next) {
161         erasure = (struct UndoErasureData *)list->data;
162         for (repl = erasure->replacement_items; repl!=NULL; repl=repl->next) {
163           it = (struct Item *)repl->data;
164           gnome_canvas_points_free(it->path);
165           if (it->brush.variable_width) g_free(it->widths);
166           g_free(it);
167         }
168         g_list_free(erasure->replacement_items);
169         g_free(erasure);
170       }
171       g_list_free(redo->erasurelist);
172     }
173     else if (redo->type == ITEM_NEW_BG_ONE || redo->type == ITEM_NEW_BG_RESIZE
174           || redo->type == ITEM_NEW_DEFAULT_BG) {
175       if (redo->bg->type == BG_PIXMAP || redo->bg->type == BG_PDF) {
176         if (redo->bg->pixbuf!=NULL) gdk_pixbuf_unref(redo->bg->pixbuf);
177         refstring_unref(redo->bg->filename);
178       }
179       g_free(redo->bg);
180     }
181     else if (redo->type == ITEM_NEW_PAGE) {
182       redo->page->group = NULL;
183       delete_page(redo->page);
184     }
185     else if (redo->type == ITEM_MOVESEL || redo->type == ITEM_REPAINTSEL) {
186       g_list_free(redo->itemlist); g_list_free(redo->auxlist);
187     }
188     else if (redo->type == ITEM_RESIZESEL) {
189       g_list_free(redo->itemlist);
190     }
191     else if (redo->type == ITEM_PASTE) {
192       for (list = redo->itemlist; list!=NULL; list=list->next) {
193         it = (struct Item *)list->data;
194         if (it->type == ITEM_STROKE) {
195           gnome_canvas_points_free(it->path);
196           if (it->brush.variable_width) g_free(it->widths);
197         }
198         g_free(it);
199       }
200       g_list_free(redo->itemlist);
201     }
202     else if (redo->type == ITEM_NEW_LAYER) {
203       g_free(redo->layer);
204     }
205     else if (redo->type == ITEM_TEXT_EDIT || redo->type == ITEM_TEXT_ATTRIB) {
206       g_free(redo->str);
207       if (redo->type == ITEM_TEXT_ATTRIB) g_free(redo->brush);
208     }
209
210     u = redo;
211     redo = redo->next;
212     g_free(u);
213   }
214   update_undo_redo_enabled();
215 }
216
217 void clear_undo_stack(void)
218 {
219   struct UndoItem *u;
220   GList *list;
221   struct UndoErasureData *erasure;
222   
223   while (undo!=NULL) {
224     // for strokes, items are already in the journal, so we don't free them
225     // for erasures, we need to free the dead items
226     if (undo->type == ITEM_ERASURE || undo->type == ITEM_RECOGNIZER) {
227       for (list = undo->erasurelist; list!=NULL; list=list->next) {
228         erasure = (struct UndoErasureData *)list->data;
229         if (erasure->item->type == ITEM_STROKE) {
230           gnome_canvas_points_free(erasure->item->path);
231           if (erasure->item->brush.variable_width) g_free(erasure->item->widths);
232         }
233         if (erasure->item->type == ITEM_TEXT)
234           { g_free(erasure->item->text); g_free(erasure->item->font_name); }
235         g_free(erasure->item);
236         g_list_free(erasure->replacement_items);
237         g_free(erasure);
238       }
239       g_list_free(undo->erasurelist);
240     }
241     else if (undo->type == ITEM_NEW_BG_ONE || undo->type == ITEM_NEW_BG_RESIZE
242           || undo->type == ITEM_NEW_DEFAULT_BG) {
243       if (undo->bg->type == BG_PIXMAP || undo->bg->type == BG_PDF) {
244         if (undo->bg->pixbuf!=NULL) gdk_pixbuf_unref(undo->bg->pixbuf);
245         refstring_unref(undo->bg->filename);
246       }
247       g_free(undo->bg);
248     }
249     else if (undo->type == ITEM_MOVESEL || undo->type == ITEM_REPAINTSEL) {
250       g_list_free(undo->itemlist); g_list_free(undo->auxlist);
251     }
252     else if (undo->type == ITEM_RESIZESEL) {
253       g_list_free(undo->itemlist);
254     }
255     else if (undo->type == ITEM_PASTE) {
256       g_list_free(undo->itemlist);
257     }
258     else if (undo->type == ITEM_DELETE_LAYER) {
259       undo->layer->group = NULL;
260       delete_layer(undo->layer);
261     }
262     else if (undo->type == ITEM_DELETE_PAGE) {
263       undo->page->group = NULL;
264       delete_page(undo->page);
265     }
266     else if (undo->type == ITEM_TEXT_EDIT || undo->type == ITEM_TEXT_ATTRIB) {
267       g_free(undo->str);
268       if (undo->type == ITEM_TEXT_ATTRIB) g_free(undo->brush);
269     }
270
271     u = undo;
272     undo = undo->next;
273     g_free(u);
274   }
275   update_undo_redo_enabled();
276 }
277
278 // free data structures 
279
280 void delete_journal(struct Journal *j)
281 {
282   while (j->pages!=NULL) {
283     delete_page((struct Page *)j->pages->data);
284     j->pages = g_list_delete_link(j->pages, j->pages);
285   }
286 }
287
288 void delete_page(struct Page *pg)
289 {
290   struct Layer *l;
291   
292   while (pg->layers!=NULL) {
293     l = (struct Layer *)pg->layers->data;
294     l->group = NULL;
295     delete_layer(l);
296     pg->layers = g_list_delete_link(pg->layers, pg->layers);
297   }
298   if (pg->group!=NULL) gtk_object_destroy(GTK_OBJECT(pg->group));
299               // this also destroys the background's canvas items
300   if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
301     if (pg->bg->pixbuf != NULL) gdk_pixbuf_unref(pg->bg->pixbuf);
302     if (pg->bg->filename != NULL) refstring_unref(pg->bg->filename);
303   }
304   g_free(pg->bg);
305   g_free(pg);
306 }
307
308 void delete_layer(struct Layer *l)
309 {
310   struct Item *item;
311   
312   while (l->items!=NULL) {
313     item = (struct Item *)l->items->data;
314     if (item->type == ITEM_STROKE && item->path != NULL) 
315       gnome_canvas_points_free(item->path);
316     if (item->type == ITEM_TEXT) {
317       g_free(item->font_name); g_free(item->text);
318     }
319     // don't need to delete the canvas_item, as it's part of the group destroyed below
320     g_free(item);
321     l->items = g_list_delete_link(l->items, l->items);
322   }
323   if (l->group!= NULL) gtk_object_destroy(GTK_OBJECT(l->group));
324   g_free(l);
325 }
326
327 // referenced strings
328
329 struct Refstring *new_refstring(const char *s)
330 {
331   struct Refstring *rs = g_new(struct Refstring, 1);
332   rs->nref = 1;
333   if (s!=NULL) rs->s = g_strdup(s);
334   else rs->s = NULL;
335   rs->aux = NULL;
336   return rs;
337 }
338
339 struct Refstring *refstring_ref(struct Refstring *rs)
340 {
341   rs->nref++;
342   return rs;
343 }
344
345 void refstring_unref(struct Refstring *rs)
346 {
347   rs->nref--;
348   if (rs->nref == 0) {
349     if (rs->s!=NULL) g_free(rs->s);
350     if (rs->aux!=NULL) g_free(rs->aux);
351     g_free(rs);
352   }
353 }
354
355
356 // some helper functions
357
358 void get_pointer_coords(GdkEvent *event, gdouble *ret)
359 {
360   double x, y;
361   gdk_event_get_coords(event, &x, &y);
362   gnome_canvas_window_to_world(canvas, x, y, ret, ret+1);
363   ret[0] -= ui.cur_page->hoffset;
364   ret[1] -= ui.cur_page->voffset;
365 }
366
367 void fix_xinput_coords(GdkEvent *event)
368 {
369   double *axes, *px, *py, axis_width;
370   GdkDevice *device;
371   int wx, wy, sx, sy, ix, iy;
372
373   if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
374     axes = event->button.axes;
375     px = &(event->button.x);
376     py = &(event->button.y);
377     device = event->button.device;
378   }
379   else if (event->type == GDK_MOTION_NOTIFY) {
380     axes = event->motion.axes;
381     px = &(event->motion.x);
382     py = &(event->motion.y);
383     device = event->motion.device;
384   }
385   else return; // nothing we know how to do
386
387   gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
388
389 #ifdef ENABLE_XINPUT_BUGFIX
390   // fix broken events with the core pointer's location
391   if (!finite(axes[0]) || !finite(axes[1]) || axes[0]==0. || axes[1]==0.) {
392     gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
393     *px = ix + sx; 
394     *py = iy + sy;
395   }
396   else {
397     gdk_window_get_origin(GTK_WIDGET(canvas)->window, &wx, &wy);  
398     axis_width = device->axes[0].max - device->axes[0].min;
399     if (axis_width>EPSILON)
400       *px = (axes[0]/axis_width)*ui.screen_width + sx - wx;
401     axis_width = device->axes[1].max - device->axes[1].min;
402     if (axis_width>EPSILON)
403       *py = (axes[1]/axis_width)*ui.screen_height + sy - wy;
404   }
405 #else
406   if (!finite(*px) || !finite(*py) || (*px==0. && *py==0.)) {
407     gdk_window_get_pointer(GTK_WIDGET(canvas)->window, &ix, &iy, NULL);
408     *px = ix + sx; 
409     *py = iy + sy;
410   }
411   else {
412     /* with GTK+ 2.16 or earlier, the event comes from the parent gdkwindow
413        and so needs to be adjusted for scrolling */
414     if (gtk_major_version == 2 && gtk_minor_version <= 16) {
415       *px += sx;
416       *py += sy;
417     }
418     /* with GTK+ 2.17, events come improperly translated, and the event's
419        GdkWindow isn't even the same for ButtonDown as for MotionNotify... */
420     if (gtk_major_version == 2 && gtk_minor_version == 17) { // GTK+ 2.17 issues !!
421       gdk_window_get_position(GTK_WIDGET(canvas)->window, &wx, &wy);
422       *px += sx - wx;
423       *py += sy - wy;
424     }
425   }
426 #endif
427 }
428
429 double get_pressure_multiplier(GdkEvent *event)
430 {
431   double *axes;
432   double rawpressure;
433   GdkDevice *device;
434
435   if (event->type == GDK_MOTION_NOTIFY) {
436     axes = event->motion.axes;
437     device = event->motion.device;
438   }
439   else {
440     axes = event->button.axes;
441     device = event->button.device;
442   }
443   
444   if (device == gdk_device_get_core_pointer()
445       || device->num_axes <= 2) return 1.0;
446
447   rawpressure = axes[2]/(device->axes[2].max - device->axes[2].min);
448   if (!finite(rawpressure)) return 1.0;
449
450   return ((1-rawpressure)*ui.width_minimum_multiplier + rawpressure*ui.width_maximum_multiplier);
451 }
452
453 void update_item_bbox(struct Item *item)
454 {
455   int i;
456   gdouble *p, h, w;
457   
458   if (item->type == ITEM_STROKE) {
459     item->bbox.left = item->bbox.right = item->path->coords[0];
460     item->bbox.top = item->bbox.bottom = item->path->coords[1];
461     for (i=1, p=item->path->coords+2; i<item->path->num_points; i++, p+=2)
462     {
463       if (p[0] < item->bbox.left) item->bbox.left = p[0];
464       if (p[0] > item->bbox.right) item->bbox.right = p[0];
465       if (p[1] < item->bbox.top) item->bbox.top = p[1];
466       if (p[1] > item->bbox.bottom) item->bbox.bottom = p[1];
467     }
468   }
469   if (item->type == ITEM_TEXT && item->canvas_item!=NULL) {
470     h=0.; w=0.;
471     g_object_get(item->canvas_item, "text_width", &w, "text_height", &h, NULL);
472     item->bbox.right = item->bbox.left + w;
473     item->bbox.bottom = item->bbox.top + h;
474   }
475 }
476
477 void make_page_clipbox(struct Page *pg)
478 {
479   GnomeCanvasPathDef *pg_clip;
480   
481   pg_clip = gnome_canvas_path_def_new_sized(4);
482   gnome_canvas_path_def_moveto(pg_clip, 0., 0.);
483   gnome_canvas_path_def_lineto(pg_clip, 0., pg->height);
484   gnome_canvas_path_def_lineto(pg_clip, pg->width, pg->height);
485   gnome_canvas_path_def_lineto(pg_clip, pg->width, 0.);
486   gnome_canvas_path_def_closepath(pg_clip);
487   gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), "path", pg_clip, NULL);
488   gnome_canvas_path_def_unref(pg_clip);
489 }
490
491 void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
492 {
493   PangoFontDescription *font_desc;
494   GnomeCanvasPoints points;
495   int j;
496
497   if (item->type == ITEM_STROKE) {
498     if (!item->brush.variable_width)
499       item->canvas_item = gnome_canvas_item_new(group,
500             gnome_canvas_line_get_type(), "points", item->path,   
501             "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
502             "fill-color-rgba", item->brush.color_rgba,  
503             "width-units", item->brush.thickness, NULL);
504     else {
505       item->canvas_item = gnome_canvas_item_new(group,
506             gnome_canvas_group_get_type(), NULL);
507       points.num_points = 2;
508       points.ref_count = 1;
509       for (j = 0; j < item->path->num_points-1; j++) {
510         points.coords = item->path->coords+2*j;
511         gnome_canvas_item_new((GnomeCanvasGroup *) item->canvas_item,
512               gnome_canvas_line_get_type(), "points", &points, 
513               "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND, 
514               "fill-color-rgba", item->brush.color_rgba,
515               "width-units", item->widths[j], NULL);
516       }
517     }
518   }
519   if (item->type == ITEM_TEXT) {
520     font_desc = pango_font_description_from_string(item->font_name);
521     pango_font_description_set_absolute_size(font_desc, 
522             item->font_size*ui.zoom*PANGO_SCALE);
523     item->canvas_item = gnome_canvas_item_new(group,
524           gnome_canvas_text_get_type(),
525           "x", item->bbox.left, "y", item->bbox.top, "anchor", GTK_ANCHOR_NW,
526           "font-desc", font_desc, "fill-color-rgba", item->brush.color_rgba,
527           "text", item->text, NULL);
528     update_item_bbox(item);
529   }
530 }
531
532 void make_canvas_items(void)
533 {
534   struct Page *pg;
535   struct Layer *l;
536   struct Item *item;
537   GList *pagelist, *layerlist, *itemlist;
538   
539   for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next) {
540     pg = (struct Page *)pagelist->data;
541     if (pg->group == NULL) {
542       pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
543          gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
544       make_page_clipbox(pg);
545     }
546     if (pg->bg->canvas_item == NULL) update_canvas_bg(pg);
547     for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
548       l = (struct Layer *)layerlist->data;
549       if (l->group == NULL)
550         l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
551            pg->group, gnome_canvas_group_get_type(), NULL);
552       for (itemlist = l->items; itemlist!=NULL; itemlist = itemlist->next) {
553         item = (struct Item *)itemlist->data;
554         if (item->canvas_item == NULL)
555           make_canvas_item_one(l->group, item);
556       }
557     }
558   }
559 }
560
561 void update_canvas_bg(struct Page *pg)
562 {
563   GnomeCanvasGroup *group;
564   GnomeCanvasPoints *seg;
565   GdkPixbuf *scaled_pix;
566   double *pt;
567   double x, y;
568   int w, h;
569   gboolean is_well_scaled;
570   
571   if (pg->bg->canvas_item != NULL)
572     gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
573   pg->bg->canvas_item = NULL;
574   
575   if (pg->bg->type == BG_SOLID)
576   {
577     pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
578                                gnome_canvas_group_get_type(), NULL);
579     group = GNOME_CANVAS_GROUP(pg->bg->canvas_item);
580     lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
581     gnome_canvas_item_new(group, gnome_canvas_rect_get_type(),
582       "x1", 0., "x2", pg->width, "y1", 0., "y2", pg->height,
583       "fill-color-rgba", pg->bg->color_rgba, NULL);
584     if (pg->bg->ruling == RULING_NONE) return;
585     seg = gnome_canvas_points_new(2);
586     pt = seg->coords;
587     if (pg->bg->ruling == RULING_GRAPH) {
588       pt[1] = 0; pt[3] = pg->height;
589       for (x=RULING_GRAPHSPACING; x<pg->width-1; x+=RULING_GRAPHSPACING) {
590         pt[0] = pt[2] = x;
591         gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
592            "points", seg, "fill-color-rgba", RULING_COLOR,
593            "width-units", RULING_THICKNESS, NULL);
594       }      
595       pt[0] = 0; pt[2] = pg->width;
596       for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
597         pt[1] = pt[3] = y;
598         gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
599            "points", seg, "fill-color-rgba", RULING_COLOR,
600            "width-units", RULING_THICKNESS, NULL);
601       }      
602       gnome_canvas_points_free(seg);
603       return;
604     }
605     pt[0] = 0; pt[2] = pg->width;
606     for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
607       pt[1] = pt[3] = y;
608       gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
609          "points", seg, "fill-color-rgba", RULING_COLOR,
610          "width-units", RULING_THICKNESS, NULL);
611     }      
612     if (pg->bg->ruling == RULING_LINED) {
613       pt[0] = pt[2] = RULING_LEFTMARGIN;
614       pt[1] = 0; pt[3] = pg->height;
615       gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
616          "points", seg, "fill-color-rgba", RULING_MARGIN_COLOR,
617          "width-units", RULING_THICKNESS, NULL);
618     }
619     gnome_canvas_points_free(seg);
620     return;
621   }
622   
623   if (pg->bg->type == BG_PIXMAP)
624   {
625     pg->bg->pixbuf_scale = 0;
626     pg->bg->canvas_item = gnome_canvas_item_new(pg->group, 
627         gnome_canvas_pixbuf_get_type(), 
628         "pixbuf", pg->bg->pixbuf,
629         "width", pg->width, "height", pg->height, 
630         "width-set", TRUE, "height-set", TRUE, 
631         NULL);
632     lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
633   }
634
635   if (pg->bg->type == BG_PDF)
636   {
637     if (pg->bg->pixbuf == NULL) return;
638     is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
639                    && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
640     if (is_well_scaled)
641       pg->bg->canvas_item = gnome_canvas_item_new(pg->group, 
642           gnome_canvas_pixbuf_get_type(), 
643           "pixbuf", pg->bg->pixbuf,
644           "width-in-pixels", TRUE, "height-in-pixels", TRUE, 
645           NULL);
646     else
647       pg->bg->canvas_item = gnome_canvas_item_new(pg->group, 
648           gnome_canvas_pixbuf_get_type(), 
649           "pixbuf", pg->bg->pixbuf,
650           "width", pg->width, "height", pg->height, 
651           "width-set", TRUE, "height-set", TRUE, 
652           NULL);
653     lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
654   }
655 }
656
657 gboolean is_visible(struct Page *pg)
658 {
659   GtkAdjustment *v_adj;
660   double ytop, ybot;
661   
662   if (!ui.view_continuous) return (pg == ui.cur_page);
663   v_adj = gtk_layout_get_vadjustment(GTK_LAYOUT(canvas));
664   ytop = v_adj->value/ui.zoom;
665   ybot = (v_adj->value + v_adj->page_size) / ui.zoom;
666   return (MAX(ytop, pg->voffset) < MIN(ybot, pg->voffset+pg->height));
667 }
668
669 void rescale_bg_pixmaps(void)
670 {
671   GList *pglist;
672   struct Page *pg;
673   GdkPixbuf *pix;
674   gboolean is_well_scaled;
675   gdouble zoom_to_request;
676   
677   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
678     pg = (struct Page *)pglist->data;
679     // in progressive mode we scale only visible pages
680     if (ui.progressive_bg && !is_visible(pg)) continue;
681
682     if (pg->bg->type == BG_PIXMAP && pg->bg->canvas_item!=NULL) {
683       g_object_get(G_OBJECT(pg->bg->canvas_item), "pixbuf", &pix, NULL);
684       if (pix!=pg->bg->pixbuf)
685         gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pg->bg->pixbuf, NULL);
686       pg->bg->pixbuf_scale = 0;
687     }
688     if (pg->bg->type == BG_PDF) { 
689       // make pixmap scale to correct size if current one is wrong
690       is_well_scaled = (fabs(pg->bg->pixel_width - pg->width*ui.zoom) < 2.
691                      && fabs(pg->bg->pixel_height - pg->height*ui.zoom) < 2.);
692       if (pg->bg->canvas_item != NULL && !is_well_scaled) {
693         g_object_get(pg->bg->canvas_item, "width-in-pixels", &is_well_scaled, NULL);
694         if (is_well_scaled)
695           gnome_canvas_item_set(pg->bg->canvas_item,
696             "width", pg->width, "height", pg->height, 
697             "width-in-pixels", FALSE, "height-in-pixels", FALSE, 
698             "width-set", TRUE, "height-set", TRUE, 
699             NULL);
700       }
701       // request an asynchronous update to a better pixmap if needed
702       zoom_to_request = MIN(ui.zoom, MAX_SAFE_RENDER_DPI/72.0);
703       if (pg->bg->pixbuf_scale == zoom_to_request) continue;
704       if (add_bgpdf_request(pg->bg->file_page_seq, zoom_to_request))
705         pg->bg->pixbuf_scale = zoom_to_request;
706     }
707   }
708 }
709
710 gboolean have_intersect(struct BBox *a, struct BBox *b)
711 {
712   return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
713          (MAX(a->left, b->left) <= MIN(a->right, b->right));
714 }
715
716 /* In libgnomecanvas 2.10.0, the lower/raise functions fail to update
717    correctly the end of the group's item list. We try to work around this.
718    DON'T USE gnome_canvas_item_raise/lower directly !! */
719
720 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
721 {
722   int i1, i2;
723   
724   i1 = g_list_index(g->item_list, item);
725   if (i1 == -1) return;
726   
727   if (after == NULL) i2 = -1;
728   else i2 = g_list_index(g->item_list, after);
729
730   if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
731   if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
732   
733   // BUGFIX for libgnomecanvas
734   g->item_list_end = g_list_last(g->item_list);
735 }
736
737 void rgb_to_gdkcolor(guint rgba, GdkColor *color)
738 {
739   color->pixel = 0;
740   color->red = ((rgba>>24)&0xff)*0x101;
741   color->green = ((rgba>>16)&0xff)*0x101;
742   color->blue = ((rgba>>8)&0xff)*0x101;
743 }
744
745 guint32 gdkcolor_to_rgba(GdkColor gdkcolor, guint16 alpha) 
746 {
747   guint32 rgba =  ((gdkcolor.red   & 0xff00) << 16) |
748                   ((gdkcolor.green & 0xff00) << 8)  |
749                   ((gdkcolor.blue  & 0xff00) )      |
750                   ((alpha & 0xff00) >> 8);
751
752   return rgba;
753 }
754
755 // some interface functions
756
757 void update_thickness_buttons(void)
758 {
759   if (ui.selection!=NULL || ui.toolno[ui.cur_mapping] >= NUM_STROKE_TOOLS) {
760     gtk_toggle_tool_button_set_active(
761       GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
762   } else 
763   switch (ui.cur_brush->thickness_no) {
764     case THICKNESS_FINE:
765       gtk_toggle_tool_button_set_active(
766         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
767       break;
768     case THICKNESS_MEDIUM:
769       gtk_toggle_tool_button_set_active(
770         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
771       break;
772     case THICKNESS_THICK:
773       gtk_toggle_tool_button_set_active(
774         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
775       break;
776     default:
777       gtk_toggle_tool_button_set_active(
778         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
779   }
780 }
781
782 void update_color_buttons(void)
783 {
784   GdkColor gdkcolor;
785   GtkColorButton *colorbutton;
786   
787   if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN 
788       && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
789     gtk_toggle_tool_button_set_active(
790       GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
791   } else
792   switch (ui.cur_brush->color_no) {
793     case COLOR_BLACK:
794       gtk_toggle_tool_button_set_active(
795         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
796       break;
797     case COLOR_BLUE:
798       gtk_toggle_tool_button_set_active(
799         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
800       break;
801     case COLOR_RED:
802       gtk_toggle_tool_button_set_active(
803         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
804       break;
805     case COLOR_GREEN:
806       gtk_toggle_tool_button_set_active(
807         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
808       break;
809     case COLOR_GRAY:
810       gtk_toggle_tool_button_set_active(
811         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
812       break;
813     case COLOR_LIGHTBLUE:
814       gtk_toggle_tool_button_set_active(
815         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
816       break;
817     case COLOR_LIGHTGREEN:
818       gtk_toggle_tool_button_set_active(
819         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
820       break;
821     case COLOR_MAGENTA:
822       gtk_toggle_tool_button_set_active(
823         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
824       break;
825     case COLOR_ORANGE:
826       gtk_toggle_tool_button_set_active(
827         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
828       break;
829     case COLOR_YELLOW:
830       gtk_toggle_tool_button_set_active(
831         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
832       break;
833     case COLOR_WHITE:
834       gtk_toggle_tool_button_set_active(
835         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
836       break;
837     default:
838       gtk_toggle_tool_button_set_active(
839         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
840   }
841
842   colorbutton = GTK_COLOR_BUTTON(GET_COMPONENT("buttonColorChooser"));
843   if ((ui.toolno[ui.cur_mapping] != TOOL_PEN && 
844        ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && 
845        ui.toolno[ui.cur_mapping] != TOOL_TEXT))
846     gdkcolor.red = gdkcolor.blue = gdkcolor.green = 0;
847   else rgb_to_gdkcolor(ui.cur_brush->color_rgba, &gdkcolor);
848   gtk_color_button_set_color(colorbutton, &gdkcolor);
849   if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) {
850     gtk_color_button_set_alpha(colorbutton,
851       (ui.cur_brush->color_rgba&0xff)*0x101);
852     gtk_color_button_set_use_alpha(colorbutton, TRUE);
853   } else {
854     gtk_color_button_set_alpha(colorbutton, 0xffff);
855     gtk_color_button_set_use_alpha(colorbutton, FALSE);
856   }
857 }
858
859 void update_tool_buttons(void)
860 {
861   switch(ui.toolno[ui.cur_mapping]) {
862     case TOOL_PEN:
863       gtk_toggle_tool_button_set_active(
864         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
865       break;
866     case TOOL_ERASER:
867       gtk_toggle_tool_button_set_active(
868         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
869       break;
870     case TOOL_HIGHLIGHTER:
871       gtk_toggle_tool_button_set_active(
872         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
873       break;
874     case TOOL_TEXT:
875       gtk_toggle_tool_button_set_active(
876         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
877       break;
878     case TOOL_SELECTREGION:
879       gtk_toggle_tool_button_set_active(
880         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
881       break;
882     case TOOL_SELECTRECT:
883       gtk_toggle_tool_button_set_active(
884         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
885       break;
886     case TOOL_VERTSPACE:
887       gtk_toggle_tool_button_set_active(
888         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
889       break;
890     case TOOL_HAND:
891       gtk_toggle_tool_button_set_active(
892         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
893       break;
894   }
895     
896   gtk_toggle_tool_button_set_active(
897       GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")), 
898       ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
899   gtk_toggle_tool_button_set_active(
900       GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")), 
901       ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
902
903   update_thickness_buttons();
904   update_color_buttons();
905 }
906
907 void update_tool_menu(void)
908 {
909   switch(ui.toolno[0]) {
910     case TOOL_PEN:
911       gtk_check_menu_item_set_active(
912         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
913       break;
914     case TOOL_ERASER:
915       gtk_check_menu_item_set_active(
916         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
917       break;
918     case TOOL_HIGHLIGHTER:
919       gtk_check_menu_item_set_active(
920         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
921       break;
922     case TOOL_TEXT:
923       gtk_check_menu_item_set_active(
924         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
925       break;
926     case TOOL_SELECTREGION:
927       gtk_check_menu_item_set_active(
928         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
929       break;
930     case TOOL_SELECTRECT:
931       gtk_check_menu_item_set_active(
932         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
933       break;
934     case TOOL_VERTSPACE:
935       gtk_check_menu_item_set_active(
936         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
937       break;
938     case TOOL_HAND:
939       gtk_check_menu_item_set_active(
940         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
941       break;
942   }
943
944   gtk_check_menu_item_set_active(
945       GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")), 
946       ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
947   gtk_check_menu_item_set_active(
948       GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")), 
949       ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
950 }
951
952 void update_ruler_indicator(void)
953 {
954   gtk_toggle_tool_button_set_active(
955       GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")), 
956       ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->ruler);
957   gtk_toggle_tool_button_set_active(
958       GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonReco")), 
959       ui.toolno[ui.cur_mapping]<NUM_STROKE_TOOLS && ui.cur_brush->recognizer);
960   gtk_check_menu_item_set_active(
961       GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")), 
962       ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].ruler);
963   gtk_check_menu_item_set_active(
964       GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsReco")), 
965       ui.toolno[0]<NUM_STROKE_TOOLS && ui.brushes[0][ui.toolno[0]].recognizer);
966 }
967
968 void update_color_menu(void)
969 {
970   if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN 
971     && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER && ui.toolno[ui.cur_mapping] != TOOL_TEXT)) {
972     gtk_check_menu_item_set_active(
973       GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
974   } else
975   switch (ui.cur_brush->color_no) {
976     case COLOR_BLACK:
977       gtk_check_menu_item_set_active(
978         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
979       break;
980     case COLOR_BLUE:
981       gtk_check_menu_item_set_active(
982         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
983       break;
984     case COLOR_RED:
985       gtk_check_menu_item_set_active(
986         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
987       break;
988     case COLOR_GREEN:
989       gtk_check_menu_item_set_active(
990         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
991       break;
992     case COLOR_GRAY:
993       gtk_check_menu_item_set_active(
994         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
995       break;
996     case COLOR_LIGHTBLUE:
997       gtk_check_menu_item_set_active(
998         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
999       break;
1000     case COLOR_LIGHTGREEN:
1001       gtk_check_menu_item_set_active(
1002         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
1003       break;
1004     case COLOR_MAGENTA:
1005       gtk_check_menu_item_set_active(
1006         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
1007       break;
1008     case COLOR_ORANGE:
1009       gtk_check_menu_item_set_active(
1010         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
1011       break;
1012     case COLOR_YELLOW:
1013       gtk_check_menu_item_set_active(
1014         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
1015       break;
1016     case COLOR_WHITE:
1017       gtk_check_menu_item_set_active(
1018         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
1019       break;
1020     default:
1021       gtk_check_menu_item_set_active(
1022         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
1023   }
1024 }
1025
1026 void update_pen_props_menu(void)
1027 {
1028   switch(ui.brushes[0][TOOL_PEN].thickness_no) {
1029     case THICKNESS_VERYFINE:
1030       gtk_check_menu_item_set_active(
1031         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
1032       break;
1033     case THICKNESS_FINE:
1034       gtk_check_menu_item_set_active(
1035         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
1036       break;
1037     case THICKNESS_MEDIUM:
1038       gtk_check_menu_item_set_active(
1039         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
1040       break;
1041     case THICKNESS_THICK:
1042       gtk_check_menu_item_set_active(
1043         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
1044       break;
1045     case THICKNESS_VERYTHICK:
1046       gtk_check_menu_item_set_active(
1047         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
1048       break;
1049   }
1050 }
1051
1052 void update_eraser_props_menu(void)
1053 {
1054   switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
1055     case THICKNESS_FINE:
1056       gtk_check_menu_item_set_active(
1057         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
1058       break;
1059     case THICKNESS_MEDIUM:
1060       gtk_check_menu_item_set_active(
1061         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
1062       break;
1063     case THICKNESS_THICK:
1064       gtk_check_menu_item_set_active(
1065         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
1066       break;
1067   }
1068   
1069   gtk_check_menu_item_set_active(
1070     GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
1071     ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
1072   gtk_check_menu_item_set_active(
1073     GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
1074     ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
1075   gtk_check_menu_item_set_active(
1076     GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
1077     ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
1078 }
1079
1080 void update_highlighter_props_menu(void)
1081 {
1082   switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
1083     case THICKNESS_FINE:
1084       gtk_check_menu_item_set_active(
1085         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
1086       break;
1087     case THICKNESS_MEDIUM:
1088       gtk_check_menu_item_set_active(
1089         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
1090       break;
1091     case THICKNESS_THICK:
1092       gtk_check_menu_item_set_active(
1093         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
1094       break;
1095   }
1096 }
1097
1098 void update_mappings_menu_linkings(void)
1099 {
1100   switch (ui.linked_brush[1]) {
1101     case BRUSH_LINKED:
1102       gtk_check_menu_item_set_active(
1103         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
1104       break;
1105     case BRUSH_COPIED:
1106       gtk_check_menu_item_set_active(
1107         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
1108       break;
1109     case BRUSH_STATIC:
1110       gtk_check_menu_item_set_active(
1111         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
1112       break;
1113   }
1114   switch (ui.linked_brush[2]) {
1115     case BRUSH_LINKED:
1116       gtk_check_menu_item_set_active(
1117         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
1118       break;
1119     case BRUSH_COPIED:
1120       gtk_check_menu_item_set_active(
1121         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
1122       break;
1123     case BRUSH_STATIC:
1124       gtk_check_menu_item_set_active(
1125         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
1126       break;
1127   }
1128 }
1129
1130 void update_mappings_menu(void)
1131 {
1132   gtk_widget_set_sensitive(GET_COMPONENT("optionsButtonMappings"), ui.use_xinput);
1133   gtk_widget_set_sensitive(GET_COMPONENT("optionsPressureSensitive"), ui.use_xinput);
1134   gtk_check_menu_item_set_active(
1135     GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
1136   gtk_check_menu_item_set_active(
1137     GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsPressureSensitive")), ui.pressure_sensitivity);
1138
1139   switch(ui.toolno[1]) {
1140     case TOOL_PEN:
1141       gtk_check_menu_item_set_active(
1142         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
1143       break;
1144     case TOOL_ERASER:
1145       gtk_check_menu_item_set_active(
1146         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
1147       break;
1148     case TOOL_HIGHLIGHTER:
1149       gtk_check_menu_item_set_active(
1150         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
1151       break;
1152     case TOOL_TEXT:
1153       gtk_check_menu_item_set_active(
1154         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
1155       break;
1156     case TOOL_SELECTREGION:
1157       gtk_check_menu_item_set_active(
1158         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
1159       break;
1160     case TOOL_SELECTRECT:
1161       gtk_check_menu_item_set_active(
1162         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
1163       break;
1164     case TOOL_VERTSPACE:
1165       gtk_check_menu_item_set_active(
1166         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
1167       break;
1168   }
1169   switch(ui.toolno[2]) {
1170     case TOOL_PEN:
1171       gtk_check_menu_item_set_active(
1172         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
1173       break;
1174     case TOOL_ERASER:
1175       gtk_check_menu_item_set_active(
1176         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
1177       break;
1178     case TOOL_HIGHLIGHTER:
1179       gtk_check_menu_item_set_active(
1180         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
1181       break;
1182     case TOOL_TEXT:
1183       gtk_check_menu_item_set_active(
1184         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
1185       break;
1186     case TOOL_SELECTREGION:
1187       gtk_check_menu_item_set_active(
1188         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
1189       break;
1190     case TOOL_SELECTRECT:
1191       gtk_check_menu_item_set_active(
1192         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
1193       break;
1194     case TOOL_VERTSPACE:
1195       gtk_check_menu_item_set_active(
1196         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1197       break;
1198   }
1199   update_mappings_menu_linkings();
1200 }
1201
1202 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1203 {
1204   int i, cx, cy;
1205   struct Layer *layer;
1206   GList *list;
1207   
1208   ui.pageno = pg;
1209
1210   /* re-show all the layers of the old page */
1211   if (ui.cur_page != NULL)
1212     for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1213       layer = (struct Layer *)list->data;
1214       if (layer->group!=NULL)
1215         gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1216     }
1217   
1218   ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1219   ui.layerno = ui.cur_page->nlayers-1;
1220   ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1221   update_page_stuff();
1222   if (ui.progressive_bg) rescale_bg_pixmaps();
1223  
1224   if (rescroll) { // scroll and force a refresh
1225 /* -- this seems to cause some display bugs ??
1226     gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1227       ui.cur_page->voffset*ui.zoom);  */
1228     gnome_canvas_get_scroll_offsets(canvas, &cx, &cy);
1229     cy = ui.cur_page->voffset*ui.zoom;
1230     gnome_canvas_scroll_to(canvas, cx, cy);
1231     
1232     if (refresh_all) 
1233       gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1234     else if (!ui.view_continuous)
1235       gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1236   }
1237 }
1238
1239 void update_page_stuff(void)
1240 {
1241   gchar tmp[10];
1242   GtkComboBox *layerbox;
1243   int i;
1244   GList *pglist;
1245   GtkSpinButton *spin;
1246   struct Page *pg;
1247   double vertpos, maxwidth;
1248
1249   // move the page groups to their rightful locations or hide them
1250   if (ui.view_continuous) {
1251     vertpos = 0.; 
1252     maxwidth = 0.;
1253     for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1254       pg = (struct Page *)pglist->data;
1255       if (pg->group!=NULL) {
1256         pg->hoffset = 0.; pg->voffset = vertpos;
1257         gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), 
1258             "x", pg->hoffset, "y", pg->voffset, NULL);
1259         gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1260       }
1261       vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1262       if (pg->width > maxwidth) maxwidth = pg->width;
1263     }
1264     vertpos -= VIEW_CONTINUOUS_SKIP;
1265     gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1266   } else {
1267     for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1268       pg = (struct Page *)pglist->data;
1269       if (pg == ui.cur_page && pg->group!=NULL) {
1270         pg->hoffset = 0.; pg->voffset = 0.;
1271         gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), 
1272             "x", pg->hoffset, "y", pg->voffset, NULL);
1273         gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1274       } else {
1275         if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1276       }
1277     }
1278     gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1279   }
1280
1281   // update the page / layer info at bottom of screen
1282
1283   spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1284   ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1285   gtk_spin_button_set_range(spin, 1, journal.npages+1);
1286     /* npages+1 will be used to create a new page at end */
1287   gtk_spin_button_set_value(spin, ui.pageno+1);
1288   g_snprintf(tmp, 10, _(" of %d"), journal.npages);
1289   gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1290
1291   layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1292   if (ui.layerbox_length == 0) {
1293     gtk_combo_box_prepend_text(layerbox, _("Background"));
1294     ui.layerbox_length++;
1295   }
1296   while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1297     gtk_combo_box_remove_text(layerbox, 0);
1298     ui.layerbox_length--;
1299   }
1300   while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1301     g_snprintf(tmp, 10, _("Layer %d"), ui.layerbox_length++);
1302     gtk_combo_box_prepend_text(layerbox, tmp);
1303   }
1304   gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1305   ui.in_update_page_stuff = FALSE;
1306   
1307   gtk_container_forall(GTK_CONTAINER(layerbox), unset_flags, (gpointer)GTK_CAN_FOCUS);
1308   
1309   // update the paper-style menu radio buttons
1310   
1311   if (ui.view_continuous)
1312     gtk_check_menu_item_set_active(
1313        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1314   else
1315     gtk_check_menu_item_set_active(
1316        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1317
1318   if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1319     switch (ui.cur_page->bg->color_no) {
1320       case COLOR_WHITE:
1321         gtk_check_menu_item_set_active(
1322           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1323         break;
1324       case COLOR_YELLOW:
1325         gtk_check_menu_item_set_active(
1326           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1327         break;
1328       case COLOR_RED:
1329         gtk_check_menu_item_set_active(
1330           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1331         break;
1332       case COLOR_ORANGE:
1333         gtk_check_menu_item_set_active(
1334           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1335         break;
1336       case COLOR_BLUE:
1337         gtk_check_menu_item_set_active(
1338           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1339         break;
1340       case COLOR_GREEN:
1341         gtk_check_menu_item_set_active(
1342           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1343         break;
1344       default:
1345         gtk_check_menu_item_set_active(
1346           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1347         break;
1348     }
1349     switch (ui.cur_page->bg->ruling) {
1350       case RULING_NONE:
1351         gtk_check_menu_item_set_active(
1352           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1353         break;
1354       case RULING_LINED:
1355         gtk_check_menu_item_set_active(
1356           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1357         break;
1358       case RULING_RULED:
1359         gtk_check_menu_item_set_active(
1360           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1361         break;
1362       case RULING_GRAPH:
1363         gtk_check_menu_item_set_active(
1364           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1365         break;
1366     }
1367   } else {
1368     gtk_check_menu_item_set_active(
1369       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1370     gtk_check_menu_item_set_active(
1371       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1372   }
1373   
1374   // enable/disable the page/layer menu items and toolbar buttons
1375
1376   gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"), 
1377      ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1378   gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1379      ui.cur_page->bg->type == BG_SOLID);
1380   
1381   gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1382   gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1383   gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1384   gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1385   gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1386   gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1387   gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1388   gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1389   
1390   gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1391   gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1392
1393   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1394   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1395 }
1396
1397 void update_toolbar_and_menu(void)
1398 {
1399   update_tool_buttons(); // takes care of other toolbar buttons as well  
1400   update_tool_menu();
1401   update_color_menu();
1402   update_pen_props_menu();
1403   update_eraser_props_menu();
1404   update_highlighter_props_menu();
1405   update_mappings_menu();
1406
1407   gtk_toggle_tool_button_set_active(
1408     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1409   gtk_check_menu_item_set_active(
1410     GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1411 }
1412
1413 void update_file_name(char *filename)
1414 {
1415   gchar tmp[100], *p;
1416   if (ui.filename != NULL) g_free(ui.filename);
1417   ui.filename = filename;
1418   if (filename == NULL) {
1419     gtk_window_set_title(GTK_WINDOW (winMain), _("Xournal"));
1420     return;
1421   }
1422   p = g_utf8_strrchr(filename, -1, '/');
1423   if (p == NULL) p = filename; 
1424   else p = g_utf8_next_char(p);
1425   g_snprintf(tmp, 100, _("Xournal - %s"), p);
1426   gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1427   new_mru_entry(filename);
1428
1429   if (filename[0]=='/') {
1430     if (ui.default_path!=NULL) g_free(ui.default_path);
1431     ui.default_path = g_path_get_dirname(filename);
1432   }
1433 }
1434
1435 void update_undo_redo_enabled(void)
1436 {
1437   gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1438   gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1439   gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1440   gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1441 }
1442
1443 void update_copy_paste_enabled(void)
1444 {
1445   gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1446   gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1447   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_item_type!=ITEM_TEXT);
1448   gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1449   gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1450   gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1451   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_item_type!=ITEM_TEXT);
1452 }
1453
1454 void update_mapping_linkings(int toolno)
1455 {
1456   int i;
1457   
1458   for (i = 1; i<=NUM_BUTTONS; i++) {
1459     if (ui.linked_brush[i] == BRUSH_LINKED) {
1460       if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1461         g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1462     }
1463     if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1464       ui.linked_brush[i] = BRUSH_STATIC;
1465       if (i==1 || i==2) update_mappings_menu_linkings();
1466     }
1467   }
1468 }
1469
1470 void set_cur_color(int color_no, guint color_rgba)
1471 {
1472   int which_mapping, tool;
1473   
1474   if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) tool = TOOL_HIGHLIGHTER;
1475   else tool = TOOL_PEN;
1476   if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1477     which_mapping = ui.cur_mapping;
1478   else which_mapping = 0;
1479
1480   ui.brushes[which_mapping][tool].color_no = color_no;
1481   if (tool == TOOL_HIGHLIGHTER && (color_rgba & 0xff) == 0xff)
1482     ui.brushes[which_mapping][tool].color_rgba = color_rgba & ui.hiliter_alpha_mask;
1483   else
1484     ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1485   update_mapping_linkings(tool);
1486 }
1487
1488 void recolor_temp_text(int color_no, guint color_rgba)
1489 {
1490   GdkColor gdkcolor;
1491   
1492   if (ui.cur_item_type!=ITEM_TEXT) return;
1493   if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1494     prepare_new_undo();
1495     undo->type = ITEM_TEXT_ATTRIB;
1496     undo->item = ui.cur_item;
1497     undo->str = g_strdup(ui.cur_item->font_name);
1498     undo->val_x = ui.cur_item->font_size;
1499     undo->brush = (struct Brush *)g_memdup(&(ui.cur_item->brush), sizeof(struct Brush));
1500   }
1501   ui.cur_item->brush.color_no = color_no;
1502   ui.cur_item->brush.color_rgba = color_rgba;
1503   rgb_to_gdkcolor(color_rgba, &gdkcolor);
1504   gtk_widget_modify_text(ui.cur_item->widget, GTK_STATE_NORMAL, &gdkcolor);
1505   gtk_widget_grab_focus(ui.cur_item->widget);
1506 }
1507
1508 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1509 {
1510   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1511     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1512       return;
1513   } 
1514   else if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_TOOL_BUTTON) {
1515     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1516       return;
1517   }
1518
1519   if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1520
1521   if (ui.cur_item_type == ITEM_TEXT)
1522     recolor_temp_text(color_no, color_rgba);
1523
1524   if (ui.selection != NULL) {
1525     recolor_selection(color_no, color_rgba);
1526     update_color_buttons();
1527     update_color_menu();
1528   }
1529   
1530   if (ui.toolno[ui.cur_mapping] != TOOL_PEN && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER
1531       && ui.toolno[ui.cur_mapping] != TOOL_TEXT) {
1532     if (ui.selection != NULL) return;
1533     ui.cur_mapping = 0;
1534     end_text();
1535     ui.toolno[ui.cur_mapping] = TOOL_PEN;
1536     ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1537     update_tool_buttons();
1538     update_tool_menu();
1539   }
1540   
1541   set_cur_color(color_no, color_rgba);
1542   update_color_buttons();
1543   update_color_menu();
1544   update_cursor();
1545 }
1546
1547 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1548 {
1549   int which_mapping;
1550   
1551   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1552     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1553       return;
1554   } else {
1555     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1556       return;
1557   }
1558
1559   if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1560
1561   if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1562     rethicken_selection(val);
1563     update_thickness_buttons();
1564   }
1565
1566   if (tool >= NUM_STROKE_TOOLS) {
1567     update_thickness_buttons(); // undo illegal button selection
1568     return;
1569   }
1570
1571   if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1572     which_mapping = ui.cur_mapping;
1573   else which_mapping = 0;
1574   if (ui.brushes[which_mapping][tool].thickness_no == val) return;
1575   end_text();
1576   ui.brushes[which_mapping][tool].thickness_no = val;
1577   ui.brushes[which_mapping][tool].thickness = predef_thickness[tool][val];
1578   update_mapping_linkings(tool);
1579   
1580   update_thickness_buttons();
1581   if (tool == TOOL_PEN) update_pen_props_menu();
1582   if (tool == TOOL_ERASER) update_eraser_props_menu();
1583   if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1584   update_cursor();
1585 }
1586
1587 void process_papercolor_activate(GtkMenuItem *menuitem, int color, guint rgba)
1588 {
1589   struct Page *pg;
1590   GList *pglist;
1591   gboolean hasdone;
1592
1593   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1594     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1595       return;
1596   }
1597
1598   if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages || color == COLOR_OTHER)
1599     gtk_check_menu_item_set_active(
1600       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1601
1602   pg = ui.cur_page;
1603   hasdone = FALSE;
1604   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1605     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1606     if (pg->bg->type == BG_SOLID && pg->bg->color_rgba != rgba) {
1607       prepare_new_undo();
1608       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1609       undo->multiop |= MULTIOP_CONT_REDO;
1610       hasdone = TRUE;
1611       undo->type = ITEM_NEW_BG_ONE;
1612       undo->page = pg;
1613       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1614       undo->bg->canvas_item = NULL;
1615
1616       pg->bg->color_no = color;
1617       pg->bg->color_rgba = rgba;
1618       update_canvas_bg(pg);
1619     }
1620     if (!ui.bg_apply_all_pages) break;
1621   }
1622   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1623 }
1624
1625 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1626 {
1627   struct Page *pg;
1628   GList *pglist;
1629   gboolean hasdone, must_upd;
1630
1631   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1632     return;
1633
1634   if (ui.bg_apply_all_pages)
1635     gtk_check_menu_item_set_active(
1636       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1637
1638   pg = ui.cur_page;
1639   hasdone = FALSE;
1640   must_upd = FALSE;
1641   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1642     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1643     if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1644       prepare_new_undo();
1645       undo->type = ITEM_NEW_BG_ONE;
1646       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1647       undo->multiop |= MULTIOP_CONT_REDO;
1648       hasdone = TRUE;
1649       undo->page = pg;
1650       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1651       undo->bg->canvas_item = NULL;
1652
1653       if (pg->bg->type != BG_SOLID) {
1654         pg->bg->type = BG_SOLID;
1655         pg->bg->color_no = COLOR_WHITE;
1656         pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1657         pg->bg->filename = NULL;
1658         pg->bg->pixbuf = NULL;
1659         must_upd = TRUE;
1660       }
1661       pg->bg->ruling = style;
1662       update_canvas_bg(pg);
1663     }
1664     if (!ui.bg_apply_all_pages) break;
1665   }
1666   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1667   if (must_upd) update_page_stuff();
1668 }
1669
1670 gboolean ok_to_close(void)
1671 {
1672   GtkWidget *dialog;
1673   GtkResponseType response;
1674
1675   if (ui.saved) return TRUE;
1676   dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1677     GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE, _("Save changes to '%s'?"),
1678     (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1679   gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_DISCARD, GTK_RESPONSE_NO);
1680   gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
1681   gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1682   gtk_dialog_set_default_response(GTK_DIALOG (dialog), GTK_RESPONSE_YES);
1683   response = gtk_dialog_run(GTK_DIALOG (dialog));
1684   gtk_widget_destroy(dialog);
1685   if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) 
1686     return FALSE; // aborted
1687   if (response == GTK_RESPONSE_YES) {
1688     on_fileSave_activate(NULL, NULL);
1689     if (!ui.saved) return FALSE; // if save failed, then we abort
1690   }
1691   return TRUE;
1692 }
1693
1694 // send the focus back to the appropriate widget
1695
1696 void reset_focus(void)
1697 {
1698   if (ui.cur_item_type == ITEM_TEXT)
1699     gtk_widget_grab_focus(ui.cur_item->widget);
1700   else
1701     gtk_widget_grab_focus(GTK_WIDGET(canvas));
1702 }
1703
1704 // selection / clipboard stuff
1705
1706 void reset_selection(void)
1707 {
1708   if (ui.selection == NULL) return;
1709   if (ui.selection->canvas_item != NULL) 
1710     gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1711   g_list_free(ui.selection->items);
1712   g_free(ui.selection);
1713   ui.selection = NULL;
1714   update_copy_paste_enabled();
1715   update_color_menu();
1716   update_thickness_buttons();
1717   update_color_buttons();
1718   update_font_button();
1719   update_cursor();
1720 }
1721
1722 void move_journal_items_by(GList *itemlist, double dx, double dy,
1723                               struct Layer *l1, struct Layer *l2, GList *depths)
1724 {
1725   struct Item *item;
1726   GnomeCanvasItem *refitem;
1727   GList *link;
1728   int i;
1729   double *pt;
1730   
1731   while (itemlist!=NULL) {
1732     item = (struct Item *)itemlist->data;
1733     if (item->type == ITEM_STROKE)
1734       for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1735         { pt[0] += dx; pt[1] += dy; }
1736     if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1737       item->bbox.left += dx;
1738       item->bbox.right += dx;
1739       item->bbox.top += dy;
1740       item->bbox.bottom += dy;
1741     }
1742     if (l1 != l2) {
1743       // find out where to insert
1744       if (depths != NULL) {
1745         if (depths->data == NULL) link = l2->items;
1746         else {
1747           link = g_list_find(l2->items, depths->data);
1748           if (link != NULL) link = link->next;
1749         }
1750       } else link = NULL;
1751       l2->items = g_list_insert_before(l2->items, link, item);
1752       l2->nitems++;
1753       l1->items = g_list_remove(l1->items, item);
1754       l1->nitems--;
1755     }
1756     if (depths != NULL) { // also raise/lower the canvas items
1757       if (item->canvas_item!=NULL) {
1758         if (depths->data == NULL) link = NULL;
1759         else link = g_list_find(l2->items, depths->data);
1760         if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1761         else refitem = NULL;
1762         lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1763       }
1764       depths = depths->next;
1765     }
1766     itemlist = itemlist->next;
1767   }
1768 }
1769
1770 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1771                              double offset_x, double offset_y)
1772 {
1773   struct Item *item;
1774   GList *list;
1775   double mean_scaling, temp;
1776   double *pt, *wid;
1777   GnomeCanvasGroup *group;
1778   int i; 
1779   
1780   /* geometric mean of x and y scalings = rescaling for stroke widths
1781      and for text font sizes */
1782   mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1783
1784   for (list = itemlist; list != NULL; list = list->next) {
1785     item = (struct Item *)list->data;
1786     if (item->type == ITEM_STROKE) {
1787       item->brush.thickness = item->brush.thickness * mean_scaling;
1788       for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1789         pt[0] = pt[0]*scaling_x + offset_x;
1790         pt[1] = pt[1]*scaling_y + offset_y;
1791       }
1792       if (item->brush.variable_width)
1793         for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1794           *wid = *wid * mean_scaling;
1795
1796       item->bbox.left = item->bbox.left*scaling_x + offset_x;
1797       item->bbox.right = item->bbox.right*scaling_x + offset_x;
1798       item->bbox.top = item->bbox.top*scaling_y + offset_y;
1799       item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1800       if (item->bbox.left > item->bbox.right) {
1801         temp = item->bbox.left;
1802         item->bbox.left = item->bbox.right;
1803         item->bbox.right = temp;
1804       }
1805       if (item->bbox.top > item->bbox.bottom) {
1806         temp = item->bbox.top;
1807         item->bbox.top = item->bbox.bottom;
1808         item->bbox.bottom = temp;
1809       }
1810     }
1811     if (item->type == ITEM_TEXT) {
1812       /* must scale about NW corner -- all other points of the text box
1813          are font- and zoom-dependent, so scaling about center of text box
1814          couldn't be undone properly. FIXME? */
1815       item->font_size *= mean_scaling;
1816       item->bbox.left = item->bbox.left*scaling_x + offset_x;
1817       item->bbox.top = item->bbox.top*scaling_y + offset_y;
1818     }
1819     // redraw the item
1820     if (item->canvas_item!=NULL) {
1821       group = (GnomeCanvasGroup *) item->canvas_item->parent;
1822       gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1823       make_canvas_item_one(group, item);
1824     }
1825   }
1826 }
1827
1828 // Switch between button mappings
1829
1830 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1831    click event is being processed ... or if ui.button_switch_mapping is
1832    enabled and mappings are switched (but even then, canvas should have
1833    a pointer grab from the initial click that switched the mapping) */
1834
1835 void switch_mapping(int m)
1836 {
1837   if (ui.cur_mapping == m) return;
1838
1839   ui.cur_mapping = m;
1840   if (ui.toolno[m] < NUM_STROKE_TOOLS) 
1841     ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1842   if (ui.toolno[m] == TOOL_TEXT)
1843     ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1844   if (m==0) ui.which_unswitch_button = 0;
1845   
1846   update_tool_buttons();
1847   update_color_menu();
1848   update_cursor();
1849 }
1850
1851 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1852 {
1853   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1854   if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1855   if (ui.toolno[m] == tool) return;
1856   switch_mapping(0);
1857   end_text();
1858     
1859   ui.toolno[m] = tool;
1860   if (ui.linked_brush[m] == BRUSH_COPIED) {
1861     ui.linked_brush[m] = BRUSH_STATIC;
1862     update_mappings_menu_linkings();
1863   }
1864 }
1865
1866 // update the ordering of components in the main vbox
1867
1868 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1869  {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1870
1871 void update_vbox_order(int *order)
1872 {
1873   int i, j;
1874   GtkWidget *child;
1875   GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1876   gboolean present[VBOX_MAIN_NITEMS];
1877   
1878   for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1879   j=0;
1880   for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1881     if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1882     present[order[i]] = TRUE;
1883     child = GET_COMPONENT(vbox_component_names[order[i]]);
1884     gtk_box_reorder_child(vboxMain, child, j++);
1885     gtk_widget_show(child);
1886   }
1887   for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1888     if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1889 }
1890
1891 gchar *make_cur_font_name(void)
1892 {
1893   gchar *str;
1894   struct Item *it;
1895
1896   if (ui.cur_item_type == ITEM_TEXT)
1897     str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1898   else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1899            ui.selection->items->next==NULL &&
1900            (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1901     str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1902   else
1903     str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1904   return str;
1905 }
1906
1907 void update_font_button(void)
1908 {
1909   gchar *str;
1910
1911   str = make_cur_font_name();
1912   gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1913   g_free(str);
1914 }
1915
1916 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1917 {
1918   return GTK_WIDGET_SENSITIVE(widget);
1919 }
1920
1921 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1922 {
1923   if (ui.cur_item_type == ITEM_TEXT) {
1924     g_signal_stop_emission_by_name(widget, "can-activate-accel");
1925     return FALSE;
1926   }
1927   return GTK_WIDGET_SENSITIVE(widget);
1928 }
1929
1930 void allow_all_accels(void)
1931 {
1932   g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1933       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1934   g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1935       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1936   g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1937       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1938   g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1939       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1940   g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1941       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1942   g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1943       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1944   g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1945       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1946   g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1947       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1948   g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1949       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1950   g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1951       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1952   g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1953       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1954   g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1955       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1956   g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1957       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1958   g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1959       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1960   g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1961       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1962   g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1963       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1964   g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1965       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1966   g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1967       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1968   g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1969       "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1970   g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1971       "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1972   g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1973       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1974   g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1975       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1976   g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1977       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1978   g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1979       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1980   g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1981       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1982 /*  g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1983       "can-activate-accel", G_CALLBACK(can_accel), NULL);  */
1984   g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1985       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1986   g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1987       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1988   g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1989       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1990   g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1991       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1992   g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1993       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1994   g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
1995       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1996 }
1997
1998 void add_scroll_bindings(void)
1999 {
2000   GtkBindingSet *binding_set;
2001   
2002   binding_set = gtk_binding_set_by_class(
2003      G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
2004   gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
2005     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
2006     G_TYPE_BOOLEAN, FALSE);  
2007   gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
2008     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
2009     G_TYPE_BOOLEAN, FALSE);  
2010   gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
2011     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
2012     G_TYPE_BOOLEAN, FALSE);  
2013   gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
2014     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
2015     G_TYPE_BOOLEAN, FALSE);  
2016   gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
2017     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
2018     G_TYPE_BOOLEAN, TRUE);  
2019   gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
2020     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
2021     G_TYPE_BOOLEAN, TRUE);  
2022   gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
2023     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
2024     G_TYPE_BOOLEAN, TRUE);  
2025   gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
2026     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
2027     G_TYPE_BOOLEAN, TRUE);  
2028 }
2029
2030 gboolean is_event_within_textview(GdkEventButton *event)
2031 {
2032   double pt[2];
2033   
2034   if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2035   get_pointer_coords((GdkEvent *)event, pt);
2036   if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2037   if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2038   return TRUE;
2039 }
2040
2041 void hide_unimplemented(void)
2042 {
2043   gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2044   gtk_widget_hide(GET_COMPONENT("journalFlatten"));  
2045   gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2046   gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2047   gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2048   gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2049   gtk_widget_hide(GET_COMPONENT("helpIndex")); 
2050
2051   /* config file only works with glib 2.6 and beyond */
2052   if (glib_minor_version<6) {
2053     gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2054     gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2055   }
2056   /* gtkprint only works with gtk+ 2.10 and beyond */
2057   if (gtk_check_version(2, 10, 0)) {
2058     gtk_widget_hide(GET_COMPONENT("filePrint"));
2059   }  
2060   
2061   /* screenshot feature doesn't work yet in Win32 */
2062 #ifdef WIN32
2063   gtk_widget_hide(GET_COMPONENT("journalScreenshot"));
2064 #endif
2065 }  
2066
2067 // toggle fullscreen mode
2068 void do_fullscreen(gboolean active)
2069 {
2070   end_text();
2071   ui.fullscreen = active;
2072   gtk_check_menu_item_set_active(
2073     GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2074   gtk_toggle_tool_button_set_active(
2075     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2076
2077   if (ui.fullscreen) {
2078 #ifdef WIN32
2079     gtk_window_get_size(GTK_WINDOW(winMain), &ui.pre_fullscreen_width, &ui.pre_fullscreen_height);
2080     gtk_widget_set_size_request(GTK_WIDGET(winMain), gdk_screen_width(),
2081                                                      gdk_screen_height());
2082 #endif
2083     gtk_window_fullscreen(GTK_WINDOW(winMain));
2084   }
2085   else {
2086 #ifdef WIN32
2087     gtk_widget_set_size_request(GTK_WIDGET(winMain), -1, -1);
2088     gtk_window_resize(GTK_WINDOW(winMain), ui.pre_fullscreen_width,
2089                                            ui.pre_fullscreen_height);
2090 #endif
2091     gtk_window_unfullscreen(GTK_WINDOW(winMain));
2092   }
2093
2094   update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2095 }
2096
2097 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2098    elements receive XInput events that they can't handle properly    */
2099
2100 // prevent interface items from getting bogus XInput events
2101
2102 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2103                                    gpointer user_data)
2104 {
2105   if (event->type == GDK_MOTION_NOTIFY &&
2106       event->motion.device != gdk_device_get_core_pointer())
2107     return TRUE;
2108   if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2109       event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2110       event->button.device != gdk_device_get_core_pointer())
2111     return TRUE;
2112   return FALSE;
2113 }
2114
2115 /* Code to turn an extended input event into a core event and send it to
2116    a different GdkWindow -- e.g. could be used when a click in a text edit box
2117    gets sent to the canvas instead due to incorrect event translation.
2118    We now turn off xinput altogether while editing text under GTK+ 2.17, so
2119    this isn't needed any more... but could become useful again someday!
2120 */
2121
2122 /*  
2123 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2124                                    gpointer user_data)
2125 {
2126   int ix, iy;
2127   GdkWindow *window;
2128
2129   if (user_data) window = (GdkWindow *)user_data;
2130   else window = widget->window;
2131
2132   if (event->type == GDK_MOTION_NOTIFY &&
2133       event->motion.device != gdk_device_get_core_pointer()) {
2134 //    printf("fixing motion\n");
2135     gdk_window_get_pointer(window, &ix, &iy, NULL);
2136     event->motion.x = ix; event->motion.y = iy;
2137     event->motion.device = gdk_device_get_core_pointer();
2138     g_object_unref(event->motion.window);
2139     event->motion.window = g_object_ref(window);
2140     gtk_widget_event(widget, event);
2141     return TRUE;
2142   }
2143   if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2144       event->button.device != gdk_device_get_core_pointer()) {
2145 //    printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2146     gdk_window_get_pointer(window, &ix, &iy, NULL);
2147     event->button.x = ix; event->button.y = iy;
2148     event->button.device = gdk_device_get_core_pointer();
2149     g_object_unref(event->button.window);
2150     event->button.window = g_object_ref(window);
2151 //    printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2152     gtk_widget_event(widget, event);
2153     return TRUE;
2154   }
2155   return FALSE;
2156 }
2157 */
2158
2159
2160 /* When enter is pressed into page spinbox, send focus back to canvas. */
2161
2162 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2163 {
2164   reset_focus();
2165   return FALSE;
2166 }
2167
2168 /* recursively unset widget flags */
2169
2170 void unset_flags(GtkWidget *w, gpointer flag)
2171 {
2172   GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2173   if(GTK_IS_CONTAINER(w))
2174     gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2175 }
2176
2177 /* reset focus when a key or button press event reaches someone, or when the
2178    page-number spin button should relinquish control... */
2179
2180 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2181 {
2182   if (w == GET_COMPONENT("hbox1")) {
2183     /* the event won't be processed since the hbox1 doesn't know what to do with it,
2184        so we might as well kill it and avoid confusing ourselves when it gets
2185        propagated further ... */
2186     return TRUE;
2187   }
2188   if (w == GET_COMPONENT("spinPageNo")) {
2189     /* we let the spin button take care of itself, and don't steal its focus,
2190        unless the user presses Esc or Tab (in those cases we intervene) */
2191     if (ev->type != GDK_KEY_PRESS) return FALSE;
2192     if (ev->key.keyval == GDK_Escape) 
2193        gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2194     else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2195        return FALSE; // let the spin button process it
2196   }
2197
2198   // otherwise, we want to make sure the canvas or text item gets focus back...
2199   reset_focus();  
2200   return FALSE;
2201 }
2202
2203 void install_focus_hooks(GtkWidget *w, gpointer data)
2204 {
2205   if (w == NULL) return;
2206   g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2207   g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2208   if (GTK_IS_MENU_ITEM(w)) {
2209     g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2210     install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2211   }
2212   if(GTK_IS_CONTAINER(w))
2213     gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);
2214 }
2215
2216 // wrapper for missing poppler functions (defunct poppler-gdk api)
2217
2218 static void
2219 wrapper_copy_cairo_surface_to_pixbuf (cairo_surface_t *surface,
2220                               GdkPixbuf       *pixbuf)
2221 {
2222   int cairo_width, cairo_height, cairo_rowstride;
2223   unsigned char *pixbuf_data, *dst, *cairo_data;
2224   int pixbuf_rowstride, pixbuf_n_channels;
2225   unsigned int *src;
2226   int x, y;
2227
2228   cairo_width = cairo_image_surface_get_width (surface);
2229   cairo_height = cairo_image_surface_get_height (surface);
2230   cairo_rowstride = cairo_image_surface_get_stride (surface);
2231   cairo_data = cairo_image_surface_get_data (surface);
2232
2233   pixbuf_data = gdk_pixbuf_get_pixels (pixbuf);
2234   pixbuf_rowstride = gdk_pixbuf_get_rowstride (pixbuf);
2235   pixbuf_n_channels = gdk_pixbuf_get_n_channels (pixbuf);
2236
2237   if (cairo_width > gdk_pixbuf_get_width (pixbuf))
2238     cairo_width = gdk_pixbuf_get_width (pixbuf);
2239   if (cairo_height > gdk_pixbuf_get_height (pixbuf))
2240     cairo_height = gdk_pixbuf_get_height (pixbuf);
2241   for (y = 0; y < cairo_height; y++)
2242     {
2243       src = (unsigned int *) (cairo_data + y * cairo_rowstride);
2244       dst = pixbuf_data + y * pixbuf_rowstride;
2245       for (x = 0; x < cairo_width; x++) 
2246         {
2247           dst[0] = (*src >> 16) & 0xff;
2248           dst[1] = (*src >> 8) & 0xff; 
2249           dst[2] = (*src >> 0) & 0xff;
2250           if (pixbuf_n_channels == 4)
2251               dst[3] = (*src >> 24) & 0xff;
2252           dst += pixbuf_n_channels;
2253           src++;
2254         }
2255     }
2256 }       
2257
2258 void
2259 wrapper_poppler_page_render_to_pixbuf (PopplerPage *page,
2260                                int src_x, int src_y,
2261                                int src_width, int src_height,
2262                                double scale,
2263                                int rotation,
2264                                GdkPixbuf *pixbuf)
2265 {
2266   cairo_t *cr;
2267   cairo_surface_t *surface;
2268
2269   surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
2270                                         src_width, src_height);
2271   cr = cairo_create (surface);
2272   cairo_save (cr);
2273   switch (rotation) {
2274   case 90:
2275           cairo_translate (cr, src_x + src_width, -src_y);
2276           break;
2277   case 180:
2278           cairo_translate (cr, src_x + src_width, src_y + src_height);
2279           break;
2280   case 270:
2281           cairo_translate (cr, -src_x, src_y + src_height);
2282           break;
2283   default:
2284           cairo_translate (cr, -src_x, -src_y);
2285   }
2286
2287   if (scale != 1.0)
2288           cairo_scale (cr, scale, scale);
2289
2290   if (rotation != 0)
2291           cairo_rotate (cr, rotation * G_PI / 180.0);
2292
2293   poppler_page_render (page, cr);
2294   cairo_restore (cr);
2295
2296   cairo_set_operator (cr, CAIRO_OPERATOR_DEST_OVER);
2297   cairo_set_source_rgb (cr, 1., 1., 1.);
2298   cairo_paint (cr);
2299
2300   cairo_destroy (cr);
2301
2302   wrapper_copy_cairo_surface_to_pixbuf (surface, pixbuf);
2303   cairo_surface_destroy (surface);
2304 }