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