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"
31 #include "libglabels/db.h"
32 #include "cairo-label-path.h"
38 /*===========================================*/
39 /* Private macros and constants. */
40 /*===========================================*/
42 #define PAPER_RGB_ARGS 1.0, 1.0, 1.0
43 #define PAPER_OUTLINE_RGB_ARGS 0.0, 0.0, 0.0
44 #define LABEL_OUTLINE_RGB_ARGS 0.5, 0.5, 0.5
46 #define SHADOW_X_OFFSET 5
47 #define SHADOW_Y_OFFSET 5
49 /*===========================================*/
51 /*===========================================*/
64 struct _glWdgtMiniPreviewPrivate {
69 lglTemplate *template;
73 gint labels_per_sheet;
85 /*===========================================*/
87 /*===========================================*/
89 static gint wdgt_mini_preview_signals[LAST_SIGNAL] = { 0 };
91 /*===========================================*/
92 /* Local function prototypes */
93 /*===========================================*/
95 static void gl_wdgt_mini_preview_finalize (GObject *object);
97 static void gl_wdgt_mini_preview_construct (glWdgtMiniPreview *preview,
101 static gboolean expose_event_cb (GtkWidget *widget,
102 GdkEventExpose *event);
103 static void style_set_cb (GtkWidget *widget,
104 GtkStyle *previous_style);
105 static gboolean button_press_event_cb (GtkWidget *widget,
106 GdkEventButton *event);
107 static gboolean motion_notify_event_cb (GtkWidget *widget,
108 GdkEventMotion *event);
109 static gboolean button_release_event_cb (GtkWidget *widget,
110 GdkEventButton *event);
113 static void redraw (GtkWidget *widget);
114 static void draw (glWdgtMiniPreview *preview,
117 static void draw_shadow (glWdgtMiniPreview *preview,
123 static void draw_paper (glWdgtMiniPreview *preview,
128 static void draw_labels (glWdgtMiniPreview *preview,
130 lglTemplate *template,
133 static gint find_closest_label (glWdgtMiniPreview *preview,
139 /****************************************************************************/
140 /* Boilerplate Object stuff. */
141 /****************************************************************************/
142 G_DEFINE_TYPE (glWdgtMiniPreview, gl_wdgt_mini_preview, GTK_TYPE_DRAWING_AREA);
146 gl_wdgt_mini_preview_class_init (glWdgtMiniPreviewClass *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_wdgt_mini_preview_parent_class = g_type_class_peek_parent (class);
155 object_class->finalize = gl_wdgt_mini_preview_finalize;
157 widget_class->expose_event = expose_event_cb;
158 widget_class->style_set = style_set_cb;
159 widget_class->button_press_event = button_press_event_cb;
160 widget_class->motion_notify_event = motion_notify_event_cb;
161 widget_class->button_release_event = button_release_event_cb;
163 wdgt_mini_preview_signals[CLICKED] =
164 g_signal_new ("clicked",
165 G_OBJECT_CLASS_TYPE(object_class),
167 G_STRUCT_OFFSET (glWdgtMiniPreviewClass, clicked),
169 gl_marshal_VOID__INT,
170 G_TYPE_NONE, 1, G_TYPE_INT);
172 wdgt_mini_preview_signals[PRESSED] =
173 g_signal_new ("pressed",
174 G_OBJECT_CLASS_TYPE(object_class),
176 G_STRUCT_OFFSET (glWdgtMiniPreviewClass, pressed),
178 gl_marshal_VOID__INT_INT,
179 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
181 gl_debug (DEBUG_MINI_PREVIEW, "END");
185 gl_wdgt_mini_preview_init (glWdgtMiniPreview *preview)
187 gl_debug (DEBUG_MINI_PREVIEW, "START");
189 preview->priv = g_new0 (glWdgtMiniPreviewPrivate, 1);
191 gtk_widget_add_events (GTK_WIDGET (preview),
192 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
193 GDK_POINTER_MOTION_MASK);
195 gl_debug (DEBUG_MINI_PREVIEW, "END");
199 gl_wdgt_mini_preview_finalize (GObject *object)
201 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (object);
203 gl_debug (DEBUG_MINI_PREVIEW, "START");
205 g_return_if_fail (object != NULL);
206 g_return_if_fail (GL_IS_WDGT_MINI_PREVIEW (object));
208 lgl_template_free (preview->priv->template);
209 g_free (preview->priv->centers);
210 g_free (preview->priv);
212 G_OBJECT_CLASS (gl_wdgt_mini_preview_parent_class)->finalize (object);
214 gl_debug (DEBUG_MINI_PREVIEW, "END");
218 gl_wdgt_mini_preview_new (gint height,
221 glWdgtMiniPreview *preview;
223 gl_debug (DEBUG_MINI_PREVIEW, "START");
225 preview = g_object_new (gl_wdgt_mini_preview_get_type (), NULL);
227 gl_wdgt_mini_preview_construct (preview, height, width);
229 gl_debug (DEBUG_MINI_PREVIEW, "END");
231 return GTK_WIDGET (preview);
234 /*--------------------------------------------------------------------------*/
235 /* Construct composite widget. */
236 /*--------------------------------------------------------------------------*/
238 gl_wdgt_mini_preview_construct (glWdgtMiniPreview *preview,
242 gl_debug (DEBUG_MINI_PREVIEW, "START");
244 preview->priv->height = height;
245 preview->priv->width = width;
247 gtk_widget_set_size_request (GTK_WIDGET (preview), width, height);
249 gl_debug (DEBUG_MINI_PREVIEW, "END");
252 /****************************************************************************/
253 /* Set label for mini-preview to determine geometry. */
254 /****************************************************************************/
255 void gl_wdgt_mini_preview_set_label_by_name (glWdgtMiniPreview *preview,
258 lglTemplate *template;
260 gl_debug (DEBUG_MINI_PREVIEW, "START");
263 template = lgl_db_lookup_template_from_name (name);
265 gl_wdgt_mini_preview_set_template (preview, template);
267 lgl_template_free (template);
269 gl_debug (DEBUG_MINI_PREVIEW, "END");
272 /****************************************************************************/
273 /* Set label for mini-preview to determine geometry. */
274 /****************************************************************************/
275 void gl_wdgt_mini_preview_set_template (glWdgtMiniPreview *preview,
276 const lglTemplate *template)
278 const lglTemplateFrame *frame;
279 lglTemplateOrigin *origins;
283 gl_debug (DEBUG_MINI_PREVIEW, "START");
285 frame = (lglTemplateFrame *)template->frames->data;
290 lgl_template_free (preview->priv->template);
291 preview->priv->template = lgl_template_dup (template);
294 * Set scale and offsets
296 w = preview->priv->width - 4 - 2*SHADOW_X_OFFSET;
297 h = preview->priv->height - 4 - 2*SHADOW_Y_OFFSET;
298 if ( (w/template->page_width) > (h/template->page_height) ) {
299 preview->priv->scale = h / template->page_height;
301 preview->priv->scale = w / template->page_width;
303 preview->priv->offset_x = (preview->priv->width/preview->priv->scale - template->page_width) / 2.0;
304 preview->priv->offset_y = (preview->priv->height/preview->priv->scale - template->page_height) / 2.0;
307 * Set labels per sheet
309 preview->priv->labels_per_sheet = lgl_template_frame_get_n_labels (frame);
314 g_free (preview->priv->centers);
315 preview->priv->centers = g_new0 (LabelCenter, preview->priv->labels_per_sheet);
316 origins = lgl_template_frame_get_origins (frame);
317 lgl_template_frame_get_size (frame, &w, &h);
318 for ( i=0; i<preview->priv->labels_per_sheet; i++ )
320 preview->priv->centers[i].x = origins[i].x + w/2.0;
321 preview->priv->centers[i].y = origins[i].y + h/2.0;
326 * Redraw modified preview
328 redraw( GTK_WIDGET (preview));
330 gl_debug (DEBUG_MINI_PREVIEW, "END");
333 /****************************************************************************/
334 /* Highlight given label outlines. */
335 /****************************************************************************/
337 gl_wdgt_mini_preview_highlight_range (glWdgtMiniPreview *preview,
341 gl_debug (DEBUG_MINI_PREVIEW, "START");
343 preview->priv->highlight_first = first_label;
344 preview->priv->highlight_last = last_label;
346 redraw( GTK_WIDGET (preview));
348 gl_debug (DEBUG_MINI_PREVIEW, "END");
352 /*--------------------------------------------------------------------------*/
353 /* Expose event handler. */
354 /*--------------------------------------------------------------------------*/
356 expose_event_cb (GtkWidget *widget,
357 GdkEventExpose *event)
361 gl_debug (DEBUG_MINI_PREVIEW, "START");
363 cr = gdk_cairo_create (widget->window);
366 event->area.x, event->area.y,
367 event->area.width, event->area.height);
370 draw (GL_WDGT_MINI_PREVIEW (widget), cr);
374 gl_debug (DEBUG_MINI_PREVIEW, "END");
378 /*--------------------------------------------------------------------------*/
379 /* Button press event handler */
380 /*--------------------------------------------------------------------------*/
382 button_press_event_cb (GtkWidget *widget,
383 GdkEventButton *event)
385 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (widget);
390 gl_debug (DEBUG_MINI_PREVIEW, "START");
392 if ( event->button == 1 )
394 cr = gdk_cairo_create (widget->window);
396 /* Set transformation. */
397 cairo_identity_matrix (cr);
398 cairo_scale (cr, preview->priv->scale, preview->priv->scale);
399 cairo_translate (cr, preview->priv->offset_x, preview->priv->offset_y);
403 cairo_device_to_user (cr, &x, &y);
405 i = find_closest_label (preview, x, y);
407 g_signal_emit (G_OBJECT(preview),
408 wdgt_mini_preview_signals[CLICKED],
411 preview->priv->first_i = i;
412 preview->priv->last_i = i;
413 g_signal_emit (G_OBJECT(preview),
414 wdgt_mini_preview_signals[PRESSED],
415 0, preview->priv->first_i, preview->priv->last_i);
417 preview->priv->dragging = TRUE;
418 preview->priv->prev_i = i;
423 gl_debug (DEBUG_MINI_PREVIEW, "END");
427 /*--------------------------------------------------------------------------*/
428 /* Motion notify event handler */
429 /*--------------------------------------------------------------------------*/
431 motion_notify_event_cb (GtkWidget *widget,
432 GdkEventMotion *event)
434 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (widget);
439 gl_debug (DEBUG_MINI_PREVIEW, "START");
441 if (preview->priv->dragging)
443 cr = gdk_cairo_create (widget->window);
445 /* Set transformation. */
446 cairo_identity_matrix (cr);
447 cairo_scale (cr, preview->priv->scale, preview->priv->scale);
448 cairo_translate (cr, preview->priv->offset_x, preview->priv->offset_y);
452 cairo_device_to_user (cr, &x, &y);
454 i = find_closest_label (preview, x, y);
456 if ( i != preview->priv->prev_i )
458 preview->priv->last_i = i;
460 g_signal_emit (G_OBJECT(preview),
461 wdgt_mini_preview_signals[PRESSED],
463 MIN (preview->priv->first_i, preview->priv->last_i),
464 MAX (preview->priv->first_i, preview->priv->last_i));
466 preview->priv->prev_i = i;
471 gl_debug (DEBUG_MINI_PREVIEW, "END");
475 /*--------------------------------------------------------------------------*/
476 /* Button release event handler */
477 /*--------------------------------------------------------------------------*/
479 button_release_event_cb (GtkWidget *widget,
480 GdkEventButton *event)
482 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (widget);
484 gl_debug (DEBUG_MINI_PREVIEW, "START");
486 if ( event->button == 1 )
488 preview->priv->dragging = FALSE;
492 gl_debug (DEBUG_MINI_PREVIEW, "END");
496 /*--------------------------------------------------------------------------*/
497 /* Style set handler (updates colors when style/theme changes). */
498 /*--------------------------------------------------------------------------*/
500 style_set_cb (GtkWidget *widget,
501 GtkStyle *previous_style)
503 gl_debug (DEBUG_MINI_PREVIEW, "START");
507 gl_debug (DEBUG_MINI_PREVIEW, "END");
510 /*--------------------------------------------------------------------------*/
512 /*--------------------------------------------------------------------------*/
514 redraw (GtkWidget *widget)
518 gl_debug (DEBUG_MINI_PREVIEW, "START");
523 region = gdk_drawable_get_clip_region (widget->window);
525 gdk_window_invalidate_region (widget->window, region, TRUE);
526 gdk_window_process_updates (widget->window, TRUE);
528 gdk_region_destroy (region);
531 gl_debug (DEBUG_MINI_PREVIEW, "END");
534 /*--------------------------------------------------------------------------*/
535 /* Find index+1 of label closest to given coordinates. */
536 /*--------------------------------------------------------------------------*/
538 find_closest_label (glWdgtMiniPreview *preview,
544 gdouble dx, dy, d2, min_d2;
546 dx = x - preview->priv->centers[0].x;
547 dy = y - preview->priv->centers[0].y;
548 min_d2 = dx*dx + dy*dy;
551 for ( i=1; i<preview->priv->labels_per_sheet; i++ )
553 dx = x - preview->priv->centers[i].x;
554 dy = y - preview->priv->centers[i].y;
567 /*--------------------------------------------------------------------------*/
568 /* Draw mini preview. */
569 /*--------------------------------------------------------------------------*/
571 draw (glWdgtMiniPreview *preview,
574 lglTemplate *template = preview->priv->template;
575 gdouble shadow_x, shadow_y;
577 gl_debug (DEBUG_MINI_PREVIEW, "START");
582 /* Set transformation. */
583 cairo_identity_matrix (cr);
584 cairo_scale (cr, preview->priv->scale, preview->priv->scale);
585 cairo_translate (cr, preview->priv->offset_x, preview->priv->offset_y);
589 shadow_x = SHADOW_X_OFFSET/preview->priv->scale;
590 shadow_y = SHADOW_Y_OFFSET/preview->priv->scale;
592 draw_shadow (preview, cr,
594 template->page_width, template->page_height);
596 draw_paper (preview, cr,
597 template->page_width, template->page_height,
598 1.0/preview->priv->scale);
600 draw_labels (preview, cr, template, 1.0/preview->priv->scale);
604 gl_debug (DEBUG_MINI_PREVIEW, "END");
609 /*--------------------------------------------------------------------------*/
610 /* Draw page shadow */
611 /*--------------------------------------------------------------------------*/
613 draw_shadow (glWdgtMiniPreview *preview,
623 gl_debug (DEBUG_MINI_PREVIEW, "START");
627 cairo_rectangle (cr, x, y, width, height);
629 style = gtk_widget_get_style (GTK_WIDGET(preview));
630 shadow_color = gl_color_from_gdk_color (&style->bg[GTK_STATE_ACTIVE]);
631 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (shadow_color));
637 gl_debug (DEBUG_MINI_PREVIEW, "END");
640 /*--------------------------------------------------------------------------*/
642 /*--------------------------------------------------------------------------*/
644 draw_paper (glWdgtMiniPreview *preview,
652 gl_debug (DEBUG_MINI_PREVIEW, "START");
654 cairo_rectangle (cr, 0.0, 0.0, width, height);
656 cairo_set_source_rgb (cr, PAPER_RGB_ARGS);
657 cairo_fill_preserve (cr);
659 cairo_set_source_rgb (cr, PAPER_OUTLINE_RGB_ARGS);
660 cairo_set_line_width (cr, line_width);
665 gl_debug (DEBUG_MINI_PREVIEW, "END");
668 /*--------------------------------------------------------------------------*/
670 /*--------------------------------------------------------------------------*/
672 draw_labels (glWdgtMiniPreview *preview,
674 lglTemplate *template,
677 const lglTemplateFrame *frame;
679 lglTemplateOrigin *origins;
681 guint highlight_color;
683 gl_debug (DEBUG_MINI_PREVIEW, "START");
685 frame = (lglTemplateFrame *)template->frames->data;
687 n_labels = lgl_template_frame_get_n_labels (frame);
688 origins = lgl_template_frame_get_origins (frame);
690 style = gtk_widget_get_style (GTK_WIDGET(preview));
691 highlight_color = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
693 for ( i=0; i < n_labels; i++ ) {
697 cairo_translate (cr, origins[i].x, origins[i].y);
698 gl_cairo_label_path (cr, template, FALSE, FALSE);
700 if ( ((i+1) >= preview->priv->highlight_first) &&
701 ((i+1) <= preview->priv->highlight_last) )
703 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (highlight_color));
707 cairo_set_source_rgb (cr, PAPER_RGB_ARGS);
709 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
710 cairo_fill_preserve (cr);
712 cairo_set_line_width (cr, line_width);
713 cairo_set_source_rgb (cr, LABEL_OUTLINE_RGB_ARGS);
722 gl_debug (DEBUG_MINI_PREVIEW, "END");