[cheese] Add cropping capability to the chooser widget
- From: Bastien Nocera <hadess src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [cheese] Add cropping capability to the chooser widget
- Date: Tue, 12 Jan 2010 12:51:01 +0000 (UTC)
commit b4e2a01d723a8bdb74c6327d862e0ef1af9ba08a
Author: Bastien Nocera <hadess hadess net>
Date: Tue Jan 12 12:48:57 2010 +0000
Add cropping capability to the chooser widget
https://bugzilla.gnome.org/show_bug.cgi?id=606730
libcheese/Makefile.am | 2 +
libcheese/cheese-avatar-chooser.c | 33 +--
libcheese/um-crop-area.c | 643 +++++++++++++++++++++++++++++++++++++
libcheese/um-crop-area.h | 62 ++++
4 files changed, 720 insertions(+), 20 deletions(-)
---
diff --git a/libcheese/Makefile.am b/libcheese/Makefile.am
index 31f79f2..0b61c14 100644
--- a/libcheese/Makefile.am
+++ b/libcheese/Makefile.am
@@ -33,6 +33,8 @@ libcheesecommon_la_SOURCES = \
libcheese_gtk_la_SOURCES = \
cheese-avatar-chooser.c \
cheese-avatar-chooser.h \
+ um-crop-area.c \
+ um-crop-area.h \
cheese-widget.c \
cheese-widget.h \
$(NULL)
diff --git a/libcheese/cheese-avatar-chooser.c b/libcheese/cheese-avatar-chooser.c
index f208bfe..62ee0fb 100644
--- a/libcheese/cheese-avatar-chooser.c
+++ b/libcheese/cheese-avatar-chooser.c
@@ -27,6 +27,7 @@
#include "cheese-countdown.h"
#include "cheese-flash.h"
#include "cheese-avatar-chooser.h"
+#include "um-crop-area.h"
enum
{
@@ -53,7 +54,6 @@ typedef struct
GtkWidget *take_button;
GtkWidget *take_again_button;
GtkWidget *countdown;
- GdkPixbuf *pixbuf;
CheeseFlash *flash;
gulong photo_taken_id;
} CheeseAvatarChooserPrivate;
@@ -75,9 +75,7 @@ cheese_widget_photo_taken_cb (CheeseCamera *camera,
gtk_widget_get_allocation (priv->camera, &allocation);
gtk_widget_set_size_request (priv->image, allocation.width, allocation.height);
- gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), pixbuf);
- g_assert (priv->pixbuf == NULL);
- priv->pixbuf = g_object_ref (pixbuf);
+ um_crop_area_set_picture (UM_CROP_AREA (priv->image), pixbuf);
gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), IMAGE_PAGE);
gtk_dialog_set_response_sensitive (GTK_DIALOG (chooser),
GTK_RESPONSE_ACCEPT,
@@ -151,9 +149,7 @@ take_again_button_clicked_cb (GtkButton *button,
GTK_RESPONSE_ACCEPT,
FALSE);
- gtk_image_set_from_pixbuf (GTK_IMAGE (priv->image), NULL);
- g_object_unref (priv->pixbuf);
- priv->pixbuf = NULL;
+ um_crop_area_set_picture (UM_CROP_AREA (priv->image), NULL);
g_object_notify (G_OBJECT (chooser), "pixbuf");
}
@@ -207,6 +203,8 @@ create_page (GtkWidget *child,
static void
cheese_avatar_chooser_init (CheeseAvatarChooser *chooser)
{
+ GtkWidget *frame;
+
CheeseAvatarChooserPrivate *priv = CHEESE_AVATAR_CHOOSER_GET_PRIVATE (chooser);
priv->flash = cheese_flash_new (GTK_WIDGET (chooser));
@@ -247,13 +245,16 @@ cheese_avatar_chooser_init (CheeseAvatarChooser *chooser)
gtk_label_new ("webcam"));
/* Image tab */
- priv->image = gtk_image_new ();
+ priv->image = um_crop_area_new ();
+ frame = gtk_frame_new (NULL);
+ gtk_container_add (GTK_CONTAINER (frame), priv->image);
+ gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
priv->take_again_button = gtk_button_new_with_mnemonic (_("_Discard photo"));
g_signal_connect (G_OBJECT (priv->take_again_button), "clicked",
G_CALLBACK (take_again_button_clicked_cb), chooser);
gtk_widget_set_sensitive (priv->take_again_button, FALSE);
gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook),
- create_page (priv->image, priv->take_again_button, NULL),
+ create_page (frame, priv->take_again_button, NULL),
gtk_label_new ("image"));
gtk_window_set_default_size (GTK_WINDOW (chooser), 400, 300);
@@ -271,11 +272,6 @@ cheese_avatar_chooser_finalize (GObject *object)
g_object_unref (priv->flash);
priv->flash = NULL;
}
- if (priv->pixbuf != NULL)
- {
- g_object_unref (priv->pixbuf);
- priv->pixbuf = NULL;
- }
G_OBJECT_CLASS (cheese_avatar_chooser_parent_class)->finalize (object);
}
@@ -294,12 +290,12 @@ cheese_avatar_chooser_get_property (GObject *object, guint prop_id,
{
CheeseAvatarChooserPrivate *priv = CHEESE_AVATAR_CHOOSER_GET_PRIVATE (object);
- g_return_if_fail (CHEESE_IS_WIDGET (object));
+ g_return_if_fail (CHEESE_IS_AVATAR_CHOOSER (object));
switch (prop_id)
{
case PROP_PIXBUF:
- g_value_set_object (value, priv->pixbuf);
+ g_value_set_object (value, um_crop_area_get_picture (UM_CROP_AREA (priv->image)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -342,10 +338,7 @@ cheese_avatar_chooser_get_picture (CheeseAvatarChooser *chooser)
priv = CHEESE_AVATAR_CHOOSER_GET_PRIVATE (chooser);
- if (priv->pixbuf == NULL)
- return NULL;
-
- return g_object_ref (priv->pixbuf);
+ return um_crop_area_get_picture (UM_CROP_AREA (priv->image));
}
/*
diff --git a/libcheese/um-crop-area.c b/libcheese/um-crop-area.c
new file mode 100644
index 0000000..a10340c
--- /dev/null
+++ b/libcheese/um-crop-area.c
@@ -0,0 +1,643 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2009 Red Hat, Inc,
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Matthias Clasen <mclasen redhat com>
+ */
+
+#include <stdlib.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "um-crop-area.h"
+
+struct _UmCropAreaPrivate {
+ GdkPixbuf *browse_pixbuf;
+ GdkPixbuf *pixbuf;
+ GdkPixbuf *color_shifted;
+ gdouble scale;
+ GdkRectangle image;
+ GdkCursorType current_cursor;
+ GdkRectangle crop;
+ gint active_region;
+ gint last_press_x;
+ gint last_press_y;
+};
+
+G_DEFINE_TYPE (UmCropArea, um_crop_area, GTK_TYPE_DRAWING_AREA);
+
+static inline guchar
+shift_color_byte (guchar b,
+ int shift)
+{
+ return CLAMP(b + shift, 0, 255);
+}
+
+static void
+shift_colors (GdkPixbuf *pixbuf,
+ gint red,
+ gint green,
+ gint blue,
+ gint alpha)
+{
+ gint x, y, offset, y_offset, rowstride, width, height;
+ guchar *pixels;
+ gint channels;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ rowstride = gdk_pixbuf_get_rowstride (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ channels = gdk_pixbuf_get_n_channels (pixbuf);
+
+ for (y = 0; y < height; y++) {
+ y_offset = y * rowstride;
+ for (x = 0; x < width; x++) {
+ offset = y_offset + x * channels;
+ if (red != 0)
+ pixels[offset] = shift_color_byte (pixels[offset], red);
+ if (green != 0)
+ pixels[offset + 1] = shift_color_byte (pixels[offset + 1], green);
+ if (blue != 0)
+ pixels[offset + 2] = shift_color_byte (pixels[offset + 2], blue);
+ if (alpha != 0 && channels >= 4)
+ pixels[offset + 3] = shift_color_byte (pixels[offset + 3], blue);
+ }
+ }
+}
+
+static void
+update_pixbufs (UmCropArea *area)
+{
+ gint width;
+ gint height;
+ GtkAllocation allocation;
+ gdouble scale;
+ GdkColor *color;
+ guint32 pixel;
+ gint dest_x, dest_y, dest_width, dest_height;
+ GtkWidget *widget;
+
+ widget = GTK_WIDGET (area);
+ gtk_widget_get_allocation (widget, &allocation);
+
+ if (area->priv->pixbuf == NULL ||
+ gdk_pixbuf_get_width (area->priv->pixbuf) != allocation.width ||
+ gdk_pixbuf_get_height (area->priv->pixbuf) != allocation.height) {
+ if (area->priv->pixbuf != NULL)
+ g_object_unref (area->priv->pixbuf);
+ area->priv->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, FALSE, 8,
+ allocation.width, allocation.height);
+
+ color = &widget->style->bg[GTK_WIDGET_STATE (widget)];
+ pixel = ((color->red & 0xff00) << 16) |
+ ((color->green & 0xff00) << 8) |
+ (color->blue & 0xff00);
+ gdk_pixbuf_fill (area->priv->pixbuf, pixel);
+
+ width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
+ height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
+
+ scale = allocation.height / (gdouble)height;
+ if (scale * width > allocation.width)
+ scale = allocation.width / (gdouble)width;
+
+ dest_width = width * scale;
+ dest_height = height * scale;
+ dest_x = (allocation.width - dest_width) / 2;
+ dest_y = (allocation.height - dest_height) / 2,
+
+ gdk_pixbuf_scale (area->priv->browse_pixbuf,
+ area->priv->pixbuf,
+ dest_x, dest_y,
+ dest_width, dest_height,
+ dest_x, dest_y,
+ scale, scale,
+ GDK_INTERP_BILINEAR);
+
+ if (area->priv->color_shifted)
+ g_object_unref (area->priv->color_shifted);
+ area->priv->color_shifted = gdk_pixbuf_copy (area->priv->pixbuf);
+ shift_colors (area->priv->color_shifted, -32, -32, -32, 0);
+
+ if (area->priv->scale == 0.0) {
+ area->priv->crop.width = 96.0 / scale;
+ area->priv->crop.height = 96.0 / scale;
+ area->priv->crop.x = (gdk_pixbuf_get_width (area->priv->browse_pixbuf) - area->priv->crop.width) / 2;
+ area->priv->crop.y = (gdk_pixbuf_get_height (area->priv->browse_pixbuf) - area->priv->crop.height) / 2;
+ }
+
+ area->priv->scale = scale;
+ area->priv->image.x = dest_x;
+ area->priv->image.y = dest_y;
+ area->priv->image.width = dest_width;
+ area->priv->image.height = dest_height;
+ }
+}
+
+static void
+crop_to_widget (UmCropArea *area,
+ GdkRectangle *crop)
+{
+ crop->x = area->priv->image.x + area->priv->crop.x * area->priv->scale;
+ crop->y = area->priv->image.y + area->priv->crop.y * area->priv->scale;
+ crop->width = area->priv->crop.width * area->priv->scale;
+ crop->height = area->priv->crop.height * area->priv->scale;
+}
+
+typedef enum {
+ OUTSIDE,
+ INSIDE,
+ TOP,
+ TOP_LEFT,
+ TOP_RIGHT,
+ BOTTOM,
+ BOTTOM_LEFT,
+ BOTTOM_RIGHT,
+ LEFT,
+ RIGHT
+} Location;
+
+static gboolean
+um_crop_area_expose (GtkWidget *widget,
+ GdkEventExpose *event)
+{
+ cairo_t *cr;
+ GdkRectangle area;
+ GdkRectangle crop;
+ gint width, height;
+ UmCropArea *uarea = UM_CROP_AREA (widget);
+
+ if (uarea->priv->browse_pixbuf == NULL)
+ return FALSE;
+
+ update_pixbufs (uarea);
+
+ width = gdk_pixbuf_get_width (uarea->priv->pixbuf);
+ height = gdk_pixbuf_get_height (uarea->priv->pixbuf);
+ crop_to_widget (uarea, &crop);
+
+ area.x = 0;
+ area.y = 0;
+ area.width = width;
+ area.height = crop.y;
+ gdk_rectangle_intersect (&area, &event->area, &area);
+ gdk_draw_pixbuf (widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ uarea->priv->color_shifted,
+ area.x, area.y,
+ area.x, area.y,
+ area.width, area.height,
+ GDK_RGB_DITHER_NONE, 0, 0);
+
+ area.x = 0;
+ area.y = crop.y;
+ area.width = crop.x;
+ area.height = crop.height;
+ gdk_rectangle_intersect (&area, &event->area, &area);
+ gdk_draw_pixbuf (widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ uarea->priv->color_shifted,
+ area.x, area.y,
+ area.x, area.y,
+ area.width, area.height,
+ GDK_RGB_DITHER_NONE, 0, 0);
+
+ area.x = crop.x;
+ area.y = crop.y;
+ area.width = crop.width;
+ area.height = crop.height;
+ gdk_rectangle_intersect (&area, &event->area, &area);
+ gdk_draw_pixbuf (widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ uarea->priv->pixbuf,
+ area.x, area.y,
+ area.x, area.y,
+ area.width, area.height,
+ GDK_RGB_DITHER_NONE, 0, 0);
+
+ area.x = crop.x + crop.width;
+ area.y = crop.y;
+ area.width = width - area.x;
+ area.height = crop.height;
+ gdk_rectangle_intersect (&area, &event->area, &area);
+ gdk_draw_pixbuf (widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ uarea->priv->color_shifted,
+ area.x, area.y,
+ area.x, area.y,
+ area.width, area.height,
+ GDK_RGB_DITHER_NONE, 0, 0);
+
+ area.x = 0;
+ area.y = crop.y + crop.width;
+ area.width = width;
+ area.height = height - area.y;
+ gdk_rectangle_intersect (&area, &event->area, &area);
+ gdk_draw_pixbuf (widget->window,
+ widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
+ uarea->priv->color_shifted,
+ area.x, area.y,
+ area.x, area.y,
+ area.width, area.height,
+ GDK_RGB_DITHER_NONE, 0, 0);
+
+ cr = gdk_cairo_create (widget->window);
+ gdk_cairo_rectangle (cr, &event->area);
+ cairo_clip (cr);
+
+ if (uarea->priv->active_region != OUTSIDE) {
+ gint x1, x2, y1, y2;
+ gdk_cairo_set_source_color (cr, &widget->style->white);
+ cairo_set_line_width (cr, 1.0);
+ x1 = crop.x + crop.width / 3.0;
+ x2 = crop.x + 2 * crop.width / 3.0;
+ y1 = crop.y + crop.height / 3.0;
+ y2 = crop.y + 2 * crop.height / 3.0;
+
+ cairo_move_to (cr, x1 + 0.5, crop.y);
+ cairo_line_to (cr, x1 + 0.5, crop.y + crop.height);
+
+ cairo_move_to (cr, x2 + 0.5, crop.y);
+ cairo_line_to (cr, x2 + 0.5, crop.y + crop.height);
+
+ cairo_move_to (cr, crop.x, y1 + 0.5);
+ cairo_line_to (cr, crop.x + crop.width, y1 + 0.5);
+
+ cairo_move_to (cr, crop.x, y2 + 0.5);
+ cairo_line_to (cr, crop.x + crop.width, y2 + 0.5);
+ cairo_stroke (cr);
+ }
+
+ gdk_cairo_set_source_color (cr, &widget->style->black);
+ cairo_set_line_width (cr, 1.0);
+ cairo_rectangle (cr,
+ crop.x + 0.5,
+ crop.y + 0.5,
+ crop.width - 1.0,
+ crop.height - 1.0);
+ cairo_stroke (cr);
+
+ gdk_cairo_set_source_color (cr, &widget->style->white);
+ cairo_set_line_width (cr, 2.0);
+ cairo_rectangle (cr,
+ crop.x + 2.0,
+ crop.y + 2.0,
+ crop.width - 4.0,
+ crop.height - 4.0);
+ cairo_stroke (cr);
+
+ cairo_destroy (cr);
+
+ return FALSE;
+}
+
+typedef enum {
+ BELOW,
+ LOWER,
+ BETWEEN,
+ UPPER,
+ ABOVE
+} Range;
+
+static Range
+find_range (gint x,
+ gint min,
+ gint max)
+{
+ gint tolerance = 12;
+
+ if (x < min - tolerance)
+ return BELOW;
+ if (x <= min + tolerance)
+ return LOWER;
+ if (x < max - tolerance)
+ return BETWEEN;
+ if (x <= max + tolerance)
+ return UPPER;
+ return ABOVE;
+}
+
+static Location
+find_location (GdkRectangle *rect,
+ gint x,
+ gint y)
+{
+ Range x_range, y_range;
+ Location location[5][5] = {
+ { OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE },
+ { OUTSIDE, TOP_LEFT, TOP, TOP_RIGHT, OUTSIDE },
+ { OUTSIDE, LEFT, INSIDE, RIGHT, OUTSIDE },
+ { OUTSIDE, BOTTOM_LEFT, BOTTOM, BOTTOM_RIGHT, OUTSIDE },
+ { OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE, OUTSIDE }
+ };
+
+ x_range = find_range (x, rect->x, rect->x + rect->width);
+ y_range = find_range (y, rect->y, rect->y + rect->height);
+
+ return location[y_range][x_range];
+}
+
+static void
+update_cursor (UmCropArea *area,
+ gint x,
+ gint y)
+{
+ gint cursor_type;
+ GdkRectangle crop;
+
+ crop_to_widget (area, &crop);
+
+ switch (find_location (&crop, x, y)) {
+ case OUTSIDE:
+ cursor_type = GDK_LEFT_PTR;
+ break;
+ case TOP_LEFT:
+ cursor_type = GDK_TOP_LEFT_CORNER;
+ break;
+ case TOP:
+ cursor_type = GDK_TOP_SIDE;
+ break;
+ case TOP_RIGHT:
+ cursor_type = GDK_TOP_RIGHT_CORNER;
+ break;
+ case LEFT:
+ cursor_type = GDK_LEFT_SIDE;
+ break;
+ case INSIDE:
+ cursor_type = GDK_FLEUR;
+ break;
+ case RIGHT:
+ cursor_type = GDK_RIGHT_SIDE;
+ break;
+ case BOTTOM_LEFT:
+ cursor_type = GDK_BOTTOM_LEFT_CORNER;
+ break;
+ case BOTTOM:
+ cursor_type = GDK_BOTTOM_SIDE;
+ break;
+ case BOTTOM_RIGHT:
+ cursor_type = GDK_BOTTOM_RIGHT_CORNER;
+ break;
+ }
+
+ if (cursor_type != area->priv->current_cursor) {
+ GdkCursor *cursor = gdk_cursor_new (cursor_type);
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (area)), cursor);
+ gdk_cursor_unref (cursor);
+ area->priv->current_cursor = cursor_type;
+ }
+}
+
+static gboolean
+um_crop_area_motion_notify_event (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ gint x, y;
+ gint x2, y2;
+ gint delta_x, delta_y;
+ gint width, height, d;
+ UmCropArea *area = UM_CROP_AREA (widget);
+
+ width = gdk_pixbuf_get_width (area->priv->browse_pixbuf);
+ height = gdk_pixbuf_get_height (area->priv->browse_pixbuf);
+
+ x = (event->x - area->priv->image.x) / area->priv->scale;
+ y = (event->y - area->priv->image.y) / area->priv->scale;
+ x = CLAMP (x, 0, width);
+ y = CLAMP (y, 0, height);
+
+ delta_x = x - area->priv->last_press_x;
+ delta_y = y - area->priv->last_press_y;
+ area->priv->last_press_x = x;
+ area->priv->last_press_y = y;
+
+ x2 = area->priv->crop.x + area->priv->crop.width;
+ y2 = area->priv->crop.y + area->priv->crop.height;
+
+ switch (area->priv->active_region) {
+ case INSIDE:
+ area->priv->crop.x = CLAMP (area->priv->crop.x + delta_x, 0, width - area->priv->crop.width);
+ area->priv->crop.y = CLAMP (area->priv->crop.y + delta_y, 0, height - area->priv->crop.height);
+ break;
+
+ case TOP_LEFT:
+ d = MAX (x2 - x, y2 - y);
+ if (d < 48 / area->priv->scale)
+ d = 48 / area->priv->scale;
+ if (x2 - d < 0)
+ d = x2;
+ if (y2 - d < 0)
+ d = y2;
+ area->priv->crop.x = x2 - d;
+ area->priv->crop.y = y2 - d;
+ area->priv->crop.width = area->priv->crop.height = d;
+ break;
+
+ case TOP:
+ case TOP_RIGHT:
+ d = MAX (y2 - y, x - area->priv->crop.x);
+ if (d < 48 / area->priv->scale)
+ d = 48 / area->priv->scale;
+ if (area->priv->crop.x + d > width)
+ d = width - area->priv->crop.x;
+ if (y2 - d < 0)
+ d = y2;
+ area->priv->crop.y = y2 - d;
+ area->priv->crop.width = area->priv->crop.height = d;
+ break;
+
+ case LEFT:
+ case BOTTOM_LEFT:
+ d = MAX (x2 - x, y - area->priv->crop.y);
+ if (d < 48 / area->priv->scale)
+ d = 48 / area->priv->scale;
+ if (area->priv->crop.y + d > height)
+ d = height - area->priv->crop.y;
+ if (x2 - d < 0)
+ d = x2;
+ area->priv->crop.x = x2 - d;
+ area->priv->crop.width = area->priv->crop.height = d;
+ break;
+
+ case RIGHT:
+ case BOTTOM_RIGHT:
+ case BOTTOM:
+ area->priv->crop.width = MAX (x - area->priv->crop.x, y - area->priv->crop.y);
+ if (area->priv->crop.width < 48 / area->priv->scale)
+ area->priv->crop.width = 48 / area->priv->scale;
+ if (area->priv->crop.x + area->priv->crop.width > width)
+ area->priv->crop.width = width - area->priv->crop.x;
+ area->priv->crop.height = area->priv->crop.width;
+ if (area->priv->crop.y + area->priv->crop.height > height)
+ area->priv->crop.height = height - area->priv->crop.y;
+ area->priv->crop.width = area->priv->crop.height;
+ break;
+
+ case OUTSIDE:
+ break;
+ default: ;
+ }
+
+ update_cursor (area, event->x, event->y);
+ gtk_widget_queue_draw (widget);
+
+ return FALSE;
+}
+
+static gboolean
+um_crop_area_button_press_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ GdkRectangle crop;
+ UmCropArea *area = UM_CROP_AREA (widget);
+
+ crop_to_widget (area, &crop);
+
+ area->priv->last_press_x = (event->x - area->priv->image.x) / area->priv->scale;
+ area->priv->last_press_y = (event->y - area->priv->image.y) / area->priv->scale;
+ area->priv->active_region = find_location (&crop, event->x, event->y);
+
+ gtk_widget_queue_draw (widget);
+
+ return FALSE;
+}
+
+static gboolean
+um_crop_area_button_release_event (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ UmCropArea *area = UM_CROP_AREA (widget);
+
+ area->priv->last_press_x = -1;
+ area->priv->last_press_y = -1;
+ area->priv->active_region = OUTSIDE;
+
+ gtk_widget_queue_draw (widget);
+
+ return FALSE;
+}
+
+static void
+um_crop_area_finalize (GObject *object)
+{
+ UmCropArea *area = UM_CROP_AREA (object);
+
+ if (area->priv->browse_pixbuf) {
+ g_object_unref (area->priv->browse_pixbuf);
+ area->priv->browse_pixbuf = NULL;
+ }
+ if (area->priv->pixbuf) {
+ g_object_unref (area->priv->pixbuf);
+ area->priv->pixbuf = NULL;
+ }
+ if (area->priv->color_shifted) {
+ g_object_unref (area->priv->color_shifted);
+ area->priv->color_shifted = NULL;
+ }
+}
+
+static void
+um_crop_area_class_init (UmCropAreaClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+ GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
+
+ object_class->finalize = um_crop_area_finalize;
+ widget_class->expose_event = um_crop_area_expose;
+ widget_class->button_press_event = um_crop_area_button_press_event;
+ widget_class->button_release_event = um_crop_area_button_release_event;
+ widget_class->motion_notify_event = um_crop_area_motion_notify_event;
+
+ g_type_class_add_private (klass, sizeof (UmCropAreaPrivate));
+}
+
+static void
+um_crop_area_init (UmCropArea *area)
+{
+ area->priv = (G_TYPE_INSTANCE_GET_PRIVATE ((area), UM_TYPE_CROP_AREA,
+ UmCropAreaPrivate));
+
+ gtk_widget_add_events (GTK_WIDGET (area), GDK_POINTER_MOTION_MASK |
+ GDK_BUTTON_PRESS_MASK |
+ GDK_BUTTON_RELEASE_MASK);
+
+ area->priv->scale = 0.0;
+ area->priv->image.x = 0;
+ area->priv->image.y = 0;
+ area->priv->image.width = 0;
+ area->priv->image.height = 0;
+ area->priv->active_region = OUTSIDE;
+}
+
+GtkWidget *
+um_crop_area_new (void)
+{
+ return g_object_new (UM_TYPE_CROP_AREA, NULL);
+}
+
+GdkPixbuf *
+um_crop_area_get_picture (UmCropArea *area)
+{
+ return gdk_pixbuf_new_subpixbuf (area->priv->browse_pixbuf,
+ area->priv->crop.x, area->priv->crop.y,
+ area->priv->crop.width, area->priv->crop.height);
+}
+
+void
+um_crop_area_set_picture (UmCropArea *area,
+ GdkPixbuf *pixbuf)
+{
+ int width;
+ int height;
+
+ if (area->priv->browse_pixbuf) {
+ g_object_unref (area->priv->browse_pixbuf);
+ area->priv->browse_pixbuf = NULL;
+ }
+ if (pixbuf) {
+ area->priv->browse_pixbuf = g_object_ref (pixbuf);
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ } else {
+ width = 0;
+ height = 0;
+ }
+
+#if 0
+ gtk_widget_get_allocation (um->browse_drawing_area, &allocation);
+ um->priv->crop.width = 96;
+ um->priv->crop.height = 96;
+ um->priv->crop.x = (allocation.width - um->priv->crop.width) / 2;
+ um->priv->crop.y = (allocation.height - um->priv->crop.height) / 2;
+#else
+ area->priv->crop.width = 96;
+ area->priv->crop.height = 96;
+ area->priv->crop.x = (width - area->priv->crop.width) / 2;
+ area->priv->crop.y = (height - area->priv->crop.height) / 2;
+#endif
+ area->priv->scale = 0.0;
+ area->priv->image.x = 0;
+ area->priv->image.y = 0;
+ area->priv->image.width = 0;
+ area->priv->image.height = 0;
+
+ gtk_widget_queue_draw (GTK_WIDGET (area));
+}
+
diff --git a/libcheese/um-crop-area.h b/libcheese/um-crop-area.h
new file mode 100644
index 0000000..c1767ad
--- /dev/null
+++ b/libcheese/um-crop-area.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright © 2009 Bastien Nocera <hadess hadess net>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _UM_CROP_AREA_H_
+#define _UM_CROP_AREA_H_
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define UM_TYPE_CROP_AREA (um_crop_area_get_type ())
+#define UM_CROP_AREA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), UM_TYPE_CROP_AREA, \
+ UmCropArea))
+#define UM_CROP_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), UM_TYPE_CROP_AREA, \
+ UmCropAreaClass))
+#define UM_IS_CROP_AREA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), UM_TYPE_CROP_AREA))
+#define UM_IS_CROP_AREA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), UM_TYPE_CROP_AREA))
+#define UM_CROP_AREA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), UM_TYPE_CROP_AREA, \
+ UmCropAreaClass))
+
+typedef struct _UmCropAreaClass UmCropAreaClass;
+typedef struct _UmCropArea UmCropArea;
+typedef struct _UmCropAreaPrivate UmCropAreaPrivate;
+
+struct _UmCropAreaClass
+{
+ GtkDrawingAreaClass parent_class;
+};
+
+struct _UmCropArea
+{
+ GtkDrawingArea parent_instance;
+ UmCropAreaPrivate *priv;
+};
+
+GType um_crop_area_get_type (void) G_GNUC_CONST;
+
+GtkWidget *um_crop_area_new (void);
+GdkPixbuf *um_crop_area_get_picture (UmCropArea *area);
+void um_crop_area_set_picture (UmCropArea *area,
+ GdkPixbuf *pixbuf);
+
+G_END_DECLS
+
+#endif /* _UM_CROP_AREA_H_ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]