]> git.sur5r.net Git - glabels/blob - src/mini-preview.c
Imported Upstream version 3.0.0
[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 draw_cb                        (GtkWidget              *widget,
124                                                 cairo_t                *cr,
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), "draw",
241                           G_CALLBACK (draw_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 draw_cb (GtkWidget       *widget,
792          cairo_t         *cr,
793          glMiniPreview   *this)
794 {
795         gl_debug (DEBUG_MINI_PREVIEW, "START");
796
797         this->priv->update_scheduled_flag = FALSE;
798         draw (this, cr);
799
800         gl_debug (DEBUG_MINI_PREVIEW, "END");
801         return FALSE;
802 }
803
804
805 /*--------------------------------------------------------------------------*/
806 /* Style set handler (updates colors when style/theme changes).             */
807 /*--------------------------------------------------------------------------*/
808 static void
809 style_set_cb (GtkWidget        *widget,
810               GtkStyle         *previous_style,
811               glMiniPreview    *this)
812 {
813         gl_debug (DEBUG_MINI_PREVIEW, "START");
814
815         redraw (this);
816
817         gl_debug (DEBUG_MINI_PREVIEW, "END");
818 }
819
820
821 /*--------------------------------------------------------------------------*/
822 /* Redraw.                                                                  */
823 /*--------------------------------------------------------------------------*/
824 static void
825 redraw (glMiniPreview      *this)
826 {
827         GdkWindow     *window;
828         GtkAllocation  allocation;
829
830         gl_debug (DEBUG_MINI_PREVIEW, "START");
831
832         window = gtk_widget_get_window (this->priv->canvas);
833
834         if (window)
835         {
836
837                 if ( !this->priv->update_scheduled_flag )
838                 {
839                         this->priv->update_scheduled_flag = TRUE;
840
841                         gtk_widget_get_allocation (GTK_WIDGET (this), &allocation);
842                         gdk_window_invalidate_rect (window, &allocation, FALSE);
843                 }
844         }
845
846         gl_debug (DEBUG_MINI_PREVIEW, "END");
847 }
848
849
850 /*--------------------------------------------------------------------------*/
851 /* Draw mini preview.                                                       */
852 /*--------------------------------------------------------------------------*/
853 static void
854 draw (glMiniPreview  *this,
855       cairo_t        *cr)
856 {
857         lglTemplate *template = this->priv->template;
858         gdouble      scale;
859         gdouble      shadow_x, shadow_y;
860
861
862         gl_debug (DEBUG_MINI_PREVIEW, "START");
863
864         if (template)
865         {
866
867                 scale = set_transform_and_get_scale (this, cr);
868
869                 /* update shadow */
870                 shadow_x = SHADOW_OFFSET/scale;
871                 shadow_y = SHADOW_OFFSET/scale;
872
873                 draw_shadow (this, cr,
874                              shadow_x, shadow_y,
875                              template->page_width, template->page_height);
876
877                 draw_paper (this, cr,
878                             template->page_width, template->page_height,
879                             1.0/scale);
880
881                 draw_labels (this, cr, template, 2.0/scale);
882
883                 if (this->priv->draw_arrow_flag)
884                 {
885                         draw_arrow (this, cr);
886                 }
887
888                 if (this->priv->label)
889                 {
890                         draw_rich_preview (this, cr);
891                 }
892
893         }
894
895         gl_debug (DEBUG_MINI_PREVIEW, "END");
896
897 }
898
899
900 /*--------------------------------------------------------------------------*/
901 /* Draw page shadow                                                         */
902 /*--------------------------------------------------------------------------*/
903 static void
904 draw_shadow (glMiniPreview      *this,
905              cairo_t            *cr,
906              gdouble             x,
907              gdouble             y,
908              gdouble             width,
909              gdouble             height)
910 {
911         GtkStyle *style;
912         guint     shadow_color;
913
914         gl_debug (DEBUG_MINI_PREVIEW, "START");
915
916         cairo_save (cr);
917
918         cairo_rectangle (cr, x, y, width, height);
919
920         style = gtk_widget_get_style (GTK_WIDGET(this));
921         shadow_color = gl_color_from_gdk_color (&style->dark[GTK_STATE_NORMAL]);
922         cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (shadow_color));
923
924         cairo_fill (cr);
925
926         cairo_restore (cr);
927
928         gl_debug (DEBUG_MINI_PREVIEW, "END");
929 }
930
931
932 /*--------------------------------------------------------------------------*/
933 /* Draw page                                                                */
934 /*--------------------------------------------------------------------------*/
935 static void
936 draw_paper (glMiniPreview      *this,
937             cairo_t            *cr,
938             gdouble             width,
939             gdouble             height,
940             gdouble             line_width)
941 {
942         GtkStyle                  *style;
943         guint                      paper_color, outline_color;
944
945         gl_debug (DEBUG_MINI_PREVIEW, "START");
946
947         cairo_save (cr);
948
949         style = gtk_widget_get_style (GTK_WIDGET(this));
950         paper_color   = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
951         outline_color = gl_color_from_gdk_color (&style->fg[GTK_STATE_NORMAL]);
952
953         cairo_rectangle (cr, 0.0, 0.0, width, height);
954
955         cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (paper_color));
956         cairo_fill_preserve (cr);
957
958         cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (outline_color));
959         cairo_set_line_width (cr, line_width);
960         cairo_stroke (cr);
961
962         cairo_restore (cr);
963
964         gl_debug (DEBUG_MINI_PREVIEW, "END");
965 }
966
967
968 /*--------------------------------------------------------------------------*/
969 /* Draw labels                                                              */
970 /*--------------------------------------------------------------------------*/
971 static void
972 draw_labels (glMiniPreview *this,
973              cairo_t       *cr,
974              lglTemplate   *template,
975              gdouble        line_width)
976 {
977         const lglTemplateFrame    *frame;
978         gint                       i, n_labels;
979         lglTemplateOrigin         *origins;
980         GtkStyle                  *style;
981         guint                      base_color;
982         guint                      highlight_color, paper_color, outline_color;
983
984         gl_debug (DEBUG_MINI_PREVIEW, "START");
985
986         frame = (lglTemplateFrame *)template->frames->data;
987
988         n_labels = lgl_template_frame_get_n_labels (frame);
989         origins  = lgl_template_frame_get_origins (frame);
990
991         style = gtk_widget_get_style (GTK_WIDGET(this));
992         base_color      = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
993
994         paper_color     = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
995         highlight_color = gl_color_set_opacity (base_color, 0.10);
996         if (this->priv->label)
997         {
998                 /* Outlines are more subtle when doing a rich preview. */
999                 outline_color   = gl_color_set_opacity (base_color, 0.25);
1000         }
1001         else
1002         {
1003                 outline_color   = gl_color_set_opacity (base_color, 1.00);
1004         }
1005
1006         for ( i=0; i < n_labels; i++ ) {
1007
1008                 cairo_save (cr);
1009
1010                 cairo_translate (cr, origins[i].x, origins[i].y);
1011                 gl_cairo_label_path (cr, template, FALSE, FALSE);
1012
1013                 if ( ((i+1) >= this->priv->highlight_first) &&
1014                      ((i+1) <= this->priv->highlight_last) )
1015                 {
1016                         cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (highlight_color));
1017                         cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
1018                         cairo_fill_preserve (cr);
1019                 }
1020
1021                 cairo_set_line_width (cr, line_width);
1022                 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (outline_color));
1023                 cairo_stroke (cr);
1024
1025                 cairo_restore (cr);
1026
1027         }
1028
1029         g_free (origins);
1030
1031         gl_debug (DEBUG_MINI_PREVIEW, "END");
1032 }
1033
1034
1035 /*--------------------------------------------------------------------------*/
1036 /* Draw arrow to indicate top of labels.                                    */
1037 /*--------------------------------------------------------------------------*/
1038 static void
1039 draw_arrow  (glMiniPreview      *this,
1040              cairo_t            *cr)
1041 {
1042         lglTemplateFrame  *frame;
1043         lglTemplateOrigin *origins;
1044         gdouble            width, height, min;
1045         gdouble            x0, y0;
1046         GtkStyle          *style;
1047         guint              base_color, arrow_color;
1048
1049         PangoLayout          *layout;
1050         PangoFontDescription *desc;
1051         PangoRectangle        rect;
1052
1053         /* Translators: "Up" refers to the direction towards the top of a label. */
1054         const gchar          *up = _("Up");
1055
1056         frame = (lglTemplateFrame *)this->priv->template->frames->data;
1057
1058         lgl_template_frame_get_size (frame, &width, &height);
1059
1060         if ( width != height )
1061         {
1062
1063                 origins = lgl_template_frame_get_origins (frame);
1064                 x0 = origins[0].x;
1065                 y0 = origins[0].y;
1066                 min = MIN (width, height);
1067                 g_free (origins);
1068
1069                 cairo_save (cr);
1070
1071                 style       = gtk_widget_get_style (GTK_WIDGET(this));
1072                 base_color  = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
1073                 arrow_color = gl_color_set_opacity (base_color, 0.25);
1074                 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (arrow_color));
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, -G_PI/2.0);
1081                 }
1082
1083                 cairo_new_path (cr);
1084                 cairo_move_to (cr, 0, -min*ARROW_SCALE/3);
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
1094                 cairo_stroke (cr);
1095
1096                 layout = pango_cairo_create_layout (cr);
1097
1098                 desc = pango_font_description_new ();
1099                 pango_font_description_set_family (desc, UP_FONT_FAMILY);
1100                 pango_font_description_set_weight (desc, PANGO_WEIGHT_BOLD);
1101                 pango_font_description_set_size (desc, min*UP_SCALE*PANGO_SCALE);
1102                 pango_layout_set_font_description (layout, desc);
1103                 pango_font_description_free (desc);
1104
1105                 pango_layout_set_text (layout, up, -1);
1106                 pango_layout_set_width (layout, -1);
1107                 pango_layout_get_pixel_extents (layout, NULL, &rect);
1108
1109                 cairo_move_to (cr, -rect.width/2, -min/4+rect.height/2);
1110
1111                 cairo_scale (cr, 1, -1);
1112                 pango_cairo_show_layout (cr, layout);
1113
1114                 cairo_restore (cr);
1115
1116         }
1117 }
1118
1119
1120 /*--------------------------------------------------------------------------*/
1121 /* Draw rich preview using print renderers.                                 */
1122 /*--------------------------------------------------------------------------*/
1123 static void
1124 draw_rich_preview (glMiniPreview          *this,
1125                    cairo_t                *cr)
1126 {
1127         glMerge      *merge;
1128         glPrintState  state;
1129
1130         merge = gl_label_get_merge (this->priv->label);
1131
1132         if (!merge)
1133         {
1134                 gl_print_simple_sheet (this->priv->label,
1135                                        cr,
1136                                        this->priv->page,
1137                                        this->priv->n_sheets,
1138                                        this->priv->first,
1139                                        this->priv->last,
1140                                        this->priv->outline_flag,
1141                                        this->priv->reverse_flag,
1142                                        this->priv->crop_marks_flag);
1143         }
1144         else
1145         {
1146                 /* FIXME: maybe the renderers should be more self contained.
1147                  *        This will only work for the first page, since
1148                  *        previous pages must be rendered to establish
1149                  *        state.
1150                  */
1151                 state.i_copy = 0;
1152                 state.p_record = (GList *)gl_merge_get_record_list (merge);
1153
1154                 if (this->priv->collate_flag)
1155                 {
1156                         gl_print_collated_merge_sheet (this->priv->label,
1157                                                        cr,
1158                                                        this->priv->page,
1159                                                        this->priv->n_copies,
1160                                                        this->priv->first,
1161                                                        this->priv->outline_flag,
1162                                                        this->priv->reverse_flag,
1163                                                        this->priv->crop_marks_flag,
1164                                                        &state);
1165                 }
1166                 else
1167                 {
1168                         gl_print_uncollated_merge_sheet (this->priv->label,
1169                                                          cr,
1170                                                          this->priv->page,
1171                                                          this->priv->n_copies,
1172                                                          this->priv->first,
1173                                                          this->priv->outline_flag,
1174                                                          this->priv->reverse_flag,
1175                                                          this->priv->crop_marks_flag,
1176                                                          &state);
1177                 }
1178         }
1179 }
1180
1181
1182
1183 /*
1184  * Local Variables:       -- emacs
1185  * mode: C                -- emacs
1186  * c-basic-offset: 8      -- emacs
1187  * tab-width: 8           -- emacs
1188  * indent-tabs-mode: nil  -- emacs
1189  * End:                   -- emacs
1190  */