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