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