1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
4 * (GLABELS) Label and Business Card Creation program for GNOME
6 * wdgt_mini_preview.c: mini preview widget module
8 * Copyright (C) 2001-2007 Jim Evins <evins@snaught.com>.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include "wdgt-mini-preview.h"
36 #define SHADOW_X_OFFSET 5
37 #define SHADOW_Y_OFFSET 5
39 /*===========================================*/
41 /*===========================================*/
54 struct _glWdgtMiniPreviewPrivate {
63 gint labels_per_sheet;
75 /*===========================================*/
77 /*===========================================*/
79 static GtkDrawingAreaClass *parent_class;
81 static gint wdgt_mini_preview_signals[LAST_SIGNAL] = { 0 };
83 /*===========================================*/
84 /* Local function prototypes */
85 /*===========================================*/
87 static void gl_wdgt_mini_preview_class_init (glWdgtMiniPreviewClass *class);
88 static void gl_wdgt_mini_preview_instance_init (glWdgtMiniPreview *preview);
89 static void gl_wdgt_mini_preview_finalize (GObject *object);
91 static void gl_wdgt_mini_preview_construct (glWdgtMiniPreview *preview,
95 static gboolean expose_event_cb (GtkWidget *widget,
96 GdkEventExpose *event);
97 static void style_set_cb (GtkWidget *widget,
98 GtkStyle *previous_style);
99 static gboolean button_press_event_cb (GtkWidget *widget,
100 GdkEventButton *event);
101 static gboolean motion_notify_event_cb (GtkWidget *widget,
102 GdkEventMotion *event);
103 static gboolean button_release_event_cb (GtkWidget *widget,
104 GdkEventButton *event);
107 static void redraw (GtkWidget *widget);
108 static void draw (glWdgtMiniPreview *preview,
111 static void draw_shadow (glWdgtMiniPreview *preview,
117 static void draw_paper (glWdgtMiniPreview *preview,
122 static void draw_labels (glWdgtMiniPreview *preview,
124 const glTemplate *template,
126 static void create_label_path (cairo_t *cr,
127 const glTemplate *template,
130 static void create_rect_label_path (cairo_t *cr,
131 const glTemplate *template,
134 static void create_round_label_path (cairo_t *cr,
135 const glTemplate *template,
138 static void create_cd_label_path (cairo_t *cr,
139 const glTemplate *template,
143 static gint find_closest_label (glWdgtMiniPreview *preview,
149 /****************************************************************************/
150 /* Boilerplate Object stuff. */
151 /****************************************************************************/
153 gl_wdgt_mini_preview_get_type (void)
155 static GType type = 0;
158 static const GTypeInfo info = {
159 sizeof (glWdgtMiniPreviewClass),
162 (GClassInitFunc) gl_wdgt_mini_preview_class_init,
165 sizeof (glWdgtMiniPreview),
167 (GInstanceInitFunc) gl_wdgt_mini_preview_instance_init,
171 type = g_type_register_static (GTK_TYPE_DRAWING_AREA,
172 "glWdgtMiniPreview", &info, 0);
179 gl_wdgt_mini_preview_class_init (glWdgtMiniPreviewClass *class)
181 GObjectClass *object_class = G_OBJECT_CLASS (class);
182 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
184 gl_debug (DEBUG_MINI_PREVIEW, "START");
186 parent_class = gtk_type_class (gtk_hbox_get_type ());
188 object_class->finalize = gl_wdgt_mini_preview_finalize;
190 widget_class->expose_event = expose_event_cb;
191 widget_class->style_set = style_set_cb;
192 widget_class->button_press_event = button_press_event_cb;
193 widget_class->motion_notify_event = motion_notify_event_cb;
194 widget_class->button_release_event = button_release_event_cb;
196 wdgt_mini_preview_signals[CLICKED] =
197 g_signal_new ("clicked",
198 G_OBJECT_CLASS_TYPE(object_class),
200 G_STRUCT_OFFSET (glWdgtMiniPreviewClass, clicked),
202 gl_marshal_VOID__INT,
203 G_TYPE_NONE, 1, G_TYPE_INT);
205 wdgt_mini_preview_signals[PRESSED] =
206 g_signal_new ("pressed",
207 G_OBJECT_CLASS_TYPE(object_class),
209 G_STRUCT_OFFSET (glWdgtMiniPreviewClass, pressed),
211 gl_marshal_VOID__INT_INT,
212 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
214 gl_debug (DEBUG_MINI_PREVIEW, "END");
218 gl_wdgt_mini_preview_instance_init (glWdgtMiniPreview *preview)
220 gl_debug (DEBUG_MINI_PREVIEW, "START");
222 preview->priv = g_new0 (glWdgtMiniPreviewPrivate, 1);
224 gtk_widget_add_events (GTK_WIDGET (preview),
225 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
226 GDK_POINTER_MOTION_MASK);
228 gl_debug (DEBUG_MINI_PREVIEW, "END");
232 gl_wdgt_mini_preview_finalize (GObject *object)
234 glWdgtMiniPreview *preview;
235 glWdgtMiniPreviewClass *class;
237 gl_debug (DEBUG_MINI_PREVIEW, "START");
239 g_return_if_fail (object != NULL);
240 g_return_if_fail (GL_IS_WDGT_MINI_PREVIEW (object));
242 preview = GL_WDGT_MINI_PREVIEW (object);
244 gl_template_free (preview->priv->template);
245 g_free (preview->priv->centers);
246 g_free (preview->priv);
248 G_OBJECT_CLASS (parent_class)->finalize (object);
250 gl_debug (DEBUG_MINI_PREVIEW, "END");
254 gl_wdgt_mini_preview_new (gint height,
257 glWdgtMiniPreview *preview;
259 gl_debug (DEBUG_MINI_PREVIEW, "START");
261 preview = g_object_new (gl_wdgt_mini_preview_get_type (), NULL);
263 gl_wdgt_mini_preview_construct (preview, height, width);
265 gl_debug (DEBUG_MINI_PREVIEW, "END");
267 return GTK_WIDGET (preview);
270 /*--------------------------------------------------------------------------*/
271 /* Construct composite widget. */
272 /*--------------------------------------------------------------------------*/
274 gl_wdgt_mini_preview_construct (glWdgtMiniPreview *preview,
278 GnomeCanvasGroup *group;
282 gl_debug (DEBUG_MINI_PREVIEW, "START");
284 preview->priv->height = height;
285 preview->priv->width = width;
287 gtk_widget_set_size_request (GTK_WIDGET (preview), width, height);
289 gl_debug (DEBUG_MINI_PREVIEW, "END");
292 /****************************************************************************/
293 /* Set label for mini-preview to determine geometry. */
294 /****************************************************************************/
295 void gl_wdgt_mini_preview_set_label_by_name (glWdgtMiniPreview *preview,
298 glTemplate *template;
300 gl_debug (DEBUG_MINI_PREVIEW, "START");
303 template = gl_template_from_name (name);
305 gl_wdgt_mini_preview_set_template (preview, template);
307 gl_template_free (template);
309 gl_debug (DEBUG_MINI_PREVIEW, "END");
312 /****************************************************************************/
313 /* Set label for mini-preview to determine geometry. */
314 /****************************************************************************/
315 void gl_wdgt_mini_preview_set_template (glWdgtMiniPreview *preview,
316 const glTemplate *template)
318 const glTemplateLabelType *label_type;
319 glTemplateOrigin *origins;
323 gl_debug (DEBUG_MINI_PREVIEW, "START");
325 label_type = gl_template_get_first_label_type (template);
330 gl_template_free (preview->priv->template);
331 preview->priv->template = gl_template_dup (template);
334 * Set scale and offsets
336 w = preview->priv->width - 4 - 2*SHADOW_X_OFFSET;
337 h = preview->priv->height - 4 - 2*SHADOW_Y_OFFSET;
338 if ( (w/template->page_width) > (h/template->page_height) ) {
339 preview->priv->scale = h / template->page_height;
341 preview->priv->scale = w / template->page_width;
343 preview->priv->offset_x = (preview->priv->width/preview->priv->scale - template->page_width) / 2.0;
344 preview->priv->offset_y = (preview->priv->height/preview->priv->scale - template->page_height) / 2.0;
347 * Set labels per sheet
349 preview->priv->labels_per_sheet = gl_template_get_n_labels (label_type);
354 g_free (preview->priv->centers);
355 preview->priv->centers = g_new0 (LabelCenter, preview->priv->labels_per_sheet);
356 origins = gl_template_get_origins (label_type);
357 gl_template_get_label_size (label_type, &w, &h);
358 for ( i=0; i<preview->priv->labels_per_sheet; i++ )
360 preview->priv->centers[i].x = origins[i].x + w/2.0;
361 preview->priv->centers[i].y = origins[i].y + h/2.0;
366 * Redraw modified preview
368 redraw( GTK_WIDGET (preview));
370 gl_debug (DEBUG_MINI_PREVIEW, "END");
373 /****************************************************************************/
374 /* Highlight given label outlines. */
375 /****************************************************************************/
377 gl_wdgt_mini_preview_highlight_range (glWdgtMiniPreview *preview,
381 gl_debug (DEBUG_MINI_PREVIEW, "START");
383 preview->priv->highlight_first = first_label;
384 preview->priv->highlight_last = last_label;
386 redraw( GTK_WIDGET (preview));
388 gl_debug (DEBUG_MINI_PREVIEW, "END");
392 /*--------------------------------------------------------------------------*/
393 /* Expose event handler. */
394 /*--------------------------------------------------------------------------*/
396 expose_event_cb (GtkWidget *widget,
397 GdkEventExpose *event)
401 gl_debug (DEBUG_MINI_PREVIEW, "START");
403 cr = gdk_cairo_create (widget->window);
406 event->area.x, event->area.y,
407 event->area.width, event->area.height);
410 draw (GL_WDGT_MINI_PREVIEW (widget), cr);
414 gl_debug (DEBUG_MINI_PREVIEW, "END");
418 /*--------------------------------------------------------------------------*/
419 /* Button press event handler */
420 /*--------------------------------------------------------------------------*/
422 button_press_event_cb (GtkWidget *widget,
423 GdkEventButton *event)
425 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (widget);
430 gl_debug (DEBUG_MINI_PREVIEW, "START");
432 if ( event->button == 1 )
434 cr = gdk_cairo_create (widget->window);
436 /* Set transformation. */
437 cairo_identity_matrix (cr);
438 cairo_scale (cr, preview->priv->scale, preview->priv->scale);
439 cairo_translate (cr, preview->priv->offset_x, preview->priv->offset_y);
443 cairo_device_to_user (cr, &x, &y);
445 i = find_closest_label (preview, x, y);
447 g_signal_emit (G_OBJECT(preview),
448 wdgt_mini_preview_signals[CLICKED],
451 preview->priv->first_i = i;
452 preview->priv->last_i = i;
453 g_signal_emit (G_OBJECT(preview),
454 wdgt_mini_preview_signals[PRESSED],
455 0, preview->priv->first_i, preview->priv->last_i);
457 preview->priv->dragging = TRUE;
458 preview->priv->prev_i = i;
463 gl_debug (DEBUG_MINI_PREVIEW, "END");
467 /*--------------------------------------------------------------------------*/
468 /* Motion notify event handler */
469 /*--------------------------------------------------------------------------*/
471 motion_notify_event_cb (GtkWidget *widget,
472 GdkEventMotion *event)
474 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (widget);
479 gl_debug (DEBUG_MINI_PREVIEW, "START");
481 if (preview->priv->dragging)
483 cr = gdk_cairo_create (widget->window);
485 /* Set transformation. */
486 cairo_identity_matrix (cr);
487 cairo_scale (cr, preview->priv->scale, preview->priv->scale);
488 cairo_translate (cr, preview->priv->offset_x, preview->priv->offset_y);
492 cairo_device_to_user (cr, &x, &y);
494 i = find_closest_label (preview, x, y);
496 if ( i != preview->priv->prev_i )
498 preview->priv->last_i = i;
500 g_signal_emit (G_OBJECT(preview),
501 wdgt_mini_preview_signals[PRESSED],
503 MIN (preview->priv->first_i, preview->priv->last_i),
504 MAX (preview->priv->first_i, preview->priv->last_i));
506 preview->priv->prev_i = i;
511 gl_debug (DEBUG_MINI_PREVIEW, "END");
515 /*--------------------------------------------------------------------------*/
516 /* Button release event handler */
517 /*--------------------------------------------------------------------------*/
519 button_release_event_cb (GtkWidget *widget,
520 GdkEventButton *event)
522 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (widget);
524 gl_debug (DEBUG_MINI_PREVIEW, "START");
526 if ( event->button == 1 )
528 preview->priv->dragging = FALSE;
532 gl_debug (DEBUG_MINI_PREVIEW, "END");
536 /*--------------------------------------------------------------------------*/
537 /* Style set handler (updates colors when style/theme changes). */
538 /*--------------------------------------------------------------------------*/
540 style_set_cb (GtkWidget *widget,
541 GtkStyle *previous_style)
543 gl_debug (DEBUG_MINI_PREVIEW, "START");
547 gl_debug (DEBUG_MINI_PREVIEW, "END");
550 /*--------------------------------------------------------------------------*/
552 /*--------------------------------------------------------------------------*/
554 redraw (GtkWidget *widget)
558 gl_debug (DEBUG_MINI_PREVIEW, "START");
563 region = gdk_drawable_get_clip_region (widget->window);
565 gdk_window_invalidate_region (widget->window, region, TRUE);
566 gdk_window_process_updates (widget->window, TRUE);
568 gdk_region_destroy (region);
571 gl_debug (DEBUG_MINI_PREVIEW, "END");
574 /*--------------------------------------------------------------------------*/
575 /* Find index+1 of label closest to given coordinates. */
576 /*--------------------------------------------------------------------------*/
578 find_closest_label (glWdgtMiniPreview *preview,
584 gdouble dx, dy, d2, min_d2;
586 dx = x - preview->priv->centers[0].x;
587 dy = y - preview->priv->centers[0].y;
588 min_d2 = dx*dx + dy*dy;
591 for ( i=1; i<preview->priv->labels_per_sheet; i++ )
593 dx = x - preview->priv->centers[i].x;
594 dy = y - preview->priv->centers[i].y;
607 /*--------------------------------------------------------------------------*/
608 /* Draw mini preview. */
609 /*--------------------------------------------------------------------------*/
611 draw (glWdgtMiniPreview *preview,
614 glTemplate *template = preview->priv->template;
615 gdouble shadow_x, shadow_y;
617 gl_debug (DEBUG_MINI_PREVIEW, "START");
622 /* Set transformation. */
623 cairo_identity_matrix (cr);
624 cairo_scale (cr, preview->priv->scale, preview->priv->scale);
625 cairo_translate (cr, preview->priv->offset_x, preview->priv->offset_y);
629 shadow_x = SHADOW_X_OFFSET/preview->priv->scale;
630 shadow_y = SHADOW_Y_OFFSET/preview->priv->scale;
632 draw_shadow (preview, cr,
634 template->page_width, template->page_height);
636 draw_paper (preview, cr,
637 template->page_width, template->page_height,
638 1.0/preview->priv->scale);
640 draw_labels (preview, cr, template, 1.0/preview->priv->scale);
644 gl_debug (DEBUG_MINI_PREVIEW, "END");
649 /*--------------------------------------------------------------------------*/
650 /* Draw page shadow */
651 /*--------------------------------------------------------------------------*/
653 draw_shadow (glWdgtMiniPreview *preview,
663 gl_debug (DEBUG_MINI_PREVIEW, "START");
667 cairo_rectangle (cr, x, y, width, height);
669 style = gtk_widget_get_style (GTK_WIDGET(preview));
670 shadow_color = gl_color_from_gdk_color (&style->bg[GTK_STATE_ACTIVE]);
671 cairo_set_source_rgb (cr,
672 GL_COLOR_F_RED (shadow_color),
673 GL_COLOR_F_GREEN (shadow_color),
674 GL_COLOR_F_BLUE (shadow_color));
680 gl_debug (DEBUG_MINI_PREVIEW, "END");
683 /*--------------------------------------------------------------------------*/
685 /*--------------------------------------------------------------------------*/
687 draw_paper (glWdgtMiniPreview *preview,
695 gl_debug (DEBUG_MINI_PREVIEW, "START");
697 cairo_rectangle (cr, 0.0, 0.0, width, height);
699 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
700 cairo_fill_preserve (cr);
702 cairo_set_source_rgb (cr, 0.0, 0.0, 0.0);
703 cairo_set_line_width (cr, line_width);
708 gl_debug (DEBUG_MINI_PREVIEW, "END");
711 /*--------------------------------------------------------------------------*/
713 /*--------------------------------------------------------------------------*/
715 draw_labels (glWdgtMiniPreview *preview,
717 const glTemplate *template,
720 const glTemplateLabelType *label_type;
722 glTemplateOrigin *origins;
724 guint highlight_color;
726 gl_debug (DEBUG_MINI_PREVIEW, "START");
728 label_type = gl_template_get_first_label_type (template);
730 n_labels = gl_template_get_n_labels (label_type);
731 origins = gl_template_get_origins (label_type);
733 style = gtk_widget_get_style (GTK_WIDGET(preview));
734 highlight_color = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
736 for ( i=0; i < n_labels; i++ ) {
740 create_label_path (cr, template, origins[i].x, origins[i].y);
742 if ( ((i+1) >= preview->priv->highlight_first) &&
743 ((i+1) <= preview->priv->highlight_last) )
745 cairo_set_source_rgb (cr,
746 GL_COLOR_F_RED (highlight_color),
747 GL_COLOR_F_GREEN (highlight_color),
748 GL_COLOR_F_BLUE (highlight_color));
752 cairo_set_source_rgb (cr, 1.0, 1.0, 1.0);
754 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
755 cairo_fill_preserve (cr);
757 cairo_set_line_width (cr, line_width);
758 cairo_set_source_rgb (cr, 0.5, 0.5, 0.5);
767 gl_debug (DEBUG_MINI_PREVIEW, "END");
770 /*--------------------------------------------------------------------------*/
771 /* Create label path */
772 /*--------------------------------------------------------------------------*/
774 create_label_path (cairo_t *cr,
775 const glTemplate *template,
779 const glTemplateLabelType *label_type;
781 gl_debug (DEBUG_MINI_PREVIEW, "START");
783 label_type = gl_template_get_first_label_type (template);
785 switch (label_type->shape) {
787 case GL_TEMPLATE_SHAPE_RECT:
788 create_rect_label_path (cr, template, x0, y0);
791 case GL_TEMPLATE_SHAPE_ROUND:
792 create_round_label_path (cr, template, x0, y0);
795 case GL_TEMPLATE_SHAPE_CD:
796 create_cd_label_path (cr, template, x0, y0);
800 g_message ("Unknown label style");
804 gl_debug (DEBUG_MINI_PREVIEW, "END");
807 /*--------------------------------------------------------------------------*/
808 /* Create rectangular label path */
809 /*--------------------------------------------------------------------------*/
811 create_rect_label_path (cairo_t *cr,
812 const glTemplate *template,
816 const glTemplateLabelType *label_type;
819 gl_debug (DEBUG_MINI_PREVIEW, "START");
821 label_type = gl_template_get_first_label_type (template);
822 gl_template_get_label_size (label_type, &w, &h);
824 cairo_rectangle (cr, x0, y0, w, h);
826 gl_debug (DEBUG_MINI_PREVIEW, "END");
829 /*--------------------------------------------------------------------------*/
830 /* Create round label path */
831 /*--------------------------------------------------------------------------*/
833 create_round_label_path (cairo_t *cr,
834 const glTemplate *template,
838 const glTemplateLabelType *label_type;
841 gl_debug (DEBUG_MINI_PREVIEW, "START");
843 label_type = gl_template_get_first_label_type (template);
844 gl_template_get_label_size (label_type, &w, &h);
846 cairo_arc (cr, x0+w/2, y0+h/2, w/2, 0.0, 2*M_PI);
847 cairo_close_path (cr);
849 gl_debug (DEBUG_MINI_PREVIEW, "END");
852 /*--------------------------------------------------------------------------*/
853 /* Create cd label path */
854 /*--------------------------------------------------------------------------*/
856 create_cd_label_path (cairo_t *cr,
857 const glTemplate *template,
861 const glTemplateLabelType *label_type;
865 gdouble theta1, theta2;
867 gl_debug (DEBUG_MINI_PREVIEW, "START");
869 label_type = gl_template_get_first_label_type (template);
870 gl_template_get_label_size (label_type, &w, &h);
875 r1 = label_type->size.cd.r1;
876 r2 = label_type->size.cd.r2;
879 * Outer path (may be clipped)
881 theta1 = acos (w / (2.0*r1));
882 theta2 = asin (h / (2.0*r1));
885 cairo_arc (cr, xc, yc, r1, theta1, theta2);
886 cairo_arc (cr, xc, yc, r1, M_PI-theta2, M_PI-theta1);
887 cairo_arc (cr, xc, yc, r1, M_PI+theta1, M_PI+theta2);
888 cairo_arc (cr, xc, yc, r1, 2*M_PI-theta2, 2*M_PI-theta1);
889 cairo_close_path (cr);
892 /* Inner path (hole) */
893 cairo_new_sub_path (cr);
894 cairo_arc (cr, xc, yc, r2, 0.0, 2*M_PI);
895 cairo_close_path (cr);
897 gl_debug (DEBUG_MINI_PREVIEW, "END");