Commit 17023e99 authored by Paolo Bacchilega's avatar Paolo Bacchilega
Browse files

location chooser: added ability to browse and select a location

parent d2958c99
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* GThumb
*
* Copyright (C) 2019 The Free Software Foundation, 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 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/>.
*/
#include <config.h>
#include <stdlib.h>
#include <glib/gi18n.h>
#include <gtk/gtk.h>
#include "glib-utils.h"
#include "gtk-utils.h"
#include "gth-file-data.h"
#include "gth-vfs-tree.h"
#include "gth-location-chooser.h"
#include "gth-location-chooser-dialog.h"
#include "gth-main.h"
#define MIN_WIDTH 600
#define MIN_HEIGHT 600
struct _GthLocationChooserDialogPrivate {
GFile *folder;
GtkWidget *folder_tree;
GtkWidget *entry;
gulong entry_changed_id;
};
G_DEFINE_TYPE_WITH_CODE (GthLocationChooserDialog,
gth_location_chooser_dialog,
GTK_TYPE_DIALOG,
G_ADD_PRIVATE (GthLocationChooserDialog))
static void
gth_location_chooser_dialog_finalize (GObject *object)
{
GthLocationChooserDialog *self;
self = GTH_LOCATION_CHOOSER_DIALOG (object);
_g_object_unref (self->priv->folder);
G_OBJECT_CLASS (gth_location_chooser_dialog_parent_class)->finalize (object);
}
static void
gth_location_chooser_dialog_class_init (GthLocationChooserDialogClass *class)
{
GObjectClass *object_class;
object_class = (GObjectClass*) class;
object_class->finalize = gth_location_chooser_dialog_finalize;
}
static void
gth_location_chooser_dialog_init (GthLocationChooserDialog *self)
{
self->priv = gth_location_chooser_dialog_get_instance_private (self);
self->priv->folder = NULL;
self->priv->entry_changed_id = 0;
}
static void
_set_folder (GthLocationChooserDialog *self,
GFile *folder)
{
if (self->priv->folder != folder) {
_g_object_unref (self->priv->folder);
self->priv->folder = _g_object_ref (folder);
}
if (self->priv->folder != NULL) {
g_signal_handler_block (self->priv->entry, self->priv->entry_changed_id);
gth_location_chooser_set_current (GTH_LOCATION_CHOOSER (self->priv->entry), self->priv->folder);
g_signal_handler_unblock (self->priv->entry, self->priv->entry_changed_id);
}
gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, (self->priv->folder != NULL));
}
static void
folder_tree_changed_cb (GthVfsTree *tree,
gpointer user_data)
{
GthLocationChooserDialog *self = user_data;
_set_folder (self, gth_vfs_tree_get_folder (tree));
}
static void
location_entry_changed_cb (GthLocationChooser *entry,
gpointer user_data)
{
GthLocationChooserDialog *self = user_data;
GFile *folder;
folder = gth_location_chooser_get_current (entry);
if (_g_file_equal_uris (folder, gth_folder_tree_get_root (GTH_FOLDER_TREE (self->priv->folder_tree)))) {
gtk_tree_view_collapse_all (GTK_TREE_VIEW (self->priv->folder_tree));
_set_folder (self, NULL);
}
else
gth_location_chooser_dialog_set_folder (self, folder);
}
static void
hidden_files_toggled_cb (GtkToggleButton *togglebutton,
gpointer user_data)
{
GthLocationChooserDialog *self = user_data;
gth_vfs_tree_set_show_hidden (GTH_VFS_TREE (self->priv->folder_tree), gtk_toggle_button_get_active (togglebutton));
}
static void
_gth_location_chooser_dialog_construct (GthLocationChooserDialog *self)
{
GtkWidget *vbox;
GtkWidget *scrolled_window;
GtkWidget *check_button;
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
gtk_widget_show (vbox);
gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), vbox, TRUE, TRUE, 0);
self->priv->entry = g_object_new (GTH_TYPE_LOCATION_CHOOSER,
"show-entry-points", FALSE,
"show-root", TRUE,
NULL);
self->priv->entry_changed_id =
g_signal_connect (self->priv->entry,
"changed",
G_CALLBACK (location_entry_changed_cb),
self);
gtk_widget_show (self->priv->entry);
gtk_box_pack_start (GTK_BOX (vbox), self->priv->entry, FALSE, FALSE, 0);
gtk_box_set_spacing (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (self))), 5);
gtk_container_set_border_width (GTK_CONTAINER (self), 5);
_gtk_dialog_add_to_window_group (GTK_DIALOG (self));
gtk_dialog_add_button (GTK_DIALOG (self), _GTK_LABEL_CANCEL, GTK_RESPONSE_CANCEL);
gtk_dialog_add_button (GTK_DIALOG (self), _GTK_LABEL_OK, GTK_RESPONSE_OK);
gtk_dialog_set_default_response (GTK_DIALOG (self), GTK_RESPONSE_OK);
_gtk_dialog_add_class_to_response (GTK_DIALOG (self), GTK_RESPONSE_OK, GTK_STYLE_CLASS_SUGGESTED_ACTION);
gtk_dialog_set_response_sensitive (GTK_DIALOG (self), GTK_RESPONSE_OK, FALSE);
scrolled_window = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_POLICY_AUTOMATIC,
GTK_POLICY_AUTOMATIC);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled_window),
GTK_SHADOW_IN);
gtk_widget_set_size_request (scrolled_window, MIN_WIDTH, MIN_HEIGHT);
gtk_widget_show (scrolled_window);
self->priv->folder_tree = gth_vfs_tree_new (NULL);
g_signal_connect (self->priv->folder_tree,
"changed",
G_CALLBACK (folder_tree_changed_cb),
self);
gtk_widget_show (self->priv->folder_tree);
gtk_container_add (GTK_CONTAINER (scrolled_window), self->priv->folder_tree);
gtk_box_pack_start (GTK_BOX (vbox), scrolled_window, TRUE, TRUE, 0);
check_button = gtk_check_button_new_with_label (_("Hidden Files"));
g_signal_connect (check_button,
"toggled",
G_CALLBACK (hidden_files_toggled_cb),
self);
gtk_widget_show (check_button);
gtk_box_pack_start (GTK_BOX (vbox), check_button, TRUE, TRUE, 0);
_set_folder (self, NULL);
}
GtkWidget *
gth_location_chooser_dialog_new (const char *title,
GtkWindow *parent)
{
GthLocationChooserDialog *self;
self = g_object_new (GTH_TYPE_LOCATION_CHOOSER_DIALOG,
"title", title,
"transient-for", parent,
"modal", TRUE,
"resizable", TRUE,
"use-header-bar", _gtk_settings_get_dialogs_use_header (),
NULL);
_gth_location_chooser_dialog_construct (self);
return (GtkWidget *) self;
}
void
gth_location_chooser_dialog_set_folder (GthLocationChooserDialog *self,
GFile *folder)
{
g_return_if_fail (GTH_IS_LOCATION_CHOOSER_DIALOG (self));
g_return_if_fail (folder != NULL);
gth_vfs_tree_set_folder (GTH_VFS_TREE (self->priv->folder_tree), folder);
}
GFile *
gth_location_chooser_dialog_get_folder (GthLocationChooserDialog *self)
{
g_return_val_if_fail (GTH_IS_LOCATION_CHOOSER_DIALOG (self), NULL);
return self->priv->folder;
}
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* GThumb
*
* Copyright (C) 2019 Free Software Foundation, 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 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 GTH_LOCATION_CHOOSER_DIALOG_H
#define GTH_LOCATION_CHOOSER_DIALOG_H
#include <glib-object.h>
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define GTH_TYPE_LOCATION_CHOOSER_DIALOG (gth_location_chooser_dialog_get_type ())
#define GTH_LOCATION_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTH_TYPE_LOCATION_CHOOSER_DIALOG, GthLocationChooserDialog))
#define GTH_LOCATION_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTH_TYPE_LOCATION_CHOOSER_DIALOG, GthLocationChooserDialogClass))
#define GTH_IS_LOCATION_CHOOSER_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTH_TYPE_LOCATION_CHOOSER_DIALOG))
#define GTH_IS_LOCATION_CHOOSER_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTH_TYPE_LOCATION_CHOOSER_DIALOG))
#define GTH_LOCATION_CHOOSER_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS((o), GTH_TYPE_LOCATION_CHOOSER_DIALOG, GthLocationChooserDialogClass))
typedef struct _GthLocationChooserDialog GthLocationChooserDialog;
typedef struct _GthLocationChooserDialogPrivate GthLocationChooserDialogPrivate;
typedef struct _GthLocationChooserDialogClass GthLocationChooserDialogClass;
struct _GthLocationChooserDialog {
GtkDialog __parent;
GthLocationChooserDialogPrivate *priv;
};
struct _GthLocationChooserDialogClass {
GtkDialogClass __parent_class;
};
GType gth_location_chooser_dialog_get_type (void) G_GNUC_CONST;
GtkWidget * gth_location_chooser_dialog_new (const char *title,
GtkWindow *parent);
void gth_location_chooser_dialog_set_folder (GthLocationChooserDialog *self,
GFile *folder);
GFile * gth_location_chooser_dialog_get_folder (GthLocationChooserDialog *self);
G_END_DECLS
#endif /* GTH_LOCATION_CHOOSER_DIALOG_H */
......@@ -28,6 +28,7 @@
#include "glib-utils.h"
#include "gth-file-source.h"
#include "gth-location-chooser.h"
#include "gth-location-chooser-dialog.h"
#include "gth-main.h"
#include "gtk-utils.h"
#include "pixbuf-utils.h"
......@@ -40,7 +41,8 @@ enum {
ITEM_TYPE_NONE,
ITEM_TYPE_SEPARATOR,
ITEM_TYPE_LOCATION,
ITEM_TYPE_ENTRY_POINT
ITEM_TYPE_ENTRY_POINT,
ITEM_TYPE_CHOOSE_LOCATION
};
enum {
......@@ -55,6 +57,7 @@ enum {
enum {
PROP_0,
PROP_SHOW_ENTRY_POINTS,
PROP_SHOW_ROOT,
PROP_RELIEF
};
......@@ -73,6 +76,7 @@ struct _GthLocationChooserPrivate {
guint update_entry_list_id;
guint update_location_list_id;
gboolean show_entry_points;
gboolean show_root;
GtkReliefStyle relief;
gboolean reload;
};
......@@ -101,6 +105,9 @@ gth_location_chooser_set_property (GObject *object,
case PROP_SHOW_ENTRY_POINTS:
gth_location_chooser_set_show_entry_points (self, g_value_get_boolean (value));
break;
case PROP_SHOW_ROOT:
self->priv->show_root = g_value_get_boolean (value);
break;
case PROP_RELIEF:
gth_location_chooser_set_relief (self, g_value_get_enum (value));
break;
......@@ -124,6 +131,9 @@ gth_location_chooser_get_property (GObject *object,
case PROP_SHOW_ENTRY_POINTS:
g_value_set_boolean (value, self->priv->show_entry_points);
break;
case PROP_SHOW_ROOT:
g_value_set_boolean (value, self->priv->show_root);
break;
case PROP_RELIEF:
g_value_set_enum (value, self->priv->relief);
break;
......@@ -193,6 +203,44 @@ get_nth_separator_pos (GthLocationChooser *self,
}
static gboolean
get_iter_from_current_file_entries (GthLocationChooser *self,
GFile *file,
GtkTreeIter *iter)
{
gboolean found = FALSE;
char *uri;
if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->model), iter))
return FALSE;
uri = g_file_get_uri (file);
do {
int item_type = ITEM_TYPE_NONE;
char *list_uri;
gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
iter,
TYPE_COLUMN, &item_type,
URI_COLUMN, &list_uri,
-1);
if (item_type == ITEM_TYPE_SEPARATOR)
break;
if (same_uri (uri, list_uri)) {
found = TRUE;
g_free (list_uri);
break;
}
g_free (list_uri);
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->model), iter));
g_free (uri);
return found;
}
static void
combo_changed_cb (GtkComboBox *widget,
gpointer user_data)
......@@ -211,7 +259,27 @@ combo_changed_cb (GtkComboBox *widget,
URI_COLUMN, &uri,
-1);
if (uri != NULL) {
if (item_type == ITEM_TYPE_CHOOSE_LOCATION) {
GtkWidget *dialog;
dialog = gth_location_chooser_dialog_new (_("Location"), _gtk_widget_get_toplevel_if_window (GTK_WIDGET (widget)));
if (self->priv->location != NULL)
gth_location_chooser_dialog_set_folder (GTH_LOCATION_CHOOSER_DIALOG (dialog), self->priv->location);
switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
case GTK_RESPONSE_OK:
gth_location_chooser_set_current (self, gth_location_chooser_dialog_get_folder (GTH_LOCATION_CHOOSER_DIALOG (dialog)));
break;
default:
/* reset the previous value. */
gth_location_chooser_set_current (self, self->priv->location);
break;
}
gtk_widget_destroy (dialog);
}
else if (uri != NULL) {
GFile *file;
file = g_file_new_for_uri (uri);
......@@ -256,12 +324,14 @@ add_file_source_entries (GthLocationChooser *self,
static void
clear_entry_point_list (GthLocationChooser *self)
clear_items_from_separator (GthLocationChooser *self,
int nth_separator,
gboolean stop_at_next_separator)
{
int first_position;
int i;
if (! get_nth_separator_pos (self, 1, &first_position))
if (! get_nth_separator_pos (self, nth_separator, &first_position))
return;
for (i = first_position + 1; TRUE; i++) {
......@@ -269,8 +339,19 @@ clear_entry_point_list (GthLocationChooser *self)
GtkTreeIter iter;
path = gtk_tree_path_new_from_indices (first_position + 1, -1);
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (self->priv->model), &iter, path))
if (gtk_tree_model_get_iter (GTK_TREE_MODEL (self->priv->model), &iter, path)) {
if (stop_at_next_separator) {
int item_type = ITEM_TYPE_NONE;
gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
&iter,
TYPE_COLUMN, &item_type,
-1);
if (item_type == ITEM_TYPE_SEPARATOR)
break;
}
gtk_tree_store_remove (self->priv->model, &iter);
}
else
break;
......@@ -279,6 +360,13 @@ clear_entry_point_list (GthLocationChooser *self)
}
static void
clear_entry_point_list (GthLocationChooser *self)
{
clear_items_from_separator (self, 1, TRUE);
}
static void
update_entry_point_list (GthLocationChooser *self)
{
......@@ -322,6 +410,22 @@ update_entry_point_list (GthLocationChooser *self)
ITEM_TYPE_ENTRY_POINT);
}
if (! get_nth_separator_pos (self, 2, &first_position)) {
GtkTreeIter iter;
gtk_tree_store_append (self->priv->model, &iter, NULL);
gtk_tree_store_set (self->priv->model, &iter,
TYPE_COLUMN, ITEM_TYPE_SEPARATOR,
-1);
gtk_tree_store_append (self->priv->model, &iter, NULL);
gtk_tree_store_set (self->priv->model, &iter,
TYPE_COLUMN, ITEM_TYPE_CHOOSE_LOCATION,
NAME_COLUMN, _("Other…"),
ELLIPSIZE_COLUMN, FALSE,
-1);
}
_g_object_list_unref (entry_points);
}
......@@ -379,44 +483,6 @@ delete_current_file_entries (GthLocationChooser *self)
}
static gboolean
get_iter_from_current_file_entries (GthLocationChooser *self,
GFile *file,
GtkTreeIter *iter)
{
gboolean found = FALSE;
char *uri;
if (! gtk_tree_model_get_iter_first (GTK_TREE_MODEL (self->priv->model), iter))
return FALSE;
uri = g_file_get_uri (file);
do {
int item_type = ITEM_TYPE_NONE;
char *list_uri;
gtk_tree_model_get (GTK_TREE_MODEL (self->priv->model),
iter,
TYPE_COLUMN, &item_type,
URI_COLUMN, &list_uri,
-1);
if (item_type == ITEM_TYPE_SEPARATOR)
break;
if (same_uri (uri, list_uri)) {
found = TRUE;
g_free (list_uri);
break;
}
g_free (list_uri);
}
while (gtk_tree_model_iter_next (GTK_TREE_MODEL (self->priv->model), iter));
g_free (uri);
return found;
}
static void
update_location_list (gpointer user_data)
{
......@@ -459,6 +525,24 @@ update_location_list (gpointer user_data)
g_object_unref (info);
}
if (self->priv->show_root) {
GIcon *icon;
icon = g_themed_icon_new ("computer-symbolic");
gtk_tree_store_insert (self->priv->model,
&iter,
NULL,
position++);
gtk_tree_store_set (self->priv->model, &iter,
TYPE_COLUMN, ITEM_TYPE_LOCATION,
ICON_COLUMN, icon,
NAME_COLUMN, _("Locations"),
URI_COLUMN, "gthumb-vfs:///",
-1);
_g_object_unref (icon);
}
_g_object_list_unref (list);
}
......@@ -505,11 +589,17 @@ gth_location_chooser_class_init (GthLocationChooserClass *klass)
g_object_class_install_property (object_class,
PROP_SHOW_ENTRY_POINTS,
g_param_spec_boolean ("show-entry-points",
"Show entry points",
"Whether to show the entry points in the list",
TRUE,
G_PARAM_READWRITE));
"Show entry points",
"Whether to show the entry points in the list",
TRUE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_SHOW_ROOT,
g_param_spec_boolean ("show-root",
"Show the VFS root",
"Whether to show the VFS root in the list",
FALSE,
G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_RELIEF,
g_param_spec_enum ("relief",
......@@ -544,6 +634,7 @@ gth_location_chooser_init (GthLocationChooser *self)
self->priv = gth_location_chooser_get_instance_private (self);
self->priv->entry_points_changed_id = 0;
self->priv->show_entry_points = TRUE;
self->priv->show_root = FALSE;
self->priv->relief = GTK_RELIEF_NORMAL;
self->priv->reload = FALSE;
......@@ -647,7 +738,7 @@ gth_location_chooser_set_show_entry_points (GthLocationChooser *self,
g_source_remove (self->priv->entry_points_changed_id);
self->priv->entry_points_changed_id = 0;
}
clear_entry_point_list (self);
clear_items_from_separator (self, 1, FALSE);
}
g_object_notify (G_OBJECT (self), "show-entry-points");
......@@ -665,6 +756,9 @@ void
gth_location_chooser_set_current (GthLocationChooser *self,
GFile *file)
{
if (file == NULL)
return;
if (file != self->priv->location) {
if (self->priv->file_source != NULL)
g_object_unref (self->priv->file_source);
......
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
* GThumb
*
* Copyright (C) 2019 Free Software Foundation, Inc.
*