]> git.donarmstrong.com Git - xournal.git/blob - src/xo-misc.c
Public release 0.2.2.
[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, 7.08, 14.17, 14.17 }, // eraser thicknesses = 1, 2.5, 5 mm
33     { 2.83, 2.83, 7.08, 14.17, 14.17 }, // highlighter thicknesses = 1, 2.5, 5 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) { // 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         pix = GDK_PIXBUF(g_object_get_data(G_OBJECT(pg->bg->canvas_item), "pixbuf"));
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   }
706     
707   gtk_toggle_tool_button_set_active(
708     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")), ui.ruler[ui.cur_mapping]);
709   update_thickness_buttons();
710   update_color_buttons();
711 }
712
713 void update_tool_menu(void)
714 {
715   switch(ui.toolno[0]) {
716     case TOOL_PEN:
717       gtk_check_menu_item_set_active(
718         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsPen")), TRUE);
719       break;
720     case TOOL_ERASER:
721       gtk_check_menu_item_set_active(
722         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsEraser")), TRUE);
723       break;
724     case TOOL_HIGHLIGHTER:
725       gtk_check_menu_item_set_active(
726         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsHighlighter")), TRUE);
727       break;
728     case TOOL_TEXT:
729       gtk_check_menu_item_set_active(
730         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
731       break;
732     case TOOL_SELECTREGION:
733       gtk_check_menu_item_set_active(
734         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
735       break;
736     case TOOL_SELECTRECT:
737       gtk_check_menu_item_set_active(
738         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRectangle")), TRUE);
739       break;
740     case TOOL_VERTSPACE:
741       gtk_check_menu_item_set_active(
742         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsVerticalSpace")), TRUE);
743       break;
744   }
745
746   gtk_check_menu_item_set_active(
747     GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")), ui.ruler[0]);
748 }
749
750 void update_ruler_indicator(void)
751 {
752   gtk_toggle_tool_button_set_active(
753     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonRuler")), ui.ruler[ui.cur_mapping]);
754   gtk_check_menu_item_set_active(
755     GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsRuler")), ui.ruler[0]);
756 }
757
758 void update_color_menu(void)
759 {
760   if (ui.selection!=NULL || (ui.toolno[ui.cur_mapping] != TOOL_PEN 
761                           && ui.toolno[ui.cur_mapping] != TOOL_HIGHLIGHTER)) {
762     gtk_check_menu_item_set_active(
763       GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorNA")), TRUE);
764   } else
765   switch (ui.cur_brush->color_no) {
766     case COLOR_BLACK:
767       gtk_check_menu_item_set_active(
768         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlack")), TRUE);
769       break;
770     case COLOR_BLUE:
771       gtk_check_menu_item_set_active(
772         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorBlue")), TRUE);
773       break;
774     case COLOR_RED:
775       gtk_check_menu_item_set_active(
776         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorRed")), TRUE);
777       break;
778     case COLOR_GREEN:
779       gtk_check_menu_item_set_active(
780         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGreen")), TRUE);
781       break;
782     case COLOR_GRAY:
783       gtk_check_menu_item_set_active(
784         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorGray")), TRUE);
785       break;
786     case COLOR_LIGHTBLUE:
787       gtk_check_menu_item_set_active(
788         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightBlue")), TRUE);
789       break;
790     case COLOR_LIGHTGREEN:
791       gtk_check_menu_item_set_active(
792         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorLightGreen")), TRUE);
793       break;
794     case COLOR_MAGENTA:
795       gtk_check_menu_item_set_active(
796         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorMagenta")), TRUE);
797       break;
798     case COLOR_ORANGE:
799       gtk_check_menu_item_set_active(
800         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOrange")), TRUE);
801       break;
802     case COLOR_YELLOW:
803       gtk_check_menu_item_set_active(
804         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorYellow")), TRUE);
805       break;
806     case COLOR_WHITE:
807       gtk_check_menu_item_set_active(
808         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorWhite")), TRUE);
809       break;
810     default:
811       gtk_check_menu_item_set_active(
812         GTK_CHECK_MENU_ITEM(GET_COMPONENT("colorOther")), TRUE);
813   }
814 }
815
816 void update_pen_props_menu(void)
817 {
818   switch(ui.brushes[0][TOOL_PEN].thickness_no) {
819     case THICKNESS_VERYFINE:
820       gtk_check_menu_item_set_active(
821         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryFine")), TRUE);
822       break;
823     case THICKNESS_FINE:
824       gtk_check_menu_item_set_active(
825         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessFine")), TRUE);
826       break;
827     case THICKNESS_MEDIUM:
828       gtk_check_menu_item_set_active(
829         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessMedium")), TRUE);
830       break;
831     case THICKNESS_THICK:
832       gtk_check_menu_item_set_active(
833         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessThick")), TRUE);
834       break;
835     case THICKNESS_VERYTHICK:
836       gtk_check_menu_item_set_active(
837         GTK_CHECK_MENU_ITEM(GET_COMPONENT("penthicknessVeryThick")), TRUE);
838       break;
839   }
840 }
841
842 void update_eraser_props_menu(void)
843 {
844   switch (ui.brushes[0][TOOL_ERASER].thickness_no) {
845     case THICKNESS_FINE:
846       gtk_check_menu_item_set_active(
847         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserFine")), TRUE);
848       break;
849     case THICKNESS_MEDIUM:
850       gtk_check_menu_item_set_active(
851         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserMedium")), TRUE);
852       break;
853     case THICKNESS_THICK:
854       gtk_check_menu_item_set_active(
855         GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserThick")), TRUE);
856       break;
857   }
858   
859   gtk_check_menu_item_set_active(
860     GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserStandard")),
861     ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STANDARD);
862   gtk_check_menu_item_set_active(
863     GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserWhiteout")),
864     ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_WHITEOUT);
865   gtk_check_menu_item_set_active(
866     GTK_CHECK_MENU_ITEM(GET_COMPONENT("eraserDeleteStrokes")),
867     ui.brushes[0][TOOL_ERASER].tool_options == TOOLOPT_ERASER_STROKES);
868 }
869
870 void update_highlighter_props_menu(void)
871 {
872   switch (ui.brushes[0][TOOL_HIGHLIGHTER].thickness_no) {
873     case THICKNESS_FINE:
874       gtk_check_menu_item_set_active(
875         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterFine")), TRUE);
876       break;
877     case THICKNESS_MEDIUM:
878       gtk_check_menu_item_set_active(
879         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterMedium")), TRUE);
880       break;
881     case THICKNESS_THICK:
882       gtk_check_menu_item_set_active(
883         GTK_CHECK_MENU_ITEM(GET_COMPONENT("highlighterThick")), TRUE);
884       break;
885   }
886 }
887
888 void update_mappings_menu_linkings(void)
889 {
890   switch (ui.linked_brush[1]) {
891     case BRUSH_LINKED:
892       gtk_check_menu_item_set_active(
893         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2LinkBrush")), TRUE);
894       break;
895     case BRUSH_COPIED:
896       gtk_check_menu_item_set_active(
897         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2CopyBrush")), TRUE);
898       break;
899     case BRUSH_STATIC:
900       gtk_check_menu_item_set_active(
901         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2NABrush")), TRUE);
902       break;
903   }
904   switch (ui.linked_brush[2]) {
905     case BRUSH_LINKED:
906       gtk_check_menu_item_set_active(
907         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3LinkBrush")), TRUE);
908       break;
909     case BRUSH_COPIED:
910       gtk_check_menu_item_set_active(
911         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3CopyBrush")), TRUE);
912       break;
913     case BRUSH_STATIC:
914       gtk_check_menu_item_set_active(
915         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3NABrush")), TRUE);
916       break;
917   }
918 }
919
920 void update_mappings_menu(void)
921 {
922   gtk_check_menu_item_set_active(
923     GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
924
925   switch(ui.toolno[1]) {
926     case TOOL_PEN:
927       gtk_check_menu_item_set_active(
928         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
929       break;
930     case TOOL_ERASER:
931       gtk_check_menu_item_set_active(
932         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
933       break;
934     case TOOL_HIGHLIGHTER:
935       gtk_check_menu_item_set_active(
936         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
937       break;
938     case TOOL_TEXT:
939       gtk_check_menu_item_set_active(
940         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
941       break;
942     case TOOL_SELECTREGION:
943       gtk_check_menu_item_set_active(
944         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
945       break;
946     case TOOL_SELECTRECT:
947       gtk_check_menu_item_set_active(
948         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
949       break;
950     case TOOL_VERTSPACE:
951       gtk_check_menu_item_set_active(
952         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
953       break;
954   }
955   switch(ui.toolno[2]) {
956     case TOOL_PEN:
957       gtk_check_menu_item_set_active(
958         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
959       break;
960     case TOOL_ERASER:
961       gtk_check_menu_item_set_active(
962         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
963       break;
964     case TOOL_HIGHLIGHTER:
965       gtk_check_menu_item_set_active(
966         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
967       break;
968     case TOOL_TEXT:
969       gtk_check_menu_item_set_active(
970         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
971       break;
972     case TOOL_SELECTREGION:
973       gtk_check_menu_item_set_active(
974         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
975       break;
976     case TOOL_SELECTRECT:
977       gtk_check_menu_item_set_active(
978         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
979       break;
980     case TOOL_VERTSPACE:
981       gtk_check_menu_item_set_active(
982         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
983       break;
984   }
985   update_mappings_menu_linkings();
986 }
987
988 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
989 {
990   int i;
991   struct Layer *layer;
992   GList *list;
993   
994   ui.pageno = pg;
995
996   /* re-show all the layers of the old page */
997   if (ui.cur_page != NULL)
998     for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
999       layer = (struct Layer *)list->data;
1000       if (layer->group!=NULL)
1001         gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1002     }
1003   
1004   ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1005   ui.layerno = ui.cur_page->nlayers-1;
1006   ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1007   update_page_stuff();
1008   if (ui.progressive_bg) rescale_bg_pixmaps();
1009  
1010   if (rescroll) { // scroll and force a refresh
1011     gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1012       ui.cur_page->voffset*ui.zoom);
1013     if (refresh_all) 
1014       gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1015     else if (!ui.view_continuous)
1016       gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1017   }
1018 }
1019
1020 void update_page_stuff(void)
1021 {
1022   gchar tmp[10];
1023   GtkComboBox *layerbox;
1024   int i;
1025   GList *pglist;
1026   GtkSpinButton *spin;
1027   struct Page *pg;
1028   double vertpos, maxwidth;
1029   struct Layer *layer;
1030   int relscroll;
1031
1032   // move the page groups to their rightful locations or hide them
1033   if (ui.view_continuous) {
1034     vertpos = 0.; 
1035     maxwidth = 0.;
1036     for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1037       pg = (struct Page *)pglist->data;
1038       if (pg->group!=NULL) {
1039         pg->hoffset = 0.; pg->voffset = vertpos;
1040         gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), 
1041             "x", pg->hoffset, "y", pg->voffset, NULL);
1042         gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1043       }
1044       vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1045       if (pg->width > maxwidth) maxwidth = pg->width;
1046     }
1047     vertpos -= VIEW_CONTINUOUS_SKIP;
1048     gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1049   } else {
1050     for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1051       pg = (struct Page *)pglist->data;
1052       if (pg == ui.cur_page && pg->group!=NULL) {
1053         pg->hoffset = 0.; pg->voffset = 0.;
1054         gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), 
1055             "x", pg->hoffset, "y", pg->voffset, NULL);
1056         gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1057       } else {
1058         if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1059       }
1060     }
1061     gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1062   }
1063
1064   // update the page / layer info at bottom of screen
1065
1066   spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1067   ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1068   gtk_spin_button_set_range(spin, 1, journal.npages+1);
1069     /* npages+1 will be used to create a new page at end */
1070   gtk_spin_button_set_value(spin, ui.pageno+1);
1071   g_snprintf(tmp, 10, " of %d", journal.npages);
1072   gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1073
1074   layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1075   if (ui.layerbox_length == 0) {
1076     gtk_combo_box_prepend_text(layerbox, "Background");
1077     ui.layerbox_length++;
1078   }
1079   while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1080     gtk_combo_box_remove_text(layerbox, 0);
1081     ui.layerbox_length--;
1082   }
1083   while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1084     g_snprintf(tmp, 10, "Layer %d", ui.layerbox_length++);
1085     gtk_combo_box_prepend_text(layerbox, tmp);
1086   }
1087   gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1088   ui.in_update_page_stuff = FALSE;
1089   
1090   // update the paper-style menu radio buttons
1091   
1092   if (ui.view_continuous)
1093     gtk_check_menu_item_set_active(
1094        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1095   else
1096     gtk_check_menu_item_set_active(
1097        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1098
1099   if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1100     switch (ui.cur_page->bg->color_no) {
1101       case COLOR_WHITE:
1102         gtk_check_menu_item_set_active(
1103           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1104         break;
1105       case COLOR_YELLOW:
1106         gtk_check_menu_item_set_active(
1107           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1108         break;
1109       case COLOR_RED:
1110         gtk_check_menu_item_set_active(
1111           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1112         break;
1113       case COLOR_ORANGE:
1114         gtk_check_menu_item_set_active(
1115           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1116         break;
1117       case COLOR_BLUE:
1118         gtk_check_menu_item_set_active(
1119           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1120         break;
1121       case COLOR_GREEN:
1122         gtk_check_menu_item_set_active(
1123           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1124         break;
1125       default:
1126         gtk_check_menu_item_set_active(
1127           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOther")), TRUE);
1128         break;
1129     }
1130     switch (ui.cur_page->bg->ruling) {
1131       case RULING_NONE:
1132         gtk_check_menu_item_set_active(
1133           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1134         break;
1135       case RULING_LINED:
1136         gtk_check_menu_item_set_active(
1137           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1138         break;
1139       case RULING_RULED:
1140         gtk_check_menu_item_set_active(
1141           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1142         break;
1143       case RULING_GRAPH:
1144         gtk_check_menu_item_set_active(
1145           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1146         break;
1147     }
1148   } else {
1149     gtk_check_menu_item_set_active(
1150       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1151     gtk_check_menu_item_set_active(
1152       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1153   }
1154   
1155   // enable/disable the page/layer menu items and toolbar buttons
1156
1157   gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"), 
1158      ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1159   gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1160      ui.cur_page->bg->type == BG_SOLID);
1161   
1162   gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1163   gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1164   gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1165   gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1166   gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1167   gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1168   gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1169   gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1170   
1171   gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1172   gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1173
1174   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1175   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1176 }
1177
1178 void update_toolbar_and_menu(void)
1179 {
1180   update_tool_buttons(); // takes care of other toolbar buttons as well  
1181   update_tool_menu();
1182   update_color_menu();
1183   update_pen_props_menu();
1184   update_eraser_props_menu();
1185   update_highlighter_props_menu();
1186   update_mappings_menu();
1187
1188   gtk_toggle_tool_button_set_active(
1189     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1190   gtk_check_menu_item_set_active(
1191     GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1192 }
1193
1194 void update_file_name(char *filename)
1195 {
1196   gchar tmp[100], *p;
1197   if (ui.filename != NULL) g_free(ui.filename);
1198   ui.filename = filename;
1199   if (filename == NULL) {
1200     gtk_window_set_title(GTK_WINDOW (winMain), "Xournal");
1201     return;
1202   }
1203   p = g_utf8_strrchr(filename, -1, '/');
1204   if (p == NULL) p = filename; 
1205   else p = g_utf8_next_char(p);
1206   g_snprintf(tmp, 100, "Xournal - %s", p);
1207   gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1208   new_mru_entry(filename);
1209 }
1210
1211 void update_undo_redo_enabled(void)
1212 {
1213   gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1214   gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1215   gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1216   gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1217 }
1218
1219 void update_copy_paste_enabled(void)
1220 {
1221   gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1222   gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1223   gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1224   gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1225   gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1226 }
1227
1228 void update_mapping_linkings(int toolno)
1229 {
1230   int i;
1231   
1232   for (i = 1; i<=NUM_BUTTONS; i++) {
1233     if (ui.linked_brush[i] == BRUSH_LINKED) {
1234       if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1235         g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1236       ui.ruler[i] = ui.ruler[0];
1237       if (ui.toolno[i]!=TOOL_PEN && ui.toolno[i]!=TOOL_HIGHLIGHTER)
1238         ui.ruler[i] = FALSE;
1239     }
1240     if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1241       ui.linked_brush[i] = BRUSH_STATIC;
1242       if (i==1 || i==2) update_mappings_menu_linkings();
1243     }
1244   }
1245 }
1246
1247 void set_cur_color(int color)
1248 {
1249   ui.cur_brush->color_no = color;
1250   if (ui.toolno[0] == TOOL_HIGHLIGHTER)
1251     ui.cur_brush->color_rgba = predef_colors_rgba[color] & HILITER_ALPHA_MASK;
1252   else
1253     ui.cur_brush->color_rgba = predef_colors_rgba[color];
1254   update_mapping_linkings(ui.toolno[0]);
1255 }
1256
1257 void process_color_activate(GtkMenuItem *menuitem, int color)
1258 {
1259   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1260     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1261       return;
1262   } else {
1263     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1264       return;
1265   }
1266
1267   if (ui.cur_mapping != 0) return; // not user-generated
1268
1269   if (ui.selection != NULL) {
1270     recolor_selection(color);
1271     update_color_buttons();
1272     update_color_menu();
1273   }
1274   
1275   if (ui.toolno[0] != TOOL_PEN && ui.toolno[0] != TOOL_HIGHLIGHTER) {
1276     if (ui.selection != NULL) return;
1277     ui.toolno[0] = TOOL_PEN;
1278     ui.cur_brush = &(ui.brushes[0][TOOL_PEN]);
1279     update_tool_buttons();
1280     update_tool_menu();
1281   }
1282   
1283   set_cur_color(color);
1284   update_color_buttons();
1285   update_color_menu();
1286   update_cursor();
1287 }
1288
1289 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1290 {
1291   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1292     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1293       return;
1294   } else {
1295     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1296       return;
1297   }
1298
1299   if (ui.cur_mapping != 0) return; // not user-generated
1300
1301   if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1302     rethicken_selection(val);
1303     update_thickness_buttons();
1304   }
1305
1306   if (tool >= NUM_STROKE_TOOLS) {
1307     update_thickness_buttons(); // undo illegal button selection
1308     return;
1309   }
1310
1311   ui.brushes[0][tool].thickness_no = val;
1312   ui.brushes[0][tool].thickness = predef_thickness[tool][val];
1313   update_mapping_linkings(tool);
1314   
1315   update_thickness_buttons();
1316   if (tool == TOOL_PEN) update_pen_props_menu();
1317   if (tool == TOOL_ERASER) update_eraser_props_menu();
1318   if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1319   update_cursor();
1320 }
1321
1322 void process_papercolor_activate(GtkMenuItem *menuitem, int color)
1323 {
1324   struct Page *pg;
1325   GList *pglist;
1326   gboolean hasdone;
1327
1328   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1329     return;
1330
1331   if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages)
1332     gtk_check_menu_item_set_active(
1333       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1334
1335   pg = ui.cur_page;
1336   hasdone = FALSE;
1337   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1338     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1339     if (pg->bg->type == BG_SOLID && pg->bg->color_no != color) {
1340       prepare_new_undo();
1341       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1342       undo->multiop |= MULTIOP_CONT_REDO;
1343       hasdone = TRUE;
1344       undo->type = ITEM_NEW_BG_ONE;
1345       undo->page = pg;
1346       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1347       undo->bg->canvas_item = NULL;
1348
1349       pg->bg->color_no = color;
1350       pg->bg->color_rgba = predef_bgcolors_rgba[color];
1351       update_canvas_bg(pg);
1352     }
1353     if (!ui.bg_apply_all_pages) break;
1354   }
1355   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1356 }
1357
1358 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1359 {
1360   struct Page *pg;
1361   GList *pglist;
1362   gboolean hasdone, must_upd;
1363
1364   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1365     return;
1366
1367   if (ui.bg_apply_all_pages)
1368     gtk_check_menu_item_set_active(
1369       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1370
1371   pg = ui.cur_page;
1372   hasdone = FALSE;
1373   must_upd = FALSE;
1374   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1375     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1376     if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1377       prepare_new_undo();
1378       undo->type = ITEM_NEW_BG_ONE;
1379       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1380       undo->multiop |= MULTIOP_CONT_REDO;
1381       hasdone = TRUE;
1382       undo->page = pg;
1383       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1384       undo->bg->canvas_item = NULL;
1385
1386       if (pg->bg->type != BG_SOLID) {
1387         pg->bg->type = BG_SOLID;
1388         pg->bg->color_no = COLOR_WHITE;
1389         pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1390         pg->bg->filename = NULL;
1391         pg->bg->pixbuf = NULL;
1392         must_upd = TRUE;
1393       }
1394       pg->bg->ruling = style;
1395       update_canvas_bg(pg);
1396     }
1397     if (!ui.bg_apply_all_pages) break;
1398   }
1399   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1400   if (must_upd) update_page_stuff();
1401 }
1402
1403 gboolean ok_to_close(void)
1404 {
1405   GtkWidget *dialog;
1406   GtkResponseType response;
1407   GList *pagelist;
1408
1409   if (ui.saved) return TRUE;
1410   dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1411     GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "Save changes to '%s'?",
1412     (ui.filename!=NULL) ? ui.filename:"Untitled");
1413   gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1414   response = gtk_dialog_run(GTK_DIALOG (dialog));
1415   gtk_widget_destroy(dialog);
1416   if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) 
1417     return FALSE; // aborted
1418   if (response == GTK_RESPONSE_YES) {
1419     on_fileSave_activate(NULL, NULL);
1420     if (!ui.saved) return FALSE; // if save failed, then we abort
1421   }
1422   return TRUE;
1423 }
1424
1425 // test if we're still busy loading a PDF background file
1426 gboolean page_ops_forbidden(void)
1427 {
1428   return (bgpdf.status != STATUS_NOT_INIT && bgpdf.create_pages);
1429 }
1430
1431 // selection / clipboard stuff
1432
1433 void reset_selection(void)
1434 {
1435   if (ui.selection == NULL) return;
1436   if (ui.selection->canvas_item != NULL) 
1437     gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1438   g_list_free(ui.selection->items);
1439   g_free(ui.selection);
1440   ui.selection = NULL;
1441   update_copy_paste_enabled();
1442   update_color_menu();
1443   update_thickness_buttons();
1444   update_color_buttons();
1445 }
1446
1447 void move_journal_items_by(GList *itemlist, double dx, double dy,
1448                               struct Layer *l1, struct Layer *l2, GList *depths)
1449 {
1450   struct Item *item;
1451   GnomeCanvasItem *refitem;
1452   GList *link;
1453   int i;
1454   double *pt;
1455   
1456   while (itemlist!=NULL) {
1457     item = (struct Item *)itemlist->data;
1458     if (item->type == ITEM_STROKE) {
1459       for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1460         { pt[0] += dx; pt[1] += dy; }
1461       item->bbox.left += dx;
1462       item->bbox.right += dx;
1463       item->bbox.top += dy;
1464       item->bbox.bottom += dy;
1465     }
1466     if (l1 != l2) {
1467       // find out where to insert
1468       if (depths != NULL) {
1469         if (depths->data == NULL) link = l2->items;
1470         else {
1471           link = g_list_find(l2->items, depths->data);
1472           if (link != NULL) link = link->next;
1473         }
1474       } else link = NULL;
1475       l2->items = g_list_insert_before(l2->items, link, item);
1476       l2->nitems++;
1477       l1->items = g_list_remove(l1->items, item);
1478       l1->nitems--;
1479     }
1480     if (depths != NULL) { // also raise/lower the canvas items
1481       if (item->canvas_item!=NULL) {
1482         if (depths->data == NULL) link = NULL;
1483         else link = g_list_find(l2->items, depths->data);
1484         if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1485         else refitem = NULL;
1486         lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1487       }
1488       depths = depths->next;
1489     }
1490     itemlist = itemlist->next;
1491   }
1492 }
1493
1494 // Switch between button mappings
1495
1496 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1497    click event is being processed ... */
1498
1499 void switch_mapping(int m)
1500 {
1501   if (ui.cur_mapping == m) return;
1502
1503   ui.cur_mapping = m;
1504   if (ui.toolno[m] < NUM_STROKE_TOOLS) 
1505     ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1506   update_tool_buttons();
1507   update_color_menu();
1508   update_cursor();
1509 }
1510
1511 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1512 {
1513   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1514   if (ui.cur_mapping!=0) return;
1515   if (ui.toolno[m] == tool) return;
1516   ui.toolno[m] = tool;
1517   ui.ruler[m] = FALSE;
1518   if (ui.linked_brush[m] == BRUSH_LINKED 
1519        && (tool==TOOL_PEN || tool==TOOL_HIGHLIGHTER))
1520     ui.ruler[m] = ui.ruler[0];
1521   if (ui.linked_brush[m] == BRUSH_COPIED) {
1522     ui.linked_brush[m] = BRUSH_STATIC;
1523     update_mappings_menu_linkings();
1524   }
1525 }