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