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