Compare commits
42 Commits
wip/matthi
...
columnview
Author | SHA1 | Date | |
---|---|---|---|
|
d17f224e71 | ||
|
b73be6e344 | ||
|
4a80f23213 | ||
|
3b8888acda | ||
|
786c95e503 | ||
|
7007c72fc5 | ||
|
9eaeb81433 | ||
|
3bddbcdd52 | ||
|
13d5d60db0 | ||
|
1b24528b9c | ||
|
6e7ebc49eb | ||
|
bdc6770bd9 | ||
|
8d2fef1e23 | ||
|
90668f7e01 | ||
|
74f72a146c | ||
|
5553bf1c39 | ||
|
77a3f81971 | ||
|
b3e0353e6b | ||
|
780915e318 | ||
|
7242b801ff | ||
|
88669080c4 | ||
|
51e8dc924b | ||
|
29b5d3ab38 | ||
|
bb06fffab1 | ||
|
94c5b00231 | ||
|
59e49dafd7 | ||
|
d5ce5b53d6 | ||
|
915bdf24a7 | ||
|
114054266b | ||
|
dcea6d6cda | ||
|
a118267ab7 | ||
|
cc63c581c3 | ||
|
dbc1a8ba11 | ||
|
515a86645a | ||
|
cbabb85e6d | ||
|
1031625bd4 | ||
|
65ed3aa1d9 | ||
|
ed04c46078 | ||
|
1b2df91e6d | ||
|
f745c0c2aa | ||
|
2ed46450ea | ||
|
6d168079d4 |
@@ -12,7 +12,7 @@ static GtkWidget *window = NULL;
|
||||
static GtkWidget *scrolledwindow;
|
||||
static int selected;
|
||||
|
||||
#define N_WIDGET_TYPES 4
|
||||
#define N_WIDGET_TYPES 6
|
||||
|
||||
|
||||
static int hincrement = 5;
|
||||
@@ -64,6 +64,7 @@ populate_icons (void)
|
||||
gtk_grid_attach (GTK_GRID (grid), create_icon (), left, top, 1, 1);
|
||||
|
||||
hincrement = 0;
|
||||
vincrement = 5;
|
||||
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
|
||||
GTK_POLICY_NEVER,
|
||||
@@ -100,6 +101,7 @@ populate_text (gboolean hilight)
|
||||
gtk_text_view_set_buffer (GTK_TEXT_VIEW (textview), buffer);
|
||||
|
||||
hincrement = 0;
|
||||
vincrement = 5;
|
||||
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
|
||||
GTK_POLICY_NEVER,
|
||||
@@ -124,6 +126,7 @@ populate_image (void)
|
||||
gtk_picture_set_can_shrink (GTK_PICTURE (image), FALSE);
|
||||
|
||||
hincrement = 5;
|
||||
vincrement = 5;
|
||||
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
@@ -131,6 +134,42 @@ populate_image (void)
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolledwindow), image);
|
||||
}
|
||||
|
||||
extern GtkWidget *create_weather_view (void);
|
||||
|
||||
static void
|
||||
populate_list (void)
|
||||
{
|
||||
GtkWidget *list;
|
||||
|
||||
list = create_weather_view ();
|
||||
|
||||
hincrement = 5;
|
||||
vincrement = 0;
|
||||
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolledwindow), list);
|
||||
}
|
||||
|
||||
extern GtkWidget *create_color_grid (void);
|
||||
|
||||
static void
|
||||
populate_grid (void)
|
||||
{
|
||||
GtkWidget *list;
|
||||
|
||||
list = create_color_grid ();
|
||||
|
||||
hincrement = 0;
|
||||
vincrement = 5;
|
||||
|
||||
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow),
|
||||
GTK_POLICY_AUTOMATIC,
|
||||
GTK_POLICY_AUTOMATIC);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolledwindow), list);
|
||||
}
|
||||
|
||||
static void
|
||||
set_widget_type (int type)
|
||||
{
|
||||
@@ -164,6 +203,16 @@ set_widget_type (int type)
|
||||
populate_image ();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Scrolling a list");
|
||||
populate_list ();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
gtk_window_set_title (GTK_WINDOW (window), "Scrolling a grid");
|
||||
populate_grid ();
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
@@ -310,6 +310,7 @@ setup_simple_listitem_cb (GtkListItemFactory *factory,
|
||||
color_expression = gtk_property_expression_new (GTK_TYPE_LIST_ITEM, expression, "item");
|
||||
|
||||
picture = gtk_picture_new ();
|
||||
gtk_widget_set_size_request (picture, 32, 32);
|
||||
gtk_expression_bind (color_expression, picture, "paintable", NULL);
|
||||
|
||||
gtk_list_item_set_child (list_item, picture);
|
||||
@@ -404,6 +405,34 @@ set_item (GBinding *binding,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
create_color_grid (void)
|
||||
{
|
||||
GtkWidget *gridview;
|
||||
GtkListItemFactory *factory;
|
||||
GListModel *model, *selection;
|
||||
|
||||
gridview = gtk_grid_view_new ();
|
||||
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
|
||||
factory = gtk_signal_list_item_factory_new ();
|
||||
g_signal_connect (factory, "setup", G_CALLBACK (setup_simple_listitem_cb), NULL);
|
||||
gtk_grid_view_set_factory (GTK_GRID_VIEW (gridview), factory);
|
||||
g_object_unref (factory);
|
||||
|
||||
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
|
||||
gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (gridview), TRUE);
|
||||
|
||||
model = G_LIST_MODEL (gtk_sort_list_model_new (create_colors_model (), NULL));
|
||||
selection = G_LIST_MODEL (gtk_multi_selection_new (model));
|
||||
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), selection);
|
||||
g_object_unref (selection);
|
||||
g_object_unref (model);
|
||||
|
||||
return gridview;
|
||||
}
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
@@ -415,7 +444,7 @@ do_listview_colors (GtkWidget *do_widget)
|
||||
GtkListItemFactory *factory;
|
||||
GListStore *factories;
|
||||
GListModel *model;
|
||||
GtkNoSelection *selection;
|
||||
|
||||
GtkSorter *sorter;
|
||||
GtkSorter *multi_sorter;
|
||||
GListStore *sorters;
|
||||
@@ -435,17 +464,10 @@ do_listview_colors (GtkWidget *do_widget)
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
|
||||
gridview = gtk_grid_view_new ();
|
||||
gtk_scrollable_set_hscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
gtk_scrollable_set_vscroll_policy (GTK_SCROLLABLE (gridview), GTK_SCROLL_NATURAL);
|
||||
|
||||
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
|
||||
|
||||
model = G_LIST_MODEL (gtk_sort_list_model_new (create_colors_model (), NULL));
|
||||
selection = gtk_no_selection_new (model);
|
||||
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), G_LIST_MODEL (selection));
|
||||
gridview = create_color_grid ();
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), gridview);
|
||||
g_object_unref (selection);
|
||||
model = gtk_grid_view_get_model (GTK_GRID_VIEW (gridview));
|
||||
g_object_get (model, "model", &model, NULL);
|
||||
|
||||
sorters = g_list_store_new (GTK_TYPE_SORTER);
|
||||
|
||||
@@ -559,7 +581,6 @@ do_listview_colors (GtkWidget *do_widget)
|
||||
G_BINDING_SYNC_CREATE,
|
||||
set_item, NULL,
|
||||
NULL, NULL);
|
||||
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
||||
|
@@ -275,13 +275,33 @@ bind_widget (GtkListItem *list_item,
|
||||
|
||||
static GtkWidget *window = NULL;
|
||||
|
||||
GtkWidget *
|
||||
create_weather_view (void)
|
||||
{
|
||||
GtkWidget *listview;
|
||||
GListModel *model, *selection;
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_functions_list_item_factory_new (setup_widget,
|
||||
bind_widget,
|
||||
NULL, NULL));
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
|
||||
model = create_weather_model ();
|
||||
selection = G_LIST_MODEL (gtk_no_selection_new (model));
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), selection);
|
||||
g_object_unref (selection);
|
||||
g_object_unref (model);
|
||||
|
||||
return listview;
|
||||
}
|
||||
|
||||
GtkWidget *
|
||||
do_listview_weather (GtkWidget *do_widget)
|
||||
{
|
||||
if (window == NULL)
|
||||
{
|
||||
GtkWidget *listview, *sw;;
|
||||
GListModel *model, *selection;
|
||||
|
||||
window = gtk_window_new ();
|
||||
gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);
|
||||
@@ -293,19 +313,7 @@ do_listview_weather (GtkWidget *do_widget)
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_window_set_child (GTK_WINDOW (window), sw);
|
||||
|
||||
listview = gtk_list_view_new_with_factory (
|
||||
gtk_functions_list_item_factory_new (setup_widget,
|
||||
bind_widget,
|
||||
NULL, NULL));
|
||||
gtk_orientable_set_orientation (GTK_ORIENTABLE (listview), GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_list_view_set_show_separators (GTK_LIST_VIEW (listview), TRUE);
|
||||
model = create_weather_model ();
|
||||
selection = G_LIST_MODEL (gtk_no_selection_new (model));
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (listview), selection);
|
||||
g_object_unref (selection);
|
||||
g_object_unref (model);
|
||||
|
||||
listview = create_weather_view ();
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), listview);
|
||||
}
|
||||
|
||||
|
@@ -479,6 +479,11 @@ gtk_list_view_set_show_separators
|
||||
gtk_list_view_get_show_separators
|
||||
gtk_list_view_set_single_click_activate
|
||||
gtk_list_view_get_single_click_activate
|
||||
gtk_list_view_set_enable_rubberband
|
||||
gtk_list_view_get_enable_rubberband
|
||||
gtk_list_view_set_search_filter
|
||||
gtk_list_view_get_search_filter
|
||||
gtk_list_view_next_match
|
||||
<SUBSECTION Standard>
|
||||
GTK_LIST_VIEW
|
||||
GTK_LIST_VIEW_CLASS
|
||||
@@ -496,6 +501,7 @@ gtk_list_view_get_type
|
||||
GtkColumnView
|
||||
gtk_column_view_new
|
||||
gtk_column_view_append_column
|
||||
gtk_column_view_insert_column
|
||||
gtk_column_view_remove_column
|
||||
gtk_column_view_get_columns
|
||||
gtk_column_view_get_model
|
||||
@@ -506,6 +512,11 @@ gtk_column_view_set_show_separators
|
||||
gtk_column_view_sort_by_column
|
||||
gtk_column_view_set_single_click_activate
|
||||
gtk_column_view_get_single_click_activate
|
||||
gtk_column_view_set_enable_rubberband
|
||||
gtk_column_view_get_enable_rubberband
|
||||
gtk_column_view_set_search_filter
|
||||
gtk_column_view_get_search_filter
|
||||
gtk_column_view_next_match
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW
|
||||
GTK_COLUMN_VIEW_CLASS
|
||||
@@ -530,6 +541,10 @@ gtk_column_view_column_set_title
|
||||
gtk_column_view_column_get_title
|
||||
gtk_column_view_column_set_sorter
|
||||
gtk_column_view_column_get_sorter
|
||||
gtk_column_view_column_set_visible
|
||||
gtk_column_view_column_get_visible
|
||||
gtk_column_view_column_set_fixed_width
|
||||
gtk_column_view_column_get_fixed_width
|
||||
<SUBSECTION Standard>
|
||||
GTK_COLUMN_VIEW_COLUMN
|
||||
GTK_COLUMN_VIEW_COLUMN_CLASS
|
||||
@@ -554,6 +569,11 @@ gtk_grid_view_set_min_columns
|
||||
gtk_grid_view_get_min_columns
|
||||
gtk_grid_view_set_single_click_activate
|
||||
gtk_grid_view_get_single_click_activate
|
||||
gtk_grid_view_set_enable_rubberband
|
||||
gtk_grid_view_get_enable_rubberband
|
||||
gtk_grid_view_set_search_filter
|
||||
gtk_grid_view_get_search_filter
|
||||
gtk_grid_view_next_match
|
||||
<SUBSECTION Standard>
|
||||
GTK_GRID_VIEW
|
||||
GTK_GRID_VIEW_CLASS
|
||||
|
@@ -175,6 +175,7 @@
|
||||
#include <gtk/gtkmessagedialog.h>
|
||||
#include <gtk/gtkmountoperation.h>
|
||||
#include <gtk/gtkmultifilter.h>
|
||||
#include <gtk/gtkmultiselection.h>
|
||||
#include <gtk/gtkmultisorter.h>
|
||||
#include <gtk/gtknative.h>
|
||||
#include <gtk/gtknativedialog.h>
|
||||
|
@@ -34,6 +34,13 @@
|
||||
#include "gtkprivate.h"
|
||||
#include "gtkscrollable.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtksizerequest.h"
|
||||
#include "gtkadjustment.h"
|
||||
#include "gtkgesturedrag.h"
|
||||
#include "gtkeventcontrollermotion.h"
|
||||
#include "gtkdragsource.h"
|
||||
#include "gtkeventcontrollerkey.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkcolumnview
|
||||
@@ -63,6 +70,21 @@ struct _GtkColumnView
|
||||
GtkColumnListItemFactory *factory;
|
||||
|
||||
GtkSorter *sorter;
|
||||
|
||||
GtkAdjustment *hadjustment;
|
||||
|
||||
gboolean in_column_resize;
|
||||
gboolean in_column_reorder;
|
||||
int drag_pos;
|
||||
int drag_x;
|
||||
int drag_offset;
|
||||
int drag_column_x;
|
||||
|
||||
GtkGesture *drag_gesture;
|
||||
|
||||
guint autoscroll_id;
|
||||
double autoscroll_x;
|
||||
double autoscroll_delta;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewClass
|
||||
@@ -82,6 +104,8 @@ enum
|
||||
PROP_VADJUSTMENT,
|
||||
PROP_VSCROLL_POLICY,
|
||||
PROP_SINGLE_CLICK_ACTIVATE,
|
||||
PROP_ENABLE_RUBBERBAND,
|
||||
PROP_SEARCH_FILTER,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
@@ -169,48 +193,77 @@ gtk_column_view_allocate_columns (GtkColumnView *self,
|
||||
int width)
|
||||
{
|
||||
GtkScrollablePolicy scroll_policy;
|
||||
int col_min, col_nat, widget_min, widget_nat, extra, col_size, x;
|
||||
int col_min, col_nat, extra, col_size, x;
|
||||
int n, n_expand, expand_size, n_extra;
|
||||
guint i;
|
||||
GtkRequestedSize *sizes;
|
||||
|
||||
gtk_column_view_measure_across (self, &col_min, &col_nat);
|
||||
gtk_widget_measure (GTK_WIDGET (self),
|
||||
GTK_ORIENTATION_HORIZONTAL, -1,
|
||||
&widget_min, &widget_nat,
|
||||
NULL, NULL);
|
||||
|
||||
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
{
|
||||
extra = widget_min - col_min;
|
||||
col_size = col_min;
|
||||
}
|
||||
else
|
||||
{
|
||||
extra = widget_nat - col_nat;
|
||||
col_size = col_nat;
|
||||
}
|
||||
width -= extra;
|
||||
width = MAX (width, col_size);
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
|
||||
n_expand = 0;
|
||||
sizes = g_newa (GtkRequestedSize, n);
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
gtk_column_view_column_measure (column, &col_min, &col_nat);
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
col_size = col_min;
|
||||
if (gtk_column_view_column_get_visible (column))
|
||||
{
|
||||
gtk_column_view_column_measure (column, &sizes[i].minimum_size, &sizes[i].natural_size);
|
||||
if (gtk_column_view_column_get_expand (column))
|
||||
n_expand++;
|
||||
}
|
||||
else
|
||||
col_size = col_nat;
|
||||
sizes[i].minimum_size = sizes[i].natural_size = 0;
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
gtk_column_view_column_allocate (column, x, col_size);
|
||||
x += col_size;
|
||||
gtk_column_view_measure_across (self, &col_min, &col_nat);
|
||||
|
||||
scroll_policy = gtk_scrollable_get_hscroll_policy (GTK_SCROLLABLE (self->listview));
|
||||
if (scroll_policy == GTK_SCROLL_MINIMUM)
|
||||
extra = MAX (width - col_min, 0);
|
||||
else
|
||||
extra = MAX (width - col_min, col_nat - col_min);
|
||||
|
||||
extra = gtk_distribute_natural_allocation (extra, n, sizes);
|
||||
if (n_expand > 0)
|
||||
{
|
||||
expand_size = extra / n_expand;
|
||||
n_extra = extra % n_expand;
|
||||
}
|
||||
else
|
||||
expand_size = n_extra = 0;
|
||||
|
||||
x = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
if (gtk_column_view_column_get_visible (column))
|
||||
{
|
||||
col_size = sizes[i].minimum_size;
|
||||
if (gtk_column_view_column_get_expand (column))
|
||||
{
|
||||
col_size += expand_size;
|
||||
if (n_extra > 0)
|
||||
{
|
||||
col_size++;
|
||||
n_extra--;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_column_view_column_allocate (column, x, col_size);
|
||||
if (self->in_column_reorder && i == self->drag_pos)
|
||||
gtk_column_view_column_set_header_position (column, self->drag_x);
|
||||
|
||||
x += col_size;
|
||||
}
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
return width + extra;
|
||||
return x;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -220,8 +273,9 @@ gtk_column_view_allocate (GtkWidget *widget,
|
||||
int baseline)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (widget);
|
||||
int full_width, header_height, min, nat;
|
||||
int full_width, header_height, min, nat, x;
|
||||
|
||||
x = gtk_adjustment_get_value (self->hadjustment);
|
||||
full_width = gtk_column_view_allocate_columns (self, width);
|
||||
|
||||
gtk_widget_measure (self->header, GTK_ORIENTATION_VERTICAL, full_width, &min, &nat, NULL, NULL);
|
||||
@@ -229,11 +283,14 @@ gtk_column_view_allocate (GtkWidget *widget,
|
||||
header_height = min;
|
||||
else
|
||||
header_height = nat;
|
||||
gtk_widget_allocate (self->header, full_width, header_height, -1, NULL);
|
||||
gtk_widget_allocate (self->header, full_width, header_height, -1,
|
||||
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (-x, 0)));
|
||||
|
||||
gtk_widget_allocate (GTK_WIDGET (self->listview),
|
||||
full_width, height - header_height, -1,
|
||||
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (0, header_height)));
|
||||
gsk_transform_translate (NULL, &GRAPHENE_POINT_INIT (-x, header_height)));
|
||||
|
||||
gtk_adjustment_configure (self->hadjustment, x, 0, full_width, width * 0.1, width * 0.9, width);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -244,6 +301,23 @@ gtk_column_view_activate_cb (GtkListView *listview,
|
||||
g_signal_emit (self, signals[ACTIVATE], 0, pos);
|
||||
}
|
||||
|
||||
static void
|
||||
adjustment_value_changed_cb (GtkAdjustment *adjustment,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
gtk_widget_queue_allocate (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
clear_adjustment (GtkColumnView *self)
|
||||
{
|
||||
if (self->hadjustment == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->hadjustment, adjustment_value_changed_cb, self);
|
||||
g_clear_object (&self->hadjustment);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_dispose (GObject *object)
|
||||
{
|
||||
@@ -262,6 +336,7 @@ gtk_column_view_dispose (GObject *object)
|
||||
g_clear_object (&self->factory);
|
||||
|
||||
g_clear_object (&self->sorter);
|
||||
clear_adjustment (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_parent_class)->dispose (object);
|
||||
}
|
||||
@@ -291,7 +366,7 @@ gtk_column_view_get_property (GObject *object,
|
||||
break;
|
||||
|
||||
case PROP_HADJUSTMENT:
|
||||
g_value_set_object (value, gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)));
|
||||
g_value_set_object (value, self->hadjustment);
|
||||
break;
|
||||
|
||||
case PROP_HSCROLL_POLICY:
|
||||
@@ -322,6 +397,14 @@ gtk_column_view_get_property (GObject *object,
|
||||
g_value_set_boolean (value, gtk_column_view_get_single_click_activate (self));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
g_value_set_boolean (value, gtk_column_view_get_enable_rubberband (self));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
g_value_set_object (value, gtk_column_view_get_search_filter (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -335,13 +418,24 @@ gtk_column_view_set_property (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkColumnView *self = GTK_COLUMN_VIEW (object);
|
||||
GtkAdjustment *adjustment;
|
||||
|
||||
switch (property_id)
|
||||
{
|
||||
case PROP_HADJUSTMENT:
|
||||
if (gtk_scrollable_get_hadjustment (GTK_SCROLLABLE (self->listview)) != g_value_get_object (value))
|
||||
adjustment = g_value_get_object (value);
|
||||
if (adjustment == NULL)
|
||||
adjustment = gtk_adjustment_new (0.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
||||
g_object_ref_sink (adjustment);
|
||||
|
||||
if (self->hadjustment != adjustment)
|
||||
{
|
||||
gtk_scrollable_set_hadjustment (GTK_SCROLLABLE (self->listview), g_value_get_object (value));
|
||||
clear_adjustment (self);
|
||||
|
||||
self->hadjustment = adjustment;
|
||||
|
||||
g_signal_connect (adjustment, "value-changed", G_CALLBACK (adjustment_value_changed_cb), self);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HADJUSTMENT]);
|
||||
}
|
||||
break;
|
||||
@@ -382,6 +476,14 @@ gtk_column_view_set_property (GObject *object,
|
||||
gtk_column_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
gtk_column_view_set_enable_rubberband (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
gtk_column_view_set_search_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -478,6 +580,30 @@ gtk_column_view_class_init (GtkColumnViewClass *klass)
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkColumnView:enable-rubberband:
|
||||
*
|
||||
* Allow rubberband selection
|
||||
*/
|
||||
properties[PROP_ENABLE_RUBBERBAND] =
|
||||
g_param_spec_boolean ("enable-rubberband",
|
||||
P_("Enable rubberband selection"),
|
||||
P_("Allow selecting items by dragging with the mouse"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkColumnView:search-filter:
|
||||
*
|
||||
* Filter used for search
|
||||
*/
|
||||
properties[PROP_SEARCH_FILTER] =
|
||||
g_param_spec_object ("search-filter",
|
||||
P_("Search filter"),
|
||||
P_("Filter used for searching"),
|
||||
GTK_TYPE_FILTER,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
@@ -507,9 +633,366 @@ gtk_column_view_class_init (GtkColumnViewClass *klass)
|
||||
gtk_widget_class_set_css_name (widget_class, I_("treeview"));
|
||||
}
|
||||
|
||||
static void update_column_resize (GtkColumnView *self,
|
||||
double x);
|
||||
static void update_column_reorder (GtkColumnView *self,
|
||||
double x);
|
||||
|
||||
static gboolean
|
||||
autoscroll_cb (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer data)
|
||||
{
|
||||
GtkColumnView *self = data;
|
||||
|
||||
gtk_adjustment_set_value (self->hadjustment,
|
||||
gtk_adjustment_get_value (self->hadjustment) + self->autoscroll_delta);
|
||||
|
||||
self->autoscroll_x += self->autoscroll_delta;
|
||||
|
||||
if (self->in_column_resize)
|
||||
update_column_resize (self, self->autoscroll_x);
|
||||
else if (self->in_column_reorder)
|
||||
update_column_reorder (self, self->autoscroll_x);
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
add_autoscroll (GtkColumnView *self,
|
||||
double x,
|
||||
double delta)
|
||||
{
|
||||
self->autoscroll_x = x;
|
||||
self->autoscroll_delta = delta;
|
||||
|
||||
if (self->autoscroll_id == 0)
|
||||
self->autoscroll_id = gtk_widget_add_tick_callback (GTK_WIDGET (self), autoscroll_cb, self, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_autoscroll (GtkColumnView *self)
|
||||
{
|
||||
if (self->autoscroll_id != 0)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (self), self->autoscroll_id);
|
||||
self->autoscroll_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
#define DRAG_WIDTH 6
|
||||
|
||||
static gboolean
|
||||
gtk_column_view_in_resize_rect (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
GtkWidget *header;
|
||||
graphene_rect_t rect;
|
||||
|
||||
header = gtk_column_view_column_get_header (column);
|
||||
|
||||
if (!gtk_widget_compute_bounds (header, self->header, &rect))
|
||||
return FALSE;
|
||||
|
||||
rect.origin.x += rect.size.width - DRAG_WIDTH / 2;
|
||||
rect.size.width = DRAG_WIDTH;
|
||||
|
||||
return graphene_rect_contains_point (&rect, &(graphene_point_t) { x, y});
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_column_view_in_header (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
GtkWidget *header;
|
||||
graphene_rect_t rect;
|
||||
|
||||
header = gtk_column_view_column_get_header (column);
|
||||
|
||||
if (!gtk_widget_compute_bounds (header, self->header, &rect))
|
||||
return FALSE;
|
||||
|
||||
return graphene_rect_contains_point (&rect, &(graphene_point_t) { x, y});
|
||||
}
|
||||
|
||||
static void
|
||||
header_drag_begin (GtkGestureDrag *gesture,
|
||||
double start_x,
|
||||
double start_y,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
int i, n;
|
||||
|
||||
self->drag_pos = -1;
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
|
||||
for (i = 0; !self->in_column_resize && i < n; i++)
|
||||
{
|
||||
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
|
||||
if (!gtk_column_view_column_get_visible (column))
|
||||
{
|
||||
g_object_unref (column);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 < n &&
|
||||
gtk_column_view_column_get_resizable (column) &&
|
||||
gtk_column_view_in_resize_rect (self, column, start_x, start_y))
|
||||
{
|
||||
int size;
|
||||
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
if (!gtk_widget_has_focus (GTK_WIDGET (self)))
|
||||
gtk_widget_grab_focus (GTK_WIDGET (self));
|
||||
|
||||
gtk_column_view_column_get_allocation (column, NULL, &size);
|
||||
gtk_column_view_column_set_fixed_width (column, size);
|
||||
|
||||
self->drag_pos = i;
|
||||
self->drag_x = start_x - size;
|
||||
self->in_column_resize = TRUE;
|
||||
|
||||
g_object_unref (column);
|
||||
break;
|
||||
}
|
||||
|
||||
if (gtk_column_view_column_get_reorderable (column) &&
|
||||
gtk_column_view_in_header (self, column, start_x, start_y))
|
||||
{
|
||||
int pos;
|
||||
|
||||
gtk_column_view_column_get_allocation (column, &pos, NULL);
|
||||
|
||||
self->drag_pos = i;
|
||||
self->drag_offset = start_x - pos;
|
||||
|
||||
g_object_unref (column);
|
||||
break;
|
||||
}
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
header_drag_end (GtkGestureDrag *gesture,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
double start_x, x;
|
||||
|
||||
gtk_gesture_drag_get_start_point (gesture, &start_x, NULL);
|
||||
x = start_x + offset_x;
|
||||
|
||||
remove_autoscroll (self);
|
||||
|
||||
if (self->in_column_resize)
|
||||
{
|
||||
self->in_column_resize = FALSE;
|
||||
}
|
||||
else if (self->in_column_reorder)
|
||||
{
|
||||
GdkEventSequence *sequence;
|
||||
GtkColumnViewColumn *column;
|
||||
GtkWidget *header;
|
||||
int i;
|
||||
|
||||
self->in_column_reorder = FALSE;
|
||||
|
||||
if (self->drag_pos == -1)
|
||||
return;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
|
||||
header = gtk_column_view_column_get_header (column);
|
||||
gtk_style_context_remove_class (gtk_widget_get_style_context (header), "dnd");
|
||||
|
||||
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
|
||||
if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
|
||||
return;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *col = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
|
||||
if (gtk_column_view_column_get_visible (col))
|
||||
{
|
||||
int pos, size;
|
||||
|
||||
gtk_column_view_column_get_allocation (col, &pos, &size);
|
||||
if (pos <= x && x <= pos + size)
|
||||
{
|
||||
gtk_column_view_insert_column (self, i, column);
|
||||
g_object_unref (col);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_object_unref (col);
|
||||
}
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
update_column_resize (GtkColumnView *self,
|
||||
double x)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
|
||||
gtk_column_view_column_set_fixed_width (column, MAX (x - self->drag_x, 0));
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
static void
|
||||
update_column_reorder (GtkColumnView *self,
|
||||
double x)
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
int width;
|
||||
int size;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
|
||||
width = gtk_widget_get_allocated_width (GTK_WIDGET (self->header));
|
||||
gtk_column_view_column_get_allocation (column, NULL, &size);
|
||||
|
||||
self->drag_x = CLAMP (x - self->drag_offset, 0, width - size);
|
||||
|
||||
gtk_widget_queue_allocate (GTK_WIDGET (self));
|
||||
gtk_column_view_column_queue_resize (column);
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
#define SCROLL_EDGE_SIZE 15
|
||||
|
||||
static void
|
||||
header_drag_update (GtkGestureDrag *gesture,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
GdkEventSequence *sequence;
|
||||
double start_x, x;
|
||||
|
||||
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
|
||||
if (!gtk_gesture_handles_sequence (GTK_GESTURE (gesture), sequence))
|
||||
return;
|
||||
|
||||
if (self->drag_pos == -1)
|
||||
return;
|
||||
|
||||
if (!self->in_column_resize && !self->in_column_reorder)
|
||||
{
|
||||
if (gtk_drag_check_threshold (GTK_WIDGET (self), 0, 0, offset_x, 0))
|
||||
{
|
||||
GtkColumnViewColumn *column;
|
||||
GtkWidget *header;
|
||||
|
||||
column = g_list_model_get_item (G_LIST_MODEL (self->columns), self->drag_pos);
|
||||
header = gtk_column_view_column_get_header (column);
|
||||
|
||||
gtk_widget_insert_after (header, self->header, gtk_widget_get_last_child (self->header));
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (header), "dnd");
|
||||
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
if (!gtk_widget_has_focus (GTK_WIDGET (self)))
|
||||
gtk_widget_grab_focus (GTK_WIDGET (self));
|
||||
|
||||
self->in_column_reorder = TRUE;
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
}
|
||||
|
||||
gtk_gesture_drag_get_start_point (gesture, &start_x, NULL);
|
||||
x = start_x + offset_x;
|
||||
|
||||
if (self->in_column_resize)
|
||||
update_column_resize (self, x);
|
||||
else if (self->in_column_reorder)
|
||||
update_column_reorder (self, x);
|
||||
|
||||
if (self->in_column_resize || self->in_column_reorder)
|
||||
{
|
||||
double value, page_size, upper;
|
||||
|
||||
value = gtk_adjustment_get_value (self->hadjustment);
|
||||
page_size = gtk_adjustment_get_page_size (self->hadjustment);
|
||||
upper = gtk_adjustment_get_upper (self->hadjustment);
|
||||
|
||||
if (x - value < SCROLL_EDGE_SIZE && value > 0)
|
||||
add_autoscroll (self, x, - (SCROLL_EDGE_SIZE - (x - value))/3.0);
|
||||
else if (value + page_size - x < SCROLL_EDGE_SIZE && value + page_size < upper)
|
||||
add_autoscroll (self, x, (SCROLL_EDGE_SIZE - (value + page_size - x))/3.0);
|
||||
else
|
||||
remove_autoscroll (self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
header_motion (GtkEventControllerMotion *controller,
|
||||
double x,
|
||||
double y,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
gboolean cursor_set = FALSE;
|
||||
int i, n;
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self->columns));
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
GtkColumnViewColumn *column = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
|
||||
if (!gtk_column_view_column_get_visible (column))
|
||||
{
|
||||
g_object_unref (column);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 < n &&
|
||||
gtk_column_view_column_get_resizable (column) &&
|
||||
gtk_column_view_in_resize_rect (self, column, x, y))
|
||||
{
|
||||
gtk_widget_set_cursor_from_name (self->header, "col-resize");
|
||||
cursor_set = TRUE;
|
||||
}
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
if (!cursor_set)
|
||||
gtk_widget_set_cursor (self->header, NULL);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
header_key_pressed (GtkEventControllerKey *controller,
|
||||
guint keyval,
|
||||
guint keycode,
|
||||
GdkModifierType modifiers,
|
||||
GtkColumnView *self)
|
||||
{
|
||||
if (self->in_column_reorder)
|
||||
{
|
||||
if (keyval == GDK_KEY_Escape)
|
||||
gtk_gesture_set_state (self->drag_gesture, GTK_EVENT_SEQUENCE_DENIED);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_init (GtkColumnView *self)
|
||||
{
|
||||
GtkEventController *controller;
|
||||
|
||||
self->columns = g_list_store_new (GTK_TYPE_COLUMN_VIEW_COLUMN);
|
||||
|
||||
self->header = gtk_list_item_widget_new (NULL, "header");
|
||||
@@ -517,6 +1000,22 @@ gtk_column_view_init (GtkColumnView *self)
|
||||
gtk_widget_set_layout_manager (self->header, gtk_column_view_layout_new (self));
|
||||
gtk_widget_set_parent (self->header, GTK_WIDGET (self));
|
||||
|
||||
controller = GTK_EVENT_CONTROLLER (gtk_gesture_drag_new ());
|
||||
g_signal_connect (controller, "drag-begin", G_CALLBACK (header_drag_begin), self);
|
||||
g_signal_connect (controller, "drag-update", G_CALLBACK (header_drag_update), self);
|
||||
g_signal_connect (controller, "drag-end", G_CALLBACK (header_drag_end), self);
|
||||
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
|
||||
gtk_widget_add_controller (self->header, controller);
|
||||
self->drag_gesture = GTK_GESTURE (controller);
|
||||
|
||||
controller = gtk_event_controller_motion_new ();
|
||||
g_signal_connect (controller, "motion", G_CALLBACK (header_motion), self);
|
||||
gtk_widget_add_controller (self->header, controller);
|
||||
|
||||
controller = gtk_event_controller_key_new ();
|
||||
g_signal_connect (controller, "key-pressed", G_CALLBACK (header_key_pressed), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), controller);
|
||||
|
||||
self->sorter = gtk_column_view_sorter_new ();
|
||||
self->factory = gtk_column_list_item_factory_new (self);
|
||||
self->listview = GTK_LIST_VIEW (gtk_list_view_new_with_factory (
|
||||
@@ -691,6 +1190,7 @@ gtk_column_view_remove_column (GtkColumnView *self,
|
||||
g_object_unref (item);
|
||||
if (item == column)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
gtk_column_view_sorter_remove_column (GTK_COLUMN_VIEW_SORTER (self->sorter), column);
|
||||
@@ -698,6 +1198,45 @@ gtk_column_view_remove_column (GtkColumnView *self,
|
||||
g_list_store_remove (self->columns, i);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_insert_column (GtkColumnView *self,
|
||||
guint position,
|
||||
GtkColumnViewColumn *column)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (column));
|
||||
g_return_if_fail (gtk_column_view_column_get_column_view (column) == NULL ||
|
||||
gtk_column_view_column_get_column_view (column) == self);
|
||||
g_return_if_fail (position <= g_list_model_get_n_items (G_LIST_MODEL (self->columns)));
|
||||
|
||||
g_object_ref (column);
|
||||
|
||||
if (gtk_column_view_column_get_column_view (column) == self)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (self->columns)); i++)
|
||||
{
|
||||
GtkColumnViewColumn *item = g_list_model_get_item (G_LIST_MODEL (self->columns), i);
|
||||
|
||||
g_object_unref (item);
|
||||
if (item == column)
|
||||
{
|
||||
g_list_store_remove (self->columns, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
gtk_column_view_column_set_column_view (column, self);
|
||||
|
||||
g_list_store_insert (self->columns, position, column);
|
||||
|
||||
gtk_column_view_column_queue_resize (column);
|
||||
|
||||
g_object_unref (column);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_column_view_measure_across (GtkColumnView *self,
|
||||
int *minimum,
|
||||
@@ -732,6 +1271,12 @@ gtk_column_view_get_header_widget (GtkColumnView *self)
|
||||
return GTK_LIST_ITEM_WIDGET (self->header);
|
||||
}
|
||||
|
||||
GtkListView *
|
||||
gtk_column_view_get_list_view (GtkColumnView *self)
|
||||
{
|
||||
return GTK_LIST_VIEW (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_sorter:
|
||||
* @self: a #GtkColumnView
|
||||
@@ -829,3 +1374,102 @@ gtk_column_view_get_single_click_activate (GtkColumnView *self)
|
||||
|
||||
return gtk_list_view_get_single_click_activate (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_enable_rubberband:
|
||||
* @self: a #GtkColumnView
|
||||
* @enable_rubberband: %TRUE to enable rubberband selection
|
||||
*
|
||||
* Sets whether selections can be changed by dragging with the mouse.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_set_enable_rubberband (GtkColumnView *self,
|
||||
gboolean enable_rubberband)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
|
||||
if (enable_rubberband == gtk_list_view_get_enable_rubberband (self->listview))
|
||||
return;
|
||||
|
||||
gtk_list_view_set_enable_rubberband (self->listview, enable_rubberband);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLE_RUBBERBAND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_enable_rubberband:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Returns whether rows can be selected by dragging with the mouse.
|
||||
*
|
||||
* Returns: %TRUE if rubberband selection is enabled
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_get_enable_rubberband (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_view_get_enable_rubberband (self->listview);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_set_search_filter:
|
||||
* @self: a #GtkColumnView
|
||||
* @filter: (nullable): the filter to use for search, or %NULL
|
||||
*
|
||||
* Sets a search filter.
|
||||
*
|
||||
* The selection will be moved to first item matching the
|
||||
* filter whenever the filter changes.
|
||||
*
|
||||
* This can be used with single selection and a string
|
||||
* filter that is connected to a search entry.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_set_search_filter (GtkColumnView *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
|
||||
|
||||
if (filter == gtk_list_view_get_search_filter (GTK_LIST_VIEW (self->listview)))
|
||||
return;
|
||||
|
||||
gtk_list_view_set_search_filter (GTK_LIST_VIEW (self->listview), filter);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_search_filter:
|
||||
* @self: a #GtkColumnView
|
||||
*
|
||||
* Gets the search filter that was set with
|
||||
* gtk_column_view_set_search_filter().
|
||||
*
|
||||
* Returns: (transfer none): The search filter of @self
|
||||
*/
|
||||
GtkFilter *
|
||||
gtk_column_view_get_search_filter (GtkColumnView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_view_get_search_filter (GTK_LIST_VIEW (self->listview));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_select_next_match:
|
||||
* @self: a #GtkColumnView
|
||||
* @forward: whether to move forward or back
|
||||
*
|
||||
* Moves the selection to the next item matching the
|
||||
* search filter set with gtk_column_view_set_search_filter().
|
||||
*/
|
||||
void
|
||||
gtk_column_view_select_next_match (GtkColumnView *self,
|
||||
gboolean forward)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW (self));
|
||||
|
||||
gtk_list_view_select_next_match (GTK_LIST_VIEW (self->listview), forward);
|
||||
}
|
||||
|
@@ -27,6 +27,7 @@
|
||||
#include <gtk/gtktypes.h>
|
||||
#include <gtk/gtksortlistmodel.h>
|
||||
#include <gtk/gtksorter.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -61,6 +62,10 @@ void gtk_column_view_append_column (GtkColumnView
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_remove_column (GtkColumnView *self,
|
||||
GtkColumnViewColumn *column);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_insert_column (GtkColumnView *self,
|
||||
guint position,
|
||||
GtkColumnViewColumn *column);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_column_view_get_model (GtkColumnView *self);
|
||||
@@ -87,6 +92,22 @@ void gtk_column_view_set_single_click_activate (GtkColumnView
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_get_single_click_activate (GtkColumnView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_enable_rubberband (GtkColumnView *self,
|
||||
gboolean enable_rubberband);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_get_enable_rubberband (GtkColumnView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_set_search_filter (GtkColumnView *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_column_view_get_search_filter (GtkColumnView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_select_next_match (GtkColumnView *self,
|
||||
gboolean forward);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_H__ */
|
||||
|
@@ -53,10 +53,18 @@ gtk_column_view_cell_measure (GtkWidget *widget,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkColumnViewCell *cell = GTK_COLUMN_VIEW_CELL (widget);
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
int fixed_width = gtk_column_view_column_get_fixed_width (cell->column);
|
||||
if (fixed_width > -1)
|
||||
*minimum = *natural = fixed_width;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -156,6 +164,7 @@ gtk_column_view_cell_new (GtkColumnViewColumn *column)
|
||||
|
||||
cell = g_object_new (GTK_TYPE_COLUMN_VIEW_CELL,
|
||||
"factory", gtk_column_view_column_get_factory (column),
|
||||
"visible", gtk_column_view_column_get_visible (column),
|
||||
NULL);
|
||||
|
||||
cell->column = g_object_ref (column);
|
||||
|
@@ -60,6 +60,16 @@ struct _GtkColumnViewColumn
|
||||
int natural_size_request;
|
||||
int allocation_offset;
|
||||
int allocation_size;
|
||||
int header_position;
|
||||
|
||||
int fixed_width;
|
||||
|
||||
guint visible : 1;
|
||||
guint resizable : 1;
|
||||
guint expand : 1;
|
||||
guint reorderable : 1;
|
||||
|
||||
GMenuModel *menu;
|
||||
|
||||
/* This list isn't sorted - this is just caching for performance */
|
||||
GtkColumnViewCell *first_cell; /* no reference, just caching */
|
||||
@@ -77,6 +87,12 @@ enum
|
||||
PROP_FACTORY,
|
||||
PROP_TITLE,
|
||||
PROP_SORTER,
|
||||
PROP_VISIBLE,
|
||||
PROP_RESIZABLE,
|
||||
PROP_EXPAND,
|
||||
PROP_REORDERABLE,
|
||||
PROP_FIXED_WIDTH,
|
||||
PROP_HEADER_MENU,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
@@ -96,6 +112,7 @@ gtk_column_view_column_dispose (GObject *object)
|
||||
g_clear_object (&self->factory);
|
||||
g_clear_object (&self->sorter);
|
||||
g_clear_pointer (&self->title, g_free);
|
||||
g_clear_object (&self->menu);
|
||||
|
||||
G_OBJECT_CLASS (gtk_column_view_column_parent_class)->dispose (object);
|
||||
}
|
||||
@@ -126,6 +143,30 @@ gtk_column_view_column_get_property (GObject *object,
|
||||
g_value_set_object (value, self->sorter);
|
||||
break;
|
||||
|
||||
case PROP_VISIBLE:
|
||||
g_value_set_boolean (value, self->visible);
|
||||
break;
|
||||
|
||||
case PROP_RESIZABLE:
|
||||
g_value_set_boolean (value, self->resizable);
|
||||
break;
|
||||
|
||||
case PROP_EXPAND:
|
||||
g_value_set_boolean (value, self->expand);
|
||||
break;
|
||||
|
||||
case PROP_REORDERABLE:
|
||||
g_value_set_boolean (value, self->reorderable);
|
||||
break;
|
||||
|
||||
case PROP_FIXED_WIDTH:
|
||||
g_value_set_int (value, self->fixed_width);
|
||||
break;
|
||||
|
||||
case PROP_HEADER_MENU:
|
||||
g_value_set_object (value, self->menu);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -154,6 +195,30 @@ gtk_column_view_column_set_property (GObject *object,
|
||||
gtk_column_view_column_set_sorter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
case PROP_VISIBLE:
|
||||
gtk_column_view_column_set_visible (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_RESIZABLE:
|
||||
gtk_column_view_column_set_resizable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_EXPAND:
|
||||
gtk_column_view_column_set_expand (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_REORDERABLE:
|
||||
gtk_column_view_column_set_reorderable (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_FIXED_WIDTH:
|
||||
gtk_column_view_column_set_fixed_width (self, g_value_get_int (value));
|
||||
break;
|
||||
|
||||
case PROP_HEADER_MENU:
|
||||
gtk_column_view_column_set_header_menu (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -217,6 +282,79 @@ gtk_column_view_column_class_init (GtkColumnViewColumnClass *klass)
|
||||
GTK_TYPE_SORTER,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:visible:
|
||||
*
|
||||
* Whether this column is visible
|
||||
*/
|
||||
properties[PROP_VISIBLE] =
|
||||
g_param_spec_boolean ("visible",
|
||||
P_("Visible"),
|
||||
P_("Whether this column is visible"),
|
||||
TRUE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:resizable:
|
||||
*
|
||||
* Whether this column is resizable
|
||||
*/
|
||||
properties[PROP_RESIZABLE] =
|
||||
g_param_spec_boolean ("resizable",
|
||||
P_("Resizable"),
|
||||
P_("Whether this column is resizable"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:expand:
|
||||
*
|
||||
* Column gets share of extra width allocated to the view
|
||||
*/
|
||||
properties[PROP_EXPAND] =
|
||||
g_param_spec_boolean ("expand",
|
||||
P_("Expand"),
|
||||
P_("column gets share of extra width"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:reorderable:
|
||||
*
|
||||
* Whether this column is reorderable
|
||||
*/
|
||||
properties[PROP_REORDERABLE] =
|
||||
g_param_spec_boolean ("reorderable",
|
||||
P_("Reorderable"),
|
||||
P_("Whether this column is reorderable"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:fixed-width:
|
||||
*
|
||||
* If not -1, this is the width that the column is allocated,
|
||||
* regardless of the size of its content.
|
||||
*/
|
||||
properties[PROP_FIXED_WIDTH] =
|
||||
g_param_spec_int ("fixed-width",
|
||||
P_("Fixed width"),
|
||||
P_("Fixed width of this column"),
|
||||
-1, G_MAXINT, -1,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
/**
|
||||
* GtkColumnViewColumn:header-menu:
|
||||
*
|
||||
* Menu model used to create the context menu for the column header.
|
||||
*/
|
||||
properties[PROP_HEADER_MENU] =
|
||||
g_param_spec_object ("header-menu",
|
||||
P_("Header menu"),
|
||||
P_("Menu to use on the title of this column"),
|
||||
G_TYPE_MENU_MODEL,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
}
|
||||
|
||||
@@ -225,6 +363,11 @@ gtk_column_view_column_init (GtkColumnViewColumn *self)
|
||||
{
|
||||
self->minimum_size_request = -1;
|
||||
self->natural_size_request = -1;
|
||||
self->visible = TRUE;
|
||||
self->resizable = FALSE;
|
||||
self->expand = FALSE;
|
||||
self->reorderable = FALSE;
|
||||
self->fixed_width = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,6 +476,12 @@ gtk_column_view_column_measure (GtkColumnViewColumn *self,
|
||||
int *minimum,
|
||||
int *natural)
|
||||
{
|
||||
if (self->fixed_width > -1)
|
||||
{
|
||||
self->minimum_size_request = self->fixed_width;
|
||||
self->natural_size_request = self->fixed_width;
|
||||
}
|
||||
|
||||
if (self->minimum_size_request < 0)
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
@@ -375,6 +524,7 @@ gtk_column_view_column_allocate (GtkColumnViewColumn *self,
|
||||
{
|
||||
self->allocation_offset = offset;
|
||||
self->allocation_size = size;
|
||||
self->header_position = offset;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -647,3 +797,298 @@ gtk_column_view_column_notify_sort (GtkColumnViewColumn *self)
|
||||
if (self->header)
|
||||
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_visible:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @visible: whether this column should be visible
|
||||
*
|
||||
* Sets whether this column should be visible in views.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_visible (GtkColumnViewColumn *self,
|
||||
gboolean visible)
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
|
||||
if (self->visible == visible)
|
||||
return;
|
||||
|
||||
self->visible = visible;
|
||||
|
||||
self->minimum_size_request = -1;
|
||||
self->natural_size_request = -1;
|
||||
|
||||
if (self->header)
|
||||
gtk_widget_set_visible (GTK_WIDGET (self->header), visible);
|
||||
|
||||
for (cell = self->first_cell; cell; cell = gtk_column_view_cell_get_next (cell))
|
||||
{
|
||||
gtk_widget_set_visible (GTK_WIDGET (cell), visible);
|
||||
}
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_VISIBLE]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_get_visible:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Returns whether this column is visible.
|
||||
*
|
||||
* Returns: %TRUE if this column is visible
|
||||
*/
|
||||
gboolean
|
||||
gtk_column_view_column_get_visible (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), TRUE);
|
||||
|
||||
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 (overflow != gtk_widget_get_overflow (GTK_WIDGET (self->header)))
|
||||
{
|
||||
GtkColumnViewCell *cell;
|
||||
|
||||
if (self->header)
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_set_header_menu:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
* @menu: (allow-none): a #GMenuModel, or %NULL
|
||||
*
|
||||
* Sets the menu model that is used to create the context menu
|
||||
* for the column header.
|
||||
*/
|
||||
void
|
||||
gtk_column_view_column_set_header_menu (GtkColumnViewColumn *self,
|
||||
GMenuModel *menu)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self));
|
||||
g_return_if_fail (menu == NULL || G_IS_MENU_MODEL (menu));
|
||||
|
||||
if (!g_set_object (&self->menu, menu))
|
||||
return;
|
||||
|
||||
if (self->header)
|
||||
gtk_column_view_title_update (GTK_COLUMN_VIEW_TITLE (self->header));
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_HEADER_MENU]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_column_view_column_get_header_menu:
|
||||
* @self: a #GtkColumnViewColumn
|
||||
*
|
||||
* Gets the menu model that is used to create the context menu
|
||||
* for the column header.
|
||||
*
|
||||
* Returns: the #GMenuModel, or %NULL
|
||||
*/
|
||||
GMenuModel *
|
||||
gtk_column_view_column_get_header_menu (GtkColumnViewColumn *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_COLUMN_VIEW_COLUMN (self), NULL);
|
||||
|
||||
return self->menu;
|
||||
}
|
||||
|
@@ -72,6 +72,41 @@ void gtk_column_view_column_set_sorter (GtkColu
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkSorter * gtk_column_view_column_get_sorter (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_visible (GtkColumnViewColumn *self,
|
||||
gboolean visible);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_column_get_visible (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_fixed_width (GtkColumnViewColumn *self,
|
||||
int fixed_width);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
int gtk_column_view_column_get_fixed_width (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_resizable (GtkColumnViewColumn *self,
|
||||
gboolean resizable);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_column_get_resizable (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_expand (GtkColumnViewColumn *self,
|
||||
gboolean expand);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_column_get_expand (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_reorderable (GtkColumnViewColumn *self,
|
||||
gboolean reorderable);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_column_view_column_get_reorderable (GtkColumnViewColumn *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_column_view_column_set_header_menu (GtkColumnViewColumn *self,
|
||||
GMenuModel *menu);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GMenuModel * gtk_column_view_column_get_header_menu (GtkColumnViewColumn *self);
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_H__ */
|
||||
|
@@ -33,6 +33,7 @@ void gtk_column_view_column_add_cell (GtkColu
|
||||
void gtk_column_view_column_remove_cell (GtkColumnViewColumn *self,
|
||||
GtkColumnViewCell *cell);
|
||||
GtkColumnViewCell * gtk_column_view_column_get_first_cell (GtkColumnViewColumn *self);
|
||||
GtkWidget * gtk_column_view_column_get_header (GtkColumnViewColumn *self);
|
||||
|
||||
void gtk_column_view_column_queue_resize (GtkColumnViewColumn *self);
|
||||
void gtk_column_view_column_measure (GtkColumnViewColumn *self,
|
||||
@@ -47,4 +48,10 @@ void gtk_column_view_column_get_allocation (GtkColu
|
||||
|
||||
void gtk_column_view_column_notify_sort (GtkColumnViewColumn *self);
|
||||
|
||||
void gtk_column_view_column_set_header_position (GtkColumnViewColumn *self,
|
||||
int offset);
|
||||
void gtk_column_view_column_get_header_allocation (GtkColumnViewColumn *self,
|
||||
int *offset,
|
||||
int *size);
|
||||
|
||||
#endif /* __GTK_COLUMN_VIEW_COLUMN_PRIVATE_H__ */
|
||||
|
@@ -118,11 +118,16 @@ gtk_column_view_layout_allocate (GtkLayoutManager *layout_manager,
|
||||
int col_x, col_width;
|
||||
|
||||
if (GTK_IS_COLUMN_VIEW_CELL (child))
|
||||
column = gtk_column_view_cell_get_column (GTK_COLUMN_VIEW_CELL (child));
|
||||
{
|
||||
column = gtk_column_view_cell_get_column (GTK_COLUMN_VIEW_CELL (child));
|
||||
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
|
||||
}
|
||||
else
|
||||
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
|
||||
{
|
||||
column = gtk_column_view_title_get_column (GTK_COLUMN_VIEW_TITLE (child));
|
||||
gtk_column_view_column_get_header_allocation (column, &col_x, &col_width);
|
||||
}
|
||||
|
||||
gtk_column_view_column_get_allocation (column, &col_x, &col_width);
|
||||
gtk_widget_size_allocate (child, &(GtkAllocation) { col_x, 0, col_width, height }, baseline);
|
||||
}
|
||||
}
|
||||
|
@@ -21,11 +21,13 @@
|
||||
#define __GTK_COLUMN_VIEW_PRIVATE_H__
|
||||
|
||||
#include "gtk/gtkcolumnview.h"
|
||||
#include "gtk/gtklistview.h"
|
||||
|
||||
#include "gtk/gtkcolumnviewsorterprivate.h"
|
||||
#include "gtk/gtklistitemwidgetprivate.h"
|
||||
|
||||
GtkListItemWidget * gtk_column_view_get_header_widget (GtkColumnView *self);
|
||||
GtkListView * gtk_column_view_get_list_view (GtkColumnView *self);
|
||||
|
||||
void gtk_column_view_measure_across (GtkColumnView *self,
|
||||
int *minimum,
|
||||
|
@@ -30,6 +30,8 @@
|
||||
#include "gtkbox.h"
|
||||
#include "gtkimage.h"
|
||||
#include "gtkgestureclick.h"
|
||||
#include "gtkpopovermenu.h"
|
||||
#include "gtknative.h"
|
||||
|
||||
struct _GtkColumnViewTitle
|
||||
{
|
||||
@@ -40,6 +42,7 @@ struct _GtkColumnViewTitle
|
||||
GtkWidget *box;
|
||||
GtkWidget *title;
|
||||
GtkWidget *sort;
|
||||
GtkWidget *popup_menu;
|
||||
};
|
||||
|
||||
struct _GtkColumnViewTitleClass
|
||||
@@ -58,10 +61,18 @@ gtk_column_view_title_measure (GtkWidget *widget,
|
||||
int *minimum_baseline,
|
||||
int *natural_baseline)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_measure (child, orientation, for_size, minimum, natural, minimum_baseline, natural_baseline);
|
||||
|
||||
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
||||
{
|
||||
int fixed_width = gtk_column_view_column_get_fixed_width (self->column);
|
||||
if (fixed_width > -1)
|
||||
*minimum = *natural = fixed_width;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -70,10 +81,14 @@ gtk_column_view_title_size_allocate (GtkWidget *widget,
|
||||
int height,
|
||||
int baseline)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
GtkWidget *child = gtk_widget_get_first_child (widget);
|
||||
|
||||
if (child)
|
||||
gtk_widget_allocate (child, width, height, baseline, NULL);
|
||||
|
||||
if (self->popup_menu)
|
||||
gtk_native_check_resize (GTK_NATIVE (self->popup_menu));
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -82,6 +97,7 @@ gtk_column_view_title_dispose (GObject *object)
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (object);
|
||||
|
||||
g_clear_pointer (&self->box, gtk_widget_unparent);
|
||||
g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
|
||||
|
||||
g_clear_object (&self->column);
|
||||
|
||||
@@ -112,13 +128,8 @@ gtk_column_view_title_resize_func (GtkWidget *widget)
|
||||
}
|
||||
|
||||
static void
|
||||
click_pressed_cb (GtkGestureClick *gesture,
|
||||
guint n_press,
|
||||
gdouble x,
|
||||
gdouble y,
|
||||
GtkWidget *widget)
|
||||
activate_sort (GtkColumnViewTitle *self)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
GtkSorter *sorter;
|
||||
GtkColumnView *view;
|
||||
GtkColumnViewSorter *view_sorter;
|
||||
@@ -132,6 +143,56 @@ click_pressed_cb (GtkGestureClick *gesture,
|
||||
gtk_column_view_sorter_add_column (view_sorter, self->column);
|
||||
}
|
||||
|
||||
static void
|
||||
show_menu (GtkColumnViewTitle *self,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
if (!self->popup_menu)
|
||||
{
|
||||
GMenuModel *model;
|
||||
|
||||
model = gtk_column_view_column_get_header_menu (self->column);
|
||||
if (!model)
|
||||
return;
|
||||
|
||||
self->popup_menu = gtk_popover_menu_new_from_model (model);
|
||||
gtk_widget_set_parent (self->popup_menu, GTK_WIDGET (self));
|
||||
gtk_popover_set_position (GTK_POPOVER (self->popup_menu), GTK_POS_BOTTOM);
|
||||
|
||||
gtk_popover_set_has_arrow (GTK_POPOVER (self->popup_menu), FALSE);
|
||||
gtk_widget_set_halign (self->popup_menu, GTK_ALIGN_START);
|
||||
}
|
||||
|
||||
if (x != -1 && y != -1)
|
||||
{
|
||||
GdkRectangle rect = { x, y, 1, 1 };
|
||||
gtk_popover_set_pointing_to (GTK_POPOVER (self->popup_menu), &rect);
|
||||
}
|
||||
else
|
||||
gtk_popover_set_pointing_to (GTK_POPOVER (self->popup_menu), NULL);
|
||||
|
||||
gtk_popover_popup (GTK_POPOVER (self->popup_menu));
|
||||
}
|
||||
|
||||
static void
|
||||
click_released_cb (GtkGestureClick *gesture,
|
||||
guint n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkWidget *widget)
|
||||
{
|
||||
GtkColumnViewTitle *self = GTK_COLUMN_VIEW_TITLE (widget);
|
||||
guint button;
|
||||
|
||||
button = gtk_gesture_single_get_current_button (GTK_GESTURE_SINGLE (gesture));
|
||||
|
||||
if (button == GDK_BUTTON_PRIMARY)
|
||||
activate_sort (self);
|
||||
else if (button == GDK_BUTTON_SECONDARY)
|
||||
show_menu (self, x, y);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_column_view_title_init (GtkColumnViewTitle *self)
|
||||
{
|
||||
@@ -150,7 +211,8 @@ gtk_column_view_title_init (GtkColumnViewTitle *self)
|
||||
gtk_box_append (GTK_BOX (self->box), self->sort);
|
||||
|
||||
gesture = gtk_gesture_click_new ();
|
||||
g_signal_connect (gesture, "pressed", G_CALLBACK (click_pressed_cb), self);
|
||||
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture), 0);
|
||||
g_signal_connect (gesture, "released", G_CALLBACK (click_released_cb), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (gesture));
|
||||
}
|
||||
|
||||
@@ -199,6 +261,8 @@ gtk_column_view_title_update (GtkColumnViewTitle *self)
|
||||
}
|
||||
else
|
||||
gtk_widget_hide (self->sort);
|
||||
|
||||
g_clear_pointer (&self->popup_menu, gtk_widget_unparent);
|
||||
}
|
||||
|
||||
GtkColumnViewColumn *
|
||||
|
@@ -91,6 +91,8 @@ enum
|
||||
PROP_MIN_COLUMNS,
|
||||
PROP_MODEL,
|
||||
PROP_SINGLE_CLICK_ACTIVATE,
|
||||
PROP_ENABLE_RUBBERBAND,
|
||||
PROP_SEARCH_FILTER,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
@@ -914,6 +916,14 @@ gtk_grid_view_get_property (GObject *object,
|
||||
g_value_set_boolean (value, gtk_list_item_manager_get_single_click_activate (self->item_manager));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
g_value_set_object (value, gtk_grid_view_get_search_filter (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -950,6 +960,14 @@ gtk_grid_view_set_property (GObject *object,
|
||||
gtk_grid_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
gtk_grid_view_set_enable_rubberband (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
gtk_grid_view_set_search_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -1062,6 +1080,30 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkGridView:enable-rubberband:
|
||||
*
|
||||
* Allow rubberband selection
|
||||
*/
|
||||
properties[PROP_ENABLE_RUBBERBAND] =
|
||||
g_param_spec_boolean ("enable-rubberband",
|
||||
P_("Enable rubberband selection"),
|
||||
P_("Allow selecting items by dragging with the mouse"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkGridView:search-filter:
|
||||
*
|
||||
* Filter used for search
|
||||
*/
|
||||
properties[PROP_SEARCH_FILTER] =
|
||||
g_param_spec_object ("search-filter",
|
||||
P_("Search filter"),
|
||||
P_("Filter used for searching"),
|
||||
GTK_TYPE_FILTER,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
@@ -1370,3 +1412,102 @@ gtk_grid_view_get_single_click_activate (GtkGridView *self)
|
||||
|
||||
return gtk_list_item_manager_get_single_click_activate (self->item_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_set_enable_rubberband:
|
||||
* @self: a #GtkGridView
|
||||
* @enable_rubberband: %TRUE to enable rubberband selection
|
||||
*
|
||||
* Sets whether selections can be changed by dragging with the mouse.
|
||||
*/
|
||||
void
|
||||
gtk_grid_view_set_enable_rubberband (GtkGridView *self,
|
||||
gboolean enable_rubberband)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_GRID_VIEW (self));
|
||||
|
||||
if (enable_rubberband == gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
gtk_list_base_set_enable_rubberband (GTK_LIST_BASE (self), enable_rubberband);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLE_RUBBERBAND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_get_enable_rubberband:
|
||||
* @self: a #GtkGridView
|
||||
*
|
||||
* Returns whether rows can be selected by dragging with the mouse.
|
||||
*
|
||||
* Returns: %TRUE if rubberband selection is enabled
|
||||
*/
|
||||
gboolean
|
||||
gtk_grid_view_get_enable_rubberband (GtkGridView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_GRID_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_set_search_filter:
|
||||
* @self: a #GtkGridView
|
||||
* @filter: (nullable): the filter to use for search, or %NULL
|
||||
*
|
||||
* Sets a search filter.
|
||||
*
|
||||
* The selection will be moved to first item matching the
|
||||
* filter whenever the filter changes.
|
||||
*
|
||||
* This can be used with single selection and a string
|
||||
* filter that is connected to a search entry.
|
||||
*/
|
||||
void
|
||||
gtk_grid_view_set_search_filter (GtkGridView *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_GRID_VIEW (self));
|
||||
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
|
||||
|
||||
if (filter == gtk_list_base_get_search_filter (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
gtk_list_base_set_search_filter (GTK_LIST_BASE (self), filter);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_get_search_filter:
|
||||
* @self: a #GtkGridView
|
||||
*
|
||||
* Gets the search filter that was set with
|
||||
* gtk_grid_view_set_search_filter().
|
||||
*
|
||||
* Returns: (transfer none): The search filter of @self
|
||||
*/
|
||||
GtkFilter *
|
||||
gtk_grid_view_get_search_filter (GtkGridView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_GRID_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_base_get_search_filter (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_grid_view_select_next_match:
|
||||
* @self: a #GtkGridView
|
||||
* @forward: whether to move forward or back
|
||||
*
|
||||
* Moves the selection to the next item matching the
|
||||
* search filter set with gtk_grid_view_set_search_filter().
|
||||
*/
|
||||
void
|
||||
gtk_grid_view_select_next_match (GtkGridView *self,
|
||||
gboolean forward)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_GRID_VIEW (self));
|
||||
|
||||
gtk_list_base_select_next_match (GTK_LIST_BASE (self), forward);
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -73,6 +74,11 @@ guint gtk_grid_view_get_max_columns (GtkGridView
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_max_columns (GtkGridView *self,
|
||||
guint max_columns);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_enable_rubberband (GtkGridView *self,
|
||||
gboolean enable_rubberband);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_grid_view_get_enable_rubberband (GtkGridView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_single_click_activate (GtkGridView *self,
|
||||
@@ -80,6 +86,16 @@ void gtk_grid_view_set_single_click_activate (GtkGridView
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_grid_view_get_single_click_activate (GtkGridView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_set_search_filter (GtkGridView *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_grid_view_get_search_filter (GtkGridView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_grid_view_select_next_match (GtkGridView *self,
|
||||
gboolean forward);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
@@ -28,6 +28,12 @@
|
||||
#include "gtkscrollable.h"
|
||||
#include "gtksingleselection.h"
|
||||
#include "gtktypebuiltins.h"
|
||||
#include "gtkgesturedrag.h"
|
||||
#include "gtkwidgetprivate.h"
|
||||
#include "gtkcssnodeprivate.h"
|
||||
#include "gtkstylecontextprivate.h"
|
||||
#include "gtksnapshot.h"
|
||||
#include "gtkmultiselection.h"
|
||||
|
||||
typedef struct _GtkListBasePrivate GtkListBasePrivate;
|
||||
|
||||
@@ -50,6 +56,24 @@ struct _GtkListBasePrivate
|
||||
GtkListItemTracker *selected;
|
||||
/* the item that has input focus */
|
||||
GtkListItemTracker *focus;
|
||||
|
||||
gboolean enable_rubberband;
|
||||
gboolean doing_rubberband;
|
||||
double rb_x1;
|
||||
double rb_y1;
|
||||
double rb_x2;
|
||||
double rb_y2;
|
||||
GtkGesture *drag_gesture;
|
||||
GtkCssNode *rubberband_node;
|
||||
GtkSelectionModel *old_selection;
|
||||
gboolean modify;
|
||||
gboolean extend;
|
||||
|
||||
guint autoscroll_id;
|
||||
double autoscroll_delta_x;
|
||||
double autoscroll_delta_y;
|
||||
|
||||
GtkFilter *search_filter;
|
||||
};
|
||||
|
||||
enum
|
||||
@@ -523,6 +547,8 @@ gtk_list_base_focus (GtkWidget *widget,
|
||||
}
|
||||
}
|
||||
|
||||
static void gtk_list_base_clear_search_filter (GtkListBase *self);
|
||||
|
||||
static void
|
||||
gtk_list_base_dispose (GObject *object)
|
||||
{
|
||||
@@ -551,6 +577,8 @@ gtk_list_base_dispose (GObject *object)
|
||||
|
||||
g_clear_object (&priv->model);
|
||||
|
||||
gtk_list_base_clear_search_filter (self);
|
||||
|
||||
G_OBJECT_CLASS (gtk_list_base_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
@@ -764,26 +792,18 @@ gtk_list_base_update_focus_tracker (GtkListBase *self)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_scroll_to_item (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
static gboolean
|
||||
gtk_list_base_scroll_to_position (GtkListBase *self,
|
||||
guint pos)
|
||||
{
|
||||
GtkListBase *self = GTK_LIST_BASE (widget);
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
int start, end;
|
||||
double align_along, align_across;
|
||||
GtkPackType side_along, side_across;
|
||||
guint pos;
|
||||
|
||||
if (!g_variant_check_format_string (parameter, "u", FALSE))
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "u", &pos);
|
||||
|
||||
/* figure out primary orientation and if position is valid */
|
||||
if (!gtk_list_base_get_allocation_along (GTK_LIST_BASE (self), pos, &start, &end))
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
end += start;
|
||||
gtk_list_base_compute_scroll_align (self,
|
||||
@@ -794,7 +814,7 @@ gtk_list_base_scroll_to_item (GtkWidget *widget,
|
||||
|
||||
/* now do the same thing with the other orientation */
|
||||
if (!gtk_list_base_get_allocation_across (GTK_LIST_BASE (self), pos, &start, &end))
|
||||
return;
|
||||
return FALSE;
|
||||
|
||||
end += start;
|
||||
gtk_list_base_compute_scroll_align (self,
|
||||
@@ -808,13 +828,32 @@ gtk_list_base_scroll_to_item (GtkWidget *widget,
|
||||
align_across, side_across,
|
||||
align_along, side_along);
|
||||
|
||||
/* HACK HACK HACK
|
||||
*
|
||||
* GTK has no way to track the focused child. But we now that when a listitem
|
||||
* gets focus, it calls this action. So we update our focus tracker from here
|
||||
* because it's the closest we can get to accurate tracking.
|
||||
*/
|
||||
gtk_list_base_update_focus_tracker (self);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_scroll_to_item (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
GtkListBase *self = GTK_LIST_BASE (widget);
|
||||
guint pos;
|
||||
|
||||
if (!g_variant_check_format_string (parameter, "u", FALSE))
|
||||
return;
|
||||
|
||||
g_variant_get (parameter, "u", &pos);
|
||||
|
||||
if (gtk_list_base_scroll_to_position (self, pos))
|
||||
{
|
||||
/* HACK HACK HACK
|
||||
*
|
||||
* GTK has no way to track the focused child. But we know that when a listitem
|
||||
* gets focus, it calls this action. So we update our focus tracker from here
|
||||
* because it's the closest we can get to accurate tracking.
|
||||
*/
|
||||
gtk_list_base_update_focus_tracker (self);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -863,6 +902,17 @@ gtk_list_base_unselect_all (GtkWidget *widget,
|
||||
gtk_selection_model_unselect_all (selection_model);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_next_match_action (GtkWidget *widget,
|
||||
const char *action_name,
|
||||
GVariant *parameter)
|
||||
{
|
||||
gboolean forward;
|
||||
|
||||
g_variant_get (parameter, "(b)", &forward);
|
||||
gtk_list_base_select_next_match (GTK_LIST_BASE (widget), forward);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_base_move_cursor_to_start (GtkWidget *widget,
|
||||
GVariant *args,
|
||||
@@ -1061,6 +1111,20 @@ gtk_list_base_add_custom_move_binding (GtkWidgetClass *widget_class,
|
||||
"(bbb)", TRUE, TRUE, TRUE);
|
||||
}
|
||||
|
||||
static void gtk_list_base_snapshot_rubberband (GtkListBase *self,
|
||||
GtkSnapshot *snapshot);
|
||||
|
||||
static void
|
||||
gtk_list_base_snapshot (GtkWidget *widget,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkListBase *self = GTK_LIST_BASE (widget);
|
||||
|
||||
GTK_WIDGET_CLASS (gtk_list_base_parent_class)->snapshot (widget, snapshot);
|
||||
|
||||
gtk_list_base_snapshot_rubberband (self, snapshot);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
{
|
||||
@@ -1069,6 +1133,7 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
gpointer iface;
|
||||
|
||||
widget_class->focus = gtk_list_base_focus;
|
||||
widget_class->snapshot = gtk_list_base_snapshot;
|
||||
|
||||
gobject_class->dispose = gtk_list_base_dispose;
|
||||
gobject_class->get_property = gtk_list_base_get_property;
|
||||
@@ -1162,6 +1227,11 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
NULL,
|
||||
gtk_list_base_unselect_all);
|
||||
|
||||
gtk_widget_class_install_action (widget_class,
|
||||
"list.next-match",
|
||||
"(b)",
|
||||
gtk_list_base_next_match_action);
|
||||
|
||||
gtk_list_base_add_move_binding (widget_class, GDK_KEY_Up, GTK_ORIENTATION_VERTICAL, -1);
|
||||
gtk_list_base_add_move_binding (widget_class, GDK_KEY_KP_Up, GTK_ORIENTATION_VERTICAL, -1);
|
||||
gtk_list_base_add_move_binding (widget_class, GDK_KEY_Down, GTK_ORIENTATION_VERTICAL, 1);
|
||||
@@ -1184,6 +1254,341 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_slash, GDK_CONTROL_MASK, "list.select-all", NULL);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_A, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.unselect-all", NULL);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL);
|
||||
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_g, GDK_CONTROL_MASK, "list.next-match", "(b)", TRUE);
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_g, GDK_CONTROL_MASK | GDK_SHIFT_MASK, "list.next-match", "(b)", FALSE);
|
||||
}
|
||||
|
||||
static void gtk_list_base_update_rubberband_selection (GtkListBase *self);
|
||||
|
||||
static gboolean
|
||||
autoscroll_cb (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
gpointer data)
|
||||
{
|
||||
GtkListBase *self = data;
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
double value;
|
||||
|
||||
value = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
gtk_adjustment_set_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL], value + priv->autoscroll_delta_x);
|
||||
value = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
gtk_adjustment_set_value (priv->adjustment[GTK_ORIENTATION_VERTICAL], value + priv->autoscroll_delta_y);
|
||||
|
||||
if (priv->doing_rubberband)
|
||||
{
|
||||
priv->rb_x2 += priv->autoscroll_delta_x;
|
||||
priv->rb_y2 += priv->autoscroll_delta_y;
|
||||
gtk_list_base_update_rubberband_selection (self);
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
|
||||
static void
|
||||
add_autoscroll (GtkListBase *self,
|
||||
double delta_x,
|
||||
double delta_y)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
priv->autoscroll_delta_x = delta_x;
|
||||
priv->autoscroll_delta_y = delta_y;
|
||||
|
||||
if (priv->autoscroll_id == 0)
|
||||
priv->autoscroll_id = gtk_widget_add_tick_callback (GTK_WIDGET (self), autoscroll_cb, self, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
remove_autoscroll (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (priv->autoscroll_id != 0)
|
||||
{
|
||||
gtk_widget_remove_tick_callback (GTK_WIDGET (self), priv->autoscroll_id);
|
||||
priv->autoscroll_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_start_rubberband (GtkListBase *self,
|
||||
double x,
|
||||
double y,
|
||||
gboolean modify,
|
||||
gboolean extend)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkCssNode *widget_node;
|
||||
double value_x, value_y;
|
||||
GtkSelectionModel *selection;
|
||||
|
||||
if (priv->doing_rubberband)
|
||||
return;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
priv->rb_x1 = priv->rb_x2 = x + value_x;
|
||||
priv->rb_y1 = priv->rb_y2 = y + value_y;
|
||||
|
||||
priv->modify = modify;
|
||||
priv->extend = extend;
|
||||
|
||||
widget_node = gtk_widget_get_css_node (GTK_WIDGET (self));
|
||||
priv->rubberband_node = gtk_css_node_new ();
|
||||
gtk_css_node_set_name (priv->rubberband_node,
|
||||
g_quark_from_static_string ("rubberband"));
|
||||
gtk_css_node_set_parent (priv->rubberband_node, widget_node);
|
||||
gtk_css_node_set_state (priv->rubberband_node, gtk_css_node_get_state (widget_node));
|
||||
g_object_unref (priv->rubberband_node);
|
||||
|
||||
selection = gtk_list_item_manager_get_model (priv->item_manager);
|
||||
|
||||
if (modify)
|
||||
priv->old_selection = GTK_SELECTION_MODEL (gtk_multi_selection_copy (selection));
|
||||
|
||||
priv->doing_rubberband = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_stop_rubberband (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (!priv->doing_rubberband)
|
||||
return;
|
||||
|
||||
priv->doing_rubberband = FALSE;
|
||||
gtk_css_node_set_parent (priv->rubberband_node, NULL);
|
||||
priv->rubberband_node = NULL;
|
||||
|
||||
g_clear_object (&priv->old_selection);
|
||||
|
||||
remove_autoscroll (self);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
#define SCROLL_EDGE_SIZE 15
|
||||
|
||||
static void
|
||||
gtk_list_base_update_rubberband (GtkListBase *self,
|
||||
double x,
|
||||
double y)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
double value_x, value_y, page_size, upper;
|
||||
double delta_x, delta_y;
|
||||
|
||||
if (!priv->doing_rubberband)
|
||||
return;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
priv->rb_x2 = x + value_x;
|
||||
priv->rb_y2 = y + value_y;
|
||||
|
||||
gtk_list_base_update_rubberband_selection (self);
|
||||
|
||||
page_size = gtk_adjustment_get_page_size (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
upper = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
|
||||
if (x < SCROLL_EDGE_SIZE && value_x > 0)
|
||||
delta_x = - (SCROLL_EDGE_SIZE - x)/3.0;
|
||||
else if (page_size - x < SCROLL_EDGE_SIZE && value_x + page_size < upper)
|
||||
delta_x = (SCROLL_EDGE_SIZE - (page_size - x))/3.0;
|
||||
else
|
||||
delta_x = 0;
|
||||
|
||||
page_size = gtk_adjustment_get_page_size (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
upper = gtk_adjustment_get_upper (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
if (y < SCROLL_EDGE_SIZE && value_y > 0)
|
||||
delta_y = - (SCROLL_EDGE_SIZE - y)/3.0;
|
||||
else if (page_size - y < SCROLL_EDGE_SIZE && value_y + page_size < upper)
|
||||
delta_y = (SCROLL_EDGE_SIZE - (page_size - y))/3.0;
|
||||
else
|
||||
delta_y = 0;
|
||||
|
||||
if (delta_x != 0 || delta_y != 0)
|
||||
add_autoscroll (self, delta_x, delta_y);
|
||||
else
|
||||
remove_autoscroll (self);
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_update_rubberband_selection (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GdkRectangle rect;
|
||||
GdkRectangle alloc;
|
||||
double value_x, value_y;
|
||||
GtkSelectionModel *model;
|
||||
GtkListItemManagerItem *item;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
rect.x = MIN (priv->rb_x1, priv->rb_x2) - value_x;
|
||||
rect.y = MIN (priv->rb_y1, priv->rb_y2) - value_y;
|
||||
rect.width = ABS (priv->rb_x1 - priv->rb_x2) + 1;
|
||||
rect.height = ABS (priv->rb_y1 - priv->rb_y2) + 1;
|
||||
|
||||
model = gtk_list_item_manager_get_model (priv->item_manager);
|
||||
|
||||
for (item = gtk_list_item_manager_get_first (priv->item_manager);
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
guint pos;
|
||||
gboolean was_selected, selected;
|
||||
|
||||
if (!item->widget)
|
||||
continue;
|
||||
|
||||
pos = gtk_list_item_manager_get_item_position (priv->item_manager, item);
|
||||
|
||||
gtk_widget_get_allocation (item->widget, &alloc);
|
||||
|
||||
selected = gdk_rectangle_intersect (&rect, &alloc, &alloc);
|
||||
|
||||
if (priv->modify)
|
||||
{
|
||||
was_selected = gtk_selection_model_is_selected (priv->old_selection, pos);
|
||||
selected = selected ^ was_selected;
|
||||
}
|
||||
|
||||
if (selected)
|
||||
gtk_selection_model_select_item (model, pos, FALSE);
|
||||
else if (!priv->extend)
|
||||
gtk_selection_model_unselect_item (model, pos);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_snapshot_rubberband (GtkListBase *self,
|
||||
GtkSnapshot *snapshot)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkStyleContext *context;
|
||||
GdkRectangle rect;
|
||||
double value_x, value_y;
|
||||
|
||||
if (!priv->doing_rubberband)
|
||||
return;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
|
||||
rect.x = MIN (priv->rb_x1, priv->rb_x2) - value_x;
|
||||
rect.y = MIN (priv->rb_y1, priv->rb_y2) - value_y;
|
||||
rect.width = ABS (priv->rb_x1 - priv->rb_x2) + 1;
|
||||
rect.height = ABS (priv->rb_y1 - priv->rb_y2) + 1;
|
||||
|
||||
context = gtk_widget_get_style_context (GTK_WIDGET (self));
|
||||
|
||||
gtk_style_context_save_to_node (context, priv->rubberband_node);
|
||||
|
||||
gtk_snapshot_render_background (snapshot, context, rect.x, rect.y, rect.width, rect.height);
|
||||
gtk_snapshot_render_frame (snapshot, context, rect.x, rect.y, rect.width, rect.height);
|
||||
|
||||
gtk_style_context_restore (context);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
get_selection_modifiers (GtkGesture *gesture,
|
||||
gboolean *modify,
|
||||
gboolean *extend)
|
||||
{
|
||||
GdkEventSequence *sequence;
|
||||
GdkEvent *event;
|
||||
GdkModifierType state;
|
||||
|
||||
*modify = FALSE;
|
||||
*extend = FALSE;
|
||||
|
||||
sequence = gtk_gesture_get_last_updated_sequence (gesture);
|
||||
event = gtk_gesture_get_last_event (gesture, sequence);
|
||||
state = gdk_event_get_modifier_state (event);
|
||||
if ((state & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
|
||||
*modify = TRUE;
|
||||
if ((state & GDK_SHIFT_MASK) == GDK_SHIFT_MASK)
|
||||
*extend = TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_drag_begin (GtkGestureDrag *gesture,
|
||||
double start_x,
|
||||
double start_y,
|
||||
GtkListBase *self)
|
||||
{
|
||||
gboolean modify;
|
||||
gboolean extend;
|
||||
|
||||
get_selection_modifiers (GTK_GESTURE (gesture), &modify, &extend);
|
||||
gtk_list_base_start_rubberband (self, start_x, start_y, modify, extend);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_drag_update (GtkGestureDrag *gesture,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkListBase *self)
|
||||
{
|
||||
double start_x, start_y;
|
||||
gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
|
||||
gtk_list_base_update_rubberband (self, start_x + offset_x, start_y + offset_y);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_drag_end (GtkGestureDrag *gesture,
|
||||
double offset_x,
|
||||
double offset_y,
|
||||
GtkListBase *self)
|
||||
{
|
||||
gtk_list_base_drag_update (gesture, offset_x, offset_y, self);
|
||||
gtk_list_base_stop_rubberband (self);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_base_set_enable_rubberband (GtkListBase *self,
|
||||
gboolean enable)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (priv->enable_rubberband == enable)
|
||||
return;
|
||||
|
||||
priv->enable_rubberband = enable;
|
||||
|
||||
if (enable)
|
||||
{
|
||||
priv->drag_gesture = gtk_gesture_drag_new ();
|
||||
g_signal_connect (priv->drag_gesture, "drag-begin", G_CALLBACK (gtk_list_base_drag_begin), self);
|
||||
g_signal_connect (priv->drag_gesture, "drag-update", G_CALLBACK (gtk_list_base_drag_update), self);
|
||||
g_signal_connect (priv->drag_gesture, "drag-end", G_CALLBACK (gtk_list_base_drag_end), self);
|
||||
gtk_widget_add_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (priv->drag_gesture));
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_widget_remove_controller (GTK_WIDGET (self), GTK_EVENT_CONTROLLER (priv->drag_gesture));
|
||||
priv->drag_gesture = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_list_base_get_enable_rubberband (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
return priv->enable_rubberband;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1538,3 +1943,183 @@ gtk_list_base_set_model (GtkListBase *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static guint
|
||||
find_first_selected (GtkSelectionModel *model)
|
||||
{
|
||||
guint i, start, n_items;
|
||||
gboolean selected;
|
||||
|
||||
n_items = 0;
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (model)); i += n_items)
|
||||
{
|
||||
gtk_selection_model_query_range (model, i, &start, &n_items, &selected);
|
||||
if (selected)
|
||||
return i;
|
||||
}
|
||||
|
||||
return GTK_INVALID_LIST_POSITION;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
match_item (GListModel *model,
|
||||
GtkFilter *filter,
|
||||
guint position)
|
||||
{
|
||||
gpointer item;
|
||||
gboolean result;
|
||||
|
||||
item = g_list_model_get_item (model, position);
|
||||
result = gtk_filter_match (filter, item);
|
||||
g_object_unref (item);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static guint
|
||||
find_next_match (GListModel *model,
|
||||
GtkFilter *filter,
|
||||
guint start,
|
||||
gboolean forward)
|
||||
{
|
||||
guint i;
|
||||
|
||||
if (start == GTK_INVALID_LIST_POSITION)
|
||||
start = 0;
|
||||
|
||||
if (forward)
|
||||
for (i = start; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (match_item (model, filter, i))
|
||||
return i;
|
||||
}
|
||||
else
|
||||
for (i = start; ; i--)
|
||||
{
|
||||
if (match_item (model, filter, i))
|
||||
return i;
|
||||
if (i == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
return GTK_INVALID_LIST_POSITION;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_search_filter_changed_cb (GtkFilter *filter,
|
||||
GtkFilterChange change,
|
||||
GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager);
|
||||
|
||||
if (model == NULL)
|
||||
return;
|
||||
|
||||
if (gtk_filter_get_strictness (priv->search_filter) == GTK_FILTER_MATCH_NONE)
|
||||
gtk_selection_model_unselect_all (model);
|
||||
else
|
||||
{
|
||||
guint position;
|
||||
|
||||
switch (change)
|
||||
{
|
||||
case GTK_FILTER_CHANGE_DIFFERENT:
|
||||
case GTK_FILTER_CHANGE_LESS_STRICT:
|
||||
position = find_next_match (G_LIST_MODEL (model), priv->search_filter, 0, TRUE);
|
||||
break;
|
||||
|
||||
case GTK_FILTER_CHANGE_MORE_STRICT:
|
||||
position = find_first_selected (model);
|
||||
if (position == GTK_INVALID_LIST_POSITION)
|
||||
position = 0;
|
||||
position = find_next_match (G_LIST_MODEL (model), priv->search_filter, position, TRUE);
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert_not_reached ();
|
||||
}
|
||||
|
||||
if (position == GTK_INVALID_LIST_POSITION)
|
||||
gtk_selection_model_unselect_all (model);
|
||||
else
|
||||
gtk_list_base_grab_focus_on_item (self, position, TRUE, FALSE, FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_clear_search_filter (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (priv->search_filter == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (priv->search_filter,
|
||||
gtk_list_base_search_filter_changed_cb,
|
||||
self);
|
||||
|
||||
g_clear_object (&priv->search_filter);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_base_set_search_filter (GtkListBase *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
if (priv->search_filter == filter)
|
||||
return;
|
||||
|
||||
gtk_list_base_clear_search_filter (self);
|
||||
|
||||
if (filter)
|
||||
{
|
||||
priv->search_filter = g_object_ref (filter);
|
||||
g_signal_connect (priv->search_filter, "changed",
|
||||
G_CALLBACK (gtk_list_base_search_filter_changed_cb), self);
|
||||
gtk_list_base_search_filter_changed_cb (priv->search_filter, GTK_FILTER_CHANGE_DIFFERENT, self);
|
||||
}
|
||||
|
||||
gtk_widget_action_set_enabled (GTK_WIDGET (self), "list.next-match", filter != NULL);
|
||||
}
|
||||
|
||||
GtkFilter *
|
||||
gtk_list_base_get_search_filter (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
return priv->search_filter;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_list_base_select_next_match (GtkListBase *self,
|
||||
gboolean forward)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkSelectionModel *model = gtk_list_item_manager_get_model (priv->item_manager);
|
||||
guint position;
|
||||
|
||||
if (!priv->search_filter)
|
||||
return FALSE;
|
||||
|
||||
position = find_first_selected (model);
|
||||
if (position == GTK_INVALID_LIST_POSITION)
|
||||
return FALSE;
|
||||
|
||||
if (forward)
|
||||
position = position + 1;
|
||||
else if (position > 0)
|
||||
position = position - 1;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
position = find_next_match (G_LIST_MODEL (model), priv->search_filter, position, forward);
|
||||
if (position == GTK_INVALID_LIST_POSITION)
|
||||
{
|
||||
gtk_widget_error_bell (GTK_WIDGET (self));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gtk_list_base_grab_focus_on_item (self, position, TRUE, FALSE, FALSE);
|
||||
return TRUE;
|
||||
}
|
||||
|
@@ -23,6 +23,7 @@
|
||||
#include "gtklistbase.h"
|
||||
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
#include "gtkfilter.h"
|
||||
#include "gtkprivate.h"
|
||||
|
||||
struct _GtkListBase
|
||||
@@ -99,5 +100,13 @@ gboolean gtk_list_base_grab_focus_on_item (GtkListBase
|
||||
gboolean select,
|
||||
gboolean modify,
|
||||
gboolean extend);
|
||||
void gtk_list_base_set_enable_rubberband (GtkListBase *self,
|
||||
gboolean enable);
|
||||
gboolean gtk_list_base_get_enable_rubberband (GtkListBase *self);
|
||||
void gtk_list_base_set_search_filter (GtkListBase *self,
|
||||
GtkFilter *filter);
|
||||
GtkFilter * gtk_list_base_get_search_filter (GtkListBase *self);
|
||||
gboolean gtk_list_base_select_next_match (GtkListBase *self,
|
||||
gboolean forward);
|
||||
|
||||
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
|
||||
|
@@ -29,6 +29,7 @@
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistitemfactoryprivate.h"
|
||||
#include "gtklistitemprivate.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtkmain.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
#include "gtkwidget.h"
|
||||
@@ -309,6 +310,8 @@ gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
|
||||
{
|
||||
GtkListItemWidgetPrivate *priv = gtk_list_item_widget_get_instance_private (self);
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
GtkWidget * parent = gtk_widget_get_parent (widget);
|
||||
gboolean rubberband;
|
||||
|
||||
if (priv->list_item && !priv->list_item->selectable && !priv->list_item->activatable)
|
||||
{
|
||||
@@ -316,7 +319,12 @@ gtk_list_item_widget_click_gesture_pressed (GtkGestureClick *gesture,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!priv->list_item || priv->list_item->selectable)
|
||||
if (GTK_IS_LIST_BASE (parent))
|
||||
rubberband = gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (parent));
|
||||
else
|
||||
rubberband = FALSE;
|
||||
|
||||
if (!rubberband && (!priv->list_item || priv->list_item->selectable))
|
||||
{
|
||||
GdkModifierType state;
|
||||
GdkEvent *event;
|
||||
|
@@ -85,6 +85,8 @@ enum
|
||||
PROP_MODEL,
|
||||
PROP_SHOW_SEPARATORS,
|
||||
PROP_SINGLE_CLICK_ACTIVATE,
|
||||
PROP_ENABLE_RUBBERBAND,
|
||||
PROP_SEARCH_FILTER,
|
||||
|
||||
N_PROPS
|
||||
};
|
||||
@@ -643,6 +645,14 @@ gtk_list_view_get_property (GObject *object,
|
||||
g_value_set_boolean (value, gtk_list_item_manager_get_single_click_activate (self->item_manager));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
g_value_set_boolean (value, gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
g_value_set_object (value, gtk_list_view_get_search_filter (self));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -675,6 +685,14 @@ gtk_list_view_set_property (GObject *object,
|
||||
gtk_list_view_set_single_click_activate (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_ENABLE_RUBBERBAND:
|
||||
gtk_list_view_set_enable_rubberband (self, g_value_get_boolean (value));
|
||||
break;
|
||||
|
||||
case PROP_SEARCH_FILTER:
|
||||
gtk_list_view_set_search_filter (self, g_value_get_object (value));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@@ -771,6 +789,30 @@ gtk_list_view_class_init (GtkListViewClass *klass)
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkListView:enable-rubberband:
|
||||
*
|
||||
* Allow rubberband selection
|
||||
*/
|
||||
properties[PROP_ENABLE_RUBBERBAND] =
|
||||
g_param_spec_boolean ("enable-rubberband",
|
||||
P_("Enable rubberband selection"),
|
||||
P_("Allow selecting items by dragging with the mouse"),
|
||||
FALSE,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
||||
|
||||
/**
|
||||
* GtkListView:search-filter:
|
||||
*
|
||||
* Filter used for search
|
||||
*/
|
||||
properties[PROP_SEARCH_FILTER] =
|
||||
g_param_spec_object ("search-filter",
|
||||
P_("Search filter"),
|
||||
P_("Filter used for searching"),
|
||||
GTK_TYPE_FILTER,
|
||||
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_PROPS, properties);
|
||||
|
||||
/**
|
||||
@@ -1033,3 +1075,103 @@ gtk_list_view_get_single_click_activate (GtkListView *self)
|
||||
|
||||
return gtk_list_item_manager_get_single_click_activate (self->item_manager);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_set_enable_rubberband:
|
||||
* @self: a #GtkListView
|
||||
* @enable_rubberband: %TRUE to enable rubberband selection
|
||||
*
|
||||
* Sets whether selections can be changed by dragging with the mouse.
|
||||
*/
|
||||
void
|
||||
gtk_list_view_set_enable_rubberband (GtkListView *self,
|
||||
gboolean enable_rubberband)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_VIEW (self));
|
||||
|
||||
if (enable_rubberband == gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
gtk_list_base_set_enable_rubberband (GTK_LIST_BASE (self), enable_rubberband);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_ENABLE_RUBBERBAND]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_get_enable_rubberband:
|
||||
* @self: a #GtkListView
|
||||
*
|
||||
* Returns whether rows can be selected by dragging with the mouse.
|
||||
*
|
||||
* Returns: %TRUE if rubberband selection is enabled
|
||||
*/
|
||||
gboolean
|
||||
gtk_list_view_get_enable_rubberband (GtkListView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), FALSE);
|
||||
|
||||
return gtk_list_base_get_enable_rubberband (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_set_search_filter:
|
||||
* @self: a #GtkListView
|
||||
* @filter: (nullable): the filter ot use for search, or %NULL
|
||||
*
|
||||
* Sets a search filter.
|
||||
*
|
||||
* The selection will be moved to first item matching the
|
||||
* filter whenever the filter changes.
|
||||
*
|
||||
* This can be used with single selection and a string
|
||||
* filter that is connected to a search entry.
|
||||
*/
|
||||
void
|
||||
gtk_list_view_set_search_filter (GtkListView *self,
|
||||
GtkFilter *filter)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_VIEW (self));
|
||||
g_return_if_fail (filter == NULL || GTK_IS_FILTER (filter));
|
||||
|
||||
if (filter == gtk_list_base_get_search_filter (GTK_LIST_BASE (self)))
|
||||
return;
|
||||
|
||||
gtk_list_base_set_search_filter (GTK_LIST_BASE (self), filter);
|
||||
|
||||
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SEARCH_FILTER]);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_get_search_filter:
|
||||
* @self: a #GtkListView
|
||||
*
|
||||
* Gets the search filter that was set with
|
||||
* gtk_list_view_set_search_filter().
|
||||
*
|
||||
* Returns: (transfer none): The search filter of @self
|
||||
*/
|
||||
GtkFilter *
|
||||
gtk_list_view_get_search_filter (GtkListView *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_LIST_VIEW (self), NULL);
|
||||
|
||||
return gtk_list_base_get_search_filter (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_view_select_next_match:
|
||||
* @self: a #GtkListView
|
||||
* @forward: whether to move forward or back
|
||||
*
|
||||
* Moves the selection to the next item matching the
|
||||
* search filter set with gtk_list_view_set_search_filter().
|
||||
*/
|
||||
void
|
||||
gtk_list_view_select_next_match (GtkListView *self,
|
||||
gboolean forward)
|
||||
{
|
||||
g_return_if_fail (GTK_IS_LIST_VIEW (self));
|
||||
|
||||
gtk_list_base_select_next_match (GTK_LIST_BASE (self), forward);
|
||||
}
|
||||
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#endif
|
||||
|
||||
#include <gtk/gtklistbase.h>
|
||||
#include <gtk/gtkfilter.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@@ -75,6 +76,22 @@ void gtk_list_view_set_single_click_activate (GtkListView
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_view_get_single_click_activate (GtkListView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_set_enable_rubberband (GtkListView *self,
|
||||
gboolean enable_rubberband);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_list_view_get_enable_rubberband (GtkListView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_set_search_filter (GtkListView *self,
|
||||
GtkFilter *filter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkFilter * gtk_list_view_get_search_filter (GtkListView *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_list_view_select_next_match (GtkListView *self,
|
||||
gboolean forward);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_LIST_VIEW_H__ */
|
||||
|
371
gtk/gtkmultiselection.c
Normal file
371
gtk/gtkmultiselection.c
Normal file
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
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 *model);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_MULTI_SELECTION_H__ */
|
@@ -41,7 +41,7 @@ static void gtk_print_backend_get_property (GObject *object,
|
||||
|
||||
struct _GtkPrintBackendPrivate
|
||||
{
|
||||
GHashTable *printers;
|
||||
GListStore *printers;
|
||||
guint printer_list_requested : 1;
|
||||
guint printer_list_done : 1;
|
||||
GtkPrintBackendStatus status;
|
||||
@@ -331,9 +331,7 @@ gtk_print_backend_init (GtkPrintBackend *backend)
|
||||
|
||||
priv = backend->priv = gtk_print_backend_get_instance_private (backend);
|
||||
|
||||
priv->printers = g_hash_table_new_full (g_str_hash, g_str_equal,
|
||||
(GDestroyNotify) g_free,
|
||||
(GDestroyNotify) g_object_unref);
|
||||
priv->printers = g_list_store_new (GTK_TYPE_PRINTER);
|
||||
priv->auth_info_required = NULL;
|
||||
priv->auth_info = NULL;
|
||||
}
|
||||
@@ -350,11 +348,7 @@ gtk_print_backend_dispose (GObject *object)
|
||||
/* We unref the printers in dispose, not in finalize so that
|
||||
* we can break refcount cycles with gtk_print_backend_destroy
|
||||
*/
|
||||
if (priv->printers)
|
||||
{
|
||||
g_hash_table_destroy (priv->printers);
|
||||
priv->printers = NULL;
|
||||
}
|
||||
g_clear_object (&priv->printers);
|
||||
|
||||
backend_parent_class->dispose (object);
|
||||
}
|
||||
@@ -411,58 +405,25 @@ fallback_printer_get_capabilities (GtkPrinter *printer)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
printer_hash_to_sorted_active_list (const gchar *key,
|
||||
gpointer value,
|
||||
GList **out_list)
|
||||
{
|
||||
GtkPrinter *printer;
|
||||
|
||||
printer = GTK_PRINTER (value);
|
||||
|
||||
if (gtk_printer_get_name (printer) == NULL)
|
||||
return;
|
||||
|
||||
if (!gtk_printer_is_active (printer))
|
||||
return;
|
||||
|
||||
*out_list = g_list_insert_sorted (*out_list, value, (GCompareFunc) gtk_printer_compare);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
gtk_print_backend_add_printer (GtkPrintBackend *backend,
|
||||
GtkPrinter *printer)
|
||||
{
|
||||
GtkPrintBackendPrivate *priv;
|
||||
|
||||
g_return_if_fail (GTK_IS_PRINT_BACKEND (backend));
|
||||
|
||||
priv = backend->priv;
|
||||
|
||||
if (!priv->printers)
|
||||
return;
|
||||
|
||||
g_hash_table_insert (priv->printers,
|
||||
g_strdup (gtk_printer_get_name (printer)),
|
||||
g_object_ref (printer));
|
||||
g_list_store_append (backend->priv->printers, printer);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_print_backend_remove_printer (GtkPrintBackend *backend,
|
||||
GtkPrinter *printer)
|
||||
{
|
||||
GtkPrintBackendPrivate *priv;
|
||||
guint position;
|
||||
|
||||
g_return_if_fail (GTK_IS_PRINT_BACKEND (backend));
|
||||
priv = backend->priv;
|
||||
|
||||
if (!priv->printers)
|
||||
return;
|
||||
|
||||
g_hash_table_remove (priv->printers,
|
||||
gtk_printer_get_name (printer));
|
||||
if (g_list_store_find (backend->priv->printers, printer, &position))
|
||||
g_list_store_remove (backend->priv->printers, position);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -488,54 +449,67 @@ gtk_print_backend_set_list_done (GtkPrintBackend *backend)
|
||||
GList *
|
||||
gtk_print_backend_get_printer_list (GtkPrintBackend *backend)
|
||||
{
|
||||
GtkPrintBackendPrivate *priv;
|
||||
GList *result;
|
||||
GList *result = NULL;
|
||||
guint i;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_PRINT_BACKEND (backend), NULL);
|
||||
|
||||
priv = backend->priv;
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (backend->priv->printers)); i++)
|
||||
{
|
||||
GtkPrinter *printer = g_list_model_get_item (G_LIST_MODEL (backend->priv->printers), i);
|
||||
result = g_list_prepend (result, printer);
|
||||
g_object_unref (printer);
|
||||
}
|
||||
|
||||
result = NULL;
|
||||
if (priv->printers != NULL)
|
||||
g_hash_table_foreach (priv->printers,
|
||||
(GHFunc) printer_hash_to_sorted_active_list,
|
||||
&result);
|
||||
|
||||
if (!priv->printer_list_requested && priv->printers != NULL)
|
||||
if (!backend->priv->printer_list_requested)
|
||||
{
|
||||
if (GTK_PRINT_BACKEND_GET_CLASS (backend)->request_printer_list)
|
||||
GTK_PRINT_BACKEND_GET_CLASS (backend)->request_printer_list (backend);
|
||||
priv->printer_list_requested = TRUE;
|
||||
backend->priv->printer_list_requested = TRUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_print_backend_printer_list_is_done (GtkPrintBackend *print_backend)
|
||||
GListModel *
|
||||
gtk_print_backend_get_printers (GtkPrintBackend *backend)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_PRINT_BACKEND (print_backend), TRUE);
|
||||
if (!backend->priv->printer_list_requested)
|
||||
{
|
||||
if (GTK_PRINT_BACKEND_GET_CLASS (backend)->request_printer_list)
|
||||
GTK_PRINT_BACKEND_GET_CLASS (backend)->request_printer_list (backend);
|
||||
backend->priv->printer_list_requested = TRUE;
|
||||
}
|
||||
|
||||
return print_backend->priv->printer_list_done;
|
||||
return G_LIST_MODEL (backend->priv->printers);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_print_backend_printer_list_is_done (GtkPrintBackend *backend)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_PRINT_BACKEND (backend), TRUE);
|
||||
|
||||
return backend->priv->printer_list_done;
|
||||
}
|
||||
|
||||
GtkPrinter *
|
||||
gtk_print_backend_find_printer (GtkPrintBackend *backend,
|
||||
const gchar *printer_name)
|
||||
{
|
||||
GtkPrintBackendPrivate *priv;
|
||||
GtkPrinter *printer;
|
||||
GtkPrinter *result = NULL;
|
||||
guint i;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_PRINT_BACKEND (backend), NULL);
|
||||
|
||||
priv = backend->priv;
|
||||
for (i = 0; !result && i < g_list_model_get_n_items (G_LIST_MODEL (backend->priv->printers)); i++)
|
||||
{
|
||||
GtkPrinter *printer = g_list_model_get_item (G_LIST_MODEL (backend->priv->printers), i);
|
||||
if (strcmp (gtk_printer_get_name (printer), printer_name) == 0)
|
||||
result = printer;
|
||||
g_object_unref (printer);
|
||||
}
|
||||
|
||||
if (priv->printers)
|
||||
printer = g_hash_table_lookup (priv->printers, printer_name);
|
||||
else
|
||||
printer = NULL;
|
||||
|
||||
return printer;
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -754,7 +728,7 @@ request_password (GtkPrintBackend *backend,
|
||||
}
|
||||
|
||||
void
|
||||
gtk_print_backend_destroy (GtkPrintBackend *print_backend)
|
||||
gtk_print_backend_destroy (GtkPrintBackend *backend)
|
||||
{
|
||||
/* The lifecycle of print backends and printers are tied, such that
|
||||
* the backend owns the printers, but the printers also ref the backend.
|
||||
@@ -762,5 +736,5 @@ gtk_print_backend_destroy (GtkPrintBackend *print_backend)
|
||||
* will be around. However, this results in a cycle, which we break
|
||||
* with this call, which causes the print backend to release its printers.
|
||||
*/
|
||||
g_object_run_dispose (G_OBJECT (print_backend));
|
||||
g_object_run_dispose (G_OBJECT (backend));
|
||||
}
|
||||
|
@@ -149,6 +149,8 @@ GType gtk_print_backend_get_type (void) G_GNUC_CONST;
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GList *gtk_print_backend_get_printer_list (GtkPrintBackend *print_backend);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel *gtk_print_backend_get_printers (GtkPrintBackend *print_backend);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_print_backend_printer_list_is_done (GtkPrintBackend *print_backend);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkPrinter *gtk_print_backend_find_printer (GtkPrintBackend *print_backend,
|
||||
|
@@ -169,7 +169,7 @@ gtk_printer_class_init (GtkPrinterClass *class)
|
||||
g_param_spec_string ("icon-name",
|
||||
P_("Icon Name"),
|
||||
P_("The icon name to use for the printer"),
|
||||
"",
|
||||
"printer",
|
||||
GTK_PARAM_READABLE));
|
||||
g_object_class_install_property (G_OBJECT_CLASS (class),
|
||||
PROP_JOB_COUNT,
|
||||
@@ -340,7 +340,7 @@ gtk_printer_get_property (GObject *object,
|
||||
if (priv->icon_name)
|
||||
g_value_set_string (value, priv->icon_name);
|
||||
else
|
||||
g_value_set_static_string (value, "");
|
||||
g_value_set_static_string (value, "printer");
|
||||
break;
|
||||
case PROP_JOB_COUNT:
|
||||
g_value_set_int (value, priv->job_count);
|
||||
|
@@ -35,7 +35,6 @@
|
||||
|
||||
#include "gtkspinbutton.h"
|
||||
#include "gtkimage.h"
|
||||
#include "gtktreeselection.h"
|
||||
#include "gtknotebook.h"
|
||||
#include "gtkscrolledwindow.h"
|
||||
#include "gtkcombobox.h"
|
||||
@@ -136,14 +135,12 @@ static void gtk_print_unix_dialog_get_property (GObject *object,
|
||||
GValue *value,
|
||||
GParamSpec *pspec);
|
||||
static void unschedule_idle_mark_conflicts (GtkPrintUnixDialog *dialog);
|
||||
static void selected_printer_changed (GtkTreeSelection *selection,
|
||||
GtkPrintUnixDialog *dialog);
|
||||
static void selected_printer_changed (GtkPrintUnixDialog *dialog);
|
||||
static void clear_per_printer_ui (GtkPrintUnixDialog *dialog);
|
||||
static void printer_added_cb (GtkPrintBackend *backend,
|
||||
GtkPrinter *printer,
|
||||
GtkPrintUnixDialog *dialog);
|
||||
static void printer_removed_cb (GtkPrintBackend *backend,
|
||||
GtkPrinter *printer,
|
||||
static void printer_added_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkPrintUnixDialog *dialog);
|
||||
static void printer_status_cb (GtkPrintBackend *backend,
|
||||
GtkPrinter *printer,
|
||||
@@ -171,12 +168,10 @@ static void draw_collate (GtkDrawingArea *da,
|
||||
int width,
|
||||
int height,
|
||||
gpointer data);
|
||||
static gboolean is_printer_active (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
GtkPrintUnixDialog *dialog);
|
||||
static gint default_printer_list_sort_func (GtkTreeModel *model,
|
||||
GtkTreeIter *a,
|
||||
GtkTreeIter *b,
|
||||
static gboolean is_printer_active (gpointer item,
|
||||
gpointer data);
|
||||
static int default_printer_list_sort_func (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer user_data);
|
||||
static gboolean paper_size_row_is_separator (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
@@ -198,20 +193,10 @@ static gboolean dialog_get_collate (GtkPrintUnixDialog *dialog);
|
||||
static gboolean dialog_get_reverse (GtkPrintUnixDialog *dialog);
|
||||
static gint dialog_get_n_copies (GtkPrintUnixDialog *dialog);
|
||||
|
||||
static void set_cell_sensitivity_func (GtkTreeViewColumn *tree_column,
|
||||
GtkCellRenderer *cell,
|
||||
GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data);
|
||||
static gboolean set_active_printer (GtkPrintUnixDialog *dialog,
|
||||
const gchar *printer_name);
|
||||
static void redraw_page_layout_preview (GtkPrintUnixDialog *dialog);
|
||||
static void load_print_backends (GtkPrintUnixDialog *dialog);
|
||||
static gboolean printer_compare (GtkTreeModel *model,
|
||||
gint column,
|
||||
const gchar *key,
|
||||
GtkTreeIter *iter,
|
||||
gpointer search_data);
|
||||
static GListModel *load_print_backends (GtkPrintUnixDialog *dialog);
|
||||
|
||||
/* GtkBuildable */
|
||||
static void gtk_print_unix_dialog_buildable_init (GtkBuildableIface *iface);
|
||||
@@ -272,22 +257,11 @@ struct _GtkPrintUnixDialog
|
||||
|
||||
GtkWidget *notebook;
|
||||
|
||||
GtkWidget *printer_treeview;
|
||||
GtkTreeViewColumn *printer_icon_column;
|
||||
GtkTreeViewColumn *printer_name_column;
|
||||
GtkTreeViewColumn *printer_location_column;
|
||||
GtkTreeViewColumn *printer_status_column;
|
||||
GtkCellRenderer *printer_icon_renderer;
|
||||
GtkCellRenderer *printer_name_renderer;
|
||||
GtkCellRenderer *printer_location_renderer;
|
||||
GtkCellRenderer *printer_status_renderer;
|
||||
GtkWidget *printer_list;
|
||||
|
||||
GtkPrintCapabilities manual_capabilities;
|
||||
GtkPrintCapabilities printer_capabilities;
|
||||
|
||||
GtkTreeModel *printer_list;
|
||||
GtkTreeModelFilter *printer_list_filter;
|
||||
|
||||
GtkPageSetup *page_setup;
|
||||
gboolean page_setup_set;
|
||||
gboolean embed_page_setup;
|
||||
@@ -491,19 +465,9 @@ gtk_print_unix_dialog_class_init (GtkPrintUnixDialogClass *class)
|
||||
"/org/gtk/libgtk/ui/gtkprintunixdialog.ui");
|
||||
|
||||
/* GtkTreeView / GtkTreeModel */
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_treeview);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_list);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_list_filter);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, page_setup_list);
|
||||
gtk_widget_class_bind_template_child(widget_class, GtkPrintUnixDialog, page_setup_list);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, custom_paper_list);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_icon_column);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_name_column);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_location_column);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_status_column);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_icon_renderer);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_name_renderer);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_location_renderer);
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, printer_status_renderer);
|
||||
|
||||
/* General Widgetry */
|
||||
gtk_widget_class_bind_template_child (widget_class, GtkPrintUnixDialog, notebook);
|
||||
@@ -558,7 +522,6 @@ gtk_print_unix_dialog_class_init (GtkPrintUnixDialogClass *class)
|
||||
gtk_widget_class_bind_template_callback (widget_class, redraw_page_layout_preview);
|
||||
gtk_widget_class_bind_template_callback (widget_class, error_dialogs);
|
||||
gtk_widget_class_bind_template_callback (widget_class, emit_ok_response);
|
||||
gtk_widget_class_bind_template_callback (widget_class, selected_printer_changed);
|
||||
gtk_widget_class_bind_template_callback (widget_class, page_range_entry_focus_changed);
|
||||
gtk_widget_class_bind_template_callback (widget_class, update_page_range_entry_sensitivity);
|
||||
gtk_widget_class_bind_template_callback (widget_class, update_print_at_entry_sensitivity);
|
||||
@@ -729,11 +692,24 @@ error_dialogs (GtkPrintUnixDialog *dialog,
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
get_printer_key (GtkPrinter *printer)
|
||||
{
|
||||
return g_strconcat ("", gtk_printer_get_name (printer), " ", gtk_printer_get_location (printer), NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_print_unix_dialog_init (GtkPrintUnixDialog *dialog)
|
||||
{
|
||||
GtkTreeSortable *sort;
|
||||
GtkWidget *widget;
|
||||
GListModel *model;
|
||||
GListModel *sorted;
|
||||
GListModel *filtered;
|
||||
GListModel *selection;
|
||||
GtkSorter *sorter;
|
||||
GtkFilter *filter;
|
||||
GtkFilter *filter1;
|
||||
GtkExpression *expression;
|
||||
|
||||
dialog->print_backends = NULL;
|
||||
dialog->current_page = -1;
|
||||
@@ -767,43 +743,7 @@ gtk_print_unix_dialog_init (GtkPrintUnixDialog *dialog)
|
||||
gtk_widget_set_visible (dialog->selection_radio, FALSE);
|
||||
gtk_widget_set_visible (dialog->conflicts_widget, FALSE);
|
||||
|
||||
/* Treeview auxiliary functions need to be setup here */
|
||||
gtk_tree_model_filter_set_visible_func (dialog->printer_list_filter,
|
||||
(GtkTreeModelFilterVisibleFunc) is_printer_active,
|
||||
dialog,
|
||||
NULL);
|
||||
|
||||
sort = GTK_TREE_SORTABLE (dialog->printer_list);
|
||||
gtk_tree_sortable_set_default_sort_func (sort,
|
||||
default_printer_list_sort_func,
|
||||
NULL,
|
||||
NULL);
|
||||
|
||||
gtk_tree_sortable_set_sort_column_id (sort,
|
||||
GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID,
|
||||
GTK_SORT_ASCENDING);
|
||||
|
||||
gtk_tree_view_set_search_equal_func (GTK_TREE_VIEW (dialog->printer_treeview),
|
||||
printer_compare, NULL, NULL);
|
||||
|
||||
gtk_tree_view_column_set_cell_data_func (dialog->printer_icon_column,
|
||||
dialog->printer_icon_renderer,
|
||||
set_cell_sensitivity_func, NULL, NULL);
|
||||
|
||||
gtk_tree_view_column_set_cell_data_func (dialog->printer_name_column,
|
||||
dialog->printer_name_renderer,
|
||||
set_cell_sensitivity_func, NULL, NULL);
|
||||
|
||||
gtk_tree_view_column_set_cell_data_func (dialog->printer_location_column,
|
||||
dialog->printer_location_renderer,
|
||||
set_cell_sensitivity_func, NULL, NULL);
|
||||
|
||||
gtk_tree_view_column_set_cell_data_func (dialog->printer_status_column,
|
||||
dialog->printer_status_renderer,
|
||||
set_cell_sensitivity_func, NULL, NULL);
|
||||
|
||||
|
||||
/* Paper size combo auxiliary funcs */
|
||||
/* Paper size combo auxilary funcs */
|
||||
gtk_combo_box_set_row_separator_func (GTK_COMBO_BOX (dialog->paper_size_combo),
|
||||
paper_size_row_is_separator, NULL, NULL);
|
||||
gtk_cell_layout_set_cell_data_func (GTK_CELL_LAYOUT (dialog->paper_size_combo),
|
||||
@@ -811,7 +751,39 @@ gtk_print_unix_dialog_init (GtkPrintUnixDialog *dialog)
|
||||
page_name_func, NULL, NULL);
|
||||
|
||||
/* Load backends */
|
||||
load_print_backends (dialog);
|
||||
model = load_print_backends (dialog);
|
||||
sorter = gtk_custom_sorter_new (default_printer_list_sort_func, NULL, NULL);
|
||||
sorted = G_LIST_MODEL (gtk_sort_list_model_new (model, sorter));
|
||||
g_object_unref (sorter);
|
||||
|
||||
filter = gtk_every_filter_new ();
|
||||
|
||||
filter1 = gtk_string_filter_new ();
|
||||
gtk_string_filter_set_match_mode (GTK_STRING_FILTER (filter1), GTK_STRING_FILTER_MATCH_MODE_SUBSTRING);
|
||||
gtk_string_filter_set_ignore_case (GTK_STRING_FILTER (filter1), TRUE);
|
||||
expression = gtk_cclosure_expression_new (G_TYPE_STRING,
|
||||
NULL, 0, NULL,
|
||||
G_CALLBACK (get_printer_key),
|
||||
NULL, NULL);
|
||||
gtk_string_filter_set_expression (GTK_STRING_FILTER (filter1), expression);
|
||||
gtk_expression_unref (expression);
|
||||
gtk_multi_filter_append (GTK_MULTI_FILTER (filter), filter1);
|
||||
|
||||
filter1 = gtk_custom_filter_new (is_printer_active, dialog, NULL);
|
||||
gtk_multi_filter_append (GTK_MULTI_FILTER (filter), filter1);
|
||||
|
||||
filtered = G_LIST_MODEL (gtk_filter_list_model_new (sorted, filter));
|
||||
g_object_unref (filter);
|
||||
|
||||
selection = G_LIST_MODEL (gtk_single_selection_new (filtered));
|
||||
gtk_single_selection_set_autoselect (GTK_SINGLE_SELECTION (selection), FALSE);
|
||||
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (selection), GTK_INVALID_LIST_POSITION);
|
||||
gtk_column_view_set_model (GTK_COLUMN_VIEW (dialog->printer_list), selection);
|
||||
g_signal_connect (selection, "items-changed", G_CALLBACK (printer_added_cb), dialog);
|
||||
g_signal_connect_swapped (selection, "notify::selected", G_CALLBACK (selected_printer_changed), dialog);
|
||||
g_object_unref (selection);
|
||||
g_object_unref (filtered);
|
||||
g_object_unref (model);
|
||||
|
||||
/* Load custom papers */
|
||||
_gtk_print_load_custom_papers (dialog->custom_paper_list);
|
||||
@@ -884,21 +856,8 @@ disconnect_printer_details_request (GtkPrintUnixDialog *dialog,
|
||||
dialog->request_details_tag = 0;
|
||||
set_busy_cursor (dialog, FALSE);
|
||||
if (details_failed)
|
||||
gtk_list_store_set (GTK_LIST_STORE (dialog->printer_list),
|
||||
g_object_get_data (G_OBJECT (dialog->request_details_printer),
|
||||
"gtk-print-tree-iter"),
|
||||
PRINTER_LIST_COL_STATE,
|
||||
_("Getting printer information failed"),
|
||||
-1);
|
||||
else
|
||||
gtk_list_store_set (GTK_LIST_STORE (dialog->printer_list),
|
||||
g_object_get_data (G_OBJECT (dialog->request_details_printer),
|
||||
"gtk-print-tree-iter"),
|
||||
PRINTER_LIST_COL_STATE,
|
||||
gtk_printer_get_state_message (dialog->request_details_printer),
|
||||
-1);
|
||||
g_object_unref (dialog->request_details_printer);
|
||||
dialog->request_details_printer = NULL;
|
||||
gtk_printer_set_state_message (dialog->request_details_printer, _("Getting printer information failed"));
|
||||
g_clear_object (&dialog->request_details_printer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -906,8 +865,6 @@ static void
|
||||
gtk_print_unix_dialog_finalize (GObject *object)
|
||||
{
|
||||
GtkPrintUnixDialog *dialog = GTK_PRINT_UNIX_DIALOG (object);
|
||||
GtkPrintBackend *backend;
|
||||
GList *node;
|
||||
|
||||
unschedule_idle_mark_conflicts (dialog);
|
||||
disconnect_printer_details_request (dialog, FALSE);
|
||||
@@ -933,18 +890,6 @@ gtk_print_unix_dialog_finalize (GObject *object)
|
||||
g_clear_pointer (&dialog->waiting_for_printer, (GDestroyNotify)g_free);
|
||||
g_clear_pointer (&dialog->format_for_printer, (GDestroyNotify)g_free);
|
||||
|
||||
for (node = dialog->print_backends; node != NULL; node = node->next)
|
||||
{
|
||||
backend = GTK_PRINT_BACKEND (node->data);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (backend, printer_added_cb, dialog);
|
||||
g_signal_handlers_disconnect_by_func (backend, printer_removed_cb, dialog);
|
||||
g_signal_handlers_disconnect_by_func (backend, printer_status_cb, dialog);
|
||||
|
||||
gtk_print_backend_destroy (backend);
|
||||
g_object_unref (backend);
|
||||
}
|
||||
|
||||
g_list_free (dialog->print_backends);
|
||||
dialog->print_backends = NULL;
|
||||
|
||||
@@ -953,17 +898,6 @@ gtk_print_unix_dialog_finalize (GObject *object)
|
||||
G_OBJECT_CLASS (gtk_print_unix_dialog_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
printer_removed_cb (GtkPrintBackend *backend,
|
||||
GtkPrinter *printer,
|
||||
GtkPrintUnixDialog *dialog)
|
||||
{
|
||||
GtkTreeIter *iter;
|
||||
|
||||
iter = g_object_get_data (G_OBJECT (printer), "gtk-print-tree-iter");
|
||||
gtk_list_store_remove (GTK_LIST_STORE (dialog->printer_list), iter);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_print_unix_dialog_buildable_init (GtkBuildableIface *iface)
|
||||
{
|
||||
@@ -985,157 +919,68 @@ gtk_print_unix_dialog_buildable_get_internal_child (GtkBuildable *buildable,
|
||||
return parent_buildable_iface->get_internal_child (buildable, builder, childname);
|
||||
}
|
||||
|
||||
/* This function controls "sensitive" property of GtkCellRenderer
|
||||
* based on pause state of printers.
|
||||
*/
|
||||
void set_cell_sensitivity_func (GtkTreeViewColumn *tree_column,
|
||||
GtkCellRenderer *cell,
|
||||
GtkTreeModel *tree_model,
|
||||
GtkTreeIter *iter,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPrinter *printer;
|
||||
|
||||
gtk_tree_model_get (tree_model, iter,
|
||||
PRINTER_LIST_COL_PRINTER_OBJ, &printer,
|
||||
-1);
|
||||
|
||||
if (printer != NULL && !gtk_printer_is_accepting_jobs (printer))
|
||||
g_object_set (cell, "sensitive", FALSE, NULL);
|
||||
else
|
||||
g_object_set (cell, "sensitive", TRUE, NULL);
|
||||
|
||||
g_clear_object (&printer);
|
||||
}
|
||||
|
||||
static void
|
||||
printer_status_cb (GtkPrintBackend *backend,
|
||||
GtkPrinter *printer,
|
||||
GtkPrintUnixDialog *dialog)
|
||||
{
|
||||
GtkTreeIter *iter;
|
||||
GtkTreeSelection *selection;
|
||||
GIcon *icon;
|
||||
|
||||
iter = g_object_get_data (G_OBJECT (printer), "gtk-print-tree-iter");
|
||||
|
||||
icon = g_themed_icon_new ("printer");
|
||||
g_themed_icon_prepend_name (G_THEMED_ICON (icon), gtk_printer_get_icon_name (printer));
|
||||
gtk_list_store_set (GTK_LIST_STORE (dialog->printer_list), iter,
|
||||
PRINTER_LIST_COL_ICON, icon,
|
||||
PRINTER_LIST_COL_STATE, gtk_printer_get_state_message (printer),
|
||||
PRINTER_LIST_COL_JOBS, gtk_printer_get_job_count (printer),
|
||||
PRINTER_LIST_COL_LOCATION, gtk_printer_get_location (printer),
|
||||
-1);
|
||||
g_object_unref (icon);
|
||||
GListModel *model;
|
||||
|
||||
/* When the pause state change then we need to update sensitive property
|
||||
* of GTK_RESPONSE_OK button inside of selected_printer_changed function.
|
||||
*/
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->printer_treeview));
|
||||
dialog->internal_printer_change = TRUE;
|
||||
selected_printer_changed (selection, dialog);
|
||||
dialog->internal_printer_change = FALSE;
|
||||
selected_printer_changed (dialog);
|
||||
|
||||
model = gtk_column_view_get_model (GTK_COLUMN_VIEW (dialog->printer_list));
|
||||
|
||||
if (gtk_print_backend_printer_list_is_done (backend) &&
|
||||
gtk_printer_is_default (printer) &&
|
||||
(gtk_tree_selection_count_selected_rows (selection) == 0))
|
||||
gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (model)) == GTK_INVALID_LIST_POSITION)
|
||||
set_active_printer (dialog, gtk_printer_get_name (printer));
|
||||
}
|
||||
|
||||
static void
|
||||
printer_added_cb (GtkPrintBackend *backend,
|
||||
GtkPrinter *printer,
|
||||
printer_added_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkPrintUnixDialog *dialog)
|
||||
{
|
||||
GtkTreeIter iter, filter_iter;
|
||||
GtkTreeSelection *selection;
|
||||
GtkTreePath *path;
|
||||
GIcon *icon;
|
||||
guint i;
|
||||
|
||||
gtk_list_store_append (GTK_LIST_STORE (dialog->printer_list), &iter);
|
||||
|
||||
g_object_set_data_full (G_OBJECT (printer),
|
||||
"gtk-print-tree-iter",
|
||||
gtk_tree_iter_copy (&iter),
|
||||
(GDestroyNotify) gtk_tree_iter_free);
|
||||
|
||||
icon = g_themed_icon_new ("printer");
|
||||
g_themed_icon_prepend_name (G_THEMED_ICON (icon), gtk_printer_get_icon_name (printer));
|
||||
gtk_list_store_set (GTK_LIST_STORE (dialog->printer_list), &iter,
|
||||
PRINTER_LIST_COL_ICON, icon,
|
||||
PRINTER_LIST_COL_NAME, gtk_printer_get_name (printer),
|
||||
PRINTER_LIST_COL_STATE, gtk_printer_get_state_message (printer),
|
||||
PRINTER_LIST_COL_JOBS, gtk_printer_get_job_count (printer),
|
||||
PRINTER_LIST_COL_LOCATION, gtk_printer_get_location (printer),
|
||||
PRINTER_LIST_COL_PRINTER_OBJ, printer,
|
||||
-1);
|
||||
g_object_unref (icon);
|
||||
|
||||
gtk_tree_model_filter_convert_child_iter_to_iter (dialog->printer_list_filter,
|
||||
&filter_iter, &iter);
|
||||
path = gtk_tree_model_get_path (GTK_TREE_MODEL (dialog->printer_list_filter), &filter_iter);
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->printer_treeview));
|
||||
|
||||
if (dialog->waiting_for_printer != NULL &&
|
||||
strcmp (gtk_printer_get_name (printer), dialog->waiting_for_printer) == 0)
|
||||
for (i = position; i < position + added; i++)
|
||||
{
|
||||
dialog->internal_printer_change = TRUE;
|
||||
gtk_tree_selection_select_iter (selection, &filter_iter);
|
||||
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->printer_treeview),
|
||||
path, NULL, TRUE, 0.5, 0.0);
|
||||
dialog->internal_printer_change = FALSE;
|
||||
g_free (dialog->waiting_for_printer);
|
||||
dialog->waiting_for_printer = NULL;
|
||||
}
|
||||
else if (is_default_printer (dialog, printer) &&
|
||||
gtk_tree_selection_count_selected_rows (selection) == 0)
|
||||
{
|
||||
dialog->internal_printer_change = TRUE;
|
||||
gtk_tree_selection_select_iter (selection, &filter_iter);
|
||||
gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (dialog->printer_treeview),
|
||||
path, NULL, TRUE, 0.5, 0.0);
|
||||
dialog->internal_printer_change = FALSE;
|
||||
}
|
||||
GtkPrinter *printer = g_list_model_get_item (model, i);
|
||||
|
||||
gtk_tree_path_free (path);
|
||||
if (dialog->waiting_for_printer != NULL &&
|
||||
strcmp (gtk_printer_get_name (printer), dialog->waiting_for_printer) == 0)
|
||||
{
|
||||
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (model), i);
|
||||
g_free (dialog->waiting_for_printer);
|
||||
dialog->waiting_for_printer = NULL;
|
||||
g_object_unref (printer);
|
||||
return;
|
||||
}
|
||||
else if (is_default_printer (dialog, printer) &&
|
||||
gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (model)) == GTK_INVALID_LIST_POSITION)
|
||||
{
|
||||
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (model), i);
|
||||
g_object_unref (printer);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_unref (printer);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
printer_list_initialize (GtkPrintUnixDialog *dialog,
|
||||
GtkPrintBackend *print_backend)
|
||||
{
|
||||
GList *list;
|
||||
GList *node;
|
||||
|
||||
g_return_if_fail (print_backend != NULL);
|
||||
|
||||
g_signal_connect_object (print_backend, "printer-added",
|
||||
(GCallback) printer_added_cb, G_OBJECT (dialog), 0);
|
||||
|
||||
g_signal_connect_object (print_backend, "printer-removed",
|
||||
(GCallback) printer_removed_cb, G_OBJECT (dialog), 0);
|
||||
|
||||
g_signal_connect_object (print_backend, "printer-status-changed",
|
||||
(GCallback) printer_status_cb, G_OBJECT (dialog), 0);
|
||||
|
||||
list = gtk_print_backend_get_printer_list (print_backend);
|
||||
|
||||
node = list;
|
||||
while (node != NULL)
|
||||
{
|
||||
printer_added_cb (print_backend, node->data, dialog);
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
g_list_free (list);
|
||||
}
|
||||
|
||||
static void
|
||||
static GListModel *
|
||||
load_print_backends (GtkPrintUnixDialog *dialog)
|
||||
{
|
||||
GList *node;
|
||||
GListStore *lists;
|
||||
GListModel *model;
|
||||
|
||||
lists = g_list_store_new (G_TYPE_LIST_MODEL);
|
||||
|
||||
if (g_module_supported ())
|
||||
dialog->print_backends = gtk_print_backend_load_modules ();
|
||||
@@ -1143,8 +988,17 @@ load_print_backends (GtkPrintUnixDialog *dialog)
|
||||
for (node = dialog->print_backends; node != NULL; node = node->next)
|
||||
{
|
||||
GtkPrintBackend *backend = node->data;
|
||||
printer_list_initialize (dialog, backend);
|
||||
|
||||
g_signal_connect_object (backend, "printer-status-changed",
|
||||
G_CALLBACK (printer_status_cb), G_OBJECT (dialog), 0);
|
||||
g_list_store_append (lists, gtk_print_backend_get_printers (backend));
|
||||
}
|
||||
|
||||
model = G_LIST_MODEL (gtk_flatten_list_model_new (GTK_TYPE_PRINTER, G_LIST_MODEL (lists)));
|
||||
|
||||
g_object_unref (lists);
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -1226,19 +1080,11 @@ gtk_print_unix_dialog_get_property (GObject *object,
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_printer_active (GtkTreeModel *model,
|
||||
GtkTreeIter *iter,
|
||||
GtkPrintUnixDialog *dialog)
|
||||
is_printer_active (gpointer item, gpointer data)
|
||||
{
|
||||
GtkPrinter *printer = item;
|
||||
GtkPrintUnixDialog *dialog = data;
|
||||
gboolean result;
|
||||
GtkPrinter *printer;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
PRINTER_LIST_COL_PRINTER_OBJ, &printer,
|
||||
-1);
|
||||
|
||||
if (printer == NULL)
|
||||
return FALSE;
|
||||
|
||||
result = gtk_printer_is_active (printer);
|
||||
|
||||
@@ -1255,61 +1101,44 @@ is_printer_active (GtkTreeModel *model,
|
||||
gtk_printer_accepts_ps (printer));
|
||||
}
|
||||
|
||||
g_object_unref (printer);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gint
|
||||
default_printer_list_sort_func (GtkTreeModel *model,
|
||||
GtkTreeIter *a,
|
||||
GtkTreeIter *b,
|
||||
static int
|
||||
default_printer_list_sort_func (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer user_data)
|
||||
{
|
||||
gchar *a_name;
|
||||
gchar *b_name;
|
||||
GtkPrinter *a_printer;
|
||||
GtkPrinter *b_printer;
|
||||
gint result;
|
||||
|
||||
gtk_tree_model_get (model, a,
|
||||
PRINTER_LIST_COL_NAME, &a_name,
|
||||
PRINTER_LIST_COL_PRINTER_OBJ, &a_printer,
|
||||
-1);
|
||||
gtk_tree_model_get (model, b,
|
||||
PRINTER_LIST_COL_NAME, &b_name,
|
||||
PRINTER_LIST_COL_PRINTER_OBJ, &b_printer,
|
||||
-1);
|
||||
GtkPrinter *a_printer = (gpointer)a;
|
||||
GtkPrinter *b_printer = (gpointer)b;
|
||||
const char *a_name;
|
||||
const char *b_name;
|
||||
|
||||
if (a_printer == NULL && b_printer == NULL)
|
||||
result = 0;
|
||||
return 0;
|
||||
else if (a_printer == NULL)
|
||||
result = G_MAXINT;
|
||||
return 1;
|
||||
else if (b_printer == NULL)
|
||||
result = G_MININT;
|
||||
else if (gtk_printer_is_virtual (a_printer) && gtk_printer_is_virtual (b_printer))
|
||||
result = 0;
|
||||
return -1;
|
||||
|
||||
if (gtk_printer_is_virtual (a_printer) && gtk_printer_is_virtual (b_printer))
|
||||
return 0;
|
||||
else if (gtk_printer_is_virtual (a_printer) && !gtk_printer_is_virtual (b_printer))
|
||||
result = G_MININT;
|
||||
return -1;
|
||||
else if (!gtk_printer_is_virtual (a_printer) && gtk_printer_is_virtual (b_printer))
|
||||
result = G_MAXINT;
|
||||
else if (a_name == NULL && b_name == NULL)
|
||||
result = 0;
|
||||
return 1;
|
||||
|
||||
a_name = gtk_printer_get_name (a_printer);
|
||||
b_name = gtk_printer_get_name (b_printer);
|
||||
|
||||
if (a_name == NULL && b_name == NULL)
|
||||
return 0;
|
||||
else if (a_name == NULL && b_name != NULL)
|
||||
result = 1;
|
||||
return 1;
|
||||
else if (a_name != NULL && b_name == NULL)
|
||||
result = -1;
|
||||
else
|
||||
result = g_ascii_strcasecmp (a_name, b_name);
|
||||
return -1;
|
||||
|
||||
g_free (a_name);
|
||||
g_free (b_name);
|
||||
if (a_printer)
|
||||
g_object_unref (a_printer);
|
||||
if (b_printer)
|
||||
g_object_unref (b_printer);
|
||||
|
||||
return result;
|
||||
return g_ascii_strcasecmp (a_name, b_name);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
@@ -1697,8 +1526,6 @@ update_dialog_from_capabilities (GtkPrintUnixDialog *dialog)
|
||||
gtk_widget_set_visible (button, (caps & GTK_PRINT_CAPABILITY_PREVIEW) != 0);
|
||||
|
||||
update_collate_icon (NULL, dialog);
|
||||
|
||||
gtk_tree_model_filter_refilter (dialog->printer_list_filter);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@@ -1901,17 +1728,12 @@ mark_conflicts (GtkPrintUnixDialog *dialog)
|
||||
|
||||
if (printer)
|
||||
{
|
||||
|
||||
g_signal_handler_block (dialog->options,
|
||||
dialog->options_changed_handler);
|
||||
g_signal_handler_block (dialog->options, dialog->options_changed_handler);
|
||||
|
||||
gtk_printer_option_set_clear_conflicts (dialog->options);
|
||||
have_conflict = _gtk_printer_mark_conflicts (printer, dialog->options);
|
||||
|
||||
have_conflict = _gtk_printer_mark_conflicts (printer,
|
||||
dialog->options);
|
||||
|
||||
g_signal_handler_unblock (dialog->options,
|
||||
dialog->options_changed_handler);
|
||||
g_signal_handler_unblock (dialog->options, dialog->options_changed_handler);
|
||||
}
|
||||
|
||||
if (have_conflict)
|
||||
@@ -1989,20 +1811,14 @@ printer_details_acquired (GtkPrinter *printer,
|
||||
disconnect_printer_details_request (dialog, !success);
|
||||
|
||||
if (success)
|
||||
{
|
||||
GtkTreeSelection *selection;
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->printer_treeview));
|
||||
|
||||
selected_printer_changed (selection, dialog);
|
||||
}
|
||||
selected_printer_changed (dialog);
|
||||
}
|
||||
|
||||
static void
|
||||
selected_printer_changed (GtkTreeSelection *selection,
|
||||
GtkPrintUnixDialog *dialog)
|
||||
selected_printer_changed (GtkPrintUnixDialog *dialog)
|
||||
{
|
||||
GListModel *model = gtk_column_view_get_model (GTK_COLUMN_VIEW (dialog->printer_list));
|
||||
GtkPrinter *printer;
|
||||
GtkTreeIter iter, filter_iter;
|
||||
|
||||
/* Whenever the user selects a printer we stop looking for
|
||||
* the printer specified in the initial settings
|
||||
@@ -2016,17 +1832,7 @@ selected_printer_changed (GtkTreeSelection *selection,
|
||||
|
||||
disconnect_printer_details_request (dialog, FALSE);
|
||||
|
||||
printer = NULL;
|
||||
if (gtk_tree_selection_get_selected (selection, NULL, &filter_iter))
|
||||
{
|
||||
gtk_tree_model_filter_convert_iter_to_child_iter (dialog->printer_list_filter,
|
||||
&iter,
|
||||
&filter_iter);
|
||||
|
||||
gtk_tree_model_get (dialog->printer_list, &iter,
|
||||
PRINTER_LIST_COL_PRINTER_OBJ, &printer,
|
||||
-1);
|
||||
}
|
||||
printer = gtk_single_selection_get_selected_item (GTK_SINGLE_SELECTION (model));
|
||||
|
||||
/* sets GTK_RESPONSE_OK button sensitivity depending on whether the printer
|
||||
* accepts/rejects jobs
|
||||
@@ -2036,33 +1842,24 @@ selected_printer_changed (GtkTreeSelection *selection,
|
||||
if (!gtk_printer_is_accepting_jobs (printer))
|
||||
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE);
|
||||
else if (dialog->current_printer == printer && gtk_printer_has_details (printer))
|
||||
|
||||
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE);
|
||||
}
|
||||
|
||||
if (printer != NULL && !gtk_printer_has_details (printer))
|
||||
{
|
||||
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, FALSE);
|
||||
dialog->request_details_tag =
|
||||
g_signal_connect (printer, "details-acquired",
|
||||
G_CALLBACK (printer_details_acquired), dialog);
|
||||
/* take the reference */
|
||||
dialog->request_details_printer = printer;
|
||||
dialog->request_details_tag = g_signal_connect (printer, "details-acquired",
|
||||
G_CALLBACK (printer_details_acquired), dialog);
|
||||
|
||||
dialog->request_details_printer = g_object_ref (printer);
|
||||
set_busy_cursor (dialog, TRUE);
|
||||
gtk_list_store_set (GTK_LIST_STORE (dialog->printer_list),
|
||||
g_object_get_data (G_OBJECT (printer), "gtk-print-tree-iter"),
|
||||
PRINTER_LIST_COL_STATE, _("Getting printer information…"),
|
||||
-1);
|
||||
gtk_printer_set_state_message (printer, _("Getting printer information…"));
|
||||
gtk_printer_request_details (printer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (printer == dialog->current_printer)
|
||||
{
|
||||
if (printer)
|
||||
g_object_unref (printer);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
|
||||
if (dialog->options)
|
||||
{
|
||||
@@ -2075,7 +1872,7 @@ selected_printer_changed (GtkTreeSelection *selection,
|
||||
|
||||
if (printer != NULL && gtk_printer_is_accepting_jobs (printer))
|
||||
gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK, TRUE);
|
||||
dialog->current_printer = printer;
|
||||
dialog->current_printer = g_object_ref (printer);
|
||||
|
||||
if (printer != NULL)
|
||||
{
|
||||
@@ -2114,86 +1911,7 @@ selected_printer_changed (GtkTreeSelection *selection,
|
||||
update_paper_sizes (dialog);
|
||||
dialog->internal_page_setup_change = FALSE;
|
||||
|
||||
g_object_notify ( G_OBJECT(dialog), "selected-printer");
|
||||
}
|
||||
|
||||
static gboolean
|
||||
printer_compare (GtkTreeModel *model,
|
||||
gint column,
|
||||
const gchar *key,
|
||||
GtkTreeIter *iter,
|
||||
gpointer search_data)
|
||||
{
|
||||
gboolean matches = FALSE;
|
||||
|
||||
if (key != NULL)
|
||||
{
|
||||
gchar *name = NULL;
|
||||
gchar *location = NULL;
|
||||
gchar *casefold_key = NULL;
|
||||
gchar *casefold_name = NULL;
|
||||
gchar *casefold_location = NULL;
|
||||
gchar **keys;
|
||||
gchar *tmp1, *tmp2;
|
||||
gint i;
|
||||
|
||||
gtk_tree_model_get (model, iter,
|
||||
PRINTER_LIST_COL_NAME, &name,
|
||||
PRINTER_LIST_COL_LOCATION, &location,
|
||||
-1);
|
||||
|
||||
casefold_key = g_utf8_casefold (key, -1);
|
||||
|
||||
if (name != NULL)
|
||||
{
|
||||
casefold_name = g_utf8_casefold (name, -1);
|
||||
g_free (name);
|
||||
}
|
||||
|
||||
if (location != NULL)
|
||||
{
|
||||
casefold_location = g_utf8_casefold (location, -1);
|
||||
g_free (location);
|
||||
}
|
||||
|
||||
if (casefold_name != NULL ||
|
||||
casefold_location != NULL)
|
||||
{
|
||||
keys = g_strsplit_set (casefold_key, " \t", 0);
|
||||
if (keys != NULL)
|
||||
{
|
||||
matches = TRUE;
|
||||
|
||||
for (i = 0; keys[i] != NULL; i++)
|
||||
{
|
||||
if (keys[i][0] != '\0')
|
||||
{
|
||||
tmp1 = tmp2 = NULL;
|
||||
|
||||
if (casefold_name != NULL)
|
||||
tmp1 = g_strstr_len (casefold_name, -1, keys[i]);
|
||||
|
||||
if (casefold_location != NULL)
|
||||
tmp2 = g_strstr_len (casefold_location, -1, keys[i]);
|
||||
|
||||
if (tmp1 == NULL && tmp2 == NULL)
|
||||
{
|
||||
matches = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
g_strfreev (keys);
|
||||
}
|
||||
}
|
||||
|
||||
g_free (casefold_location);
|
||||
g_free (casefold_name);
|
||||
g_free (casefold_key);
|
||||
}
|
||||
|
||||
return !matches;
|
||||
g_object_notify (G_OBJECT (dialog), "selected-printer");
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -3488,42 +3206,28 @@ static gboolean
|
||||
set_active_printer (GtkPrintUnixDialog *dialog,
|
||||
const gchar *printer_name)
|
||||
{
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter, filter_iter;
|
||||
GtkTreeSelection *selection;
|
||||
GListModel *model;
|
||||
GtkPrinter *printer;
|
||||
guint i;
|
||||
|
||||
model = GTK_TREE_MODEL (dialog->printer_list);
|
||||
model = gtk_column_view_get_model (GTK_COLUMN_VIEW (dialog->printer_list));
|
||||
|
||||
if (gtk_tree_model_get_iter_first (model, &iter))
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
do
|
||||
printer = g_list_model_get_item (model, i);
|
||||
|
||||
if (strcmp (gtk_printer_get_name (printer), printer_name) == 0)
|
||||
{
|
||||
gtk_tree_model_get (GTK_TREE_MODEL (dialog->printer_list), &iter,
|
||||
PRINTER_LIST_COL_PRINTER_OBJ, &printer,
|
||||
-1);
|
||||
if (printer == NULL)
|
||||
continue;
|
||||
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (model), i);
|
||||
|
||||
if (strcmp (gtk_printer_get_name (printer), printer_name) == 0)
|
||||
{
|
||||
gtk_tree_model_filter_convert_child_iter_to_iter (dialog->printer_list_filter,
|
||||
&filter_iter, &iter);
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->printer_treeview));
|
||||
dialog->internal_printer_change = TRUE;
|
||||
gtk_tree_selection_select_iter (selection, &filter_iter);
|
||||
dialog->internal_printer_change = FALSE;
|
||||
g_free (dialog->waiting_for_printer);
|
||||
dialog->waiting_for_printer = NULL;
|
||||
|
||||
g_object_unref (printer);
|
||||
return TRUE;
|
||||
}
|
||||
g_free (dialog->waiting_for_printer);
|
||||
dialog->waiting_for_printer = NULL;
|
||||
|
||||
g_object_unref (printer);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
} while (gtk_tree_model_iter_next (model, &iter));
|
||||
g_object_unref (printer);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
@@ -3697,13 +3401,8 @@ gtk_print_unix_dialog_set_manual_capabilities (GtkPrintUnixDialog *dialog,
|
||||
|
||||
if (dialog->current_printer)
|
||||
{
|
||||
GtkTreeSelection *selection;
|
||||
|
||||
selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->printer_treeview));
|
||||
g_clear_object (&dialog->current_printer);
|
||||
dialog->internal_printer_change = TRUE;
|
||||
selected_printer_changed (selection, dialog);
|
||||
dialog->internal_printer_change = FALSE;
|
||||
selected_printer_changed (dialog);
|
||||
}
|
||||
|
||||
g_object_notify (G_OBJECT (dialog), "manual-capabilities");
|
||||
|
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 negatie 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__ */
|
@@ -96,12 +96,14 @@ variant_editor_new (const GVariantType *type,
|
||||
else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING))
|
||||
{
|
||||
editor = gtk_entry_new ();
|
||||
gtk_editable_set_width_chars (GTK_EDITABLE (editor), 10);
|
||||
g_signal_connect (editor, "notify::text", G_CALLBACK (variant_editor_changed_cb), d);
|
||||
}
|
||||
else
|
||||
{
|
||||
editor = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
|
||||
entry = gtk_entry_new ();
|
||||
gtk_editable_set_width_chars (GTK_EDITABLE (entry), 10);
|
||||
gtk_box_append (GTK_BOX (editor), entry);
|
||||
label = gtk_label_new (g_variant_type_peek_string (type));
|
||||
gtk_box_append (GTK_BOX (editor), label);
|
||||
@@ -284,7 +286,8 @@ constructed (GObject *object)
|
||||
row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
|
||||
activate = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
|
||||
gtk_box_append (GTK_BOX (row), activate);
|
||||
gtk_size_group_add_widget (r->priv->sg, activate);
|
||||
if (r->priv->sg)
|
||||
gtk_size_group_add_widget (r->priv->sg, activate);
|
||||
|
||||
r->priv->activate_button = gtk_button_new_with_label (_("Activate"));
|
||||
g_signal_connect (r->priv->activate_button, "clicked", G_CALLBACK (activate_action), r);
|
||||
@@ -307,7 +310,8 @@ constructed (GObject *object)
|
||||
r->priv->state_type = g_variant_type_copy (g_variant_get_type (state));
|
||||
row = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 10);
|
||||
label = gtk_label_new (_("Set State"));
|
||||
gtk_size_group_add_widget (r->priv->sg, label);
|
||||
if (r->priv->sg)
|
||||
gtk_size_group_add_widget (r->priv->sg, label);
|
||||
gtk_box_append (GTK_BOX (row), label);
|
||||
r->priv->state_entry = variant_editor_new (r->priv->state_type, state_changed, r);
|
||||
variant_editor_set_value (r->priv->state_entry, state);
|
||||
@@ -327,7 +331,7 @@ finalize (GObject *object)
|
||||
GtkInspectorActionEditor *r = GTK_INSPECTOR_ACTION_EDITOR (object);
|
||||
|
||||
g_free (r->priv->name);
|
||||
g_object_unref (r->priv->sg);
|
||||
g_clear_object (&r->priv->sg);
|
||||
if (r->priv->state_type)
|
||||
g_variant_type_free (r->priv->state_type);
|
||||
g_signal_handlers_disconnect_by_func (r->priv->group, action_enabled_changed_cb, r);
|
||||
|
61
gtk/inspector/action-holder.c
Normal file
61
gtk/inspector/action-holder.c
Normal file
@@ -0,0 +1,61 @@
|
||||
|
||||
#include "action-holder.h"
|
||||
|
||||
struct _ActionHolder {
|
||||
GObject instance;
|
||||
|
||||
GActionGroup *group;
|
||||
char *name;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (ActionHolder, action_holder, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
action_holder_init (ActionHolder *holder)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
action_holder_finalize (GObject *object)
|
||||
{
|
||||
ActionHolder *holder = ACTION_HOLDER (object);
|
||||
|
||||
g_object_unref (holder->group);
|
||||
g_free (holder->name);
|
||||
|
||||
G_OBJECT_CLASS (action_holder_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
action_holder_class_init (ActionHolderClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = action_holder_finalize;
|
||||
}
|
||||
|
||||
ActionHolder *
|
||||
action_holder_new (GActionGroup *group,
|
||||
const char *name)
|
||||
{
|
||||
ActionHolder *holder;
|
||||
|
||||
holder = g_object_new (ACTION_TYPE_HOLDER, NULL);
|
||||
|
||||
holder->group = g_object_ref (group);
|
||||
holder->name = g_strdup (name);
|
||||
|
||||
return holder;
|
||||
}
|
||||
|
||||
GActionGroup *
|
||||
action_holder_get_group (ActionHolder *holder)
|
||||
{
|
||||
return holder->group;
|
||||
}
|
||||
|
||||
const char *
|
||||
action_holder_get_name (ActionHolder *holder)
|
||||
{
|
||||
return holder->name;
|
||||
}
|
17
gtk/inspector/action-holder.h
Normal file
17
gtk/inspector/action-holder.h
Normal file
@@ -0,0 +1,17 @@
|
||||
|
||||
#ifndef __ACTION_HOLDER_H__
|
||||
#define __ACTION_HOLDER_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define ACTION_TYPE_HOLDER (action_holder_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (ActionHolder, action_holder, ACTION, HOLDER, GObject)
|
||||
|
||||
ActionHolder * action_holder_new (GActionGroup *group,
|
||||
const char *name);
|
||||
|
||||
GActionGroup *action_holder_get_group (ActionHolder *holder);
|
||||
const char *action_holder_get_name (ActionHolder *holder);
|
||||
|
||||
#endif /* __ACTION_HOLDER_H__ */
|
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "actions.h"
|
||||
#include "action-editor.h"
|
||||
#include "action-holder.h"
|
||||
|
||||
#include "gtkapplication.h"
|
||||
#include "gtkapplicationwindow.h"
|
||||
@@ -37,13 +38,11 @@
|
||||
struct _GtkInspectorActionsPrivate
|
||||
{
|
||||
GtkWidget *list;
|
||||
GtkSizeGroup *name;
|
||||
GtkSizeGroup *enabled;
|
||||
GtkSizeGroup *parameter;
|
||||
GtkSizeGroup *state;
|
||||
GtkSizeGroup *activate;
|
||||
GActionGroup *group;
|
||||
GtkWidget *button;
|
||||
|
||||
GActionGroup *group;
|
||||
GListModel *actions;
|
||||
GtkColumnViewColumn *name;
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -61,105 +60,170 @@ gtk_inspector_actions_init (GtkInspectorActions *sl)
|
||||
}
|
||||
|
||||
static void
|
||||
add_action (GtkInspectorActions *sl,
|
||||
GActionGroup *group,
|
||||
const gchar *name)
|
||||
action_added_cb (GActionGroup *group,
|
||||
const gchar *action_name,
|
||||
GtkInspectorActions *sl)
|
||||
{
|
||||
gboolean enabled;
|
||||
const gchar *parameter;
|
||||
GVariant *state;
|
||||
gchar *state_string;
|
||||
GtkWidget *row;
|
||||
GtkWidget *label;
|
||||
GtkWidget *box;
|
||||
char *key = g_strdup (name);
|
||||
GtkWidget *editor;
|
||||
ActionHolder *holder = action_holder_new (group, action_name);
|
||||
g_list_store_append (G_LIST_STORE (sl->priv->actions), holder);
|
||||
g_object_unref (holder);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
setup_name_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
gtk_list_item_set_child (list_item, label);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_name_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gpointer item;
|
||||
GtkWidget *label;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
label = gtk_list_item_get_child (list_item);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (label), action_holder_get_name (ACTION_HOLDER (item)));
|
||||
}
|
||||
|
||||
static void
|
||||
setup_enabled_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.5);
|
||||
gtk_list_item_set_child (list_item, label);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_enabled_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gpointer item;
|
||||
GtkWidget *label;
|
||||
GActionGroup *group;
|
||||
const char *name;
|
||||
gboolean enabled;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
label = gtk_list_item_get_child (list_item);
|
||||
|
||||
group = action_holder_get_group (ACTION_HOLDER (item));
|
||||
name = action_holder_get_name (ACTION_HOLDER (item));
|
||||
enabled = g_action_group_get_action_enabled (group, name);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (label), enabled ? "+" : "-");
|
||||
}
|
||||
|
||||
static void
|
||||
setup_parameter_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.5);
|
||||
gtk_list_item_set_child (list_item, label);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
|
||||
}
|
||||
|
||||
static void
|
||||
bind_parameter_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gpointer item;
|
||||
GtkWidget *label;
|
||||
GActionGroup *group;
|
||||
const char *name;
|
||||
const char *parameter;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
label = gtk_list_item_get_child (list_item);
|
||||
|
||||
group = action_holder_get_group (ACTION_HOLDER (item));
|
||||
name = action_holder_get_name (ACTION_HOLDER (item));
|
||||
parameter = (const gchar *)g_action_group_get_action_parameter_type (group, name);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (label), parameter);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_state_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_widget_set_margin_start (label, 5);
|
||||
gtk_widget_set_margin_end (label, 5);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
gtk_list_item_set_child (list_item, label);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
|
||||
}
|
||||
|
||||
static void
|
||||
bind_state_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gpointer item;
|
||||
GtkWidget *label;
|
||||
GActionGroup *group;
|
||||
const char *name;
|
||||
GVariant *state;
|
||||
char *state_string;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
label = gtk_list_item_get_child (list_item);
|
||||
|
||||
group = action_holder_get_group (ACTION_HOLDER (item));
|
||||
name = action_holder_get_name (ACTION_HOLDER (item));
|
||||
state = g_action_group_get_action_state (group, name);
|
||||
if (state)
|
||||
state_string = g_variant_print (state, FALSE);
|
||||
else
|
||||
state_string = g_strdup ("");
|
||||
|
||||
row = gtk_list_box_row_new ();
|
||||
g_object_set_data_full (G_OBJECT (row), "key", key, g_free);
|
||||
|
||||
gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box);
|
||||
|
||||
label = gtk_label_new (name);
|
||||
gtk_widget_add_css_class (label, "cell");
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
||||
gtk_size_group_add_widget (sl->priv->name, label);
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
|
||||
label = gtk_label_new (enabled ? "+" : "-");
|
||||
gtk_widget_add_css_class (label, "cell");
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
||||
gtk_widget_set_halign (label, GTK_ALIGN_CENTER);
|
||||
gtk_size_group_add_widget (sl->priv->enabled, label);
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
|
||||
g_object_set_data (G_OBJECT (row), "enabled", label);
|
||||
|
||||
label = gtk_label_new (parameter);
|
||||
gtk_widget_add_css_class (label, "cell");
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
||||
gtk_size_group_add_widget (sl->priv->parameter, label);
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
|
||||
label = gtk_label_new (state_string);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
||||
gtk_widget_add_css_class (label, "cell");
|
||||
gtk_size_group_add_widget (sl->priv->state, label);
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
g_object_set_data (G_OBJECT (row), "state", label);
|
||||
|
||||
editor = gtk_inspector_action_editor_new (group, name, sl->priv->activate);
|
||||
gtk_widget_add_css_class (editor, "cell");
|
||||
gtk_box_append (GTK_BOX (box), editor);
|
||||
g_object_set_data (G_OBJECT (row), "editor", editor);
|
||||
|
||||
gtk_list_box_insert (GTK_LIST_BOX (sl->priv->list), row, -1);
|
||||
gtk_label_set_label (GTK_LABEL (label), state_string);
|
||||
|
||||
g_free (state_string);
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
find_row (GtkInspectorActions *sl,
|
||||
const char *action_name)
|
||||
{
|
||||
GtkWidget *row = NULL;
|
||||
GtkWidget *widget;
|
||||
const char *key;
|
||||
|
||||
for (widget = gtk_widget_get_first_child (sl->priv->list);
|
||||
widget;
|
||||
widget = gtk_widget_get_next_sibling (widget))
|
||||
{
|
||||
if (!GTK_IS_LIST_BOX_ROW (widget))
|
||||
continue;
|
||||
|
||||
key = g_object_get_data (G_OBJECT (widget), "key");
|
||||
if (g_str_equal (key, action_name))
|
||||
{
|
||||
row = widget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return row;
|
||||
if (state)
|
||||
g_variant_unref (state);
|
||||
}
|
||||
|
||||
static void
|
||||
action_added_cb (GActionGroup *group,
|
||||
const gchar *action_name,
|
||||
GtkInspectorActions *sl)
|
||||
bind_changes_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
add_action (sl, group, action_name);
|
||||
gpointer item;
|
||||
GActionGroup *group;
|
||||
const char *name;
|
||||
GtkWidget *editor;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
|
||||
group = action_holder_get_group (ACTION_HOLDER (item));
|
||||
name = action_holder_get_name (ACTION_HOLDER (item));
|
||||
|
||||
editor = gtk_inspector_action_editor_new (group, name, NULL);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (editor), "cell");
|
||||
gtk_list_item_set_child (list_item, editor);
|
||||
}
|
||||
|
||||
static void
|
||||
unbind_changes_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
gtk_list_item_set_child (list_item, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -167,21 +231,37 @@ action_removed_cb (GActionGroup *group,
|
||||
const gchar *action_name,
|
||||
GtkInspectorActions *sl)
|
||||
{
|
||||
GtkWidget *row;
|
||||
int i;
|
||||
|
||||
row = find_row (sl, action_name);
|
||||
if (row)
|
||||
gtk_list_box_remove (GTK_LIST_BOX (sl->priv->list), row);
|
||||
for (i = 0; i < g_list_model_get_n_items (sl->priv->actions); i++)
|
||||
{
|
||||
ActionHolder *holder = g_list_model_get_item (sl->priv->actions, i);
|
||||
|
||||
if (group == action_holder_get_group (holder) &&
|
||||
strcmp (action_name, action_holder_get_name (holder)) == 0)
|
||||
g_list_store_remove (G_LIST_STORE (sl->priv->actions), i);
|
||||
|
||||
g_object_unref (holder);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_row_enabled (GtkWidget *row,
|
||||
gboolean enabled)
|
||||
notify_action_changed (GtkInspectorActions *sl,
|
||||
GActionGroup *group,
|
||||
const char *action_name)
|
||||
{
|
||||
GtkWidget *label;
|
||||
int i;
|
||||
|
||||
label = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "enabled"));
|
||||
gtk_label_set_label (GTK_LABEL (label), enabled ? "+" : "-" );
|
||||
for (i = 0; i < g_list_model_get_n_items (sl->priv->actions); i++)
|
||||
{
|
||||
ActionHolder *holder = g_list_model_get_item (sl->priv->actions, i);
|
||||
|
||||
if (group == action_holder_get_group (holder) &&
|
||||
strcmp (action_name, action_holder_get_name (holder)) == 0)
|
||||
g_list_model_items_changed (sl->priv->actions, i, 1, 1);
|
||||
|
||||
g_object_unref (holder);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -190,26 +270,7 @@ action_enabled_changed_cb (GActionGroup *group,
|
||||
gboolean enabled,
|
||||
GtkInspectorActions *sl)
|
||||
{
|
||||
GtkWidget *row;
|
||||
|
||||
row = find_row (sl, action_name);
|
||||
set_row_enabled (row, enabled);
|
||||
}
|
||||
|
||||
static void
|
||||
set_row_state (GtkWidget *row,
|
||||
GVariant *state)
|
||||
{
|
||||
gchar *state_string;
|
||||
GtkWidget *label;
|
||||
|
||||
if (state)
|
||||
state_string = g_variant_print (state, FALSE);
|
||||
else
|
||||
state_string = g_strdup ("");
|
||||
label = GTK_WIDGET (g_object_get_data (G_OBJECT (row), "state"));
|
||||
gtk_label_set_label (GTK_LABEL (label), state_string);
|
||||
g_free (state_string);
|
||||
notify_action_changed (sl, group, action_name);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -218,35 +279,14 @@ action_state_changed_cb (GActionGroup *group,
|
||||
GVariant *state,
|
||||
GtkInspectorActions *sl)
|
||||
{
|
||||
GtkWidget *row;
|
||||
|
||||
row = find_row (sl, action_name);
|
||||
set_row_state (row, state);
|
||||
notify_action_changed (sl, group, action_name);
|
||||
}
|
||||
|
||||
static void
|
||||
refresh_all (GtkInspectorActions *sl)
|
||||
{
|
||||
GtkWidget *widget;
|
||||
|
||||
for (widget = gtk_widget_get_first_child (sl->priv->list);
|
||||
widget;
|
||||
widget = gtk_widget_get_next_sibling (widget))
|
||||
{
|
||||
const char *name = g_object_get_data (G_OBJECT (widget), "key");
|
||||
gboolean enabled;
|
||||
GVariant *state;
|
||||
GtkInspectorActionEditor *r;
|
||||
|
||||
enabled = g_action_group_get_action_enabled (sl->priv->group, name);
|
||||
state = g_action_group_get_action_state (sl->priv->group, name);
|
||||
|
||||
set_row_enabled (widget, enabled);
|
||||
set_row_state (widget, state);
|
||||
|
||||
r = (GtkInspectorActionEditor*)g_object_get_data (G_OBJECT (widget), "editor");
|
||||
gtk_inspector_action_editor_update (r, enabled, state);
|
||||
}
|
||||
guint n = g_list_model_get_n_items (sl->priv->actions);
|
||||
g_list_model_items_changed (sl->priv->actions, 0, n, n);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -283,7 +323,7 @@ add_group (GtkInspectorActions *sl,
|
||||
|
||||
names = g_action_group_list_actions (group);
|
||||
for (i = 0; names[i]; i++)
|
||||
add_action (sl, group, names[i]);
|
||||
action_added_cb (group, names[i], sl);
|
||||
g_strfreev (names);
|
||||
|
||||
g_set_object (&sl->priv->group, group);
|
||||
@@ -305,7 +345,6 @@ gtk_inspector_actions_set_object (GtkInspectorActions *sl,
|
||||
{
|
||||
GtkWidget *stack;
|
||||
GtkStackPage *page;
|
||||
GtkWidget *child;
|
||||
|
||||
stack = gtk_widget_get_parent (GTK_WIDGET (sl));
|
||||
page = gtk_stack_get_page (GTK_STACK (stack), GTK_WIDGET (sl));
|
||||
@@ -315,8 +354,7 @@ gtk_inspector_actions_set_object (GtkInspectorActions *sl,
|
||||
if (sl->priv->group)
|
||||
remove_group (sl, page, sl->priv->group);
|
||||
|
||||
while ((child = gtk_widget_get_first_child (sl->priv->list)))
|
||||
gtk_list_box_remove (GTK_LIST_BOX (sl->priv->list), child);
|
||||
g_list_store_remove_all (G_LIST_STORE (sl->priv->actions));
|
||||
|
||||
if (GTK_IS_APPLICATION (object))
|
||||
add_group (sl, page, G_ACTION_GROUP (object));
|
||||
@@ -328,6 +366,8 @@ gtk_inspector_actions_set_object (GtkInspectorActions *sl,
|
||||
if (muxer)
|
||||
add_group (sl, page, G_ACTION_GROUP (muxer));
|
||||
}
|
||||
|
||||
gtk_column_view_sort_by_column (GTK_COLUMN_VIEW (sl->priv->list), sl->priv->name, GTK_SORT_ASCENDING);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -370,13 +410,48 @@ set_property (GObject *object,
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
holder_name (gpointer item)
|
||||
{
|
||||
return g_strdup (action_holder_get_name (ACTION_HOLDER (item)));
|
||||
}
|
||||
|
||||
static void
|
||||
constructed (GObject *object)
|
||||
{
|
||||
GtkInspectorActions *sl = GTK_INSPECTOR_ACTIONS (object);
|
||||
GtkSorter *sorter;
|
||||
GListModel *sorted;
|
||||
GListModel *model;
|
||||
|
||||
g_signal_connect_swapped (sl->priv->button, "clicked",
|
||||
G_CALLBACK (refresh_all), sl);
|
||||
|
||||
sorter = gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING,
|
||||
NULL,
|
||||
0, NULL,
|
||||
(GCallback)holder_name,
|
||||
NULL, NULL));
|
||||
gtk_column_view_column_set_sorter (sl->priv->name, sorter);
|
||||
g_object_unref (sorter);
|
||||
|
||||
sl->priv->actions = G_LIST_MODEL (g_list_store_new (ACTION_TYPE_HOLDER));
|
||||
sorted = G_LIST_MODEL (gtk_sort_list_model_new (sl->priv->actions,
|
||||
gtk_column_view_get_sorter (GTK_COLUMN_VIEW (sl->priv->list))));
|
||||
model = G_LIST_MODEL (gtk_no_selection_new (sorted));
|
||||
gtk_column_view_set_model (GTK_COLUMN_VIEW (sl->priv->list), model);
|
||||
g_object_unref (sorted);
|
||||
g_object_unref (model);
|
||||
}
|
||||
|
||||
static void
|
||||
finalize (GObject *object)
|
||||
{
|
||||
GtkInspectorActions *sl = GTK_INSPECTOR_ACTIONS (object);
|
||||
|
||||
g_object_unref (sl->priv->actions);
|
||||
|
||||
G_OBJECT_CLASS (gtk_inspector_actions_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -385,6 +460,7 @@ gtk_inspector_actions_class_init (GtkInspectorActionsClass *klass)
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
||||
|
||||
object_class->finalize = finalize;
|
||||
object_class->get_property = get_property;
|
||||
object_class->set_property = set_property;
|
||||
object_class->constructed = constructed;
|
||||
@@ -396,10 +472,16 @@ gtk_inspector_actions_class_init (GtkInspectorActionsClass *klass)
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/actions.ui");
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, list);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, name);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, enabled);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, parameter);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, state);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorActions, activate);
|
||||
gtk_widget_class_bind_template_callback (widget_class, setup_name_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, bind_name_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, setup_enabled_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, bind_enabled_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, setup_parameter_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, bind_parameter_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, setup_state_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, bind_state_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, bind_changes_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, unbind_changes_cb);
|
||||
}
|
||||
|
||||
// vim: set et sw=2 ts=2:
|
||||
|
@@ -1,98 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface domain="gtk40">
|
||||
<object class="GtkListStore" id="model">
|
||||
<columns>
|
||||
<column type="gchararray"/>
|
||||
<column type="gchararray"/>
|
||||
<column type="gboolean"/>
|
||||
<column type="gchararray"/>
|
||||
<column type="gchararray"/>
|
||||
<column type="gpointer"/>
|
||||
</columns>
|
||||
</object>
|
||||
<template class="GtkInspectorActions" parent="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<style>
|
||||
<class name="view"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<style>
|
||||
<class name="header"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkLabel" id="name_heading">
|
||||
<property name="label" translatable="yes">Name</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="enabled_heading">
|
||||
<property name="label" translatable="yes">Enabled</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="parameter_heading">
|
||||
<property name="label" translatable="yes">Parameter Type</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="state_heading">
|
||||
<property name="label" translatable="yes">State</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="changes_heading">
|
||||
<property name="label" translatable="yes"></property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="list">
|
||||
<object class="GtkColumnView" id="list">
|
||||
<style>
|
||||
<class name="list"/>
|
||||
</style>
|
||||
<property name="selection-mode">none</property>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="name">
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_name_cb"/>
|
||||
<signal name="bind" handler="bind_name_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="enabled">
|
||||
<property name="title" translatable="yes">Enabled</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_enabled_cb"/>
|
||||
<signal name="bind" handler="bind_enabled_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="parameter">
|
||||
<property name="title" translatable="yes">Parameter Type</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_parameter_cb"/>
|
||||
<signal name="bind" handler="bind_parameter_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="state">
|
||||
<property name="title" translatable="yes">State</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_state_cb"/>
|
||||
<signal name="bind" handler="bind_state_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="changes">
|
||||
<property name="title"></property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="bind" handler="bind_changes_cb"/>
|
||||
<signal name="unbind" handler="unbind_changes_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
<object class="GtkSizeGroup" id="name">
|
||||
<property name="mode">horizontal</property>
|
||||
<widgets>
|
||||
<widget name="name_heading"/>
|
||||
</widgets>
|
||||
</object>
|
||||
<object class="GtkSizeGroup" id="enabled">
|
||||
<property name="mode">horizontal</property>
|
||||
<widgets>
|
||||
<widget name="enabled_heading"/>
|
||||
</widgets>
|
||||
</object>
|
||||
<object class="GtkSizeGroup" id="parameter">
|
||||
<property name="mode">horizontal</property>
|
||||
<widgets>
|
||||
<widget name="parameter_heading"/>
|
||||
</widgets>
|
||||
</object>
|
||||
<object class="GtkSizeGroup" id="state">
|
||||
<property name="mode">horizontal</property>
|
||||
<widgets>
|
||||
<widget name="state_heading"/>
|
||||
</widgets>
|
||||
</object>
|
||||
<object class="GtkSizeGroup" id="activate">
|
||||
<property name="mode">horizontal</property>
|
||||
</object>
|
||||
</interface>
|
||||
|
@@ -1,21 +1,29 @@
|
||||
/* some style for the inspector */
|
||||
|
||||
.header {
|
||||
background: lightgray;
|
||||
border: 1px solid gray;
|
||||
.list header {
|
||||
background: white;
|
||||
border: 1px solid lightgray;
|
||||
}
|
||||
|
||||
.header>* {
|
||||
.list header button {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.list header button.dnd {
|
||||
background: gray;
|
||||
}
|
||||
|
||||
.list header>* {
|
||||
padding: 2px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.header sort_indicator {
|
||||
.list header sort_indicator {
|
||||
min-width: 16px;
|
||||
}
|
||||
|
||||
.header>*:not(:last-child) {
|
||||
border-right: 1px solid gray;
|
||||
.list header>*:not(:last-child) {
|
||||
border-right: 1px solid lightgray;
|
||||
}
|
||||
|
||||
.list .cell {
|
||||
|
@@ -38,4 +38,7 @@ inspector_sources = files(
|
||||
'updatesoverlay.c',
|
||||
'visual.c',
|
||||
'window.c',
|
||||
'prop-holder.c',
|
||||
'resource-holder.c',
|
||||
'action-holder.c'
|
||||
)
|
||||
|
@@ -336,6 +336,29 @@ object_tree_tree_view_get_children (GObject *object)
|
||||
return G_LIST_MODEL (result);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
object_tree_column_view_get_children (GObject *object)
|
||||
{
|
||||
GtkColumnView *view = GTK_COLUMN_VIEW (object);
|
||||
GListStore *result_list;
|
||||
GtkFlattenListModel *result;
|
||||
GListModel *columns, *sublist;
|
||||
|
||||
result_list = g_list_store_new (G_TYPE_LIST_MODEL);
|
||||
|
||||
columns = gtk_column_view_get_columns (view);
|
||||
g_list_store_append (result_list, columns);
|
||||
|
||||
sublist = object_tree_widget_get_children (object);
|
||||
g_list_store_append (result_list, sublist);
|
||||
g_object_unref (sublist);
|
||||
|
||||
result = gtk_flatten_list_model_new (G_TYPE_OBJECT, G_LIST_MODEL (result_list));
|
||||
g_object_unref (result_list);
|
||||
|
||||
return G_LIST_MODEL (result);
|
||||
}
|
||||
|
||||
static GListModel *
|
||||
object_tree_icon_view_get_children (GObject *object)
|
||||
{
|
||||
@@ -499,6 +522,11 @@ static const ObjectTreeClassFuncs object_tree_class_funcs[] = {
|
||||
object_tree_widget_get_parent,
|
||||
object_tree_tree_view_get_children
|
||||
},
|
||||
{
|
||||
gtk_column_view_get_type,
|
||||
object_tree_widget_get_parent,
|
||||
object_tree_column_view_get_children
|
||||
},
|
||||
{
|
||||
gtk_combo_box_get_type,
|
||||
object_tree_widget_get_parent,
|
||||
|
@@ -45,6 +45,7 @@
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title">Type</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_type_cb"/>
|
||||
@@ -57,6 +58,7 @@
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title">Name</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_name_cb"/>
|
||||
@@ -68,6 +70,7 @@
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title">Label</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_label_cb"/>
|
||||
|
145
gtk/inspector/prop-holder.c
Normal file
145
gtk/inspector/prop-holder.c
Normal file
@@ -0,0 +1,145 @@
|
||||
#include "prop-holder.h"
|
||||
|
||||
enum {
|
||||
PROP_OBJECT = 1,
|
||||
PROP_PSPEC,
|
||||
PROP_NAME,
|
||||
NUM_PROPERTIES
|
||||
};
|
||||
|
||||
static GParamSpec *properties[NUM_PROPERTIES];
|
||||
|
||||
struct _PropHolder {
|
||||
GObject instance;
|
||||
|
||||
GObject *object;
|
||||
GParamSpec *pspec;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (PropHolder, prop_holder, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
prop_holder_init (PropHolder *holder)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
prop_holder_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PropHolder *holder = PROP_HOLDER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_OBJECT:
|
||||
holder->object = g_value_dup_object (value);
|
||||
break;
|
||||
|
||||
case PROP_PSPEC:
|
||||
holder->pspec = g_value_get_param (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
prop_holder_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PropHolder *holder = PROP_HOLDER (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_OBJECT:
|
||||
g_value_set_object (value, holder->object);
|
||||
break;
|
||||
|
||||
case PROP_PSPEC:
|
||||
g_value_set_param (value, holder->pspec);
|
||||
break;
|
||||
|
||||
case PROP_NAME:
|
||||
g_value_set_string (value, holder->pspec->name);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
prop_holder_finalize (GObject *object)
|
||||
{
|
||||
PropHolder *holder = PROP_HOLDER (object);
|
||||
|
||||
g_object_unref (holder->object);
|
||||
|
||||
G_OBJECT_CLASS (prop_holder_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
prop_holder_class_init (PropHolderClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = prop_holder_finalize;
|
||||
object_class->set_property = prop_holder_set_property;
|
||||
object_class->get_property = prop_holder_get_property;
|
||||
|
||||
properties[PROP_OBJECT] =
|
||||
g_param_spec_object ("object", "object", "object",
|
||||
G_TYPE_OBJECT,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_PSPEC] =
|
||||
g_param_spec_param ("pspec", "pspec", "pspec",
|
||||
G_TYPE_PARAM,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
properties[PROP_NAME] =
|
||||
g_param_spec_string ("name", "name", "name",
|
||||
NULL,
|
||||
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
|
||||
|
||||
g_object_class_install_properties (object_class, NUM_PROPERTIES, properties);
|
||||
}
|
||||
|
||||
PropHolder *
|
||||
prop_holder_new (GObject *object,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
PropHolder *holder;
|
||||
|
||||
holder = g_object_new (PROP_TYPE_HOLDER,
|
||||
"object", object,
|
||||
"pspec", pspec,
|
||||
NULL);
|
||||
|
||||
return holder;
|
||||
}
|
||||
|
||||
GObject *
|
||||
prop_holder_get_object (PropHolder *holder)
|
||||
{
|
||||
return holder->object;
|
||||
}
|
||||
|
||||
GParamSpec *
|
||||
prop_holder_get_pspec (PropHolder *holder)
|
||||
{
|
||||
return holder->pspec;
|
||||
}
|
||||
|
||||
const char *
|
||||
prop_holder_get_name (PropHolder *holder)
|
||||
{
|
||||
return holder->pspec->name;
|
||||
}
|
17
gtk/inspector/prop-holder.h
Normal file
17
gtk/inspector/prop-holder.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#ifndef __PROP_HOLDER_H__
|
||||
#define __PROP_HOLDER_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define PROP_TYPE_HOLDER (prop_holder_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (PropHolder, prop_holder, PROP, HOLDER, GObject)
|
||||
|
||||
PropHolder * prop_holder_new (GObject *object,
|
||||
GParamSpec *pspeC);
|
||||
|
||||
GObject *prop_holder_get_object (PropHolder *holder);
|
||||
GParamSpec *prop_holder_get_pspec (PropHolder *holder);
|
||||
const char *prop_holder_get_name (PropHolder *holder);
|
||||
|
||||
#endif /* __PROP_HOLDER_H__ */
|
@@ -44,6 +44,7 @@
|
||||
#include "gtkroot.h"
|
||||
#include "gtkgestureclick.h"
|
||||
#include "gtkstylecontext.h"
|
||||
#include "prop-holder.h"
|
||||
|
||||
enum
|
||||
{
|
||||
@@ -52,11 +53,6 @@ enum
|
||||
PROP_SEARCH_ENTRY
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
COLUMN_NAME,
|
||||
COLUMN_ORIGIN
|
||||
} SortColumn;
|
||||
|
||||
struct _GtkInspectorPropListPrivate
|
||||
{
|
||||
GObject *object;
|
||||
@@ -64,17 +60,11 @@ struct _GtkInspectorPropListPrivate
|
||||
GtkInspectorObjectTree *object_tree;
|
||||
GtkWidget *search_entry;
|
||||
GtkWidget *search_stack;
|
||||
GtkWidget *list2;
|
||||
GtkWidget *name_sort_indicator;
|
||||
GtkWidget *origin_sort_indicator;
|
||||
GtkWidget *name_heading;
|
||||
GtkWidget *origin_heading;
|
||||
SortColumn sort_column;
|
||||
GtkSortType sort_direction;
|
||||
GtkSizeGroup *names;
|
||||
GtkSizeGroup *types;
|
||||
GtkSizeGroup *values;
|
||||
GtkSizeGroup *origins;
|
||||
GtkWidget *list;
|
||||
GtkFilter *filter;
|
||||
GtkColumnViewColumn *name;
|
||||
GtkColumnViewColumn *type;
|
||||
GtkColumnViewColumn *origin;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorPropList, gtk_inspector_prop_list, GTK_TYPE_BOX)
|
||||
@@ -94,103 +84,66 @@ show_search_entry (GtkInspectorPropList *pl)
|
||||
pl->priv->search_entry);
|
||||
}
|
||||
|
||||
static void
|
||||
apply_sort (GtkInspectorPropList *pl,
|
||||
SortColumn column,
|
||||
GtkSortType direction)
|
||||
static char *
|
||||
holder_prop (gpointer item)
|
||||
{
|
||||
const char *icon_name;
|
||||
GParamSpec *prop = prop_holder_get_pspec (PROP_HOLDER (item));
|
||||
|
||||
icon_name = direction == GTK_SORT_ASCENDING ? "pan-down-symbolic"
|
||||
: "pan-up-symbolic";
|
||||
|
||||
if (column == COLUMN_NAME)
|
||||
{
|
||||
gtk_image_clear (GTK_IMAGE (pl->priv->origin_sort_indicator));
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (pl->priv->name_sort_indicator),
|
||||
icon_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_image_clear (GTK_IMAGE (pl->priv->name_sort_indicator));
|
||||
gtk_image_set_from_icon_name (GTK_IMAGE (pl->priv->origin_sort_indicator),
|
||||
icon_name);
|
||||
}
|
||||
|
||||
pl->priv->sort_column = column;
|
||||
pl->priv->sort_direction = direction;
|
||||
|
||||
gtk_list_box_invalidate_sort (GTK_LIST_BOX (pl->priv->list2));
|
||||
return g_strdup (prop->name);
|
||||
}
|
||||
|
||||
static void
|
||||
sort_changed (GtkGestureClick *gesture,
|
||||
int n_press,
|
||||
double x,
|
||||
double y,
|
||||
GtkInspectorPropList *pl)
|
||||
static char *
|
||||
holder_type (gpointer item)
|
||||
{
|
||||
SortColumn column;
|
||||
GtkSortType direction;
|
||||
GtkWidget *widget;
|
||||
GParamSpec *prop = prop_holder_get_pspec (PROP_HOLDER (item));
|
||||
|
||||
widget = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
|
||||
if (widget == pl->priv->name_heading)
|
||||
column = COLUMN_NAME;
|
||||
else
|
||||
column = COLUMN_ORIGIN;
|
||||
|
||||
if (pl->priv->sort_column == column &&
|
||||
pl->priv->sort_direction == GTK_SORT_ASCENDING)
|
||||
direction = GTK_SORT_DESCENDING;
|
||||
else
|
||||
direction = GTK_SORT_ASCENDING;
|
||||
|
||||
apply_sort (pl, column, direction);
|
||||
return g_strdup (g_type_name (prop->value_type));
|
||||
}
|
||||
|
||||
static const char *
|
||||
row_get_column (GtkListBoxRow *row,
|
||||
SortColumn column)
|
||||
static char *
|
||||
holder_origin (gpointer item)
|
||||
{
|
||||
GParamSpec *prop = g_object_get_data (G_OBJECT (row), "pspec");
|
||||
GParamSpec *prop = prop_holder_get_pspec (PROP_HOLDER (item));
|
||||
|
||||
if (column == COLUMN_NAME)
|
||||
return prop->name;
|
||||
else
|
||||
return g_type_name (prop->owner_type);
|
||||
}
|
||||
|
||||
static int
|
||||
sort_func (GtkListBoxRow *row1,
|
||||
GtkListBoxRow *row2,
|
||||
gpointer user_data)
|
||||
{
|
||||
GtkInspectorPropList *pl = user_data;
|
||||
const char *s1 = row_get_column (row1, pl->priv->sort_column);
|
||||
const char *s2 = row_get_column (row2, pl->priv->sort_column);
|
||||
int ret = strcmp (s1, s2);
|
||||
|
||||
return pl->priv->sort_direction == GTK_SORT_ASCENDING ? ret : -ret;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
filter_func (GtkListBoxRow *row,
|
||||
gpointer data)
|
||||
{
|
||||
GtkInspectorPropList *pl = data;
|
||||
GParamSpec *pspec = (GParamSpec *)g_object_get_data (G_OBJECT (row), "pspec");
|
||||
const char *text = gtk_editable_get_text (GTK_EDITABLE (pl->priv->search_entry));
|
||||
|
||||
return g_str_has_prefix (pspec->name, text);
|
||||
return g_strdup (g_type_name (prop->owner_type));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_inspector_prop_list_init (GtkInspectorPropList *pl)
|
||||
{
|
||||
GtkSorter *sorter;
|
||||
|
||||
pl->priv = gtk_inspector_prop_list_get_instance_private (pl);
|
||||
gtk_widget_init_template (GTK_WIDGET (pl));
|
||||
apply_sort (pl, COLUMN_NAME, GTK_SORT_ASCENDING);
|
||||
pl->priv->filter = gtk_string_filter_new ();
|
||||
gtk_string_filter_set_match_mode (GTK_STRING_FILTER (pl->priv->filter), GTK_STRING_FILTER_MATCH_MODE_PREFIX);
|
||||
|
||||
sorter = gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
|
||||
0, NULL,
|
||||
(GCallback)holder_prop,
|
||||
NULL, NULL));
|
||||
|
||||
gtk_string_filter_set_expression (GTK_STRING_FILTER (pl->priv->filter),
|
||||
gtk_string_sorter_get_expression (GTK_STRING_SORTER (sorter)));
|
||||
|
||||
gtk_column_view_column_set_sorter (pl->priv->name, sorter);
|
||||
g_object_unref (sorter);
|
||||
|
||||
sorter = gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
|
||||
0, NULL,
|
||||
(GCallback)holder_type,
|
||||
NULL, NULL));
|
||||
|
||||
gtk_column_view_column_set_sorter (pl->priv->type, sorter);
|
||||
g_object_unref (sorter);
|
||||
|
||||
sorter = gtk_string_sorter_new (gtk_cclosure_expression_new (G_TYPE_STRING, NULL,
|
||||
0, NULL,
|
||||
(GCallback)holder_origin,
|
||||
NULL, NULL));
|
||||
|
||||
gtk_column_view_column_set_sorter (pl->priv->origin, sorter);
|
||||
g_object_unref (sorter);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -253,6 +206,7 @@ show_object (GtkInspectorPropEditor *editor,
|
||||
gtk_inspector_object_tree_activate_object (pl->priv->object_tree, object);
|
||||
}
|
||||
|
||||
|
||||
static void cleanup_object (GtkInspectorPropList *pl);
|
||||
|
||||
static void
|
||||
@@ -265,6 +219,16 @@ finalize (GObject *object)
|
||||
G_OBJECT_CLASS (gtk_inspector_prop_list_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
update_filter (GtkInspectorPropList *pl,
|
||||
GtkSearchEntry *entry)
|
||||
{
|
||||
const char *text;
|
||||
|
||||
text = gtk_editable_get_text (GTK_EDITABLE (entry));
|
||||
gtk_string_filter_set_search (GTK_STRING_FILTER (pl->priv->filter), text);
|
||||
}
|
||||
|
||||
static void
|
||||
constructed (GObject *object)
|
||||
{
|
||||
@@ -278,10 +242,7 @@ constructed (GObject *object)
|
||||
g_signal_connect_swapped (pl->priv->search_entry, "search-started",
|
||||
G_CALLBACK (show_search_entry), pl);
|
||||
g_signal_connect_swapped (pl->priv->search_entry, "search-changed",
|
||||
G_CALLBACK (gtk_list_box_invalidate_filter), pl->priv->list2);
|
||||
|
||||
gtk_list_box_set_filter_func (GTK_LIST_BOX (pl->priv->list2), filter_func, pl, NULL);
|
||||
gtk_list_box_set_sort_func (GTK_LIST_BOX (pl->priv->list2), sort_func, pl, NULL);
|
||||
G_CALLBACK (update_filter), pl);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -298,7 +259,7 @@ update_key_capture (GtkInspectorPropList *pl)
|
||||
focus = gtk_root_get_focus (GTK_ROOT (toplevel));
|
||||
|
||||
if (GTK_IS_EDITABLE (focus) &&
|
||||
gtk_widget_is_ancestor (focus, pl->priv->list2))
|
||||
gtk_widget_is_ancestor (focus, pl->priv->list))
|
||||
capture_widget = NULL;
|
||||
else
|
||||
capture_widget = toplevel;
|
||||
@@ -344,6 +305,120 @@ unroot (GtkWidget *widget)
|
||||
GTK_WIDGET_CLASS (gtk_inspector_prop_list_parent_class)->unroot (widget);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_name_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
gtk_list_item_set_child (list_item, label);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
|
||||
}
|
||||
|
||||
static void
|
||||
bind_name_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GObject *item;
|
||||
GtkWidget *label;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
label = gtk_list_item_get_child (list_item);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (label), prop_holder_get_name (PROP_HOLDER (item)));
|
||||
}
|
||||
|
||||
static void
|
||||
setup_type_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
gtk_list_item_set_child (list_item, label);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
|
||||
}
|
||||
|
||||
static void
|
||||
bind_type_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GObject *item;
|
||||
GtkWidget *label;
|
||||
GParamSpec *prop;
|
||||
const char *type;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
label = gtk_list_item_get_child (list_item);
|
||||
|
||||
prop = prop_holder_get_pspec (PROP_HOLDER (item));
|
||||
type = g_type_name (G_PARAM_SPEC_VALUE_TYPE (prop));
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (label), type);
|
||||
}
|
||||
|
||||
static void
|
||||
setup_origin_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GtkWidget *label;
|
||||
|
||||
label = gtk_label_new (NULL);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0.0);
|
||||
gtk_list_item_set_child (list_item, label);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (label), "cell");
|
||||
}
|
||||
|
||||
static void
|
||||
bind_origin_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item)
|
||||
{
|
||||
GObject *item;
|
||||
GtkWidget *label;
|
||||
GParamSpec *prop;
|
||||
const char *origin;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
label = gtk_list_item_get_child (list_item);
|
||||
|
||||
prop = prop_holder_get_pspec (PROP_HOLDER (item));
|
||||
origin = g_type_name (prop->owner_type);
|
||||
|
||||
gtk_label_set_label (GTK_LABEL (label), origin);
|
||||
}
|
||||
|
||||
static void
|
||||
bind_value_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item,
|
||||
gpointer data)
|
||||
{
|
||||
GObject *item;
|
||||
GtkWidget *widget;
|
||||
GObject *object;
|
||||
const char *name;
|
||||
|
||||
item = gtk_list_item_get_item (list_item);
|
||||
|
||||
object = prop_holder_get_object (PROP_HOLDER (item));
|
||||
name = prop_holder_get_name (PROP_HOLDER (item));
|
||||
|
||||
widget = gtk_inspector_prop_editor_new (object, name, NULL);
|
||||
g_signal_connect (widget, "show-object", G_CALLBACK (show_object), data);
|
||||
gtk_list_item_set_child (list_item, widget);
|
||||
gtk_style_context_add_class (gtk_widget_get_style_context (widget), "cell");
|
||||
}
|
||||
|
||||
static void
|
||||
unbind_value_cb (GtkSignalListItemFactory *factory,
|
||||
GtkListItem *list_item,
|
||||
gpointer data)
|
||||
{
|
||||
gtk_list_item_set_child (list_item, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_inspector_prop_list_class_init (GtkInspectorPropListClass *klass)
|
||||
{
|
||||
@@ -369,16 +444,18 @@ gtk_inspector_prop_list_class_init (GtkInspectorPropListClass *klass)
|
||||
GTK_TYPE_WIDGET, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
||||
|
||||
gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/prop-list.ui");
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, list2);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, names);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, types);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, values);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, origins);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, name_heading);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, name_sort_indicator);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, origin_heading);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, origin_sort_indicator);
|
||||
gtk_widget_class_bind_template_callback (widget_class, sort_changed);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, list);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, name);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, type);
|
||||
gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorPropList, origin);
|
||||
gtk_widget_class_bind_template_callback (widget_class, setup_name_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, bind_name_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, setup_type_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, bind_type_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, setup_origin_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, bind_origin_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, bind_value_cb);
|
||||
gtk_widget_class_bind_template_callback (widget_class, unbind_value_cb);
|
||||
}
|
||||
|
||||
/* Like g_strdup_value_contents, but keeps the type name separate */
|
||||
@@ -483,88 +560,6 @@ strdup_value_contents (const GValue *value,
|
||||
}
|
||||
}
|
||||
|
||||
static GtkWidget *
|
||||
gtk_inspector_prop_list_create_row (GtkInspectorPropList *pl,
|
||||
GParamSpec *prop)
|
||||
{
|
||||
GValue gvalue = {0};
|
||||
gchar *value;
|
||||
gchar *type;
|
||||
gchar *attribute = NULL;
|
||||
gboolean writable;
|
||||
GtkWidget *row;
|
||||
GtkWidget *box;
|
||||
GtkWidget *label;
|
||||
GtkWidget *widget;
|
||||
|
||||
g_value_init (&gvalue, prop->value_type);
|
||||
g_object_get_property (pl->priv->object, prop->name, &gvalue);
|
||||
|
||||
strdup_value_contents (&gvalue, &value, &type);
|
||||
|
||||
if (GTK_IS_CELL_RENDERER (pl->priv->object))
|
||||
{
|
||||
gpointer *layout;
|
||||
GtkCellArea *area;
|
||||
gint column = -1;
|
||||
|
||||
area = NULL;
|
||||
layout = g_object_get_data (pl->priv->object, "gtk-inspector-cell-layout");
|
||||
if (layout)
|
||||
area = gtk_cell_layout_get_area (GTK_CELL_LAYOUT (layout));
|
||||
if (area)
|
||||
column = gtk_cell_area_attribute_get_column (area,
|
||||
GTK_CELL_RENDERER (pl->priv->object),
|
||||
prop->name);
|
||||
|
||||
if (column != -1)
|
||||
attribute = g_strdup_printf ("%d", column);
|
||||
}
|
||||
|
||||
writable = ((prop->flags & G_PARAM_WRITABLE) != 0) &&
|
||||
((prop->flags & G_PARAM_CONSTRUCT_ONLY) == 0);
|
||||
|
||||
row = gtk_list_box_row_new ();
|
||||
gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
|
||||
g_object_set_data (G_OBJECT (row), "pspec", prop);
|
||||
|
||||
box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), box);
|
||||
|
||||
label = gtk_label_new (prop->name);
|
||||
gtk_widget_add_css_class (label, "cell");
|
||||
gtk_widget_set_sensitive (label, writable);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
||||
gtk_size_group_add_widget (pl->priv->names, label);
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
|
||||
label = gtk_label_new (type ? type : "");
|
||||
gtk_widget_add_css_class (label, "cell");
|
||||
gtk_widget_set_sensitive (label, writable);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
||||
gtk_size_group_add_widget (pl->priv->types, label);
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
|
||||
label = gtk_label_new (g_type_name (prop->owner_type));
|
||||
gtk_widget_add_css_class (label, "cell");
|
||||
gtk_widget_set_sensitive (label, writable);
|
||||
gtk_label_set_xalign (GTK_LABEL (label), 0);
|
||||
gtk_size_group_add_widget (pl->priv->origins, label);
|
||||
gtk_box_append (GTK_BOX (box), label);
|
||||
|
||||
widget = gtk_inspector_prop_editor_new (pl->priv->object, prop->name, pl->priv->values);
|
||||
gtk_widget_add_css_class (widget, "cell");
|
||||
gtk_box_append (GTK_BOX (box), widget);
|
||||
g_signal_connect (widget, "show-object", G_CALLBACK (show_object), pl);
|
||||
|
||||
g_free (value);
|
||||
g_free (type);
|
||||
g_free (attribute);
|
||||
g_value_unset (&gvalue);
|
||||
|
||||
return row;
|
||||
}
|
||||
|
||||
static void
|
||||
cleanup_object (GtkInspectorPropList *pl)
|
||||
{
|
||||
@@ -583,7 +578,10 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
|
||||
GParamSpec **props;
|
||||
guint num_properties;
|
||||
guint i;
|
||||
GtkWidget *w;
|
||||
GListStore *store;
|
||||
GListModel *list;
|
||||
GListModel *filtered;
|
||||
GtkSortListModel *sorted;
|
||||
|
||||
if (!object)
|
||||
return FALSE;
|
||||
@@ -600,19 +598,19 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
|
||||
|
||||
pl->priv->object = object;
|
||||
|
||||
while ((w = gtk_widget_get_first_child (pl->priv->list2)))
|
||||
gtk_list_box_remove (GTK_LIST_BOX (pl->priv->list2), w);
|
||||
store = g_list_store_new (PROP_TYPE_HOLDER);
|
||||
|
||||
for (i = 0; i < num_properties; i++)
|
||||
{
|
||||
GParamSpec *prop = props[i];
|
||||
GtkWidget *row;
|
||||
PropHolder *holder;
|
||||
|
||||
if (! (prop->flags & G_PARAM_READABLE))
|
||||
continue;
|
||||
|
||||
row = gtk_inspector_prop_list_create_row (pl, prop);
|
||||
gtk_list_box_insert (GTK_LIST_BOX (pl->priv->list2), row, -1);
|
||||
holder = prop_holder_new (object, prop);
|
||||
g_list_store_append (store, holder);
|
||||
g_object_unref (holder);
|
||||
}
|
||||
|
||||
g_free (props);
|
||||
@@ -620,8 +618,21 @@ gtk_inspector_prop_list_set_object (GtkInspectorPropList *pl,
|
||||
if (GTK_IS_WIDGET (object))
|
||||
g_signal_connect_object (object, "destroy", G_CALLBACK (cleanup_object), pl, G_CONNECT_SWAPPED);
|
||||
|
||||
filtered = G_LIST_MODEL (gtk_filter_list_model_new (G_LIST_MODEL (store), pl->priv->filter));
|
||||
sorted = gtk_sort_list_model_new (filtered, NULL);
|
||||
list = G_LIST_MODEL (gtk_no_selection_new (G_LIST_MODEL (sorted)));
|
||||
|
||||
gtk_column_view_set_model (GTK_COLUMN_VIEW (pl->priv->list), list);
|
||||
gtk_sort_list_model_set_sorter (sorted, gtk_column_view_get_sorter (GTK_COLUMN_VIEW (pl->priv->list)));
|
||||
gtk_column_view_sort_by_column (GTK_COLUMN_VIEW (pl->priv->list), pl->priv->name, GTK_SORT_ASCENDING);
|
||||
|
||||
gtk_widget_show (GTK_WIDGET (pl));
|
||||
|
||||
g_object_unref (list);
|
||||
g_object_unref (sorted);
|
||||
g_object_unref (filtered);
|
||||
g_object_unref (store);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@@ -8,87 +8,61 @@
|
||||
<style>
|
||||
<class name="view"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<style>
|
||||
<class name="header"/>
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkBox" id="name_heading">
|
||||
<property name="hexpand">0</property>
|
||||
<child>
|
||||
<object class="GtkGestureClick">
|
||||
<signal name="pressed" handler="sort_changed" swapped="no"/>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">Name</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="hexpand">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="name_sort_indicator">
|
||||
<style>
|
||||
<class name="sort_indicator"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="type_heading">
|
||||
<property name="label">Type</property>
|
||||
<property name="xalign">0</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox" id="origin_heading">
|
||||
<property name="hexpand">0</property>
|
||||
<child>
|
||||
<object class="GtkGestureClick">
|
||||
<signal name="pressed" handler="sort_changed" swapped="no"/>
|
||||
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="label">Defined at</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="hexpand">1</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkImage" id="origin_sort_indicator">
|
||||
<style>
|
||||
<class name="sort_indicator"/>
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel" id="value_heading">
|
||||
<property name="label">Value</property>
|
||||
<property name="xalign">0</property>
|
||||
<property name="hexpand">0</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkScrolledWindow">
|
||||
<property name="hexpand">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="list2">
|
||||
<object class="GtkColumnView" id="list">
|
||||
<style>
|
||||
<class name="list"/>
|
||||
</style>
|
||||
<property name="selection-mode">none</property>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="name">
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_name_cb"/>
|
||||
<signal name="bind" handler="bind_name_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="type">
|
||||
<property name="title" translatable="yes">Type</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_type_cb"/>
|
||||
<signal name="bind" handler="bind_type_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="origin">
|
||||
<property name="title" translatable="yes">Defined At</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_origin_cb"/>
|
||||
<signal name="bind" handler="bind_origin_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title" translatable="yes">Value</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="bind" handler="bind_value_cb"/>
|
||||
<signal name="unbind" handler="unbind_value_cb"/>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
@@ -96,28 +70,4 @@
|
||||
</object>
|
||||
</child>
|
||||
</template>
|
||||
<object class="GtkSizeGroup" id="names">
|
||||
<property name="mode">horizontal</property>
|
||||
<widgets>
|
||||
<widget name="name_heading"/>
|
||||
</widgets>
|
||||
</object>
|
||||
<object class="GtkSizeGroup" id="types">
|
||||
<property name="mode">horizontal</property>
|
||||
<widgets>
|
||||
<widget name="type_heading"/>
|
||||
</widgets>
|
||||
</object>
|
||||
<object class="GtkSizeGroup" id="origins">
|
||||
<property name="mode">horizontal</property>
|
||||
<widgets>
|
||||
<widget name="origin_heading"/>
|
||||
</widgets>
|
||||
</object>
|
||||
<object class="GtkSizeGroup" id="values">
|
||||
<property name="mode">horizontal</property>
|
||||
<widgets>
|
||||
<widget name="value_heading"/>
|
||||
</widgets>
|
||||
</object>
|
||||
</interface>
|
||||
|
106
gtk/inspector/resource-holder.c
Normal file
106
gtk/inspector/resource-holder.c
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "resource-holder.h"
|
||||
|
||||
struct _ResourceHolder {
|
||||
GObject instance;
|
||||
|
||||
char *name;
|
||||
char *path;
|
||||
int count;
|
||||
gsize size;
|
||||
GListModel *children;
|
||||
ResourceHolder *parent;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (ResourceHolder, resource_holder, G_TYPE_OBJECT)
|
||||
|
||||
static void
|
||||
resource_holder_init (ResourceHolder *holder)
|
||||
{
|
||||
}
|
||||
|
||||
static void
|
||||
resource_holder_finalize (GObject *object)
|
||||
{
|
||||
ResourceHolder *holder = RESOURCE_HOLDER (object);
|
||||
|
||||
g_free (holder->name);
|
||||
g_free (holder->path);
|
||||
g_clear_object (&holder->children);
|
||||
|
||||
G_OBJECT_CLASS (resource_holder_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
resource_holder_class_init (ResourceHolderClass *class)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
||||
|
||||
object_class->finalize = resource_holder_finalize;
|
||||
}
|
||||
|
||||
ResourceHolder *
|
||||
resource_holder_new (const char *name,
|
||||
const char *path,
|
||||
int count,
|
||||
gsize size,
|
||||
GListModel *children)
|
||||
{
|
||||
ResourceHolder *holder;
|
||||
|
||||
holder = g_object_new (RESOURCE_TYPE_HOLDER, NULL);
|
||||
|
||||
holder->name = g_strdup (name);
|
||||
holder->path = g_strdup (path);
|
||||
holder->count = count;
|
||||
holder->size = size;
|
||||
g_set_object (&holder->children, children);
|
||||
|
||||
if (children)
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < g_list_model_get_n_items (children); i++)
|
||||
{
|
||||
ResourceHolder *child = g_list_model_get_item (children, i);
|
||||
child->parent = holder;
|
||||
g_object_unref (child);
|
||||
}
|
||||
}
|
||||
|
||||
return holder;
|
||||
}
|
||||
|
||||
const char *
|
||||
resource_holder_get_name (ResourceHolder *holder)
|
||||
{
|
||||
return holder->name;
|
||||
}
|
||||
|
||||
const char *
|
||||
resource_holder_get_path (ResourceHolder *holder)
|
||||
{
|
||||
return holder->path;
|
||||
}
|
||||
|
||||
int
|
||||
resource_holder_get_count (ResourceHolder *holder)
|
||||
{
|
||||
return holder->count;
|
||||
}
|
||||
|
||||
gsize
|
||||
resource_holder_get_size (ResourceHolder *holder)
|
||||
{
|
||||
return holder->size;
|
||||
}
|
||||
|
||||
GListModel *
|
||||
resource_holder_get_children (ResourceHolder *holder)
|
||||
{
|
||||
return holder->children;
|
||||
}
|
||||
|
||||
ResourceHolder *
|
||||
resource_holder_get_parent (ResourceHolder *holder)
|
||||
{
|
||||
return holder->parent;
|
||||
}
|
24
gtk/inspector/resource-holder.h
Normal file
24
gtk/inspector/resource-holder.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#ifndef __RESOURCE_HOLDER_H__
|
||||
#define __RESOURCE_HOLDER_H__
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#define RESOURCE_TYPE_HOLDER (resource_holder_get_type ())
|
||||
|
||||
G_DECLARE_FINAL_TYPE (ResourceHolder, resource_holder, RESOURCE, HOLDER, GObject)
|
||||
|
||||
ResourceHolder * resource_holder_new (const char *name,
|
||||
const char *path,
|
||||
int count,
|
||||
gsize size,
|
||||
GListModel *children);
|
||||
|
||||
const char *resource_holder_get_name (ResourceHolder *holder);
|
||||
const char *resource_holder_get_path (ResourceHolder *holder);
|
||||
int resource_holder_get_count (ResourceHolder *holder);
|
||||
gsize resource_holder_get_size (ResourceHolder *holder);
|
||||
GListModel *resource_holder_get_children (ResourceHolder *holder);
|
||||
ResourceHolder *resource_holder_get_parent (ResourceHolder *holder);
|
||||
|
||||
#endif /* __RESOURCE_HOLDER_H__ */
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -30,6 +30,9 @@
|
||||
<object class="GtkSearchEntry" id="search_entry">
|
||||
<property name="max-width-chars">40</property>
|
||||
<signal name="search-changed" handler="on_search_changed"/>
|
||||
<signal name="next-match" handler="next_match"/>
|
||||
<signal name="previous-match" handler="previous_match"/>
|
||||
<signal name="stop-search" handler="stop_search"/>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
@@ -54,56 +57,45 @@
|
||||
<property name="vexpand">1</property>
|
||||
<property name="hscrollbar-policy">never</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="tree">
|
||||
<property name="model">model</property>
|
||||
<property name="enable-search">0</property>
|
||||
<property name="enable-grid-lines">vertical</property>
|
||||
<signal name="row-activated" handler="row_activated"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection">
|
||||
<signal name="changed" handler="on_selection_changed"/>
|
||||
</object>
|
||||
</child>
|
||||
<object class="GtkColumnView" id="list">
|
||||
<signal name="activate" handler="on_row_activated"/>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="path_column">
|
||||
<object class="GtkColumnViewColumn" id="path">
|
||||
<property name="title" translatable="yes">Path</property>
|
||||
<property name="resizable">1</property>
|
||||
<property name="sort-column-id">0</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText">
|
||||
<property name="scale">0.8</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<property name="width-chars">10</property>
|
||||
<property name="max-width-chars">5</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_name_cb"/>
|
||||
<signal name="bind" handler="bind_name_cb"/>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="count_column">
|
||||
<object class="GtkColumnViewColumn" id="count">
|
||||
<property name="title" translatable="yes">Count</property>
|
||||
<property name="resizable">1</property>
|
||||
<property name="sort-column-id">1</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="count_renderer">
|
||||
<property name="scale">0.8</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_count_cb"/>
|
||||
<signal name="bind" handler="bind_count_cb"/>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="size_column">
|
||||
<object class="GtkColumnViewColumn" id="size">
|
||||
<property name="title" translatable="yes">Size</property>
|
||||
<property name="resizable">1</property>
|
||||
<property name="sort-column-id">2</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="size_renderer">
|
||||
<property name="scale">0.8</property>
|
||||
<property name="factory">
|
||||
<object class="GtkSignalListItemFactory">
|
||||
<signal name="setup" handler="setup_size_cb"/>
|
||||
<signal name="bind" handler="bind_size_cb"/>
|
||||
</object>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn" id="filler">
|
||||
<property name="expand">1</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@@ -135,6 +135,7 @@ gtk_private_sources = files([
|
||||
'gtkscaler.c',
|
||||
'gtksearchengine.c',
|
||||
'gtksearchenginemodel.c',
|
||||
'gtkset.c',
|
||||
'gtksizerequestcache.c',
|
||||
'gtkstyleanimation.c',
|
||||
'gtkstylecascade.c',
|
||||
@@ -303,6 +304,7 @@ gtk_public_sources = files([
|
||||
'gtkmodules.c',
|
||||
'gtkmountoperation.c',
|
||||
'gtkmultifilter.c',
|
||||
'gtkmultiselection.c',
|
||||
'gtkmultisorter.c',
|
||||
'gtknativedialog.c',
|
||||
'gtknomediafile.c',
|
||||
@@ -577,6 +579,7 @@ gtk_public_headers = files([
|
||||
'gtkmessagedialog.h',
|
||||
'gtkmountoperation.h',
|
||||
'gtkmultifilter.h',
|
||||
'gtkmultiselection.h',
|
||||
'gtkmultisorter.h',
|
||||
'gtknative.h',
|
||||
'gtknativedialog.h',
|
||||
|
@@ -17,19 +17,6 @@
|
||||
<column type="gboolean"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkListStore" id="printer_list">
|
||||
<columns>
|
||||
<column type="GIcon"/>
|
||||
<column type="gchararray"/>
|
||||
<column type="gchararray"/>
|
||||
<column type="gint"/>
|
||||
<column type="gchararray"/>
|
||||
<column type="GObject"/>
|
||||
</columns>
|
||||
</object>
|
||||
<object class="GtkTreeModelFilter" id="printer_list_filter">
|
||||
<property name="child-model">printer_list</property>
|
||||
</object>
|
||||
<object class="GtkAdjustment" id="scale_spin_adjustment">
|
||||
<property name="lower">1</property>
|
||||
<property name="upper">1000</property>
|
||||
@@ -72,58 +59,131 @@
|
||||
<property name="has-frame">1</property>
|
||||
<property name="vexpand">1</property>
|
||||
<child>
|
||||
<object class="GtkTreeView" id="printer_treeview">
|
||||
<property name="model">printer_list_filter</property>
|
||||
<signal name="row-activated" handler="emit_ok_response" swapped="no"/>
|
||||
<child internal-child="selection">
|
||||
<object class="GtkTreeSelection" id="treeview-selection1">
|
||||
<property name="mode">browse</property>
|
||||
<signal name="changed" handler="selected_printer_changed" swapped="no"/>
|
||||
</object>
|
||||
</child>
|
||||
<object class="GtkColumnView" id="printer_list">
|
||||
<property name="can-focus">1</property>
|
||||
<property name="halign">fill</property>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="printer_icon_column">
|
||||
<child>
|
||||
<object class="GtkCellRendererPixbuf" id="printer_icon_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="gicon">0</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="printer_name_column">
|
||||
<property name="title" translatable="yes">Printer</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="printer_name_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">1</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="printer_location_column">
|
||||
<property name="title" translatable="yes" comments="this is the header for the location column in the print dialog">Location</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="printer_location_renderer"/>
|
||||
<attributes>
|
||||
<attribute name="text">4</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkTreeViewColumn" id="printer_status_column">
|
||||
<property name="title" translatable="yes" comments="this is the header for the printer status column in the print dialog">Status</property>
|
||||
<child>
|
||||
<object class="GtkCellRendererText" id="printer_status_renderer">
|
||||
<property name="ellipsize">end</property>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkImage">
|
||||
<binding name="icon-name">
|
||||
<lookup name="icon-name" type="GtkPrinter">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="sensitive">
|
||||
<lookup name="accepting-jobs" type="GtkPrinter">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
<attributes>
|
||||
<attribute name="text">2</attribute>
|
||||
</attributes>
|
||||
</child>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title" translatable="yes">Name</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<lookup name="name" type="GtkPrinter">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="sensitive">
|
||||
<lookup name="accepting-jobs" type="GtkPrinter">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title" translatable="yes" comments="this is the header for the location column in the print dialog">Location</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<binding name="label">
|
||||
<lookup name="location" type="GtkPrinter">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="sensitive">
|
||||
<lookup name="accepting-jobs" type="GtkPrinter">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkColumnViewColumn">
|
||||
<property name="title" translatable="yes" comments="this is the header for the printer status column in the print dialog">Status</property>
|
||||
<property name="expand">1</property>
|
||||
<property name="factory">
|
||||
<object class="GtkBuilderListItemFactory">
|
||||
<property name="bytes"><![CDATA[
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="GtkListItem">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="xalign">0</property>
|
||||
<property name="ellipsize">end</property>
|
||||
<binding name="label">
|
||||
<lookup name="state-message" type="GtkPrinter">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
<binding name="sensitive">
|
||||
<lookup name="accepting-jobs" type="GtkPrinter">
|
||||
<lookup name="item">GtkListItem</lookup>
|
||||
</lookup>
|
||||
</binding>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
]]></property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
|
@@ -488,8 +488,9 @@ const char *ui_file =
|
||||
"<interface>\n" \
|
||||
" <template class='GtkListItem'>\n" \
|
||||
" <property name='child'>\n" \
|
||||
" <object class='GtkCheckButton'>\n" \
|
||||
" <binding name='active'>\n" \
|
||||
" <object class='GtkImage'>\n" \
|
||||
" <property name='icon-name'>object-select-symbolic</property>\n" \
|
||||
" <binding name='visible'>\n" \
|
||||
" <closure type='gboolean' function='get_boolean'>\n" \
|
||||
" <lookup name='item' type='GtkTreeListRow'><lookup name='item'>GtkListItem</lookup></lookup>\n" \
|
||||
" <constant type='gchararray'>" attr "</constant>" \
|
||||
@@ -606,12 +607,13 @@ struct {
|
||||
#define G_FILE_ATTRIBUTE_RECENT_MODIFIED "recent::modified" /* int64 (time_t) */
|
||||
#endif
|
||||
|
||||
const char *factory_ui =
|
||||
const char *factory_ui_name =
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<interface>\n"
|
||||
" <template class='GtkListItem'>\n"
|
||||
" <property name='child'>\n"
|
||||
" <object class='GtkLabel'>\n"
|
||||
" <property name='xalign'>0</property>\n"
|
||||
" <binding name='label'>\n"
|
||||
" <lookup name='title' type='GtkColumnViewColumn'>\n"
|
||||
" <lookup name='item'>GtkListItem</lookup>\n"
|
||||
@@ -622,6 +624,124 @@ const char *factory_ui =
|
||||
" </template>\n"
|
||||
"</interface>\n";
|
||||
|
||||
const char *factory_ui_visible =
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<interface>\n"
|
||||
" <template class='GtkListItem'>\n"
|
||||
" <property name='child'>\n"
|
||||
" <object class='GtkCheckButton'>\n"
|
||||
" <signal name='toggled' handler='column_visible_toggled' object='GtkListItem'/>\n"
|
||||
" <binding name='active'>\n"
|
||||
" <lookup name='visible' type='GtkColumnViewColumn'>\n"
|
||||
" <lookup name='item'>GtkListItem</lookup>\n"
|
||||
" </lookup>\n"
|
||||
" </binding>\n"
|
||||
" </object>\n"
|
||||
" </property>\n"
|
||||
" </template>\n"
|
||||
"</interface>\n";
|
||||
|
||||
const char *factory_ui_resizable =
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<interface>\n"
|
||||
" <template class='GtkListItem'>\n"
|
||||
" <property name='child'>\n"
|
||||
" <object class='GtkCheckButton'>\n"
|
||||
" <signal name='toggled' handler='column_resizable_toggled' object='GtkListItem'/>\n"
|
||||
" <binding name='active'>\n"
|
||||
" <lookup name='resizable' type='GtkColumnViewColumn'>\n"
|
||||
" <lookup name='item'>GtkListItem</lookup>\n"
|
||||
" </lookup>\n"
|
||||
" </binding>\n"
|
||||
" </object>\n"
|
||||
" </property>\n"
|
||||
" </template>\n"
|
||||
"</interface>\n";
|
||||
|
||||
const char *factory_ui_reorderable =
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<interface>\n"
|
||||
" <template class='GtkListItem'>\n"
|
||||
" <property name='child'>\n"
|
||||
" <object class='GtkCheckButton'>\n"
|
||||
" <signal name='toggled' handler='column_reorderable_toggled' object='GtkListItem'/>\n"
|
||||
" <binding name='active'>\n"
|
||||
" <lookup name='reorderable' type='GtkColumnViewColumn'>\n"
|
||||
" <lookup name='item'>GtkListItem</lookup>\n"
|
||||
" </lookup>\n"
|
||||
" </binding>\n"
|
||||
" </object>\n"
|
||||
" </property>\n"
|
||||
" </template>\n"
|
||||
"</interface>\n";
|
||||
|
||||
const char *factory_ui_width =
|
||||
"<?xml version='1.0' encoding='UTF-8'?>\n"
|
||||
"<interface>\n"
|
||||
" <template class='GtkListItem'>\n"
|
||||
" <property name='child'>\n"
|
||||
" <object class='GtkSpinButton'>\n"
|
||||
" <property name='digits'>0</property>\n"
|
||||
" <property name='numeric'>1</property>\n"
|
||||
" <property name='adjustment'>\n"
|
||||
" <object class='GtkAdjustment'>\n"
|
||||
" <property name='lower'>-1</property>\n"
|
||||
" <property name='upper'>10000</property>\n"
|
||||
" <property name='step-increment'>1</property>\n"
|
||||
" <property name='page-increment'>10</property>\n"
|
||||
" </object>\n"
|
||||
" </property>\n"
|
||||
" <signal name='value-changed' handler='column_width_changed' object='GtkListItem'/>\n"
|
||||
" <binding name='value'>\n"
|
||||
" <closure type='gdouble' function='cast_to_double'>\n"
|
||||
" <lookup name='fixed-width' type='GtkColumnViewColumn'>\n"
|
||||
" <lookup name='item'>GtkListItem</lookup>\n"
|
||||
" </lookup>\n"
|
||||
" </closure>\n"
|
||||
" </binding>\n"
|
||||
" </object>\n"
|
||||
" </property>\n"
|
||||
" </template>\n"
|
||||
"</interface>\n";
|
||||
|
||||
static void
|
||||
column_visible_toggled (GtkListItem *item, GtkToggleButton *button)
|
||||
{
|
||||
GtkColumnViewColumn *column = gtk_list_item_get_item (item);
|
||||
|
||||
gtk_column_view_column_set_visible (column, gtk_toggle_button_get_active (button));
|
||||
}
|
||||
|
||||
static void
|
||||
column_resizable_toggled (GtkListItem *item, GtkToggleButton *button)
|
||||
{
|
||||
GtkColumnViewColumn *column = gtk_list_item_get_item (item);
|
||||
|
||||
gtk_column_view_column_set_resizable (column, gtk_toggle_button_get_active (button));
|
||||
}
|
||||
|
||||
static void
|
||||
column_reorderable_toggled (GtkListItem *item, GtkToggleButton *button)
|
||||
{
|
||||
GtkColumnViewColumn *column = gtk_list_item_get_item (item);
|
||||
|
||||
gtk_column_view_column_set_reorderable (column, gtk_toggle_button_get_active (button));
|
||||
}
|
||||
|
||||
static void
|
||||
column_width_changed (GtkListItem *item, GtkSpinButton *button)
|
||||
{
|
||||
GtkColumnViewColumn *column = gtk_list_item_get_item (item);
|
||||
|
||||
gtk_column_view_column_set_fixed_width (column, gtk_spin_button_get_value_as_int (button));
|
||||
}
|
||||
|
||||
static double
|
||||
cast_to_double (gpointer _this, int value)
|
||||
{
|
||||
return (double)value;
|
||||
}
|
||||
|
||||
static GtkBuilderScope *
|
||||
create_scope (void)
|
||||
{
|
||||
@@ -634,6 +754,11 @@ create_scope (void)
|
||||
ADD_SYMBOL (get_object);
|
||||
ADD_SYMBOL (get_string);
|
||||
ADD_SYMBOL (get_boolean);
|
||||
ADD_SYMBOL (column_visible_toggled);
|
||||
ADD_SYMBOL (column_resizable_toggled);
|
||||
ADD_SYMBOL (column_reorderable_toggled);
|
||||
ADD_SYMBOL (column_width_changed);
|
||||
ADD_SYMBOL (cast_to_double);
|
||||
|
||||
return scope;
|
||||
#undef ADD_SYMBOL
|
||||
@@ -668,11 +793,60 @@ search_changed_cb (GtkSearchEntry *entry,
|
||||
gtk_filter_changed (custom_filter, GTK_FILTER_CHANGE_DIFFERENT);
|
||||
}
|
||||
|
||||
static void
|
||||
move_column (GtkColumnView *list, gboolean down)
|
||||
{
|
||||
GListModel *columns;
|
||||
guint position;
|
||||
GtkColumnViewColumn *selected;
|
||||
GtkColumnView *view;
|
||||
|
||||
columns = gtk_column_view_get_model (list);
|
||||
position = gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (columns));
|
||||
selected = gtk_single_selection_get_selected_item (GTK_SINGLE_SELECTION (columns));
|
||||
view = gtk_column_view_column_get_column_view (selected);
|
||||
|
||||
if (down && position + 1 < g_list_model_get_n_items (columns))
|
||||
position++;
|
||||
else if (!down && position > 0)
|
||||
position--;
|
||||
else
|
||||
return;
|
||||
|
||||
gtk_column_view_insert_column (view, position, selected);
|
||||
gtk_single_selection_set_selected (GTK_SINGLE_SELECTION (columns), position);
|
||||
gtk_widget_grab_focus (GTK_WIDGET (list));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
key_pressed_cb (GtkEventControllerKey *controller,
|
||||
guint keyval,
|
||||
guint keycode,
|
||||
GdkModifierType modifiers,
|
||||
GtkColumnView *list)
|
||||
{
|
||||
gboolean down;
|
||||
|
||||
if ((modifiers & GDK_CONTROL_MASK) == 0)
|
||||
return FALSE;
|
||||
|
||||
if (keyval == GDK_KEY_Down)
|
||||
down = TRUE;
|
||||
else if (keyval == GDK_KEY_Up)
|
||||
down = FALSE;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
move_column (list, down);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
GListModel *toplevels;
|
||||
GtkWidget *win, *hbox, *vbox, *sw, *view, *list, *search_entry, *statusbar;
|
||||
GtkWidget *win, *paned, *vbox, *sw, *view, *list, *search_entry, *statusbar;
|
||||
GListModel *dirmodel;
|
||||
GtkTreeListModel *tree;
|
||||
GtkFilterListModel *filter;
|
||||
@@ -683,17 +857,21 @@ main (int argc, char *argv[])
|
||||
GtkBuilderScope *scope;
|
||||
GtkBuilder *builder;
|
||||
GError *error = NULL;
|
||||
GtkColumnViewColumn *column;
|
||||
GListModel *model;
|
||||
GtkEventController *controller;
|
||||
|
||||
gtk_init ();
|
||||
|
||||
win = gtk_window_new ();
|
||||
gtk_window_set_default_size (GTK_WINDOW (win), 800, 600);
|
||||
|
||||
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
|
||||
gtk_window_set_child (GTK_WINDOW (win), hbox);
|
||||
paned = gtk_paned_new (GTK_ORIENTATION_HORIZONTAL);
|
||||
gtk_paned_set_wide_handle (GTK_PANED (paned), TRUE);
|
||||
gtk_window_set_child (GTK_WINDOW (win), paned);
|
||||
|
||||
vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
||||
gtk_box_append (GTK_BOX (hbox), vbox);
|
||||
gtk_paned_set_start_child (GTK_PANED (paned), vbox);
|
||||
|
||||
search_entry = gtk_search_entry_new ();
|
||||
gtk_box_append (GTK_BOX (vbox), search_entry);
|
||||
@@ -750,13 +928,58 @@ main (int argc, char *argv[])
|
||||
g_object_unref (sort);
|
||||
g_object_unref (tree);
|
||||
|
||||
list = gtk_list_view_new_with_factory (
|
||||
gtk_builder_list_item_factory_new_from_bytes (scope, g_bytes_new_static (factory_ui, strlen (factory_ui))));
|
||||
gtk_list_view_set_model (GTK_LIST_VIEW (list), gtk_column_view_get_columns (GTK_COLUMN_VIEW (view)));
|
||||
gtk_box_append (GTK_BOX (hbox), list);
|
||||
list = gtk_column_view_new ();
|
||||
|
||||
column = gtk_column_view_column_new_with_factory ("Name",
|
||||
gtk_builder_list_item_factory_new_from_bytes (scope, g_bytes_new_static (factory_ui_name, strlen (factory_ui_name))));
|
||||
gtk_column_view_column_set_resizable (column, FALSE);
|
||||
gtk_column_view_column_set_reorderable (column, FALSE);
|
||||
gtk_column_view_append_column (GTK_COLUMN_VIEW (list), column);
|
||||
g_object_unref (column);
|
||||
|
||||
column = gtk_column_view_column_new_with_factory ("Visible",
|
||||
gtk_builder_list_item_factory_new_from_bytes (scope, g_bytes_new_static (factory_ui_visible, strlen (factory_ui_visible))));
|
||||
gtk_column_view_column_set_resizable (column, FALSE);
|
||||
gtk_column_view_column_set_reorderable (column, FALSE);
|
||||
gtk_column_view_append_column (GTK_COLUMN_VIEW (list), column);
|
||||
g_object_unref (column);
|
||||
|
||||
column = gtk_column_view_column_new_with_factory ("Resizable",
|
||||
gtk_builder_list_item_factory_new_from_bytes (scope, g_bytes_new_static (factory_ui_resizable, strlen (factory_ui_resizable))));
|
||||
gtk_column_view_column_set_resizable (column, FALSE);
|
||||
gtk_column_view_column_set_reorderable (column, FALSE);
|
||||
gtk_column_view_append_column (GTK_COLUMN_VIEW (list), column);
|
||||
g_object_unref (column);
|
||||
|
||||
column = gtk_column_view_column_new_with_factory ("Reorderable",
|
||||
gtk_builder_list_item_factory_new_from_bytes (scope, g_bytes_new_static (factory_ui_reorderable, strlen (factory_ui_reorderable))));
|
||||
gtk_column_view_column_set_resizable (column, FALSE);
|
||||
gtk_column_view_column_set_reorderable (column, FALSE);
|
||||
gtk_column_view_append_column (GTK_COLUMN_VIEW (list), column);
|
||||
g_object_unref (column);
|
||||
|
||||
column = gtk_column_view_column_new_with_factory ("Width",
|
||||
gtk_builder_list_item_factory_new_from_bytes (scope, g_bytes_new_static (factory_ui_width, strlen (factory_ui_width))));
|
||||
gtk_column_view_column_set_resizable (column, FALSE);
|
||||
gtk_column_view_column_set_reorderable (column, FALSE);
|
||||
gtk_column_view_append_column (GTK_COLUMN_VIEW (list), column);
|
||||
g_object_unref (column);
|
||||
|
||||
model = G_LIST_MODEL (gtk_single_selection_new (gtk_column_view_get_columns (GTK_COLUMN_VIEW (view))));
|
||||
gtk_column_view_set_model (GTK_COLUMN_VIEW (list), model);
|
||||
g_object_unref (model);
|
||||
|
||||
sw = gtk_scrolled_window_new (NULL, NULL);
|
||||
gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (sw), list);
|
||||
gtk_paned_set_end_child (GTK_PANED (paned), sw);
|
||||
|
||||
g_object_unref (scope);
|
||||
|
||||
controller = gtk_event_controller_key_new ();
|
||||
g_signal_connect (controller, "key-pressed", G_CALLBACK (key_pressed_cb), list);
|
||||
gtk_event_controller_set_propagation_phase (controller, GTK_PHASE_CAPTURE);
|
||||
gtk_widget_add_controller (list, controller);
|
||||
|
||||
gtk_widget_show (win);
|
||||
|
||||
toplevels = gtk_window_get_toplevels ();
|
||||
|
@@ -39,6 +39,7 @@ tests = [
|
||||
['listbox'],
|
||||
['main'],
|
||||
['maplistmodel'],
|
||||
['multiselection'],
|
||||
['notify'],
|
||||
['no-gtk-init'],
|
||||
['object'],
|
||||
|
393
testsuite/gtk/multiselection.c
Normal file
393
testsuite/gtk/multiselection.c
Normal file
@@ -0,0 +1,393 @@
|
||||
/*
|
||||
* Copyright (C) 2019, Red Hat, Inc.
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
static GQuark number_quark;
|
||||
static GQuark changes_quark;
|
||||
static GQuark selection_quark;
|
||||
|
||||
static guint
|
||||
get (GListModel *model,
|
||||
guint position)
|
||||
{
|
||||
GObject *object = g_list_model_get_item (model, position);
|
||||
g_assert (object != NULL);
|
||||
return GPOINTER_TO_UINT (g_object_get_qdata (object, number_quark));
|
||||
}
|
||||
|
||||
static char *
|
||||
model_to_string (GListModel *model)
|
||||
{
|
||||
GString *string = g_string_new (NULL);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (i > 0)
|
||||
g_string_append (string, " ");
|
||||
g_string_append_printf (string, "%u", get (model, i));
|
||||
}
|
||||
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static char *
|
||||
selection_to_string (GListModel *model)
|
||||
{
|
||||
GString *string = g_string_new (NULL);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (model); i++)
|
||||
{
|
||||
if (!gtk_selection_model_is_selected (GTK_SELECTION_MODEL (model), i))
|
||||
continue;
|
||||
|
||||
if (string->len > 0)
|
||||
g_string_append (string, " ");
|
||||
g_string_append_printf (string, "%u", get (model, i));
|
||||
}
|
||||
|
||||
return g_string_free (string, FALSE);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step);
|
||||
|
||||
static GObject *
|
||||
make_object (guint number)
|
||||
{
|
||||
GObject *object;
|
||||
|
||||
/* 0 cannot be differentiated from NULL, so don't use it */
|
||||
g_assert (number != 0);
|
||||
|
||||
object = g_object_new (G_TYPE_OBJECT, NULL);
|
||||
g_object_set_qdata (object, number_quark, GUINT_TO_POINTER (number));
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static void
|
||||
splice (GListStore *store,
|
||||
guint pos,
|
||||
guint removed,
|
||||
guint *numbers,
|
||||
guint added)
|
||||
{
|
||||
GObject **objects;
|
||||
guint i;
|
||||
|
||||
objects = g_new0 (GObject *, added);
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
objects[i] = make_object (numbers[i]);
|
||||
|
||||
g_list_store_splice (store, pos, removed, (gpointer *) objects, added);
|
||||
|
||||
for (i = 0; i < added; i++)
|
||||
g_object_unref (objects[i]);
|
||||
|
||||
g_free (objects);
|
||||
}
|
||||
|
||||
static void
|
||||
add (GListStore *store,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_append (store, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
static void
|
||||
insert (GListStore *store,
|
||||
guint position,
|
||||
guint number)
|
||||
{
|
||||
GObject *object = make_object (number);
|
||||
g_list_store_insert (store, position, object);
|
||||
g_object_unref (object);
|
||||
}
|
||||
|
||||
#define assert_model(model, expected) G_STMT_START{ \
|
||||
char *s = model_to_string (G_LIST_MODEL (model)); \
|
||||
if (!g_str_equal (s, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, s, "==", expected); \
|
||||
g_free (s); \
|
||||
}G_STMT_END
|
||||
|
||||
#define ignore_changes(model) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_changes(model, expected) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), changes_quark); \
|
||||
if (!g_str_equal (changes->str, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, changes->str, "==", expected); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_selection(model, expected) G_STMT_START{ \
|
||||
char *s = selection_to_string (G_LIST_MODEL (model)); \
|
||||
if (!g_str_equal (s, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, s, "==", expected); \
|
||||
g_free (s); \
|
||||
}G_STMT_END
|
||||
|
||||
#define ignore_selection_changes(model) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
#define assert_selection_changes(model, expected) G_STMT_START{ \
|
||||
GString *changes = g_object_get_qdata (G_OBJECT (model), selection_quark); \
|
||||
if (!g_str_equal (changes->str, expected)) \
|
||||
g_assertion_message_cmpstr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
|
||||
#model " == " #expected, changes->str, "==", expected); \
|
||||
g_string_set_size (changes, 0); \
|
||||
}G_STMT_END
|
||||
|
||||
static GListStore *
|
||||
new_empty_store (void)
|
||||
{
|
||||
return g_list_store_new (G_TYPE_OBJECT);
|
||||
}
|
||||
|
||||
static GListStore *
|
||||
new_store (guint start,
|
||||
guint end,
|
||||
guint step)
|
||||
{
|
||||
GListStore *store = new_empty_store ();
|
||||
guint i;
|
||||
|
||||
for (i = start; i <= end; i += step)
|
||||
add (store, i);
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
static void
|
||||
items_changed (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GString *changes)
|
||||
{
|
||||
g_assert (removed != 0 || added != 0);
|
||||
|
||||
if (changes->len)
|
||||
g_string_append (changes, ", ");
|
||||
|
||||
if (removed == 1 && added == 0)
|
||||
{
|
||||
g_string_append_printf (changes, "-%u", position);
|
||||
}
|
||||
else if (removed == 0 && added == 1)
|
||||
{
|
||||
g_string_append_printf (changes, "+%u", position);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_string_append_printf (changes, "%u", position);
|
||||
if (removed > 0)
|
||||
g_string_append_printf (changes, "-%u", removed);
|
||||
if (added > 0)
|
||||
g_string_append_printf (changes, "+%u", added);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
selection_changed (GListModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
GString *changes)
|
||||
{
|
||||
if (changes->len)
|
||||
g_string_append (changes, ", ");
|
||||
|
||||
g_string_append_printf (changes, "%u:%u", position, n_items);
|
||||
}
|
||||
|
||||
static void
|
||||
free_changes (gpointer data)
|
||||
{
|
||||
GString *changes = data;
|
||||
|
||||
/* all changes must have been checked via assert_changes() before */
|
||||
g_assert_cmpstr (changes->str, ==, "");
|
||||
|
||||
g_string_free (changes, TRUE);
|
||||
}
|
||||
|
||||
static GtkSelectionModel *
|
||||
new_model (GListStore *store)
|
||||
{
|
||||
GtkSelectionModel *result;
|
||||
GString *changes;
|
||||
|
||||
result = GTK_SELECTION_MODEL (gtk_multi_selection_new (G_LIST_MODEL (store)));
|
||||
|
||||
changes = g_string_new ("");
|
||||
g_object_set_qdata_full (G_OBJECT(result), changes_quark, changes, free_changes);
|
||||
g_signal_connect (result, "items-changed", G_CALLBACK (items_changed), changes);
|
||||
|
||||
changes = g_string_new ("");
|
||||
g_object_set_qdata_full (G_OBJECT(result), selection_quark, changes, free_changes);
|
||||
g_signal_connect (result, "selection-changed", G_CALLBACK (selection_changed), changes);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
test_create (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 2);
|
||||
selection = new_model (store);
|
||||
|
||||
assert_model (selection, "1 3 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
assert_model (selection, "1 3 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_changes (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store);
|
||||
assert_model (selection, "1 2 3 4 5");
|
||||
assert_changes (selection, "");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_list_store_remove (store, 3);
|
||||
assert_model (selection, "1 2 3 5");
|
||||
assert_changes (selection, "-3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
insert (store, 3, 99);
|
||||
assert_model (selection, "1 2 3 99 5");
|
||||
assert_changes (selection, "+3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
splice (store, 3, 2, (guint[]) { 97 }, 1);
|
||||
assert_model (selection, "1 2 3 97");
|
||||
assert_changes (selection, "3-2+1");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
test_selection (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
gboolean ret;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_item (selection, 3, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "4");
|
||||
assert_selection_changes (selection, "3:1");
|
||||
|
||||
ret = gtk_selection_model_unselect_item (selection, 3);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "3:1");
|
||||
|
||||
ret = gtk_selection_model_select_item (selection, 1, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "2");
|
||||
assert_selection_changes (selection, "1:1");
|
||||
|
||||
ret = gtk_selection_model_select_range (selection, 3, 2, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "2 4 5");
|
||||
assert_selection_changes (selection, "3:2");
|
||||
|
||||
ret = gtk_selection_model_unselect_range (selection, 3, 2);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "2");
|
||||
assert_selection_changes (selection, "3:2");
|
||||
|
||||
ret = gtk_selection_model_select_all (selection);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "1 2 3 4 5");
|
||||
assert_selection_changes (selection, "0:5");
|
||||
|
||||
ret = gtk_selection_model_unselect_all (selection);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "0:5");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
setlocale (LC_ALL, "C");
|
||||
g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id=%s");
|
||||
|
||||
number_quark = g_quark_from_static_string ("Hell and fire was spawned to be released.");
|
||||
changes_quark = g_quark_from_static_string ("What did I see? Can I believe what I saw?");
|
||||
selection_quark = g_quark_from_static_string ("Mana mana, badibidibi");
|
||||
|
||||
g_test_add_func ("/multiselection/create", test_create);
|
||||
#if GLIB_CHECK_VERSION (2, 58, 0) /* g_list_store_splice() is broken before 2.58 */
|
||||
g_test_add_func ("/multiselection/changes", test_changes);
|
||||
#endif
|
||||
g_test_add_func ("/multiselection/selection", test_selection);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
@@ -642,6 +642,49 @@ test_query_range (void)
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
static void
|
||||
selection_changed_cb (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gpointer data)
|
||||
{
|
||||
int *counter = data;
|
||||
|
||||
(*counter)++;
|
||||
}
|
||||
|
||||
/* Test that the selection emits selection-changed when an item
|
||||
* is autoselected due to the underlying store going from empty
|
||||
* to non-empty.
|
||||
*/
|
||||
static void
|
||||
test_empty (void)
|
||||
{
|
||||
GListStore *store;
|
||||
GtkSingleSelection *selection;
|
||||
int counter;
|
||||
GtkFilter *filter;
|
||||
|
||||
store = g_list_store_new (GTK_TYPE_FILTER);
|
||||
|
||||
counter = 0;
|
||||
selection = gtk_single_selection_new (G_LIST_MODEL (store));
|
||||
g_signal_connect (selection, "selection-changed", G_CALLBACK (selection_changed_cb), &counter);
|
||||
|
||||
g_assert_cmpint (gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (selection)), ==, GTK_INVALID_LIST_POSITION);
|
||||
|
||||
filter = gtk_string_filter_new ();
|
||||
g_list_store_append (store, filter);
|
||||
g_object_unref (filter);
|
||||
|
||||
g_assert_cmpint (gtk_single_selection_get_selected (GTK_SINGLE_SELECTION (selection)), ==, 0);
|
||||
|
||||
g_assert_cmpint (counter, ==, 1);
|
||||
|
||||
g_object_unref (selection);
|
||||
g_object_unref (store);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
@@ -660,6 +703,7 @@ main (int argc, char *argv[])
|
||||
g_test_add_func ("/singleselection/persistence", test_persistence);
|
||||
g_test_add_func ("/singleselection/query-range", test_query_range);
|
||||
g_test_add_func ("/singleselection/changes", test_changes);
|
||||
g_test_add_func ("/singleselection/empty", test_empty);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Reference in New Issue
Block a user