forked from AuroraMiddleware/gtk
Merge branch 'wip/otte/bitset' into 'master'
Improve selection handling API for rubberbanding See merge request GNOME/gtk!2086
This commit is contained in:
commit
e04191a5ea
@ -30,7 +30,6 @@ struct _GtkColor
|
||||
char *name;
|
||||
GdkRGBA color;
|
||||
int h, s, v;
|
||||
gboolean selected;
|
||||
};
|
||||
|
||||
enum {
|
||||
@ -43,7 +42,6 @@ enum {
|
||||
PROP_HUE,
|
||||
PROP_SATURATION,
|
||||
PROP_VALUE,
|
||||
PROP_SELECTED,
|
||||
|
||||
N_COLOR_PROPS
|
||||
};
|
||||
@ -206,10 +204,6 @@ gtk_color_get_property (GObject *object,
|
||||
g_value_set_int (value, self->v);
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
g_value_set_boolean (value, self->selected);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@ -239,10 +233,6 @@ gtk_color_set_property (GObject *object,
|
||||
self->v = round (100 * v);
|
||||
break;
|
||||
|
||||
case PROP_SELECTED:
|
||||
self->selected = g_value_get_boolean (value);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
|
||||
break;
|
||||
@ -284,8 +274,6 @@ gtk_color_class_init (GtkColorClass *klass)
|
||||
g_param_spec_int ("saturation", NULL, NULL, 0, 100, 0, G_PARAM_READABLE);
|
||||
color_properties[PROP_VALUE] =
|
||||
g_param_spec_int ("value", NULL, NULL, 0, 100, 0, G_PARAM_READABLE);
|
||||
color_properties[PROP_SELECTED] =
|
||||
g_param_spec_boolean ("selected", NULL, NULL, FALSE, G_PARAM_READWRITE);
|
||||
|
||||
g_object_class_install_properties (gobject_class, N_COLOR_PROPS, color_properties);
|
||||
}
|
||||
@ -673,7 +661,7 @@ create_color_grid (void)
|
||||
|
||||
model = G_LIST_MODEL (gtk_sort_list_model_new (gtk_color_list_new (0), NULL));
|
||||
|
||||
selection = G_LIST_MODEL (gtk_property_selection_new (model, "selected"));
|
||||
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);
|
||||
|
@ -48,6 +48,7 @@
|
||||
|
||||
<chapter id="Lists">
|
||||
<title>GListModel support</title>
|
||||
<xi:include href="xml/gtkbitset.xml" />
|
||||
<xi:include href="xml/gtkexpression.xml" />
|
||||
<xi:include href="xml/gtkfilterlistmodel.xml" />
|
||||
<section>
|
||||
@ -72,7 +73,6 @@
|
||||
<xi:include href="xml/gtknoselection.xml" />
|
||||
<xi:include href="xml/gtksingleselection.xml" />
|
||||
<xi:include href="xml/gtkmultiselection.xml" />
|
||||
<xi:include href="xml/gtkpropertyselection.xml" />
|
||||
</section>
|
||||
<xi:include href="xml/gtkbookmarklist.xml" />
|
||||
<xi:include href="xml/gtkdirectorylist.xml" />
|
||||
|
@ -339,21 +339,65 @@ gtk_list_box_get_type
|
||||
gtk_list_box_row_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkbitset</FILE>
|
||||
<TITLE>GtkBitset</TITLE>
|
||||
GtkBitset
|
||||
gtk_bitset_ref
|
||||
gtk_bitset_unref
|
||||
gtk_bitset_new_empty
|
||||
gtk_bitset_copy
|
||||
<SUBSECTION>
|
||||
gtk_bitset_contains
|
||||
gtk_bitset_is_empty
|
||||
gtk_bitset_equals
|
||||
gtk_bitset_get_minimum
|
||||
gtk_bitset_get_maximum
|
||||
<SUBSECTION>
|
||||
gtk_bitset_remove_all
|
||||
gtk_bitset_add
|
||||
gtk_bitset_remove
|
||||
gtk_bitset_add_range
|
||||
gtk_bitset_remove_range
|
||||
gtk_bitset_add_range_closed
|
||||
gtk_bitset_remove_range_closed
|
||||
gtk_bitset_add_rectangle
|
||||
gtk_bitset_remove_rectangle
|
||||
gtk_bitset_union
|
||||
gtk_bitset_intersect
|
||||
gtk_bitset_subtract
|
||||
gtk_bitset_difference
|
||||
gtk_bitset_shift_left
|
||||
gtk_bitset_shift_right
|
||||
gtk_bitset_slice
|
||||
<SUBSECTION>
|
||||
GtkBitsetIter
|
||||
gtk_bitset_iter_init_first
|
||||
gtk_bitset_iter_init_last
|
||||
gtk_bitset_iter_init_at
|
||||
gtk_bitset_iter_next
|
||||
gtk_bitset_iter_previous
|
||||
gtk_bitset_iter_get_value
|
||||
gtk_bitset_iter_is_valid
|
||||
<SUBSECTION Private>
|
||||
GTK_TYPE_BITSET
|
||||
gtk_bitset_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkselectionmodel</FILE>
|
||||
<TITLE>GtkSelectionModel</TITLE>
|
||||
GtkSelectionModel
|
||||
gtk_selection_model_is_selected
|
||||
gtk_selection_model_get_selection
|
||||
gtk_selection_model_get_selection_in_range
|
||||
gtk_selection_model_select_item
|
||||
gtk_selection_model_unselect_item
|
||||
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
|
||||
gtk_selection_model_set_selection
|
||||
<SUBSECTION>
|
||||
gtk_selection_model_selection_changed
|
||||
<SUBSECTION Standard>
|
||||
@ -404,17 +448,6 @@ gtk_multi_selection_new
|
||||
gtk_multi_selection_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtkpropertyselection</FILE>
|
||||
<TITLE>GtkPropertySelection</TITLE>
|
||||
GtkPropertySelection
|
||||
gtk_property_selection_new
|
||||
gtk_property_selection_get_model
|
||||
gtk_property_selection_get_property
|
||||
<SUBSECTION Private>
|
||||
gtk_property_selection_get_type
|
||||
</SECTION>
|
||||
|
||||
<SECTION>
|
||||
<FILE>gtklistitem</FILE>
|
||||
<TITLE>GtkListItem</TITLE>
|
||||
|
@ -18,6 +18,7 @@ gtk_aspect_frame_get_type
|
||||
gtk_assistant_get_type
|
||||
gtk_assistant_page_get_type
|
||||
gtk_bin_layout_get_type
|
||||
gtk_bitset_get_type
|
||||
gtk_bookmark_list_get_type
|
||||
gtk_box_get_type
|
||||
gtk_box_layout_get_type
|
||||
@ -171,7 +172,6 @@ gtk_print_operation_preview_get_type
|
||||
gtk_print_settings_get_type
|
||||
@DISABLE_ON_W32@gtk_print_unix_dialog_get_type
|
||||
gtk_progress_bar_get_type
|
||||
gtk_property_selection_get_type
|
||||
gtk_radio_button_get_type
|
||||
gtk_range_get_type
|
||||
gtk_recent_manager_get_type
|
||||
|
@ -176,7 +176,6 @@ private_headers = [
|
||||
'gtkroundedboxprivate.h',
|
||||
'gtkscalerprivate.h',
|
||||
'gtksearchentryprivate.h',
|
||||
'gtkset.h',
|
||||
'gtksettingsprivate.h',
|
||||
'gtkshortcutcontrollerprivate.h',
|
||||
'gtkshortcutsshortcutprivate.h',
|
||||
@ -216,6 +215,7 @@ private_headers = [
|
||||
'gtkwin32themeprivate.h',
|
||||
'gtkwindowprivate.h',
|
||||
'gtk-text-input-client-protocol.h',
|
||||
'roaring.h',
|
||||
]
|
||||
|
||||
images = [
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include <gtk/gtkaspectframe.h>
|
||||
#include <gtk/gtkassistant.h>
|
||||
#include <gtk/gtkbinlayout.h>
|
||||
#include <gtk/gtkbitset.h>
|
||||
#include <gtk/gtkbookmarklist.h>
|
||||
#include <gtk/gtkborder.h>
|
||||
#include <gtk/gtkboxlayout.h>
|
||||
@ -200,7 +201,6 @@
|
||||
#include <gtk/gtkprintoperationpreview.h>
|
||||
#include <gtk/gtkprintsettings.h>
|
||||
#include <gtk/gtkprogressbar.h>
|
||||
#include <gtk/gtkpropertyselection.h>
|
||||
#include <gtk/gtkradiobutton.h>
|
||||
#include <gtk/gtkrange.h>
|
||||
#include <gtk/gtkrecentmanager.h>
|
||||
|
865
gtk/gtkbitset.c
Normal file
865
gtk/gtkbitset.c
Normal file
@ -0,0 +1,865 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* 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: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include "gtkbitset.h"
|
||||
|
||||
#include "roaring.c"
|
||||
|
||||
/**
|
||||
* SECTION:gtkbitset
|
||||
* @Short_description: Sets of integers
|
||||
* @Title: GtkBitset
|
||||
* @See_also: GtkSelectionModel
|
||||
*
|
||||
* #GtkBitset is a data structure for representing a set of unsigned integers.
|
||||
* Another name for this data structure is "bitmap".
|
||||
*
|
||||
* This version is based on [roaring bitmaps](https://roaringbitmap.org/).
|
||||
*
|
||||
* A bitset allows adding a set of integers and provides support for set operations
|
||||
* like unions, intersections and checks for equality or if a value is contained
|
||||
* in the set. #GtkBitset also contains various functions to query metadata about
|
||||
* the bitset, such as the minimum or maximum values or its size.
|
||||
*
|
||||
* The fastest way to iterate values in a bitset is #GtkBitsetIter which allows
|
||||
* quick iteration of all the values in a bitset.
|
||||
*
|
||||
* The main use case #GtkBitset is implementing complex selections for #GtkSelectionModel.
|
||||
*/
|
||||
|
||||
/**
|
||||
* GtkBitset: (ref-func gtk_bitset_ref) (unref-func gtk_bitset_unref)
|
||||
*
|
||||
* The `GtkBitset` structure contains only private data.
|
||||
*/
|
||||
struct _GtkBitset
|
||||
{
|
||||
int ref_count;
|
||||
roaring_bitmap_t roaring;
|
||||
};
|
||||
|
||||
|
||||
G_DEFINE_BOXED_TYPE (GtkBitset, gtk_bitset,
|
||||
gtk_bitset_ref,
|
||||
gtk_bitset_unref)
|
||||
|
||||
/**
|
||||
* gtk_bitset_ref:
|
||||
* @self: (allow-none): a #GtkBitset
|
||||
*
|
||||
* Acquires a reference on the given #GtkBitset.
|
||||
*
|
||||
* Returns: (transfer none): the #GtkBitset with an additional reference
|
||||
*/
|
||||
GtkBitset *
|
||||
gtk_bitset_ref (GtkBitset *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
self->ref_count += 1;
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_unref:
|
||||
* @self: (allow-none): a #GtkBitset
|
||||
*
|
||||
* Releases a reference on the given #GtkBitset.
|
||||
*
|
||||
* If the reference was the last, the resources associated to the @self are
|
||||
* freed.
|
||||
*/
|
||||
void
|
||||
gtk_bitset_unref (GtkBitset *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (self->ref_count > 0);
|
||||
|
||||
self->ref_count -= 1;
|
||||
if (self->ref_count > 0)
|
||||
return;
|
||||
|
||||
ra_clear (&self->roaring.high_low_container);
|
||||
g_slice_free (GtkBitset, self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_contains:
|
||||
* @self: a #GtkBitset
|
||||
* @value: the value to check
|
||||
*
|
||||
* Checks if the given @value has been added to @bitset
|
||||
*
|
||||
* Returns: %TRUE if @self contains @value
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_contains (const GtkBitset *self,
|
||||
guint value)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
|
||||
return roaring_bitmap_contains (&self->roaring, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_is_empty:
|
||||
* @self: a #GtkBitset
|
||||
*
|
||||
* Check if no value is contained in bitset.
|
||||
*
|
||||
* Returns: %TRUE if @self is empty
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_is_empty (const GtkBitset *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, TRUE);
|
||||
|
||||
return roaring_bitmap_is_empty (&self->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_equals:
|
||||
* @self: a #GtkBitset
|
||||
* @other: another #GtkBitset
|
||||
*
|
||||
* Returns %TRUE if @self and @other contain the same values.
|
||||
*
|
||||
* Returns: %TRUE if @self and @other contain the same values
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_equals (const GtkBitset *self,
|
||||
const GtkBitset *other)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, other == NULL);
|
||||
g_return_val_if_fail (other != NULL, FALSE);
|
||||
|
||||
if (self == other)
|
||||
return TRUE;
|
||||
|
||||
return roaring_bitmap_equals (&self->roaring, &other->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_get_minimum:
|
||||
* @self: a #GtkBitset
|
||||
*
|
||||
* Returns the smallest value in @self. If @self is empty,
|
||||
* G_MAXUINT is returned.
|
||||
*
|
||||
* Returns: The smallest value in @self
|
||||
**/
|
||||
guint
|
||||
gtk_bitset_get_minimum (const GtkBitset *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, G_MAXUINT);
|
||||
|
||||
return roaring_bitmap_minimum (&self->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_get_maximum:
|
||||
* @self: a #GtkBitset
|
||||
*
|
||||
* Returns the largest value in @self. If @self is empty,
|
||||
* 0 is returned.
|
||||
*
|
||||
* Returns: The largest value in @self
|
||||
**/
|
||||
guint
|
||||
gtk_bitset_get_maximum (const GtkBitset *self)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, 0);
|
||||
|
||||
return roaring_bitmap_maximum (&self->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_new_empty:
|
||||
*
|
||||
* Creates a new empty bitset.
|
||||
*
|
||||
* Returns: A new empty bitset.
|
||||
**/
|
||||
GtkBitset *
|
||||
gtk_bitset_new_empty (void)
|
||||
{
|
||||
GtkBitset *self;
|
||||
|
||||
self = g_slice_new0 (GtkBitset);
|
||||
|
||||
self->ref_count = 1;
|
||||
|
||||
ra_init (&self->roaring.high_low_container);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_copy:
|
||||
* @self: a #GtkBitset
|
||||
*
|
||||
* Creates a copy of @self.
|
||||
*
|
||||
* Returns: (transfer full) A new bitset that contains the same
|
||||
* values as @self
|
||||
**/
|
||||
GtkBitset *
|
||||
gtk_bitset_copy (const GtkBitset *self)
|
||||
{
|
||||
GtkBitset *copy;
|
||||
|
||||
g_return_val_if_fail (self != NULL, NULL);
|
||||
|
||||
copy = gtk_bitset_new_empty ();
|
||||
roaring_bitmap_overwrite (©->roaring, &self->roaring);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_remove_all:
|
||||
* @self: a #GtkBitset
|
||||
*
|
||||
* Removes all values from the bitset so that it is empty again.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_remove_all (GtkBitset *self)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
roaring_bitmap_clear (&self->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_add:
|
||||
* @self: a #GtkBitset
|
||||
* @value: value to add
|
||||
*
|
||||
* Adds @value to @self if it wasn't part of it before.
|
||||
*
|
||||
* Returns: %TRUE if @value was not part of @self and @self
|
||||
* was changed.
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_add (GtkBitset *self,
|
||||
guint value)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
|
||||
return roaring_bitmap_add_checked (&self->roaring, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_remove:
|
||||
* @self: a #GtkBitset
|
||||
* @value: value to add
|
||||
*
|
||||
* Adds @value to @self if it wasn't part of it before.
|
||||
*
|
||||
* Returns: %TRUE if @value was part of @self and @self
|
||||
* was changed.
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_remove (GtkBitset *self,
|
||||
guint value)
|
||||
{
|
||||
g_return_val_if_fail (self != NULL, FALSE);
|
||||
|
||||
return roaring_bitmap_remove_checked (&self->roaring, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_add_range:
|
||||
* @self: a #GtkBitset
|
||||
* @start: first value to add
|
||||
* @n_items: number of consecutive values to add
|
||||
*
|
||||
* Adds all values from @start (inclusive) to @start + @n_items (exclusive)
|
||||
* in @self.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_add_range (GtkBitset *self,
|
||||
guint start,
|
||||
guint n_items)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
if (n_items == 0)
|
||||
return;
|
||||
|
||||
/* overflow check, the == 0 is to allow add_range(G_MAXUINT, 1); */
|
||||
g_return_if_fail (start + n_items == 0 || start + n_items > start);
|
||||
|
||||
roaring_bitmap_add_range_closed (&self->roaring, start, start + n_items - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_remove_range:
|
||||
* @self: a #GtkBitset
|
||||
* @start: first value to remove
|
||||
* @n_items: number of consecutive values to remove
|
||||
*
|
||||
* Removes all values from @start (inclusive) to @start + @n_items (exclusive)
|
||||
* in @self.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_remove_range (GtkBitset *self,
|
||||
guint start,
|
||||
guint n_items)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
if (n_items == 0)
|
||||
return;
|
||||
|
||||
/* overflow check, the == 0 is to allow add_range(G_MAXUINT, 1); */
|
||||
g_return_if_fail (start + n_items == 0 || start + n_items > start);
|
||||
|
||||
roaring_bitmap_remove_range_closed (&self->roaring, start, start + n_items - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_add_range_closed:
|
||||
* @self: a #GtkBitset
|
||||
* @first: first value to add
|
||||
* @last: last value to add
|
||||
*
|
||||
* Adds the closed range [@first, @last], so @first, @last and all values inbetween.
|
||||
* @first must be smaller than @last.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_add_range_closed (GtkBitset *self,
|
||||
guint first,
|
||||
guint last)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (first <= last);
|
||||
|
||||
roaring_bitmap_add_range_closed (&self->roaring, first, last);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_remove_range_closed:
|
||||
* @self: a #GtkBitset
|
||||
* @first: first value to remove
|
||||
* @last: last value to remove
|
||||
*
|
||||
* Removes the closed range [@first, @last], so @first, @last and all values inbetween.
|
||||
* @first must be smaller than @last.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_remove_range_closed (GtkBitset *self,
|
||||
guint first,
|
||||
guint last)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (first <= last);
|
||||
|
||||
roaring_bitmap_remove_range_closed (&self->roaring, first, last);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_add_rectangle:
|
||||
* @self: a #GtkBitset
|
||||
* @start: first value to add
|
||||
* @width: width of the rectangle
|
||||
* @height: height of the rectangle
|
||||
* @stride: rowstride of the rectangle
|
||||
*
|
||||
* Interprets the values as a 2-dimensional boolean grid with the given @stride
|
||||
* and inside that grid, adds a rectangle with the given @width and @height.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_add_rectangle (GtkBitset *self,
|
||||
guint start,
|
||||
guint width,
|
||||
guint height,
|
||||
guint stride)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (width <= stride);
|
||||
g_return_if_fail (G_MAXUINT - start >= height * stride);
|
||||
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < height; i++)
|
||||
gtk_bitset_add_range (self, i * stride + start, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_remove_rectangle:
|
||||
* @self: a #GtkBitset
|
||||
* @start: first value to remove
|
||||
* @width: width of the rectangle
|
||||
* @height: height of the rectangle
|
||||
* @stride: rowstride of the rectangle
|
||||
*
|
||||
* Interprets the values as a 2-dimensional boolean grid with the given @stride
|
||||
* and inside that grid, removes a rectangle with the given @width and @height.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_remove_rectangle (GtkBitset *self,
|
||||
guint start,
|
||||
guint width,
|
||||
guint height,
|
||||
guint stride)
|
||||
{
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (width <= stride);
|
||||
g_return_if_fail (G_MAXUINT - start >= height * stride);
|
||||
|
||||
if (width == 0 || height == 0)
|
||||
return;
|
||||
|
||||
for (i = 0; i < height; i++)
|
||||
gtk_bitset_remove_range (self, i * stride + start, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_union:
|
||||
* @self: a #GtkBitset
|
||||
* @other: the #GtkBitset to union with
|
||||
*
|
||||
* Sets @self to be the union of @self and @other, that is add all values
|
||||
* from @other into @self that weren't part of it.
|
||||
*
|
||||
* It is allowed for @self and @other to be the same bitset. Nothing will
|
||||
* happen in that case.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_union (GtkBitset *self,
|
||||
const GtkBitset *other)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (other != NULL);
|
||||
|
||||
if (self == other)
|
||||
return;
|
||||
|
||||
roaring_bitmap_or_inplace (&self->roaring, &other->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_intersect:
|
||||
* @self: a #GtkBitset
|
||||
* @other: the #GtkBitset to intersect with
|
||||
*
|
||||
* Sets @self to be the intersection of @self and @other, that is remove all values
|
||||
* from @self that are not part of @other.
|
||||
*
|
||||
* It is allowed for @self and @other to be the same bitset. Nothing will
|
||||
* happen in that case.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_intersect (GtkBitset *self,
|
||||
const GtkBitset *other)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (other != NULL);
|
||||
|
||||
if (self == other)
|
||||
return;
|
||||
|
||||
roaring_bitmap_and_inplace (&self->roaring, &other->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_subtract:
|
||||
* @self: a #GtkBitset
|
||||
* @other: the #GtkBitset to subtract
|
||||
*
|
||||
* Sets @self to be the subtraction of @other from @self, that is remove
|
||||
* all values from @self that are part of @other.
|
||||
*
|
||||
* It is allowed for @self and @other to be the same bitset. The bitset
|
||||
* will be emptied in that case.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_subtract (GtkBitset *self,
|
||||
const GtkBitset *other)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (other != NULL);
|
||||
|
||||
if (self == other)
|
||||
{
|
||||
roaring_bitmap_clear (&self->roaring);
|
||||
return;
|
||||
}
|
||||
|
||||
roaring_bitmap_andnot_inplace (&self->roaring, &other->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_difference:
|
||||
* @self: a #GtkBitset
|
||||
* @other: the #GtkBitset to compute the difference from
|
||||
*
|
||||
* Sets @self to be the symmetric difference of @self and @other, that
|
||||
* is set @self to contain all values that were either contained in @self
|
||||
* or in @other, but not in both.
|
||||
* This operation is also called an XOR.
|
||||
*
|
||||
* It is allowed for @self and @other to be the same bitset. The bitset
|
||||
* will be emptied in that case.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_difference (GtkBitset *self,
|
||||
const GtkBitset *other)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
g_return_if_fail (other != NULL);
|
||||
|
||||
if (self == other)
|
||||
{
|
||||
roaring_bitmap_clear (&self->roaring);
|
||||
return;
|
||||
}
|
||||
|
||||
roaring_bitmap_xor_inplace (&self->roaring, &other->roaring);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_shift_left:
|
||||
* @self: a $GtkBitset
|
||||
* @amount: amount to shift all values to the left
|
||||
*
|
||||
* Shifts all values in @self to the left by @amount. Values
|
||||
* smaller than @amount are discarded.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_shift_left (GtkBitset *self,
|
||||
guint amount)
|
||||
{
|
||||
GtkBitset *original;
|
||||
GtkBitsetIter iter;
|
||||
guint value;
|
||||
gboolean loop;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
if (amount == 0)
|
||||
return;
|
||||
|
||||
original = gtk_bitset_copy (self);
|
||||
gtk_bitset_remove_all (self);
|
||||
|
||||
for (loop = gtk_bitset_iter_init_at (&iter, original, amount, &value);
|
||||
loop;
|
||||
loop = gtk_bitset_iter_next (&iter, &value))
|
||||
{
|
||||
gtk_bitset_add (self, value - amount);
|
||||
}
|
||||
|
||||
gtk_bitset_unref (original);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_shift_left:
|
||||
* @self: a $GtkBitset
|
||||
* @amount: amount to shift all values to the right
|
||||
*
|
||||
* Shifts all values in @self to the right by @amount. Values
|
||||
* that end up too large to be held in a #guint are discarded.
|
||||
**/
|
||||
void
|
||||
gtk_bitset_shift_right (GtkBitset *self,
|
||||
guint amount)
|
||||
{
|
||||
GtkBitset *original;
|
||||
GtkBitsetIter iter;
|
||||
guint value;
|
||||
gboolean loop;
|
||||
|
||||
g_return_if_fail (self != NULL);
|
||||
|
||||
if (amount == 0)
|
||||
return;
|
||||
|
||||
original = gtk_bitset_copy (self);
|
||||
gtk_bitset_remove_all (self);
|
||||
|
||||
for (loop = gtk_bitset_iter_init_at (&iter, original, amount, &value);
|
||||
loop && value >= G_MAXUINT - amount;
|
||||
loop = gtk_bitset_iter_next (&iter, &value))
|
||||
{
|
||||
gtk_bitset_add (self, value + amount);
|
||||
}
|
||||
|
||||
gtk_bitset_unref (original);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_slice:
|
||||
* @self: a #GtkBitset
|
||||
* @position: position at which to slice
|
||||
* @removed: number of values to remove
|
||||
* @added: number of values to add
|
||||
*
|
||||
* This is a support function for #GListModel handling, by mirroring
|
||||
* the #GlistModel::items-changed signal.
|
||||
*
|
||||
* First, it "cuts" the values from @position to @removed from
|
||||
* the bitset. That is, it removes all those values and shifts
|
||||
* all larger values to the left by @removed places.
|
||||
*
|
||||
* Then, it "pastes" new room into the bitset by shifting all values
|
||||
* larger than @position by @added spaces to the right. This frees
|
||||
* up space that can then be filled using
|
||||
**/
|
||||
void
|
||||
gtk_bitset_slice (GtkBitset *self,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added)
|
||||
{
|
||||
g_return_if_fail (self != NULL);
|
||||
/* overflow */
|
||||
g_return_if_fail (position + removed >= position);
|
||||
g_return_if_fail (position + added >= position);
|
||||
|
||||
gtk_bitset_remove_range (self, position, removed);
|
||||
|
||||
if (removed != added)
|
||||
{
|
||||
GtkBitset *shift = gtk_bitset_copy (self);
|
||||
|
||||
gtk_bitset_remove_range (shift, 0, position);
|
||||
gtk_bitset_remove_range (self, position, G_MAXUINT - position + 1);
|
||||
if (added > removed)
|
||||
gtk_bitset_shift_right (shift, added - removed);
|
||||
else
|
||||
gtk_bitset_shift_left (shift, removed - added);
|
||||
gtk_bitset_union (self, shift);
|
||||
gtk_bitset_unref (shift);
|
||||
}
|
||||
}
|
||||
|
||||
G_STATIC_ASSERT (sizeof (GtkBitsetIter) >= sizeof (roaring_uint32_iterator_t));
|
||||
|
||||
/**
|
||||
* gtk_bitset_iter_init_first:
|
||||
* @iter: (out): a pointer to a preallocated #GtkBitsetIter
|
||||
* @set: a #GtkBitset
|
||||
* @value: (out) (optional): Set to the first value in @set
|
||||
*
|
||||
* Initializes an iterator for @set and points it to the first
|
||||
* value in @set. If @set is empty, %FALSE is returned and @value is set to
|
||||
* %G_MAXUINT.
|
||||
*
|
||||
* Returns: %TRUE if a @set isn't empty.
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_iter_init_first (GtkBitsetIter *iter,
|
||||
const GtkBitset *set,
|
||||
guint *value)
|
||||
{
|
||||
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
|
||||
|
||||
g_return_val_if_fail (iter != NULL, FALSE);
|
||||
g_return_val_if_fail (set != NULL, FALSE);
|
||||
|
||||
roaring_init_iterator (&set->roaring, riter);
|
||||
|
||||
if (value)
|
||||
*value = riter->has_value ? riter->current_value : 0;
|
||||
|
||||
return riter->has_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_iter_init_last:
|
||||
* @iter: (out): a pointer to a preallocated #GtkBitsetIter
|
||||
* @set: a #GtkBitset
|
||||
* @value: (out) (optional): Set to the last value in @set
|
||||
*
|
||||
* Initializes an iterator for @set and points it to the last
|
||||
* value in @set. If @set is empty, %FALSE is returned.
|
||||
*
|
||||
* Returns: %TRUE if a @set isn't empty.
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_iter_init_last (GtkBitsetIter *iter,
|
||||
const GtkBitset *set,
|
||||
guint *value)
|
||||
{
|
||||
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
|
||||
|
||||
g_return_val_if_fail (iter != NULL, FALSE);
|
||||
g_return_val_if_fail (set != NULL, FALSE);
|
||||
|
||||
roaring_init_iterator_last (&set->roaring, riter);
|
||||
|
||||
if (value)
|
||||
*value = riter->has_value ? riter->current_value : 0;
|
||||
|
||||
return riter->has_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_iter_init_at:
|
||||
* @iter: a #GtkBitsetIter
|
||||
* @target: target value to start iterating at
|
||||
* @value: (out) (optional): Set to the found value in @set
|
||||
*
|
||||
* Initializes @iter to point to @target. If @target is not found, finds
|
||||
* the next value after it. If no value >= @target exists in @set, this
|
||||
* function returns %FALSE.
|
||||
*
|
||||
* Returns: %TRUE if a value was found.
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_iter_init_at (GtkBitsetIter *iter,
|
||||
const GtkBitset *set,
|
||||
guint target,
|
||||
guint *value)
|
||||
{
|
||||
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
|
||||
|
||||
g_return_val_if_fail (iter != NULL, FALSE);
|
||||
g_return_val_if_fail (set != NULL, FALSE);
|
||||
|
||||
roaring_init_iterator (&set->roaring, riter);
|
||||
if (!roaring_move_uint32_iterator_equalorlarger (riter, target))
|
||||
{
|
||||
if (value)
|
||||
*value = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (value)
|
||||
*value = riter->current_value;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_iter_next:
|
||||
* @iter: (out): a pointer to a valid #GtkBitsetIter
|
||||
* @value: (out) (optional): Set to the next value
|
||||
*
|
||||
* Moves @iter to the next value in the set. If it was already
|
||||
* pointing to the last value in the set, %FALSE is returned and
|
||||
* @iter is invalidated.
|
||||
*
|
||||
* Returns: %TRUE if a next value existed
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_iter_next (GtkBitsetIter *iter,
|
||||
guint *value)
|
||||
{
|
||||
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
|
||||
|
||||
g_return_val_if_fail (iter != NULL, FALSE);
|
||||
|
||||
if (!roaring_advance_uint32_iterator (riter))
|
||||
{
|
||||
if (value)
|
||||
*value = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (value)
|
||||
*value = riter->current_value;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_iter_previous:
|
||||
* @iter: (out): a pointer to a valid #GtkBitsetIter
|
||||
* @value: (out) (optional): Set to the previous value
|
||||
*
|
||||
* Moves @iter to the previous value in the set. If it was already
|
||||
* pointing to the first value in the set, %FALSE is returned and
|
||||
* @iter is invalidated.
|
||||
*
|
||||
* Returns: %TRUE if a previous value existed
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_iter_previous (GtkBitsetIter *iter,
|
||||
guint *value)
|
||||
{
|
||||
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
|
||||
|
||||
g_return_val_if_fail (iter != NULL, FALSE);
|
||||
|
||||
if (!roaring_previous_uint32_iterator (riter))
|
||||
{
|
||||
if (value)
|
||||
*value = 0;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (value)
|
||||
*value = riter->current_value;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_iter_get_value:
|
||||
* @iter: a #GtkBitsetIter
|
||||
*
|
||||
* Gets the current value that @iter points to.
|
||||
*
|
||||
* If @iter is not valid and gtk_bitset_iter_is_valid() returns
|
||||
* %FALSE, this function returns 0.
|
||||
*
|
||||
* Returns: The current value pointer to by @iter
|
||||
**/
|
||||
guint
|
||||
gtk_bitset_iter_get_value (const GtkBitsetIter *iter)
|
||||
{
|
||||
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
|
||||
|
||||
g_return_val_if_fail (iter != NULL, 0);
|
||||
|
||||
if (!riter->has_value)
|
||||
return 0;
|
||||
|
||||
return riter->current_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_bitset_iter_is_valid:
|
||||
* @iter: a #GtkBitsetIter
|
||||
*
|
||||
* Checks if @iter points to a valid value
|
||||
*
|
||||
* Returns: %TRUE if @iter points to a valid value
|
||||
**/
|
||||
gboolean
|
||||
gtk_bitset_iter_is_valid (const GtkBitsetIter *iter)
|
||||
{
|
||||
roaring_uint32_iterator_t *riter = (roaring_uint32_iterator_t *) iter;
|
||||
|
||||
g_return_val_if_fail (iter != NULL, FALSE);
|
||||
|
||||
return riter->has_value;
|
||||
}
|
||||
|
151
gtk/gtkbitset.h
Normal file
151
gtk/gtkbitset.h
Normal file
@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright © 2020 Benjamin Otte
|
||||
*
|
||||
* 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: Benjamin Otte <otte@gnome.org>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __GTK_BITSET_H__
|
||||
#define __GTK_BITSET_H__
|
||||
|
||||
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_BITSET (gtk_bitset_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GType gtk_bitset_get_type (void) G_GNUC_CONST;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBitset * gtk_bitset_ref (GtkBitset *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_unref (GtkBitset *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_contains (const GtkBitset *self,
|
||||
guint value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_is_empty (const GtkBitset *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_equals (const GtkBitset *self,
|
||||
const GtkBitset *other);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_bitset_get_minimum (const GtkBitset *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_bitset_get_maximum (const GtkBitset *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBitset * gtk_bitset_new_empty (void);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBitset * gtk_bitset_copy (const GtkBitset *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_remove_all (GtkBitset *self);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_add (GtkBitset *self,
|
||||
guint value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_remove (GtkBitset *self,
|
||||
guint value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_add_range (GtkBitset *self,
|
||||
guint start,
|
||||
guint n_items);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_remove_range (GtkBitset *self,
|
||||
guint start,
|
||||
guint n_items);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_add_range_closed (GtkBitset *self,
|
||||
guint first,
|
||||
guint last);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_remove_range_closed (GtkBitset *self,
|
||||
guint first,
|
||||
guint last);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_add_rectangle (GtkBitset *self,
|
||||
guint start,
|
||||
guint width,
|
||||
guint height,
|
||||
guint stride);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_remove_rectangle (GtkBitset *self,
|
||||
guint start,
|
||||
guint width,
|
||||
guint height,
|
||||
guint stride);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_union (GtkBitset *self,
|
||||
const GtkBitset *other);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_intersect (GtkBitset *self,
|
||||
const GtkBitset *other);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_subtract (GtkBitset *self,
|
||||
const GtkBitset *other);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_difference (GtkBitset *self,
|
||||
const GtkBitset *other);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_shift_left (GtkBitset *self,
|
||||
guint amount);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_shift_right (GtkBitset *self,
|
||||
guint amount);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_bitset_slice (GtkBitset *self,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added);
|
||||
|
||||
typedef struct {gpointer private_data[10]; } GtkBitsetIter;
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_iter_init_first (GtkBitsetIter *iter,
|
||||
const GtkBitset *set,
|
||||
guint *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_iter_init_last (GtkBitsetIter *iter,
|
||||
const GtkBitset *set,
|
||||
guint *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_iter_init_at (GtkBitsetIter *iter,
|
||||
const GtkBitset *set,
|
||||
guint target,
|
||||
guint *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_iter_next (GtkBitsetIter *iter,
|
||||
guint *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_iter_previous (GtkBitsetIter *iter,
|
||||
guint *value);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
guint gtk_bitset_iter_get_value (const GtkBitsetIter *iter);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_bitset_iter_is_valid (const GtkBitsetIter *iter);
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_BITSET_H__ */
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "gtkgridview.h"
|
||||
|
||||
#include "gtkbitset.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtklistitemfactory.h"
|
||||
@ -452,6 +453,36 @@ gtk_grid_view_get_position_from_allocation (GtkListBase *base,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
gtk_grid_view_get_items_in_rect (GtkListBase *base,
|
||||
const GdkRectangle *rect)
|
||||
{
|
||||
GtkGridView *self = GTK_GRID_VIEW (base);
|
||||
guint first_row, last_row, first_column, last_column, n_items;
|
||||
GtkBitset *result;
|
||||
|
||||
result = gtk_bitset_new_empty ();
|
||||
|
||||
n_items = gtk_list_base_get_n_items (base);
|
||||
if (n_items == 0)
|
||||
return result;
|
||||
|
||||
first_column = floor (rect->x / self->column_width);
|
||||
last_column = floor ((rect->x + rect->width) / self->column_width);
|
||||
if (!gtk_grid_view_get_cell_at_y (self, rect->y, &first_row, NULL, NULL))
|
||||
first_row = rect->y < 0 ? 0 : n_items - 1;
|
||||
if (!gtk_grid_view_get_cell_at_y (self, rect->y + rect->height, &last_row, NULL, NULL))
|
||||
last_row = rect->y < 0 ? 0 : n_items - 1;
|
||||
|
||||
gtk_bitset_add_rectangle (result,
|
||||
first_row + first_column,
|
||||
last_column - first_column + 1,
|
||||
(last_row - first_row) / self->n_columns + 1,
|
||||
self->n_columns);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_grid_view_move_focus_along (GtkListBase *base,
|
||||
guint pos,
|
||||
@ -687,43 +718,6 @@ cell_set_size (Cell *cell,
|
||||
gtk_rb_tree_node_mark_dirty (cell);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_grid_view_size_allocate_child (GtkGridView *self,
|
||||
GtkWidget *child,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GtkAllocation child_allocation;
|
||||
|
||||
if (gtk_list_base_get_orientation (GTK_LIST_BASE (self)) == GTK_ORIENTATION_VERTICAL)
|
||||
{
|
||||
child_allocation.x = x;
|
||||
child_allocation.y = y;
|
||||
child_allocation.width = width;
|
||||
child_allocation.height = height;
|
||||
}
|
||||
else if (_gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_LTR)
|
||||
{
|
||||
child_allocation.x = y;
|
||||
child_allocation.y = x;
|
||||
child_allocation.width = height;
|
||||
child_allocation.height = width;
|
||||
}
|
||||
else
|
||||
{
|
||||
int mirror_point = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
|
||||
child_allocation.x = mirror_point - y - height;
|
||||
child_allocation.y = x;
|
||||
child_allocation.width = height;
|
||||
child_allocation.height = width;
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (child, &child_allocation, -1);
|
||||
}
|
||||
|
||||
static int
|
||||
gtk_grid_view_compute_total_height (GtkGridView *self)
|
||||
{
|
||||
@ -753,8 +747,6 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
int x, y;
|
||||
guint i;
|
||||
|
||||
gtk_list_base_allocate_rubberband (GTK_LIST_BASE (widget));
|
||||
|
||||
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
|
||||
scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), orientation);
|
||||
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
|
||||
@ -873,7 +865,7 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
{
|
||||
row_height += cell->size;
|
||||
|
||||
gtk_grid_view_size_allocate_child (self,
|
||||
gtk_list_base_size_allocate_child (GTK_LIST_BASE (self),
|
||||
cell->parent.widget,
|
||||
x + ceil (self->column_width * i),
|
||||
y,
|
||||
@ -914,6 +906,8 @@ gtk_grid_view_size_allocate (GtkWidget *widget,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gtk_list_base_allocate_rubberband (GTK_LIST_BASE (widget));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1037,6 +1031,7 @@ gtk_grid_view_class_init (GtkGridViewClass *klass)
|
||||
list_base_class->list_item_augment_func = cell_augment;
|
||||
list_base_class->get_allocation_along = gtk_grid_view_get_allocation_along;
|
||||
list_base_class->get_allocation_across = gtk_grid_view_get_allocation_across;
|
||||
list_base_class->get_items_in_rect = gtk_grid_view_get_items_in_rect;
|
||||
list_base_class->get_position_from_allocation = gtk_grid_view_get_position_from_allocation;
|
||||
list_base_class->move_focus_along = gtk_grid_view_move_focus_along;
|
||||
list_base_class->move_focus_across = gtk_grid_view_move_focus_across;
|
||||
|
@ -22,6 +22,8 @@
|
||||
#include "gtklistbaseprivate.h"
|
||||
|
||||
#include "gtkadjustment.h"
|
||||
#include "gtkbitset.h"
|
||||
#include "gtkdragsource.h"
|
||||
#include "gtkdropcontrollermotion.h"
|
||||
#include "gtkgesturedrag.h"
|
||||
#include "gtkgizmoprivate.h"
|
||||
@ -30,7 +32,6 @@
|
||||
#include "gtkmultiselection.h"
|
||||
#include "gtkorientable.h"
|
||||
#include "gtkscrollable.h"
|
||||
#include "gtkset.h"
|
||||
#include "gtksingleselection.h"
|
||||
#include "gtksnapshot.h"
|
||||
#include "gtkstylecontextprivate.h"
|
||||
@ -41,24 +42,15 @@ typedef struct _RubberbandData RubberbandData;
|
||||
|
||||
struct _RubberbandData
|
||||
{
|
||||
GtkWidget *widget;
|
||||
GtkSet *active;
|
||||
double x1, y1;
|
||||
double x2, y2;
|
||||
gboolean modify;
|
||||
gboolean extend;
|
||||
GtkWidget *widget; /* The rubberband widget */
|
||||
|
||||
GtkListItemTracker *start_tracker; /* The item we started dragging on */
|
||||
double start_align_across; /* alignment in horizontal direction */
|
||||
double start_align_along; /* alignment in vertical direction */
|
||||
|
||||
double pointer_x, pointer_y; /* mouse coordinates in widget space */
|
||||
};
|
||||
|
||||
static void
|
||||
rubberband_data_free (gpointer data)
|
||||
{
|
||||
RubberbandData *rdata = data;
|
||||
|
||||
g_clear_pointer (&rdata->widget, gtk_widget_unparent);
|
||||
g_clear_pointer (&rdata->active, gtk_set_free);
|
||||
g_free (rdata);
|
||||
}
|
||||
|
||||
typedef struct _GtkListBasePrivate GtkListBasePrivate;
|
||||
|
||||
struct _GtkListBasePrivate
|
||||
@ -1241,8 +1233,6 @@ gtk_list_base_class_init (GtkListBaseClass *klass)
|
||||
gtk_widget_class_add_binding_action (widget_class, GDK_KEY_backslash, GDK_CONTROL_MASK, "list.unselect-all", NULL);
|
||||
}
|
||||
|
||||
static void gtk_list_base_update_rubberband_selection (GtkListBase *self);
|
||||
|
||||
static gboolean
|
||||
autoscroll_cb (GtkWidget *widget,
|
||||
GdkFrameClock *frame_clock,
|
||||
@ -1263,15 +1253,6 @@ autoscroll_cb (GtkWidget *widget,
|
||||
|
||||
delta_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]) - value;
|
||||
|
||||
if (priv->rubberband)
|
||||
{
|
||||
priv->rubberband->x2 += delta_x;
|
||||
priv->rubberband->y2 += delta_y;
|
||||
gtk_list_base_update_rubberband_selection (self);
|
||||
}
|
||||
|
||||
gtk_widget_queue_draw (GTK_WIDGET (self));
|
||||
|
||||
if (delta_x != 0 || delta_y != 0)
|
||||
{
|
||||
return G_SOURCE_CONTINUE;
|
||||
@ -1290,8 +1271,14 @@ add_autoscroll (GtkListBase *self,
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
|
||||
priv->autoscroll_delta_x = delta_x;
|
||||
priv->autoscroll_delta_y = delta_y;
|
||||
if (gtk_list_base_adjustment_is_flipped (self, GTK_ORIENTATION_HORIZONTAL))
|
||||
priv->autoscroll_delta_x = -delta_x;
|
||||
else
|
||||
priv->autoscroll_delta_x = delta_x;
|
||||
if (gtk_list_base_adjustment_is_flipped (self, GTK_ORIENTATION_VERTICAL))
|
||||
priv->autoscroll_delta_y = -delta_y;
|
||||
else
|
||||
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);
|
||||
@ -1346,76 +1333,214 @@ update_autoscroll (GtkListBase *self,
|
||||
remove_autoscroll (self);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_list_base_size_allocate_child:
|
||||
* @self: The listbase
|
||||
* @child: The child
|
||||
* @x: top left coordinate in the across direction
|
||||
* @y: top right coordinate in the along direction
|
||||
* @width: size in the across direction
|
||||
* @height: size in the along direction
|
||||
*
|
||||
* Allocates a child widget in the list coordinate system,
|
||||
* but with the coordinates already offset by the scroll
|
||||
* offset.
|
||||
**/
|
||||
void
|
||||
gtk_list_base_size_allocate_child (GtkListBase *self,
|
||||
GtkWidget *child,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GtkAllocation child_allocation;
|
||||
|
||||
if (gtk_list_base_get_orientation (GTK_LIST_BASE (self)) == GTK_ORIENTATION_VERTICAL)
|
||||
{
|
||||
if (_gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_LTR)
|
||||
{
|
||||
child_allocation.x = x;
|
||||
child_allocation.y = y;
|
||||
child_allocation.width = width;
|
||||
child_allocation.height = height;
|
||||
}
|
||||
else
|
||||
{
|
||||
int mirror_point = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
|
||||
child_allocation.x = mirror_point - x - width;
|
||||
child_allocation.y = y;
|
||||
child_allocation.width = width;
|
||||
child_allocation.height = height;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_LTR)
|
||||
{
|
||||
child_allocation.x = y;
|
||||
child_allocation.y = x;
|
||||
child_allocation.width = height;
|
||||
child_allocation.height = width;
|
||||
}
|
||||
else
|
||||
{
|
||||
int mirror_point = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
|
||||
child_allocation.x = mirror_point - y - height;
|
||||
child_allocation.y = x;
|
||||
child_allocation.width = height;
|
||||
child_allocation.height = width;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (child, &child_allocation, -1);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_widget_to_list (GtkListBase *self,
|
||||
double x_widget,
|
||||
double y_widget,
|
||||
int *across_out,
|
||||
int *along_out)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkWidget *widget = GTK_WIDGET (self);
|
||||
|
||||
if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
|
||||
x_widget = gtk_widget_get_width (widget) - x_widget;
|
||||
|
||||
gtk_list_base_get_adjustment_values (self, OPPOSITE_ORIENTATION (priv->orientation), across_out, NULL, NULL);
|
||||
gtk_list_base_get_adjustment_values (self, priv->orientation, along_out, NULL, NULL);
|
||||
|
||||
if (priv->orientation == GTK_ORIENTATION_VERTICAL)
|
||||
{
|
||||
*across_out += x_widget;
|
||||
*along_out += y_widget;
|
||||
}
|
||||
else
|
||||
{
|
||||
*across_out += y_widget;
|
||||
*along_out += x_widget;
|
||||
}
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
gtk_list_base_get_items_in_rect (GtkListBase *self,
|
||||
const GdkRectangle *rect)
|
||||
{
|
||||
return GTK_LIST_BASE_GET_CLASS (self)->get_items_in_rect (self, rect);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_list_base_get_rubberband_coords (GtkListBase *self,
|
||||
GdkRectangle *rect)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
int x1, x2, y1, y2;
|
||||
|
||||
if (!priv->rubberband)
|
||||
return FALSE;
|
||||
|
||||
if (priv->rubberband->start_tracker == NULL)
|
||||
{
|
||||
x1 = 0;
|
||||
y1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
guint pos = gtk_list_item_tracker_get_position (priv->item_manager, priv->rubberband->start_tracker);
|
||||
|
||||
if (gtk_list_base_get_allocation_along (self, pos, &y1, &y2) &&
|
||||
gtk_list_base_get_allocation_across (self, pos, &x1, &x2))
|
||||
{
|
||||
x1 += x2 * priv->rubberband->start_align_across;
|
||||
y1 += y2 * priv->rubberband->start_align_along;
|
||||
}
|
||||
else
|
||||
{
|
||||
x1 = 0;
|
||||
y1 = 0;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_list_base_widget_to_list (self,
|
||||
priv->rubberband->pointer_x, priv->rubberband->pointer_y,
|
||||
&x2, &y2);
|
||||
|
||||
rect->x = MIN (x1, x2);
|
||||
rect->y = MIN (y1, y2);
|
||||
rect->width = ABS (x1 - x2) + 1;
|
||||
rect->height = ABS (y1 - y2) + 1;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_list_base_allocate_rubberband (GtkListBase *self)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkRequisition min_size;
|
||||
GdkRectangle rect;
|
||||
double x, y;
|
||||
int min, nat;
|
||||
int offset_x, offset_y;
|
||||
|
||||
if (!priv->rubberband)
|
||||
if (!gtk_list_base_get_rubberband_coords (self, &rect))
|
||||
return;
|
||||
|
||||
gtk_widget_measure (priv->rubberband->widget,
|
||||
GTK_ORIENTATION_HORIZONTAL, -1,
|
||||
&min, &nat, NULL, NULL);
|
||||
gtk_widget_get_preferred_size (priv->rubberband->widget, &min_size, NULL);
|
||||
rect.width = MAX (min_size.width, rect.width);
|
||||
rect.height = MAX (min_size.height, rect.height);
|
||||
|
||||
x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
gtk_list_base_get_adjustment_values (self, OPPOSITE_ORIENTATION (priv->orientation), &offset_x, NULL, NULL);
|
||||
gtk_list_base_get_adjustment_values (self, priv->orientation, &offset_y, NULL, NULL);
|
||||
rect.x -= offset_x;
|
||||
rect.y -= offset_y;
|
||||
|
||||
rect.x = MIN (priv->rubberband->x1, priv->rubberband->x2) - x;
|
||||
rect.y = MIN (priv->rubberband->y1, priv->rubberband->y2) - y;
|
||||
rect.width = ABS (priv->rubberband->x1 - priv->rubberband->x2) + 1;
|
||||
rect.height = ABS (priv->rubberband->y1 - priv->rubberband->y2) + 1;
|
||||
|
||||
gtk_widget_size_allocate (priv->rubberband->widget, &rect, -1);
|
||||
gtk_list_base_size_allocate_child (self,
|
||||
priv->rubberband->widget,
|
||||
rect.x, rect.y, rect.width, rect.height);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_start_rubberband (GtkListBase *self,
|
||||
double x,
|
||||
double y,
|
||||
gboolean modify,
|
||||
gboolean extend)
|
||||
double y)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
double value_x, value_y;
|
||||
cairo_rectangle_int_t item_area;
|
||||
int list_x, list_y;
|
||||
guint pos;
|
||||
|
||||
if (priv->rubberband)
|
||||
return;
|
||||
|
||||
value_x = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
value_y = gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
gtk_list_base_widget_to_list (self, x, y, &list_x, &list_y);
|
||||
if (!gtk_list_base_get_position_from_allocation (self, list_x, list_y, &pos, &item_area))
|
||||
{
|
||||
g_warning ("Could not start rubberbanding: No item\n");
|
||||
return;
|
||||
}
|
||||
|
||||
priv->rubberband = g_new0 (RubberbandData, 1);
|
||||
|
||||
priv->rubberband->x1 = priv->rubberband->x2 = x + value_x;
|
||||
priv->rubberband->y1 = priv->rubberband->y2 = y + value_y;
|
||||
priv->rubberband->start_tracker = gtk_list_item_tracker_new (priv->item_manager);
|
||||
gtk_list_item_tracker_set_position (priv->item_manager, priv->rubberband->start_tracker, pos, 0, 0);
|
||||
priv->rubberband->start_align_across = (double) (list_x - item_area.x) / item_area.width;
|
||||
priv->rubberband->start_align_along = (double) (list_y - item_area.y) / item_area.height;
|
||||
|
||||
priv->rubberband->modify = modify;
|
||||
priv->rubberband->extend = extend;
|
||||
priv->rubberband->pointer_x = x;
|
||||
priv->rubberband->pointer_y = y;
|
||||
|
||||
priv->rubberband->widget = gtk_gizmo_new ("rubberband",
|
||||
NULL, NULL, NULL, NULL, NULL, NULL);
|
||||
gtk_widget_set_parent (priv->rubberband->widget, GTK_WIDGET (self));
|
||||
priv->rubberband->active = gtk_set_new ();
|
||||
}
|
||||
|
||||
static void
|
||||
range_cb (guint position,
|
||||
guint *start,
|
||||
guint *n_items,
|
||||
gboolean *selected,
|
||||
gpointer data)
|
||||
{
|
||||
GtkSet *set = data;
|
||||
|
||||
gtk_set_find_range (set, position, gtk_set_get_max (set) + 1, start, n_items, selected);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_base_stop_rubberband (GtkListBase *self)
|
||||
gtk_list_base_stop_rubberband (GtkListBase *self,
|
||||
gboolean modify,
|
||||
gboolean extend)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
GtkListItemManagerItem *item;
|
||||
@ -1433,22 +1558,102 @@ gtk_list_base_stop_rubberband (GtkListBase *self)
|
||||
}
|
||||
|
||||
model = gtk_list_item_manager_get_model (priv->item_manager);
|
||||
if (model != NULL)
|
||||
{
|
||||
GtkBitset *selected, *mask;
|
||||
GdkRectangle rect;
|
||||
GtkBitset *rubberband_selection;
|
||||
|
||||
if (priv->rubberband->modify)
|
||||
{
|
||||
gtk_selection_model_unselect_callback (model, range_cb, priv->rubberband->active);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_selection_model_select_callback (model,
|
||||
!priv->rubberband->extend,
|
||||
range_cb, priv->rubberband->active);
|
||||
if (!gtk_list_base_get_rubberband_coords (self, &rect))
|
||||
return;
|
||||
|
||||
rubberband_selection = gtk_list_base_get_items_in_rect (self, &rect);
|
||||
if (gtk_bitset_is_empty (rubberband_selection))
|
||||
{
|
||||
gtk_bitset_unref (rubberband_selection);
|
||||
return;
|
||||
}
|
||||
|
||||
if (modify && extend) /* Ctrl + Shift */
|
||||
{
|
||||
GtkBitset *current;
|
||||
guint min = gtk_bitset_get_minimum (rubberband_selection);
|
||||
guint max = gtk_bitset_get_maximum (rubberband_selection);
|
||||
/* toggle the rubberband, keep the rest */
|
||||
current = gtk_selection_model_get_selection_in_range (model, min, max - min + 1);
|
||||
selected = gtk_bitset_copy (current);
|
||||
gtk_bitset_unref (current);
|
||||
gtk_bitset_intersect (selected, rubberband_selection);
|
||||
gtk_bitset_difference (selected, rubberband_selection);
|
||||
|
||||
mask = gtk_bitset_ref (rubberband_selection);
|
||||
}
|
||||
else if (modify) /* Ctrl */
|
||||
{
|
||||
/* select the rubberband, keep the rest */
|
||||
selected = gtk_bitset_ref (rubberband_selection);
|
||||
mask = gtk_bitset_ref (rubberband_selection);
|
||||
}
|
||||
else if (extend) /* Shift */
|
||||
{
|
||||
/* unselect the rubberband, keep the rest */
|
||||
selected = gtk_bitset_new_empty ();
|
||||
mask = gtk_bitset_ref (rubberband_selection);
|
||||
}
|
||||
else /* no modifer */
|
||||
{
|
||||
/* select the rubberband, clear the rest */
|
||||
selected = gtk_bitset_ref (rubberband_selection);
|
||||
mask = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add_range (mask, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
|
||||
}
|
||||
|
||||
gtk_selection_model_set_selection (model, selected, mask);
|
||||
|
||||
gtk_bitset_unref (selected);
|
||||
gtk_bitset_unref (mask);
|
||||
gtk_bitset_unref (rubberband_selection);
|
||||
}
|
||||
|
||||
g_clear_pointer (&priv->rubberband, rubberband_data_free);
|
||||
gtk_list_item_tracker_free (priv->item_manager, priv->rubberband->start_tracker);
|
||||
g_clear_pointer (&priv->rubberband->widget, gtk_widget_unparent);
|
||||
g_free (priv->rubberband);
|
||||
priv->rubberband = NULL;
|
||||
|
||||
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);
|
||||
GtkListItemManagerItem *item;
|
||||
GdkRectangle rect;
|
||||
guint pos;
|
||||
GtkBitset *rubberband_selection;
|
||||
|
||||
if (!gtk_list_base_get_rubberband_coords (self, &rect))
|
||||
return;
|
||||
|
||||
rubberband_selection = gtk_list_base_get_items_in_rect (self, &rect);
|
||||
|
||||
pos = 0;
|
||||
for (item = gtk_list_item_manager_get_first (priv->item_manager);
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
if (item->widget)
|
||||
{
|
||||
if (gtk_bitset_contains (rubberband_selection, pos))
|
||||
gtk_widget_set_state_flags (item->widget, GTK_STATE_FLAG_ACTIVE, FALSE);
|
||||
else
|
||||
gtk_widget_unset_state_flags (item->widget, GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
|
||||
pos += item->n_items;
|
||||
}
|
||||
|
||||
gtk_bitset_unref (rubberband_selection);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1461,51 +1666,14 @@ gtk_list_base_update_rubberband (GtkListBase *self,
|
||||
if (!priv->rubberband)
|
||||
return;
|
||||
|
||||
priv->rubberband->x2 = x + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_HORIZONTAL]);
|
||||
priv->rubberband->y2 = y + gtk_adjustment_get_value (priv->adjustment[GTK_ORIENTATION_VERTICAL]);
|
||||
priv->rubberband->pointer_x = x;
|
||||
priv->rubberband->pointer_y = y;
|
||||
|
||||
gtk_list_base_update_rubberband_selection (self);
|
||||
|
||||
update_autoscroll (self, x, y);
|
||||
|
||||
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;
|
||||
GtkListItemManagerItem *item;
|
||||
|
||||
gtk_list_base_allocate_rubberband (self);
|
||||
gtk_widget_get_allocation (priv->rubberband->widget, &rect);
|
||||
|
||||
for (item = gtk_list_item_manager_get_first (priv->item_manager);
|
||||
item != NULL;
|
||||
item = gtk_rb_tree_node_get_next (item))
|
||||
{
|
||||
guint pos;
|
||||
|
||||
if (!item->widget)
|
||||
continue;
|
||||
|
||||
pos = gtk_list_item_manager_get_item_position (priv->item_manager, item);
|
||||
|
||||
gtk_widget_get_allocation (item->widget, &alloc);
|
||||
|
||||
if (gdk_rectangle_intersect (&rect, &alloc, &alloc))
|
||||
{
|
||||
gtk_set_add_item (priv->rubberband->active, pos);
|
||||
gtk_widget_set_state_flags (item->widget, GTK_STATE_FLAG_ACTIVE, FALSE);
|
||||
}
|
||||
else
|
||||
{
|
||||
gtk_set_remove_item (priv->rubberband->active, pos);
|
||||
gtk_widget_unset_state_flags (item->widget, GTK_STATE_FLAG_ACTIVE);
|
||||
}
|
||||
}
|
||||
gtk_widget_queue_allocate (GTK_WIDGET (self));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -1529,27 +1697,25 @@ get_selection_modifiers (GtkGesture *gesture,
|
||||
*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)
|
||||
{
|
||||
GtkListBasePrivate *priv = gtk_list_base_get_instance_private (self);
|
||||
double start_x, start_y;
|
||||
|
||||
gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
|
||||
|
||||
if (!priv->rubberband)
|
||||
{
|
||||
if (!gtk_drag_check_threshold (GTK_WIDGET (self), 0, 0, offset_x, offset_y))
|
||||
return;
|
||||
|
||||
gtk_gesture_set_state (GTK_GESTURE (gesture), GTK_EVENT_SEQUENCE_CLAIMED);
|
||||
gtk_list_base_start_rubberband (self, start_x, start_y);
|
||||
}
|
||||
gtk_list_base_update_rubberband (self, start_x + offset_x, start_y + offset_y);
|
||||
}
|
||||
|
||||
@ -1559,8 +1725,11 @@ gtk_list_base_drag_end (GtkGestureDrag *gesture,
|
||||
double offset_y,
|
||||
GtkListBase *self)
|
||||
{
|
||||
gboolean modify, extend;
|
||||
|
||||
gtk_list_base_drag_update (gesture, offset_x, offset_y, self);
|
||||
gtk_list_base_stop_rubberband (self);
|
||||
get_selection_modifiers (GTK_GESTURE (gesture), &modify, &extend);
|
||||
gtk_list_base_stop_rubberband (self, modify, extend);
|
||||
}
|
||||
|
||||
void
|
||||
@ -1577,7 +1746,6 @@ gtk_list_base_set_enable_rubberband (GtkListBase *self,
|
||||
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));
|
||||
|
@ -54,6 +54,8 @@ struct _GtkListBaseClass
|
||||
int along,
|
||||
guint *pos,
|
||||
cairo_rectangle_int_t *area);
|
||||
GtkBitset * (* get_items_in_rect) (GtkListBase *self,
|
||||
const cairo_rectangle_int_t *rect);
|
||||
guint (* move_focus_along) (GtkListBase *self,
|
||||
guint pos,
|
||||
int steps);
|
||||
@ -99,10 +101,17 @@ 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_allocate_rubberband (GtkListBase *self);
|
||||
|
||||
void gtk_list_base_size_allocate_child (GtkListBase *self,
|
||||
GtkWidget *child,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height);
|
||||
|
||||
#endif /* __GTK_LIST_BASE_PRIVATE_H__ */
|
||||
|
@ -88,10 +88,10 @@ GtkSelectionModel * gtk_list_item_manager_get_model (GtkListItemMana
|
||||
|
||||
guint gtk_list_item_manager_get_size (GtkListItemManager *self);
|
||||
void gtk_list_item_manager_set_single_click_activate
|
||||
(GtkListItemManager *self,
|
||||
gboolean single_click_activate);
|
||||
(GtkListItemManager *self,
|
||||
gboolean single_click_activate);
|
||||
gboolean gtk_list_item_manager_get_single_click_activate
|
||||
(GtkListItemManager *self);
|
||||
(GtkListItemManager *self);
|
||||
|
||||
GtkListItemTracker * gtk_list_item_tracker_new (GtkListItemManager *self);
|
||||
void gtk_list_item_tracker_free (GtkListItemManager *self,
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "gtklistview.h"
|
||||
|
||||
#include "gtkbitset.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtklistbaseprivate.h"
|
||||
#include "gtklistitemmanagerprivate.h"
|
||||
@ -374,6 +375,36 @@ gtk_list_view_get_allocation_across (GtkListBase *base,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
gtk_list_view_get_items_in_rect (GtkListBase *base,
|
||||
const cairo_rectangle_int_t *rect)
|
||||
{
|
||||
GtkListView *self = GTK_LIST_VIEW (base);
|
||||
guint first, last, n_items;
|
||||
GtkBitset *result;
|
||||
ListRow *row;
|
||||
|
||||
result = gtk_bitset_new_empty ();
|
||||
|
||||
n_items = gtk_list_base_get_n_items (base);
|
||||
if (n_items == 0)
|
||||
return result;
|
||||
|
||||
row = gtk_list_view_get_row_at_y (self, rect->y, NULL);
|
||||
if (row)
|
||||
first = gtk_list_item_manager_get_item_position (self->item_manager, row);
|
||||
else
|
||||
first = rect->y < 0 ? 0 : n_items - 1;
|
||||
row = gtk_list_view_get_row_at_y (self, rect->y + rect->height, NULL);
|
||||
if (row)
|
||||
last = gtk_list_item_manager_get_item_position (self->item_manager, row);
|
||||
else
|
||||
last = rect->y < 0 ? 0 : n_items - 1;
|
||||
|
||||
gtk_bitset_add_range_closed (result, first, last);
|
||||
return result;
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_list_view_move_focus_along (GtkListBase *base,
|
||||
guint pos,
|
||||
@ -553,43 +584,6 @@ gtk_list_view_measure (GtkWidget *widget,
|
||||
gtk_list_view_measure_across (widget, orientation, for_size, minimum, natural);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_size_allocate_child (GtkListView *self,
|
||||
GtkWidget *child,
|
||||
int x,
|
||||
int y,
|
||||
int width,
|
||||
int height)
|
||||
{
|
||||
GtkAllocation child_allocation;
|
||||
|
||||
if (gtk_list_base_get_orientation (GTK_LIST_BASE (self)) == GTK_ORIENTATION_VERTICAL)
|
||||
{
|
||||
child_allocation.x = x;
|
||||
child_allocation.y = y;
|
||||
child_allocation.width = width;
|
||||
child_allocation.height = height;
|
||||
}
|
||||
else if (_gtk_widget_get_direction (GTK_WIDGET (self)) == GTK_TEXT_DIR_LTR)
|
||||
{
|
||||
child_allocation.x = y;
|
||||
child_allocation.y = x;
|
||||
child_allocation.width = height;
|
||||
child_allocation.height = width;
|
||||
}
|
||||
else
|
||||
{
|
||||
int mirror_point = gtk_widget_get_width (GTK_WIDGET (self));
|
||||
|
||||
child_allocation.x = mirror_point - y - height;
|
||||
child_allocation.y = x;
|
||||
child_allocation.width = height;
|
||||
child_allocation.height = width;
|
||||
}
|
||||
|
||||
gtk_widget_size_allocate (child, &child_allocation, -1);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_list_view_size_allocate (GtkWidget *widget,
|
||||
int width,
|
||||
@ -604,8 +598,6 @@ gtk_list_view_size_allocate (GtkWidget *widget,
|
||||
GtkOrientation orientation, opposite_orientation;
|
||||
GtkScrollablePolicy scroll_policy;
|
||||
|
||||
gtk_list_base_allocate_rubberband (GTK_LIST_BASE (self));
|
||||
|
||||
orientation = gtk_list_base_get_orientation (GTK_LIST_BASE (self));
|
||||
opposite_orientation = OPPOSITE_ORIENTATION (orientation);
|
||||
scroll_policy = gtk_list_base_get_scroll_policy (GTK_LIST_BASE (self), orientation);
|
||||
@ -685,7 +677,7 @@ gtk_list_view_size_allocate (GtkWidget *widget,
|
||||
{
|
||||
if (row->parent.widget)
|
||||
{
|
||||
gtk_list_view_size_allocate_child (self,
|
||||
gtk_list_base_size_allocate_child (GTK_LIST_BASE (self),
|
||||
row->parent.widget,
|
||||
x,
|
||||
y,
|
||||
@ -695,6 +687,8 @@ gtk_list_view_size_allocate (GtkWidget *widget,
|
||||
|
||||
y += row->height * row->parent.n_items;
|
||||
}
|
||||
|
||||
gtk_list_base_allocate_rubberband (GTK_LIST_BASE (self));
|
||||
}
|
||||
|
||||
static void
|
||||
@ -810,6 +804,7 @@ gtk_list_view_class_init (GtkListViewClass *klass)
|
||||
list_base_class->list_item_augment_func = list_row_augment;
|
||||
list_base_class->get_allocation_along = gtk_list_view_get_allocation_along;
|
||||
list_base_class->get_allocation_across = gtk_list_view_get_allocation_across;
|
||||
list_base_class->get_items_in_rect = gtk_list_view_get_items_in_rect;
|
||||
list_base_class->get_position_from_allocation = gtk_list_view_get_position_from_allocation;
|
||||
list_base_class->move_focus_along = gtk_list_view_move_focus_along;
|
||||
list_base_class->move_focus_across = gtk_list_view_move_focus_across;
|
||||
|
@ -21,9 +21,9 @@
|
||||
|
||||
#include "gtkmultiselection.h"
|
||||
|
||||
#include "gtkbitset.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
#include "gtkset.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkmultiselection
|
||||
@ -47,7 +47,8 @@ struct _GtkMultiSelection
|
||||
|
||||
GListModel *model;
|
||||
|
||||
GtkSet *selected;
|
||||
GtkBitset *selected;
|
||||
GHashTable *items; /* item => position */
|
||||
};
|
||||
|
||||
struct _GtkMultiSelectionClass
|
||||
@ -103,175 +104,106 @@ gtk_multi_selection_is_selected (GtkSelectionModel *model,
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
|
||||
return gtk_set_contains (self->selected, position);
|
||||
return gtk_bitset_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);
|
||||
guint min = G_MAXUINT;
|
||||
guint max = 0;
|
||||
|
||||
if (exclusive)
|
||||
{
|
||||
min = gtk_set_get_min (self->selected);
|
||||
max = gtk_set_get_max (self->selected);
|
||||
gtk_set_remove_all (self->selected);
|
||||
}
|
||||
|
||||
gtk_set_add_range (self->selected, position, n_items);
|
||||
|
||||
min = MIN (position, min);
|
||||
max = MAX (max, position + n_items - 1);
|
||||
|
||||
gtk_selection_model_selection_changed (model, min, max - min + 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_unselect_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
static GtkBitset *
|
||||
gtk_multi_selection_get_selection_in_range (GtkSelectionModel *model,
|
||||
guint pos,
|
||||
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;
|
||||
return gtk_bitset_ref (self->selected);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean exclusive)
|
||||
static void
|
||||
gtk_multi_selection_toggle_selection (GtkMultiSelection *self,
|
||||
GtkBitset *changes)
|
||||
{
|
||||
return gtk_multi_selection_select_range (model, position, 1, exclusive);
|
||||
}
|
||||
GListModel *model = G_LIST_MODEL (self);
|
||||
GtkBitsetIter change_iter, selected_iter;
|
||||
GtkBitset *selected;
|
||||
guint change_pos, selected_pos;
|
||||
gboolean more;
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_unselect_item (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
return gtk_multi_selection_unselect_range (model, position, 1);
|
||||
}
|
||||
gtk_bitset_difference (self->selected, changes);
|
||||
|
||||
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);
|
||||
}
|
||||
selected = gtk_bitset_copy (changes);
|
||||
gtk_bitset_intersect (selected, self->selected);
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_unselect_all (GtkSelectionModel *model)
|
||||
{
|
||||
return gtk_multi_selection_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
|
||||
}
|
||||
if (!gtk_bitset_iter_init_first (&selected_iter, selected, &selected_pos))
|
||||
selected_pos = G_MAXUINT;
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_add_or_remove (GtkSelectionModel *model,
|
||||
gboolean unselect_rest,
|
||||
gboolean add,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
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;
|
||||
|
||||
if (unselect_rest)
|
||||
for (more = gtk_bitset_iter_init_first (&change_iter, changes, &change_pos);
|
||||
more;
|
||||
more = gtk_bitset_iter_next (&change_iter, &change_pos))
|
||||
{
|
||||
min = gtk_set_get_min (self->selected);
|
||||
max = gtk_set_get_max (self->selected);
|
||||
gtk_set_remove_all (self->selected);
|
||||
}
|
||||
gpointer item = g_list_model_get_item (model, change_pos);
|
||||
|
||||
for (pos = 0; pos < n; pos = start + n_items)
|
||||
{
|
||||
callback (pos, &start, &n_items, &in, data);
|
||||
|
||||
if (n_items == 0)
|
||||
break;
|
||||
|
||||
g_assert (start <= pos && pos < start + n_items);
|
||||
|
||||
if (in)
|
||||
if (change_pos < selected_pos)
|
||||
{
|
||||
if (start < min)
|
||||
min = start;
|
||||
if (start + n_items - 1 > max)
|
||||
max = start + n_items - 1;
|
||||
|
||||
if (add)
|
||||
gtk_set_add_range (self->selected, start, n_items);
|
||||
else
|
||||
gtk_set_remove_range (self->selected, start, n_items);
|
||||
g_hash_table_remove (self->items, item);
|
||||
g_object_unref (item);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert (change_pos == selected_pos);
|
||||
|
||||
pos = start + n_items;
|
||||
g_hash_table_insert (self->items, item, GUINT_TO_POINTER (change_pos));
|
||||
|
||||
if (!gtk_bitset_iter_next (&selected_iter, &selected_pos))
|
||||
selected_pos = G_MAXUINT;
|
||||
}
|
||||
}
|
||||
|
||||
gtk_bitset_unref (selected);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_set_selection (GtkSelectionModel *model,
|
||||
GtkBitset *selected,
|
||||
GtkBitset *mask)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (model);
|
||||
GtkBitset *changes;
|
||||
guint min, max, n_items;
|
||||
|
||||
/* changes = (self->selected XOR selected) AND mask
|
||||
* But doing it this way avoids looking at all values outside the mask
|
||||
*/
|
||||
changes = gtk_bitset_copy (selected);
|
||||
gtk_bitset_difference (changes, self->selected);
|
||||
gtk_bitset_intersect (changes, mask);
|
||||
|
||||
min = gtk_bitset_get_minimum (changes);
|
||||
max = gtk_bitset_get_maximum (changes);
|
||||
|
||||
/* sanity check */
|
||||
n_items = g_list_model_get_n_items (self->model);
|
||||
if (max >= n_items)
|
||||
{
|
||||
gtk_bitset_remove_range_closed (changes, n_items, max);
|
||||
max = gtk_bitset_get_maximum (changes);
|
||||
}
|
||||
|
||||
/* actually do the change */
|
||||
gtk_multi_selection_toggle_selection (self, changes);
|
||||
|
||||
gtk_bitset_unref (changes);
|
||||
|
||||
if (min <= max)
|
||||
gtk_selection_model_selection_changed (model, min, max - min + 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_select_callback (GtkSelectionModel *model,
|
||||
gboolean unselect_rest,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
return gtk_multi_selection_add_or_remove (model, unselect_rest, TRUE, callback, data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_multi_selection_unselect_callback (GtkSelectionModel *model,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
return gtk_multi_selection_add_or_remove (model, FALSE, FALSE, callback, data);
|
||||
}
|
||||
|
||||
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->select_callback = gtk_multi_selection_select_callback;
|
||||
iface->unselect_callback = gtk_multi_selection_unselect_callback;
|
||||
iface->query_range = gtk_multi_selection_query_range;
|
||||
iface->get_selection_in_range = gtk_multi_selection_get_selection_in_range;
|
||||
iface->set_selection = gtk_multi_selection_set_selection;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (GtkMultiSelection, gtk_multi_selection, G_TYPE_OBJECT, 0,
|
||||
@ -287,8 +219,57 @@ gtk_multi_selection_items_changed_cb (GListModel *model,
|
||||
guint added,
|
||||
GtkMultiSelection *self)
|
||||
{
|
||||
gtk_set_remove_range (self->selected, position, removed);
|
||||
gtk_set_shift (self->selected, position, (int)added - (int)removed);
|
||||
GHashTableIter iter;
|
||||
gpointer item, pos_pointer;
|
||||
GHashTable *pending = NULL;
|
||||
guint i;
|
||||
|
||||
gtk_bitset_slice (self->selected, position, removed, added);
|
||||
|
||||
g_hash_table_iter_init (&iter, self->items);
|
||||
while (g_hash_table_iter_next (&iter, &item, &pos_pointer))
|
||||
{
|
||||
guint pos = GPOINTER_TO_UINT (pos_pointer);
|
||||
|
||||
if (pos < position)
|
||||
continue;
|
||||
else if (pos >= position + removed)
|
||||
g_hash_table_iter_replace (&iter, GUINT_TO_POINTER (pos - removed + added));
|
||||
else /* if pos is in the removed range */
|
||||
{
|
||||
if (added == 0)
|
||||
{
|
||||
g_hash_table_iter_remove (&iter);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_hash_table_iter_steal (&iter);
|
||||
if (pending == NULL)
|
||||
pending = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
|
||||
g_hash_table_add (pending, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = position; pending != NULL && i < position + added; i++)
|
||||
{
|
||||
item = g_list_model_get_item (model, i);
|
||||
if (g_hash_table_contains (pending, item))
|
||||
{
|
||||
gtk_bitset_add (self->selected, i);
|
||||
g_hash_table_insert (self->items, item, GUINT_TO_POINTER (i));
|
||||
g_hash_table_remove (pending, item);
|
||||
if (g_hash_table_size (pending) == 0)
|
||||
g_clear_pointer (&pending, g_hash_table_unref);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_object_unref (item);
|
||||
}
|
||||
}
|
||||
|
||||
g_clear_pointer (&pending, g_hash_table_unref);
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
}
|
||||
|
||||
@ -306,9 +287,9 @@ gtk_multi_selection_clear_model (GtkMultiSelection *self)
|
||||
|
||||
static void
|
||||
gtk_multi_selection_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (object);
|
||||
@ -332,9 +313,9 @@ gtk_multi_selection_set_property (GObject *object,
|
||||
|
||||
static void
|
||||
gtk_multi_selection_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkMultiSelection *self = GTK_MULTI_SELECTION (object);
|
||||
|
||||
@ -357,7 +338,8 @@ gtk_multi_selection_dispose (GObject *object)
|
||||
|
||||
gtk_multi_selection_clear_model (self);
|
||||
|
||||
g_clear_pointer (&self->selected, gtk_set_free);
|
||||
g_clear_pointer (&self->selected, gtk_bitset_unref);
|
||||
g_clear_pointer (&self->items, g_hash_table_unref);
|
||||
|
||||
G_OBJECT_CLASS (gtk_multi_selection_parent_class)->dispose (object);
|
||||
}
|
||||
@ -389,7 +371,8 @@ gtk_multi_selection_class_init (GtkMultiSelectionClass *klass)
|
||||
static void
|
||||
gtk_multi_selection_init (GtkMultiSelection *self)
|
||||
{
|
||||
self->selected = gtk_set_new ();
|
||||
self->selected = gtk_bitset_new_empty ();
|
||||
self->items = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "gtknoselection.h"
|
||||
|
||||
#include "gtkbitset.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
|
||||
@ -96,25 +97,19 @@ gtk_no_selection_is_selected (GtkSelectionModel *model,
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_query_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint *start_range,
|
||||
guint *n_range,
|
||||
gboolean *selected)
|
||||
static GtkBitset *
|
||||
gtk_no_selection_get_selection_in_range (GtkSelectionModel *model,
|
||||
guint pos,
|
||||
guint n_items)
|
||||
{
|
||||
GtkNoSelection *self = GTK_NO_SELECTION (model);
|
||||
|
||||
*start_range = 0;
|
||||
*n_range = g_list_model_get_n_items (self->model);
|
||||
*selected = FALSE;
|
||||
return gtk_bitset_new_empty ();
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_no_selection_selection_model_init (GtkSelectionModelInterface *iface)
|
||||
{
|
||||
iface->is_selected = gtk_no_selection_is_selected;
|
||||
iface->query_range = gtk_no_selection_query_range;
|
||||
iface->is_selected = gtk_no_selection_is_selected;
|
||||
iface->get_selection_in_range = gtk_no_selection_get_selection_in_range;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (GtkNoSelection, gtk_no_selection, G_TYPE_OBJECT, 0,
|
||||
|
@ -1,532 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2020 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 "gtkpropertyselection.h"
|
||||
|
||||
#include "gtkintl.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
|
||||
/**
|
||||
* SECTION:gtkpropertyselection
|
||||
* @Short_description: A selection model that uses an item property
|
||||
* @Title: GtkPropertySelection
|
||||
* @see_also: #GtkSelectionModel
|
||||
*
|
||||
* GtkPropertySelection is an implementation of the #GtkSelectionModel
|
||||
* interface that stores the selected state for each item in a property
|
||||
* of the item.
|
||||
*
|
||||
* The property named by #GtkPropertySelection:property must be writable
|
||||
* boolean property of the item type. GtkPropertySelection preserves the
|
||||
* selected state of items when they are added to the model, but it does
|
||||
* not listen to changes of the property while the item is a part of the
|
||||
* model. It assumes that it has *exclusive* access to the property.
|
||||
*
|
||||
* The advantage of storing the selected state in item properties is that
|
||||
* the state is *persistent* -- when an item is removed and re-added to
|
||||
* the model, it will still have the same selection state. In particular,
|
||||
* this makes the selection persist across changes of the sort order if
|
||||
* the underlying model is a #GtkSortListModel.
|
||||
*/
|
||||
|
||||
struct _GtkPropertySelection
|
||||
{
|
||||
GObject parent_instance;
|
||||
|
||||
GListModel *model;
|
||||
char *property;
|
||||
};
|
||||
|
||||
struct _GtkPropertySelectionClass
|
||||
{
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
PROP_MODEL,
|
||||
PROP_PROPERTY,
|
||||
|
||||
N_PROPS,
|
||||
};
|
||||
|
||||
static GParamSpec *properties[N_PROPS] = { NULL, };
|
||||
|
||||
static GType
|
||||
gtk_property_selection_get_item_type (GListModel *list)
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (list);
|
||||
|
||||
return g_list_model_get_item_type (self->model);
|
||||
}
|
||||
|
||||
static guint
|
||||
gtk_property_selection_get_n_items (GListModel *list)
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (list);
|
||||
|
||||
return g_list_model_get_n_items (self->model);
|
||||
}
|
||||
|
||||
static gpointer
|
||||
gtk_property_selection_get_item (GListModel *list,
|
||||
guint position)
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (list);
|
||||
|
||||
return g_list_model_get_item (self->model, position);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_property_selection_list_model_init (GListModelInterface *iface)
|
||||
{
|
||||
iface->get_item_type = gtk_property_selection_get_item_type;
|
||||
iface->get_n_items = gtk_property_selection_get_n_items;
|
||||
iface->get_item = gtk_property_selection_get_item;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
is_selected (GtkPropertySelection *self,
|
||||
guint position)
|
||||
{
|
||||
gpointer item;
|
||||
gboolean ret;
|
||||
|
||||
item = g_list_model_get_item (self->model, position);
|
||||
g_object_get (item, self->property, &ret, NULL);
|
||||
g_object_unref (item);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void
|
||||
set_selected (GtkPropertySelection *self,
|
||||
guint position,
|
||||
gboolean selected)
|
||||
{
|
||||
gpointer item;
|
||||
|
||||
item = g_list_model_get_item (self->model, position);
|
||||
g_object_set (item, self->property, selected, NULL);
|
||||
g_object_unref (item);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_is_selected (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
return is_selected (GTK_PROPERTY_SELECTION (model), position);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_select_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items,
|
||||
gboolean exclusive)
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (model);
|
||||
guint i;
|
||||
guint n;
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
if (exclusive)
|
||||
{
|
||||
for (i = 0; i < n; i++)
|
||||
set_selected (self, i, FALSE);
|
||||
}
|
||||
|
||||
for (i = position; i < position + n_items; i++)
|
||||
set_selected (self, i, TRUE);
|
||||
|
||||
/* FIXME: do better here */
|
||||
if (exclusive)
|
||||
gtk_selection_model_selection_changed (model, 0, n);
|
||||
else
|
||||
gtk_selection_model_selection_changed (model, position, n_items);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_unselect_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (model);
|
||||
guint i;
|
||||
|
||||
for (i = position; i < position + n_items; i++)
|
||||
set_selected (self, i, FALSE);
|
||||
|
||||
gtk_selection_model_selection_changed (model, position, n_items);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean exclusive)
|
||||
{
|
||||
return gtk_property_selection_select_range (model, position, 1, exclusive);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_unselect_item (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
return gtk_property_selection_unselect_range (model, position, 1);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_select_all (GtkSelectionModel *model)
|
||||
{
|
||||
return gtk_property_selection_select_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)), FALSE);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_unselect_all (GtkSelectionModel *model)
|
||||
{
|
||||
return gtk_property_selection_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_add_or_remove (GtkSelectionModel *model,
|
||||
gboolean unselect_rest,
|
||||
gboolean add,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (model);
|
||||
guint pos, start, n, n_items;
|
||||
gboolean in;
|
||||
guint min, max;
|
||||
guint i;
|
||||
|
||||
n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
if (unselect_rest)
|
||||
{
|
||||
for (i = 0; i < n_items; i++)
|
||||
set_selected (self, i, FALSE);
|
||||
}
|
||||
|
||||
min = G_MAXUINT;
|
||||
max = 0;
|
||||
|
||||
pos = 0;
|
||||
do
|
||||
{
|
||||
callback (pos, &start, &n, &in, data);
|
||||
if (in)
|
||||
{
|
||||
if (start < min)
|
||||
min = start;
|
||||
if (start + n - 1 > max)
|
||||
max = start + n - 1;
|
||||
|
||||
for (i = start; i < start + n; i++)
|
||||
set_selected (self, i, add);
|
||||
}
|
||||
pos = start + n;
|
||||
}
|
||||
while (n > 0);
|
||||
|
||||
/* FIXME: do better here */
|
||||
if (unselect_rest)
|
||||
gtk_selection_model_selection_changed (model, 0, n_items);
|
||||
else if (min <= max)
|
||||
gtk_selection_model_selection_changed (model, min, max - min + 1);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_select_callback (GtkSelectionModel *model,
|
||||
gboolean unselect_rest,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
return gtk_property_selection_add_or_remove (model, unselect_rest, TRUE, callback, data);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_property_selection_unselect_callback (GtkSelectionModel *model,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
return gtk_property_selection_add_or_remove (model, FALSE, FALSE, callback, data);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_property_selection_query_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint *start_range,
|
||||
guint *n_items,
|
||||
gboolean *selected)
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (model);
|
||||
guint n;
|
||||
gboolean sel;
|
||||
guint start, end;
|
||||
|
||||
n = g_list_model_get_n_items (G_LIST_MODEL (self));
|
||||
sel = is_selected (self, position);
|
||||
|
||||
start = position;
|
||||
while (start > 0)
|
||||
{
|
||||
if (is_selected (self, start - 1) != sel)
|
||||
break;
|
||||
start--;
|
||||
}
|
||||
|
||||
end = position;
|
||||
while (end + 1 < n)
|
||||
{
|
||||
if (is_selected (self, end + 1) != sel)
|
||||
break;
|
||||
end++;
|
||||
}
|
||||
|
||||
*start_range = start;
|
||||
*n_items = end - start + 1;
|
||||
*selected = sel;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_property_selection_selection_model_init (GtkSelectionModelInterface *iface)
|
||||
{
|
||||
iface->is_selected = gtk_property_selection_is_selected;
|
||||
iface->select_item = gtk_property_selection_select_item;
|
||||
iface->unselect_item = gtk_property_selection_unselect_item;
|
||||
iface->select_range = gtk_property_selection_select_range;
|
||||
iface->unselect_range = gtk_property_selection_unselect_range;
|
||||
iface->select_all = gtk_property_selection_select_all;
|
||||
iface->unselect_all = gtk_property_selection_unselect_all;
|
||||
iface->select_callback = gtk_property_selection_select_callback;
|
||||
iface->unselect_callback = gtk_property_selection_unselect_callback;
|
||||
iface->query_range = gtk_property_selection_query_range;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (GtkPropertySelection, gtk_property_selection, G_TYPE_OBJECT, 0,
|
||||
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL,
|
||||
gtk_property_selection_list_model_init)
|
||||
G_IMPLEMENT_INTERFACE (GTK_TYPE_SELECTION_MODEL,
|
||||
gtk_property_selection_selection_model_init))
|
||||
|
||||
static void
|
||||
gtk_property_selection_items_changed_cb (GListModel *model,
|
||||
guint position,
|
||||
guint removed,
|
||||
guint added,
|
||||
GtkPropertySelection *self)
|
||||
{
|
||||
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_property_selection_clear_model (GtkPropertySelection *self)
|
||||
{
|
||||
if (self->model == NULL)
|
||||
return;
|
||||
|
||||
g_signal_handlers_disconnect_by_func (self->model,
|
||||
gtk_property_selection_items_changed_cb,
|
||||
self);
|
||||
g_clear_object (&self->model);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_property_selection_real_set_property (GObject *object,
|
||||
guint prop_id,
|
||||
const GValue *value,
|
||||
GParamSpec *pspec)
|
||||
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_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_property_selection_items_changed_cb),
|
||||
self);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTY:
|
||||
{
|
||||
GObjectClass *class;
|
||||
GParamSpec *prop;
|
||||
|
||||
self->property = g_value_dup_string (value);
|
||||
g_warn_if_fail (self->property != NULL);
|
||||
|
||||
class = g_type_class_ref (g_list_model_get_item_type (self->model));
|
||||
prop = g_object_class_find_property (class, self->property);
|
||||
g_warn_if_fail (prop != NULL &&
|
||||
prop->value_type == G_TYPE_BOOLEAN &&
|
||||
((prop->flags & (G_PARAM_READABLE|G_PARAM_WRITABLE|G_PARAM_CONSTRUCT_ONLY)) ==
|
||||
(G_PARAM_READABLE|G_PARAM_WRITABLE)));
|
||||
g_type_class_unref (class);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_property_selection_real_get_property (GObject *object,
|
||||
guint prop_id,
|
||||
GValue *value,
|
||||
GParamSpec *pspec)
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (object);
|
||||
|
||||
switch (prop_id)
|
||||
{
|
||||
case PROP_MODEL:
|
||||
g_value_set_object (value, self->model);
|
||||
break;
|
||||
|
||||
case PROP_PROPERTY:
|
||||
g_value_set_string (value, self->property);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_property_selection_dispose (GObject *object)
|
||||
{
|
||||
GtkPropertySelection *self = GTK_PROPERTY_SELECTION (object);
|
||||
|
||||
gtk_property_selection_clear_model (self);
|
||||
|
||||
g_free (self->property);
|
||||
|
||||
G_OBJECT_CLASS (gtk_property_selection_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_property_selection_class_init (GtkPropertySelectionClass *klass)
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
gobject_class->get_property = gtk_property_selection_real_get_property;
|
||||
gobject_class->set_property = gtk_property_selection_real_set_property;
|
||||
gobject_class->dispose = gtk_property_selection_dispose;
|
||||
|
||||
/**
|
||||
* GtkPropertySelection: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);
|
||||
|
||||
properties[PROP_PROPERTY] =
|
||||
g_param_spec_string ("property",
|
||||
P_("Property"),
|
||||
P_("Item property to store selection state in"),
|
||||
NULL,
|
||||
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_property_selection_init (GtkPropertySelection *self)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_property_selection_new:
|
||||
* @model: (transfer none): the #GListModel to manage
|
||||
* @property: the item property to use
|
||||
*
|
||||
* Creates a new property selection to handle @model.
|
||||
*
|
||||
* @property must be the name of a writable boolean property
|
||||
* of the item type of @model.
|
||||
*
|
||||
* Note that GtkPropertySelection does not monitor the property
|
||||
* for changes while the item is part of the model, but it does
|
||||
* inherit the initial value when an item is added to the model.
|
||||
*
|
||||
* Returns: (transfer full) (type GtkPropertySelection): a new #GtkPropertySelection
|
||||
**/
|
||||
GListModel *
|
||||
gtk_property_selection_new (GListModel *model,
|
||||
const char *property)
|
||||
{
|
||||
g_return_val_if_fail (G_IS_LIST_MODEL (model), NULL);
|
||||
|
||||
return g_object_new (GTK_TYPE_PROPERTY_SELECTION,
|
||||
"model", model,
|
||||
"property", property,
|
||||
NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_property_selection_get_model:
|
||||
* @self: a #GtkPropertySelection
|
||||
*
|
||||
* Gets the underlying model.
|
||||
*
|
||||
* Returns: (transfer none): the underlying model
|
||||
*/
|
||||
GListModel *
|
||||
gtk_property_selection_get_model (GtkPropertySelection *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_PROPERTY_SELECTION (self), NULL);
|
||||
|
||||
return self->model;
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_property_selection_get_property:
|
||||
* @self: a #GtkPropertySelection
|
||||
*
|
||||
* Gets the name of the item property that @self stores
|
||||
* the selection in.
|
||||
*
|
||||
* Returns: the name of the property
|
||||
*/
|
||||
const char *
|
||||
gtk_property_selection_get_property (GtkPropertySelection *self)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_PROPERTY_SELECTION (self), NULL);
|
||||
|
||||
return self->property;
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* 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_PROPERTY_SELECTION_H__
|
||||
#define __GTK_PROPERTY_SELECTION_H__
|
||||
|
||||
#include <gtk/gtktypes.h>
|
||||
#include <gtk/gtkselectionmodel.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_PROPERTY_SELECTION (gtk_property_selection_get_type ())
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
G_DECLARE_FINAL_TYPE (GtkPropertySelection, gtk_property_selection, GTK, PROPERTY_SELECTION, GObject)
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_property_selection_new (GListModel *model,
|
||||
const char *property);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GListModel * gtk_property_selection_get_model (GtkPropertySelection *self);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
const char * gtk_property_selection_get_property (GtkPropertySelection *self);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_PROPERTY_SELECTION_H__ */
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "gtkselectionmodel.h"
|
||||
|
||||
#include "gtkbitset.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkmarshalers.h"
|
||||
|
||||
@ -79,7 +80,33 @@ static gboolean
|
||||
gtk_selection_model_default_is_selected (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
return FALSE;
|
||||
GtkBitset *bitset;
|
||||
gboolean selected;
|
||||
|
||||
bitset = gtk_selection_model_get_selection_in_range (model, position, 1);
|
||||
selected = gtk_bitset_contains (bitset, position);
|
||||
gtk_bitset_unref (bitset);
|
||||
|
||||
return selected;
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
gtk_selection_model_default_get_selection_in_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
GtkBitset *bitset;
|
||||
guint i;
|
||||
|
||||
bitset = gtk_bitset_new_empty ();
|
||||
|
||||
for (i = position; i < position + n_items; i++)
|
||||
{
|
||||
if (gtk_selection_model_is_selected (model, i))
|
||||
gtk_bitset_add (bitset, i);
|
||||
}
|
||||
|
||||
return bitset;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -87,13 +114,48 @@ gtk_selection_model_default_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
gboolean unselect_rest)
|
||||
{
|
||||
return FALSE;
|
||||
GtkBitset *selected;
|
||||
GtkBitset *mask;
|
||||
gboolean result;
|
||||
|
||||
selected = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add (selected, position);
|
||||
if (unselect_rest)
|
||||
{
|
||||
mask = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add_range (mask, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = gtk_bitset_ref (selected);
|
||||
}
|
||||
|
||||
result = gtk_selection_model_set_selection (model, selected, mask);
|
||||
|
||||
gtk_bitset_unref (selected);
|
||||
gtk_bitset_unref (mask);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_selection_model_default_unselect_item (GtkSelectionModel *model,
|
||||
guint position)
|
||||
{
|
||||
return FALSE;
|
||||
GtkBitset *selected;
|
||||
GtkBitset *mask;
|
||||
gboolean result;
|
||||
|
||||
selected = gtk_bitset_new_empty ();
|
||||
mask = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add (mask, position);
|
||||
|
||||
result = gtk_selection_model_set_selection (model, selected, mask);
|
||||
|
||||
gtk_bitset_unref (selected);
|
||||
gtk_bitset_unref (mask);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -102,7 +164,28 @@ gtk_selection_model_default_select_range (GtkSelectionModel *model,
|
||||
guint n_items,
|
||||
gboolean unselect_rest)
|
||||
{
|
||||
return FALSE;
|
||||
GtkBitset *selected;
|
||||
GtkBitset *mask;
|
||||
gboolean result;
|
||||
|
||||
selected = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add_range (selected, position, n_items);
|
||||
if (unselect_rest)
|
||||
{
|
||||
mask = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add_range (mask, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
|
||||
}
|
||||
else
|
||||
{
|
||||
mask = gtk_bitset_ref (selected);
|
||||
}
|
||||
|
||||
result = gtk_selection_model_set_selection (model, selected, mask);
|
||||
|
||||
gtk_bitset_unref (selected);
|
||||
gtk_bitset_unref (mask);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -110,24 +193,20 @@ gtk_selection_model_default_unselect_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
GtkBitset *selected;
|
||||
GtkBitset *mask;
|
||||
gboolean result;
|
||||
|
||||
static gboolean
|
||||
gtk_selection_model_default_select_callback (GtkSelectionModel *model,
|
||||
gboolean unselect_rest,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
selected = gtk_bitset_new_empty ();
|
||||
mask = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add_range (mask, position, n_items);
|
||||
|
||||
static gboolean
|
||||
gtk_selection_model_default_unselect_callback (GtkSelectionModel *model,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
return FALSE;
|
||||
result = gtk_selection_model_set_selection (model, selected, mask);
|
||||
|
||||
gtk_bitset_unref (selected);
|
||||
gtk_bitset_unref (mask);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
@ -142,40 +221,26 @@ gtk_selection_model_default_unselect_all (GtkSelectionModel *model)
|
||||
return gtk_selection_model_unselect_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_selection_model_default_query_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint *start_range,
|
||||
guint *n_items,
|
||||
gboolean *selected)
|
||||
static gboolean
|
||||
gtk_selection_model_default_set_selection (GtkSelectionModel *model,
|
||||
GtkBitset *selected,
|
||||
GtkBitset *mask)
|
||||
{
|
||||
*start_range = position;
|
||||
|
||||
if (position >= g_list_model_get_n_items (G_LIST_MODEL (model)))
|
||||
{
|
||||
*n_items = 0;
|
||||
*selected = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*n_items = 1;
|
||||
*selected = gtk_selection_model_is_selected (model, position);
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_selection_model_default_init (GtkSelectionModelInterface *iface)
|
||||
{
|
||||
iface->is_selected = gtk_selection_model_default_is_selected;
|
||||
iface->get_selection_in_range = gtk_selection_model_default_get_selection_in_range;
|
||||
iface->select_item = gtk_selection_model_default_select_item;
|
||||
iface->unselect_item = gtk_selection_model_default_unselect_item;
|
||||
iface->select_range = gtk_selection_model_default_select_range;
|
||||
iface->unselect_range = gtk_selection_model_default_unselect_range;
|
||||
iface->select_all = gtk_selection_model_default_select_all;
|
||||
iface->unselect_all = gtk_selection_model_default_unselect_all;
|
||||
iface->select_callback = gtk_selection_model_default_select_callback;
|
||||
iface->unselect_callback = gtk_selection_model_default_unselect_callback;
|
||||
iface->query_range = gtk_selection_model_default_query_range;
|
||||
iface->set_selection = gtk_selection_model_default_set_selection;
|
||||
|
||||
/**
|
||||
* GtkSelectionModel::selection-changed
|
||||
@ -225,6 +290,62 @@ gtk_selection_model_is_selected (GtkSelectionModel *model,
|
||||
return iface->is_selected (model, position);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_selection_model_get_selection:
|
||||
* @model: a #GtkSelectionModel
|
||||
*
|
||||
* Gets the set containing all currently selected items in the model.
|
||||
*
|
||||
* This function may be slow, so if you are only interested in single item,
|
||||
* consider using gtk_selection_model_is_selected() or if you are only
|
||||
* interested in a few consider gtk_selection_model_get_selection_in_range().
|
||||
*
|
||||
* Returns: (transfer full): a #GtkBitset containing all the values currently
|
||||
* selected in @model. If no items are selected, the bitset is empty.
|
||||
* The bitset must not be modified.
|
||||
**/
|
||||
GtkBitset *
|
||||
gtk_selection_model_get_selection (GtkSelectionModel *model)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), gtk_bitset_new_empty ());
|
||||
|
||||
return gtk_selection_model_get_selection_in_range (model, 0, g_list_model_get_n_items (G_LIST_MODEL (model)));
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_selection_model_get_selection_in_range:
|
||||
* @model: a #GtkSelectionModel
|
||||
* @position: start of the queired range
|
||||
* @n_items: number of items in the queried range
|
||||
*
|
||||
* Gets a set containing a set where the values in the range [position,
|
||||
* position + n_items) match the selected state of the items in that range.
|
||||
* All values outside that range are undefined.
|
||||
*
|
||||
* This function is an optimization for gtk_selection_model_get_selection() when
|
||||
* you are only interested in part of the model's selected state. A common use
|
||||
* case is in response to the :selection-changed signal.
|
||||
*
|
||||
* Returns: A #GtkBitset that matches the selection state for the given state
|
||||
* with all other values being undefined.
|
||||
* The bitset must not be modified.
|
||||
**/
|
||||
GtkBitset *
|
||||
gtk_selection_model_get_selection_in_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), gtk_bitset_new_empty ());
|
||||
|
||||
if (n_items == 0)
|
||||
return gtk_bitset_new_empty ();
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->get_selection_in_range (model, position, n_items);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_selection_model_select_item:
|
||||
* @model: a #GtkSelectionModel
|
||||
@ -232,6 +353,9 @@ gtk_selection_model_is_selected (GtkSelectionModel *model,
|
||||
* @unselect_rest: whether previously selected items should be unselected
|
||||
*
|
||||
* Requests to select an item in the model.
|
||||
*
|
||||
* Returns: %TRUE if this action was supported and no fallback should be
|
||||
* tried. This does not mean the item was selected.
|
||||
*/
|
||||
gboolean
|
||||
gtk_selection_model_select_item (GtkSelectionModel *model,
|
||||
@ -240,7 +364,7 @@ gtk_selection_model_select_item (GtkSelectionModel *model,
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->select_item (model, position, unselect_rest);
|
||||
@ -252,6 +376,9 @@ gtk_selection_model_select_item (GtkSelectionModel *model,
|
||||
* @position: the position of the item to unselect
|
||||
*
|
||||
* Requests to unselect an item in the model.
|
||||
*
|
||||
* Returns: %TRUE if this action was supported and no fallback should be
|
||||
* tried. This does not mean the item was unselected.
|
||||
*/
|
||||
gboolean
|
||||
gtk_selection_model_unselect_item (GtkSelectionModel *model,
|
||||
@ -259,7 +386,7 @@ gtk_selection_model_unselect_item (GtkSelectionModel *model,
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->unselect_item (model, position);
|
||||
@ -273,6 +400,9 @@ gtk_selection_model_unselect_item (GtkSelectionModel *model,
|
||||
* @unselect_rest: whether previously selected items should be unselected
|
||||
*
|
||||
* Requests to select a range of items in the model.
|
||||
*
|
||||
* Returns: %TRUE if this action was supported and no fallback should be
|
||||
* tried. This does not mean the range was selected.
|
||||
*/
|
||||
gboolean
|
||||
gtk_selection_model_select_range (GtkSelectionModel *model,
|
||||
@ -282,7 +412,7 @@ gtk_selection_model_select_range (GtkSelectionModel *model,
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->select_range (model, position, n_items, unselect_rest);
|
||||
@ -295,6 +425,9 @@ gtk_selection_model_select_range (GtkSelectionModel *model,
|
||||
* @n_items: the number of items to unselect
|
||||
*
|
||||
* Requests to unselect a range of items in the model.
|
||||
*
|
||||
* Returns: %TRUE if this action was supported and no fallback should be
|
||||
* tried. This does not mean the range was unselected.
|
||||
*/
|
||||
gboolean
|
||||
gtk_selection_model_unselect_range (GtkSelectionModel *model,
|
||||
@ -303,7 +436,7 @@ gtk_selection_model_unselect_range (GtkSelectionModel *model,
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->unselect_range (model, position, n_items);
|
||||
@ -314,13 +447,16 @@ gtk_selection_model_unselect_range (GtkSelectionModel *model,
|
||||
* @model: a #GtkSelectionModel
|
||||
*
|
||||
* Requests to select all items in the model.
|
||||
*
|
||||
* Returns: %TRUE if this action was supported and no fallback should be
|
||||
* tried. This does not mean that all items are now selected.
|
||||
*/
|
||||
gboolean
|
||||
gtk_selection_model_select_all (GtkSelectionModel *model)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->select_all (model);
|
||||
@ -331,96 +467,74 @@ gtk_selection_model_select_all (GtkSelectionModel *model)
|
||||
* @model: a #GtkSelectionModel
|
||||
*
|
||||
* Requests to unselect all items in the model.
|
||||
*
|
||||
* Returns: %TRUE if this action was supported and no fallback should be
|
||||
* tried. This does not mean that all items are now unselected.
|
||||
*/
|
||||
gboolean
|
||||
gtk_selection_model_unselect_all (GtkSelectionModel *model)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), 0);
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->unselect_all (model);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_selection_model_select_callback:
|
||||
* gtk_selection_model_set_selection:
|
||||
* @model: a #GtkSelectionModel
|
||||
* @unselect_rest: whether previously selected items should be unselected
|
||||
* @callback: (scope call): a #GtkSelectionCallback to determine items to select
|
||||
* @data: data to pass to @callback
|
||||
* @selected: bitmask specifying if items should be selected or
|
||||
* unselected
|
||||
* @mask: bitmask specifying which items should be updated
|
||||
*
|
||||
* Requests to select all items for which @callback returns
|
||||
* @selected as TRUE.
|
||||
*/
|
||||
* This is the most advanced selection updating method that allows
|
||||
* the most fine-grained control over selection changes.
|
||||
* If you can, you should try the simpler versions, as implementations
|
||||
* are more likely to implement support for those.
|
||||
*
|
||||
* Requests that the selection state of all positions set in @mask be
|
||||
* updated to the respecitve value in the @selected bitmask.
|
||||
*
|
||||
* In pseudocode, it would look something like this:
|
||||
*
|
||||
* |[<!-- language="C" -->
|
||||
* for (i = 0; i < n_items; i++)
|
||||
* {
|
||||
* // don't change values not in the mask
|
||||
* if (!gtk_bitset_contains (mask, i))
|
||||
* continue;
|
||||
*
|
||||
* if (gtk_bitset_contains (selected, i))
|
||||
* select_item (i);
|
||||
* else
|
||||
* unselect_item (i);
|
||||
* }
|
||||
*
|
||||
* gtk_selection_model_selection_changed (model, first_changed_item, n_changed_items);
|
||||
* ]|
|
||||
*
|
||||
* @mask and @selected must not be modified. They may refer to the same bitset,
|
||||
* which would mean that every item in the set should be selected.
|
||||
*
|
||||
* Returns: %TRUE if this action was supported and no fallback should be
|
||||
* tried. This does not mean that all items were updated according
|
||||
* to the inputs.
|
||||
**/
|
||||
gboolean
|
||||
gtk_selection_model_select_callback (GtkSelectionModel *model,
|
||||
gboolean unselect_rest,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
|
||||
|
||||
return GTK_SELECTION_MODEL_GET_IFACE (model)->select_callback (model, unselect_rest, callback, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_selection_model_unselect_callback:
|
||||
* @model: a #GtkSelectionModel
|
||||
* @callback: (scope call): 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,
|
||||
gpointer data)
|
||||
{
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
|
||||
|
||||
return GTK_SELECTION_MODEL_GET_IFACE (model)->unselect_callback (model, callback, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* gtk_selection_model_query_range:
|
||||
* @model: a #GtkSelectionModel
|
||||
* @position: the position inside the range
|
||||
* @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
|
||||
*
|
||||
* This function allows to query the selection status of multiple elements at once.
|
||||
* It is passed a position and returns a range of elements of uniform selection status.
|
||||
*
|
||||
* If @position is greater than the number of items in @model, @n_items is set to 0.
|
||||
* Otherwise the returned range is guaranteed to include the passed-in position, so
|
||||
* @n_items will be >= 1.
|
||||
*
|
||||
* Positions directly adjacent to the returned range may have the same selection
|
||||
* status as the returned range.
|
||||
*
|
||||
* This is an optimization function to make iterating over a model faster when few
|
||||
* items are selected. However, it is valid behavior for implementations to use a
|
||||
* naive implementation that only ever returns a single element.
|
||||
*/
|
||||
void
|
||||
gtk_selection_model_query_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint *start_range,
|
||||
guint *n_items,
|
||||
gboolean *selected)
|
||||
gtk_selection_model_set_selection (GtkSelectionModel *model,
|
||||
GtkBitset *selected,
|
||||
GtkBitset *mask)
|
||||
{
|
||||
GtkSelectionModelInterface *iface;
|
||||
|
||||
g_return_if_fail (GTK_IS_SELECTION_MODEL (model));
|
||||
g_return_if_fail (start_range != NULL);
|
||||
g_return_if_fail (n_items != NULL);
|
||||
g_return_if_fail (selected != NULL);
|
||||
g_return_val_if_fail (GTK_IS_SELECTION_MODEL (model), FALSE);
|
||||
g_return_val_if_fail (selected != NULL, FALSE);
|
||||
g_return_val_if_fail (mask != NULL, FALSE);
|
||||
|
||||
iface = GTK_SELECTION_MODEL_GET_IFACE (model);
|
||||
return iface->query_range (model, position, start_range, n_items, selected);
|
||||
return iface->set_selection (model, selected, mask);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -24,7 +24,7 @@
|
||||
#error "Only <gtk/gtk.h> can be included directly."
|
||||
#endif
|
||||
|
||||
#include <gdk/gdk.h>
|
||||
#include <gtk/gtktypes.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
@ -33,39 +33,12 @@ 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,
|
||||
gboolean *selected,
|
||||
gpointer data);
|
||||
|
||||
|
||||
/**
|
||||
* GtkSelectionModelInterface:
|
||||
* @is_selected: Return if the item at the given position is selected.
|
||||
* @get_selection_in_range: Return a bitset with all currently selected
|
||||
* items in the given range. By default, this function will call
|
||||
* #GtkSelectionModel::is_selected() on all items in the given range.
|
||||
* @select_item: Select the item in the given position. If the operation
|
||||
* is known to fail, return %FALSE.
|
||||
* @unselect_item: Unselect the item in the given position. If the
|
||||
@ -79,12 +52,22 @@ typedef void (* GtkSelectionCallback) (guint position,
|
||||
* unsupported or known to fail for all items, return %FALSE.
|
||||
* @unselect_all: Unselect all items in the model. If the operation is
|
||||
* unsupported or known to fail for all items, return %FALSE.
|
||||
* @set_selection: Set selection state of all items in mask to selected.
|
||||
* See gtk_selection_model_set_selection() for a detailed explanation
|
||||
* of this function.
|
||||
*
|
||||
* The list of virtual functions for the #GtkSelectionModel interface.
|
||||
* All getter functions are mandatory to implement, but the model does
|
||||
* not need to implement any functions to support selecting or unselecting
|
||||
* items. Of course, if the model does not do that, it means that users
|
||||
* cannot select or unselect items in a list widgets using the model.
|
||||
* No function must be implemented, but unless #GtkSelectionModel::is_selected()
|
||||
* is implemented, it will not be possible to select items in the set.
|
||||
*
|
||||
* The model does not need to implement any functions to support either
|
||||
* selecting or unselecting items. Of course, if the model does not do that,
|
||||
* it means that users cannot select or unselect items in a list widget
|
||||
* using the model.
|
||||
*
|
||||
* All selection functions fall back to #GtkSelectionModel::set_selection()
|
||||
* so it is sufficient to implement just that function for full selection
|
||||
* support.
|
||||
*/
|
||||
struct _GtkSelectionModelInterface
|
||||
{
|
||||
@ -94,6 +77,9 @@ struct _GtkSelectionModelInterface
|
||||
/*< public >*/
|
||||
gboolean (* is_selected) (GtkSelectionModel *model,
|
||||
guint position);
|
||||
GtkBitset * (* get_selection_in_range) (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items);
|
||||
|
||||
gboolean (* select_item) (GtkSelectionModel *model,
|
||||
guint position,
|
||||
@ -109,23 +95,21 @@ struct _GtkSelectionModelInterface
|
||||
guint n_items);
|
||||
gboolean (* select_all) (GtkSelectionModel *model);
|
||||
gboolean (* unselect_all) (GtkSelectionModel *model);
|
||||
gboolean (* select_callback) (GtkSelectionModel *model,
|
||||
gboolean unselect_rest,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data);
|
||||
gboolean (* unselect_callback) (GtkSelectionModel *model,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data);
|
||||
void (* query_range) (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint *start_range,
|
||||
guint *n_items,
|
||||
gboolean *selected);
|
||||
gboolean (* set_selection) (GtkSelectionModel *model,
|
||||
GtkBitset *selected,
|
||||
GtkBitset *mask);
|
||||
};
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_is_selected (GtkSelectionModel *model,
|
||||
guint position);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBitset * gtk_selection_model_get_selection (GtkSelectionModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
GtkBitset * gtk_selection_model_get_selection_in_range
|
||||
(GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_select_item (GtkSelectionModel *model,
|
||||
@ -147,23 +131,10 @@ GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_select_all (GtkSelectionModel *model);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_unselect_all (GtkSelectionModel *model);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_select_callback (GtkSelectionModel *model,
|
||||
gboolean unselect_rest,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data);
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
gboolean gtk_selection_model_unselect_callback (GtkSelectionModel *model,
|
||||
GtkSelectionCallback callback,
|
||||
gpointer data);
|
||||
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
void gtk_selection_model_query_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint *start_range,
|
||||
guint *n_items,
|
||||
gboolean *selected);
|
||||
gboolean gtk_selection_model_set_selection (GtkSelectionModel *model,
|
||||
GtkBitset *selected,
|
||||
GtkBitset *mask);
|
||||
|
||||
/* for implementations only */
|
||||
GDK_AVAILABLE_IN_ALL
|
||||
|
383
gtk/gtkset.c
383
gtk/gtkset.c
@ -1,383 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2019 Red Hat, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Authors: Matthias Clasen <mclasen@redhat.com>
|
||||
*/
|
||||
|
||||
#include "gtkset.h"
|
||||
|
||||
/* Store a set of unsigned integers as a sorted array of ranges.
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
guint first;
|
||||
guint n_items;
|
||||
} Range;
|
||||
|
||||
struct _GtkSet
|
||||
{
|
||||
GArray *ranges;
|
||||
};
|
||||
|
||||
typedef struct
|
||||
{
|
||||
GtkSet *set;
|
||||
Range *current;
|
||||
int idx;
|
||||
guint pos;
|
||||
} GtkRealSetIter;
|
||||
|
||||
GtkSet *
|
||||
gtk_set_new (void)
|
||||
{
|
||||
GtkSet *set;
|
||||
|
||||
set = g_new (GtkSet, 1);
|
||||
set->ranges = g_array_new (FALSE, FALSE, sizeof (Range));
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
GtkSet *
|
||||
gtk_set_copy (GtkSet *set)
|
||||
{
|
||||
GtkSet *copy;
|
||||
|
||||
copy = g_new (GtkSet, 1);
|
||||
copy->ranges = g_array_copy (set->ranges);
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_free (GtkSet *set)
|
||||
{
|
||||
g_array_free (set->ranges, TRUE);
|
||||
g_free (set);
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_set_contains (GtkSet *set,
|
||||
guint item)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
|
||||
if (item < r->first)
|
||||
return FALSE;
|
||||
|
||||
if (item < r->first + r->n_items)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_remove_all (GtkSet *set)
|
||||
{
|
||||
g_array_set_size (set->ranges, 0);
|
||||
}
|
||||
|
||||
static int
|
||||
range_compare (Range *r, Range *s)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (r->first + r->n_items < s->first)
|
||||
ret = -1;
|
||||
else if (s->first + s->n_items < r->first)
|
||||
ret = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_add_range (GtkSet *set,
|
||||
guint first_item,
|
||||
guint n_items)
|
||||
{
|
||||
int i;
|
||||
Range s;
|
||||
int first = -1;
|
||||
int last = -1;
|
||||
|
||||
s.first = first_item;
|
||||
s.n_items = n_items;
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
int cmp = range_compare (&s, r);
|
||||
|
||||
if (cmp < 0)
|
||||
break;
|
||||
|
||||
if (cmp == 0)
|
||||
{
|
||||
if (first < 0)
|
||||
first = i;
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (first > -1)
|
||||
{
|
||||
Range *r;
|
||||
guint start;
|
||||
guint end;
|
||||
|
||||
r = &g_array_index (set->ranges, Range, first);
|
||||
start = MIN (s.first, r->first);
|
||||
|
||||
r = &g_array_index (set->ranges, Range, last);
|
||||
end = MAX (s.first + s.n_items - 1, r->first + r->n_items - 1);
|
||||
|
||||
s.first = start;
|
||||
s.n_items = end - start + 1;
|
||||
|
||||
g_array_remove_range (set->ranges, first, last - first + 1);
|
||||
g_array_insert_val (set->ranges, first, s);
|
||||
}
|
||||
else
|
||||
g_array_insert_val (set->ranges, i, s);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_remove_range (GtkSet *set,
|
||||
guint first_item,
|
||||
guint n_items)
|
||||
{
|
||||
Range s;
|
||||
int i;
|
||||
int first = -1;
|
||||
int last = -1;
|
||||
|
||||
s.first = first_item;
|
||||
s.n_items = n_items;
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
int cmp = range_compare (&s, r);
|
||||
|
||||
if (cmp < 0)
|
||||
break;
|
||||
|
||||
if (cmp == 0)
|
||||
{
|
||||
if (first < 0)
|
||||
first = i;
|
||||
last = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (first > -1)
|
||||
{
|
||||
Range *r;
|
||||
Range a[2];
|
||||
int k = 0;
|
||||
|
||||
r = &g_array_index (set->ranges, Range, first);
|
||||
if (r->first < s.first)
|
||||
{
|
||||
a[k].first = r->first;
|
||||
a[k].n_items = s.first - r->first;
|
||||
k++;
|
||||
}
|
||||
r = &g_array_index (set->ranges, Range, last);
|
||||
if (r->first + r->n_items > s.first + s.n_items)
|
||||
{
|
||||
a[k].first = s.first + s.n_items;
|
||||
a[k].n_items = r->first + r->n_items - a[k].first;
|
||||
k++;
|
||||
}
|
||||
g_array_remove_range (set->ranges, first, last - first + 1);
|
||||
if (k > 0)
|
||||
g_array_insert_vals (set->ranges, first, a, k);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_find_range (GtkSet *set,
|
||||
guint position,
|
||||
guint upper_bound,
|
||||
guint *start,
|
||||
guint *n_items,
|
||||
gboolean *contained)
|
||||
{
|
||||
int i;
|
||||
int last = 0;
|
||||
|
||||
if (position >= upper_bound)
|
||||
{
|
||||
*start = 0;
|
||||
*n_items = 0;
|
||||
*contained = FALSE;
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
|
||||
if (position < r->first)
|
||||
{
|
||||
*start = last;
|
||||
*n_items = r->first - last;
|
||||
*contained = FALSE;
|
||||
|
||||
return;
|
||||
}
|
||||
else if (r->first <= position && position < r->first + r->n_items)
|
||||
{
|
||||
*start = r->first;
|
||||
*n_items = r->n_items;
|
||||
*contained = TRUE;
|
||||
|
||||
return;
|
||||
}
|
||||
else
|
||||
last = r->first + r->n_items;
|
||||
}
|
||||
|
||||
*start = last;
|
||||
*n_items = upper_bound - last;
|
||||
*contained = FALSE;
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_add_item (GtkSet *set,
|
||||
guint item)
|
||||
{
|
||||
gtk_set_add_range (set, item, 1);
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_remove_item (GtkSet *set,
|
||||
guint item)
|
||||
{
|
||||
gtk_set_remove_range (set, item, 1);
|
||||
}
|
||||
|
||||
/* This is peculiar operation: Replace every number n >= first by n + shift
|
||||
* This is only supported for negative shifts if the shifting does not cause
|
||||
* any ranges to overlap.
|
||||
*/
|
||||
void
|
||||
gtk_set_shift (GtkSet *set,
|
||||
guint first,
|
||||
int shift)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < set->ranges->len; i++)
|
||||
{
|
||||
Range *r = &g_array_index (set->ranges, Range, i);
|
||||
if (r->first >= first)
|
||||
r->first += shift;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
gtk_set_iter_init (GtkSetIter *iter,
|
||||
GtkSet *set)
|
||||
{
|
||||
GtkRealSetIter *ri = (GtkRealSetIter *)iter;
|
||||
|
||||
ri->set = set;
|
||||
ri->idx = -1;
|
||||
ri->current = 0;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_set_iter_next (GtkSetIter *iter,
|
||||
guint *item)
|
||||
{
|
||||
GtkRealSetIter *ri = (GtkRealSetIter *)iter;
|
||||
|
||||
if (ri->idx == -1)
|
||||
{
|
||||
next_range:
|
||||
ri->idx++;
|
||||
|
||||
if (ri->idx == ri->set->ranges->len)
|
||||
return FALSE;
|
||||
|
||||
ri->current = &g_array_index (ri->set->ranges, Range, ri->idx);
|
||||
ri->pos = ri->current->first;
|
||||
}
|
||||
else
|
||||
{
|
||||
ri->pos++;
|
||||
if (ri->pos == ri->current->first + ri->current->n_items)
|
||||
goto next_range;
|
||||
}
|
||||
|
||||
*item = ri->pos;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
gtk_set_is_empty (GtkSet *set)
|
||||
{
|
||||
return set->ranges->len == 0;
|
||||
}
|
||||
|
||||
guint
|
||||
gtk_set_get_min (GtkSet *set)
|
||||
{
|
||||
Range *r;
|
||||
|
||||
if (gtk_set_is_empty (set))
|
||||
return 0;
|
||||
|
||||
r = &g_array_index (set->ranges, Range, 0);
|
||||
|
||||
return r->first;
|
||||
}
|
||||
|
||||
guint
|
||||
gtk_set_get_max (GtkSet *set)
|
||||
{
|
||||
Range *r;
|
||||
|
||||
if (gtk_set_is_empty (set))
|
||||
return 0;
|
||||
|
||||
r = &g_array_index (set->ranges, Range, set->ranges->len - 1);
|
||||
|
||||
return r->first + r->n_items - 1;
|
||||
}
|
||||
|
||||
#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
|
74
gtk/gtkset.h
74
gtk/gtkset.h
@ -1,74 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
|
||||
gboolean gtk_set_is_empty (GtkSet *set);
|
||||
guint gtk_set_get_min (GtkSet *set);
|
||||
guint gtk_set_get_max (GtkSet *set);
|
||||
|
||||
#endif /* __GTK_SET_H__ */
|
@ -21,6 +21,7 @@
|
||||
|
||||
#include "gtksingleselection.h"
|
||||
|
||||
#include "gtkbitset.h"
|
||||
#include "gtkintl.h"
|
||||
#include "gtkselectionmodel.h"
|
||||
|
||||
@ -110,6 +111,21 @@ gtk_single_selection_is_selected (GtkSelectionModel *model,
|
||||
return self->selected == position;
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
gtk_single_selection_get_selection_in_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint n_items)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (model);
|
||||
GtkBitset *result;
|
||||
|
||||
result = gtk_bitset_new_empty ();
|
||||
if (self->selected != GTK_INVALID_LIST_POSITION)
|
||||
gtk_bitset_add (result, self->selected);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
gtk_single_selection_select_item (GtkSelectionModel *model,
|
||||
guint position,
|
||||
@ -138,57 +154,13 @@ gtk_single_selection_unselect_item (GtkSelectionModel *model,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_query_range (GtkSelectionModel *model,
|
||||
guint position,
|
||||
guint *start_range,
|
||||
guint *n_range,
|
||||
gboolean *selected)
|
||||
{
|
||||
GtkSingleSelection *self = GTK_SINGLE_SELECTION (model);
|
||||
guint n_items;
|
||||
|
||||
n_items = g_list_model_get_n_items (self->model);
|
||||
|
||||
if (position >= n_items)
|
||||
{
|
||||
*start_range = position;
|
||||
*n_range = 0;
|
||||
*selected = FALSE;
|
||||
}
|
||||
else if (self->selected == GTK_INVALID_LIST_POSITION)
|
||||
{
|
||||
*start_range = 0;
|
||||
*n_range = n_items;
|
||||
*selected = FALSE;
|
||||
}
|
||||
else if (position < self->selected)
|
||||
{
|
||||
*start_range = 0;
|
||||
*n_range = self->selected;
|
||||
*selected = FALSE;
|
||||
}
|
||||
else if (position > self->selected)
|
||||
{
|
||||
*start_range = self->selected + 1;
|
||||
*n_range = n_items - *start_range;
|
||||
*selected = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
*start_range = self->selected;
|
||||
*n_range = 1;
|
||||
*selected = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_single_selection_selection_model_init (GtkSelectionModelInterface *iface)
|
||||
{
|
||||
iface->is_selected = gtk_single_selection_is_selected;
|
||||
iface->get_selection_in_range = gtk_single_selection_get_selection_in_range;
|
||||
iface->select_item = gtk_single_selection_select_item;
|
||||
iface->unselect_item = gtk_single_selection_unselect_item;
|
||||
iface->query_range = gtk_single_selection_query_range;
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_EXTENDED (GtkSingleSelection, gtk_single_selection, G_TYPE_OBJECT, 0,
|
||||
|
@ -34,6 +34,7 @@
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef struct _GtkAdjustment GtkAdjustment;
|
||||
typedef struct _GtkBitset GtkBitset;
|
||||
typedef struct _GtkBuilder GtkBuilder;
|
||||
typedef struct _GtkBuilderScope GtkBuilderScope;
|
||||
typedef struct _GtkClipboard GtkClipboard;
|
||||
|
@ -134,7 +134,6 @@ gtk_private_sources = files([
|
||||
'gtkscaler.c',
|
||||
'gtksearchengine.c',
|
||||
'gtksearchenginemodel.c',
|
||||
'gtkset.c',
|
||||
'gtksizerequestcache.c',
|
||||
'gtkstyleanimation.c',
|
||||
'gtkstylecascade.c',
|
||||
@ -164,6 +163,7 @@ gtk_public_sources = files([
|
||||
'gtkaspectframe.c',
|
||||
'gtkassistant.c',
|
||||
'gtkbinlayout.c',
|
||||
'gtkbitset.c',
|
||||
'gtkbookmarklist.c',
|
||||
'gtkborder.c',
|
||||
'gtkboxlayout.c',
|
||||
@ -329,7 +329,6 @@ gtk_public_sources = files([
|
||||
'gtkprintsettings.c',
|
||||
'gtkprogressbar.c',
|
||||
'gtkpropertylookuplistmodel.c',
|
||||
'gtkpropertyselection.c',
|
||||
'gtkradiobutton.c',
|
||||
'gtkrange.c',
|
||||
'gtktreerbtree.c',
|
||||
@ -451,6 +450,7 @@ gtk_public_headers = files([
|
||||
'gtkaspectframe.h',
|
||||
'gtkassistant.h',
|
||||
'gtkbinlayout.h',
|
||||
'gtkbitset.h',
|
||||
'gtkbookmarklist.h',
|
||||
'gtkborder.h',
|
||||
'gtkbox.h',
|
||||
@ -606,7 +606,6 @@ gtk_public_headers = files([
|
||||
'gtkprintoperationpreview.h',
|
||||
'gtkprintsettings.h',
|
||||
'gtkprogressbar.h',
|
||||
'gtkpropertyselection.h',
|
||||
'gtkradiobutton.h',
|
||||
'gtkrange.h',
|
||||
'gtkrecentmanager.h',
|
||||
|
11453
gtk/roaring.c
Normal file
11453
gtk/roaring.c
Normal file
File diff suppressed because it is too large
Load Diff
7249
gtk/roaring.h
Normal file
7249
gtk/roaring.h
Normal file
File diff suppressed because it is too large
Load Diff
@ -255,7 +255,6 @@ gtk/gtkprintoperation-win32.c
|
||||
gtk/gtkprintunixdialog.c
|
||||
gtk/gtkprogressbar.c
|
||||
gtk/gtkpropertylookuplistmodel.c
|
||||
gtk/gtkpropertyselection.c
|
||||
gtk/gtkradiobutton.c
|
||||
gtk/gtkrange.c
|
||||
gtk/gtkrecentmanager.c
|
||||
|
@ -257,7 +257,6 @@ gtk/gtkprintoperation-win32.c
|
||||
gtk/gtkprintunixdialog.c
|
||||
gtk/gtkprogressbar.c
|
||||
gtk/gtkpropertylookuplistmodel.c
|
||||
gtk/gtkpropertyselection.c
|
||||
gtk/gtkradiobutton.c
|
||||
gtk/gtkrange.c
|
||||
gtk/gtkrecentmanager.c
|
||||
|
369
testsuite/gtk/bitset.c
Normal file
369
testsuite/gtk/bitset.c
Normal file
@ -0,0 +1,369 @@
|
||||
/* GtkRBTree tests.
|
||||
*
|
||||
* Copyright (C) 2011, Red Hat, Inc.
|
||||
* Authors: Benjamin Otte <otte@gnome.org>
|
||||
*
|
||||
* 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>
|
||||
|
||||
#define LARGE_VALUE (1000 * 1000)
|
||||
|
||||
static GtkBitset *
|
||||
create_powers_of_10 (void)
|
||||
{
|
||||
GtkBitset *set;
|
||||
guint i;
|
||||
|
||||
set = gtk_bitset_new_empty ();
|
||||
for (i = 1; i <= LARGE_VALUE; i *= 10)
|
||||
gtk_bitset_add (set, i);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
create_powers_of_10_ranges (void)
|
||||
{
|
||||
GtkBitset *set;
|
||||
guint i, j;
|
||||
|
||||
set = gtk_bitset_new_empty ();
|
||||
for (i = 1, j = 0; i <= LARGE_VALUE; i *= 10, j++)
|
||||
gtk_bitset_add_range (set, i - j, 2 * j);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
create_large_range (void)
|
||||
{
|
||||
GtkBitset *set;
|
||||
|
||||
set = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add_range (set, 0, LARGE_VALUE);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
static GtkBitset *
|
||||
create_large_rectangle (void)
|
||||
{
|
||||
GtkBitset *set;
|
||||
|
||||
set = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add_rectangle (set, 0, 900, 900, 1000);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
static struct {
|
||||
GtkBitset * (* create) (void);
|
||||
guint n_elements;
|
||||
guint minimum;
|
||||
guint maximum;
|
||||
} bitsets[] =
|
||||
{
|
||||
{ gtk_bitset_new_empty, 0, G_MAXUINT, 0 },
|
||||
{ create_powers_of_10, 7, 1, LARGE_VALUE },
|
||||
{ create_powers_of_10_ranges, 42, 9, LARGE_VALUE + 5, },
|
||||
{ create_large_range, LARGE_VALUE, 0, LARGE_VALUE - 1 },
|
||||
{ create_large_rectangle, 900 * 900, 0, 899899 }
|
||||
};
|
||||
|
||||
/* UTILITIES */
|
||||
|
||||
/* TEST */
|
||||
|
||||
static void
|
||||
test_is_empty (void)
|
||||
{
|
||||
guint i;
|
||||
GtkBitset *set;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (bitsets); i++)
|
||||
{
|
||||
set = bitsets[i].create();
|
||||
|
||||
if (bitsets[i].n_elements == 0)
|
||||
g_assert_true (gtk_bitset_is_empty (set));
|
||||
else
|
||||
g_assert_false (gtk_bitset_is_empty (set));
|
||||
|
||||
gtk_bitset_unref (set);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_minimum (void)
|
||||
{
|
||||
guint i;
|
||||
GtkBitset *set;
|
||||
GtkBitsetIter iter;
|
||||
gboolean success;
|
||||
guint value;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (bitsets); i++)
|
||||
{
|
||||
set = bitsets[i].create();
|
||||
|
||||
g_assert_cmpint (gtk_bitset_get_minimum (set), ==, bitsets[i].minimum);
|
||||
|
||||
success = gtk_bitset_iter_init_first (&iter, set, &value);
|
||||
if (success)
|
||||
{
|
||||
g_assert_false (bitsets[i].n_elements == 0);
|
||||
g_assert_cmpint (value, ==, bitsets[i].minimum);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_true (bitsets[i].n_elements == 0);
|
||||
g_assert_cmpint (value, ==, 0);
|
||||
}
|
||||
g_assert_cmpint (gtk_bitset_iter_is_valid (&iter), ==, success);
|
||||
g_assert_cmpint (gtk_bitset_iter_get_value (&iter), ==, value);
|
||||
|
||||
gtk_bitset_unref (set);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_maximum (void)
|
||||
{
|
||||
guint i;
|
||||
GtkBitset *set;
|
||||
GtkBitsetIter iter;
|
||||
gboolean success;
|
||||
guint value;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (bitsets); i++)
|
||||
{
|
||||
set = bitsets[i].create();
|
||||
|
||||
g_assert_cmpint (gtk_bitset_get_maximum (set), ==, bitsets[i].maximum);
|
||||
|
||||
success = gtk_bitset_iter_init_last (&iter, set, &value);
|
||||
if (success)
|
||||
{
|
||||
g_assert_false (bitsets[i].n_elements == 0);
|
||||
g_assert_cmpint (value, ==, bitsets[i].maximum);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_assert_true (bitsets[i].n_elements == 0);
|
||||
g_assert_cmpint (value, ==, 0);
|
||||
}
|
||||
g_assert_cmpint (gtk_bitset_iter_is_valid (&iter), ==, success);
|
||||
g_assert_cmpint (gtk_bitset_iter_get_value (&iter), ==, value);
|
||||
|
||||
gtk_bitset_unref (set);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_equals (void)
|
||||
{
|
||||
guint i, j;
|
||||
GtkBitset *iset, *jset;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (bitsets); i++)
|
||||
{
|
||||
iset = bitsets[i].create();
|
||||
|
||||
g_assert_true (gtk_bitset_equals (iset, iset));
|
||||
|
||||
for (j = 0; j < G_N_ELEMENTS (bitsets); j++)
|
||||
{
|
||||
jset = bitsets[j].create();
|
||||
|
||||
if (i == j)
|
||||
g_assert_true (gtk_bitset_equals (iset, jset));
|
||||
else
|
||||
g_assert_false (gtk_bitset_equals (iset, jset));
|
||||
|
||||
gtk_bitset_unref (jset);
|
||||
}
|
||||
|
||||
gtk_bitset_unref (iset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_union (void)
|
||||
{
|
||||
guint i, j, k, min, max;
|
||||
GtkBitset *iset, *jset, *testset;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (bitsets); i++)
|
||||
{
|
||||
iset = bitsets[i].create();
|
||||
|
||||
g_assert_true (gtk_bitset_equals (iset, iset));
|
||||
|
||||
for (j = 0; j < G_N_ELEMENTS (bitsets); j++)
|
||||
{
|
||||
jset = bitsets[j].create();
|
||||
|
||||
testset = gtk_bitset_copy (iset);
|
||||
gtk_bitset_union (testset, jset);
|
||||
|
||||
min = MIN (gtk_bitset_get_minimum (iset), gtk_bitset_get_minimum (jset));
|
||||
g_assert_cmpint (min, <=, gtk_bitset_get_minimum (testset));
|
||||
max = MAX (gtk_bitset_get_maximum (iset), gtk_bitset_get_maximum (jset));
|
||||
g_assert_cmpint (max, >=, gtk_bitset_get_maximum (testset));
|
||||
|
||||
for (k = min; k <= max; k++)
|
||||
{
|
||||
g_assert_cmpint (gtk_bitset_contains (iset, k) || gtk_bitset_contains (jset, k), ==, gtk_bitset_contains (testset, k));
|
||||
}
|
||||
|
||||
gtk_bitset_unref (testset);
|
||||
gtk_bitset_unref (jset);
|
||||
}
|
||||
|
||||
gtk_bitset_unref (iset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_intersect (void)
|
||||
{
|
||||
guint i, j, k, min, max;
|
||||
GtkBitset *iset, *jset, *testset;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (bitsets); i++)
|
||||
{
|
||||
iset = bitsets[i].create();
|
||||
|
||||
g_assert_true (gtk_bitset_equals (iset, iset));
|
||||
|
||||
for (j = 0; j < G_N_ELEMENTS (bitsets); j++)
|
||||
{
|
||||
jset = bitsets[j].create();
|
||||
|
||||
testset = gtk_bitset_copy (iset);
|
||||
gtk_bitset_intersect (testset, jset);
|
||||
|
||||
min = MIN (gtk_bitset_get_minimum (iset), gtk_bitset_get_minimum (jset));
|
||||
g_assert_cmpint (min, <=, gtk_bitset_get_minimum (testset));
|
||||
max = MAX (gtk_bitset_get_maximum (iset), gtk_bitset_get_maximum (jset));
|
||||
g_assert_cmpint (max, >=, gtk_bitset_get_maximum (testset));
|
||||
|
||||
for (k = min; k <= max; k++)
|
||||
{
|
||||
g_assert_cmpint (gtk_bitset_contains (iset, k) && gtk_bitset_contains (jset, k), ==, gtk_bitset_contains (testset, k));
|
||||
}
|
||||
|
||||
gtk_bitset_unref (testset);
|
||||
gtk_bitset_unref (jset);
|
||||
}
|
||||
|
||||
gtk_bitset_unref (iset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_difference (void)
|
||||
{
|
||||
guint i, j, k, min, max;
|
||||
GtkBitset *iset, *jset, *testset;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (bitsets); i++)
|
||||
{
|
||||
iset = bitsets[i].create();
|
||||
|
||||
g_assert_true (gtk_bitset_equals (iset, iset));
|
||||
|
||||
for (j = 0; j < G_N_ELEMENTS (bitsets); j++)
|
||||
{
|
||||
jset = bitsets[j].create();
|
||||
|
||||
testset = gtk_bitset_copy (iset);
|
||||
gtk_bitset_difference (testset, jset);
|
||||
|
||||
min = MIN (gtk_bitset_get_minimum (iset), gtk_bitset_get_minimum (jset));
|
||||
g_assert_cmpint (min, <=, gtk_bitset_get_minimum (testset));
|
||||
max = MAX (gtk_bitset_get_maximum (iset), gtk_bitset_get_maximum (jset));
|
||||
g_assert_cmpint (max, >=, gtk_bitset_get_maximum (testset));
|
||||
|
||||
for (k = min; k <= max; k++)
|
||||
{
|
||||
g_assert_cmpint (gtk_bitset_contains (iset, k) ^ gtk_bitset_contains (jset, k), ==, gtk_bitset_contains (testset, k));
|
||||
}
|
||||
|
||||
gtk_bitset_unref (testset);
|
||||
gtk_bitset_unref (jset);
|
||||
}
|
||||
|
||||
gtk_bitset_unref (iset);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
test_subtract (void)
|
||||
{
|
||||
guint i, j, k, min, max;
|
||||
GtkBitset *iset, *jset, *testset;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (bitsets); i++)
|
||||
{
|
||||
iset = bitsets[i].create();
|
||||
|
||||
g_assert_true (gtk_bitset_equals (iset, iset));
|
||||
|
||||
for (j = 0; j < G_N_ELEMENTS (bitsets); j++)
|
||||
{
|
||||
jset = bitsets[j].create();
|
||||
|
||||
testset = gtk_bitset_copy (iset);
|
||||
gtk_bitset_subtract (testset, jset);
|
||||
|
||||
min = MIN (gtk_bitset_get_minimum (iset), gtk_bitset_get_minimum (jset));
|
||||
g_assert_cmpint (min, <=, gtk_bitset_get_minimum (testset));
|
||||
max = MAX (gtk_bitset_get_maximum (iset), gtk_bitset_get_maximum (jset));
|
||||
g_assert_cmpint (max, >=, gtk_bitset_get_maximum (testset));
|
||||
|
||||
for (k = min; k <= max; k++)
|
||||
{
|
||||
g_assert_cmpint (gtk_bitset_contains (iset, k) && !gtk_bitset_contains (jset, k), ==, gtk_bitset_contains (testset, k));
|
||||
}
|
||||
|
||||
gtk_bitset_unref (testset);
|
||||
gtk_bitset_unref (jset);
|
||||
}
|
||||
|
||||
gtk_bitset_unref (iset);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
g_test_init (&argc, &argv, NULL);
|
||||
setlocale (LC_ALL, "C");
|
||||
|
||||
g_test_add_func ("/bitset/is_empty", test_is_empty);
|
||||
g_test_add_func ("/bitset/minimum", test_minimum);
|
||||
g_test_add_func ("/bitset/maximum", test_maximum);
|
||||
g_test_add_func ("/bitset/equals", test_equals);
|
||||
g_test_add_func ("/bitset/union", test_union);
|
||||
g_test_add_func ("/bitset/intersect", test_intersect);
|
||||
g_test_add_func ("/bitset/difference", test_difference);
|
||||
g_test_add_func ("/bitset/subtract", test_subtract);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
@ -104,9 +104,6 @@ test_type (gconstpointer data)
|
||||
g_type_is_a (type, GTK_TYPE_SHORTCUT_ACTION))
|
||||
return;
|
||||
|
||||
if (g_type_is_a (type, GTK_TYPE_PROPERTY_SELECTION))
|
||||
return;
|
||||
|
||||
klass = g_type_class_ref (type);
|
||||
|
||||
if (g_type_is_a (type, GTK_TYPE_SETTINGS))
|
||||
|
@ -12,6 +12,7 @@ tests = [
|
||||
['accessible'],
|
||||
['action'],
|
||||
['adjustment'],
|
||||
['bitset'],
|
||||
['bitmask', ['../../gtk/gtkallocatedbitmask.c'], ['-DGTK_COMPILATION', '-UG_ENABLE_DEBUG']],
|
||||
['builder', [], [], gtk_tests_export_dynamic_ldflag],
|
||||
['builderparser'],
|
||||
|
@ -396,7 +396,7 @@ test_select_range (void)
|
||||
ret = gtk_selection_model_select_range (selection, 3, 2, FALSE);
|
||||
g_assert_true (ret);
|
||||
assert_selection (selection, "3 4 5");
|
||||
assert_selection_changes (selection, "3:2");
|
||||
assert_selection_changes (selection, "4:1");
|
||||
|
||||
ret = gtk_selection_model_select_range (selection, 0, 1, TRUE);
|
||||
g_assert_true (ret);
|
||||
@ -408,7 +408,7 @@ test_select_range (void)
|
||||
}
|
||||
|
||||
/* Test that removing and readding items
|
||||
* clears the selected state.
|
||||
* doesn't clear the selected state.
|
||||
*/
|
||||
static void
|
||||
test_readd (void)
|
||||
@ -432,62 +432,19 @@ test_readd (void)
|
||||
|
||||
g_list_model_items_changed (G_LIST_MODEL (store), 1, 3, 3);
|
||||
assert_changes (selection, "1-3+3");
|
||||
assert_selection (selection, "");
|
||||
assert_selection (selection, "3 4");
|
||||
|
||||
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)
|
||||
test_set_selection (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 }
|
||||
};
|
||||
GtkBitset *selected, *mask;
|
||||
|
||||
store = new_store (1, 10, 1);
|
||||
|
||||
@ -496,13 +453,26 @@ test_callback (void)
|
||||
assert_selection (selection, "");
|
||||
assert_selection_changes (selection, "");
|
||||
|
||||
ret = gtk_selection_model_select_callback (selection, FALSE, select_some, data);
|
||||
selected = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add_range (selected, 2, 3);
|
||||
gtk_bitset_add_range (selected, 6, 3);
|
||||
mask = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add_range (mask, 0, 100); /* too big on purpose */
|
||||
ret = gtk_selection_model_set_selection (selection, selected, mask);
|
||||
g_assert_true (ret);
|
||||
gtk_bitset_unref (selected);
|
||||
gtk_bitset_unref (mask);
|
||||
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);
|
||||
selected = gtk_bitset_new_empty ();
|
||||
mask = gtk_bitset_new_empty ();
|
||||
gtk_bitset_add (mask, 3);
|
||||
gtk_bitset_add (mask, 7);
|
||||
ret = gtk_selection_model_set_selection (selection, selected, mask);
|
||||
g_assert_true (ret);
|
||||
gtk_bitset_unref (selected);
|
||||
gtk_bitset_unref (mask);
|
||||
assert_selection (selection, "3 5 7 9");
|
||||
assert_selection_changes (selection, "3:5");
|
||||
|
||||
@ -528,7 +498,7 @@ main (int argc, char *argv[])
|
||||
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);
|
||||
g_test_add_func ("/multiselection/set_selection", test_set_selection);
|
||||
|
||||
return g_test_run ();
|
||||
}
|
||||
|
@ -425,10 +425,6 @@ test_type (gconstpointer data)
|
||||
g_type_is_a (type, GTK_TYPE_NAMED_ACTION))
|
||||
return;
|
||||
|
||||
/* needs special item type in underlying model */
|
||||
if (g_type_is_a (type, GTK_TYPE_PROPERTY_SELECTION))
|
||||
return;
|
||||
|
||||
klass = g_type_class_ref (type);
|
||||
|
||||
if (g_type_is_a (type, GTK_TYPE_SETTINGS))
|
||||
|
@ -80,8 +80,7 @@ test_finalize_object (gconstpointer data)
|
||||
NULL);
|
||||
g_object_unref (list_store);
|
||||
}
|
||||
else if (g_type_is_a (test_type, GTK_TYPE_LAYOUT_CHILD) ||
|
||||
g_type_is_a (test_type, GTK_TYPE_PROPERTY_SELECTION))
|
||||
else if (g_type_is_a (test_type, GTK_TYPE_LAYOUT_CHILD))
|
||||
{
|
||||
char *msg = g_strdup_printf ("Skipping %s", g_type_name (test_type));
|
||||
g_test_skip (msg);
|
||||
|
@ -593,28 +593,30 @@ test_persistence (void)
|
||||
}
|
||||
|
||||
static void
|
||||
check_query_range (GtkSelectionModel *selection)
|
||||
check_get_selection (GtkSelectionModel *selection)
|
||||
{
|
||||
guint i, j;
|
||||
guint position, n_items;
|
||||
gboolean selected;
|
||||
GtkBitset *set;
|
||||
guint i, n_items;
|
||||
|
||||
/* check that range always contains position, and has uniform selection */
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (selection)); i++)
|
||||
{
|
||||
gtk_selection_model_query_range (selection, i, &position, &n_items, &selected);
|
||||
g_assert_cmpint (position, <=, i);
|
||||
g_assert_cmpint (i, <, position + n_items);
|
||||
for (j = position; j < position + n_items; j++)
|
||||
g_assert_true (selected == gtk_selection_model_is_selected (selection, j));
|
||||
}
|
||||
set = gtk_selection_model_get_selection (selection);
|
||||
|
||||
/* check that out-of-range returns the correct invalid values */
|
||||
i = MIN (i, g_random_int ());
|
||||
gtk_selection_model_query_range (selection, i, &position, &n_items, &selected);
|
||||
g_assert_cmpint (position, ==, i);
|
||||
g_assert_cmpint (n_items, ==, 0);
|
||||
g_assert_true (!selected);
|
||||
n_items = g_list_model_get_n_items (G_LIST_MODEL (selection));
|
||||
if (n_items == 0)
|
||||
{
|
||||
g_assert_true (gtk_bitset_is_empty (set));
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < n_items; i++)
|
||||
{
|
||||
g_assert_cmpint (gtk_bitset_contains (set, i), ==, gtk_selection_model_is_selected (selection, i));
|
||||
}
|
||||
|
||||
/* check that out-of-range has no bits set */
|
||||
g_assert_cmpint (gtk_bitset_get_maximum (set), <, g_list_model_get_n_items (G_LIST_MODEL (selection)));
|
||||
}
|
||||
|
||||
gtk_bitset_unref (set);
|
||||
}
|
||||
|
||||
static void
|
||||
@ -625,16 +627,16 @@ test_query_range (void)
|
||||
|
||||
store = new_store (1, 5, 1);
|
||||
selection = new_model (store, TRUE, TRUE);
|
||||
check_query_range (selection);
|
||||
check_get_selection (selection);
|
||||
|
||||
gtk_selection_model_unselect_item (selection, 0);
|
||||
check_query_range (selection);
|
||||
check_get_selection (selection);
|
||||
|
||||
gtk_selection_model_select_item (selection, 2, TRUE);
|
||||
check_query_range (selection);
|
||||
check_get_selection (selection);
|
||||
|
||||
gtk_selection_model_select_item (selection, 4, TRUE);
|
||||
check_query_range (selection);
|
||||
check_get_selection (selection);
|
||||
|
||||
ignore_selection_changes (selection);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user