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