]> git.donarmstrong.com Git - xournal.git/blob - src/xo-misc.c
Add a hand tool (+ other misc updates)
[xournal.git] / src / xo-misc.c
1 // comment out the line below if you want the thickness settings of v 0.2
2 #define NEW_THICKNESS
3
4 #ifdef HAVE_CONFIG_H
5 #  include <config.h>
6 #endif
7
8 #include <math.h>
9 #include <string.h>
10 #include <gtk/gtk.h>
11 #include <libgnomecanvas/libgnomecanvas.h>
12
13 #include "xournal.h"
14 #include "xo-interface.h"
15 #include "xo-support.h"
16 #include "xo-callbacks.h"
17 #include "xo-misc.h"
18 #include "xo-file.h"
19 #include "xo-paint.h"
20
21 // some global constants
22
23 guint predef_colors_rgba[COLOR_MAX] =
24   { 0x000000ff, 0x3333ccff, 0xff0000ff, 0x008000ff,
25     0x808080ff, 0x00c0ffff, 0x00ff00ff, 0xff00ffff,
26     0xff8000ff, 0xffff00ff, 0xffffffff };
27
28 guint predef_bgcolors_rgba[COLOR_MAX] = // meaningless ones set to white
29   { 0xffffffff, 0xa0e8ffff, 0xffc0d4ff, 0x80ffc0ff,
30     0xffffffff, 0xa0e8ffff, 0x80ffc0ff, 0xffc0d4ff,
31     0xffc080ff, 0xffff80ff, 0xffffffff };
32
33 #ifdef NEW_THICKNESS
34 double predef_thickness[NUM_STROKE_TOOLS][THICKNESS_MAX] =
35   { { 0.42, 0.85, 1.41,  2.26, 5.67 }, // pen thicknesses = 0.15, 0.3, 0.5, 0.8, 2 mm
36     { 2.83, 2.83, 8.50, 19.84, 19.84 }, // eraser thicknesses = 1, 2.5, 5 mm
37     { 2.83, 2.83, 8.50, 19.84, 19.84 }, // highlighter thicknesses = 1, 2.5, 5 mm
38   };
39 #else
40 double predef_thickness[NUM_STROKE_TOOLS][THICKNESS_MAX] =
41   { { 0.42, 0.85, 1.41,  2.26, 5.67 }, // pen thicknesses = 0.15, 0.3, 0.5, 0.8, 2 mm
42     { 2.83, 2.83, 7.08, 14.17, 14.17 }, // eraser thicknesses = 1, 2.5, 5 mm
43     { 2.83, 2.83, 7.08, 14.17, 14.17 }, // highlighter thicknesses = 1, 2.5, 5 mm
44   };
45 #endif
46
47 // some manipulation functions
48
49 struct Page *new_page(struct Page *template)
50 {
51   struct Page *pg = (struct Page *) g_memdup(template, sizeof(struct Page));
52   struct Layer *l = g_new(struct Layer, 1);
53   
54   l->items = NULL;
55   l->nitems = 0;
56   pg->layers = g_list_append(NULL, l);
57   pg->nlayers = 1;
58   pg->bg = (struct Background *)g_memdup(template->bg, sizeof(struct Background));
59   pg->bg->canvas_item = NULL;
60   if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
61     gdk_pixbuf_ref(pg->bg->pixbuf);
62     refstring_ref(pg->bg->filename);
63   }
64   pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
65       gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
66   make_page_clipbox(pg);
67   update_canvas_bg(pg);
68   l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
69       pg->group, gnome_canvas_group_get_type(), NULL);
70   
71   return pg;
72 }
73
74 /* Create a page from a background. 
75    Note: bg should be an UNREFERENCED background.
76    If needed, first duplicate it and increase the refcount of the pixbuf.
77 */
78 struct Page *new_page_with_bg(struct Background *bg, double width, double height)
79 {
80   struct Page *pg = g_new(struct Page, 1);
81   struct Layer *l = g_new(struct Layer, 1);
82   
83   l->items = NULL;
84   l->nitems = 0;
85   pg->layers = g_list_append(NULL, l);
86   pg->nlayers = 1;
87   pg->bg = bg;
88   pg->bg->canvas_item = NULL;
89   pg->height = height;
90   pg->width = width;
91   pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
92       gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
93   make_page_clipbox(pg);
94   update_canvas_bg(pg);
95   l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
96       pg->group, gnome_canvas_group_get_type(), NULL);
97   
98   return pg;
99 }
100
101 void realloc_cur_path(int n)
102 {
103   if (n <= ui.cur_path_storage_alloc) return;
104   ui.cur_path_storage_alloc = n+10;
105   ui.cur_path.coords = g_realloc(ui.cur_path.coords, 2*(n+10)*sizeof(double));
106 }
107
108 // undo utility functions
109
110 void prepare_new_undo(void)
111 {
112   struct UndoItem *u;
113   // add a new UndoItem on the stack  
114   u = (struct UndoItem *)g_malloc(sizeof(struct UndoItem));
115   u->next = undo;
116   u->multiop = 0;
117   undo = u;
118   ui.saved = FALSE;
119   clear_redo_stack();
120 }
121
122 void clear_redo_stack(void)
123 {
124   struct UndoItem *u;  
125   GList *list, *repl;
126   struct UndoErasureData *erasure;
127   struct Item *it;
128
129   /* Warning: the redo items might reference items from past redo entries,
130      which have been destroyed before them. Be careful! As a rule, it's
131      safe to destroy data which has been created at the current history step,
132      it's unsafe to refer to any data from previous history steps */
133   
134   while (redo!=NULL) {
135     if (redo->type == ITEM_STROKE) {
136       gnome_canvas_points_free(redo->item->path);
137       g_free(redo->item);
138       /* the strokes are unmapped, so there are no associated canvas items */
139     }
140     else if (redo->type == ITEM_ERASURE) {
141       for (list = redo->erasurelist; list!=NULL; list=list->next) {
142         erasure = (struct UndoErasureData *)list->data;
143         for (repl = erasure->replacement_items; repl!=NULL; repl=repl->next) {
144           it = (struct Item *)repl->data;
145           gnome_canvas_points_free(it->path);
146           g_free(it);
147         }
148         g_list_free(erasure->replacement_items);
149         g_free(erasure);
150       }
151       g_list_free(redo->erasurelist);
152     }
153     else if (redo->type == ITEM_NEW_BG_ONE || redo->type == ITEM_NEW_BG_RESIZE
154           || redo->type == ITEM_NEW_DEFAULT_BG) {
155       if (redo->bg->type == BG_PIXMAP || redo->bg->type == BG_PDF) {
156         if (redo->bg->pixbuf!=NULL) gdk_pixbuf_unref(redo->bg->pixbuf);
157         refstring_unref(redo->bg->filename);
158       }
159       g_free(redo->bg);
160     }
161     else if (redo->type == ITEM_NEW_PAGE) {
162       redo->page->group = NULL;
163       delete_page(redo->page);
164     }
165     else if (redo->type == ITEM_MOVESEL || redo->type == ITEM_REPAINTSEL) {
166       g_list_free(redo->itemlist); g_list_free(redo->auxlist);
167     }
168     else if (redo->type == ITEM_PASTE) {
169       for (list = redo->itemlist; list!=NULL; list=list->next) {
170         it = (struct Item *)list->data;
171         if (it->type == ITEM_STROKE) gnome_canvas_points_free(it->path);
172         g_free(it);
173       }
174       g_list_free(redo->itemlist);
175     }
176     else if (redo->type == ITEM_NEW_LAYER) {
177       g_free(redo->layer);
178     }
179
180     u = redo;
181     redo = redo->next;
182     g_free(u);
183   }
184   update_undo_redo_enabled();
185 }
186
187 void clear_undo_stack(void)
188 {
189   struct UndoItem *u;
190   GList *list;
191   struct UndoErasureData *erasure;
192   
193   while (undo!=NULL) {
194     // for strokes, items are already in the journal, so we don't free them
195     // for erasures, we need to free the dead items
196     if (undo->type == ITEM_ERASURE) {
197       for (list = undo->erasurelist; list!=NULL; list=list->next) {
198         erasure = (struct UndoErasureData *)list->data;
199         gnome_canvas_points_free(erasure->item->path);
200         g_free(erasure->item);
201         g_list_free(erasure->replacement_items);
202         g_free(erasure);
203       }
204       g_list_free(undo->erasurelist);
205     }
206     else if (undo->type == ITEM_NEW_BG_ONE || undo->type == ITEM_NEW_BG_RESIZE
207           || undo->type == ITEM_NEW_DEFAULT_BG) {
208       if (undo->bg->type == BG_PIXMAP || undo->bg->type == BG_PDF) {
209         if (undo->bg->pixbuf!=NULL) gdk_pixbuf_unref(undo->bg->pixbuf);
210         refstring_unref(undo->bg->filename);
211       }
212       g_free(undo->bg);
213     }
214     else if (undo->type == ITEM_MOVESEL || undo->type == ITEM_REPAINTSEL) {
215       g_list_free(undo->itemlist); g_list_free(undo->auxlist);
216     }
217     else if (undo->type == ITEM_PASTE) {
218       g_list_free(undo->itemlist);
219     }
220     else if (undo->type == ITEM_DELETE_LAYER) {
221       undo->layer->group = NULL;
222       delete_layer(undo->layer);
223     }
224     else if (undo->type == ITEM_DELETE_PAGE) {
225       undo->page->group = NULL;
226       delete_page(undo->page);
227     }
228
229     u = undo;
230     undo = undo->next;
231     g_free(u);
232   }
233   update_undo_redo_enabled();
234 }
235
236 // free data structures 
237
238 void delete_journal(struct Journal *j)
239 {
240   while (j->pages!=NULL) {
241     delete_page((struct Page *)j->pages->data);
242     j->pages = g_list_delete_link(j->pages, j->pages);
243   }
244 }
245
246 void delete_page(struct Page *pg)
247 {
248   struct Layer *l;
249   
250   while (pg->layers!=NULL) {
251     l = (struct Layer *)pg->layers->data;
252     l->group = NULL;
253     delete_layer(l);
254     pg->layers = g_list_delete_link(pg->layers, pg->layers);
255   }
256   if (pg->group!=NULL) gtk_object_destroy(GTK_OBJECT(pg->group));
257               // this also destroys the background's canvas items
258   if (pg->bg->type == BG_PIXMAP || pg->bg->type == BG_PDF) {
259     if (pg->bg->pixbuf != NULL) gdk_pixbuf_unref(pg->bg->pixbuf);
260     if (pg->bg->filename != NULL) refstring_unref(pg->bg->filename);
261   }
262   g_free(pg->bg);
263   g_free(pg);
264 }
265
266 void delete_layer(struct Layer *l)
267 {
268   struct Item *item;
269   
270   while (l->items!=NULL) {
271     item = (struct Item *)l->items->data;
272     if (item->type == ITEM_STROKE && item->path != NULL) 
273       gnome_canvas_points_free(item->path);
274     // don't need to delete the canvas_item, as it's part of the group destroyed below
275     g_free(item);
276     l->items = g_list_delete_link(l->items, l->items);
277   }
278   if (l->group!= NULL) gtk_object_destroy(GTK_OBJECT(l->group));
279   g_free(l);
280 }
281
282 // referenced strings
283
284 struct Refstring *new_refstring(const char *s)
285 {
286   struct Refstring *rs = g_new(struct Refstring, 1);
287   rs->nref = 1;
288   if (s!=NULL) rs->s = g_strdup(s);
289   else rs->s = NULL;
290   rs->aux = NULL;
291   return rs;
292 }
293
294 struct Refstring *refstring_ref(struct Refstring *rs)
295 {
296   rs->nref++;
297   return rs;
298 }
299
300 void refstring_unref(struct Refstring *rs)
301 {
302   rs->nref--;
303   if (rs->nref == 0) {
304     if (rs->s!=NULL) g_free(rs->s);
305     if (rs->aux!=NULL) g_free(rs->aux);
306     g_free(rs);
307   }
308 }
309
310
311 // some helper functions
312
313 void get_pointer_coords(GdkEvent *event, gdouble *ret)
314 {
315   double x, y;
316   gdk_event_get_coords(event, &x, &y);
317   gnome_canvas_window_to_world(canvas, x, y, ret, ret+1);
318   ret[0] -= ui.cur_page->hoffset;
319   ret[1] -= ui.cur_page->voffset;
320 }
321
322 void fix_xinput_coords(GdkEvent *event)
323 {
324   double *axes, *px, *py, axis_width;
325   GdkDevice *device;
326   int wx, wy, sx, sy;
327   
328   if (event->type == GDK_BUTTON_PRESS || event->type == GDK_BUTTON_RELEASE) {
329     axes = event->button.axes;
330     px = &(event->button.x);
331     py = &(event->button.y);
332     device = event->button.device;
333   }
334   else if (event->type == GDK_MOTION_NOTIFY) {
335     axes = event->motion.axes;
336     px = &(event->motion.x);
337     py = &(event->motion.y);
338     device = event->motion.device;
339   }
340   else return; // nothing we know how to do
341   
342   gdk_window_get_origin(event->any.window, &wx, &wy);
343   gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
344   
345   axis_width = device->axes[0].max - device->axes[0].min;
346   if (axis_width>EPSILON)
347     *px = (axes[0]/axis_width)*ui.screen_width + sx - wx;
348
349   axis_width = device->axes[1].max - device->axes[1].min;
350   if (axis_width>EPSILON)
351     *py = (axes[1]/axis_width)*ui.screen_height + sy - wy;
352 }
353
354 void update_item_bbox(struct Item *item)
355 {
356   int i;
357   double *p;
358   
359   if (item->type == ITEM_STROKE) {
360     item->bbox.left = item->bbox.right = item->path->coords[0];
361     item->bbox.top = item->bbox.bottom = item->path->coords[1];
362     for (i=1, p=item->path->coords+2; i<item->path->num_points; i++, p+=2)
363     {
364       if (p[0] < item->bbox.left) item->bbox.left = p[0];
365       if (p[0] > item->bbox.right) item->bbox.right = p[0];
366       if (p[1] < item->bbox.top) item->bbox.top = p[1];
367       if (p[1] > item->bbox.bottom) item->bbox.bottom = p[1];
368     }
369   }
370 }
371
372 void make_page_clipbox(struct Page *pg)
373 {
374   GnomeCanvasPathDef *pg_clip;
375   
376   pg_clip = gnome_canvas_path_def_new_sized(4);
377   gnome_canvas_path_def_moveto(pg_clip, 0., 0.);
378   gnome_canvas_path_def_lineto(pg_clip, 0., pg->height);
379   gnome_canvas_path_def_lineto(pg_clip, pg->width, pg->height);
380   gnome_canvas_path_def_lineto(pg_clip, pg->width, 0.);
381   gnome_canvas_path_def_closepath(pg_clip);
382   gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), "path", pg_clip, NULL);
383   gnome_canvas_path_def_unref(pg_clip);
384 }
385
386 void make_canvas_items(void)
387 {
388   struct Page *pg;
389   struct Layer *l;
390   struct Item *item;
391   GList *pagelist, *layerlist, *itemlist;
392   
393   for (pagelist = journal.pages; pagelist!=NULL; pagelist = pagelist->next) {
394     pg = (struct Page *)pagelist->data;
395     if (pg->group == NULL) {
396       pg->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
397          gnome_canvas_root(canvas), gnome_canvas_clipgroup_get_type(), NULL);
398       make_page_clipbox(pg);
399     }
400     if (pg->bg->canvas_item == NULL) update_canvas_bg(pg);
401     for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
402       l = (struct Layer *)layerlist->data;
403       if (l->group == NULL)
404         l->group = (GnomeCanvasGroup *) gnome_canvas_item_new(
405            pg->group, gnome_canvas_group_get_type(), NULL);
406       for (itemlist = l->items; itemlist!=NULL; itemlist = itemlist->next) {
407         item = (struct Item *)itemlist->data;
408         if (item->type == ITEM_STROKE && item->canvas_item == NULL) {
409           item->canvas_item = gnome_canvas_item_new(l->group,
410               gnome_canvas_line_get_type(), "points", item->path,
411               "cap-style", GDK_CAP_ROUND, "join-style", GDK_JOIN_ROUND,
412               "fill-color-rgba", item->brush.color_rgba,
413               "width-units", item->brush.thickness, NULL);
414         }
415       }
416     }
417   }
418 }
419
420 void update_canvas_bg(struct Page *pg)
421 {
422   GnomeCanvasGroup *group;
423   GnomeCanvasPoints *seg;
424   GdkPixbuf *scaled_pix;
425   double *pt;
426   double x, y;
427   int w, h;
428   
429   if (pg->bg->canvas_item != NULL)
430     gtk_object_destroy(GTK_OBJECT(pg->bg->canvas_item));
431   pg->bg->canvas_item = NULL;
432   
433   if (pg->bg->type == BG_SOLID)
434   {
435     pg->bg->canvas_item = gnome_canvas_item_new(pg->group,
436                                gnome_canvas_group_get_type(), NULL);
437     group = GNOME_CANVAS_GROUP(pg->bg->canvas_item);
438     lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
439     gnome_canvas_item_new(group, gnome_canvas_rect_get_type(),
440       "x1", 0., "x2", pg->width, "y1", 0., "y2", pg->height,
441       "fill-color-rgba", pg->bg->color_rgba, NULL);
442     if (pg->bg->ruling == RULING_NONE) return;
443     seg = gnome_canvas_points_new(2);
444     pt = seg->coords;
445     if (pg->bg->ruling == RULING_GRAPH) {
446       pt[1] = 0; pt[3] = pg->height;
447       for (x=RULING_GRAPHSPACING; x<pg->width-1; x+=RULING_GRAPHSPACING) {
448         pt[0] = pt[2] = x;
449         gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
450            "points", seg, "fill-color-rgba", RULING_COLOR,
451            "width-units", RULING_THICKNESS, NULL);
452       }      
453       pt[0] = 0; pt[2] = pg->width;
454       for (y=RULING_GRAPHSPACING; y<pg->height-1; y+=RULING_GRAPHSPACING) {
455         pt[1] = pt[3] = y;
456         gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
457            "points", seg, "fill-color-rgba", RULING_COLOR,
458            "width-units", RULING_THICKNESS, NULL);
459       }      
460       gnome_canvas_points_free(seg);
461       return;
462     }
463     pt[0] = 0; pt[2] = pg->width;
464     for (y=RULING_TOPMARGIN; y<pg->height-1; y+=RULING_SPACING) {
465       pt[1] = pt[3] = y;
466       gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
467          "points", seg, "fill-color-rgba", RULING_COLOR,
468          "width-units", RULING_THICKNESS, NULL);
469     }      
470     if (pg->bg->ruling == RULING_LINED) {
471       pt[0] = pt[2] = RULING_LEFTMARGIN;
472       pt[1] = 0; pt[3] = pg->height;
473       gnome_canvas_item_new(group, gnome_canvas_line_get_type(),
474          "points", seg, "fill-color-rgba", RULING_MARGIN_COLOR,
475          "width-units", RULING_THICKNESS, NULL);
476     }
477     gnome_canvas_points_free(seg);
478     return;
479   }
480   
481   if (pg->bg->type == BG_PIXMAP)
482   {
483     if (ui.antialias_bg) {
484       set_cursor_busy(TRUE);
485       w = (int)floor(pg->width*ui.zoom+0.5);
486       h = (int)floor(pg->height*ui.zoom+0.5);
487       if (w == gdk_pixbuf_get_width(pg->bg->pixbuf) &&
488           h == gdk_pixbuf_get_height(pg->bg->pixbuf))
489         scaled_pix = gdk_pixbuf_ref(pg->bg->pixbuf);
490       else
491         scaled_pix = gdk_pixbuf_scale_simple(pg->bg->pixbuf, w, h, GDK_INTERP_BILINEAR);
492       pg->bg->pixbuf_scale = ui.zoom;
493       set_cursor_busy(FALSE);
494     }
495     else {
496       scaled_pix = gdk_pixbuf_ref(pg->bg->pixbuf);
497       pg->bg->pixbuf_scale = 0;
498     }
499     pg->bg->canvas_item = gnome_canvas_item_new(pg->group, 
500         gnome_canvas_pixbuf_get_type(), 
501         "pixbuf", scaled_pix,
502         "width", pg->width, "height", pg->height, 
503         "width-set", TRUE, "height-set", TRUE, 
504         NULL);
505     gdk_pixbuf_unref(scaled_pix);
506     lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
507   }
508
509   if (pg->bg->type == BG_PDF)
510   {
511     if (pg->bg->pixbuf == NULL) return;
512     pg->bg->canvas_item = gnome_canvas_item_new(pg->group, 
513         gnome_canvas_pixbuf_get_type(), 
514         "pixbuf", pg->bg->pixbuf,
515         "width", pg->width, "height", pg->height, 
516         "width-set", TRUE, "height-set", TRUE, 
517         NULL);
518     lower_canvas_item_to(pg->group, pg->bg->canvas_item, NULL);
519   }
520
521 }
522
523 gboolean is_visible(struct Page *pg)
524 {
525   GtkAdjustment *v_adj;
526   double ytop, ybot;
527   
528   if (!ui.view_continuous) return (pg == ui.cur_page);
529   v_adj = gtk_layout_get_vadjustment(GTK_LAYOUT(canvas));
530   ytop = v_adj->value/ui.zoom;
531   ybot = (v_adj->value + v_adj->page_size) / ui.zoom;
532   return (MAX(ytop, pg->voffset) < MIN(ybot, pg->voffset+pg->height));
533 }
534
535 void rescale_bg_pixmaps(void)
536 {
537   GList *pglist;
538   struct Page *pg;
539   GdkPixbuf *pix;
540   
541   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
542     pg = (struct Page *)pglist->data;
543     // in progressive mode we scale only visible pages
544     if (ui.progressive_bg && !is_visible(pg)) continue;
545
546     if (pg->bg->type == BG_PIXMAP) { // do the rescaling ourselves
547       if (ui.antialias_bg) {
548         if (pg->bg->pixbuf_scale == ui.zoom) continue;
549         set_cursor_busy(TRUE);
550         pix = gdk_pixbuf_scale_simple(pg->bg->pixbuf,
551           (int)floor(pg->width*ui.zoom+0.5), (int)floor(pg->height*ui.zoom+0.5),
552           GDK_INTERP_BILINEAR);
553         gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pix, NULL);
554         gdk_pixbuf_unref(pix);
555         pg->bg->pixbuf_scale = ui.zoom;
556         set_cursor_busy(FALSE);
557       } else
558       {
559         pix = GDK_PIXBUF(g_object_get_data(G_OBJECT(pg->bg->canvas_item), "pixbuf"));
560         if (pix!=pg->bg->pixbuf)
561           gnome_canvas_item_set(pg->bg->canvas_item, "pixbuf", pg->bg->pixbuf, NULL);
562         pg->bg->pixbuf_scale = 0;
563       }
564     }
565     if (pg->bg->type == BG_PDF) { // request an asynchronous update
566       if (pg->bg->pixbuf_scale == ui.zoom) continue;
567       add_bgpdf_request(pg->bg->file_page_seq, ui.zoom, FALSE);
568       pg->bg->pixbuf_scale = ui.zoom;
569     }
570   }
571 }
572
573 gboolean have_intersect(struct BBox *a, struct BBox *b)
574 {
575   return (MAX(a->top, b->top) <= MIN(a->bottom, b->bottom)) &&
576          (MAX(a->left, b->left) <= MIN(a->right, b->right));
577 }
578
579 /* In libgnomecanvas 2.10.0, the lower/raise functions fail to update
580    correctly the end of the group's item list. We try to work around this.
581    DON'T USE gnome_canvas_item_raise/lower directly !! */
582
583 void lower_canvas_item_to(GnomeCanvasGroup *g, GnomeCanvasItem *item, GnomeCanvasItem *after)
584 {
585   int i1, i2;
586   
587   i1 = g_list_index(g->item_list, item);
588   if (i1 == -1) return;
589   
590   if (after == NULL) i2 = -1;
591   else i2 = g_list_index(g->item_list, after);
592
593   if (i1 < i2) gnome_canvas_item_raise(item, i2-i1);
594   if (i1 > i2+1) gnome_canvas_item_lower(item, i1-i2-1);
595   
596   // BUGFIX for libgnomecanvas
597   g->item_list_end = g_list_last(g->item_list);
598 }
599
600 // some interface functions
601
602 void update_thickness_buttons(void)
603 {
604   if (ui.selection!=NULL || ui.toolno[ui.cur_mapping] >= NUM_STROKE_TOOLS) {
605     gtk_toggle_tool_button_set_active(
606       GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
607   } else 
608   switch (ui.cur_brush->thickness_no) {
609     case THICKNESS_FINE:
610       gtk_toggle_tool_button_set_active(
611         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFine")), TRUE);
612       break;
613     case THICKNESS_MEDIUM:
614       gtk_toggle_tool_button_set_active(
615         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMedium")), TRUE);
616       break;
617     case THICKNESS_THICK:
618       gtk_toggle_tool_button_set_active(
619         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThick")), TRUE);
620       break;
621     default:
622       gtk_toggle_tool_button_set_active(
623         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonThicknessOther")), TRUE);
624   }
625 }
626
627 void update_color_buttons(void)
628 {
629   if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN 
630                           && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER)) {
631     gtk_toggle_tool_button_set_active(
632       GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
633   } else
634   switch (ui.cur_brush->color_no) {
635     case COLOR_BLACK:
636       gtk_toggle_tool_button_set_active(
637         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlack")), TRUE);
638       break;
639     case COLOR_BLUE:
640       gtk_toggle_tool_button_set_active(
641         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonBlue")), TRUE);
642       break;
643     case COLOR_RED:
644       gtk_toggle_tool_button_set_active(
645         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRed")), TRUE);
646       break;
647     case COLOR_GREEN:
648       gtk_toggle_tool_button_set_active(
649         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGreen")), TRUE);
650       break;
651     case COLOR_GRAY:
652       gtk_toggle_tool_button_set_active(
653         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonGray")), TRUE);
654       break;
655     case COLOR_LIGHTBLUE:
656       gtk_toggle_tool_button_set_active(
657         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightBlue")), TRUE);
658       break;
659     case COLOR_LIGHTGREEN:
660       gtk_toggle_tool_button_set_active(
661         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonLightGreen")), TRUE);
662       break;
663     case COLOR_MAGENTA:
664       gtk_toggle_tool_button_set_active(
665         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonMagenta")), TRUE);
666       break;
667     case COLOR_ORANGE:
668       gtk_toggle_tool_button_set_active(
669         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonOrange")), TRUE);
670       break;
671     case COLOR_YELLOW:
672       gtk_toggle_tool_button_set_active(
673         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonYellow")), TRUE);
674       break;
675     case COLOR_WHITE:
676       gtk_toggle_tool_button_set_active(
677         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonWhite")), TRUE);
678       break;
679     default:
680       gtk_toggle_tool_button_set_active(
681         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonColorOther")), TRUE);
682   }
683 }
684
685 void update_tool_buttons(void)
686 {
687   switch(ui.toolno[ui.cur_mapping]) {
688     case TOOL_PEN:
689       gtk_toggle_tool_button_set_active(
690         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonPen")), TRUE);
691       break;
692     case TOOL_ERASER:
693       gtk_toggle_tool_button_set_active(
694         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonEraser")), TRUE);
695       break;
696     case TOOL_HIGHLIGHTER:
697       gtk_toggle_tool_button_set_active(
698         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHighlighter")), TRUE);
699       break;
700     case TOOL_TEXT:
701       gtk_toggle_tool_button_set_active(
702         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
703       break;
704     case TOOL_SELECTREGION:
705       gtk_toggle_tool_button_set_active(
706         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
707       break;
708     case TOOL_SELECTRECT:
709       gtk_toggle_tool_button_set_active(
710         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRectangle")), TRUE);
711       break;
712     case TOOL_VERTSPACE:
713       gtk_toggle_tool_button_set_active(
714         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonVerticalSpace")), TRUE);
715       break;
716     case TOOL_HAND:
717       gtk_toggle_tool_button_set_active(
718         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonHand")), TRUE);
719       break;
720   }
721     
722   gtk_toggle_tool_button_set_active(
723     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")), ui.ruler[ui.cur_mapping]);
724   update_thickness_buttons();
725   update_color_buttons();
726 }
727
728 void update_tool_menu(void)
729 {
730   switch(ui.toolno[0]) {
731     case TOOL_PEN:
732       gtk_check_menu_item_set_active(
733         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
734       break;
735     case TOOL_ERASER:
736       gtk_check_menu_item_set_active(
737         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
738       break;
739     case TOOL_HIGHLIGHTER:
740       gtk_check_menu_item_set_active(
741         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
742       break;
743     case TOOL_TEXT:
744       gtk_check_menu_item_set_active(
745         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
746       break;
747     case TOOL_SELECTREGION:
748       gtk_check_menu_item_set_active(
749         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
750       break;
751     case TOOL_SELECTRECT:
752       gtk_check_menu_item_set_active(
753         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
754       break;
755     case TOOL_VERTSPACE:
756       gtk_check_menu_item_set_active(
757         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
758       break;
759     case TOOL_HAND:
760       gtk_check_menu_item_set_active(
761         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHand")), TRUE);
762       break;
763   }
764
765   gtk_check_menu_item_set_active(
766     GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")), ui.ruler[0]);
767 }
768
769 void update_ruler_indicator(void)
770 {
771   gtk_toggle_tool_button_set_active(
772     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")), ui.ruler[ui.cur_mapping]);
773   gtk_check_menu_item_set_active(
774     GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")), ui.ruler[0]);
775 }
776
777 void update_color_menu(void)
778 {
779   if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN 
780                           && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER)) {
781     gtk_check_menu_item_set_active(
782       GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
783   } else
784   switch (ui.cur_brush->color_no) {
785     case COLOR_BLACK:
786       gtk_check_menu_item_set_active(
787         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
788       break;
789     case COLOR_BLUE:
790       gtk_check_menu_item_set_active(
791         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
792       break;
793     case COLOR_RED:
794       gtk_check_menu_item_set_active(
795         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
796       break;
797     case COLOR_GREEN:
798       gtk_check_menu_item_set_active(
799         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
800       break;
801     case COLOR_GRAY:
802       gtk_check_menu_item_set_active(
803         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
804       break;
805     case COLOR_LIGHTBLUE:
806       gtk_check_menu_item_set_active(
807         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
808       break;
809     case COLOR_LIGHTGREEN:
810       gtk_check_menu_item_set_active(
811         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
812       break;
813     case COLOR_MAGENTA:
814       gtk_check_menu_item_set_active(
815         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
816       break;
817     case COLOR_ORANGE:
818       gtk_check_menu_item_set_active(
819         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
820       break;
821     case COLOR_YELLOW:
822       gtk_check_menu_item_set_active(
823         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
824       break;
825     case COLOR_WHITE:
826       gtk_check_menu_item_set_active(
827         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
828       break;
829     default:
830       gtk_check_menu_item_set_active(
831         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOther")), TRUE);
832   }
833 }
834
835 void update_pen_props_menu(void)
836 {
837   switch(ui.brushes[0][TOOL_PEN].thickness_no) {
838     case THICKNESS_VERYFINE:
839       gtk_check_menu_item_set_active(
840         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
841       break;
842     case THICKNESS_FINE:
843       gtk_check_menu_item_set_active(
844         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
845       break;
846     case THICKNESS_MEDIUM:
847       gtk_check_menu_item_set_active(
848         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
849       break;
850     case THICKNESS_THICK:
851       gtk_check_menu_item_set_active(
852         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
853       break;
854     case THICKNESS_VERYTHICK:
855       gtk_check_menu_item_set_active(
856         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
857       break;
858   }
859 }
860
861 void update_eraser_props_menu(void)
862 {
863   switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
864     case THICKNESS_FINE:
865       gtk_check_menu_item_set_active(
866         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
867       break;
868     case THICKNESS_MEDIUM:
869       gtk_check_menu_item_set_active(
870         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
871       break;
872     case THICKNESS_THICK:
873       gtk_check_menu_item_set_active(
874         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
875       break;
876   }
877   
878   gtk_check_menu_item_set_active(
879     GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
880     ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
881   gtk_check_menu_item_set_active(
882     GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
883     ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
884   gtk_check_menu_item_set_active(
885     GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
886     ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
887 }
888
889 void update_highlighter_props_menu(void)
890 {
891   switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
892     case THICKNESS_FINE:
893       gtk_check_menu_item_set_active(
894         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
895       break;
896     case THICKNESS_MEDIUM:
897       gtk_check_menu_item_set_active(
898         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
899       break;
900     case THICKNESS_THICK:
901       gtk_check_menu_item_set_active(
902         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
903       break;
904   }
905 }
906
907 void update_mappings_menu_linkings(void)
908 {
909   switch (ui.linked_brush[1]) {
910     case BRUSH_LINKED:
911       gtk_check_menu_item_set_active(
912         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
913       break;
914     case BRUSH_COPIED:
915       gtk_check_menu_item_set_active(
916         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
917       break;
918     case BRUSH_STATIC:
919       gtk_check_menu_item_set_active(
920         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
921       break;
922   }
923   switch (ui.linked_brush[2]) {
924     case BRUSH_LINKED:
925       gtk_check_menu_item_set_active(
926         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
927       break;
928     case BRUSH_COPIED:
929       gtk_check_menu_item_set_active(
930         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
931       break;
932     case BRUSH_STATIC:
933       gtk_check_menu_item_set_active(
934         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
935       break;
936   }
937 }
938
939 void update_mappings_menu(void)
940 {
941   gtk_check_menu_item_set_active(
942     GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
943
944   switch(ui.toolno[1]) {
945     case TOOL_PEN:
946       gtk_check_menu_item_set_active(
947         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
948       break;
949     case TOOL_ERASER:
950       gtk_check_menu_item_set_active(
951         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
952       break;
953     case TOOL_HIGHLIGHTER:
954       gtk_check_menu_item_set_active(
955         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
956       break;
957     case TOOL_TEXT:
958       gtk_check_menu_item_set_active(
959         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
960       break;
961     case TOOL_SELECTREGION:
962       gtk_check_menu_item_set_active(
963         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
964       break;
965     case TOOL_SELECTRECT:
966       gtk_check_menu_item_set_active(
967         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
968       break;
969     case TOOL_VERTSPACE:
970       gtk_check_menu_item_set_active(
971         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
972       break;
973   }
974   switch(ui.toolno[2]) {
975     case TOOL_PEN:
976       gtk_check_menu_item_set_active(
977         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
978       break;
979     case TOOL_ERASER:
980       gtk_check_menu_item_set_active(
981         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
982       break;
983     case TOOL_HIGHLIGHTER:
984       gtk_check_menu_item_set_active(
985         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
986       break;
987     case TOOL_TEXT:
988       gtk_check_menu_item_set_active(
989         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
990       break;
991     case TOOL_SELECTREGION:
992       gtk_check_menu_item_set_active(
993         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
994       break;
995     case TOOL_SELECTRECT:
996       gtk_check_menu_item_set_active(
997         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
998       break;
999     case TOOL_VERTSPACE:
1000       gtk_check_menu_item_set_active(
1001         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
1002       break;
1003   }
1004   update_mappings_menu_linkings();
1005 }
1006
1007 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
1008 {
1009   int i;
1010   struct Layer *layer;
1011   GList *list;
1012   
1013   ui.pageno = pg;
1014
1015   /* re-show all the layers of the old page */
1016   if (ui.cur_page != NULL)
1017     for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1018       layer = (struct Layer *)list->data;
1019       if (layer->group!=NULL)
1020         gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1021     }
1022   
1023   ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1024   ui.layerno = ui.cur_page->nlayers-1;
1025   ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1026   update_page_stuff();
1027   if (ui.progressive_bg) rescale_bg_pixmaps();
1028  
1029   if (rescroll) { // scroll and force a refresh
1030     gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1031       ui.cur_page->voffset*ui.zoom);
1032     if (refresh_all) 
1033       gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1034     else if (!ui.view_continuous)
1035       gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1036   }
1037 }
1038
1039 void update_page_stuff(void)
1040 {
1041   gchar tmp[10];
1042   GtkComboBox *layerbox;
1043   int i;
1044   GList *pglist;
1045   GtkSpinButton *spin;
1046   struct Page *pg;
1047   double vertpos, maxwidth;
1048   struct Layer *layer;
1049   int relscroll;
1050
1051   // move the page groups to their rightful locations or hide them
1052   if (ui.view_continuous) {
1053     vertpos = 0.; 
1054     maxwidth = 0.;
1055     for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1056       pg = (struct Page *)pglist->data;
1057       if (pg->group!=NULL) {
1058         pg->hoffset = 0.; pg->voffset = vertpos;
1059         gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), 
1060             "x", pg->hoffset, "y", pg->voffset, NULL);
1061         gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1062       }
1063       vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1064       if (pg->width > maxwidth) maxwidth = pg->width;
1065     }
1066     vertpos -= VIEW_CONTINUOUS_SKIP;
1067     gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1068   } else {
1069     for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1070       pg = (struct Page *)pglist->data;
1071       if (pg == ui.cur_page && pg->group!=NULL) {
1072         pg->hoffset = 0.; pg->voffset = 0.;
1073         gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), 
1074             "x", pg->hoffset, "y", pg->voffset, NULL);
1075         gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1076       } else {
1077         if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1078       }
1079     }
1080     gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1081   }
1082
1083   // update the page / layer info at bottom of screen
1084
1085   spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1086   ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1087   gtk_spin_button_set_range(spin, 1, journal.npages+1);
1088     /* npages+1 will be used to create a new page at end */
1089   gtk_spin_button_set_value(spin, ui.pageno+1);
1090   g_snprintf(tmp, 10, " of %d", journal.npages);
1091   gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1092
1093   layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1094   if (ui.layerbox_length == 0) {
1095     gtk_combo_box_prepend_text(layerbox, "Background");
1096     ui.layerbox_length++;
1097   }
1098   while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1099     gtk_combo_box_remove_text(layerbox, 0);
1100     ui.layerbox_length--;
1101   }
1102   while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1103     g_snprintf(tmp, 10, "Layer %d", ui.layerbox_length++);
1104     gtk_combo_box_prepend_text(layerbox, tmp);
1105   }
1106   gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1107   ui.in_update_page_stuff = FALSE;
1108   
1109   // update the paper-style menu radio buttons
1110   
1111   if (ui.view_continuous)
1112     gtk_check_menu_item_set_active(
1113        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1114   else
1115     gtk_check_menu_item_set_active(
1116        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1117
1118   if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1119     switch (ui.cur_page->bg->color_no) {
1120       case COLOR_WHITE:
1121         gtk_check_menu_item_set_active(
1122           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1123         break;
1124       case COLOR_YELLOW:
1125         gtk_check_menu_item_set_active(
1126           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1127         break;
1128       case COLOR_RED:
1129         gtk_check_menu_item_set_active(
1130           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1131         break;
1132       case COLOR_ORANGE:
1133         gtk_check_menu_item_set_active(
1134           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1135         break;
1136       case COLOR_BLUE:
1137         gtk_check_menu_item_set_active(
1138           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1139         break;
1140       case COLOR_GREEN:
1141         gtk_check_menu_item_set_active(
1142           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1143         break;
1144       default:
1145         gtk_check_menu_item_set_active(
1146           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOther")), TRUE);
1147         break;
1148     }
1149     switch (ui.cur_page->bg->ruling) {
1150       case RULING_NONE:
1151         gtk_check_menu_item_set_active(
1152           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1153         break;
1154       case RULING_LINED:
1155         gtk_check_menu_item_set_active(
1156           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1157         break;
1158       case RULING_RULED:
1159         gtk_check_menu_item_set_active(
1160           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1161         break;
1162       case RULING_GRAPH:
1163         gtk_check_menu_item_set_active(
1164           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1165         break;
1166     }
1167   } else {
1168     gtk_check_menu_item_set_active(
1169       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1170     gtk_check_menu_item_set_active(
1171       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1172   }
1173   
1174   // enable/disable the page/layer menu items and toolbar buttons
1175
1176   gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"), 
1177      ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1178   gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1179      ui.cur_page->bg->type == BG_SOLID);
1180   
1181   gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1182   gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1183   gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1184   gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1185   gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1186   gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1187   gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1188   gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1189   
1190   gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1191   gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1192
1193   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1194   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1195 }
1196
1197 void update_toolbar_and_menu(void)
1198 {
1199   update_tool_buttons(); // takes care of other toolbar buttons as well  
1200   update_tool_menu();
1201   update_color_menu();
1202   update_pen_props_menu();
1203   update_eraser_props_menu();
1204   update_highlighter_props_menu();
1205   update_mappings_menu();
1206
1207   gtk_toggle_tool_button_set_active(
1208     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1209   gtk_check_menu_item_set_active(
1210     GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1211 }
1212
1213 void update_file_name(char *filename)
1214 {
1215   gchar tmp[100], *p;
1216   if (ui.filename != NULL) g_free(ui.filename);
1217   ui.filename = filename;
1218   if (filename == NULL) {
1219     gtk_window_set_title(GTK_WINDOW (winMain), "Xournal");
1220     return;
1221   }
1222   p = g_utf8_strrchr(filename, -1, '/');
1223   if (p == NULL) p = filename; 
1224   else p = g_utf8_next_char(p);
1225   g_snprintf(tmp, 100, "Xournal - %s", p);
1226   gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1227   new_mru_entry(filename);
1228 }
1229
1230 void update_undo_redo_enabled(void)
1231 {
1232   gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1233   gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1234   gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1235   gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1236 }
1237
1238 void update_copy_paste_enabled(void)
1239 {
1240   gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1241   gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1242   gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1243   gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1244   gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1245 }
1246
1247 void update_mapping_linkings(int toolno)
1248 {
1249   int i;
1250   
1251   for (i = 1; i<=NUM_BUTTONS; i++) {
1252     if (ui.linked_brush[i] == BRUSH_LINKED) {
1253       if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1254         g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1255       ui.ruler[i] = ui.ruler[0];
1256       if (ui.toolno[i]!=TOOL_PEN && ui.toolno[i]!=TOOL_HIGHLIGHTER)
1257         ui.ruler[i] = FALSE;
1258     }
1259     if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1260       ui.linked_brush[i] = BRUSH_STATIC;
1261       if (i==1 || i==2) update_mappings_menu_linkings();
1262     }
1263   }
1264 }
1265
1266 void set_cur_color(int color)
1267 {
1268   ui.cur_brush->color_no = color;
1269   if (ui.toolno[0] == TOOL_HIGHLIGHTER)
1270     ui.cur_brush->color_rgba = predef_colors_rgba[color] & HILITER_ALPHA_MASK;
1271   else
1272     ui.cur_brush->color_rgba = predef_colors_rgba[color];
1273   update_mapping_linkings(ui.toolno[0]);
1274 }
1275
1276 void process_color_activate(GtkMenuItem *menuitem, int color)
1277 {
1278   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1279     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1280       return;
1281   } else {
1282     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1283       return;
1284   }
1285
1286   if (ui.cur_mapping != 0) return; // not user-generated
1287
1288   if (ui.selection != NULL) {
1289     recolor_selection(color);
1290     update_color_buttons();
1291     update_color_menu();
1292   }
1293   
1294   if (ui.toolno[0] != TOOL_PEN && ui.toolno[0] != TOOL_HIGHLIGHTER) {
1295     if (ui.selection != NULL) return;
1296     ui.toolno[0] = TOOL_PEN;
1297     ui.cur_brush = &(ui.brushes[0][TOOL_PEN]);
1298     update_tool_buttons();
1299     update_tool_menu();
1300   }
1301   
1302   set_cur_color(color);
1303   update_color_buttons();
1304   update_color_menu();
1305   update_cursor();
1306 }
1307
1308 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1309 {
1310   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1311     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1312       return;
1313   } else {
1314     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1315       return;
1316   }
1317
1318   if (ui.cur_mapping != 0) return; // not user-generated
1319
1320   if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1321     rethicken_selection(val);
1322     update_thickness_buttons();
1323   }
1324
1325   if (tool >= NUM_STROKE_TOOLS) {
1326     update_thickness_buttons(); // undo illegal button selection
1327     return;
1328   }
1329
1330   ui.brushes[0][tool].thickness_no = val;
1331   ui.brushes[0][tool].thickness = predef_thickness[tool][val];
1332   update_mapping_linkings(tool);
1333   
1334   update_thickness_buttons();
1335   if (tool == TOOL_PEN) update_pen_props_menu();
1336   if (tool == TOOL_ERASER) update_eraser_props_menu();
1337   if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1338   update_cursor();
1339 }
1340
1341 void process_papercolor_activate(GtkMenuItem *menuitem, int color)
1342 {
1343   struct Page *pg;
1344   GList *pglist;
1345   gboolean hasdone;
1346
1347   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1348     return;
1349
1350   if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages)
1351     gtk_check_menu_item_set_active(
1352       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1353
1354   pg = ui.cur_page;
1355   hasdone = FALSE;
1356   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1357     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1358     if (pg->bg->type == BG_SOLID && pg->bg->color_no != color) {
1359       prepare_new_undo();
1360       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1361       undo->multiop |= MULTIOP_CONT_REDO;
1362       hasdone = TRUE;
1363       undo->type = ITEM_NEW_BG_ONE;
1364       undo->page = pg;
1365       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1366       undo->bg->canvas_item = NULL;
1367
1368       pg->bg->color_no = color;
1369       pg->bg->color_rgba = predef_bgcolors_rgba[color];
1370       update_canvas_bg(pg);
1371     }
1372     if (!ui.bg_apply_all_pages) break;
1373   }
1374   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1375 }
1376
1377 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1378 {
1379   struct Page *pg;
1380   GList *pglist;
1381   gboolean hasdone, must_upd;
1382
1383   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1384     return;
1385
1386   if (ui.bg_apply_all_pages)
1387     gtk_check_menu_item_set_active(
1388       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1389
1390   pg = ui.cur_page;
1391   hasdone = FALSE;
1392   must_upd = FALSE;
1393   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1394     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1395     if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1396       prepare_new_undo();
1397       undo->type = ITEM_NEW_BG_ONE;
1398       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1399       undo->multiop |= MULTIOP_CONT_REDO;
1400       hasdone = TRUE;
1401       undo->page = pg;
1402       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1403       undo->bg->canvas_item = NULL;
1404
1405       if (pg->bg->type != BG_SOLID) {
1406         pg->bg->type = BG_SOLID;
1407         pg->bg->color_no = COLOR_WHITE;
1408         pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1409         pg->bg->filename = NULL;
1410         pg->bg->pixbuf = NULL;
1411         must_upd = TRUE;
1412       }
1413       pg->bg->ruling = style;
1414       update_canvas_bg(pg);
1415     }
1416     if (!ui.bg_apply_all_pages) break;
1417   }
1418   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1419   if (must_upd) update_page_stuff();
1420 }
1421
1422 gboolean ok_to_close(void)
1423 {
1424   GtkWidget *dialog;
1425   GtkResponseType response;
1426   GList *pagelist;
1427
1428   if (ui.saved) return TRUE;
1429   dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1430     GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "Save changes to '%s'?",
1431     (ui.filename!=NULL) ? ui.filename:"Untitled");
1432   gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1433   response = gtk_dialog_run(GTK_DIALOG (dialog));
1434   gtk_widget_destroy(dialog);
1435   if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) 
1436     return FALSE; // aborted
1437   if (response == GTK_RESPONSE_YES) {
1438     on_fileSave_activate(NULL, NULL);
1439     if (!ui.saved) return FALSE; // if save failed, then we abort
1440   }
1441   return TRUE;
1442 }
1443
1444 // test if we're still busy loading a PDF background file
1445 gboolean page_ops_forbidden(void)
1446 {
1447   return (bgpdf.status != STATUS_NOT_INIT && bgpdf.create_pages);
1448 }
1449
1450 // selection / clipboard stuff
1451
1452 void reset_selection(void)
1453 {
1454   if (ui.selection == NULL) return;
1455   if (ui.selection->canvas_item != NULL) 
1456     gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1457   g_list_free(ui.selection->items);
1458   g_free(ui.selection);
1459   ui.selection = NULL;
1460   update_copy_paste_enabled();
1461   update_color_menu();
1462   update_thickness_buttons();
1463   update_color_buttons();
1464 }
1465
1466 void move_journal_items_by(GList *itemlist, double dx, double dy,
1467                               struct Layer *l1, struct Layer *l2, GList *depths)
1468 {
1469   struct Item *item;
1470   GnomeCanvasItem *refitem;
1471   GList *link;
1472   int i;
1473   double *pt;
1474   
1475   while (itemlist!=NULL) {
1476     item = (struct Item *)itemlist->data;
1477     if (item->type == ITEM_STROKE) {
1478       for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1479         { pt[0] += dx; pt[1] += dy; }
1480       item->bbox.left += dx;
1481       item->bbox.right += dx;
1482       item->bbox.top += dy;
1483       item->bbox.bottom += dy;
1484     }
1485     if (l1 != l2) {
1486       // find out where to insert
1487       if (depths != NULL) {
1488         if (depths->data == NULL) link = l2->items;
1489         else {
1490           link = g_list_find(l2->items, depths->data);
1491           if (link != NULL) link = link->next;
1492         }
1493       } else link = NULL;
1494       l2->items = g_list_insert_before(l2->items, link, item);
1495       l2->nitems++;
1496       l1->items = g_list_remove(l1->items, item);
1497       l1->nitems--;
1498     }
1499     if (depths != NULL) { // also raise/lower the canvas items
1500       if (item->canvas_item!=NULL) {
1501         if (depths->data == NULL) link = NULL;
1502         else link = g_list_find(l2->items, depths->data);
1503         if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1504         else refitem = NULL;
1505         lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1506       }
1507       depths = depths->next;
1508     }
1509     itemlist = itemlist->next;
1510   }
1511 }
1512
1513 // Switch between button mappings
1514
1515 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1516    click event is being processed ... */
1517
1518 void switch_mapping(int m)
1519 {
1520   if (ui.cur_mapping == m) return;
1521
1522   ui.cur_mapping = m;
1523   if (ui.toolno[m] < NUM_STROKE_TOOLS) 
1524     ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1525   update_tool_buttons();
1526   update_color_menu();
1527   update_cursor();
1528 }
1529
1530 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1531 {
1532   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1533   if (ui.cur_mapping!=0) return;
1534   if (ui.toolno[m] == tool) return;
1535   ui.toolno[m] = tool;
1536   ui.ruler[m] = FALSE;
1537   if (ui.linked_brush[m] == BRUSH_LINKED 
1538        && (tool==TOOL_PEN || tool==TOOL_HIGHLIGHTER))
1539     ui.ruler[m] = ui.ruler[0];
1540   if (ui.linked_brush[m] == BRUSH_COPIED) {
1541     ui.linked_brush[m] = BRUSH_STATIC;
1542     update_mappings_menu_linkings();
1543   }
1544 }