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"
35 /*===========================================*/
36 /* Private macros and constants. */
37 /*===========================================*/
40 #define SHADOW_OFFSET 3
43 /*===========================================*/
45 /*===========================================*/
58 struct _glMiniPreviewPrivate {
62 lglTemplate *template;
63 gint labels_per_sheet;
76 /*===========================================*/
78 /*===========================================*/
80 static gint mini_preview_signals[LAST_SIGNAL] = { 0 };
83 /*===========================================*/
84 /* Local function prototypes */
85 /*===========================================*/
87 static void gl_mini_preview_finalize (GObject *object);
89 static void gl_mini_preview_construct (glMiniPreview *this,
93 static gboolean button_press_event_cb (GtkWidget *widget,
94 GdkEventButton *event);
95 static gboolean motion_notify_event_cb (GtkWidget *widget,
96 GdkEventMotion *event);
97 static gboolean button_release_event_cb (GtkWidget *widget,
98 GdkEventButton *event);
101 static gboolean expose_event_cb (GtkWidget *widget,
102 GdkEventExpose *event,
103 glMiniPreview *this);
104 static void style_set_cb (GtkWidget *widget,
105 GtkStyle *previous_style,
106 glMiniPreview *this);
108 static void redraw (glMiniPreview *this);
109 static void draw (glMiniPreview *this,
112 static void draw_shadow (glMiniPreview *this,
118 static void draw_paper (glMiniPreview *this,
123 static void draw_labels (glMiniPreview *this,
125 lglTemplate *template,
128 static gint find_closest_label (glMiniPreview *this,
132 static gdouble set_transform_and_get_scale (glMiniPreview *this,
136 /****************************************************************************/
137 /* Object infrastructure. */
138 /****************************************************************************/
139 G_DEFINE_TYPE (glMiniPreview, gl_mini_preview, GTK_TYPE_EVENT_BOX);
142 /*****************************************************************************/
143 /* Class Init Function. */
144 /*****************************************************************************/
146 gl_mini_preview_class_init (glMiniPreviewClass *class)
148 GObjectClass *object_class = G_OBJECT_CLASS (class);
149 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
151 gl_debug (DEBUG_MINI_PREVIEW, "START");
153 gl_mini_preview_parent_class = g_type_class_peek_parent (class);
155 object_class->finalize = gl_mini_preview_finalize;
157 widget_class->button_press_event = button_press_event_cb;
158 widget_class->motion_notify_event = motion_notify_event_cb;
159 widget_class->button_release_event = button_release_event_cb;
161 mini_preview_signals[CLICKED] =
162 g_signal_new ("clicked",
163 G_OBJECT_CLASS_TYPE(object_class),
165 G_STRUCT_OFFSET (glMiniPreviewClass, clicked),
167 gl_marshal_VOID__INT,
168 G_TYPE_NONE, 1, G_TYPE_INT);
170 mini_preview_signals[PRESSED] =
171 g_signal_new ("pressed",
172 G_OBJECT_CLASS_TYPE(object_class),
174 G_STRUCT_OFFSET (glMiniPreviewClass, pressed),
176 gl_marshal_VOID__INT_INT,
177 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
179 gl_debug (DEBUG_MINI_PREVIEW, "END");
183 /*****************************************************************************/
184 /* Object Instance Init Function. */
185 /*****************************************************************************/
187 gl_mini_preview_init (glMiniPreview *this)
189 gl_debug (DEBUG_MINI_PREVIEW, "START");
191 this->priv = g_new0 (glMiniPreviewPrivate, 1);
193 gtk_widget_add_events (GTK_WIDGET (this),
194 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
195 GDK_POINTER_MOTION_MASK);
197 gtk_event_box_set_visible_window (GTK_EVENT_BOX (this), FALSE);
199 this->priv->canvas = gtk_drawing_area_new ();
200 GTK_WIDGET_SET_FLAGS (this->priv->canvas, GTK_NO_WINDOW);
201 gtk_container_add (GTK_CONTAINER (this), this->priv->canvas);
203 g_signal_connect (G_OBJECT (this->priv->canvas), "expose-event",
204 G_CALLBACK (expose_event_cb), this);
205 g_signal_connect (G_OBJECT (this->priv->canvas), "style-set",
206 G_CALLBACK (style_set_cb), this);
208 gl_debug (DEBUG_MINI_PREVIEW, "END");
212 /*****************************************************************************/
213 /* Finalize Method. */
214 /*****************************************************************************/
216 gl_mini_preview_finalize (GObject *object)
218 glMiniPreview *this = GL_MINI_PREVIEW (object);
220 gl_debug (DEBUG_MINI_PREVIEW, "START");
222 g_return_if_fail (object != NULL);
223 g_return_if_fail (GL_IS_MINI_PREVIEW (object));
225 lgl_template_free (this->priv->template);
226 g_free (this->priv->centers);
229 G_OBJECT_CLASS (gl_mini_preview_parent_class)->finalize (object);
231 gl_debug (DEBUG_MINI_PREVIEW, "END");
235 /*****************************************************************************/
236 /** New Object Generator. */
237 /*****************************************************************************/
239 gl_mini_preview_new (gint height,
244 gl_debug (DEBUG_MINI_PREVIEW, "START");
246 this = g_object_new (gl_mini_preview_get_type (), NULL);
248 gl_mini_preview_construct (this, height, width);
250 gl_debug (DEBUG_MINI_PREVIEW, "END");
252 return GTK_WIDGET (this);
256 /*--------------------------------------------------------------------------*/
257 /* Construct composite widget. */
258 /*--------------------------------------------------------------------------*/
260 gl_mini_preview_construct (glMiniPreview *this,
264 gl_debug (DEBUG_MINI_PREVIEW, "START");
266 gtk_widget_set_size_request (GTK_WIDGET (this->priv->canvas), width, height);
268 gl_debug (DEBUG_MINI_PREVIEW, "END");
272 /****************************************************************************/
273 /* Set label for mini-preview to determine geometry. */
274 /****************************************************************************/
276 gl_mini_preview_set_label_by_name (glMiniPreview *this,
279 lglTemplate *template;
281 gl_debug (DEBUG_MINI_PREVIEW, "START");
284 template = lgl_db_lookup_template_from_name (name);
286 gl_mini_preview_set_template (this, template);
288 lgl_template_free (template);
290 gl_debug (DEBUG_MINI_PREVIEW, "END");
294 /****************************************************************************/
295 /* Set label for mini-preview to determine geometry. */
296 /****************************************************************************/
298 gl_mini_preview_set_template (glMiniPreview *this,
299 const lglTemplate *template)
301 const lglTemplateFrame *frame;
302 lglTemplateOrigin *origins;
306 gl_debug (DEBUG_MINI_PREVIEW, "START");
308 frame = (lglTemplateFrame *)template->frames->data;
313 lgl_template_free (this->priv->template);
314 this->priv->template = lgl_template_dup (template);
317 * Set labels per sheet
319 this->priv->labels_per_sheet = lgl_template_frame_get_n_labels (frame);
324 g_free (this->priv->centers);
325 this->priv->centers = g_new0 (LabelCenter, this->priv->labels_per_sheet);
326 origins = lgl_template_frame_get_origins (frame);
327 lgl_template_frame_get_size (frame, &w, &h);
328 for ( i=0; i<this->priv->labels_per_sheet; i++ )
330 this->priv->centers[i].x = origins[i].x + w/2.0;
331 this->priv->centers[i].y = origins[i].y + h/2.0;
336 * Redraw modified preview
340 gl_debug (DEBUG_MINI_PREVIEW, "END");
344 /****************************************************************************/
345 /* Highlight given label outlines. */
346 /****************************************************************************/
348 gl_mini_preview_highlight_range (glMiniPreview *this,
352 gl_debug (DEBUG_MINI_PREVIEW, "START");
354 this->priv->highlight_first = first_label;
355 this->priv->highlight_last = last_label;
359 gl_debug (DEBUG_MINI_PREVIEW, "END");
363 /*--------------------------------------------------------------------------*/
364 /* Set transformation and return scale. */
365 /*--------------------------------------------------------------------------*/
367 set_transform_and_get_scale (glMiniPreview *this,
370 lglTemplate *template = this->priv->template;
374 gdouble offset_x, offset_y;
376 /* Establish scale and origin. */
377 w = GTK_WIDGET (this)->allocation.width;
378 h = GTK_WIDGET (this)->allocation.height;
380 /* establish scale. */
381 scale = MIN( (w - 2*MARGIN - 2*SHADOW_OFFSET)/template->page_width,
382 (h - 2*MARGIN - 2*SHADOW_OFFSET)/template->page_height );
384 /* Find offset to center preview. */
385 offset_x = (w/scale - template->page_width) / 2.0;
386 offset_y = (h/scale - template->page_height) / 2.0;
388 /* Set transformation. */
389 cairo_scale (cr, scale, scale);
390 cairo_translate (cr, offset_x, offset_y);
396 /*--------------------------------------------------------------------------*/
397 /* Button press event handler */
398 /*--------------------------------------------------------------------------*/
400 button_press_event_cb (GtkWidget *widget,
401 GdkEventButton *event)
403 glMiniPreview *this = GL_MINI_PREVIEW (widget);
409 gl_debug (DEBUG_MINI_PREVIEW, "START");
411 if ( event->button == 1 )
413 cr = gdk_cairo_create (GTK_WIDGET (this->priv->canvas)->window);
415 scale = set_transform_and_get_scale (this, cr);
419 cairo_device_to_user (cr, &x, &y);
421 i = find_closest_label (this, x, y);
423 g_signal_emit (G_OBJECT(this),
424 mini_preview_signals[CLICKED],
427 this->priv->first_i = i;
428 this->priv->last_i = i;
429 g_signal_emit (G_OBJECT(this),
430 mini_preview_signals[PRESSED],
431 0, this->priv->first_i, this->priv->last_i);
433 this->priv->dragging = TRUE;
434 this->priv->prev_i = i;
439 gl_debug (DEBUG_MINI_PREVIEW, "END");
444 /*--------------------------------------------------------------------------*/
445 /* Motion notify event handler */
446 /*--------------------------------------------------------------------------*/
448 motion_notify_event_cb (GtkWidget *widget,
449 GdkEventMotion *event)
451 glMiniPreview *this = GL_MINI_PREVIEW (widget);
457 gl_debug (DEBUG_MINI_PREVIEW, "START");
459 if (this->priv->dragging)
461 cr = gdk_cairo_create (GTK_WIDGET (this->priv->canvas)->window);
463 scale = set_transform_and_get_scale (this, cr);
467 cairo_device_to_user (cr, &x, &y);
469 i = find_closest_label (this, x, y);
471 if ( i != this->priv->prev_i )
473 this->priv->last_i = i;
475 g_signal_emit (G_OBJECT(this),
476 mini_preview_signals[PRESSED],
478 MIN (this->priv->first_i, this->priv->last_i),
479 MAX (this->priv->first_i, this->priv->last_i));
481 this->priv->prev_i = i;
486 gl_debug (DEBUG_MINI_PREVIEW, "END");
491 /*--------------------------------------------------------------------------*/
492 /* Button release event handler */
493 /*--------------------------------------------------------------------------*/
495 button_release_event_cb (GtkWidget *widget,
496 GdkEventButton *event)
498 glMiniPreview *this = GL_MINI_PREVIEW (widget);
500 gl_debug (DEBUG_MINI_PREVIEW, "START");
502 if ( event->button == 1 )
504 this->priv->dragging = FALSE;
508 gl_debug (DEBUG_MINI_PREVIEW, "END");
513 /*--------------------------------------------------------------------------*/
514 /* Find index+1 of label closest to given coordinates. */
515 /*--------------------------------------------------------------------------*/
517 find_closest_label (glMiniPreview *this,
523 gdouble dx, dy, d2, min_d2;
525 dx = x - this->priv->centers[0].x;
526 dy = y - this->priv->centers[0].y;
527 min_d2 = dx*dx + dy*dy;
530 for ( i=1; i<this->priv->labels_per_sheet; i++ )
532 dx = x - this->priv->centers[i].x;
533 dy = y - this->priv->centers[i].y;
547 /*--------------------------------------------------------------------------*/
548 /* Expose event handler. */
549 /*--------------------------------------------------------------------------*/
551 expose_event_cb (GtkWidget *widget,
552 GdkEventExpose *event,
557 gl_debug (DEBUG_MINI_PREVIEW, "START");
559 cr = gdk_cairo_create (widget->window);
562 event->area.x, event->area.y,
563 event->area.width, event->area.height);
565 cairo_translate (cr, widget->allocation.x, widget->allocation.y);
571 gl_debug (DEBUG_MINI_PREVIEW, "END");
576 /*--------------------------------------------------------------------------*/
577 /* Style set handler (updates colors when style/theme changes). */
578 /*--------------------------------------------------------------------------*/
580 style_set_cb (GtkWidget *widget,
581 GtkStyle *previous_style,
584 gl_debug (DEBUG_MINI_PREVIEW, "START");
588 gl_debug (DEBUG_MINI_PREVIEW, "END");
592 /*--------------------------------------------------------------------------*/
594 /*--------------------------------------------------------------------------*/
596 redraw (glMiniPreview *this)
600 gl_debug (DEBUG_MINI_PREVIEW, "START");
602 if (GTK_WIDGET (this->priv->canvas)->window)
605 region = gdk_drawable_get_clip_region (GTK_WIDGET (this->priv->canvas)->window);
607 gdk_window_invalidate_region (GTK_WIDGET (this->priv->canvas)->window, region, TRUE);
608 gdk_window_process_updates (GTK_WIDGET (this->priv->canvas)->window, TRUE);
610 gdk_region_destroy (region);
613 gl_debug (DEBUG_MINI_PREVIEW, "END");
617 /*--------------------------------------------------------------------------*/
618 /* Draw mini preview. */
619 /*--------------------------------------------------------------------------*/
621 draw (glMiniPreview *this,
624 lglTemplate *template = this->priv->template;
626 gdouble shadow_x, shadow_y;
629 gl_debug (DEBUG_MINI_PREVIEW, "START");
634 scale = set_transform_and_get_scale (this, cr);
637 shadow_x = SHADOW_OFFSET/scale;
638 shadow_y = SHADOW_OFFSET/scale;
640 draw_shadow (this, cr,
642 template->page_width, template->page_height);
644 draw_paper (this, cr,
645 template->page_width, template->page_height,
648 draw_labels (this, cr, template, 1.0/scale);
652 gl_debug (DEBUG_MINI_PREVIEW, "END");
657 /*--------------------------------------------------------------------------*/
658 /* Draw page shadow */
659 /*--------------------------------------------------------------------------*/
661 draw_shadow (glMiniPreview *this,
671 gl_debug (DEBUG_MINI_PREVIEW, "START");
675 cairo_rectangle (cr, x, y, width, height);
677 style = gtk_widget_get_style (GTK_WIDGET(this));
678 shadow_color = gl_color_from_gdk_color (&style->dark[GTK_STATE_NORMAL]);
679 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (shadow_color));
685 gl_debug (DEBUG_MINI_PREVIEW, "END");
689 /*--------------------------------------------------------------------------*/
691 /*--------------------------------------------------------------------------*/
693 draw_paper (glMiniPreview *this,
700 guint paper_color, outline_color;
702 gl_debug (DEBUG_MINI_PREVIEW, "START");
706 style = gtk_widget_get_style (GTK_WIDGET(this));
707 paper_color = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
708 outline_color = gl_color_from_gdk_color (&style->fg[GTK_STATE_NORMAL]);
710 cairo_rectangle (cr, 0.0, 0.0, width, height);
712 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (paper_color));
713 cairo_fill_preserve (cr);
715 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (outline_color));
716 cairo_set_line_width (cr, line_width);
721 gl_debug (DEBUG_MINI_PREVIEW, "END");
725 /*--------------------------------------------------------------------------*/
727 /*--------------------------------------------------------------------------*/
729 draw_labels (glMiniPreview *this,
731 lglTemplate *template,
734 const lglTemplateFrame *frame;
736 lglTemplateOrigin *origins;
738 guint highlight_color, paper_color, outline_color;
740 gl_debug (DEBUG_MINI_PREVIEW, "START");
742 frame = (lglTemplateFrame *)template->frames->data;
744 n_labels = lgl_template_frame_get_n_labels (frame);
745 origins = lgl_template_frame_get_origins (frame);
747 style = gtk_widget_get_style (GTK_WIDGET(this));
748 highlight_color = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
749 paper_color = gl_color_from_gdk_color (&style->light[GTK_STATE_NORMAL]);
750 outline_color = gl_color_from_gdk_color (&style->fg[GTK_STATE_NORMAL]);
752 for ( i=0; i < n_labels; i++ ) {
756 cairo_translate (cr, origins[i].x, origins[i].y);
757 gl_cairo_label_path (cr, template, FALSE, FALSE);
759 if ( ((i+1) >= this->priv->highlight_first) &&
760 ((i+1) <= this->priv->highlight_last) )
762 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (highlight_color));
766 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (paper_color));
768 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
769 cairo_fill_preserve (cr);
771 cairo_set_line_width (cr, line_width);
772 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (outline_color));
781 gl_debug (DEBUG_MINI_PREVIEW, "END");
787 * Local Variables: -- emacs
789 * c-basic-offset: 8 -- emacs
790 * tab-width: 8 -- emacs
791 * indent-tabs-mode: nil -- emacs