]> git.donarmstrong.com Git - xournal.git/blob - src/xo-misc.c
focus and other patches
[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   // update the paper-style menu radio buttons
1285   
1286   if (ui.view_continuous)
1287     gtk_check_menu_item_set_active(
1288        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1289   else
1290     gtk_check_menu_item_set_active(
1291        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1292
1293   if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1294     switch (ui.cur_page->bg->color_no) {
1295       case COLOR_WHITE:
1296         gtk_check_menu_item_set_active(
1297           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1298         break;
1299       case COLOR_YELLOW:
1300         gtk_check_menu_item_set_active(
1301           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1302         break;
1303       case COLOR_RED:
1304         gtk_check_menu_item_set_active(
1305           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1306         break;
1307       case COLOR_ORANGE:
1308         gtk_check_menu_item_set_active(
1309           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1310         break;
1311       case COLOR_BLUE:
1312         gtk_check_menu_item_set_active(
1313           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1314         break;
1315       case COLOR_GREEN:
1316         gtk_check_menu_item_set_active(
1317           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1318         break;
1319       default:
1320         gtk_check_menu_item_set_active(
1321           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOther")), TRUE);
1322         break;
1323     }
1324     switch (ui.cur_page->bg->ruling) {
1325       case RULING_NONE:
1326         gtk_check_menu_item_set_active(
1327           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1328         break;
1329       case RULING_LINED:
1330         gtk_check_menu_item_set_active(
1331           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1332         break;
1333       case RULING_RULED:
1334         gtk_check_menu_item_set_active(
1335           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1336         break;
1337       case RULING_GRAPH:
1338         gtk_check_menu_item_set_active(
1339           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1340         break;
1341     }
1342   } else {
1343     gtk_check_menu_item_set_active(
1344       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1345     gtk_check_menu_item_set_active(
1346       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1347   }
1348   
1349   // enable/disable the page/layer menu items and toolbar buttons
1350
1351   gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"), 
1352      ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1353   gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1354      ui.cur_page->bg->type == BG_SOLID);
1355   
1356   gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1357   gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1358   gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1359   gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1360   gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1361   gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1362   gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1363   gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1364   
1365   gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1366   gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1367
1368   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1369   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1370 }
1371
1372 void update_toolbar_and_menu(void)
1373 {
1374   update_tool_buttons(); // takes care of other toolbar buttons as well  
1375   update_tool_menu();
1376   update_color_menu();
1377   update_pen_props_menu();
1378   update_eraser_props_menu();
1379   update_highlighter_props_menu();
1380   update_mappings_menu();
1381
1382   gtk_toggle_tool_button_set_active(
1383     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1384   gtk_check_menu_item_set_active(
1385     GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1386 }
1387
1388 void update_file_name(char *filename)
1389 {
1390   gchar tmp[100], *p;
1391   if (ui.filename != NULL) g_free(ui.filename);
1392   ui.filename = filename;
1393   if (filename == NULL) {
1394     gtk_window_set_title(GTK_WINDOW (winMain), _("Xournal"));
1395     return;
1396   }
1397   p = g_utf8_strrchr(filename, -1, '/');
1398   if (p == NULL) p = filename; 
1399   else p = g_utf8_next_char(p);
1400   g_snprintf(tmp, 100, _("Xournal - %s"), p);
1401   gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1402   new_mru_entry(filename);
1403
1404   if (filename[0]=='/') {
1405     if (ui.default_path!=NULL) g_free(ui.default_path);
1406     ui.default_path = g_path_get_dirname(filename);
1407   }
1408 }
1409
1410 void update_undo_redo_enabled(void)
1411 {
1412   gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1413   gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1414   gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1415   gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1416 }
1417
1418 void update_copy_paste_enabled(void)
1419 {
1420   gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1421   gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1422   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_item_type!=ITEM_TEXT);
1423   gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1424   gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1425   gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1426   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_item_type!=ITEM_TEXT);
1427 }
1428
1429 void update_mapping_linkings(int toolno)
1430 {
1431   int i;
1432   
1433   for (i = 1; i<=NUM_BUTTONS; i++) {
1434     if (ui.linked_brush[i] == BRUSH_LINKED) {
1435       if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1436         g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1437     }
1438     if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1439       ui.linked_brush[i] = BRUSH_STATIC;
1440       if (i==1 || i==2) update_mappings_menu_linkings();
1441     }
1442   }
1443 }
1444
1445 void set_cur_color(int color_no, guint color_rgba)
1446 {
1447   int which_mapping, tool;
1448   
1449   if (ui.toolno[ui.cur_mapping] == TOOL_HIGHLIGHTER) tool = TOOL_HIGHLIGHTER;
1450   else tool = TOOL_PEN;
1451   if (ui.cur_mapping>0 && ui.linked_brush[ui.cur_mapping]!=BRUSH_LINKED)
1452     which_mapping = ui.cur_mapping;
1453   else which_mapping = 0;
1454
1455   ui.brushes[which_mapping][tool].color_no = color_no;
1456   if (tool == TOOL_HIGHLIGHTER && (color_rgba & 0xff) == 0xff)
1457     ui.brushes[which_mapping][tool].color_rgba = color_rgba & ui.hiliter_alpha_mask;
1458   else
1459     ui.brushes[which_mapping][tool].color_rgba = color_rgba;
1460   update_mapping_linkings(tool);
1461 }
1462
1463 void recolor_temp_text(int color_no, guint color_rgba)
1464 {
1465   GdkColor gdkcolor;
1466   
1467   if (ui.cur_item_type!=ITEM_TEXT) return;
1468   if (ui.cur_item->text!=NULL && ui.cur_item->brush.color_rgba != color_rgba) {
1469     prepare_new_undo();
1470     undo->type = ITEM_TEXT_ATTRIB;
1471     undo->item = ui.cur_item;
1472     undo->str = g_strdup(ui.cur_item->font_name);
1473     undo->val_x = ui.cur_item->font_size;
1474     undo->brush = (struct Brush *)g_memdup(&(ui.cur_item->brush), sizeof(struct Brush));
1475   }
1476   ui.cur_item->brush.color_no = color_no;
1477   ui.cur_item->brush.color_rgba = color_rgba;
1478   rgb_to_gdkcolor(color_rgba, &gdkcolor);
1479   gtk_widget_modify_text(ui.cur_item->widget, GTK_STATE_NORMAL, &gdkcolor);
1480   gtk_widget_grab_focus(ui.cur_item->widget);
1481 }
1482
1483 void process_color_activate(GtkMenuItem *menuitem, int color_no, guint color_rgba)
1484 {
1485   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1486     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1487       return;
1488   } 
1489   else if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_TOOL_BUTTON) {
1490     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1491       return;
1492   }
1493
1494   if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1495   reset_focus();
1496
1497   if (ui.cur_item_type == ITEM_TEXT)
1498     recolor_temp_text(color_no, color_rgba);
1499
1500   if (ui.selection != NULL) {
1501     recolor_selection(color_no, color_rgba);
1502     update_color_buttons();
1503     update_color_menu();
1504   }
1505   
1506   if (ui.toolno[ui.cur_mapping] != TOOL_PEN && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER
1507       && ui.toolno[ui.cur_mapping] != TOOL_TEXT) {
1508     if (ui.selection != NULL) return;
1509     ui.cur_mapping = 0;
1510     end_text();
1511     ui.toolno[ui.cur_mapping] = TOOL_PEN;
1512     ui.cur_brush = &(ui.brushes[ui.cur_mapping][TOOL_PEN]);
1513     update_tool_buttons();
1514     update_tool_menu();
1515   }
1516   
1517   set_cur_color(color_no, color_rgba);
1518   update_color_buttons();
1519   update_color_menu();
1520   update_cursor();
1521 }
1522
1523 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1524 {
1525   int which_mapping;
1526   
1527   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1528     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1529       return;
1530   } else {
1531     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1532       return;
1533   }
1534
1535   if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
1536
1537   if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1538     reset_focus();
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   reset_focus();
1553   end_text();
1554   ui.brushes[which_mapping][tool].thickness_no = val;
1555   ui.brushes[which_mapping][tool].thickness = predef_thickness[tool][val];
1556   update_mapping_linkings(tool);
1557   
1558   update_thickness_buttons();
1559   if (tool == TOOL_PEN) update_pen_props_menu();
1560   if (tool == TOOL_ERASER) update_eraser_props_menu();
1561   if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1562   update_cursor();
1563 }
1564
1565 void process_papercolor_activate(GtkMenuItem *menuitem, int color)
1566 {
1567   struct Page *pg;
1568   GList *pglist;
1569   gboolean hasdone;
1570
1571   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1572     return;
1573
1574   if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages)
1575     gtk_check_menu_item_set_active(
1576       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1577
1578   pg = ui.cur_page;
1579   hasdone = FALSE;
1580   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1581     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1582     if (pg->bg->type == BG_SOLID && pg->bg->color_no != color) {
1583       prepare_new_undo();
1584       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1585       undo->multiop |= MULTIOP_CONT_REDO;
1586       hasdone = TRUE;
1587       undo->type = ITEM_NEW_BG_ONE;
1588       undo->page = pg;
1589       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1590       undo->bg->canvas_item = NULL;
1591
1592       pg->bg->color_no = color;
1593       pg->bg->color_rgba = predef_bgcolors_rgba[color];
1594       update_canvas_bg(pg);
1595     }
1596     if (!ui.bg_apply_all_pages) break;
1597   }
1598   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1599 }
1600
1601 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1602 {
1603   struct Page *pg;
1604   GList *pglist;
1605   gboolean hasdone, must_upd;
1606
1607   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1608     return;
1609
1610   if (ui.bg_apply_all_pages)
1611     gtk_check_menu_item_set_active(
1612       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1613
1614   pg = ui.cur_page;
1615   hasdone = FALSE;
1616   must_upd = FALSE;
1617   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1618     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1619     if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1620       prepare_new_undo();
1621       undo->type = ITEM_NEW_BG_ONE;
1622       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1623       undo->multiop |= MULTIOP_CONT_REDO;
1624       hasdone = TRUE;
1625       undo->page = pg;
1626       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1627       undo->bg->canvas_item = NULL;
1628
1629       if (pg->bg->type != BG_SOLID) {
1630         pg->bg->type = BG_SOLID;
1631         pg->bg->color_no = COLOR_WHITE;
1632         pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1633         pg->bg->filename = NULL;
1634         pg->bg->pixbuf = NULL;
1635         must_upd = TRUE;
1636       }
1637       pg->bg->ruling = style;
1638       update_canvas_bg(pg);
1639     }
1640     if (!ui.bg_apply_all_pages) break;
1641   }
1642   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1643   if (must_upd) update_page_stuff();
1644 }
1645
1646 gboolean ok_to_close(void)
1647 {
1648   GtkWidget *dialog;
1649   GtkResponseType response;
1650
1651   if (ui.saved) return TRUE;
1652   dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1653     GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, _("Save changes to '%s'?"),
1654     (ui.filename!=NULL) ? ui.filename:_("Untitled"));
1655   gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1656   response = gtk_dialog_run(GTK_DIALOG (dialog));
1657   gtk_widget_destroy(dialog);
1658   if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) 
1659     return FALSE; // aborted
1660   if (response == GTK_RESPONSE_YES) {
1661     on_fileSave_activate(NULL, NULL);
1662     if (!ui.saved) return FALSE; // if save failed, then we abort
1663   }
1664   return TRUE;
1665 }
1666
1667 // send the focus back to the appropriate widget
1668 void reset_focus(void)
1669 {
1670 /*
1671   if (ui.cur_item_type == ITEM_TEXT)
1672     gtk_widget_grab_focus(ui.cur_item->widget);
1673   else
1674     gtk_widget_grab_focus(GTK_WIDGET(canvas));
1675   reset_recognizer();
1676 */
1677 }
1678
1679 // selection / clipboard stuff
1680
1681 void reset_selection(void)
1682 {
1683   if (ui.selection == NULL) return;
1684   if (ui.selection->canvas_item != NULL) 
1685     gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1686   g_list_free(ui.selection->items);
1687   g_free(ui.selection);
1688   ui.selection = NULL;
1689   update_copy_paste_enabled();
1690   update_color_menu();
1691   update_thickness_buttons();
1692   update_color_buttons();
1693   update_font_button();
1694   update_cursor();
1695 }
1696
1697 void move_journal_items_by(GList *itemlist, double dx, double dy,
1698                               struct Layer *l1, struct Layer *l2, GList *depths)
1699 {
1700   struct Item *item;
1701   GnomeCanvasItem *refitem;
1702   GList *link;
1703   int i;
1704   double *pt;
1705   
1706   while (itemlist!=NULL) {
1707     item = (struct Item *)itemlist->data;
1708     if (item->type == ITEM_STROKE)
1709       for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1710         { pt[0] += dx; pt[1] += dy; }
1711     if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
1712       item->bbox.left += dx;
1713       item->bbox.right += dx;
1714       item->bbox.top += dy;
1715       item->bbox.bottom += dy;
1716     }
1717     if (l1 != l2) {
1718       // find out where to insert
1719       if (depths != NULL) {
1720         if (depths->data == NULL) link = l2->items;
1721         else {
1722           link = g_list_find(l2->items, depths->data);
1723           if (link != NULL) link = link->next;
1724         }
1725       } else link = NULL;
1726       l2->items = g_list_insert_before(l2->items, link, item);
1727       l2->nitems++;
1728       l1->items = g_list_remove(l1->items, item);
1729       l1->nitems--;
1730     }
1731     if (depths != NULL) { // also raise/lower the canvas items
1732       if (item->canvas_item!=NULL) {
1733         if (depths->data == NULL) link = NULL;
1734         else link = g_list_find(l2->items, depths->data);
1735         if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1736         else refitem = NULL;
1737         lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1738       }
1739       depths = depths->next;
1740     }
1741     itemlist = itemlist->next;
1742   }
1743 }
1744
1745 void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y,
1746                              double offset_x, double offset_y)
1747 {
1748   struct Item *item;
1749   GList *list;
1750   double mean_scaling, temp;
1751   double *pt, *wid;
1752   GnomeCanvasGroup *group;
1753   int i; 
1754   
1755   /* geometric mean of x and y scalings = rescaling for stroke widths
1756      and for text font sizes */
1757   mean_scaling = sqrt(fabs(scaling_x * scaling_y));
1758
1759   for (list = itemlist; list != NULL; list = list->next) {
1760     item = (struct Item *)list->data;
1761     if (item->type == ITEM_STROKE) {
1762       item->brush.thickness = item->brush.thickness * mean_scaling;
1763       for (i=0, pt=item->path->coords; i<item->path->num_points; i++, pt+=2) {
1764         pt[0] = pt[0]*scaling_x + offset_x;
1765         pt[1] = pt[1]*scaling_y + offset_y;
1766       }
1767       if (item->brush.variable_width)
1768         for (i=0, wid=item->widths; i<item->path->num_points-1; i++, wid++)
1769           *wid = *wid * mean_scaling;
1770
1771       item->bbox.left = item->bbox.left*scaling_x + offset_x;
1772       item->bbox.right = item->bbox.right*scaling_x + offset_x;
1773       item->bbox.top = item->bbox.top*scaling_y + offset_y;
1774       item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
1775       if (item->bbox.left > item->bbox.right) {
1776         temp = item->bbox.left;
1777         item->bbox.left = item->bbox.right;
1778         item->bbox.right = temp;
1779       }
1780       if (item->bbox.top > item->bbox.bottom) {
1781         temp = item->bbox.top;
1782         item->bbox.top = item->bbox.bottom;
1783         item->bbox.bottom = temp;
1784       }
1785     }
1786     if (item->type == ITEM_TEXT) {
1787       /* must scale about NW corner -- all other points of the text box
1788          are font- and zoom-dependent, so scaling about center of text box
1789          couldn't be undone properly. FIXME? */
1790       item->font_size *= mean_scaling;
1791       item->bbox.left = item->bbox.left*scaling_x + offset_x;
1792       item->bbox.top = item->bbox.top*scaling_y + offset_y;
1793     }
1794     // redraw the item
1795     if (item->canvas_item!=NULL) {
1796       group = (GnomeCanvasGroup *) item->canvas_item->parent;
1797       gtk_object_destroy(GTK_OBJECT(item->canvas_item));
1798       make_canvas_item_one(group, item);
1799     }
1800   }
1801 }
1802
1803 // Switch between button mappings
1804
1805 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1806    click event is being processed ... or if ui.button_switch_mapping is
1807    enabled and mappings are switched (but even then, canvas should have
1808    a pointer grab from the initial click that switched the mapping) */
1809
1810 void switch_mapping(int m)
1811 {
1812   if (ui.cur_mapping == m) return;
1813
1814   ui.cur_mapping = m;
1815   if (ui.toolno[m] < NUM_STROKE_TOOLS) 
1816     ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1817   if (ui.toolno[m] == TOOL_TEXT)
1818     ui.cur_brush = &(ui.brushes[m][TOOL_PEN]);
1819   if (m==0) ui.which_unswitch_button = 0;
1820   
1821   update_tool_buttons();
1822   update_color_menu();
1823   update_cursor();
1824 }
1825
1826 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1827 {
1828   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1829   if (ui.cur_mapping!=0 && !ui.button_switch_mapping) return;
1830   if (ui.toolno[m] == tool) return;
1831   switch_mapping(0);
1832   end_text();
1833   reset_focus();
1834     
1835   ui.toolno[m] = tool;
1836   if (ui.linked_brush[m] == BRUSH_COPIED) {
1837     ui.linked_brush[m] = BRUSH_STATIC;
1838     update_mappings_menu_linkings();
1839   }
1840 }
1841
1842 // update the ordering of components in the main vbox
1843
1844 const char *vbox_component_names[VBOX_MAIN_NITEMS]=
1845  {"scrolledwindowMain", "menubar", "toolbarMain", "toolbarPen", "hbox1"};
1846
1847 void update_vbox_order(int *order)
1848 {
1849   int i, j;
1850   GtkWidget *child;
1851   GtkBox *vboxMain = GTK_BOX(GET_COMPONENT("vboxMain"));
1852   gboolean present[VBOX_MAIN_NITEMS];
1853   
1854   for (i=0; i<VBOX_MAIN_NITEMS; i++) present[i] = FALSE;
1855   j=0;
1856   for (i=0; i<VBOX_MAIN_NITEMS; i++) {
1857     if (order[i]<0 || order[i]>=VBOX_MAIN_NITEMS) continue;
1858     present[order[i]] = TRUE;
1859     child = GET_COMPONENT(vbox_component_names[order[i]]);
1860     gtk_box_reorder_child(vboxMain, child, j++);
1861     gtk_widget_show(child);
1862   }
1863   for (i=1; i<VBOX_MAIN_NITEMS; i++) // hide others, but not the drawing area!
1864     if (!present[i]) gtk_widget_hide(GET_COMPONENT(vbox_component_names[i]));
1865 }
1866
1867 gchar *make_cur_font_name(void)
1868 {
1869   gchar *str;
1870   struct Item *it;
1871
1872   if (ui.cur_item_type == ITEM_TEXT)
1873     str = g_strdup_printf("%s %.1f", ui.cur_item->font_name, ui.cur_item->font_size);
1874   else if (ui.selection!=NULL && ui.selection->items!=NULL &&
1875            ui.selection->items->next==NULL &&
1876            (it=(struct Item*)ui.selection->items->data)->type == ITEM_TEXT)
1877     str = g_strdup_printf("%s %.1f", it->font_name, it->font_size);
1878   else
1879     str = g_strdup_printf("%s %.1f", ui.font_name, ui.font_size);
1880   return str;
1881 }
1882
1883 void update_font_button(void)
1884 {
1885   gchar *str;
1886
1887   str = make_cur_font_name();
1888   gtk_font_button_set_font_name(GTK_FONT_BUTTON(GET_COMPONENT("fontButton")), str);
1889   g_free(str);
1890 }
1891
1892 gboolean can_accel(GtkWidget *widget, guint id, gpointer data)
1893 {
1894   return GTK_WIDGET_SENSITIVE(widget);
1895 }
1896
1897 gboolean can_accel_except_text(GtkWidget *widget, guint id, gpointer data)
1898 {
1899   if (ui.cur_item_type == ITEM_TEXT) {
1900     g_signal_stop_emission_by_name(widget, "can-activate-accel");
1901     return FALSE;
1902   }
1903   return GTK_WIDGET_SENSITIVE(widget);
1904 }
1905
1906 void allow_all_accels(void)
1907 {
1908   g_signal_connect((gpointer) GET_COMPONENT("fileNew"),
1909       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1910   g_signal_connect((gpointer) GET_COMPONENT("fileOpen"),
1911       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1912   g_signal_connect((gpointer) GET_COMPONENT("fileSave"),
1913       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1914   g_signal_connect((gpointer) GET_COMPONENT("filePrint"),
1915       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1916   g_signal_connect((gpointer) GET_COMPONENT("filePrintPDF"),
1917       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1918   g_signal_connect((gpointer) GET_COMPONENT("fileQuit"),
1919       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1920   g_signal_connect((gpointer) GET_COMPONENT("editUndo"),
1921       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1922   g_signal_connect((gpointer) GET_COMPONENT("editRedo"),
1923       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1924   g_signal_connect((gpointer) GET_COMPONENT("editCut"),
1925       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1926   g_signal_connect((gpointer) GET_COMPONENT("editCopy"),
1927       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1928   g_signal_connect((gpointer) GET_COMPONENT("editPaste"),
1929       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1930   g_signal_connect((gpointer) GET_COMPONENT("editDelete"),
1931       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1932   g_signal_connect((gpointer) GET_COMPONENT("viewFullscreen"),
1933       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1934   g_signal_connect((gpointer) GET_COMPONENT("viewZoomIn"),
1935       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1936   g_signal_connect((gpointer) GET_COMPONENT("viewZoomOut"),
1937       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1938   g_signal_connect((gpointer) GET_COMPONENT("viewNormalSize"),
1939       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1940   g_signal_connect((gpointer) GET_COMPONENT("viewPageWidth"),
1941       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1942   g_signal_connect((gpointer) GET_COMPONENT("viewFirstPage"),
1943       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1944   g_signal_connect((gpointer) GET_COMPONENT("viewPreviousPage"),
1945       "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1946   g_signal_connect((gpointer) GET_COMPONENT("viewNextPage"),
1947       "can-activate-accel", G_CALLBACK(can_accel_except_text), NULL);
1948   g_signal_connect((gpointer) GET_COMPONENT("viewLastPage"),
1949       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1950   g_signal_connect((gpointer) GET_COMPONENT("toolsPen"),
1951       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1952   g_signal_connect((gpointer) GET_COMPONENT("toolsEraser"),
1953       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1954   g_signal_connect((gpointer) GET_COMPONENT("toolsHighlighter"),
1955       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1956   g_signal_connect((gpointer) GET_COMPONENT("toolsText"),
1957       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1958 /*  g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"),
1959       "can-activate-accel", G_CALLBACK(can_accel), NULL);  */
1960   g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"),
1961       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1962   g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"),
1963       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1964   g_signal_connect((gpointer) GET_COMPONENT("toolsHand"),
1965       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1966   g_signal_connect((gpointer) GET_COMPONENT("toolsTextFont"),
1967       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1968   g_signal_connect((gpointer) GET_COMPONENT("toolsRuler"),
1969       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1970   g_signal_connect((gpointer) GET_COMPONENT("toolsReco"),
1971       "can-activate-accel", G_CALLBACK(can_accel), NULL);
1972 }
1973
1974 void add_scroll_bindings(void)
1975 {
1976   GtkBindingSet *binding_set;
1977   
1978   binding_set = gtk_binding_set_by_class(
1979      G_OBJECT_GET_CLASS(GET_COMPONENT("scrolledwindowMain")));
1980   gtk_binding_entry_add_signal(binding_set, GDK_Up, 0,
1981     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
1982     G_TYPE_BOOLEAN, FALSE);  
1983   gtk_binding_entry_add_signal(binding_set, GDK_KP_Up, 0,
1984     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
1985     G_TYPE_BOOLEAN, FALSE);  
1986   gtk_binding_entry_add_signal(binding_set, GDK_Down, 0,
1987     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
1988     G_TYPE_BOOLEAN, FALSE);  
1989   gtk_binding_entry_add_signal(binding_set, GDK_KP_Down, 0,
1990     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
1991     G_TYPE_BOOLEAN, FALSE);  
1992   gtk_binding_entry_add_signal(binding_set, GDK_Left, 0,
1993     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
1994     G_TYPE_BOOLEAN, TRUE);  
1995   gtk_binding_entry_add_signal(binding_set, GDK_KP_Left, 0,
1996     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_BACKWARD, 
1997     G_TYPE_BOOLEAN, TRUE);  
1998   gtk_binding_entry_add_signal(binding_set, GDK_Right, 0,
1999     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
2000     G_TYPE_BOOLEAN, TRUE);  
2001   gtk_binding_entry_add_signal(binding_set, GDK_KP_Right, 0,
2002     "scroll_child", 2, GTK_TYPE_SCROLL_TYPE, GTK_SCROLL_STEP_FORWARD, 
2003     G_TYPE_BOOLEAN, TRUE);  
2004 }
2005
2006 gboolean is_event_within_textview(GdkEventButton *event)
2007 {
2008   double pt[2];
2009   
2010   if (ui.cur_item_type!=ITEM_TEXT) return FALSE;
2011   get_pointer_coords((GdkEvent *)event, pt);
2012   if (pt[0]<ui.cur_item->bbox.left || pt[0]>ui.cur_item->bbox.right) return FALSE;
2013   if (pt[1]<ui.cur_item->bbox.top || pt[1]>ui.cur_item->bbox.bottom) return FALSE;
2014   return TRUE;
2015 }
2016
2017 void hide_unimplemented(void)
2018 {
2019   gtk_widget_hide(GET_COMPONENT("filePrintOptions"));
2020   gtk_widget_hide(GET_COMPONENT("journalFlatten"));  
2021   gtk_widget_hide(GET_COMPONENT("papercolorOther")); 
2022   gtk_widget_hide(GET_COMPONENT("toolsSelectRegion"));
2023   gtk_widget_hide(GET_COMPONENT("buttonSelectRegion"));
2024   gtk_widget_hide(GET_COMPONENT("button2SelectRegion"));
2025   gtk_widget_hide(GET_COMPONENT("button3SelectRegion"));
2026   gtk_widget_hide(GET_COMPONENT("helpIndex")); 
2027
2028   /* config file only works with glib 2.6 and beyond */
2029   if (glib_minor_version<6) {
2030     gtk_widget_hide(GET_COMPONENT("optionsAutoSavePrefs"));
2031     gtk_widget_hide(GET_COMPONENT("optionsSavePreferences"));
2032   }
2033   /* gtkprint only works with gtk+ 2.10 and beyond */
2034   if (gtk_check_version(2, 10, 0)) {
2035     gtk_widget_hide(GET_COMPONENT("filePrint"));
2036   }  
2037 }  
2038
2039 // toggle fullscreen mode
2040 void do_fullscreen(gboolean active)
2041 {
2042   end_text();
2043   reset_focus();
2044   ui.fullscreen = active;
2045   gtk_check_menu_item_set_active(
2046     GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
2047   gtk_toggle_tool_button_set_active(
2048     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
2049
2050   if (ui.fullscreen) gtk_window_fullscreen(GTK_WINDOW(winMain));
2051   else gtk_window_unfullscreen(GTK_WINDOW(winMain));
2052   
2053   update_vbox_order(ui.vertical_order[ui.fullscreen?1:0]);
2054 }
2055
2056 /* attempt to work around GTK+ 2.16/2.17 bugs where random interface
2057    elements receive XInput events that they can't handle properly    */
2058
2059 // prevent interface items from getting bogus XInput events
2060
2061 gboolean filter_extended_events (GtkWidget *widget, GdkEvent *event,
2062                                    gpointer user_data)
2063 {
2064   if (event->type == GDK_MOTION_NOTIFY &&
2065       event->motion.device != gdk_device_get_core_pointer())
2066     return TRUE;
2067   if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_2BUTTON_PRESS ||
2068       event->type == GDK_3BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2069       event->button.device != gdk_device_get_core_pointer())
2070     return TRUE;
2071   return FALSE;
2072 }
2073
2074 /* Code to turn an extended input event into a core event and send it to
2075    a different GdkWindow -- e.g. could be used when a click in a text edit box
2076    gets sent to the canvas instead due to incorrect event translation.
2077    We now turn off xinput altogether while editing text under GTK+ 2.17, so
2078    this isn't needed any more... but could become useful again someday!
2079 */
2080
2081 /*  
2082 gboolean fix_extended_events (GtkWidget *widget, GdkEvent *event,
2083                                    gpointer user_data)
2084 {
2085   int ix, iy;
2086   GdkWindow *window;
2087
2088   if (user_data) window = (GdkWindow *)user_data;
2089   else window = widget->window;
2090
2091   if (event->type == GDK_MOTION_NOTIFY &&
2092       event->motion.device != gdk_device_get_core_pointer()) {
2093 //    printf("fixing motion\n");
2094     gdk_window_get_pointer(window, &ix, &iy, NULL);
2095     event->motion.x = ix; event->motion.y = iy;
2096     event->motion.device = gdk_device_get_core_pointer();
2097     g_object_unref(event->motion.window);
2098     event->motion.window = g_object_ref(window);
2099     gtk_widget_event(widget, event);
2100     return TRUE;
2101   }
2102   if ((event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) &&
2103       event->button.device != gdk_device_get_core_pointer()) {
2104 //    printf("fixing button from pos = %f, %f\n", event->button.x, event->button.y);
2105     gdk_window_get_pointer(window, &ix, &iy, NULL);
2106     event->button.x = ix; event->button.y = iy;
2107     event->button.device = gdk_device_get_core_pointer();
2108     g_object_unref(event->button.window);
2109     event->button.window = g_object_ref(window);
2110 //    printf("fixing button to pos = %f, %f\n", event->button.x, event->button.y);
2111     gtk_widget_event(widget, event);
2112     return TRUE;
2113   }
2114   return FALSE;
2115 }
2116 */
2117
2118 // disable xinput when layer combo box is popped up, to avoid GTK+ 2.17 crash
2119
2120 gboolean combobox_popup_disable_xinput (GtkWidget *widget, GdkEvent *event,
2121                                    gpointer user_data)
2122 {
2123   gboolean is_shown;
2124   
2125   g_object_get(G_OBJECT(widget), "popup-shown", &is_shown, NULL);
2126   gtk_widget_set_extension_events(GTK_WIDGET (canvas), 
2127        (ui.use_xinput && !is_shown)?GDK_EXTENSION_EVENTS_ALL:GDK_EXTENSION_EVENTS_NONE);
2128 }
2129
2130 /* When enter is pressed into page spinbox, send focus back to canvas. */
2131
2132 gboolean handle_activate_signal(GtkWidget *widget, gpointer user_data)
2133 {
2134   gtk_widget_grab_focus(GTK_WIDGET(canvas));
2135   return FALSE;
2136 }
2137
2138 /* recursively unset widget flags */
2139
2140 void unset_flags(GtkWidget *w, gpointer flag)
2141 {
2142   GTK_WIDGET_UNSET_FLAGS(w, (GtkWidgetFlags)flag);
2143   if(GTK_IS_CONTAINER(w))
2144     gtk_container_foreach(GTK_CONTAINER(w), unset_flags, flag);
2145 }
2146