mirror of
https://gitlab.gnome.org/GNOME/gtk.git
synced 2024-12-29 15:01:23 +00:00
386b63b85d
In 99.9% of all cases, these are just NULL, NULL. So just do away with these arguments, people can use the setters for the rare cases where they want the scrolled window to use a different adjustment.
668 lines
18 KiB
C
668 lines
18 KiB
C
#include <gtk/gtk.h>
|
|
|
|
#define FILE_INFO_TYPE_SELECTION (file_info_selection_get_type ())
|
|
|
|
G_DECLARE_FINAL_TYPE (FileInfoSelection, file_info_selection, FILE_INFO, SELECTION, GObject)
|
|
|
|
struct _FileInfoSelection
|
|
{
|
|
GObject parent_instance;
|
|
|
|
GListModel *model;
|
|
};
|
|
|
|
struct _FileInfoSelectionClass
|
|
{
|
|
GObjectClass parent_class;
|
|
};
|
|
|
|
static GType
|
|
file_info_selection_get_item_type (GListModel *list)
|
|
{
|
|
FileInfoSelection *self = FILE_INFO_SELECTION (list);
|
|
|
|
return g_list_model_get_item_type (self->model);
|
|
}
|
|
|
|
static guint
|
|
file_info_selection_get_n_items (GListModel *list)
|
|
{
|
|
FileInfoSelection *self = FILE_INFO_SELECTION (list);
|
|
|
|
return g_list_model_get_n_items (self->model);
|
|
}
|
|
|
|
static gpointer
|
|
file_info_selection_get_item (GListModel *list,
|
|
guint position)
|
|
{
|
|
FileInfoSelection *self = FILE_INFO_SELECTION (list);
|
|
|
|
return g_list_model_get_item (self->model, position);
|
|
}
|
|
|
|
static void
|
|
file_info_selection_list_model_init (GListModelInterface *iface)
|
|
{
|
|
iface->get_item_type = file_info_selection_get_item_type;
|
|
iface->get_n_items = file_info_selection_get_n_items;
|
|
iface->get_item = file_info_selection_get_item;
|
|
}
|
|
|
|
static gboolean
|
|
file_info_selection_is_selected (GtkSelectionModel *model,
|
|
guint position)
|
|
{
|
|
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
|
gpointer item;
|
|
|
|
item = g_list_model_get_item (self->model, position);
|
|
if (item == NULL)
|
|
return FALSE;
|
|
|
|
if (GTK_IS_TREE_LIST_ROW (item))
|
|
{
|
|
GtkTreeListRow *row = item;
|
|
item = gtk_tree_list_row_get_item (row);
|
|
g_object_unref (row);
|
|
}
|
|
|
|
return g_file_info_get_attribute_boolean (item, "filechooser::selected");
|
|
}
|
|
|
|
static void
|
|
file_info_selection_set_selected (FileInfoSelection *self,
|
|
guint position,
|
|
gboolean selected)
|
|
{
|
|
gpointer item;
|
|
|
|
item = g_list_model_get_item (self->model, position);
|
|
if (item == NULL)
|
|
return;
|
|
|
|
if (GTK_IS_TREE_LIST_ROW (item))
|
|
{
|
|
GtkTreeListRow *row = item;
|
|
item = gtk_tree_list_row_get_item (row);
|
|
g_object_unref (row);
|
|
}
|
|
|
|
g_file_info_set_attribute_boolean (item, "filechooser::selected", selected);
|
|
}
|
|
|
|
static gboolean
|
|
file_info_selection_select_item (GtkSelectionModel *model,
|
|
guint position,
|
|
gboolean exclusive)
|
|
{
|
|
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
|
|
|
if (exclusive)
|
|
{
|
|
guint i;
|
|
|
|
for (i = 0; i < g_list_model_get_n_items (self->model); i++)
|
|
file_info_selection_set_selected (self, i, i == position);
|
|
|
|
gtk_selection_model_selection_changed (model, 0, g_list_model_get_n_items (self->model));
|
|
}
|
|
else
|
|
{
|
|
file_info_selection_set_selected (self, position, TRUE);
|
|
|
|
gtk_selection_model_selection_changed (model, position, 1);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
file_info_selection_unselect_item (GtkSelectionModel *model,
|
|
guint position)
|
|
{
|
|
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
|
|
|
file_info_selection_set_selected (self, position, FALSE);
|
|
|
|
gtk_selection_model_selection_changed (model, position, 1);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
file_info_selection_select_range (GtkSelectionModel *model,
|
|
guint position,
|
|
guint n_items,
|
|
gboolean exclusive)
|
|
{
|
|
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
|
guint i;
|
|
|
|
if (exclusive)
|
|
for (i = 0; i < position; i++)
|
|
file_info_selection_set_selected (self, i, FALSE);
|
|
|
|
for (i = position; i < position + n_items; i++)
|
|
file_info_selection_set_selected (self, i, TRUE);
|
|
|
|
if (exclusive)
|
|
for (i = position + n_items; i < g_list_model_get_n_items (self->model); i++)
|
|
file_info_selection_set_selected (self, i, FALSE);
|
|
|
|
if (exclusive)
|
|
gtk_selection_model_selection_changed (model, 0, g_list_model_get_n_items (self->model));
|
|
else
|
|
gtk_selection_model_selection_changed (model, position, n_items);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
file_info_selection_unselect_range (GtkSelectionModel *model,
|
|
guint position,
|
|
guint n_items)
|
|
{
|
|
FileInfoSelection *self = FILE_INFO_SELECTION (model);
|
|
guint i;
|
|
|
|
for (i = position; i < position + n_items; i++)
|
|
file_info_selection_set_selected (self, i, FALSE);
|
|
|
|
gtk_selection_model_selection_changed (model, position, n_items);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
file_info_selection_selection_model_init (GtkSelectionModelInterface *iface)
|
|
{
|
|
iface->is_selected = file_info_selection_is_selected;
|
|
iface->select_item = file_info_selection_select_item;
|
|
iface->unselect_item = file_info_selection_unselect_item;
|
|
iface->select_range = file_info_selection_select_range;
|
|
iface->unselect_range = file_info_selection_unselect_range;
|
|
}
|
|
|
|
G_DEFINE_TYPE_EXTENDED (FileInfoSelection, file_info_selection, G_TYPE_OBJECT, 0,
|
|
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
|
|
file_info_selection_list_model_init)
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
|
|
file_info_selection_selection_model_init))
|
|
|
|
static void
|
|
file_info_selection_items_changed_cb (GListModel *model,
|
|
guint position,
|
|
guint removed,
|
|
guint added,
|
|
FileInfoSelection *self)
|
|
{
|
|
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
|
}
|
|
|
|
static void
|
|
file_info_selection_clear_model (FileInfoSelection *self)
|
|
{
|
|
if (self->model == NULL)
|
|
return;
|
|
|
|
g_signal_handlers_disconnect_by_func (self->model,
|
|
file_info_selection_items_changed_cb,
|
|
self);
|
|
g_clear_object (&self->model);
|
|
}
|
|
|
|
static void
|
|
file_info_selection_dispose (GObject *object)
|
|
{
|
|
FileInfoSelection *self = FILE_INFO_SELECTION (object);
|
|
|
|
file_info_selection_clear_model (self);
|
|
|
|
G_OBJECT_CLASS (file_info_selection_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
file_info_selection_class_init (FileInfoSelectionClass *klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
gobject_class->dispose = file_info_selection_dispose;
|
|
}
|
|
|
|
static void
|
|
file_info_selection_init (FileInfoSelection *self)
|
|
{
|
|
}
|
|
|
|
static FileInfoSelection *
|
|
file_info_selection_new (GListModel *model)
|
|
{
|
|
FileInfoSelection *result;
|
|
|
|
result = g_object_new (FILE_INFO_TYPE_SELECTION, NULL);
|
|
|
|
result->model = g_object_ref (model);
|
|
g_signal_connect (result->model, "items-changed",
|
|
G_CALLBACK (file_info_selection_items_changed_cb), result);
|
|
|
|
return result;
|
|
}
|
|
|
|
/*** ---------------------- ***/
|
|
|
|
GSList *pending = NULL;
|
|
guint active = 0;
|
|
|
|
static void
|
|
loading_cb (GtkDirectoryList *dir,
|
|
GParamSpec *pspec,
|
|
gpointer unused)
|
|
{
|
|
if (gtk_directory_list_is_loading (dir))
|
|
{
|
|
active++;
|
|
/* HACK: ensure loading finishes and the dir doesn't get destroyed */
|
|
g_object_ref (dir);
|
|
}
|
|
else
|
|
{
|
|
active--;
|
|
g_object_unref (dir);
|
|
|
|
while (active < 20 && pending)
|
|
{
|
|
GtkDirectoryList *dir2 = pending->data;
|
|
pending = g_slist_remove (pending, dir2);
|
|
gtk_directory_list_set_file (dir2, g_object_get_data (G_OBJECT (dir2), "file"));
|
|
g_object_unref (dir2);
|
|
}
|
|
}
|
|
}
|
|
|
|
static GtkDirectoryList *
|
|
create_directory_list (GFile *file)
|
|
{
|
|
GtkDirectoryList *dir;
|
|
|
|
dir = gtk_directory_list_new (G_FILE_ATTRIBUTE_STANDARD_TYPE
|
|
"," G_FILE_ATTRIBUTE_STANDARD_NAME
|
|
"," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
|
|
NULL);
|
|
gtk_directory_list_set_io_priority (dir, G_PRIORITY_DEFAULT_IDLE);
|
|
g_signal_connect (dir, "notify::loading", G_CALLBACK (loading_cb), NULL);
|
|
g_assert (!gtk_directory_list_is_loading (dir));
|
|
|
|
if (active > 20)
|
|
{
|
|
g_object_set_data_full (G_OBJECT (dir), "file", g_object_ref (file), g_object_unref);
|
|
pending = g_slist_prepend (pending, g_object_ref (dir));
|
|
}
|
|
else
|
|
{
|
|
gtk_directory_list_set_file (dir, file);
|
|
}
|
|
|
|
return dir;
|
|
}
|
|
|
|
static char *
|
|
get_file_path (GFileInfo *info)
|
|
{
|
|
GFile *file;
|
|
|
|
file = G_FILE (g_file_info_get_attribute_object (info, "standard::file"));
|
|
return g_file_get_path (file);
|
|
}
|
|
|
|
static GListModel *
|
|
create_list_model_for_directory (gpointer file)
|
|
{
|
|
GtkSortListModel *sort;
|
|
GtkDirectoryList *dir;
|
|
GtkSorter *sorter;
|
|
|
|
if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) != G_FILE_TYPE_DIRECTORY)
|
|
return NULL;
|
|
|
|
dir = create_directory_list (file);
|
|
sorter = gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING, NULL, 0, NULL, (GCallback) get_file_path, NULL, NULL));
|
|
sort = gtk_sort_list_model_new (G_LIST_MODEL (dir), sorter);
|
|
|
|
g_object_unref (sorter);
|
|
g_object_unref (dir);
|
|
|
|
return G_LIST_MODEL (sort);
|
|
}
|
|
|
|
typedef struct _RowData RowData;
|
|
struct _RowData
|
|
{
|
|
GtkWidget *expander;
|
|
GtkWidget *icon;
|
|
GtkWidget *name;
|
|
GCancellable *cancellable;
|
|
|
|
GtkTreeListRow *current_item;
|
|
};
|
|
|
|
static void row_data_notify_item (GtkListItem *item,
|
|
GParamSpec *pspec,
|
|
RowData *data);
|
|
static void
|
|
row_data_unbind (RowData *data)
|
|
{
|
|
if (data->current_item == NULL)
|
|
return;
|
|
|
|
if (data->cancellable)
|
|
{
|
|
g_cancellable_cancel (data->cancellable);
|
|
g_clear_object (&data->cancellable);
|
|
}
|
|
|
|
g_clear_object (&data->current_item);
|
|
}
|
|
|
|
static void
|
|
row_data_update_info (RowData *data,
|
|
GFileInfo *info)
|
|
{
|
|
GIcon *icon;
|
|
const char *thumbnail_path;
|
|
|
|
thumbnail_path = g_file_info_get_attribute_byte_string (info, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
|
|
if (thumbnail_path)
|
|
{
|
|
/* XXX: not async */
|
|
GFile *thumbnail_file = g_file_new_for_path (thumbnail_path);
|
|
icon = g_file_icon_new (thumbnail_file);
|
|
g_object_unref (thumbnail_file);
|
|
}
|
|
else
|
|
{
|
|
icon = g_file_info_get_icon (info);
|
|
}
|
|
|
|
gtk_widget_set_visible (data->icon, icon != NULL);
|
|
gtk_image_set_from_gicon (GTK_IMAGE (data->icon), icon);
|
|
}
|
|
|
|
static void
|
|
copy_attribute (GFileInfo *to,
|
|
GFileInfo *from,
|
|
const gchar *attribute)
|
|
{
|
|
GFileAttributeType type;
|
|
gpointer value;
|
|
|
|
if (g_file_info_get_attribute_data (from, attribute, &type, &value, NULL))
|
|
g_file_info_set_attribute (to, attribute, type, value);
|
|
}
|
|
|
|
static void
|
|
row_data_got_thumbnail_info_cb (GObject *source,
|
|
GAsyncResult *res,
|
|
gpointer _data)
|
|
{
|
|
RowData *data = _data; /* invalid if operation was cancelled */
|
|
GFile *file = G_FILE (source);
|
|
GFileInfo *queried, *info;
|
|
|
|
queried = g_file_query_info_finish (file, res, NULL);
|
|
if (queried == NULL)
|
|
return;
|
|
|
|
/* now we know row is valid */
|
|
|
|
info = gtk_tree_list_row_get_item (data->current_item);
|
|
|
|
copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAIL_PATH);
|
|
copy_attribute (info, queried, G_FILE_ATTRIBUTE_THUMBNAILING_FAILED);
|
|
copy_attribute (info, queried, G_FILE_ATTRIBUTE_STANDARD_ICON);
|
|
|
|
g_object_unref (queried);
|
|
|
|
row_data_update_info (data, info);
|
|
|
|
g_clear_object (&data->cancellable);
|
|
}
|
|
|
|
static void
|
|
row_data_bind (RowData *data,
|
|
GtkTreeListRow *item)
|
|
{
|
|
GFileInfo *info;
|
|
|
|
row_data_unbind (data);
|
|
|
|
if (item == NULL)
|
|
return;
|
|
|
|
data->current_item = g_object_ref (item);
|
|
|
|
gtk_tree_expander_set_list_row (GTK_TREE_EXPANDER (data->expander), item);
|
|
|
|
info = gtk_tree_list_row_get_item (item);
|
|
|
|
if (!g_file_info_has_attribute (info, "filechooser::queried"))
|
|
{
|
|
data->cancellable = g_cancellable_new ();
|
|
g_file_info_set_attribute_boolean (info, "filechooser::queried", TRUE);
|
|
g_file_query_info_async (G_FILE (g_file_info_get_attribute_object (info, "standard::file")),
|
|
G_FILE_ATTRIBUTE_THUMBNAIL_PATH ","
|
|
G_FILE_ATTRIBUTE_THUMBNAILING_FAILED ","
|
|
G_FILE_ATTRIBUTE_STANDARD_ICON,
|
|
G_FILE_QUERY_INFO_NONE,
|
|
G_PRIORITY_DEFAULT,
|
|
data->cancellable,
|
|
row_data_got_thumbnail_info_cb,
|
|
data);
|
|
}
|
|
|
|
row_data_update_info (data, info);
|
|
|
|
gtk_label_set_label (GTK_LABEL (data->name), g_file_info_get_display_name (info));
|
|
|
|
g_object_unref (info);
|
|
}
|
|
|
|
static void
|
|
row_data_notify_item (GtkListItem *item,
|
|
GParamSpec *pspec,
|
|
RowData *data)
|
|
{
|
|
row_data_bind (data, gtk_list_item_get_item (item));
|
|
}
|
|
|
|
static void
|
|
row_data_free (gpointer _data)
|
|
{
|
|
RowData *data = _data;
|
|
|
|
row_data_unbind (data);
|
|
|
|
g_slice_free (RowData, data);
|
|
}
|
|
|
|
static void
|
|
setup_widget (GtkListItem *list_item,
|
|
gpointer unused)
|
|
{
|
|
GtkWidget *box, *child;
|
|
RowData *data;
|
|
|
|
data = g_slice_new0 (RowData);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
|
gtk_list_item_set_child (list_item, box);
|
|
|
|
child = gtk_label_new (NULL);
|
|
gtk_label_set_width_chars (GTK_LABEL (child), 5);
|
|
gtk_box_append (GTK_BOX (box), child);
|
|
|
|
data->expander = gtk_tree_expander_new ();
|
|
gtk_box_append (GTK_BOX (box), data->expander);
|
|
|
|
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
|
gtk_tree_expander_set_child (GTK_TREE_EXPANDER (data->expander), box);
|
|
|
|
data->icon = gtk_image_new ();
|
|
gtk_box_append (GTK_BOX (box), data->icon);
|
|
|
|
data->name = gtk_label_new (NULL);
|
|
gtk_label_set_max_width_chars (GTK_LABEL (data->name), 25);
|
|
gtk_label_set_ellipsize (GTK_LABEL (data->name), PANGO_ELLIPSIZE_END);
|
|
gtk_box_append (GTK_BOX (box), data->name);
|
|
|
|
g_signal_connect (list_item, "notify::item", G_CALLBACK (row_data_notify_item), data);
|
|
g_object_set_data_full (G_OBJECT (list_item), "row-data", data, row_data_free);
|
|
}
|
|
|
|
static GListModel *
|
|
create_list_model_for_file_info (gpointer file_info,
|
|
gpointer unused)
|
|
{
|
|
GFile *file = G_FILE (g_file_info_get_attribute_object (file_info, "standard::file"));
|
|
|
|
if (file == NULL)
|
|
return NULL;
|
|
|
|
return create_list_model_for_directory (file);
|
|
}
|
|
|
|
static gboolean
|
|
update_statusbar (GtkStatusbar *statusbar)
|
|
{
|
|
GListModel *model = g_object_get_data (G_OBJECT (statusbar), "model");
|
|
GString *string = g_string_new (NULL);
|
|
guint n;
|
|
gboolean result = G_SOURCE_REMOVE;
|
|
|
|
gtk_statusbar_remove_all (statusbar, 0);
|
|
|
|
n = g_list_model_get_n_items (model);
|
|
g_string_append_printf (string, "%u", n);
|
|
if (GTK_IS_FILTER_LIST_MODEL (model))
|
|
{
|
|
guint n_unfiltered = g_list_model_get_n_items (gtk_filter_list_model_get_model (GTK_FILTER_LIST_MODEL (model)));
|
|
if (n != n_unfiltered)
|
|
g_string_append_printf (string, "/%u", n_unfiltered);
|
|
}
|
|
g_string_append (string, " items");
|
|
|
|
if (pending || active)
|
|
{
|
|
g_string_append_printf (string, " (%u directories remaining)", active + g_slist_length (pending));
|
|
result = G_SOURCE_CONTINUE;
|
|
}
|
|
result = G_SOURCE_CONTINUE;
|
|
|
|
gtk_statusbar_push (statusbar, 0, string->str);
|
|
g_free (string->str);
|
|
|
|
return result;
|
|
}
|
|
|
|
static gboolean
|
|
match_file (gpointer item, gpointer data)
|
|
{
|
|
GtkWidget *search_entry = data;
|
|
GFileInfo *info = gtk_tree_list_row_get_item (item);
|
|
GFile *file = G_FILE (g_file_info_get_attribute_object (info, "standard::file"));
|
|
char *path;
|
|
gboolean result;
|
|
|
|
path = g_file_get_path (file);
|
|
|
|
result = strstr (path, gtk_editable_get_text (GTK_EDITABLE (search_entry))) != NULL;
|
|
|
|
g_object_unref (info);
|
|
g_free (path);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void
|
|
search_changed_cb (GtkSearchEntry *entry,
|
|
GtkFilter *custom_filter)
|
|
{
|
|
gtk_filter_changed (custom_filter, GTK_FILTER_CHANGE_DIFFERENT);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
GtkWidget *win, *vbox, *sw, *listview, *search_entry, *statusbar;
|
|
GListModel *dirmodel;
|
|
GtkTreeListModel *tree;
|
|
GtkFilterListModel *filter;
|
|
GtkFilter *custom_filter;
|
|
FileInfoSelection *selectionmodel;
|
|
GFile *root;
|
|
GListModel *toplevels;
|
|
|
|
gtk_init ();
|
|
|
|
win = gtk_window_new ();
|
|
gtk_window_set_default_size (GTK_WINDOW (win), 400, 600);
|
|
|
|
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_window_set_child (GTK_WINDOW (win), vbox);
|
|
|
|
search_entry = gtk_search_entry_new ();
|
|
gtk_box_append (GTK_BOX (vbox), search_entry);
|
|
|
|
sw = gtk_scrolled_window_new ();
|
|
gtk_widget_set_vexpand (sw, TRUE);
|
|
gtk_search_entry_set_key_capture_widget (GTK_SEARCH_ENTRY (search_entry), sw);
|
|
gtk_box_append (GTK_BOX (vbox), sw);
|
|
|
|
listview = gtk_grid_view_new_with_factory (
|
|
gtk_functions_list_item_factory_new (setup_widget,
|
|
NULL,
|
|
NULL, NULL));
|
|
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), listview);
|
|
|
|
if (argc > 1)
|
|
root = g_file_new_for_commandline_arg (argv[1]);
|
|
else
|
|
root = g_file_new_for_path (g_get_current_dir ());
|
|
dirmodel = create_list_model_for_directory (root);
|
|
tree = gtk_tree_list_model_new (FALSE,
|
|
dirmodel,
|
|
TRUE,
|
|
create_list_model_for_file_info,
|
|
NULL, NULL);
|
|
g_object_unref (dirmodel);
|
|
g_object_unref (root);
|
|
|
|
custom_filter = gtk_custom_filter_new (match_file, search_entry, NULL);
|
|
filter = gtk_filter_list_model_new (G_LIST_MODEL (tree), custom_filter);
|
|
g_signal_connect (search_entry, "search-changed", G_CALLBACK (search_changed_cb), custom_filter);
|
|
g_object_unref (custom_filter);
|
|
|
|
selectionmodel = file_info_selection_new (G_LIST_MODEL (filter));
|
|
g_object_unref (filter);
|
|
|
|
gtk_grid_view_set_model (GTK_GRID_VIEW (listview), G_LIST_MODEL (selectionmodel));
|
|
|
|
statusbar = gtk_statusbar_new ();
|
|
gtk_widget_add_tick_callback (statusbar, (GtkTickCallback) update_statusbar, NULL, NULL);
|
|
g_object_set_data (G_OBJECT (statusbar), "model", filter);
|
|
g_signal_connect_swapped (filter, "items-changed", G_CALLBACK (update_statusbar), statusbar);
|
|
update_statusbar (GTK_STATUSBAR (statusbar));
|
|
gtk_box_append (GTK_BOX (vbox), statusbar);
|
|
|
|
g_object_unref (tree);
|
|
g_object_unref (selectionmodel);
|
|
|
|
gtk_widget_show (win);
|
|
|
|
toplevels = gtk_window_get_toplevels ();
|
|
while (g_list_model_get_n_items (toplevels))
|
|
g_main_context_iteration (NULL, TRUE);
|
|
|
|
return 0;
|
|
}
|