]> git.sur5r.net Git - glabels/blob - src/mini-preview.c
Provides barcode even if invalid for correct sizing
[glabels] / src / mini-preview.c
1 /*
2  *  mini-preview.c
3  *  Copyright (C) 2001-2009  Jim Evins <evins@snaught.com>.
4  *
5  *  This file is part of gLabels.
6  *
7  *  gLabels is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  gLabels is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with gLabels.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include "mini-preview.h"
24
25 #include <math.h>
26 #include <glib/gi18n.h>
27
28 #include <libglabels.h>
29 #include "cairo-label-path.h"
30 #include "marshal.h"
31 #include "color.h"
32 #include "print.h"
33
34 #include "debug.h"
35
36
37 /*===========================================*/
38 /* Private macros and constants.             */
39 /*===========================================*/
40
41 #define MARGIN 2
42 #define SHADOW_OFFSET 3
43
44 #define ARROW_SCALE 0.35
45 #define UP_FONT_FAMILY "Sans"
46 #define UP_SCALE 0.15
47
48 /*===========================================*/
49 /* Private types                             */
50 /*===========================================*/
51
52 enum {
53         CLICKED,
54         PRESSED,
55         RELEASED,
56         LAST_SIGNAL
57 };
58
59 typedef struct {
60         gdouble x;
61         gdouble y;
62 } LabelCenter;
63
64 struct _glMiniPreviewPrivate {
65
66         GtkWidget      *canvas;
67
68         lglTemplate    *template;
69         gint            labels_per_sheet;
70         LabelCenter    *centers;
71
72         gint            highlight_first;
73         gint            highlight_last;
74
75         gboolean        dragging;
76         gint            first_i;
77         gint            last_i;
78         gint            prev_i;
79
80         gboolean        draw_arrow_flag;
81         gboolean        rotate_flag;
82
83         gboolean        update_scheduled_flag;
84
85         glLabel        *label;
86         gint            page;
87         gint            n_sheets;
88         gint            n_copies;
89         gint            first;
90         gint            last;
91         gboolean        collate_flag;
92         gboolean        outline_flag;
93         gboolean        reverse_flag;
94         gboolean        crop_marks_flag;
95 };
96
97
98 /*===========================================*/
99 /* Private globals                           */
100 /*===========================================*/
101
102 static gint mini_preview_signals[LAST_SIGNAL] = { 0 };
103
104
105 /*===========================================*/
106 /* Local function prototypes                 */
107 /*===========================================*/
108
109 static void     gl_mini_preview_finalize       (GObject                *object);
110
111 static void     gl_mini_preview_construct      (glMiniPreview          *this,
112                                                 gint                    height,
113                                                 gint                    width);
114
115 static gboolean button_press_event_cb          (GtkWidget              *widget,
116                                                 GdkEventButton         *event);
117 static gboolean motion_notify_event_cb         (GtkWidget              *widget,
118                                                 GdkEventMotion         *event);
119 static gboolean button_release_event_cb        (GtkWidget              *widget,
120                                                 GdkEventButton         *event);
121
122
123 static gboolean expose_event_cb                (GtkWidget              *widget,
124                                                 GdkEventExpose         *event,
125                                                 glMiniPreview          *this);
126 static void     style_set_cb                   (GtkWidget              *widget,
127                                                 GtkStyle               *previous_style,
128                                                 glMiniPreview          *this);
129
130 static void     redraw                         (glMiniPreview          *this);
131 static void     draw                           (glMiniPreview          *this,
132                                                 cairo_t                *cr);
133
134 static void     draw_shadow                    (glMiniPreview          *this,
135                                                 cairo_t                *cr,
136                                                 gdouble                 x,
137                                                 gdouble                 y,
138                                                 gdouble                 width,
139                                                 gdouble                 height);
140 static void     draw_paper                     (glMiniPreview          *this,
141                                                 cairo_t                *cr,
142                                                 gdouble                 width,
143                                                 gdouble                 height,
144                                                 gdouble                 line_width);
145 static void     draw_labels                    (glMiniPreview          *this,
146                                                 cairo_t                *cr,
147                                                 lglTemplate            *template,
148                                                 gdouble                 line_width);
149 static void     draw_arrow                     (glMiniPreview          *this,
150                                                 cairo_t                *cr);
151
152 static void     draw_rich_preview              (glMiniPreview          *this,
153                                                 cairo_t                *cr);
154
155
156 static gint     find_closest_label             (glMiniPreview          *this,
157                                                 gdouble                 x,
158                                                 gdouble                 y);
159
160 static gdouble  set_transform_and_get_scale    (glMiniPreview          *this,
161                                                 cairo_t                *cr);
162
163
164 /****************************************************************************/
165 /* Object infrastructure.                                                   */
166 /****************************************************************************/
167 G_DEFINE_TYPE (glMiniPreview, gl_mini_preview, GTK_TYPE_EVENT_BOX);
168
169
170 /*****************************************************************************/
171 /* Class Init Function.                                                      */
172 /*****************************************************************************/
173 static void
174 gl_mini_preview_class_init (glMiniPreviewClass *class)
175 {
176         GObjectClass   *object_class = G_OBJECT_CLASS (class);
177         GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
178
179         gl_debug (DEBUG_MINI_PREVIEW, "START");
180
181         gl_mini_preview_parent_class = g_type_class_peek_parent (class);
182
183         object_class->finalize = gl_mini_preview_finalize;
184
185         widget_class->button_press_event   = button_press_event_cb;
186         widget_class->motion_notify_event  = motion_notify_event_cb;
187         widget_class->button_release_event = button_release_event_cb;
188
189         mini_preview_signals[CLICKED] =
190             g_signal_new ("clicked",
191                           G_OBJECT_CLASS_TYPE(object_class),
192                           G_SIGNAL_RUN_LAST,
193                           G_STRUCT_OFFSET (glMiniPreviewClass, clicked),
194                           NULL, NULL,
195                           gl_marshal_VOID__INT,
196                           G_TYPE_NONE, 1, G_TYPE_INT);
197
198         mini_preview_signals[PRESSED] =
199             g_signal_new ("pressed",
200                           G_OBJECT_CLASS_TYPE(object_class),
201                           G_SIGNAL_RUN_LAST,
202                           G_STRUCT_OFFSET (glMiniPreviewClass, pressed),
203                           NULL, NULL,
204                           gl_marshal_VOID__INT_INT,
205                           G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
206
207         mini_preview_signals[RELEASED] =
208             g_signal_new ("released",
209                           G_OBJECT_CLASS_TYPE(object_class),
210                           G_SIGNAL_RUN_LAST,
211                           G_STRUCT_OFFSET (glMiniPreviewClass, released),
212                           NULL, NULL,
213                           gl_marshal_VOID__INT_INT,
214                           G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
215
216         gl_debug (DEBUG_MINI_PREVIEW, "END");
217 }
218
219
220 /*****************************************************************************/
221 /* Object Instance Init Function.                                            */
222 /*****************************************************************************/
223 static void
224 gl_mini_preview_init (glMiniPreview *this)
225 {
226         gl_debug (DEBUG_MINI_PREVIEW, "START");
227
228         this->priv = g_new0 (glMiniPreviewPrivate, 1);
229
230         gtk_widget_add_events (GTK_WIDGET (this),
231                                GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
232                                GDK_POINTER_MOTION_MASK);
233
234         gtk_event_box_set_visible_window (GTK_EVENT_BOX (this), FALSE);
235
236         this->priv->canvas = gtk_drawing_area_new ();
237         gtk_widget_set_has_window(this->priv->canvas, FALSE);
238         gtk_container_add (GTK_CONTAINER (this), this->priv->canvas);
239
240         g_signal_connect (G_OBJECT (this->priv->canvas), "expose-event",
241                           G_CALLBACK (expose_event_cb), this);
242         g_signal_connect (G_OBJECT (this->priv->canvas), "style-set",
243                           G_CALLBACK (style_set_cb), this);
244
245         gl_debug (DEBUG_MINI_PREVIEW, "END");
246 }
247
248
249 /*****************************************************************************/
250 /* Finalize Method.                                                          */
251 /*****************************************************************************/
252 static void
253 gl_mini_preview_finalize (GObject *object)
254 {
255         glMiniPreview *this = GL_MINI_PREVIEW (object);
256
257         gl_debug (DEBUG_MINI_PREVIEW, "START");
258
259         g_return_if_fail (object != NULL);
260         g_return_if_fail (GL_IS_MINI_PREVIEW (object));
261
262         if (this->priv->label)
263         {
264                 g_object_unref (this->priv->label);
265         }
266         lgl_template_free (this->priv->template);
267         g_free (this->priv->centers);
268         g_free (this->priv);
269
270         G_OBJECT_CLASS (gl_mini_preview_parent_class)->finalize (object);
271
272         gl_debug (DEBUG_MINI_PREVIEW, "END");
273 }
274
275
276 /*****************************************************************************/
277 /** New Object Generator.                                                    */
278 /*****************************************************************************/
279 GtkWidget *
280 gl_mini_preview_new (gint height,
281                      gint width)
282 {
283         glMiniPreview *this;
284
285         gl_debug (DEBUG_MINI_PREVIEW, "START");
286
287         this = g_object_new (gl_mini_preview_get_type (), NULL);
288
289         gl_mini_preview_construct (this, height, width);
290
291         gl_debug (DEBUG_MINI_PREVIEW, "END");
292
293         return GTK_WIDGET (this);
294 }
295
296
297 /*--------------------------------------------------------------------------*/
298 /* Construct composite widget.                                              */
299 /*--------------------------------------------------------------------------*/
300 static void
301 gl_mini_preview_construct (glMiniPreview *this,
302                            gint           height,
303                            gint           width)
304 {
305         gl_debug (DEBUG_MINI_PREVIEW, "START");
306
307         gtk_widget_set_size_request (GTK_WIDGET (this->priv->canvas), width, height);
308
309         gl_debug (DEBUG_MINI_PREVIEW, "END");
310 }
311
312
313 /****************************************************************************/
314 /* Set template for mini-preview to determine geometry.                     */
315 /****************************************************************************/
316 void
317 gl_mini_preview_set_by_name (glMiniPreview *this,
318                              const gchar   *name)
319 {
320         lglTemplate *template;
321
322         gl_debug (DEBUG_MINI_PREVIEW, "START");
323
324         /* Fetch template */
325         template = lgl_db_lookup_template_from_name (name);
326
327         gl_mini_preview_set_template (this, template);
328
329         lgl_template_free (template);
330
331         gl_debug (DEBUG_MINI_PREVIEW, "END");
332 }
333
334
335 /****************************************************************************/
336 /* Set template for mini-preview to determine geometry.                     */
337 /****************************************************************************/
338 void
339 gl_mini_preview_set_template (glMiniPreview     *this,
340                               const lglTemplate *template)
341 {
342         const lglTemplateFrame    *frame;
343         lglTemplateOrigin         *origins;
344         gdouble                    w, h;
345         gint                       i;
346
347         gl_debug (DEBUG_MINI_PREVIEW, "START");
348
349         frame = (lglTemplateFrame *)template->frames->data;
350
351         /*
352          * Set template
353          */
354         lgl_template_free (this->priv->template);
355         this->priv->template = lgl_template_dup (template);
356
357         /*
358          * Set labels per sheet
359          */
360         this->priv->labels_per_sheet = lgl_template_frame_get_n_labels (frame);
361
362         /*
363          * Initialize centers
364          */
365         g_free (this->priv->centers);
366         this->priv->centers = g_new0 (LabelCenter, this->priv->labels_per_sheet);
367         origins = lgl_template_frame_get_origins (frame);
368         lgl_template_frame_get_size (frame, &w, &h);
369         for ( i=0; i<this->priv->labels_per_sheet; i++ )
370         {
371                 this->priv->centers[i].x = origins[i].x + w/2.0;
372                 this->priv->centers[i].y = origins[i].y + h/2.0;
373         }
374         g_free (origins);
375
376         /*
377          * Redraw modified preview
378          */
379         redraw (this);
380
381         gl_debug (DEBUG_MINI_PREVIEW, "END");
382 }
383
384
385 /****************************************************************************/
386 /* Highlight given label outlines.                                          */
387 /****************************************************************************/
388 void
389 gl_mini_preview_highlight_range (glMiniPreview *this,
390                                  gint           first_label,
391                                  gint           last_label)
392 {
393         gl_debug (DEBUG_MINI_PREVIEW, "START");
394
395         if ( (first_label != this->priv->highlight_first) ||
396              (last_label  != this->priv->highlight_last) )
397         {
398
399                 this->priv->highlight_first = first_label;
400                 this->priv->highlight_last =  last_label;
401
402                 redraw (this);
403
404         }
405
406         gl_debug (DEBUG_MINI_PREVIEW, "END");
407 }
408
409
410 /****************************************************************************/
411 /* Set draw arrow.                                                          */
412 /****************************************************************************/
413 void
414 gl_mini_preview_set_draw_arrow (glMiniPreview     *this,
415                                 gboolean           draw_arrow_flag)
416 {
417         if ( draw_arrow_flag != this->priv->draw_arrow_flag )
418         {
419                 this->priv->draw_arrow_flag = draw_arrow_flag;
420                 redraw (this);
421         }
422 }
423
424
425 /****************************************************************************/
426 /* Set rotate flag.                                                         */
427 /****************************************************************************/
428 void
429 gl_mini_preview_set_rotate (glMiniPreview     *this,
430                             gboolean           rotate_flag)
431 {
432         if ( rotate_flag != this->priv->rotate_flag )
433         {
434                 this->priv->rotate_flag = rotate_flag;
435                 redraw (this);
436         }
437 }
438
439
440 /****************************************************************************/
441 /* Set label.                                                               */
442 /****************************************************************************/
443 void
444 gl_mini_preview_set_label (glMiniPreview     *this,
445                            glLabel           *label)
446 {
447         if ( this->priv->label )
448         {
449                 g_object_unref (this->priv->label);
450         }
451         this->priv->label = g_object_ref (label);
452         redraw (this);
453 }
454
455
456 /****************************************************************************/
457 /* Set page number.                                                         */
458 /****************************************************************************/
459 void
460 gl_mini_preview_set_page (glMiniPreview     *this,
461                           gint               page)
462 {
463         if ( page != this->priv->page )
464         {
465                 this->priv->page = page;
466                 redraw (this);
467         }
468 }
469
470
471 /****************************************************************************/
472 /* Set number of sheets.                                                    */
473 /****************************************************************************/
474 void
475 gl_mini_preview_set_n_sheets (glMiniPreview     *this,
476                               gint               n_sheets)
477 {
478         if ( n_sheets != this->priv->n_sheets )
479         {
480                 this->priv->n_sheets = n_sheets;
481                 redraw (this);
482         }
483 }
484
485
486 /****************************************************************************/
487 /* Set number of copies (merge only).                                       */
488 /****************************************************************************/
489 void
490 gl_mini_preview_set_n_copies (glMiniPreview     *this,
491                               gint               n_copies)
492 {
493         if ( n_copies != this->priv->n_copies )
494         {
495                 this->priv->n_copies = n_copies;
496                 redraw (this);
497         }
498 }
499
500
501 /****************************************************************************/
502 /* Set first label number on first sheet.                                   */
503 /****************************************************************************/
504 void
505 gl_mini_preview_set_first (glMiniPreview     *this,
506                            gint               first)
507 {
508         if ( first != this->priv->first )
509         {
510                 this->priv->first = first;
511                 redraw (this);
512         }
513 }
514
515
516 /****************************************************************************/
517 /* Set last label number on first sheet (non-merge only).                   */
518 /****************************************************************************/
519 void
520 gl_mini_preview_set_last (glMiniPreview     *this,
521                           gint               last)
522 {
523         if ( last != this->priv->last )
524         {
525                 this->priv->last = last;
526                 redraw (this);
527         }
528 }
529
530
531 /****************************************************************************/
532 /* Set collate flag (merge only).                                           */
533 /****************************************************************************/
534 void
535 gl_mini_preview_set_collate_flag (glMiniPreview     *this,
536                                   gboolean           collate_flag)
537 {
538         if ( collate_flag != this->priv->collate_flag )
539         {
540                 this->priv->collate_flag = collate_flag;
541                 redraw (this);
542         }
543 }
544
545
546 /****************************************************************************/
547 /* Set outline flag.                                                        */
548 /****************************************************************************/
549 void
550 gl_mini_preview_set_outline_flag (glMiniPreview     *this,
551                                   gboolean           outline_flag)
552 {
553         if ( outline_flag != this->priv->outline_flag )
554         {
555                 this->priv->outline_flag = outline_flag;
556                 redraw (this);
557         }
558 }
559
560
561 /****************************************************************************/
562 /* Set reverse flag.                                                        */
563 /****************************************************************************/
564 void
565 gl_mini_preview_set_reverse_flag (glMiniPreview     *this,
566                                   gboolean           reverse_flag)
567 {
568         if ( reverse_flag != this->priv->reverse_flag )
569         {
570                 this->priv->reverse_flag = reverse_flag;
571                 redraw (this);
572         }
573 }
574
575
576 /****************************************************************************/
577 /* Set crop marks flag.                                                     */
578 /****************************************************************************/
579 void
580 gl_mini_preview_set_crop_marks_flag (glMiniPreview     *this,
581                                      gboolean           crop_marks_flag)
582 {
583         if ( crop_marks_flag != this->priv->crop_marks_flag )
584         {
585                 this->priv->crop_marks_flag = crop_marks_flag;
586                 redraw (this);
587         }
588 }
589
590
591 /*--------------------------------------------------------------------------*/
592 /* Set transformation and return scale.                                     */
593 /*--------------------------------------------------------------------------*/
594 static gdouble
595 set_transform_and_get_scale (glMiniPreview *this,
596                              cairo_t       *cr)
597 {
598         lglTemplate   *template = this->priv->template;
599         GtkAllocation  allocation;
600         gdouble        w, h;
601         gdouble        scale;
602         gdouble        offset_x, offset_y;
603
604         /* Establish scale and origin. */
605         gtk_widget_get_allocation (GTK_WIDGET (this), &allocation);
606         w = allocation.width;
607         h = allocation.height;
608
609         /* establish scale. */
610         scale = MIN( (w - 2*MARGIN - 2*SHADOW_OFFSET)/template->page_width,
611                      (h - 2*MARGIN - 2*SHADOW_OFFSET)/template->page_height );
612
613         /* Find offset to center preview. */
614         offset_x = (w/scale - template->page_width) / 2.0;
615         offset_y = (h/scale - template->page_height) / 2.0;
616
617         /* Set transformation. */
618         cairo_scale (cr, scale, scale);
619         cairo_translate (cr, offset_x, offset_y);
620
621         return scale;
622 }
623
624
625 /*--------------------------------------------------------------------------*/
626 /* Button press event handler                                               */
627 /*--------------------------------------------------------------------------*/
628 static gboolean
629 button_press_event_cb (GtkWidget      *widget,
630                        GdkEventButton *event)
631 {
632         glMiniPreview     *this = GL_MINI_PREVIEW (widget);
633         GdkWindow         *window;
634         cairo_t           *cr;
635         gdouble            scale;
636         gdouble            x, y;
637         gint               i;
638
639         gl_debug (DEBUG_MINI_PREVIEW, "START");
640
641         if ( event->button == 1 )
642         {
643
644                 window = gtk_widget_get_window (this->priv->canvas);
645
646                 cr = gdk_cairo_create (window);
647
648                 scale = set_transform_and_get_scale (this, cr);
649
650                 x = event->x;
651                 y = event->y;
652                 cairo_device_to_user (cr, &x, &y);
653
654                 i = find_closest_label (this, x, y);
655
656                 g_signal_emit (G_OBJECT(this),
657                                mini_preview_signals[CLICKED],
658                                0, i);
659
660                 this->priv->first_i = i;
661                 this->priv->last_i  = i;
662                 g_signal_emit (G_OBJECT(this),
663                                mini_preview_signals[PRESSED],
664                                0, this->priv->first_i, this->priv->last_i);
665
666                 this->priv->dragging = TRUE;
667                 this->priv->prev_i   = i;
668
669                 cairo_destroy (cr);
670         }
671
672         gl_debug (DEBUG_MINI_PREVIEW, "END");
673         return FALSE;
674 }
675
676
677 /*--------------------------------------------------------------------------*/
678 /* Motion notify event handler                                              */
679 /*--------------------------------------------------------------------------*/
680 static gboolean
681 motion_notify_event_cb (GtkWidget      *widget,
682                         GdkEventMotion *event)
683 {
684         glMiniPreview *this = GL_MINI_PREVIEW (widget);
685         GdkWindow     *window;
686         cairo_t       *cr;
687         gdouble        scale;
688         gdouble        x, y;
689         gint           i;
690
691         gl_debug (DEBUG_MINI_PREVIEW, "START");
692
693         if (this->priv->dragging)
694         {
695                 window = gtk_widget_get_window (this->priv->canvas);
696
697                 cr = gdk_cairo_create (window);
698
699                 scale = set_transform_and_get_scale (this, cr);
700
701                 x = event->x;
702                 y = event->y;
703                 cairo_device_to_user (cr, &x, &y);
704
705                 i = find_closest_label (this, x, y);
706
707                 if ( i != this->priv->prev_i )
708                 {
709                         this->priv->last_i = i;
710
711                         g_signal_emit (G_OBJECT(this),
712                                        mini_preview_signals[PRESSED],
713                                        0,
714                                        MIN (this->priv->first_i, this->priv->last_i),
715                                        MAX (this->priv->first_i, this->priv->last_i));
716
717                         this->priv->prev_i = i;
718                 }
719                 cairo_destroy (cr);
720         }
721
722         gl_debug (DEBUG_MINI_PREVIEW, "END");
723         return FALSE;
724 }
725
726
727 /*--------------------------------------------------------------------------*/
728 /* Button release event handler                                             */
729 /*--------------------------------------------------------------------------*/
730 static gboolean
731 button_release_event_cb (GtkWidget      *widget,
732                          GdkEventButton *event)
733 {
734         glMiniPreview *this = GL_MINI_PREVIEW (widget);
735
736         gl_debug (DEBUG_MINI_PREVIEW, "START");
737
738         if ( event->button == 1 )
739         {
740                 this->priv->dragging = FALSE;
741
742         }
743
744         g_signal_emit (G_OBJECT(this),
745                        mini_preview_signals[RELEASED],
746                        0, this->priv->first_i, this->priv->last_i);
747
748         gl_debug (DEBUG_MINI_PREVIEW, "END");
749         return FALSE;
750 }
751
752
753 /*--------------------------------------------------------------------------*/
754 /* Find index+1 of label closest to given coordinates.                      */
755 /*--------------------------------------------------------------------------*/
756 static gint
757 find_closest_label (glMiniPreview      *this,
758                     gdouble             x,
759                     gdouble             y)
760 {
761         gint    i;
762         gint    min_i;
763         gdouble dx, dy, d2, min_d2;
764
765         dx = x - this->priv->centers[0].x;
766         dy = y - this->priv->centers[0].y;
767         min_d2 = dx*dx + dy*dy;
768         min_i = 0;
769
770         for ( i=1; i<this->priv->labels_per_sheet; i++ )
771         {
772                 dx = x - this->priv->centers[i].x;
773                 dy = y - this->priv->centers[i].y;
774                 d2 = dx*dx + dy*dy;
775
776                 if ( d2 < min_d2 )
777                 {
778                         min_d2 = d2;
779                         min_i  = i;
780                 }
781         }
782
783         return min_i + 1;
784 }
785
786
787 /*--------------------------------------------------------------------------*/
788 /* Expose event handler.                                                    */
789 /*--------------------------------------------------------------------------*/
790 static gboolean
791 expose_event_cb (GtkWidget       *widget,
792                  GdkEventExpose  *event,
793                  glMiniPreview   *this)
794 {
795         GdkWindow     *window;
796         cairo_t       *cr;
797         GtkAllocation  allocation;
798
799         gl_debug (DEBUG_MINI_PREVIEW, "START");
800
801         this->priv->update_scheduled_flag = FALSE;
802
803         window = gtk_widget_get_window (widget);
804
805         cr = gdk_cairo_create (window);
806
807         cairo_rectangle (cr,
808                         event->area.x, event->area.y,
809                         event->area.width, event->area.height);
810         cairo_clip (cr);
811
812         gtk_widget_get_allocation (widget, &allocation);
813         cairo_translate (cr, allocation.x, allocation.y);
814
815         draw (this, cr);
816
817         cairo_destroy (cr);
818
819         gl_debug (DEBUG_MINI_PREVIEW, "END");
820         return FALSE;
821 }
822
823
824 /*--------------------------------------------------------------------------*/
825 /* Style set handler (updates colors when style/theme changes).             */
826 /*--------------------------------------------------------------------------*/
827 static void
828 style_set_cb (GtkWidget        *widget,
829               GtkStyle         *previous_style,
830               glMiniPreview    *this)
831 {
832         gl_debug (DEBUG_MINI_PREVIEW, "START");
833
834         redraw (this);
835
836         gl_debug (DEBUG_MINI_PREVIEW, "END");
837 }
838
839
840 /*--------------------------------------------------------------------------*/
841 /* Redraw.                                                                  */
842 /*--------------------------------------------------------------------------*/
843 static void
844 redraw (glMiniPreview      *this)
845 {
846         GdkWindow *window;
847         GdkRegion *region;
848
849         gl_debug (DEBUG_MINI_PREVIEW, "START");
850
851         window = gtk_widget_get_window (this->priv->canvas);
852
853         if (window)
854         {
855
856                 if ( !this->priv->update_scheduled_flag )
857                 {
858                         this->priv->update_scheduled_flag = TRUE;
859
860                         region = gdk_drawable_get_clip_region (window);
861                         gdk_window_invalidate_region (window, region, TRUE);
862                         gdk_region_destroy (region);
863                 }
864         }
865
866         gl_debug (DEBUG_MINI_PREVIEW, "END");
867 }
868
869
870 /*--------------------------------------------------------------------------*/
871 /* Draw mini preview.                                                       */
872 /*--------------------------------------------------------------------------*/
873 static void
874 draw (glMiniPreview  *this,
875       cairo_t        *cr)
876 {
877         lglTemplate *template = this->priv->template;
878         gdouble      scale;
879         gdouble      shadow_x, shadow_y;
880
881
882         gl_debug (DEBUG_MINI_PREVIEW, "START");
883
884         if (template)
885         {
886
887                 scale = set_transform_and_get_scale (this, cr);
888
889                 /* update shadow */
890                 shadow_x = SHADOW_OFFSET/scale;
891                 shadow_y = SHADOW_OFFSET/scale;
892
893                 draw_shadow (this, cr,
894                              shadow_x, shadow_y,
895                              template->page_width, template->page_height);
896
897                 draw_paper (this, cr,
898                             template->page_width, template->page_height,
899                             1.0/scale);
900
901                 draw_labels (this, cr, template, 2.0/scale);
902
903                 if (this->priv->draw_arrow_flag)
904                 {
905                         draw_arrow (this, cr);
906                 }
907
908                 if (this->priv->label)
909                 {
910                         draw_rich_preview (this, cr);
911                 }
912
913         }
914
915         gl_debug (DEBUG_MINI_PREVIEW, "END");
916
917 }
918
919
920 /*--------------------------------------------------------------------------*/
921 /* Draw page shadow                                                         */
922 /*--------------------------------------------------------------------------*/
923 static void
924 draw_shadow (glMiniPreview      *this,
925              cairo_t            *cr,
926              gdouble             x,
927              gdouble             y,
928              gdouble             width,
929              gdouble             height)
930 {
931         GtkStyle *style;
932         guint     shadow_color;
933
934         gl_debug (DEBUG_MINI_PREVIEW, "START");
935
936         cairo_save (cr);
937
938         cairo_rectangle (cr, x, y, width, height);
939
940         style = gtk_widget_get_style (GTK_WIDGET(this));
941         shadow_color = gl_color_from_gdk_color (&style->dark[GTK_STATE_NORMAL]);
942         cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (shadow_color));
943
944         cairo_fill (cr);
945
946         cairo_restore (cr);
947
948         gl_debug (DEBUG_MINI_PREVIEW, "END");
949 }
950
951
952 /*--------------------------------------------------------------------------*/
953 /* Draw page                                                                */
954 /*--------------------------------------------------------------------------*/
955 static void
956 draw_paper (glMiniPreview      *this,
957             cairo_t            *cr,
958             gdouble             width,
959             gdouble             height,
960             gdouble             line_width)
961 {
962         GtkStyle                  *style;
963         guint                      paper_color, outline_color;
964
965         gl_debug (DEBUG_MINI_PREVIEW, "START");
966
967         cairo_save (cr);
968
969         style = gtk_widget_get_style (GTK_WIDGET(this));
970         paper_color   = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
971         outline_color = gl_color_from_gdk_color (&style->fg[GTK_STATE_NORMAL]);
972
973         cairo_rectangle (cr, 0.0, 0.0, width, height);
974
975         cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (paper_color));
976         cairo_fill_preserve (cr);
977
978         cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (outline_color));
979         cairo_set_line_width (cr, line_width);
980         cairo_stroke (cr);
981
982         cairo_restore (cr);
983
984         gl_debug (DEBUG_MINI_PREVIEW, "END");
985 }
986
987
988 /*--------------------------------------------------------------------------*/
989 /* Draw labels                                                              */
990 /*--------------------------------------------------------------------------*/
991 static void
992 draw_labels (glMiniPreview *this,
993              cairo_t       *cr,
994              lglTemplate   *template,
995              gdouble        line_width)
996 {
997         const lglTemplateFrame    *frame;
998         gint                       i, n_labels;
999         lglTemplateOrigin         *origins;
1000         GtkStyle                  *style;
1001         guint                      base_color;
1002         guint                      highlight_color, paper_color, outline_color;
1003
1004         gl_debug (DEBUG_MINI_PREVIEW, "START");
1005
1006         frame = (lglTemplateFrame *)template->frames->data;
1007
1008         n_labels = lgl_template_frame_get_n_labels (frame);
1009         origins  = lgl_template_frame_get_origins (frame);
1010
1011         style = gtk_widget_get_style (GTK_WIDGET(this));
1012         base_color      = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
1013
1014         paper_color     = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
1015         highlight_color = gl_color_set_opacity (base_color, 0.10);
1016         if (this->priv->label)
1017         {
1018                 /* Outlines are more subtle when doing a rich preview. */
1019                 outline_color   = gl_color_set_opacity (base_color, 0.25);
1020         }
1021         else
1022         {
1023                 outline_color   = gl_color_set_opacity (base_color, 1.00);
1024         }
1025
1026         for ( i=0; i < n_labels; i++ ) {
1027
1028                 cairo_save (cr);
1029
1030                 cairo_translate (cr, origins[i].x, origins[i].y);
1031                 gl_cairo_label_path (cr, template, FALSE, FALSE);
1032
1033                 if ( ((i+1) >= this->priv->highlight_first) &&
1034                      ((i+1) <= this->priv->highlight_last) )
1035                 {
1036                         cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (highlight_color));
1037                         cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
1038                         cairo_fill_preserve (cr);
1039                 }
1040
1041                 cairo_set_line_width (cr, line_width);
1042                 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (outline_color));
1043                 cairo_stroke (cr);
1044
1045                 cairo_restore (cr);
1046
1047         }
1048
1049         g_free (origins);
1050
1051         gl_debug (DEBUG_MINI_PREVIEW, "END");
1052 }
1053
1054
1055 /*--------------------------------------------------------------------------*/
1056 /* Draw arrow to indicate top of labels.                                    */
1057 /*--------------------------------------------------------------------------*/
1058 static void
1059 draw_arrow  (glMiniPreview      *this,
1060              cairo_t            *cr)
1061 {
1062         lglTemplateFrame  *frame;
1063         lglTemplateOrigin *origins;
1064         gdouble            width, height, min;
1065         gdouble            x0, y0;
1066         GtkStyle          *style;
1067         guint              base_color, arrow_color;
1068
1069         PangoLayout          *layout;
1070         PangoFontDescription *desc;
1071         PangoRectangle        rect;
1072
1073         /* Translators: "Up" refers to the direction towards the top of a label. */
1074         const gchar          *up = _("Up");
1075
1076         frame = (lglTemplateFrame *)this->priv->template->frames->data;
1077
1078         lgl_template_frame_get_size (frame, &width, &height);
1079
1080         if ( width != height )
1081         {
1082
1083                 origins = lgl_template_frame_get_origins (frame);
1084                 x0 = origins[0].x;
1085                 y0 = origins[0].y;
1086                 min = MIN (width, height);
1087                 g_free (origins);
1088
1089                 cairo_save (cr);
1090
1091                 style       = gtk_widget_get_style (GTK_WIDGET(this));
1092                 base_color  = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
1093                 arrow_color = gl_color_set_opacity (base_color, 0.25);
1094                 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (arrow_color));
1095
1096                 cairo_translate (cr, x0 + width/2, y0 + height/2);
1097                 cairo_scale (cr, 1, -1);
1098                 if ( this->priv->rotate_flag )
1099                 {
1100                         cairo_rotate (cr, -M_PI/2.0);
1101                 }
1102
1103                 cairo_new_path (cr);
1104                 cairo_move_to (cr, 0, -min*ARROW_SCALE/3);
1105                 cairo_line_to (cr, 0, min*ARROW_SCALE);
1106
1107                 cairo_new_sub_path (cr);
1108                 cairo_move_to (cr, -min*ARROW_SCALE/2, min*ARROW_SCALE/2);
1109                 cairo_line_to (cr, 0, min*ARROW_SCALE);
1110                 cairo_line_to (cr, min*ARROW_SCALE/2, min*ARROW_SCALE/2);
1111
1112                 cairo_set_line_width (cr, 0.25*min*ARROW_SCALE);
1113
1114                 cairo_stroke (cr);
1115
1116                 layout = pango_cairo_create_layout (cr);
1117
1118                 desc = pango_font_description_new ();
1119                 pango_font_description_set_family (desc, UP_FONT_FAMILY);
1120                 pango_font_description_set_weight (desc, PANGO_WEIGHT_BOLD);
1121                 pango_font_description_set_size (desc, min*UP_SCALE*PANGO_SCALE);
1122                 pango_layout_set_font_description (layout, desc);
1123                 pango_font_description_free (desc);
1124
1125                 pango_layout_set_text (layout, up, -1);
1126                 pango_layout_set_width (layout, -1);
1127                 pango_layout_get_pixel_extents (layout, NULL, &rect);
1128
1129                 cairo_move_to (cr, -rect.width/2, -min/4+rect.height/2);
1130
1131                 cairo_scale (cr, 1, -1);
1132                 pango_cairo_show_layout (cr, layout);
1133
1134                 cairo_restore (cr);
1135
1136         }
1137 }
1138
1139
1140 /*--------------------------------------------------------------------------*/
1141 /* Draw rich preview using print renderers.                                 */
1142 /*--------------------------------------------------------------------------*/
1143 static void
1144 draw_rich_preview (glMiniPreview          *this,
1145                    cairo_t                *cr)
1146 {
1147         glMerge      *merge;
1148         glPrintState  state;
1149
1150         merge = gl_label_get_merge (this->priv->label);
1151
1152         if (!merge)
1153         {
1154                 gl_print_simple_sheet (this->priv->label,
1155                                        cr,
1156                                        this->priv->page,
1157                                        this->priv->n_sheets,
1158                                        this->priv->first,
1159                                        this->priv->last,
1160                                        this->priv->outline_flag,
1161                                        this->priv->reverse_flag,
1162                                        this->priv->crop_marks_flag);
1163         }
1164         else
1165         {
1166                 /* FIXME: maybe the renderers should be more self contained.
1167                  *        This will only work for the first page, since
1168                  *        previous pages must be rendered to establish
1169                  *        state.
1170                  */
1171                 state.i_copy = 0;
1172                 state.p_record = (GList *)gl_merge_get_record_list (merge);
1173
1174                 if (this->priv->collate_flag)
1175                 {
1176                         gl_print_collated_merge_sheet (this->priv->label,
1177                                                        cr,
1178                                                        this->priv->page,
1179                                                        this->priv->n_copies,
1180                                                        this->priv->first,
1181                                                        this->priv->outline_flag,
1182                                                        this->priv->reverse_flag,
1183                                                        this->priv->crop_marks_flag,
1184                                                        &state);
1185                 }
1186                 else
1187                 {
1188                         gl_print_uncollated_merge_sheet (this->priv->label,
1189                                                          cr,
1190                                                          this->priv->page,
1191                                                          this->priv->n_copies,
1192                                                          this->priv->first,
1193                                                          this->priv->outline_flag,
1194                                                          this->priv->reverse_flag,
1195                                                          this->priv->crop_marks_flag,
1196                                                          &state);
1197                 }
1198         }
1199 }
1200
1201
1202
1203 /*
1204  * Local Variables:       -- emacs
1205  * mode: C                -- emacs
1206  * c-basic-offset: 8      -- emacs
1207  * tab-width: 8           -- emacs
1208  * indent-tabs-mode: nil  -- emacs
1209  * End:                   -- emacs
1210  */