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