Compare commits
26 Commits
main
...
columnview
Author | SHA1 | Date | |
---|---|---|---|
|
543cf3f907 | ||
|
e904668cb1 | ||
|
15d494ddc4 | ||
|
92729a9551 | ||
|
e98f70d6d5 | ||
|
84b6d14ad2 | ||
|
3f5e859795 | ||
|
780a9c1a1f | ||
|
b9c3a15d60 | ||
|
076a40c9bb | ||
|
d8ab29a431 | ||
|
ea0b36b330 | ||
|
c8157480ce | ||
|
3eb0f92705 | ||
|
10ac5cd977 | ||
|
d41fe03b73 | ||
|
bf8856e3fb | ||
|
4a46e8331e | ||
|
241b20c39f | ||
|
151cd0a296 | ||
|
05b6becd2f | ||
|
a2d6f1f0cb | ||
|
d2af96f9c8 | ||
|
b1ab5e187d | ||
|
fd51444967 | ||
|
843999265b |
@@ -422,9 +422,10 @@ create_color_grid (void)
|
||||
g_object_unref (factory);
|
||||
|
||||
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
|
||||
gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (gridview), TRUE);
|
||||
|
||||
model = G_LIST_MODEL (gtk_sort_list_model_new (create_colors_model (), NULL));
|
||||
selection = G_LIST_MODEL (gtk_no_selection_new (model));
|
||||
selection = G_LIST_MODEL (gtk_multi_selection_new (model));
|
||||
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), selection);
|
||||
g_object_unref (selection);
|
||||
g_object_unref (model);
|
||||
|
@@ -70,6 +70,7 @@
|
||||
<xi:include href="xml/gtkselectionmodel.xml" />
|
||||
<xi:include href="xml/gtknoselection.xml" />
|
||||
<xi:include href="xml/gtksingleselection.xml" />
|
||||
<xi:include href="xml/gtkmultiselection.xml" />
|
||||
<xi:include href="xml/gtkdirectorylist.xml" />
|
||||
</chapter>
|
||||
|
||||
|
@@ -392,6 +392,16 @@ gtk_single_selection_set_can_unselect
|
||||
gtk_single_selection_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkmultiselection</FILE>
|
||||
<TITLE>GtkMultiSeledction</TITLE>
|
||||
GtkMultiSelection
|
||||
gtk_multi_selection_new
|
||||
gtk_multi_selection_copy
|
||||
<SUBSECTION Private>
|
||||
gtk_multi_selection_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklistitem</FILE>
|
||||
<TITLE>GtkListItem</TITLE>
|
||||
@@ -479,6 +489,11 @@ gtk_list_view_set_show_separators
|
||||
gtk_list_view_get_show_separators
|
||||
gtk_list_view_set_single_click_activate
|
||||
gtk_list_view_get_single_click_activate
|
||||
gtk_list_view_set_enable_rubberband
|
||||
gtk_list_view_get_enable_rubberband
|
||||
gtk_list_view_set_search_filter
|
||||
gtk_list_view_get_search_filter
|
||||
gtk_list_view_next_match
|
||||
<SUBSECTION Standard>
|
||||
GTK_LIST_VIEW
|
||||
GTK_LIST_VIEW_CLASS
|
||||
@@ -507,6 +522,11 @@ gtk_column_view_set_show_separators
|
||||
gtk_column_view_sort_by_column
|
||||
gtk_column_view_set_single_click_activate
|
||||
gtk_column_view_get_single_click_activate
|
||||
gtk_column_view_set_enable_rubberband
|
||||
gtk_column_view_get_enable_rubberband
|
||||
gtk_column_view_set_search_filter
|
||||
gtk_column_view_get_search_filter
|
||||
gtk_column_view_next_match
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW
|
||||
GTK_COLUMN_VIEW_CLASS
|
||||
@@ -533,6 +553,12 @@ gtk_column_view_column_set_sorter
|
||||
gtk_column_view_column_get_sorter
|
||||
gtk_column_view_column_set_visible
|
||||
gtk_column_view_column_get_visible
|
||||
gtk_column_view_column_set_reorderable
|
||||
gtk_column_view_column_get_reorderable
|
||||
gtk_column_view_column_set_fixed_width
|
||||
gtk_column_view_column_get_fixed_width
|
||||
gtk_column_view_column_set_expand
|
||||
gtk_column_view_column_get_expand
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW_COLUMN
|
||||
GTK_COLUMN_VIEW_COLUMN_CLASS
|
||||
@@ -557,6 +583,11 @@ gtk_grid_view_set_min_columns
|
||||
gtk_grid_view_get_min_columns
|
||||
gtk_grid_view_set_single_click_activate
|
||||
gtk_grid_view_get_single_click_activate
|
||||
gtk_grid_view_set_enable_rubberband
|
||||
gtk_grid_view_get_enable_rubberband
|
||||
gtk_grid_view_set_search_filter
|
||||
gtk_grid_view_get_search_filter
|
||||
gtk_grid_view_next_match
|
||||
<SUBSECTION Standard>
|
||||
GTK_GRID_VIEW
|
||||
GTK_GRID_VIEW_CLASS
|
||||
|
@@ -139,6 +139,7 @@ gtk_menu_button_get_type
|
||||
gtk_message_dialog_get_type
|
||||
gtk_mount_operation_get_type
|
||||
gtk_multi_filter_get_type
|
||||
gtk_multi_selection_get_type
|
||||
gtk_multi_sorter_get_type
|
||||
gtk_native_get_type
|
||||
gtk_native_dialog_get_type
|
||||
|
@@ -174,6 +174,7 @@
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtkmountoperation.h>
|
||||
#include <gtk/gtkmultifilter.h>
|
||||
#include <gtk/gtkmultiselection.h>
|
||||
#include <gtk/gtkmultisorter.h>
|
||||
#include <gtk/gtknative.h>
|
||||
#include <gtk/gtknativedialog.h>
|
||||
|
@@ -34,6 +34,13 @@
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkscrollable.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtksizerequest.h"
|
||||
#include "gtkadjustment.h"
|
||||
#include "gtkgesturedrag.h"
|
||||
#include "gtkeventcontrollermotion.h"
|
||||
#include "gtkdragsource.h"
|
||||
#include "gtkeventcontrollerkey.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcolumnview
|
||||
@@ -63,6 +70,21 @@ struct _GtkColumnView
|
||||
GtkColumnListItemFactory *factory;
|
||||
|
||||
GtkSorter *sorter;
|
||||
|
||||
GtkAdjustment *hadjustment;
|
||||
|
||||
gboolean in_column_resize;
|
||||
gboolean in_column_reorder;
|
||||
int drag_pos;
|
||||
int drag_x;
|
||||
int drag_offset;
|
||||
int drag_column_x;
|
||||
|
||||
GtkGesture *drag_gesture;
|
||||
|
||||
guint autoscroll_id;
|
||||
double autoscroll_x;
|
||||
double autoscroll_delta;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewClass
|
||||
@@ -82,6 +104,8 @@ enum
|
||||
PROP_VADJUSTMENT,
|
||||
PROP_VSCROLL_POLICY,
|
||||
PROP_SINGLE_CLICK_ACTIVATE,
|
||||
PROP_ENABLE_RUBBERBAND,
|
||||
PROP_SEARCH_FILTER,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
@@ -169,48 +193,77 @@ gtk_column_view_allocate_columns (GtkColumnView *self,
|
||||
int width)
|
||||
{
|
||||
GtkScrollablePolicy scroll_policy;
|
||||
int col_min, col_nat, widget_min, widget_nat, extra, col_size, x;
|
||||
int col_min, col_nat, extra, col_size, x;
|
||||
int n, n_expand, expand_size, n_extra;
|
||||
guint i;
|
||||
GtkRequestedSize *sizes;
|
||||
|
||||
gtk_column_view_measure_across (self, &col_min, &col_nat);
|
||||
gtk_widget_measure (GTK_WIDGET (self),
|
||||
GTK_ORIENTATION_HORIZONTAL, -1,
|
||||
&widget_min, &widget_nat,
|
||||
NULL, NULL);
|
||||
|
||||
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
{
|
||||
extra = widget_min - col_min;
|
||||
col_size = col_min;
|
||||
}
|
||||
else
|
||||
{
|
||||
extra = widget_nat - col_nat;
|
||||
col_size = col_nat;
|
||||
}
|
||||
width -= extra;
|
||||
width = MAX (width, col_size);
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
|
||||
n_expand = 0;
|
||||
sizes = g_newa (GtkRequestedSize, n);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
gtk_column_view_column_measure (column, &col_min, &col_nat);
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
col_size = col_min;
|
||||
if (gtk_column_view_column_get_visible (column))
|
||||
{
|
||||
gtk_column_view_column_measure (column, &sizes[i].minimum_size, &sizes[i].natural_size);
|
||||
if (gtk_column_view_column_get_expand (column))
|
||||
n_expand++;
|
||||
}
|
||||
else
|
||||
col_size = col_nat;
|
||||
sizes[i].minimum_size = sizes[i].natural_size = 0;
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
gtk_column_view_column_allocate (column, x, col_size);
|
||||
x += col_size;
|
||||
gtk_column_view_measure_across (self, &col_min, &col_nat);
|
||||
|
||||
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
extra = MAX (width - col_min, 0);
|
||||
else
|
||||
extra = MAX (width - col_min, col_nat - col_min);
|
||||
|
||||
extra = gtk_distribute_natural_allocation (extra, n, sizes);
|
||||
if (n_expand > 0)
|
||||
{
|
||||
expand_size = extra / n_expand;
|
||||
n_extra = extra % n_expand;
|
||||
}
|
||||
else
|
||||
expand_size = n_extra = 0;
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
if (gtk_column_view_column_get_visible (column))
|
||||
{
|
||||
col_size = sizes[i].minimum_size;
|
||||
if (gtk_column_view_column_get_expand (column))
|
||||
{
|
||||
col_size += expand_size;
|
||||
if (n_extra > 0)
|
||||
{
|
||||
col_size++;
|
||||
n_extra--;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_column_view_column_allocate (column, x, col_size);
|
||||
if (self->in_column_reorder && i == self->drag_pos)
|
||||
gtk_column_view_column_set_header_position (column, self->drag_x);
|
||||
|
||||
x += col_size;
|
||||
}
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
return width + extra;
|
||||
return x;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -220,8 +273,9 @@ gtk_column_view_allocate (GtkWidget *widget,
|
||||
int baseline)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
|
||||
int full_width, header_height, min, nat;
|
||||
int full_width, header_height, min, nat, x;
|
||||
|
||||
x = gtk_adjustment_get_value (self->hadjustment);
|
||||
full_width = gtk_column_view_allocate_columns (self, width);
|
||||
|
||||
gtk_widget_measure (self->header, GTK_ORIENTATION_VERTICAL, full_width, &min, &nat, NULL, NULL);
|
||||
@@ -229,11 +283,14 @@ gtk_column_view_allocate (GtkWidget *widget,
|
||||
header_height = min;
|
||||
else
|
||||
header_height = nat;
|
||||
gtk_widget_allocate (self->header, full_width, header_height, -1, NULL);
|
||||
gtk_widget_allocate (self->header, full_width, header_height, -1,
|
||||
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (-x, 0)));
|
||||
|
||||
gtk_widget_allocate (GTK_WIDGET (self->listview),
|
||||
full_width, height - header_height, -1,
|
||||
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (0, header_height)));
|
||||
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (-x, header_height)));
|
||||
|
||||
gtk_adjustment_configure (self->hadjustment, x, 0, full_width, width * 0.1, width * 0.9, width);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -244,6 +301,23 @@ gtk_column_view_activate_cb (GtkListView *listview,
|
||||
g_signal_emit (self, signals[ACTIVATE], 0, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
adjustment_value_changed_cb (GtkAdjustment *adjustment,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
gtk_widget_queue_allocate (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
clear_adjustment (GtkColumnView *self)
|
||||
{
|
||||
if (self->hadjustment == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->hadjustment, adjustment_value_changed_cb, self);
|
||||
g_clear_object (&self->hadjustment);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_dispose (GObject *object)
|
||||
{
|
||||
@@ -262,6 +336,7 @@ gtk_column_view_dispose (GObject *object)
|
||||
g_clear_object (&self->factory);
|
||||
|
||||
g_clear_object (&self->sorter);
|
||||
clear_adjustment (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
|
||||
}
|
||||
@@ -291,7 +366,7 @@ gtk_column_view_get_property (GObject *object,
|
||||
break;
|
||||
|
||||
case PROP_HADJUSTMENT:
|
||||
g_value_set_object (value, gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)));
|
||||
g_value_set_object (value, self->hadjustment);
|
||||
break;
|
||||
|
||||
case PROP_HSCROLL_POLICY:
|
||||
@@ -322,6 +397,14 @@ gtk_column_view_get_property (GObject *object,
|
||||
g_value_set_boolean (value, gtk_column_view_get_single_click_activate (self));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
g_value_set_boolean (value, gtk_column_view_get_enable_rubberband (self));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
g_value_set_object (value, gtk_column_view_get_search_filter (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -335,13 +418,24 @@ gtk_column_view_set_property (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
GtkAdjustment *adjustment;
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_HADJUSTMENT:
|
||||
if (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)) != g_value_get_object (value))
|
||||
adjustment = g_value_get_object (value);
|
||||
if (adjustment == NULL)
|
||||
adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
g_object_ref_sink (adjustment);
|
||||
|
||||
if (self->hadjustment != adjustment)
|
||||
{
|
||||
gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (self->listview), g_value_get_object (value));
|
||||
clear_adjustment (self);
|
||||
|
||||
self->hadjustment = adjustment;
|
||||
|
||||
g_signal_connect (adjustment, "value-changed", G_CALLBACK (adjustment_value_changed_cb), self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HADJUSTMENT]);
|
||||
}
|
||||
break;
|
||||
@@ -382,6 +476,14 @@ gtk_column_view_set_property (GObject *object,
|
||||
gtk_column_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
gtk_column_view_set_enable_rubberband (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
gtk_column_view_set_search_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -478,6 +580,30 @@ gtk_column_view_class_init (GtkColumnViewClass *klass)
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkColumnView:enable-rubberband:
|
||||
*
|
||||
* Allow rubberband selection
|
||||
*/
|
||||
properties[PROP_ENABLE_RUBBERBAND] =
|
||||
g_param_spec_boolean ("enable-rubberband",
|
||||
P_("Enable rubberband selection"),
|
||||
P_("Allow selecting items by dragging with the mouse"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkColumnView:search-filter:
|
||||
*
|
||||
* Filter used for search
|
||||
*/
|
||||
properties[PROP_SEARCH_FILTER] =
|
||||
g_param_spec_object ("search-filter",
|
||||
P_("Search filter"),
|
||||
P_("Filter used for searching"),
|
||||
GTK_TYPE_FILTER,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
@@ -507,9 +633,366 @@ gtk_column_view_class_init (GtkColumnViewClass *klass)
|
||||
gtk_widget_class_set_css_name (widget_class, I_("treeview"));
|
||||
}
|
||||
|
||||
static void update_column_resize (GtkColumnView *self,
|
||||
double x);
|
||||
static void update_column_reorder (GtkColumnView *self,
|
||||
double x);
|
||||
|
||||
static gboolean
|
||||
autoscroll_cb (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer data)
|
||||
{
|
||||
GtkColumnView *self = data;
|
||||
|
||||
gtk_adjustment_set_value (self->hadjustment,
|
||||
gtk_adjustment_get_value (self->hadjustment) + self->autoscroll_delta);
|
||||
|
||||
self->autoscroll_x += self->autoscroll_delta;
|
||||
|
||||
if (self->in_column_resize)
|
||||
update_column_resize (self, self->autoscroll_x);
|
||||
else if (self->in_column_reorder)
|
||||
update_column_reorder (self, self->autoscroll_x);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
add_autoscroll (GtkColumnView *self,
|
||||
double x,
|
||||
double delta)
|
||||
{
|
||||
self->autoscroll_x = x;
|
||||
self->autoscroll_delta = delta;
|
||||
|
||||
if (self->autoscroll_id == 0)
|
||||
self->autoscroll_id = gtk_widget_add_tick_callback (GTK_WIDGET (self), autoscroll_cb, self, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_autoscroll (GtkColumnView *self)
|
||||
{
|
||||
if (self->autoscroll_id != 0)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->autoscroll_id);
|
||||
self->autoscroll_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define DRAG_WIDTH 6
|
||||
|
||||
static gboolean
|
||||
gtk_column_view_in_resize_rect (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
GtkWidget *header;
|
||||
graphene_rect_t rect;
|
||||
|
||||
header = gtk_column_view_column_get_header (column);
|
||||
|
||||
if (!gtk_widget_compute_bounds (header, self->header, &rect))
|
||||
return FALSE;
|
||||
|
||||
rect.origin.x += rect.size.width - DRAG_WIDTH / 2;
|
||||
rect.size.width = DRAG_WIDTH;
|
||||
|
||||
return graphene_rect_contains_point (&rect, &(graphene_point_t) { x, y});
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_column_view_in_header (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
GtkWidget *header;
|
||||
graphene_rect_t rect;
|
||||
|
||||
header = gtk_column_view_column_get_header (column);
|
||||
|
||||
if (!gtk_widget_compute_bounds (header, self->header, &rect))
|
||||
return FALSE;
|
||||
|
||||
return graphene_rect_contains_point (&rect, &(graphene_point_t) { x, y});
|
||||
}
|
||||
|
||||
static void
|
||||
header_drag_begin (GtkGestureDrag *gesture,
|
||||
double start_x,
|
||||
double start_y,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
self->drag_pos = -1;
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
|
||||
for (i = 0; !self->in_column_resize && i < n; i++)
|
||||
{
|
||||
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
|
||||
if (!gtk_column_view_column_get_visible (column))
|
||||
{
|
||||
g_object_unref (column);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 < n &&
|
||||
gtk_column_view_column_get_resizable (column) &&
|
||||
gtk_column_view_in_resize_rect (self, column, start_x, start_y))
|
||||
{
|
||||
int size;
|
||||
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
if (!gtk_widget_has_focus (GTK_WIDGET (self)))
|
||||
gtk_widget_grab_focus (GTK_WIDGET (self));
|
||||
|
||||
gtk_column_view_column_get_allocation (column, NULL, &size);
|
||||
gtk_column_view_column_set_fixed_width (column, size);
|
||||
|
||||
self->drag_pos = i;
|
||||
self->drag_x = start_x - size;
|
||||
self->in_column_resize = TRUE;
|
||||
|
||||
g_object_unref (column);
|
||||
break;
|
||||
}
|
||||
|
||||
if (gtk_column_view_column_get_reorderable (column) &&
|
||||
gtk_column_view_in_header (self, column, start_x, start_y))
|
||||
{
|
||||
int pos;
|
||||
|
||||
gtk_column_view_column_get_allocation (column, &pos, NULL);
|
||||
|
||||
self->drag_pos = i;
|
||||
self->drag_offset = start_x - pos;
|
||||
|
||||
g_object_unref (column);
|
||||
break;
|
||||
}
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
header_drag_end (GtkGestureDrag *gesture,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
double start_x, x;
|
||||
|
||||
gtk_gesture_drag_get_start_point (gesture, &start_x, NULL);
|
||||
x = start_x + offset_x;
|
||||
|
||||
remove_autoscroll (self);
|
||||
|
||||
if (self->in_column_resize)
|
||||
{
|
||||
self->in_column_resize = FALSE;
|
||||
}
|
||||
else if (self->in_column_reorder)
|
||||
{
|
||||
GdkEventSequence *sequence;
|
||||
GtkColumnViewColumn *column;
|
||||
GtkWidget *header;
|
||||
int i;
|
||||
|
||||
self->in_column_reorder = FALSE;
|
||||
|
||||
if (self->drag_pos == -1)
|
||||
return;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
|
||||
header = gtk_column_view_column_get_header (column);
|
||||
gtk_style_context_remove_class (gtk_widget_get_style_context (header), "dnd");
|
||||
|
||||
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
|
||||
if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
|
||||
return;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *col = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
|
||||
if (gtk_column_view_column_get_visible (col))
|
||||
{
|
||||
int pos, size;
|
||||
|
||||
gtk_column_view_column_get_allocation (col, &pos, &size);
|
||||
if (pos <= x && x <= pos + size)
|
||||
{
|
||||
gtk_column_view_insert_column (self, i, column);
|
||||
g_object_unref (col);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (col);
|
||||
}
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_column_resize (GtkColumnView *self,
|
||||
double x)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
|
||||
gtk_column_view_column_set_fixed_width (column, MAX (x - self->drag_x, 0));
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
static void
|
||||
update_column_reorder (GtkColumnView *self,
|
||||
double x)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
int width;
|
||||
int size;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
|
||||
width = gtk_widget_get_allocated_width (GTK_WIDGET (self->header));
|
||||
gtk_column_view_column_get_allocation (column, NULL, &size);
|
||||
|
||||
self->drag_x = CLAMP (x - self->drag_offset, 0, width - size);
|
||||
|
||||
gtk_widget_queue_allocate (GTK_WIDGET (self));
|
||||
gtk_column_view_column_queue_resize (column);
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
#define SCROLL_EDGE_SIZE 15
|
||||
|
||||
static void
|
||||
header_drag_update (GtkGestureDrag *gesture,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
GdkEventSequence *sequence;
|
||||
double start_x, x;
|
||||
|
||||
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
|
||||
if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
|
||||
return;
|
||||
|
||||
if (self->drag_pos == -1)
|
||||
return;
|
||||
|
||||
if (!self->in_column_resize && !self->in_column_reorder)
|
||||
{
|
||||
if (gtk_drag_check_threshold (GTK_WIDGET (self), 0, 0, offset_x, 0))
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
GtkWidget *header;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
|
||||
header = gtk_column_view_column_get_header (column);
|
||||
|
||||
gtk_widget_insert_after (header, self->header, gtk_widget_get_last_child (self->header));
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (header), "dnd");
|
||||
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
if (!gtk_widget_has_focus (GTK_WIDGET (self)))
|
||||
gtk_widget_grab_focus (GTK_WIDGET (self));
|
||||
|
||||
self->in_column_reorder = TRUE;
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_gesture_drag_get_start_point (gesture, &start_x, NULL);
|
||||
x = start_x + offset_x;
|
||||
|
||||
if (self->in_column_resize)
|
||||
update_column_resize (self, x);
|
||||
else if (self->in_column_reorder)
|
||||
update_column_reorder (self, x);
|
||||
|
||||
if (self->in_column_resize || self->in_column_reorder)
|
||||
{
|
||||
double value, page_size, upper;
|
||||
|
||||
value = gtk_adjustment_get_value (self->hadjustment);
|
||||
page_size = gtk_adjustment_get_page_size (self->hadjustment);
|
||||
upper = gtk_adjustment_get_upper (self->hadjustment);
|
||||
|
||||
if (x - value < SCROLL_EDGE_SIZE && value > 0)
|
||||
add_autoscroll (self, x, - (SCROLL_EDGE_SIZE - (x - value))/3.0);
|
||||
else if (value + page_size - x < SCROLL_EDGE_SIZE && value + page_size < upper)
|
||||
add_autoscroll (self, x, (SCROLL_EDGE_SIZE - (value + page_size - x))/3.0);
|
||||
else
|
||||
remove_autoscroll (self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
header_motion (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
gboolean cursor_set = FALSE;
|
||||
int i, n;
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
|
||||
if (!gtk_column_view_column_get_visible (column))
|
||||
{
|
||||
g_object_unref (column);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 < n &&
|
||||
gtk_column_view_column_get_resizable (column) &&
|
||||
gtk_column_view_in_resize_rect (self, column, x, y))
|
||||
{
|
||||
gtk_widget_set_cursor_from_name (self->header, "col-resize");
|
||||
cursor_set = TRUE;
|
||||
}
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
if (!cursor_set)
|
||||
gtk_widget_set_cursor (self->header, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
header_key_pressed (GtkEventControllerKey *controller,
|
||||
guint keyval,
|
||||
guint keycode,
|
||||
GdkModifierType modifiers,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
if (self->in_column_reorder)
|
||||
{
|
||||
if (keyval == GDK_KEY_Escape)
|
||||
gtk_gesture_set_state (self->drag_gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_init (GtkColumnView *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
|
||||
|
||||
self->header = gtk_list_item_widget_new (NULL, "header");
|
||||
@@ -517,6 +1000,22 @@ gtk_column_view_init (GtkColumnView *self)
|
||||
gtk_widget_set_layout_manager (self->header, gtk_column_view_layout_new (self));
|
||||
gtk_widget_set_parent (self->header, GTK_WIDGET (self));
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
|
||||
g_signal_connect (controller, "drag-begin", G_CALLBACK (header_drag_begin), self);
|
||||
g_signal_connect (controller, "drag-update", G_CALLBACK (header_drag_update), self);
|
||||
g_signal_connect (controller, "drag-end", G_CALLBACK (header_drag_end), self);
|
||||
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
|
||||
gtk_widget_add_controller (self->header, controller);
|
||||
self->drag_gesture = GTK_GESTURE (controller);
|
||||
|
||||
controller = gtk_event_controller_motion_new ();
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (header_motion), self);
|
||||
gtk_widget_add_controller (self->header, controller);
|
||||
|
||||
controller = gtk_event_controller_key_new ();
|
||||
g_signal_connect (controller, "key-pressed", G_CALLBACK (header_key_pressed), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
self->sorter = gtk_column_view_sorter_new ();
|
||||
self->factory = gtk_column_list_item_factory_new (self);
|
||||
self->listview = GTK_LIST_VIEW (gtk_list_view_new_with_factory (
|
||||
@@ -530,6 +1029,7 @@ gtk_column_view_init (GtkColumnView *self)
|
||||
g_quark_from_static_string (I_("view")));
|
||||
|
||||
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
|
||||
gtk_widget_set_focusable (GTK_WIDGET (self), TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -782,6 +1282,12 @@ gtk_column_view_get_header_widget (GtkColumnView *self)
|
||||
return GTK_LIST_ITEM_WIDGET (self->header);
|
||||
}
|
||||
|
||||
GtkListView *
|
||||
gtk_column_view_get_list_view (GtkColumnView *self)
|
||||
{
|
||||
return GTK_LIST_VIEW (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_sorter:
|
||||
* @self: a #GtkColumnView
|
||||
@@ -879,3 +1385,102 @@ gtk_column_view_get_single_click_activate (GtkColumnView *self)
|
||||
|
||||
return gtk_list_view_get_single_click_activate (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_enable_rubberband:
|
||||
* @self: a #GtkColumnView
|
||||
* @enable_rubberband: %TRUE to enable rubberband selection
|
||||
*
|
||||
* Sets whether selections can be changed by dragging with the mouse.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_set_enable_rubberband (GtkColumnView *self,
|
||||
gboolean enable_rubberband)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
|
||||
if (enable_rubberband == gtk_list_view_get_enable_rubberband (self->listview))
|
||||
return;
|
||||
|
||||
gtk_list_view_set_enable_rubberband (self->listview, enable_rubberband);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLE_RUBBERBAND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_enable_rubberband:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns whether rows can be selected by dragging with the mouse.
|
||||
*
|
||||
* Returns: %TRUE if rubberband selection is enabled
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_get_enable_rubberband (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_view_get_enable_rubberband (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_search_filter:
|
||||
* @self: a #GtkColumnView
|
||||
* @filter: (nullable): the filter to use for search, or %NULL
|
||||
*
|
||||
* Sets a search filter.
|
||||
*
|
||||
* The selection will be moved to first item matching the
|
||||
* filter whenever the filter changes.
|
||||
*
|
||||
* This can be used with single selection and a string
|
||||
* filter that is connected to a search entry.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_set_search_filter (GtkColumnView *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
|
||||
|
||||
if (filter == gtk_list_view_get_search_filter (GTK_LIST_VIEW (self->listview)))
|
||||
return;
|
||||
|
||||
gtk_list_view_set_search_filter (GTK_LIST_VIEW (self->listview), filter);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_search_filter:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Gets the search filter that was set with
|
||||
* gtk_column_view_set_search_filter().
|
||||
*
|
||||
* Returns: (transfer none): The search filter of @self
|
||||
*/
|
||||
GtkFilter *
|
||||
gtk_column_view_get_search_filter (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_view_get_search_filter (GTK_LIST_VIEW (self->listview));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_select_next_match:
|
||||
* @self: a #GtkColumnView
|
||||
* @forward: whether to move forward or back
|
||||
*
|
||||
* Moves the selection to the next item matching the
|
||||
* search filter set with gtk_column_view_set_search_filter().
|
||||
*/
|
||||
void
|
||||
gtk_column_view_select_next_match (GtkColumnView *self,
|
||||
gboolean forward)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
|
||||
gtk_list_view_select_next_match (GTK_LIST_VIEW (self->listview), forward);
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include <gtk/gtktypes.h>
|
||||
#include <gtk/gtksortlistmodel.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -92,6 +93,22 @@ void gtk_column_view_set_single_click_activate (GtkColumnView
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_get_single_click_activate (GtkColumnView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_enable_rubberband (GtkColumnView *self,
|
||||
gboolean enable_rubberband);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_get_enable_rubberband (GtkColumnView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_search_filter (GtkColumnView *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_column_view_get_search_filter (GtkColumnView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_select_next_match (GtkColumnView *self,
|
||||
gboolean forward);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_H__ */
|
||||
|
@@ -53,10 +53,18 @@ gtk_column_view_cell_measure (GtkWidget *widget,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkColumnViewCell *cell = GTK_COLUMN_VIEW_CELL (widget);
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
int fixed_width = gtk_column_view_column_get_fixed_width (cell->column);
|
||||
if (fixed_width > -1)
|
||||
*minimum = *natural = fixed_width;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -60,8 +60,14 @@ struct _GtkColumnViewColumn
|
||||
int natural_size_request;
|
||||
int allocation_offset;
|
||||
int allocation_size;
|
||||
int header_position;
|
||||
|
||||
gboolean visible;
|
||||
int fixed_width;
|
||||
|
||||
guint visible : 1;
|
||||
guint resizable : 1;
|
||||
guint expand : 1;
|
||||
guint reorderable : 1;
|
||||
|
||||
/* This list isn't sorted - this is just caching for performance */
|
||||
GtkColumnViewCell *first_cell; /* no reference, just caching */
|
||||
@@ -80,6 +86,10 @@ enum
|
||||
PROP_TITLE,
|
||||
PROP_SORTER,
|
||||
PROP_VISIBLE,
|
||||
PROP_RESIZABLE,
|
||||
PROP_EXPAND,
|
||||
PROP_REORDERABLE,
|
||||
PROP_FIXED_WIDTH,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
@@ -133,6 +143,22 @@ gtk_column_view_column_get_property (GObject *object,
|
||||
g_value_set_boolean (value, self->visible);
|
||||
break;
|
||||
|
||||
case PROP_RESIZABLE:
|
||||
g_value_set_boolean (value, self->resizable);
|
||||
break;
|
||||
|
||||
case PROP_EXPAND:
|
||||
g_value_set_boolean (value, self->expand);
|
||||
break;
|
||||
|
||||
case PROP_REORDERABLE:
|
||||
g_value_set_boolean (value, self->reorderable);
|
||||
break;
|
||||
|
||||
case PROP_FIXED_WIDTH:
|
||||
g_value_set_int (value, self->fixed_width);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -165,6 +191,22 @@ gtk_column_view_column_set_property (GObject *object,
|
||||
gtk_column_view_column_set_visible (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_RESIZABLE:
|
||||
gtk_column_view_column_set_resizable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_EXPAND:
|
||||
gtk_column_view_column_set_expand (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_REORDERABLE:
|
||||
gtk_column_view_column_set_reorderable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_FIXED_WIDTH:
|
||||
gtk_column_view_column_set_fixed_width (self, g_value_get_int (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -240,6 +282,55 @@ gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:resizable:
|
||||
*
|
||||
* Whether this column is resizable
|
||||
*/
|
||||
properties[PROP_RESIZABLE] =
|
||||
g_param_spec_boolean ("resizable",
|
||||
P_("Resizable"),
|
||||
P_("Whether this column is resizable"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:expand:
|
||||
*
|
||||
* Column gets share of extra width allocated to the view
|
||||
*/
|
||||
properties[PROP_EXPAND] =
|
||||
g_param_spec_boolean ("expand",
|
||||
P_("Expand"),
|
||||
P_("column gets share of extra width"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:reorderable:
|
||||
*
|
||||
* Whether this column is reorderable
|
||||
*/
|
||||
properties[PROP_REORDERABLE] =
|
||||
g_param_spec_boolean ("reorderable",
|
||||
P_("Reorderable"),
|
||||
P_("Whether this column is reorderable"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:fixed-width:
|
||||
*
|
||||
* If not -1, this is the width that the column is allocated,
|
||||
* regardless of the size of its content.
|
||||
*/
|
||||
properties[PROP_FIXED_WIDTH] =
|
||||
g_param_spec_int ("fixed-width",
|
||||
P_("Fixed width"),
|
||||
P_("Fixed width of this column"),
|
||||
-1, G_MAXINT, -1,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
@@ -249,6 +340,10 @@ gtk_column_view_column_init (GtkColumnViewColumn *self)
|
||||
self->minimum_size_request = -1;
|
||||
self->natural_size_request = -1;
|
||||
self->visible = TRUE;
|
||||
self->resizable = FALSE;
|
||||
self->expand = FALSE;
|
||||
self->reorderable = FALSE;
|
||||
self->fixed_width = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -357,6 +452,12 @@ gtk_column_view_column_measure (GtkColumnViewColumn *self,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
if (self->fixed_width > -1)
|
||||
{
|
||||
self->minimum_size_request = self->fixed_width;
|
||||
self->natural_size_request = self->fixed_width;
|
||||
}
|
||||
|
||||
if (self->minimum_size_request < 0)
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
@@ -399,6 +500,7 @@ gtk_column_view_column_allocate (GtkColumnViewColumn *self,
|
||||
{
|
||||
self->allocation_offset = offset;
|
||||
self->allocation_size = size;
|
||||
self->header_position = offset;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -721,3 +823,207 @@ gtk_column_view_column_get_visible (GtkColumnViewColumn *self)
|
||||
|
||||
return self->visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_resizable:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @resizable: whether this column should be resizable
|
||||
*
|
||||
* Sets whether this column should be resizable by dragging.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_resizable (GtkColumnViewColumn *self,
|
||||
gboolean resizable)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
|
||||
if (self->resizable == resizable)
|
||||
return;
|
||||
|
||||
self->resizable = resizable;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_RESIZABLE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_resizable:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns whether this column is resizable.
|
||||
*
|
||||
* Returns: %TRUE if this column is resizable
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_column_get_resizable (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE);
|
||||
|
||||
return self->resizable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_expand:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @expand: %TRUE if this column should expand to fill available sace
|
||||
*
|
||||
* Sets the column to take available extra space.
|
||||
*
|
||||
* The extra space is shared equally amongst all columns that
|
||||
* have the expand set to %TRUE.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_expand (GtkColumnViewColumn *self,
|
||||
gboolean expand)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
|
||||
if (self->expand == expand)
|
||||
return;
|
||||
|
||||
self->expand = expand;
|
||||
|
||||
if (self->visible && self->view)
|
||||
gtk_widget_queue_resize (GTK_WIDGET (self->view));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_EXPAND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_expand:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Returns whether this column should expand.
|
||||
*
|
||||
* Returns: %TRUE if this column expands
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_column_get_expand (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE);
|
||||
|
||||
return self->expand;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_reorderable:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @reorderable: whether this column should be reorderable
|
||||
*
|
||||
* Sets whether this column should be reorderable by dragging.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_reorderable (GtkColumnViewColumn *self,
|
||||
gboolean reorderable)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
|
||||
if (self->reorderable == reorderable)
|
||||
return;
|
||||
|
||||
self->reorderable = reorderable;
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_REORDERABLE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_reorderable:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns whether this column is reorderable.
|
||||
*
|
||||
* Returns: %TRUE if this column is reorderable
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_column_get_reorderable (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE);
|
||||
|
||||
return self->reorderable;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_fixed_width:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @fixed_width: the new fixed width, or -1
|
||||
*
|
||||
* If @fixed_width is not -1, sets the fixed width of @column;
|
||||
* otherwise unsets it.
|
||||
*
|
||||
* Setting a fixed width overrides the automatically calculated
|
||||
* width. Interactive resizing also sets the “fixed-width” property.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_fixed_width (GtkColumnViewColumn *self,
|
||||
int fixed_width)
|
||||
{
|
||||
GtkOverflow overflow;
|
||||
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
g_return_if_fail (fixed_width >= -1);
|
||||
|
||||
if (self->fixed_width == fixed_width)
|
||||
return;
|
||||
|
||||
self->fixed_width = fixed_width;
|
||||
|
||||
if (fixed_width > -1)
|
||||
overflow = GTK_OVERFLOW_HIDDEN;
|
||||
else
|
||||
overflow = GTK_OVERFLOW_VISIBLE;
|
||||
|
||||
if (self->header &&
|
||||
overflow != gtk_widget_get_overflow (GTK_WIDGET (self->header)))
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
|
||||
gtk_widget_set_overflow (GTK_WIDGET (self->header), overflow);
|
||||
|
||||
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
|
||||
gtk_widget_set_overflow (GTK_WIDGET (cell), overflow);
|
||||
}
|
||||
|
||||
gtk_column_view_column_queue_resize (self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_FIXED_WIDTH]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_fixed_width:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Gets the fixed width of the column.
|
||||
*
|
||||
* Returns: the fixed with of the column
|
||||
*/
|
||||
int
|
||||
gtk_column_view_column_get_fixed_width (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), -1);
|
||||
|
||||
return self->fixed_width;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
gtk_column_view_column_get_header (GtkColumnViewColumn *self)
|
||||
{
|
||||
return self->header;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_set_header_position (GtkColumnViewColumn *self,
|
||||
int offset)
|
||||
{
|
||||
self->header_position = offset;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_column_get_header_allocation (GtkColumnViewColumn *self,
|
||||
int *offset,
|
||||
int *size)
|
||||
{
|
||||
if (offset)
|
||||
*offset = self->header_position;
|
||||
|
||||
if (size)
|
||||
*size = self->allocation_size;
|
||||
}
|
||||
|
@@ -78,6 +78,30 @@ void gtk_column_view_column_set_visible (GtkColu
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_column_get_visible (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_fixed_width (GtkColumnViewColumn *self,
|
||||
int fixed_width);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gtk_column_view_column_get_fixed_width (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_resizable (GtkColumnViewColumn *self,
|
||||
gboolean resizable);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_column_get_resizable (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_expand (GtkColumnViewColumn *self,
|
||||
gboolean expand);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_column_get_expand (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_reorderable (GtkColumnViewColumn *self,
|
||||
gboolean reorderable);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_column_get_reorderable (GtkColumnViewColumn *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */
|
||||
|
@@ -33,6 +33,7 @@ void gtk_column_view_column_add_cell (GtkColu
|
||||
void gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell);
|
||||
GtkColumnViewCell * gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self);
|
||||
GtkWidget * gtk_column_view_column_get_header (GtkColumnViewColumn *self);
|
||||
|
||||
void gtk_column_view_column_queue_resize (GtkColumnViewColumn *self);
|
||||
void gtk_column_view_column_measure (GtkColumnViewColumn *self,
|
||||
@@ -47,4 +48,10 @@ void gtk_column_view_column_get_allocation (GtkColu
|
||||
|
||||
void gtk_column_view_column_notify_sort (GtkColumnViewColumn *self);
|
||||
|
||||
void gtk_column_view_column_set_header_position (GtkColumnViewColumn *self,
|
||||
int offset);
|
||||
void gtk_column_view_column_get_header_allocation (GtkColumnViewColumn *self,
|
||||
int *offset,
|
||||
int *size);
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */
|
||||
|
@@ -118,11 +118,16 @@ gtk_column_view_layout_allocate (GtkLayoutManager *layout_manager,
|
||||
int col_x, col_width;
|
||||
|
||||
if (GTK_IS_COLUMN_VIEW_CELL (child))
|
||||
column = gtk_column_view_cell_get_column (GTK_COLUMN_VIEW_CELL (child));
|
||||
{
|
||||
column = gtk_column_view_cell_get_column (GTK_COLUMN_VIEW_CELL (child));
|
||||
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
|
||||
}
|
||||
else
|
||||
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
|
||||
{
|
||||
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
|
||||
gtk_column_view_column_get_header_allocation (column, &col_x, &col_width);
|
||||
}
|
||||
|
||||
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
|
||||
gtk_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
|
||||
}
|
||||
}
|
||||
|
@@ -21,11 +21,13 @@
|
||||
#define __GTK_COLUMN_VIEW_PRIVATE_H__
|
||||
|
||||
#include "gtk/gtkcolumnview.h"
|
||||
#include "gtk/gtklistview.h"
|
||||
|
||||
#include "gtk/gtkcolumnviewsorterprivate.h"
|
||||
#include "gtk/gtklistitemwidgetprivate.h"
|
||||
|
||||
GtkListItemWidget * gtk_column_view_get_header_widget (GtkColumnView *self);
|
||||
GtkListView * gtk_column_view_get_list_view (GtkColumnView *self);
|
||||
|
||||
void gtk_column_view_measure_across (GtkColumnView *self,
|
||||
int *minimum,
|
||||
|
@@ -58,10 +58,18 @@ gtk_column_view_title_measure (GtkWidget *widget,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
int fixed_width = gtk_column_view_column_get_fixed_width (self->column);
|
||||
if (fixed_width > -1)
|
||||
*minimum = *natural = fixed_width;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -112,11 +120,11 @@ gtk_column_view_title_resize_func (GtkWidget *widget)
|
||||
}
|
||||
|
||||
static void
|
||||
click_pressed_cb (GtkGestureClick *gesture,
|
||||
guint n_press,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
GtkWidget *widget)
|
||||
click_released_cb (GtkGestureClick *gesture,
|
||||
guint n_press,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
GtkSorter *sorter;
|
||||
@@ -150,7 +158,7 @@ gtk_column_view_title_init (GtkColumnViewTitle *self)
|
||||
gtk_box_append (GTK_BOX (self->box), self->sort);
|
||||
|
||||
gesture = gtk_gesture_click_new ();
|
||||
g_signal_connect (gesture, "pressed", G_CALLBACK (click_pressed_cb), self);
|
||||
g_signal_connect (gesture, "released", G_CALLBACK (click_released_cb), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
||||
}
|
||||
|
||||
|
@@ -91,6 +91,8 @@ enum
|
||||
PROP_MIN_COLUMNS,
|
||||
PROP_MODEL,
|
||||
PROP_SINGLE_CLICK_ACTIVATE,
|
||||
PROP_ENABLE_RUBBERBAND,
|
||||
PROP_SEARCH_FILTER,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
@@ -914,6 +916,14 @@ gtk_grid_view_get_property (GObject *object,
|
||||
g_value_set_boolean (value, gtk_list_item_manager_get_single_click_activate (self->item_manager));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
g_value_set_object (value, gtk_grid_view_get_search_filter (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -950,6 +960,14 @@ gtk_grid_view_set_property (GObject *object,
|
||||
gtk_grid_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
gtk_grid_view_set_enable_rubberband (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
gtk_grid_view_set_search_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -1062,6 +1080,30 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkGridView:enable-rubberband:
|
||||
*
|
||||
* Allow rubberband selection
|
||||
*/
|
||||
properties[PROP_ENABLE_RUBBERBAND] =
|
||||
g_param_spec_boolean ("enable-rubberband",
|
||||
P_("Enable rubberband selection"),
|
||||
P_("Allow selecting items by dragging with the mouse"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkGridView:search-filter:
|
||||
*
|
||||
* Filter used for search
|
||||
*/
|
||||
properties[PROP_SEARCH_FILTER] =
|
||||
g_param_spec_object ("search-filter",
|
||||
P_("Search filter"),
|
||||
P_("Filter used for searching"),
|
||||
GTK_TYPE_FILTER,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
@@ -1370,3 +1412,102 @@ gtk_grid_view_get_single_click_activate (GtkGridView *self)
|
||||
|
||||
return gtk_list_item_manager_get_single_click_activate (self->item_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_set_enable_rubberband:
|
||||
* @self: a #GtkGridView
|
||||
* @enable_rubberband: %TRUE to enable rubberband selection
|
||||
*
|
||||
* Sets whether selections can be changed by dragging with the mouse.
|
||||
*/
|
||||
void
|
||||
gtk_grid_view_set_enable_rubberband (GtkGridView *self,
|
||||
gboolean enable_rubberband)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_GRID_VIEW (self));
|
||||
|
||||
if (enable_rubberband == gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
gtk_list_base_set_enable_rubberband (GTK_LIST_BASE (self), enable_rubberband);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLE_RUBBERBAND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_get_enable_rubberband:
|
||||
* @self: a #GtkGridView
|
||||
*
|
||||
* Returns whether rows can be selected by dragging with the mouse.
|
||||
*
|
||||
* Returns: %TRUE if rubberband selection is enabled
|
||||
*/
|
||||
gboolean
|
||||
gtk_grid_view_get_enable_rubberband (GtkGridView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_GRID_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_set_search_filter:
|
||||
* @self: a #GtkGridView
|
||||
* @filter: (nullable): the filter to use for search, or %NULL
|
||||
*
|
||||
* Sets a search filter.
|
||||
*
|
||||
* The selection will be moved to first item matching the
|
||||
* filter whenever the filter changes.
|
||||
*
|
||||
* This can be used with single selection and a string
|
||||
* filter that is connected to a search entry.
|
||||
*/
|
||||
void
|
||||
gtk_grid_view_set_search_filter (GtkGridView *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_GRID_VIEW (self));
|
||||
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
|
||||
|
||||
if (filter == gtk_list_base_get_search_filter (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
gtk_list_base_set_search_filter (GTK_LIST_BASE (self), filter);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_get_search_filter:
|
||||
* @self: a #GtkGridView
|
||||
*
|
||||
* Gets the search filter that was set with
|
||||
* gtk_grid_view_set_search_filter().
|
||||
*
|
||||
* Returns: (transfer none): The search filter of @self
|
||||
*/
|
||||
GtkFilter *
|
||||
gtk_grid_view_get_search_filter (GtkGridView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_GRID_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_base_get_search_filter (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_select_next_match:
|
||||
* @self: a #GtkGridView
|
||||
* @forward: whether to move forward or back
|
||||
*
|
||||
* Moves the selection to the next item matching the
|
||||
* search filter set with gtk_grid_view_set_search_filter().
|
||||
*/
|
||||
void
|
||||
gtk_grid_view_select_next_match (GtkGridView *self,
|
||||
gboolean forward)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_GRID_VIEW (self));
|
||||
|
||||
gtk_list_base_select_next_match (GTK_LIST_BASE (self), forward);
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -73,6 +74,11 @@ guint gtk_grid_view_get_max_columns (GtkGridView
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_max_columns (GtkGridView *self,
|
||||
guint max_columns);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_enable_rubberband (GtkGridView *self,
|
||||
gboolean enable_rubberband);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_grid_view_get_enable_rubberband (GtkGridView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_single_click_activate (GtkGridView *self,
|
||||
@@ -80,6 +86,16 @@ void gtk_grid_view_set_single_click_activate (GtkGridView
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_grid_view_get_single_click_activate (GtkGridView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_search_filter (GtkGridView *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_grid_view_get_search_filter (GtkGridView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_select_next_match (GtkGridView *self,
|
||||
gboolean forward);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@@ -28,6 +28,12 @@
|
||||
#include "gtkscrollable.h"
|
||||
#include "gtksingleselection.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkgesturedrag.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkstylecontextprivate.h"
|
||||
#include "gtksnapshot.h"
|
||||
#include "gtkmultiselection.h"
|
||||
|
||||
typedef struct _GtkListBasePrivate GtkListBasePrivate;
|
||||
|
||||
@@ -50,6 +56,24 @@ struct _GtkListBasePrivate
|
||||
GtkListItemTracker *selected;
|
||||
/* the item that has input focus */
|
||||
GtkListItemTracker *focus;
|
||||
|
||||
gboolean enable_rubberband;
|
||||
gboolean doing_rubberband;
|
||||
double rb_x1;
|
||||
double rb_y1;
|
||||
double rb_x2;
|
||||
double rb_y2;
|
||||
GtkGesture *drag_gesture;
|
||||
GtkCssNode *rubberband_node;
|
||||
GtkSelectionModel *old_selection;
|
||||
gboolean modify;
|
||||
gboolean extend;
|
||||
|
||||
guint autoscroll_id;
|
||||
double autoscroll_delta_x;
|
||||
double autoscroll_delta_y;
|
||||
|
||||
GtkFilter *search_filter;
|
||||
};
|
||||
|
||||
enum
|
||||
@@ -523,6 +547,8 @@ gtk_list_base_focus (GtkWidget *widget,
|
||||
}
|
||||
}
|
||||
|
||||
static void gtk_list_base_clear_search_filter (GtkListBase *self);
|
||||
|
||||
static void
|
||||
gtk_list_base_dispose (GObject *object)
|
||||
{
|
||||
@@ -551,6 +577,8 @@ gtk_list_base_dispose (GObject *object)
|
||||
|
||||
g_clear_object (&priv->model);
|
||||
|
||||
gtk_list_base_clear_search_filter (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_base_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
@@ -764,26 +792,18 @@ gtk_list_base_update_focus_tracker (GtkListBase *self)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_scroll_to_item (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
static gboolean
|
||||
gtk_list_base_scroll_to_position (GtkListBase *self,
|
||||
guint pos)
|
||||
{
|
||||
GtkListBase *self = GTK_LIST_BASE (widget);
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
int start, end;
|
||||
double align_along, align_across;
|
||||
GtkPackType side_along, side_across;
|
||||
guint pos;
|
||||
|
||||
if (!g_variant_check_format_string (parameter, "u", FALSE))
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "u", &pos);
|
||||
|
||||
/* figure out primary orientation and if position is valid */
|
||||
if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self), pos, &start, &end))
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
end += start;
|
||||
gtk_list_base_compute_scroll_align (self,
|
||||
@@ -794,7 +814,7 @@ gtk_list_base_scroll_to_item (GtkWidget *widget,
|
||||
|
||||
/* now do the same thing with the other orientation */
|
||||
if (!gtk_list_base_get_allocation_across (GTK_LIST_BASE (self), pos, &start, &end))
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
end += start;
|
||||
gtk_list_base_compute_scroll_align (self,
|
||||
@@ -808,13 +828,32 @@ gtk_list_base_scroll_to_item (GtkWidget *widget,
|
||||
align_across, side_across,
|
||||
align_along, side_along);
|
||||
|
||||
/* HACK HACK HACK
|
||||
*
|
||||
* GTK has no way to track the focused child. But we now that when a listitem
|
||||
* gets focus, it calls this action. So we update our focus tracker from here
|
||||
* because it's the closest we can get to accurate tracking.
|
||||
*/
|
||||
gtk_list_base_update_focus_tracker (self);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_scroll_to_item (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkListBase *self = GTK_LIST_BASE (widget);
|
||||
guint pos;
|
||||
|
||||
if (!g_variant_check_format_string (parameter, "u", FALSE))
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "u", &pos);
|
||||
|
||||
if (gtk_list_base_scroll_to_position (self, pos))
|
||||
{
|
||||
/* HACK HACK HACK
|
||||
*
|
||||
* GTK has no way to track the focused child. But we know that when a listitem
|
||||
* gets focus, it calls this action. So we update our focus tracker from here
|
||||
* because it's the closest we can get to accurate tracking.
|
||||
*/
|
||||
gtk_list_base_update_focus_tracker (self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -863,6 +902,17 @@ gtk_list_base_unselect_all (GtkWidget *widget,
|
||||
gtk_selection_model_unselect_all (selection_model);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_next_match_action (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
gboolean forward;
|
||||
|
||||
g_variant_get (parameter, "(b)", &forward);
|
||||
gtk_list_base_select_next_match (GTK_LIST_BASE (widget), forward);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_base_move_cursor_to_start (GtkWidget *widget,
|
||||
GVariant *args,
|
||||
@@ -1061,6 +1111,20 @@ gtk_list_base_add_custom_move_binding (GtkWidgetClass *widget_class,
|
||||
"(bbb)", TRUE, TRUE, TRUE);
|
||||
}
|
||||
|
||||
static void gtk_list_base_snapshot_rubberband (GtkListBase *self,
|
||||
GtkSnapshot *snapshot);
|
||||
|
||||
static void
|
||||
gtk_list_base_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkListBase *self = GTK_LIST_BASE (widget);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_list_base_parent_class)->snapshot (widget, snapshot);
|
||||
|
||||
gtk_list_base_snapshot_rubberband (self, snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
{
|
||||
@@ -1069,6 +1133,7 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
gpointer iface;
|
||||
|
||||
widget_class->focus = gtk_list_base_focus;
|
||||
widget_class->snapshot = gtk_list_base_snapshot;
|
||||
|
||||
gobject_class->dispose = gtk_list_base_dispose;
|
||||
gobject_class->get_property = gtk_list_base_get_property;
|
||||
@@ -1162,6 +1227,11 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
NULL,
|
||||
gtk_list_base_unselect_all);
|
||||
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"list.next-match",
|
||||
"(b)",
|
||||
gtk_list_base_next_match_action);
|
||||
|
||||
gtk_list_base_add_move_binding (widget_class, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1);
|
||||
gtk_list_base_add_move_binding (widget_class, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1);
|
||||
gtk_list_base_add_move_binding (widget_class, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1);
|
||||
@@ -1184,6 +1254,341 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.unselect-all", NULL);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL);
|
||||
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_g, GDK_CONTROL_MASK, "list.next-match", "(b)", TRUE);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_g, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.next-match", "(b)", FALSE);
|
||||
}
|
||||
|
||||
static void gtk_list_base_update_rubberband_selection (GtkListBase *self);
|
||||
|
||||
static gboolean
|
||||
autoscroll_cb (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer data)
|
||||
{
|
||||
GtkListBase *self = data;
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
double value;
|
||||
|
||||
value = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
gtk_adjustment_set_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL], value + priv->autoscroll_delta_x);
|
||||
value = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
gtk_adjustment_set_value (priv->adjustment[GTK_ORIENTATION_VERTICAL], value + priv->autoscroll_delta_y);
|
||||
|
||||
if (priv->doing_rubberband)
|
||||
{
|
||||
priv->rb_x2 += priv->autoscroll_delta_x;
|
||||
priv->rb_y2 += priv->autoscroll_delta_y;
|
||||
gtk_list_base_update_rubberband_selection (self);
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
add_autoscroll (GtkListBase *self,
|
||||
double delta_x,
|
||||
double delta_y)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
priv->autoscroll_delta_x = delta_x;
|
||||
priv->autoscroll_delta_y = delta_y;
|
||||
|
||||
if (priv->autoscroll_id == 0)
|
||||
priv->autoscroll_id = gtk_widget_add_tick_callback (GTK_WIDGET (self), autoscroll_cb, self, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_autoscroll (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (priv->autoscroll_id != 0)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (self), priv->autoscroll_id);
|
||||
priv->autoscroll_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_start_rubberband (GtkListBase *self,
|
||||
double x,
|
||||
double y,
|
||||
gboolean modify,
|
||||
gboolean extend)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkCssNode *widget_node;
|
||||
double value_x, value_y;
|
||||
GtkSelectionModel *selection;
|
||||
|
||||
if (priv->doing_rubberband)
|
||||
return;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
priv->rb_x1 = priv->rb_x2 = x + value_x;
|
||||
priv->rb_y1 = priv->rb_y2 = y + value_y;
|
||||
|
||||
priv->modify = modify;
|
||||
priv->extend = extend;
|
||||
|
||||
widget_node = gtk_widget_get_css_node (GTK_WIDGET (self));
|
||||
priv->rubberband_node = gtk_css_node_new ();
|
||||
gtk_css_node_set_name (priv->rubberband_node,
|
||||
g_quark_from_static_string ("rubberband"));
|
||||
gtk_css_node_set_parent (priv->rubberband_node, widget_node);
|
||||
gtk_css_node_set_state (priv->rubberband_node, gtk_css_node_get_state (widget_node));
|
||||
g_object_unref (priv->rubberband_node);
|
||||
|
||||
selection = gtk_list_item_manager_get_model (priv->item_manager);
|
||||
|
||||
if (modify)
|
||||
priv->old_selection = GTK_SELECTION_MODEL (gtk_multi_selection_copy (selection));
|
||||
|
||||
priv->doing_rubberband = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_stop_rubberband (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (!priv->doing_rubberband)
|
||||
return;
|
||||
|
||||
priv->doing_rubberband = FALSE;
|
||||
gtk_css_node_set_parent (priv->rubberband_node, NULL);
|
||||
priv->rubberband_node = NULL;
|
||||
|
||||
g_clear_object (&priv->old_selection);
|
||||
|
||||
remove_autoscroll (self);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
#define SCROLL_EDGE_SIZE 15
|
||||
|
||||
static void
|
||||
gtk_list_base_update_rubberband (GtkListBase *self,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
double value_x, value_y, page_size, upper;
|
||||
double delta_x, delta_y;
|
||||
|
||||
if (!priv->doing_rubberband)
|
||||
return;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
priv->rb_x2 = x + value_x;
|
||||
priv->rb_y2 = y + value_y;
|
||||
|
||||
gtk_list_base_update_rubberband_selection (self);
|
||||
|
||||
page_size = gtk_adjustment_get_page_size (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
upper = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
|
||||
if (x < SCROLL_EDGE_SIZE && value_x > 0)
|
||||
delta_x = - (SCROLL_EDGE_SIZE - x)/3.0;
|
||||
else if (page_size - x < SCROLL_EDGE_SIZE && value_x + page_size < upper)
|
||||
delta_x = (SCROLL_EDGE_SIZE - (page_size - x))/3.0;
|
||||
else
|
||||
delta_x = 0;
|
||||
|
||||
page_size = gtk_adjustment_get_page_size (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
upper = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
if (y < SCROLL_EDGE_SIZE && value_y > 0)
|
||||
delta_y = - (SCROLL_EDGE_SIZE - y)/3.0;
|
||||
else if (page_size - y < SCROLL_EDGE_SIZE && value_y + page_size < upper)
|
||||
delta_y = (SCROLL_EDGE_SIZE - (page_size - y))/3.0;
|
||||
else
|
||||
delta_y = 0;
|
||||
|
||||
if (delta_x != 0 || delta_y != 0)
|
||||
add_autoscroll (self, delta_x, delta_y);
|
||||
else
|
||||
remove_autoscroll (self);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_update_rubberband_selection (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GdkRectangle rect;
|
||||
GdkRectangle alloc;
|
||||
double value_x, value_y;
|
||||
GtkSelectionModel *model;
|
||||
GtkListItemManagerItem *item;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
rect.x = MIN (priv->rb_x1, priv->rb_x2) - value_x;
|
||||
rect.y = MIN (priv->rb_y1, priv->rb_y2) - value_y;
|
||||
rect.width = ABS (priv->rb_x1 - priv->rb_x2) + 1;
|
||||
rect.height = ABS (priv->rb_y1 - priv->rb_y2) + 1;
|
||||
|
||||
model = gtk_list_item_manager_get_model (priv->item_manager);
|
||||
|
||||
for (item = gtk_list_item_manager_get_first (priv->item_manager);
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
guint pos;
|
||||
gboolean was_selected, selected;
|
||||
|
||||
if (!item->widget)
|
||||
continue;
|
||||
|
||||
pos = gtk_list_item_manager_get_item_position (priv->item_manager, item);
|
||||
|
||||
gtk_widget_get_allocation (item->widget, &alloc);
|
||||
|
||||
selected = gdk_rectangle_intersect (&rect, &alloc, &alloc);
|
||||
|
||||
if (priv->modify)
|
||||
{
|
||||
was_selected = gtk_selection_model_is_selected (priv->old_selection, pos);
|
||||
selected = selected ^ was_selected;
|
||||
}
|
||||
|
||||
if (selected)
|
||||
gtk_selection_model_select_item (model, pos, FALSE);
|
||||
else if (!priv->extend)
|
||||
gtk_selection_model_unselect_item (model, pos);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_snapshot_rubberband (GtkListBase *self,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkStyleContext *context;
|
||||
GdkRectangle rect;
|
||||
double value_x, value_y;
|
||||
|
||||
if (!priv->doing_rubberband)
|
||||
return;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
rect.x = MIN (priv->rb_x1, priv->rb_x2) - value_x;
|
||||
rect.y = MIN (priv->rb_y1, priv->rb_y2) - value_y;
|
||||
rect.width = ABS (priv->rb_x1 - priv->rb_x2) + 1;
|
||||
rect.height = ABS (priv->rb_y1 - priv->rb_y2) + 1;
|
||||
|
||||
context = gtk_widget_get_style_context (GTK_WIDGET (self));
|
||||
|
||||
gtk_style_context_save_to_node (context, priv->rubberband_node);
|
||||
|
||||
gtk_snapshot_render_background (snapshot, context, rect.x, rect.y, rect.width, rect.height);
|
||||
gtk_snapshot_render_frame (snapshot, context, rect.x, rect.y, rect.width, rect.height);
|
||||
|
||||
gtk_style_context_restore (context);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
get_selection_modifiers (GtkGesture *gesture,
|
||||
gboolean *modify,
|
||||
gboolean *extend)
|
||||
{
|
||||
GdkEventSequence *sequence;
|
||||
GdkEvent *event;
|
||||
GdkModifierType state;
|
||||
|
||||
*modify = FALSE;
|
||||
*extend = FALSE;
|
||||
|
||||
sequence = gtk_gesture_get_last_updated_sequence (gesture);
|
||||
event = gtk_gesture_get_last_event (gesture, sequence);
|
||||
state = gdk_event_get_modifier_state (event);
|
||||
if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
|
||||
*modify = TRUE;
|
||||
if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
|
||||
*extend = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_drag_begin (GtkGestureDrag *gesture,
|
||||
double start_x,
|
||||
double start_y,
|
||||
GtkListBase *self)
|
||||
{
|
||||
gboolean modify;
|
||||
gboolean extend;
|
||||
|
||||
get_selection_modifiers (GTK_GESTURE (gesture), &modify, &extend);
|
||||
gtk_list_base_start_rubberband (self, start_x, start_y, modify, extend);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_drag_update (GtkGestureDrag *gesture,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkListBase *self)
|
||||
{
|
||||
double start_x, start_y;
|
||||
gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
|
||||
gtk_list_base_update_rubberband (self, start_x + offset_x, start_y + offset_y);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_drag_end (GtkGestureDrag *gesture,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkListBase *self)
|
||||
{
|
||||
gtk_list_base_drag_update (gesture, offset_x, offset_y, self);
|
||||
gtk_list_base_stop_rubberband (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_base_set_enable_rubberband (GtkListBase *self,
|
||||
gboolean enable)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (priv->enable_rubberband == enable)
|
||||
return;
|
||||
|
||||
priv->enable_rubberband = enable;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
priv->drag_gesture = gtk_gesture_drag_new ();
|
||||
g_signal_connect (priv->drag_gesture, "drag-begin", G_CALLBACK (gtk_list_base_drag_begin), self);
|
||||
g_signal_connect (priv->drag_gesture, "drag-update", G_CALLBACK (gtk_list_base_drag_update), self);
|
||||
g_signal_connect (priv->drag_gesture, "drag-end", G_CALLBACK (gtk_list_base_drag_end), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (priv->drag_gesture));
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_remove_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (priv->drag_gesture));
|
||||
priv->drag_gesture = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_list_base_get_enable_rubberband (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
return priv->enable_rubberband;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1538,3 +1943,183 @@ gtk_list_base_set_model (GtkListBase *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
find_first_selected (GtkSelectionModel *model)
|
||||
{
|
||||
guint i, start, n_items;
|
||||
gboolean selected;
|
||||
|
||||
n_items = 0;
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i += n_items)
|
||||
{
|
||||
gtk_selection_model_query_range (model, i, &start, &n_items, &selected);
|
||||
if (selected)
|
||||
return i;
|
||||
}
|
||||
|
||||
return GTK_INVALID_LIST_POSITION;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
match_item (GListModel *model,
|
||||
GtkFilter *filter,
|
||||
guint position)
|
||||
{
|
||||
gpointer item;
|
||||
gboolean result;
|
||||
|
||||
item = g_list_model_get_item (model, position);
|
||||
result = gtk_filter_match (filter, item);
|
||||
g_object_unref (item);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static guint
|
||||
find_next_match (GListModel *model,
|
||||
GtkFilter *filter,
|
||||
guint start,
|
||||
gboolean forward)
|
||||
{
|
||||
guint i;
|
||||
|
||||
if (start == GTK_INVALID_LIST_POSITION)
|
||||
start = 0;
|
||||
|
||||
if (forward)
|
||||
for (i = start; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (match_item (model, filter, i))
|
||||
return i;
|
||||
}
|
||||
else
|
||||
for (i = start; ; i--)
|
||||
{
|
||||
if (match_item (model, filter, i))
|
||||
return i;
|
||||
if (i == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return GTK_INVALID_LIST_POSITION;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_search_filter_changed_cb (GtkFilter *filter,
|
||||
GtkFilterChange change,
|
||||
GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager);
|
||||
|
||||
if (model == NULL)
|
||||
return;
|
||||
|
||||
if (gtk_filter_get_strictness (priv->search_filter) == GTK_FILTER_MATCH_NONE)
|
||||
gtk_selection_model_unselect_all (model);
|
||||
else
|
||||
{
|
||||
guint position;
|
||||
|
||||
switch (change)
|
||||
{
|
||||
case GTK_FILTER_CHANGE_DIFFERENT:
|
||||
case GTK_FILTER_CHANGE_LESS_STRICT:
|
||||
position = find_next_match (G_LIST_MODEL (model), priv->search_filter, 0, TRUE);
|
||||
break;
|
||||
|
||||
case GTK_FILTER_CHANGE_MORE_STRICT:
|
||||
position = find_first_selected (model);
|
||||
if (position == GTK_INVALID_LIST_POSITION)
|
||||
position = 0;
|
||||
position = find_next_match (G_LIST_MODEL (model), priv->search_filter, position, TRUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (position == GTK_INVALID_LIST_POSITION)
|
||||
gtk_selection_model_unselect_all (model);
|
||||
else
|
||||
gtk_list_base_grab_focus_on_item (self, position, TRUE, FALSE, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_clear_search_filter (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (priv->search_filter == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (priv->search_filter,
|
||||
gtk_list_base_search_filter_changed_cb,
|
||||
self);
|
||||
|
||||
g_clear_object (&priv->search_filter);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_base_set_search_filter (GtkListBase *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (priv->search_filter == filter)
|
||||
return;
|
||||
|
||||
gtk_list_base_clear_search_filter (self);
|
||||
|
||||
if (filter)
|
||||
{
|
||||
priv->search_filter = g_object_ref (filter);
|
||||
g_signal_connect (priv->search_filter, "changed",
|
||||
G_CALLBACK (gtk_list_base_search_filter_changed_cb), self);
|
||||
gtk_list_base_search_filter_changed_cb (priv->search_filter, GTK_FILTER_CHANGE_DIFFERENT, self);
|
||||
}
|
||||
|
||||
gtk_widget_action_set_enabled (GTK_WIDGET (self), "list.next-match", filter != NULL);
|
||||
}
|
||||
|
||||
GtkFilter *
|
||||
gtk_list_base_get_search_filter (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
return priv->search_filter;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_list_base_select_next_match (GtkListBase *self,
|
||||
gboolean forward)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager);
|
||||
guint position;
|
||||
|
||||
if (!priv->search_filter)
|
||||
return FALSE;
|
||||
|
||||
position = find_first_selected (model);
|
||||
if (position == GTK_INVALID_LIST_POSITION)
|
||||
return FALSE;
|
||||
|
||||
if (forward)
|
||||
position = position + 1;
|
||||
else if (position > 0)
|
||||
position = position - 1;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
position = find_next_match (G_LIST_MODEL (model), priv->search_filter, position, forward);
|
||||
if (position == GTK_INVALID_LIST_POSITION)
|
||||
{
|
||||
gtk_widget_error_bell (GTK_WIDGET (self));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gtk_list_base_grab_focus_on_item (self, position, TRUE, FALSE, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "gtklistbase.h"
|
||||
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
#include "gtkfilter.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
struct _GtkListBase
|
||||
@@ -99,5 +100,13 @@ gboolean gtk_list_base_grab_focus_on_item (GtkListBase
|
||||
gboolean select,
|
||||
gboolean modify,
|
||||
gboolean extend);
|
||||
void gtk_list_base_set_enable_rubberband (GtkListBase *self,
|
||||
gboolean enable);
|
||||
gboolean gtk_list_base_get_enable_rubberband (GtkListBase *self);
|
||||
void gtk_list_base_set_search_filter (GtkListBase *self,
|
||||
GtkFilter *filter);
|
||||
GtkFilter * gtk_list_base_get_search_filter (GtkListBase *self);
|
||||
gboolean gtk_list_base_select_next_match (GtkListBase *self,
|
||||
gboolean forward);
|
||||
|
||||
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
#include "gtkwidget.h"
|
||||
@@ -309,6 +310,8 @@ gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
GtkWidget * parent = gtk_widget_get_parent (widget);
|
||||
gboolean rubberband;
|
||||
|
||||
if (priv->list_item && !priv->list_item->selectable && !priv->list_item->activatable)
|
||||
{
|
||||
@@ -316,7 +319,12 @@ gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!priv->list_item || priv->list_item->selectable)
|
||||
if (GTK_IS_LIST_BASE (parent))
|
||||
rubberband = gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (parent));
|
||||
else
|
||||
rubberband = FALSE;
|
||||
|
||||
if (!rubberband && (!priv->list_item || priv->list_item->selectable))
|
||||
{
|
||||
GdkModifierType state;
|
||||
GdkEvent *event;
|
||||
|
@@ -85,6 +85,8 @@ enum
|
||||
PROP_MODEL,
|
||||
PROP_SHOW_SEPARATORS,
|
||||
PROP_SINGLE_CLICK_ACTIVATE,
|
||||
PROP_ENABLE_RUBBERBAND,
|
||||
PROP_SEARCH_FILTER,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
@@ -643,6 +645,14 @@ gtk_list_view_get_property (GObject *object,
|
||||
g_value_set_boolean (value, gtk_list_item_manager_get_single_click_activate (self->item_manager));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
g_value_set_object (value, gtk_list_view_get_search_filter (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -675,6 +685,14 @@ gtk_list_view_set_property (GObject *object,
|
||||
gtk_list_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
gtk_list_view_set_enable_rubberband (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
gtk_list_view_set_search_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -771,6 +789,30 @@ gtk_list_view_class_init (GtkListViewClass *klass)
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkListView:enable-rubberband:
|
||||
*
|
||||
* Allow rubberband selection
|
||||
*/
|
||||
properties[PROP_ENABLE_RUBBERBAND] =
|
||||
g_param_spec_boolean ("enable-rubberband",
|
||||
P_("Enable rubberband selection"),
|
||||
P_("Allow selecting items by dragging with the mouse"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkListView:search-filter:
|
||||
*
|
||||
* Filter used for search
|
||||
*/
|
||||
properties[PROP_SEARCH_FILTER] =
|
||||
g_param_spec_object ("search-filter",
|
||||
P_("Search filter"),
|
||||
P_("Filter used for searching"),
|
||||
GTK_TYPE_FILTER,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
@@ -1033,3 +1075,103 @@ gtk_list_view_get_single_click_activate (GtkListView *self)
|
||||
|
||||
return gtk_list_item_manager_get_single_click_activate (self->item_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_set_enable_rubberband:
|
||||
* @self: a #GtkListView
|
||||
* @enable_rubberband: %TRUE to enable rubberband selection
|
||||
*
|
||||
* Sets whether selections can be changed by dragging with the mouse.
|
||||
*/
|
||||
void
|
||||
gtk_list_view_set_enable_rubberband (GtkListView *self,
|
||||
gboolean enable_rubberband)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_VIEW (self));
|
||||
|
||||
if (enable_rubberband == gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
gtk_list_base_set_enable_rubberband (GTK_LIST_BASE (self), enable_rubberband);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLE_RUBBERBAND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_get_enable_rubberband:
|
||||
* @self: a #GtkListView
|
||||
*
|
||||
* Returns whether rows can be selected by dragging with the mouse.
|
||||
*
|
||||
* Returns: %TRUE if rubberband selection is enabled
|
||||
*/
|
||||
gboolean
|
||||
gtk_list_view_get_enable_rubberband (GtkListView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_set_search_filter:
|
||||
* @self: a #GtkListView
|
||||
* @filter: (nullable): the filter ot use for search, or %NULL
|
||||
*
|
||||
* Sets a search filter.
|
||||
*
|
||||
* The selection will be moved to first item matching the
|
||||
* filter whenever the filter changes.
|
||||
*
|
||||
* This can be used with single selection and a string
|
||||
* filter that is connected to a search entry.
|
||||
*/
|
||||
void
|
||||
gtk_list_view_set_search_filter (GtkListView *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_VIEW (self));
|
||||
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
|
||||
|
||||
if (filter == gtk_list_base_get_search_filter (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
gtk_list_base_set_search_filter (GTK_LIST_BASE (self), filter);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_get_search_filter:
|
||||
* @self: a #GtkListView
|
||||
*
|
||||
* Gets the search filter that was set with
|
||||
* gtk_list_view_set_search_filter().
|
||||
*
|
||||
* Returns: (transfer none): The search filter of @self
|
||||
*/
|
||||
GtkFilter *
|
||||
gtk_list_view_get_search_filter (GtkListView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_base_get_search_filter (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_select_next_match:
|
||||
* @self: a #GtkListView
|
||||
* @forward: whether to move forward or back
|
||||
*
|
||||
* Moves the selection to the next item matching the
|
||||
* search filter set with gtk_list_view_set_search_filter().
|
||||
*/
|
||||
void
|
||||
gtk_list_view_select_next_match (GtkListView *self,
|
||||
gboolean forward)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_VIEW (self));
|
||||
|
||||
gtk_list_base_select_next_match (GTK_LIST_BASE (self), forward);
|
||||
}
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -75,6 +76,22 @@ void gtk_list_view_set_single_click_activate (GtkListView
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_view_get_single_click_activate (GtkListView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_set_enable_rubberband (GtkListView *self,
|
||||
gboolean enable_rubberband);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_view_get_enable_rubberband (GtkListView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_set_search_filter (GtkListView *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_list_view_get_search_filter (GtkListView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_select_next_match (GtkListView *self,
|
||||
gboolean forward);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_VIEW_H__ */
|
||||
|
380
gtk/gtkmultiselection.c
Normal file
380
gtk/gtkmultiselection.c
Normal file
@@ -0,0 +1,380 @@
|
||||
/*
|
||||
* 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;
|
||||
};
|
||||
|
||||
struct _GtkMultiSelectionClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_MODEL,
|
||||
|
||||
N_PROPS,
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
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 gboolean
|
||||
gtk_multi_selection_select_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gboolean exclusive)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
|
||||
if (exclusive)
|
||||
gtk_set_remove_all (self->selected);
|
||||
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);
|
||||
|
||||
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)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
guint pos, n_items;
|
||||
|
||||
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_query_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint *start_range,
|
||||
guint *n_items,
|
||||
gboolean *selected)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
guint upper_bound = g_list_model_get_n_items (self->model);
|
||||
|
||||
gtk_set_find_range (self->selected, position, upper_bound, start_range, n_items, selected);
|
||||
}
|
||||
|
||||
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;
|
||||
iface->query_range = gtk_multi_selection_query_range;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
gtk_set_remove_range (self->selected, position, removed);
|
||||
gtk_set_shift (self->selected, position, (int)added - (int)removed);
|
||||
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:
|
||||
self->model = g_value_dup_object (value);
|
||||
g_warn_if_fail (self->model != NULL);
|
||||
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;
|
||||
|
||||
/**
|
||||
* GtkMultiSelection:model
|
||||
*
|
||||
* The list managed by this selection
|
||||
*/
|
||||
properties[PROP_MODEL] =
|
||||
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);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
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
|
||||
**/
|
||||
GListModel *
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_multi_selection_copy:
|
||||
* @selection: the #GtkSelectionModel to copy
|
||||
*
|
||||
* Creates a #GtkMultiSelection that has the same underlying
|
||||
* model and the same selected items as @selection.
|
||||
*
|
||||
* Returns: (transfer full): a new #GtkMultiSelection
|
||||
*/
|
||||
GtkMultiSelection *
|
||||
gtk_multi_selection_copy (GtkSelectionModel *selection)
|
||||
{
|
||||
GtkMultiSelection *copy;
|
||||
GListModel *model;
|
||||
|
||||
g_object_get (selection, "model", &model, NULL);
|
||||
|
||||
copy = GTK_MULTI_SELECTION (gtk_multi_selection_new (model));
|
||||
|
||||
if (GTK_IS_MULTI_SELECTION (selection))
|
||||
{
|
||||
GtkMultiSelection *multi = GTK_MULTI_SELECTION (selection);
|
||||
|
||||
gtk_set_free (copy->selected);
|
||||
copy->selected = gtk_set_copy (multi->selected);
|
||||
copy->last_selected = multi->last_selected;
|
||||
}
|
||||
else
|
||||
{
|
||||
guint pos, n;
|
||||
guint start, n_items;
|
||||
gboolean selected;
|
||||
|
||||
n = g_list_model_get_n_items (model);
|
||||
n_items = 0;
|
||||
for (pos = 0; pos < n; pos += n_items)
|
||||
{
|
||||
gtk_selection_model_query_range (selection, pos, &start, &n_items, &selected);
|
||||
if (selected)
|
||||
gtk_selection_model_select_range (GTK_SELECTION_MODEL (copy), start, n_items, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (model);
|
||||
|
||||
return copy;
|
||||
}
|
41
gtk/gtkmultiselection.h
Normal file
41
gtk/gtkmultiselection.h
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* 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>
|
||||
#include <gtk/gtkselectionmodel.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
|
||||
GListModel * gtk_multi_selection_new (GListModel *model);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkMultiSelection * gtk_multi_selection_copy (GtkSelectionModel *selection);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_MULTI_SELECTION_H__ */
|
@@ -29,6 +29,7 @@
|
||||
#include "gtkprintbackendprivate.h"
|
||||
|
||||
|
||||
static void gtk_print_backend_finalize (GObject *object);
|
||||
static void gtk_print_backend_dispose (GObject *object);
|
||||
static void gtk_print_backend_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
@@ -251,6 +252,7 @@ gtk_print_backend_class_init (GtkPrintBackendClass *class)
|
||||
|
||||
backend_parent_class = g_type_class_peek_parent (class);
|
||||
|
||||
object_class->finalize = gtk_print_backend_finalize;
|
||||
object_class->dispose = gtk_print_backend_dispose;
|
||||
object_class->set_property = gtk_print_backend_set_property;
|
||||
object_class->get_property = gtk_print_backend_get_property;
|
||||
@@ -348,11 +350,21 @@ gtk_print_backend_dispose (GObject *object)
|
||||
/* We unref the printers in dispose, not in finalize so that
|
||||
* we can break refcount cycles with gtk_print_backend_destroy
|
||||
*/
|
||||
g_clear_object (&priv->printers);
|
||||
g_list_store_remove_all (priv->printers);
|
||||
|
||||
backend_parent_class->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_print_backend_finalize (GObject *object)
|
||||
{
|
||||
GtkPrintBackend *backend = GTK_PRINT_BACKEND (object);
|
||||
GtkPrintBackendPrivate *priv = backend->priv;
|
||||
|
||||
g_clear_object (&priv->printers);
|
||||
|
||||
backend_parent_class->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
fallback_printer_request_details (GtkPrinter *printer)
|
||||
@@ -361,7 +373,7 @@ fallback_printer_request_details (GtkPrinter *printer)
|
||||
|
||||
static gboolean
|
||||
fallback_printer_mark_conflicts (GtkPrinter *printer,
|
||||
GtkPrinterOptionSet *options)
|
||||
GtkPrinterOptionSet *options)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
351
gtk/gtkset.c
Normal file
351
gtk/gtkset.c
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
GtkSet *
|
||||
gtk_set_copy (GtkSet *set)
|
||||
{
|
||||
GtkSet *copy;
|
||||
|
||||
copy = g_new (GtkSet, 1);
|
||||
copy->ranges = g_array_copy (set->ranges);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
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_find_range (GtkSet *set,
|
||||
guint position,
|
||||
guint upper_bound,
|
||||
guint *start,
|
||||
guint *n_items,
|
||||
gboolean *contained)
|
||||
{
|
||||
int i;
|
||||
int last = 0;
|
||||
|
||||
if (position >= upper_bound)
|
||||
{
|
||||
*start = 0;
|
||||
*n_items = 0;
|
||||
*contained = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
|
||||
if (position < r->first)
|
||||
{
|
||||
*start = last;
|
||||
*n_items = r->first - last;
|
||||
*contained = FALSE;
|
||||
|
||||
return;
|
||||
}
|
||||
else if (r->first <= position && position < r->first + r->n_items)
|
||||
{
|
||||
*start = r->first;
|
||||
*n_items = r->n_items;
|
||||
*contained = TRUE;
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
last = r->first + r->n_items;
|
||||
}
|
||||
|
||||
*start = last;
|
||||
*n_items = upper_bound - last;
|
||||
*contained = FALSE;
|
||||
}
|
||||
|
||||
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 shifts 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
|
70
gtk/gtkset.h
Normal file
70
gtk/gtkset.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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);
|
||||
GtkSet *gtk_set_copy (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_find_range (GtkSet *set,
|
||||
guint position,
|
||||
guint upper_bound,
|
||||
guint *start,
|
||||
guint *n_items,
|
||||
gboolean *contained);
|
||||
|
||||
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__ */
|
@@ -62,6 +62,7 @@
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="changes">
|
||||
<property name="title"></property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="bind" handler="bind_changes_cb"/>
|
||||
|
@@ -45,6 +45,7 @@
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title">Type</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_type_cb"/>
|
||||
@@ -57,6 +58,7 @@
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title">Name</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_name_cb"/>
|
||||
@@ -68,6 +70,7 @@
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title">Label</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_label_cb"/>
|
||||
|
@@ -54,6 +54,7 @@
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title" translatable="yes">Value</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="bind" handler="bind_value_cb"/>
|
||||
|
@@ -62,6 +62,7 @@
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="path">
|
||||
<property name="title" translatable="yes">Path</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_name_cb"/>
|
||||
@@ -92,6 +93,11 @@
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="filler">
|
||||
<property name="expand">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@@ -134,6 +134,7 @@ gtk_private_sources = files([
|
||||
'gtkscaler.c',
|
||||
'gtksearchengine.c',
|
||||
'gtksearchenginemodel.c',
|
||||
'gtkset.c',
|
||||
'gtksizerequestcache.c',
|
||||
'gtkstyleanimation.c',
|
||||
'gtkstylecascade.c',
|
||||
@@ -301,6 +302,7 @@ gtk_public_sources = files([
|
||||
'gtkmodules.c',
|
||||
'gtkmountoperation.c',
|
||||
'gtkmultifilter.c',
|
||||
'gtkmultiselection.c',
|
||||
'gtkmultisorter.c',
|
||||
'gtknativedialog.c',
|
||||
'gtknomediafile.c',
|
||||
@@ -574,6 +576,7 @@ gtk_public_headers = files([
|
||||
'gtkmessagedialog.h',
|
||||
'gtkmountoperation.h',
|
||||
'gtkmultifilter.h',
|
||||
'gtkmultiselection.h',
|
||||
'gtkmultisorter.h',
|
||||
'gtknative.h',
|
||||
'gtknativedialog.h',
|
||||
|
@@ -114,7 +114,8 @@ test_type (gconstpointer data)
|
||||
}
|
||||
else if (g_type_is_a (type, GTK_TYPE_FILTER_LIST_MODEL) ||
|
||||
g_type_is_a (type, GTK_TYPE_NO_SELECTION) ||
|
||||
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION))
|
||||
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION) ||
|
||||
g_type_is_a (type, GTK_TYPE_MULTI_SELECTION))
|
||||
{
|
||||
GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
|
||||
instance = g_object_new (type,
|
||||
@@ -277,7 +278,8 @@ G_GNUC_END_IGNORE_DEPRECATIONS
|
||||
|
||||
if ((g_type_is_a (type, GTK_TYPE_FILTER_LIST_MODEL) ||
|
||||
g_type_is_a (type, GTK_TYPE_NO_SELECTION) ||
|
||||
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION)) &&
|
||||
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION) ||
|
||||
g_type_is_a (type, GTK_TYPE_MULTI_SELECTION)) &&
|
||||
strcmp (pspec->name, "model") == 0)
|
||||
continue;
|
||||
|
||||
|
@@ -39,6 +39,7 @@ tests = [
|
||||
['listbox'],
|
||||
['main'],
|
||||
['maplistmodel'],
|
||||
['multiselection'],
|
||||
['notify'],
|
||||
['no-gtk-init'],
|
||||
['object'],
|
||||
|
393
testsuite/gtk/multiselection.c
Normal file
393
testsuite/gtk/multiselection.c
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* 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;
|
||||
guint i;
|
||||
|
||||
objects = g_new0 (GObject *, added);
|
||||
|
||||
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]);
|
||||
|
||||
g_free (objects);
|
||||
}
|
||||
|
||||
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 ignore_selection_changes(model) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
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 ("/multiselection/changes", test_changes);
|
||||
#endif
|
||||
g_test_add_func ("/multiselection/selection", test_selection);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
@@ -457,7 +457,8 @@ test_type (gconstpointer data)
|
||||
}
|
||||
else if (g_type_is_a (type, GTK_TYPE_FILTER_LIST_MODEL) ||
|
||||
g_type_is_a (type, GTK_TYPE_NO_SELECTION) ||
|
||||
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION))
|
||||
g_type_is_a (type, GTK_TYPE_SINGLE_SELECTION) ||
|
||||
g_type_is_a (type, GTK_TYPE_MULTI_SELECTION))
|
||||
{
|
||||
GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
|
||||
instance = g_object_new (type,
|
||||
|
@@ -71,7 +71,8 @@ test_finalize_object (gconstpointer data)
|
||||
}
|
||||
else if (g_type_is_a (test_type, GTK_TYPE_FILTER_LIST_MODEL) ||
|
||||
g_type_is_a (test_type, GTK_TYPE_NO_SELECTION) ||
|
||||
g_type_is_a (test_type, GTK_TYPE_SINGLE_SELECTION))
|
||||
g_type_is_a (test_type, GTK_TYPE_SINGLE_SELECTION) ||
|
||||
g_type_is_a (test_type, GTK_TYPE_MULTI_SELECTION))
|
||||
{
|
||||
GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
|
||||
object = g_object_new (test_type,
|
||||
|
Reference in New Issue
Block a user