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