Compare commits

...

5 Commits

Author SHA1 Message Date
Matthias Clasen
e2de76d465 More UI refinements 2015-04-10 14:04:40 -04:00
Matthias Clasen
0bb12749d1 UI refinements 2015-04-10 13:12:46 -04:00
Matthias Clasen
24e1ecce23 Trivial formatting fix 2015-04-10 08:02:57 -04:00
Matthias Clasen
86c97fd710 Remove no longer needed in-tree editing 2015-04-09 21:03:13 -04:00
Matthias Clasen
5e35d3c8de Use a popover for creating new folders 2015-04-09 20:56:54 -04:00
2 changed files with 204 additions and 109 deletions

View File

@@ -229,6 +229,10 @@ struct _GtkFileChooserWidgetPrivate {
GtkWidget *browse_path_bar_hbox; GtkWidget *browse_path_bar_hbox;
GtkSizeGroup *browse_path_bar_size_group; GtkSizeGroup *browse_path_bar_size_group;
GtkWidget *browse_path_bar; GtkWidget *browse_path_bar;
GtkWidget *new_folder_name_entry;
GtkWidget *new_folder_create_button;
GtkWidget *new_folder_error_label;
GtkWidget *new_folder_popover;
GtkFileSystemModel *browse_files_model; GtkFileSystemModel *browse_files_model;
char *browse_files_last_selected_name; char *browse_files_last_selected_name;
@@ -301,9 +305,6 @@ struct _GtkFileChooserWidgetPrivate {
guint location_changed_id; guint location_changed_id;
GSource *edited_idle;
char *edited_new_text;
gulong settings_signal_id; gulong settings_signal_id;
int icon_size; int icon_size;
@@ -655,8 +656,6 @@ gtk_file_chooser_widget_finalize (GObject *object)
g_free (priv->preview_display_name); g_free (priv->preview_display_name);
g_free (priv->edited_new_text);
impl->priv = NULL; impl->priv = NULL;
G_OBJECT_CLASS (gtk_file_chooser_widget_parent_class)->finalize (object); G_OBJECT_CLASS (gtk_file_chooser_widget_parent_class)->finalize (object);
@@ -937,113 +936,158 @@ new_folder_button_clicked (GtkButton *button,
gtk_tree_path_free (path); gtk_tree_path_free (path);
} }
static GSource * static void
add_idle_while_impl_is_alive (GtkFileChooserWidget *impl, GCallback callback) new_folder_popover_active (GtkWidget *button,
{ GParamSpec *pspec,
GSource *source; GtkFileChooserWidget *impl)
source = g_idle_source_new ();
g_source_set_closure (source,
g_cclosure_new_object (callback, G_OBJECT (impl)));
g_source_attach (source, NULL);
return source;
}
/* Idle handler for creating a new folder after editing its name cell, or for
* canceling the editing.
*/
static gboolean
edited_idle_cb (GtkFileChooserWidget *impl)
{ {
GtkFileChooserWidgetPrivate *priv = impl->priv; GtkFileChooserWidgetPrivate *priv = impl->priv;
gdk_threads_enter (); gtk_entry_set_text (GTK_ENTRY (priv->new_folder_name_entry), "");
gtk_widget_set_sensitive (priv->new_folder_create_button, FALSE);
g_source_destroy (priv->edited_idle); gtk_label_set_text (GTK_LABEL (priv->new_folder_error_label), "");
priv->edited_idle = NULL; }
_gtk_file_system_model_remove_editable (priv->browse_files_model); struct FileExistsData
g_object_set (priv->list_name_renderer, "editable", FALSE, NULL); {
GtkFileChooserWidget *impl;
gboolean file_exists_and_is_not_folder;
GFile *parent_file;
GFile *file;
};
gtk_widget_set_sensitive (priv->browse_new_folder_button, TRUE); static void
name_exists_get_info_cb (GCancellable *cancellable,
GFileInfo *info,
const GError *error,
gpointer user_data)
{
struct FileExistsData *data = user_data;
GtkFileChooserWidget *impl = data->impl;
GtkFileChooserWidgetPrivate *priv = impl->priv;
if (priv->edited_new_text /* not cancelled? */ if (cancellable != priv->file_exists_get_info_cancellable)
&& (strlen (priv->edited_new_text) != 0) goto out;
&& (strcmp (priv->edited_new_text, DEFAULT_NEW_FOLDER_NAME) != 0)) /* Don't create folder if name is empty or has not been edited */
priv->file_exists_get_info_cancellable = NULL;
if (g_cancellable_is_cancelled (cancellable))
goto out;
if (info != NULL)
{ {
GError *error = NULL; const gchar *msg;
GFile *file;
file = g_file_get_child_for_display_name (priv->current_folder, if (_gtk_file_info_consider_as_directory (info))
priv->edited_new_text, msg = _("A folder with that name already exists");
&error);
if (file)
{
GError *error = NULL;
if (g_file_make_directory (file, NULL, &error))
change_folder_and_display_error (impl, file, FALSE);
else
error_creating_folder_dialog (impl, file, error);
g_object_unref (file);
}
else else
error_creating_folder_dialog (impl, file, error); msg = _("A file with that name already exists");
g_free (priv->edited_new_text); gtk_widget_set_sensitive (priv->new_folder_create_button, FALSE);
priv->edited_new_text = NULL; gtk_label_set_text (GTK_LABEL (priv->new_folder_error_label), msg);
}
else
{
gtk_widget_set_sensitive (priv->new_folder_create_button, TRUE);
gtk_label_set_text (GTK_LABEL (priv->new_folder_error_label), "");
} }
gdk_threads_leave (); out:
g_object_unref (impl);
return FALSE; g_object_unref (data->file);
g_object_unref (data->parent_file);
g_free (data);
g_object_unref (cancellable);
} }
static void static void
queue_edited_idle (GtkFileChooserWidget *impl, check_valid_folder_name (GtkFileChooserWidget *impl,
const gchar *new_text) const gchar *name)
{ {
GtkFileChooserWidgetPrivate *priv = impl->priv; GtkFileChooserWidgetPrivate *priv = impl->priv;
/* We create the folder in an idle handler so that we don't modify the tree gtk_widget_set_sensitive (priv->new_folder_create_button, FALSE);
* just now.
*/
if (!priv->edited_idle) if (name[0] == '\0')
priv->edited_idle = add_idle_while_impl_is_alive (impl, G_CALLBACK (edited_idle_cb)); gtk_label_set_text (GTK_LABEL (priv->new_folder_error_label), "");
else if (strcmp (name, ".") == 0)
gtk_label_set_text (GTK_LABEL (priv->new_folder_error_label),
_("A folder cannot be called “.”"));
else if (strcmp (name, "..") == 0)
gtk_label_set_text (GTK_LABEL (priv->new_folder_error_label),
_("A folder cannot be called “..”"));
else if (strchr (name, '/') != NULL)
gtk_label_set_text (GTK_LABEL (priv->new_folder_error_label),
_("Folder names cannot contain “/”"));
else
{
GFile *file;
GError *error = NULL;
g_free (priv->edited_new_text); file = g_file_get_child_for_display_name (priv->current_folder, name, &error);
priv->edited_new_text = g_strdup (new_text); if (file == NULL)
{
gtk_label_set_text (GTK_LABEL (priv->new_folder_error_label), error->message);
g_error_free (error);
}
else
{
struct FileExistsData *data;
gtk_label_set_text (GTK_LABEL (priv->new_folder_error_label), "");
data = g_new0 (struct FileExistsData, 1);
data->impl = g_object_ref (impl);
data->parent_file = g_object_ref (priv->current_folder);
data->file = g_object_ref (file);
if (priv->file_exists_get_info_cancellable)
g_cancellable_cancel (priv->file_exists_get_info_cancellable);
priv->file_exists_get_info_cancellable =
_gtk_file_system_get_info (priv->file_system,
file,
"standard::type",
name_exists_get_info_cb,
data);
g_object_unref (file);
}
}
} }
/* Callback used from the text cell renderer when the new folder is named */
static void static void
renderer_edited_cb (GtkCellRendererText *cell_renderer_text, new_folder_name_changed (GtkEntry *entry,
const gchar *path, GtkFileChooserWidget *impl)
const gchar *new_text,
GtkFileChooserWidget *impl)
{ {
/* work around bug #154921 */ check_valid_folder_name (impl, gtk_entry_get_text (entry));
g_object_set (cell_renderer_text,
"mode", GTK_CELL_RENDERER_MODE_INERT, NULL);
queue_edited_idle (impl, new_text);
} }
/* Callback used from the text cell renderer when the new folder edition gets
* canceled.
*/
static void static void
renderer_editing_canceled_cb (GtkCellRendererText *cell_renderer_text, new_folder_create_clicked (GtkButton *button,
GtkFileChooserWidget *impl) GtkFileChooserWidget *impl)
{ {
/* work around bug #154921 */ GtkFileChooserWidgetPrivate *priv = impl->priv;
g_object_set (cell_renderer_text, GError *error = NULL;
"mode", GTK_CELL_RENDERER_MODE_INERT, NULL); GFile *file;
queue_edited_idle (impl, NULL); const gchar *name;
}
name = gtk_entry_get_text (GTK_ENTRY (priv->new_folder_name_entry));
file = g_file_get_child_for_display_name (priv->current_folder, name, &error);
gtk_widget_hide (priv->new_folder_popover);
if (file)
{
if (g_file_make_directory (file, NULL, &error))
change_folder_and_display_error (impl, file, FALSE);
else
error_creating_folder_dialog (impl, file, error);
g_object_unref (file);
}
else
error_creating_folder_dialog (impl, file, error);
}
struct selection_check_closure { struct selection_check_closure {
GtkFileChooserWidget *impl; GtkFileChooserWidget *impl;
@@ -5482,14 +5526,6 @@ should_respond_after_confirm_overwrite (GtkFileChooserWidget *impl,
} }
} }
struct FileExistsData
{
GtkFileChooserWidget *impl;
gboolean file_exists_and_is_not_folder;
GFile *parent_file;
GFile *file;
};
static void static void
name_entry_get_parent_info_cb (GCancellable *cancellable, name_entry_get_parent_info_cb (GCancellable *cancellable,
GFileInfo *info, GFileInfo *info,
@@ -7491,6 +7527,10 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_mtime_column); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_mtime_column);
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_size_column); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_size_column);
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_location_column); gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, list_location_column);
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, new_folder_name_entry);
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, new_folder_create_button);
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, new_folder_error_label);
gtk_widget_class_bind_template_child_private (widget_class, GtkFileChooserWidget, new_folder_popover);
/* And a *lot* of callbacks to bind ... */ /* And a *lot* of callbacks to bind ... */
gtk_widget_class_bind_template_callback (widget_class, browse_files_key_press_event_cb); gtk_widget_class_bind_template_callback (widget_class, browse_files_key_press_event_cb);
@@ -7503,8 +7543,6 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
gtk_widget_class_bind_template_callback (widget_class, file_list_drag_motion_cb); gtk_widget_class_bind_template_callback (widget_class, file_list_drag_motion_cb);
gtk_widget_class_bind_template_callback (widget_class, list_selection_changed); gtk_widget_class_bind_template_callback (widget_class, list_selection_changed);
gtk_widget_class_bind_template_callback (widget_class, list_cursor_changed); gtk_widget_class_bind_template_callback (widget_class, list_cursor_changed);
gtk_widget_class_bind_template_callback (widget_class, renderer_editing_canceled_cb);
gtk_widget_class_bind_template_callback (widget_class, renderer_edited_cb);
gtk_widget_class_bind_template_callback (widget_class, filter_combo_changed); gtk_widget_class_bind_template_callback (widget_class, filter_combo_changed);
gtk_widget_class_bind_template_callback (widget_class, new_folder_button_clicked); gtk_widget_class_bind_template_callback (widget_class, new_folder_button_clicked);
gtk_widget_class_bind_template_callback (widget_class, path_bar_clicked); gtk_widget_class_bind_template_callback (widget_class, path_bar_clicked);
@@ -7513,6 +7551,9 @@ gtk_file_chooser_widget_class_init (GtkFileChooserWidgetClass *class)
gtk_widget_class_bind_template_callback (widget_class, places_sidebar_show_enter_location_cb); gtk_widget_class_bind_template_callback (widget_class, places_sidebar_show_enter_location_cb);
gtk_widget_class_bind_template_callback (widget_class, search_entry_activate_cb); gtk_widget_class_bind_template_callback (widget_class, search_entry_activate_cb);
gtk_widget_class_bind_template_callback (widget_class, search_entry_stop_cb); gtk_widget_class_bind_template_callback (widget_class, search_entry_stop_cb);
gtk_widget_class_bind_template_callback (widget_class, new_folder_popover_active);
gtk_widget_class_bind_template_callback (widget_class, new_folder_name_changed);
gtk_widget_class_bind_template_callback (widget_class, new_folder_create_clicked);
} }
static void static void

View File

@@ -78,22 +78,13 @@
</packing> </packing>
</child> </child>
<child> <child>
<object class="GtkButton" id="browse_new_folder_button"> <object class="GtkMenuButton" id="browse_new_folder_button">
<property name="tooltip-text" translatable="yes">Create Folder</property> <property name="label" translatable="yes">Create _Folder</property>
<property name="can_focus">True</property> <property name="can_focus">True</property>
<property name="receives_default">True</property> <property name="receives_default">False</property>
<property name="use_underline">True</property> <property name="use_underline">True</property>
<signal name="clicked" handler="new_folder_button_clicked" swapped="no"/> <property name="popover">new_folder_popover</property>
<style> <signal name="notify::active" handler="new_folder_popover_active"/>
<class name="image-button"/>
</style>
<child>
<object class="GtkImage">
<property name="visible">True</property>
<property name="icon-name">list-add-symbolic</property>
<property name="icon-size">1</property>
</object>
</child>
</object> </object>
<packing> <packing>
<property name="expand">False</property> <property name="expand">False</property>
@@ -226,8 +217,6 @@
<child> <child>
<object class="GtkCellRendererText" id="list_name_renderer"> <object class="GtkCellRendererText" id="list_name_renderer">
<property name="ellipsize">end</property> <property name="ellipsize">end</property>
<signal name="edited" handler="renderer_edited_cb" swapped="no"/>
<signal name="editing-canceled" handler="renderer_editing_canceled_cb" swapped="no"/>
</object> </object>
</child> </child>
</object> </object>
@@ -372,4 +361,69 @@
<widget name="browse_new_folder_button"/> <widget name="browse_new_folder_button"/>
</widgets> </widgets>
</object> </object>
<object class="GtkPopover" id="new_folder_popover">
<property name="modal">True</property>
<child>
<object class="GtkGrid">
<property name="visible">True</property>
<property name="margin">10</property>
<property name="column-spacing">6</property>
<property name="row-spacing">6</property>
<property name="row-homogeneous">False</property>
<child>
<object class="GtkLabel">
<property name="visible">True</property>
<property name="label" translatable="yes">Folder Name</property>
<property name="halign">start</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">0</property>
<property name="width">2</property>
</packing>
</child>
<child>
<object class="GtkEntry" id="new_folder_name_entry">
<property name="visible">True</property>
<property name="width-chars">25</property>
<signal name="changed" handler="new_folder_name_changed"/>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkButton" id="new_folder_create_button">
<property name="visible">True</property>
<property name="sensitive">False</property>
<property name="label" translatable="yes">_Create</property>
<property name="use_underline">True</property>
<signal name="clicked" handler="new_folder_create_clicked"/>
<style>
<class name="suggested-action"/>
</style>
</object>
<packing>
<property name="left-attach">1</property>
<property name="top-attach">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="new_folder_error_label">
<property name="visible">True</property>
<property name="halign">start</property>
</object>
<packing>
<property name="left-attach">0</property>
<property name="top-attach">2</property>
<property name="width">2</property>
</packing>
</child>
</object>
</child>
</object>
</interface> </interface>