/* * Copyright © 2009 Bastien Nocera * * 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 . */ #include "config.h" #include #include #include "cheese-camera.h" #include "cheese-widget-private.h" #include "cheese-flash.h" #include "cheese-avatar-widget.h" #include "um-crop-area.h" /** * SECTION:cheese-avatar-widget * @short_description: A photo capture dialog for avatars * @stability: Unstable * @include: cheese/cheese-avatar-widget.h * * #CheeseAvatarWidget presents a simple window to the user for taking a photo * for use as an avatar. */ enum { PROP_0, PROP_PIXBUF, PROP_LAST }; enum { WIDGET_PAGE = 0, IMAGE_PAGE = 1, }; typedef struct { GtkWidget *notebook; GtkWidget *camera; GtkWidget *image; GtkWidget *take_button; GtkWidget *take_again_button; GtkSizeGroup *sizegroup; CheeseFlash *flash; gulong photo_taken_id; } CheeseAvatarWidgetPrivate; static GParamSpec *properties[PROP_LAST]; G_DEFINE_TYPE_WITH_PRIVATE (CheeseAvatarWidget, cheese_avatar_widget, GTK_TYPE_BIN) /* * cheese_widget_photo_taken_cb: * @camera: a #CheeseCamera * @pixbuf: the #GdkPixbuf of the image that was just taken * @choose: a #CheeseAvatarWidget * * Show the image that was just taken from the camera (as @pixbuf) in the * cropping tool. */ static void cheese_widget_photo_taken_cb (CheeseCamera *camera, GdkPixbuf *pixbuf, CheeseAvatarWidget *widget) { CheeseAvatarWidgetPrivate *priv; GtkAllocation allocation; priv = cheese_avatar_widget_get_instance_private (widget); gtk_widget_get_allocation (priv->camera, &allocation); gtk_widget_set_size_request (priv->image, allocation.width, allocation.height); um_crop_area_set_picture (UM_CROP_AREA (priv->image), pixbuf); gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), IMAGE_PAGE); gtk_widget_set_sensitive (priv->take_button, TRUE); g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_PIXBUF]); } /* * take_button_clicked_cb: * @button: the #GtkButton that was clicked * @widget: the #CheeseAvatarWidget * * Take a photo with the #CheeseCamera of @widget. When the photo has been * taken, call cheese_widget_photo_taken_cb(). */ static void take_button_clicked_cb (GtkButton *button, CheeseAvatarWidget *widget) { CheeseAvatarWidgetPrivate *priv; GObject *camera; priv = cheese_avatar_widget_get_instance_private (widget); camera = cheese_widget_get_camera (CHEESE_WIDGET (priv->camera)); if (priv->photo_taken_id == 0) { gtk_widget_set_sensitive (priv->take_button, FALSE); priv->photo_taken_id = g_signal_connect (G_OBJECT (camera), "photo-taken", G_CALLBACK (cheese_widget_photo_taken_cb), widget); } if (cheese_camera_take_photo_pixbuf (CHEESE_CAMERA (camera))) { cheese_flash_fire (CHEESE_FLASH (priv->flash)); ca_gtk_play_for_widget (GTK_WIDGET (widget), 0, CA_PROP_EVENT_ID, "camera-shutter", CA_PROP_MEDIA_ROLE, "event", CA_PROP_EVENT_DESCRIPTION, _("Shutter sound"), NULL); } else { g_assert_not_reached (); } } /* * take_again_button_clicked_cb: * @button: the #GtkButton that was clicked * @widget: the #CheeseAvatarWidget * * Switch the @widget back to the camera view, ready to take another photo. */ static void take_again_button_clicked_cb (GtkButton *button, CheeseAvatarWidget *widget) { CheeseAvatarWidgetPrivate *priv; priv = cheese_avatar_widget_get_instance_private (widget); gtk_notebook_set_current_page (GTK_NOTEBOOK (priv->notebook), WIDGET_PAGE); um_crop_area_set_picture (UM_CROP_AREA (priv->image), NULL); g_object_notify_by_pspec (G_OBJECT (widget), properties[PROP_PIXBUF]); } /* state_change_cb: * @object: the #CheeseWidget on which the state changed * @param_spec: the (unused) parameter specification * @widget: a #CheeseAvatarWidget * * Handle state changes on the #CheeseWidget, and update the UI appropriately. */ static void state_change_cb (GObject *object, GParamSpec *param_spec, CheeseAvatarWidget *widget) { CheeseAvatarWidgetPrivate *priv; CheeseWidgetState state; g_object_get (object, "state", &state, NULL); priv = cheese_avatar_widget_get_instance_private (widget); switch (state) { case CHEESE_WIDGET_STATE_READY: gtk_widget_set_sensitive (priv->take_button, TRUE); gtk_widget_set_sensitive (priv->take_again_button, TRUE); break; case CHEESE_WIDGET_STATE_ERROR: gtk_widget_set_sensitive (priv->take_button, FALSE); gtk_widget_set_sensitive (priv->take_again_button, FALSE); break; case CHEESE_WIDGET_STATE_NONE: break; default: g_assert_not_reached (); } } /* * create_page: * @child: the #CheeseWidget to pack into the container * @button: the #GtkButton for taking a photo * * Create the widgets for the #CheeseAvatarWidget and pack them into a * container. * * Returns: a #GtkBox containing the individual #CheeseAvatarWidget widgets */ static GtkWidget * create_page (GtkWidget *child, GtkWidget *button) { GtkWidget *vgrid, *bar; GtkStyleContext *context; vgrid = gtk_grid_new (); gtk_grid_attach (GTK_GRID (vgrid), child, 0, 0, 1, 1); gtk_widget_set_hexpand (child, TRUE); gtk_widget_set_vexpand (child, TRUE); bar = gtk_header_bar_new (); context = gtk_widget_get_style_context (GTK_WIDGET (bar)); gtk_style_context_remove_class (context, "header-bar"); gtk_style_context_add_class (context, "inline-toolbar"); gtk_style_context_add_class (context, "toolbar"); gtk_style_context_add_class (context, GTK_STYLE_CLASS_HORIZONTAL); g_object_set (G_OBJECT (button), "margin-top", 6, "margin-bottom", 6, NULL); gtk_header_bar_set_custom_title (GTK_HEADER_BAR (bar), button); gtk_grid_attach (GTK_GRID (vgrid), bar, 0, 1, 1, 1); return vgrid; } static void cheese_avatar_widget_init (CheeseAvatarWidget *widget) { CheeseAvatarWidgetPrivate *priv; GtkWidget *frame; GtkWidget *image; priv = cheese_avatar_widget_get_instance_private (widget); priv->flash = cheese_flash_new (GTK_WIDGET (widget)); priv->notebook = gtk_notebook_new (); g_object_set(G_OBJECT (priv->notebook), "margin", 12, NULL); gtk_notebook_set_show_border (GTK_NOTEBOOK (priv->notebook), FALSE); gtk_notebook_set_show_tabs (GTK_NOTEBOOK (priv->notebook), FALSE); gtk_container_add (GTK_CONTAINER (widget), priv->notebook); /* Camera tab */ priv->camera = cheese_widget_new (); g_signal_connect (G_OBJECT (priv->camera), "notify::state", G_CALLBACK (state_change_cb), widget); image = gtk_image_new_from_icon_name ("camera-photo-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); priv->take_button = gtk_button_new (); gtk_button_set_image (GTK_BUTTON (priv->take_button), image); g_signal_connect (G_OBJECT (priv->take_button), "clicked", G_CALLBACK (take_button_clicked_cb), widget); gtk_widget_set_sensitive (priv->take_button, FALSE); gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), create_page (priv->camera, priv->take_button), gtk_label_new ("webcam")); /* Image tab */ 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 (_("_Take Another Picture")); g_signal_connect (G_OBJECT (priv->take_again_button), "clicked", G_CALLBACK (take_again_button_clicked_cb), widget); gtk_widget_set_sensitive (priv->take_again_button, FALSE); gtk_notebook_append_page (GTK_NOTEBOOK (priv->notebook), create_page (frame, priv->take_again_button), gtk_label_new ("image")); priv->sizegroup = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); gtk_size_group_add_widget (priv->sizegroup, priv->take_button); gtk_size_group_add_widget (priv->sizegroup, priv->take_again_button); gtk_widget_show_all (GTK_WIDGET (widget)); } static void cheese_avatar_widget_finalize (GObject *object) { CheeseAvatarWidgetPrivate *priv; priv = cheese_avatar_widget_get_instance_private (CHEESE_AVATAR_WIDGET (object)); g_clear_object (&priv->flash); g_clear_object (&priv->sizegroup); G_OBJECT_CLASS (cheese_avatar_widget_parent_class)->finalize (object); } static void cheese_avatar_widget_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { CheeseAvatarWidgetPrivate *priv; priv = cheese_avatar_widget_get_instance_private (CHEESE_AVATAR_WIDGET (object)); switch (prop_id) { case PROP_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); break; } } static void cheese_avatar_widget_class_init (CheeseAvatarWidgetClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = cheese_avatar_widget_finalize; object_class->get_property = cheese_avatar_widget_get_property; /** * CheeseAvatarWidget:pixbuf: * * A #GdkPixbuf object representing the cropped area of the picture, or %NULL. */ properties[PROP_PIXBUF] = g_param_spec_object ("pixbuf", "Pixbuf", "A #GdkPixbuf object representing the cropped area of the picture, or %NULL.", GDK_TYPE_PIXBUF, G_PARAM_READABLE); g_object_class_install_properties (object_class, PROP_LAST, properties); } /** * cheese_avatar_widget_new: * * Creates a new #CheeseAvatarWidget dialogue. * * Returns: a #CheeseAvatarWidget */ GtkWidget * cheese_avatar_widget_new (void) { return g_object_new (CHEESE_TYPE_AVATAR_WIDGET, NULL); } /** * cheese_avatar_widget_get_picture: * @widget: a #CheeseAvatarWidget dialogue * * Returns the portion of image selected through the builtin cropping tool, * after a picture has been captured on the webcam. * * Return value: a #GdkPixbuf object, or %NULL if no picture has been taken yet */ GdkPixbuf * cheese_avatar_widget_get_picture (CheeseAvatarWidget *widget) { CheeseAvatarWidgetPrivate *priv; g_return_val_if_fail (CHEESE_IS_AVATAR_WIDGET (widget), NULL); priv = cheese_avatar_widget_get_instance_private (widget); return um_crop_area_get_picture (UM_CROP_AREA (priv->image)); }