diff --git a/src/avatarmanipulation.cpp b/src/avatarmanipulation.cpp index 2578f217fa88d15d56e28acd394251a26374689e..35148cd0c7dd9bc2cd6d0471df7a2e9d3a02f8a0 100644 --- a/src/avatarmanipulation.cpp +++ b/src/avatarmanipulation.cpp @@ -72,11 +72,18 @@ struct _AvatarManipulationPrivate GtkWidget *video_widget; GtkWidget *box_views_and_controls; GtkWidget *box_controls; - GtkWidget *button_take_photo; + + GtkWidget *button_box_current; + GtkWidget *button_box_photo; + GtkWidget *button_box_edit; + + GtkWidget *button_start_camera; GtkWidget *button_choose_picture; - GtkWidget *button_trash_avatar; + GtkWidget *button_take_photo; + GtkWidget *button_return_photo; GtkWidget *button_set_avatar; - GtkWidget *button_export_avatar; + GtkWidget *button_return_edit; + GtkWidget *selector_widget; GtkWidget *stack_views; GtkWidget *image_avatar; @@ -93,6 +100,13 @@ struct _AvatarManipulationPrivate GdkModifierType button_pressed; AvatarManipulationState state; + AvatarManipulationState last_state; + + /* this is used to keep track of the state of the preview when the camera is used to take a + * photo; if a call is in progress, then the preview should already be started and we don't want + * to stop it when the settings are closed, in this case + */ + gboolean video_started_by_avatar_manipulation; }; G_DEFINE_TYPE_WITH_PRIVATE(AvatarManipulation, avatar_manipulation, GTK_TYPE_BOX); @@ -102,11 +116,11 @@ G_DEFINE_TYPE_WITH_PRIVATE(AvatarManipulation, avatar_manipulation, GTK_TYPE_BOX static void set_state(AvatarManipulation *self, AvatarManipulationState state); +static void start_camera(AvatarManipulation *self); static void take_a_photo(AvatarManipulation *self); static void choose_picture(AvatarManipulation *self); -static void export_avatar(AvatarManipulation *self); +static void return_to_previous_state(AvatarManipulation *self); static void update_preview_cb(GtkFileChooser *file_chooser, GtkWidget *preview); -static void trash_photo(AvatarManipulation *self); static void set_avatar(AvatarManipulation *self); static void got_snapshot(AvatarManipulation *parent); @@ -125,7 +139,8 @@ avatar_manipulation_dispose(GObject *object) AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(object); /* make sure we stop the preview and the video widget */ - Video::PreviewManager::instance().stopPreview(); + if (priv->video_started_by_avatar_manipulation) + Video::PreviewManager::instance().stopPreview(); if (priv->video_widget) { gtk_container_remove(GTK_CONTAINER(priv->stack_views), priv->video_widget); priv->video_widget = NULL; @@ -162,9 +177,12 @@ avatar_manipulation_new_from_wizard(void) { auto self = avatar_manipulation_new(); - /* in this mode, we want to automatically go to the new avatar state, unless one already exists */ - if (!ProfileModel::instance().selectedProfile()->person()->photo().isValid()) - set_state(AVATAR_MANIPULATION(self), AVATAR_MANIPULATION_STATE_NEW); + /* in this mode, we want to automatically go to the PHOTO avatar state, unless one already exists */ + if (!ProfileModel::instance().selectedProfile()->person()->photo().isValid()) { + // check if there is a camera + if (Video::DeviceModel::instance().rowCount() > 0) + set_state(AVATAR_MANIPULATION(self), AVATAR_MANIPULATION_STATE_PHOTO); + } return self; } @@ -179,14 +197,18 @@ avatar_manipulation_class_init(AvatarManipulationClass *klass) gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, box_views_and_controls); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, box_controls); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_start_camera); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_choose_picture); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_take_photo); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_return_photo); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_set_avatar); - gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_trash_avatar); - gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_choose_picture); - gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_export_avatar); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_return_edit); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, stack_views); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, image_avatar); gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, vbox_selector); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_box_current); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_box_photo); + gtk_widget_class_bind_template_child_private(GTK_WIDGET_CLASS (klass), AvatarManipulation, button_box_edit); } static void @@ -204,7 +226,7 @@ avatar_manipulation_init(AvatarManipulation *self) /* Signals used to handle backing surface */ g_signal_connect(priv->selector_widget, "draw", G_CALLBACK (selector_widget_draw), self); - g_signal_connect(priv->selector_widget,"configure-event", G_CALLBACK (selector_widget_configure_event), self); + g_signal_connect(priv->selector_widget, "configure-event", G_CALLBACK (selector_widget_configure_event), self); /* Event signals */ g_signal_connect(priv->selector_widget, "motion-notify-event", G_CALLBACK(selector_widget_motion_notify_event), self); g_signal_connect(priv->selector_widget, "button-press-event", G_CALLBACK(selector_widget_button_press_event), self); @@ -216,11 +238,12 @@ avatar_manipulation_init(AvatarManipulation *self) /* signals */ + g_signal_connect_swapped(priv->button_start_camera, "clicked", G_CALLBACK(start_camera), self); g_signal_connect_swapped(priv->button_choose_picture, "clicked", G_CALLBACK(choose_picture), self); g_signal_connect_swapped(priv->button_take_photo, "clicked", G_CALLBACK(take_a_photo), self); + g_signal_connect_swapped(priv->button_return_photo, "clicked", G_CALLBACK(return_to_previous_state), self); g_signal_connect_swapped(priv->button_set_avatar, "clicked", G_CALLBACK(set_avatar), self); - g_signal_connect_swapped(priv->button_trash_avatar, "clicked", G_CALLBACK(trash_photo), self); - g_signal_connect_swapped(priv->button_export_avatar, "clicked", G_CALLBACK(export_avatar), self); + g_signal_connect_swapped(priv->button_return_edit, "clicked", G_CALLBACK(return_to_previous_state), self); set_state(self, AVATAR_MANIPULATION_STATE_CURRENT); @@ -234,6 +257,9 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) // been done by the caller AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self); + // save prev state + priv->last_state = priv->state; + switch (state) { case AVATAR_MANIPULATION_STATE_CURRENT: { @@ -251,62 +277,60 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) } gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_avatar"); - // gtk_widget_set_size_request(priv->video_widget, VIDEO_WIDTH, VIDEO_HEIGHT); - /* available actions: trash or export avatar */ - gtk_widget_set_visible(priv->button_trash_avatar, true); - gtk_widget_set_visible(priv->button_export_avatar, true); - gtk_widget_set_visible(priv->button_set_avatar, false); - gtk_widget_set_visible(priv->button_take_photo, false); - gtk_widget_set_visible(priv->button_choose_picture, false); + /* available actions: start camera (if available) or choose image */ + if (Video::DeviceModel::instance().rowCount() > 0) { + // TODO: update if a video device gets inserted while in this state + gtk_widget_set_visible(priv->button_start_camera, true); + } + gtk_widget_set_visible(priv->button_box_current, true); + gtk_widget_set_visible(priv->button_box_photo, false); + gtk_widget_set_visible(priv->button_box_edit, false); - /* make sure video widget and camera is no longer running */ - Video::PreviewManager::instance().stopPreview(); + /* make sure video widget and camera is not running */ + if (priv->video_started_by_avatar_manipulation) + Video::PreviewManager::instance().stopPreview(); if (priv->video_widget) { gtk_container_remove(GTK_CONTAINER(priv->stack_views), priv->video_widget); priv->video_widget = NULL; } } break; - case AVATAR_MANIPULATION_STATE_NEW: + case AVATAR_MANIPULATION_STATE_PHOTO: { - /* check if a video device is available, if so show the camera stream; otherwise we can - * only choose a file, and we keep showing the current/default avatar - * - * TODO: update if a video device gets inserted while in this state - */ - if (Video::DeviceModel::instance().rowCount() > 0) { - priv->video_widget = video_widget_new(); - // gtk_widget_set_size_request(priv->video_widget, VIDEO_WIDTH, VIDEO_HEIGHT); - video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget), - Video::PreviewManager::instance().previewRenderer(), - VIDEO_RENDERER_REMOTE); - g_signal_connect_swapped(priv->video_widget, "snapshot-taken", G_CALLBACK (got_snapshot), self); - gtk_widget_set_vexpand_set(priv->video_widget, FALSE); - gtk_widget_set_hexpand_set(priv->video_widget, FALSE); - gtk_widget_set_visible(priv->video_widget, true); - gtk_stack_add_named(GTK_STACK(priv->stack_views), priv->video_widget, "page_photobooth"); - gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_photobooth"); - + // start the video; if its not available we should not be in this state + priv->video_widget = video_widget_new(); + g_signal_connect_swapped(priv->video_widget, "snapshot-taken", G_CALLBACK (got_snapshot), self); + gtk_widget_set_vexpand_set(priv->video_widget, FALSE); + gtk_widget_set_hexpand_set(priv->video_widget, FALSE); + gtk_widget_set_visible(priv->video_widget, true); + gtk_stack_add_named(GTK_STACK(priv->stack_views), priv->video_widget, "page_photobooth"); + gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_photobooth"); + + + /* local renderer, but set as "remote" so that it takes up the whole screen */ + video_widget_push_new_renderer(VIDEO_WIDGET(priv->video_widget), + Video::PreviewManager::instance().previewRenderer(), + VIDEO_RENDERER_REMOTE); + + if (!Video::PreviewManager::instance().isPreviewing()) { + priv->video_started_by_avatar_manipulation = TRUE; Video::PreviewManager::instance().startPreview(); - - /* available action in this case: take snapshot */ - gtk_widget_set_visible(priv->button_take_photo, true); } else { - gtk_widget_set_visible(priv->button_take_photo, false); + priv->video_started_by_avatar_manipulation = FALSE; } - /* available actions: choose file */ - gtk_widget_set_visible(priv->button_choose_picture, true); - gtk_widget_set_visible(priv->button_trash_avatar, false); - gtk_widget_set_visible(priv->button_export_avatar, false); - gtk_widget_set_visible(priv->button_set_avatar, false); + /* available actions: take snapshot, return*/ + gtk_widget_set_visible(priv->button_box_current, false); + gtk_widget_set_visible(priv->button_box_photo, true); + gtk_widget_set_visible(priv->button_box_edit, false); } break; case AVATAR_MANIPULATION_STATE_EDIT: { - /* make sure video widget and camera is no longer running */ - Video::PreviewManager::instance().stopPreview(); + /* make sure video widget and camera is not running */ + if (priv->video_started_by_avatar_manipulation) + Video::PreviewManager::instance().stopPreview(); if (priv->video_widget) { gtk_container_remove(GTK_CONTAINER(priv->stack_views), priv->video_widget); priv->video_widget = NULL; @@ -317,12 +341,10 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) priv->origin[1] = 0; priv->length = INITIAL_LENTGH; - /* available actions: set avatar, trash avatar */ - gtk_widget_set_visible(priv->button_set_avatar, true); - gtk_widget_set_visible(priv->button_trash_avatar, true); - gtk_widget_set_visible(priv->button_take_photo, false); - gtk_widget_set_visible(priv->button_choose_picture, false); - gtk_widget_set_visible(priv->button_export_avatar, false); + /* available actions: set avatar, return */ + gtk_widget_set_visible(priv->button_box_current, false); + gtk_widget_set_visible(priv->button_box_photo, false); + gtk_widget_set_visible(priv->button_box_edit, true); gtk_stack_set_visible_child_name(GTK_STACK(priv->stack_views), "page_edit_view"); } @@ -333,20 +355,16 @@ set_state(AvatarManipulation *self, AvatarManipulationState state) } static void -take_a_photo(AvatarManipulation *self) +start_camera(AvatarManipulation *self) { - AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self); - video_widget_take_snapshot(VIDEO_WIDGET(priv->video_widget)); + set_state(self, AVATAR_MANIPULATION_STATE_PHOTO); } static void -trash_photo(AvatarManipulation *self) +take_a_photo(AvatarManipulation *self) { - /* clear the avatar in the profile */ - ProfileModel::instance().selectedProfile()->person()->setPhoto(QVariant()); - ProfileModel::instance().selectedProfile()->save(); - - set_state(self, AVATAR_MANIPULATION_STATE_NEW); + AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self); + video_widget_take_snapshot(VIDEO_WIDGET(priv->video_widget)); } static void @@ -389,6 +407,13 @@ set_avatar(AvatarManipulation *self) set_state(self, AVATAR_MANIPULATION_STATE_CURRENT); } +static void +return_to_previous_state(AvatarManipulation *self) +{ + AvatarManipulationPrivate *priv = AVATAR_MANIPULATION_GET_PRIVATE(self); + set_state(self, priv->last_state); +} + static void choose_picture(AvatarManipulation *self) { @@ -448,44 +473,6 @@ choose_picture(AvatarManipulation *self) gtk_widget_destroy(dialog); } -static void -export_avatar(AvatarManipulation *self) -{ - GtkWidget *ring_main_window = gtk_widget_get_toplevel(GTK_WIDGET(self)); - - auto dialog = gtk_file_chooser_dialog_new(_("Save Avatar Image"), - GTK_WINDOW(ring_main_window), - GTK_FILE_CHOOSER_ACTION_SAVE, - _("_Cancel"), - GTK_RESPONSE_CANCEL, - _("_Save"), - GTK_RESPONSE_ACCEPT, - NULL); - - gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog), TRUE); - - /* start the file chooser */ - auto res = gtk_dialog_run (GTK_DIALOG (dialog)); - if (res == GTK_RESPONSE_ACCEPT) { - if (auto filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog))) { - GError* error = nullptr; - auto photo = ProfileModel::instance().selectedProfile()->person()->photo(); - std::shared_ptr<GdkPixbuf> pixbuf_photo = photo.value<std::shared_ptr<GdkPixbuf>>(); - - if (photo.isValid()) { - gdk_pixbuf_save(pixbuf_photo.get(), filename, "png", &error, NULL); - if (error){ - g_warning("(export_avatar) could not save avatar to file: %s\n", error->message); - g_error_free(error); - } - } - g_free (filename); - } - } - - gtk_widget_destroy (dialog); -} - static void update_preview_cb(GtkFileChooser *file_chooser, GtkWidget *preview) { diff --git a/src/avatarmanipulation.h b/src/avatarmanipulation.h index cb76840c38d847d4f043bba2480d354af988891f..bc9c38e7ecd5adf6631bb0fa66500f34369bc96c 100644 --- a/src/avatarmanipulation.h +++ b/src/avatarmanipulation.h @@ -37,19 +37,18 @@ typedef struct _AvatarManipulationClass AvatarManipulationClass; /** * AvatarManipulationState: * @AVATAR_MANIPULATION_STATE_CURRENT: The initial state. The widget will display the current - * or the default avatar, with the possible actions being to delete (and select a new one) or to - * save to disk the current avatar. - * @AVATAR_MANIPULATION_STATE_NEW: The state in which the user is selecting a new avatar. If - * a video device is available, it will will be turned on. The possible actions being to take a - * snapshot or to select an existing image from a file. - * @AVATAR_MANIPULATION_STATE_EDIT: The state after selecting a new image when the user must - * select the square area they wish to use for the avatar. The possible actions being to confirm - * the selection or to discard it. + * or the default avatar, with the possible actions being to start the camera (if available) to + * take a new photo) or to select an existing image. + * @AVATAR_MANIPULATION_STATE_PHOTO: The state in which the camera is on and the video is being + * shown. The possible actions are to take a snapshot or to return to the previous state. + * @AVATAR_MANIPULATION_STATE_EDIT: The state after selecting a new image (or snapping a photo). The + * user must select the square area they wish to use for the avatar. The possible actions are to + * confirm the selection or to return to the previous state. */ typedef enum { AVATAR_MANIPULATION_STATE_CURRENT, - AVATAR_MANIPULATION_STATE_NEW, + AVATAR_MANIPULATION_STATE_PHOTO, AVATAR_MANIPULATION_STATE_EDIT } AvatarManipulationState; diff --git a/ui/avatarmanipulation.ui b/ui/avatarmanipulation.ui index 5b83dd495d3ea02a333f9c71271a1195c490d313..f54e7f5469460864ff8e51fb1c14846de3e7deac 100644 --- a/ui/avatarmanipulation.ui +++ b/ui/avatarmanipulation.ui @@ -1,34 +1,32 @@ <?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.19.0 --> <interface> <requires lib="gtk+" version="3.10"/> <object class="GtkImage" id="image_add_symbolic"> <property name="visible">True</property> - <property name="can_focus">False</property> <property name="icon_name">list-add-symbolic</property> </object> <object class="GtkImage" id="image_camera_photo"> <property name="visible">True</property> - <property name="can_focus">False</property> <property name="icon_name">camera-photo-symbolic</property> </object> - <object class="GtkImage" id="image_save_symbolic"> - <property name="can_focus">False</property> - <property name="icon_name">document-save-symbolic</property> + <object class="GtkImage" id="image_take_photo"> + <property name="visible">True</property> + <property name="icon_name">object-select-symbolic</property> </object> - <object class="GtkImage" id="image_select_symbolic"> + <object class="GtkImage" id="image_select_area"> <property name="visible">True</property> - <property name="can_focus">False</property> <property name="icon_name">object-select-symbolic</property> </object> - <object class="GtkImage" id="image_trash_symbolic"> + <object class="GtkImage" id="image_return_photo"> + <property name="visible">True</property> + <property name="icon_name">edit-undo-symbolic</property> + </object> + <object class="GtkImage" id="image_return_edit"> <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="icon_name">user-trash-symbolic</property> + <property name="icon_name">edit-undo-symbolic</property> </object> <template class="AvatarManipulation" parent="GtkBox"> <property name="visible">True</property> - <property name="can_focus">False</property> <property name="halign">center</property> <property name="valign">center</property> <property name="orientation">vertical</property> @@ -73,90 +71,108 @@ <child> <object class="GtkBox" id="box_controls"> <property name="visible">True</property> - <property name="can_focus">False</property> <property name="halign">center</property> <property name="valign">center</property> - <style> - <class name="linked"/> - <class name="horizontal"/> - </style> - <child> - <object class="GtkButton" id="button_take_photo"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="halign">center</property> - <property name="valign">center</property> - <property name="image">image_camera_photo</property> - <property name="always_show_image">True</property> - <property name="tooltip-text" translatable="yes">Take photo</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> + + <!-- initial state: take photo or choose image --> <child> - <object class="GtkButton" id="button_trash_avatar"> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="halign">center</property> - <property name="valign">center</property> - <property name="image">image_trash_symbolic</property> - <property name="always_show_image">True</property> - <property name="tooltip-text" translatable="yes">Delete current image</property> - </object> - <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkButton" id="button_set_avatar"> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="halign">center</property> - <property name="valign">center</property> - <property name="image">image_select_symbolic</property> - <property name="tooltip-text" translatable="yes">Set selection as image</property> + <object class="GtkBox" id="button_box_current"> + <style> + <class name="linked"/> + </style> + <child> + <object class="GtkButton" id="button_start_camera"> + <property name="visible">True</property> + <property name="receives_default">True</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="image">image_camera_photo</property> + <property name="tooltip-text" translatable="yes">Take photo</property> + </object> + </child> + <child> + <object class="GtkButton" id="button_choose_picture"> + <property name="visible">True</property> + <property name="receives_default">True</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="image">image_add_symbolic</property> + <property name="tooltip-text" translatable="yes">Choose image from file</property> + </object> + </child> </object> <packing> <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> + <property name="fill">False</property> </packing> </child> + + <!-- 2nd state: snap photo or return --> <child> - <object class="GtkButton" id="button_choose_picture"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="halign">center</property> - <property name="valign">center</property> - <property name="image">image_add_symbolic</property> - <property name="tooltip-text" translatable="yes">Choose image from file</property> + <object class="GtkBox" id="button_box_photo"> + <style> + <class name="linked"/> + </style> + <child> + <object class="GtkButton" id="button_take_photo"> + <property name="visible">True</property> + <property name="receives_default">True</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="image">image_take_photo</property> + <property name="tooltip-text" translatable="yes">Take photo</property> + </object> + </child> + <child> + <object class="GtkButton" id="button_return_photo"> + <property name="visible">True</property> + <property name="receives_default">True</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="image">image_return_photo</property> + <property name="tooltip-text" translatable="yes">Return</property> + </object> + </child> </object> <packing> - <property name="expand">True</property> - <property name="fill">True</property> - <property name="position">3</property> + <property name="expand">False</property> + <property name="fill">False</property> </packing> </child> + + <!-- 3rd state: select photo area or return --> <child> - <object class="GtkButton" id="button_export_avatar"> - <property name="can_focus">True</property> - <property name="receives_default">True</property> - <property name="image">image_save_symbolic</property> - <property name="tooltip-text" translatable="yes">Save image to file</property> + <object class="GtkBox" id="button_box_edit"> + <style> + <class name="linked"/> + </style> + <child> + <object class="GtkButton" id="button_set_avatar"> + <property name="visible">True</property> + <property name="receives_default">True</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="image">image_select_area</property> + <property name="tooltip-text" translatable="yes">Set selection as image</property> + </object> + </child> + <child> + <object class="GtkButton" id="button_return_edit"> + <property name="visible">True</property> + <property name="receives_default">True</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="image">image_return_edit</property> + <property name="tooltip-text" translatable="yes">Return</property> + </object> + </child> </object> <packing> <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">4</property> + <property name="fill">False</property> </packing> </child> + </object> <packing> <property name="expand">False</property>