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