Compare commits
55 Commits
css-variab
...
wip/matthi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d9b7ca4aee | ||
|
|
10aefb5c79 | ||
|
|
8833158176 | ||
|
|
964e5a9fe2 | ||
|
|
615337863b | ||
|
|
437fa857f4 | ||
|
|
9757582e8d | ||
|
|
21ccf55aed | ||
|
|
62fce3bdf5 | ||
|
|
30db87b120 | ||
|
|
9e2eb047d0 | ||
|
|
a68ee2d4f1 | ||
|
|
ce1ac00c59 | ||
|
|
75a1bafcc0 | ||
|
|
c70130edc5 | ||
|
|
596b9202ce | ||
|
|
c8f27d1ac6 | ||
|
|
a0f8bf0f6b | ||
|
|
d0ef5f5db4 | ||
|
|
dfd280acd0 | ||
|
|
c97f62a471 | ||
|
|
135a875c25 | ||
|
|
09cbb5154f | ||
|
|
9e2250e35d | ||
|
|
d844c98c8a | ||
|
|
779fb56213 | ||
|
|
ad25afc298 | ||
|
|
18d7a7daaa | ||
|
|
be4fe26fef | ||
|
|
46fa122c98 | ||
|
|
2b780de342 | ||
|
|
ec0483bdfe | ||
|
|
1547d81eb9 | ||
|
|
498351784c | ||
|
|
1f07f9bbae | ||
|
|
aa71d8fc51 | ||
|
|
44e4ddfdf0 | ||
|
|
3481dddbc2 | ||
|
|
841d6cb327 | ||
|
|
b21fd5fd22 | ||
|
|
660a6f4e8e | ||
|
|
72902be840 | ||
|
|
2a7746dba6 | ||
|
|
badad04eb7 | ||
|
|
81690c7049 | ||
|
|
ae1331ee92 | ||
|
|
e2354e5f3b | ||
|
|
2d472da14b | ||
|
|
7edeef2f71 | ||
|
|
07ec4f9de0 | ||
|
|
b00b9500e7 | ||
|
|
d7e64c4423 | ||
|
|
5596063220 | ||
|
|
3485a4582c | ||
|
|
f1c34aefe0 |
@@ -179,6 +179,7 @@
|
||||
<file>links.c</file>
|
||||
<file>listbox.c</file>
|
||||
<file>list_store.c</file>
|
||||
<file>listview.c</file>
|
||||
<file>markup.c</file>
|
||||
<file>menus.c</file>
|
||||
<file>modelbutton.c</file>
|
||||
@@ -223,6 +224,11 @@
|
||||
<file>messages.txt</file>
|
||||
<file>apple-red.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/listview">
|
||||
<file>listview.ui</file>
|
||||
<file>messages.txt</file>
|
||||
<file>apple-red.png</file>
|
||||
</gresource>
|
||||
<gresource prefix="/popover">
|
||||
<file>popover.ui</file>
|
||||
</gresource>
|
||||
|
||||
@@ -9,16 +9,11 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "message.h"
|
||||
|
||||
static GdkPixbuf *avatar_pixbuf_other;
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
#define GTK_TYPE_MESSAGE (gtk_message_get_type ())
|
||||
#define GTK_MESSAGE(message) (G_TYPE_CHECK_INSTANCE_CAST ((message), GTK_TYPE_MESSAGE, GtkMessage))
|
||||
#define GTK_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MESSAGE, GtkMessageClass))
|
||||
#define GTK_IS_MESSAGE(message) (G_TYPE_CHECK_INSTANCE_TYPE ((message), GTK_TYPE_MESSAGE))
|
||||
#define GTK_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_MESSAGE))
|
||||
#define GTK_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MESSAGE, GtkMessageClass))
|
||||
|
||||
#define GTK_TYPE_MESSAGE_ROW (gtk_message_row_get_type ())
|
||||
#define GTK_MESSAGE_ROW(message_row) (G_TYPE_CHECK_INSTANCE_CAST ((message_row), GTK_TYPE_MESSAGE_ROW, GtkMessageRow))
|
||||
#define GTK_MESSAGE_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MESSAGE_ROW, GtkMessageRowClass))
|
||||
@@ -26,33 +21,10 @@ static GtkWidget *window = NULL;
|
||||
#define GTK_IS_MESSAGE_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_MESSAGE_ROW))
|
||||
#define GTK_MESSAGE_ROW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MESSAGE_ROW, GtkMessageRowClass))
|
||||
|
||||
typedef struct _GtkMessage GtkMessage;
|
||||
typedef struct _GtkMessageClass GtkMessageClass;
|
||||
typedef struct _GtkMessageRow GtkMessageRow;
|
||||
typedef struct _GtkMessageRowClass GtkMessageRowClass;
|
||||
typedef struct _GtkMessageRowPrivate GtkMessageRowPrivate;
|
||||
|
||||
|
||||
struct _GtkMessage
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
guint id;
|
||||
char *sender_name;
|
||||
char *sender_nick;
|
||||
char *message;
|
||||
gint64 time;
|
||||
guint reply_to;
|
||||
char *resent_by;
|
||||
int n_favorites;
|
||||
int n_reshares;
|
||||
};
|
||||
|
||||
struct _GtkMessageClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
struct _GtkMessageRow
|
||||
{
|
||||
GtkListBoxRow parent;
|
||||
@@ -83,84 +55,10 @@ struct _GtkMessageRowPrivate
|
||||
GtkButton *expand_button;
|
||||
};
|
||||
|
||||
GType gtk_message_get_type (void) G_GNUC_CONST;
|
||||
GType gtk_message_row_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_DEFINE_TYPE (GtkMessage, gtk_message, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
gtk_message_finalize (GObject *obj)
|
||||
{
|
||||
GtkMessage *msg = GTK_MESSAGE (obj);
|
||||
|
||||
g_free (msg->sender_name);
|
||||
g_free (msg->sender_nick);
|
||||
g_free (msg->message);
|
||||
g_free (msg->resent_by);
|
||||
|
||||
G_OBJECT_CLASS (gtk_message_parent_class)->finalize (obj);
|
||||
}
|
||||
static void
|
||||
gtk_message_class_init (GtkMessageClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->finalize = gtk_message_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_message_init (GtkMessage *msg)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_message_parse (GtkMessage *msg, const char *str)
|
||||
{
|
||||
char **strv;
|
||||
int i;
|
||||
|
||||
strv = g_strsplit (str, "|", 0);
|
||||
|
||||
i = 0;
|
||||
msg->id = strtol (strv[i++], NULL, 10);
|
||||
msg->sender_name = g_strdup (strv[i++]);
|
||||
msg->sender_nick = g_strdup (strv[i++]);
|
||||
msg->message = g_strdup (strv[i++]);
|
||||
msg->time = strtol (strv[i++], NULL, 10);
|
||||
if (strv[i])
|
||||
{
|
||||
msg->reply_to = strtol (strv[i++], NULL, 10);
|
||||
if (strv[i])
|
||||
{
|
||||
if (*strv[i])
|
||||
msg->resent_by = g_strdup (strv[i]);
|
||||
i++;
|
||||
if (strv[i])
|
||||
{
|
||||
msg->n_favorites = strtol (strv[i++], NULL, 10);
|
||||
if (strv[i])
|
||||
{
|
||||
msg->n_reshares = strtol (strv[i++], NULL, 10);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (strv);
|
||||
}
|
||||
|
||||
static GtkMessage *
|
||||
gtk_message_new (const char *str)
|
||||
{
|
||||
GtkMessage *msg;
|
||||
msg = g_object_new (gtk_message_get_type (), NULL);
|
||||
gtk_message_parse (msg, str);
|
||||
return msg;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkMessageRow, gtk_message_row, GTK_TYPE_LIST_BOX_ROW);
|
||||
|
||||
|
||||
static void
|
||||
gtk_message_row_update (GtkMessageRow *row)
|
||||
{
|
||||
@@ -333,15 +231,48 @@ row_activated (GtkListBox *listbox, GtkListBoxRow *row)
|
||||
gtk_message_row_expand (GTK_MESSAGE_ROW (row));
|
||||
}
|
||||
|
||||
static void
|
||||
update_count (GtkListBox *listbox, GtkLabel *label)
|
||||
{
|
||||
GList *children = gtk_container_get_children (GTK_CONTAINER (listbox));
|
||||
guint n_items = g_list_length (children);
|
||||
g_list_free (children);
|
||||
|
||||
char *text = g_strdup_printf ("%u rows", n_items);
|
||||
gtk_label_set_label (label, text);
|
||||
g_free (text);
|
||||
}
|
||||
|
||||
static GtkWidget *header_label;
|
||||
|
||||
static void
|
||||
add_more (GtkListBox *listbox)
|
||||
{
|
||||
GBytes *data;
|
||||
char **lines;
|
||||
int i;
|
||||
|
||||
data = g_resources_lookup_data ("/listbox/messages.txt", 0, NULL);
|
||||
lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0);
|
||||
|
||||
for (i = 0; lines[i] != NULL && *lines[i]; i++)
|
||||
{
|
||||
GtkMessage *message = gtk_message_new (lines[i]);
|
||||
GtkMessageRow *row = gtk_message_row_new (message);
|
||||
gtk_container_add (GTK_CONTAINER (listbox), GTK_WIDGET (row));
|
||||
}
|
||||
|
||||
g_strfreev (lines);
|
||||
g_bytes_unref (data);
|
||||
|
||||
update_count (listbox, GTK_LABEL (header_label));
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_listbox (GtkWidget *do_widget)
|
||||
{
|
||||
GtkWidget *scrolled, *listbox, *vbox, *label;
|
||||
GtkMessage *message;
|
||||
GtkMessageRow *row;
|
||||
GBytes *data;
|
||||
char **lines;
|
||||
int i;
|
||||
GtkWidget *header, *more;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
@@ -350,15 +281,27 @@ do_listbox (GtkWidget *do_widget)
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_title (GTK_WINDOW (window), "List Box");
|
||||
gtk_window_set_default_size (GTK_WINDOW (window),
|
||||
400, 600);
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 600);
|
||||
|
||||
/* NULL window variable when window is closed */
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed),
|
||||
&window);
|
||||
|
||||
listbox = gtk_list_box_new ();
|
||||
|
||||
header = gtk_header_bar_new ();
|
||||
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), TRUE);
|
||||
gtk_header_bar_set_title (GTK_HEADER_BAR (header), "List View");
|
||||
header_label = gtk_label_new ("");
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), header_label);
|
||||
more = gtk_button_new_from_icon_name ("list-add");
|
||||
|
||||
g_signal_connect_swapped (more, "clicked", G_CALLBACK (add_more), listbox);
|
||||
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), more);
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||||
gtk_container_add (GTK_CONTAINER (window), vbox);
|
||||
label = gtk_label_new ("Messages from Gtk+ and friends");
|
||||
@@ -367,26 +310,14 @@ do_listbox (GtkWidget *do_widget)
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_set_vexpand (scrolled, TRUE);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), scrolled);
|
||||
listbox = gtk_list_box_new ();
|
||||
gtk_container_add (GTK_CONTAINER (scrolled), listbox);
|
||||
|
||||
gtk_list_box_set_sort_func (GTK_LIST_BOX (listbox), (GtkListBoxSortFunc)gtk_message_row_sort, listbox, NULL);
|
||||
gtk_list_box_set_activate_on_single_click (GTK_LIST_BOX (listbox), FALSE);
|
||||
g_signal_connect (listbox, "row-activated", G_CALLBACK (row_activated), NULL);
|
||||
|
||||
data = g_resources_lookup_data ("/listbox/messages.txt", 0, NULL);
|
||||
lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0);
|
||||
|
||||
for (i = 0; lines[i] != NULL && *lines[i]; i++)
|
||||
{
|
||||
message = gtk_message_new (lines[i]);
|
||||
row = gtk_message_row_new (message);
|
||||
gtk_widget_show (GTK_WIDGET (row));
|
||||
gtk_container_add (GTK_CONTAINER (listbox), GTK_WIDGET (row));
|
||||
}
|
||||
|
||||
g_strfreev (lines);
|
||||
g_bytes_unref (data);
|
||||
add_more (listbox);
|
||||
update_count (listbox, header_label);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
|
||||
323
demos/gtk-demo/listview.c
Normal file
323
demos/gtk-demo/listview.c
Normal file
@@ -0,0 +1,323 @@
|
||||
/* List View
|
||||
*
|
||||
* GtkListView allows lists with complicated layouts, using
|
||||
* models to hold the data, and creating rows on demand.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "message.h"
|
||||
|
||||
static GdkPixbuf *avatar_pixbuf_other;
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
#define GTK_TYPE_MSG_ROW (gtk_msg_row_get_type ())
|
||||
#define GTK_MSG_ROW(msg_row) (G_TYPE_CHECK_INSTANCE_CAST ((msg_row), GTK_TYPE_MSG_ROW, GtkMsgRow))
|
||||
#define GTK_MSG_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MSG_ROW, GtkMsgRowClass))
|
||||
#define GTK_IS_MSG_ROW(msg_row) (G_TYPE_CHECK_INSTANCE_TYPE ((msg_row), GTK_TYPE_MSG_ROW))
|
||||
#define GTK_IS_MSG_ROW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_MSG_ROW))
|
||||
#define GTK_MSG_ROW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MSG_ROW, GtkMsgRowClass))
|
||||
|
||||
typedef struct _GtkMsgRow GtkMsgRow;
|
||||
typedef struct _GtkMsgRowClass GtkMsgRowClass;
|
||||
|
||||
struct _GtkMsgRow
|
||||
{
|
||||
GtkBin parent;
|
||||
|
||||
GtkMessage *message;
|
||||
GtkRevealer *details_revealer;
|
||||
GtkImage *avatar_image;
|
||||
GtkWidget *extra_buttons_box;
|
||||
GtkLabel *content_label;
|
||||
GtkLabel *source_name;
|
||||
GtkLabel *source_nick;
|
||||
GtkLabel *short_time_label;
|
||||
GtkLabel *detailed_time_label;
|
||||
GtkBox *resent_box;
|
||||
GtkLinkButton *resent_by_button;
|
||||
GtkLabel *n_favorites_label;
|
||||
GtkLabel *n_reshares_label;
|
||||
GtkButton *expand_button;
|
||||
};
|
||||
|
||||
struct _GtkMsgRowClass
|
||||
{
|
||||
GtkBinClass parent_class;
|
||||
};
|
||||
|
||||
static GType gtk_msg_row_get_type (void) G_GNUC_CONST;
|
||||
|
||||
G_DEFINE_TYPE (GtkMsgRow, gtk_msg_row, GTK_TYPE_BIN);
|
||||
|
||||
static void
|
||||
gtk_msg_row_update (GtkMsgRow *row)
|
||||
{
|
||||
GDateTime *t;
|
||||
char *s;
|
||||
|
||||
gtk_label_set_text (row->source_name, row->message->sender_name);
|
||||
gtk_label_set_text (row->source_nick, row->message->sender_nick);
|
||||
gtk_label_set_text (row->content_label, row->message->message);
|
||||
t = g_date_time_new_from_unix_utc (row->message->time);
|
||||
s = g_date_time_format (t, "%e %b %y");
|
||||
gtk_label_set_text (row->short_time_label, s);
|
||||
g_free (s);
|
||||
s = g_date_time_format (t, "%X - %e %b %Y");
|
||||
gtk_label_set_text (row->detailed_time_label, s);
|
||||
g_free (s);
|
||||
g_date_time_unref (t);
|
||||
|
||||
gtk_widget_set_visible (GTK_WIDGET(row->n_favorites_label),
|
||||
row->message->n_favorites != 0);
|
||||
s = g_strdup_printf ("<b>%d</b>\nFavorites", row->message->n_favorites);
|
||||
gtk_label_set_markup (row->n_favorites_label, s);
|
||||
g_free (s);
|
||||
|
||||
gtk_widget_set_visible (GTK_WIDGET(row->n_reshares_label),
|
||||
row->message->n_reshares != 0);
|
||||
s = g_strdup_printf ("<b>%d</b>\nReshares", row->message->n_reshares);
|
||||
gtk_label_set_markup (row->n_reshares_label, s);
|
||||
g_free (s);
|
||||
|
||||
gtk_widget_set_visible (GTK_WIDGET (row->resent_box), row->message->resent_by != NULL);
|
||||
if (row->message->resent_by)
|
||||
gtk_button_set_label (GTK_BUTTON (row->resent_by_button), row->message->resent_by);
|
||||
|
||||
if (strcmp (row->message->sender_nick, "@GTKtoolkit") == 0)
|
||||
{
|
||||
gtk_image_set_from_icon_name (row->avatar_image, "gtk3-demo");
|
||||
gtk_image_set_icon_size (row->avatar_image, GTK_ICON_SIZE_LARGE);
|
||||
}
|
||||
else
|
||||
gtk_image_set_from_pixbuf (row->avatar_image, avatar_pixbuf_other);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_msg_row_expand (GtkMsgRow *row)
|
||||
{
|
||||
gboolean expand;
|
||||
|
||||
expand = !gtk_revealer_get_reveal_child (row->details_revealer);
|
||||
|
||||
gtk_revealer_set_reveal_child (row->details_revealer, expand);
|
||||
if (expand)
|
||||
gtk_button_set_label (row->expand_button, "Hide");
|
||||
else
|
||||
gtk_button_set_label (row->expand_button, "Expand");
|
||||
}
|
||||
|
||||
static void
|
||||
expand_clicked (GtkMsgRow *row,
|
||||
GtkButton *button)
|
||||
{
|
||||
gtk_msg_row_expand (row);
|
||||
}
|
||||
|
||||
static void
|
||||
reshare_clicked (GtkMsgRow *row,
|
||||
GtkButton *button)
|
||||
{
|
||||
row->message->n_reshares++;
|
||||
gtk_msg_row_update (row);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
favorite_clicked (GtkMsgRow *row,
|
||||
GtkButton *button)
|
||||
{
|
||||
row->message->n_favorites++;
|
||||
gtk_msg_row_update (row);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_msg_row_state_flags_changed (GtkWidget *widget,
|
||||
GtkStateFlags previous_state_flags)
|
||||
{
|
||||
GtkMsgRow *row = GTK_MSG_ROW (widget);
|
||||
GtkStateFlags flags;
|
||||
|
||||
flags = gtk_widget_get_state_flags (widget);
|
||||
|
||||
gtk_widget_set_visible (row->extra_buttons_box,
|
||||
flags & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_SELECTED));
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_msg_row_parent_class)->state_flags_changed (widget, previous_state_flags);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_msg_row_finalize (GObject *obj)
|
||||
{
|
||||
G_OBJECT_CLASS (gtk_msg_row_parent_class)->finalize(obj);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_msg_row_class_init (GtkMsgRowClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gtk_msg_row_finalize;
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/listview/listview.ui");
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, content_label);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, source_name);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, source_nick);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, short_time_label);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, detailed_time_label);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, extra_buttons_box);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, details_revealer);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, avatar_image);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, resent_box);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, resent_by_button);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, n_reshares_label);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, n_favorites_label);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkMsgRow, expand_button);
|
||||
gtk_widget_class_bind_template_callback (widget_class, expand_clicked);
|
||||
gtk_widget_class_bind_template_callback (widget_class, reshare_clicked);
|
||||
gtk_widget_class_bind_template_callback (widget_class, favorite_clicked);
|
||||
|
||||
widget_class->state_flags_changed = gtk_msg_row_state_flags_changed;
|
||||
}
|
||||
|
||||
static void
|
||||
row_activated (GtkGestureMultiPress *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkMsgRow *row)
|
||||
{
|
||||
if (n_press == 2)
|
||||
gtk_msg_row_expand (row);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_msg_row_init (GtkMsgRow *row)
|
||||
{
|
||||
GtkGesture *double_click;
|
||||
|
||||
gtk_widget_init_template (GTK_WIDGET (row));
|
||||
|
||||
double_click = gtk_gesture_multi_press_new ();
|
||||
g_signal_connect (double_click, "pressed", G_CALLBACK (row_activated), row);
|
||||
gtk_widget_add_controller (GTK_WIDGET (row), GTK_EVENT_CONTROLLER (double_click));
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_message_sort (gconstpointer a, gconstpointer b, gpointer data)
|
||||
{
|
||||
return ((const GtkMessage *)b)->time - ((const GtkMessage *)a)->time;
|
||||
}
|
||||
|
||||
static void
|
||||
bind_msg_row (GObject *list_item, GParamSpec *pspec, gpointer data)
|
||||
{
|
||||
GtkMessage *message = (GtkMessage *)gtk_list_item_get_item (GTK_LIST_ITEM (list_item));
|
||||
GtkMsgRow *row = (GtkMsgRow *) gtk_bin_get_child (GTK_BIN (list_item));
|
||||
|
||||
row->message = message;
|
||||
if (message)
|
||||
gtk_msg_row_update (row);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_row (GtkListItem *item,
|
||||
gpointer data)
|
||||
{
|
||||
g_signal_connect (item, "notify::item", G_CALLBACK (bind_msg_row), data);
|
||||
gtk_container_add (GTK_CONTAINER (item), g_object_new (gtk_msg_row_get_type (), NULL));
|
||||
}
|
||||
|
||||
static void
|
||||
update_count (GListModel *model, guint position, guint removed, guint added, GtkLabel *label)
|
||||
{
|
||||
guint n_items = g_list_model_get_n_items (model);
|
||||
char *text = g_strdup_printf ("%u rows", n_items);
|
||||
gtk_label_set_label (label, text);
|
||||
g_free (text);
|
||||
}
|
||||
|
||||
static void
|
||||
add_more (GListModel *model)
|
||||
{
|
||||
GBytes *data;
|
||||
char **lines;
|
||||
int i;
|
||||
|
||||
data = g_resources_lookup_data ("/listview/messages.txt", 0, NULL);
|
||||
lines = g_strsplit (g_bytes_get_data (data, NULL), "\n", 0);
|
||||
|
||||
for (i = 0; lines[i] != NULL && *lines[i]; i++)
|
||||
{
|
||||
GtkMessage *message = gtk_message_new (lines[i]);
|
||||
g_list_store_append (G_LIST_STORE (model), message);
|
||||
}
|
||||
|
||||
g_strfreev (lines);
|
||||
g_bytes_unref (data);
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_listview (GtkWidget *do_widget)
|
||||
{
|
||||
GtkWidget *scrolled, *listview, *vbox, *label;
|
||||
GtkWidget *header, *header_label, *more;
|
||||
GListModel *model;
|
||||
|
||||
if (!window)
|
||||
{
|
||||
avatar_pixbuf_other = gdk_pixbuf_new_from_resource_at_scale ("/listbox/apple-red.png", 32, 32, FALSE, NULL);
|
||||
|
||||
model = G_LIST_MODEL (g_list_store_new (gtk_message_get_type ()));
|
||||
|
||||
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_display (GTK_WINDOW (window),
|
||||
gtk_widget_get_display (do_widget));
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 400, 600);
|
||||
|
||||
/* NULL window variable when window is closed */
|
||||
g_signal_connect (window, "destroy",
|
||||
G_CALLBACK (gtk_widget_destroyed),
|
||||
&window);
|
||||
|
||||
header = gtk_header_bar_new ();
|
||||
gtk_header_bar_set_show_title_buttons (GTK_HEADER_BAR (header), TRUE);
|
||||
gtk_header_bar_set_title (GTK_HEADER_BAR (header), "List View");
|
||||
header_label = gtk_label_new ("");
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), header_label);
|
||||
more = gtk_button_new_from_icon_name ("list-add");
|
||||
|
||||
g_signal_connect_swapped (more, "clicked", G_CALLBACK (add_more), model);
|
||||
g_signal_connect (model, "items-changed", G_CALLBACK (update_count), header_label);
|
||||
|
||||
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), more);
|
||||
gtk_window_set_titlebar (GTK_WINDOW (window), header);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 12);
|
||||
gtk_container_add (GTK_CONTAINER (window), vbox);
|
||||
label = gtk_label_new ("Messages from Gtk+ and friends");
|
||||
gtk_box_pack_start (GTK_BOX (vbox), label);
|
||||
scrolled = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
|
||||
gtk_widget_set_vexpand (scrolled, TRUE);
|
||||
gtk_box_pack_start (GTK_BOX (vbox), scrolled);
|
||||
listview = gtk_list_view_new ();
|
||||
gtk_container_add (GTK_CONTAINER (scrolled), listview);
|
||||
|
||||
gtk_list_view_set_functions (GTK_LIST_VIEW (listview), setup_row, NULL, NULL, NULL);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (gtk_sort_list_model_new (model, gtk_message_sort, NULL, NULL)));
|
||||
|
||||
add_more (model);
|
||||
}
|
||||
|
||||
if (!gtk_widget_get_visible (window))
|
||||
gtk_widget_show (window);
|
||||
else
|
||||
gtk_widget_destroy (window);
|
||||
|
||||
return window;
|
||||
}
|
||||
302
demos/gtk-demo/listview.ui
Normal file
302
demos/gtk-demo/listview.ui
Normal file
@@ -0,0 +1,302 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface domain="gtk40">
|
||||
<!-- interface-requires gtk+ 3.10 -->
|
||||
<!-- interface-requires gtkdemo 3.10 -->
|
||||
<object class="GtkMenu" id="menu1">
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem1">
|
||||
<property name="label" translatable="yes">Email message</property>
|
||||
<property name="use-underline">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuItem" id="menuitem2">
|
||||
<property name="label" translatable="yes">Embed message</property>
|
||||
<property name="use-underline">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<template class="GtkMsgRow" parent="GtkBin">
|
||||
<child>
|
||||
<object class="GtkGrid" id="grid1">
|
||||
<property name="hexpand">1</property>
|
||||
<child>
|
||||
<object class="GtkImage" id="avatar_image">
|
||||
<property name="width-request">32</property>
|
||||
<property name="height-request">32</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="margin-top">8</property>
|
||||
<property name="margin-bottom">8</property>
|
||||
<property name="margin-start">8</property>
|
||||
<property name="margin-end">8</property>
|
||||
<property name="icon-name">image-missing</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">0</property>
|
||||
<property name="height">5</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box1">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="baseline-position">top</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="button2">
|
||||
<property name="can-focus">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="valign">baseline</property>
|
||||
<property name="relief">none</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="source_name">
|
||||
<property name="valign">baseline</property>
|
||||
<property name="label" translatable="0">Username</property>
|
||||
<attributes>
|
||||
<attribute name="weight" value="bold"/>
|
||||
</attributes>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="source_nick">
|
||||
<property name="valign">baseline</property>
|
||||
<property name="label" translatable="0">@nick</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="short_time_label">
|
||||
<property name="valign">baseline</property>
|
||||
<property name="label" translatable="yes">38m</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="pack-type">end</property>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="content_label">
|
||||
<property name="halign">start</property>
|
||||
<property name="valign">start</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="yalign">0</property>
|
||||
<property name="label" translatable="0">Message</property>
|
||||
<property name="wrap">1</property>
|
||||
<accessibility>
|
||||
<role type="static"/>
|
||||
</accessibility>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="resent_box">
|
||||
<child>
|
||||
<object class="GtkImage" id="image2">
|
||||
<property name="icon-name">media-playlist-repeat</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label4">
|
||||
<property name="label" translatable="yes">Resent by</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLinkButton" id="resent_by_button">
|
||||
<property name="label" translatable="0">reshareer</property>
|
||||
<property name="can-focus">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="uri">http://www.gtk.org</property>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box3">
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="expand_button">
|
||||
<property name="label" translatable="yes">Expand</property>
|
||||
<property name="can-focus">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="relief">none</property>
|
||||
<signal name="clicked" handler="expand_clicked" swapped="yes"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="extra_buttons_box">
|
||||
<property name="visible">0</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="reply-button">
|
||||
<property name="label" translatable="yes">Reply</property>
|
||||
<property name="can-focus">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="relief">none</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="reshare-button">
|
||||
<property name="label" translatable="yes">Reshare</property>
|
||||
<property name="can-focus">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="relief">none</property>
|
||||
<signal name="clicked" handler="reshare_clicked" swapped="yes"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="favorite-buttton">
|
||||
<property name="label" translatable="yes">Favorite</property>
|
||||
<property name="can-focus">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="relief">none</property>
|
||||
<signal name="clicked" handler="favorite_clicked" swapped="yes"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkMenuButton" id="more-button">
|
||||
<property name="can-focus">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="relief">none</property>
|
||||
<property name="popup">menu1</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="label7">
|
||||
<property name="label" translatable="yes">More...</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkRevealer" id="details_revealer">
|
||||
<child>
|
||||
<object class="GtkBox" id="box5">
|
||||
<property name="orientation">vertical</property>
|
||||
<child>
|
||||
<object class="GtkBox" id="box7">
|
||||
<property name="margin-top">2</property>
|
||||
<property name="margin-bottom">2</property>
|
||||
<property name="spacing">8</property>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame1">
|
||||
<property name="shadow-type">none</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="n_reshares_label">
|
||||
<property name="label" translatable="0"><b>2</b>
|
||||
Reshares</property>
|
||||
<property name="use-markup">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkFrame" id="frame2">
|
||||
<property name="shadow-type">none</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="n_favorites_label">
|
||||
<property name="label" translatable="0"><b>2</b>
|
||||
FAVORITES</property>
|
||||
<property name="use-markup">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child type="label_item"/>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="box6">
|
||||
<child>
|
||||
<object class="GtkLabel" id="detailed_time_label">
|
||||
<property name="label" translatable="0">4:25 AM - 14 Jun 13 </property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="button5">
|
||||
<property name="label" translatable="yes">Details</property>
|
||||
<property name="can-focus">1</property>
|
||||
<property name="receives-default">1</property>
|
||||
<property name="relief">none</property>
|
||||
<style>
|
||||
<class name="dim-label"/>
|
||||
</style>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">1</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="position">2</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">1</property>
|
||||
<property name="top-attach">4</property>
|
||||
</packing>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
</interface>
|
||||
@@ -36,6 +36,7 @@ demos = files([
|
||||
'infobar.c',
|
||||
'links.c',
|
||||
'listbox.c',
|
||||
'listview.c',
|
||||
'flowbox.c',
|
||||
'list_store.c',
|
||||
'markup.c',
|
||||
@@ -76,7 +77,7 @@ demos = files([
|
||||
|
||||
gtkdemo_deps = [ libgtk_dep, ]
|
||||
|
||||
extra_demo_sources = files(['main.c', 'gtkfishbowl.c', 'fontplane.c', 'gtkgears.c', 'puzzlepiece.c'])
|
||||
extra_demo_sources = files(['main.c', 'gtkfishbowl.c', 'fontplane.c', 'gtkgears.c', 'puzzlepiece.c', 'message.c'])
|
||||
|
||||
if harfbuzz_dep.found() and pangoft_dep.found()
|
||||
demos += files('font_features.c')
|
||||
|
||||
79
demos/gtk-demo/message.c
Normal file
79
demos/gtk-demo/message.c
Normal file
@@ -0,0 +1,79 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
#include "message.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
G_DEFINE_TYPE (GtkMessage, gtk_message, G_TYPE_OBJECT);
|
||||
|
||||
static void
|
||||
gtk_message_finalize (GObject *obj)
|
||||
{
|
||||
GtkMessage *msg = GTK_MESSAGE (obj);
|
||||
|
||||
g_free (msg->sender_name);
|
||||
g_free (msg->sender_nick);
|
||||
g_free (msg->message);
|
||||
g_free (msg->resent_by);
|
||||
|
||||
G_OBJECT_CLASS (gtk_message_parent_class)->finalize (obj);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_message_class_init (GtkMessageClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
object_class->finalize = gtk_message_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_message_init (GtkMessage *msg)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_message_parse (GtkMessage *msg, const char *str)
|
||||
{
|
||||
char **strv;
|
||||
int i;
|
||||
|
||||
strv = g_strsplit (str, "|", 0);
|
||||
|
||||
i = 0;
|
||||
msg->id = strtol (strv[i++], NULL, 10);
|
||||
msg->sender_name = g_strdup (strv[i++]);
|
||||
msg->sender_nick = g_strdup (strv[i++]);
|
||||
msg->message = g_strdup (strv[i++]);
|
||||
msg->time = strtol (strv[i++], NULL, 10);
|
||||
if (strv[i])
|
||||
{
|
||||
msg->reply_to = strtol (strv[i++], NULL, 10);
|
||||
if (strv[i])
|
||||
{
|
||||
if (*strv[i])
|
||||
msg->resent_by = g_strdup (strv[i]);
|
||||
i++;
|
||||
if (strv[i])
|
||||
{
|
||||
msg->n_favorites = strtol (strv[i++], NULL, 10);
|
||||
if (strv[i])
|
||||
{
|
||||
msg->n_reshares = strtol (strv[i++], NULL, 10);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (strv);
|
||||
}
|
||||
|
||||
GtkMessage *
|
||||
gtk_message_new (const char *str)
|
||||
{
|
||||
GtkMessage *msg;
|
||||
msg = g_object_new (gtk_message_get_type (), NULL);
|
||||
gtk_message_parse (msg, str);
|
||||
return msg;
|
||||
}
|
||||
34
demos/gtk-demo/message.h
Normal file
34
demos/gtk-demo/message.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#define GTK_TYPE_MESSAGE (gtk_message_get_type ())
|
||||
#define GTK_MESSAGE(message) (G_TYPE_CHECK_INSTANCE_CAST ((message), GTK_TYPE_MESSAGE, GtkMessage))
|
||||
#define GTK_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_MESSAGE, GtkMessageClass))
|
||||
#define GTK_IS_MESSAGE(message) (G_TYPE_CHECK_INSTANCE_TYPE ((message), GTK_TYPE_MESSAGE))
|
||||
#define GTK_IS_MESSAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_MESSAGE))
|
||||
#define GTK_MESSAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_MESSAGE, GtkMessageClass))
|
||||
|
||||
typedef struct _GtkMessage GtkMessage;
|
||||
typedef struct _GtkMessageClass GtkMessageClass;
|
||||
|
||||
struct _GtkMessage
|
||||
{
|
||||
GObject parent;
|
||||
|
||||
guint id;
|
||||
char *sender_name;
|
||||
char *sender_nick;
|
||||
char *message;
|
||||
gint64 time;
|
||||
guint reply_to;
|
||||
char *resent_by;
|
||||
int n_favorites;
|
||||
int n_reshares;
|
||||
};
|
||||
|
||||
struct _GtkMessageClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
GType gtk_message_get_type (void) G_GNUC_CONST;
|
||||
GtkMessage * gtk_message_new (const char *str);
|
||||
@@ -83,6 +83,7 @@
|
||||
<xi:include href="xml/gtkrevealer.xml" />
|
||||
<xi:include href="xml/gtklistbox.xml" />
|
||||
<xi:include href="xml/gtkflowbox.xml" />
|
||||
<xi:include href="xml/gtklistview.xml" />
|
||||
<xi:include href="xml/gtkstack.xml" />
|
||||
<xi:include href="xml/gtkstackswitcher.xml" />
|
||||
<xi:include href="xml/gtkstacksidebar.xml" />
|
||||
|
||||
@@ -442,6 +442,68 @@ gtk_list_box_get_type
|
||||
gtk_list_box_row_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkselectionmodel</FILE>
|
||||
<TITLE>GtkSelectionModel</TITLE>
|
||||
GtkSelectionModel
|
||||
gtk_selection_model_is_selected
|
||||
gtk_selection_model_select_item
|
||||
gtk_selection_model_unselect_item
|
||||
gtk_selection_model_select_range
|
||||
gtk_selection_model_unselect_range
|
||||
gtk_selection_model_select_all
|
||||
gtk_selection_model_unselect_all
|
||||
<SUBSECTION>
|
||||
gtk_selection_model_selection_changed
|
||||
<SUBSECTION Standard>
|
||||
GTK_SELECTION_MODEL
|
||||
GTK_SELECTION_MODEL_CLASS
|
||||
GTK_SELECTION_MODEL_GET_CLASS
|
||||
GTK_IS_SELECTION_MODEL
|
||||
GTK_IS_SELECTION_MODEL_CLASS
|
||||
GTK_TYPE_SELECTION_MODEL
|
||||
<SUBSECTION Private>
|
||||
gtk_selection_model_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklistitem</FILE>
|
||||
<TITLE>GtkListItem</TITLE>
|
||||
GtkListItem
|
||||
gtk_list_item_get_item
|
||||
gtk_list_item_get_position
|
||||
gtk_list_item_get_selected
|
||||
gtk_list_item_get_selectable
|
||||
gtk_list_item_set_selectable
|
||||
<SUBSECTION Standard>
|
||||
GTK_LIST_ITEM
|
||||
GTK_LIST_ITEM_CLASS
|
||||
GTK_LIST_ITEM_GET_CLASS
|
||||
GTK_IS_LIST_ITEM
|
||||
GTK_IS_LIST_ITEM_CLASS
|
||||
GTK_TYPE_LIST_ITEM
|
||||
<SUBSECTION Private>
|
||||
gtk_list_item_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklistview</FILE>
|
||||
<TITLE>GtkListView</TITLE>
|
||||
GtkListView
|
||||
gtk_list_view_new
|
||||
gtk_list_view_set_model
|
||||
gtk_list_view_get_model
|
||||
<SUBSECTION Standard>
|
||||
GTK_LIST_VIEW
|
||||
GTK_LIST_VIEW_CLASS
|
||||
GTK_LIST_VIEW_GET_CLASS
|
||||
GTK_IS_LIST_VIEW
|
||||
GTK_IS_LIST_VIEW_CLASS
|
||||
GTK_TYPE_LIST_VIEW
|
||||
<SUBSECTION Private>
|
||||
gtk_list_view_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkbuildable</FILE>
|
||||
GtkBuildable
|
||||
|
||||
@@ -44,8 +44,8 @@ typedef struct _GtkTreeViewAccessibleCellInfo GtkTreeViewAccessibleCellInfo;
|
||||
struct _GtkTreeViewAccessibleCellInfo
|
||||
{
|
||||
GtkCellAccessible *cell;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
GtkTreeViewColumn *cell_col_ref;
|
||||
GtkTreeViewAccessible *view;
|
||||
};
|
||||
@@ -58,8 +58,8 @@ static gboolean is_cell_showing (GtkTreeView
|
||||
GdkRectangle *cell_rect);
|
||||
|
||||
static void cell_info_new (GtkTreeViewAccessible *accessible,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreeViewColumn *tv_col,
|
||||
GtkCellAccessible *cell);
|
||||
static gint get_column_number (GtkTreeView *tree_view,
|
||||
@@ -67,8 +67,8 @@ static gint get_column_number (GtkTreeView
|
||||
|
||||
static gboolean get_rbtree_column_from_index (GtkTreeView *tree_view,
|
||||
gint index,
|
||||
GtkRBTree **tree,
|
||||
GtkRBNode **node,
|
||||
GtkTreeRBTree **tree,
|
||||
GtkTreeRBNode **node,
|
||||
GtkTreeViewColumn **column);
|
||||
|
||||
static GtkTreeViewAccessibleCellInfo* find_cell_info (GtkTreeViewAccessible *view,
|
||||
@@ -231,7 +231,7 @@ gtk_tree_view_accessible_widget_unset (GtkAccessible *gtkaccessible)
|
||||
static gint
|
||||
get_n_rows (GtkTreeView *tree_view)
|
||||
{
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBTree *tree;
|
||||
|
||||
tree = _gtk_tree_view_get_rbtree (tree_view);
|
||||
|
||||
@@ -312,7 +312,7 @@ set_cell_data (GtkTreeView *treeview,
|
||||
|
||||
model = gtk_tree_view_get_model (treeview);
|
||||
|
||||
if (GTK_RBNODE_FLAG_SET (cell_info->node, GTK_RBNODE_IS_PARENT) &&
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (cell_info->node, GTK_TREE_RBNODE_IS_PARENT) &&
|
||||
cell_info->cell_col_ref == gtk_tree_view_get_expander_column (treeview))
|
||||
{
|
||||
is_expander = TRUE;
|
||||
@@ -342,8 +342,8 @@ set_cell_data (GtkTreeView *treeview,
|
||||
|
||||
static GtkCellAccessible *
|
||||
peek_cell (GtkTreeViewAccessible *accessible,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreeViewColumn *column)
|
||||
{
|
||||
GtkTreeViewAccessibleCellInfo lookup, *cell_info;
|
||||
@@ -415,8 +415,8 @@ create_cell_accessible (GtkTreeView *treeview,
|
||||
static GtkCellAccessible *
|
||||
create_cell (GtkTreeView *treeview,
|
||||
GtkTreeViewAccessible *accessible,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreeViewColumn *column)
|
||||
{
|
||||
GtkCellAccessible *cell;
|
||||
@@ -439,8 +439,8 @@ gtk_tree_view_accessible_ref_child (AtkObject *obj,
|
||||
GtkCellAccessible *cell;
|
||||
GtkTreeView *tree_view;
|
||||
GtkTreeViewColumn *tv_col;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
AtkObject *child;
|
||||
|
||||
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (obj));
|
||||
@@ -536,8 +536,8 @@ gtk_tree_view_accessible_ref_accessible_at_point (AtkComponent *component,
|
||||
gint x_pos, y_pos;
|
||||
gint bx, by;
|
||||
GtkCellAccessible *cell;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
|
||||
widget = gtk_accessible_get_widget (GTK_ACCESSIBLE (component));
|
||||
if (widget == NULL)
|
||||
@@ -680,8 +680,8 @@ gtk_tree_view_accessible_is_row_selected (AtkTable *table,
|
||||
gint row)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
|
||||
if (row < 0)
|
||||
return FALSE;
|
||||
@@ -690,13 +690,13 @@ gtk_tree_view_accessible_is_row_selected (AtkTable *table,
|
||||
if (widget == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!_gtk_rbtree_find_index (_gtk_tree_view_get_rbtree (GTK_TREE_VIEW (widget)),
|
||||
if (!_gtk_tree_rbtree_find_index (_gtk_tree_view_get_rbtree (GTK_TREE_VIEW (widget)),
|
||||
row,
|
||||
&tree,
|
||||
&node))
|
||||
return FALSE;
|
||||
|
||||
return GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED);
|
||||
return GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -719,8 +719,8 @@ get_selected_rows (GtkTreeModel *model,
|
||||
gpointer datap)
|
||||
{
|
||||
SelectedRowsData *data = datap;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
int id;
|
||||
|
||||
if (_gtk_tree_view_find_node (data->treeview,
|
||||
@@ -730,7 +730,7 @@ get_selected_rows (GtkTreeModel *model,
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
id = _gtk_rbtree_node_get_index (tree, node);
|
||||
id = _gtk_tree_rbtree_node_get_index (tree, node);
|
||||
|
||||
g_array_append_val (data->array, id);
|
||||
}
|
||||
@@ -773,8 +773,8 @@ gtk_tree_view_accessible_add_row_selection (AtkTable *table,
|
||||
{
|
||||
GtkTreeView *treeview;
|
||||
GtkTreePath *path;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
|
||||
if (row < 0)
|
||||
return FALSE;
|
||||
@@ -783,13 +783,13 @@ gtk_tree_view_accessible_add_row_selection (AtkTable *table,
|
||||
if (treeview == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!_gtk_rbtree_find_index (_gtk_tree_view_get_rbtree (treeview),
|
||||
if (!_gtk_tree_rbtree_find_index (_gtk_tree_view_get_rbtree (treeview),
|
||||
row,
|
||||
&tree,
|
||||
&node))
|
||||
return FALSE;
|
||||
|
||||
if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
return FALSE;
|
||||
|
||||
path = _gtk_tree_path_new_from_rbtree (tree, node);
|
||||
@@ -805,8 +805,8 @@ gtk_tree_view_accessible_remove_row_selection (AtkTable *table,
|
||||
{
|
||||
GtkTreeView *treeview;
|
||||
GtkTreePath *path;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
|
||||
if (row < 0)
|
||||
return FALSE;
|
||||
@@ -815,13 +815,13 @@ gtk_tree_view_accessible_remove_row_selection (AtkTable *table,
|
||||
if (treeview == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (!_gtk_rbtree_find_index (_gtk_tree_view_get_rbtree (treeview),
|
||||
if (!_gtk_tree_rbtree_find_index (_gtk_tree_view_get_rbtree (treeview),
|
||||
row,
|
||||
&tree,
|
||||
&node))
|
||||
return FALSE;
|
||||
|
||||
if (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
return FALSE;
|
||||
|
||||
path = _gtk_tree_path_new_from_rbtree (tree, node);
|
||||
@@ -1217,10 +1217,10 @@ gtk_tree_view_accessible_get_renderer_state (GtkCellAccessibleParent *parent,
|
||||
|
||||
flags = 0;
|
||||
|
||||
if (GTK_RBNODE_FLAG_SET (cell_info->node, GTK_RBNODE_IS_SELECTED))
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (cell_info->node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
flags |= GTK_CELL_RENDERER_SELECTED;
|
||||
|
||||
if (GTK_RBNODE_FLAG_SET (cell_info->node, GTK_RBNODE_IS_PRELIT))
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (cell_info->node, GTK_TREE_RBNODE_IS_PRELIT))
|
||||
flags |= GTK_CELL_RENDERER_PRELIT;
|
||||
|
||||
if (gtk_tree_view_column_get_sort_indicator (cell_info->cell_col_ref))
|
||||
@@ -1230,7 +1230,7 @@ gtk_tree_view_accessible_get_renderer_state (GtkCellAccessibleParent *parent,
|
||||
|
||||
if (cell_info->cell_col_ref == gtk_tree_view_get_expander_column (treeview))
|
||||
{
|
||||
if (GTK_RBNODE_FLAG_SET (cell_info->node, GTK_RBNODE_IS_PARENT))
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (cell_info->node, GTK_TREE_RBNODE_IS_PARENT))
|
||||
flags |= GTK_CELL_RENDERER_EXPANDABLE;
|
||||
|
||||
if (cell_info->node->children)
|
||||
@@ -1241,8 +1241,8 @@ gtk_tree_view_accessible_get_renderer_state (GtkCellAccessibleParent *parent,
|
||||
{
|
||||
GtkTreeViewColumn *column;
|
||||
GtkTreePath *path;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node = NULL;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node = NULL;
|
||||
|
||||
gtk_tree_view_get_cursor (treeview, &path, &column);
|
||||
if (path)
|
||||
@@ -1334,8 +1334,8 @@ gtk_tree_view_accessible_update_relationset (GtkCellAccessibleParent *parent,
|
||||
GtkTreeViewColumn *column;
|
||||
GtkTreeView *treeview;
|
||||
AtkRelation *relation;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
AtkObject *object;
|
||||
|
||||
/* Don't set relations on cells that aren't direct descendants of the treeview.
|
||||
@@ -1378,9 +1378,9 @@ gtk_tree_view_accessible_update_relationset (GtkCellAccessibleParent *parent,
|
||||
tree = cell_info->node->children;
|
||||
if (tree)
|
||||
{
|
||||
for (node = _gtk_rbtree_first (tree);
|
||||
for (node = _gtk_tree_rbtree_first (tree);
|
||||
node != NULL;
|
||||
node = _gtk_rbtree_next (tree, node))
|
||||
node = _gtk_tree_rbtree_next (tree, node))
|
||||
{
|
||||
object = ATK_OBJECT (peek_cell (accessible, tree, node, column));
|
||||
if (object == NULL)
|
||||
@@ -1413,7 +1413,7 @@ gtk_tree_view_accessible_get_cell_position (GtkCellAccessibleParent *parent,
|
||||
return;
|
||||
|
||||
if (row)
|
||||
(*row) = _gtk_rbtree_node_get_index (cell_info->tree, cell_info->node);
|
||||
(*row) = _gtk_tree_rbtree_node_get_index (cell_info->tree, cell_info->node);
|
||||
if (column)
|
||||
(*column) = get_column_number (tree_view, cell_info->cell_col_ref);
|
||||
}
|
||||
@@ -1517,7 +1517,7 @@ cell_info_get_index (GtkTreeView *tree_view,
|
||||
{
|
||||
int index;
|
||||
|
||||
index = _gtk_rbtree_node_get_index (info->tree, info->node) + 1;
|
||||
index = _gtk_tree_rbtree_node_get_index (info->tree, info->node) + 1;
|
||||
index *= get_n_columns (tree_view);
|
||||
index += get_column_number (tree_view, info->cell_col_ref);
|
||||
|
||||
@@ -1526,8 +1526,8 @@ cell_info_get_index (GtkTreeView *tree_view,
|
||||
|
||||
static void
|
||||
cell_info_new (GtkTreeViewAccessible *accessible,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreeViewColumn *tv_col,
|
||||
GtkCellAccessible *cell)
|
||||
{
|
||||
@@ -1581,8 +1581,8 @@ get_column_number (GtkTreeView *treeview,
|
||||
static gboolean
|
||||
get_rbtree_column_from_index (GtkTreeView *tree_view,
|
||||
gint index,
|
||||
GtkRBTree **tree,
|
||||
GtkRBNode **node,
|
||||
GtkTreeRBTree **tree,
|
||||
GtkTreeRBNode **node,
|
||||
GtkTreeViewColumn **column)
|
||||
{
|
||||
guint n_columns = get_n_columns (tree_view);
|
||||
@@ -1598,7 +1598,7 @@ get_rbtree_column_from_index (GtkTreeView *tree_view,
|
||||
{
|
||||
g_return_val_if_fail (node != NULL, FALSE);
|
||||
|
||||
if (!_gtk_rbtree_find_index (_gtk_tree_view_get_rbtree (tree_view),
|
||||
if (!_gtk_tree_rbtree_find_index (_gtk_tree_view_get_rbtree (tree_view),
|
||||
index / n_columns,
|
||||
tree,
|
||||
node))
|
||||
@@ -1652,8 +1652,8 @@ get_header_from_column (GtkTreeViewColumn *tv_col)
|
||||
|
||||
void
|
||||
_gtk_tree_view_accessible_add (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node)
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node)
|
||||
{
|
||||
GtkTreeViewAccessible *accessible;
|
||||
guint row, n_rows, n_cols, i;
|
||||
@@ -1664,12 +1664,12 @@ _gtk_tree_view_accessible_add (GtkTreeView *treeview,
|
||||
|
||||
if (node == NULL)
|
||||
{
|
||||
row = tree->parent_tree ? _gtk_rbtree_node_get_index (tree->parent_tree, tree->parent_node) : 0;
|
||||
row = tree->parent_tree ? _gtk_tree_rbtree_node_get_index (tree->parent_tree, tree->parent_node) : 0;
|
||||
n_rows = tree->root->total_count;
|
||||
}
|
||||
else
|
||||
{
|
||||
row = _gtk_rbtree_node_get_index (tree, node);
|
||||
row = _gtk_tree_rbtree_node_get_index (tree, node);
|
||||
n_rows = 1 + (node->children ? node->children->root->total_count : 0);
|
||||
}
|
||||
|
||||
@@ -1688,8 +1688,8 @@ _gtk_tree_view_accessible_add (GtkTreeView *treeview,
|
||||
|
||||
void
|
||||
_gtk_tree_view_accessible_remove (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node)
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node)
|
||||
{
|
||||
GtkTreeViewAccessibleCellInfo *cell_info;
|
||||
GHashTableIter iter;
|
||||
@@ -1704,12 +1704,12 @@ _gtk_tree_view_accessible_remove (GtkTreeView *treeview,
|
||||
|
||||
if (node == NULL)
|
||||
{
|
||||
row = tree->parent_tree ? _gtk_rbtree_node_get_index (tree->parent_tree, tree->parent_node) : 0;
|
||||
row = tree->parent_tree ? _gtk_tree_rbtree_node_get_index (tree->parent_tree, tree->parent_node) : 0;
|
||||
n_rows = tree->root->total_count + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
row = _gtk_rbtree_node_get_index (tree, node);
|
||||
row = _gtk_tree_rbtree_node_get_index (tree, node);
|
||||
n_rows = 1 + (node->children ? node->children->root->total_count : 0);
|
||||
|
||||
tree = node->children;
|
||||
@@ -1731,7 +1731,7 @@ _gtk_tree_view_accessible_remove (GtkTreeView *treeview,
|
||||
{
|
||||
if (node == cell_info->node ||
|
||||
tree == cell_info->tree ||
|
||||
(tree && _gtk_rbtree_contains (tree, cell_info->tree)))
|
||||
(tree && _gtk_tree_rbtree_contains (tree, cell_info->tree)))
|
||||
g_hash_table_iter_remove (&iter);
|
||||
}
|
||||
}
|
||||
@@ -1739,8 +1739,8 @@ _gtk_tree_view_accessible_remove (GtkTreeView *treeview,
|
||||
|
||||
void
|
||||
_gtk_tree_view_accessible_changed (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node)
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node)
|
||||
{
|
||||
GtkTreeViewAccessible *accessible;
|
||||
guint i;
|
||||
@@ -1955,8 +1955,8 @@ _gtk_tree_view_accessible_update_focus_column (GtkTreeView *treeview,
|
||||
{
|
||||
GtkTreeViewAccessible *accessible;
|
||||
AtkObject *obj;
|
||||
GtkRBTree *cursor_tree;
|
||||
GtkRBNode *cursor_node;
|
||||
GtkTreeRBTree *cursor_tree;
|
||||
GtkTreeRBNode *cursor_node;
|
||||
GtkCellAccessible *cell;
|
||||
|
||||
old_focus = get_effective_focus_column (treeview, old_focus);
|
||||
@@ -1994,8 +1994,8 @@ _gtk_tree_view_accessible_update_focus_column (GtkTreeView *treeview,
|
||||
|
||||
void
|
||||
_gtk_tree_view_accessible_add_state (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkCellRendererState state)
|
||||
{
|
||||
GtkTreeViewAccessible *accessible;
|
||||
@@ -2059,8 +2059,8 @@ _gtk_tree_view_accessible_add_state (GtkTreeView *treeview,
|
||||
|
||||
void
|
||||
_gtk_tree_view_accessible_remove_state (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkCellRendererState state)
|
||||
{
|
||||
GtkTreeViewAccessible *accessible;
|
||||
|
||||
@@ -25,14 +25,14 @@ G_BEGIN_DECLS
|
||||
/* called by treeview code */
|
||||
void _gtk_tree_view_accessible_reorder (GtkTreeView *treeview);
|
||||
void _gtk_tree_view_accessible_add (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
void _gtk_tree_view_accessible_remove (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
void _gtk_tree_view_accessible_changed (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
|
||||
void _gtk_tree_view_accessible_add_column (GtkTreeView *treeview,
|
||||
GtkTreeViewColumn *column,
|
||||
@@ -52,12 +52,12 @@ void _gtk_tree_view_accessible_update_focus_column
|
||||
GtkTreeViewColumn *new_focus);
|
||||
|
||||
void _gtk_tree_view_accessible_add_state (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkCellRendererState state);
|
||||
void _gtk_tree_view_accessible_remove_state (GtkTreeView *treeview,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkCellRendererState state);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
@@ -139,7 +139,9 @@
|
||||
#include <gtk/gtklevelbar.h>
|
||||
#include <gtk/gtklinkbutton.h>
|
||||
#include <gtk/gtklistbox.h>
|
||||
#include <gtk/gtklistitem.h>
|
||||
#include <gtk/gtkliststore.h>
|
||||
#include <gtk/gtklistview.h>
|
||||
#include <gtk/gtklockbutton.h>
|
||||
#include <gtk/gtkmain.h>
|
||||
#include <gtk/gtkmaplistmodel.h>
|
||||
@@ -155,7 +157,9 @@
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtkmodelbutton.h>
|
||||
#include <gtk/gtkmountoperation.h>
|
||||
#include <gtk/gtkmultiselection.h>
|
||||
#include <gtk/gtknativedialog.h>
|
||||
#include <gtk/gtknoselection.h>
|
||||
#include <gtk/gtknotebook.h>
|
||||
#include <gtk/gtkorientable.h>
|
||||
#include <gtk/gtkoverlay.h>
|
||||
@@ -186,6 +190,7 @@
|
||||
#include <gtk/gtksearchbar.h>
|
||||
#include <gtk/gtksearchentry.h>
|
||||
#include <gtk/gtkselection.h>
|
||||
#include <gtk/gtkselectionmodel.h>
|
||||
#include <gtk/gtkseparator.h>
|
||||
#include <gtk/gtkseparatormenuitem.h>
|
||||
#include <gtk/gtkseparatortoolitem.h>
|
||||
@@ -196,6 +201,7 @@
|
||||
#include <gtk/gtkshortcutsshortcut.h>
|
||||
#include <gtk/gtkshortcutswindow.h>
|
||||
#include <gtk/gtkshow.h>
|
||||
#include <gtk/gtksingleselection.h>
|
||||
#include <gtk/gtkslicelistmodel.h>
|
||||
#include <gtk/gtksnapshot.h>
|
||||
#include <gtk/gtksortlistmodel.h>
|
||||
|
||||
@@ -1,761 +0,0 @@
|
||||
/* gtkrbtree.c
|
||||
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkcssrbtreeprivate.h"
|
||||
|
||||
#include "gtkdebug.h"
|
||||
|
||||
typedef struct _GtkCssRbNode GtkCssRbNode;
|
||||
|
||||
struct _GtkCssRbTree
|
||||
{
|
||||
guint ref_count;
|
||||
|
||||
gsize element_size;
|
||||
gsize augment_size;
|
||||
GtkCssRbTreeAugmentFunc augment_func;
|
||||
GDestroyNotify clear_func;
|
||||
GDestroyNotify clear_augment_func;
|
||||
|
||||
GtkCssRbNode *root;
|
||||
};
|
||||
|
||||
struct _GtkCssRbNode
|
||||
{
|
||||
guint red :1;
|
||||
guint dirty :1;
|
||||
|
||||
GtkCssRbNode *left;
|
||||
GtkCssRbNode *right;
|
||||
GtkCssRbNode *parent;
|
||||
};
|
||||
|
||||
#define NODE_FROM_POINTER(ptr) ((GtkCssRbNode *) ((ptr) ? (((guchar *) (ptr)) - sizeof (GtkCssRbNode)) : NULL))
|
||||
#define NODE_TO_POINTER(node) ((gpointer) ((node) ? (((guchar *) (node)) + sizeof (GtkCssRbNode)) : NULL))
|
||||
#define NODE_TO_AUG_POINTER(tree, node) ((gpointer) ((node) ? (((guchar *) (node)) + sizeof (GtkCssRbNode) + (tree)->element_size) : NULL))
|
||||
|
||||
static inline gsize
|
||||
gtk_css_rb_node_get_size (GtkCssRbTree *tree)
|
||||
{
|
||||
return sizeof (GtkCssRbNode) + tree->element_size + tree->augment_size;
|
||||
}
|
||||
|
||||
static GtkCssRbNode *
|
||||
gtk_css_rb_node_new (GtkCssRbTree *tree)
|
||||
{
|
||||
GtkCssRbNode *result;
|
||||
|
||||
result = g_slice_alloc0 (gtk_css_rb_node_get_size (tree));
|
||||
|
||||
result->red = TRUE;
|
||||
result->dirty = TRUE;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_css_rb_node_free (GtkCssRbTree *tree,
|
||||
GtkCssRbNode *node)
|
||||
{
|
||||
if (tree->clear_func)
|
||||
tree->clear_func (NODE_TO_POINTER (node));
|
||||
if (tree->clear_augment_func)
|
||||
tree->clear_augment_func (NODE_TO_AUG_POINTER (tree, node));
|
||||
|
||||
g_slice_free1 (gtk_css_rb_node_get_size (tree), node);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_css_rb_node_free_deep (GtkCssRbTree *tree,
|
||||
GtkCssRbNode *node)
|
||||
{
|
||||
GtkCssRbNode *right = node->right;
|
||||
|
||||
if (node->left)
|
||||
gtk_css_rb_node_free_deep (tree, node->left);
|
||||
|
||||
gtk_css_rb_node_free (tree, node);
|
||||
|
||||
if (right)
|
||||
gtk_css_rb_node_free_deep (tree, right);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_css_rb_node_mark_dirty (GtkCssRbNode *node,
|
||||
gboolean mark_parent)
|
||||
{
|
||||
if (node->dirty)
|
||||
return;
|
||||
|
||||
node->dirty = TRUE;
|
||||
|
||||
if (mark_parent && node->parent)
|
||||
gtk_css_rb_node_mark_dirty (node->parent, TRUE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_css_rb_node_clean (GtkCssRbTree *tree,
|
||||
GtkCssRbNode *node)
|
||||
{
|
||||
if (!node->dirty)
|
||||
return;
|
||||
|
||||
node->dirty = FALSE;
|
||||
if (tree->augment_func)
|
||||
tree->augment_func (tree,
|
||||
NODE_TO_AUG_POINTER (tree, node),
|
||||
NODE_TO_POINTER (node),
|
||||
NODE_TO_POINTER (node->left),
|
||||
NODE_TO_POINTER (node->right));
|
||||
}
|
||||
|
||||
static GtkCssRbNode *
|
||||
gtk_css_rb_node_get_first (GtkCssRbNode *node)
|
||||
{
|
||||
while (node->left)
|
||||
node = node->left;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static GtkCssRbNode *
|
||||
gtk_css_rb_node_get_last (GtkCssRbNode *node)
|
||||
{
|
||||
while (node->right)
|
||||
node = node->right;
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static GtkCssRbNode *
|
||||
gtk_css_rb_node_get_previous (GtkCssRbNode *node)
|
||||
{
|
||||
GtkCssRbNode *parent;
|
||||
|
||||
if (node->left)
|
||||
return gtk_css_rb_node_get_last (node->left);
|
||||
|
||||
for (parent = node->parent; parent != NULL; parent = node->parent)
|
||||
{
|
||||
if (parent->right == node)
|
||||
return parent;
|
||||
|
||||
node = parent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static GtkCssRbNode *
|
||||
gtk_css_rb_node_get_next (GtkCssRbNode *node)
|
||||
{
|
||||
GtkCssRbNode *parent;
|
||||
|
||||
if (node->right)
|
||||
return gtk_css_rb_node_get_first (node->right);
|
||||
|
||||
for (parent = node->parent; parent != NULL; parent = node->parent)
|
||||
{
|
||||
if (parent->left == node)
|
||||
return parent;
|
||||
|
||||
node = parent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_css_rb_node_rotate_left (GtkCssRbTree *tree,
|
||||
GtkCssRbNode *node)
|
||||
{
|
||||
GtkCssRbNode *right;
|
||||
|
||||
right = node->right;
|
||||
|
||||
node->right = right->left;
|
||||
if (right->left)
|
||||
right->left->parent = node;
|
||||
|
||||
right->parent = node->parent;
|
||||
if (node->parent)
|
||||
{
|
||||
if (node == node->parent->left)
|
||||
node->parent->left = right;
|
||||
else
|
||||
node->parent->right = right;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree->root = right;
|
||||
}
|
||||
|
||||
right->left = node;
|
||||
node->parent = right;
|
||||
|
||||
gtk_css_rb_node_mark_dirty (node, FALSE);
|
||||
gtk_css_rb_node_mark_dirty (right, FALSE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_css_rb_node_rotate_right (GtkCssRbTree *tree,
|
||||
GtkCssRbNode *node)
|
||||
{
|
||||
GtkCssRbNode *left;
|
||||
|
||||
left = node->left;
|
||||
|
||||
node->left = left->right;
|
||||
if (left->right)
|
||||
left->right->parent = node;
|
||||
|
||||
left->parent = node->parent;
|
||||
if (node->parent)
|
||||
{
|
||||
if (node == node->parent->right)
|
||||
node->parent->right = left;
|
||||
else
|
||||
node->parent->left = left;
|
||||
}
|
||||
else
|
||||
{
|
||||
tree->root = left;
|
||||
}
|
||||
|
||||
/* link node and left */
|
||||
left->right = node;
|
||||
node->parent = left;
|
||||
|
||||
gtk_css_rb_node_mark_dirty (node, FALSE);
|
||||
gtk_css_rb_node_mark_dirty (left, FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_red (GtkCssRbNode *node_or_null)
|
||||
{
|
||||
if (node_or_null == NULL)
|
||||
return FALSE;
|
||||
else
|
||||
return node_or_null->red;
|
||||
}
|
||||
|
||||
static inline gboolean
|
||||
is_black (GtkCssRbNode *node_or_null)
|
||||
{
|
||||
return !is_red (node_or_null);
|
||||
}
|
||||
|
||||
static void
|
||||
set_black (GtkCssRbNode *node_or_null)
|
||||
{
|
||||
if (node_or_null == NULL)
|
||||
return;
|
||||
|
||||
node_or_null->red = FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
set_red (GtkCssRbNode *node_or_null)
|
||||
{
|
||||
if (node_or_null == NULL)
|
||||
return;
|
||||
|
||||
node_or_null->red = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_css_rb_tree_insert_fixup (GtkCssRbTree *tree,
|
||||
GtkCssRbNode *node)
|
||||
{
|
||||
|
||||
/* check Red-Black properties */
|
||||
while (node->parent && is_red (node->parent))
|
||||
{
|
||||
/* we have a violation */
|
||||
g_assert (node->parent->parent);
|
||||
|
||||
if (node->parent == node->parent->parent->left)
|
||||
{
|
||||
GtkCssRbNode *uncle = node->parent->parent->right;
|
||||
|
||||
if (is_red (uncle))
|
||||
{
|
||||
/* uncle is red */
|
||||
set_black (node->parent);
|
||||
set_black (uncle);
|
||||
set_red (node->parent->parent);
|
||||
node = node->parent->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* uncle is black */
|
||||
if (node == node->parent->right)
|
||||
{
|
||||
/* make node a left child */
|
||||
node = node->parent;
|
||||
gtk_css_rb_node_rotate_left (tree, node);
|
||||
}
|
||||
/* recolor and rotate */
|
||||
set_black (node->parent);
|
||||
set_red (node->parent->parent);
|
||||
gtk_css_rb_node_rotate_right (tree, node->parent->parent);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* mirror image of above code */
|
||||
GtkCssRbNode *uncle = node->parent->parent->left;
|
||||
|
||||
if (is_red (uncle))
|
||||
{
|
||||
/* uncle is red */
|
||||
set_black (node->parent);
|
||||
set_black (uncle);
|
||||
set_red (node->parent->parent);
|
||||
node = node->parent->parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* uncle is black */
|
||||
if (node == node->parent->left)
|
||||
{
|
||||
node = node->parent;
|
||||
gtk_css_rb_node_rotate_right (tree, node);
|
||||
}
|
||||
set_black (node->parent);
|
||||
set_red (node->parent->parent);
|
||||
gtk_css_rb_node_rotate_left (tree, node->parent->parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_black (tree->root);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_css_rb_tree_remove_node_fixup (GtkCssRbTree *tree,
|
||||
GtkCssRbNode *node,
|
||||
GtkCssRbNode *parent)
|
||||
{
|
||||
while (node != tree->root && is_black (node))
|
||||
{
|
||||
if (node == parent->left)
|
||||
{
|
||||
GtkCssRbNode *w = parent->right;
|
||||
|
||||
if (is_red (w))
|
||||
{
|
||||
set_black (w);
|
||||
set_red (parent);
|
||||
gtk_css_rb_node_rotate_left (tree, parent);
|
||||
w = parent->right;
|
||||
}
|
||||
if (is_black (w->left) && is_black (w->right))
|
||||
{
|
||||
set_red (w);
|
||||
node = parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_black (w->right))
|
||||
{
|
||||
set_black (w->left);
|
||||
set_red (w);
|
||||
gtk_css_rb_node_rotate_right (tree, w);
|
||||
w = parent->right;
|
||||
}
|
||||
w->red = parent->red;
|
||||
set_black (parent);
|
||||
set_black (w->right);
|
||||
gtk_css_rb_node_rotate_left (tree, parent);
|
||||
node = tree->root;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkCssRbNode *w = parent->left;
|
||||
if (is_red (w))
|
||||
{
|
||||
set_black (w);
|
||||
set_red (parent);
|
||||
gtk_css_rb_node_rotate_right (tree, parent);
|
||||
w = parent->left;
|
||||
}
|
||||
if (is_black (w->right) && is_black (w->left))
|
||||
{
|
||||
set_red (w);
|
||||
node = parent;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (is_black (w->left))
|
||||
{
|
||||
set_black (w->right);
|
||||
set_red (w);
|
||||
gtk_css_rb_node_rotate_left (tree, w);
|
||||
w = parent->left;
|
||||
}
|
||||
w->red = parent->red;
|
||||
set_black (parent);
|
||||
set_black (w->left);
|
||||
gtk_css_rb_node_rotate_right (tree, parent);
|
||||
node = tree->root;
|
||||
}
|
||||
}
|
||||
|
||||
parent = node->parent;
|
||||
}
|
||||
|
||||
set_black (node);
|
||||
}
|
||||
|
||||
GtkCssRbTree *
|
||||
gtk_css_rb_tree_new_for_size (gsize element_size,
|
||||
gsize augment_size,
|
||||
GtkCssRbTreeAugmentFunc augment_func,
|
||||
GDestroyNotify clear_func,
|
||||
GDestroyNotify clear_augment_func)
|
||||
{
|
||||
GtkCssRbTree *tree;
|
||||
|
||||
tree = g_slice_new0 (GtkCssRbTree);
|
||||
tree->ref_count = 1;
|
||||
|
||||
tree->element_size = element_size;
|
||||
tree->augment_size = augment_size;
|
||||
tree->augment_func = augment_func;
|
||||
tree->clear_func = clear_func;
|
||||
tree->clear_augment_func = clear_augment_func;
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
GtkCssRbTree *
|
||||
gtk_css_rb_tree_ref (GtkCssRbTree *tree)
|
||||
{
|
||||
tree->ref_count++;
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_css_rb_tree_unref (GtkCssRbTree *tree)
|
||||
{
|
||||
tree->ref_count--;
|
||||
if (tree->ref_count > 0)
|
||||
return;
|
||||
|
||||
if (tree->root)
|
||||
gtk_css_rb_node_free_deep (tree, tree->root);
|
||||
|
||||
g_slice_free (GtkCssRbTree, tree);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_get_first (GtkCssRbTree *tree)
|
||||
{
|
||||
if (tree->root == NULL)
|
||||
return NULL;
|
||||
|
||||
return NODE_TO_POINTER (gtk_css_rb_node_get_first (tree->root));
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_get_last (GtkCssRbTree *tree)
|
||||
{
|
||||
if (tree->root == NULL)
|
||||
return NULL;
|
||||
|
||||
return NODE_TO_POINTER (gtk_css_rb_node_get_last (tree->root));
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_get_previous (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (gtk_css_rb_node_get_previous (NODE_FROM_POINTER (node)));
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_get_next (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (gtk_css_rb_node_get_next (NODE_FROM_POINTER (node)));
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_get_root (GtkCssRbTree *tree)
|
||||
{
|
||||
return NODE_TO_POINTER (tree->root);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_get_parent (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (NODE_FROM_POINTER (node)->parent);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_get_left (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (NODE_FROM_POINTER (node)->left);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_get_right (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
return NODE_TO_POINTER (NODE_FROM_POINTER (node)->right);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_get_augment (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
GtkCssRbNode *rbnode = NODE_FROM_POINTER (node);
|
||||
|
||||
gtk_css_rb_node_clean (tree, rbnode);
|
||||
|
||||
return NODE_TO_AUG_POINTER (tree, rbnode);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_css_rb_tree_mark_dirty (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
gtk_css_rb_node_mark_dirty (NODE_FROM_POINTER (node), TRUE);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_insert_before (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
GtkCssRbNode *result;
|
||||
|
||||
/* setup new node */
|
||||
result = gtk_css_rb_node_new (tree);
|
||||
|
||||
if (tree->root == NULL)
|
||||
{
|
||||
g_assert (node == NULL);
|
||||
tree->root = result;
|
||||
}
|
||||
else if (node == NULL)
|
||||
{
|
||||
return gtk_css_rb_tree_insert_after (tree, gtk_css_rb_tree_get_last (tree));
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkCssRbNode *current = NODE_FROM_POINTER (node);
|
||||
|
||||
if (current->left)
|
||||
{
|
||||
current = gtk_css_rb_node_get_last (current->left);
|
||||
current->right = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
current->left = result;
|
||||
}
|
||||
result->parent = current;
|
||||
gtk_css_rb_node_mark_dirty (current, TRUE);
|
||||
}
|
||||
|
||||
gtk_css_rb_tree_insert_fixup (tree, result);
|
||||
|
||||
return NODE_TO_POINTER (result);
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_insert_after (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
GtkCssRbNode *result;
|
||||
|
||||
/* setup new node */
|
||||
result = gtk_css_rb_node_new (tree);
|
||||
|
||||
if (tree->root == NULL)
|
||||
{
|
||||
g_assert (node == NULL);
|
||||
tree->root = result;
|
||||
}
|
||||
else if (node == NULL)
|
||||
{
|
||||
return gtk_css_rb_tree_insert_before (tree, gtk_css_rb_tree_get_first (tree));
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkCssRbNode *current = NODE_FROM_POINTER (node);
|
||||
|
||||
if (current->right)
|
||||
{
|
||||
current = gtk_css_rb_node_get_first (current->right);
|
||||
current->left = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
current->right = result;
|
||||
}
|
||||
result->parent = current;
|
||||
gtk_css_rb_node_mark_dirty (current, TRUE);
|
||||
}
|
||||
|
||||
gtk_css_rb_tree_insert_fixup (tree, result);
|
||||
|
||||
return NODE_TO_POINTER (result);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_css_rb_tree_remove (GtkCssRbTree *tree,
|
||||
gpointer node)
|
||||
{
|
||||
GtkCssRbNode *x, *y, *real_node;
|
||||
|
||||
real_node = NODE_FROM_POINTER (node);
|
||||
y = real_node;
|
||||
if (y->left && y->right)
|
||||
{
|
||||
y = y->right;
|
||||
|
||||
while (y->left)
|
||||
y = y->left;
|
||||
}
|
||||
|
||||
/* x is y's only child, or nil */
|
||||
if (y->left)
|
||||
x = y->left;
|
||||
else
|
||||
x = y->right;
|
||||
|
||||
/* remove y from the parent chain */
|
||||
if (x != NULL)
|
||||
x->parent = y->parent;
|
||||
if (y->parent)
|
||||
{
|
||||
if (y == y->parent->left)
|
||||
y->parent->left = x;
|
||||
else
|
||||
y->parent->right = x;
|
||||
gtk_css_rb_node_mark_dirty (y->parent, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
tree->root = x;
|
||||
}
|
||||
|
||||
/* We need to clean up the validity of the tree.
|
||||
*/
|
||||
if (is_black (y))
|
||||
gtk_css_rb_tree_remove_node_fixup (tree, x, y->parent);
|
||||
|
||||
if (y != real_node)
|
||||
{
|
||||
/* Move the node over */
|
||||
if (is_red (real_node) != is_red (y))
|
||||
y->red = !y->red;
|
||||
|
||||
y->left = real_node->left;
|
||||
if (y->left)
|
||||
y->left->parent = y;
|
||||
y->right = real_node->right;
|
||||
if (y->right)
|
||||
y->right->parent = y;
|
||||
y->parent = real_node->parent;
|
||||
if (y->parent)
|
||||
{
|
||||
if (y->parent->left == real_node)
|
||||
y->parent->left = y;
|
||||
else
|
||||
y->parent->right = y;
|
||||
gtk_css_rb_node_mark_dirty (y->parent, TRUE);
|
||||
}
|
||||
else
|
||||
{
|
||||
tree->root = y;
|
||||
}
|
||||
gtk_css_rb_node_mark_dirty (y, TRUE);
|
||||
}
|
||||
|
||||
gtk_css_rb_node_free (tree, real_node);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_css_rb_tree_remove_all (GtkCssRbTree *tree)
|
||||
{
|
||||
if (tree->root)
|
||||
gtk_css_rb_node_free_deep (tree, tree->root);
|
||||
|
||||
tree->root = NULL;
|
||||
}
|
||||
|
||||
gpointer
|
||||
gtk_css_rb_tree_find (GtkCssRbTree *tree,
|
||||
gpointer *out_before,
|
||||
gpointer *out_after,
|
||||
GtkCssRbTreeFindFunc find_func,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkCssRbNode *node, *before = NULL, *after = NULL;
|
||||
int cmp;
|
||||
|
||||
if (tree->root == NULL)
|
||||
{
|
||||
if (out_before)
|
||||
*out_before = NULL;
|
||||
if (out_after)
|
||||
*out_after = NULL;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = tree->root;
|
||||
for (cmp = find_func (tree, NODE_TO_POINTER (node), user_data);
|
||||
cmp != 0;
|
||||
cmp = find_func (tree, NODE_TO_POINTER (node), user_data))
|
||||
{
|
||||
if (cmp < 0)
|
||||
{
|
||||
before = node;
|
||||
node = node->right;
|
||||
}
|
||||
else /* cmp > 0 */
|
||||
{
|
||||
after = node;
|
||||
node = node->left;
|
||||
}
|
||||
if (node == NULL)
|
||||
{
|
||||
if (out_before)
|
||||
*out_before = NODE_TO_POINTER (before);
|
||||
if (out_after)
|
||||
*out_after = NODE_TO_POINTER (after);
|
||||
return NULL;;
|
||||
}
|
||||
}
|
||||
|
||||
if (out_before)
|
||||
*out_before = NODE_TO_POINTER (gtk_css_rb_node_get_previous (node));
|
||||
if (out_after)
|
||||
*out_after = NODE_TO_POINTER (gtk_css_rb_node_get_next (node));
|
||||
|
||||
return NODE_TO_POINTER (node);
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
/* gtkrb_tree.h
|
||||
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* A Red-Black Tree implementation used specifically by GtkTreeView.
|
||||
*/
|
||||
#ifndef __GTK_CSS_RB_TREE_H__
|
||||
#define __GTK_CSS_RB_TREE_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
typedef struct _GtkCssRbTree GtkCssRbTree;
|
||||
|
||||
typedef void (* GtkCssRbTreeAugmentFunc) (GtkCssRbTree *tree,
|
||||
gpointer node_augment,
|
||||
gpointer node,
|
||||
gpointer left,
|
||||
gpointer right);
|
||||
typedef int (* GtkCssRbTreeFindFunc) (GtkCssRbTree *tree,
|
||||
gpointer node,
|
||||
gpointer user_data);
|
||||
|
||||
GtkCssRbTree * gtk_css_rb_tree_new_for_size (gsize element_size,
|
||||
gsize augment_size,
|
||||
GtkCssRbTreeAugmentFunc augment_func,
|
||||
GDestroyNotify clear_func,
|
||||
GDestroyNotify clear_augment_func);
|
||||
#define gtk_css_rb_tree_new(type, augment_type, augment_func, clear_func, clear_augment_func) \
|
||||
gtk_css_rb_tree_new_for_size (sizeof (type), sizeof (augment_type), (augment_func), (clear_func), (clear_augment_func))
|
||||
|
||||
GtkCssRbTree * gtk_css_rb_tree_ref (GtkCssRbTree *tree);
|
||||
void gtk_css_rb_tree_unref (GtkCssRbTree *tree);
|
||||
|
||||
gpointer gtk_css_rb_tree_get_first (GtkCssRbTree *tree);
|
||||
gpointer gtk_css_rb_tree_get_last (GtkCssRbTree *tree);
|
||||
gpointer gtk_css_rb_tree_get_previous (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_css_rb_tree_get_next (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
|
||||
gpointer gtk_css_rb_tree_get_root (GtkCssRbTree *tree);
|
||||
gpointer gtk_css_rb_tree_get_parent (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_css_rb_tree_get_left (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_css_rb_tree_get_right (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_css_rb_tree_get_augment (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
|
||||
void gtk_css_rb_tree_mark_dirty (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
|
||||
gpointer gtk_css_rb_tree_insert_before (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_css_rb_tree_insert_after (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
void gtk_css_rb_tree_remove (GtkCssRbTree *tree,
|
||||
gpointer node);
|
||||
void gtk_css_rb_tree_remove_all (GtkCssRbTree *tree);
|
||||
|
||||
gpointer gtk_css_rb_tree_find (GtkCssRbTree *tree,
|
||||
gpointer *out_before,
|
||||
gpointer *out_after,
|
||||
GtkCssRbTreeFindFunc find_func,
|
||||
gpointer user_data);
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __GTK_CSS_RB_TREE_H__ */
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "gtkfilterlistmodel.h"
|
||||
|
||||
#include "gtkcssrbtreeprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
@@ -69,7 +69,7 @@ struct _GtkFilterListModel
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
|
||||
GtkCssRbTree *items; /* NULL if filter_func == NULL */
|
||||
GtkRbTree *items; /* NULL if filter_func == NULL */
|
||||
};
|
||||
|
||||
struct _GtkFilterListModelClass
|
||||
@@ -80,22 +80,22 @@ struct _GtkFilterListModelClass
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static FilterNode *
|
||||
gtk_filter_list_model_get_nth_filtered (GtkCssRbTree *tree,
|
||||
gtk_filter_list_model_get_nth_filtered (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *out_unfiltered)
|
||||
{
|
||||
FilterNode *node, *tmp;
|
||||
guint unfiltered;
|
||||
|
||||
node = gtk_css_rb_tree_get_root (tree);
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
unfiltered = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_css_rb_tree_get_left (tree, node);
|
||||
tmp = gtk_rb_tree_get_left (tree, node);
|
||||
if (tmp)
|
||||
{
|
||||
FilterAugment *aug = gtk_css_rb_tree_get_augment (tree, tmp);
|
||||
FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_visible)
|
||||
{
|
||||
node = tmp;
|
||||
@@ -114,7 +114,7 @@ gtk_filter_list_model_get_nth_filtered (GtkCssRbTree *tree,
|
||||
|
||||
unfiltered++;
|
||||
|
||||
node = gtk_css_rb_tree_get_right (tree, node);
|
||||
node = gtk_rb_tree_get_right (tree, node);
|
||||
}
|
||||
|
||||
if (out_unfiltered)
|
||||
@@ -124,22 +124,22 @@ gtk_filter_list_model_get_nth_filtered (GtkCssRbTree *tree,
|
||||
}
|
||||
|
||||
static FilterNode *
|
||||
gtk_filter_list_model_get_nth (GtkCssRbTree *tree,
|
||||
gtk_filter_list_model_get_nth (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *out_filtered)
|
||||
{
|
||||
FilterNode *node, *tmp;
|
||||
guint filtered;
|
||||
|
||||
node = gtk_css_rb_tree_get_root (tree);
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
filtered = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_css_rb_tree_get_left (tree, node);
|
||||
tmp = gtk_rb_tree_get_left (tree, node);
|
||||
if (tmp)
|
||||
{
|
||||
FilterAugment *aug = gtk_css_rb_tree_get_augment (tree, tmp);
|
||||
FilterAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
@@ -156,7 +156,7 @@ gtk_filter_list_model_get_nth (GtkCssRbTree *tree,
|
||||
if (node->visible)
|
||||
filtered++;
|
||||
|
||||
node = gtk_css_rb_tree_get_right (tree, node);
|
||||
node = gtk_rb_tree_get_right (tree, node);
|
||||
}
|
||||
|
||||
if (out_filtered)
|
||||
@@ -186,11 +186,11 @@ gtk_filter_list_model_get_n_items (GListModel *list)
|
||||
if (!self->items)
|
||||
return g_list_model_get_n_items (self->model);
|
||||
|
||||
node = gtk_css_rb_tree_get_root (self->items);
|
||||
node = gtk_rb_tree_get_root (self->items);
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
aug = gtk_css_rb_tree_get_augment (self->items, node);
|
||||
aug = gtk_rb_tree_get_augment (self->items, node);
|
||||
return aug->n_visible;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ gtk_filter_list_model_add_items (GtkFilterListModel *self,
|
||||
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
node = gtk_css_rb_tree_insert_before (self->items, after);
|
||||
node = gtk_rb_tree_insert_before (self->items, after);
|
||||
node->visible = gtk_filter_list_model_run_filter (self, position + i);
|
||||
if (node->visible)
|
||||
n_visible++;
|
||||
@@ -280,10 +280,10 @@ gtk_filter_list_model_items_changed_cb (GListModel *model,
|
||||
filter_removed = 0;
|
||||
for (i = 0; i < removed; i++)
|
||||
{
|
||||
FilterNode *next = gtk_css_rb_tree_get_next (self->items, node);
|
||||
FilterNode *next = gtk_rb_tree_get_next (self->items, node);
|
||||
if (node->visible)
|
||||
filter_removed++;
|
||||
gtk_css_rb_tree_remove (self->items, node);
|
||||
gtk_rb_tree_remove (self->items, node);
|
||||
node = next;
|
||||
}
|
||||
|
||||
@@ -354,7 +354,7 @@ gtk_filter_list_model_clear_model (GtkFilterListModel *self)
|
||||
g_signal_handlers_disconnect_by_func (self->model, gtk_filter_list_model_items_changed_cb, self);
|
||||
g_clear_object (&self->model);
|
||||
if (self->items)
|
||||
gtk_css_rb_tree_remove_all (self->items);
|
||||
gtk_rb_tree_remove_all (self->items);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -368,7 +368,7 @@ gtk_filter_list_model_dispose (GObject *object)
|
||||
self->filter_func = NULL;
|
||||
self->user_data = NULL;
|
||||
self->user_destroy = NULL;
|
||||
g_clear_pointer (&self->items, gtk_css_rb_tree_unref);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_filter_list_model_parent_class)->dispose (object);
|
||||
};
|
||||
@@ -428,13 +428,13 @@ gtk_filter_list_model_init (GtkFilterListModel *self)
|
||||
|
||||
|
||||
static void
|
||||
gtk_filter_list_model_augment (GtkCssRbTree *filter,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
gtk_filter_list_model_augment (GtkRbTree *filter,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
FilterNode *node= _node;
|
||||
FilterNode *node = _node;
|
||||
FilterAugment *aug = _aug;
|
||||
|
||||
aug->n_items = 1;
|
||||
@@ -442,13 +442,13 @@ gtk_filter_list_model_augment (GtkCssRbTree *filter,
|
||||
|
||||
if (left)
|
||||
{
|
||||
FilterAugment *left_aug = gtk_css_rb_tree_get_augment (filter, left);
|
||||
FilterAugment *left_aug = gtk_rb_tree_get_augment (filter, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_visible += left_aug->n_visible;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
FilterAugment *right_aug = gtk_css_rb_tree_get_augment (filter, right);
|
||||
FilterAugment *right_aug = gtk_rb_tree_get_augment (filter, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_visible += right_aug->n_visible;
|
||||
}
|
||||
@@ -543,13 +543,13 @@ gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
|
||||
|
||||
if (!will_be_filtered)
|
||||
{
|
||||
g_clear_pointer (&self->items, gtk_css_rb_tree_unref);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
}
|
||||
else if (!was_filtered)
|
||||
{
|
||||
guint i, n_items;
|
||||
|
||||
self->items = gtk_css_rb_tree_new (FilterNode,
|
||||
self->items = gtk_rb_tree_new (FilterNode,
|
||||
FilterAugment,
|
||||
gtk_filter_list_model_augment,
|
||||
NULL, NULL);
|
||||
@@ -558,7 +558,7 @@ gtk_filter_list_model_set_filter_func (GtkFilterListModel *self,
|
||||
n_items = g_list_model_get_n_items (self->model);
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
FilterNode *node = gtk_css_rb_tree_insert_before (self->items, NULL);
|
||||
FilterNode *node = gtk_rb_tree_insert_before (self->items, NULL);
|
||||
node->visible = TRUE;
|
||||
}
|
||||
}
|
||||
@@ -675,9 +675,9 @@ gtk_filter_list_model_refilter (GtkFilterListModel *self)
|
||||
last_change = 0;
|
||||
n_is_visible = 0;
|
||||
n_was_visible = 0;
|
||||
for (i = 0, node = gtk_css_rb_tree_get_first (self->items);
|
||||
for (i = 0, node = gtk_rb_tree_get_first (self->items);
|
||||
node != NULL;
|
||||
i++, node = gtk_css_rb_tree_get_next (self->items, node))
|
||||
i++, node = gtk_rb_tree_get_next (self->items, node))
|
||||
{
|
||||
visible = gtk_filter_list_model_run_filter (self, i);
|
||||
if (visible == node->visible)
|
||||
@@ -691,7 +691,7 @@ gtk_filter_list_model_refilter (GtkFilterListModel *self)
|
||||
}
|
||||
|
||||
node->visible = visible;
|
||||
gtk_css_rb_tree_mark_dirty (self->items, node);
|
||||
gtk_rb_tree_mark_dirty (self->items, node);
|
||||
first_change = MIN (n_is_visible, first_change);
|
||||
if (visible)
|
||||
n_is_visible++;
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "gtkflattenlistmodel.h"
|
||||
|
||||
#include "gtkcssrbtreeprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
@@ -66,7 +66,7 @@ struct _GtkFlattenListModel
|
||||
|
||||
GType item_type;
|
||||
GListModel *model;
|
||||
GtkCssRbTree *items; /* NULL if model == NULL */
|
||||
GtkRbTree *items; /* NULL if model == NULL */
|
||||
};
|
||||
|
||||
struct _GtkFlattenListModelClass
|
||||
@@ -77,21 +77,21 @@ struct _GtkFlattenListModelClass
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static FlattenNode *
|
||||
gtk_flatten_list_model_get_nth (GtkCssRbTree *tree,
|
||||
gtk_flatten_list_model_get_nth (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *model_position)
|
||||
{
|
||||
FlattenNode *node, *tmp;
|
||||
guint model_n_items;
|
||||
|
||||
node = gtk_css_rb_tree_get_root (tree);
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_css_rb_tree_get_left (tree, node);
|
||||
tmp = gtk_rb_tree_get_left (tree, node);
|
||||
if (tmp)
|
||||
{
|
||||
FlattenAugment *aug = gtk_css_rb_tree_get_augment (tree, tmp);
|
||||
FlattenAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
@@ -105,7 +105,7 @@ gtk_flatten_list_model_get_nth (GtkCssRbTree *tree,
|
||||
break;
|
||||
position -= model_n_items;
|
||||
|
||||
node = gtk_css_rb_tree_get_right (tree, node);
|
||||
node = gtk_rb_tree_get_right (tree, node);
|
||||
}
|
||||
|
||||
if (model_position)
|
||||
@@ -115,22 +115,22 @@ gtk_flatten_list_model_get_nth (GtkCssRbTree *tree,
|
||||
}
|
||||
|
||||
static FlattenNode *
|
||||
gtk_flatten_list_model_get_nth_model (GtkCssRbTree *tree,
|
||||
gtk_flatten_list_model_get_nth_model (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *items_before)
|
||||
{
|
||||
FlattenNode *node, *tmp;
|
||||
guint before;
|
||||
|
||||
node = gtk_css_rb_tree_get_root (tree);
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
before = 0;
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_css_rb_tree_get_left (tree, node);
|
||||
tmp = gtk_rb_tree_get_left (tree, node);
|
||||
if (tmp)
|
||||
{
|
||||
FlattenAugment *aug = gtk_css_rb_tree_get_augment (tree, tmp);
|
||||
FlattenAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_models)
|
||||
{
|
||||
node = tmp;
|
||||
@@ -145,7 +145,7 @@ gtk_flatten_list_model_get_nth_model (GtkCssRbTree *tree,
|
||||
position--;
|
||||
before += g_list_model_get_n_items (node->model);
|
||||
|
||||
node = gtk_css_rb_tree_get_right (tree, node);
|
||||
node = gtk_rb_tree_get_right (tree, node);
|
||||
}
|
||||
|
||||
if (items_before)
|
||||
@@ -172,11 +172,11 @@ gtk_flatten_list_model_get_n_items (GListModel *list)
|
||||
if (!self->items)
|
||||
return 0;
|
||||
|
||||
node = gtk_css_rb_tree_get_root (self->items);
|
||||
node = gtk_rb_tree_get_root (self->items);
|
||||
if (node == NULL)
|
||||
return 0;
|
||||
|
||||
aug = gtk_css_rb_tree_get_augment (self->items, node);
|
||||
aug = gtk_rb_tree_get_augment (self->items, node);
|
||||
return aug->n_items;
|
||||
}
|
||||
|
||||
@@ -220,18 +220,18 @@ gtk_flatten_list_model_items_changed_cb (GListModel *model,
|
||||
GtkFlattenListModel *self = node->list;
|
||||
guint real_position;
|
||||
|
||||
gtk_css_rb_tree_mark_dirty (self->items, node);
|
||||
gtk_rb_tree_mark_dirty (self->items, node);
|
||||
|
||||
for (real_position = position;
|
||||
(parent = gtk_css_rb_tree_get_parent (self->items, node)) != NULL;
|
||||
(parent = gtk_rb_tree_get_parent (self->items, node)) != NULL;
|
||||
node = parent)
|
||||
{
|
||||
FlattenNode *left = gtk_css_rb_tree_get_left (self->items, parent);
|
||||
FlattenNode *left = gtk_rb_tree_get_left (self->items, parent);
|
||||
if (left != node)
|
||||
{
|
||||
if (left)
|
||||
{
|
||||
FlattenAugment *aug = gtk_css_rb_tree_get_augment (self->items, left);
|
||||
FlattenAugment *aug = gtk_rb_tree_get_augment (self->items, left);
|
||||
real_position += aug->n_items;
|
||||
}
|
||||
real_position += g_list_model_get_n_items (parent->model);
|
||||
@@ -251,13 +251,13 @@ gtk_flatten_list_model_clear_node (gpointer _node)
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_flatten_list_model_augment (GtkCssRbTree *flatten,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
gtk_flatten_list_model_augment (GtkRbTree *flatten,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
FlattenNode *node= _node;
|
||||
FlattenNode *node = _node;
|
||||
FlattenAugment *aug = _aug;
|
||||
|
||||
aug->n_items = g_list_model_get_n_items (node->model);
|
||||
@@ -265,13 +265,13 @@ gtk_flatten_list_model_augment (GtkCssRbTree *flatten,
|
||||
|
||||
if (left)
|
||||
{
|
||||
FlattenAugment *left_aug = gtk_css_rb_tree_get_augment (flatten, left);
|
||||
FlattenAugment *left_aug = gtk_rb_tree_get_augment (flatten, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_models += left_aug->n_models;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
FlattenAugment *right_aug = gtk_css_rb_tree_get_augment (flatten, right);
|
||||
FlattenAugment *right_aug = gtk_rb_tree_get_augment (flatten, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_models += right_aug->n_models;
|
||||
}
|
||||
@@ -289,7 +289,7 @@ gtk_flatten_list_model_add_items (GtkFlattenListModel *self,
|
||||
added = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
node = gtk_css_rb_tree_insert_before (self->items, after);
|
||||
node = gtk_rb_tree_insert_before (self->items, after);
|
||||
node->model = g_list_model_get_item (self->model, position + i);
|
||||
g_warn_if_fail (g_type_is_a (g_list_model_get_item_type (node->model), self->item_type));
|
||||
g_signal_connect (node->model,
|
||||
@@ -366,9 +366,9 @@ gtk_flatten_list_model_model_items_changed_cb (GListModel *model,
|
||||
real_removed = 0;
|
||||
for (i = 0; i < removed; i++)
|
||||
{
|
||||
FlattenNode *next = gtk_css_rb_tree_get_next (self->items, node);
|
||||
FlattenNode *next = gtk_rb_tree_get_next (self->items, node);
|
||||
real_removed += g_list_model_get_n_items (node->model);
|
||||
gtk_css_rb_tree_remove (self->items, node);
|
||||
gtk_rb_tree_remove (self->items, node);
|
||||
node = next;
|
||||
}
|
||||
|
||||
@@ -385,7 +385,7 @@ gtk_flatten_list_clear_model (GtkFlattenListModel *self)
|
||||
{
|
||||
g_signal_handlers_disconnect_by_func (self->model, gtk_flatten_list_model_model_items_changed_cb, self);
|
||||
g_clear_object (&self->model);
|
||||
g_clear_pointer (&self->items, gtk_css_rb_tree_unref);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,11 +501,11 @@ gtk_flatten_list_model_set_model (GtkFlattenListModel *self,
|
||||
{
|
||||
g_object_ref (model);
|
||||
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_flatten_list_model_model_items_changed_cb), self);
|
||||
self->items = gtk_css_rb_tree_new (FlattenNode,
|
||||
FlattenAugment,
|
||||
gtk_flatten_list_model_augment,
|
||||
gtk_flatten_list_model_clear_node,
|
||||
NULL);
|
||||
self->items = gtk_rb_tree_new (FlattenNode,
|
||||
FlattenAugment,
|
||||
gtk_flatten_list_model_augment,
|
||||
gtk_flatten_list_model_clear_node,
|
||||
NULL);
|
||||
|
||||
added = gtk_flatten_list_model_add_items (self, NULL, 0, g_list_model_get_n_items (model));
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "gtkadjustment.h"
|
||||
#include "gtkbuildable.h"
|
||||
#include "gtkbox.h"
|
||||
#include "gtkcellrenderertext.h"
|
||||
#include "gtkcssnumbervalueprivate.h"
|
||||
#include "gtkentry.h"
|
||||
#include "gtksearchentry.h"
|
||||
@@ -46,8 +45,6 @@
|
||||
#include "gtkspinbutton.h"
|
||||
#include "gtkstylecontextprivate.h"
|
||||
#include "gtktextview.h"
|
||||
#include "gtktreeselection.h"
|
||||
#include "gtktreeview.h"
|
||||
#include "gtkwidget.h"
|
||||
#include "gtksettings.h"
|
||||
#include "gtkdialog.h"
|
||||
@@ -55,6 +52,10 @@
|
||||
#include "gtkcombobox.h"
|
||||
#include "gtkgesturemultipress.h"
|
||||
#include "gtkeventcontrollerscroll.h"
|
||||
#include "gtklistview.h"
|
||||
#include "gtkfilterlistmodel.h"
|
||||
#include "gtksingleselection.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
|
||||
#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
|
||||
#include <pango/pangofc-font.h>
|
||||
@@ -100,13 +101,12 @@ struct _GtkFontChooserWidgetPrivate
|
||||
GtkWidget *stack;
|
||||
GtkWidget *grid;
|
||||
GtkWidget *search_entry;
|
||||
GtkWidget *family_face_list;
|
||||
GtkTreeViewColumn *family_face_column;
|
||||
GtkCellRenderer *family_face_cell;
|
||||
GtkWidget *list_scrolled_window;
|
||||
GtkWidget *list_stack;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeModel *filter_model;
|
||||
|
||||
GtkWidget *family_face_listview;
|
||||
GListModel *listmodel;
|
||||
GListModel *filtermodel;
|
||||
GListModel *selectionmodel;
|
||||
|
||||
GtkWidget *preview;
|
||||
GtkWidget *preview2;
|
||||
@@ -127,8 +127,7 @@ struct _GtkFontChooserWidgetPrivate
|
||||
PangoFontDescription *font_desc;
|
||||
char *font_features;
|
||||
PangoLanguage *language;
|
||||
GtkTreeIter font_iter; /* invalid if font not available or pointer into model
|
||||
(not filter_model) to the row containing font */
|
||||
guint font_position;
|
||||
GtkFontFilterFunc filter_func;
|
||||
gpointer filter_data;
|
||||
GDestroyNotify filter_data_destroy;
|
||||
@@ -150,14 +149,6 @@ enum {
|
||||
PROP_TWEAK_ACTION
|
||||
};
|
||||
|
||||
/* Keep in line with GtkTreeStore defined in gtkfontchooserwidget.ui */
|
||||
enum {
|
||||
FAMILY_COLUMN,
|
||||
FACE_COLUMN,
|
||||
FONT_DESC_COLUMN,
|
||||
PREVIEW_TITLE_COLUMN
|
||||
};
|
||||
|
||||
static void gtk_font_chooser_widget_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
@@ -173,7 +164,7 @@ static void gtk_font_chooser_widget_display_changed (GtkWidget *widge
|
||||
|
||||
static gboolean gtk_font_chooser_widget_find_font (GtkFontChooserWidget *fontchooser,
|
||||
const PangoFontDescription *font_desc,
|
||||
GtkTreeIter *iter);
|
||||
guint *position);
|
||||
static void gtk_font_chooser_widget_ensure_selection (GtkFontChooserWidget *fontchooser);
|
||||
|
||||
static gchar *gtk_font_chooser_widget_get_font (GtkFontChooserWidget *fontchooser);
|
||||
@@ -183,7 +174,7 @@ static void gtk_font_chooser_widget_set_font (GtkFontChooserWidget *
|
||||
static PangoFontDescription *gtk_font_chooser_widget_get_font_desc (GtkFontChooserWidget *fontchooser);
|
||||
static void gtk_font_chooser_widget_merge_font_desc(GtkFontChooserWidget *fontchooser,
|
||||
const PangoFontDescription *font_desc,
|
||||
GtkTreeIter *iter);
|
||||
guint position);
|
||||
static void gtk_font_chooser_widget_take_font_desc (GtkFontChooserWidget *fontchooser,
|
||||
PangoFontDescription *font_desc);
|
||||
|
||||
@@ -191,29 +182,26 @@ static void gtk_font_chooser_widget_take_font_desc (GtkFontChoo
|
||||
static const gchar *gtk_font_chooser_widget_get_preview_text (GtkFontChooserWidget *fontchooser);
|
||||
static void gtk_font_chooser_widget_set_preview_text (GtkFontChooserWidget *fontchooser,
|
||||
const gchar *text);
|
||||
static PangoAttrList *gtk_font_chooser_widget_get_preview_attributes (GtkFontChooserWidget *fontchooser,
|
||||
const PangoFontDescription *font_desc);
|
||||
|
||||
static gboolean gtk_font_chooser_widget_get_show_preview_entry (GtkFontChooserWidget *fontchooser);
|
||||
static void gtk_font_chooser_widget_set_show_preview_entry (GtkFontChooserWidget *fontchooser,
|
||||
gboolean show_preview_entry);
|
||||
|
||||
static void gtk_font_chooser_widget_set_cell_size (GtkFontChooserWidget *fontchooser);
|
||||
static void gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
|
||||
gboolean force);
|
||||
static void gtk_font_chooser_widget_populate_features (GtkFontChooserWidget *fontchooser);
|
||||
static gboolean visible_func (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer user_data);
|
||||
static void gtk_font_chooser_widget_cell_data_func (GtkTreeViewColumn *column,
|
||||
GtkCellRenderer *cell,
|
||||
GtkTreeModel *tree_model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer user_data);
|
||||
static gboolean visible_func (gpointer item,
|
||||
gpointer user_data);
|
||||
static void gtk_font_chooser_widget_set_level (GtkFontChooserWidget *fontchooser,
|
||||
GtkFontChooserLevel level);
|
||||
static GtkFontChooserLevel gtk_font_chooser_widget_get_level (GtkFontChooserWidget *fontchooser);
|
||||
static void gtk_font_chooser_widget_set_language (GtkFontChooserWidget *fontchooser,
|
||||
const char *language);
|
||||
static void selection_changed (GtkTreeSelection *selection,
|
||||
static void selection_changed (GtkSelectionModel *selection,
|
||||
guint position,
|
||||
guint n_items,
|
||||
GtkFontChooserWidget *fontchooser);
|
||||
static void update_font_features (GtkFontChooserWidget *fontchooser);
|
||||
|
||||
@@ -226,48 +214,37 @@ G_DEFINE_TYPE_WITH_CODE (GtkFontChooserWidget, gtk_font_chooser_widget, GTK_TYPE
|
||||
|
||||
typedef struct _GtkDelayedFontDescription GtkDelayedFontDescription;
|
||||
struct _GtkDelayedFontDescription {
|
||||
PangoFontFace *face;
|
||||
GObject parent;
|
||||
PangoFontFamily *family;
|
||||
PangoFontFace *face;
|
||||
char *title;
|
||||
PangoFontDescription *desc;
|
||||
guint ref_count;
|
||||
};
|
||||
|
||||
typedef GObjectClass GtkDelayedFontDescriptionClass;
|
||||
|
||||
#define GTK_TYPE_DELAYED_FONT_DESCRIPTION (gtk_delayed_font_description_get_type ())
|
||||
static GType gtk_delayed_font_description_get_type (void);
|
||||
|
||||
G_DEFINE_TYPE (GtkDelayedFontDescription, gtk_delayed_font_description, G_TYPE_OBJECT)
|
||||
|
||||
static GtkDelayedFontDescription *
|
||||
gtk_delayed_font_description_new (PangoFontFace *face)
|
||||
gtk_delayed_font_description_new (PangoFontFamily *family,
|
||||
PangoFontFace *face,
|
||||
const char *title)
|
||||
{
|
||||
GtkDelayedFontDescription *result;
|
||||
|
||||
result = g_slice_new0 (GtkDelayedFontDescription);
|
||||
result = g_object_new (GTK_TYPE_DELAYED_FONT_DESCRIPTION, NULL);
|
||||
|
||||
result->family = g_object_ref (family);
|
||||
result->face = g_object_ref (face);
|
||||
result->title = g_strdup (title);
|
||||
result->desc = NULL;
|
||||
result->ref_count = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GtkDelayedFontDescription *
|
||||
gtk_delayed_font_description_ref (GtkDelayedFontDescription *desc)
|
||||
{
|
||||
desc->ref_count++;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_delayed_font_description_unref (GtkDelayedFontDescription *desc)
|
||||
{
|
||||
desc->ref_count--;
|
||||
|
||||
if (desc->ref_count > 0)
|
||||
return;
|
||||
|
||||
g_object_unref (desc->face);
|
||||
if (desc->desc)
|
||||
pango_font_description_free (desc->desc);
|
||||
|
||||
g_slice_free (GtkDelayedFontDescription, desc);
|
||||
}
|
||||
|
||||
static const PangoFontDescription *
|
||||
gtk_delayed_font_description_get (GtkDelayedFontDescription *desc)
|
||||
{
|
||||
@@ -277,12 +254,40 @@ gtk_delayed_font_description_get (GtkDelayedFontDescription *desc)
|
||||
return desc->desc;
|
||||
}
|
||||
|
||||
#define GTK_TYPE_DELAYED_FONT_DESCRIPTION (gtk_delayed_font_description_get_type ())
|
||||
GType gtk_delayed_font_description_get_type (void);
|
||||
static const char *
|
||||
gtk_delayed_font_description_get_title (GtkDelayedFontDescription *desc)
|
||||
{
|
||||
return desc->title;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_delayed_font_description_init (GtkDelayedFontDescription *d)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_delayed_font_description_finalize (GObject *object)
|
||||
{
|
||||
GtkDelayedFontDescription *desc = (GtkDelayedFontDescription *)object;
|
||||
|
||||
g_object_unref (desc->family);
|
||||
g_object_unref (desc->face);
|
||||
g_free (desc->title);
|
||||
if (desc->desc)
|
||||
pango_font_description_free (desc->desc);
|
||||
|
||||
G_OBJECT_CLASS (gtk_delayed_font_description_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_delayed_font_description_class_init (GtkDelayedFontDescriptionClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = gtk_delayed_font_description_finalize;
|
||||
}
|
||||
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GtkDelayedFontDescription, gtk_delayed_font_description,
|
||||
gtk_delayed_font_description_ref,
|
||||
gtk_delayed_font_description_unref)
|
||||
static void
|
||||
gtk_font_chooser_widget_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
@@ -360,7 +365,7 @@ gtk_font_chooser_widget_get_property (GObject *object,
|
||||
static void
|
||||
gtk_font_chooser_widget_refilter_font_list (GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (fontchooser->priv->filter_model));
|
||||
gtk_filter_list_model_refilter (GTK_FILTER_LIST_MODEL (fontchooser->priv->filtermodel));
|
||||
gtk_font_chooser_widget_ensure_selection (fontchooser);
|
||||
}
|
||||
|
||||
@@ -426,6 +431,13 @@ output_cb (GtkSpinButton *spin,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GtkDelayedFontDescription *
|
||||
gtk_font_chooser_widget_get_desc (GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
return (GtkDelayedFontDescription *)g_list_model_get_item (priv->selectionmodel, priv->font_position);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_font_chooser_widget_update_marks (GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
@@ -436,21 +448,15 @@ gtk_font_chooser_widget_update_marks (GtkFontChooserWidget *fontchooser)
|
||||
gint i, n_sizes;
|
||||
gdouble value, spin_value;
|
||||
|
||||
if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (priv->model), &priv->font_iter))
|
||||
if (priv->font_position != GTK_INVALID_LIST_POSITION)
|
||||
{
|
||||
PangoFontFace *face;
|
||||
GtkDelayedFontDescription *desc = gtk_font_chooser_widget_get_desc (fontchooser);
|
||||
|
||||
gtk_tree_model_get (priv->model, &priv->font_iter,
|
||||
FACE_COLUMN, &face,
|
||||
-1);
|
||||
|
||||
pango_font_face_list_sizes (face, &font_sizes, &n_sizes);
|
||||
pango_font_face_list_sizes (desc->face, &font_sizes, &n_sizes);
|
||||
|
||||
/* It seems not many fonts actually have a sane set of sizes */
|
||||
for (i = 0; i < n_sizes; i++)
|
||||
font_sizes[i] = font_sizes[i] / PANGO_SCALE;
|
||||
|
||||
g_object_unref (face);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -510,58 +516,6 @@ gtk_font_chooser_widget_update_marks (GtkFontChooserWidget *fontchooser)
|
||||
g_free (font_sizes);
|
||||
}
|
||||
|
||||
static void
|
||||
row_activated_cb (GtkTreeView *view,
|
||||
GtkTreePath *path,
|
||||
GtkTreeViewColumn *column,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkFontChooserWidget *fontchooser = user_data;
|
||||
gchar *fontname;
|
||||
|
||||
fontname = gtk_font_chooser_widget_get_font (fontchooser);
|
||||
_gtk_font_chooser_font_activated (GTK_FONT_CHOOSER (fontchooser), fontname);
|
||||
g_free (fontname);
|
||||
}
|
||||
|
||||
static void
|
||||
cursor_changed_cb (GtkTreeView *treeview,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkFontChooserWidget *fontchooser = user_data;
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
GtkDelayedFontDescription *desc;
|
||||
GtkTreeIter filter_iter, iter;
|
||||
GtkTreePath *path = NULL;
|
||||
|
||||
gtk_tree_view_get_cursor (treeview, &path, NULL);
|
||||
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
if (!gtk_tree_model_get_iter (priv->filter_model, &filter_iter, path))
|
||||
{
|
||||
gtk_tree_path_free (path);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_tree_path_free (path);
|
||||
|
||||
gtk_tree_model_filter_convert_iter_to_child_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
|
||||
&iter,
|
||||
&filter_iter);
|
||||
gtk_tree_model_get (priv->model, &iter,
|
||||
FONT_DESC_COLUMN, &desc,
|
||||
-1);
|
||||
|
||||
pango_font_description_set_variations (priv->font_desc, NULL);
|
||||
gtk_font_chooser_widget_merge_font_desc (fontchooser,
|
||||
gtk_delayed_font_description_get (desc),
|
||||
&iter);
|
||||
|
||||
gtk_delayed_font_description_unref (desc);
|
||||
}
|
||||
|
||||
static void
|
||||
resize_by_scroll_cb (GtkEventControllerScroll *controller,
|
||||
double dx,
|
||||
@@ -608,7 +562,7 @@ rows_changed_cb (GtkFontChooserWidget *fontchooser)
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
const char *page;
|
||||
|
||||
if (gtk_tree_model_iter_n_children (priv->filter_model, NULL) == 0)
|
||||
if (g_list_model_get_n_items (priv->selectionmodel) == 0)
|
||||
page = "empty";
|
||||
else
|
||||
page = "list";
|
||||
@@ -719,13 +673,8 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
|
||||
"/org/gtk/libgtk/ui/gtkfontchooserwidget.ui");
|
||||
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, search_entry);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_list);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_column);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_cell);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, list_scrolled_window);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, family_face_listview);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, list_stack);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, model);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, filter_model);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, preview);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, preview2);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkFontChooserWidget, size_label);
|
||||
@@ -740,13 +689,8 @@ gtk_font_chooser_widget_class_init (GtkFontChooserWidgetClass *klass)
|
||||
|
||||
gtk_widget_class_bind_template_callback (widget_class, text_changed_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, stop_search_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, cursor_changed_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, row_activated_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, gtk_font_chooser_widget_set_cell_size);
|
||||
gtk_widget_class_bind_template_callback (widget_class, rows_changed_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, size_change_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, output_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, selection_changed);
|
||||
gtk_widget_class_bind_template_callback (widget_class, resize_by_scroll_cb);
|
||||
|
||||
gtk_widget_class_set_css_name (widget_class, I_("fontchooser"));
|
||||
@@ -820,6 +764,66 @@ axis_free (gpointer v)
|
||||
g_free (a);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_row (GtkListItem *list_item,
|
||||
gpointer data)
|
||||
{
|
||||
GtkFontChooserWidget *fontchooser = data;
|
||||
GtkWidget *row;
|
||||
GtkDelayedFontDescription *item;
|
||||
const PangoFontDescription *desc;
|
||||
PangoAttrList *attrs;
|
||||
const char *title;
|
||||
|
||||
item = (GtkDelayedFontDescription *) gtk_list_item_get_item (list_item);
|
||||
desc = gtk_delayed_font_description_get (item);
|
||||
attrs = gtk_font_chooser_widget_get_preview_attributes (fontchooser, desc);
|
||||
title = gtk_delayed_font_description_get_title (item);
|
||||
|
||||
row = gtk_bin_get_child (GTK_BIN (list_item));
|
||||
gtk_label_set_label (GTK_LABEL (row), title);
|
||||
gtk_label_set_attributes (GTK_LABEL (row), attrs);
|
||||
|
||||
pango_attr_list_unref (attrs);
|
||||
}
|
||||
|
||||
static void
|
||||
row_activated_cb (GtkGestureMultiPress *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
gchar *fontname;
|
||||
|
||||
if (n_press == 1)
|
||||
return;
|
||||
|
||||
fontname = gtk_font_chooser_widget_get_font (fontchooser);
|
||||
_gtk_font_chooser_font_activated (GTK_FONT_CHOOSER (fontchooser), fontname);
|
||||
g_free (fontname);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_row (GtkListItem *list_item,
|
||||
gpointer data)
|
||||
{
|
||||
GtkFontChooserWidget *fontchooser = data;
|
||||
GtkWidget *row;
|
||||
GtkEventController *double_click;
|
||||
|
||||
row = gtk_label_new ("");
|
||||
gtk_label_set_xalign (GTK_LABEL (row), 0.0);
|
||||
g_object_set (row, "margin", 10, NULL);
|
||||
|
||||
double_click = gtk_gesture_multi_press_new ();
|
||||
g_signal_connect (double_click, "pressed",
|
||||
G_CALLBACK (row_activated_cb), fontchooser);
|
||||
gtk_widget_add_controller (row, double_click);
|
||||
|
||||
gtk_container_add (GTK_CONTAINER (list_item), row);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
@@ -854,19 +858,17 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
|
||||
gtk_adjustment_set_upper (gtk_range_get_adjustment (GTK_RANGE (priv->size_slider)),
|
||||
(gdouble)(G_MAXINT / PANGO_SCALE));
|
||||
|
||||
/* Setup treeview/model auxilary functions */
|
||||
gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (priv->filter_model),
|
||||
visible_func, (gpointer)priv, NULL);
|
||||
|
||||
gtk_tree_view_column_set_cell_data_func (priv->family_face_column,
|
||||
priv->family_face_cell,
|
||||
gtk_font_chooser_widget_cell_data_func,
|
||||
fontchooser,
|
||||
NULL);
|
||||
|
||||
priv->tweak_action = G_ACTION (g_simple_action_new_stateful ("tweak", NULL, g_variant_new_boolean (FALSE)));
|
||||
g_signal_connect (priv->tweak_action, "change-state", G_CALLBACK (change_tweak), fontchooser);
|
||||
|
||||
priv->listmodel = G_LIST_MODEL (g_list_store_new (GTK_TYPE_DELAYED_FONT_DESCRIPTION));
|
||||
priv->filtermodel = G_LIST_MODEL (gtk_filter_list_model_new (priv->listmodel, visible_func, fontchooser, NULL));
|
||||
priv->selectionmodel = G_LIST_MODEL (gtk_single_selection_new (priv->filtermodel));
|
||||
g_signal_connect_swapped (priv->selectionmodel, "items-changed", G_CALLBACK (rows_changed_cb), fontchooser);
|
||||
g_signal_connect (priv->selectionmodel, "selection-changed", G_CALLBACK (selection_changed), fontchooser);
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (priv->family_face_listview), priv->selectionmodel);
|
||||
gtk_list_view_set_functions (GTK_LIST_VIEW (priv->family_face_listview), setup_row, bind_row, fontchooser, NULL);
|
||||
|
||||
/* Load data and set initial style-dependent parameters */
|
||||
gtk_font_chooser_widget_load_fonts (fontchooser, TRUE);
|
||||
|
||||
@@ -874,7 +876,6 @@ gtk_font_chooser_widget_init (GtkFontChooserWidget *fontchooser)
|
||||
gtk_font_chooser_widget_populate_features (fontchooser);
|
||||
#endif
|
||||
|
||||
gtk_font_chooser_widget_set_cell_size (fontchooser);
|
||||
gtk_font_chooser_widget_take_font_desc (fontchooser, NULL);
|
||||
|
||||
gtk_search_entry_set_key_capture_widget (GTK_SEARCH_ENTRY (priv->search_entry),
|
||||
@@ -909,7 +910,6 @@ gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
|
||||
gboolean force)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
GtkListStore *list_store;
|
||||
gint n_families, i;
|
||||
PangoFontFamily **families;
|
||||
guint fontconfig_timestamp;
|
||||
@@ -932,8 +932,6 @@ gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
|
||||
if (!need_reload && !force)
|
||||
return;
|
||||
|
||||
list_store = GTK_LIST_STORE (priv->model);
|
||||
|
||||
if (priv->font_map)
|
||||
font_map = priv->font_map;
|
||||
else
|
||||
@@ -942,14 +940,11 @@ gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
|
||||
|
||||
qsort (families, n_families, sizeof (PangoFontFamily *), cmp_families);
|
||||
|
||||
g_signal_handlers_block_by_func (priv->family_face_list, cursor_changed_cb, fontchooser);
|
||||
g_signal_handlers_block_by_func (priv->filter_model, rows_changed_cb, fontchooser);
|
||||
gtk_list_store_clear (list_store);
|
||||
g_list_store_remove_all (G_LIST_STORE (priv->listmodel));
|
||||
|
||||
/* Iterate over families and faces */
|
||||
for (i = 0; i < n_families; i++)
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
PangoFontFace **faces;
|
||||
int j, n_faces;
|
||||
const gchar *fam_name = pango_font_family_get_name (families[i]);
|
||||
@@ -969,17 +964,12 @@ gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
|
||||
else
|
||||
title = g_strdup (fam_name);
|
||||
|
||||
desc = gtk_delayed_font_description_new (faces[j]);
|
||||
desc = gtk_delayed_font_description_new (families[i], faces[j], title);
|
||||
|
||||
gtk_list_store_insert_with_values (list_store, &iter, -1,
|
||||
FAMILY_COLUMN, families[i],
|
||||
FACE_COLUMN, faces[j],
|
||||
FONT_DESC_COLUMN, desc,
|
||||
PREVIEW_TITLE_COLUMN, title,
|
||||
-1);
|
||||
g_list_store_append (G_LIST_STORE (priv->listmodel), desc);
|
||||
|
||||
g_object_unref (desc);
|
||||
g_free (title);
|
||||
gtk_delayed_font_description_unref (desc);
|
||||
|
||||
if ((priv->level & GTK_FONT_CHOOSER_LEVEL_STYLE) == 0)
|
||||
break;
|
||||
@@ -990,46 +980,28 @@ gtk_font_chooser_widget_load_fonts (GtkFontChooserWidget *fontchooser,
|
||||
|
||||
g_free (families);
|
||||
|
||||
rows_changed_cb (fontchooser);
|
||||
|
||||
g_signal_handlers_unblock_by_func (priv->filter_model, rows_changed_cb, fontchooser);
|
||||
g_signal_handlers_unblock_by_func (priv->family_face_list, cursor_changed_cb, fontchooser);
|
||||
|
||||
/* now make sure the font list looks right */
|
||||
if (!gtk_font_chooser_widget_find_font (fontchooser, priv->font_desc, &priv->font_iter))
|
||||
memset (&priv->font_iter, 0, sizeof (GtkTreeIter));
|
||||
if (!gtk_font_chooser_widget_find_font (fontchooser, priv->font_desc, &priv->font_position))
|
||||
priv->font_position = GTK_INVALID_LIST_POSITION;
|
||||
|
||||
gtk_font_chooser_widget_ensure_selection (fontchooser);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
visible_func (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer user_data)
|
||||
visible_func (gpointer item, gpointer user_data)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = user_data;
|
||||
GtkDelayedFontDescription *desc = (GtkDelayedFontDescription *)item;
|
||||
GtkFontChooserWidget *fontchooser = user_data;
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
gboolean result = TRUE;
|
||||
const gchar *search_text;
|
||||
gchar **split_terms;
|
||||
gchar *font_name, *font_name_casefold;
|
||||
gchar *font_name_casefold;
|
||||
guint i;
|
||||
|
||||
if (priv->filter_func != NULL)
|
||||
{
|
||||
PangoFontFamily *family;
|
||||
PangoFontFace *face;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
FAMILY_COLUMN, &family,
|
||||
FACE_COLUMN, &face,
|
||||
-1);
|
||||
|
||||
result = priv->filter_func (family, face, priv->filter_data);
|
||||
|
||||
g_object_unref (family);
|
||||
g_object_unref (face);
|
||||
|
||||
if (!result)
|
||||
if (!priv->filter_func (desc->family, desc->face, priv->filter_data))
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
@@ -1038,15 +1010,11 @@ visible_func (GtkTreeModel *model,
|
||||
if (strlen (search_text) == 0)
|
||||
return TRUE;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
PREVIEW_TITLE_COLUMN, &font_name,
|
||||
-1);
|
||||
|
||||
if (font_name == NULL)
|
||||
if (desc->title == NULL)
|
||||
return FALSE;
|
||||
|
||||
split_terms = g_strsplit (search_text, " ", 0);
|
||||
font_name_casefold = g_utf8_casefold (font_name, -1);
|
||||
font_name_casefold = g_utf8_casefold (desc->title, -1);
|
||||
|
||||
for (i = 0; split_terms[i] && result; i++)
|
||||
{
|
||||
@@ -1059,7 +1027,6 @@ visible_func (GtkTreeModel *model,
|
||||
}
|
||||
|
||||
g_free (font_name_casefold);
|
||||
g_free (font_name);
|
||||
g_strfreev (split_terms);
|
||||
|
||||
return result;
|
||||
@@ -1069,11 +1036,10 @@ visible_func (GtkTreeModel *model,
|
||||
static int
|
||||
gtk_font_chooser_widget_get_preview_text_height (GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
GtkWidget *treeview = fontchooser->priv->family_face_list;
|
||||
GtkStyleContext *context;
|
||||
double dpi, font_size;
|
||||
|
||||
context = gtk_widget_get_style_context (treeview);
|
||||
context = gtk_widget_get_style_context (fontchooser->priv->family_face_listview);
|
||||
dpi = _gtk_css_number_value_get (_gtk_style_context_peek_property (context,
|
||||
GTK_CSS_PROPERTY_DPI),
|
||||
100);
|
||||
@@ -1105,65 +1071,6 @@ gtk_font_chooser_widget_get_preview_attributes (GtkFontChooserWidget *font
|
||||
return attrs;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_font_chooser_widget_cell_data_func (GtkTreeViewColumn *column,
|
||||
GtkCellRenderer *cell,
|
||||
GtkTreeModel *tree_model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkFontChooserWidget *fontchooser = user_data;
|
||||
GtkDelayedFontDescription *desc;
|
||||
PangoAttrList *attrs;
|
||||
char *preview_title;
|
||||
|
||||
gtk_tree_model_get (tree_model, iter,
|
||||
PREVIEW_TITLE_COLUMN, &preview_title,
|
||||
FONT_DESC_COLUMN, &desc,
|
||||
-1);
|
||||
|
||||
attrs = gtk_font_chooser_widget_get_preview_attributes (fontchooser,
|
||||
gtk_delayed_font_description_get (desc));
|
||||
|
||||
g_object_set (cell,
|
||||
"xpad", 20,
|
||||
"ypad", 10,
|
||||
"attributes", attrs,
|
||||
"text", preview_title,
|
||||
NULL);
|
||||
|
||||
gtk_delayed_font_description_unref (desc);
|
||||
pango_attr_list_unref (attrs);
|
||||
g_free (preview_title);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_font_chooser_widget_set_cell_size (GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
PangoAttrList *attrs;
|
||||
GtkRequisition size;
|
||||
|
||||
gtk_cell_renderer_set_fixed_size (priv->family_face_cell, -1, -1);
|
||||
|
||||
attrs = gtk_font_chooser_widget_get_preview_attributes (fontchooser, NULL);
|
||||
|
||||
g_object_set (priv->family_face_cell,
|
||||
"xpad", 20,
|
||||
"ypad", 10,
|
||||
"attributes", attrs,
|
||||
"text", "x",
|
||||
NULL);
|
||||
|
||||
pango_attr_list_unref (attrs);
|
||||
|
||||
gtk_cell_renderer_get_preferred_size (priv->family_face_cell,
|
||||
priv->family_face_list,
|
||||
&size,
|
||||
NULL);
|
||||
gtk_cell_renderer_set_fixed_size (priv->family_face_cell, size.width, size.height);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_font_chooser_widget_finalize (GObject *object)
|
||||
{
|
||||
@@ -1188,6 +1095,10 @@ gtk_font_chooser_widget_finalize (GObject *object)
|
||||
|
||||
g_free (priv->font_features);
|
||||
|
||||
g_object_unref (priv->selectionmodel);
|
||||
g_object_unref (priv->filtermodel);
|
||||
g_object_unref (priv->listmodel);
|
||||
|
||||
G_OBJECT_CLASS (gtk_font_chooser_widget_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
@@ -1201,53 +1112,40 @@ my_pango_font_family_equal (const char *familya,
|
||||
static gboolean
|
||||
gtk_font_chooser_widget_find_font (GtkFontChooserWidget *fontchooser,
|
||||
const PangoFontDescription *font_desc,
|
||||
/* out arguments */
|
||||
GtkTreeIter *iter)
|
||||
guint *position)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
gboolean valid;
|
||||
int i;
|
||||
|
||||
if (pango_font_description_get_family (font_desc) == NULL)
|
||||
return FALSE;
|
||||
|
||||
for (valid = gtk_tree_model_get_iter_first (priv->model, iter);
|
||||
valid;
|
||||
valid = gtk_tree_model_iter_next (priv->model, iter))
|
||||
for (i = 0; i < g_list_model_get_n_items (priv->selectionmodel); i++)
|
||||
{
|
||||
GtkDelayedFontDescription *desc;
|
||||
PangoFontDescription *merged;
|
||||
PangoFontFamily *family;
|
||||
|
||||
gtk_tree_model_get (priv->model, iter,
|
||||
FAMILY_COLUMN, &family,
|
||||
FONT_DESC_COLUMN, &desc,
|
||||
-1);
|
||||
desc = (GtkDelayedFontDescription *)g_list_model_get_item (priv->selectionmodel, i);
|
||||
|
||||
if (!my_pango_font_family_equal (pango_font_description_get_family (font_desc),
|
||||
pango_font_family_get_name (family)))
|
||||
{
|
||||
gtk_delayed_font_description_unref (desc);
|
||||
g_object_unref (family);
|
||||
continue;
|
||||
}
|
||||
pango_font_family_get_name (desc->family)))
|
||||
continue;
|
||||
|
||||
merged = pango_font_description_copy_static (gtk_delayed_font_description_get (desc));
|
||||
|
||||
pango_font_description_merge_static (merged, font_desc, FALSE);
|
||||
if (pango_font_description_equal (merged, font_desc))
|
||||
{
|
||||
gtk_delayed_font_description_unref (desc);
|
||||
pango_font_description_free (merged);
|
||||
g_object_unref (family);
|
||||
break;
|
||||
*position = i;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gtk_delayed_font_description_unref (desc);
|
||||
pango_font_description_free (merged);
|
||||
g_object_unref (family);
|
||||
}
|
||||
|
||||
return valid;
|
||||
*position = GTK_INVALID_LIST_POSITION;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1288,36 +1186,24 @@ static PangoFontFamily *
|
||||
gtk_font_chooser_widget_get_family (GtkFontChooser *chooser)
|
||||
{
|
||||
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (chooser);
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
PangoFontFamily *family;
|
||||
GtkDelayedFontDescription *desc = gtk_font_chooser_widget_get_desc (fontchooser);
|
||||
|
||||
if (!gtk_list_store_iter_is_valid (GTK_LIST_STORE (priv->model), &priv->font_iter))
|
||||
return NULL;
|
||||
if (desc)
|
||||
return desc->family;
|
||||
|
||||
gtk_tree_model_get (priv->model, &priv->font_iter,
|
||||
FAMILY_COLUMN, &family,
|
||||
-1);
|
||||
g_object_unref (family);
|
||||
|
||||
return family;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PangoFontFace *
|
||||
gtk_font_chooser_widget_get_face (GtkFontChooser *chooser)
|
||||
{
|
||||
GtkFontChooserWidget *fontchooser = GTK_FONT_CHOOSER_WIDGET (chooser);
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
PangoFontFace *face;
|
||||
GtkDelayedFontDescription *desc = gtk_font_chooser_widget_get_desc (fontchooser);
|
||||
|
||||
if (!gtk_list_store_iter_is_valid (GTK_LIST_STORE (priv->model), &priv->font_iter))
|
||||
return NULL;
|
||||
if (desc)
|
||||
return desc->face;
|
||||
|
||||
gtk_tree_model_get (priv->model, &priv->font_iter,
|
||||
FACE_COLUMN, &face,
|
||||
-1);
|
||||
g_object_unref (face);
|
||||
|
||||
return face;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static gint
|
||||
@@ -1347,10 +1233,8 @@ static PangoFontDescription *
|
||||
gtk_font_chooser_widget_get_font_desc (GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
GtkTreeSelection *selection;
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->family_face_list));
|
||||
if (gtk_tree_selection_count_selected_rows (selection) > 0)
|
||||
if (priv->font_position != GTK_INVALID_LIST_POSITION)
|
||||
return fontchooser->priv->font_desc;
|
||||
|
||||
return NULL;
|
||||
@@ -1368,35 +1252,27 @@ gtk_font_chooser_widget_set_font (GtkFontChooserWidget *fontchooser,
|
||||
|
||||
static void
|
||||
gtk_font_chooser_widget_update_font_name (GtkFontChooserWidget *fontchooser,
|
||||
GtkTreeSelection *selection)
|
||||
GtkSelectionModel *selection)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
PangoFontFamily *family;
|
||||
PangoFontFace *face;
|
||||
GtkDelayedFontDescription *desc;
|
||||
const PangoFontDescription *font_desc;
|
||||
PangoAttrList *attrs;
|
||||
const char *fam_name;
|
||||
const char *face_name;
|
||||
char *title;
|
||||
guint position;
|
||||
|
||||
gtk_tree_selection_get_selected (selection, &model, &iter);
|
||||
gtk_tree_model_get (model, &iter,
|
||||
FAMILY_COLUMN, &family,
|
||||
FACE_COLUMN, &face,
|
||||
FONT_DESC_COLUMN, &desc,
|
||||
-1);
|
||||
position = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (selection));
|
||||
desc = (GtkDelayedFontDescription *)g_list_model_get_item (G_LIST_MODEL (selection), position);
|
||||
|
||||
fam_name = pango_font_family_get_name (family);
|
||||
face_name = pango_font_face_get_face_name (face);
|
||||
if (!desc)
|
||||
return;
|
||||
|
||||
fam_name = pango_font_family_get_name (desc->family);
|
||||
face_name = pango_font_face_get_face_name (desc->face);
|
||||
font_desc = gtk_delayed_font_description_get (desc);
|
||||
|
||||
g_object_unref (family);
|
||||
g_object_unref (face);
|
||||
gtk_delayed_font_description_unref (desc);
|
||||
|
||||
if ((priv->level & GTK_FONT_CHOOSER_LEVEL_STYLE) != 0)
|
||||
title = g_strconcat (fam_name, " ", face_name, NULL);
|
||||
else
|
||||
@@ -1411,18 +1287,32 @@ gtk_font_chooser_widget_update_font_name (GtkFontChooserWidget *fontchooser,
|
||||
}
|
||||
|
||||
static void
|
||||
selection_changed (GtkTreeSelection *selection,
|
||||
selection_changed (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
guint selected;
|
||||
|
||||
g_object_notify (G_OBJECT (fontchooser), "font");
|
||||
g_object_notify (G_OBJECT (fontchooser), "font-desc");
|
||||
|
||||
if (gtk_tree_selection_count_selected_rows (selection) > 0)
|
||||
selected = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (model));
|
||||
if (selected != GTK_INVALID_LIST_POSITION)
|
||||
{
|
||||
gtk_font_chooser_widget_update_font_name (fontchooser, selection);
|
||||
GtkDelayedFontDescription *desc = (GtkDelayedFontDescription *)g_list_model_get_item (priv->selectionmodel, position);
|
||||
|
||||
gtk_font_chooser_widget_update_font_name (fontchooser, model);
|
||||
g_simple_action_set_enabled (G_SIMPLE_ACTION (priv->tweak_action), TRUE);
|
||||
|
||||
if (desc)
|
||||
{
|
||||
pango_font_description_set_variations (priv->font_desc, NULL);
|
||||
gtk_font_chooser_widget_merge_font_desc (fontchooser,
|
||||
gtk_delayed_font_description_get (desc),
|
||||
selected);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1434,29 +1324,6 @@ selection_changed (GtkTreeSelection *selection,
|
||||
static void
|
||||
gtk_font_chooser_widget_ensure_selection (GtkFontChooserWidget *fontchooser)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreeIter filter_iter;
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->family_face_list));
|
||||
|
||||
if (gtk_list_store_iter_is_valid (GTK_LIST_STORE (priv->model), &priv->font_iter) &&
|
||||
gtk_tree_model_filter_convert_child_iter_to_iter (GTK_TREE_MODEL_FILTER (priv->filter_model),
|
||||
&filter_iter,
|
||||
&priv->font_iter))
|
||||
{
|
||||
GtkTreePath *path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->filter_model),
|
||||
&filter_iter);
|
||||
|
||||
gtk_tree_selection_select_iter (selection, &filter_iter);
|
||||
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->family_face_list),
|
||||
path, NULL, FALSE, 0.0, 0.0);
|
||||
gtk_tree_path_free (path);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_tree_selection_unselect_all (selection);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_HARFBUZZ) && defined(HAVE_PANGOFT)
|
||||
@@ -2265,13 +2132,12 @@ update_font_features (GtkFontChooserWidget *fontchooser)
|
||||
static void
|
||||
gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser,
|
||||
const PangoFontDescription *font_desc,
|
||||
GtkTreeIter *iter)
|
||||
guint position)
|
||||
{
|
||||
GtkFontChooserWidgetPrivate *priv = fontchooser->priv;
|
||||
PangoFontMask mask;
|
||||
|
||||
g_assert (font_desc != NULL);
|
||||
/* iter may be NULL if the font doesn't exist on the list */
|
||||
|
||||
mask = pango_font_description_get_set_fields (font_desc);
|
||||
|
||||
@@ -2295,13 +2161,9 @@ gtk_font_chooser_widget_merge_font_desc (GtkFontChooserWidget *fontchooser
|
||||
{
|
||||
gboolean has_tweak = FALSE;
|
||||
|
||||
if (&priv->font_iter != iter)
|
||||
if (priv->font_position != position)
|
||||
{
|
||||
if (iter == NULL)
|
||||
memset (&priv->font_iter, 0, sizeof (GtkTreeIter));
|
||||
else
|
||||
memcpy (&priv->font_iter, iter, sizeof (GtkTreeIter));
|
||||
|
||||
priv->font_position = position;
|
||||
gtk_font_chooser_widget_ensure_selection (fontchooser);
|
||||
}
|
||||
|
||||
@@ -2336,16 +2198,16 @@ gtk_font_chooser_widget_take_font_desc (GtkFontChooserWidget *fontchooser,
|
||||
if (mask & (PANGO_FONT_MASK_FAMILY | PANGO_FONT_MASK_STYLE | PANGO_FONT_MASK_VARIANT |
|
||||
PANGO_FONT_MASK_WEIGHT | PANGO_FONT_MASK_STRETCH))
|
||||
{
|
||||
GtkTreeIter iter;
|
||||
guint pos;
|
||||
|
||||
if (gtk_font_chooser_widget_find_font (fontchooser, font_desc, &iter))
|
||||
gtk_font_chooser_widget_merge_font_desc (fontchooser, font_desc, &iter);
|
||||
if (gtk_font_chooser_widget_find_font (fontchooser, font_desc, &pos))
|
||||
gtk_font_chooser_widget_merge_font_desc (fontchooser, font_desc, pos);
|
||||
else
|
||||
gtk_font_chooser_widget_merge_font_desc (fontchooser, font_desc, NULL);
|
||||
gtk_font_chooser_widget_merge_font_desc (fontchooser, font_desc, GTK_INVALID_LIST_POSITION);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_font_chooser_widget_merge_font_desc (fontchooser, font_desc, &priv->font_iter);
|
||||
gtk_font_chooser_widget_merge_font_desc (fontchooser, font_desc, priv->font_position);
|
||||
}
|
||||
|
||||
pango_font_description_free (font_desc);
|
||||
@@ -2370,9 +2232,7 @@ gtk_font_chooser_widget_set_preview_text (GtkFontChooserWidget *fontchooser,
|
||||
|
||||
g_object_notify (G_OBJECT (fontchooser), "preview-text");
|
||||
|
||||
/* XXX: There's no API to tell the treeview that a column has changed,
|
||||
* so we just */
|
||||
gtk_widget_queue_draw (priv->family_face_list);
|
||||
gtk_widget_queue_draw (priv->family_face_listview);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -2414,7 +2274,7 @@ gtk_font_chooser_widget_set_font_map (GtkFontChooser *chooser,
|
||||
if (!fontmap)
|
||||
fontmap = pango_cairo_font_map_get_default ();
|
||||
|
||||
context = gtk_widget_get_pango_context (priv->family_face_list);
|
||||
context = gtk_widget_get_pango_context (priv->family_face_listview);
|
||||
pango_context_set_font_map (context, fontmap);
|
||||
|
||||
context = gtk_widget_get_pango_context (priv->preview);
|
||||
|
||||
459
gtk/gtklistitem.c
Normal file
459
gtk/gtklistitem.c
Normal file
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkgesturemultipress.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtklistitem
|
||||
* @title: GtkListItem
|
||||
* @short_description: Widget used to represent items of a ListModel
|
||||
* @see_also: #GtkListView, #GListModel
|
||||
*
|
||||
* #GtkListItem is the widget that GTK list-handling containers such
|
||||
* as #GtkListView create to represent items in a #GListModel.
|
||||
* They are managed by the container and cannot be created by application
|
||||
* code.
|
||||
*
|
||||
* #GtkListIems are container widgets that need to be populated by
|
||||
* application code. The container provides functions to do that.
|
||||
*
|
||||
* #GtkListItems exist in 2 stages:
|
||||
*
|
||||
* 1. The unbound stage where the listitem is not currently connected to
|
||||
* an item in the list. In that case, the GtkListItem:item property is
|
||||
* set to %NULL.
|
||||
*
|
||||
* 2. The bound stage where the listitem references an item from the list.
|
||||
* The GtkListItem:item property is not %NULL.
|
||||
*/
|
||||
|
||||
struct _GtkListItem
|
||||
{
|
||||
GtkBin parent_instance;
|
||||
|
||||
GtkListItemManager *manager; /* no ref, the manager refs us */
|
||||
|
||||
GObject *item;
|
||||
guint position;
|
||||
|
||||
guint selectable : 1;
|
||||
guint selected : 1;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
PROP_ITEM,
|
||||
PROP_POSITION,
|
||||
PROP_SELECTABLE,
|
||||
PROP_SELECTED,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkListItem, gtk_list_item, GTK_TYPE_BIN)
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static void
|
||||
gtk_list_item_dispose (GObject *object)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
g_assert (self->item == NULL);
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_item_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_get_property (GObject *object,
|
||||
guint property_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_ITEM:
|
||||
g_value_set_object (value, self->item);
|
||||
break;
|
||||
|
||||
case PROP_POSITION:
|
||||
g_value_set_uint (value, self->position);
|
||||
break;
|
||||
|
||||
case PROP_SELECTABLE:
|
||||
g_value_set_boolean (value, self->selectable);
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
g_value_set_boolean (value, self->selected);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_set_property (GObject *object,
|
||||
guint property_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkListItem *self = GTK_LIST_ITEM (object);
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_SELECTABLE:
|
||||
gtk_list_item_set_selectable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_class_init (GtkListItemClass *klass)
|
||||
{
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->dispose = gtk_list_item_dispose;
|
||||
gobject_class->get_property = gtk_list_item_get_property;
|
||||
gobject_class->set_property = gtk_list_item_set_property;
|
||||
|
||||
/**
|
||||
* GtkListItem:item:
|
||||
*
|
||||
* Displayed item
|
||||
*/
|
||||
properties[PROP_ITEM] =
|
||||
g_param_spec_object ("item",
|
||||
P_("Item"),
|
||||
P_("Displayed item"),
|
||||
G_TYPE_OBJECT,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:position:
|
||||
*
|
||||
* Position in the item
|
||||
*/
|
||||
properties[PROP_POSITION] =
|
||||
g_param_spec_uint ("position",
|
||||
P_("Position"),
|
||||
P_("Position of the item"),
|
||||
0, G_MAXUINT, 0,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:selectable:
|
||||
*
|
||||
* If the item can be selected by the user
|
||||
*/
|
||||
properties[PROP_SELECTABLE] =
|
||||
g_param_spec_boolean ("selectable",
|
||||
P_("Selectable"),
|
||||
P_("If the item can be selected by the user"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkListItem:selected:
|
||||
*
|
||||
* If the item is currently selected
|
||||
*/
|
||||
properties[PROP_SELECTED] =
|
||||
g_param_spec_boolean ("selected",
|
||||
P_("Selected"),
|
||||
P_("If the item is currently selected"),
|
||||
FALSE,
|
||||
G_PARAM_READABLE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/* This gets overwritten by gtk_list_item_new() but better safe than sorry */
|
||||
gtk_widget_class_set_css_name (widget_class, I_("row"));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_multipress_gesture_pressed (GtkGestureMultiPress *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkListItem *self)
|
||||
{
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
GdkModifierType state;
|
||||
GdkModifierType mask;
|
||||
gboolean extend = FALSE, modify = FALSE;
|
||||
|
||||
if (!self->selectable)
|
||||
{
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (gtk_get_current_event_state (&state))
|
||||
{
|
||||
mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_MODIFY_SELECTION);
|
||||
if ((state & mask) == mask)
|
||||
modify = TRUE;
|
||||
mask = gtk_widget_get_modifier_mask (widget, GDK_MODIFIER_INTENT_EXTEND_SELECTION);
|
||||
if ((state & mask) == mask)
|
||||
extend = TRUE;
|
||||
}
|
||||
|
||||
gtk_list_item_manager_select (self->manager, self, modify, extend);
|
||||
|
||||
gtk_widget_set_state_flags (widget, GTK_STATE_FLAG_ACTIVE, FALSE);
|
||||
|
||||
if (gtk_widget_get_focus_on_click (widget))
|
||||
gtk_widget_grab_focus (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_multipress_gesture_released (GtkGestureMultiPress *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkListItem *self)
|
||||
{
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_multipress_gesture_canceled (GtkGestureMultiPress *gesture,
|
||||
GdkEventSequence *sequence,
|
||||
GtkListItem *self)
|
||||
{
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_init (GtkListItem *self)
|
||||
{
|
||||
GtkGesture *gesture;
|
||||
|
||||
gtk_widget_set_has_surface (GTK_WIDGET (self), FALSE);
|
||||
|
||||
self->selectable = TRUE;
|
||||
gtk_widget_set_can_focus (GTK_WIDGET (self), TRUE);
|
||||
|
||||
gesture = gtk_gesture_multi_press_new ();
|
||||
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
|
||||
GTK_PHASE_BUBBLE);
|
||||
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
|
||||
FALSE);
|
||||
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
|
||||
GDK_BUTTON_PRIMARY);
|
||||
g_signal_connect (gesture, "pressed",
|
||||
G_CALLBACK (gtk_list_item_multipress_gesture_pressed), self);
|
||||
g_signal_connect (gesture, "released",
|
||||
G_CALLBACK (gtk_list_item_multipress_gesture_released), self);
|
||||
g_signal_connect (gesture, "cancel",
|
||||
G_CALLBACK (gtk_list_item_multipress_gesture_canceled), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
||||
}
|
||||
|
||||
GtkListItem *
|
||||
gtk_list_item_new (GtkListItemManager *manager,
|
||||
const char *css_name)
|
||||
{
|
||||
GtkListItem *result;
|
||||
|
||||
g_return_val_if_fail (css_name != NULL, NULL);
|
||||
|
||||
result = g_object_new (GTK_TYPE_LIST_ITEM,
|
||||
"css-name", css_name,
|
||||
NULL);
|
||||
|
||||
result->manager = manager;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_item:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Gets the item that is currently displayed in model that @self is
|
||||
* currently bound to or %NULL if @self is unbound.
|
||||
*
|
||||
* Returns: (nullable) (transfer none) (type GObject): The item displayed
|
||||
**/
|
||||
gpointer
|
||||
gtk_list_item_get_item (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), NULL);
|
||||
|
||||
return self->item;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_position:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Gets the position in the model that @self currently displays.
|
||||
* If @self is unbound, 0 is returned.
|
||||
*
|
||||
* Returns: The position of this item
|
||||
**/
|
||||
guint
|
||||
gtk_list_item_get_position (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), 0);
|
||||
|
||||
return self->position;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_set_item (GtkListItem *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
g_return_if_fail (item == NULL || G_IS_OBJECT (item));
|
||||
|
||||
if (self->item == item)
|
||||
return;
|
||||
|
||||
g_clear_object (&self->item);
|
||||
if (item)
|
||||
self->item = g_object_ref (item);
|
||||
|
||||
gtk_css_node_invalidate (gtk_widget_get_css_node (GTK_WIDGET (self)), GTK_CSS_CHANGE_ANIMATIONS);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ITEM]);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_set_position (GtkListItem *self,
|
||||
guint position)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->position == position)
|
||||
return;
|
||||
|
||||
self->position = position;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_POSITION]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_selected:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Checks if the item is displayed as selected. The selected state is
|
||||
* maintained by the container and its list model and cannot be set
|
||||
* otherwise.
|
||||
*
|
||||
* Returns: %TRUE if the item is selected.
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_get_selected (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
|
||||
|
||||
return self->selected;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_set_selected (GtkListItem *self,
|
||||
gboolean selected)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->selected == selected)
|
||||
return;
|
||||
|
||||
self->selected = selected;
|
||||
|
||||
if (selected)
|
||||
gtk_widget_set_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED, FALSE);
|
||||
else
|
||||
gtk_widget_unset_state_flags (GTK_WIDGET (self), GTK_STATE_FLAG_SELECTED);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_get_selectable:
|
||||
* @self: a #GtkListItem
|
||||
*
|
||||
* Checks if a list item has been set to be selectable via
|
||||
* gtk_list_item_set_selectable().
|
||||
*
|
||||
* Do not confuse this function with gtk_list_item_get_selected().
|
||||
*
|
||||
* Returns: %TRUE if the item is selectable
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_get_selectable (GtkListItem *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (self), FALSE);
|
||||
|
||||
return self->selectable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_set_selectable:
|
||||
* @self: a #GtkListItem
|
||||
* @selectable: if the item should be selectable
|
||||
*
|
||||
* Sets @self to be selectable. If an item is selectable, clicking
|
||||
* on the item or using the keyboard will try to select or unselect
|
||||
* the item. If this succeeds is up to the model to determine, as
|
||||
* it is managing the selected state.
|
||||
*
|
||||
* Note that this means that making an item non-selectable has no
|
||||
* influence on the selected state at all. A non-selectable item
|
||||
* may still be selected.
|
||||
*
|
||||
* By default, list items are selectable. When rebinding them to
|
||||
* a new item, they will also be reset to be selectable by GTK.
|
||||
**/
|
||||
void
|
||||
gtk_list_item_set_selectable (GtkListItem *self,
|
||||
gboolean selectable)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (self));
|
||||
|
||||
if (self->selectable == selectable)
|
||||
return;
|
||||
|
||||
self->selectable = selectable;
|
||||
|
||||
gtk_widget_set_can_focus (GTK_WIDGET (self), self->selectable);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTABLE]);
|
||||
}
|
||||
51
gtk/gtklistitem.h
Normal file
51
gtk/gtklistitem.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_H__
|
||||
#define __GTK_LIST_ITEM_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkbin.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM (gtk_list_item_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkListItem, gtk_list_item, GTK, LIST_ITEM, GtkBin)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gpointer gtk_list_item_get_item (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_list_item_get_position (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selected (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_item_get_selectable (GtkListItem *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_item_set_selectable (GtkListItem *self,
|
||||
gboolean selectable);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_H__ */
|
||||
150
gtk/gtklistitemfactory.c
Normal file
150
gtk/gtklistitemfactory.c
Normal file
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
|
||||
#include "gtklistitemprivate.h"
|
||||
|
||||
struct _GtkListItemFactory
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkListItemSetupFunc setup_func;
|
||||
GtkListItemBindFunc bind_func;
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
};
|
||||
|
||||
struct _GtkListItemFactoryClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkListItemFactory, gtk_list_item_factory, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_finalize (GObject *object)
|
||||
{
|
||||
GtkListItemFactory *self = GTK_LIST_ITEM_FACTORY (object);
|
||||
|
||||
if (self->user_destroy)
|
||||
self->user_destroy (self->user_data);
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_item_factory_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_class_init (GtkListItemFactoryClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = gtk_list_item_factory_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_factory_init (GtkListItemFactory *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_list_item_factory_new (GtkListItemSetupFunc setup_func,
|
||||
GtkListItemBindFunc bind_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy)
|
||||
{
|
||||
GtkListItemFactory *self;
|
||||
|
||||
g_return_val_if_fail (setup_func || bind_func, NULL);
|
||||
g_return_val_if_fail (user_data != NULL || user_destroy == NULL, NULL);
|
||||
|
||||
self = g_object_new (GTK_TYPE_LIST_ITEM_FACTORY, NULL);
|
||||
|
||||
self->setup_func = setup_func;
|
||||
self->bind_func = bind_func;
|
||||
self->user_data = user_data;
|
||||
self->user_destroy = user_destroy;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_setup (GtkListItemFactory *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
|
||||
if (self->setup_func)
|
||||
self->setup_func (list_item, self->user_data);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_bind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (list_item));
|
||||
|
||||
gtk_list_item_set_item (list_item, item);
|
||||
gtk_list_item_set_position (list_item, position);
|
||||
gtk_list_item_set_selected (list_item, selected);
|
||||
|
||||
if (self->bind_func)
|
||||
self->bind_func (list_item, self->user_data);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (list_item));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_update (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gboolean selected)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (list_item));
|
||||
|
||||
gtk_list_item_set_position (list_item, position);
|
||||
gtk_list_item_set_selected (list_item, selected);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (list_item));
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_factory_unbind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (list_item));
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (list_item));
|
||||
|
||||
gtk_list_item_set_item (list_item, NULL);
|
||||
gtk_list_item_set_position (list_item, 0);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (list_item));
|
||||
}
|
||||
64
gtk/gtklistitemfactoryprivate.h
Normal file
64
gtk/gtklistitemfactoryprivate.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_FACTORY_H__
|
||||
#define __GTK_LIST_ITEM_FACTORY_H__
|
||||
|
||||
#include <gtk/gtklistitem.h>
|
||||
#include <gtk/gtklistview.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM_FACTORY (gtk_list_item_factory_get_type ())
|
||||
#define GTK_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactory))
|
||||
#define GTK_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactoryClass))
|
||||
#define GTK_IS_LIST_ITEM_FACTORY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_FACTORY))
|
||||
#define GTK_IS_LIST_ITEM_FACTORY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_FACTORY))
|
||||
#define GTK_LIST_ITEM_FACTORY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_FACTORY, GtkListItemFactoryClass))
|
||||
|
||||
typedef struct _GtkListItemFactory GtkListItemFactory;
|
||||
typedef struct _GtkListItemFactoryClass GtkListItemFactoryClass;
|
||||
|
||||
GType gtk_list_item_factory_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkListItemFactory * gtk_list_item_factory_new (GtkListItemSetupFunc setup_func,
|
||||
GtkListItemBindFunc bind_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
void gtk_list_item_factory_setup (GtkListItemFactory *self,
|
||||
GtkListItem *list_item);
|
||||
|
||||
void gtk_list_item_factory_bind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gpointer item,
|
||||
gboolean selected);
|
||||
void gtk_list_item_factory_update (GtkListItemFactory *self,
|
||||
GtkListItem *list_item,
|
||||
guint position,
|
||||
gboolean selected);
|
||||
void gtk_list_item_factory_unbind (GtkListItemFactory *self,
|
||||
GtkListItem *list_item);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_FACTORY_H__ */
|
||||
423
gtk/gtklistitemmanager.c
Normal file
423
gtk/gtklistitemmanager.c
Normal file
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
|
||||
#include "gtklistitemprivate.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
|
||||
struct _GtkListItemManager
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GtkWidget *widget;
|
||||
GtkSelectionModel *model;
|
||||
GtkListItemFactory *factory;
|
||||
};
|
||||
|
||||
struct _GtkListItemManagerClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
struct _GtkListItemManagerChange
|
||||
{
|
||||
GHashTable *items;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (GtkListItemManager, gtk_list_item_manager, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
gtk_list_item_manager_dispose (GObject *object)
|
||||
{
|
||||
GtkListItemManager *self = GTK_LIST_ITEM_MANAGER (object);
|
||||
|
||||
g_clear_object (&self->model);
|
||||
g_clear_object (&self->factory);
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_item_manager_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_manager_class_init (GtkListItemManagerClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->dispose = gtk_list_item_manager_dispose;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_item_manager_init (GtkListItemManager *self)
|
||||
{
|
||||
}
|
||||
|
||||
GtkListItemManager *
|
||||
gtk_list_item_manager_new (GtkWidget *widget)
|
||||
{
|
||||
GtkListItemManager *self;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_WIDGET (widget), NULL);
|
||||
|
||||
self = g_object_new (GTK_TYPE_LIST_ITEM_MANAGER, NULL);
|
||||
|
||||
self->widget = widget;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_manager_set_factory (GtkListItemManager *self,
|
||||
GtkListItemFactory *factory)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_FACTORY (factory));
|
||||
|
||||
if (self->factory == factory)
|
||||
return;
|
||||
|
||||
g_clear_object (&self->factory);
|
||||
|
||||
self->factory = g_object_ref (factory);
|
||||
}
|
||||
|
||||
GtkListItemFactory *
|
||||
gtk_list_item_manager_get_factory (GtkListItemManager *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
|
||||
|
||||
return self->factory;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_manager_set_model (GtkListItemManager *self,
|
||||
GtkSelectionModel *model)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
|
||||
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
||||
|
||||
if (self->model == model)
|
||||
return;
|
||||
|
||||
g_clear_object (&self->model);
|
||||
|
||||
if (model)
|
||||
self->model = g_object_ref (model);
|
||||
}
|
||||
|
||||
GtkSelectionModel *
|
||||
gtk_list_item_manager_get_model (GtkListItemManager *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
|
||||
|
||||
return self->model;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_item_manager_select (GtkListItemManager *self,
|
||||
GtkListItem *item,
|
||||
gboolean modify,
|
||||
gboolean extend)
|
||||
{
|
||||
guint pos = gtk_list_item_get_position (item);
|
||||
|
||||
if (modify)
|
||||
{
|
||||
if (gtk_list_item_get_selected (item))
|
||||
gtk_selection_model_unselect_item (self->model, pos);
|
||||
else
|
||||
gtk_selection_model_select_item (self->model, pos, FALSE, extend);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_selection_model_select_item (self->model, pos, TRUE, extend);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* gtk_list_item_manager_get_size:
|
||||
* @self: a #GtkListItemManager
|
||||
*
|
||||
* Queries the number of widgets currently handled by @self.
|
||||
*
|
||||
* This includes both widgets that have been acquired and
|
||||
* those currently waiting to be used again.
|
||||
*
|
||||
* Returns: Number of widgets handled by @self
|
||||
**/
|
||||
guint
|
||||
gtk_list_item_manager_get_size (GtkListItemManager *self)
|
||||
{
|
||||
return g_hash_table_size (self->pool);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* gtk_list_item_manager_begin_change:
|
||||
* @self: a #GtkListItemManager
|
||||
*
|
||||
* Begins a change operation in response to a model's items-changed
|
||||
* signal.
|
||||
* During an ongoing change operation, list items will not be discarded
|
||||
* when released but will be kept around in anticipation of them being
|
||||
* added back in a different posiion later.
|
||||
*
|
||||
* Once it is known that no more list items will be reused,
|
||||
* gtk_list_item_manager_end_change() should be called. This should happen
|
||||
* as early as possible, so the list items held for the change can be
|
||||
* reqcquired.
|
||||
*
|
||||
* Returns: The object to use for this change
|
||||
**/
|
||||
GtkListItemManagerChange *
|
||||
gtk_list_item_manager_begin_change (GtkListItemManager *self)
|
||||
{
|
||||
GtkListItemManagerChange *change;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
|
||||
|
||||
change = g_slice_new (GtkListItemManagerChange);
|
||||
change->items = g_hash_table_new (g_direct_hash, g_direct_equal);
|
||||
|
||||
return change;
|
||||
}
|
||||
|
||||
/*
|
||||
* gtk_list_item_manager_end_change:
|
||||
* @self: a #GtkListItemManager
|
||||
* @change: a change
|
||||
*
|
||||
* Ends a change operation begun with gtk_list_item_manager_begin_change()
|
||||
* and releases all list items still cached.
|
||||
**/
|
||||
void
|
||||
gtk_list_item_manager_end_change (GtkListItemManager *self,
|
||||
GtkListItemManagerChange *change)
|
||||
{
|
||||
GHashTableIter iter;
|
||||
gpointer list_item;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
|
||||
|
||||
g_hash_table_iter_init (&iter, change->items);
|
||||
while (g_hash_table_iter_next (&iter, NULL, &list_item))
|
||||
{
|
||||
gtk_list_item_manager_release_list_item (self, NULL, list_item);
|
||||
}
|
||||
|
||||
g_hash_table_unref (change->items);
|
||||
g_slice_free (GtkListItemManagerChange, change);
|
||||
}
|
||||
|
||||
/*
|
||||
* gtk_list_item_manager_change_contains:
|
||||
* @change: a #GtkListItemManagerChange
|
||||
* @list_item: The item that may have been released into this change set
|
||||
*
|
||||
* Checks if @list_item has been released as part of @change but not been
|
||||
* reacquired yet.
|
||||
*
|
||||
* This is useful to test before calling gtk_list_item_manager_end_change()
|
||||
* if special actions need to be performed when important list items - like
|
||||
* the focused item - are about to be deleted.
|
||||
*
|
||||
* Returns: %TRUE if the item is part of this change
|
||||
**/
|
||||
gboolean
|
||||
gtk_list_item_manager_change_contains (GtkListItemManagerChange *change,
|
||||
GtkWidget *list_item)
|
||||
{
|
||||
g_return_val_if_fail (change != NULL, FALSE);
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM (list_item), FALSE);
|
||||
|
||||
return g_hash_table_lookup (change->items, gtk_list_item_get_item (GTK_LIST_ITEM (list_item))) == list_item;
|
||||
}
|
||||
|
||||
/*
|
||||
* gtk_list_item_manager_acquire_list_item:
|
||||
* @self: a #GtkListItemManager
|
||||
* @position: the row in the model to create a list item for
|
||||
* @prev_sibling: the widget this widget should be inserted before or %NULL
|
||||
* if it should be the first widget
|
||||
*
|
||||
* Creates a list item widget to use for @position. No widget may
|
||||
* yet exist that is used for @position.
|
||||
*
|
||||
* When the returned item is no longer needed, the caller is responsible
|
||||
* for calling gtk_list_item_manager_release_list_item().
|
||||
* A particular case is when the row at @position is removed. In that case,
|
||||
* all list items in the removed range must be released before
|
||||
* gtk_list_item_manager_model_changed() is called.
|
||||
*
|
||||
* Returns: a properly setup widget to use in @position
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
|
||||
guint position,
|
||||
GtkWidget *prev_sibling)
|
||||
{
|
||||
GtkListItem *result;
|
||||
gpointer item;
|
||||
gboolean selected;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
|
||||
g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL);
|
||||
|
||||
result = gtk_list_item_new (self, "row");
|
||||
gtk_list_item_factory_setup (self->factory, result);
|
||||
|
||||
item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
|
||||
selected = gtk_selection_model_is_selected (self->model, position);
|
||||
gtk_list_item_factory_bind (self->factory, result, position, item, selected);
|
||||
g_object_unref (item);
|
||||
gtk_widget_insert_after (GTK_WIDGET (result), self->widget, prev_sibling);
|
||||
|
||||
return GTK_WIDGET (result);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_manager_try_acquire_list_item_from_change:
|
||||
* @self: a #GtkListItemManager
|
||||
* @position: the row in the model to create a list item for
|
||||
* @prev_sibling: the widget this widget should be inserted after or %NULL
|
||||
* if it should be the first widget
|
||||
*
|
||||
* Like gtk_list_item_manager_acquire_list_item(), but only tries to acquire list
|
||||
* items from those previously released as part of @change.
|
||||
* If no matching list item is found, %NULL is returned and the caller should use
|
||||
* gtk_list_item_manager_acquire_list_item().
|
||||
*
|
||||
* Returns: (nullable): a properly setup widget to use in @position or %NULL if
|
||||
* no item for reuse existed
|
||||
**/
|
||||
GtkWidget *
|
||||
gtk_list_item_manager_try_reacquire_list_item (GtkListItemManager *self,
|
||||
GtkListItemManagerChange *change,
|
||||
guint position,
|
||||
GtkWidget *prev_sibling)
|
||||
{
|
||||
GtkListItem *result;
|
||||
gpointer item;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_LIST_ITEM_MANAGER (self), NULL);
|
||||
g_return_val_if_fail (prev_sibling == NULL || GTK_IS_WIDGET (prev_sibling), NULL);
|
||||
|
||||
/* XXX: can we avoid temporarily allocating items on failure? */
|
||||
item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
|
||||
if (g_hash_table_steal_extended (change->items, item, NULL, (gpointer *) &result))
|
||||
{
|
||||
gtk_list_item_factory_update (self->factory, result, position, FALSE);
|
||||
gtk_widget_insert_after (GTK_WIDGET (result), self->widget, prev_sibling);
|
||||
/* XXX: Should we let the listview do this? */
|
||||
gtk_widget_queue_resize (GTK_WIDGET (result));
|
||||
}
|
||||
else
|
||||
{
|
||||
result = NULL;
|
||||
}
|
||||
g_object_unref (item);
|
||||
|
||||
return GTK_WIDGET (result);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_manager_move_list_item:
|
||||
* @self: a #GtkListItemManager
|
||||
* @list_item: an acquired #GtkListItem that should be moved to represent
|
||||
* a different row
|
||||
* @position: the new position of that list item
|
||||
* @prev_sibling: the new previous sibling
|
||||
*
|
||||
* Moves the widget to represent a new position in the listmodel without
|
||||
* releasing the item.
|
||||
*
|
||||
* This is most useful when scrolling.
|
||||
**/
|
||||
void
|
||||
gtk_list_item_manager_move_list_item (GtkListItemManager *self,
|
||||
GtkWidget *list_item,
|
||||
guint position,
|
||||
GtkWidget *prev_sibling)
|
||||
{
|
||||
gpointer item;
|
||||
gboolean selected;
|
||||
|
||||
item = g_list_model_get_item (G_LIST_MODEL (self->model), position);
|
||||
selected = gtk_selection_model_is_selected (self->model, position);
|
||||
gtk_list_item_factory_bind (self->factory, GTK_LIST_ITEM (list_item), position, item, selected);
|
||||
gtk_widget_insert_after (list_item, _gtk_widget_get_parent (list_item), prev_sibling);
|
||||
g_object_unref (item);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_item_manager_update_list_item:
|
||||
* @self: a #GtkListItemManager
|
||||
* @item: a #GtkListItem that has been acquired
|
||||
* @position: the new position of that list item
|
||||
*
|
||||
* Updates the position of the given @item. This function must be called whenever
|
||||
* the position of an item changes, like when new items are added before it.
|
||||
**/
|
||||
void
|
||||
gtk_list_item_manager_update_list_item (GtkListItemManager *self,
|
||||
GtkWidget *item,
|
||||
guint position)
|
||||
{
|
||||
gboolean selected;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (item));
|
||||
|
||||
selected = gtk_selection_model_is_selected (self->model, position);
|
||||
gtk_list_item_factory_update (self->factory, GTK_LIST_ITEM (item), position, selected);
|
||||
}
|
||||
|
||||
/*
|
||||
* gtk_list_item_manager_release_list_item:
|
||||
* @self: a #GtkListItemManager
|
||||
* @change: (allow-none): The change associated with this release or
|
||||
* %NULL if this is a final removal
|
||||
* @item: an item previously acquired with
|
||||
* gtk_list_item_manager_acquire_list_item()
|
||||
*
|
||||
* Releases an item that was previously acquired via
|
||||
* gtk_list_item_manager_acquire_list_item() and is no longer in use.
|
||||
**/
|
||||
void
|
||||
gtk_list_item_manager_release_list_item (GtkListItemManager *self,
|
||||
GtkListItemManagerChange *change,
|
||||
GtkWidget *item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM_MANAGER (self));
|
||||
g_return_if_fail (GTK_IS_LIST_ITEM (item));
|
||||
|
||||
if (change != NULL)
|
||||
{
|
||||
if (g_hash_table_insert (change->items, gtk_list_item_get_item (GTK_LIST_ITEM (item)), item))
|
||||
return;
|
||||
|
||||
g_warning ("FIXME: Handle the same item multiple times in the list.\nLars says this totally should not happen, but here we are.");
|
||||
}
|
||||
|
||||
gtk_list_item_factory_unbind (self->factory, GTK_LIST_ITEM (item));
|
||||
gtk_widget_unparent (item);
|
||||
}
|
||||
88
gtk/gtklistitemmanagerprivate.h
Normal file
88
gtk/gtklistitemmanagerprivate.h
Normal file
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_MANAGER_H__
|
||||
#define __GTK_LIST_ITEM_MANAGER_H__
|
||||
|
||||
#include "gtk/gtktypes.h"
|
||||
|
||||
#include "gtk/gtklistitemfactoryprivate.h"
|
||||
#include "gtk/gtkselectionmodel.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_LIST_ITEM_MANAGER (gtk_list_item_manager_get_type ())
|
||||
#define GTK_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManager))
|
||||
#define GTK_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass))
|
||||
#define GTK_IS_LIST_ITEM_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GTK_TYPE_LIST_ITEM_MANAGER))
|
||||
#define GTK_IS_LIST_ITEM_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GTK_TYPE_LIST_ITEM_MANAGER))
|
||||
#define GTK_LIST_ITEM_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GTK_TYPE_LIST_ITEM_MANAGER, GtkListItemManagerClass))
|
||||
|
||||
typedef struct _GtkListItemManager GtkListItemManager;
|
||||
typedef struct _GtkListItemManagerClass GtkListItemManagerClass;
|
||||
typedef struct _GtkListItemManagerChange GtkListItemManagerChange;
|
||||
|
||||
GType gtk_list_item_manager_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GtkListItemManager * gtk_list_item_manager_new (GtkWidget *widget);
|
||||
|
||||
void gtk_list_item_manager_set_factory (GtkListItemManager *self,
|
||||
GtkListItemFactory *factory);
|
||||
GtkListItemFactory * gtk_list_item_manager_get_factory (GtkListItemManager *self);
|
||||
void gtk_list_item_manager_set_model (GtkListItemManager *self,
|
||||
GtkSelectionModel *model);
|
||||
GtkSelectionModel * gtk_list_item_manager_get_model (GtkListItemManager *self);
|
||||
|
||||
guint gtk_list_item_manager_get_size (GtkListItemManager *self);
|
||||
|
||||
void gtk_list_item_manager_select (GtkListItemManager *self,
|
||||
GtkListItem *item,
|
||||
gboolean modify,
|
||||
gboolean extend);
|
||||
|
||||
GtkListItemManagerChange *
|
||||
gtk_list_item_manager_begin_change (GtkListItemManager *self);
|
||||
void gtk_list_item_manager_end_change (GtkListItemManager *self,
|
||||
GtkListItemManagerChange *change);
|
||||
gboolean gtk_list_item_manager_change_contains (GtkListItemManagerChange *change,
|
||||
GtkWidget *list_item);
|
||||
|
||||
GtkWidget * gtk_list_item_manager_acquire_list_item (GtkListItemManager *self,
|
||||
guint position,
|
||||
GtkWidget *prev_sibling);
|
||||
GtkWidget * gtk_list_item_manager_try_reacquire_list_item
|
||||
(GtkListItemManager *self,
|
||||
GtkListItemManagerChange *change,
|
||||
guint position,
|
||||
GtkWidget *prev_sibling);
|
||||
void gtk_list_item_manager_update_list_item (GtkListItemManager *self,
|
||||
GtkWidget *item,
|
||||
guint position);
|
||||
void gtk_list_item_manager_move_list_item (GtkListItemManager *self,
|
||||
GtkWidget *list_item,
|
||||
guint position,
|
||||
GtkWidget *prev_sibling);
|
||||
void gtk_list_item_manager_release_list_item (GtkListItemManager *self,
|
||||
GtkListItemManagerChange *change,
|
||||
GtkWidget *widget);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_MANAGER_H__ */
|
||||
41
gtk/gtklistitemprivate.h
Normal file
41
gtk/gtklistitemprivate.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_ITEM_PRIVATE_H__
|
||||
#define __GTK_LIST_ITEM_PRIVATE_H__
|
||||
|
||||
#include "gtklistitem.h"
|
||||
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
GtkListItem * gtk_list_item_new (GtkListItemManager *manager,
|
||||
const char *css_name);
|
||||
|
||||
void gtk_list_item_set_item (GtkListItem *self,
|
||||
gpointer item);
|
||||
void gtk_list_item_set_position (GtkListItem *self,
|
||||
guint position);
|
||||
void gtk_list_item_set_selected (GtkListItem *self,
|
||||
gboolean selected);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_ITEM_PRIVATE_H__ */
|
||||
@@ -206,15 +206,12 @@ gtk_list_list_model_new_with_size (GType item_type,
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_list_model_item_added (GtkListListModel *self,
|
||||
gpointer item)
|
||||
static guint
|
||||
gtk_list_list_model_find (GtkListListModel *self,
|
||||
gpointer item)
|
||||
{
|
||||
gpointer x;
|
||||
guint position;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
g_return_if_fail (item != NULL);
|
||||
gpointer x;
|
||||
|
||||
position = 0;
|
||||
for (x = self->get_first (self->data);
|
||||
@@ -222,7 +219,17 @@ gtk_list_list_model_item_added (GtkListListModel *self,
|
||||
x = self->get_next (x, self->data))
|
||||
position++;
|
||||
|
||||
gtk_list_list_model_item_added_at (self, position);
|
||||
return position;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_list_model_item_added (GtkListListModel *self,
|
||||
gpointer item)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
g_return_if_fail (item != NULL);
|
||||
|
||||
gtk_list_list_model_item_added_at (self, gtk_list_list_model_find (self, item));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -241,26 +248,49 @@ void
|
||||
gtk_list_list_model_item_removed (GtkListListModel *self,
|
||||
gpointer previous)
|
||||
{
|
||||
gpointer x;
|
||||
guint position;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
|
||||
if (previous == NULL)
|
||||
position = 0;
|
||||
else
|
||||
position = 1 + gtk_list_list_model_find (self, previous);
|
||||
|
||||
gtk_list_list_model_item_removed_at (self, position);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_list_model_item_moved (GtkListListModel *self,
|
||||
gpointer item,
|
||||
gpointer previous_previous)
|
||||
{
|
||||
guint position, previous_position;
|
||||
guint min, max;
|
||||
|
||||
g_return_if_fail (GTK_IS_LIST_LIST_MODEL (self));
|
||||
g_return_if_fail (item != previous_previous);
|
||||
|
||||
position = gtk_list_list_model_find (self, item);
|
||||
|
||||
if (previous_previous == NULL)
|
||||
{
|
||||
position = 0;
|
||||
previous_position = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
position = 1;
|
||||
|
||||
for (x = self->get_first (self->data);
|
||||
x != previous;
|
||||
x = self->get_next (x, self->data))
|
||||
position++;
|
||||
previous_position = gtk_list_list_model_find (self, previous_previous);
|
||||
if (position > previous_position)
|
||||
previous_position++;
|
||||
}
|
||||
|
||||
gtk_list_list_model_item_removed_at (self, position);
|
||||
/* item didn't move */
|
||||
if (position == previous_position)
|
||||
return;
|
||||
|
||||
min = MIN (position, previous_position);
|
||||
max = MAX (position, previous_position) + 1;
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), min, max - min, max - min);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -64,6 +64,9 @@ void gtk_list_list_model_item_removed (GtkListListMode
|
||||
gpointer previous);
|
||||
void gtk_list_list_model_item_removed_at (GtkListListModel *self,
|
||||
guint position);
|
||||
void gtk_list_list_model_item_moved (GtkListListModel *self,
|
||||
gpointer item,
|
||||
gpointer previous_previous);
|
||||
|
||||
void gtk_list_list_model_clear (GtkListListModel *self);
|
||||
|
||||
|
||||
1421
gtk/gtklistview.c
Normal file
1421
gtk/gtklistview.c
Normal file
File diff suppressed because it is too large
Load Diff
82
gtk/gtklistview.h
Normal file
82
gtk/gtklistview.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_LIST_VIEW_H__
|
||||
#define __GTK_LIST_VIEW_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtkwidget.h>
|
||||
#include <gtk/gtklistitem.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**
|
||||
* GtkListItemSetupFunc:
|
||||
* @item: the #GtkListItem to set up
|
||||
* @user_data: (closure): user data
|
||||
*
|
||||
* Called whenever a new list item needs to be setup for managing a row in
|
||||
* the list.
|
||||
*
|
||||
* At this point, the list item is not bound yet, so gtk_list_item_get_item()
|
||||
* will return %NULL.
|
||||
* The list item will later be bound to an item via the #GtkListItemBindFunc.
|
||||
*/
|
||||
typedef void (* GtkListItemSetupFunc) (GtkListItem *item, gpointer user_data);
|
||||
|
||||
/**
|
||||
* GtkListItemBindFunc:
|
||||
* @item: the #GtkListItem to bind
|
||||
* @user_data: (closure): user data
|
||||
*
|
||||
* Binds a#GtkListItem previously set up via a #GtkListItemSetupFunc to
|
||||
* an @item.
|
||||
*
|
||||
* Rebinding a @item to different @items is supported as well as
|
||||
* unbinding it by setting @item to %NULL.
|
||||
*/
|
||||
typedef void (* GtkListItemBindFunc) (GtkListItem *item,
|
||||
gpointer user_data);
|
||||
|
||||
#define GTK_TYPE_LIST_VIEW (gtk_list_view_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkListView, gtk_list_view, GTK, LIST_VIEW, GtkWidget)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkWidget * gtk_list_view_new (void);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_list_view_get_model (GtkListView *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_set_model (GtkListView *self,
|
||||
GListModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_set_functions (GtkListView *self,
|
||||
GtkListItemSetupFunc setup_func,
|
||||
GtkListItemBindFunc bind_func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify user_destroy);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_VIEW_H__ */
|
||||
@@ -1633,11 +1633,11 @@ handle_pointing_event (GdkEvent *event)
|
||||
|
||||
if (event->any.type == GDK_BUTTON_RELEASE)
|
||||
{
|
||||
old_target = target;
|
||||
target = gtk_widget_pick (GTK_WIDGET (toplevel), x, y);
|
||||
if (target == NULL)
|
||||
target = GTK_WIDGET (toplevel);
|
||||
gtk_synthesize_crossing_events (toplevel, old_target, target, event,
|
||||
GtkWidget *new_target;
|
||||
new_target = gtk_widget_pick (GTK_WIDGET (toplevel), x, y);
|
||||
if (new_target == NULL)
|
||||
new_target = GTK_WIDGET (toplevel);
|
||||
gtk_synthesize_crossing_events (toplevel, target, new_target, event,
|
||||
GDK_CROSSING_UNGRAB);
|
||||
gtk_window_maybe_update_cursor (toplevel, NULL, device);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "gtkmaplistmodel.h"
|
||||
|
||||
#include "gtkcssrbtreeprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
@@ -73,7 +73,7 @@ struct _GtkMapListModel
|
||||
gpointer user_data;
|
||||
GDestroyNotify user_destroy;
|
||||
|
||||
GtkCssRbTree *items; /* NULL if map_func == NULL */
|
||||
GtkRbTree *items; /* NULL if map_func == NULL */
|
||||
};
|
||||
|
||||
struct _GtkMapListModelClass
|
||||
@@ -84,21 +84,21 @@ struct _GtkMapListModelClass
|
||||
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
|
||||
|
||||
static MapNode *
|
||||
gtk_map_list_model_get_nth (GtkCssRbTree *tree,
|
||||
gtk_map_list_model_get_nth (GtkRbTree *tree,
|
||||
guint position,
|
||||
guint *out_start_pos)
|
||||
{
|
||||
MapNode *node, *tmp;
|
||||
guint start_pos = position;
|
||||
|
||||
node = gtk_css_rb_tree_get_root (tree);
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_css_rb_tree_get_left (tree, node);
|
||||
tmp = gtk_rb_tree_get_left (tree, node);
|
||||
if (tmp)
|
||||
{
|
||||
MapAugment *aug = gtk_css_rb_tree_get_augment (tree, tmp);
|
||||
MapAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
@@ -114,7 +114,7 @@ gtk_map_list_model_get_nth (GtkCssRbTree *tree,
|
||||
}
|
||||
position -= node->n_items;
|
||||
|
||||
node = gtk_css_rb_tree_get_right (tree, node);
|
||||
node = gtk_rb_tree_get_right (tree, node);
|
||||
}
|
||||
|
||||
if (out_start_pos)
|
||||
@@ -165,18 +165,18 @@ gtk_map_list_model_get_item (GListModel *list,
|
||||
|
||||
if (offset != position)
|
||||
{
|
||||
MapNode *before = gtk_css_rb_tree_insert_before (self->items, node);
|
||||
MapNode *before = gtk_rb_tree_insert_before (self->items, node);
|
||||
before->n_items = position - offset;
|
||||
node->n_items -= before->n_items;
|
||||
gtk_css_rb_tree_mark_dirty (self->items, node);
|
||||
gtk_rb_tree_mark_dirty (self->items, node);
|
||||
}
|
||||
|
||||
if (node->n_items > 1)
|
||||
{
|
||||
MapNode *after = gtk_css_rb_tree_insert_after (self->items, node);
|
||||
MapNode *after = gtk_rb_tree_insert_after (self->items, node);
|
||||
after->n_items = node->n_items - 1;
|
||||
node->n_items = 1;
|
||||
gtk_css_rb_tree_mark_dirty (self->items, node);
|
||||
gtk_rb_tree_mark_dirty (self->items, node);
|
||||
}
|
||||
|
||||
node->item = self->map_func (g_list_model_get_item (self->model, position), self->user_data);
|
||||
@@ -225,9 +225,9 @@ gtk_map_list_model_items_changed_cb (GListModel *model,
|
||||
end = start + node->n_items;
|
||||
if (start == position && end <= position + removed)
|
||||
{
|
||||
MapNode *next = gtk_css_rb_tree_get_next (self->items, node);
|
||||
MapNode *next = gtk_rb_tree_get_next (self->items, node);
|
||||
removed -= node->n_items;
|
||||
gtk_css_rb_tree_remove (self->items, node);
|
||||
gtk_rb_tree_remove (self->items, node);
|
||||
node = next;
|
||||
}
|
||||
else
|
||||
@@ -236,16 +236,16 @@ gtk_map_list_model_items_changed_cb (GListModel *model,
|
||||
{
|
||||
node->n_items -= removed;
|
||||
removed = 0;
|
||||
gtk_css_rb_tree_mark_dirty (self->items, node);
|
||||
gtk_rb_tree_mark_dirty (self->items, node);
|
||||
}
|
||||
else if (start < position)
|
||||
{
|
||||
guint overlap = node->n_items - (position - start);
|
||||
node->n_items -= overlap;
|
||||
gtk_css_rb_tree_mark_dirty (self->items, node);
|
||||
gtk_rb_tree_mark_dirty (self->items, node);
|
||||
removed -= overlap;
|
||||
start = position;
|
||||
node = gtk_css_rb_tree_get_next (self->items, node);
|
||||
node = gtk_rb_tree_get_next (self->items, node);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -253,12 +253,12 @@ gtk_map_list_model_items_changed_cb (GListModel *model,
|
||||
if (added)
|
||||
{
|
||||
if (node == NULL)
|
||||
node = gtk_css_rb_tree_insert_before (self->items, NULL);
|
||||
node = gtk_rb_tree_insert_before (self->items, NULL);
|
||||
else if (node->item)
|
||||
node = gtk_css_rb_tree_insert_after (self->items, node);
|
||||
node = gtk_rb_tree_insert_after (self->items, node);
|
||||
|
||||
node->n_items += added;
|
||||
gtk_css_rb_tree_mark_dirty (self->items, node);
|
||||
gtk_rb_tree_mark_dirty (self->items, node);
|
||||
}
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
@@ -337,7 +337,7 @@ gtk_map_list_model_dispose (GObject *object)
|
||||
self->map_func = NULL;
|
||||
self->user_data = NULL;
|
||||
self->user_destroy = NULL;
|
||||
g_clear_pointer (&self->items, gtk_css_rb_tree_unref);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_map_list_model_parent_class)->dispose (object);
|
||||
};
|
||||
@@ -397,25 +397,25 @@ gtk_map_list_model_init (GtkMapListModel *self)
|
||||
|
||||
|
||||
static void
|
||||
gtk_map_list_model_augment (GtkCssRbTree *map,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
gtk_map_list_model_augment (GtkRbTree *map,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
MapNode *node= _node;
|
||||
MapNode *node = _node;
|
||||
MapAugment *aug = _aug;
|
||||
|
||||
aug->n_items = node->n_items;
|
||||
|
||||
if (left)
|
||||
{
|
||||
MapAugment *left_aug = gtk_css_rb_tree_get_augment (map, left);
|
||||
MapAugment *left_aug = gtk_rb_tree_get_augment (map, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
MapAugment *right_aug = gtk_css_rb_tree_get_augment (map, right);
|
||||
MapAugment *right_aug = gtk_rb_tree_get_augment (map, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
}
|
||||
}
|
||||
@@ -473,11 +473,11 @@ gtk_map_list_model_init_items (GtkMapListModel *self)
|
||||
|
||||
if (self->items)
|
||||
{
|
||||
gtk_css_rb_tree_remove_all (self->items);
|
||||
gtk_rb_tree_remove_all (self->items);
|
||||
}
|
||||
else
|
||||
{
|
||||
self->items = gtk_css_rb_tree_new (MapNode,
|
||||
self->items = gtk_rb_tree_new (MapNode,
|
||||
MapAugment,
|
||||
gtk_map_list_model_augment,
|
||||
gtk_map_list_model_clear_node,
|
||||
@@ -487,14 +487,14 @@ gtk_map_list_model_init_items (GtkMapListModel *self)
|
||||
n_items = g_list_model_get_n_items (self->model);
|
||||
if (n_items)
|
||||
{
|
||||
MapNode *node = gtk_css_rb_tree_insert_before (self->items, NULL);
|
||||
MapNode *node = gtk_rb_tree_insert_before (self->items, NULL);
|
||||
node->n_items = g_list_model_get_n_items (self->model);
|
||||
gtk_css_rb_tree_mark_dirty (self->items, node);
|
||||
gtk_rb_tree_mark_dirty (self->items, node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_pointer (&self->items, gtk_css_rb_tree_unref);
|
||||
g_clear_pointer (&self->items, gtk_rb_tree_unref);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
374
gtk/gtkmultiselection.c
Normal file
374
gtk/gtkmultiselection.c
Normal file
@@ -0,0 +1,374 @@
|
||||
/*
|
||||
* Copyright © 2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkmultiselection.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
#include "gtksingleselection.h"
|
||||
#include "gtkset.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkmultiselection
|
||||
* @Short_description: A selection model that allows selecting a multiple items
|
||||
* @Title: GtkMultiSelection
|
||||
* @see_also: #GtkSelectionModel
|
||||
*
|
||||
* GtkMultiSelection is an implementation of the #GtkSelectionModel interface
|
||||
* that allows selecting multiple elements.
|
||||
*/
|
||||
|
||||
struct _GtkMultiSelection
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GListModel *model;
|
||||
|
||||
GtkSet *selected;
|
||||
guint last_selected;
|
||||
};
|
||||
|
||||
/*
|
||||
* We store a set of positions for selected items. This can be maintained
|
||||
* efficiently as long as it consists of a small number of ranges. In
|
||||
* degenerate cases such as 'every second item in the list', it will
|
||||
* be O(|model|).
|
||||
*
|
||||
* To implement persistence across add/remove changes in the underlying
|
||||
* model (for example, resorting), we mark the selected objects, which
|
||||
* is also going to be O(|model|) in the 'select all' case.
|
||||
*/
|
||||
|
||||
struct _GtkMultiSelectionClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
/* selectionmodel */
|
||||
PROP_MODEL,
|
||||
|
||||
N_PROPS = PROP_MODEL
|
||||
};
|
||||
|
||||
static GType
|
||||
gtk_multi_selection_get_item_type (GListModel *list)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (list);
|
||||
|
||||
return g_list_model_get_item_type (self->model);
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_multi_selection_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (list);
|
||||
|
||||
return g_list_model_get_n_items (self->model);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_multi_selection_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (list);
|
||||
|
||||
return g_list_model_get_item (self->model, position);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_multi_selection_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_multi_selection_get_item_type;
|
||||
iface->get_n_items = gtk_multi_selection_get_n_items;
|
||||
iface->get_item = gtk_multi_selection_get_item;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_is_selected (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
|
||||
return gtk_set_contains (self->selected, position);
|
||||
}
|
||||
|
||||
static void
|
||||
mark_selected (GtkMultiSelection *self, gboolean in)
|
||||
{
|
||||
GtkSetIter iter;
|
||||
guint pos;
|
||||
|
||||
gtk_set_iter_init (&iter, self->selected);
|
||||
while (gtk_set_iter_next (&iter, &pos))
|
||||
{
|
||||
/* Mark the object as being selected in this multiselection.
|
||||
* See gtk_multi_selection_items_changed_cb, where this is
|
||||
* used to identify objects that were removed and readded.
|
||||
*/
|
||||
GObject *obj = g_list_model_get_item (self->model, pos);
|
||||
g_object_set_data (obj, "GtkMultiSelection", in ? self : NULL);
|
||||
g_object_unref (obj);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
mark_range (GtkMultiSelection *self, guint first, guint n_items, gboolean in)
|
||||
{
|
||||
guint pos;
|
||||
|
||||
for (pos = first; pos < first + n_items; pos++)
|
||||
{
|
||||
GObject *obj = g_list_model_get_item (self->model, pos);
|
||||
g_object_set_data (obj, "GtkMultiSelection", in ? self : NULL);
|
||||
g_object_unref (obj);
|
||||
}
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_select_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gboolean exclusive)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
|
||||
if (exclusive)
|
||||
{
|
||||
mark_selected (self, FALSE);
|
||||
gtk_set_remove_all (self->selected);
|
||||
}
|
||||
mark_range (self, position, n_items, TRUE);
|
||||
gtk_set_add_range (self->selected, position, n_items);
|
||||
gtk_selection_model_selection_changed (model, position, n_items);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_unselect_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
|
||||
mark_range (self, position, n_items, FALSE);
|
||||
gtk_set_remove_range (self->selected, position, n_items);
|
||||
gtk_selection_model_selection_changed (model, position, n_items);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean exclusive,
|
||||
gboolean extend)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
guint pos, n_items;
|
||||
|
||||
if (extend && self->last_selected != GTK_INVALID_LIST_POSITION)
|
||||
{
|
||||
pos = MIN (position, self->last_selected);
|
||||
n_items = MAX (position, self->last_selected) - pos + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos = position;
|
||||
n_items = 1;
|
||||
}
|
||||
|
||||
self->last_selected = position;
|
||||
return gtk_multi_selection_select_range (model, pos, n_items, exclusive);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_unselect_item (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
return gtk_multi_selection_unselect_range (model, position, 1);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_select_all (GtkSelectionModel *model)
|
||||
{
|
||||
return gtk_multi_selection_select_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)), FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_unselect_all (GtkSelectionModel *model)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
self->last_selected = GTK_INVALID_LIST_POSITION;
|
||||
return gtk_multi_selection_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
gtk_multi_selection_selection_model_init (GtkSelectionModelInterface *iface)
|
||||
{
|
||||
iface->is_selected = gtk_multi_selection_is_selected;
|
||||
iface->select_item = gtk_multi_selection_select_item;
|
||||
iface->unselect_item = gtk_multi_selection_unselect_item;
|
||||
iface->select_range = gtk_multi_selection_select_range;
|
||||
iface->unselect_range = gtk_multi_selection_unselect_range;
|
||||
iface->select_all = gtk_multi_selection_select_all;
|
||||
iface->unselect_all = gtk_multi_selection_unselect_all;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (GtkMultiSelection, gtk_multi_selection, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
|
||||
gtk_multi_selection_list_model_init)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
|
||||
gtk_multi_selection_selection_model_init))
|
||||
|
||||
static void
|
||||
gtk_multi_selection_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkMultiSelection *self)
|
||||
{
|
||||
guint pos;
|
||||
|
||||
gtk_set_remove_range (self->selected, position, removed);
|
||||
gtk_set_shift (self->selected, position, (int)added - (int)removed);
|
||||
for (pos = position; pos < position + added; pos++)
|
||||
{
|
||||
GObject *obj = g_list_model_get_item (self->model, pos);
|
||||
if (g_object_get_data (obj, "GtkMultiSelection") == self)
|
||||
gtk_set_add_item (self->selected, pos);
|
||||
g_object_unref (obj);
|
||||
}
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_multi_selection_clear_model (GtkMultiSelection *self)
|
||||
{
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->model,
|
||||
gtk_multi_selection_items_changed_cb,
|
||||
self);
|
||||
g_clear_object (&self->model);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_multi_selection_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MODEL:
|
||||
gtk_multi_selection_clear_model (self);
|
||||
self->model = g_value_dup_object (value);
|
||||
if (self->model)
|
||||
g_signal_connect (self->model, "items-changed",
|
||||
G_CALLBACK (gtk_multi_selection_items_changed_cb), self);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_multi_selection_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_multi_selection_dispose (GObject *object)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (object);
|
||||
|
||||
gtk_multi_selection_clear_model (self);
|
||||
|
||||
g_clear_pointer (&self->selected, gtk_set_free);
|
||||
self->last_selected = GTK_INVALID_LIST_POSITION;
|
||||
|
||||
G_OBJECT_CLASS (gtk_multi_selection_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_multi_selection_class_init (GtkMultiSelectionClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gtk_multi_selection_get_property;
|
||||
gobject_class->set_property = gtk_multi_selection_set_property;
|
||||
gobject_class->dispose = gtk_multi_selection_dispose;
|
||||
|
||||
g_object_class_override_property (gobject_class, PROP_MODEL, "model");
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_multi_selection_init (GtkMultiSelection *self)
|
||||
{
|
||||
self->selected = gtk_set_new ();
|
||||
self->last_selected = GTK_INVALID_LIST_POSITION;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_multi_selection_new:
|
||||
* @model: (transfer none): the #GListModel to manage
|
||||
*
|
||||
* Creates a new selection to handle @model.
|
||||
*
|
||||
* Returns: (transfer full) (type GtkMultiSelection): a new #GtkMultiSelection
|
||||
**/
|
||||
GtkMultiSelection *
|
||||
gtk_multi_selection_new (GListModel *model)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_MULTI_SELECTION,
|
||||
"model", model,
|
||||
NULL);
|
||||
}
|
||||
|
||||
37
gtk/gtkmultiselection.h
Normal file
37
gtk/gtkmultiselection.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright © 2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_MULTI_SELECTION_H__
|
||||
#define __GTK_MULTI_SELECTION_H__
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_MULTI_SELECTION (gtk_multi_selection_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkMultiSelection, gtk_multi_selection, GTK, MULTI_SELECTION, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkMultiSelection * gtk_multi_selection_new (GListModel *model);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_MULTI_SELECTION_H__ */
|
||||
205
gtk/gtknoselection.c
Normal file
205
gtk/gtknoselection.c
Normal file
@@ -0,0 +1,205 @@
|
||||
/*
|
||||
* Copyright © 2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtknoselection.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtknoselection
|
||||
* @Short_description: A selection model that does not allow selecting items
|
||||
* @Title: GtkNoSelection
|
||||
* @see_also: #GtkSelectionModel
|
||||
*
|
||||
* GtkNoSelection is an implementation of the #GtkSelectionModel interface
|
||||
* that never selects any items. It can be used where a #GtkSelectionModel
|
||||
* is needed, but selection is not desired.
|
||||
*/
|
||||
struct _GtkNoSelection
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GListModel *model;
|
||||
};
|
||||
|
||||
struct _GtkNoSelectionClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
|
||||
/* selectionmodel */
|
||||
PROP_MODEL,
|
||||
|
||||
N_PROPS = PROP_MODEL
|
||||
};
|
||||
|
||||
static GType
|
||||
gtk_no_selection_get_item_type (GListModel *list)
|
||||
{
|
||||
return g_list_model_get_item_type (GTK_NO_SELECTION (list)->model);
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_no_selection_get_n_items (GListModel *list)
|
||||
{
|
||||
return g_list_model_get_n_items (GTK_NO_SELECTION (list)->model);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_no_selection_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
return g_list_model_get_item (GTK_NO_SELECTION (list)->model, position);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_no_selection_get_item_type;
|
||||
iface->get_n_items = gtk_no_selection_get_n_items;
|
||||
iface->get_item = gtk_no_selection_get_item;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_selection_model_init (GtkSelectionModelInterface *iface)
|
||||
{
|
||||
/* the default implementation does what we want */
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (GtkNoSelection, gtk_no_selection, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
|
||||
gtk_no_selection_list_model_init)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
|
||||
gtk_no_selection_selection_model_init))
|
||||
|
||||
static void
|
||||
gtk_no_selection_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkNoSelection *self)
|
||||
{
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_clear_model (GtkNoSelection *self)
|
||||
{
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->model,
|
||||
gtk_no_selection_items_changed_cb,
|
||||
self);
|
||||
g_clear_object (&self->model);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkNoSelection *self = GTK_NO_SELECTION (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MODEL:
|
||||
gtk_no_selection_clear_model (self);
|
||||
self->model = g_value_dup_object (value);
|
||||
if (self->model)
|
||||
g_signal_connect (self->model, "items-changed",
|
||||
G_CALLBACK (gtk_no_selection_items_changed_cb), self);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkNoSelection *self = GTK_NO_SELECTION (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_dispose (GObject *object)
|
||||
{
|
||||
gtk_no_selection_clear_model (GTK_NO_SELECTION (object));
|
||||
|
||||
G_OBJECT_CLASS (gtk_no_selection_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_class_init (GtkNoSelectionClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gtk_no_selection_get_property;
|
||||
gobject_class->set_property = gtk_no_selection_set_property;
|
||||
gobject_class->dispose = gtk_no_selection_dispose;
|
||||
|
||||
g_object_class_override_property (gobject_class, PROP_MODEL, "model");
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_init (GtkNoSelection *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_no_selection_new:
|
||||
* @model: (transfer none): the #GListModel to manage
|
||||
*
|
||||
* Creates a new selection to handle @model.
|
||||
*
|
||||
* Returns: (transfer full) (type GtkNoSelection): a new #GtkNoSelection
|
||||
**/
|
||||
GtkNoSelection *
|
||||
gtk_no_selection_new (GListModel *model)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_NO_SELECTION,
|
||||
"model", model,
|
||||
NULL);
|
||||
}
|
||||
37
gtk/gtknoselection.h
Normal file
37
gtk/gtknoselection.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright © 2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_NO_SELECTION_H__
|
||||
#define __GTK_NO_SELECTION_H__
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_NO_SELECTION (gtk_no_selection_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkNoSelection, gtk_no_selection, GTK, NO_SELECTION, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkNoSelection * gtk_no_selection_new (GListModel *model);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_NO_SELECTION_H__ */
|
||||
1991
gtk/gtkrbtree.c
1991
gtk/gtkrbtree.c
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,4 @@
|
||||
/* gtkrbtreeprivate.h
|
||||
/* gtkrbtree.h
|
||||
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
/* A Red-Black Tree implementation used specifically by GtkTreeView.
|
||||
*/
|
||||
#ifndef __GTK_RBTREE_PRIVATE_H__
|
||||
#define __GTK_RBTREE_PRIVATE_H__
|
||||
#ifndef __GTK_RB_TREE_H__
|
||||
#define __GTK_RB_TREE_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
@@ -26,147 +26,64 @@
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_RBNODE_BLACK = 1 << 0,
|
||||
GTK_RBNODE_RED = 1 << 1,
|
||||
GTK_RBNODE_IS_PARENT = 1 << 2,
|
||||
GTK_RBNODE_IS_SELECTED = 1 << 3,
|
||||
GTK_RBNODE_IS_PRELIT = 1 << 4,
|
||||
GTK_RBNODE_INVALID = 1 << 7,
|
||||
GTK_RBNODE_COLUMN_INVALID = 1 << 8,
|
||||
GTK_RBNODE_DESCENDANTS_INVALID = 1 << 9,
|
||||
GTK_RBNODE_NON_COLORS = GTK_RBNODE_IS_PARENT |
|
||||
GTK_RBNODE_IS_SELECTED |
|
||||
GTK_RBNODE_IS_PRELIT |
|
||||
GTK_RBNODE_INVALID |
|
||||
GTK_RBNODE_COLUMN_INVALID |
|
||||
GTK_RBNODE_DESCENDANTS_INVALID
|
||||
} GtkRBNodeColor;
|
||||
typedef struct _GtkRbTree GtkRbTree;
|
||||
|
||||
typedef struct _GtkRBTree GtkRBTree;
|
||||
typedef struct _GtkRBNode GtkRBNode;
|
||||
typedef struct _GtkRBTreeView GtkRBTreeView;
|
||||
typedef void (* GtkRbTreeAugmentFunc) (GtkRbTree *tree,
|
||||
gpointer node_augment,
|
||||
gpointer node,
|
||||
gpointer left,
|
||||
gpointer right);
|
||||
typedef int (* GtkRbTreeFindFunc) (GtkRbTree *tree,
|
||||
gpointer node,
|
||||
gpointer user_data);
|
||||
|
||||
typedef void (*GtkRBTreeTraverseFunc) (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
gpointer data);
|
||||
GtkRbTree * gtk_rb_tree_new_for_size (gsize element_size,
|
||||
gsize augment_size,
|
||||
GtkRbTreeAugmentFunc augment_func,
|
||||
GDestroyNotify clear_func,
|
||||
GDestroyNotify clear_augment_func);
|
||||
#define gtk_rb_tree_new(type, augment_type, augment_func, clear_func, clear_augment_func) \
|
||||
gtk_rb_tree_new_for_size (sizeof (type), sizeof (augment_type), (augment_func), (clear_func), (clear_augment_func))
|
||||
|
||||
struct _GtkRBTree
|
||||
{
|
||||
GtkRBNode *root;
|
||||
GtkRBTree *parent_tree;
|
||||
GtkRBNode *parent_node;
|
||||
};
|
||||
GtkRbTree * gtk_rb_tree_ref (GtkRbTree *tree);
|
||||
void gtk_rb_tree_unref (GtkRbTree *tree);
|
||||
|
||||
struct _GtkRBNode
|
||||
{
|
||||
guint flags : 14;
|
||||
gpointer gtk_rb_tree_get_first (GtkRbTree *tree);
|
||||
gpointer gtk_rb_tree_get_last (GtkRbTree *tree);
|
||||
gpointer gtk_rb_tree_get_previous (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_rb_tree_get_next (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
|
||||
/* count is the number of nodes beneath us, plus 1 for ourselves.
|
||||
* i.e. node->left->count + node->right->count + 1
|
||||
*/
|
||||
gint count;
|
||||
gpointer gtk_rb_tree_get_root (GtkRbTree *tree);
|
||||
gpointer gtk_rb_tree_get_parent (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_rb_tree_get_left (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_rb_tree_get_right (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_rb_tree_get_augment (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
|
||||
GtkRBNode *left;
|
||||
GtkRBNode *right;
|
||||
GtkRBNode *parent;
|
||||
void gtk_rb_tree_mark_dirty (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
|
||||
/* count the number of total nodes beneath us, including nodes
|
||||
* of children trees.
|
||||
* i.e. node->left->count + node->right->count + node->children->root->count + 1
|
||||
*/
|
||||
guint total_count;
|
||||
|
||||
/* this is the total of sizes of
|
||||
* node->left, node->right, our own height, and the height
|
||||
* of all trees in ->children, iff children exists because
|
||||
* the thing is expanded.
|
||||
*/
|
||||
gint offset;
|
||||
gpointer gtk_rb_tree_insert_before (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
gpointer gtk_rb_tree_insert_after (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
void gtk_rb_tree_remove (GtkRbTree *tree,
|
||||
gpointer node);
|
||||
void gtk_rb_tree_remove_all (GtkRbTree *tree);
|
||||
|
||||
/* Child trees */
|
||||
GtkRBTree *children;
|
||||
};
|
||||
|
||||
|
||||
#define GTK_RBNODE_GET_COLOR(node) (node?(((node->flags>K_RBNODE_RED)==GTK_RBNODE_RED)?GTK_RBNODE_RED:GTK_RBNODE_BLACK):GTK_RBNODE_BLACK)
|
||||
#define GTK_RBNODE_SET_COLOR(node,color) if((node->flags&color)!=color)node->flags=node->flags^(GTK_RBNODE_RED|GTK_RBNODE_BLACK)
|
||||
#define GTK_RBNODE_GET_HEIGHT(node) (node->offset-(node->left->offset+node->right->offset+(node->children?node->children->root->offset:0)))
|
||||
#define GTK_RBNODE_SET_FLAG(node, flag) G_STMT_START{ (node->flags|=flag); }G_STMT_END
|
||||
#define GTK_RBNODE_UNSET_FLAG(node, flag) G_STMT_START{ (node->flags&=~(flag)); }G_STMT_END
|
||||
#define GTK_RBNODE_FLAG_SET(node, flag) (node?(((node->flags&flag)==flag)?TRUE:FALSE):FALSE)
|
||||
|
||||
|
||||
GtkRBTree *_gtk_rbtree_new (void);
|
||||
void _gtk_rbtree_free (GtkRBTree *tree);
|
||||
void _gtk_rbtree_remove (GtkRBTree *tree);
|
||||
void _gtk_rbtree_destroy (GtkRBTree *tree);
|
||||
GtkRBNode *_gtk_rbtree_insert_before (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
gint height,
|
||||
gboolean valid);
|
||||
GtkRBNode *_gtk_rbtree_insert_after (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
gint height,
|
||||
gboolean valid);
|
||||
void _gtk_rbtree_remove_node (GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
gboolean _gtk_rbtree_is_nil (GtkRBNode *node);
|
||||
void _gtk_rbtree_reorder (GtkRBTree *tree,
|
||||
gint *new_order,
|
||||
gint length);
|
||||
gboolean _gtk_rbtree_contains (GtkRBTree *tree,
|
||||
GtkRBTree *potential_child);
|
||||
GtkRBNode *_gtk_rbtree_find_count (GtkRBTree *tree,
|
||||
gint count);
|
||||
void _gtk_rbtree_node_set_height (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
gint height);
|
||||
void _gtk_rbtree_node_mark_invalid(GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
void _gtk_rbtree_node_mark_valid (GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
void _gtk_rbtree_column_invalid (GtkRBTree *tree);
|
||||
void _gtk_rbtree_mark_invalid (GtkRBTree *tree);
|
||||
void _gtk_rbtree_set_fixed_height (GtkRBTree *tree,
|
||||
gint height,
|
||||
gboolean mark_valid);
|
||||
gint _gtk_rbtree_node_find_offset (GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
guint _gtk_rbtree_node_get_index (GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
gboolean _gtk_rbtree_find_index (GtkRBTree *tree,
|
||||
guint index,
|
||||
GtkRBTree **new_tree,
|
||||
GtkRBNode **new_node);
|
||||
gint _gtk_rbtree_find_offset (GtkRBTree *tree,
|
||||
gint offset,
|
||||
GtkRBTree **new_tree,
|
||||
GtkRBNode **new_node);
|
||||
void _gtk_rbtree_traverse (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GTraverseType order,
|
||||
GtkRBTreeTraverseFunc func,
|
||||
gpointer data);
|
||||
GtkRBNode *_gtk_rbtree_first (GtkRBTree *tree);
|
||||
GtkRBNode *_gtk_rbtree_next (GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
GtkRBNode *_gtk_rbtree_prev (GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
void _gtk_rbtree_next_full (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkRBTree **new_tree,
|
||||
GtkRBNode **new_node);
|
||||
void _gtk_rbtree_prev_full (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkRBTree **new_tree,
|
||||
GtkRBNode **new_node);
|
||||
|
||||
gint _gtk_rbtree_get_depth (GtkRBTree *tree);
|
||||
gpointer gtk_rb_tree_find (GtkRbTree *tree,
|
||||
gpointer *out_before,
|
||||
gpointer *out_after,
|
||||
GtkRbTreeFindFunc find_func,
|
||||
gpointer user_data);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __GTK_RBTREE_PRIVATE_H__ */
|
||||
#endif /* __GTK_RB_TREE_H__ */
|
||||
|
||||
290
gtk/gtkselectionmodel.c
Normal file
290
gtk/gtkselectionmodel.c
Normal file
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkselectionmodel.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkmarshalers.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkselectionmodel
|
||||
* @Title: GtkSelectionModel
|
||||
* @Short_description: An extension of the list model interface that handles selections
|
||||
* @See_also: #GListModel, #GtkSingleSelection
|
||||
*
|
||||
* #GtkSelectionModel is an interface that extends the #GListModel interface by adding
|
||||
* support for selections. This support is then used by widgets using list models to add
|
||||
* the ability to select and unselect various items.
|
||||
*
|
||||
* GTK provides default implementations of the mode common selection modes such as
|
||||
* #GtkSingleSelection, so you will only need to implement this interface if you want
|
||||
* detailed control about how selections should be handled.
|
||||
*
|
||||
* A #GtkSelectionModel supports a single boolean per row indicating if a row is selected
|
||||
* or not. This can be queried via gtk_selection_model_is_selected(). When the selected
|
||||
* state of one or more rows changes, the model will emit the
|
||||
* GtkSelectionModel::selection-changed signal by calling the
|
||||
* gtk_selection_model_selection_changed() function. The positions given in that signal
|
||||
* may have their selection state changed, though that is not a requirement.
|
||||
* If new items added to the model via the #GListModel::items-changed signal are selected
|
||||
* or not is up to the implementation.
|
||||
*
|
||||
* Additionally, the interface can expose functionality to select and unselect items.
|
||||
* If these functions are implemented, GTK's list widgets will allow users to select and
|
||||
* unselect items. However, #GtkSelectionModels are free to only implement them
|
||||
* partially or not at all. In that case the widgets will not support the unimplemented
|
||||
* operations.
|
||||
*
|
||||
* When selecting or unselecting is supported by a model, the return values of the
|
||||
* selection functions do NOT indicate if selection or unselection happened. They are
|
||||
* only meant to indicate complete failure, like when this mode of selecting is not
|
||||
* supported by the model.
|
||||
* Selections may happen asynchronously, so the only reliable way to find out when an
|
||||
* item was selected is to listen to the signals that indicate selection.
|
||||
*/
|
||||
|
||||
G_DEFINE_INTERFACE (GtkSelectionModel, gtk_selection_model, G_TYPE_LIST_MODEL)
|
||||
|
||||
enum {
|
||||
SELECTION_CHANGED,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
static gboolean
|
||||
gtk_selection_model_default_is_selected (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_selection_model_default_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean exclusive,
|
||||
gboolean extend)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
static gboolean
|
||||
gtk_selection_model_default_unselect_item (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_selection_model_default_select_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gboolean exclusive)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_selection_model_default_unselect_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_selection_model_default_select_all (GtkSelectionModel *model)
|
||||
{
|
||||
return gtk_selection_model_select_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)), FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_selection_model_default_unselect_all (GtkSelectionModel *model)
|
||||
{
|
||||
return gtk_selection_model_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_selection_model_default_init (GtkSelectionModelInterface *iface)
|
||||
{
|
||||
iface->is_selected = gtk_selection_model_default_is_selected;
|
||||
iface->select_item = gtk_selection_model_default_select_item;
|
||||
iface->unselect_item = gtk_selection_model_default_unselect_item;
|
||||
iface->select_range = gtk_selection_model_default_select_range;
|
||||
iface->unselect_range = gtk_selection_model_default_unselect_range;
|
||||
iface->select_all = gtk_selection_model_default_select_all;
|
||||
iface->unselect_all = gtk_selection_model_default_unselect_all;
|
||||
|
||||
/**
|
||||
* GtkSelectionModel::selection-changed
|
||||
* @model: a #GtkSelectionModel
|
||||
* @position: The first item that may have changed
|
||||
* @n_items: number of items with changes
|
||||
*
|
||||
* Emitted when the selection state of some of the items in @model changes.
|
||||
*
|
||||
* Note that this signal does not specify the new selection state of the items,
|
||||
* they need to be queried manually.
|
||||
* It is also not necessary for a model to change the selection state of any of
|
||||
* the items in the selection model, though it would be rather useless to emit
|
||||
* such a signal.
|
||||
*/
|
||||
signals[SELECTION_CHANGED] =
|
||||
g_signal_new ("selection-changed",
|
||||
GTK_TYPE_SELECTION_MODEL,
|
||||
G_SIGNAL_RUN_LAST,
|
||||
0,
|
||||
NULL, NULL,
|
||||
_gtk_marshal_VOID__UINT_UINT,
|
||||
G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
|
||||
g_signal_set_va_marshaller (signals[SELECTION_CHANGED],
|
||||
GTK_TYPE_SELECTION_MODEL,
|
||||
_gtk_marshal_VOID__UINT_UINTv);
|
||||
|
||||
g_object_interface_install_property (iface,
|
||||
g_param_spec_object ("model",
|
||||
P_("Model"),
|
||||
P_("List managed by this selection"),
|
||||
G_TYPE_LIST_MODEL,
|
||||
G_PARAM_READWRITE
|
||||
| G_PARAM_CONSTRUCT_ONLY
|
||||
| G_PARAM_EXPLICIT_NOTIFY
|
||||
| G_PARAM_STATIC_STRINGS));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_selection_model_is_selected:
|
||||
* @model: a #GtkSelectionModel
|
||||
* @position: the position of the item to query
|
||||
*
|
||||
* Checks if the given item is selected.
|
||||
*
|
||||
* Returns: %TRUE if the item is selected
|
||||
**/
|
||||
gboolean
|
||||
gtk_selection_model_is_selected (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->is_selected (model, position);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_selection_model_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean exclusive,
|
||||
gboolean extend)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->select_item (model, position, exclusive, extend);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_selection_model_unselect_item (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->unselect_item (model, position);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_selection_model_select_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gboolean exclusive)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->select_range (model, position, n_items, exclusive);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_selection_model_unselect_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->unselect_range (model, position, n_items);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_selection_model_select_all (GtkSelectionModel *model)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->select_all (model);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_selection_model_unselect_all (GtkSelectionModel *model)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->unselect_all (model);
|
||||
}
|
||||
|
||||
GListModel *
|
||||
gtk_selection_model_get_model (GtkSelectionModel *model)
|
||||
{
|
||||
GListModel *child;
|
||||
|
||||
g_object_get (model, "model", &child, NULL);
|
||||
if (child)
|
||||
g_object_unref (child);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_selection_model_selection_changed (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_SELECTION_MODEL (model));
|
||||
|
||||
g_signal_emit (model, signals[SELECTION_CHANGED], 0, position, n_items);
|
||||
}
|
||||
|
||||
122
gtk/gtkselectionmodel.h
Normal file
122
gtk/gtkselectionmodel.h
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_SELECTION_MODEL_H__
|
||||
#define __GTK_SELECTION_MODEL_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_SELECTION_MODEL (gtk_selection_model_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_INTERFACE (GtkSelectionModel, gtk_selection_model, GTK, SELECTION_MODEL, GListModel)
|
||||
|
||||
/**
|
||||
* GtkSelectionModelInterface:
|
||||
* @is_selected: Return if the item at the given position is selected.
|
||||
* @select_item: Select the item in the given position. If the operation
|
||||
* is known to fail, return %FALSE.
|
||||
* @unselect_item: Unselect the item in the given position. If the
|
||||
* operation is known to fail, return %FALSE.
|
||||
* @select_range: Select all items in the given range. If the operation
|
||||
* is unsupported or known to fail for all items, return %FALSE.
|
||||
* @unselect_range: Unselect all items in the given range. If the
|
||||
* operation is unsupported or known to fail for all items, return
|
||||
* %FALSE.
|
||||
* @select_all: Select all items in the model. If the operation is
|
||||
* unsupported or known to fail for all items, return %FALSE.
|
||||
* @unselect_all: Unselect all items in the model. If the operation is
|
||||
* unsupported or known to fail for all items, return %FALSE.
|
||||
*
|
||||
* The list of virtual functions for the #GtkSelectionModel interface.
|
||||
* All getter functions are mandatory to implement, but the model does
|
||||
* not need to implement any functions to support selecting or unselecting
|
||||
* items. Of course, if the model does not do that, it means that users
|
||||
* cannot select or unselect items in a list widgets using the model.
|
||||
*/
|
||||
struct _GtkSelectionModelInterface
|
||||
{
|
||||
/*< private >*/
|
||||
GTypeInterface g_iface;
|
||||
|
||||
/*< public >*/
|
||||
gboolean (* is_selected) (GtkSelectionModel *model,
|
||||
guint position);
|
||||
|
||||
gboolean (* select_item) (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean exclusive,
|
||||
gboolean extend);
|
||||
gboolean (* unselect_item) (GtkSelectionModel *model,
|
||||
guint position);
|
||||
gboolean (* select_range) (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gboolean exclusive);
|
||||
gboolean (* unselect_range) (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items);
|
||||
gboolean (* select_all) (GtkSelectionModel *model);
|
||||
gboolean (* unselect_all) (GtkSelectionModel *model);
|
||||
};
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_is_selected (GtkSelectionModel *model,
|
||||
guint position);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean exclusive,
|
||||
gboolean extend);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_unselect_item (GtkSelectionModel *model,
|
||||
guint position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_select_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gboolean exclusive);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_unselect_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_select_all (GtkSelectionModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_unselect_all (GtkSelectionModel *model);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_selection_model_get_model (GtkSelectionModel *model);
|
||||
|
||||
/* for implementations only */
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_selection_model_selection_changed (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SELECTION_MODEL_H__ */
|
||||
292
gtk/gtkset.c
Normal file
292
gtk/gtkset.c
Normal file
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* Copyright © 2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "gtkset.h"
|
||||
|
||||
/* Store a set of unsigned integers as a sorted array of ranges.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint first;
|
||||
guint n_items;
|
||||
} Range;
|
||||
|
||||
struct _GtkSet
|
||||
{
|
||||
GArray *ranges;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkSet *set;
|
||||
Range *current;
|
||||
int idx;
|
||||
guint pos;
|
||||
} GtkRealSetIter;
|
||||
|
||||
GtkSet *
|
||||
gtk_set_new (void)
|
||||
{
|
||||
GtkSet *set;
|
||||
|
||||
set = g_new (GtkSet, 1);
|
||||
set->ranges = g_array_new (FALSE, FALSE, sizeof (Range));
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_free (GtkSet *set)
|
||||
{
|
||||
g_array_free (set->ranges, TRUE);
|
||||
g_free (set);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_set_contains (GtkSet *set,
|
||||
guint item)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
|
||||
if (item < r->first)
|
||||
return FALSE;
|
||||
|
||||
if (item < r->first + r->n_items)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_remove_all (GtkSet *set)
|
||||
{
|
||||
g_array_set_size (set->ranges, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
range_compare (Range *r, Range *s)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (r->first + r->n_items < s->first)
|
||||
ret = -1;
|
||||
else if (s->first + s->n_items < r->first)
|
||||
ret = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_add_range (GtkSet *set,
|
||||
guint first_item,
|
||||
guint n_items)
|
||||
{
|
||||
int i;
|
||||
Range s;
|
||||
int first = -1;
|
||||
int last = -1;
|
||||
|
||||
s.first = first_item;
|
||||
s.n_items = n_items;
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
int cmp = range_compare (&s, r);
|
||||
|
||||
if (cmp < 0)
|
||||
break;
|
||||
|
||||
if (cmp == 0)
|
||||
{
|
||||
if (first < 0)
|
||||
first = i;
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (first > -1)
|
||||
{
|
||||
Range *r;
|
||||
guint start;
|
||||
guint end;
|
||||
|
||||
r = &g_array_index (set->ranges, Range, first);
|
||||
start = MIN (s.first, r->first);
|
||||
|
||||
r = &g_array_index (set->ranges, Range, last);
|
||||
end = MAX (s.first + s.n_items - 1, r->first + r->n_items - 1);
|
||||
|
||||
s.first = start;
|
||||
s.n_items = end - start + 1;
|
||||
|
||||
g_array_remove_range (set->ranges, first, last - first + 1);
|
||||
g_array_insert_val (set->ranges, first, s);
|
||||
}
|
||||
else
|
||||
g_array_insert_val (set->ranges, i, s);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_remove_range (GtkSet *set,
|
||||
guint first_item,
|
||||
guint n_items)
|
||||
{
|
||||
Range s;
|
||||
int i;
|
||||
int first = -1;
|
||||
int last = -1;
|
||||
|
||||
s.first = first_item;
|
||||
s.n_items = n_items;
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
int cmp = range_compare (&s, r);
|
||||
|
||||
if (cmp < 0)
|
||||
break;
|
||||
|
||||
if (cmp == 0)
|
||||
{
|
||||
if (first < 0)
|
||||
first = i;
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (first > -1)
|
||||
{
|
||||
Range *r;
|
||||
Range a[2];
|
||||
int k = 0;
|
||||
|
||||
r = &g_array_index (set->ranges, Range, first);
|
||||
if (r->first < s.first)
|
||||
{
|
||||
a[k].first = r->first;
|
||||
a[k].n_items = s.first - r->first;
|
||||
k++;
|
||||
}
|
||||
r = &g_array_index (set->ranges, Range, last);
|
||||
if (r->first + r->n_items > s.first + s.n_items)
|
||||
{
|
||||
a[k].first = s.first + s.n_items;
|
||||
a[k].n_items = r->first + r->n_items - a[k].first;
|
||||
k++;
|
||||
}
|
||||
g_array_remove_range (set->ranges, first, last - first + 1);
|
||||
if (k > 0)
|
||||
g_array_insert_vals (set->ranges, first, a, k);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_add_item (GtkSet *set,
|
||||
guint item)
|
||||
{
|
||||
gtk_set_add_range (set, item, 1);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_remove_item (GtkSet *set,
|
||||
guint item)
|
||||
{
|
||||
gtk_set_remove_range (set, item, 1);
|
||||
}
|
||||
|
||||
/* This is peculiar operation: Replace every number n >= first by n + shift
|
||||
* This is only supported for negative shift if the shifting does not cause any
|
||||
* ranges to overlap.
|
||||
*/
|
||||
void
|
||||
gtk_set_shift (GtkSet *set,
|
||||
guint first,
|
||||
int shift)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
if (r->first >= first)
|
||||
r->first += shift;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_iter_init (GtkSetIter *iter,
|
||||
GtkSet *set)
|
||||
{
|
||||
GtkRealSetIter *ri = (GtkRealSetIter *)iter;
|
||||
|
||||
ri->set = set;
|
||||
ri->idx = -1;
|
||||
ri->current = 0;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_set_iter_next (GtkSetIter *iter,
|
||||
guint *item)
|
||||
{
|
||||
GtkRealSetIter *ri = (GtkRealSetIter *)iter;
|
||||
|
||||
if (ri->idx == -1)
|
||||
{
|
||||
next_range:
|
||||
ri->idx++;
|
||||
|
||||
if (ri->idx == ri->set->ranges->len)
|
||||
return FALSE;
|
||||
|
||||
ri->current = &g_array_index (ri->set->ranges, Range, ri->idx);
|
||||
ri->pos = ri->current->first;
|
||||
}
|
||||
else
|
||||
{
|
||||
ri->pos++;
|
||||
if (ri->pos == ri->current->first + ri->current->n_items)
|
||||
goto next_range;
|
||||
}
|
||||
|
||||
*item = ri->pos;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
void
|
||||
gtk_set_dump (GtkSet *set)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
g_print (" %u:%u", r->first, r->n_items);
|
||||
}
|
||||
g_print ("\n");
|
||||
}
|
||||
#endif
|
||||
63
gtk/gtkset.h
Normal file
63
gtk/gtkset.h
Normal file
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright © 2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_SET_H__
|
||||
#define __GTK_SET_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
typedef struct _GtkSet GtkSet;
|
||||
typedef struct _GtkSetIter GtkSetIter;
|
||||
|
||||
struct _GtkSetIter
|
||||
{
|
||||
gpointer dummy1;
|
||||
gpointer dummy2;
|
||||
int dummy3;
|
||||
int dummy4;
|
||||
};
|
||||
|
||||
GtkSet *gtk_set_new (void);
|
||||
void gtk_set_free (GtkSet *set);
|
||||
|
||||
gboolean gtk_set_contains (GtkSet *set,
|
||||
guint item);
|
||||
|
||||
void gtk_set_remove_all (GtkSet *set);
|
||||
void gtk_set_add_item (GtkSet *set,
|
||||
guint item);
|
||||
void gtk_set_remove_item (GtkSet *set,
|
||||
guint item);
|
||||
void gtk_set_add_range (GtkSet *set,
|
||||
guint first,
|
||||
guint n);
|
||||
void gtk_set_remove_range (GtkSet *set,
|
||||
guint first,
|
||||
guint n);
|
||||
|
||||
void gtk_set_shift (GtkSet *set,
|
||||
guint first,
|
||||
int shift);
|
||||
|
||||
void gtk_set_iter_init (GtkSetIter *iter,
|
||||
GtkSet *set);
|
||||
gboolean gtk_set_iter_next (GtkSetIter *iter,
|
||||
guint *item);
|
||||
|
||||
#endif /* __GTK_SET_H__ */
|
||||
559
gtk/gtksingleselection.c
Normal file
559
gtk/gtksingleselection.c
Normal file
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtksingleselection.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtksingleselection
|
||||
* @Short_description: A selection model that allows selecting a single item
|
||||
* @Title: GtkSingleSelection
|
||||
* @see_also: #GtkSelectionModel
|
||||
*
|
||||
* GtkSingleSelection is an implementation of the #GtkSelectionModel interface
|
||||
* that allows selecting a single element. It is the default selection method
|
||||
* used by list widgets in GTK.
|
||||
*/
|
||||
struct _GtkSingleSelection
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GListModel *model;
|
||||
guint selected;
|
||||
gpointer selected_item;
|
||||
|
||||
guint autoselect : 1;
|
||||
guint can_unselect : 1;
|
||||
};
|
||||
|
||||
struct _GtkSingleSelectionClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_AUTOSELECT,
|
||||
PROP_CAN_UNSELECT,
|
||||
PROP_SELECTED,
|
||||
|
||||
/* selectionmodel */
|
||||
PROP_MODEL,
|
||||
N_PROPS = PROP_MODEL
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static GType
|
||||
gtk_single_selection_get_item_type (GListModel *list)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (list);
|
||||
|
||||
return g_list_model_get_item_type (self->model);
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_single_selection_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (list);
|
||||
|
||||
return g_list_model_get_n_items (self->model);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_single_selection_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (list);
|
||||
|
||||
return g_list_model_get_item (self->model, position);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_single_selection_get_item_type;
|
||||
iface->get_n_items = gtk_single_selection_get_n_items;
|
||||
iface->get_item = gtk_single_selection_get_item;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_single_selection_is_selected (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (model);
|
||||
|
||||
return self->selected == position;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_single_selection_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean exclusive,
|
||||
gboolean extend)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (model);
|
||||
|
||||
/* XXX: Should we check that position < n_items here? */
|
||||
gtk_single_selection_set_selected (self, position);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_single_selection_unselect_item (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (model);
|
||||
|
||||
if (!self->can_unselect)
|
||||
return FALSE;
|
||||
|
||||
if (self->selected == position)
|
||||
gtk_single_selection_set_selected (self, GTK_INVALID_LIST_POSITION);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_selection_model_init (GtkSelectionModelInterface *iface)
|
||||
{
|
||||
iface->is_selected = gtk_single_selection_is_selected;
|
||||
iface->select_item = gtk_single_selection_select_item;
|
||||
iface->unselect_item = gtk_single_selection_unselect_item;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (GtkSingleSelection, gtk_single_selection, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
|
||||
gtk_single_selection_list_model_init)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
|
||||
gtk_single_selection_selection_model_init))
|
||||
|
||||
static void
|
||||
gtk_single_selection_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkSingleSelection *self)
|
||||
{
|
||||
gboolean emit_selection_changed = FALSE;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
if (self->selected_item == NULL)
|
||||
{
|
||||
if (self->autoselect)
|
||||
{
|
||||
self->selected_item = g_list_model_get_item (self->model, 0);
|
||||
if (self->selected_item)
|
||||
{
|
||||
self->selected = 0;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
|
||||
emit_selection_changed = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (self->selected < position)
|
||||
{
|
||||
/* unchanged */
|
||||
}
|
||||
else if (self->selected >= position + removed)
|
||||
{
|
||||
self->selected += added - removed;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
|
||||
}
|
||||
else
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
{
|
||||
gpointer item = g_list_model_get_item (model, position + i);
|
||||
if (item == self->selected_item)
|
||||
{
|
||||
/* the item moved */
|
||||
if (self->selected != position + i)
|
||||
{
|
||||
self->selected = position + i;
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i == added)
|
||||
{
|
||||
/* the item really was deleted */
|
||||
g_clear_object (&self->selected_item);
|
||||
if (self->autoselect)
|
||||
{
|
||||
self->selected = position + (self->selected - position) * added / removed;
|
||||
self->selected_item = g_list_model_get_item (self->model, self->selected);
|
||||
if (self->selected_item == NULL && position > 0)
|
||||
{
|
||||
self->selected = position - 1;
|
||||
self->selected_item = g_list_model_get_item (self->model, self->selected);
|
||||
g_assert (self->selected_item);
|
||||
}
|
||||
emit_selection_changed = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_clear_object (&self->selected_item);
|
||||
self->selected = GTK_INVALID_LIST_POSITION;
|
||||
}
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
|
||||
}
|
||||
}
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
|
||||
if (emit_selection_changed && self->selected != GTK_INVALID_LIST_POSITION)
|
||||
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), self->selected, 1);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_clear_model (GtkSingleSelection *self)
|
||||
{
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->model,
|
||||
gtk_single_selection_items_changed_cb,
|
||||
self);
|
||||
g_clear_object (&self->model);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_AUTOSELECT:
|
||||
gtk_single_selection_set_autoselect (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_CAN_UNSELECT:
|
||||
gtk_single_selection_set_can_unselect (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_MODEL:
|
||||
gtk_single_selection_clear_model (self);
|
||||
self->model = g_value_dup_object (value);
|
||||
if (self->model)
|
||||
g_signal_connect (self->model, "items-changed",
|
||||
G_CALLBACK (gtk_single_selection_items_changed_cb), self);
|
||||
if (self->autoselect)
|
||||
gtk_single_selection_set_selected (self, 0);
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
gtk_single_selection_set_selected (self, g_value_get_uint (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_AUTOSELECT:
|
||||
g_value_set_boolean (value, self->autoselect);
|
||||
break;
|
||||
|
||||
case PROP_CAN_UNSELECT:
|
||||
g_value_set_boolean (value, self->can_unselect);
|
||||
break;
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
g_value_set_uint (value, self->selected);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_dispose (GObject *object)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (object);
|
||||
|
||||
gtk_single_selection_clear_model (self);
|
||||
|
||||
self->selected = GTK_INVALID_LIST_POSITION;
|
||||
g_clear_object (&self->selected_item);
|
||||
|
||||
G_OBJECT_CLASS (gtk_single_selection_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_class_init (GtkSingleSelectionClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gtk_single_selection_get_property;
|
||||
gobject_class->set_property = gtk_single_selection_set_property;
|
||||
gobject_class->dispose = gtk_single_selection_dispose;
|
||||
|
||||
g_object_class_override_property (gobject_class, PROP_MODEL, "model");
|
||||
|
||||
/**
|
||||
* GtkSingleSelection:autoselect
|
||||
*
|
||||
* If the selection will always select an item
|
||||
*/
|
||||
properties[PROP_AUTOSELECT] =
|
||||
g_param_spec_boolean ("autoselect",
|
||||
P_("Autoselect"),
|
||||
P_("If the selection will always select an item"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkSingleSelection:can-unselect
|
||||
*
|
||||
* If unselecting the selected item is allowed
|
||||
*/
|
||||
properties[PROP_CAN_UNSELECT] =
|
||||
g_param_spec_boolean ("can-unselect",
|
||||
P_("Can unselect"),
|
||||
P_("If unselecting the selected item is allowed"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkSingleSelection:selected
|
||||
*
|
||||
* Position of the selected item
|
||||
*/
|
||||
properties[PROP_SELECTED] =
|
||||
g_param_spec_uint ("selected",
|
||||
P_("Selected"),
|
||||
P_("Position of the selected item"),
|
||||
0, G_MAXUINT, GTK_INVALID_LIST_POSITION,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_init (GtkSingleSelection *self)
|
||||
{
|
||||
self->selected = GTK_INVALID_LIST_POSITION;
|
||||
self->autoselect = TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_single_selection_new:
|
||||
* @model: (transfer none): the #GListModel to manage
|
||||
*
|
||||
* Creates a new selection to handle @model.
|
||||
*
|
||||
* Returns: (transfer full) (type GtkSingleSelection): a new #GtkSingleSelection
|
||||
**/
|
||||
GtkSingleSelection *
|
||||
gtk_single_selection_new (GListModel *model)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_SINGLE_SELECTION,
|
||||
"model", model,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_single_selection_get_selected:
|
||||
* @self: a #GtkSingleSelection
|
||||
*
|
||||
* Gets the position of the selected item. If no item is selected,
|
||||
* #GTK_INVALID_LIST_POSITION is returned.
|
||||
*
|
||||
* Returns: The position of the selected item
|
||||
**/
|
||||
guint
|
||||
gtk_single_selection_get_selected (GtkSingleSelection *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SINGLE_SELECTION (self), GTK_INVALID_LIST_POSITION);
|
||||
|
||||
return self->selected;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_single_selection_set_selected:
|
||||
* @self: a #GtkSingleSelection
|
||||
* @position: the item to select or #GTK_INVALID_LIST_POSITION
|
||||
*
|
||||
* Selects the item at the given position. If the list does not have an item at
|
||||
* @position or #GTK_INVALID_LIST_POSITION is given, the behavior depends on the
|
||||
* value of the GtkSingleSelection:autoselect property: If it is set, no change
|
||||
* will occur and the old item will stay selected. If it is unset, the selection
|
||||
* will be unset and no item will be selected.
|
||||
**/
|
||||
void
|
||||
gtk_single_selection_set_selected (GtkSingleSelection *self,
|
||||
guint position)
|
||||
{
|
||||
gpointer new_selected = NULL;
|
||||
guint old_position;
|
||||
|
||||
g_return_if_fail (GTK_IS_SINGLE_SELECTION (self));
|
||||
|
||||
if (self->selected == position)
|
||||
return;
|
||||
|
||||
if (self->model)
|
||||
new_selected = g_list_model_get_item (self->model, position);
|
||||
|
||||
if (new_selected == NULL)
|
||||
position = GTK_INVALID_LIST_POSITION;
|
||||
|
||||
if (self->selected == position)
|
||||
return;
|
||||
|
||||
old_position = self->selected;
|
||||
self->selected = position;
|
||||
g_clear_object (&self->selected_item);
|
||||
self->selected_item = new_selected;
|
||||
|
||||
if (old_position == GTK_INVALID_LIST_POSITION)
|
||||
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), position, 1);
|
||||
else if (position == GTK_INVALID_LIST_POSITION)
|
||||
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), old_position, 1);
|
||||
else if (position < old_position)
|
||||
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), position, old_position - position + 1);
|
||||
else
|
||||
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), old_position, position - old_position + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_single_selection_get_autoselect:
|
||||
* @self: a #GtkSingleSelection
|
||||
*
|
||||
* Checks if autoselect has been enabled or disabled via
|
||||
* gtk_single_selection_set_autoselect().
|
||||
*
|
||||
* Returns: %TRUE if autoselect is enabled
|
||||
**/
|
||||
gboolean
|
||||
gtk_single_selection_get_autoselect (GtkSingleSelection *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SINGLE_SELECTION (self), TRUE);
|
||||
|
||||
return self->autoselect;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_single_selection_set_autoselect:
|
||||
* @self: a #GtkSingleSelection
|
||||
* @autoselect: %TRUE to always select an item
|
||||
*
|
||||
* If @autoselect is %TRUE, @self will enforce that an item is always
|
||||
* selected. It will select a new item when the currently selected
|
||||
* item is deleted and it will disallow unselecting the current item.
|
||||
**/
|
||||
void
|
||||
gtk_single_selection_set_autoselect (GtkSingleSelection *self,
|
||||
gboolean autoselect)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_SINGLE_SELECTION (self));
|
||||
|
||||
if (self->autoselect == autoselect)
|
||||
return;
|
||||
|
||||
self->autoselect = autoselect;
|
||||
|
||||
g_object_freeze_notify (G_OBJECT (self));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_AUTOSELECT]);
|
||||
|
||||
if (self->autoselect && !self->selected_item)
|
||||
gtk_single_selection_set_selected (self, 0);
|
||||
|
||||
g_object_thaw_notify (G_OBJECT (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_single_selection_get_can_unselect:
|
||||
* @self: a #GtkSingleSelection
|
||||
*
|
||||
* If %TRUE, gtk_selection_model_unselect_item() is supported and allows
|
||||
* unselecting the selected item.
|
||||
*
|
||||
* Returns: %TRUE to support unselecting
|
||||
**/
|
||||
gboolean
|
||||
gtk_single_selection_get_can_unselect (GtkSingleSelection *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SINGLE_SELECTION (self), FALSE);
|
||||
|
||||
return self->can_unselect;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_single_selection_set_can_unselect:
|
||||
* @self: a #GtkSingleSelection
|
||||
* @can_unselect: %TRUE to allow unselecting
|
||||
*
|
||||
* If %TRUE, unselecting the current item via
|
||||
* gtk_selection_model_unselect_item() is supported.
|
||||
*
|
||||
* Note that setting GtkSingleSelection:autoselect will cause the
|
||||
* unselecting to not work, so it practically makes no sense to set
|
||||
* both at the same time the same time..
|
||||
**/
|
||||
void
|
||||
gtk_single_selection_set_can_unselect (GtkSingleSelection *self,
|
||||
gboolean can_unselect)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_SINGLE_SELECTION (self));
|
||||
|
||||
if (self->can_unselect == can_unselect)
|
||||
return;
|
||||
|
||||
self->can_unselect = can_unselect;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_CAN_UNSELECT]);
|
||||
}
|
||||
|
||||
65
gtk/gtksingleselection.h
Normal file
65
gtk/gtksingleselection.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright © 2018 Benjamin Otte
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#ifndef __GTK_SINGLE_SELECTION_H__
|
||||
#define __GTK_SINGLE_SELECTION_H__
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_SINGLE_SELECTION (gtk_single_selection_get_type ())
|
||||
|
||||
/**
|
||||
* GTK_INVALID_LIST_POSITION:
|
||||
*
|
||||
* The value used to refer to a guaranteed invalid position in a #GListModel. This
|
||||
* value may be returned from some functions, others may accept it as input.
|
||||
* Its interpretion may differ for different functions.
|
||||
*
|
||||
* Refer to each function's documentation for if this value is allowed and what it
|
||||
* does.
|
||||
*/
|
||||
#define GTK_INVALID_LIST_POSITION (G_MAXUINT)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkSingleSelection, gtk_single_selection, GTK, SINGLE_SELECTION, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSingleSelection * gtk_single_selection_new (GListModel *model);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_single_selection_get_selected (GtkSingleSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_single_selection_set_selected (GtkSingleSelection *self,
|
||||
guint position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_single_selection_get_autoselect (GtkSingleSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_single_selection_set_autoselect (GtkSingleSelection *self,
|
||||
gboolean autoselect);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_single_selection_get_can_unselect (GtkSingleSelection *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_single_selection_set_can_unselect (GtkSingleSelection *self,
|
||||
gboolean can_unselect);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_SINGLE_SELECTION_H__ */
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include "gtkslicelistmodel.h"
|
||||
|
||||
#include "gtkcssrbtreeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
@@ -157,13 +156,19 @@ gtk_slice_list_model_items_changed_cb (GListModel *model,
|
||||
else
|
||||
{
|
||||
guint n_after, n_before;
|
||||
guint skip;
|
||||
|
||||
if (position > self->offset)
|
||||
skip = position - self->offset;
|
||||
else
|
||||
skip = 0;
|
||||
|
||||
n_after = g_list_model_get_n_items (self->model);
|
||||
n_before = n_after - added + removed;
|
||||
n_after = CLAMP (n_after, self->offset, self->offset + self->size) - self->offset;
|
||||
n_before = CLAMP (n_before, self->offset, self->offset + self->size) - self->offset;
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_before, n_after);
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), skip, n_before - skip, n_after - skip);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include "gtktreelistmodel.h"
|
||||
|
||||
#include "gtkcssrbtreeprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
@@ -50,7 +50,7 @@ struct _TreeNode
|
||||
{
|
||||
GListModel *model;
|
||||
GtkTreeListRow *row;
|
||||
GtkCssRbTree *children;
|
||||
GtkRbTree *children;
|
||||
union {
|
||||
TreeNode *parent;
|
||||
GtkTreeListModel *list;
|
||||
@@ -112,19 +112,19 @@ static TreeNode *
|
||||
tree_node_get_nth_child (TreeNode *node,
|
||||
guint position)
|
||||
{
|
||||
GtkCssRbTree *tree;
|
||||
GtkRbTree *tree;
|
||||
TreeNode *child, *tmp;
|
||||
TreeAugment *aug;
|
||||
|
||||
tree = node->children;
|
||||
child = gtk_css_rb_tree_get_root (tree);
|
||||
child = gtk_rb_tree_get_root (tree);
|
||||
|
||||
while (child)
|
||||
{
|
||||
tmp = gtk_css_rb_tree_get_left (tree, child);
|
||||
tmp = gtk_rb_tree_get_left (tree, child);
|
||||
if (tmp)
|
||||
{
|
||||
aug = gtk_css_rb_tree_get_augment (tree, tmp);
|
||||
aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_local)
|
||||
{
|
||||
child = tmp;
|
||||
@@ -138,7 +138,7 @@ tree_node_get_nth_child (TreeNode *node,
|
||||
|
||||
position--;
|
||||
|
||||
child = gtk_css_rb_tree_get_right (tree, child);
|
||||
child = gtk_rb_tree_get_right (tree, child);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
@@ -153,27 +153,27 @@ tree_node_get_n_children (TreeNode *node)
|
||||
if (node->children == NULL)
|
||||
return 0;
|
||||
|
||||
child_node = gtk_css_rb_tree_get_root (node->children);
|
||||
child_node = gtk_rb_tree_get_root (node->children);
|
||||
if (child_node == NULL)
|
||||
return 0;
|
||||
|
||||
child_aug = gtk_css_rb_tree_get_augment (node->children, child_node);
|
||||
child_aug = gtk_rb_tree_get_augment (node->children, child_node);
|
||||
|
||||
return child_aug->n_items;
|
||||
}
|
||||
|
||||
static guint
|
||||
tree_node_get_local_position (GtkCssRbTree *tree,
|
||||
tree_node_get_local_position (GtkRbTree *tree,
|
||||
TreeNode *node)
|
||||
{
|
||||
TreeNode *left, *parent;
|
||||
TreeAugment *left_aug;
|
||||
guint n;
|
||||
|
||||
left = gtk_css_rb_tree_get_left (tree, node);
|
||||
left = gtk_rb_tree_get_left (tree, node);
|
||||
if (left)
|
||||
{
|
||||
left_aug = gtk_css_rb_tree_get_augment (tree, left);
|
||||
left_aug = gtk_rb_tree_get_augment (tree, left);
|
||||
n = left_aug->n_local;
|
||||
}
|
||||
else
|
||||
@@ -181,11 +181,11 @@ tree_node_get_local_position (GtkCssRbTree *tree,
|
||||
n = 0;
|
||||
}
|
||||
|
||||
for (parent = gtk_css_rb_tree_get_parent (tree, node);
|
||||
for (parent = gtk_rb_tree_get_parent (tree, node);
|
||||
parent;
|
||||
parent = gtk_css_rb_tree_get_parent (tree, node))
|
||||
parent = gtk_rb_tree_get_parent (tree, node))
|
||||
{
|
||||
left = gtk_css_rb_tree_get_left (tree, parent);
|
||||
left = gtk_rb_tree_get_left (tree, parent);
|
||||
if (left == node)
|
||||
{
|
||||
/* we are the left node, nothing changes */
|
||||
@@ -196,7 +196,7 @@ tree_node_get_local_position (GtkCssRbTree *tree,
|
||||
n++;
|
||||
if (left)
|
||||
{
|
||||
left_aug = gtk_css_rb_tree_get_augment (tree, left);
|
||||
left_aug = gtk_rb_tree_get_augment (tree, left);
|
||||
n += left_aug->n_local;
|
||||
}
|
||||
}
|
||||
@@ -209,7 +209,7 @@ tree_node_get_local_position (GtkCssRbTree *tree,
|
||||
static guint
|
||||
tree_node_get_position (TreeNode *node)
|
||||
{
|
||||
GtkCssRbTree *tree;
|
||||
GtkRbTree *tree;
|
||||
TreeNode *left, *parent;
|
||||
TreeAugment *left_aug;
|
||||
guint n;
|
||||
@@ -220,18 +220,18 @@ tree_node_get_position (TreeNode *node)
|
||||
{
|
||||
tree = node->parent->children;
|
||||
|
||||
left = gtk_css_rb_tree_get_left (tree, node);
|
||||
left = gtk_rb_tree_get_left (tree, node);
|
||||
if (left)
|
||||
{
|
||||
left_aug = gtk_css_rb_tree_get_augment (tree, left);
|
||||
left_aug = gtk_rb_tree_get_augment (tree, left);
|
||||
n += left_aug->n_items;
|
||||
}
|
||||
|
||||
for (parent = gtk_css_rb_tree_get_parent (tree, node);
|
||||
for (parent = gtk_rb_tree_get_parent (tree, node);
|
||||
parent;
|
||||
parent = gtk_css_rb_tree_get_parent (tree, node))
|
||||
parent = gtk_rb_tree_get_parent (tree, node))
|
||||
{
|
||||
left = gtk_css_rb_tree_get_left (tree, parent);
|
||||
left = gtk_rb_tree_get_left (tree, parent);
|
||||
if (left == node)
|
||||
{
|
||||
/* we are the left node, nothing changes */
|
||||
@@ -242,7 +242,7 @@ tree_node_get_position (TreeNode *node)
|
||||
n += 1 + tree_node_get_n_children (parent);
|
||||
if (left)
|
||||
{
|
||||
left_aug = gtk_css_rb_tree_get_augment (tree, left);
|
||||
left_aug = gtk_rb_tree_get_augment (tree, left);
|
||||
n += left_aug->n_items;
|
||||
}
|
||||
}
|
||||
@@ -262,7 +262,7 @@ tree_node_mark_dirty (TreeNode *node)
|
||||
!node->is_root;
|
||||
node = node->parent)
|
||||
{
|
||||
gtk_css_rb_tree_mark_dirty (node->parent->children, node);
|
||||
gtk_rb_tree_mark_dirty (node->parent->children, node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,7 +270,7 @@ static TreeNode *
|
||||
gtk_tree_list_model_get_nth (GtkTreeListModel *self,
|
||||
guint position)
|
||||
{
|
||||
GtkCssRbTree *tree;
|
||||
GtkRbTree *tree;
|
||||
TreeNode *node, *tmp;
|
||||
guint n_children;
|
||||
|
||||
@@ -279,14 +279,14 @@ gtk_tree_list_model_get_nth (GtkTreeListModel *self,
|
||||
return NULL;
|
||||
|
||||
tree = self->root_node.children;
|
||||
node = gtk_css_rb_tree_get_root (tree);
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
tmp = gtk_css_rb_tree_get_left (tree, node);
|
||||
tmp = gtk_rb_tree_get_left (tree, node);
|
||||
if (tmp)
|
||||
{
|
||||
TreeAugment *aug = gtk_css_rb_tree_get_augment (tree, tmp);
|
||||
TreeAugment *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (position < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
@@ -304,12 +304,12 @@ gtk_tree_list_model_get_nth (GtkTreeListModel *self,
|
||||
if (position < n_children)
|
||||
{
|
||||
tree = node->children;
|
||||
node = gtk_css_rb_tree_get_root (tree);
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
continue;
|
||||
}
|
||||
position -= n_children;
|
||||
|
||||
node = gtk_css_rb_tree_get_right (tree, node);
|
||||
node = gtk_rb_tree_get_right (tree, node);
|
||||
}
|
||||
|
||||
g_return_val_if_reached (NULL);
|
||||
@@ -406,8 +406,8 @@ gtk_tree_list_model_items_changed_cb (GListModel *model,
|
||||
for (i = 0; i < removed; i++)
|
||||
{
|
||||
tmp = child;
|
||||
child = gtk_css_rb_tree_get_next (node->children, child);
|
||||
gtk_css_rb_tree_remove (node->children, tmp);
|
||||
child = gtk_rb_tree_get_next (node->children, child);
|
||||
gtk_rb_tree_remove (node->children, tmp);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -418,7 +418,7 @@ gtk_tree_list_model_items_changed_cb (GListModel *model,
|
||||
tree_added = added;
|
||||
for (i = 0; i < added; i++)
|
||||
{
|
||||
child = gtk_css_rb_tree_insert_before (node->children, child);
|
||||
child = gtk_rb_tree_insert_before (node->children, child);
|
||||
child->parent = node;
|
||||
}
|
||||
if (self->autoexpand)
|
||||
@@ -426,7 +426,7 @@ gtk_tree_list_model_items_changed_cb (GListModel *model,
|
||||
for (i = 0; i < added; i++)
|
||||
{
|
||||
tree_added += gtk_tree_list_model_expand_node (self, child);
|
||||
child = gtk_css_rb_tree_get_next (node->children, child);
|
||||
child = gtk_rb_tree_get_next (node->children, child);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,15 +456,15 @@ gtk_tree_list_model_clear_node (gpointer data)
|
||||
g_object_unref (node->model);
|
||||
}
|
||||
if (node->children)
|
||||
gtk_css_rb_tree_unref (node->children);
|
||||
gtk_rb_tree_unref (node->children);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_list_model_augment (GtkCssRbTree *tree,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
gtk_tree_list_model_augment (GtkRbTree *tree,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
gpointer right)
|
||||
{
|
||||
TreeAugment *aug = _aug;
|
||||
|
||||
@@ -474,13 +474,13 @@ gtk_tree_list_model_augment (GtkCssRbTree *tree,
|
||||
|
||||
if (left)
|
||||
{
|
||||
TreeAugment *left_aug = gtk_css_rb_tree_get_augment (tree, left);
|
||||
TreeAugment *left_aug = gtk_rb_tree_get_augment (tree, left);
|
||||
aug->n_items += left_aug->n_items;
|
||||
aug->n_local += left_aug->n_local;
|
||||
}
|
||||
if (right)
|
||||
{
|
||||
TreeAugment *right_aug = gtk_css_rb_tree_get_augment (tree, right);
|
||||
TreeAugment *right_aug = gtk_rb_tree_get_augment (tree, right);
|
||||
aug->n_items += right_aug->n_items;
|
||||
aug->n_local += right_aug->n_local;
|
||||
}
|
||||
@@ -499,7 +499,7 @@ gtk_tree_list_model_init_node (GtkTreeListModel *list,
|
||||
"items-changed",
|
||||
G_CALLBACK (gtk_tree_list_model_items_changed_cb),
|
||||
self);
|
||||
self->children = gtk_css_rb_tree_new (TreeNode,
|
||||
self->children = gtk_rb_tree_new (TreeNode,
|
||||
TreeAugment,
|
||||
gtk_tree_list_model_augment,
|
||||
gtk_tree_list_model_clear_node,
|
||||
@@ -509,7 +509,7 @@ gtk_tree_list_model_init_node (GtkTreeListModel *list,
|
||||
node = NULL;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
node = gtk_css_rb_tree_insert_after (self->children, node);
|
||||
node = gtk_rb_tree_insert_after (self->children, node);
|
||||
node->parent = self;
|
||||
if (list->autoexpand)
|
||||
gtk_tree_list_model_expand_node (list, node);
|
||||
@@ -561,7 +561,7 @@ gtk_tree_list_model_collapse_node (GtkTreeListModel *self,
|
||||
|
||||
n_items = tree_node_get_n_children (node);
|
||||
|
||||
g_clear_pointer (&node->children, gtk_css_rb_tree_unref);
|
||||
g_clear_pointer (&node->children, gtk_rb_tree_unref);
|
||||
g_clear_object (&node->model);
|
||||
|
||||
tree_node_mark_dirty (node);
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <gtk/gtktreeview.h>
|
||||
#include <gtk/gtktreeselection.h>
|
||||
#include <gtk/gtkrbtreeprivate.h>
|
||||
#include <gtk/gtktreerbtreeprivate.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -36,21 +36,21 @@ GtkTreeSelectMode;
|
||||
|
||||
/* functions that shouldn't be exported */
|
||||
void _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
|
||||
GtkRBNode *node,
|
||||
GtkRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreePath *path,
|
||||
GtkTreeSelectMode mode,
|
||||
gboolean override_browse_mode);
|
||||
void _gtk_tree_selection_emit_changed (GtkTreeSelection *selection);
|
||||
gboolean _gtk_tree_view_find_node (GtkTreeView *tree_view,
|
||||
GtkTreePath *path,
|
||||
GtkRBTree **tree,
|
||||
GtkRBNode **node);
|
||||
GtkTreeRBTree **tree,
|
||||
GtkTreeRBNode **node);
|
||||
gboolean _gtk_tree_view_get_cursor_node (GtkTreeView *tree_view,
|
||||
GtkRBTree **tree,
|
||||
GtkRBNode **node);
|
||||
GtkTreePath *_gtk_tree_path_new_from_rbtree (GtkRBTree *tree,
|
||||
GtkRBNode *node);
|
||||
GtkTreeRBTree **tree,
|
||||
GtkTreeRBNode **node);
|
||||
GtkTreePath *_gtk_tree_path_new_from_rbtree (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
|
||||
void _gtk_tree_view_add_editable (GtkTreeView *tree_view,
|
||||
GtkTreeViewColumn *column,
|
||||
@@ -72,7 +72,7 @@ void _gtk_tree_view_get_row_separator_func (GtkTreeView
|
||||
GtkTreePath *_gtk_tree_view_get_anchor_path (GtkTreeView *tree_view);
|
||||
void _gtk_tree_view_set_anchor_path (GtkTreeView *tree_view,
|
||||
GtkTreePath *anchor_path);
|
||||
GtkRBTree * _gtk_tree_view_get_rbtree (GtkTreeView *tree_view);
|
||||
GtkTreeRBTree * _gtk_tree_view_get_rbtree (GtkTreeView *tree_view);
|
||||
|
||||
GtkTreeViewColumn *_gtk_tree_view_get_focus_column (GtkTreeView *tree_view);
|
||||
void _gtk_tree_view_set_focus_column (GtkTreeView *tree_view,
|
||||
@@ -83,7 +83,7 @@ GtkTreeSelection* _gtk_tree_selection_new_with_tree_view (GtkTreeView *tree
|
||||
void _gtk_tree_selection_set_tree_view (GtkTreeSelection *selection,
|
||||
GtkTreeView *tree_view);
|
||||
gboolean _gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreePath *path);
|
||||
|
||||
|
||||
|
||||
1738
gtk/gtktreerbtree.c
Normal file
1738
gtk/gtktreerbtree.c
Normal file
File diff suppressed because it is too large
Load Diff
172
gtk/gtktreerbtreeprivate.h
Normal file
172
gtk/gtktreerbtreeprivate.h
Normal file
@@ -0,0 +1,172 @@
|
||||
/* gtkrbtreeprivate.h
|
||||
* Copyright (C) 2000 Red Hat, Inc., Jonathan Blandford <jrb@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* A Red-Black Tree implementation used specifically by GtkTreeView.
|
||||
*/
|
||||
#ifndef __GTK_TREE_RBTREE_PRIVATE_H__
|
||||
#define __GTK_TREE_RBTREE_PRIVATE_H__
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
|
||||
typedef enum
|
||||
{
|
||||
GTK_TREE_RBNODE_BLACK = 1 << 0,
|
||||
GTK_TREE_RBNODE_RED = 1 << 1,
|
||||
GTK_TREE_RBNODE_IS_PARENT = 1 << 2,
|
||||
GTK_TREE_RBNODE_IS_SELECTED = 1 << 3,
|
||||
GTK_TREE_RBNODE_IS_PRELIT = 1 << 4,
|
||||
GTK_TREE_RBNODE_INVALID = 1 << 7,
|
||||
GTK_TREE_RBNODE_COLUMN_INVALID = 1 << 8,
|
||||
GTK_TREE_RBNODE_DESCENDANTS_INVALID = 1 << 9,
|
||||
GTK_TREE_RBNODE_NON_COLORS = GTK_TREE_RBNODE_IS_PARENT |
|
||||
GTK_TREE_RBNODE_IS_SELECTED |
|
||||
GTK_TREE_RBNODE_IS_PRELIT |
|
||||
GTK_TREE_RBNODE_INVALID |
|
||||
GTK_TREE_RBNODE_COLUMN_INVALID |
|
||||
GTK_TREE_RBNODE_DESCENDANTS_INVALID
|
||||
} GtkTreeRBNodeColor;
|
||||
|
||||
typedef struct _GtkTreeRBTree GtkTreeRBTree;
|
||||
typedef struct _GtkTreeRBNode GtkTreeRBNode;
|
||||
typedef struct _GtkTreeRBTreeView GtkTreeRBTreeView;
|
||||
|
||||
typedef void (*GtkTreeRBTreeTraverseFunc) (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gpointer data);
|
||||
|
||||
struct _GtkTreeRBTree
|
||||
{
|
||||
GtkTreeRBNode *root;
|
||||
GtkTreeRBTree *parent_tree;
|
||||
GtkTreeRBNode *parent_node;
|
||||
};
|
||||
|
||||
struct _GtkTreeRBNode
|
||||
{
|
||||
guint flags : 14;
|
||||
|
||||
/* count is the number of nodes beneath us, plus 1 for ourselves.
|
||||
* i.e. node->left->count + node->right->count + 1
|
||||
*/
|
||||
gint count;
|
||||
|
||||
GtkTreeRBNode *left;
|
||||
GtkTreeRBNode *right;
|
||||
GtkTreeRBNode *parent;
|
||||
|
||||
/* count the number of total nodes beneath us, including nodes
|
||||
* of children trees.
|
||||
* i.e. node->left->count + node->right->count + node->children->root->count + 1
|
||||
*/
|
||||
guint total_count;
|
||||
|
||||
/* this is the total of sizes of
|
||||
* node->left, node->right, our own height, and the height
|
||||
* of all trees in ->children, iff children exists because
|
||||
* the thing is expanded.
|
||||
*/
|
||||
gint offset;
|
||||
|
||||
/* Child trees */
|
||||
GtkTreeRBTree *children;
|
||||
};
|
||||
|
||||
|
||||
#define GTK_TREE_RBNODE_GET_COLOR(node) (node?(((node->flags>K_TREE_RBNODE_RED)==GTK_TREE_RBNODE_RED)?GTK_TREE_RBNODE_RED:GTK_TREE_RBNODE_BLACK):GTK_TREE_RBNODE_BLACK)
|
||||
#define GTK_TREE_RBNODE_SET_COLOR(node,color) if((node->flags&color)!=color)node->flags=node->flags^(GTK_TREE_RBNODE_RED|GTK_TREE_RBNODE_BLACK)
|
||||
#define GTK_TREE_RBNODE_GET_HEIGHT(node) (node->offset-(node->left->offset+node->right->offset+(node->children?node->children->root->offset:0)))
|
||||
#define GTK_TREE_RBNODE_SET_FLAG(node, flag) G_STMT_START{ (node->flags|=flag); }G_STMT_END
|
||||
#define GTK_TREE_RBNODE_UNSET_FLAG(node, flag) G_STMT_START{ (node->flags&=~(flag)); }G_STMT_END
|
||||
#define GTK_TREE_RBNODE_FLAG_SET(node, flag) (node?(((node->flags&flag)==flag)?TRUE:FALSE):FALSE)
|
||||
|
||||
|
||||
GtkTreeRBTree *_gtk_tree_rbtree_new (void);
|
||||
void _gtk_tree_rbtree_free (GtkTreeRBTree *tree);
|
||||
void _gtk_tree_rbtree_remove (GtkTreeRBTree *tree);
|
||||
void _gtk_tree_rbtree_destroy (GtkTreeRBTree *tree);
|
||||
GtkTreeRBNode *_gtk_tree_rbtree_insert_before (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gint height,
|
||||
gboolean valid);
|
||||
GtkTreeRBNode *_gtk_tree_rbtree_insert_after (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gint height,
|
||||
gboolean valid);
|
||||
void _gtk_tree_rbtree_remove_node (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
gboolean _gtk_tree_rbtree_is_nil (GtkTreeRBNode *node);
|
||||
void _gtk_tree_rbtree_reorder (GtkTreeRBTree *tree,
|
||||
gint *new_order,
|
||||
gint length);
|
||||
gboolean _gtk_tree_rbtree_contains (GtkTreeRBTree *tree,
|
||||
GtkTreeRBTree *potential_child);
|
||||
GtkTreeRBNode *_gtk_tree_rbtree_find_count (GtkTreeRBTree *tree,
|
||||
gint count);
|
||||
void _gtk_tree_rbtree_node_set_height (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gint height);
|
||||
void _gtk_tree_rbtree_node_mark_invalid(GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
void _gtk_tree_rbtree_node_mark_valid (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
void _gtk_tree_rbtree_column_invalid (GtkTreeRBTree *tree);
|
||||
void _gtk_tree_rbtree_mark_invalid (GtkTreeRBTree *tree);
|
||||
void _gtk_tree_rbtree_set_fixed_height (GtkTreeRBTree *tree,
|
||||
gint height,
|
||||
gboolean mark_valid);
|
||||
gint _gtk_tree_rbtree_node_find_offset (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
guint _gtk_tree_rbtree_node_get_index (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
gboolean _gtk_tree_rbtree_find_index (GtkTreeRBTree *tree,
|
||||
guint index,
|
||||
GtkTreeRBTree **new_tree,
|
||||
GtkTreeRBNode **new_node);
|
||||
gint _gtk_tree_rbtree_find_offset (GtkTreeRBTree *tree,
|
||||
gint offset,
|
||||
GtkTreeRBTree **new_tree,
|
||||
GtkTreeRBNode **new_node);
|
||||
void _gtk_tree_rbtree_traverse (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GTraverseType order,
|
||||
GtkTreeRBTreeTraverseFunc func,
|
||||
gpointer data);
|
||||
GtkTreeRBNode *_gtk_tree_rbtree_first (GtkTreeRBTree *tree);
|
||||
GtkTreeRBNode *_gtk_tree_rbtree_next (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
GtkTreeRBNode *_gtk_tree_rbtree_prev (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node);
|
||||
void _gtk_tree_rbtree_next_full (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreeRBTree **new_tree,
|
||||
GtkTreeRBNode **new_node);
|
||||
void _gtk_tree_rbtree_prev_full (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreeRBTree **new_tree,
|
||||
GtkTreeRBNode **new_node);
|
||||
|
||||
gint _gtk_tree_rbtree_get_depth (GtkTreeRBTree *tree);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#endif /* __GTK_TREE_RBTREE_PRIVATE_H__ */
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <string.h>
|
||||
#include "gtktreeselection.h"
|
||||
#include "gtktreeprivate.h"
|
||||
#include "gtkrbtreeprivate.h"
|
||||
#include "gtktreerbtreeprivate.h"
|
||||
#include "gtkmarshalers.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkprivate.h"
|
||||
@@ -74,8 +74,8 @@ static void gtk_tree_selection_finalize (GObject *object)
|
||||
static gint gtk_tree_selection_real_select_all (GtkTreeSelection *selection);
|
||||
static gint gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection);
|
||||
static gint gtk_tree_selection_real_select_node (GtkTreeSelection *selection,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gboolean select);
|
||||
static void gtk_tree_selection_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
@@ -313,8 +313,8 @@ gtk_tree_selection_set_mode (GtkTreeSelection *selection,
|
||||
else if (type == GTK_SELECTION_SINGLE ||
|
||||
type == GTK_SELECTION_BROWSE)
|
||||
{
|
||||
GtkRBTree *tree = NULL;
|
||||
GtkRBNode *node = NULL;
|
||||
GtkTreeRBTree *tree = NULL;
|
||||
GtkTreeRBNode *node = NULL;
|
||||
gint selected = FALSE;
|
||||
GtkTreePath *anchor_path = NULL;
|
||||
|
||||
@@ -327,7 +327,7 @@ gtk_tree_selection_set_mode (GtkTreeSelection *selection,
|
||||
&tree,
|
||||
&node);
|
||||
|
||||
if (node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
selected = TRUE;
|
||||
}
|
||||
|
||||
@@ -473,8 +473,8 @@ gtk_tree_selection_get_selected (GtkTreeSelection *selection,
|
||||
GtkTreeIter *iter)
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
GtkTreePath *anchor_path;
|
||||
gboolean retval = FALSE;
|
||||
gboolean found_node;
|
||||
@@ -503,7 +503,7 @@ gtk_tree_selection_get_selected (GtkTreeSelection *selection,
|
||||
&tree,
|
||||
&node);
|
||||
|
||||
if (found_node && GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (found_node && GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
{
|
||||
/* we only want to return the anchor if it exists in the rbtree and
|
||||
* is selected.
|
||||
@@ -550,8 +550,8 @@ gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv;
|
||||
GList *list = NULL;
|
||||
GtkRBTree *tree = NULL;
|
||||
GtkRBNode *node = NULL;
|
||||
GtkTreeRBTree *tree = NULL;
|
||||
GtkTreeRBNode *node = NULL;
|
||||
GtkTreePath *path;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), NULL);
|
||||
@@ -585,18 +585,18 @@ gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
node = _gtk_rbtree_first (tree);
|
||||
node = _gtk_tree_rbtree_first (tree);
|
||||
path = gtk_tree_path_new_first ();
|
||||
|
||||
while (node != NULL)
|
||||
{
|
||||
if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
list = g_list_prepend (list, gtk_tree_path_copy (path));
|
||||
|
||||
if (node->children)
|
||||
{
|
||||
tree = node->children;
|
||||
node = _gtk_rbtree_first (tree);
|
||||
node = _gtk_tree_rbtree_first (tree);
|
||||
|
||||
gtk_tree_path_append_index (path, 0);
|
||||
}
|
||||
@@ -606,7 +606,7 @@ gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
|
||||
|
||||
do
|
||||
{
|
||||
node = _gtk_rbtree_next (tree, node);
|
||||
node = _gtk_tree_rbtree_next (tree, node);
|
||||
if (node != NULL)
|
||||
{
|
||||
done = TRUE;
|
||||
@@ -638,19 +638,19 @@ gtk_tree_selection_get_selected_rows (GtkTreeSelection *selection,
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_tree_selection_count_selected_rows_helper (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
gtk_tree_selection_count_selected_rows_helper (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gpointer data)
|
||||
{
|
||||
gint *count = (gint *)data;
|
||||
|
||||
g_return_if_fail (node != NULL);
|
||||
|
||||
if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
(*count)++;
|
||||
|
||||
if (node->children)
|
||||
_gtk_rbtree_traverse (node->children, node->children->root,
|
||||
_gtk_tree_rbtree_traverse (node->children, node->children->root,
|
||||
G_PRE_ORDER,
|
||||
gtk_tree_selection_count_selected_rows_helper, data);
|
||||
}
|
||||
@@ -668,7 +668,7 @@ gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv;
|
||||
gint count = 0;
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBTree *tree;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), 0);
|
||||
|
||||
@@ -690,7 +690,7 @@ gtk_tree_selection_count_selected_rows (GtkTreeSelection *selection)
|
||||
return 0;
|
||||
}
|
||||
|
||||
_gtk_rbtree_traverse (tree, tree->root,
|
||||
_gtk_tree_rbtree_traverse (tree, tree->root,
|
||||
G_PRE_ORDER,
|
||||
gtk_tree_selection_count_selected_rows_helper,
|
||||
&count);
|
||||
@@ -724,8 +724,8 @@ gtk_tree_selection_selected_foreach (GtkTreeSelection *selection,
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv;
|
||||
GtkTreePath *path;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeModel *model;
|
||||
|
||||
@@ -759,7 +759,7 @@ gtk_tree_selection_selected_foreach (GtkTreeSelection *selection,
|
||||
return;
|
||||
}
|
||||
|
||||
node = _gtk_rbtree_first (tree);
|
||||
node = _gtk_tree_rbtree_first (tree);
|
||||
|
||||
g_object_ref (model);
|
||||
|
||||
@@ -782,7 +782,7 @@ gtk_tree_selection_selected_foreach (GtkTreeSelection *selection,
|
||||
|
||||
while (node != NULL)
|
||||
{
|
||||
if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
{
|
||||
gtk_tree_model_get_iter (model, &iter, path);
|
||||
(* func) (model, path, &iter, data);
|
||||
@@ -794,7 +794,7 @@ gtk_tree_selection_selected_foreach (GtkTreeSelection *selection,
|
||||
if (node->children)
|
||||
{
|
||||
tree = node->children;
|
||||
node = _gtk_rbtree_first (tree);
|
||||
node = _gtk_tree_rbtree_first (tree);
|
||||
|
||||
gtk_tree_path_append_index (path, 0);
|
||||
}
|
||||
@@ -804,7 +804,7 @@ gtk_tree_selection_selected_foreach (GtkTreeSelection *selection,
|
||||
|
||||
do
|
||||
{
|
||||
node = _gtk_rbtree_next (tree, node);
|
||||
node = _gtk_tree_rbtree_next (tree, node);
|
||||
if (node != NULL)
|
||||
{
|
||||
done = TRUE;
|
||||
@@ -860,8 +860,8 @@ gtk_tree_selection_select_path (GtkTreeSelection *selection,
|
||||
GtkTreePath *path)
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv;
|
||||
GtkRBNode *node;
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
gboolean ret;
|
||||
GtkTreeSelectMode mode = 0;
|
||||
|
||||
@@ -877,7 +877,7 @@ gtk_tree_selection_select_path (GtkTreeSelection *selection,
|
||||
&tree,
|
||||
&node);
|
||||
|
||||
if (node == NULL || GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
|
||||
if (node == NULL || GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) ||
|
||||
ret == TRUE)
|
||||
return;
|
||||
|
||||
@@ -904,8 +904,8 @@ gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
|
||||
GtkTreePath *path)
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv;
|
||||
GtkRBNode *node;
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
gboolean ret;
|
||||
|
||||
g_return_if_fail (GTK_IS_TREE_SELECTION (selection));
|
||||
@@ -920,7 +920,7 @@ gtk_tree_selection_unselect_path (GtkTreeSelection *selection,
|
||||
&tree,
|
||||
&node);
|
||||
|
||||
if (node == NULL || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
|
||||
if (node == NULL || !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) ||
|
||||
ret == TRUE)
|
||||
return;
|
||||
|
||||
@@ -1016,8 +1016,8 @@ gtk_tree_selection_path_is_selected (GtkTreeSelection *selection,
|
||||
GtkTreePath *path)
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv;
|
||||
GtkRBNode *node;
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
gboolean ret;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_TREE_SELECTION (selection), FALSE);
|
||||
@@ -1035,7 +1035,7 @@ gtk_tree_selection_path_is_selected (GtkTreeSelection *selection,
|
||||
&tree,
|
||||
&node);
|
||||
|
||||
if ((node == NULL) || !GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) ||
|
||||
if ((node == NULL) || !GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) ||
|
||||
ret == TRUE)
|
||||
return FALSE;
|
||||
|
||||
@@ -1088,19 +1088,19 @@ struct _TempTuple {
|
||||
};
|
||||
|
||||
static void
|
||||
select_all_helper (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
select_all_helper (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gpointer data)
|
||||
{
|
||||
struct _TempTuple *tuple = data;
|
||||
|
||||
if (node->children)
|
||||
_gtk_rbtree_traverse (node->children,
|
||||
_gtk_tree_rbtree_traverse (node->children,
|
||||
node->children->root,
|
||||
G_PRE_ORDER,
|
||||
select_all_helper,
|
||||
data);
|
||||
if (!GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
{
|
||||
tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, TRUE) || tuple->dirty;
|
||||
}
|
||||
@@ -1115,7 +1115,7 @@ gtk_tree_selection_real_select_all (GtkTreeSelection *selection)
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv = selection->priv;
|
||||
struct _TempTuple *tuple;
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBTree *tree;
|
||||
|
||||
tree = _gtk_tree_view_get_rbtree (priv->tree_view);
|
||||
|
||||
@@ -1127,7 +1127,7 @@ gtk_tree_selection_real_select_all (GtkTreeSelection *selection)
|
||||
tuple->selection = selection;
|
||||
tuple->dirty = FALSE;
|
||||
|
||||
_gtk_rbtree_traverse (tree, tree->root,
|
||||
_gtk_tree_rbtree_traverse (tree, tree->root,
|
||||
G_PRE_ORDER,
|
||||
select_all_helper,
|
||||
tuple);
|
||||
@@ -1169,19 +1169,19 @@ gtk_tree_selection_select_all (GtkTreeSelection *selection)
|
||||
}
|
||||
|
||||
static void
|
||||
unselect_all_helper (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
unselect_all_helper (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gpointer data)
|
||||
{
|
||||
struct _TempTuple *tuple = data;
|
||||
|
||||
if (node->children)
|
||||
_gtk_rbtree_traverse (node->children,
|
||||
_gtk_tree_rbtree_traverse (node->children,
|
||||
node->children->root,
|
||||
G_PRE_ORDER,
|
||||
unselect_all_helper,
|
||||
data);
|
||||
if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
{
|
||||
tuple->dirty = gtk_tree_selection_real_select_node (tuple->selection, tree, node, FALSE) || tuple->dirty;
|
||||
}
|
||||
@@ -1196,8 +1196,8 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
|
||||
if (priv->type == GTK_SELECTION_SINGLE ||
|
||||
priv->type == GTK_SELECTION_BROWSE)
|
||||
{
|
||||
GtkRBTree *tree = NULL;
|
||||
GtkRBNode *node = NULL;
|
||||
GtkTreeRBTree *tree = NULL;
|
||||
GtkTreeRBNode *node = NULL;
|
||||
GtkTreePath *anchor_path;
|
||||
|
||||
anchor_path = _gtk_tree_view_get_anchor_path (priv->tree_view);
|
||||
@@ -1215,7 +1215,7 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
|
||||
if (tree == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
{
|
||||
if (gtk_tree_selection_real_select_node (selection, tree, node, FALSE))
|
||||
{
|
||||
@@ -1227,14 +1227,14 @@ gtk_tree_selection_real_unselect_all (GtkTreeSelection *selection)
|
||||
}
|
||||
else
|
||||
{
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBTree *tree;
|
||||
|
||||
tuple = g_new (struct _TempTuple, 1);
|
||||
tuple->selection = selection;
|
||||
tuple->dirty = FALSE;
|
||||
|
||||
tree = _gtk_tree_view_get_rbtree (priv->tree_view);
|
||||
_gtk_rbtree_traverse (tree, tree->root,
|
||||
_gtk_tree_rbtree_traverse (tree, tree->root,
|
||||
G_PRE_ORDER,
|
||||
unselect_all_helper,
|
||||
tuple);
|
||||
@@ -1287,8 +1287,8 @@ gtk_tree_selection_real_modify_range (GtkTreeSelection *selection,
|
||||
GtkTreePath *end_path)
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv = selection->priv;
|
||||
GtkRBNode *start_node = NULL, *end_node = NULL;
|
||||
GtkRBTree *start_tree, *end_tree;
|
||||
GtkTreeRBNode *start_node = NULL, *end_node = NULL;
|
||||
GtkTreeRBTree *start_tree, *end_tree;
|
||||
GtkTreePath *anchor_path = NULL;
|
||||
gboolean dirty = FALSE;
|
||||
|
||||
@@ -1347,11 +1347,11 @@ gtk_tree_selection_real_modify_range (GtkTreeSelection *selection,
|
||||
if (start_node->children)
|
||||
{
|
||||
start_tree = start_node->children;
|
||||
start_node = _gtk_rbtree_first (start_tree);
|
||||
start_node = _gtk_tree_rbtree_first (start_tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
_gtk_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
|
||||
_gtk_tree_rbtree_next_full (start_tree, start_node, &start_tree, &start_node);
|
||||
if (start_tree == NULL)
|
||||
{
|
||||
/* we just ran out of tree. That means someone passed in bogus values.
|
||||
@@ -1422,7 +1422,7 @@ gtk_tree_selection_unselect_range (GtkTreeSelection *selection,
|
||||
|
||||
gboolean
|
||||
_gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreePath *path)
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv = selection->priv;
|
||||
@@ -1449,7 +1449,7 @@ _gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection,
|
||||
|
||||
if (priv->user_func)
|
||||
return (*priv->user_func) (selection, model, path,
|
||||
GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED),
|
||||
GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED),
|
||||
priv->user_data);
|
||||
else
|
||||
return TRUE;
|
||||
@@ -1466,8 +1466,8 @@ _gtk_tree_selection_row_is_selectable (GtkTreeSelection *selection,
|
||||
*/
|
||||
void
|
||||
_gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
|
||||
GtkRBNode *node,
|
||||
GtkRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreePath *path,
|
||||
GtkTreeSelectMode mode,
|
||||
gboolean override_browse_mode)
|
||||
@@ -1556,7 +1556,7 @@ _gtk_tree_selection_internal_select_node (GtkTreeSelection *selection,
|
||||
|
||||
_gtk_tree_view_set_anchor_path (priv->tree_view, path);
|
||||
|
||||
if ((flags & GTK_RBNODE_IS_SELECTED) == GTK_RBNODE_IS_SELECTED)
|
||||
if ((flags & GTK_TREE_RBNODE_IS_SELECTED) == GTK_TREE_RBNODE_IS_SELECTED)
|
||||
dirty |= gtk_tree_selection_real_select_node (selection, tree, node, FALSE);
|
||||
else
|
||||
dirty |= gtk_tree_selection_real_select_node (selection, tree, node, TRUE);
|
||||
@@ -1598,8 +1598,8 @@ _gtk_tree_selection_emit_changed (GtkTreeSelection *selection)
|
||||
|
||||
static gint
|
||||
gtk_tree_selection_real_select_node (GtkTreeSelection *selection,
|
||||
GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gboolean select)
|
||||
{
|
||||
GtkTreeSelectionPrivate *priv = selection->priv;
|
||||
@@ -1610,7 +1610,7 @@ gtk_tree_selection_real_select_node (GtkTreeSelection *selection,
|
||||
|
||||
select = !! select;
|
||||
|
||||
if (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED) != select)
|
||||
if (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED) != select)
|
||||
{
|
||||
path = _gtk_tree_path_new_from_rbtree (tree, node);
|
||||
toggle = _gtk_tree_selection_row_is_selectable (selection, node, path);
|
||||
@@ -1619,14 +1619,14 @@ gtk_tree_selection_real_select_node (GtkTreeSelection *selection,
|
||||
|
||||
if (toggle)
|
||||
{
|
||||
if (!GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_IS_SELECTED))
|
||||
if (!GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_IS_SELECTED))
|
||||
{
|
||||
GTK_RBNODE_SET_FLAG (node, GTK_RBNODE_IS_SELECTED);
|
||||
GTK_TREE_RBNODE_SET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED);
|
||||
_gtk_tree_view_accessible_add_state (priv->tree_view, tree, node, GTK_CELL_RENDERER_SELECTED);
|
||||
}
|
||||
else
|
||||
{
|
||||
GTK_RBNODE_UNSET_FLAG (node, GTK_RBNODE_IS_SELECTED);
|
||||
GTK_TREE_RBNODE_UNSET_FLAG (node, GTK_TREE_RBNODE_IS_SELECTED);
|
||||
_gtk_tree_view_accessible_remove_state (priv->tree_view, tree, node, GTK_CELL_RENDERER_SELECTED);
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -6583,7 +6583,7 @@ gtk_widget_reposition_after (GtkWidget *widget,
|
||||
if (parent->priv->children_observer)
|
||||
{
|
||||
if (prev_previous)
|
||||
g_warning ("oops");
|
||||
gtk_list_list_model_item_moved (parent->priv->children_observer, widget, prev_previous);
|
||||
else
|
||||
gtk_list_list_model_item_added (parent->priv->children_observer, widget);
|
||||
}
|
||||
|
||||
@@ -79,7 +79,6 @@ gtk_private_sources = files([
|
||||
'gtkcssparser.c',
|
||||
'gtkcsspathnode.c',
|
||||
'gtkcsspositionvalue.c',
|
||||
'gtkcssrbtree.c',
|
||||
'gtkcssrepeatvalue.c',
|
||||
'gtkcssrgbavalue.c',
|
||||
'gtkcssselector.c',
|
||||
@@ -132,11 +131,13 @@ gtk_private_sources = files([
|
||||
'gtkprintutils.c',
|
||||
'gtkprivate.c',
|
||||
'gtkprogresstracker.c',
|
||||
'gtkrbtree.c',
|
||||
'gtkquery.c',
|
||||
'gtkscaler.c',
|
||||
'gtksearchengine.c',
|
||||
'gtksearchenginemodel.c',
|
||||
'gtksearchenginesimple.c',
|
||||
'gtkset.c',
|
||||
'gtksizerequestcache.c',
|
||||
'gtkstyleanimation.c',
|
||||
'gtkstylecascade.c',
|
||||
@@ -266,8 +267,12 @@ gtk_public_sources = files([
|
||||
'gtklevelbar.c',
|
||||
'gtklinkbutton.c',
|
||||
'gtklistbox.c',
|
||||
'gtklistitem.c',
|
||||
'gtklistitemfactory.c',
|
||||
'gtklistitemmanager.c',
|
||||
'gtklistlistmodel.c',
|
||||
'gtkliststore.c',
|
||||
'gtklistview.c',
|
||||
'gtklockbutton.c',
|
||||
'gtkmain.c',
|
||||
'gtkmaplistmodel.c',
|
||||
@@ -285,8 +290,10 @@ gtk_public_sources = files([
|
||||
'gtkmodelmenuitem.c',
|
||||
'gtkmodules.c',
|
||||
'gtkmountoperation.c',
|
||||
'gtkmultiselection.c',
|
||||
'gtknativedialog.c',
|
||||
'gtknomediafile.c',
|
||||
'gtknoselection.c',
|
||||
'gtknotebook.c',
|
||||
'gtkorientable.c',
|
||||
'gtkoverlay.c',
|
||||
@@ -307,7 +314,7 @@ gtk_public_sources = files([
|
||||
'gtkradiomenuitem.c',
|
||||
'gtkradiotoolbutton.c',
|
||||
'gtkrange.c',
|
||||
'gtkrbtree.c',
|
||||
'gtktreerbtree.c',
|
||||
'gtkrecentmanager.c',
|
||||
'gtkrender.c',
|
||||
'gtkrenderbackground.c',
|
||||
@@ -324,6 +331,7 @@ gtk_public_sources = files([
|
||||
'gtksearchbar.c',
|
||||
'gtksearchentry.c',
|
||||
'gtkselection.c',
|
||||
'gtkselectionmodel.c',
|
||||
'gtkseparator.c',
|
||||
'gtkseparatormenuitem.c',
|
||||
'gtkseparatortoolitem.c',
|
||||
@@ -335,6 +343,7 @@ gtk_public_sources = files([
|
||||
'gtkshortcutswindow.c',
|
||||
'gtkshow.c',
|
||||
'gtksidebarrow.c',
|
||||
'gtksingleselection.c',
|
||||
'gtksizegroup.c',
|
||||
'gtksizerequest.c',
|
||||
'gtkslicelistmodel.c',
|
||||
@@ -513,7 +522,9 @@ gtk_public_headers = files([
|
||||
'gtklevelbar.h',
|
||||
'gtklinkbutton.h',
|
||||
'gtklistbox.h',
|
||||
'gtklistitem.h',
|
||||
'gtkliststore.h',
|
||||
'gtklistview.h',
|
||||
'gtklockbutton.h',
|
||||
'gtkmain.h',
|
||||
'gtkmaplistmodel.h',
|
||||
@@ -527,10 +538,12 @@ gtk_public_headers = files([
|
||||
'gtkmenushell.h',
|
||||
'gtkmenutoolbutton.h',
|
||||
'gtkmessagedialog.h',
|
||||
'gtkmultiselection.h',
|
||||
'gtkmodelbutton.h',
|
||||
'gtkmountoperation.h',
|
||||
'gtknativedialog.h',
|
||||
'gtknotebook.h',
|
||||
'gtknoselection.h',
|
||||
'gtkorientable.h',
|
||||
'gtkoverlay.h',
|
||||
'gtkpadcontroller.h',
|
||||
@@ -560,6 +573,7 @@ gtk_public_headers = files([
|
||||
'gtksearchbar.h',
|
||||
'gtksearchentry.h',
|
||||
'gtkselection.h',
|
||||
'gtkselectionmodel.h',
|
||||
'gtkseparator.h',
|
||||
'gtkseparatormenuitem.h',
|
||||
'gtkseparatortoolitem.h',
|
||||
@@ -570,6 +584,7 @@ gtk_public_headers = files([
|
||||
'gtkshortcutsshortcut.h',
|
||||
'gtkshortcutswindow.h',
|
||||
'gtkshow.h',
|
||||
'gtksingleselection.h',
|
||||
'gtksizegroup.h',
|
||||
'gtksizerequest.h',
|
||||
'gtkslicelistmodel.h',
|
||||
|
||||
@@ -1,23 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface domain="gtk40">
|
||||
<!-- interface-requires gtk+ 3.10 -->
|
||||
<object class="GtkListStore" id="model">
|
||||
<columns>
|
||||
<!-- column-name family -->
|
||||
<column type="PangoFontFamily"/>
|
||||
<!-- column-name face -->
|
||||
<column type="PangoFontFace"/>
|
||||
<!-- column-name description -->
|
||||
<column type="GtkDelayedFontDescription"/>
|
||||
<!-- column-name preview-title -->
|
||||
<column type="gchararray"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkTreeModelFilter" id="filter_model">
|
||||
<property name="child-model">model</property>
|
||||
<signal name="row-deleted" handler="rows_changed_cb" swapped="yes"/>
|
||||
<signal name="row-inserted" handler="rows_changed_cb" swapped="yes"/>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="slider_adjustment">
|
||||
<property name="upper">100</property>
|
||||
<property name="step-increment">1</property>
|
||||
@@ -63,7 +46,7 @@
|
||||
<property name="row-spacing">6</property>
|
||||
<property name="column-spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow" id="list_scrolled_window">
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="width-request">400</property>
|
||||
<property name="height-request">300</property>
|
||||
<property name="can-focus">1</property>
|
||||
@@ -72,38 +55,13 @@
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<property name="shadow-type">etched-in</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="family_face_list">
|
||||
<property name="can-focus">1</property>
|
||||
<property name="model">filter_model</property>
|
||||
<property name="headers-visible">0</property>
|
||||
<property name="enable-search">0</property>
|
||||
<property name="fixed-height-mode">1</property>
|
||||
<signal name="cursor-changed" handler="cursor_changed_cb" swapped="no"/>
|
||||
<signal name="row-activated" handler="row_activated_cb" swapped="no"/>
|
||||
<signal name="style-updated" handler="gtk_font_chooser_widget_set_cell_size" object="GtkFontChooserWidget" after="yes" swapped="yes"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection1">
|
||||
<property name="mode">browse</property>
|
||||
<signal name="changed" handler="selection_changed"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="family_face_column">
|
||||
<property name="sizing">fixed</property>
|
||||
<property name="title" translatable="yes">Font Family</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="family_face_cell">
|
||||
<property name="ellipsize">end</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<object class="GtkListView" id="family_face_listview">
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
<packing>
|
||||
<property name="left-attach">0</property>
|
||||
<property name="top-attach">1</property>
|
||||
<property name="top-attach">0</property>
|
||||
<property name="width">3</property>
|
||||
</packing>
|
||||
</child>
|
||||
|
||||
@@ -62,6 +62,8 @@ gtk_tests = [
|
||||
['testlist2'],
|
||||
['testlist3'],
|
||||
['testlist4'],
|
||||
['testlistview'],
|
||||
['testlistview-animating'],
|
||||
['testlevelbar'],
|
||||
['testlockbutton'],
|
||||
['testmenubutton'],
|
||||
|
||||
192
tests/testlistview-animating.c
Normal file
192
tests/testlistview-animating.c
Normal file
@@ -0,0 +1,192 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#ifdef SMALL
|
||||
#define AVERAGE 15
|
||||
#define VARIANCE 10
|
||||
#else
|
||||
#define AVERAGE 300
|
||||
#define VARIANCE 200
|
||||
#endif
|
||||
|
||||
static void
|
||||
update_label (GtkListItem *list_item,
|
||||
GParamSpec *pspec,
|
||||
GtkLabel *label)
|
||||
{
|
||||
gpointer item;
|
||||
char *s;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
|
||||
if (item)
|
||||
s = g_strdup_printf ("%u: %s",
|
||||
gtk_list_item_get_position (list_item),
|
||||
(const char *) g_object_get_data (item, "message"));
|
||||
else
|
||||
s = NULL;
|
||||
|
||||
gtk_label_set_text (label, s);
|
||||
|
||||
g_free (s);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_list_item (GtkListItem *list_item,
|
||||
gpointer unused)
|
||||
{
|
||||
GtkWidget *label = gtk_label_new ("");
|
||||
|
||||
g_signal_connect (list_item, "notify", G_CALLBACK (update_label), label);
|
||||
gtk_container_add (GTK_CONTAINER (list_item), label);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
create_widget_for_listbox (gpointer item,
|
||||
gpointer unused)
|
||||
{
|
||||
const char *message = g_object_get_data (item, "message");
|
||||
GtkWidget *widget;
|
||||
|
||||
widget = gtk_label_new (message);
|
||||
|
||||
return widget;
|
||||
}
|
||||
|
||||
static gboolean reverse_sort;
|
||||
|
||||
static int
|
||||
compare (gconstpointer first,
|
||||
gconstpointer second,
|
||||
gpointer unused)
|
||||
{
|
||||
int diff = (GPOINTER_TO_UINT (g_object_get_data ((gpointer) first, "counter")) % 1000)
|
||||
- (GPOINTER_TO_UINT (g_object_get_data ((gpointer) second, "counter")) % 1000);
|
||||
|
||||
if (reverse_sort)
|
||||
return -diff;
|
||||
else
|
||||
return diff;
|
||||
}
|
||||
|
||||
static void
|
||||
add (GListStore *store)
|
||||
{
|
||||
static guint counter;
|
||||
GObject *o;
|
||||
char *message;
|
||||
guint pos;
|
||||
|
||||
counter++;
|
||||
o = g_object_new (G_TYPE_OBJECT, NULL);
|
||||
g_object_set_data (o, "counter", GUINT_TO_POINTER (counter));
|
||||
message = g_strdup_printf ("Item %u", counter);
|
||||
g_object_set_data_full (o, "message", message, g_free);
|
||||
|
||||
pos = g_random_int_range (0, g_list_model_get_n_items (G_LIST_MODEL (store)) + 1);
|
||||
g_list_store_insert (store, pos, o);
|
||||
g_object_unref (o);
|
||||
}
|
||||
|
||||
static void
|
||||
delete (GListStore *store)
|
||||
{
|
||||
guint pos;
|
||||
|
||||
pos = g_random_int_range (0, g_list_model_get_n_items (G_LIST_MODEL (store)));
|
||||
g_list_store_remove (store, pos);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
do_stuff (gpointer store)
|
||||
{
|
||||
if (g_random_int_range (AVERAGE - VARIANCE, AVERAGE + VARIANCE) < g_list_model_get_n_items (store))
|
||||
delete (store);
|
||||
else
|
||||
add (store);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
revert_sort (gpointer sort)
|
||||
{
|
||||
reverse_sort = !reverse_sort;
|
||||
|
||||
gtk_sort_list_model_resort (sort);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *win, *hbox, *vbox, *sw, *listview, *listbox, *label;
|
||||
GListStore *store;
|
||||
GtkSortListModel *sort;
|
||||
guint i;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
store = g_list_store_new (G_TYPE_OBJECT);
|
||||
for (i = 0; i < AVERAGE; i++)
|
||||
add (store);
|
||||
sort = gtk_sort_list_model_new (G_LIST_MODEL (store),
|
||||
compare,
|
||||
NULL, NULL);
|
||||
|
||||
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 400, 600);
|
||||
g_signal_connect (win, "destroy", G_CALLBACK (gtk_main_quit), win);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
||||
gtk_container_add (GTK_CONTAINER (win), hbox);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), vbox);
|
||||
|
||||
label = gtk_label_new ("GtkListView");
|
||||
gtk_container_add (GTK_CONTAINER (vbox), label);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_set_hexpand (sw, TRUE);
|
||||
gtk_widget_set_vexpand (sw, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), sw);
|
||||
|
||||
listview = gtk_list_view_new ();
|
||||
gtk_list_view_set_functions (GTK_LIST_VIEW (listview),
|
||||
setup_list_item,
|
||||
NULL,
|
||||
NULL, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (sw), listview);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
|
||||
gtk_container_add (GTK_CONTAINER (hbox), vbox);
|
||||
|
||||
label = gtk_label_new ("GtkListBox");
|
||||
gtk_container_add (GTK_CONTAINER (vbox), label);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_set_hexpand (sw, TRUE);
|
||||
gtk_widget_set_vexpand (sw, TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), sw);
|
||||
|
||||
listbox = gtk_list_box_new ();
|
||||
gtk_container_add (GTK_CONTAINER (sw), listbox);
|
||||
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (sort));
|
||||
gtk_list_box_bind_model (GTK_LIST_BOX (listbox),
|
||||
G_LIST_MODEL (sort),
|
||||
create_widget_for_listbox,
|
||||
NULL, NULL);
|
||||
|
||||
g_timeout_add (100, do_stuff, store);
|
||||
g_timeout_add_seconds (3, revert_sort, sort);
|
||||
|
||||
gtk_widget_show (win);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
g_object_unref (store);
|
||||
|
||||
return 0;
|
||||
}
|
||||
493
tests/testlistview.c
Normal file
493
tests/testlistview.c
Normal file
@@ -0,0 +1,493 @@
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define ROWS 30
|
||||
|
||||
GSList *pending = NULL;
|
||||
guint active = 0;
|
||||
|
||||
static void
|
||||
got_files (GObject *enumerate,
|
||||
GAsyncResult *res,
|
||||
gpointer store);
|
||||
|
||||
static gboolean
|
||||
start_enumerate (GListStore *store)
|
||||
{
|
||||
GFileEnumerator *enumerate;
|
||||
GFile *file = g_object_get_data (G_OBJECT (store), "file");
|
||||
GError *error = NULL;
|
||||
|
||||
enumerate = g_file_enumerate_children (file,
|
||||
G_FILE_ATTRIBUTE_STANDARD_TYPE
|
||||
"," G_FILE_ATTRIBUTE_STANDARD_ICON
|
||||
"," G_FILE_ATTRIBUTE_STANDARD_NAME
|
||||
"," G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
|
||||
0,
|
||||
NULL,
|
||||
&error);
|
||||
|
||||
if (enumerate == NULL)
|
||||
{
|
||||
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TOO_MANY_OPEN_FILES) && active)
|
||||
{
|
||||
g_clear_error (&error);
|
||||
pending = g_slist_prepend (pending, g_object_ref (store));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
g_clear_error (&error);
|
||||
g_object_unref (store);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (active > 20)
|
||||
{
|
||||
g_object_unref (enumerate);
|
||||
pending = g_slist_prepend (pending, g_object_ref (store));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
active++;
|
||||
g_file_enumerator_next_files_async (enumerate,
|
||||
g_file_is_native (file) ? 5000 : 100,
|
||||
G_PRIORITY_DEFAULT_IDLE,
|
||||
NULL,
|
||||
got_files,
|
||||
g_object_ref (store));
|
||||
|
||||
g_object_unref (enumerate);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
got_files (GObject *enumerate,
|
||||
GAsyncResult *res,
|
||||
gpointer store)
|
||||
{
|
||||
GList *l, *files;
|
||||
GFile *file = g_object_get_data (store, "file");
|
||||
GPtrArray *array;
|
||||
|
||||
files = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (enumerate), res, NULL);
|
||||
if (files == NULL)
|
||||
{
|
||||
g_object_unref (store);
|
||||
if (pending)
|
||||
{
|
||||
GListStore *store = pending->data;
|
||||
pending = g_slist_remove (pending, store);
|
||||
start_enumerate (store);
|
||||
}
|
||||
active--;
|
||||
return;
|
||||
}
|
||||
|
||||
array = g_ptr_array_new ();
|
||||
g_ptr_array_new_with_free_func (g_object_unref);
|
||||
for (l = files; l; l = l->next)
|
||||
{
|
||||
GFileInfo *info = l->data;
|
||||
GFile *child;
|
||||
|
||||
child = g_file_get_child (file, g_file_info_get_name (info));
|
||||
g_object_set_data_full (G_OBJECT (info), "file", child, g_object_unref);
|
||||
g_ptr_array_add (array, info);
|
||||
}
|
||||
g_list_free (files);
|
||||
|
||||
g_list_store_splice (store, g_list_model_get_n_items (store), 0, array->pdata, array->len);
|
||||
g_ptr_array_unref (array);
|
||||
|
||||
g_file_enumerator_next_files_async (G_FILE_ENUMERATOR (enumerate),
|
||||
g_file_is_native (file) ? 5000 : 100,
|
||||
G_PRIORITY_DEFAULT_IDLE,
|
||||
NULL,
|
||||
got_files,
|
||||
store);
|
||||
}
|
||||
|
||||
static int
|
||||
compare_files (gconstpointer first,
|
||||
gconstpointer second,
|
||||
gpointer unused)
|
||||
{
|
||||
GFile *first_file, *second_file;
|
||||
char *first_path, *second_path;
|
||||
int result;
|
||||
#if 0
|
||||
GFileType first_type, second_type;
|
||||
|
||||
/* This is a bit slow, because each g_file_query_file_type() does a stat() */
|
||||
first_type = g_file_query_file_type (G_FILE (first), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL);
|
||||
second_type = g_file_query_file_type (G_FILE (second), G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL);
|
||||
|
||||
if (first_type == G_FILE_TYPE_DIRECTORY && second_type != G_FILE_TYPE_DIRECTORY)
|
||||
return -1;
|
||||
if (first_type != G_FILE_TYPE_DIRECTORY && second_type == G_FILE_TYPE_DIRECTORY)
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
first_file = g_object_get_data (G_OBJECT (first), "file");
|
||||
second_file = g_object_get_data (G_OBJECT (second), "file");
|
||||
first_path = g_file_get_path (first_file);
|
||||
second_path = g_file_get_path (second_file);
|
||||
|
||||
result = strcasecmp (first_path, second_path);
|
||||
|
||||
g_free (first_path);
|
||||
g_free (second_path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_list_model_for_directory (gpointer file)
|
||||
{
|
||||
GListStore *store;
|
||||
|
||||
if (g_file_query_file_type (file, G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, NULL) != G_FILE_TYPE_DIRECTORY)
|
||||
return NULL;
|
||||
|
||||
store = g_list_store_new (G_TYPE_FILE_INFO);
|
||||
g_object_set_data_full (G_OBJECT (store), "file", g_object_ref (file), g_object_unref);
|
||||
|
||||
if (!start_enumerate (store))
|
||||
return NULL;
|
||||
|
||||
return G_LIST_MODEL (store);
|
||||
}
|
||||
|
||||
typedef struct _RowData RowData;
|
||||
struct _RowData
|
||||
{
|
||||
GtkWidget *depth_box;
|
||||
GtkWidget *expander;
|
||||
GtkWidget *icon;
|
||||
GtkWidget *name;
|
||||
|
||||
GtkTreeListRow *current_item;
|
||||
GBinding *expander_binding;
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
g_binding_unbind (data->expander_binding);
|
||||
|
||||
g_clear_object (&data->current_item);
|
||||
}
|
||||
|
||||
static void
|
||||
row_data_bind (RowData *data,
|
||||
GtkTreeListRow *item)
|
||||
{
|
||||
GFileInfo *info;
|
||||
GIcon *icon;
|
||||
guint depth;
|
||||
|
||||
row_data_unbind (data);
|
||||
|
||||
if (item == NULL)
|
||||
return;
|
||||
|
||||
data->current_item = g_object_ref (item);
|
||||
|
||||
depth = gtk_tree_list_row_get_depth (item);
|
||||
gtk_widget_set_size_request (data->depth_box, 16 * depth, 0);
|
||||
|
||||
gtk_widget_set_sensitive (data->expander, gtk_tree_list_row_is_expandable (item));
|
||||
data->expander_binding = g_object_bind_property (item, "expanded", data->expander, "active", G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
|
||||
|
||||
info = gtk_tree_list_row_get_item (item);
|
||||
|
||||
icon = g_file_info_get_icon (info);
|
||||
gtk_widget_set_visible (data->icon, icon != NULL);
|
||||
if (icon)
|
||||
gtk_image_set_from_gicon (GTK_IMAGE (data->icon), icon);
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 4);
|
||||
gtk_container_add (GTK_CONTAINER (list_item), box);
|
||||
|
||||
child = gtk_label_new (NULL);
|
||||
gtk_label_set_width_chars (GTK_LABEL (child), 5);
|
||||
gtk_label_set_xalign (GTK_LABEL (child), 1.0);
|
||||
g_object_bind_property (list_item, "position", child, "label", G_BINDING_SYNC_CREATE);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
|
||||
data->depth_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_container_add (GTK_CONTAINER (box), data->depth_box);
|
||||
|
||||
child = g_object_new (GTK_TYPE_BOX, "css-name", "expander", NULL);
|
||||
gtk_container_add (GTK_CONTAINER (box), child);
|
||||
data->expander = g_object_new (GTK_TYPE_TOGGLE_BUTTON, "css-name", "title", NULL);
|
||||
gtk_button_set_relief (GTK_BUTTON (data->expander), GTK_RELIEF_NONE);
|
||||
gtk_container_add (GTK_CONTAINER (child), data->expander);
|
||||
child = g_object_new (GTK_TYPE_SPINNER, "css-name", "arrow", NULL);
|
||||
gtk_container_add (GTK_CONTAINER (data->expander), child);
|
||||
|
||||
data->icon = gtk_image_new ();
|
||||
gtk_container_add (GTK_CONTAINER (box), data->icon);
|
||||
|
||||
data->name = gtk_label_new (NULL);
|
||||
gtk_container_add (GTK_CONTAINER (box), data->name);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
create_list_model_for_file_info (gpointer file_info,
|
||||
gpointer unused)
|
||||
{
|
||||
GFile *file = g_object_get_data (file_info, "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;
|
||||
}
|
||||
|
||||
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_object_get_data (G_OBJECT (info), "file");
|
||||
char *path;
|
||||
gboolean result;
|
||||
|
||||
path = g_file_get_path (file);
|
||||
|
||||
result = strstr (path, gtk_entry_get_text (GTK_ENTRY (search_entry))) != NULL;
|
||||
|
||||
g_object_unref (info);
|
||||
g_free (path);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean invert_sort;
|
||||
|
||||
static void
|
||||
toggle_sort (GtkButton *button, GtkSortListModel *sort)
|
||||
{
|
||||
invert_sort = !invert_sort;
|
||||
|
||||
gtk_button_set_icon_name (button, invert_sort ? "view-sort-descending" : "view-sort-ascending");
|
||||
|
||||
gtk_sort_list_model_resort (sort);
|
||||
}
|
||||
|
||||
static int
|
||||
sort_tree (gconstpointer a, gconstpointer b, gpointer data)
|
||||
{
|
||||
GtkTreeListRow *ra = (GtkTreeListRow *) a;
|
||||
GtkTreeListRow *rb = (GtkTreeListRow *) b;
|
||||
GtkTreeListRow *pa, *pb;
|
||||
guint da, db;
|
||||
int i;
|
||||
GFile *ia, *ib;
|
||||
int cmp = 0;
|
||||
|
||||
da = gtk_tree_list_row_get_depth (ra);
|
||||
db = gtk_tree_list_row_get_depth (rb);
|
||||
|
||||
if (da > db)
|
||||
for (i = 0; i < da - db; i++)
|
||||
{
|
||||
ra = gtk_tree_list_row_get_parent (ra);
|
||||
if (ra) g_object_unref (ra);
|
||||
}
|
||||
if (db > da)
|
||||
for (i = 0; i < db - da; i++)
|
||||
{
|
||||
rb = gtk_tree_list_row_get_parent (rb);
|
||||
if (rb) g_object_unref (rb);
|
||||
}
|
||||
|
||||
/* now ra and rb are ancestors of a and b at the same depth */
|
||||
|
||||
if (ra == rb)
|
||||
return da - db;
|
||||
|
||||
pa = ra;
|
||||
pb = rb;
|
||||
do {
|
||||
ra = pa;
|
||||
rb = pb;
|
||||
pa = gtk_tree_list_row_get_parent (ra);
|
||||
pb = gtk_tree_list_row_get_parent (rb);
|
||||
if (pa) g_object_unref (pa);
|
||||
if (pb) g_object_unref (pb);
|
||||
} while (pa != pb);
|
||||
|
||||
/* now ra and rb are ancestors of a and b that have a common parent */
|
||||
|
||||
ia = gtk_tree_list_row_get_item (ra);
|
||||
ib = gtk_tree_list_row_get_item (rb);
|
||||
|
||||
cmp = compare_files (ia, ib, NULL);
|
||||
|
||||
g_object_unref (ia);
|
||||
g_object_unref (ib);
|
||||
|
||||
if (invert_sort)
|
||||
cmp = -cmp;
|
||||
|
||||
return cmp;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GtkWidget *win, *vbox, *sw, *listview, *search_entry, *statusbar;
|
||||
GtkWidget *hbox, *button;
|
||||
GListModel *dirmodel;
|
||||
GtkTreeListModel *tree;
|
||||
GtkFilterListModel *filter;
|
||||
GtkSortListModel *sort;
|
||||
GtkSelectionModel *selection;
|
||||
GFile *root;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 400, 600);
|
||||
g_signal_connect (win, "destroy", G_CALLBACK (gtk_main_quit), win);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_container_add (GTK_CONTAINER (win), vbox);
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), hbox);
|
||||
|
||||
search_entry = gtk_search_entry_new ();
|
||||
gtk_container_add (GTK_CONTAINER (hbox), search_entry);
|
||||
gtk_widget_set_hexpand (search_entry, TRUE);
|
||||
button = gtk_button_new_from_icon_name ("view-sort-ascending");
|
||||
gtk_container_add (GTK_CONTAINER (hbox), button);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_widget_set_vexpand (sw, TRUE);
|
||||
gtk_search_entry_set_key_capture_widget (GTK_SEARCH_ENTRY (search_entry), sw);
|
||||
gtk_container_add (GTK_CONTAINER (vbox), sw);
|
||||
|
||||
listview = gtk_list_view_new ();
|
||||
|
||||
gtk_list_view_set_functions (GTK_LIST_VIEW (listview),
|
||||
setup_widget,
|
||||
NULL,
|
||||
NULL, NULL);
|
||||
gtk_container_add (GTK_CONTAINER (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);
|
||||
|
||||
sort = gtk_sort_list_model_new (G_LIST_MODEL (tree), sort_tree, NULL, NULL);
|
||||
|
||||
g_signal_connect (button, "clicked", G_CALLBACK (toggle_sort), sort);
|
||||
|
||||
filter = gtk_filter_list_model_new (G_LIST_MODEL (sort),
|
||||
match_file,
|
||||
search_entry,
|
||||
NULL);
|
||||
|
||||
g_signal_connect_swapped (search_entry, "search-changed", G_CALLBACK (gtk_filter_list_model_refilter), filter);
|
||||
selection = GTK_SELECTION_MODEL (gtk_single_selection_new (G_LIST_MODEL (filter)));
|
||||
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), G_LIST_MODEL (selection));
|
||||
|
||||
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_container_add (GTK_CONTAINER (vbox), statusbar);
|
||||
|
||||
g_object_unref (tree);
|
||||
g_object_unref (filter);
|
||||
|
||||
gtk_widget_show (win);
|
||||
|
||||
gtk_main ();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -17,7 +17,7 @@ tests = [
|
||||
['cellarea'],
|
||||
['check-icon-names'],
|
||||
['cssprovider'],
|
||||
['cssrbtree-crash', ['../../gtk/gtkcssrbtree.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
|
||||
['rbtree-crash', ['../../gtk/gtkrbtree.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
|
||||
['defaultvalue'],
|
||||
['entry'],
|
||||
['filterlistmodel'],
|
||||
@@ -33,17 +33,21 @@ tests = [
|
||||
['listbox'],
|
||||
['main'],
|
||||
['maplistmodel'],
|
||||
['multiselection'],
|
||||
['notify'],
|
||||
['no-gtk-init'],
|
||||
['noselection'],
|
||||
['object'],
|
||||
['objects-finalize'],
|
||||
['papersize'],
|
||||
['propertylookuplistmodel', ['../../gtk/gtkpropertylookuplistmodel.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
|
||||
['rbtree', ['../../gtk/gtkrbtree.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
|
||||
['rbtree', ['../../gtk/gtktreerbtree.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
|
||||
['recentmanager'],
|
||||
['regression-tests'],
|
||||
['scrolledwindow'],
|
||||
['searchbar'],
|
||||
['singleselection'],
|
||||
['slicelistmodel'],
|
||||
['sortlistmodel'],
|
||||
['spinbutton'],
|
||||
['stylecontext'],
|
||||
|
||||
436
testsuite/gtk/multiselection.c
Normal file
436
testsuite/gtk/multiselection.c
Normal file
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
* Copyright (C) 2019, Red Hat, Inc.
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GQuark number_quark;
|
||||
static GQuark changes_quark;
|
||||
static GQuark selection_quark;
|
||||
|
||||
static guint
|
||||
get (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
GObject *object = g_list_model_get_item (model, position);
|
||||
g_assert (object != NULL);
|
||||
return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
|
||||
}
|
||||
|
||||
static char *
|
||||
model_to_string (GListModel *model)
|
||||
{
|
||||
GString *string = g_string_new (NULL);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (i > 0)
|
||||
g_string_append (string, " ");
|
||||
g_string_append_printf (string, "%u", get (model, i));
|
||||
}
|
||||
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static char *
|
||||
selection_to_string (GListModel *model)
|
||||
{
|
||||
GString *string = g_string_new (NULL);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (!gtk_selection_model_is_selected (GTK_SELECTION_MODEL (model), i))
|
||||
continue;
|
||||
|
||||
if (string->len > 0)
|
||||
g_string_append (string, " ");
|
||||
g_string_append_printf (string, "%u", get (model, i));
|
||||
}
|
||||
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step);
|
||||
|
||||
static GObject *
|
||||
make_object (guint number)
|
||||
{
|
||||
GObject *object;
|
||||
|
||||
/* 0 cannot be differentiated from NULL, so don't use it */
|
||||
g_assert (number != 0);
|
||||
|
||||
object = g_object_new (G_TYPE_OBJECT, NULL);
|
||||
g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static void
|
||||
splice (GListStore *store,
|
||||
guint pos,
|
||||
guint removed,
|
||||
guint *numbers,
|
||||
guint added)
|
||||
{
|
||||
GObject *objects[added];
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
objects[i] = make_object (numbers[i]);
|
||||
|
||||
g_list_store_splice (store, pos, removed, (gpointer *) objects, added);
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
g_object_unref (objects[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
add (GListStore *store,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_append (store, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
static void
|
||||
insert (GListStore *store,
|
||||
guint position,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_insert (store, position, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
#define assert_model(model, expected) G_STMT_START{ \
|
||||
char *s = model_to_string (G_LIST_MODEL (model)); \
|
||||
if (!g_str_equal (s, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, s, "==", expected); \
|
||||
g_free (s); \
|
||||
}G_STMT_END
|
||||
|
||||
#define ignore_changes(model) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_changes(model, expected) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
|
||||
if (!g_str_equal (changes->str, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, changes->str, "==", expected); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_selection(model, expected) G_STMT_START{ \
|
||||
char *s = selection_to_string (G_LIST_MODEL (model)); \
|
||||
if (!g_str_equal (s, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, s, "==", expected); \
|
||||
g_free (s); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_selection_changes(model, expected) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \
|
||||
if (!g_str_equal (changes->str, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, changes->str, "==", expected); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
static GListStore *
|
||||
new_empty_store (void)
|
||||
{
|
||||
return g_list_store_new (G_TYPE_OBJECT);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step)
|
||||
{
|
||||
GListStore *store = new_empty_store ();
|
||||
guint i;
|
||||
|
||||
for (i = start; i <= end; i += step)
|
||||
add (store, i);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
static void
|
||||
items_changed (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GString *changes)
|
||||
{
|
||||
g_assert (removed != 0 || added != 0);
|
||||
|
||||
if (changes->len)
|
||||
g_string_append (changes, ", ");
|
||||
|
||||
if (removed == 1 && added == 0)
|
||||
{
|
||||
g_string_append_printf (changes, "-%u", position);
|
||||
}
|
||||
else if (removed == 0 && added == 1)
|
||||
{
|
||||
g_string_append_printf (changes, "+%u", position);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_string_append_printf (changes, "%u", position);
|
||||
if (removed > 0)
|
||||
g_string_append_printf (changes, "-%u", removed);
|
||||
if (added > 0)
|
||||
g_string_append_printf (changes, "+%u", added);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
selection_changed (GListModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
GString *changes)
|
||||
{
|
||||
if (changes->len)
|
||||
g_string_append (changes, ", ");
|
||||
|
||||
g_string_append_printf (changes, "%u:%u", position, n_items);
|
||||
}
|
||||
|
||||
static void
|
||||
free_changes (gpointer data)
|
||||
{
|
||||
GString *changes = data;
|
||||
|
||||
/* all changes must have been checked via assert_changes() before */
|
||||
g_assert_cmpstr (changes->str, ==, "");
|
||||
|
||||
g_string_free (changes, TRUE);
|
||||
}
|
||||
|
||||
static GtkSelectionModel *
|
||||
new_model (GListStore *store)
|
||||
{
|
||||
GtkSelectionModel *result;
|
||||
GString *changes;
|
||||
|
||||
result = GTK_SELECTION_MODEL (gtk_multi_selection_new (G_LIST_MODEL (store)));
|
||||
|
||||
changes = g_string_new ("");
|
||||
g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes);
|
||||
g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes);
|
||||
|
||||
changes = g_string_new ("");
|
||||
g_object_set_qdata_full (G_OBJECT(result), selection_quark, changes, free_changes);
|
||||
g_signal_connect (result, "selection-changed", G_CALLBACK (selection_changed), changes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
test_create (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 2);
|
||||
selection = new_model (store);
|
||||
|
||||
assert_model (selection, "1 3 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
assert_model (selection, "1 3 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_changes (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store);
|
||||
assert_model (selection, "1 2 3 4 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_list_store_remove (store, 3);
|
||||
assert_model (selection, "1 2 3 5");
|
||||
assert_changes (selection, "-3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
insert (store, 3, 99);
|
||||
assert_model (selection, "1 2 3 99 5");
|
||||
assert_changes (selection, "+3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
splice (store, 3, 2, (guint[]) { 97 }, 1);
|
||||
assert_model (selection, "1 2 3 97");
|
||||
assert_changes (selection, "3-2+1");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_selection (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
gboolean ret;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_item (selection, 3, FALSE, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "4");
|
||||
assert_selection_changes (selection, "3:1");
|
||||
|
||||
ret = gtk_selection_model_unselect_item (selection, 3);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "3:1");
|
||||
|
||||
ret = gtk_selection_model_select_item (selection, 1, FALSE, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "2");
|
||||
assert_selection_changes (selection, "1:1");
|
||||
|
||||
ret = gtk_selection_model_select_range (selection, 3, 2, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "2 4 5");
|
||||
assert_selection_changes (selection, "3:2");
|
||||
|
||||
ret = gtk_selection_model_unselect_range (selection, 3, 2);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "2");
|
||||
assert_selection_changes (selection, "3:2");
|
||||
|
||||
ret = gtk_selection_model_select_all (selection);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "1 2 3 4 5");
|
||||
assert_selection_changes (selection, "0:5");
|
||||
|
||||
ret = gtk_selection_model_unselect_all (selection);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "0:5");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static int
|
||||
sort_inverse (gconstpointer a, gconstpointer b, gpointer data)
|
||||
{
|
||||
int ia = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (a), number_quark));
|
||||
int ib = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (b), number_quark));
|
||||
|
||||
return ib - ia;
|
||||
}
|
||||
|
||||
static void
|
||||
test_persistence (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (2, 1, 1);
|
||||
add (store, 1);
|
||||
add (store, 3);
|
||||
add (store, 5);
|
||||
add (store, 4);
|
||||
add (store, 2);
|
||||
selection = new_model (store);
|
||||
|
||||
gtk_selection_model_select_range (selection, 0, 3, FALSE);
|
||||
|
||||
assert_selection (selection, "1 3 5");
|
||||
assert_selection_changes (selection, "0:3");
|
||||
|
||||
g_assert_true (gtk_selection_model_is_selected (selection, 0));
|
||||
g_assert_true (gtk_selection_model_is_selected (selection, 1));
|
||||
g_assert_true (gtk_selection_model_is_selected (selection, 2));
|
||||
g_assert_false (gtk_selection_model_is_selected (selection, 3));
|
||||
g_assert_false (gtk_selection_model_is_selected (selection, 4));
|
||||
|
||||
g_list_store_sort (store, sort_inverse, NULL);
|
||||
|
||||
assert_selection (selection, "5 3 1");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_assert_true (gtk_selection_model_is_selected (selection, 0));
|
||||
g_assert_false (gtk_selection_model_is_selected (selection, 1));
|
||||
g_assert_true (gtk_selection_model_is_selected (selection, 2));
|
||||
g_assert_false (gtk_selection_model_is_selected (selection, 3));
|
||||
g_assert_true (gtk_selection_model_is_selected (selection, 4));
|
||||
|
||||
ignore_changes (selection);
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
setlocale (LC_ALL, "C");
|
||||
g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s");
|
||||
|
||||
number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released.");
|
||||
changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?");
|
||||
selection_quark = g_quark_from_static_string ("Mana mana, badibidibi");
|
||||
|
||||
g_test_add_func ("/multiselection/create", test_create);
|
||||
#if GLIB_CHECK_VERSION (2, 58, 0) /* g_list_store_splice() is broken before 2.58 */
|
||||
g_test_add_func ("/singleselection/changes", test_changes);
|
||||
#endif
|
||||
g_test_add_func ("/multiselection/selection", test_selection);
|
||||
g_test_add_func ("/multiselection/persistence", test_persistence);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
373
testsuite/gtk/noselection.c
Normal file
373
testsuite/gtk/noselection.c
Normal file
@@ -0,0 +1,373 @@
|
||||
/*
|
||||
* Copyright (C) 2019, Red Hat, Inc.
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GQuark number_quark;
|
||||
static GQuark changes_quark;
|
||||
static GQuark selection_quark;
|
||||
|
||||
static guint
|
||||
get (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
GObject *object = g_list_model_get_item (model, position);
|
||||
g_assert (object != NULL);
|
||||
return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
|
||||
}
|
||||
|
||||
static char *
|
||||
model_to_string (GListModel *model)
|
||||
{
|
||||
GString *string = g_string_new (NULL);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (i > 0)
|
||||
g_string_append (string, " ");
|
||||
g_string_append_printf (string, "%u", get (model, i));
|
||||
}
|
||||
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static char *
|
||||
selection_to_string (GListModel *model)
|
||||
{
|
||||
GString *string = g_string_new (NULL);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (!gtk_selection_model_is_selected (GTK_SELECTION_MODEL (model), i))
|
||||
continue;
|
||||
|
||||
if (i > 0)
|
||||
g_string_append (string, " ");
|
||||
g_string_append_printf (string, "%u", get (model, i));
|
||||
}
|
||||
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step);
|
||||
|
||||
static GObject *
|
||||
make_object (guint number)
|
||||
{
|
||||
GObject *object;
|
||||
|
||||
/* 0 cannot be differentiated from NULL, so don't use it */
|
||||
g_assert (number != 0);
|
||||
|
||||
object = g_object_new (G_TYPE_OBJECT, NULL);
|
||||
g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static void
|
||||
splice (GListStore *store,
|
||||
guint pos,
|
||||
guint removed,
|
||||
guint *numbers,
|
||||
guint added)
|
||||
{
|
||||
GObject *objects[added];
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
objects[i] = make_object (numbers[i]);
|
||||
|
||||
g_list_store_splice (store, pos, removed, (gpointer *) objects, added);
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
g_object_unref (objects[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
add (GListStore *store,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_append (store, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
static void
|
||||
insert (GListStore *store,
|
||||
guint position,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_insert (store, position, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
#define assert_model(model, expected) G_STMT_START{ \
|
||||
char *s = model_to_string (G_LIST_MODEL (model)); \
|
||||
if (!g_str_equal (s, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, s, "==", expected); \
|
||||
g_free (s); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_changes(model, expected) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
|
||||
if (!g_str_equal (changes->str, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, changes->str, "==", expected); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_selection(model, expected) G_STMT_START{ \
|
||||
char *s = selection_to_string (G_LIST_MODEL (model)); \
|
||||
if (!g_str_equal (s, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, s, "==", expected); \
|
||||
g_free (s); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_selection_changes(model, expected) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \
|
||||
if (!g_str_equal (changes->str, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, changes->str, "==", expected); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
static GListStore *
|
||||
new_empty_store (void)
|
||||
{
|
||||
return g_list_store_new (G_TYPE_OBJECT);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step)
|
||||
{
|
||||
GListStore *store = new_empty_store ();
|
||||
guint i;
|
||||
|
||||
for (i = start; i <= end; i += step)
|
||||
add (store, i);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
static void
|
||||
items_changed (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GString *changes)
|
||||
{
|
||||
g_assert (removed != 0 || added != 0);
|
||||
|
||||
if (changes->len)
|
||||
g_string_append (changes, ", ");
|
||||
|
||||
if (removed == 1 && added == 0)
|
||||
{
|
||||
g_string_append_printf (changes, "-%u", position);
|
||||
}
|
||||
else if (removed == 0 && added == 1)
|
||||
{
|
||||
g_string_append_printf (changes, "+%u", position);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_string_append_printf (changes, "%u", position);
|
||||
if (removed > 0)
|
||||
g_string_append_printf (changes, "-%u", removed);
|
||||
if (added > 0)
|
||||
g_string_append_printf (changes, "+%u", added);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
selection_changed (GListModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
GString *changes)
|
||||
{
|
||||
if (changes->len)
|
||||
g_string_append (changes, ", ");
|
||||
|
||||
g_string_append_printf (changes, "%u:%u", position, n_items);
|
||||
}
|
||||
|
||||
static void
|
||||
free_changes (gpointer data)
|
||||
{
|
||||
GString *changes = data;
|
||||
|
||||
/* all changes must have been checked via assert_changes() before */
|
||||
g_assert_cmpstr (changes->str, ==, "");
|
||||
|
||||
g_string_free (changes, TRUE);
|
||||
}
|
||||
|
||||
static GtkSelectionModel *
|
||||
new_model (GListStore *store)
|
||||
{
|
||||
GtkSelectionModel *result;
|
||||
GString *changes;
|
||||
|
||||
result = GTK_SELECTION_MODEL (gtk_no_selection_new (G_LIST_MODEL (store)));
|
||||
|
||||
changes = g_string_new ("");
|
||||
g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes);
|
||||
g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes);
|
||||
|
||||
changes = g_string_new ("");
|
||||
g_object_set_qdata_full (G_OBJECT(result), selection_quark, changes, free_changes);
|
||||
g_signal_connect (result, "selection-changed", G_CALLBACK (selection_changed), changes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
test_create (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 2);
|
||||
selection = new_model (store);
|
||||
assert_model (selection, "1 3 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
assert_model (selection, "1 3 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_changes (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store);
|
||||
assert_model (selection, "1 2 3 4 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_list_store_remove (store, 3);
|
||||
assert_model (selection, "1 2 3 5");
|
||||
assert_changes (selection, "-3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
insert (store, 3, 99);
|
||||
assert_model (selection, "1 2 3 99 5");
|
||||
assert_changes (selection, "+3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
splice (store, 3, 2, (guint[]) { 97 }, 1);
|
||||
assert_model (selection, "1 2 3 97");
|
||||
assert_changes (selection, "3-2+1");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_selection (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
gboolean ret;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_item (selection, 3, FALSE, FALSE);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_unselect_item (selection, 3);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_range (selection, 3, 2, FALSE);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_unselect_range (selection, 4, 2);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_all (selection);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_unselect_all (selection);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
setlocale (LC_ALL, "C");
|
||||
g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s");
|
||||
|
||||
number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released.");
|
||||
changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?");
|
||||
selection_quark = g_quark_from_static_string ("Mana mana, badibidibi");
|
||||
|
||||
g_test_add_func ("/noselection/create", test_create);
|
||||
#if GLIB_CHECK_VERSION (2, 58, 0) /* g_list_store_splice() is broken before 2.58 */
|
||||
g_test_add_func ("/noselection/changes", test_changes);
|
||||
#endif
|
||||
g_test_add_func ("/noselection/selection", test_selection);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
@@ -21,7 +21,7 @@
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gtk/gtkcssrbtreeprivate.h"
|
||||
#include "gtk/gtkrbtreeprivate.h"
|
||||
|
||||
typedef struct _Node Node;
|
||||
typedef struct _Aug Aug;
|
||||
@@ -35,7 +35,7 @@ struct _Aug {
|
||||
};
|
||||
|
||||
static void
|
||||
augment (GtkCssRbTree *tree,
|
||||
augment (GtkRbTree *tree,
|
||||
gpointer _aug,
|
||||
gpointer _node,
|
||||
gpointer left,
|
||||
@@ -47,33 +47,33 @@ augment (GtkCssRbTree *tree,
|
||||
|
||||
if (left)
|
||||
{
|
||||
Aug *left_aug = gtk_css_rb_tree_get_augment (tree, left);
|
||||
Aug *left_aug = gtk_rb_tree_get_augment (tree, left);
|
||||
|
||||
aug->n_items += left_aug->n_items;
|
||||
}
|
||||
|
||||
if (right)
|
||||
{
|
||||
Aug *right_aug = gtk_css_rb_tree_get_augment (tree, right);
|
||||
Aug *right_aug = gtk_rb_tree_get_augment (tree, right);
|
||||
|
||||
aug->n_items += right_aug->n_items;
|
||||
}
|
||||
}
|
||||
|
||||
static Node *
|
||||
get (GtkCssRbTree *tree,
|
||||
get (GtkRbTree *tree,
|
||||
guint pos)
|
||||
{
|
||||
Node *node, *tmp;
|
||||
|
||||
node = gtk_css_rb_tree_get_root (tree);
|
||||
node = gtk_rb_tree_get_root (tree);
|
||||
|
||||
while (node)
|
||||
{
|
||||
tmp = gtk_css_rb_tree_get_left (tree, node);
|
||||
tmp = gtk_rb_tree_get_left (tree, node);
|
||||
if (tmp)
|
||||
{
|
||||
Aug *aug = gtk_css_rb_tree_get_augment (tree, tmp);
|
||||
Aug *aug = gtk_rb_tree_get_augment (tree, tmp);
|
||||
if (pos < aug->n_items)
|
||||
{
|
||||
node = tmp;
|
||||
@@ -86,32 +86,32 @@ get (GtkCssRbTree *tree,
|
||||
break;
|
||||
pos--;
|
||||
|
||||
node = gtk_css_rb_tree_get_right (tree, node);
|
||||
node = gtk_rb_tree_get_right (tree, node);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
static void
|
||||
add (GtkCssRbTree *tree,
|
||||
add (GtkRbTree *tree,
|
||||
guint pos)
|
||||
{
|
||||
Node *node = get (tree, pos);
|
||||
|
||||
gtk_css_rb_tree_insert_before (tree, node);
|
||||
gtk_rb_tree_insert_before (tree, node);
|
||||
}
|
||||
|
||||
static void
|
||||
delete (GtkCssRbTree *tree,
|
||||
delete (GtkRbTree *tree,
|
||||
guint pos)
|
||||
{
|
||||
Node *node = get (tree, pos);
|
||||
|
||||
gtk_css_rb_tree_remove (tree, node);
|
||||
gtk_rb_tree_remove (tree, node);
|
||||
}
|
||||
|
||||
static guint
|
||||
print_node (GtkCssRbTree *tree,
|
||||
print_node (GtkRbTree *tree,
|
||||
Node *node,
|
||||
guint depth,
|
||||
const char *prefix,
|
||||
@@ -119,12 +119,12 @@ print_node (GtkCssRbTree *tree,
|
||||
{
|
||||
Node *child;
|
||||
|
||||
child = gtk_css_rb_tree_get_left (tree, node);
|
||||
child = gtk_rb_tree_get_left (tree, node);
|
||||
if (child)
|
||||
n = print_node (tree, child, depth + 1, "/", n);
|
||||
g_print ("%*s %u\n", 2 * depth, prefix, n);
|
||||
n++;
|
||||
child = gtk_css_rb_tree_get_right (tree, node);
|
||||
child = gtk_rb_tree_get_right (tree, node);
|
||||
if (child)
|
||||
n = print_node (tree, child, depth + 1, "\\", n);
|
||||
|
||||
@@ -132,18 +132,18 @@ print_node (GtkCssRbTree *tree,
|
||||
}
|
||||
|
||||
static void
|
||||
print (GtkCssRbTree *tree)
|
||||
print (GtkRbTree *tree)
|
||||
{
|
||||
print_node (tree, gtk_css_rb_tree_get_root (tree), 0, "", 0);
|
||||
print_node (tree, gtk_rb_tree_get_root (tree), 0, "", 0);
|
||||
}
|
||||
|
||||
static void
|
||||
test_crash (void)
|
||||
{
|
||||
GtkCssRbTree *tree;
|
||||
GtkRbTree *tree;
|
||||
guint i;
|
||||
|
||||
tree = gtk_css_rb_tree_new (Node, Aug, augment, NULL, NULL);
|
||||
tree = gtk_rb_tree_new (Node, Aug, augment, NULL, NULL);
|
||||
|
||||
for (i = 0; i < 300; i++)
|
||||
add (tree, i);
|
||||
@@ -270,7 +270,7 @@ test_crash (void)
|
||||
print (tree);
|
||||
delete (tree, 102);
|
||||
|
||||
gtk_css_rb_tree_unref (tree);
|
||||
gtk_rb_tree_unref (tree);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -1,4 +1,4 @@
|
||||
/* GtkRBTree tests.
|
||||
/* GtkTreeRBTree tests.
|
||||
*
|
||||
* Copyright (C) 2011, Red Hat, Inc.
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
@@ -19,12 +19,12 @@
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include "../../gtk/gtkrbtreeprivate.h"
|
||||
#include "../../gtk/gtktreerbtreeprivate.h"
|
||||
|
||||
/* _gtk_rbtree_test */
|
||||
/* _gtk_tree_rbtree_test */
|
||||
|
||||
static guint
|
||||
get_total_count (GtkRBNode *node)
|
||||
get_total_count (GtkTreeRBNode *node)
|
||||
{
|
||||
guint child_total = 0;
|
||||
|
||||
@@ -38,12 +38,12 @@ get_total_count (GtkRBNode *node)
|
||||
}
|
||||
|
||||
static guint
|
||||
count_total (GtkRBTree *tree,
|
||||
GtkRBNode *node)
|
||||
count_total (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node)
|
||||
{
|
||||
guint res;
|
||||
|
||||
if (_gtk_rbtree_is_nil (node))
|
||||
if (_gtk_tree_rbtree_is_nil (node))
|
||||
return 0;
|
||||
|
||||
res =
|
||||
@@ -62,11 +62,11 @@ count_total (GtkRBTree *tree,
|
||||
}
|
||||
|
||||
static gint
|
||||
_count_nodes (GtkRBTree *tree,
|
||||
GtkRBNode *node)
|
||||
_count_nodes (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node)
|
||||
{
|
||||
gint res;
|
||||
if (_gtk_rbtree_is_nil (node))
|
||||
if (_gtk_tree_rbtree_is_nil (node))
|
||||
return 0;
|
||||
|
||||
g_assert (node->left);
|
||||
@@ -81,95 +81,95 @@ _count_nodes (GtkRBTree *tree,
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_rbtree_test_height (GtkRBTree *tree,
|
||||
GtkRBNode *node)
|
||||
_gtk_tree_rbtree_test_height (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node)
|
||||
{
|
||||
gint computed_offset = 0;
|
||||
|
||||
/* This whole test is sort of a useless truism. */
|
||||
|
||||
if (!_gtk_rbtree_is_nil (node->left))
|
||||
if (!_gtk_tree_rbtree_is_nil (node->left))
|
||||
computed_offset += node->left->offset;
|
||||
|
||||
if (!_gtk_rbtree_is_nil (node->right))
|
||||
if (!_gtk_tree_rbtree_is_nil (node->right))
|
||||
computed_offset += node->right->offset;
|
||||
|
||||
if (node->children && !_gtk_rbtree_is_nil (node->children->root))
|
||||
if (node->children && !_gtk_tree_rbtree_is_nil (node->children->root))
|
||||
computed_offset += node->children->root->offset;
|
||||
|
||||
if (GTK_RBNODE_GET_HEIGHT (node) + computed_offset != node->offset)
|
||||
if (GTK_TREE_RBNODE_GET_HEIGHT (node) + computed_offset != node->offset)
|
||||
g_error ("node has broken offset");
|
||||
|
||||
if (!_gtk_rbtree_is_nil (node->left))
|
||||
_gtk_rbtree_test_height (tree, node->left);
|
||||
if (!_gtk_tree_rbtree_is_nil (node->left))
|
||||
_gtk_tree_rbtree_test_height (tree, node->left);
|
||||
|
||||
if (!_gtk_rbtree_is_nil (node->right))
|
||||
_gtk_rbtree_test_height (tree, node->right);
|
||||
if (!_gtk_tree_rbtree_is_nil (node->right))
|
||||
_gtk_tree_rbtree_test_height (tree, node->right);
|
||||
|
||||
if (node->children && !_gtk_rbtree_is_nil (node->children->root))
|
||||
_gtk_rbtree_test_height (node->children, node->children->root);
|
||||
if (node->children && !_gtk_tree_rbtree_is_nil (node->children->root))
|
||||
_gtk_tree_rbtree_test_height (node->children, node->children->root);
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_rbtree_test_dirty (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
_gtk_tree_rbtree_test_dirty (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gint expected_dirtyness)
|
||||
{
|
||||
|
||||
if (expected_dirtyness)
|
||||
{
|
||||
g_assert (GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID) ||
|
||||
GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID) ||
|
||||
GTK_RBNODE_FLAG_SET (node->left, GTK_RBNODE_DESCENDANTS_INVALID) ||
|
||||
GTK_RBNODE_FLAG_SET (node->right, GTK_RBNODE_DESCENDANTS_INVALID) ||
|
||||
(node->children && GTK_RBNODE_FLAG_SET (node->children->root, GTK_RBNODE_DESCENDANTS_INVALID)));
|
||||
g_assert (GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) ||
|
||||
GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID) ||
|
||||
GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID) ||
|
||||
GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID) ||
|
||||
(node->children && GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID)));
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID) &&
|
||||
! GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID));
|
||||
if (!_gtk_rbtree_is_nil (node->left))
|
||||
g_assert (! GTK_RBNODE_FLAG_SET (node->left, GTK_RBNODE_DESCENDANTS_INVALID));
|
||||
if (!_gtk_rbtree_is_nil (node->right))
|
||||
g_assert (! GTK_RBNODE_FLAG_SET (node->right, GTK_RBNODE_DESCENDANTS_INVALID));
|
||||
g_assert (! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID) &&
|
||||
! GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID));
|
||||
if (!_gtk_tree_rbtree_is_nil (node->left))
|
||||
g_assert (! GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID));
|
||||
if (!_gtk_tree_rbtree_is_nil (node->right))
|
||||
g_assert (! GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID));
|
||||
if (node->children != NULL)
|
||||
g_assert (! GTK_RBNODE_FLAG_SET (node->children->root, GTK_RBNODE_DESCENDANTS_INVALID));
|
||||
g_assert (! GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID));
|
||||
}
|
||||
|
||||
if (!_gtk_rbtree_is_nil (node->left))
|
||||
_gtk_rbtree_test_dirty (tree, node->left, GTK_RBNODE_FLAG_SET (node->left, GTK_RBNODE_DESCENDANTS_INVALID));
|
||||
if (!_gtk_rbtree_is_nil (node->right))
|
||||
_gtk_rbtree_test_dirty (tree, node->right, GTK_RBNODE_FLAG_SET (node->right, GTK_RBNODE_DESCENDANTS_INVALID));
|
||||
if (node->children != NULL && !_gtk_rbtree_is_nil (node->children->root))
|
||||
_gtk_rbtree_test_dirty (node->children, node->children->root, GTK_RBNODE_FLAG_SET (node->children->root, GTK_RBNODE_DESCENDANTS_INVALID));
|
||||
if (!_gtk_tree_rbtree_is_nil (node->left))
|
||||
_gtk_tree_rbtree_test_dirty (tree, node->left, GTK_TREE_RBNODE_FLAG_SET (node->left, GTK_TREE_RBNODE_DESCENDANTS_INVALID));
|
||||
if (!_gtk_tree_rbtree_is_nil (node->right))
|
||||
_gtk_tree_rbtree_test_dirty (tree, node->right, GTK_TREE_RBNODE_FLAG_SET (node->right, GTK_TREE_RBNODE_DESCENDANTS_INVALID));
|
||||
if (node->children != NULL && !_gtk_tree_rbtree_is_nil (node->children->root))
|
||||
_gtk_tree_rbtree_test_dirty (node->children, node->children->root, GTK_TREE_RBNODE_FLAG_SET (node->children->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID));
|
||||
}
|
||||
|
||||
static void _gtk_rbtree_test_structure (GtkRBTree *tree);
|
||||
static void _gtk_tree_rbtree_test_structure (GtkTreeRBTree *tree);
|
||||
|
||||
static guint
|
||||
_gtk_rbtree_test_structure_helper (GtkRBTree *tree,
|
||||
GtkRBNode *node)
|
||||
_gtk_tree_rbtree_test_structure_helper (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node)
|
||||
{
|
||||
guint left_blacks, right_blacks;
|
||||
|
||||
g_assert (!_gtk_rbtree_is_nil (node));
|
||||
g_assert (!_gtk_tree_rbtree_is_nil (node));
|
||||
|
||||
g_assert (node->left != NULL);
|
||||
g_assert (node->right != NULL);
|
||||
g_assert (node->parent != NULL);
|
||||
|
||||
if (!_gtk_rbtree_is_nil (node->left))
|
||||
if (!_gtk_tree_rbtree_is_nil (node->left))
|
||||
{
|
||||
g_assert (node->left->parent == node);
|
||||
left_blacks = _gtk_rbtree_test_structure_helper (tree, node->left);
|
||||
left_blacks = _gtk_tree_rbtree_test_structure_helper (tree, node->left);
|
||||
}
|
||||
else
|
||||
left_blacks = 0;
|
||||
|
||||
if (!_gtk_rbtree_is_nil (node->right))
|
||||
if (!_gtk_tree_rbtree_is_nil (node->right))
|
||||
{
|
||||
g_assert (node->right->parent == node);
|
||||
right_blacks = _gtk_rbtree_test_structure_helper (tree, node->right);
|
||||
right_blacks = _gtk_tree_rbtree_test_structure_helper (tree, node->right);
|
||||
}
|
||||
else
|
||||
right_blacks = 0;
|
||||
@@ -179,29 +179,29 @@ _gtk_rbtree_test_structure_helper (GtkRBTree *tree,
|
||||
g_assert (node->children->parent_tree == tree);
|
||||
g_assert (node->children->parent_node == node);
|
||||
|
||||
_gtk_rbtree_test_structure (node->children);
|
||||
_gtk_tree_rbtree_test_structure (node->children);
|
||||
}
|
||||
|
||||
g_assert (left_blacks == right_blacks);
|
||||
|
||||
return left_blacks + (GTK_RBNODE_GET_COLOR (node) == GTK_RBNODE_BLACK ? 1 : 0);
|
||||
return left_blacks + (GTK_TREE_RBNODE_GET_COLOR (node) == GTK_TREE_RBNODE_BLACK ? 1 : 0);
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_rbtree_test_structure (GtkRBTree *tree)
|
||||
_gtk_tree_rbtree_test_structure (GtkTreeRBTree *tree)
|
||||
{
|
||||
g_assert (tree->root);
|
||||
if (_gtk_rbtree_is_nil (tree->root))
|
||||
if (_gtk_tree_rbtree_is_nil (tree->root))
|
||||
return;
|
||||
|
||||
g_assert (_gtk_rbtree_is_nil (tree->root->parent));
|
||||
_gtk_rbtree_test_structure_helper (tree, tree->root);
|
||||
g_assert (_gtk_tree_rbtree_is_nil (tree->root->parent));
|
||||
_gtk_tree_rbtree_test_structure_helper (tree, tree->root);
|
||||
}
|
||||
|
||||
static void
|
||||
_gtk_rbtree_test (GtkRBTree *tree)
|
||||
_gtk_tree_rbtree_test (GtkTreeRBTree *tree)
|
||||
{
|
||||
GtkRBTree *tmp_tree;
|
||||
GtkTreeRBTree *tmp_tree;
|
||||
|
||||
if (tree == NULL)
|
||||
return;
|
||||
@@ -211,24 +211,24 @@ _gtk_rbtree_test (GtkRBTree *tree)
|
||||
while (tmp_tree->parent_tree)
|
||||
tmp_tree = tmp_tree->parent_tree;
|
||||
|
||||
if (_gtk_rbtree_is_nil (tmp_tree->root))
|
||||
if (_gtk_tree_rbtree_is_nil (tmp_tree->root))
|
||||
return;
|
||||
|
||||
_gtk_rbtree_test_structure (tmp_tree);
|
||||
_gtk_tree_rbtree_test_structure (tmp_tree);
|
||||
|
||||
g_assert ((_count_nodes (tmp_tree, tmp_tree->root->left) +
|
||||
_count_nodes (tmp_tree, tmp_tree->root->right) + 1) == tmp_tree->root->count);
|
||||
|
||||
_gtk_rbtree_test_height (tmp_tree, tmp_tree->root);
|
||||
_gtk_rbtree_test_dirty (tmp_tree, tmp_tree->root, GTK_RBNODE_FLAG_SET (tmp_tree->root, GTK_RBNODE_DESCENDANTS_INVALID));
|
||||
_gtk_tree_rbtree_test_height (tmp_tree, tmp_tree->root);
|
||||
_gtk_tree_rbtree_test_dirty (tmp_tree, tmp_tree->root, GTK_TREE_RBNODE_FLAG_SET (tmp_tree->root, GTK_TREE_RBNODE_DESCENDANTS_INVALID));
|
||||
g_assert (count_total (tmp_tree, tmp_tree->root) == tmp_tree->root->total_count);
|
||||
}
|
||||
|
||||
/* gtk_rbtree_print() - unused, for debugging only */
|
||||
|
||||
static void
|
||||
gtk_rbtree_print_node (GtkRBTree *tree,
|
||||
GtkRBNode *node,
|
||||
gtk_rbtree_print_node (GtkTreeRBTree *tree,
|
||||
GtkTreeRBNode *node,
|
||||
gint depth)
|
||||
{
|
||||
gint i;
|
||||
@@ -237,37 +237,37 @@ gtk_rbtree_print_node (GtkRBTree *tree,
|
||||
|
||||
g_print ("(%p - %s) (Offset %d) (Parity %d) (Validity %d%d%d)\n",
|
||||
node,
|
||||
(GTK_RBNODE_GET_COLOR (node) == GTK_RBNODE_BLACK)?"BLACK":" RED ",
|
||||
(GTK_TREE_RBNODE_GET_COLOR (node) == GTK_TREE_RBNODE_BLACK)?"BLACK":" RED ",
|
||||
node->offset,
|
||||
node->total_count,
|
||||
(GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_DESCENDANTS_INVALID))?1:0,
|
||||
(GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_INVALID))?1:0,
|
||||
(GTK_RBNODE_FLAG_SET (node, GTK_RBNODE_COLUMN_INVALID))?1:0);
|
||||
(GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_DESCENDANTS_INVALID))?1:0,
|
||||
(GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_INVALID))?1:0,
|
||||
(GTK_TREE_RBNODE_FLAG_SET (node, GTK_TREE_RBNODE_COLUMN_INVALID))?1:0);
|
||||
if (node->children != NULL)
|
||||
{
|
||||
g_print ("Looking at child.\n");
|
||||
gtk_rbtree_print_node (node->children, node->children->root, depth + 1);
|
||||
g_print ("Done looking at child.\n");
|
||||
}
|
||||
if (!_gtk_rbtree_is_nil (node->left))
|
||||
if (!_gtk_tree_rbtree_is_nil (node->left))
|
||||
{
|
||||
gtk_rbtree_print_node (tree, node->left, depth+1);
|
||||
}
|
||||
if (!_gtk_rbtree_is_nil (node->right))
|
||||
if (!_gtk_tree_rbtree_is_nil (node->right))
|
||||
{
|
||||
gtk_rbtree_print_node (tree, node->right, depth+1);
|
||||
}
|
||||
}
|
||||
|
||||
/* not static so the debugger finds it. */
|
||||
void gtk_rbtree_print (GtkRBTree *tree);
|
||||
void gtk_rbtree_print (GtkTreeRBTree *tree);
|
||||
|
||||
void
|
||||
gtk_rbtree_print (GtkRBTree *tree)
|
||||
gtk_rbtree_print (GtkTreeRBTree *tree)
|
||||
{
|
||||
g_return_if_fail (tree != NULL);
|
||||
|
||||
if (_gtk_rbtree_is_nil (tree->root))
|
||||
if (_gtk_tree_rbtree_is_nil (tree->root))
|
||||
g_print ("Empty tree...\n");
|
||||
else
|
||||
gtk_rbtree_print_node (tree, tree->root, 0);
|
||||
@@ -276,13 +276,13 @@ gtk_rbtree_print (GtkRBTree *tree)
|
||||
/* actual tests */
|
||||
|
||||
static guint
|
||||
append_elements (GtkRBTree *tree,
|
||||
append_elements (GtkTreeRBTree *tree,
|
||||
guint depth,
|
||||
guint elements_per_depth,
|
||||
gboolean check,
|
||||
guint height)
|
||||
{
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBNode *node;
|
||||
guint i;
|
||||
|
||||
g_assert (depth > 0);
|
||||
@@ -292,33 +292,33 @@ append_elements (GtkRBTree *tree,
|
||||
|
||||
for (i = 0; i < elements_per_depth; i++)
|
||||
{
|
||||
node = _gtk_rbtree_insert_after (tree, node, ++height, TRUE);
|
||||
node = _gtk_tree_rbtree_insert_after (tree, node, ++height, TRUE);
|
||||
if (depth)
|
||||
{
|
||||
node->children = _gtk_rbtree_new ();
|
||||
node->children = _gtk_tree_rbtree_new ();
|
||||
node->children->parent_tree = tree;
|
||||
node->children->parent_node = node;
|
||||
height = append_elements (node->children, depth, elements_per_depth, check, height);
|
||||
}
|
||||
if (check)
|
||||
_gtk_rbtree_test (tree);
|
||||
_gtk_tree_rbtree_test (tree);
|
||||
}
|
||||
|
||||
return height;
|
||||
}
|
||||
|
||||
static GtkRBTree *
|
||||
static GtkTreeRBTree *
|
||||
create_rbtree (guint depth,
|
||||
guint elements_per_depth,
|
||||
gboolean check)
|
||||
{
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBTree *tree;
|
||||
|
||||
tree = _gtk_rbtree_new ();
|
||||
tree = _gtk_tree_rbtree_new ();
|
||||
|
||||
append_elements (tree, depth, elements_per_depth, check, 0);
|
||||
|
||||
_gtk_rbtree_test (tree);
|
||||
_gtk_tree_rbtree_test (tree);
|
||||
|
||||
return tree;
|
||||
}
|
||||
@@ -326,106 +326,106 @@ create_rbtree (guint depth,
|
||||
static void
|
||||
test_create (void)
|
||||
{
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBTree *tree;
|
||||
|
||||
tree = create_rbtree (5, 5, TRUE);
|
||||
|
||||
_gtk_rbtree_free (tree);
|
||||
_gtk_tree_rbtree_free (tree);
|
||||
}
|
||||
|
||||
static void
|
||||
test_insert_after (void)
|
||||
{
|
||||
guint i;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
|
||||
tree = _gtk_rbtree_new ();
|
||||
tree = _gtk_tree_rbtree_new ();
|
||||
node = NULL;
|
||||
|
||||
for (i = 1; i <= 100; i++)
|
||||
{
|
||||
node = _gtk_rbtree_insert_after (tree, node, i, TRUE);
|
||||
_gtk_rbtree_test (tree);
|
||||
node = _gtk_tree_rbtree_insert_after (tree, node, i, TRUE);
|
||||
_gtk_tree_rbtree_test (tree);
|
||||
g_assert (tree->root->count == i);
|
||||
g_assert (tree->root->total_count == i);
|
||||
g_assert (tree->root->offset == i * (i + 1) / 2);
|
||||
}
|
||||
|
||||
_gtk_rbtree_free (tree);
|
||||
_gtk_tree_rbtree_free (tree);
|
||||
}
|
||||
|
||||
static void
|
||||
test_insert_before (void)
|
||||
{
|
||||
guint i;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
|
||||
tree = _gtk_rbtree_new ();
|
||||
tree = _gtk_tree_rbtree_new ();
|
||||
node = NULL;
|
||||
|
||||
for (i = 1; i <= 100; i++)
|
||||
{
|
||||
node = _gtk_rbtree_insert_before (tree, node, i, TRUE);
|
||||
_gtk_rbtree_test (tree);
|
||||
node = _gtk_tree_rbtree_insert_before (tree, node, i, TRUE);
|
||||
_gtk_tree_rbtree_test (tree);
|
||||
g_assert (tree->root->count == i);
|
||||
g_assert (tree->root->total_count == i);
|
||||
g_assert (tree->root->offset == i * (i + 1) / 2);
|
||||
}
|
||||
|
||||
_gtk_rbtree_free (tree);
|
||||
_gtk_tree_rbtree_free (tree);
|
||||
}
|
||||
|
||||
static void
|
||||
test_remove_node (void)
|
||||
{
|
||||
GtkRBTree *tree;
|
||||
GtkTreeRBTree *tree;
|
||||
|
||||
tree = create_rbtree (3, 16, g_test_thorough ());
|
||||
|
||||
while (tree->root->count > 1)
|
||||
{
|
||||
GtkRBTree *find_tree;
|
||||
GtkRBNode *find_node;
|
||||
GtkTreeRBTree *find_tree;
|
||||
GtkTreeRBNode *find_node;
|
||||
guint i;
|
||||
|
||||
i = g_test_rand_int_range (0, tree->root->total_count);
|
||||
if (!_gtk_rbtree_find_index (tree, i, &find_tree, &find_node))
|
||||
if (!_gtk_tree_rbtree_find_index (tree, i, &find_tree, &find_node))
|
||||
{
|
||||
/* We search an available index, so we mustn't fail. */
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
_gtk_rbtree_test (find_tree);
|
||||
_gtk_tree_rbtree_test (find_tree);
|
||||
|
||||
if (find_tree->root->count == 1)
|
||||
{
|
||||
_gtk_rbtree_remove (find_tree);
|
||||
_gtk_tree_rbtree_remove (find_tree);
|
||||
}
|
||||
else
|
||||
_gtk_rbtree_remove_node (find_tree, find_node);
|
||||
_gtk_rbtree_test (tree);
|
||||
_gtk_tree_rbtree_remove_node (find_tree, find_node);
|
||||
_gtk_tree_rbtree_test (tree);
|
||||
}
|
||||
|
||||
_gtk_rbtree_free (tree);
|
||||
_gtk_tree_rbtree_free (tree);
|
||||
}
|
||||
|
||||
static void
|
||||
test_remove_root (void)
|
||||
{
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
|
||||
tree = _gtk_rbtree_new ();
|
||||
tree = _gtk_tree_rbtree_new ();
|
||||
|
||||
node = _gtk_rbtree_insert_after (tree, NULL, 1, TRUE);
|
||||
_gtk_rbtree_insert_after (tree, node, 2, TRUE);
|
||||
_gtk_rbtree_insert_before (tree, node, 3, TRUE);
|
||||
node = _gtk_tree_rbtree_insert_after (tree, NULL, 1, TRUE);
|
||||
_gtk_tree_rbtree_insert_after (tree, node, 2, TRUE);
|
||||
_gtk_tree_rbtree_insert_before (tree, node, 3, TRUE);
|
||||
|
||||
_gtk_rbtree_remove_node (tree, node);
|
||||
_gtk_tree_rbtree_remove_node (tree, node);
|
||||
|
||||
_gtk_rbtree_free (tree);
|
||||
_gtk_tree_rbtree_free (tree);
|
||||
}
|
||||
|
||||
static gint *
|
||||
@@ -446,29 +446,29 @@ fisher_yates_shuffle (guint n_items)
|
||||
return list;
|
||||
}
|
||||
|
||||
static GtkRBTree *
|
||||
static GtkTreeRBTree *
|
||||
create_unsorted_tree (gint *order,
|
||||
guint n)
|
||||
{
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
guint i;
|
||||
|
||||
tree = _gtk_rbtree_new ();
|
||||
tree = _gtk_tree_rbtree_new ();
|
||||
node = NULL;
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
node = _gtk_rbtree_insert_after (tree, node, 0, TRUE);
|
||||
node = _gtk_tree_rbtree_insert_after (tree, node, 0, TRUE);
|
||||
}
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
node = _gtk_rbtree_find_count (tree, order[i] + 1);
|
||||
_gtk_rbtree_node_set_height (tree, node, i);
|
||||
node = _gtk_tree_rbtree_find_count (tree, order[i] + 1);
|
||||
_gtk_tree_rbtree_node_set_height (tree, node, i);
|
||||
}
|
||||
|
||||
_gtk_rbtree_test (tree);
|
||||
_gtk_tree_rbtree_test (tree);
|
||||
|
||||
return tree;
|
||||
}
|
||||
@@ -477,8 +477,8 @@ static void
|
||||
test_reorder (void)
|
||||
{
|
||||
guint n = g_test_perf () ? 1000000 : 100;
|
||||
GtkRBTree *tree;
|
||||
GtkRBNode *node;
|
||||
GtkTreeRBTree *tree;
|
||||
GtkTreeRBNode *node;
|
||||
gint *reorder;
|
||||
guint i;
|
||||
double elapsed;
|
||||
@@ -488,23 +488,23 @@ test_reorder (void)
|
||||
|
||||
g_test_timer_start ();
|
||||
|
||||
_gtk_rbtree_reorder (tree, reorder, n);
|
||||
_gtk_tree_rbtree_reorder (tree, reorder, n);
|
||||
|
||||
elapsed = g_test_timer_elapsed ();
|
||||
if (g_test_perf ())
|
||||
g_test_minimized_result (elapsed, "reordering rbtree with %u items: %gsec", n, elapsed);
|
||||
|
||||
_gtk_rbtree_test (tree);
|
||||
_gtk_tree_rbtree_test (tree);
|
||||
|
||||
for (node = _gtk_rbtree_first (tree), i = 0;
|
||||
for (node = _gtk_tree_rbtree_first (tree), i = 0;
|
||||
node != NULL;
|
||||
node = _gtk_rbtree_next (tree, node), i++)
|
||||
node = _gtk_tree_rbtree_next (tree, node), i++)
|
||||
{
|
||||
g_assert (GTK_RBNODE_GET_HEIGHT (node) == i);
|
||||
g_assert (GTK_TREE_RBNODE_GET_HEIGHT (node) == i);
|
||||
}
|
||||
g_assert (i == n);
|
||||
|
||||
_gtk_rbtree_free (tree);
|
||||
_gtk_tree_rbtree_free (tree);
|
||||
|
||||
g_free (reorder);
|
||||
}
|
||||
|
||||
489
testsuite/gtk/singleselection.c
Normal file
489
testsuite/gtk/singleselection.c
Normal file
@@ -0,0 +1,489 @@
|
||||
/*
|
||||
* Copyright (C) 2019, Red Hat, Inc.
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GQuark number_quark;
|
||||
static GQuark changes_quark;
|
||||
static GQuark selection_quark;
|
||||
|
||||
static guint
|
||||
get (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
GObject *object = g_list_model_get_item (model, position);
|
||||
g_assert (object != NULL);
|
||||
return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
|
||||
}
|
||||
|
||||
static char *
|
||||
model_to_string (GListModel *model)
|
||||
{
|
||||
GString *string = g_string_new (NULL);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (i > 0)
|
||||
g_string_append (string, " ");
|
||||
g_string_append_printf (string, "%u", get (model, i));
|
||||
}
|
||||
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static char *
|
||||
selection_to_string (GListModel *model)
|
||||
{
|
||||
GString *string = g_string_new (NULL);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (!gtk_selection_model_is_selected (GTK_SELECTION_MODEL (model), i))
|
||||
continue;
|
||||
|
||||
if (string->len > 0)
|
||||
g_string_append (string, " ");
|
||||
g_string_append_printf (string, "%u", get (model, i));
|
||||
}
|
||||
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step);
|
||||
|
||||
static GObject *
|
||||
make_object (guint number)
|
||||
{
|
||||
GObject *object;
|
||||
|
||||
/* 0 cannot be differentiated from NULL, so don't use it */
|
||||
g_assert (number != 0);
|
||||
|
||||
object = g_object_new (G_TYPE_OBJECT, NULL);
|
||||
g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static void
|
||||
splice (GListStore *store,
|
||||
guint pos,
|
||||
guint removed,
|
||||
guint *numbers,
|
||||
guint added)
|
||||
{
|
||||
GObject *objects[added];
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
objects[i] = make_object (numbers[i]);
|
||||
|
||||
g_list_store_splice (store, pos, removed, (gpointer *) objects, added);
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
g_object_unref (objects[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
add (GListStore *store,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_append (store, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
static void
|
||||
insert (GListStore *store,
|
||||
guint position,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_insert (store, position, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
#define assert_model(model, expected) G_STMT_START{ \
|
||||
char *s = model_to_string (G_LIST_MODEL (model)); \
|
||||
if (!g_str_equal (s, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, s, "==", expected); \
|
||||
g_free (s); \
|
||||
}G_STMT_END
|
||||
|
||||
#define ignore_changes(model) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_changes(model, expected) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
|
||||
if (!g_str_equal (changes->str, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, changes->str, "==", expected); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_selection(model, expected) G_STMT_START{ \
|
||||
char *s = selection_to_string (G_LIST_MODEL (model)); \
|
||||
if (!g_str_equal (s, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, s, "==", expected); \
|
||||
g_free (s); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_selection_changes(model, expected) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \
|
||||
if (!g_str_equal (changes->str, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, changes->str, "==", expected); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
static GListStore *
|
||||
new_empty_store (void)
|
||||
{
|
||||
return g_list_store_new (G_TYPE_OBJECT);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step)
|
||||
{
|
||||
GListStore *store = new_empty_store ();
|
||||
guint i;
|
||||
|
||||
for (i = start; i <= end; i += step)
|
||||
add (store, i);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
static void
|
||||
items_changed (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GString *changes)
|
||||
{
|
||||
g_assert (removed != 0 || added != 0);
|
||||
|
||||
if (changes->len)
|
||||
g_string_append (changes, ", ");
|
||||
|
||||
if (removed == 1 && added == 0)
|
||||
{
|
||||
g_string_append_printf (changes, "-%u", position);
|
||||
}
|
||||
else if (removed == 0 && added == 1)
|
||||
{
|
||||
g_string_append_printf (changes, "+%u", position);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_string_append_printf (changes, "%u", position);
|
||||
if (removed > 0)
|
||||
g_string_append_printf (changes, "-%u", removed);
|
||||
if (added > 0)
|
||||
g_string_append_printf (changes, "+%u", added);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
selection_changed (GListModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
GString *changes)
|
||||
{
|
||||
if (changes->len)
|
||||
g_string_append (changes, ", ");
|
||||
|
||||
g_string_append_printf (changes, "%u:%u", position, n_items);
|
||||
}
|
||||
|
||||
static void
|
||||
free_changes (gpointer data)
|
||||
{
|
||||
GString *changes = data;
|
||||
|
||||
/* all changes must have been checked via assert_changes() before */
|
||||
g_assert_cmpstr (changes->str, ==, "");
|
||||
|
||||
g_string_free (changes, TRUE);
|
||||
}
|
||||
|
||||
static GtkSelectionModel *
|
||||
new_model (GListStore *store, gboolean autoselect, gboolean can_unselect)
|
||||
{
|
||||
GtkSelectionModel *result;
|
||||
GString *changes;
|
||||
|
||||
result = GTK_SELECTION_MODEL (gtk_single_selection_new (G_LIST_MODEL (store)));
|
||||
|
||||
/* We want to return an empty selection unless autoselect is true,
|
||||
* so undo the initial selection due to autoselect defaulting to TRUE.
|
||||
*/
|
||||
gtk_single_selection_set_autoselect (GTK_SINGLE_SELECTION (result), FALSE);
|
||||
gtk_single_selection_set_can_unselect (GTK_SINGLE_SELECTION (result), TRUE);
|
||||
gtk_selection_model_unselect_item (result, 0);
|
||||
|
||||
gtk_single_selection_set_autoselect (GTK_SINGLE_SELECTION (result), autoselect);
|
||||
gtk_single_selection_set_can_unselect (GTK_SINGLE_SELECTION (result), can_unselect);
|
||||
|
||||
changes = g_string_new ("");
|
||||
g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes);
|
||||
g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes);
|
||||
|
||||
changes = g_string_new ("");
|
||||
g_object_set_qdata_full (G_OBJECT(result), selection_quark, changes, free_changes);
|
||||
g_signal_connect (result, "selection-changed", G_CALLBACK (selection_changed), changes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
test_create (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 2);
|
||||
selection = new_model (store, FALSE, FALSE);
|
||||
g_assert_false (gtk_single_selection_get_autoselect (GTK_SINGLE_SELECTION (selection)));
|
||||
|
||||
assert_model (selection, "1 3 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
assert_model (selection, "1 3 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_changes (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store, FALSE, FALSE);
|
||||
assert_model (selection, "1 2 3 4 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_list_store_remove (store, 3);
|
||||
assert_model (selection, "1 2 3 5");
|
||||
assert_changes (selection, "-3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
insert (store, 3, 99);
|
||||
assert_model (selection, "1 2 3 99 5");
|
||||
assert_changes (selection, "+3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
splice (store, 3, 2, (guint[]) { 97 }, 1);
|
||||
assert_model (selection, "1 2 3 97");
|
||||
assert_changes (selection, "3-2+1");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_selection (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
gboolean ret;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store, TRUE, FALSE);
|
||||
assert_selection (selection, "1");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_item (selection, 3, FALSE, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "4");
|
||||
assert_selection_changes (selection, "0:4");
|
||||
|
||||
ret = gtk_selection_model_unselect_item (selection, 3);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "4");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_item (selection, 1, FALSE, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "2");
|
||||
assert_selection_changes (selection, "1:3");
|
||||
|
||||
ret = gtk_selection_model_select_range (selection, 3, 2, FALSE);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "2");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_unselect_range (selection, 4, 2);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "2");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_all (selection);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "2");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_unselect_all (selection);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "2");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_autoselect (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (2, 1, 1);
|
||||
selection = new_model (store, TRUE, FALSE);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
add (store, 1);
|
||||
assert_selection (selection, "1");
|
||||
assert_selection_changes (selection, "0:1");
|
||||
|
||||
splice (store, 0, 1, (guint[]) { 97 }, 1);
|
||||
assert_selection (selection, "97");
|
||||
assert_selection_changes (selection, "0:1");
|
||||
|
||||
ignore_changes (selection);
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_can_unselect (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
gboolean ret;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store, TRUE, FALSE);
|
||||
assert_selection (selection, "1");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_unselect_item (selection, 0);
|
||||
g_assert_false (ret);
|
||||
assert_selection (selection, "1");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
gtk_single_selection_set_can_unselect (GTK_SINGLE_SELECTION (selection), TRUE);
|
||||
|
||||
assert_selection (selection, "1");
|
||||
ret = gtk_selection_model_unselect_item (selection, 0);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "0:1");
|
||||
|
||||
ignore_changes (selection);
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static int
|
||||
sort_inverse (gconstpointer a, gconstpointer b, gpointer data)
|
||||
{
|
||||
int ia = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (a), number_quark));
|
||||
int ib = GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (b), number_quark));
|
||||
|
||||
return ib - ia;
|
||||
}
|
||||
|
||||
static void
|
||||
test_persistence (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store, TRUE, FALSE);
|
||||
assert_selection (selection, "1");
|
||||
assert_selection_changes (selection, "");
|
||||
g_assert_true (gtk_selection_model_is_selected (selection, 0));
|
||||
g_assert_false (gtk_selection_model_is_selected (selection, 4));
|
||||
|
||||
g_list_store_sort (store, sort_inverse, NULL);
|
||||
|
||||
assert_selection (selection, "1");
|
||||
assert_selection_changes (selection, "");
|
||||
g_assert_false (gtk_selection_model_is_selected (selection, 0));
|
||||
g_assert_true (gtk_selection_model_is_selected (selection, 4));
|
||||
|
||||
ignore_changes (selection);
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
setlocale (LC_ALL, "C");
|
||||
g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s");
|
||||
|
||||
number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released.");
|
||||
changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?");
|
||||
selection_quark = g_quark_from_static_string ("Mana mana, badibidibi");
|
||||
|
||||
g_test_add_func ("/singleselection/create", test_create);
|
||||
#if GLIB_CHECK_VERSION (2, 58, 0) /* g_list_store_splice() is broken before 2.58 */
|
||||
g_test_add_func ("/singleselection/changes", test_changes);
|
||||
#endif
|
||||
g_test_add_func ("/singleselection/selection", test_selection);
|
||||
g_test_add_func ("/singleselection/autoselect", test_autoselect);
|
||||
g_test_add_func ("/singleselection/can-unselect", test_can_unselect);
|
||||
g_test_add_func ("/singleselection/persistence", test_persistence);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
341
testsuite/gtk/slicelistmodel.c
Normal file
341
testsuite/gtk/slicelistmodel.c
Normal file
@@ -0,0 +1,341 @@
|
||||
/*
|
||||
* Copyright (C) 2019, Red Hat, Inc.
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GQuark number_quark;
|
||||
static GQuark changes_quark;
|
||||
|
||||
static guint
|
||||
get (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
GObject *object = g_list_model_get_item (model, position);
|
||||
g_assert (object != NULL);
|
||||
return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
|
||||
}
|
||||
|
||||
static char *
|
||||
model_to_string (GListModel *model)
|
||||
{
|
||||
GString *string = g_string_new (NULL);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (i > 0)
|
||||
g_string_append (string, " ");
|
||||
g_string_append_printf (string, "%u", get (model, i));
|
||||
}
|
||||
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step);
|
||||
|
||||
static GObject *
|
||||
make_object (guint number)
|
||||
{
|
||||
GObject *object;
|
||||
|
||||
/* 0 cannot be differentiated from NULL, so don't use it */
|
||||
g_assert (number != 0);
|
||||
|
||||
object = g_object_new (G_TYPE_OBJECT, NULL);
|
||||
g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static void
|
||||
splice (GListStore *store,
|
||||
guint pos,
|
||||
guint removed,
|
||||
guint *numbers,
|
||||
guint added)
|
||||
{
|
||||
GObject *objects[added];
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
objects[i] = make_object (numbers[i]);
|
||||
|
||||
g_list_store_splice (store, pos, removed, (gpointer *) objects, added);
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
g_object_unref (objects[i]);
|
||||
}
|
||||
|
||||
static void
|
||||
add (GListStore *store,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_append (store, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
static void
|
||||
insert (GListStore *store,
|
||||
guint position,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_insert (store, position, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
#define assert_model(model, expected) G_STMT_START{ \
|
||||
char *s = model_to_string (G_LIST_MODEL (model)); \
|
||||
if (!g_str_equal (s, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, s, "==", expected); \
|
||||
g_free (s); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_changes(model, expected) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
|
||||
if (!g_str_equal (changes->str, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, changes->str, "==", expected); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
static GListStore *
|
||||
new_empty_store (void)
|
||||
{
|
||||
return g_list_store_new (G_TYPE_OBJECT);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step)
|
||||
{
|
||||
GListStore *store = new_empty_store ();
|
||||
guint i;
|
||||
|
||||
for (i = start; i <= end; i += step)
|
||||
add (store, i);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
static void
|
||||
items_changed (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GString *changes)
|
||||
{
|
||||
g_assert (removed != 0 || added != 0);
|
||||
|
||||
if (changes->len)
|
||||
g_string_append (changes, ", ");
|
||||
|
||||
if (removed == 1 && added == 0)
|
||||
{
|
||||
g_string_append_printf (changes, "-%u", position);
|
||||
}
|
||||
else if (removed == 0 && added == 1)
|
||||
{
|
||||
g_string_append_printf (changes, "+%u", position);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_string_append_printf (changes, "%u", position);
|
||||
if (removed > 0)
|
||||
g_string_append_printf (changes, "-%u", removed);
|
||||
if (added > 0)
|
||||
g_string_append_printf (changes, "+%u", added);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
free_changes (gpointer data)
|
||||
{
|
||||
GString *changes = data;
|
||||
|
||||
/* all changes must have been checked via assert_changes() before */
|
||||
g_assert_cmpstr (changes->str, ==, "");
|
||||
|
||||
g_string_free (changes, TRUE);
|
||||
}
|
||||
|
||||
static GtkSliceListModel *
|
||||
new_model (GListStore *store, guint offset, guint size)
|
||||
{
|
||||
GtkSliceListModel *result;
|
||||
GString *changes;
|
||||
|
||||
result = gtk_slice_list_model_new_for_type (G_TYPE_OBJECT);
|
||||
if (store)
|
||||
gtk_slice_list_model_set_model (result, G_LIST_MODEL (store));
|
||||
gtk_slice_list_model_set_offset (result, offset);
|
||||
gtk_slice_list_model_set_size (result, size);
|
||||
|
||||
changes = g_string_new ("");
|
||||
g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes);
|
||||
g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
test_create_empty (void)
|
||||
{
|
||||
GtkSliceListModel *slice;
|
||||
|
||||
slice = new_model (NULL, 0, 0);
|
||||
assert_model (slice, "");
|
||||
assert_changes (slice, "");
|
||||
|
||||
g_object_unref (slice);
|
||||
}
|
||||
|
||||
static void
|
||||
test_create (void)
|
||||
{
|
||||
GtkSliceListModel *slice;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 2);
|
||||
slice = new_model (store, 0, 10);
|
||||
assert_model (slice, "1 3 5");
|
||||
assert_changes (slice, "");
|
||||
|
||||
g_object_unref (store);
|
||||
assert_model (slice, "1 3 5");
|
||||
assert_changes (slice, "");
|
||||
|
||||
g_object_unref (slice);
|
||||
}
|
||||
|
||||
static void
|
||||
test_set_model (void)
|
||||
{
|
||||
GtkSliceListModel *slice;
|
||||
GListStore *store;
|
||||
|
||||
slice = new_model (NULL, 0, 2);
|
||||
assert_model (slice, "");
|
||||
assert_changes (slice, "");
|
||||
|
||||
store = new_store (1, 7, 2);
|
||||
gtk_slice_list_model_set_model (slice, G_LIST_MODEL (store));
|
||||
assert_model (slice, "1 3");
|
||||
assert_changes (slice, "0+2");
|
||||
|
||||
gtk_slice_list_model_set_model (slice, NULL);
|
||||
assert_model (slice, "");
|
||||
assert_changes (slice, "0-2");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (slice);
|
||||
}
|
||||
|
||||
static void
|
||||
test_set_slice (void)
|
||||
{
|
||||
GtkSliceListModel *slice;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 7, 2);
|
||||
slice = new_model (store, 0, 3);
|
||||
assert_model (slice, "1 3 5");
|
||||
assert_changes (slice, "");
|
||||
|
||||
gtk_slice_list_model_set_offset (slice, 1);
|
||||
assert_model (slice, "3 5 7");
|
||||
assert_changes (slice, "0-3+3");
|
||||
|
||||
gtk_slice_list_model_set_size (slice, 2);
|
||||
assert_model (slice, "3 5");
|
||||
assert_changes (slice, "-2");
|
||||
|
||||
gtk_slice_list_model_set_size (slice, 10);
|
||||
assert_model (slice, "3 5 7");
|
||||
assert_changes (slice, "+2");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (slice);
|
||||
}
|
||||
|
||||
static void
|
||||
test_changes (void)
|
||||
{
|
||||
GtkSliceListModel *slice;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 20, 1);
|
||||
slice = new_model (store, 10, 5);
|
||||
assert_model (slice, "11 12 13 14 15");
|
||||
assert_changes (slice, "");
|
||||
|
||||
g_list_store_remove (store, 19);
|
||||
assert_changes (slice, "");
|
||||
|
||||
g_list_store_remove (store, 1);
|
||||
assert_model (slice, "12 13 14 15 16");
|
||||
assert_changes (slice, "0-5+5");
|
||||
|
||||
insert (store, 12, 99);
|
||||
assert_model (slice, "12 13 99 14 15");
|
||||
assert_changes (slice, "2-3+3");
|
||||
|
||||
splice (store, 13, 6, (guint[]) { 97 }, 1);
|
||||
assert_model (slice, "12 13 99 97");
|
||||
assert_changes (slice, "3-2+1");
|
||||
|
||||
splice (store, 13, 1, (guint[]) { 36, 37, 38 }, 3);
|
||||
assert_model (slice, "12 13 99 36 37");
|
||||
assert_changes (slice, "3-1+2");
|
||||
|
||||
g_list_store_remove_all (store);
|
||||
assert_model (slice, "");
|
||||
assert_changes (slice, "0-5");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (slice);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
setlocale (LC_ALL, "C");
|
||||
g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s");
|
||||
|
||||
number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released.");
|
||||
changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?");
|
||||
|
||||
g_test_add_func ("/slicelistmodel/create_empty", test_create_empty);
|
||||
g_test_add_func ("/slicelistmodel/create", test_create);
|
||||
g_test_add_func ("/slicelistmodel/set-model", test_set_model);
|
||||
g_test_add_func ("/slicelistmodel/set-slice", test_set_slice);
|
||||
#if GLIB_CHECK_VERSION (2, 58, 0) /* g_list_store_splice() is broken before 2.58 */
|
||||
g_test_add_func ("/slicelistmodel/changes", test_changes);
|
||||
#endif
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
Reference in New Issue
Block a user