Compare commits

..

42 Commits

Author SHA1 Message Date
Matthias Clasen
d17f224e71 Add search support
Add a filter to GtkListBase, and move the selection
to the first matching item whenever the filter changes.
This is meant to be used with single selection and
a string filter that is hooked up to a search entry.
2020-05-30 22:27:54 -04:00
Matthias Clasen
b73be6e344 printdialog: Port to column view
This is not 100% complete. The search is not there yet.
2020-05-30 22:27:54 -04:00
Matthias Clasen
4a80f23213 printer: Fix the default value of icon-name
Just set this to "printer", so we don't have
to fix it up in the print dialog.
2020-05-30 20:01:14 -04:00
Matthias Clasen
3b8888acda printbackend: Add a list model getter
Now that we have a list model for printers,
we can start using it.
2020-05-30 20:01:14 -04:00
Matthias Clasen
786c95e503 printbackend: Use a list store 2020-05-30 20:01:14 -04:00
Matthias Clasen
7007c72fc5 inspector: Expand the actions list 2020-05-30 20:01:14 -04:00
Matthias Clasen
9eaeb81433 inspector: Expand the resource list
This is an experiment with adding a filler column.
2020-05-30 20:01:14 -04:00
Matthias Clasen
3bddbcdd52 inspector: Expand the property list
It looks better this way.
2020-05-30 20:01:14 -04:00
Matthias Clasen
13d5d60db0 inspector: Expand the object tree
This is how it used to look, and it looks better that way.
2020-05-30 20:01:14 -04:00
Matthias Clasen
1b24528b9c columnview: Take expand into account
When allocating columns, distribute extra space
to columns that have expand set to TRUE.
2020-05-30 20:01:14 -04:00
Matthias Clasen
6e7ebc49eb columnviewcolumn: Add an expand property
This will be used to determine how to distribute
available extra space in a column view.
2020-05-30 20:01:14 -04:00
Matthias Clasen
bdc6770bd9 gtk-demo: Add more scrolling benchmarks
Add a listview and gridview to the scrolling
benchmarks.
2020-05-30 20:01:14 -04:00
Matthias Clasen
8d2fef1e23 gtk-demo: Make gridview demo use rubberbanding 2020-05-30 20:01:14 -04:00
Matthias Clasen
90668f7e01 Add rubberband api
Add an enable-rubberband property to GtkListView,
GtkGridView and GtkColumnView.
2020-05-30 20:01:14 -04:00
Matthias Clasen
74f72a146c listbase: Add rubberband selection
Implement the typical rubberband selection, including
autoscroll. This is only useful with multiselection,
and not very compatible with single-click-activate.
Therefore, it is not enabled by default, and needs
to be turned on with the enable-rubber-band property.
2020-05-30 20:01:14 -04:00
Matthias Clasen
5553bf1c39 Add GtkMultiSelection
This is implemented using a private GtkSet helper.
2020-05-30 20:01:14 -04:00
Matthias Clasen
77a3f81971 Add a selection model test
The test shows that we are failing to emit
::selection-changed in some circumstances.
2020-05-30 20:01:14 -04:00
Matthias Clasen
b3e0353e6b testcolumnview: Reordering in the column editor
Use Ctrl-Up/Down to move the column around.
2020-05-30 20:01:14 -04:00
Matthias Clasen
780915e318 testcolumnview: Flesh out column editor
Turn the column list into an editor with
controls for visibility, resizability, reorderability
and width.
2020-05-30 20:01:14 -04:00
Matthias Clasen
7242b801ff columnviewtitle: Display a context menu
When the ::header-menu property is set on the
column, use the menu model to create and show
a context menu.
2020-05-30 20:01:14 -04:00
Matthias Clasen
88669080c4 columnviewcolumn: Add a menu property
Add a ::header-menu property that will be used
to create a context menu for the header of the
column.
2020-05-30 20:01:14 -04:00
Matthias Clasen
51e8dc924b columnview: Add autoscroll
Autoscroll when the pointer gets close to the
edge during column resizing or reordering. This
is similar to what the treeview does, but it is
implemented using a tick callback, and has
variable speed.
2020-05-30 20:01:14 -04:00
Matthias Clasen
29b5d3ab38 columnview: Allow to cancel reorder with Escape
The treeview does this too.
2020-05-30 20:01:14 -04:00
Matthias Clasen
bb06fffab1 columnview: Interactive column reordering
Allow rearranging columns by dragging, in the same
way the treeview does.

We add the "dnd" style class to the header while
it is dragged, and we move the header of the dragged
column to the end of its parents children, so that
it gets drawn on top.
2020-05-30 20:01:14 -04:00
Matthias Clasen
94c5b00231 columnviewcolumn: Add a reorderable property
This will be used for interactive column reordering
in the future.
2020-05-30 20:01:14 -04:00
Matthias Clasen
59e49dafd7 columnviewlayout: Use header allocation for titles
Normally, this will be identical to the column
allocation, but we will temporarily change it
during column reordering.
2020-05-30 20:01:14 -04:00
Matthias Clasen
d5ce5b53d6 columnviewcolumn: Add reordering helpers
Add helper functions that let us temporarily give
a different allocation to headers. These will be
used to implement interactive column reordering
in GtkColumnView.
2020-05-30 20:01:14 -04:00
Matthias Clasen
915bdf24a7 columnviewtitle: Invert on release
This is necessary to make drag-to-reorder work
without triggering inversion.
2020-05-30 20:01:14 -04:00
Matthias Clasen
114054266b columnview: Interactive column resizing
This copies just enough of the treeview code to
get columns moving.
2020-05-30 20:01:14 -04:00
Matthias Clasen
dcea6d6cda columnviewcolumn: Add a helper
We need to check whether clicks are in the headers
of columns, so let the column view get at the the
header widget.
2020-05-30 20:01:14 -04:00
Matthias Clasen
a118267ab7 columnviewcolumn: Add a resizable property
This will be used for interactive column resizing
in the future.
2020-05-30 20:01:14 -04:00
Matthias Clasen
cc63c581c3 columnview: Add a helper
The column code needs to get access to the
listitem widgets that are children of the listview,
so add a getter.
2020-05-30 20:01:14 -04:00
Matthias Clasen
dbc1a8ba11 columnview: Add column reordering
Add an API to allow reordering columns.
2020-05-30 20:01:14 -04:00
Matthias Clasen
515a86645a columnview: Implement horizontal scrolling
The listview inside always thinks it gets its full size,
and updates its horizontal adjustment accordingly.

So keep our own adjustment, and update it in size_allocate.
2020-05-30 20:01:14 -04:00
Matthias Clasen
cbabb85e6d columnview: Revise scroll-minimum handling
Tweak the behavior slightly. We don't show
a scrollbar as long as we have at least
min-size available, but we still give the
entire size to the child, up to nat-size.

This matches how viewports handle scroll-minimum.
2020-05-30 20:01:14 -04:00
Matthias Clasen
1031625bd4 columnviewcolumn: Add a visible property
This lets us hide columns, which is useful.
2020-05-30 20:01:14 -04:00
Matthias Clasen
65ed3aa1d9 inspector: Touch up list styling
This is just the minimal amount of work to make
headers recognizable.
2020-05-30 20:01:14 -04:00
Matthias Clasen
ed04c46078 inspector: Use a column view for actions
A straight conversion from list box to column view.
2020-05-30 20:01:14 -04:00
Matthias Clasen
1b2df91e6d inspector: Make the resource list sortable
This is using a GtkTreeListRowSorter to keep expanded
state of the tree while changing the sorting.
2020-05-30 20:01:14 -04:00
Matthias Clasen
f745c0c2aa inspector: Use a column view for the resource list
A conversion from tree view to column view.
2020-05-30 20:01:14 -04:00
Matthias Clasen
2ed46450ea inspector: Use a column view for properties
Just a straight conversion from list box to column view.
2020-05-30 20:01:14 -04:00
Matthias Clasen
6d168079d4 inspector: Add columns to the object tree
Add columnview columns in the object tree.
We do the same for treeview columns.
2020-05-30 20:01:14 -04:00
43 changed files with 4673 additions and 1242 deletions

View File

@@ -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 ();
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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__ */

View File

@@ -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);

View File

@@ -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;
}

View File

@@ -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__ */

View File

@@ -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__ */

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -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 *

View File

@@ -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);
}

View File

@@ -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

View File

@@ -2238,7 +2238,7 @@ extract_mnemonic_keyval (const char *text,
p = g_utf8_next_char (p);
c = g_utf8_get_char (p);
if (c != '_' && c != '\0')
if (c != '_' && c != '0')
{
const gsize byte_index = p - text - 1; /* Of the _ */

View File

@@ -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;
}

View File

@@ -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__ */

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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
View 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
View 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__ */

View File

@@ -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));
}

View File

@@ -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,

View File

@@ -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);

View File

@@ -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
View 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
View 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__ */

View File

@@ -192,21 +192,6 @@ G_DEFINE_TYPE_EXTENDED (GtkSingleSelection, gtk_single_selection, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
gtk_single_selection_selection_model_init))
static void
emit_selection_changed (GtkSelectionModel *self,
int old_position,
int position)
{
if (old_position == GTK_INVALID_LIST_POSITION)
gtk_selection_model_selection_changed (self, position, 1);
else if (position == GTK_INVALID_LIST_POSITION)
gtk_selection_model_selection_changed (self, old_position, 1);
else if (position < old_position)
gtk_selection_model_selection_changed (self, position, old_position - position + 1);
else
gtk_selection_model_selection_changed (self, old_position, position - old_position + 1);
}
static void
gtk_single_selection_items_changed_cb (GListModel *model,
guint position,
@@ -215,9 +200,6 @@ gtk_single_selection_items_changed_cb (GListModel *model,
GtkSingleSelection *self)
{
g_object_freeze_notify (G_OBJECT (self));
int selected_before;
selected_before = self->selected;
if (self->selected_item == NULL)
{
@@ -313,9 +295,6 @@ gtk_single_selection_items_changed_cb (GListModel *model,
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
if (self->selected != selected_before)
emit_selection_changed (GTK_SELECTION_MODEL (self), selected_before, self->selected);
g_object_thaw_notify (G_OBJECT (self));
}
@@ -584,7 +563,14 @@ gtk_single_selection_set_selected (GtkSingleSelection *self,
g_clear_object (&self->selected_item);
self->selected_item = new_selected;
emit_selection_changed (GTK_SELECTION_MODEL (self), old_position, position);
if (old_position == GTK_INVALID_LIST_POSITION)
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), position, 1);
else if (position == GTK_INVALID_LIST_POSITION)
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), old_position, 1);
else if (position < old_position)
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), position, old_position - position + 1);
else
gtk_selection_model_selection_changed (GTK_SELECTION_MODEL (self), old_position, position - old_position + 1);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED]);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SELECTED_ITEM]);

View File

@@ -62,6 +62,7 @@
<child>
<object class="GtkColumnViewColumn" id="changes">
<property name="title"></property>
<property name="expand">1</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="bind" handler="bind_changes_cb"/>

View File

@@ -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"/>

View File

@@ -54,6 +54,7 @@
<child>
<object class="GtkColumnViewColumn">
<property name="title" translatable="yes">Value</property>
<property name="expand">1</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="bind" handler="bind_value_cb"/>

View File

@@ -62,6 +62,7 @@
<child>
<object class="GtkColumnViewColumn" id="path">
<property name="title" translatable="yes">Path</property>
<property name="expand">1</property>
<property name="factory">
<object class="GtkSignalListItemFactory">
<signal name="setup" handler="setup_name_cb"/>
@@ -92,6 +93,11 @@
</property>
</object>
</child>
<child>
<object class="GtkColumnViewColumn" id="filler">
<property name="expand">1</property>
</object>
</child>
</object>
</child>
</object>

View File

@@ -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',

View File

@@ -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>

View File

@@ -9,8 +9,8 @@ msgid ""
msgstr ""
"Project-Id-Version: gtk+ 2.4.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-05-31 05:35+0000\n"
"PO-Revision-Date: 2020-05-31 09:11+0300\n"
"POT-Creation-Date: 2020-05-28 09:12+0000\n"
"PO-Revision-Date: 2020-05-28 13:03+0300\n"
"Last-Translator: Yuri Chornoivan <yurchor@ukr.net>\n"
"Language-Team: Ukrainian <kde-i18n-uk@kde.org>\n"
"Language: uk\n"
@@ -460,7 +460,7 @@ msgid "Whether to wrap the license text."
msgstr "Чи переносити рядку у тексті ліцензії."
#: gtk/gtkaccellabel.c:135 gtk/gtkbutton.c:225 gtk/gtkexpander.c:320
#: gtk/gtkframe.c:168 gtk/gtklabel.c:745 gtk/gtkmenubutton.c:422
#: gtk/gtkframe.c:168 gtk/gtklabel.c:746 gtk/gtkmenubutton.c:422
msgid "Label"
msgstr "Позначка"
@@ -469,12 +469,12 @@ msgid "The text displayed next to the accelerator"
msgstr "Текст, який буде показано поряд із акселератором"
#: gtk/gtkaccellabel.c:142 gtk/gtkbutton.c:232 gtk/gtkexpander.c:328
#: gtk/gtklabel.c:766 gtk/gtkmenubutton.c:429 gtk/gtkstack.c:431
#: gtk/gtklabel.c:767 gtk/gtkmenubutton.c:429 gtk/gtkstack.c:431
msgid "Use underline"
msgstr "Використовувати підкреслення"
#: gtk/gtkaccellabel.c:143 gtk/gtkbutton.c:233 gtk/gtkexpander.c:329
#: gtk/gtklabel.c:767 gtk/gtkmenubutton.c:430
#: gtk/gtklabel.c:768 gtk/gtkmenubutton.c:430
msgid ""
"If set, an underline in the text indicates the next character should be used "
"for the mnemonic accelerator key"
@@ -744,7 +744,7 @@ msgstr ""
#: gtk/gtkdragicon.c:372 gtk/gtkexpander.c:366 gtk/gtkflowbox.c:513
#: gtk/gtkframe.c:190 gtk/gtklistbox.c:3455 gtk/gtknotebook.c:570
#: gtk/gtkoverlay.c:319 gtk/gtkpopover.c:1665 gtk/gtkrevealer.c:354
#: gtk/gtkscrolledwindow.c:758 gtk/gtksearchbar.c:316 gtk/gtkstack.c:381
#: gtk/gtkscrolledwindow.c:759 gtk/gtksearchbar.c:316 gtk/gtkstack.c:381
#: gtk/gtkviewport.c:390 gtk/gtkwindow.c:946 gtk/gtkwindowhandle.c:546
msgid "Child"
msgstr "Вкладений елемент"
@@ -752,7 +752,7 @@ msgstr "Вкладений елемент"
#: gtk/gtkaspectframe.c:167 gtk/gtkbutton.c:254 gtk/gtkexpander.c:367
#: gtk/gtkflowbox.c:514 gtk/gtkframe.c:191 gtk/gtklistbox.c:3456
#: gtk/gtkoverlay.c:320 gtk/gtkpopover.c:1666 gtk/gtkrevealer.c:355
#: gtk/gtkscrolledwindow.c:759 gtk/gtksearchbar.c:317 gtk/gtkviewport.c:391
#: gtk/gtkscrolledwindow.c:760 gtk/gtksearchbar.c:317 gtk/gtkviewport.c:391
#: gtk/gtkwindow.c:947 gtk/gtkwindowhandle.c:547
msgid "The child widget"
msgstr "Дочірній віджет"
@@ -806,7 +806,7 @@ msgid "The pages of the assistant."
msgstr "Сторінки помічника."
#: gtk/gtkbox.c:259 gtk/gtkboxlayout.c:730 gtk/gtkcellareabox.c:317
#: gtk/gtkiconview.c:472 gtk/gtktreeviewcolumn.c:304
#: gtk/gtkiconview.c:473 gtk/gtktreeviewcolumn.c:304
msgid "Spacing"
msgstr "Інтервал"
@@ -840,27 +840,27 @@ msgstr "Розподілити рівномірно за простором"
msgid "Spacing between widgets"
msgstr "Інтервал між віджетами"
#: gtk/gtkbuilder.c:313
#: gtk/gtkbuilder.c:311
msgid "Translation Domain"
msgstr "Домен перекладу"
#: gtk/gtkbuilder.c:314
#: gtk/gtkbuilder.c:312
msgid "The translation domain used by gettext"
msgstr "Домен перекладу, що використовується gettext"
#: gtk/gtkbuilder.c:325
#: gtk/gtkbuilder.c:323
msgid "Current object"
msgstr "Поточний об'єкт"
#: gtk/gtkbuilder.c:326
#: gtk/gtkbuilder.c:324
msgid "The object the builder is evaluating for"
msgstr "Об'єкт для оцінки збиральником"
#: gtk/gtkbuilder.c:337 gtk/gtkshortcutcontroller.c:551
#: gtk/gtkbuilder.c:335 gtk/gtkshortcutcontroller.c:550
msgid "Scope"
msgstr "Область"
#: gtk/gtkbuilder.c:338
#: gtk/gtkbuilder.c:336
msgid "The scope the builder is operating in"
msgstr "Область, у якій працює будівник"
@@ -871,7 +871,7 @@ msgid ""
msgstr "Текст віджета позначки в кнопці, якщо кнопка містить віджет позначки"
#: gtk/gtkbutton.c:239 gtk/gtkcombobox.c:671 gtk/gtkentry.c:469
#: gtk/gtkscrolledwindow.c:640
#: gtk/gtkscrolledwindow.c:641
msgid "Has Frame"
msgstr "Має рамку"
@@ -1187,10 +1187,10 @@ msgstr "Встановлення тла комірки"
msgid "Whether the cell background color is set"
msgstr "Чи встановлено колір тла комірки"
#: gtk/gtkcellrenderercombo.c:136 gtk/gtkfilterlistmodel.c:683
#: gtk/gtkcellrenderercombo.c:136 gtk/gtkfilterlistmodel.c:416
#: gtk/gtkflattenlistmodel.c:438 gtk/gtkmaplistmodel.c:404
#: gtk/gtkshortcutcontroller.c:539 gtk/gtkslicelistmodel.c:287
#: gtk/gtksortlistmodel.c:394 gtk/gtktreelistmodel.c:716
#: gtk/gtkshortcutcontroller.c:538 gtk/gtkslicelistmodel.c:287
#: gtk/gtksortlistmodel.c:338 gtk/gtktreelistmodel.c:716
msgid "Model"
msgstr "Модель"
@@ -1377,7 +1377,7 @@ msgstr "Розмітка"
msgid "Marked up text to render"
msgstr "Розмітка тексту до візуалізації"
#: gtk/gtkcellrenderertext.c:269 gtk/gtkentry.c:890 gtk/gtklabel.c:752
#: gtk/gtkcellrenderertext.c:269 gtk/gtkentry.c:890 gtk/gtklabel.c:753
#: gtk/gtktext.c:894
msgid "Attributes"
msgstr "Атрибути"
@@ -1536,7 +1536,7 @@ msgstr ""
"під час відтворення тексту. Якщо ви не розумієте цей параметр, то, напевно, "
"він вам не потрібний."
#: gtk/gtkcellrenderertext.c:446 gtk/gtklabel.c:872 gtk/gtkprogressbar.c:246
#: gtk/gtkcellrenderertext.c:446 gtk/gtklabel.c:873 gtk/gtkprogressbar.c:246
msgid "Ellipsize"
msgstr "Овал"
@@ -1549,15 +1549,15 @@ msgstr ""
"для показу всього рядка"
#: gtk/gtkcellrenderertext.c:463 gtk/gtkfilechooserbutton.c:524
#: gtk/gtklabel.c:890
#: gtk/gtklabel.c:891
msgid "Width In Characters"
msgstr "Ширина у символах"
#: gtk/gtkcellrenderertext.c:464 gtk/gtklabel.c:891
#: gtk/gtkcellrenderertext.c:464 gtk/gtklabel.c:892
msgid "The desired width of the label, in characters"
msgstr "Бажана ширина, у символах"
#: gtk/gtkcellrenderertext.c:483 gtk/gtklabel.c:924
#: gtk/gtkcellrenderertext.c:483 gtk/gtklabel.c:925
msgid "Maximum Width In Characters"
msgstr "Максимальна ширина в символах"
@@ -1772,12 +1772,12 @@ msgstr "Модель CellView"
msgid "The model for cell view"
msgstr "Модель для перегляду комірки"
#: gtk/gtkcellview.c:215 gtk/gtkentrycompletion.c:439 gtk/gtkiconview.c:583
#: gtk/gtkcellview.c:215 gtk/gtkentrycompletion.c:439 gtk/gtkiconview.c:584
#: gtk/gtktreepopover.c:213 gtk/gtktreeviewcolumn.c:423
msgid "Cell Area"
msgstr "Ділянка комірки"
#: gtk/gtkcellview.c:216 gtk/gtkentrycompletion.c:440 gtk/gtkiconview.c:584
#: gtk/gtkcellview.c:216 gtk/gtkentrycompletion.c:440 gtk/gtkiconview.c:585
#: gtk/gtktreepopover.c:214 gtk/gtktreeviewcolumn.c:424
msgid "The GtkCellArea used to layout cells"
msgstr "GtkCellArea для компонування комірок"
@@ -1891,7 +1891,7 @@ msgstr "Колір RGBA"
msgid "Color as RGBA"
msgstr "Колір як RGBA"
#: gtk/gtkcolorswatch.c:485 gtk/gtklabel.c:835 gtk/gtklistbox.c:3448
#: gtk/gtkcolorswatch.c:485 gtk/gtklabel.c:836 gtk/gtklistbox.c:3448
msgid "Selectable"
msgstr "Вибирається"
@@ -2283,11 +2283,11 @@ msgstr "Максимальна ширина у символах"
msgid "The desired maximum width of the entry, in characters"
msgstr "Бажана максимальна ширина поля введення, у символах"
#: gtk/gtkeditable.c:423 gtk/gtklabel.c:789
#: gtk/gtkeditable.c:423 gtk/gtklabel.c:790
msgid "X align"
msgstr "Вирівнювання за X"
#: gtk/gtkeditable.c:424 gtk/gtklabel.c:790
#: gtk/gtkeditable.c:424 gtk/gtklabel.c:791
msgid ""
"The horizontal alignment, from 0 (left) to 1 (right). Reversed for RTL "
"layouts."
@@ -2603,7 +2603,7 @@ msgstr "Піктограма емодзі"
msgid "Whether to show an icon for Emoji"
msgstr "Чи слід показувати піктограму емодзі"
#: gtk/gtkentry.c:928 gtk/gtklabel.c:954 gtk/gtkpasswordentry.c:418
#: gtk/gtkentry.c:928 gtk/gtklabel.c:955 gtk/gtkpasswordentry.c:418
#: gtk/gtktext.c:940 gtk/gtktextview.c:1078
msgid "Extra menu"
msgstr "Додаткове меню"
@@ -2636,7 +2636,7 @@ msgstr "Мінімальна довжина ключа"
msgid "Minimum length of the search key in order to look up matches"
msgstr "Мінімальна довжина ключа, при пошуку відповідностей"
#: gtk/gtkentrycompletion.c:354 gtk/gtkiconview.c:401
#: gtk/gtkentrycompletion.c:354 gtk/gtkiconview.c:402
msgid "Text column"
msgstr "Текстовий стовпчик"
@@ -2741,11 +2741,11 @@ msgstr "Чи розширювач був відкритий для показу
msgid "Text of the expanders label"
msgstr "Текст мітки розгортання"
#: gtk/gtkexpander.c:336 gtk/gtklabel.c:759 gtk/gtkmodelbutton.c:1149
#: gtk/gtkexpander.c:336 gtk/gtklabel.c:760 gtk/gtkmodelbutton.c:1149
msgid "Use markup"
msgstr "Використовувати розмітку"
#: gtk/gtkexpander.c:337 gtk/gtklabel.c:760
#: gtk/gtkexpander.c:337 gtk/gtklabel.c:761
msgid "The text of the label includes XML markup. See pango_parse_markup()"
msgstr "Текст позначки включає розмітку XML. Див. pango_parse_markup()"
@@ -2797,7 +2797,7 @@ msgstr "Дія"
msgid "The type of operation that the file selector is performing"
msgstr "Тип операції, яку виконує діалог вибору файлів"
#: gtk/gtkfilechooser.c:160 gtk/gtkfilterlistmodel.c:659
#: gtk/gtkfilechooser.c:160
msgid "Filter"
msgstr "Фільтр"
@@ -2854,24 +2854,27 @@ msgstr "Підзаголовок"
msgid "The human-readable name for this filter"
msgstr "Зручна для читання назва цього фільтра"
#: gtk/gtkfilterlistmodel.c:660
#| msgid "If a filter is set for this model"
msgid "The filter set for this model"
msgstr "Набір фільтрів для цієї моделі"
#: gtk/gtkfilterlistmodel.c:392
msgid "has filter"
msgstr "має фільтр"
#: gtk/gtkfilterlistmodel.c:671 gtk/gtkflattenlistmodel.c:426
#: gtk/gtkfilterlistmodel.c:393
msgid "If a filter is set for this model"
msgstr "Чи встановлено фільтр для цієї моделі"
#: gtk/gtkfilterlistmodel.c:404 gtk/gtkflattenlistmodel.c:426
#: gtk/gtkmaplistmodel.c:392 gtk/gtkpropertylookuplistmodel.c:376
#: gtk/gtkslicelistmodel.c:275 gtk/gtksortlistmodel.c:382
#: gtk/gtkslicelistmodel.c:275 gtk/gtksortlistmodel.c:326
msgid "Item type"
msgstr "Тип запису"
#: gtk/gtkfilterlistmodel.c:672 gtk/gtkflattenlistmodel.c:427
#: gtk/gtkfilterlistmodel.c:405 gtk/gtkflattenlistmodel.c:427
#: gtk/gtkmaplistmodel.c:393 gtk/gtkpropertylookuplistmodel.c:377
#: gtk/gtkslicelistmodel.c:276
msgid "The type of elements of this object"
msgstr "Тип елементів цього об'єкта"
#: gtk/gtkfilterlistmodel.c:684
#: gtk/gtkfilterlistmodel.c:417
msgid "The model being filtered"
msgstr "Модель, яка фільтрується"
@@ -2887,22 +2890,22 @@ msgstr "Перетворення дочірнього об'єкта із фік
msgid "The model being flattened"
msgstr "Модель, яка сплощується"
#: gtk/gtkflowbox.c:3578 gtk/gtkiconview.c:368 gtk/gtklistbox.c:488
#: gtk/gtkflowbox.c:3578 gtk/gtkiconview.c:369 gtk/gtklistbox.c:488
#: gtk/gtktreeselection.c:140
msgid "Selection mode"
msgstr "Режим виділення"
#: gtk/gtkflowbox.c:3579 gtk/gtkiconview.c:369 gtk/gtklistbox.c:489
#: gtk/gtkflowbox.c:3579 gtk/gtkiconview.c:370 gtk/gtklistbox.c:489
msgid "The selection mode"
msgstr "Режим виділення"
#: gtk/gtkflowbox.c:3592 gtk/gtkiconview.c:597 gtk/gtklistbox.c:496
#: gtk/gtktreeview.c:1203
#: gtk/gtkflowbox.c:3592 gtk/gtkiconview.c:598 gtk/gtklistbox.c:496
#: gtk/gtktreeview.c:1205
msgid "Activate on Single Click"
msgstr "Активувати одиночним клацанням"
#: gtk/gtkflowbox.c:3593 gtk/gtkiconview.c:598 gtk/gtklistbox.c:497
#: gtk/gtktreeview.c:1204
#: gtk/gtkflowbox.c:3593 gtk/gtkiconview.c:599 gtk/gtklistbox.c:497
#: gtk/gtktreeview.c:1206
msgid "Activate row on a single click"
msgstr "Активувати рядок одиночним клацанням"
@@ -3014,11 +3017,11 @@ msgstr "Параметри шрифту у форматі рядка"
msgid "Language for which features have been selected"
msgstr "Мова, для якої були вибрані параметри"
#: gtk/gtkfontchooserwidget.c:664
#: gtk/gtkfontchooserwidget.c:740
msgid "The tweak action"
msgstr "Дія коригування"
#: gtk/gtkfontchooserwidget.c:665
#: gtk/gtkfontchooserwidget.c:741
msgid "The toggle action to switch to the tweak page"
msgstr "Увімкнути дію для перемикання сторінки коригування"
@@ -3249,6 +3252,7 @@ msgid "Icon name"
msgstr "Назва піктограми"
#: gtk/gtkicontheme.c:3588
#| msgid "The icon name choosen during lookup"
msgid "The icon name chosen during lookup"
msgstr "Назва піктограми, яку вибрано під час пошуку"
@@ -3260,112 +3264,112 @@ msgstr "Є символічною"
msgid "If the icon is symbolic"
msgstr "Чи є піктограма символічною"
#: gtk/gtkiconview.c:385
#: gtk/gtkiconview.c:386
msgid "Pixbuf column"
msgstr "Стовпчик pixbuf"
#: gtk/gtkiconview.c:386
#: gtk/gtkiconview.c:387
msgid "Model column used to retrieve the icon pixbuf from"
msgstr ""
"Модель стовпця, що використовується для отримання зображення (pixbuf) значка"
#: gtk/gtkiconview.c:402
#: gtk/gtkiconview.c:403
msgid "Model column used to retrieve the text from"
msgstr "Модель стовпчика, що використовується для отримання з неї тексту"
#: gtk/gtkiconview.c:419
#: gtk/gtkiconview.c:420
msgid "Markup column"
msgstr "Розмітка стовпчика"
#: gtk/gtkiconview.c:420
#: gtk/gtkiconview.c:421
msgid "Model column used to retrieve the text if using Pango markup"
msgstr ""
"Модель стовпчика, що використовується для тримання з неї тексту, якщо "
"використовується розмітка pango"
#: gtk/gtkiconview.c:427
#: gtk/gtkiconview.c:428
msgid "Icon View Model"
msgstr "Модель Icon View"
#: gtk/gtkiconview.c:428
#: gtk/gtkiconview.c:429
msgid "The model for the icon view"
msgstr "Модель для перегляду у вигляді значків"
#: gtk/gtkiconview.c:442
#: gtk/gtkiconview.c:443
msgid "Number of columns"
msgstr "Кількість стовпчиків"
#: gtk/gtkiconview.c:443
#: gtk/gtkiconview.c:444
msgid "Number of columns to display"
msgstr "Кількість стовпчиків, які показуються"
#: gtk/gtkiconview.c:458
#: gtk/gtkiconview.c:459
msgid "Width for each item"
msgstr "Віджет для кожного елемента"
#: gtk/gtkiconview.c:459
#: gtk/gtkiconview.c:460
msgid "The width used for each item"
msgstr "Ширина, що використовується для кожного елемента"
#: gtk/gtkiconview.c:473
#: gtk/gtkiconview.c:474
msgid "Space which is inserted between cells of an item"
msgstr "Простір, що вставляється між комірками елемента"
#: gtk/gtkiconview.c:486
#: gtk/gtkiconview.c:487
msgid "Row Spacing"
msgstr "Міжрядковий інтервал"
#: gtk/gtkiconview.c:487
#: gtk/gtkiconview.c:488
msgid "Space which is inserted between grid rows"
msgstr "Простір, що вставляється між рядками сітки"
#: gtk/gtkiconview.c:500
#: gtk/gtkiconview.c:501
msgid "Column Spacing"
msgstr "Інтервал між стовпчиками"
#: gtk/gtkiconview.c:501
#: gtk/gtkiconview.c:502
msgid "Space which is inserted between grid columns"
msgstr "Простір, що вставляється між стовпчиками сітки"
#: gtk/gtkiconview.c:514
#: gtk/gtkiconview.c:515
msgid "Margin"
msgstr "Відступ"
#: gtk/gtkiconview.c:515
#: gtk/gtkiconview.c:516
msgid "Space which is inserted at the edges of the icon view"
msgstr "Простір, що вставляється на краях перегляду піктограми"
#: gtk/gtkiconview.c:528
#: gtk/gtkiconview.c:529
msgid "Item Orientation"
msgstr "Орієнтація пункту "
#: gtk/gtkiconview.c:529
#: gtk/gtkiconview.c:530
msgid ""
"How the text and icon of each item are positioned relative to each other"
msgstr ""
"Як текст та значки кожного елемента розташовуються один відносно іншого"
#: gtk/gtkiconview.c:543 gtk/gtktreeview.c:1069 gtk/gtktreeviewcolumn.c:379
#: gtk/gtkiconview.c:544 gtk/gtktreeview.c:1071 gtk/gtktreeviewcolumn.c:379
msgid "Reorderable"
msgstr "Дозволено перестановку"
#: gtk/gtkiconview.c:544 gtk/gtktreeview.c:1070
#: gtk/gtkiconview.c:545 gtk/gtktreeview.c:1072
msgid "View is reorderable"
msgstr "Перегляд можна перегрупувати"
#: gtk/gtkiconview.c:551 gtk/gtktreeview.c:1189
#: gtk/gtkiconview.c:552 gtk/gtktreeview.c:1191
msgid "Tooltip Column"
msgstr "Стовпчик підказки"
#: gtk/gtkiconview.c:552
#: gtk/gtkiconview.c:553
msgid "The column in the model containing the tooltip texts for the items"
msgstr "Стовпчик у моделі, що містить текстову підказку для елементів"
#: gtk/gtkiconview.c:567
#: gtk/gtkiconview.c:568
msgid "Item Padding"
msgstr "Доповнення елемента"
#: gtk/gtkiconview.c:568
#: gtk/gtkiconview.c:569
msgid "Padding around icon view items"
msgstr "Доповнення навколо значків"
@@ -3445,19 +3449,19 @@ msgstr "Чи потрібно додавати стандартну кнопку
msgid "Controls whether the info bar shows its contents or not"
msgstr "Керує тим, чи буде показано вміст інформаційної панелі"
#: gtk/gtklabel.c:746
#: gtk/gtklabel.c:747
msgid "The text of the label"
msgstr "Текст позначки"
#: gtk/gtklabel.c:753
#: gtk/gtklabel.c:754
msgid "A list of style attributes to apply to the text of the label"
msgstr "Список стильових ознак для застосування до тексту позначки"
#: gtk/gtklabel.c:773 gtk/gtktexttag.c:386 gtk/gtktextview.c:877
#: gtk/gtklabel.c:774 gtk/gtktexttag.c:386 gtk/gtktextview.c:877
msgid "Justification"
msgstr "Вирівнювання"
#: gtk/gtklabel.c:774
#: gtk/gtklabel.c:775
msgid ""
"The alignment of the lines in the text of the label relative to each other. "
"This does NOT affect the alignment of the label within its allocation. See "
@@ -3467,80 +3471,80 @@ msgstr ""
"вирівнювання мітки всередині вказаного для неї місця. З цього питання див. "
"GtkLabel::xalign"
#: gtk/gtklabel.c:805
#: gtk/gtklabel.c:806
msgid "Y align"
msgstr "Вирівнювання за Y"
#: gtk/gtklabel.c:806
#: gtk/gtklabel.c:807
msgid "The vertical alignment, from 0 (top) to 1 (bottom)"
msgstr "Вертикальне вирівнювання, від 0 (вгору) до 1 (вниз)"
#: gtk/gtklabel.c:813
#: gtk/gtklabel.c:814
msgid "Line wrap"
msgstr "Перенос рядків"
#: gtk/gtklabel.c:814
#: gtk/gtklabel.c:815
msgid "If set, wrap lines if the text becomes too wide"
msgstr "Якщо встановлено, занадто довгі рядки переносяться"
#: gtk/gtklabel.c:827
#: gtk/gtklabel.c:828
msgid "Line wrap mode"
msgstr "Режим переносу рядків"
#: gtk/gtklabel.c:828
#: gtk/gtklabel.c:829
msgid "If wrap is set, controls how linewrapping is done"
msgstr ""
"Якщо встановлено перенос рядків, контролює як виконувати перенос рядків"
#: gtk/gtklabel.c:836
#: gtk/gtklabel.c:837
msgid "Whether the label text can be selected with the mouse"
msgstr "Чи може текст позначки бути виділений використовуючи мишу"
#: gtk/gtklabel.c:842
#: gtk/gtklabel.c:843
msgid "Mnemonic key"
msgstr "Мнемонічна клавіша"
#: gtk/gtklabel.c:843
#: gtk/gtklabel.c:844
msgid "The mnemonic accelerator key for this label"
msgstr "Мнемонічна клавіша-прискорювач для цієї позначки"
#: gtk/gtklabel.c:850
#: gtk/gtklabel.c:851
msgid "Mnemonic widget"
msgstr "Мнемонічний віджет"
#: gtk/gtklabel.c:851
#: gtk/gtklabel.c:852
msgid "The widget to be activated when the labels mnemonic key is pressed"
msgstr ""
"Віджет, який буде активовано під час натиснення мнемонічної клавіші позначки"
#: gtk/gtklabel.c:873
#: gtk/gtklabel.c:874
msgid ""
"The preferred place to ellipsize the string, if the label does not have "
"enough room to display the entire string"
msgstr ""
"Бажане місце для овалу рядка, якщо для показу усього рядка не вистачає місця."
#: gtk/gtklabel.c:907
#: gtk/gtklabel.c:908
msgid "Single Line Mode"
msgstr "Режим одного рядка"
#: gtk/gtklabel.c:908
#: gtk/gtklabel.c:909
msgid "Whether the label is in single line mode"
msgstr "Чи знаходиться рядок у режимі одного рядка"
#: gtk/gtklabel.c:925
#: gtk/gtklabel.c:926
msgid "The desired maximum width of the label, in characters"
msgstr "Бажана максимальна ширина ярлика, у символах"
#: gtk/gtklabel.c:940
#: gtk/gtklabel.c:941
msgid "Number of lines"
msgstr "Кількість ліній"
#: gtk/gtklabel.c:941
#: gtk/gtklabel.c:942
msgid "The desired number of lines, when ellipsizing a wrapping label"
msgstr "Число рядків при скороченні мітки з використанням трьох крапок"
#: gtk/gtklabel.c:955 gtk/gtktext.c:941 gtk/gtktextview.c:1079
#: gtk/gtklabel.c:956 gtk/gtktext.c:941 gtk/gtktextview.c:1079
msgid "Menu model to append to the context menu"
msgstr "Модель меню для долучення до контекстного меню"
@@ -3815,7 +3819,7 @@ msgstr "Гучність"
msgid "Volume of the audio stream."
msgstr "Гучність відтворення потоку звукових даних."
#: gtk/gtkmenubutton.c:370 gtk/gtkpopovermenubar.c:593 gtk/gtkpopovermenu.c:517
#: gtk/gtkmenubutton.c:370 gtk/gtkpopovermenubar.c:595 gtk/gtkpopovermenu.c:518
msgid "Menu model"
msgstr "Модель меню"
@@ -4499,19 +4503,19 @@ msgstr "Мнемоніка видима"
msgid "Whether mnemonics are currently visible in this popover"
msgstr "Чи показано мнемоніку у цьому накладному меню"
#: gtk/gtkpopovermenubar.c:594
#: gtk/gtkpopovermenubar.c:596
msgid "The model from which the bar is made."
msgstr "Модель, на основі якої створено панель."
#: gtk/gtkpopovermenu.c:509
#: gtk/gtkpopovermenu.c:510
msgid "Visible submenu"
msgstr "Видиме підменю"
#: gtk/gtkpopovermenu.c:510
#: gtk/gtkpopovermenu.c:511
msgid "The name of the visible submenu"
msgstr "Назва видимого підменю"
#: gtk/gtkpopovermenu.c:518
#: gtk/gtkpopovermenu.c:519
msgid "The model from which the menu is made."
msgstr "Модель, для якої створено меню."
@@ -5050,105 +5054,105 @@ msgstr "Політика вертикальної прокрутки"
msgid "The GtkAdjustment that contains the current value of this scrollbar"
msgstr "Об'єкт GtkAdjustment, що містить поточне значення цієї смужки гортання"
#: gtk/gtkscrolledwindow.c:602
#: gtk/gtkscrolledwindow.c:603
msgid "Horizontal Adjustment"
msgstr "Горизонтальне вирівнювання"
#: gtk/gtkscrolledwindow.c:603
#: gtk/gtkscrolledwindow.c:604
msgid "The GtkAdjustment for the horizontal position"
msgstr "Об'єкт GtkAdjustment для горизонтальної позиції"
#: gtk/gtkscrolledwindow.c:609
#: gtk/gtkscrolledwindow.c:610
msgid "Vertical Adjustment"
msgstr "Вертикальне вирівнювання"
#: gtk/gtkscrolledwindow.c:610
#: gtk/gtkscrolledwindow.c:611
msgid "The GtkAdjustment for the vertical position"
msgstr "Об'єкт GtkAdjustment для вертикальної позиції"
#: gtk/gtkscrolledwindow.c:616
#: gtk/gtkscrolledwindow.c:617
msgid "Horizontal Scrollbar Policy"
msgstr "Правило горизонтальної прокрутки"
#: gtk/gtkscrolledwindow.c:617
#: gtk/gtkscrolledwindow.c:618
msgid "When the horizontal scrollbar is displayed"
msgstr "Коли показано горизонтальну панель прокрутки"
#: gtk/gtkscrolledwindow.c:624
#: gtk/gtkscrolledwindow.c:625
msgid "Vertical Scrollbar Policy"
msgstr "Правило вертикальної прокрутки"
#: gtk/gtkscrolledwindow.c:625
#: gtk/gtkscrolledwindow.c:626
msgid "When the vertical scrollbar is displayed"
msgstr "Коли показано вертикальну панель прокрутки"
#: gtk/gtkscrolledwindow.c:632
#: gtk/gtkscrolledwindow.c:633
msgid "Window Placement"
msgstr "Розміщення вікна"
#: gtk/gtkscrolledwindow.c:633
#: gtk/gtkscrolledwindow.c:634
msgid "Where the contents are located with respect to the scrollbars."
msgstr "Чи розташовувати вміст у відповідності зі смугами гортання."
#: gtk/gtkscrolledwindow.c:641
#: gtk/gtkscrolledwindow.c:642
msgid "Whether to draw a frame around the contents"
msgstr "Чи слід малювати рамку навколо вмісту"
#: gtk/gtkscrolledwindow.c:652
#: gtk/gtkscrolledwindow.c:653
msgid "Minimum Content Width"
msgstr "Мінімальна ширина вмісту"
#: gtk/gtkscrolledwindow.c:653
#: gtk/gtkscrolledwindow.c:654
msgid "The minimum width that the scrolled window will allocate to its content"
msgstr "Мінімальна ширина, яку вікна прокрутки виділять з його вмістом"
#: gtk/gtkscrolledwindow.c:664
#: gtk/gtkscrolledwindow.c:665
msgid "Minimum Content Height"
msgstr "Мінімальна висота вмісту"
#: gtk/gtkscrolledwindow.c:665
#: gtk/gtkscrolledwindow.c:666
msgid ""
"The minimum height that the scrolled window will allocate to its content"
msgstr "Мінімальна висота, яку вікна прокрутки виділять з його вмістом"
#: gtk/gtkscrolledwindow.c:677
#: gtk/gtkscrolledwindow.c:678
msgid "Kinetic Scrolling"
msgstr "Кінетичне прокручування"
#: gtk/gtkscrolledwindow.c:678
#: gtk/gtkscrolledwindow.c:679
msgid "Kinetic scrolling mode."
msgstr "Режим кінетичного прокручування."
#: gtk/gtkscrolledwindow.c:695
#: gtk/gtkscrolledwindow.c:696
msgid "Overlay Scrolling"
msgstr "Накладні смуги гортання"
#: gtk/gtkscrolledwindow.c:696
#: gtk/gtkscrolledwindow.c:697
msgid "Overlay scrolling mode"
msgstr "Режим накладних смуг гортання"
#: gtk/gtkscrolledwindow.c:707
#: gtk/gtkscrolledwindow.c:708
msgid "Maximum Content Width"
msgstr "Максимальна ширина вмісту"
#: gtk/gtkscrolledwindow.c:708
#: gtk/gtkscrolledwindow.c:709
msgid "The maximum width that the scrolled window will allocate to its content"
msgstr "Максимальна ширина, яку вікна зі смужками гортання матимуть для вмісту"
#: gtk/gtkscrolledwindow.c:719
#: gtk/gtkscrolledwindow.c:720
msgid "Maximum Content Height"
msgstr "Максимальна висота вмісту"
#: gtk/gtkscrolledwindow.c:720
#: gtk/gtkscrolledwindow.c:721
msgid ""
"The maximum height that the scrolled window will allocate to its content"
msgstr "Максимальна висота, яку вікна зі смужками гортання матимуть для вмісту"
#: gtk/gtkscrolledwindow.c:735 gtk/gtkscrolledwindow.c:736
#: gtk/gtkscrolledwindow.c:736 gtk/gtkscrolledwindow.c:737
msgid "Propagate Natural Width"
msgstr "Передавати природну ширину"
#: gtk/gtkscrolledwindow.c:751 gtk/gtkscrolledwindow.c:752
#: gtk/gtkscrolledwindow.c:752 gtk/gtkscrolledwindow.c:753
msgid "Propagate Natural Height"
msgstr "Передавати природну висоту"
@@ -5587,19 +5591,19 @@ msgstr "Чи показувати курсор у тексті"
msgid "Whether to use overlay scrollbars"
msgstr "Чи використовувати накладання смужок гортання"
#: gtk/gtkshortcutaction.c:942
#: gtk/gtkshortcutaction.c:935
msgid "Signal Name"
msgstr "Назва сигналу"
#: gtk/gtkshortcutaction.c:943
#: gtk/gtkshortcutaction.c:936
msgid "The name of the signal to emit"
msgstr "Назва сигналу, який слід надіслати"
#: gtk/gtkshortcutaction.c:1181 gtk/gtkshortcutsshortcut.c:718
#: gtk/gtkshortcutaction.c:1174 gtk/gtkshortcutsshortcut.c:718
msgid "Action Name"
msgstr "Назва дії"
#: gtk/gtkshortcutaction.c:1182
#: gtk/gtkshortcutaction.c:1175
msgid "The name of the action to activate"
msgstr "Назва дії для активації"
@@ -5623,21 +5627,21 @@ msgstr "Перемикач"
msgid "The trigger for this shortcut"
msgstr "Перемикач для цього скорочення"
#: gtk/gtkshortcutcontroller.c:526
#: gtk/gtkshortcutcontroller.c:525
msgid "Mnemonic modifers"
msgstr "Модифікатори мнемоніки"
#: gtk/gtkshortcutcontroller.c:527
#: gtk/gtkshortcutcontroller.c:526
msgid "The modifiers to be pressed to allow mnemonics activation"
msgstr ""
"Клавіші-модифікатори, які має бути натиснуто, щоб уможливити активацію "
"мнемоніки"
#: gtk/gtkshortcutcontroller.c:540
#: gtk/gtkshortcutcontroller.c:539
msgid "A list model to take shortcuts from"
msgstr "Модель списку, з якої слід брати скорочення"
#: gtk/gtkshortcutcontroller.c:552
#: gtk/gtkshortcutcontroller.c:551
msgid "What scope the shortcuts will be handled in"
msgstr "Область, у якій оброблятимуться скорочення"
@@ -5819,21 +5823,19 @@ msgstr "Зсув зрізу"
msgid "Maximum size of slice"
msgstr "Максимальний розмір зрізу"
#: gtk/gtksortlistmodel.c:370
#| msgid "Sort order"
msgid "Sorter"
msgstr "Сортувальник"
#: gtk/gtksortlistmodel.c:314
msgid "has sort"
msgstr "має упорядкування"
#: gtk/gtksortlistmodel.c:371
#| msgid "The trigger for this shortcut"
msgid "The sorter for this model"
msgstr "Сортувальник для цієї моделі"
#: gtk/gtksortlistmodel.c:315
msgid "If a sort function is set for this model"
msgstr "Чи встановлено функцію упорядкування для цієї моделі"
#: gtk/gtksortlistmodel.c:383
#: gtk/gtksortlistmodel.c:327
msgid "The type of items of this list"
msgstr "Тип пунктів у цьому списку"
#: gtk/gtksortlistmodel.c:395
#: gtk/gtksortlistmodel.c:339
msgid "The model being sorted"
msgstr "Модель, яка зберігається"
@@ -6748,126 +6750,126 @@ msgstr "модель"
msgid "The model for the popover"
msgstr "Модель для накладного меню"
#: gtk/gtktreeview.c:1041
#: gtk/gtktreeview.c:1043
msgid "TreeView Model"
msgstr "Модель TreeView"
#: gtk/gtktreeview.c:1042
#: gtk/gtktreeview.c:1044
msgid "The model for the tree view"
msgstr "Модель для перегляду у вигляді дерева"
#: gtk/gtktreeview.c:1048
#: gtk/gtktreeview.c:1050
msgid "Headers Visible"
msgstr "Заголовки видимі"
#: gtk/gtktreeview.c:1049
#: gtk/gtktreeview.c:1051
msgid "Show the column header buttons"
msgstr "Показувати кнопки заголовків стовпчиків"
#: gtk/gtktreeview.c:1055
#: gtk/gtktreeview.c:1057
msgid "Headers Clickable"
msgstr "Заголовки натискаються"
#: gtk/gtktreeview.c:1056
#: gtk/gtktreeview.c:1058
msgid "Column headers respond to click events"
msgstr "Заголовки стовпчиків відповідають на події клацання"
#: gtk/gtktreeview.c:1062
#: gtk/gtktreeview.c:1064
msgid "Expander Column"
msgstr "Колонка-розширювач"
#: gtk/gtktreeview.c:1063
#: gtk/gtktreeview.c:1065
msgid "Set the column for the expander column"
msgstr "Встановити колонку для розширювача"
#: gtk/gtktreeview.c:1076
#: gtk/gtktreeview.c:1078
msgid "Enable Search"
msgstr "Дозволено пошук"
#: gtk/gtktreeview.c:1077
#: gtk/gtktreeview.c:1079
msgid "View allows user to search through columns interactively"
msgstr ""
"Перегляд дозволяє користувачу виконувати пошук у стовпчиках в інтерактивному "
"режимі"
#: gtk/gtktreeview.c:1083
#: gtk/gtktreeview.c:1085
msgid "Search Column"
msgstr "Стовпчик пошуку"
#: gtk/gtktreeview.c:1084
#: gtk/gtktreeview.c:1086
msgid "Model column to search through during interactive search"
msgstr ""
"Стовпчик моделі, за яким слід виконати інтерактивний пошук в міру набору"
#: gtk/gtktreeview.c:1100
#: gtk/gtktreeview.c:1102
msgid "Fixed Height Mode"
msgstr "Режим фіксованої висоти"
#: gtk/gtktreeview.c:1101
#: gtk/gtktreeview.c:1103
msgid "Speeds up GtkTreeView by assuming that all rows have the same height"
msgstr "Прискорює GtkTreeView, вважаючи що всі рядки мають ту ж саму висоту"
#: gtk/gtktreeview.c:1118
#: gtk/gtktreeview.c:1120
msgid "Hover Selection"
msgstr "Курсорне виділення"
#: gtk/gtktreeview.c:1119
#: gtk/gtktreeview.c:1121
msgid "Whether the selection should follow the pointer"
msgstr "Чи повинно виділення слідувати за вказівником"
#: gtk/gtktreeview.c:1135
#: gtk/gtktreeview.c:1137
msgid "Hover Expand"
msgstr "Курсорне розширення"
#: gtk/gtktreeview.c:1136
#: gtk/gtktreeview.c:1138
msgid ""
"Whether rows should be expanded/collapsed when the pointer moves over them"
msgstr ""
"Чи повинні рядки розгортатись/згортатись при переміщенні над ними вказівника"
#: gtk/gtktreeview.c:1147
#: gtk/gtktreeview.c:1149
msgid "Show Expanders"
msgstr "Показувати розширювачі"
#: gtk/gtktreeview.c:1148
#: gtk/gtktreeview.c:1150
msgid "View has expanders"
msgstr "Віджет містить розширювачі"
#: gtk/gtktreeview.c:1159
#: gtk/gtktreeview.c:1161
msgid "Level Indentation"
msgstr "Вирівнювання позначок"
#: gtk/gtktreeview.c:1160
#: gtk/gtktreeview.c:1162
msgid "Extra indentation for each level"
msgstr "Додаткове вирівнювання для кожного рівня"
#: gtk/gtktreeview.c:1167
#: gtk/gtktreeview.c:1169
msgid "Rubber Banding"
msgstr "Гумова стрічка"
#: gtk/gtktreeview.c:1168
#: gtk/gtktreeview.c:1170
msgid ""
"Whether to enable selection of multiple items by dragging the mouse pointer"
msgstr ""
"Чи дозволяти виділення кількох елементів перетягуванням вказівника миші"
#: gtk/gtktreeview.c:1174
#: gtk/gtktreeview.c:1176
msgid "Enable Grid Lines"
msgstr "Увімкнути лінії сітки"
#: gtk/gtktreeview.c:1175
#: gtk/gtktreeview.c:1177
msgid "Whether grid lines should be drawn in the tree view"
msgstr "Чи слід показувати лінії ґраток у віджеті"
#: gtk/gtktreeview.c:1182
#: gtk/gtktreeview.c:1184
msgid "Enable Tree Lines"
msgstr "Увімкнути лінії рівня вкладення"
#: gtk/gtktreeview.c:1183
#: gtk/gtktreeview.c:1185
msgid "Whether tree lines should be drawn in the tree view"
msgstr "Чи слід показувати лінії рівня вкладення у віджеті"
#: gtk/gtktreeview.c:1190
#: gtk/gtktreeview.c:1192
msgid "The column in the model containing the tooltip texts for the rows"
msgstr ""
"Стовпчик у моделі джерела даних, що містить текстові підказки для рядків"
@@ -7464,15 +7466,6 @@ msgstr "Назва профілю кольору"
msgid "The title of the color profile to use"
msgstr "Заголовок вікна вибору профілю кольору"
#~ msgid "has filter"
#~ msgstr "має фільтр"
#~ msgid "has sort"
#~ msgstr "має упорядкування"
#~ msgid "If a sort function is set for this model"
#~ msgstr "Чи встановлено функцію упорядкування для цієї моделі"
#~ msgid "Application menu"
#~ msgstr "Меню програм"

660
po/uk.po

File diff suppressed because it is too large Load Diff

View File

@@ -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 ();

View File

@@ -39,6 +39,7 @@ tests = [
['listbox'],
['main'],
['maplistmodel'],
['multiselection'],
['notify'],
['no-gtk-init'],
['object'],

View 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 ();
}

View File

@@ -425,7 +425,7 @@ test_autoselect (void)
assert_model (selection, "1");
assert_changes (selection, "+0");
assert_selection (selection, "1");
assert_selection_changes (selection, "0:1");
assert_selection_changes (selection, "");
splice (store, 0, 1, (guint[]) { 7, 8, 9 }, 3);
assert_model (selection, "7 8 9");