3 * Copyright (C) 2001-2009 Jim Evins <evins@snaught.com>.
5 * This file is part of gLabels.
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.
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.
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/>.
23 #include "mini-preview.h"
27 #include "libglabels/db.h"
28 #include "cairo-label-path.h"
36 /*===========================================*/
37 /* Private macros and constants. */
38 /*===========================================*/
41 #define SHADOW_OFFSET 3
44 /*===========================================*/
46 /*===========================================*/
60 struct _glMiniPreviewPrivate {
64 lglTemplate *template;
65 gint labels_per_sheet;
76 gboolean update_scheduled_flag;
84 gboolean collate_flag;
85 gboolean outline_flag;
86 gboolean reverse_flag;
87 gboolean crop_marks_flag;
91 /*===========================================*/
93 /*===========================================*/
95 static gint mini_preview_signals[LAST_SIGNAL] = { 0 };
98 /*===========================================*/
99 /* Local function prototypes */
100 /*===========================================*/
102 static void gl_mini_preview_finalize (GObject *object);
104 static void gl_mini_preview_construct (glMiniPreview *this,
108 static gboolean button_press_event_cb (GtkWidget *widget,
109 GdkEventButton *event);
110 static gboolean motion_notify_event_cb (GtkWidget *widget,
111 GdkEventMotion *event);
112 static gboolean button_release_event_cb (GtkWidget *widget,
113 GdkEventButton *event);
116 static gboolean expose_event_cb (GtkWidget *widget,
117 GdkEventExpose *event,
118 glMiniPreview *this);
119 static void style_set_cb (GtkWidget *widget,
120 GtkStyle *previous_style,
121 glMiniPreview *this);
123 static void redraw (glMiniPreview *this);
124 static void draw (glMiniPreview *this,
127 static void draw_shadow (glMiniPreview *this,
133 static void draw_paper (glMiniPreview *this,
138 static void draw_labels (glMiniPreview *this,
140 lglTemplate *template,
142 static void draw_rich_preview (glMiniPreview *this,
146 static gint find_closest_label (glMiniPreview *this,
150 static gdouble set_transform_and_get_scale (glMiniPreview *this,
154 /****************************************************************************/
155 /* Object infrastructure. */
156 /****************************************************************************/
157 G_DEFINE_TYPE (glMiniPreview, gl_mini_preview, GTK_TYPE_EVENT_BOX);
160 /*****************************************************************************/
161 /* Class Init Function. */
162 /*****************************************************************************/
164 gl_mini_preview_class_init (glMiniPreviewClass *class)
166 GObjectClass *object_class = G_OBJECT_CLASS (class);
167 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
169 gl_debug (DEBUG_MINI_PREVIEW, "START");
171 gl_mini_preview_parent_class = g_type_class_peek_parent (class);
173 object_class->finalize = gl_mini_preview_finalize;
175 widget_class->button_press_event = button_press_event_cb;
176 widget_class->motion_notify_event = motion_notify_event_cb;
177 widget_class->button_release_event = button_release_event_cb;
179 mini_preview_signals[CLICKED] =
180 g_signal_new ("clicked",
181 G_OBJECT_CLASS_TYPE(object_class),
183 G_STRUCT_OFFSET (glMiniPreviewClass, clicked),
185 gl_marshal_VOID__INT,
186 G_TYPE_NONE, 1, G_TYPE_INT);
188 mini_preview_signals[PRESSED] =
189 g_signal_new ("pressed",
190 G_OBJECT_CLASS_TYPE(object_class),
192 G_STRUCT_OFFSET (glMiniPreviewClass, pressed),
194 gl_marshal_VOID__INT_INT,
195 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
197 mini_preview_signals[RELEASED] =
198 g_signal_new ("released",
199 G_OBJECT_CLASS_TYPE(object_class),
201 G_STRUCT_OFFSET (glMiniPreviewClass, released),
203 gl_marshal_VOID__INT_INT,
204 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
206 gl_debug (DEBUG_MINI_PREVIEW, "END");
210 /*****************************************************************************/
211 /* Object Instance Init Function. */
212 /*****************************************************************************/
214 gl_mini_preview_init (glMiniPreview *this)
216 gl_debug (DEBUG_MINI_PREVIEW, "START");
218 this->priv = g_new0 (glMiniPreviewPrivate, 1);
220 gtk_widget_add_events (GTK_WIDGET (this),
221 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
222 GDK_POINTER_MOTION_MASK);
224 gtk_event_box_set_visible_window (GTK_EVENT_BOX (this), FALSE);
226 this->priv->canvas = gtk_drawing_area_new ();
227 gtk_widget_set_has_window(this->priv->canvas, FALSE);
228 gtk_container_add (GTK_CONTAINER (this), this->priv->canvas);
230 g_signal_connect (G_OBJECT (this->priv->canvas), "expose-event",
231 G_CALLBACK (expose_event_cb), this);
232 g_signal_connect (G_OBJECT (this->priv->canvas), "style-set",
233 G_CALLBACK (style_set_cb), this);
235 gl_debug (DEBUG_MINI_PREVIEW, "END");
239 /*****************************************************************************/
240 /* Finalize Method. */
241 /*****************************************************************************/
243 gl_mini_preview_finalize (GObject *object)
245 glMiniPreview *this = GL_MINI_PREVIEW (object);
247 gl_debug (DEBUG_MINI_PREVIEW, "START");
249 g_return_if_fail (object != NULL);
250 g_return_if_fail (GL_IS_MINI_PREVIEW (object));
252 if (this->priv->label)
254 g_object_unref (this->priv->label);
256 lgl_template_free (this->priv->template);
257 g_free (this->priv->centers);
260 G_OBJECT_CLASS (gl_mini_preview_parent_class)->finalize (object);
262 gl_debug (DEBUG_MINI_PREVIEW, "END");
266 /*****************************************************************************/
267 /** New Object Generator. */
268 /*****************************************************************************/
270 gl_mini_preview_new (gint height,
275 gl_debug (DEBUG_MINI_PREVIEW, "START");
277 this = g_object_new (gl_mini_preview_get_type (), NULL);
279 gl_mini_preview_construct (this, height, width);
281 gl_debug (DEBUG_MINI_PREVIEW, "END");
283 return GTK_WIDGET (this);
287 /*--------------------------------------------------------------------------*/
288 /* Construct composite widget. */
289 /*--------------------------------------------------------------------------*/
291 gl_mini_preview_construct (glMiniPreview *this,
295 gl_debug (DEBUG_MINI_PREVIEW, "START");
297 gtk_widget_set_size_request (GTK_WIDGET (this->priv->canvas), width, height);
299 gl_debug (DEBUG_MINI_PREVIEW, "END");
303 /****************************************************************************/
304 /* Set template for mini-preview to determine geometry. */
305 /****************************************************************************/
307 gl_mini_preview_set_by_name (glMiniPreview *this,
310 lglTemplate *template;
312 gl_debug (DEBUG_MINI_PREVIEW, "START");
315 template = lgl_db_lookup_template_from_name (name);
317 gl_mini_preview_set_template (this, template);
319 lgl_template_free (template);
321 gl_debug (DEBUG_MINI_PREVIEW, "END");
325 /****************************************************************************/
326 /* Set template for mini-preview to determine geometry. */
327 /****************************************************************************/
329 gl_mini_preview_set_template (glMiniPreview *this,
330 const lglTemplate *template)
332 const lglTemplateFrame *frame;
333 lglTemplateOrigin *origins;
337 gl_debug (DEBUG_MINI_PREVIEW, "START");
339 frame = (lglTemplateFrame *)template->frames->data;
344 lgl_template_free (this->priv->template);
345 this->priv->template = lgl_template_dup (template);
348 * Set labels per sheet
350 this->priv->labels_per_sheet = lgl_template_frame_get_n_labels (frame);
355 g_free (this->priv->centers);
356 this->priv->centers = g_new0 (LabelCenter, this->priv->labels_per_sheet);
357 origins = lgl_template_frame_get_origins (frame);
358 lgl_template_frame_get_size (frame, &w, &h);
359 for ( i=0; i<this->priv->labels_per_sheet; i++ )
361 this->priv->centers[i].x = origins[i].x + w/2.0;
362 this->priv->centers[i].y = origins[i].y + h/2.0;
367 * Redraw modified preview
371 gl_debug (DEBUG_MINI_PREVIEW, "END");
375 /****************************************************************************/
376 /* Highlight given label outlines. */
377 /****************************************************************************/
379 gl_mini_preview_highlight_range (glMiniPreview *this,
383 gl_debug (DEBUG_MINI_PREVIEW, "START");
385 if ( (first_label != this->priv->highlight_first) ||
386 (last_label != this->priv->highlight_last) )
389 this->priv->highlight_first = first_label;
390 this->priv->highlight_last = last_label;
396 gl_debug (DEBUG_MINI_PREVIEW, "END");
400 /****************************************************************************/
402 /****************************************************************************/
404 gl_mini_preview_set_label (glMiniPreview *this,
407 if ( this->priv->label )
409 g_object_unref (this->priv->label);
411 this->priv->label = g_object_ref (label);
416 /****************************************************************************/
417 /* Set page number. */
418 /****************************************************************************/
420 gl_mini_preview_set_page (glMiniPreview *this,
423 if ( page != this->priv->page )
425 this->priv->page = page;
431 /****************************************************************************/
432 /* Set number of sheets. */
433 /****************************************************************************/
435 gl_mini_preview_set_n_sheets (glMiniPreview *this,
438 if ( n_sheets != this->priv->n_sheets )
440 this->priv->n_sheets = n_sheets;
446 /****************************************************************************/
447 /* Set number of copies (merge only). */
448 /****************************************************************************/
450 gl_mini_preview_set_n_copies (glMiniPreview *this,
453 if ( n_copies != this->priv->n_copies )
455 this->priv->n_copies = n_copies;
461 /****************************************************************************/
462 /* Set first label number on first sheet. */
463 /****************************************************************************/
465 gl_mini_preview_set_first (glMiniPreview *this,
468 if ( first != this->priv->first )
470 this->priv->first = first;
476 /****************************************************************************/
477 /* Set last label number on first sheet (non-merge only). */
478 /****************************************************************************/
480 gl_mini_preview_set_last (glMiniPreview *this,
483 if ( last != this->priv->last )
485 this->priv->last = last;
491 /****************************************************************************/
492 /* Set collate flag (merge only). */
493 /****************************************************************************/
495 gl_mini_preview_set_collate_flag (glMiniPreview *this,
496 gboolean collate_flag)
498 if ( collate_flag != this->priv->collate_flag )
500 this->priv->collate_flag = collate_flag;
506 /****************************************************************************/
507 /* Set outline flag. */
508 /****************************************************************************/
510 gl_mini_preview_set_outline_flag (glMiniPreview *this,
511 gboolean outline_flag)
513 if ( outline_flag != this->priv->outline_flag )
515 this->priv->outline_flag = outline_flag;
521 /****************************************************************************/
522 /* Set reverse flag. */
523 /****************************************************************************/
525 gl_mini_preview_set_reverse_flag (glMiniPreview *this,
526 gboolean reverse_flag)
528 if ( reverse_flag != this->priv->reverse_flag )
530 this->priv->reverse_flag = reverse_flag;
536 /****************************************************************************/
537 /* Set crop marks flag. */
538 /****************************************************************************/
540 gl_mini_preview_set_crop_marks_flag (glMiniPreview *this,
541 gboolean crop_marks_flag)
543 if ( crop_marks_flag != this->priv->crop_marks_flag )
545 this->priv->crop_marks_flag = crop_marks_flag;
551 /*--------------------------------------------------------------------------*/
552 /* Set transformation and return scale. */
553 /*--------------------------------------------------------------------------*/
555 set_transform_and_get_scale (glMiniPreview *this,
558 lglTemplate *template = this->priv->template;
559 GtkAllocation allocation;
562 gdouble offset_x, offset_y;
564 /* Establish scale and origin. */
565 gtk_widget_get_allocation (GTK_WIDGET (this), &allocation);
566 w = allocation.width;
567 h = allocation.height;
569 /* establish scale. */
570 scale = MIN( (w - 2*MARGIN - 2*SHADOW_OFFSET)/template->page_width,
571 (h - 2*MARGIN - 2*SHADOW_OFFSET)/template->page_height );
573 /* Find offset to center preview. */
574 offset_x = (w/scale - template->page_width) / 2.0;
575 offset_y = (h/scale - template->page_height) / 2.0;
577 /* Set transformation. */
578 cairo_scale (cr, scale, scale);
579 cairo_translate (cr, offset_x, offset_y);
585 /*--------------------------------------------------------------------------*/
586 /* Button press event handler */
587 /*--------------------------------------------------------------------------*/
589 button_press_event_cb (GtkWidget *widget,
590 GdkEventButton *event)
592 glMiniPreview *this = GL_MINI_PREVIEW (widget);
599 gl_debug (DEBUG_MINI_PREVIEW, "START");
601 if ( event->button == 1 )
604 window = gtk_widget_get_window (this->priv->canvas);
606 cr = gdk_cairo_create (window);
608 scale = set_transform_and_get_scale (this, cr);
612 cairo_device_to_user (cr, &x, &y);
614 i = find_closest_label (this, x, y);
616 g_signal_emit (G_OBJECT(this),
617 mini_preview_signals[CLICKED],
620 this->priv->first_i = i;
621 this->priv->last_i = i;
622 g_signal_emit (G_OBJECT(this),
623 mini_preview_signals[PRESSED],
624 0, this->priv->first_i, this->priv->last_i);
626 this->priv->dragging = TRUE;
627 this->priv->prev_i = i;
632 gl_debug (DEBUG_MINI_PREVIEW, "END");
637 /*--------------------------------------------------------------------------*/
638 /* Motion notify event handler */
639 /*--------------------------------------------------------------------------*/
641 motion_notify_event_cb (GtkWidget *widget,
642 GdkEventMotion *event)
644 glMiniPreview *this = GL_MINI_PREVIEW (widget);
651 gl_debug (DEBUG_MINI_PREVIEW, "START");
653 if (this->priv->dragging)
655 window = gtk_widget_get_window (this->priv->canvas);
657 cr = gdk_cairo_create (window);
659 scale = set_transform_and_get_scale (this, cr);
663 cairo_device_to_user (cr, &x, &y);
665 i = find_closest_label (this, x, y);
667 if ( i != this->priv->prev_i )
669 this->priv->last_i = i;
671 g_signal_emit (G_OBJECT(this),
672 mini_preview_signals[PRESSED],
674 MIN (this->priv->first_i, this->priv->last_i),
675 MAX (this->priv->first_i, this->priv->last_i));
677 this->priv->prev_i = i;
682 gl_debug (DEBUG_MINI_PREVIEW, "END");
687 /*--------------------------------------------------------------------------*/
688 /* Button release event handler */
689 /*--------------------------------------------------------------------------*/
691 button_release_event_cb (GtkWidget *widget,
692 GdkEventButton *event)
694 glMiniPreview *this = GL_MINI_PREVIEW (widget);
696 gl_debug (DEBUG_MINI_PREVIEW, "START");
698 if ( event->button == 1 )
700 this->priv->dragging = FALSE;
704 g_signal_emit (G_OBJECT(this),
705 mini_preview_signals[RELEASED],
706 0, this->priv->first_i, this->priv->last_i);
708 gl_debug (DEBUG_MINI_PREVIEW, "END");
713 /*--------------------------------------------------------------------------*/
714 /* Find index+1 of label closest to given coordinates. */
715 /*--------------------------------------------------------------------------*/
717 find_closest_label (glMiniPreview *this,
723 gdouble dx, dy, d2, min_d2;
725 dx = x - this->priv->centers[0].x;
726 dy = y - this->priv->centers[0].y;
727 min_d2 = dx*dx + dy*dy;
730 for ( i=1; i<this->priv->labels_per_sheet; i++ )
732 dx = x - this->priv->centers[i].x;
733 dy = y - this->priv->centers[i].y;
747 /*--------------------------------------------------------------------------*/
748 /* Expose event handler. */
749 /*--------------------------------------------------------------------------*/
751 expose_event_cb (GtkWidget *widget,
752 GdkEventExpose *event,
757 GtkAllocation allocation;
759 gl_debug (DEBUG_MINI_PREVIEW, "START");
761 this->priv->update_scheduled_flag = FALSE;
763 window = gtk_widget_get_window (widget);
765 cr = gdk_cairo_create (window);
768 event->area.x, event->area.y,
769 event->area.width, event->area.height);
772 gtk_widget_get_allocation (widget, &allocation);
773 cairo_translate (cr, allocation.x, allocation.y);
779 gl_debug (DEBUG_MINI_PREVIEW, "END");
784 /*--------------------------------------------------------------------------*/
785 /* Style set handler (updates colors when style/theme changes). */
786 /*--------------------------------------------------------------------------*/
788 style_set_cb (GtkWidget *widget,
789 GtkStyle *previous_style,
792 gl_debug (DEBUG_MINI_PREVIEW, "START");
796 gl_debug (DEBUG_MINI_PREVIEW, "END");
800 /*--------------------------------------------------------------------------*/
802 /*--------------------------------------------------------------------------*/
804 redraw (glMiniPreview *this)
809 gl_debug (DEBUG_MINI_PREVIEW, "START");
811 window = gtk_widget_get_window (this->priv->canvas);
816 if ( !this->priv->update_scheduled_flag )
818 this->priv->update_scheduled_flag = TRUE;
820 region = gdk_drawable_get_clip_region (window);
821 gdk_window_invalidate_region (window, region, TRUE);
822 gdk_region_destroy (region);
826 gl_debug (DEBUG_MINI_PREVIEW, "END");
830 /*--------------------------------------------------------------------------*/
831 /* Draw mini preview. */
832 /*--------------------------------------------------------------------------*/
834 draw (glMiniPreview *this,
837 lglTemplate *template = this->priv->template;
839 gdouble shadow_x, shadow_y;
842 gl_debug (DEBUG_MINI_PREVIEW, "START");
847 scale = set_transform_and_get_scale (this, cr);
850 shadow_x = SHADOW_OFFSET/scale;
851 shadow_y = SHADOW_OFFSET/scale;
853 draw_shadow (this, cr,
855 template->page_width, template->page_height);
857 draw_paper (this, cr,
858 template->page_width, template->page_height,
861 draw_labels (this, cr, template, 1.0/scale);
863 if (this->priv->label)
865 draw_rich_preview (this, cr);
870 gl_debug (DEBUG_MINI_PREVIEW, "END");
875 /*--------------------------------------------------------------------------*/
876 /* Draw page shadow */
877 /*--------------------------------------------------------------------------*/
879 draw_shadow (glMiniPreview *this,
889 gl_debug (DEBUG_MINI_PREVIEW, "START");
893 cairo_rectangle (cr, x, y, width, height);
895 style = gtk_widget_get_style (GTK_WIDGET(this));
896 shadow_color = gl_color_from_gdk_color (&style->dark[GTK_STATE_NORMAL]);
897 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (shadow_color));
903 gl_debug (DEBUG_MINI_PREVIEW, "END");
907 /*--------------------------------------------------------------------------*/
909 /*--------------------------------------------------------------------------*/
911 draw_paper (glMiniPreview *this,
918 guint paper_color, outline_color;
920 gl_debug (DEBUG_MINI_PREVIEW, "START");
924 style = gtk_widget_get_style (GTK_WIDGET(this));
925 paper_color = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
926 outline_color = gl_color_from_gdk_color (&style->fg[GTK_STATE_NORMAL]);
928 cairo_rectangle (cr, 0.0, 0.0, width, height);
930 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (paper_color));
931 cairo_fill_preserve (cr);
933 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (outline_color));
934 cairo_set_line_width (cr, line_width);
939 gl_debug (DEBUG_MINI_PREVIEW, "END");
943 /*--------------------------------------------------------------------------*/
945 /*--------------------------------------------------------------------------*/
947 draw_labels (glMiniPreview *this,
949 lglTemplate *template,
952 const lglTemplateFrame *frame;
954 lglTemplateOrigin *origins;
957 guint highlight_color, paper_color, outline_color;
959 gl_debug (DEBUG_MINI_PREVIEW, "START");
961 frame = (lglTemplateFrame *)template->frames->data;
963 n_labels = lgl_template_frame_get_n_labels (frame);
964 origins = lgl_template_frame_get_origins (frame);
966 style = gtk_widget_get_style (GTK_WIDGET(this));
967 base_color = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
969 paper_color = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
970 highlight_color = gl_color_set_opacity (base_color, 0.10);
971 if (this->priv->label)
973 /* Outlines are more subtle when doing a rich preview. */
974 outline_color = gl_color_set_opacity (base_color, 0.05);
978 outline_color = gl_color_set_opacity (base_color, 1.00);
981 for ( i=0; i < n_labels; i++ ) {
985 cairo_translate (cr, origins[i].x, origins[i].y);
986 gl_cairo_label_path (cr, template, FALSE, FALSE);
988 if ( ((i+1) >= this->priv->highlight_first) &&
989 ((i+1) <= this->priv->highlight_last) )
991 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (highlight_color));
992 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
993 cairo_fill_preserve (cr);
996 cairo_set_line_width (cr, line_width);
997 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (outline_color));
1006 gl_debug (DEBUG_MINI_PREVIEW, "END");
1010 /*--------------------------------------------------------------------------*/
1011 /* Draw rich preview using print renderers. */
1012 /*--------------------------------------------------------------------------*/
1014 draw_rich_preview (glMiniPreview *this,
1020 merge = gl_label_get_merge (this->priv->label);
1024 gl_print_simple_sheet (this->priv->label,
1027 this->priv->n_sheets,
1030 this->priv->outline_flag,
1031 this->priv->reverse_flag,
1032 this->priv->crop_marks_flag);
1036 /* FIXME: maybe the renderers should be more self contained.
1037 * This will only work for the first page, since
1038 * previous pages must be rendered to establish
1042 state.p_record = (GList *)gl_merge_get_record_list (merge);
1044 if (this->priv->collate_flag)
1046 gl_print_collated_merge_sheet (this->priv->label,
1049 this->priv->n_copies,
1051 this->priv->outline_flag,
1052 this->priv->reverse_flag,
1053 this->priv->crop_marks_flag,
1058 gl_print_uncollated_merge_sheet (this->priv->label,
1061 this->priv->n_copies,
1063 this->priv->outline_flag,
1064 this->priv->reverse_flag,
1065 this->priv->crop_marks_flag,
1074 * Local Variables: -- emacs
1076 * c-basic-offset: 8 -- emacs
1077 * tab-width: 8 -- emacs
1078 * indent-tabs-mode: nil -- emacs