Compare commits
26 Commits
gdk-win32-
...
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);
|
g_object_unref (factory);
|
||||||
|
|
||||||
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
|
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));
|
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);
|
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), selection);
|
||||||
g_object_unref (selection);
|
g_object_unref (selection);
|
||||||
g_object_unref (model);
|
g_object_unref (model);
|
||||||
|
@@ -70,6 +70,7 @@
|
|||||||
<xi:include href="xml/gtkselectionmodel.xml" />
|
<xi:include href="xml/gtkselectionmodel.xml" />
|
||||||
<xi:include href="xml/gtknoselection.xml" />
|
<xi:include href="xml/gtknoselection.xml" />
|
||||||
<xi:include href="xml/gtksingleselection.xml" />
|
<xi:include href="xml/gtksingleselection.xml" />
|
||||||
|
<xi:include href="xml/gtkmultiselection.xml" />
|
||||||
<xi:include href="xml/gtkdirectorylist.xml" />
|
<xi:include href="xml/gtkdirectorylist.xml" />
|
||||||
</chapter>
|
</chapter>
|
||||||
|
|
||||||
|
@@ -392,6 +392,16 @@ gtk_single_selection_set_can_unselect
|
|||||||
gtk_single_selection_get_type
|
gtk_single_selection_get_type
|
||||||
</SECTION>
|
</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>
|
<SECTION>
|
||||||
<FILE>gtklistitem</FILE>
|
<FILE>gtklistitem</FILE>
|
||||||
<TITLE>GtkListItem</TITLE>
|
<TITLE>GtkListItem</TITLE>
|
||||||
@@ -479,6 +489,11 @@ gtk_list_view_set_show_separators
|
|||||||
gtk_list_view_get_show_separators
|
gtk_list_view_get_show_separators
|
||||||
gtk_list_view_set_single_click_activate
|
gtk_list_view_set_single_click_activate
|
||||||
gtk_list_view_get_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>
|
<SUBSECTION Standard>
|
||||||
GTK_LIST_VIEW
|
GTK_LIST_VIEW
|
||||||
GTK_LIST_VIEW_CLASS
|
GTK_LIST_VIEW_CLASS
|
||||||
@@ -507,6 +522,11 @@ gtk_column_view_set_show_separators
|
|||||||
gtk_column_view_sort_by_column
|
gtk_column_view_sort_by_column
|
||||||
gtk_column_view_set_single_click_activate
|
gtk_column_view_set_single_click_activate
|
||||||
gtk_column_view_get_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>
|
<SUBSECTION Standard>
|
||||||
GTK_COLUMN_VIEW
|
GTK_COLUMN_VIEW
|
||||||
GTK_COLUMN_VIEW_CLASS
|
GTK_COLUMN_VIEW_CLASS
|
||||||
@@ -533,6 +553,12 @@ gtk_column_view_column_set_sorter
|
|||||||
gtk_column_view_column_get_sorter
|
gtk_column_view_column_get_sorter
|
||||||
gtk_column_view_column_set_visible
|
gtk_column_view_column_set_visible
|
||||||
gtk_column_view_column_get_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>
|
<SUBSECTION Standard>
|
||||||
GTK_COLUMN_VIEW_COLUMN
|
GTK_COLUMN_VIEW_COLUMN
|
||||||
GTK_COLUMN_VIEW_COLUMN_CLASS
|
GTK_COLUMN_VIEW_COLUMN_CLASS
|
||||||
@@ -557,6 +583,11 @@ gtk_grid_view_set_min_columns
|
|||||||
gtk_grid_view_get_min_columns
|
gtk_grid_view_get_min_columns
|
||||||
gtk_grid_view_set_single_click_activate
|
gtk_grid_view_set_single_click_activate
|
||||||
gtk_grid_view_get_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>
|
<SUBSECTION Standard>
|
||||||
GTK_GRID_VIEW
|
GTK_GRID_VIEW
|
||||||
GTK_GRID_VIEW_CLASS
|
GTK_GRID_VIEW_CLASS
|
||||||
|
@@ -139,6 +139,7 @@ gtk_menu_button_get_type
|
|||||||
gtk_message_dialog_get_type
|
gtk_message_dialog_get_type
|
||||||
gtk_mount_operation_get_type
|
gtk_mount_operation_get_type
|
||||||
gtk_multi_filter_get_type
|
gtk_multi_filter_get_type
|
||||||
|
gtk_multi_selection_get_type
|
||||||
gtk_multi_sorter_get_type
|
gtk_multi_sorter_get_type
|
||||||
gtk_native_get_type
|
gtk_native_get_type
|
||||||
gtk_native_dialog_get_type
|
gtk_native_dialog_get_type
|
||||||
|
@@ -174,6 +174,7 @@
|
|||||||
#include <gtk/gtkmessagedialog.h>
|
#include <gtk/gtkmessagedialog.h>
|
||||||
#include <gtk/gtkmountoperation.h>
|
#include <gtk/gtkmountoperation.h>
|
||||||
#include <gtk/gtkmultifilter.h>
|
#include <gtk/gtkmultifilter.h>
|
||||||
|
#include <gtk/gtkmultiselection.h>
|
||||||
#include <gtk/gtkmultisorter.h>
|
#include <gtk/gtkmultisorter.h>
|
||||||
#include <gtk/gtknative.h>
|
#include <gtk/gtknative.h>
|
||||||
#include <gtk/gtknativedialog.h>
|
#include <gtk/gtknativedialog.h>
|
||||||
|
@@ -34,6 +34,13 @@
|
|||||||
#include "gtkprivate.h"
|
#include "gtkprivate.h"
|
||||||
#include "gtkscrollable.h"
|
#include "gtkscrollable.h"
|
||||||
#include "gtkwidgetprivate.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
|
* SECTION:gtkcolumnview
|
||||||
@@ -63,6 +70,21 @@ struct _GtkColumnView
|
|||||||
GtkColumnListItemFactory *factory;
|
GtkColumnListItemFactory *factory;
|
||||||
|
|
||||||
GtkSorter *sorter;
|
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
|
struct _GtkColumnViewClass
|
||||||
@@ -82,6 +104,8 @@ enum
|
|||||||
PROP_VADJUSTMENT,
|
PROP_VADJUSTMENT,
|
||||||
PROP_VSCROLL_POLICY,
|
PROP_VSCROLL_POLICY,
|
||||||
PROP_SINGLE_CLICK_ACTIVATE,
|
PROP_SINGLE_CLICK_ACTIVATE,
|
||||||
|
PROP_ENABLE_RUBBERBAND,
|
||||||
|
PROP_SEARCH_FILTER,
|
||||||
|
|
||||||
N_PROPS
|
N_PROPS
|
||||||
};
|
};
|
||||||
@@ -169,48 +193,77 @@ gtk_column_view_allocate_columns (GtkColumnView *self,
|
|||||||
int width)
|
int width)
|
||||||
{
|
{
|
||||||
GtkScrollablePolicy scroll_policy;
|
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;
|
guint i;
|
||||||
|
GtkRequestedSize *sizes;
|
||||||
|
|
||||||
gtk_column_view_measure_across (self, &col_min, &col_nat);
|
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
|
||||||
gtk_widget_measure (GTK_WIDGET (self),
|
n_expand = 0;
|
||||||
GTK_ORIENTATION_HORIZONTAL, -1,
|
sizes = g_newa (GtkRequestedSize, n);
|
||||||
&widget_min, &widget_nat,
|
for (i = 0; i < n; i++)
|
||||||
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++)
|
|
||||||
{
|
{
|
||||||
GtkColumnViewColumn *column;
|
GtkColumnViewColumn *column;
|
||||||
|
|
||||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||||
gtk_column_view_column_measure (column, &col_min, &col_nat);
|
if (gtk_column_view_column_get_visible (column))
|
||||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
{
|
||||||
col_size = col_min;
|
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
|
else
|
||||||
col_size = col_nat;
|
sizes[i].minimum_size = sizes[i].natural_size = 0;
|
||||||
|
g_object_unref (column);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
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;
|
x += col_size;
|
||||||
|
}
|
||||||
|
|
||||||
g_object_unref (column);
|
g_object_unref (column);
|
||||||
}
|
}
|
||||||
|
|
||||||
return width + extra;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -220,8 +273,9 @@ gtk_column_view_allocate (GtkWidget *widget,
|
|||||||
int baseline)
|
int baseline)
|
||||||
{
|
{
|
||||||
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
|
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);
|
full_width = gtk_column_view_allocate_columns (self, width);
|
||||||
|
|
||||||
gtk_widget_measure (self->header, GTK_ORIENTATION_VERTICAL, full_width, &min, &nat, NULL, NULL);
|
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;
|
header_height = min;
|
||||||
else
|
else
|
||||||
header_height = nat;
|
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),
|
gtk_widget_allocate (GTK_WIDGET (self->listview),
|
||||||
full_width, height - header_height, -1,
|
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
|
static void
|
||||||
@@ -244,6 +301,23 @@ gtk_column_view_activate_cb (GtkListView *listview,
|
|||||||
g_signal_emit (self, signals[ACTIVATE], 0, pos);
|
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
|
static void
|
||||||
gtk_column_view_dispose (GObject *object)
|
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->factory);
|
||||||
|
|
||||||
g_clear_object (&self->sorter);
|
g_clear_object (&self->sorter);
|
||||||
|
clear_adjustment (self);
|
||||||
|
|
||||||
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
|
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
@@ -291,7 +366,7 @@ gtk_column_view_get_property (GObject *object,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case PROP_HADJUSTMENT:
|
case PROP_HADJUSTMENT:
|
||||||
g_value_set_object (value, gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)));
|
g_value_set_object (value, self->hadjustment);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PROP_HSCROLL_POLICY:
|
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));
|
g_value_set_boolean (value, gtk_column_view_get_single_click_activate (self));
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -335,13 +418,24 @@ gtk_column_view_set_property (GObject *object,
|
|||||||
GParamSpec *pspec)
|
GParamSpec *pspec)
|
||||||
{
|
{
|
||||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||||
|
GtkAdjustment *adjustment;
|
||||||
|
|
||||||
switch (property_id)
|
switch (property_id)
|
||||||
{
|
{
|
||||||
case PROP_HADJUSTMENT:
|
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]);
|
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HADJUSTMENT]);
|
||||||
}
|
}
|
||||||
break;
|
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));
|
gtk_column_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -478,6 +580,30 @@ gtk_column_view_class_init (GtkColumnViewClass *klass)
|
|||||||
FALSE,
|
FALSE,
|
||||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
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);
|
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"));
|
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
|
static void
|
||||||
gtk_column_view_init (GtkColumnView *self)
|
gtk_column_view_init (GtkColumnView *self)
|
||||||
{
|
{
|
||||||
|
GtkEventController *controller;
|
||||||
|
|
||||||
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
|
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
|
||||||
|
|
||||||
self->header = gtk_list_item_widget_new (NULL, "header");
|
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_layout_manager (self->header, gtk_column_view_layout_new (self));
|
||||||
gtk_widget_set_parent (self->header, GTK_WIDGET (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->sorter = gtk_column_view_sorter_new ();
|
||||||
self->factory = gtk_column_list_item_factory_new (self);
|
self->factory = gtk_column_list_item_factory_new (self);
|
||||||
self->listview = GTK_LIST_VIEW (gtk_list_view_new_with_factory (
|
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")));
|
g_quark_from_static_string (I_("view")));
|
||||||
|
|
||||||
gtk_widget_set_overflow (GTK_WIDGET (self), GTK_OVERFLOW_HIDDEN);
|
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);
|
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:
|
* gtk_column_view_get_sorter:
|
||||||
* @self: a #GtkColumnView
|
* @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);
|
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/gtktypes.h>
|
||||||
#include <gtk/gtksortlistmodel.h>
|
#include <gtk/gtksortlistmodel.h>
|
||||||
#include <gtk/gtksorter.h>
|
#include <gtk/gtksorter.h>
|
||||||
|
#include <gtk/gtkfilter.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@@ -92,6 +93,22 @@ void gtk_column_view_set_single_click_activate (GtkColumnView
|
|||||||
GDK_AVAILABLE_IN_ALL
|
GDK_AVAILABLE_IN_ALL
|
||||||
gboolean gtk_column_view_get_single_click_activate (GtkColumnView *self);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __GTK_COLUMN_VIEW_H__ */
|
#endif /* __GTK_COLUMN_VIEW_H__ */
|
||||||
|
@@ -53,10 +53,18 @@ gtk_column_view_cell_measure (GtkWidget *widget,
|
|||||||
int *minimum_baseline,
|
int *minimum_baseline,
|
||||||
int *natural_baseline)
|
int *natural_baseline)
|
||||||
{
|
{
|
||||||
|
GtkColumnViewCell *cell = GTK_COLUMN_VIEW_CELL (widget);
|
||||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||||
|
|
||||||
if (child)
|
if (child)
|
||||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
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
|
static void
|
||||||
|
@@ -60,8 +60,14 @@ struct _GtkColumnViewColumn
|
|||||||
int natural_size_request;
|
int natural_size_request;
|
||||||
int allocation_offset;
|
int allocation_offset;
|
||||||
int allocation_size;
|
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 */
|
/* This list isn't sorted - this is just caching for performance */
|
||||||
GtkColumnViewCell *first_cell; /* no reference, just caching */
|
GtkColumnViewCell *first_cell; /* no reference, just caching */
|
||||||
@@ -80,6 +86,10 @@ enum
|
|||||||
PROP_TITLE,
|
PROP_TITLE,
|
||||||
PROP_SORTER,
|
PROP_SORTER,
|
||||||
PROP_VISIBLE,
|
PROP_VISIBLE,
|
||||||
|
PROP_RESIZABLE,
|
||||||
|
PROP_EXPAND,
|
||||||
|
PROP_REORDERABLE,
|
||||||
|
PROP_FIXED_WIDTH,
|
||||||
|
|
||||||
N_PROPS
|
N_PROPS
|
||||||
};
|
};
|
||||||
@@ -133,6 +143,22 @@ gtk_column_view_column_get_property (GObject *object,
|
|||||||
g_value_set_boolean (value, self->visible);
|
g_value_set_boolean (value, self->visible);
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
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));
|
gtk_column_view_column_set_visible (self, g_value_get_boolean (value));
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -240,6 +282,55 @@ gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
|
|||||||
TRUE,
|
TRUE,
|
||||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
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);
|
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->minimum_size_request = -1;
|
||||||
self->natural_size_request = -1;
|
self->natural_size_request = -1;
|
||||||
self->visible = TRUE;
|
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 *minimum,
|
||||||
int *natural)
|
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)
|
if (self->minimum_size_request < 0)
|
||||||
{
|
{
|
||||||
GtkColumnViewCell *cell;
|
GtkColumnViewCell *cell;
|
||||||
@@ -399,6 +500,7 @@ gtk_column_view_column_allocate (GtkColumnViewColumn *self,
|
|||||||
{
|
{
|
||||||
self->allocation_offset = offset;
|
self->allocation_offset = offset;
|
||||||
self->allocation_size = size;
|
self->allocation_size = size;
|
||||||
|
self->header_position = offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -721,3 +823,207 @@ gtk_column_view_column_get_visible (GtkColumnViewColumn *self)
|
|||||||
|
|
||||||
return self->visible;
|
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
|
GDK_AVAILABLE_IN_ALL
|
||||||
gboolean gtk_column_view_column_get_visible (GtkColumnViewColumn *self);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */
|
#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,
|
void gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
|
||||||
GtkColumnViewCell *cell);
|
GtkColumnViewCell *cell);
|
||||||
GtkColumnViewCell * gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self);
|
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_queue_resize (GtkColumnViewColumn *self);
|
||||||
void gtk_column_view_column_measure (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_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__ */
|
#endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */
|
||||||
|
@@ -118,11 +118,16 @@ gtk_column_view_layout_allocate (GtkLayoutManager *layout_manager,
|
|||||||
int col_x, col_width;
|
int col_x, col_width;
|
||||||
|
|
||||||
if (GTK_IS_COLUMN_VIEW_CELL (child))
|
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));
|
||||||
else
|
|
||||||
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
|
|
||||||
|
|
||||||
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
|
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
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_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
|
gtk_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -21,11 +21,13 @@
|
|||||||
#define __GTK_COLUMN_VIEW_PRIVATE_H__
|
#define __GTK_COLUMN_VIEW_PRIVATE_H__
|
||||||
|
|
||||||
#include "gtk/gtkcolumnview.h"
|
#include "gtk/gtkcolumnview.h"
|
||||||
|
#include "gtk/gtklistview.h"
|
||||||
|
|
||||||
#include "gtk/gtkcolumnviewsorterprivate.h"
|
#include "gtk/gtkcolumnviewsorterprivate.h"
|
||||||
#include "gtk/gtklistitemwidgetprivate.h"
|
#include "gtk/gtklistitemwidgetprivate.h"
|
||||||
|
|
||||||
GtkListItemWidget * gtk_column_view_get_header_widget (GtkColumnView *self);
|
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,
|
void gtk_column_view_measure_across (GtkColumnView *self,
|
||||||
int *minimum,
|
int *minimum,
|
||||||
|
@@ -58,10 +58,18 @@ gtk_column_view_title_measure (GtkWidget *widget,
|
|||||||
int *minimum_baseline,
|
int *minimum_baseline,
|
||||||
int *natural_baseline)
|
int *natural_baseline)
|
||||||
{
|
{
|
||||||
|
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||||
|
|
||||||
if (child)
|
if (child)
|
||||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
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
|
static void
|
||||||
@@ -112,7 +120,7 @@ gtk_column_view_title_resize_func (GtkWidget *widget)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
click_pressed_cb (GtkGestureClick *gesture,
|
click_released_cb (GtkGestureClick *gesture,
|
||||||
guint n_press,
|
guint n_press,
|
||||||
gdouble x,
|
gdouble x,
|
||||||
gdouble y,
|
gdouble y,
|
||||||
@@ -150,7 +158,7 @@ gtk_column_view_title_init (GtkColumnViewTitle *self)
|
|||||||
gtk_box_append (GTK_BOX (self->box), self->sort);
|
gtk_box_append (GTK_BOX (self->box), self->sort);
|
||||||
|
|
||||||
gesture = gtk_gesture_click_new ();
|
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));
|
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -91,6 +91,8 @@ enum
|
|||||||
PROP_MIN_COLUMNS,
|
PROP_MIN_COLUMNS,
|
||||||
PROP_MODEL,
|
PROP_MODEL,
|
||||||
PROP_SINGLE_CLICK_ACTIVATE,
|
PROP_SINGLE_CLICK_ACTIVATE,
|
||||||
|
PROP_ENABLE_RUBBERBAND,
|
||||||
|
PROP_SEARCH_FILTER,
|
||||||
|
|
||||||
N_PROPS
|
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));
|
g_value_set_boolean (value, gtk_list_item_manager_get_single_click_activate (self->item_manager));
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
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));
|
gtk_grid_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -1062,6 +1080,30 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
|
|||||||
FALSE,
|
FALSE,
|
||||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
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);
|
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);
|
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
|
#endif
|
||||||
|
|
||||||
#include <gtk/gtklistbase.h>
|
#include <gtk/gtklistbase.h>
|
||||||
|
#include <gtk/gtkfilter.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@@ -73,6 +74,11 @@ guint gtk_grid_view_get_max_columns (GtkGridView
|
|||||||
GDK_AVAILABLE_IN_ALL
|
GDK_AVAILABLE_IN_ALL
|
||||||
void gtk_grid_view_set_max_columns (GtkGridView *self,
|
void gtk_grid_view_set_max_columns (GtkGridView *self,
|
||||||
guint max_columns);
|
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
|
GDK_AVAILABLE_IN_ALL
|
||||||
void gtk_grid_view_set_single_click_activate (GtkGridView *self,
|
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
|
GDK_AVAILABLE_IN_ALL
|
||||||
gboolean gtk_grid_view_get_single_click_activate (GtkGridView *self);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
|
@@ -28,6 +28,12 @@
|
|||||||
#include "gtkscrollable.h"
|
#include "gtkscrollable.h"
|
||||||
#include "gtksingleselection.h"
|
#include "gtksingleselection.h"
|
||||||
#include "gtktypebuiltins.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;
|
typedef struct _GtkListBasePrivate GtkListBasePrivate;
|
||||||
|
|
||||||
@@ -50,6 +56,24 @@ struct _GtkListBasePrivate
|
|||||||
GtkListItemTracker *selected;
|
GtkListItemTracker *selected;
|
||||||
/* the item that has input focus */
|
/* the item that has input focus */
|
||||||
GtkListItemTracker *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
|
enum
|
||||||
@@ -523,6 +547,8 @@ gtk_list_base_focus (GtkWidget *widget,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gtk_list_base_clear_search_filter (GtkListBase *self);
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gtk_list_base_dispose (GObject *object)
|
gtk_list_base_dispose (GObject *object)
|
||||||
{
|
{
|
||||||
@@ -551,6 +577,8 @@ gtk_list_base_dispose (GObject *object)
|
|||||||
|
|
||||||
g_clear_object (&priv->model);
|
g_clear_object (&priv->model);
|
||||||
|
|
||||||
|
gtk_list_base_clear_search_filter (self);
|
||||||
|
|
||||||
G_OBJECT_CLASS (gtk_list_base_parent_class)->dispose (object);
|
G_OBJECT_CLASS (gtk_list_base_parent_class)->dispose (object);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -764,26 +792,18 @@ gtk_list_base_update_focus_tracker (GtkListBase *self)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
gtk_list_base_scroll_to_item (GtkWidget *widget,
|
gtk_list_base_scroll_to_position (GtkListBase *self,
|
||||||
const char *action_name,
|
guint pos)
|
||||||
GVariant *parameter)
|
|
||||||
{
|
{
|
||||||
GtkListBase *self = GTK_LIST_BASE (widget);
|
|
||||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||||
int start, end;
|
int start, end;
|
||||||
double align_along, align_across;
|
double align_along, align_across;
|
||||||
GtkPackType side_along, side_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 */
|
/* figure out primary orientation and if position is valid */
|
||||||
if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self), pos, &start, &end))
|
if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self), pos, &start, &end))
|
||||||
return;
|
return FALSE;
|
||||||
|
|
||||||
end += start;
|
end += start;
|
||||||
gtk_list_base_compute_scroll_align (self,
|
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 */
|
/* now do the same thing with the other orientation */
|
||||||
if (!gtk_list_base_get_allocation_across (GTK_LIST_BASE (self), pos, &start, &end))
|
if (!gtk_list_base_get_allocation_across (GTK_LIST_BASE (self), pos, &start, &end))
|
||||||
return;
|
return FALSE;
|
||||||
|
|
||||||
end += start;
|
end += start;
|
||||||
gtk_list_base_compute_scroll_align (self,
|
gtk_list_base_compute_scroll_align (self,
|
||||||
@@ -808,13 +828,32 @@ gtk_list_base_scroll_to_item (GtkWidget *widget,
|
|||||||
align_across, side_across,
|
align_across, side_across,
|
||||||
align_along, side_along);
|
align_along, side_along);
|
||||||
|
|
||||||
|
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
|
/* HACK HACK HACK
|
||||||
*
|
*
|
||||||
* GTK has no way to track the focused child. But we now that when a listitem
|
* 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
|
* 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.
|
* because it's the closest we can get to accurate tracking.
|
||||||
*/
|
*/
|
||||||
gtk_list_base_update_focus_tracker (self);
|
gtk_list_base_update_focus_tracker (self);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@@ -863,6 +902,17 @@ gtk_list_base_unselect_all (GtkWidget *widget,
|
|||||||
gtk_selection_model_unselect_all (selection_model);
|
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
|
static gboolean
|
||||||
gtk_list_base_move_cursor_to_start (GtkWidget *widget,
|
gtk_list_base_move_cursor_to_start (GtkWidget *widget,
|
||||||
GVariant *args,
|
GVariant *args,
|
||||||
@@ -1061,6 +1111,20 @@ gtk_list_base_add_custom_move_binding (GtkWidgetClass *widget_class,
|
|||||||
"(bbb)", TRUE, TRUE, TRUE);
|
"(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
|
static void
|
||||||
gtk_list_base_class_init (GtkListBaseClass *klass)
|
gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||||
{
|
{
|
||||||
@@ -1069,6 +1133,7 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
|||||||
gpointer iface;
|
gpointer iface;
|
||||||
|
|
||||||
widget_class->focus = gtk_list_base_focus;
|
widget_class->focus = gtk_list_base_focus;
|
||||||
|
widget_class->snapshot = gtk_list_base_snapshot;
|
||||||
|
|
||||||
gobject_class->dispose = gtk_list_base_dispose;
|
gobject_class->dispose = gtk_list_base_dispose;
|
||||||
gobject_class->get_property = gtk_list_base_get_property;
|
gobject_class->get_property = gtk_list_base_get_property;
|
||||||
@@ -1162,6 +1227,11 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
|||||||
NULL,
|
NULL,
|
||||||
gtk_list_base_unselect_all);
|
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_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_KP_Up, GTK_ORIENTATION_VERTICAL, -1);
|
||||||
gtk_list_base_add_move_binding (widget_class, GDK_KEY_Down, 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_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_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_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
|
static void
|
||||||
@@ -1538,3 +1943,183 @@ gtk_list_base_set_model (GtkListBase *self,
|
|||||||
return TRUE;
|
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 "gtklistbase.h"
|
||||||
|
|
||||||
#include "gtklistitemmanagerprivate.h"
|
#include "gtklistitemmanagerprivate.h"
|
||||||
|
#include "gtkfilter.h"
|
||||||
#include "gtkprivate.h"
|
#include "gtkprivate.h"
|
||||||
|
|
||||||
struct _GtkListBase
|
struct _GtkListBase
|
||||||
@@ -99,5 +100,13 @@ gboolean gtk_list_base_grab_focus_on_item (GtkListBase
|
|||||||
gboolean select,
|
gboolean select,
|
||||||
gboolean modify,
|
gboolean modify,
|
||||||
gboolean extend);
|
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__ */
|
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
#include "gtkintl.h"
|
#include "gtkintl.h"
|
||||||
#include "gtklistitemfactoryprivate.h"
|
#include "gtklistitemfactoryprivate.h"
|
||||||
#include "gtklistitemprivate.h"
|
#include "gtklistitemprivate.h"
|
||||||
|
#include "gtklistbaseprivate.h"
|
||||||
#include "gtkmain.h"
|
#include "gtkmain.h"
|
||||||
#include "gtkselectionmodel.h"
|
#include "gtkselectionmodel.h"
|
||||||
#include "gtkwidget.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);
|
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||||
GtkWidget *widget = GTK_WIDGET (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)
|
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;
|
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;
|
GdkModifierType state;
|
||||||
GdkEvent *event;
|
GdkEvent *event;
|
||||||
|
@@ -85,6 +85,8 @@ enum
|
|||||||
PROP_MODEL,
|
PROP_MODEL,
|
||||||
PROP_SHOW_SEPARATORS,
|
PROP_SHOW_SEPARATORS,
|
||||||
PROP_SINGLE_CLICK_ACTIVATE,
|
PROP_SINGLE_CLICK_ACTIVATE,
|
||||||
|
PROP_ENABLE_RUBBERBAND,
|
||||||
|
PROP_SEARCH_FILTER,
|
||||||
|
|
||||||
N_PROPS
|
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));
|
g_value_set_boolean (value, gtk_list_item_manager_get_single_click_activate (self->item_manager));
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
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));
|
gtk_list_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||||
break;
|
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:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||||
break;
|
break;
|
||||||
@@ -771,6 +789,30 @@ gtk_list_view_class_init (GtkListViewClass *klass)
|
|||||||
FALSE,
|
FALSE,
|
||||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
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);
|
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);
|
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
|
#endif
|
||||||
|
|
||||||
#include <gtk/gtklistbase.h>
|
#include <gtk/gtklistbase.h>
|
||||||
|
#include <gtk/gtkfilter.h>
|
||||||
|
|
||||||
G_BEGIN_DECLS
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
@@ -75,6 +76,22 @@ void gtk_list_view_set_single_click_activate (GtkListView
|
|||||||
GDK_AVAILABLE_IN_ALL
|
GDK_AVAILABLE_IN_ALL
|
||||||
gboolean gtk_list_view_get_single_click_activate (GtkListView *self);
|
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
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* __GTK_LIST_VIEW_H__ */
|
#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"
|
#include "gtkprintbackendprivate.h"
|
||||||
|
|
||||||
|
|
||||||
|
static void gtk_print_backend_finalize (GObject *object);
|
||||||
static void gtk_print_backend_dispose (GObject *object);
|
static void gtk_print_backend_dispose (GObject *object);
|
||||||
static void gtk_print_backend_set_property (GObject *object,
|
static void gtk_print_backend_set_property (GObject *object,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
@@ -251,6 +252,7 @@ gtk_print_backend_class_init (GtkPrintBackendClass *class)
|
|||||||
|
|
||||||
backend_parent_class = g_type_class_peek_parent (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->dispose = gtk_print_backend_dispose;
|
||||||
object_class->set_property = gtk_print_backend_set_property;
|
object_class->set_property = gtk_print_backend_set_property;
|
||||||
object_class->get_property = gtk_print_backend_get_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 unref the printers in dispose, not in finalize so that
|
||||||
* we can break refcount cycles with gtk_print_backend_destroy
|
* 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);
|
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
|
static void
|
||||||
fallback_printer_request_details (GtkPrinter *printer)
|
fallback_printer_request_details (GtkPrinter *printer)
|
||||||
|
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>
|
<child>
|
||||||
<object class="GtkColumnViewColumn" id="changes">
|
<object class="GtkColumnViewColumn" id="changes">
|
||||||
<property name="title"></property>
|
<property name="title"></property>
|
||||||
|
<property name="expand">1</property>
|
||||||
<property name="factory">
|
<property name="factory">
|
||||||
<object class="GtkSignalListItemFactory">
|
<object class="GtkSignalListItemFactory">
|
||||||
<signal name="bind" handler="bind_changes_cb"/>
|
<signal name="bind" handler="bind_changes_cb"/>
|
||||||
|
@@ -45,6 +45,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkColumnViewColumn">
|
<object class="GtkColumnViewColumn">
|
||||||
<property name="title">Type</property>
|
<property name="title">Type</property>
|
||||||
|
<property name="expand">1</property>
|
||||||
<property name="factory">
|
<property name="factory">
|
||||||
<object class="GtkSignalListItemFactory">
|
<object class="GtkSignalListItemFactory">
|
||||||
<signal name="setup" handler="setup_type_cb"/>
|
<signal name="setup" handler="setup_type_cb"/>
|
||||||
@@ -57,6 +58,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkColumnViewColumn">
|
<object class="GtkColumnViewColumn">
|
||||||
<property name="title">Name</property>
|
<property name="title">Name</property>
|
||||||
|
<property name="expand">1</property>
|
||||||
<property name="factory">
|
<property name="factory">
|
||||||
<object class="GtkSignalListItemFactory">
|
<object class="GtkSignalListItemFactory">
|
||||||
<signal name="setup" handler="setup_name_cb"/>
|
<signal name="setup" handler="setup_name_cb"/>
|
||||||
@@ -68,6 +70,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkColumnViewColumn">
|
<object class="GtkColumnViewColumn">
|
||||||
<property name="title">Label</property>
|
<property name="title">Label</property>
|
||||||
|
<property name="expand">1</property>
|
||||||
<property name="factory">
|
<property name="factory">
|
||||||
<object class="GtkSignalListItemFactory">
|
<object class="GtkSignalListItemFactory">
|
||||||
<signal name="setup" handler="setup_label_cb"/>
|
<signal name="setup" handler="setup_label_cb"/>
|
||||||
|
@@ -54,6 +54,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkColumnViewColumn">
|
<object class="GtkColumnViewColumn">
|
||||||
<property name="title" translatable="yes">Value</property>
|
<property name="title" translatable="yes">Value</property>
|
||||||
|
<property name="expand">1</property>
|
||||||
<property name="factory">
|
<property name="factory">
|
||||||
<object class="GtkSignalListItemFactory">
|
<object class="GtkSignalListItemFactory">
|
||||||
<signal name="bind" handler="bind_value_cb"/>
|
<signal name="bind" handler="bind_value_cb"/>
|
||||||
|
@@ -62,6 +62,7 @@
|
|||||||
<child>
|
<child>
|
||||||
<object class="GtkColumnViewColumn" id="path">
|
<object class="GtkColumnViewColumn" id="path">
|
||||||
<property name="title" translatable="yes">Path</property>
|
<property name="title" translatable="yes">Path</property>
|
||||||
|
<property name="expand">1</property>
|
||||||
<property name="factory">
|
<property name="factory">
|
||||||
<object class="GtkSignalListItemFactory">
|
<object class="GtkSignalListItemFactory">
|
||||||
<signal name="setup" handler="setup_name_cb"/>
|
<signal name="setup" handler="setup_name_cb"/>
|
||||||
@@ -92,6 +93,11 @@
|
|||||||
</property>
|
</property>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkColumnViewColumn" id="filler">
|
||||||
|
<property name="expand">1</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
</object>
|
</object>
|
||||||
|
@@ -134,6 +134,7 @@ gtk_private_sources = files([
|
|||||||
'gtkscaler.c',
|
'gtkscaler.c',
|
||||||
'gtksearchengine.c',
|
'gtksearchengine.c',
|
||||||
'gtksearchenginemodel.c',
|
'gtksearchenginemodel.c',
|
||||||
|
'gtkset.c',
|
||||||
'gtksizerequestcache.c',
|
'gtksizerequestcache.c',
|
||||||
'gtkstyleanimation.c',
|
'gtkstyleanimation.c',
|
||||||
'gtkstylecascade.c',
|
'gtkstylecascade.c',
|
||||||
@@ -301,6 +302,7 @@ gtk_public_sources = files([
|
|||||||
'gtkmodules.c',
|
'gtkmodules.c',
|
||||||
'gtkmountoperation.c',
|
'gtkmountoperation.c',
|
||||||
'gtkmultifilter.c',
|
'gtkmultifilter.c',
|
||||||
|
'gtkmultiselection.c',
|
||||||
'gtkmultisorter.c',
|
'gtkmultisorter.c',
|
||||||
'gtknativedialog.c',
|
'gtknativedialog.c',
|
||||||
'gtknomediafile.c',
|
'gtknomediafile.c',
|
||||||
@@ -574,6 +576,7 @@ gtk_public_headers = files([
|
|||||||
'gtkmessagedialog.h',
|
'gtkmessagedialog.h',
|
||||||
'gtkmountoperation.h',
|
'gtkmountoperation.h',
|
||||||
'gtkmultifilter.h',
|
'gtkmultifilter.h',
|
||||||
|
'gtkmultiselection.h',
|
||||||
'gtkmultisorter.h',
|
'gtkmultisorter.h',
|
||||||
'gtknative.h',
|
'gtknative.h',
|
||||||
'gtknativedialog.h',
|
'gtknativedialog.h',
|
||||||
|
@@ -114,7 +114,8 @@ test_type (gconstpointer data)
|
|||||||
}
|
}
|
||||||
else if (g_type_is_a (type, GTK_TYPE_FILTER_LIST_MODEL) ||
|
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_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);
|
GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
|
||||||
instance = g_object_new (type,
|
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) ||
|
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_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)
|
strcmp (pspec->name, "model") == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
@@ -39,6 +39,7 @@ tests = [
|
|||||||
['listbox'],
|
['listbox'],
|
||||||
['main'],
|
['main'],
|
||||||
['maplistmodel'],
|
['maplistmodel'],
|
||||||
|
['multiselection'],
|
||||||
['notify'],
|
['notify'],
|
||||||
['no-gtk-init'],
|
['no-gtk-init'],
|
||||||
['object'],
|
['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) ||
|
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_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);
|
GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
|
||||||
instance = g_object_new (type,
|
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) ||
|
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_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);
|
GListStore *list_store = g_list_store_new (G_TYPE_OBJECT);
|
||||||
object = g_object_new (test_type,
|
object = g_object_new (test_type,
|
||||||
|
Reference in New Issue
Block a user