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