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