]> git.donarmstrong.com Git - xournal.git/blob - src/xo-misc.c
Release 0.3.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, 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) { // 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_check_menu_item_set_active(
931     GTK_CHECK_MENU_ITEM(GET_COMPONENT("optionsButtonMappings")), ui.use_erasertip);
932
933   switch(ui.toolno[1]) {
934     case TOOL_PEN:
935       gtk_check_menu_item_set_active(
936         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Pen")), TRUE);
937       break;
938     case TOOL_ERASER:
939       gtk_check_menu_item_set_active(
940         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Eraser")), TRUE);
941       break;
942     case TOOL_HIGHLIGHTER:
943       gtk_check_menu_item_set_active(
944         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Highlighter")), TRUE);
945       break;
946     case TOOL_TEXT:
947       gtk_check_menu_item_set_active(
948         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
949       break;
950     case TOOL_SELECTREGION:
951       gtk_check_menu_item_set_active(
952         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
953       break;
954     case TOOL_SELECTRECT:
955       gtk_check_menu_item_set_active(
956         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRectangle")), TRUE);
957       break;
958     case TOOL_VERTSPACE:
959       gtk_check_menu_item_set_active(
960         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2VerticalSpace")), TRUE);
961       break;
962   }
963   switch(ui.toolno[2]) {
964     case TOOL_PEN:
965       gtk_check_menu_item_set_active(
966         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Pen")), TRUE);
967       break;
968     case TOOL_ERASER:
969       gtk_check_menu_item_set_active(
970         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Eraser")), TRUE);
971       break;
972     case TOOL_HIGHLIGHTER:
973       gtk_check_menu_item_set_active(
974         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Highlighter")), TRUE);
975       break;
976     case TOOL_TEXT:
977       gtk_check_menu_item_set_active(
978         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
979       break;
980     case TOOL_SELECTREGION:
981       gtk_check_menu_item_set_active(
982         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
983       break;
984     case TOOL_SELECTRECT:
985       gtk_check_menu_item_set_active(
986         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRectangle")), TRUE);
987       break;
988     case TOOL_VERTSPACE:
989       gtk_check_menu_item_set_active(
990         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3VerticalSpace")), TRUE);
991       break;
992   }
993   update_mappings_menu_linkings();
994 }
995
996 void do_switch_page(int pg, gboolean rescroll, gboolean refresh_all)
997 {
998   int i;
999   struct Layer *layer;
1000   GList *list;
1001   
1002   ui.pageno = pg;
1003
1004   /* re-show all the layers of the old page */
1005   if (ui.cur_page != NULL)
1006     for (i=0, list = ui.cur_page->layers; list!=NULL; i++, list = list->next) {
1007       layer = (struct Layer *)list->data;
1008       if (layer->group!=NULL)
1009         gnome_canvas_item_show(GNOME_CANVAS_ITEM(layer->group));
1010     }
1011   
1012   ui.cur_page = g_list_nth_data(journal.pages, ui.pageno);
1013   ui.layerno = ui.cur_page->nlayers-1;
1014   ui.cur_layer = (struct Layer *)(g_list_last(ui.cur_page->layers)->data);
1015   update_page_stuff();
1016   if (ui.progressive_bg) rescale_bg_pixmaps();
1017  
1018   if (rescroll) { // scroll and force a refresh
1019     gtk_adjustment_set_value(gtk_layout_get_vadjustment(GTK_LAYOUT(canvas)),
1020       ui.cur_page->voffset*ui.zoom);
1021     if (refresh_all) 
1022       gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
1023     else if (!ui.view_continuous)
1024       gnome_canvas_item_move(GNOME_CANVAS_ITEM(ui.cur_page->group), 0., 0.);
1025   }
1026 }
1027
1028 void update_page_stuff(void)
1029 {
1030   gchar tmp[10];
1031   GtkComboBox *layerbox;
1032   int i;
1033   GList *pglist;
1034   GtkSpinButton *spin;
1035   struct Page *pg;
1036   double vertpos, maxwidth;
1037   struct Layer *layer;
1038   int relscroll;
1039
1040   // move the page groups to their rightful locations or hide them
1041   if (ui.view_continuous) {
1042     vertpos = 0.; 
1043     maxwidth = 0.;
1044     for (i=0, pglist = journal.pages; pglist!=NULL; i++, pglist = pglist->next) {
1045       pg = (struct Page *)pglist->data;
1046       if (pg->group!=NULL) {
1047         pg->hoffset = 0.; pg->voffset = vertpos;
1048         gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), 
1049             "x", pg->hoffset, "y", pg->voffset, NULL);
1050         gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1051       }
1052       vertpos += pg->height + VIEW_CONTINUOUS_SKIP;
1053       if (pg->width > maxwidth) maxwidth = pg->width;
1054     }
1055     vertpos -= VIEW_CONTINUOUS_SKIP;
1056     gnome_canvas_set_scroll_region(canvas, 0, 0, maxwidth, vertpos);
1057   } else {
1058     for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1059       pg = (struct Page *)pglist->data;
1060       if (pg == ui.cur_page && pg->group!=NULL) {
1061         pg->hoffset = 0.; pg->voffset = 0.;
1062         gnome_canvas_item_set(GNOME_CANVAS_ITEM(pg->group), 
1063             "x", pg->hoffset, "y", pg->voffset, NULL);
1064         gnome_canvas_item_show(GNOME_CANVAS_ITEM(pg->group));
1065       } else {
1066         if (pg->group!=NULL) gnome_canvas_item_hide(GNOME_CANVAS_ITEM(pg->group));
1067       }
1068     }
1069     gnome_canvas_set_scroll_region(canvas, 0, 0, ui.cur_page->width, ui.cur_page->height);
1070   }
1071
1072   // update the page / layer info at bottom of screen
1073
1074   spin = GTK_SPIN_BUTTON(GET_COMPONENT("spinPageNo"));
1075   ui.in_update_page_stuff = TRUE; // avoid a bad retroaction
1076   gtk_spin_button_set_range(spin, 1, journal.npages+1);
1077     /* npages+1 will be used to create a new page at end */
1078   gtk_spin_button_set_value(spin, ui.pageno+1);
1079   g_snprintf(tmp, 10, " of %d", journal.npages);
1080   gtk_label_set_text(GTK_LABEL(GET_COMPONENT("labelNumpages")), tmp);
1081
1082   layerbox = GTK_COMBO_BOX(GET_COMPONENT("comboLayer"));
1083   if (ui.layerbox_length == 0) {
1084     gtk_combo_box_prepend_text(layerbox, "Background");
1085     ui.layerbox_length++;
1086   }
1087   while (ui.layerbox_length > ui.cur_page->nlayers+1) {
1088     gtk_combo_box_remove_text(layerbox, 0);
1089     ui.layerbox_length--;
1090   }
1091   while (ui.layerbox_length < ui.cur_page->nlayers+1) {
1092     g_snprintf(tmp, 10, "Layer %d", ui.layerbox_length++);
1093     gtk_combo_box_prepend_text(layerbox, tmp);
1094   }
1095   gtk_combo_box_set_active(layerbox, ui.cur_page->nlayers-1-ui.layerno);
1096   ui.in_update_page_stuff = FALSE;
1097   
1098   // update the paper-style menu radio buttons
1099   
1100   if (ui.view_continuous)
1101     gtk_check_menu_item_set_active(
1102        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewContinuous")), TRUE);
1103   else
1104     gtk_check_menu_item_set_active(
1105        GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewOnePage")), TRUE);
1106
1107   if (ui.cur_page->bg->type == BG_SOLID && !ui.bg_apply_all_pages) {
1108     switch (ui.cur_page->bg->color_no) {
1109       case COLOR_WHITE:
1110         gtk_check_menu_item_set_active(
1111           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorWhite")), TRUE);
1112         break;
1113       case COLOR_YELLOW:
1114         gtk_check_menu_item_set_active(
1115           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorYellow")), TRUE);
1116         break;
1117       case COLOR_RED:
1118         gtk_check_menu_item_set_active(
1119           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorPink")), TRUE);
1120         break;
1121       case COLOR_ORANGE:
1122         gtk_check_menu_item_set_active(
1123           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOrange")), TRUE);
1124         break;
1125       case COLOR_BLUE:
1126         gtk_check_menu_item_set_active(
1127           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorBlue")), TRUE);
1128         break;
1129       case COLOR_GREEN:
1130         gtk_check_menu_item_set_active(
1131           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorGreen")), TRUE);
1132         break;
1133       default:
1134         gtk_check_menu_item_set_active(
1135           GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorOther")), TRUE);
1136         break;
1137     }
1138     switch (ui.cur_page->bg->ruling) {
1139       case RULING_NONE:
1140         gtk_check_menu_item_set_active(
1141           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstylePlain")), TRUE);
1142         break;
1143       case RULING_LINED:
1144         gtk_check_menu_item_set_active(
1145           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleLined")), TRUE);
1146         break;
1147       case RULING_RULED:
1148         gtk_check_menu_item_set_active(
1149           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleRuled")), TRUE);
1150         break;
1151       case RULING_GRAPH:
1152         gtk_check_menu_item_set_active(
1153           GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleGraph")), TRUE);
1154         break;
1155     }
1156   } else {
1157     gtk_check_menu_item_set_active(
1158       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1159     gtk_check_menu_item_set_active(
1160       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1161   }
1162   
1163   // enable/disable the page/layer menu items and toolbar buttons
1164
1165   gtk_widget_set_sensitive(GET_COMPONENT("journalPaperColor"), 
1166      ui.cur_page->bg->type == BG_SOLID || ui.bg_apply_all_pages);
1167   gtk_widget_set_sensitive(GET_COMPONENT("journalSetAsDefault"),
1168      ui.cur_page->bg->type == BG_SOLID);
1169   
1170   gtk_widget_set_sensitive(GET_COMPONENT("viewFirstPage"), ui.pageno!=0);
1171   gtk_widget_set_sensitive(GET_COMPONENT("viewPreviousPage"), ui.pageno!=0);
1172   gtk_widget_set_sensitive(GET_COMPONENT("viewNextPage"), TRUE);
1173   gtk_widget_set_sensitive(GET_COMPONENT("viewLastPage"), ui.pageno!=journal.npages-1);
1174   gtk_widget_set_sensitive(GET_COMPONENT("buttonFirstPage"), ui.pageno!=0);
1175   gtk_widget_set_sensitive(GET_COMPONENT("buttonPreviousPage"), ui.pageno!=0);
1176   gtk_widget_set_sensitive(GET_COMPONENT("buttonNextPage"), TRUE);
1177   gtk_widget_set_sensitive(GET_COMPONENT("buttonLastPage"), ui.pageno!=journal.npages-1);
1178   
1179   gtk_widget_set_sensitive(GET_COMPONENT("viewShowLayer"), ui.layerno!=ui.cur_page->nlayers-1);
1180   gtk_widget_set_sensitive(GET_COMPONENT("viewHideLayer"), ui.layerno>=0);
1181
1182   gtk_widget_set_sensitive(GET_COMPONENT("editPaste"), ui.cur_layer!=NULL);
1183   gtk_widget_set_sensitive(GET_COMPONENT("buttonPaste"), ui.cur_layer!=NULL);
1184 }
1185
1186 void update_toolbar_and_menu(void)
1187 {
1188   update_tool_buttons(); // takes care of other toolbar buttons as well  
1189   update_tool_menu();
1190   update_color_menu();
1191   update_pen_props_menu();
1192   update_eraser_props_menu();
1193   update_highlighter_props_menu();
1194   update_mappings_menu();
1195
1196   gtk_toggle_tool_button_set_active(
1197     GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonFullscreen")), ui.fullscreen);
1198   gtk_check_menu_item_set_active(
1199     GTK_CHECK_MENU_ITEM(GET_COMPONENT("viewFullscreen")), ui.fullscreen);
1200 }
1201
1202 void update_file_name(char *filename)
1203 {
1204   gchar tmp[100], *p;
1205   if (ui.filename != NULL) g_free(ui.filename);
1206   ui.filename = filename;
1207   if (filename == NULL) {
1208     gtk_window_set_title(GTK_WINDOW (winMain), "Xournal");
1209     return;
1210   }
1211   p = g_utf8_strrchr(filename, -1, '/');
1212   if (p == NULL) p = filename; 
1213   else p = g_utf8_next_char(p);
1214   g_snprintf(tmp, 100, "Xournal - %s", p);
1215   gtk_window_set_title(GTK_WINDOW (winMain), tmp);
1216   new_mru_entry(filename);
1217 }
1218
1219 void update_undo_redo_enabled(void)
1220 {
1221   gtk_widget_set_sensitive(GET_COMPONENT("editUndo"), undo!=NULL);
1222   gtk_widget_set_sensitive(GET_COMPONENT("editRedo"), redo!=NULL);
1223   gtk_widget_set_sensitive(GET_COMPONENT("buttonUndo"), undo!=NULL);
1224   gtk_widget_set_sensitive(GET_COMPONENT("buttonRedo"), redo!=NULL);
1225 }
1226
1227 void update_copy_paste_enabled(void)
1228 {
1229   gtk_widget_set_sensitive(GET_COMPONENT("editCut"), ui.selection!=NULL);
1230   gtk_widget_set_sensitive(GET_COMPONENT("editCopy"), ui.selection!=NULL);
1231   gtk_widget_set_sensitive(GET_COMPONENT("editDelete"), ui.selection!=NULL);
1232   gtk_widget_set_sensitive(GET_COMPONENT("buttonCut"), ui.selection!=NULL);
1233   gtk_widget_set_sensitive(GET_COMPONENT("buttonCopy"), ui.selection!=NULL);
1234 }
1235
1236 void update_mapping_linkings(int toolno)
1237 {
1238   int i;
1239   
1240   for (i = 1; i<=NUM_BUTTONS; i++) {
1241     if (ui.linked_brush[i] == BRUSH_LINKED) {
1242       if (toolno >= 0 && toolno < NUM_STROKE_TOOLS)
1243         g_memmove(&(ui.brushes[i][toolno]), &(ui.brushes[0][toolno]), sizeof(struct Brush));
1244       ui.ruler[i] = ui.ruler[0];
1245       if (ui.toolno[i]!=TOOL_PEN && ui.toolno[i]!=TOOL_HIGHLIGHTER)
1246         ui.ruler[i] = FALSE;
1247     }
1248     if (ui.linked_brush[i] == BRUSH_COPIED && toolno == ui.toolno[i]) {
1249       ui.linked_brush[i] = BRUSH_STATIC;
1250       if (i==1 || i==2) update_mappings_menu_linkings();
1251     }
1252   }
1253 }
1254
1255 void set_cur_color(int color)
1256 {
1257   ui.cur_brush->color_no = color;
1258   if (ui.toolno[0] == TOOL_HIGHLIGHTER)
1259     ui.cur_brush->color_rgba = predef_colors_rgba[color] & HILITER_ALPHA_MASK;
1260   else
1261     ui.cur_brush->color_rgba = predef_colors_rgba[color];
1262   update_mapping_linkings(ui.toolno[0]);
1263 }
1264
1265 void process_color_activate(GtkMenuItem *menuitem, int color)
1266 {
1267   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1268     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1269       return;
1270   } else {
1271     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1272       return;
1273   }
1274
1275   if (ui.cur_mapping != 0) return; // not user-generated
1276
1277   if (ui.selection != NULL) {
1278     recolor_selection(color);
1279     update_color_buttons();
1280     update_color_menu();
1281   }
1282   
1283   if (ui.toolno[0] != TOOL_PEN && ui.toolno[0] != TOOL_HIGHLIGHTER) {
1284     if (ui.selection != NULL) return;
1285     ui.toolno[0] = TOOL_PEN;
1286     ui.cur_brush = &(ui.brushes[0][TOOL_PEN]);
1287     update_tool_buttons();
1288     update_tool_menu();
1289   }
1290   
1291   set_cur_color(color);
1292   update_color_buttons();
1293   update_color_menu();
1294   update_cursor();
1295 }
1296
1297 void process_thickness_activate(GtkMenuItem *menuitem, int tool, int val)
1298 {
1299   if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
1300     if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1301       return;
1302   } else {
1303     if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
1304       return;
1305   }
1306
1307   if (ui.cur_mapping != 0) return; // not user-generated
1308
1309   if (ui.selection != NULL && GTK_OBJECT_TYPE(menuitem) != GTK_TYPE_RADIO_MENU_ITEM) {
1310     rethicken_selection(val);
1311     update_thickness_buttons();
1312   }
1313
1314   if (tool >= NUM_STROKE_TOOLS) {
1315     update_thickness_buttons(); // undo illegal button selection
1316     return;
1317   }
1318
1319   ui.brushes[0][tool].thickness_no = val;
1320   ui.brushes[0][tool].thickness = predef_thickness[tool][val];
1321   update_mapping_linkings(tool);
1322   
1323   update_thickness_buttons();
1324   if (tool == TOOL_PEN) update_pen_props_menu();
1325   if (tool == TOOL_ERASER) update_eraser_props_menu();
1326   if (tool == TOOL_HIGHLIGHTER) update_highlighter_props_menu();
1327   update_cursor();
1328 }
1329
1330 void process_papercolor_activate(GtkMenuItem *menuitem, int color)
1331 {
1332   struct Page *pg;
1333   GList *pglist;
1334   gboolean hasdone;
1335
1336   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1337     return;
1338
1339   if ((ui.cur_page->bg->type != BG_SOLID) || ui.bg_apply_all_pages)
1340     gtk_check_menu_item_set_active(
1341       GTK_CHECK_MENU_ITEM(GET_COMPONENT("papercolorNA")), TRUE);
1342
1343   pg = ui.cur_page;
1344   hasdone = FALSE;
1345   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1346     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1347     if (pg->bg->type == BG_SOLID && pg->bg->color_no != color) {
1348       prepare_new_undo();
1349       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1350       undo->multiop |= MULTIOP_CONT_REDO;
1351       hasdone = TRUE;
1352       undo->type = ITEM_NEW_BG_ONE;
1353       undo->page = pg;
1354       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1355       undo->bg->canvas_item = NULL;
1356
1357       pg->bg->color_no = color;
1358       pg->bg->color_rgba = predef_bgcolors_rgba[color];
1359       update_canvas_bg(pg);
1360     }
1361     if (!ui.bg_apply_all_pages) break;
1362   }
1363   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1364 }
1365
1366 void process_paperstyle_activate(GtkMenuItem *menuitem, int style)
1367 {
1368   struct Page *pg;
1369   GList *pglist;
1370   gboolean hasdone, must_upd;
1371
1372   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
1373     return;
1374
1375   if (ui.bg_apply_all_pages)
1376     gtk_check_menu_item_set_active(
1377       GTK_CHECK_MENU_ITEM(GET_COMPONENT("paperstyleNA")), TRUE);
1378
1379   pg = ui.cur_page;
1380   hasdone = FALSE;
1381   must_upd = FALSE;
1382   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
1383     if (ui.bg_apply_all_pages) pg = (struct Page *)pglist->data;
1384     if (pg->bg->type != BG_SOLID || pg->bg->ruling != style) {
1385       prepare_new_undo();
1386       undo->type = ITEM_NEW_BG_ONE;
1387       if (hasdone) undo->multiop |= MULTIOP_CONT_UNDO;
1388       undo->multiop |= MULTIOP_CONT_REDO;
1389       hasdone = TRUE;
1390       undo->page = pg;
1391       undo->bg = (struct Background *)g_memdup(pg->bg, sizeof(struct Background));
1392       undo->bg->canvas_item = NULL;
1393
1394       if (pg->bg->type != BG_SOLID) {
1395         pg->bg->type = BG_SOLID;
1396         pg->bg->color_no = COLOR_WHITE;
1397         pg->bg->color_rgba = predef_bgcolors_rgba[COLOR_WHITE];
1398         pg->bg->filename = NULL;
1399         pg->bg->pixbuf = NULL;
1400         must_upd = TRUE;
1401       }
1402       pg->bg->ruling = style;
1403       update_canvas_bg(pg);
1404     }
1405     if (!ui.bg_apply_all_pages) break;
1406   }
1407   if (hasdone) undo->multiop -= MULTIOP_CONT_REDO;
1408   if (must_upd) update_page_stuff();
1409 }
1410
1411 gboolean ok_to_close(void)
1412 {
1413   GtkWidget *dialog;
1414   GtkResponseType response;
1415   GList *pagelist;
1416
1417   if (ui.saved) return TRUE;
1418   dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_DESTROY_WITH_PARENT,
1419     GTK_MESSAGE_WARNING, GTK_BUTTONS_YES_NO, "Save changes to '%s'?",
1420     (ui.filename!=NULL) ? ui.filename:"Untitled");
1421   gtk_dialog_add_button(GTK_DIALOG (dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
1422   response = gtk_dialog_run(GTK_DIALOG (dialog));
1423   gtk_widget_destroy(dialog);
1424   if (response == GTK_RESPONSE_CANCEL || response == GTK_RESPONSE_DELETE_EVENT) 
1425     return FALSE; // aborted
1426   if (response == GTK_RESPONSE_YES) {
1427     on_fileSave_activate(NULL, NULL);
1428     if (!ui.saved) return FALSE; // if save failed, then we abort
1429   }
1430   return TRUE;
1431 }
1432
1433 // test if we're still busy loading a PDF background file
1434 gboolean page_ops_forbidden(void)
1435 {
1436   return (bgpdf.status != STATUS_NOT_INIT && bgpdf.create_pages);
1437 }
1438
1439 // selection / clipboard stuff
1440
1441 void reset_selection(void)
1442 {
1443   if (ui.selection == NULL) return;
1444   if (ui.selection->canvas_item != NULL) 
1445     gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_item));
1446   g_list_free(ui.selection->items);
1447   g_free(ui.selection);
1448   ui.selection = NULL;
1449   update_copy_paste_enabled();
1450   update_color_menu();
1451   update_thickness_buttons();
1452   update_color_buttons();
1453 }
1454
1455 void move_journal_items_by(GList *itemlist, double dx, double dy,
1456                               struct Layer *l1, struct Layer *l2, GList *depths)
1457 {
1458   struct Item *item;
1459   GnomeCanvasItem *refitem;
1460   GList *link;
1461   int i;
1462   double *pt;
1463   
1464   while (itemlist!=NULL) {
1465     item = (struct Item *)itemlist->data;
1466     if (item->type == ITEM_STROKE) {
1467       for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
1468         { pt[0] += dx; pt[1] += dy; }
1469       item->bbox.left += dx;
1470       item->bbox.right += dx;
1471       item->bbox.top += dy;
1472       item->bbox.bottom += dy;
1473     }
1474     if (l1 != l2) {
1475       // find out where to insert
1476       if (depths != NULL) {
1477         if (depths->data == NULL) link = l2->items;
1478         else {
1479           link = g_list_find(l2->items, depths->data);
1480           if (link != NULL) link = link->next;
1481         }
1482       } else link = NULL;
1483       l2->items = g_list_insert_before(l2->items, link, item);
1484       l2->nitems++;
1485       l1->items = g_list_remove(l1->items, item);
1486       l1->nitems--;
1487     }
1488     if (depths != NULL) { // also raise/lower the canvas items
1489       if (item->canvas_item!=NULL) {
1490         if (depths->data == NULL) link = NULL;
1491         else link = g_list_find(l2->items, depths->data);
1492         if (link != NULL) refitem = ((struct Item *)(link->data))->canvas_item;
1493         else refitem = NULL;
1494         lower_canvas_item_to(l2->group, item->canvas_item, refitem);
1495       }
1496       depths = depths->next;
1497     }
1498     itemlist = itemlist->next;
1499   }
1500 }
1501
1502 // Switch between button mappings
1503
1504 /* NOTE ABOUT BUTTON MAPPINGS: ui.cur_mapping is 0 except while a canvas
1505    click event is being processed ... */
1506
1507 void switch_mapping(int m)
1508 {
1509   if (ui.cur_mapping == m) return;
1510
1511   ui.cur_mapping = m;
1512   if (ui.toolno[m] < NUM_STROKE_TOOLS) 
1513     ui.cur_brush = &(ui.brushes[m][ui.toolno[m]]);
1514   update_tool_buttons();
1515   update_color_menu();
1516   update_cursor();
1517 }
1518
1519 void process_mapping_activate(GtkMenuItem *menuitem, int m, int tool)
1520 {
1521   if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) return;
1522   if (ui.cur_mapping!=0) return;
1523   if (ui.toolno[m] == tool) return;
1524   ui.toolno[m] = tool;
1525   ui.ruler[m] = FALSE;
1526   if (ui.linked_brush[m] == BRUSH_LINKED 
1527        && (tool==TOOL_PEN || tool==TOOL_HIGHLIGHTER))
1528     ui.ruler[m] = ui.ruler[0];
1529   if (ui.linked_brush[m] == BRUSH_COPIED) {
1530     ui.linked_brush[m] = BRUSH_STATIC;
1531     update_mappings_menu_linkings();
1532   }
1533 }