forked from AuroraMiddleware/gtk
Merge branch 'misc-multiselection' into 'master'
Misc multiselection See merge request GNOME/gtk!2055
This commit is contained in:
commit
1567db1f02
@ -350,6 +350,9 @@ gtk_selection_model_select_range
|
||||
gtk_selection_model_unselect_range
|
||||
gtk_selection_model_select_all
|
||||
gtk_selection_model_unselect_all
|
||||
GtkSelectionCallback
|
||||
gtk_selection_model_select_callback
|
||||
gtk_selection_model_unselect_callback
|
||||
gtk_selection_model_query_range
|
||||
<SUBSECTION>
|
||||
gtk_selection_model_selection_changed
|
||||
|
@ -23,7 +23,6 @@
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
#include "gtksingleselection.h"
|
||||
#include "gtkset.h"
|
||||
|
||||
/**
|
||||
@ -34,6 +33,12 @@
|
||||
*
|
||||
* GtkMultiSelection is an implementation of the #GtkSelectionModel interface
|
||||
* that allows selecting multiple elements.
|
||||
*
|
||||
* Note that due to the way the selection is stored, newly added items are
|
||||
* always unselected, even if they were just removed from the model, and were
|
||||
* selected before. In particular this means that changing the sort order of
|
||||
* an underlying sort model will clear the selection. In other words, the
|
||||
* selection is *not persistent*.
|
||||
*/
|
||||
|
||||
struct _GtkMultiSelection
|
||||
@ -175,32 +180,40 @@ gtk_multi_selection_add_or_remove (GtkSelectionModel *model,
|
||||
gpointer data)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
guint pos, start, n;
|
||||
guint pos, start, n_items;
|
||||
gboolean in;
|
||||
guint min, max;
|
||||
guint n;
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
|
||||
min = G_MAXUINT;
|
||||
max = 0;
|
||||
|
||||
pos = 0;
|
||||
do
|
||||
for (pos = 0; pos < n; pos = start + n_items)
|
||||
{
|
||||
callback (pos, &start, &n, &in, data);
|
||||
callback (pos, &start, &n_items, &in, data);
|
||||
|
||||
if (n_items == 0)
|
||||
break;
|
||||
|
||||
g_assert (start <= pos && pos < start + n_items);
|
||||
|
||||
if (in)
|
||||
{
|
||||
if (start < min)
|
||||
min = start;
|
||||
if (start + n - 1 > max)
|
||||
max = start + n - 1;
|
||||
if (start + n_items - 1 > max)
|
||||
max = start + n_items - 1;
|
||||
|
||||
if (add)
|
||||
gtk_set_add_range (self->selected, start, n);
|
||||
gtk_set_add_range (self->selected, start, n_items);
|
||||
else
|
||||
gtk_set_remove_range (self->selected, start, n);
|
||||
gtk_set_remove_range (self->selected, start, n_items);
|
||||
}
|
||||
pos = start + n;
|
||||
|
||||
pos = start + n_items;
|
||||
}
|
||||
while (n > 0);
|
||||
|
||||
if (min <= max)
|
||||
gtk_selection_model_selection_changed (model, min, max - min + 1);
|
||||
|
@ -342,6 +342,15 @@ gtk_selection_model_unselect_all (GtkSelectionModel *model)
|
||||
return iface->unselect_all (model);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_selection_model_select_callback:
|
||||
* @model: a #GtkSelectionModel
|
||||
* @callback: a #GtkSelectionCallback to determine items to select
|
||||
* @data: data to pass to @callback
|
||||
*
|
||||
* Requests to select all items for which @callback returns
|
||||
* @selected as TRUE.
|
||||
*/
|
||||
gboolean
|
||||
gtk_selection_model_select_callback (GtkSelectionModel *model,
|
||||
GtkSelectionCallback callback,
|
||||
@ -352,6 +361,15 @@ gtk_selection_model_select_callback (GtkSelectionModel *model,
|
||||
return GTK_SELECTION_MODEL_GET_IFACE (model)->select_callback (model, callback, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_selection_model_unselect_callback:
|
||||
* @model: a #GtkSelectionModel
|
||||
* @callback: a #GtkSelectionCallback to determine items to select
|
||||
* @data: data to pass to @callback
|
||||
*
|
||||
* Requests to unselect all items for which @callback returns
|
||||
* @selected as TRUE.
|
||||
*/
|
||||
gboolean
|
||||
gtk_selection_model_unselect_callback (GtkSelectionModel *model,
|
||||
GtkSelectionCallback callback,
|
||||
|
@ -33,6 +33,29 @@ G_BEGIN_DECLS
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_INTERFACE (GtkSelectionModel, gtk_selection_model, GTK, SELECTION_MODEL, GListModel)
|
||||
|
||||
/**
|
||||
* GtkSelectionCallback:
|
||||
* @position: the position to query
|
||||
* @start_range: (out): returns the position of the first element of the range
|
||||
* @n_items: (out): returns the size of the range
|
||||
* @selected: (out): returns whether items in @range are selected
|
||||
* @data: callback data
|
||||
*
|
||||
* Callback type for determining items to operate on with
|
||||
* gtk_selection_model_select_callback() or
|
||||
* gtk_selection_model_unselect_callback().
|
||||
*
|
||||
* The callback determines a range of consecutive items around
|
||||
* @position which should either all
|
||||
* be changed, in which case @selected is set to %TRUE, or all not
|
||||
* be changed, in which case @selected is set to %FALSE.
|
||||
*
|
||||
* @start_range and @n_items are set to return the range.
|
||||
*
|
||||
* The callback will be called repeatedly to find all ranges
|
||||
* to operate on until it has exhausted the items of the model,
|
||||
* or until it returns an empty range (ie @n_items == 0).
|
||||
*/
|
||||
typedef void (* GtkSelectionCallback) (guint position,
|
||||
guint *start_range,
|
||||
guint *n_items,
|
||||
@ -128,6 +151,7 @@ GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_select_callback (GtkSelectionModel *model,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_unselect_callback (GtkSelectionModel *model,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data);
|
||||
|
@ -30,9 +30,14 @@
|
||||
* @Title: GtkSingleSelection
|
||||
* @see_also: #GtkSelectionModel
|
||||
*
|
||||
* GtkSingleSelection is an implementation of the #GtkSelectionModel interface
|
||||
* GtkSingleSelection is an implementation of the #GtkSelectionModel interface
|
||||
* that allows selecting a single element. It is the default selection method
|
||||
* used by list widgets in GTK.
|
||||
*
|
||||
* Note that the selection is *persistent* -- if the selected item is removed
|
||||
* and re-added in the same ::items-changed emission, it stays selected. In
|
||||
* particular, this means that changing the sort order of an underlying sort
|
||||
* model will preserve the selection.
|
||||
*/
|
||||
struct _GtkSingleSelection
|
||||
{
|
||||
|
@ -372,6 +372,10 @@ test_selection (void)
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
/* Verify that select_range with exclusive = TRUE
|
||||
* sends a selection-changed signal that covers
|
||||
* preexisting items that got unselected
|
||||
*/
|
||||
static void
|
||||
test_select_range (void)
|
||||
{
|
||||
@ -403,6 +407,108 @@ test_select_range (void)
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
/* Test that removing and readding items
|
||||
* clears the selected state.
|
||||
*/
|
||||
static void
|
||||
test_readd (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
GListStore *store;
|
||||
gboolean ret;
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
|
||||
selection = new_model (store);
|
||||
assert_model (selection, "1 2 3 4 5");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_range (selection, 2, 2, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_model (selection, "1 2 3 4 5");
|
||||
assert_selection (selection, "3 4");
|
||||
assert_selection_changes (selection, "2:2");
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (store), 1, 3, 3);
|
||||
assert_changes (selection, "1-3+3");
|
||||
assert_selection (selection, "");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
guint start;
|
||||
guint n;
|
||||
gboolean in;
|
||||
} SelectionData;
|
||||
|
||||
static void
|
||||
select_some (guint position,
|
||||
guint *start,
|
||||
guint *n,
|
||||
gboolean *selected,
|
||||
gpointer data)
|
||||
{
|
||||
SelectionData *sdata = data;
|
||||
guint i;
|
||||
|
||||
for (i = 0; sdata[i].n != 0; i++)
|
||||
{
|
||||
if (sdata[i].start <= position &&
|
||||
position < sdata[i].start + sdata[i].n)
|
||||
break;
|
||||
}
|
||||
|
||||
*start = sdata[i].start;
|
||||
*n = sdata[i].n;
|
||||
*selected = sdata[i].in;
|
||||
}
|
||||
|
||||
static void
|
||||
test_callback (void)
|
||||
{
|
||||
GtkSelectionModel *selection;
|
||||
gboolean ret;
|
||||
GListStore *store;
|
||||
SelectionData data[] = {
|
||||
{ 0, 2, FALSE },
|
||||
{ 2, 3, TRUE },
|
||||
{ 5, 2, FALSE },
|
||||
{ 6, 3, TRUE },
|
||||
{ 9, 1, FALSE },
|
||||
{ 0, 0, FALSE }
|
||||
};
|
||||
|
||||
SelectionData more_data[] = {
|
||||
{ 0, 3, FALSE },
|
||||
{ 3, 1, TRUE },
|
||||
{ 4, 3, FALSE },
|
||||
{ 7, 1, TRUE },
|
||||
{ 0, 0, FALSE }
|
||||
};
|
||||
|
||||
store = new_store (1, 10, 1);
|
||||
|
||||
selection = new_model (store);
|
||||
assert_model (selection, "1 2 3 4 5 6 7 8 9 10");
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_callback (selection, select_some, data);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "3 4 5 7 8 9");
|
||||
assert_selection_changes (selection, "2:7");
|
||||
|
||||
ret = gtk_selection_model_unselect_callback (selection, select_some, more_data);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "3 5 7 9");
|
||||
assert_selection_changes (selection, "3:5");
|
||||
|
||||
g_object_unref (store);
|
||||
g_object_unref (selection);
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
@ -421,6 +527,8 @@ main (int argc, char *argv[])
|
||||
#endif
|
||||
g_test_add_func ("/multiselection/selection", test_selection);
|
||||
g_test_add_func ("/multiselection/select-range", test_select_range);
|
||||
g_test_add_func ("/multiselection/readd", test_readd);
|
||||
g_test_add_func ("/multiselection/callback", test_callback);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user