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_FLAGS (this->priv->canvas, GTK_NO_WINDOW);
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;
561 gdouble offset_x, offset_y;
563 /* Establish scale and origin. */
564 w = GTK_WIDGET (this)->allocation.width;
565 h = GTK_WIDGET (this)->allocation.height;
567 /* establish scale. */
568 scale = MIN( (w - 2*MARGIN - 2*SHADOW_OFFSET)/template->page_width,
569 (h - 2*MARGIN - 2*SHADOW_OFFSET)/template->page_height );
571 /* Find offset to center preview. */
572 offset_x = (w/scale - template->page_width) / 2.0;
573 offset_y = (h/scale - template->page_height) / 2.0;
575 /* Set transformation. */
576 cairo_scale (cr, scale, scale);
577 cairo_translate (cr, offset_x, offset_y);
583 /*--------------------------------------------------------------------------*/
584 /* Button press event handler */
585 /*--------------------------------------------------------------------------*/
587 button_press_event_cb (GtkWidget *widget,
588 GdkEventButton *event)
590 glMiniPreview *this = GL_MINI_PREVIEW (widget);
596 gl_debug (DEBUG_MINI_PREVIEW, "START");
598 if ( event->button == 1 )
600 cr = gdk_cairo_create (GTK_WIDGET (this->priv->canvas)->window);
602 scale = set_transform_and_get_scale (this, cr);
606 cairo_device_to_user (cr, &x, &y);
608 i = find_closest_label (this, x, y);
610 g_signal_emit (G_OBJECT(this),
611 mini_preview_signals[CLICKED],
614 this->priv->first_i = i;
615 this->priv->last_i = i;
616 g_signal_emit (G_OBJECT(this),
617 mini_preview_signals[PRESSED],
618 0, this->priv->first_i, this->priv->last_i);
620 this->priv->dragging = TRUE;
621 this->priv->prev_i = i;
626 gl_debug (DEBUG_MINI_PREVIEW, "END");
631 /*--------------------------------------------------------------------------*/
632 /* Motion notify event handler */
633 /*--------------------------------------------------------------------------*/
635 motion_notify_event_cb (GtkWidget *widget,
636 GdkEventMotion *event)
638 glMiniPreview *this = GL_MINI_PREVIEW (widget);
644 gl_debug (DEBUG_MINI_PREVIEW, "START");
646 if (this->priv->dragging)
648 cr = gdk_cairo_create (GTK_WIDGET (this->priv->canvas)->window);
650 scale = set_transform_and_get_scale (this, cr);
654 cairo_device_to_user (cr, &x, &y);
656 i = find_closest_label (this, x, y);
658 if ( i != this->priv->prev_i )
660 this->priv->last_i = i;
662 g_signal_emit (G_OBJECT(this),
663 mini_preview_signals[PRESSED],
665 MIN (this->priv->first_i, this->priv->last_i),
666 MAX (this->priv->first_i, this->priv->last_i));
668 this->priv->prev_i = i;
673 gl_debug (DEBUG_MINI_PREVIEW, "END");
678 /*--------------------------------------------------------------------------*/
679 /* Button release event handler */
680 /*--------------------------------------------------------------------------*/
682 button_release_event_cb (GtkWidget *widget,
683 GdkEventButton *event)
685 glMiniPreview *this = GL_MINI_PREVIEW (widget);
687 gl_debug (DEBUG_MINI_PREVIEW, "START");
689 if ( event->button == 1 )
691 this->priv->dragging = FALSE;
695 g_signal_emit (G_OBJECT(this),
696 mini_preview_signals[RELEASED],
697 0, this->priv->first_i, this->priv->last_i);
699 gl_debug (DEBUG_MINI_PREVIEW, "END");
704 /*--------------------------------------------------------------------------*/
705 /* Find index+1 of label closest to given coordinates. */
706 /*--------------------------------------------------------------------------*/
708 find_closest_label (glMiniPreview *this,
714 gdouble dx, dy, d2, min_d2;
716 dx = x - this->priv->centers[0].x;
717 dy = y - this->priv->centers[0].y;
718 min_d2 = dx*dx + dy*dy;
721 for ( i=1; i<this->priv->labels_per_sheet; i++ )
723 dx = x - this->priv->centers[i].x;
724 dy = y - this->priv->centers[i].y;
738 /*--------------------------------------------------------------------------*/
739 /* Expose event handler. */
740 /*--------------------------------------------------------------------------*/
742 expose_event_cb (GtkWidget *widget,
743 GdkEventExpose *event,
748 gl_debug (DEBUG_MINI_PREVIEW, "START");
750 this->priv->update_scheduled_flag = FALSE;
752 cr = gdk_cairo_create (widget->window);
755 event->area.x, event->area.y,
756 event->area.width, event->area.height);
758 cairo_translate (cr, widget->allocation.x, widget->allocation.y);
764 gl_debug (DEBUG_MINI_PREVIEW, "END");
769 /*--------------------------------------------------------------------------*/
770 /* Style set handler (updates colors when style/theme changes). */
771 /*--------------------------------------------------------------------------*/
773 style_set_cb (GtkWidget *widget,
774 GtkStyle *previous_style,
777 gl_debug (DEBUG_MINI_PREVIEW, "START");
781 gl_debug (DEBUG_MINI_PREVIEW, "END");
785 /*--------------------------------------------------------------------------*/
787 /*--------------------------------------------------------------------------*/
789 redraw (glMiniPreview *this)
793 gl_debug (DEBUG_MINI_PREVIEW, "START");
795 if (GTK_WIDGET (this->priv->canvas)->window)
798 if ( !this->priv->update_scheduled_flag )
800 this->priv->update_scheduled_flag = TRUE;
802 region = gdk_drawable_get_clip_region (GTK_WIDGET (this->priv->canvas)->window);
803 gdk_window_invalidate_region (GTK_WIDGET (this->priv->canvas)->window, region, TRUE);
804 gdk_region_destroy (region);
808 gl_debug (DEBUG_MINI_PREVIEW, "END");
812 /*--------------------------------------------------------------------------*/
813 /* Draw mini preview. */
814 /*--------------------------------------------------------------------------*/
816 draw (glMiniPreview *this,
819 lglTemplate *template = this->priv->template;
821 gdouble shadow_x, shadow_y;
824 gl_debug (DEBUG_MINI_PREVIEW, "START");
829 scale = set_transform_and_get_scale (this, cr);
832 shadow_x = SHADOW_OFFSET/scale;
833 shadow_y = SHADOW_OFFSET/scale;
835 draw_shadow (this, cr,
837 template->page_width, template->page_height);
839 draw_paper (this, cr,
840 template->page_width, template->page_height,
843 draw_labels (this, cr, template, 1.0/scale);
845 if (this->priv->label)
847 draw_rich_preview (this, cr);
852 gl_debug (DEBUG_MINI_PREVIEW, "END");
857 /*--------------------------------------------------------------------------*/
858 /* Draw page shadow */
859 /*--------------------------------------------------------------------------*/
861 draw_shadow (glMiniPreview *this,
871 gl_debug (DEBUG_MINI_PREVIEW, "START");
875 cairo_rectangle (cr, x, y, width, height);
877 style = gtk_widget_get_style (GTK_WIDGET(this));
878 shadow_color = gl_color_from_gdk_color (&style->dark[GTK_STATE_NORMAL]);
879 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (shadow_color));
885 gl_debug (DEBUG_MINI_PREVIEW, "END");
889 /*--------------------------------------------------------------------------*/
891 /*--------------------------------------------------------------------------*/
893 draw_paper (glMiniPreview *this,
900 guint paper_color, outline_color;
902 gl_debug (DEBUG_MINI_PREVIEW, "START");
906 style = gtk_widget_get_style (GTK_WIDGET(this));
907 paper_color = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
908 outline_color = gl_color_from_gdk_color (&style->fg[GTK_STATE_NORMAL]);
910 cairo_rectangle (cr, 0.0, 0.0, width, height);
912 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (paper_color));
913 cairo_fill_preserve (cr);
915 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (outline_color));
916 cairo_set_line_width (cr, line_width);
921 gl_debug (DEBUG_MINI_PREVIEW, "END");
925 /*--------------------------------------------------------------------------*/
927 /*--------------------------------------------------------------------------*/
929 draw_labels (glMiniPreview *this,
931 lglTemplate *template,
934 const lglTemplateFrame *frame;
936 lglTemplateOrigin *origins;
939 guint highlight_color, paper_color, outline_color;
941 gl_debug (DEBUG_MINI_PREVIEW, "START");
943 frame = (lglTemplateFrame *)template->frames->data;
945 n_labels = lgl_template_frame_get_n_labels (frame);
946 origins = lgl_template_frame_get_origins (frame);
948 style = gtk_widget_get_style (GTK_WIDGET(this));
949 base_color = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
951 paper_color = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
952 highlight_color = gl_color_set_opacity (base_color, 0.10);
953 if (this->priv->label)
955 /* Outlines are more subtle when doing a rich preview. */
956 outline_color = gl_color_set_opacity (base_color, 0.05);
960 outline_color = gl_color_set_opacity (base_color, 1.00);
963 for ( i=0; i < n_labels; i++ ) {
967 cairo_translate (cr, origins[i].x, origins[i].y);
968 gl_cairo_label_path (cr, template, FALSE, FALSE);
970 if ( ((i+1) >= this->priv->highlight_first) &&
971 ((i+1) <= this->priv->highlight_last) )
973 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (highlight_color));
974 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
975 cairo_fill_preserve (cr);
978 cairo_set_line_width (cr, line_width);
979 cairo_set_source_rgba (cr, GL_COLOR_RGBA_ARGS (outline_color));
988 gl_debug (DEBUG_MINI_PREVIEW, "END");
992 /*--------------------------------------------------------------------------*/
993 /* Draw rich preview using print renderers. */
994 /*--------------------------------------------------------------------------*/
996 draw_rich_preview (glMiniPreview *this,
1002 merge = gl_label_get_merge (this->priv->label);
1006 gl_print_simple_sheet (this->priv->label,
1009 this->priv->n_sheets,
1012 this->priv->outline_flag,
1013 this->priv->reverse_flag,
1014 this->priv->crop_marks_flag);
1018 /* FIXME: maybe the renderers should be more self contained.
1019 * This will only work for the first page, since
1020 * previous pages must be rendered to establish
1024 state.p_record = (GList *)gl_merge_get_record_list (merge);
1026 if (this->priv->collate_flag)
1028 gl_print_collated_merge_sheet (this->priv->label,
1031 this->priv->n_copies,
1033 this->priv->outline_flag,
1034 this->priv->reverse_flag,
1035 this->priv->crop_marks_flag,
1040 gl_print_uncollated_merge_sheet (this->priv->label,
1043 this->priv->n_copies,
1045 this->priv->outline_flag,
1046 this->priv->reverse_flag,
1047 this->priv->crop_marks_flag,
1056 * Local Variables: -- emacs
1058 * c-basic-offset: 8 -- emacs
1059 * tab-width: 8 -- emacs
1060 * indent-tabs-mode: nil -- emacs