#define DEFAULT_TITLE N_("Select a File")
#define DESKTOP_DISPLAY_NAME N_("Desktop")
-#define FALLBACK_DISPLAY_NAME N_("(None)")
+#define FALLBACK_DISPLAY_NAME N_("(None)") /* this string is used in gtk+/gtk/tests/filechooser.c - change it there if you change it here */
#define FALLBACK_ICON_NAME "stock_unknown"
#define FALLBACK_ICON_SIZE 16
ROW_TYPE_CURRENT_FOLDER,
ROW_TYPE_OTHER_SEPARATOR,
ROW_TYPE_OTHER,
+ ROW_TYPE_EMPTY_SELECTION,
ROW_TYPE_INVALID = -1
}
RowType row_type);
static void model_free_row_data (GtkFileChooserButton *button,
GtkTreeIter *iter);
-static inline void model_add_special (GtkFileChooserButton *button);
-static inline void model_add_other (GtkFileChooserButton *button);
+static void model_add_special (GtkFileChooserButton *button);
+static void model_add_other (GtkFileChooserButton *button);
+static void model_add_empty_selection (GtkFileChooserButton *button);
static void model_add_volumes (GtkFileChooserButton *button,
GSList *volumes);
static void model_add_bookmarks (GtkFileChooserButton *button,
static void combo_box_changed_cb (GtkComboBox *combo_box,
gpointer user_data);
+static void combo_box_notify_popup_shown_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data);
static void button_clicked_cb (GtkButton *real_button,
gpointer user_data);
/* Keep in sync with columns enum, line 88 */
priv->model =
GTK_TREE_MODEL (gtk_list_store_new (NUM_COLUMNS,
- GDK_TYPE_PIXBUF, /* Icon */
- G_TYPE_STRING, /* Display Name */
- G_TYPE_CHAR, /* Row Type */
- G_TYPE_POINTER /* Volume || Path */,
- G_TYPE_BOOLEAN /* Is Folder? */,
- G_TYPE_POINTER /* cancellable */));
+ GDK_TYPE_PIXBUF, /* ICON_COLUMN */
+ G_TYPE_STRING, /* DISPLAY_NAME_COLUMN */
+ G_TYPE_CHAR, /* TYPE_COLUMN */
+ G_TYPE_POINTER /* DATA_COLUMN (Volume || Path) */,
+ G_TYPE_BOOLEAN /* IS_FOLDER_COLUMN */,
+ G_TYPE_POINTER /* CANCELLABLE_COLUMN */));
priv->combo_box = gtk_combo_box_new ();
- priv->combo_box_changed_id =
- g_signal_connect (priv->combo_box, "changed",
- G_CALLBACK (combo_box_changed_cb), button);
+ priv->combo_box_changed_id = g_signal_connect (priv->combo_box, "changed",
+ G_CALLBACK (combo_box_changed_cb), button);
+
+ g_signal_connect (priv->combo_box, "notify::popup-shown",
+ G_CALLBACK (combo_box_notify_popup_shown_cb), button);
+
gtk_box_pack_start (GTK_BOX (button), priv->combo_box, TRUE, TRUE, 0);
gtk_widget_set_halign (priv->combo_box, GTK_ALIGN_FILL);
priv->current_folder_while_inactive = g_object_ref (file);
+ update_combo_box (button);
+
g_signal_emit_by_name (button, "current-folder-changed");
return TRUE;
priv->selection_while_inactive = g_object_ref (file);
+ update_label_and_image (button);
+ update_combo_box (button);
+
return TRUE;
}
}
g_object_unref (priv->selection_while_inactive);
priv->selection_while_inactive = NULL;
}
+
+ update_label_and_image (button);
+ update_combo_box (button);
}
}
}
{
g_object_unref (priv->selection_while_inactive);
priv->selection_while_inactive = NULL;
+
+ update_label_and_image (button);
+ update_combo_box (button);
}
}
}
-static GSList *
-gtk_file_chooser_button_get_files (GtkFileChooser *chooser)
+static GFile *
+get_selected_file (GtkFileChooserButton *button)
{
- GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
GtkFileChooserButtonPrivate *priv = button->priv;
- GtkFileChooser *delegate;
-
- delegate = g_object_get_qdata (G_OBJECT (chooser),
- GTK_FILE_CHOOSER_DELEGATE_QUARK);
if (priv->active)
- return gtk_file_chooser_get_files (delegate);
+ return gtk_file_chooser_get_file (GTK_FILE_CHOOSER (priv->dialog));
else
{
- GSList *result;
-
- result = NULL;
-
if (priv->selection_while_inactive)
- result = g_slist_prepend (NULL, g_object_ref (priv->selection_while_inactive));
+ return g_object_ref (priv->selection_while_inactive);
else if (gtk_file_chooser_get_action (GTK_FILE_CHOOSER (priv->dialog)) == GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER)
{
/* If there is no "real" selection in SELECT_FOLDER mode, then we'll just return
* the current folder, since that is what GtkFileChooserDefault would do.
*/
if (priv->current_folder_while_inactive)
- result = g_slist_prepend (NULL, g_object_ref (priv->current_folder_while_inactive));
+ return g_object_ref (priv->current_folder_while_inactive);
}
-
- return result;
}
+
+ return NULL;
+}
+
+static GSList *
+gtk_file_chooser_button_get_files (GtkFileChooser *chooser)
+{
+ GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (chooser);
+
+ return g_slist_prepend (NULL, get_selected_file (button));
}
static gboolean
model_add_other (button);
+ model_add_empty_selection (button);
+
priv->filter_model = gtk_tree_model_filter_new (priv->model, NULL);
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
filter_model_visible_func,
g_signal_connect (priv->fs, "bookmarks-changed",
G_CALLBACK (fs_bookmarks_changed_cb), object);
+ update_label_and_image (button);
+ update_combo_box (button);
+
return object;
}
if (row_type == ROW_TYPE_OTHER)
return retval;
+ retval++;
+
+ if (row_type == ROW_TYPE_EMPTY_SELECTION)
+ return retval;
+
g_assert_not_reached ();
return -1;
}
g_object_unref (model_cancellable);
}
-static inline void
+static void
model_add_special (GtkFileChooserButton *button)
{
const gchar *homedir;
base_file = _gtk_file_system_volume_get_root (volume);
if (base_file != NULL)
{
- if (!g_file_is_native (base_file))
+ if (!_gtk_file_has_native_path (base_file))
{
g_object_unref (base_file);
continue;
file = l->data;
- if (g_file_is_native (file))
+ if (_gtk_file_has_native_path (file))
{
gtk_list_store_insert (store, &iter, pos);
gtk_list_store_set (store, &iter,
}
}
-static inline void
+static void
model_add_other (GtkFileChooserButton *button)
{
GtkListStore *store;
-1);
}
+static void
+model_add_empty_selection (GtkFileChooserButton *button)
+{
+ GtkListStore *store;
+ GtkTreeIter iter;
+ gint pos;
+
+ store = GTK_LIST_STORE (button->priv->model);
+ pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
+
+ gtk_list_store_insert (store, &iter, pos);
+ gtk_list_store_set (store, &iter,
+ ICON_COLUMN, NULL,
+ DISPLAY_NAME_COLUMN, _(FALLBACK_DISPLAY_NAME),
+ TYPE_COLUMN, ROW_TYPE_EMPTY_SELECTION,
+ DATA_COLUMN, NULL,
+ IS_FOLDER_COLUMN, FALSE,
+ -1);
+}
+
static void
model_remove_rows (GtkFileChooserButton *button,
gint pos,
}
/* Filter Model */
-static inline gboolean
+static gboolean
test_if_file_is_visible (GtkFileSystem *fs,
GFile *file,
gboolean local_only,
if (!file)
return FALSE;
- if (local_only && !g_file_is_native (file))
+ if (local_only && !_gtk_file_has_native_path (file))
return FALSE;
if (!is_folder)
if (base_file)
{
- if (!g_file_is_native (base_file))
+ if (!_gtk_file_has_native_path (base_file))
retval = FALSE;
g_object_unref (base_file);
}
}
}
break;
+ case ROW_TYPE_EMPTY_SELECTION:
+ {
+ gboolean popup_shown;
+
+ g_object_get (priv->combo_box,
+ "popup-shown", &popup_shown,
+ NULL);
+
+ if (popup_shown)
+ retval = FALSE;
+ else
+ {
+ GFile *selected;
+
+ /* When the combo box is not popped up... */
+
+ selected = get_selected_file (button);
+ if (selected)
+ retval = FALSE; /* ... nonempty selection means the ROW_TYPE_EMPTY_SELECTION is *not* visible... */
+ else
+ retval = TRUE; /* ... and empty selection means the ROW_TYPE_EMPTY_SELECTION *is* visible */
+
+ if (selected)
+ g_object_unref (selected);
+ }
+
+ break;
+ }
default:
retval = TRUE;
break;
return (type == ROW_TYPE_BOOKMARK_SEPARATOR ||
type == ROW_TYPE_CURRENT_FOLDER_SEPARATOR ||
type == ROW_TYPE_OTHER_SEPARATOR);
-}
+}
+
+static void
+select_combo_box_row_no_notify (GtkFileChooserButton *button, int pos)
+{
+ GtkFileChooserButtonPrivate *priv = button->priv;
+ GtkTreeIter iter, filter_iter;
+
+ gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
+ gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
+ &filter_iter, &iter);
+
+ g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
+ gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter);
+ g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id);
+}
static void
update_combo_box (GtkFileChooserButton *button)
{
GtkFileChooserButtonPrivate *priv = button->priv;
- GSList *files;
+ GFile *file;
GtkTreeIter iter;
gboolean row_found;
- gtk_tree_model_get_iter_first (priv->filter_model, &iter);
-
- files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (priv->dialog));
+ file = get_selected_file (button);
row_found = FALSE;
+ gtk_tree_model_get_iter_first (priv->filter_model, &iter);
+
do
{
gchar type;
case ROW_TYPE_SHORTCUT:
case ROW_TYPE_BOOKMARK:
case ROW_TYPE_CURRENT_FOLDER:
- row_found = (files &&
- files->data &&
- g_file_equal (data, files->data));
+ row_found = (file && g_file_equal (data, file));
break;
case ROW_TYPE_VOLUME:
{
base_file = _gtk_file_system_volume_get_root (data);
if (base_file)
{
- row_found = (files &&
- files->data &&
- g_file_equal (base_file, files->data));
+ row_found = (file && g_file_equal (base_file, file));
g_object_unref (base_file);
}
}
}
while (!row_found && gtk_tree_model_iter_next (priv->filter_model, &iter));
- /* If it hasn't been found already, update & select the current-folder row. */
- if (!row_found && files && files->data)
+ if (!row_found)
{
- GtkTreeIter filter_iter;
gint pos;
-
- model_update_current_folder (button, files->data);
- gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
- pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
- gtk_tree_model_iter_nth_child (priv->model, &iter, NULL, pos);
+ /* If it hasn't been found already, update & select the current-folder row. */
+ if (file)
+ {
+ model_update_current_folder (button, file);
+ pos = model_get_type_position (button, ROW_TYPE_CURRENT_FOLDER);
+ }
+ else
+ {
+ /* No selection; switch to that row */
- gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
- &filter_iter, &iter);
+ pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
+ }
- g_signal_handler_block (priv->combo_box, priv->combo_box_changed_id);
- gtk_combo_box_set_active_iter (GTK_COMBO_BOX (priv->combo_box), &filter_iter);
- g_signal_handler_unblock (priv->combo_box, priv->combo_box_changed_id);
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
+
+ select_combo_box_row_no_notify (button, pos);
}
- g_slist_foreach (files, (GFunc) g_object_unref, NULL);
- g_slist_free (files);
+ if (file)
+ g_object_unref (file);
}
/* Button */
{
GtkFileChooserButtonPrivate *priv = button->priv;
gchar *label_text;
- GSList *files;
+ GFile *file;
+
+ file = get_selected_file (button);
- files = gtk_file_chooser_get_files (GTK_FILE_CHOOSER (priv->dialog));
label_text = NULL;
if (priv->update_button_cancellable)
priv->update_button_cancellable = NULL;
}
- if (files && files->data)
+ if (file)
{
- GFile *file;
GtkFileSystemVolume *volume = NULL;
- file = files->data;
-
volume = _gtk_file_system_get_volume_for_file (priv->fs, file);
if (volume)
{
_gtk_file_system_volume_unref (volume);
if (label_text)
- goto out;
+ goto out;
}
if (g_file_is_native (file))
}
}
out:
- g_slist_foreach (files, (GFunc) g_object_unref, NULL);
- g_slist_free (files);
+
+ if (file)
+ g_object_unref (file);
if (label_text)
{
case ROW_TYPE_SHORTCUT:
case ROW_TYPE_BOOKMARK:
case ROW_TYPE_CURRENT_FOLDER:
- gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
if (data)
- gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog),
- data, NULL);
+ gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), data, NULL);
break;
case ROW_TYPE_VOLUME:
{
GFile *base_file;
- gtk_file_chooser_unselect_all (GTK_FILE_CHOOSER (priv->dialog));
base_file = _gtk_file_system_volume_get_root (data);
if (base_file)
{
- gtk_file_chooser_set_current_folder_file (GTK_FILE_CHOOSER (priv->dialog),
- base_file, NULL);
+ gtk_file_chooser_button_select_file (GTK_FILE_CHOOSER (button), base_file, NULL);
g_object_unref (base_file);
}
}
}
}
+/* Calback for the "notify::popup-shown" signal on the combo box.
+ * When the combo is popped up, we don't want the ROW_TYPE_EMPTY_SELECTION to be visible
+ * at all; otherwise we would be showing a "(None)" item in the combo box's popup.
+ *
+ * However, when the combo box is *not* popped up, we want the empty-selection row
+ * to be visible depending on the selection.
+ *
+ * Since all that is done through the filter_model_visible_func(), this means
+ * that we need to refilter the model when the combo box pops up - hence the
+ * present signal handler.
+ */
+static void
+combo_box_notify_popup_shown_cb (GObject *object,
+ GParamSpec *pspec,
+ gpointer user_data)
+{
+ GtkFileChooserButton *button = GTK_FILE_CHOOSER_BUTTON (user_data);
+ GtkFileChooserButtonPrivate *priv = button->priv;
+ gboolean popup_shown;
+
+ g_object_get (priv->combo_box,
+ "popup-shown", &popup_shown,
+ NULL);
+
+ /* Indicate that the ROW_TYPE_EMPTY_SELECTION will change visibility... */
+ gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (priv->filter_model));
+
+ /* If the combo box popup got dismissed, go back to showing the ROW_TYPE_EMPTY_SELECTION if needed */
+ if (!popup_shown)
+ {
+ GFile *selected = get_selected_file (button);
+
+ if (!selected)
+ {
+ int pos;
+
+ pos = model_get_type_position (button, ROW_TYPE_EMPTY_SELECTION);
+ select_combo_box_row_no_notify (button, pos);
+ }
+ else
+ g_object_unref (selected);
+ }
+}
+
/* Button */
static void
button_clicked_cb (GtkButton *real_button,
/* If the path isn't local but we're in local-only mode now, remove
* the custom-folder row */
- if (data && g_file_is_native (G_FILE (data)) &&
+ if (data && _gtk_file_has_native_path (G_FILE (data)) &&
gtk_file_chooser_get_local_only (GTK_FILE_CHOOSER (priv->dialog)))
{
pos--;
g_signal_emit_by_name (button, "current-folder-changed");
g_signal_emit_by_name (button, "selection-changed");
-
- update_label_and_image (button);
- update_combo_box (button);
}
else
{
priv->active = FALSE;
}
+ update_label_and_image (button);
+ update_combo_box (button);
+
gtk_widget_set_sensitive (priv->combo_box, TRUE);
gtk_widget_hide (priv->dialog);