]> git.donarmstrong.com Git - xournal.git/blob - src/xo-misc.c
more work on keyboard focus issues
[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) { // do the rescaling ourselves
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       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("papercolorOther")), 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)
1565 {
1566   struct Page *pg;
1567   GList *pglist;
1568   gboolean hasdone;
1569
1570   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1571     return;
1572
1573   if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages)
1574     gtk_check_menu_item_set_active(
1575       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1576
1577   pg = ui.cur_page;
1578   hasdone = FALSE;
1579   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1580     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1581     if (pg->bg->type == BG_SOLID && pg->bg->color_no != color) {
1582       prepare_new_undo();
1583       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1584       undo->multiop |= MULTIOP_CONT_REDO;
1585       hasdone = TRUE;
1586       undo->type = ITEM_NEW_BG_ONE;
1587       undo->page = pg;
1588       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1589       undo->bg->canvas_item = NULL;
1590
1591       pg->bg->color_no = color;
1592       pg->bg->color_rgba = predef_bgcolors_rgba[color];
1593       update_canvas_bg(pg);
1594     }
1595     if (!ui.bg_apply_all_pages) break;
1596   }
1597   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1598 }
1599
1600 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1601 {
1602   struct Page *pg;
1603   GList *pglist;
1604   gboolean hasdone, must_upd;
1605
1606   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1607     return;
1608
1609   if (ui.bg_apply_all_pages)
1610     gtk_check_menu_item_set_active(
1611       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1612
1613   pg = ui.cur_page;
1614   hasdone = FALSE;
1615   must_upd = FALSE;
1616   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1617     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1618     if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1619       prepare_new_undo();
1620       undo->type = ITEM_NEW_BG_ONE;
1621       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1622       undo->multiop |= MULTIOP_CONT_REDO;
1623       hasdone = TRUE;
1624       undo->page = pg;
1625       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1626       undo->bg->canvas_item = NULL;
1627
1628       if (pg->bg->type != BG_SOLID) {
1629         pg->bg->type = BG_SOLID;
1630         pg->bg->color_no = COLOR_WHITE;
1631         pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1632         pg->bg->filename = NULL;
1633         pg->bg->pixbuf = NULL;
1634         must_upd = TRUE;
1635       }
1636       pg->bg->ruling = style;
1637       update_canvas_bg(pg);
1638     }
1639     if (!ui.bg_apply_all_pages) break;
1640   }
1641   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1642   if (must_upd) update_page_stuff();
1643 }
1644
1645 gboolean ok_to_close(void)
1646 {
1647   GtkWidget *dialog;
1648   GtkResponseType response;
1649
1650   if (ui.saved) return TRUE;
1651   dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1652     GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Save changes to '%s'?"),
1653     (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1654   gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1655   response = gtk_dialog_run(GTK_DIALOG (dialog));
1656   gtk_widget_destroy(dialog);
1657   if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) 
1658     return FALSE; // aborted
1659   if (response == GTK_RESPONSE_YES) {
1660     on_fileSave_activate(NULL, NULL);
1661     if (!ui.saved) return FALSE; // if save failed, then we abort
1662   }
1663   return TRUE;
1664 }
1665
1666 // send the focus back to the appropriate widget
1667
1668 void reset_focus(void)
1669 {
1670   if (ui.cur_item_type == ITEM_TEXT)
1671     gtk_widget_grab_focus(ui.cur_item->widget);
1672   else
1673     gtk_widget_grab_focus(GTK_WIDGET(canvas));
1674 }
1675
1676 // selection / clipboard stuff
1677
1678 void reset_selection(void)
1679 {
1680   if (ui.selection == NULL) return;
1681   if (ui.selection->canvas_item != NULL) 
1682     gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1683   g_list_free(ui.selection->items);
1684   g_free(ui.selection);
1685   ui.selection = NULL;
1686   update_copy_paste_enabled();
1687   update_color_menu();
1688   update_thickness_buttons();
1689   update_color_buttons();
1690   update_font_button();
1691   update_cursor();
1692 }
1693
1694 void move_journal_items_by(GList *itemlist, double dx, double dy,
1695                               struct Layer *l1, struct Layer *l2, GList *depths)
1696 {
1697   struct Item *item;
1698   GnomeCanvasItem *refitem;
1699   GList *link;
1700   int i;
1701   double *pt;
1702   
1703   while (itemlist!=NULL) {
1704     item = (struct Item *)itemlist->data;
1705     if (item->type == ITEM_STROKE)
1706       for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1707         { pt[0] += dx; pt[1] += dy; }
1708     if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1709       item->bbox.left += dx;
1710       item->bbox.right += dx;
1711       item->bbox.top += dy;
1712       item->bbox.bottom += dy;
1713     }
1714     if (l1 != l2) {
1715       // find out where to insert
1716       if (depths != NULL) {
1717         if (depths->data == NULL) link = l2->items;
1718         else {
1719           link = g_list_find(l2->items, depths->data);
1720           if (link != NULL) link = link->next;
1721         }
1722       } else link = NULL;
1723       l2->items = g_list_insert_before(l2->items, link, item);
1724       l2->nitems++;
1725       l1->items = g_list_remove(l1->items, item);
1726       l1->nitems--;
1727     }
1728     if (depths != NULL) { // also raise/lower the canvas items
1729       if (item->canvas_item!=NULL) {
1730         if (depths->data == NULL) link = NULL;
1731         else link = g_list_find(l2->items, depths->data);
1732         if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1733         else refitem = NULL;
1734         lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1735       }
1736       depths = depths->next;
1737     }
1738     itemlist = itemlist->next;
1739   }
1740 }
1741
1742 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1743                              double offset_x, double offset_y)
1744 {
1745   struct Item *item;
1746   GList *list;
1747   double mean_scaling, temp;
1748   double *pt, *wid;
1749   GnomeCanvasGroup *group;
1750   int i; 
1751   
1752   /* geometric mean of x and y scalings = rescaling for stroke widths
1753      and for text font sizes */
1754   mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1755
1756   for (list = itemlist; list != NULL; list = list->next) {
1757     item = (struct Item *)list->data;
1758     if (item->type == ITEM_STROKE) {
1759       item->brush.thickness = item->brush.thickness * mean_scaling;
1760       for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1761         pt[0] = pt[0]*scaling_x + offset_x;
1762         pt[1] = pt[1]*scaling_y + offset_y;
1763       }
1764       if (item->brush.variable_width)
1765         for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1766           *wid = *wid * mean_scaling;
1767
1768       item->bbox.left = item->bbox.left*scaling_x + offset_x;
1769       item->bbox.right = item->bbox.right*scaling_x + offset_x;
1770       item->bbox.top = item->bbox.top*scaling_y + offset_y;
1771       item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1772       if (item->bbox.left > item->bbox.right) {
1773         temp = item->bbox.left;
1774         item->bbox.left = item->bbox.right;
1775         item->bbox.right = temp;
1776       }
1777       if (item->bbox.top > item->bbox.bottom) {
1778         temp = item->bbox.top;
1779         item->bbox.top = item->bbox.bottom;
1780         item->bbox.bottom = temp;
1781       }
1782     }
1783     if (item->type == ITEM_TEXT) {
1784       /* must scale about NW corner -- all other points of the text box
1785          are font- and zoom-dependent, so scaling about center of text box
1786          couldn't be undone properly. FIXME? */
1787       item->font_size *= mean_scaling;
1788       item->bbox.left = item->bbox.left*scaling_x + offset_x;
1789       item->bbox.top = item->bbox.top*scaling_y + offset_y;
1790     }
1791     // redraw the item
1792     if (item->canvas_item!=NULL) {
1793       group = (GnomeCanvasGroup *) item->canvas_item->parent;
1794       gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1795       make_canvas_item_one(group, item);
1796     }
1797   }
1798 }
1799
1800 // Switch between button mappings
1801
1802 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1803    click event is being processed ... or if ui.button_switch_mapping is
1804    enabled and mappings are switched (but even then, canvas should have
1805    a pointer grab from the initial click that switched the mapping) */
1806
1807 void switch_mapping(int m)
1808 {
1809   if (ui.cur_mapping == m) return;
1810
1811   ui.cur_mapping = m;
1812   if (ui.toolno[m] < NUM_STROKE_TOOLS) 
1813     ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1814   if (ui.toolno[m] == TOOL_TEXT)
1815     ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1816   if (m==0) ui.which_unswitch_button = 0;
1817   
1818   update_tool_buttons();
1819   update_color_menu();
1820   update_cursor();
1821 }
1822
1823 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1824 {
1825   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1826   if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1827   if (ui.toolno[m] == tool) return;
1828   switch_mapping(0);
1829   end_text();
1830     
1831   ui.toolno[m] = tool;
1832   if (ui.linked_brush[m] == BRUSH_COPIED) {
1833     ui.linked_brush[m] = BRUSH_STATIC;
1834     update_mappings_menu_linkings();
1835   }
1836 }
1837
1838 // update the ordering of components in the main vbox
1839
1840 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1841  {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1842
1843 void update_vbox_order(int *order)
1844 {
1845   int i, j;
1846   GtkWidget *child;
1847   GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1848   gboolean present[VBOX_MAIN_NITEMS];
1849   
1850   for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1851   j=0;
1852   for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1853     if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1854     present[order[i]] = TRUE;
1855     child = GET_COMPONENT(vbox_component_names[order[i]]);
1856     gtk_box_reorder_child(vboxMain, child, j++);
1857     gtk_widget_show(child);
1858   }
1859   for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1860     if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1861 }
1862
1863 gchar *make_cur_font_name(void)
1864 {
1865   gchar *str;
1866   struct Item *it;
1867
1868   if (ui.cur_item_type == ITEM_TEXT)
1869     str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1870   else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1871            ui.selection->items->next==NULL &&
1872            (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1873     str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1874   else
1875     str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1876   return str;
1877 }
1878
1879 void update_font_button(void)
1880 {
1881   gchar *str;
1882
1883   str = make_cur_font_name();
1884   gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1885   g_free(str);
1886 }
1887
1888 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1889 {
1890   return GTK_WIDGET_SENSITIVE(widget);
1891 }
1892
1893 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1894 {
1895   if (ui.cur_item_type == ITEM_TEXT) {
1896     g_signal_stop_emission_by_name(widget, "can-activate-accel");
1897     return FALSE;
1898   }
1899   return GTK_WIDGET_SENSITIVE(widget);
1900 }
1901
1902 void allow_all_accels(void)
1903 {
1904   g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1905       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1906   g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1907       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1908   g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1909       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1910   g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1911       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1912   g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1913       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1914   g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1915       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1916   g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1917       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1918   g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1919       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1920   g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1921       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1922   g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1923       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1924   g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1925       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1926   g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1927       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1928   g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1929       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1930   g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1931       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1932   g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1933       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1934   g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1935       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1936   g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1937       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1938   g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1939       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1940   g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1941       "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1942   g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1943       "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1944   g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1945       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1946   g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1947       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1948   g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1949       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1950   g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1951       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1952   g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1953       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1954 /*  g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1955       "can-activate-accel", G_CALLBACK(can_accel), NULL);  */
1956   g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1957       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1958   g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1959       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1960   g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1961       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1962   g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1963       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1964   g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1965       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1966   g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
1967       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1968 }
1969
1970 void add_scroll_bindings(void)
1971 {
1972   GtkBindingSet *binding_set;
1973   
1974   binding_set = gtk_binding_set_by_class(
1975      G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
1976   gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
1977     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
1978     G_TYPE_BOOLEAN, FALSE);  
1979   gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
1980     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
1981     G_TYPE_BOOLEAN, FALSE);  
1982   gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
1983     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
1984     G_TYPE_BOOLEAN, FALSE);  
1985   gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
1986     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
1987     G_TYPE_BOOLEAN, FALSE);  
1988   gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
1989     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
1990     G_TYPE_BOOLEAN, TRUE);  
1991   gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
1992     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
1993     G_TYPE_BOOLEAN, TRUE);  
1994   gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
1995     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
1996     G_TYPE_BOOLEAN, TRUE);  
1997   gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
1998     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
1999     G_TYPE_BOOLEAN, TRUE);  
2000 }
2001
2002 gboolean is_event_within_textview(GdkEventButton *event)
2003 {
2004   double pt[2];
2005   
2006   if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2007   get_pointer_coords((GdkEvent *)event, pt);
2008   if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2009   if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2010   return TRUE;
2011 }
2012
2013 void hide_unimplemented(void)
2014 {
2015   gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2016   gtk_widget_hide(GET_COMPONENT("journalFlatten"));  
2017   gtk_widget_hide(GET_COMPONENT("papercolorOther")); 
2018   gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2019   gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2020   gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2021   gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2022   gtk_widget_hide(GET_COMPONENT("helpIndex")); 
2023
2024   /* config file only works with glib 2.6 and beyond */
2025   if (glib_minor_version<6) {
2026     gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2027     gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2028   }
2029   /* gtkprint only works with gtk+ 2.10 and beyond */
2030   if (gtk_check_version(2, 10, 0)) {
2031     gtk_widget_hide(GET_COMPONENT("filePrint"));
2032   }  
2033 }  
2034
2035 // toggle fullscreen mode
2036 void do_fullscreen(gboolean active)
2037 {
2038   end_text();
2039   ui.fullscreen = active;
2040   gtk_check_menu_item_set_active(
2041     GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2042   gtk_toggle_tool_button_set_active(
2043     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2044
2045   if (ui.fullscreen) gtk_window_fullscreen(GTK_WINDOW(winMain));
2046   else gtk_window_unfullscreen(GTK_WINDOW(winMain));
2047   
2048   update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2049 }
2050
2051 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2052    elements receive XInput events that they can't handle properly    */
2053
2054 // prevent interface items from getting bogus XInput events
2055
2056 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2057                                    gpointer user_data)
2058 {
2059   if (event->type == GDK_MOTION_NOTIFY &&
2060       event->motion.device != gdk_device_get_core_pointer())
2061     return TRUE;
2062   if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2063       event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2064       event->button.device != gdk_device_get_core_pointer())
2065     return TRUE;
2066   return FALSE;
2067 }
2068
2069 /* Code to turn an extended input event into a core event and send it to
2070    a different GdkWindow -- e.g. could be used when a click in a text edit box
2071    gets sent to the canvas instead due to incorrect event translation.
2072    We now turn off xinput altogether while editing text under GTK+ 2.17, so
2073    this isn't needed any more... but could become useful again someday!
2074 */
2075
2076 /*  
2077 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2078                                    gpointer user_data)
2079 {
2080   int ix, iy;
2081   GdkWindow *window;
2082
2083   if (user_data) window = (GdkWindow *)user_data;
2084   else window = widget->window;
2085
2086   if (event->type == GDK_MOTION_NOTIFY &&
2087       event->motion.device != gdk_device_get_core_pointer()) {
2088 //    printf("fixing motion\n");
2089     gdk_window_get_pointer(window, &ix, &iy, NULL);
2090     event->motion.x = ix; event->motion.y = iy;
2091     event->motion.device = gdk_device_get_core_pointer();
2092     g_object_unref(event->motion.window);
2093     event->motion.window = g_object_ref(window);
2094     gtk_widget_event(widget, event);
2095     return TRUE;
2096   }
2097   if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2098       event->button.device != gdk_device_get_core_pointer()) {
2099 //    printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2100     gdk_window_get_pointer(window, &ix, &iy, NULL);
2101     event->button.x = ix; event->button.y = iy;
2102     event->button.device = gdk_device_get_core_pointer();
2103     g_object_unref(event->button.window);
2104     event->button.window = g_object_ref(window);
2105 //    printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2106     gtk_widget_event(widget, event);
2107     return TRUE;
2108   }
2109   return FALSE;
2110 }
2111 */
2112
2113 // disable xinput when layer combo box is popped up, to avoid GTK+ 2.17 crash
2114
2115 gboolean combobox_popup_disable_xinput (GtkWidget *widget, GdkEvent *event,
2116                                    gpointer user_data)
2117 {
2118   gboolean is_shown;
2119   
2120   g_object_get(G_OBJECT(widget), "popup-shown", &is_shown, NULL);
2121   gtk_widget_set_extension_events(GTK_WIDGET (canvas), 
2122        (ui.use_xinput && !is_shown)?GDK_EXTENSION_EVENTS_ALL:GDK_EXTENSION_EVENTS_NONE);
2123 }
2124
2125 /* When enter is pressed into page spinbox, send focus back to canvas. */
2126
2127 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2128 {
2129   reset_focus();
2130   return FALSE;
2131 }
2132
2133 /* recursively unset widget flags */
2134
2135 void unset_flags(GtkWidget *w, gpointer flag)
2136 {
2137   GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2138   if(GTK_IS_CONTAINER(w))
2139     gtk_container_forall(GTK_CONTAINER(w), unset_flags, flag);
2140 }
2141
2142 /* reset focus when a key or button press event reaches someone, or when the
2143    page-number spin button should relinquish control... */
2144
2145 gboolean intercept_activate_events(GtkWidget *w, GdkEvent *ev, gpointer data)
2146 {
2147   if (w == GET_COMPONENT("hbox1")) {
2148     /* the event won't be processed since the hbox1 doesn't know what to do with it,
2149        so we might as well kill it and avoid confusing ourselves when it gets
2150        propagated further ... */
2151     return TRUE;
2152   }
2153   if (w == GET_COMPONENT("spinPageNo")) {
2154     /* we let the spin button take care of itself, and don't steal its focus,
2155        unless the user presses Esc or Tab (in those cases we intervene) */
2156     if (ev->type != GDK_KEY_PRESS) return FALSE;
2157     if (ev->key.keyval == GDK_Escape) 
2158        gtk_spin_button_set_value(GTK_SPIN_BUTTON(w), ui.pageno+1); // abort
2159     else if (ev->key.keyval != GDK_Tab && ev->key.keyval != GDK_ISO_Left_Tab)
2160        return FALSE; // let the spin button process it
2161   }
2162
2163   // otherwise, we want to make sure the canvas or text item gets focus back...
2164   reset_focus();  
2165   return FALSE;
2166 }
2167
2168 void install_focus_hooks(GtkWidget *w, gpointer data)
2169 {
2170   if (w == NULL) return;
2171   g_signal_connect(w, "key-press-event", G_CALLBACK(intercept_activate_events), data);
2172   g_signal_connect(w, "button-press-event", G_CALLBACK(intercept_activate_events), data);
2173   if (GTK_IS_MENU_ITEM(w)) {
2174     g_signal_connect(w, "activate", G_CALLBACK(intercept_activate_events), data);
2175     install_focus_hooks(gtk_menu_item_get_submenu(GTK_MENU_ITEM(w)), data);
2176   }
2177   if(GTK_IS_CONTAINER(w))
2178     gtk_container_forall(GTK_CONTAINER(w), install_focus_hooks, data);
2179 }