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 "cairo-label-path.h"
37 /*===========================================*/
38 /* Private macros and constants. */
39 /*===========================================*/
41 #define PAPER_RGB_ARGS 1.0, 1.0, 1.0
42 #define PAPER_OUTLINE_RGB_ARGS 0.0, 0.0, 0.0
43 #define LABEL_OUTLINE_RGB_ARGS 0.5, 0.5, 0.5
45 #define SHADOW_X_OFFSET 5
46 #define SHADOW_Y_OFFSET 5
48 /*===========================================*/
50 /*===========================================*/
63 struct _glWdgtMiniPreviewPrivate {
68 lglTemplate *template;
72 gint labels_per_sheet;
84 /*===========================================*/
86 /*===========================================*/
88 static gint wdgt_mini_preview_signals[LAST_SIGNAL] = { 0 };
90 /*===========================================*/
91 /* Local function prototypes */
92 /*===========================================*/
94 static void gl_wdgt_mini_preview_finalize (GObject *object);
96 static void gl_wdgt_mini_preview_construct (glWdgtMiniPreview *preview,
100 static gboolean expose_event_cb (GtkWidget *widget,
101 GdkEventExpose *event);
102 static void style_set_cb (GtkWidget *widget,
103 GtkStyle *previous_style);
104 static gboolean button_press_event_cb (GtkWidget *widget,
105 GdkEventButton *event);
106 static gboolean motion_notify_event_cb (GtkWidget *widget,
107 GdkEventMotion *event);
108 static gboolean button_release_event_cb (GtkWidget *widget,
109 GdkEventButton *event);
112 static void redraw (GtkWidget *widget);
113 static void draw (glWdgtMiniPreview *preview,
116 static void draw_shadow (glWdgtMiniPreview *preview,
122 static void draw_paper (glWdgtMiniPreview *preview,
127 static void draw_labels (glWdgtMiniPreview *preview,
129 lglTemplate *template,
132 static gint find_closest_label (glWdgtMiniPreview *preview,
138 /****************************************************************************/
139 /* Boilerplate Object stuff. */
140 /****************************************************************************/
141 G_DEFINE_TYPE (glWdgtMiniPreview, gl_wdgt_mini_preview, GTK_TYPE_DRAWING_AREA);
145 gl_wdgt_mini_preview_class_init (glWdgtMiniPreviewClass *class)
147 GObjectClass *object_class = G_OBJECT_CLASS (class);
148 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (class);
150 gl_debug (DEBUG_MINI_PREVIEW, "START");
152 gl_wdgt_mini_preview_parent_class = gtk_type_class (gtk_hbox_get_type ());
154 object_class->finalize = gl_wdgt_mini_preview_finalize;
156 widget_class->expose_event = expose_event_cb;
157 widget_class->style_set = style_set_cb;
158 widget_class->button_press_event = button_press_event_cb;
159 widget_class->motion_notify_event = motion_notify_event_cb;
160 widget_class->button_release_event = button_release_event_cb;
162 wdgt_mini_preview_signals[CLICKED] =
163 g_signal_new ("clicked",
164 G_OBJECT_CLASS_TYPE(object_class),
166 G_STRUCT_OFFSET (glWdgtMiniPreviewClass, clicked),
168 gl_marshal_VOID__INT,
169 G_TYPE_NONE, 1, G_TYPE_INT);
171 wdgt_mini_preview_signals[PRESSED] =
172 g_signal_new ("pressed",
173 G_OBJECT_CLASS_TYPE(object_class),
175 G_STRUCT_OFFSET (glWdgtMiniPreviewClass, pressed),
177 gl_marshal_VOID__INT_INT,
178 G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
180 gl_debug (DEBUG_MINI_PREVIEW, "END");
184 gl_wdgt_mini_preview_init (glWdgtMiniPreview *preview)
186 gl_debug (DEBUG_MINI_PREVIEW, "START");
188 preview->priv = g_new0 (glWdgtMiniPreviewPrivate, 1);
190 gtk_widget_add_events (GTK_WIDGET (preview),
191 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
192 GDK_POINTER_MOTION_MASK);
194 gl_debug (DEBUG_MINI_PREVIEW, "END");
198 gl_wdgt_mini_preview_finalize (GObject *object)
200 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (object);
202 gl_debug (DEBUG_MINI_PREVIEW, "START");
204 g_return_if_fail (object != NULL);
205 g_return_if_fail (GL_IS_WDGT_MINI_PREVIEW (object));
207 lgl_template_free (preview->priv->template);
208 g_free (preview->priv->centers);
209 g_free (preview->priv);
211 G_OBJECT_CLASS (gl_wdgt_mini_preview_parent_class)->finalize (object);
213 gl_debug (DEBUG_MINI_PREVIEW, "END");
217 gl_wdgt_mini_preview_new (gint height,
220 glWdgtMiniPreview *preview;
222 gl_debug (DEBUG_MINI_PREVIEW, "START");
224 preview = g_object_new (gl_wdgt_mini_preview_get_type (), NULL);
226 gl_wdgt_mini_preview_construct (preview, height, width);
228 gl_debug (DEBUG_MINI_PREVIEW, "END");
230 return GTK_WIDGET (preview);
233 /*--------------------------------------------------------------------------*/
234 /* Construct composite widget. */
235 /*--------------------------------------------------------------------------*/
237 gl_wdgt_mini_preview_construct (glWdgtMiniPreview *preview,
241 gl_debug (DEBUG_MINI_PREVIEW, "START");
243 preview->priv->height = height;
244 preview->priv->width = width;
246 gtk_widget_set_size_request (GTK_WIDGET (preview), width, height);
248 gl_debug (DEBUG_MINI_PREVIEW, "END");
251 /****************************************************************************/
252 /* Set label for mini-preview to determine geometry. */
253 /****************************************************************************/
254 void gl_wdgt_mini_preview_set_label_by_name (glWdgtMiniPreview *preview,
257 lglTemplate *template;
259 gl_debug (DEBUG_MINI_PREVIEW, "START");
262 template = lgl_template_from_name (name);
264 gl_wdgt_mini_preview_set_template (preview, template);
266 lgl_template_free (template);
268 gl_debug (DEBUG_MINI_PREVIEW, "END");
271 /****************************************************************************/
272 /* Set label for mini-preview to determine geometry. */
273 /****************************************************************************/
274 void gl_wdgt_mini_preview_set_template (glWdgtMiniPreview *preview,
275 const lglTemplate *template)
277 const lglTemplateFrame *frame;
278 lglTemplateOrigin *origins;
282 gl_debug (DEBUG_MINI_PREVIEW, "START");
284 frame = lgl_template_get_first_frame (template);
289 lgl_template_free (preview->priv->template);
290 preview->priv->template = lgl_template_dup (template);
293 * Set scale and offsets
295 w = preview->priv->width - 4 - 2*SHADOW_X_OFFSET;
296 h = preview->priv->height - 4 - 2*SHADOW_Y_OFFSET;
297 if ( (w/template->page_width) > (h/template->page_height) ) {
298 preview->priv->scale = h / template->page_height;
300 preview->priv->scale = w / template->page_width;
302 preview->priv->offset_x = (preview->priv->width/preview->priv->scale - template->page_width) / 2.0;
303 preview->priv->offset_y = (preview->priv->height/preview->priv->scale - template->page_height) / 2.0;
306 * Set labels per sheet
308 preview->priv->labels_per_sheet = lgl_template_frame_get_n_labels (frame);
313 g_free (preview->priv->centers);
314 preview->priv->centers = g_new0 (LabelCenter, preview->priv->labels_per_sheet);
315 origins = lgl_template_frame_get_origins (frame);
316 lgl_template_frame_get_size (frame, &w, &h);
317 for ( i=0; i<preview->priv->labels_per_sheet; i++ )
319 preview->priv->centers[i].x = origins[i].x + w/2.0;
320 preview->priv->centers[i].y = origins[i].y + h/2.0;
325 * Redraw modified preview
327 redraw( GTK_WIDGET (preview));
329 gl_debug (DEBUG_MINI_PREVIEW, "END");
332 /****************************************************************************/
333 /* Highlight given label outlines. */
334 /****************************************************************************/
336 gl_wdgt_mini_preview_highlight_range (glWdgtMiniPreview *preview,
340 gl_debug (DEBUG_MINI_PREVIEW, "START");
342 preview->priv->highlight_first = first_label;
343 preview->priv->highlight_last = last_label;
345 redraw( GTK_WIDGET (preview));
347 gl_debug (DEBUG_MINI_PREVIEW, "END");
351 /*--------------------------------------------------------------------------*/
352 /* Expose event handler. */
353 /*--------------------------------------------------------------------------*/
355 expose_event_cb (GtkWidget *widget,
356 GdkEventExpose *event)
360 gl_debug (DEBUG_MINI_PREVIEW, "START");
362 cr = gdk_cairo_create (widget->window);
365 event->area.x, event->area.y,
366 event->area.width, event->area.height);
369 draw (GL_WDGT_MINI_PREVIEW (widget), cr);
373 gl_debug (DEBUG_MINI_PREVIEW, "END");
377 /*--------------------------------------------------------------------------*/
378 /* Button press event handler */
379 /*--------------------------------------------------------------------------*/
381 button_press_event_cb (GtkWidget *widget,
382 GdkEventButton *event)
384 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (widget);
389 gl_debug (DEBUG_MINI_PREVIEW, "START");
391 if ( event->button == 1 )
393 cr = gdk_cairo_create (widget->window);
395 /* Set transformation. */
396 cairo_identity_matrix (cr);
397 cairo_scale (cr, preview->priv->scale, preview->priv->scale);
398 cairo_translate (cr, preview->priv->offset_x, preview->priv->offset_y);
402 cairo_device_to_user (cr, &x, &y);
404 i = find_closest_label (preview, x, y);
406 g_signal_emit (G_OBJECT(preview),
407 wdgt_mini_preview_signals[CLICKED],
410 preview->priv->first_i = i;
411 preview->priv->last_i = i;
412 g_signal_emit (G_OBJECT(preview),
413 wdgt_mini_preview_signals[PRESSED],
414 0, preview->priv->first_i, preview->priv->last_i);
416 preview->priv->dragging = TRUE;
417 preview->priv->prev_i = i;
422 gl_debug (DEBUG_MINI_PREVIEW, "END");
426 /*--------------------------------------------------------------------------*/
427 /* Motion notify event handler */
428 /*--------------------------------------------------------------------------*/
430 motion_notify_event_cb (GtkWidget *widget,
431 GdkEventMotion *event)
433 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (widget);
438 gl_debug (DEBUG_MINI_PREVIEW, "START");
440 if (preview->priv->dragging)
442 cr = gdk_cairo_create (widget->window);
444 /* Set transformation. */
445 cairo_identity_matrix (cr);
446 cairo_scale (cr, preview->priv->scale, preview->priv->scale);
447 cairo_translate (cr, preview->priv->offset_x, preview->priv->offset_y);
451 cairo_device_to_user (cr, &x, &y);
453 i = find_closest_label (preview, x, y);
455 if ( i != preview->priv->prev_i )
457 preview->priv->last_i = i;
459 g_signal_emit (G_OBJECT(preview),
460 wdgt_mini_preview_signals[PRESSED],
462 MIN (preview->priv->first_i, preview->priv->last_i),
463 MAX (preview->priv->first_i, preview->priv->last_i));
465 preview->priv->prev_i = i;
470 gl_debug (DEBUG_MINI_PREVIEW, "END");
474 /*--------------------------------------------------------------------------*/
475 /* Button release event handler */
476 /*--------------------------------------------------------------------------*/
478 button_release_event_cb (GtkWidget *widget,
479 GdkEventButton *event)
481 glWdgtMiniPreview *preview = GL_WDGT_MINI_PREVIEW (widget);
483 gl_debug (DEBUG_MINI_PREVIEW, "START");
485 if ( event->button == 1 )
487 preview->priv->dragging = FALSE;
491 gl_debug (DEBUG_MINI_PREVIEW, "END");
495 /*--------------------------------------------------------------------------*/
496 /* Style set handler (updates colors when style/theme changes). */
497 /*--------------------------------------------------------------------------*/
499 style_set_cb (GtkWidget *widget,
500 GtkStyle *previous_style)
502 gl_debug (DEBUG_MINI_PREVIEW, "START");
506 gl_debug (DEBUG_MINI_PREVIEW, "END");
509 /*--------------------------------------------------------------------------*/
511 /*--------------------------------------------------------------------------*/
513 redraw (GtkWidget *widget)
517 gl_debug (DEBUG_MINI_PREVIEW, "START");
522 region = gdk_drawable_get_clip_region (widget->window);
524 gdk_window_invalidate_region (widget->window, region, TRUE);
525 gdk_window_process_updates (widget->window, TRUE);
527 gdk_region_destroy (region);
530 gl_debug (DEBUG_MINI_PREVIEW, "END");
533 /*--------------------------------------------------------------------------*/
534 /* Find index+1 of label closest to given coordinates. */
535 /*--------------------------------------------------------------------------*/
537 find_closest_label (glWdgtMiniPreview *preview,
543 gdouble dx, dy, d2, min_d2;
545 dx = x - preview->priv->centers[0].x;
546 dy = y - preview->priv->centers[0].y;
547 min_d2 = dx*dx + dy*dy;
550 for ( i=1; i<preview->priv->labels_per_sheet; i++ )
552 dx = x - preview->priv->centers[i].x;
553 dy = y - preview->priv->centers[i].y;
566 /*--------------------------------------------------------------------------*/
567 /* Draw mini preview. */
568 /*--------------------------------------------------------------------------*/
570 draw (glWdgtMiniPreview *preview,
573 lglTemplate *template = preview->priv->template;
574 gdouble shadow_x, shadow_y;
576 gl_debug (DEBUG_MINI_PREVIEW, "START");
581 /* Set transformation. */
582 cairo_identity_matrix (cr);
583 cairo_scale (cr, preview->priv->scale, preview->priv->scale);
584 cairo_translate (cr, preview->priv->offset_x, preview->priv->offset_y);
588 shadow_x = SHADOW_X_OFFSET/preview->priv->scale;
589 shadow_y = SHADOW_Y_OFFSET/preview->priv->scale;
591 draw_shadow (preview, cr,
593 template->page_width, template->page_height);
595 draw_paper (preview, cr,
596 template->page_width, template->page_height,
597 1.0/preview->priv->scale);
599 draw_labels (preview, cr, template, 1.0/preview->priv->scale);
603 gl_debug (DEBUG_MINI_PREVIEW, "END");
608 /*--------------------------------------------------------------------------*/
609 /* Draw page shadow */
610 /*--------------------------------------------------------------------------*/
612 draw_shadow (glWdgtMiniPreview *preview,
622 gl_debug (DEBUG_MINI_PREVIEW, "START");
626 cairo_rectangle (cr, x, y, width, height);
628 style = gtk_widget_get_style (GTK_WIDGET(preview));
629 shadow_color = gl_color_from_gdk_color (&style->bg[GTK_STATE_ACTIVE]);
630 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (shadow_color));
636 gl_debug (DEBUG_MINI_PREVIEW, "END");
639 /*--------------------------------------------------------------------------*/
641 /*--------------------------------------------------------------------------*/
643 draw_paper (glWdgtMiniPreview *preview,
651 gl_debug (DEBUG_MINI_PREVIEW, "START");
653 cairo_rectangle (cr, 0.0, 0.0, width, height);
655 cairo_set_source_rgb (cr, PAPER_RGB_ARGS);
656 cairo_fill_preserve (cr);
658 cairo_set_source_rgb (cr, PAPER_OUTLINE_RGB_ARGS);
659 cairo_set_line_width (cr, line_width);
664 gl_debug (DEBUG_MINI_PREVIEW, "END");
667 /*--------------------------------------------------------------------------*/
669 /*--------------------------------------------------------------------------*/
671 draw_labels (glWdgtMiniPreview *preview,
673 lglTemplate *template,
676 const lglTemplateFrame *frame;
678 lglTemplateOrigin *origins;
680 guint highlight_color;
682 gl_debug (DEBUG_MINI_PREVIEW, "START");
684 frame = lgl_template_get_first_frame (template);
686 n_labels = lgl_template_frame_get_n_labels (frame);
687 origins = lgl_template_frame_get_origins (frame);
689 style = gtk_widget_get_style (GTK_WIDGET(preview));
690 highlight_color = gl_color_from_gdk_color (&style->base[GTK_STATE_SELECTED]);
692 for ( i=0; i < n_labels; i++ ) {
696 cairo_translate (cr, origins[i].x, origins[i].y);
697 gl_cairo_label_path (cr, template, FALSE, FALSE);
699 if ( ((i+1) >= preview->priv->highlight_first) &&
700 ((i+1) <= preview->priv->highlight_last) )
702 cairo_set_source_rgb (cr, GL_COLOR_RGB_ARGS (highlight_color));
706 cairo_set_source_rgb (cr, PAPER_RGB_ARGS);
708 cairo_set_fill_rule (cr, CAIRO_FILL_RULE_EVEN_ODD);
709 cairo_fill_preserve (cr);
711 cairo_set_line_width (cr, line_width);
712 cairo_set_source_rgb (cr, LABEL_OUTLINE_RGB_ARGS);
721 gl_debug (DEBUG_MINI_PREVIEW, "END");