2013-06-10 09:17:10 +00:00
|
|
|
|
/*
|
|
|
|
|
* Copyright (C) 2012 Alexander Larsson <alexl@redhat.com>
|
|
|
|
|
*
|
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
* License as published by the Free Software Foundation; either
|
|
|
|
|
* version 2 of the License, or (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
|
*
|
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
2018-02-08 23:52:44 +00:00
|
|
|
|
#include "gtklistbox.h"
|
|
|
|
|
|
2018-02-02 09:08:41 +00:00
|
|
|
|
#include "gtkactionhelperprivate.h"
|
2014-07-03 00:36:41 +00:00
|
|
|
|
#include "gtkadjustmentprivate.h"
|
2020-05-03 04:09:18 +00:00
|
|
|
|
#include "gtkbinlayout.h"
|
2018-02-08 23:52:44 +00:00
|
|
|
|
#include "gtkbuildable.h"
|
2015-05-30 03:03:11 +00:00
|
|
|
|
#include "gtkcssnodeprivate.h"
|
2019-05-29 17:10:46 +00:00
|
|
|
|
#include "gtkgestureclick.h"
|
2018-02-08 23:52:44 +00:00
|
|
|
|
#include "gtkintl.h"
|
|
|
|
|
#include "gtkmain.h"
|
2013-06-15 16:13:28 +00:00
|
|
|
|
#include "gtkmarshalers.h"
|
|
|
|
|
#include "gtkprivate.h"
|
2018-02-08 23:52:44 +00:00
|
|
|
|
#include "gtkscrollable.h"
|
|
|
|
|
#include "gtktypebuiltins.h"
|
2014-08-13 09:32:47 +00:00
|
|
|
|
#include "gtkwidgetprivate.h"
|
2018-02-08 23:52:44 +00:00
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
#include <float.h>
|
|
|
|
|
#include <math.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* SECTION:gtklistbox
|
|
|
|
|
* @Short_description: A list container
|
|
|
|
|
* @Title: GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @See_also: #GtkScrolledWindow
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* A GtkListBox is a vertical container that contains GtkListBoxRow
|
|
|
|
|
* children. These rows can by dynamically sorted and filtered, and
|
|
|
|
|
* headers can be added dynamically depending on the row content.
|
|
|
|
|
* It also allows keyboard and mouse navigation and selection like
|
|
|
|
|
* a typical list.
|
|
|
|
|
*
|
|
|
|
|
* Using GtkListBox is often an alternative to #GtkTreeView, especially
|
|
|
|
|
* when the list contents has a more complicated layout than what is allowed
|
|
|
|
|
* by a #GtkCellRenderer, or when the contents is interactive (i.e. has a
|
|
|
|
|
* button in it).
|
|
|
|
|
*
|
|
|
|
|
* Although a #GtkListBox must have only #GtkListBoxRow children you can
|
|
|
|
|
* add any kind of widget to it via gtk_container_add(), and a #GtkListBoxRow
|
|
|
|
|
* widget will automatically be inserted between the list and the widget.
|
|
|
|
|
*
|
2014-07-16 03:45:27 +00:00
|
|
|
|
* #GtkListBoxRows can be marked as activatable or selectable. If a row
|
|
|
|
|
* is activatable, #GtkListBox::row-activated will be emitted for it when
|
|
|
|
|
* the user tries to activate it. If it is selectable, the row will be marked
|
|
|
|
|
* as selected when the user tries to select it.
|
|
|
|
|
*
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* The GtkListBox widget was added in GTK+ 3.10.
|
2015-11-05 18:48:02 +00:00
|
|
|
|
*
|
2019-06-18 20:43:07 +00:00
|
|
|
|
* # GtkListBox as GtkBuildable
|
|
|
|
|
*
|
|
|
|
|
* The GtkListBox implementation of the #GtkBuildable interface supports
|
|
|
|
|
* setting a child as the placeholder by specifying “placeholder” as the “type”
|
|
|
|
|
* attribute of a <child> element. See gtk_list_box_set_placeholder() for info.
|
|
|
|
|
*
|
2015-11-05 18:48:02 +00:00
|
|
|
|
* # CSS nodes
|
|
|
|
|
*
|
2016-09-20 22:32:00 +00:00
|
|
|
|
* |[<!-- language="plain" -->
|
2019-04-13 00:24:40 +00:00
|
|
|
|
* list[.separators]
|
2016-09-20 22:32:00 +00:00
|
|
|
|
* ╰── row[.activatable]
|
|
|
|
|
* ]|
|
|
|
|
|
*
|
2019-04-13 00:24:40 +00:00
|
|
|
|
* GtkListBox uses a single CSS node named list. It may carry the .separators style
|
2020-05-11 16:47:20 +00:00
|
|
|
|
* class, when the #GtkListBox:show-separators property is set. Each GtkListBoxRow uses
|
2016-09-20 22:32:00 +00:00
|
|
|
|
* a single CSS node named row. The row nodes get the .activatable
|
2015-11-05 18:48:02 +00:00
|
|
|
|
* style class added when appropriate.
|
2013-06-15 16:13:28 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2019-05-29 00:00:40 +00:00
|
|
|
|
typedef struct _GtkListBoxClass GtkListBoxClass;
|
|
|
|
|
|
|
|
|
|
struct _GtkListBox
|
|
|
|
|
{
|
2020-05-08 12:48:00 +00:00
|
|
|
|
GtkWidget parent_instance;
|
2019-05-29 00:00:40 +00:00
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GSequence *children;
|
2013-06-11 12:09:19 +00:00
|
|
|
|
GHashTable *header_hash;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2013-06-11 14:53:31 +00:00
|
|
|
|
GtkWidget *placeholder;
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkListBoxSortFunc sort_func;
|
|
|
|
|
gpointer sort_func_target;
|
|
|
|
|
GDestroyNotify sort_func_target_destroy_notify;
|
|
|
|
|
|
|
|
|
|
GtkListBoxFilterFunc filter_func;
|
|
|
|
|
gpointer filter_func_target;
|
|
|
|
|
GDestroyNotify filter_func_target_destroy_notify;
|
|
|
|
|
|
2013-06-11 12:09:19 +00:00
|
|
|
|
GtkListBoxUpdateHeaderFunc update_header_func;
|
|
|
|
|
gpointer update_header_func_target;
|
|
|
|
|
GDestroyNotify update_header_func_target_destroy_notify;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
GtkListBoxRow *selected_row;
|
|
|
|
|
GtkListBoxRow *cursor_row;
|
|
|
|
|
|
|
|
|
|
GtkListBoxRow *active_row;
|
|
|
|
|
|
|
|
|
|
GtkSelectionMode selection_mode;
|
|
|
|
|
|
2017-10-29 15:10:12 +00:00
|
|
|
|
gulong adjustment_changed_id;
|
|
|
|
|
GtkWidget *scrollable_parent;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkAdjustment *adjustment;
|
|
|
|
|
gboolean activate_single_click;
|
2017-12-31 04:23:16 +00:00
|
|
|
|
gboolean accept_unpaired_release;
|
2019-04-13 00:24:40 +00:00
|
|
|
|
gboolean show_separators;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
/* DnD */
|
|
|
|
|
GtkListBoxRow *drag_highlighted_row;
|
2013-06-11 14:20:45 +00:00
|
|
|
|
|
|
|
|
|
int n_visible_rows;
|
2015-02-09 10:49:31 +00:00
|
|
|
|
|
|
|
|
|
GListModel *bound_model;
|
|
|
|
|
GtkListBoxCreateWidgetFunc create_widget_func;
|
|
|
|
|
gpointer create_widget_func_data;
|
|
|
|
|
GDestroyNotify create_widget_func_data_destroy;
|
2020-03-28 20:52:41 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct _GtkListBoxClass
|
|
|
|
|
{
|
2020-05-08 12:48:00 +00:00
|
|
|
|
GtkWidgetClass parent_class;
|
2020-03-28 20:52:41 +00:00
|
|
|
|
|
|
|
|
|
void (*row_selected) (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row);
|
|
|
|
|
void (*row_activated) (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row);
|
|
|
|
|
void (*activate_cursor_row) (GtkListBox *box);
|
|
|
|
|
void (*toggle_cursor_row) (GtkListBox *box);
|
|
|
|
|
void (*move_cursor) (GtkListBox *box,
|
|
|
|
|
GtkMovementStep step,
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int count,
|
2020-04-11 18:52:43 +00:00
|
|
|
|
gboolean extend,
|
|
|
|
|
gboolean modify);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
void (*selected_rows_changed) (GtkListBox *box);
|
|
|
|
|
void (*select_all) (GtkListBox *box);
|
|
|
|
|
void (*unselect_all) (GtkListBox *box);
|
|
|
|
|
};
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
typedef struct
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-05-03 04:09:18 +00:00
|
|
|
|
GtkWidget *child;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GSequenceIter *iter;
|
2013-06-11 12:09:19 +00:00
|
|
|
|
GtkWidget *header;
|
2017-11-24 13:26:39 +00:00
|
|
|
|
GtkActionHelper *action_helper;
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int y;
|
|
|
|
|
int height;
|
2014-07-16 02:42:29 +00:00
|
|
|
|
guint visible :1;
|
|
|
|
|
guint selected :1;
|
|
|
|
|
guint activatable :1;
|
2014-07-16 03:10:51 +00:00
|
|
|
|
guint selectable :1;
|
2013-07-02 11:44:17 +00:00
|
|
|
|
} GtkListBoxRowPrivate;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
ROW_SELECTED,
|
|
|
|
|
ROW_ACTIVATED,
|
|
|
|
|
ACTIVATE_CURSOR_ROW,
|
|
|
|
|
TOGGLE_CURSOR_ROW,
|
|
|
|
|
MOVE_CURSOR,
|
2014-03-28 23:21:57 +00:00
|
|
|
|
SELECTED_ROWS_CHANGED,
|
|
|
|
|
SELECT_ALL,
|
|
|
|
|
UNSELECT_ALL,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
LAST_SIGNAL
|
|
|
|
|
};
|
|
|
|
|
|
2013-09-10 12:12:07 +00:00
|
|
|
|
enum {
|
|
|
|
|
ROW__ACTIVATE,
|
|
|
|
|
ROW__LAST_SIGNAL
|
|
|
|
|
};
|
|
|
|
|
|
2014-07-13 00:37:41 +00:00
|
|
|
|
enum {
|
2013-06-10 09:17:10 +00:00
|
|
|
|
PROP_0,
|
|
|
|
|
PROP_SELECTION_MODE,
|
|
|
|
|
PROP_ACTIVATE_ON_SINGLE_CLICK,
|
2017-12-31 04:23:16 +00:00
|
|
|
|
PROP_ACCEPT_UNPAIRED_RELEASE,
|
2019-04-13 00:24:40 +00:00
|
|
|
|
PROP_SHOW_SEPARATORS,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
LAST_PROPERTY
|
|
|
|
|
};
|
|
|
|
|
|
2014-07-13 00:37:41 +00:00
|
|
|
|
enum {
|
|
|
|
|
ROW_PROP_0,
|
|
|
|
|
ROW_PROP_ACTIVATABLE,
|
2014-07-16 03:10:51 +00:00
|
|
|
|
ROW_PROP_SELECTABLE,
|
2020-05-03 04:09:18 +00:00
|
|
|
|
ROW_PROP_CHILD,
|
2017-11-24 13:26:39 +00:00
|
|
|
|
|
|
|
|
|
/* actionable properties */
|
|
|
|
|
ROW_PROP_ACTION_NAME,
|
|
|
|
|
ROW_PROP_ACTION_TARGET,
|
|
|
|
|
|
|
|
|
|
LAST_ROW_PROPERTY = ROW_PROP_ACTION_NAME
|
2014-07-13 00:37:41 +00:00
|
|
|
|
};
|
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
#define ROW_PRIV(row) ((GtkListBoxRowPrivate*)gtk_list_box_row_get_instance_private ((GtkListBoxRow*)(row)))
|
|
|
|
|
|
2018-03-08 02:13:57 +00:00
|
|
|
|
static GtkBuildableIface *parent_buildable_iface;
|
|
|
|
|
|
2017-11-24 13:26:39 +00:00
|
|
|
|
static void gtk_list_box_buildable_interface_init (GtkBuildableIface *iface);
|
|
|
|
|
|
2020-05-03 04:09:18 +00:00
|
|
|
|
static void gtk_list_box_row_buildable_iface_init (GtkBuildableIface *iface);
|
2017-11-24 13:26:39 +00:00
|
|
|
|
static void gtk_list_box_row_actionable_iface_init (GtkActionableInterface *iface);
|
2014-10-12 22:54:44 +00:00
|
|
|
|
|
2020-05-08 12:48:00 +00:00
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GtkListBox, gtk_list_box, GTK_TYPE_WIDGET,
|
2014-10-12 22:54:44 +00:00
|
|
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
|
|
|
|
|
gtk_list_box_buildable_interface_init))
|
2020-05-03 04:09:18 +00:00
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GtkListBoxRow, gtk_list_box_row, GTK_TYPE_WIDGET,
|
2017-11-24 13:26:39 +00:00
|
|
|
|
G_ADD_PRIVATE (GtkListBoxRow)
|
2020-05-03 04:09:18 +00:00
|
|
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_BUILDABLE,
|
|
|
|
|
gtk_list_box_row_buildable_iface_init )
|
2017-11-24 13:26:39 +00:00
|
|
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTIONABLE, gtk_list_box_row_actionable_iface_init))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
static void gtk_list_box_apply_filter_all (GtkListBox *box);
|
|
|
|
|
static void gtk_list_box_update_header (GtkListBox *box,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GSequenceIter *iter);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
static GSequenceIter * gtk_list_box_get_next_visible (GtkListBox *box,
|
|
|
|
|
GSequenceIter *iter);
|
|
|
|
|
static void gtk_list_box_apply_filter (GtkListBox *box,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkListBoxRow *row);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
static void gtk_list_box_add_move_binding (GtkWidgetClass *widget_class,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
guint keyval,
|
|
|
|
|
GdkModifierType modmask,
|
|
|
|
|
GtkMovementStep step,
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int count);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
static void gtk_list_box_update_cursor (GtkListBox *box,
|
2015-02-10 19:02:08 +00:00
|
|
|
|
GtkListBoxRow *row,
|
|
|
|
|
gboolean grab_focus);
|
2013-09-28 02:00:38 +00:00
|
|
|
|
static void gtk_list_box_show (GtkWidget *widget);
|
|
|
|
|
static gboolean gtk_list_box_focus (GtkWidget *widget,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkDirectionType direction);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
static GSequenceIter* gtk_list_box_get_previous_visible (GtkListBox *box,
|
|
|
|
|
GSequenceIter *iter);
|
|
|
|
|
static GtkListBoxRow *gtk_list_box_get_first_focusable (GtkListBox *box);
|
|
|
|
|
static GtkListBoxRow *gtk_list_box_get_last_focusable (GtkListBox *box);
|
2015-08-16 22:11:50 +00:00
|
|
|
|
static void gtk_list_box_compute_expand (GtkWidget *widget,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
gboolean *hexpand,
|
|
|
|
|
gboolean *vexpand);
|
2013-09-28 02:00:38 +00:00
|
|
|
|
static GtkSizeRequestMode gtk_list_box_get_request_mode (GtkWidget *widget);
|
|
|
|
|
static void gtk_list_box_size_allocate (GtkWidget *widget,
|
2018-08-16 04:53:03 +00:00
|
|
|
|
int width,
|
|
|
|
|
int height,
|
2018-03-31 19:02:28 +00:00
|
|
|
|
int baseline);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
static void gtk_list_box_activate_cursor_row (GtkListBox *box);
|
|
|
|
|
static void gtk_list_box_toggle_cursor_row (GtkListBox *box);
|
|
|
|
|
static void gtk_list_box_move_cursor (GtkListBox *box,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkMovementStep step,
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int count,
|
2020-04-11 18:52:43 +00:00
|
|
|
|
gboolean extend,
|
|
|
|
|
gboolean modify);
|
2017-10-29 15:10:12 +00:00
|
|
|
|
static void gtk_list_box_parent_cb (GObject *object,
|
|
|
|
|
GParamSpec *pspec,
|
|
|
|
|
gpointer user_data);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
static void gtk_list_box_select_row_internal (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row);
|
|
|
|
|
static void gtk_list_box_unselect_row_internal (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row);
|
|
|
|
|
static void gtk_list_box_select_all_between (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row1,
|
|
|
|
|
GtkListBoxRow *row2,
|
|
|
|
|
gboolean modify);
|
|
|
|
|
static gboolean gtk_list_box_unselect_all_internal (GtkListBox *box);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
static void gtk_list_box_selected_rows_changed (GtkListBox *box);
|
2017-12-31 04:23:16 +00:00
|
|
|
|
static void gtk_list_box_set_accept_unpaired_release (GtkListBox *box,
|
|
|
|
|
gboolean accept);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
2019-05-29 17:10:46 +00:00
|
|
|
|
static void gtk_list_box_click_gesture_pressed (GtkGestureClick *gesture,
|
|
|
|
|
guint n_press,
|
2020-07-24 20:32:16 +00:00
|
|
|
|
double x,
|
|
|
|
|
double y,
|
2019-05-29 17:10:46 +00:00
|
|
|
|
GtkListBox *box);
|
|
|
|
|
static void gtk_list_box_click_gesture_released (GtkGestureClick *gesture,
|
|
|
|
|
guint n_press,
|
2020-07-24 20:32:16 +00:00
|
|
|
|
double x,
|
|
|
|
|
double y,
|
2019-05-29 17:10:46 +00:00
|
|
|
|
GtkListBox *box);
|
|
|
|
|
static void gtk_list_box_click_unpaired_release (GtkGestureClick *gesture,
|
2020-07-24 20:32:16 +00:00
|
|
|
|
double x,
|
|
|
|
|
double y,
|
2019-05-29 17:10:46 +00:00
|
|
|
|
guint button,
|
|
|
|
|
GdkEventSequence *sequence,
|
|
|
|
|
GtkListBox *box);
|
|
|
|
|
static void gtk_list_box_click_gesture_stopped (GtkGestureClick *gesture,
|
|
|
|
|
GtkListBox *box);
|
2014-06-05 13:40:02 +00:00
|
|
|
|
|
2014-07-16 03:13:43 +00:00
|
|
|
|
static void gtk_list_box_update_row_styles (GtkListBox *box);
|
|
|
|
|
static void gtk_list_box_update_row_style (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row);
|
2014-06-05 13:40:02 +00:00
|
|
|
|
|
2015-02-09 10:49:31 +00:00
|
|
|
|
static void gtk_list_box_bound_model_changed (GListModel *list,
|
|
|
|
|
guint position,
|
|
|
|
|
guint removed,
|
|
|
|
|
guint added,
|
|
|
|
|
gpointer user_data);
|
|
|
|
|
|
2015-08-25 17:16:24 +00:00
|
|
|
|
static void gtk_list_box_check_model_compat (GtkListBox *box);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
|
2017-05-03 20:16:47 +00:00
|
|
|
|
static void gtk_list_box_measure (GtkWidget *widget,
|
|
|
|
|
GtkOrientation orientation,
|
|
|
|
|
int for_size,
|
|
|
|
|
int *minimum,
|
|
|
|
|
int *natural,
|
|
|
|
|
int *minimum_baseline,
|
|
|
|
|
int *natural_baseline);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static GParamSpec *properties[LAST_PROPERTY] = { NULL, };
|
|
|
|
|
static guint signals[LAST_SIGNAL] = { 0 };
|
2014-07-13 00:37:41 +00:00
|
|
|
|
static GParamSpec *row_properties[LAST_ROW_PROPERTY] = { NULL, };
|
2013-09-10 12:12:07 +00:00
|
|
|
|
static guint row_signals[ROW__LAST_SIGNAL] = { 0 };
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-05-03 04:09:18 +00:00
|
|
|
|
|
|
|
|
|
static GtkBuildableIface *parent_row_buildable_iface;
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_buildable_add_child (GtkBuildable *buildable,
|
|
|
|
|
GtkBuilder *builder,
|
|
|
|
|
GObject *child,
|
2020-07-24 18:40:36 +00:00
|
|
|
|
const char *type)
|
2020-05-03 04:09:18 +00:00
|
|
|
|
{
|
|
|
|
|
if (GTK_IS_WIDGET (child))
|
|
|
|
|
gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (buildable), GTK_WIDGET (child));
|
|
|
|
|
else
|
|
|
|
|
parent_row_buildable_iface->add_child (buildable, builder, child, type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_buildable_iface_init (GtkBuildableIface *iface)
|
|
|
|
|
{
|
|
|
|
|
parent_row_buildable_iface = g_type_interface_peek_parent (iface);
|
|
|
|
|
|
|
|
|
|
iface->add_child = gtk_list_box_row_buildable_add_child;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_new:
|
|
|
|
|
*
|
|
|
|
|
* Creates a new #GtkListBox container.
|
|
|
|
|
*
|
|
|
|
|
* Returns: a new #GtkListBox
|
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkWidget *
|
|
|
|
|
gtk_list_box_new (void)
|
|
|
|
|
{
|
|
|
|
|
return g_object_new (GTK_TYPE_LIST_BOX, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_get_property (GObject *obj,
|
|
|
|
|
guint property_id,
|
|
|
|
|
GValue *value,
|
|
|
|
|
GParamSpec *pspec)
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
GtkListBox *box = GTK_LIST_BOX (obj);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
switch (property_id)
|
|
|
|
|
{
|
|
|
|
|
case PROP_SELECTION_MODE:
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_value_set_enum (value, box->selection_mode);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
break;
|
|
|
|
|
case PROP_ACTIVATE_ON_SINGLE_CLICK:
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_value_set_boolean (value, box->activate_single_click);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
break;
|
2017-12-31 04:23:16 +00:00
|
|
|
|
case PROP_ACCEPT_UNPAIRED_RELEASE:
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_value_set_boolean (value, box->accept_unpaired_release);
|
2017-12-31 04:23:16 +00:00
|
|
|
|
break;
|
2019-04-13 00:24:40 +00:00
|
|
|
|
case PROP_SHOW_SEPARATORS:
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_value_set_boolean (value, box->show_separators);
|
2019-04-13 00:24:40 +00:00
|
|
|
|
break;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_set_property (GObject *obj,
|
|
|
|
|
guint property_id,
|
|
|
|
|
const GValue *value,
|
|
|
|
|
GParamSpec *pspec)
|
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
GtkListBox *box = GTK_LIST_BOX (obj);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
switch (property_id)
|
|
|
|
|
{
|
|
|
|
|
case PROP_SELECTION_MODE:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_set_selection_mode (box, g_value_get_enum (value));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
break;
|
|
|
|
|
case PROP_ACTIVATE_ON_SINGLE_CLICK:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_set_activate_on_single_click (box, g_value_get_boolean (value));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
break;
|
2017-12-31 04:23:16 +00:00
|
|
|
|
case PROP_ACCEPT_UNPAIRED_RELEASE:
|
|
|
|
|
gtk_list_box_set_accept_unpaired_release (box, g_value_get_boolean (value));
|
|
|
|
|
break;
|
2019-04-13 00:24:40 +00:00
|
|
|
|
case PROP_SHOW_SEPARATORS:
|
|
|
|
|
gtk_list_box_set_show_separators (box, g_value_get_boolean (value));
|
|
|
|
|
break;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-08 12:48:00 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_dispose (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *child;
|
|
|
|
|
|
|
|
|
|
while ((child = gtk_widget_get_first_child (GTK_WIDGET (object))))
|
|
|
|
|
gtk_list_box_remove (GTK_LIST_BOX (object), child);
|
|
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (gtk_list_box_parent_class)->dispose (object);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_finalize (GObject *obj)
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
GtkListBox *box = GTK_LIST_BOX (obj);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->sort_func_target_destroy_notify != NULL)
|
|
|
|
|
box->sort_func_target_destroy_notify (box->sort_func_target);
|
|
|
|
|
if (box->filter_func_target_destroy_notify != NULL)
|
|
|
|
|
box->filter_func_target_destroy_notify (box->filter_func_target);
|
|
|
|
|
if (box->update_header_func_target_destroy_notify != NULL)
|
|
|
|
|
box->update_header_func_target_destroy_notify (box->update_header_func_target);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_clear_object (&box->adjustment);
|
|
|
|
|
g_clear_object (&box->drag_highlighted_row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_sequence_free (box->children);
|
|
|
|
|
g_hash_table_unref (box->header_hash);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->bound_model)
|
2015-02-09 10:49:31 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->create_widget_func_data_destroy)
|
|
|
|
|
box->create_widget_func_data_destroy (box->create_widget_func_data);
|
2015-02-09 10:49:31 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_signal_handlers_disconnect_by_func (box->bound_model, gtk_list_box_bound_model_changed, obj);
|
|
|
|
|
g_clear_object (&box->bound_model);
|
2015-02-09 10:49:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
G_OBJECT_CLASS (gtk_list_box_parent_class)->finalize (obj);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_class_init (GtkListBoxClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
|
|
|
|
|
|
object_class->get_property = gtk_list_box_get_property;
|
|
|
|
|
object_class->set_property = gtk_list_box_set_property;
|
2020-05-08 12:48:00 +00:00
|
|
|
|
object_class->dispose = gtk_list_box_dispose;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
object_class->finalize = gtk_list_box_finalize;
|
2020-05-08 12:48:00 +00:00
|
|
|
|
|
2013-09-28 02:00:38 +00:00
|
|
|
|
widget_class->show = gtk_list_box_show;
|
|
|
|
|
widget_class->focus = gtk_list_box_focus;
|
2020-04-08 11:43:28 +00:00
|
|
|
|
widget_class->grab_focus = gtk_widget_grab_focus_self;
|
2015-08-16 22:11:50 +00:00
|
|
|
|
widget_class->compute_expand = gtk_list_box_compute_expand;
|
2013-09-28 02:00:38 +00:00
|
|
|
|
widget_class->get_request_mode = gtk_list_box_get_request_mode;
|
2017-05-03 20:16:47 +00:00
|
|
|
|
widget_class->measure = gtk_list_box_measure;
|
2013-09-28 02:00:38 +00:00
|
|
|
|
widget_class->size_allocate = gtk_list_box_size_allocate;
|
|
|
|
|
klass->activate_cursor_row = gtk_list_box_activate_cursor_row;
|
|
|
|
|
klass->toggle_cursor_row = gtk_list_box_toggle_cursor_row;
|
|
|
|
|
klass->move_cursor = gtk_list_box_move_cursor;
|
2014-03-29 01:34:39 +00:00
|
|
|
|
klass->select_all = gtk_list_box_select_all;
|
|
|
|
|
klass->unselect_all = gtk_list_box_unselect_all;
|
|
|
|
|
klass->selected_rows_changed = gtk_list_box_selected_rows_changed;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
properties[PROP_SELECTION_MODE] =
|
|
|
|
|
g_param_spec_enum ("selection-mode",
|
2013-06-15 16:13:28 +00:00
|
|
|
|
P_("Selection mode"),
|
|
|
|
|
P_("The selection mode"),
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_TYPE_SELECTION_MODE,
|
|
|
|
|
GTK_SELECTION_SINGLE,
|
2014-06-07 03:46:32 +00:00
|
|
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
properties[PROP_ACTIVATE_ON_SINGLE_CLICK] =
|
|
|
|
|
g_param_spec_boolean ("activate-on-single-click",
|
2013-06-15 16:13:28 +00:00
|
|
|
|
P_("Activate on Single Click"),
|
|
|
|
|
P_("Activate row on a single click"),
|
2013-06-10 09:17:10 +00:00
|
|
|
|
TRUE,
|
2014-06-07 03:46:32 +00:00
|
|
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2017-12-31 04:23:16 +00:00
|
|
|
|
properties[PROP_ACCEPT_UNPAIRED_RELEASE] =
|
|
|
|
|
g_param_spec_boolean ("accept-unpaired-release",
|
|
|
|
|
P_("Accept unpaired release"),
|
|
|
|
|
P_("Accept unpaired release"),
|
|
|
|
|
FALSE,
|
|
|
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
|
2019-04-13 00:24:40 +00:00
|
|
|
|
properties[PROP_SHOW_SEPARATORS] =
|
|
|
|
|
g_param_spec_boolean ("show-separators",
|
|
|
|
|
P_("Show separators"),
|
|
|
|
|
P_("Show separators between rows"),
|
|
|
|
|
FALSE,
|
|
|
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
g_object_class_install_properties (object_class, LAST_PROPERTY, properties);
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* GtkListBox::row-selected:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: the #GtkListBox
|
2014-05-26 16:13:02 +00:00
|
|
|
|
* @row: (nullable): the selected row
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* The ::row-selected signal is emitted when a new row is selected, or
|
|
|
|
|
* (with a %NULL @row) when the selection is cleared.
|
|
|
|
|
*
|
2014-05-08 11:33:01 +00:00
|
|
|
|
* When the @box is using #GTK_SELECTION_MULTIPLE, this signal will not
|
|
|
|
|
* give you the full picture of selection changes, and you should use
|
|
|
|
|
* the #GtkListBox::selected-rows-changed signal instead.
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
signals[ROW_SELECTED] =
|
2015-09-12 13:13:00 +00:00
|
|
|
|
g_signal_new (I_("row-selected"),
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_TYPE_LIST_BOX,
|
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
|
G_STRUCT_OFFSET (GtkListBoxClass, row_selected),
|
|
|
|
|
NULL, NULL,
|
2019-05-31 03:56:50 +00:00
|
|
|
|
NULL,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
G_TYPE_NONE, 1,
|
2013-06-11 10:59:32 +00:00
|
|
|
|
GTK_TYPE_LIST_BOX_ROW);
|
2013-06-15 16:13:28 +00:00
|
|
|
|
|
2014-03-28 23:21:57 +00:00
|
|
|
|
/**
|
|
|
|
|
* GtkListBox::selected-rows-changed:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: the #GtkListBox on wich the signal is emitted
|
2014-03-28 23:21:57 +00:00
|
|
|
|
*
|
|
|
|
|
* The ::selected-rows-changed signal is emitted when the
|
|
|
|
|
* set of selected rows changes.
|
|
|
|
|
*/
|
2015-09-12 13:13:00 +00:00
|
|
|
|
signals[SELECTED_ROWS_CHANGED] = g_signal_new (I_("selected-rows-changed"),
|
2014-03-28 23:21:57 +00:00
|
|
|
|
GTK_TYPE_LIST_BOX,
|
|
|
|
|
G_SIGNAL_RUN_FIRST,
|
|
|
|
|
G_STRUCT_OFFSET (GtkListBoxClass, selected_rows_changed),
|
|
|
|
|
NULL, NULL,
|
2019-05-29 20:05:19 +00:00
|
|
|
|
NULL,
|
2014-03-28 23:21:57 +00:00
|
|
|
|
G_TYPE_NONE, 0);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* GtkListBox::select-all:
|
|
|
|
|
* @box: the #GtkListBox on which the signal is emitted
|
|
|
|
|
*
|
|
|
|
|
* The ::select-all signal is a [keybinding signal][GtkBindingSignal]
|
|
|
|
|
* which gets emitted to select all children of the box, if the selection
|
|
|
|
|
* mode permits it.
|
|
|
|
|
*
|
|
|
|
|
* The default bindings for this signal is Ctrl-a.
|
|
|
|
|
*/
|
2015-09-12 13:13:00 +00:00
|
|
|
|
signals[SELECT_ALL] = g_signal_new (I_("select-all"),
|
2014-03-28 23:21:57 +00:00
|
|
|
|
GTK_TYPE_LIST_BOX,
|
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
|
|
|
G_STRUCT_OFFSET (GtkListBoxClass, select_all),
|
|
|
|
|
NULL, NULL,
|
2019-05-29 20:05:19 +00:00
|
|
|
|
NULL,
|
2014-03-28 23:21:57 +00:00
|
|
|
|
G_TYPE_NONE, 0);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* GtkListBox::unselect-all:
|
|
|
|
|
* @box: the #GtkListBox on which the signal is emitted
|
|
|
|
|
*
|
|
|
|
|
* The ::unselect-all signal is a [keybinding signal][GtkBindingSignal]
|
|
|
|
|
* which gets emitted to unselect all children of the box, if the selection
|
|
|
|
|
* mode permits it.
|
|
|
|
|
*
|
|
|
|
|
* The default bindings for this signal is Ctrl-Shift-a.
|
|
|
|
|
*/
|
2015-09-12 13:13:00 +00:00
|
|
|
|
signals[UNSELECT_ALL] = g_signal_new (I_("unselect-all"),
|
|
|
|
|
GTK_TYPE_LIST_BOX,
|
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
|
|
|
G_STRUCT_OFFSET (GtkListBoxClass, unselect_all),
|
|
|
|
|
NULL, NULL,
|
2019-05-29 20:05:19 +00:00
|
|
|
|
NULL,
|
2015-09-12 13:13:00 +00:00
|
|
|
|
G_TYPE_NONE, 0);
|
2014-03-28 23:21:57 +00:00
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* GtkListBox::row-activated:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: the #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @row: the activated row
|
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* The ::row-activated signal is emitted when a row has been activated by the user.
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
signals[ROW_ACTIVATED] =
|
2015-09-12 13:13:00 +00:00
|
|
|
|
g_signal_new (I_("row-activated"),
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_TYPE_LIST_BOX,
|
|
|
|
|
G_SIGNAL_RUN_LAST,
|
|
|
|
|
G_STRUCT_OFFSET (GtkListBoxClass, row_activated),
|
|
|
|
|
NULL, NULL,
|
2019-05-31 03:56:50 +00:00
|
|
|
|
NULL,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
G_TYPE_NONE, 1,
|
2013-06-11 10:59:32 +00:00
|
|
|
|
GTK_TYPE_LIST_BOX_ROW);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
signals[ACTIVATE_CURSOR_ROW] =
|
2015-09-12 13:13:00 +00:00
|
|
|
|
g_signal_new (I_("activate-cursor-row"),
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_TYPE_LIST_BOX,
|
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
|
|
|
G_STRUCT_OFFSET (GtkListBoxClass, activate_cursor_row),
|
|
|
|
|
NULL, NULL,
|
2019-05-29 20:05:19 +00:00
|
|
|
|
NULL,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
G_TYPE_NONE, 0);
|
|
|
|
|
signals[TOGGLE_CURSOR_ROW] =
|
2015-09-12 13:13:00 +00:00
|
|
|
|
g_signal_new (I_("toggle-cursor-row"),
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_TYPE_LIST_BOX,
|
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
|
|
|
G_STRUCT_OFFSET (GtkListBoxClass, toggle_cursor_row),
|
|
|
|
|
NULL, NULL,
|
2019-05-29 20:05:19 +00:00
|
|
|
|
NULL,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
G_TYPE_NONE, 0);
|
|
|
|
|
signals[MOVE_CURSOR] =
|
2015-09-12 13:13:00 +00:00
|
|
|
|
g_signal_new (I_("move-cursor"),
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_TYPE_LIST_BOX,
|
|
|
|
|
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
|
|
|
|
|
G_STRUCT_OFFSET (GtkListBoxClass, move_cursor),
|
|
|
|
|
NULL, NULL,
|
2020-04-11 18:52:43 +00:00
|
|
|
|
_gtk_marshal_VOID__ENUM_INT_BOOLEAN_BOOLEAN,
|
|
|
|
|
G_TYPE_NONE, 4,
|
|
|
|
|
GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN);
|
2019-06-06 22:27:54 +00:00
|
|
|
|
g_signal_set_va_marshaller (signals[MOVE_CURSOR],
|
|
|
|
|
G_TYPE_FROM_CLASS (klass),
|
2020-04-11 18:52:43 +00:00
|
|
|
|
_gtk_marshal_VOID__ENUM_INT_BOOLEAN_BOOLEANv);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
widget_class->activate_signal = signals[ACTIVATE_CURSOR_ROW];
|
|
|
|
|
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_Home, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_BUFFER_ENDS, -1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Home, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_BUFFER_ENDS, -1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_End, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_BUFFER_ENDS, 1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_End, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_BUFFER_ENDS, 1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_Up, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_DISPLAY_LINES, -1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Up, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_DISPLAY_LINES, -1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_Down, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_DISPLAY_LINES, 1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Down, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_DISPLAY_LINES, 1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_Page_Up, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_PAGES, -1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Page_Up, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_PAGES, -1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_Page_Down, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_PAGES, 1);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (widget_class, GDK_KEY_KP_Page_Down, 0,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_MOVEMENT_PAGES, 1);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_widget_class_add_binding_signal (widget_class,
|
|
|
|
|
GDK_KEY_space, GDK_CONTROL_MASK,
|
|
|
|
|
"toggle-cursor-row",
|
|
|
|
|
NULL);
|
|
|
|
|
gtk_widget_class_add_binding_signal (widget_class,
|
|
|
|
|
GDK_KEY_KP_Space, GDK_CONTROL_MASK,
|
|
|
|
|
"toggle-cursor-row",
|
|
|
|
|
NULL);
|
|
|
|
|
|
|
|
|
|
gtk_widget_class_add_binding_signal (widget_class,
|
|
|
|
|
GDK_KEY_a, GDK_CONTROL_MASK,
|
|
|
|
|
"select-all",
|
|
|
|
|
NULL);
|
|
|
|
|
gtk_widget_class_add_binding_signal (widget_class,
|
|
|
|
|
GDK_KEY_a, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
|
|
|
|
|
"unselect-all",
|
|
|
|
|
NULL);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
|
2020-03-19 22:03:16 +00:00
|
|
|
|
gtk_widget_class_set_css_name (widget_class, I_("list"));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-12-12 04:48:34 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_init (GtkListBox *box)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *widget = GTK_WIDGET (box);
|
2018-03-09 05:14:59 +00:00
|
|
|
|
GtkGesture *gesture;
|
2015-12-12 04:48:34 +00:00
|
|
|
|
|
2020-05-05 18:57:21 +00:00
|
|
|
|
gtk_widget_set_focusable (GTK_WIDGET (box), TRUE);
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->selection_mode = GTK_SELECTION_SINGLE;
|
|
|
|
|
box->activate_single_click = TRUE;
|
2015-12-12 04:48:34 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->children = g_sequence_new (NULL);
|
|
|
|
|
box->header_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
|
2019-05-29 17:10:46 +00:00
|
|
|
|
gesture = gtk_gesture_click_new ();
|
2018-03-09 05:14:59 +00:00
|
|
|
|
gtk_event_controller_set_propagation_phase (GTK_EVENT_CONTROLLER (gesture),
|
2015-12-12 04:48:34 +00:00
|
|
|
|
GTK_PHASE_BUBBLE);
|
2018-03-09 05:14:59 +00:00
|
|
|
|
gtk_gesture_single_set_touch_only (GTK_GESTURE_SINGLE (gesture),
|
2015-12-12 04:48:34 +00:00
|
|
|
|
FALSE);
|
2018-03-09 05:14:59 +00:00
|
|
|
|
gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (gesture),
|
2015-12-12 04:48:34 +00:00
|
|
|
|
GDK_BUTTON_PRIMARY);
|
2018-03-09 05:14:59 +00:00
|
|
|
|
g_signal_connect (gesture, "pressed",
|
2019-05-29 17:10:46 +00:00
|
|
|
|
G_CALLBACK (gtk_list_box_click_gesture_pressed), box);
|
2018-03-09 05:14:59 +00:00
|
|
|
|
g_signal_connect (gesture, "released",
|
2019-05-29 17:10:46 +00:00
|
|
|
|
G_CALLBACK (gtk_list_box_click_gesture_released), box);
|
2018-03-09 05:14:59 +00:00
|
|
|
|
g_signal_connect (gesture, "stopped",
|
2019-05-29 17:10:46 +00:00
|
|
|
|
G_CALLBACK (gtk_list_box_click_gesture_stopped), box);
|
2018-03-09 05:14:59 +00:00
|
|
|
|
g_signal_connect (gesture, "unpaired-release",
|
2019-05-29 17:10:46 +00:00
|
|
|
|
G_CALLBACK (gtk_list_box_click_unpaired_release), box);
|
2018-03-09 05:14:59 +00:00
|
|
|
|
gtk_widget_add_controller (widget, GTK_EVENT_CONTROLLER (gesture));
|
2017-10-29 15:10:12 +00:00
|
|
|
|
|
|
|
|
|
g_signal_connect (box, "notify::parent", G_CALLBACK (gtk_list_box_parent_cb), NULL);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_get_selected_row:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*
|
|
|
|
|
* Gets the selected row.
|
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* Note that the box may allow multiple selection, in which
|
|
|
|
|
* case you should use gtk_list_box_selected_foreach() to
|
|
|
|
|
* find all selected rows.
|
|
|
|
|
*
|
|
|
|
|
* Returns: (transfer none): the selected row
|
2013-06-15 16:13:28 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkListBoxRow *
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_selected_row (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX (box), NULL);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
return box->selected_row;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_get_row_at_index:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-15 16:13:28 +00:00
|
|
|
|
* @index_: the index of the row
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* Gets the n-th child in the list (not counting headers).
|
2014-05-04 17:38:31 +00:00
|
|
|
|
* If @_index is negative or larger than the number of items in the
|
|
|
|
|
* list, %NULL is returned.
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*
|
2015-12-01 12:40:46 +00:00
|
|
|
|
* Returns: (transfer none) (nullable): the child #GtkWidget or %NULL
|
2013-06-15 16:13:28 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkListBoxRow *
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_row_at_index (GtkListBox *box,
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int index_)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX (box), NULL);
|
2013-09-28 01:39:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
iter = g_sequence_get_iter_at_pos (box->children, index_);
|
2014-05-04 17:38:31 +00:00
|
|
|
|
if (!g_sequence_iter_is_end (iter))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return g_sequence_get (iter);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2016-01-06 09:58:08 +00:00
|
|
|
|
static int
|
|
|
|
|
row_y_cmp_func (gconstpointer a,
|
|
|
|
|
gconstpointer b,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
int y = GPOINTER_TO_INT (b);
|
|
|
|
|
GtkListBoxRowPrivate *row_priv = ROW_PRIV (a);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (y < row_priv->y)
|
|
|
|
|
return 1;
|
|
|
|
|
else if (y >= row_priv->y + row_priv->height)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_get_row_at_y:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-10 09:17:10 +00:00
|
|
|
|
* @y: position
|
|
|
|
|
*
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* Gets the row at the @y position.
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*
|
2016-01-06 09:46:29 +00:00
|
|
|
|
* Returns: (transfer none) (nullable): the row or %NULL
|
|
|
|
|
* in case no row exists for the given y coordinate.
|
2013-06-15 16:13:28 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkListBoxRow *
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_row_at_y (GtkListBox *box,
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int y)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX (box), NULL);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
iter = g_sequence_lookup (box->children,
|
2016-01-06 09:58:08 +00:00
|
|
|
|
GINT_TO_POINTER (y),
|
|
|
|
|
row_y_cmp_func,
|
|
|
|
|
NULL);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2016-01-06 09:58:08 +00:00
|
|
|
|
if (iter)
|
|
|
|
|
return GTK_LIST_BOX_ROW (g_sequence_get (iter));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2016-01-06 09:58:08 +00:00
|
|
|
|
return NULL;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_select_row:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-10 09:17:10 +00:00
|
|
|
|
* @row: (allow-none): The row to select or %NULL
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* Make @row the currently selected row.
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_select_row (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxRow *row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-05-08 09:42:56 +00:00
|
|
|
|
gboolean dirty = FALSE;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2014-03-28 23:21:57 +00:00
|
|
|
|
g_return_if_fail (row == NULL || GTK_IS_LIST_BOX_ROW (row));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
if (row)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_select_row_internal (box, row);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
else
|
2014-05-08 09:42:56 +00:00
|
|
|
|
dirty = gtk_list_box_unselect_all_internal (box);
|
|
|
|
|
|
|
|
|
|
if (dirty)
|
|
|
|
|
{
|
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, NULL);
|
|
|
|
|
g_signal_emit (box, signals[SELECTED_ROWS_CHANGED], 0);
|
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2018-02-05 19:13:20 +00:00
|
|
|
|
/**
|
2014-03-28 23:21:57 +00:00
|
|
|
|
* gtk_list_box_unselect_row:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2014-03-28 23:21:57 +00:00
|
|
|
|
* @row: the row to unselected
|
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* Unselects a single row of @box, if the selection mode allows it.
|
2018-02-05 19:13:20 +00:00
|
|
|
|
*/
|
2014-03-28 23:21:57 +00:00
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_unselect_row (GtkListBox *box,
|
2014-03-28 23:21:57 +00:00
|
|
|
|
GtkListBoxRow *row)
|
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2014-03-28 23:21:57 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX_ROW (row));
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_unselect_row_internal (box, row);
|
2014-03-28 23:21:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_select_all:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2014-03-28 23:21:57 +00:00
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* Select all children of @box, if the selection mode allows it.
|
2014-03-28 23:21:57 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_select_all (GtkListBox *box)
|
2014-03-28 23:21:57 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->selection_mode != GTK_SELECTION_MULTIPLE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (g_sequence_get_length (box->children) > 0)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_select_all_between (box, NULL, NULL, FALSE);
|
|
|
|
|
g_signal_emit (box, signals[SELECTED_ROWS_CHANGED], 0);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
}
|
2014-03-28 23:21:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_unselect_all:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2014-03-28 23:21:57 +00:00
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* Unselect all children of @box, if the selection mode allows it.
|
2014-03-28 23:21:57 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_unselect_all (GtkListBox *box)
|
2014-03-28 23:21:57 +00:00
|
|
|
|
{
|
2014-03-29 01:34:39 +00:00
|
|
|
|
gboolean dirty = FALSE;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2014-03-28 23:21:57 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->selection_mode == GTK_SELECTION_BROWSE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
dirty = gtk_list_box_unselect_all_internal (box);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
|
|
|
|
if (dirty)
|
2014-05-08 09:42:56 +00:00
|
|
|
|
{
|
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, NULL);
|
|
|
|
|
g_signal_emit (box, signals[SELECTED_ROWS_CHANGED], 0);
|
|
|
|
|
}
|
2014-03-29 01:34:39 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_selected_rows_changed (GtkListBox *box)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
{
|
2014-03-28 23:21:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* GtkListBoxForeachFunc:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2014-03-28 23:21:57 +00:00
|
|
|
|
* @row: a #GtkListBoxRow
|
|
|
|
|
* @user_data: (closure): user data
|
|
|
|
|
*
|
|
|
|
|
* A function used by gtk_list_box_selected_foreach().
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* It will be called on every selected child of the @box.
|
2014-03-28 23:21:57 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_selected_foreach:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2014-03-28 23:21:57 +00:00
|
|
|
|
* @func: (scope call): the function to call for each selected child
|
|
|
|
|
* @data: user data to pass to the function
|
|
|
|
|
*
|
|
|
|
|
* Calls a function for each selected child.
|
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* Note that the selection cannot be modified from within this function.
|
2014-03-28 23:21:57 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_selected_foreach (GtkListBox *box,
|
2014-03-28 23:21:57 +00:00
|
|
|
|
GtkListBoxForeachFunc func,
|
|
|
|
|
gpointer data)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2014-03-28 23:21:57 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2014-03-28 23:21:57 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
if (gtk_list_box_row_is_selected (row))
|
2014-03-29 03:07:41 +00:00
|
|
|
|
(*func) (box, row, data);
|
2014-03-28 23:21:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2014-04-13 20:55:28 +00:00
|
|
|
|
* gtk_list_box_get_selected_rows:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2014-03-28 23:21:57 +00:00
|
|
|
|
*
|
|
|
|
|
* Creates a list of all selected children.
|
|
|
|
|
*
|
|
|
|
|
* Returns: (element-type GtkListBoxRow) (transfer container):
|
|
|
|
|
* A #GList containing the #GtkWidget for each selected child.
|
|
|
|
|
* Free with g_list_free() when done.
|
|
|
|
|
*/
|
|
|
|
|
GList *
|
2014-04-13 20:55:28 +00:00
|
|
|
|
gtk_list_box_get_selected_rows (GtkListBox *box)
|
2014-03-28 23:21:57 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
GList *selected = NULL;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX (box), NULL);
|
2014-03-28 23:21:57 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2014-03-28 23:21:57 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
if (gtk_list_box_row_is_selected (row))
|
|
|
|
|
selected = g_list_prepend (selected, row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return g_list_reverse (selected);
|
|
|
|
|
}
|
2013-06-11 14:53:31 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_set_placeholder:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 14:53:31 +00:00
|
|
|
|
* @placeholder: (allow-none): a #GtkWidget or %NULL
|
|
|
|
|
*
|
|
|
|
|
* Sets the placeholder widget that is shown in the list when
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* it doesn't display any visible children.
|
2013-06-11 14:53:31 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_set_placeholder (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkWidget *placeholder)
|
2013-06-11 14:53:31 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-11 14:53:31 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->placeholder)
|
2013-06-11 14:53:31 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
gtk_widget_unparent (box->placeholder);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (box));
|
2013-06-11 14:53:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->placeholder = placeholder;
|
2013-06-11 14:53:31 +00:00
|
|
|
|
|
|
|
|
|
if (placeholder)
|
|
|
|
|
{
|
2016-11-06 13:52:56 +00:00
|
|
|
|
gtk_widget_set_parent (placeholder, GTK_WIDGET (box));
|
|
|
|
|
gtk_widget_set_child_visible (placeholder,
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->n_visible_rows == 0);
|
2013-06-11 14:53:31 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_set_adjustment:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-15 16:13:28 +00:00
|
|
|
|
* @adjustment: (allow-none): the adjustment, or %NULL
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* Sets the adjustment (if any) that the widget uses to
|
|
|
|
|
* for vertical scrolling. For instance, this is used
|
|
|
|
|
* to get the page size for PageUp/Down key handling.
|
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* In the normal case when the @box is packed inside
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* a #GtkScrolledWindow the adjustment from that will
|
|
|
|
|
* be picked up automatically, so there is no need
|
|
|
|
|
* to manually do that.
|
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_set_adjustment (GtkListBox *box,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkAdjustment *adjustment)
|
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2015-07-20 02:08:36 +00:00
|
|
|
|
g_return_if_fail (adjustment == NULL || GTK_IS_ADJUSTMENT (adjustment));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2015-07-20 02:08:36 +00:00
|
|
|
|
if (adjustment)
|
|
|
|
|
g_object_ref_sink (adjustment);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->adjustment)
|
|
|
|
|
g_object_unref (box->adjustment);
|
|
|
|
|
box->adjustment = adjustment;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_get_adjustment:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* Gets the adjustment (if any) that the widget uses to
|
|
|
|
|
* for vertical scrolling.
|
|
|
|
|
*
|
2014-02-19 23:49:43 +00:00
|
|
|
|
* Returns: (transfer none): the adjustment
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkAdjustment *
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_adjustment (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX (box), NULL);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
return box->adjustment;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-07-20 02:08:36 +00:00
|
|
|
|
static void
|
|
|
|
|
adjustment_changed (GObject *object,
|
|
|
|
|
GParamSpec *pspec,
|
|
|
|
|
gpointer data)
|
|
|
|
|
{
|
|
|
|
|
GtkAdjustment *adjustment;
|
|
|
|
|
|
|
|
|
|
adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (object));
|
|
|
|
|
gtk_list_box_set_adjustment (GTK_LIST_BOX (data), adjustment);
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 14:07:20 +00:00
|
|
|
|
static void
|
2017-10-29 15:10:12 +00:00
|
|
|
|
gtk_list_box_parent_cb (GObject *object,
|
|
|
|
|
GParamSpec *pspec,
|
|
|
|
|
gpointer user_data)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
GtkListBox *box = GTK_LIST_BOX (object);
|
2013-06-10 14:07:20 +00:00
|
|
|
|
GtkWidget *parent;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2017-10-29 15:10:12 +00:00
|
|
|
|
parent = gtk_widget_get_parent (GTK_WIDGET (object));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->adjustment_changed_id != 0 &&
|
|
|
|
|
box->scrollable_parent != NULL)
|
2017-10-29 15:10:12 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_signal_handler_disconnect (box->scrollable_parent,
|
|
|
|
|
box->adjustment_changed_id);
|
2017-10-29 15:10:12 +00:00
|
|
|
|
}
|
2015-07-20 02:08:36 +00:00
|
|
|
|
|
2013-06-10 14:07:20 +00:00
|
|
|
|
if (parent && GTK_IS_SCROLLABLE (parent))
|
|
|
|
|
{
|
2017-10-29 15:10:12 +00:00
|
|
|
|
adjustment_changed (G_OBJECT (parent), NULL, object);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->scrollable_parent = parent;
|
|
|
|
|
box->adjustment_changed_id = g_signal_connect (parent, "notify::vadjustment",
|
2017-10-29 15:10:12 +00:00
|
|
|
|
G_CALLBACK (adjustment_changed), object);
|
2013-06-10 14:07:20 +00:00
|
|
|
|
}
|
2015-07-20 02:08:36 +00:00
|
|
|
|
else
|
2017-10-29 15:10:12 +00:00
|
|
|
|
{
|
|
|
|
|
gtk_list_box_set_adjustment (GTK_LIST_BOX (object), NULL);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->adjustment_changed_id = 0;
|
|
|
|
|
box->scrollable_parent = NULL;
|
2017-10-29 15:10:12 +00:00
|
|
|
|
}
|
2013-06-10 14:07:20 +00:00
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_set_selection_mode:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @mode: The #GtkSelectionMode
|
|
|
|
|
*
|
|
|
|
|
* Sets how selection works in the listbox.
|
|
|
|
|
* See #GtkSelectionMode for details.
|
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_set_selection_mode (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkSelectionMode mode)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 01:34:39 +00:00
|
|
|
|
gboolean dirty = FALSE;
|
2013-09-28 01:39:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->selection_mode == mode)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
return;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
if (mode == GTK_SELECTION_NONE ||
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->selection_mode == GTK_SELECTION_MULTIPLE)
|
2015-07-26 06:40:26 +00:00
|
|
|
|
dirty = gtk_list_box_unselect_all_internal (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->selection_mode = mode;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-07-16 03:13:43 +00:00
|
|
|
|
gtk_list_box_update_row_styles (box);
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (box), properties[PROP_SELECTION_MODE]);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
|
|
|
|
if (dirty)
|
2014-04-13 20:51:49 +00:00
|
|
|
|
{
|
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, NULL);
|
|
|
|
|
g_signal_emit (box, signals[SELECTED_ROWS_CHANGED], 0);
|
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_get_selection_mode:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* Gets the selection mode of the listbox.
|
|
|
|
|
*
|
|
|
|
|
* Returns: a #GtkSelectionMode
|
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkSelectionMode
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_selection_mode (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX (box), GTK_SELECTION_NONE);
|
2013-09-28 01:39:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
return box->selection_mode;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_set_filter_func:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2020-01-13 01:11:15 +00:00
|
|
|
|
* @filter_func: (allow-none): callback that lets you filter which rows to show
|
|
|
|
|
* @user_data: (closure): user data passed to @filter_func
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @destroy: destroy notifier for @user_data
|
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* By setting a filter function on the @box one can decide dynamically which
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* of the rows to show. For instance, to implement a search function on a list that
|
|
|
|
|
* filters the original list to only show the matching rows.
|
|
|
|
|
*
|
|
|
|
|
* The @filter_func will be called for each row after the call, and it will
|
|
|
|
|
* continue to be called each time a row changes (via gtk_list_box_row_changed()) or
|
2013-06-11 14:58:22 +00:00
|
|
|
|
* when gtk_list_box_invalidate_filter() is called.
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
2015-08-25 17:20:53 +00:00
|
|
|
|
* Note that using a filter function is incompatible with using a model
|
|
|
|
|
* (see gtk_list_box_bind_model()).
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_set_filter_func (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxFilterFunc filter_func,
|
|
|
|
|
gpointer user_data,
|
|
|
|
|
GDestroyNotify destroy)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->filter_func_target_destroy_notify != NULL)
|
|
|
|
|
box->filter_func_target_destroy_notify (box->filter_func_target);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->filter_func = filter_func;
|
|
|
|
|
box->filter_func_target = user_data;
|
|
|
|
|
box->filter_func_target_destroy_notify = destroy;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2015-08-25 17:16:24 +00:00
|
|
|
|
gtk_list_box_check_model_compat (box);
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_invalidate_filter (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* gtk_list_box_set_header_func:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2020-01-13 01:11:15 +00:00
|
|
|
|
* @update_header: (allow-none): callback that lets you add row headers
|
|
|
|
|
* @user_data: (closure): user data passed to @update_header
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @destroy: destroy notifier for @user_data
|
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* By setting a header function on the @box one can dynamically add headers
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* in front of rows, depending on the contents of the row and its position in the list.
|
|
|
|
|
* For instance, one could use it to add headers in front of the first item of a
|
|
|
|
|
* new kind, in a list sorted by the kind.
|
|
|
|
|
*
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* The @update_header can look at the current header widget using gtk_list_box_row_get_header()
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* and either update the state of the widget as needed, or set a new one using
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* gtk_list_box_row_set_header(). If no header is needed, set the header to %NULL.
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* Note that you may get many calls @update_header to this for a particular row when e.g.
|
2014-02-07 18:32:47 +00:00
|
|
|
|
* changing things that don’t affect the header. In this case it is important for performance
|
2014-07-12 18:46:00 +00:00
|
|
|
|
* to not blindly replace an existing header with an identical one.
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* The @update_header function will be called for each row after the call, and it will
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* continue to be called each time a row changes (via gtk_list_box_row_changed()) and when
|
|
|
|
|
* the row before changes (either by gtk_list_box_row_changed() on the previous row, or when
|
|
|
|
|
* the previous row becomes a different row). It is also called for all rows when
|
2013-06-11 14:58:22 +00:00
|
|
|
|
* gtk_list_box_invalidate_headers() is called.
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_set_header_func (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxUpdateHeaderFunc update_header,
|
|
|
|
|
gpointer user_data,
|
|
|
|
|
GDestroyNotify destroy)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->update_header_func_target_destroy_notify != NULL)
|
|
|
|
|
box->update_header_func_target_destroy_notify (box->update_header_func_target);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->update_header_func = update_header;
|
|
|
|
|
box->update_header_func_target = user_data;
|
|
|
|
|
box->update_header_func_target_destroy_notify = destroy;
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_invalidate_headers (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
2013-06-11 14:58:22 +00:00
|
|
|
|
* gtk_list_box_invalidate_filter:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* Update the filtering for all rows. Call this when result
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* of the filter function on the @box is changed due
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* to an external factor. For instance, this would be used
|
|
|
|
|
* if the filter function just looked for a specific search
|
|
|
|
|
* string and the entry with the search string has changed.
|
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_invalidate_filter (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_apply_filter_all (box);
|
|
|
|
|
gtk_list_box_invalidate_headers (box);
|
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 13:54:49 +00:00
|
|
|
|
static int
|
2013-06-10 09:17:10 +00:00
|
|
|
|
do_sort (GtkListBoxRow *a,
|
|
|
|
|
GtkListBoxRow *b,
|
2014-03-29 03:07:41 +00:00
|
|
|
|
GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
return box->sort_func (a, b, box->sort_func_target);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-05-30 03:03:11 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_css_node_foreach (gpointer data,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget **previous = user_data;
|
|
|
|
|
GtkWidget *row = data;
|
|
|
|
|
GtkCssNode *row_node;
|
|
|
|
|
GtkCssNode *prev_node;
|
|
|
|
|
|
|
|
|
|
if (*previous)
|
|
|
|
|
{
|
|
|
|
|
prev_node = gtk_widget_get_css_node (*previous);
|
|
|
|
|
row_node = gtk_widget_get_css_node (row);
|
2015-07-12 19:39:49 +00:00
|
|
|
|
gtk_css_node_insert_after (gtk_css_node_get_parent (row_node),
|
|
|
|
|
row_node,
|
|
|
|
|
prev_node);
|
2015-05-30 03:03:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*previous = row;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
2013-06-11 14:58:22 +00:00
|
|
|
|
* gtk_list_box_invalidate_sort:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* Update the sorting for all rows. Call this when result
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* of the sort function on the @box is changed due
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* to an external factor.
|
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_invalidate_sort (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2015-05-30 03:03:11 +00:00
|
|
|
|
GtkWidget *previous = NULL;
|
2013-09-28 01:39:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->sort_func == NULL)
|
2016-06-20 22:17:00 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_sequence_sort (box->children, (GCompareDataFunc)do_sort, box);
|
|
|
|
|
g_sequence_foreach (box->children, gtk_list_box_css_node_foreach, &previous);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_invalidate_headers (box);
|
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 14:04:58 +00:00
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_do_reseparate (GtkListBox *box)
|
2013-06-11 14:04:58 +00:00
|
|
|
|
{
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2013-06-11 14:04:58 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_update_header (box, iter);
|
2013-06-11 14:04:58 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (box));
|
2013-06-11 14:04:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
2013-06-11 14:58:22 +00:00
|
|
|
|
* gtk_list_box_invalidate_headers:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* Update the separators for all rows. Call this when result
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* of the header function on the @box is changed due
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* to an external factor.
|
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_invalidate_headers (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
if (!gtk_widget_get_visible (GTK_WIDGET (box)))
|
2013-06-11 14:04:58 +00:00
|
|
|
|
return;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_do_reseparate (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_set_sort_func:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2020-01-13 01:11:15 +00:00
|
|
|
|
* @sort_func: (allow-none): the sort function
|
|
|
|
|
* @user_data: (closure): user data passed to @sort_func
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @destroy: destroy notifier for @user_data
|
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* By setting a sort function on the @box one can dynamically reorder the rows
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* of the list, based on the contents of the rows.
|
|
|
|
|
*
|
|
|
|
|
* The @sort_func will be called for each row after the call, and will continue to
|
|
|
|
|
* be called each time a row changes (via gtk_list_box_row_changed()) and when
|
2013-06-11 14:58:22 +00:00
|
|
|
|
* gtk_list_box_invalidate_sort() is called.
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
2015-08-25 17:20:53 +00:00
|
|
|
|
* Note that using a sort function is incompatible with using a model
|
|
|
|
|
* (see gtk_list_box_bind_model()).
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_set_sort_func (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxSortFunc sort_func,
|
|
|
|
|
gpointer user_data,
|
|
|
|
|
GDestroyNotify destroy)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->sort_func_target_destroy_notify != NULL)
|
|
|
|
|
box->sort_func_target_destroy_notify (box->sort_func_target);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->sort_func = sort_func;
|
|
|
|
|
box->sort_func_target = user_data;
|
|
|
|
|
box->sort_func_target_destroy_notify = destroy;
|
2013-09-28 01:39:10 +00:00
|
|
|
|
|
2015-08-25 17:16:24 +00:00
|
|
|
|
gtk_list_box_check_model_compat (box);
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_invalidate_sort (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_got_row_changed (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxRow *row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
GtkListBoxRowPrivate *row_priv = ROW_PRIV (row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GSequenceIter *prev_next, *next;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-09-28 01:39:10 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX_ROW (row));
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
prev_next = gtk_list_box_get_next_visible (box, row_priv->iter);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->sort_func != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2013-07-02 11:44:17 +00:00
|
|
|
|
g_sequence_sort_changed (row_priv->iter,
|
2013-06-10 09:17:10 +00:00
|
|
|
|
(GCompareDataFunc)do_sort,
|
2014-03-29 03:07:41 +00:00
|
|
|
|
box);
|
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_apply_filter (box, row);
|
|
|
|
|
if (gtk_widget_get_visible (GTK_WIDGET (box)))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
next = gtk_list_box_get_next_visible (box, row_priv->iter);
|
|
|
|
|
gtk_list_box_update_header (box, row_priv->iter);
|
|
|
|
|
gtk_list_box_update_header (box, next);
|
|
|
|
|
gtk_list_box_update_header (box, prev_next);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_set_activate_on_single_click:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @single: a boolean
|
|
|
|
|
*
|
2013-06-15 16:13:28 +00:00
|
|
|
|
* If @single is %TRUE, rows will be activated when you click on them,
|
|
|
|
|
* otherwise you need to double-click.
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_set_activate_on_single_click (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
gboolean single)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
single = single != FALSE;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->activate_single_click == single)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->activate_single_click = single;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (box), properties[PROP_ACTIVATE_ON_SINGLE_CLICK]);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-12 07:40:06 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_get_activate_on_single_click:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-12 07:40:06 +00:00
|
|
|
|
*
|
|
|
|
|
* Returns whether rows activate on single clicks.
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE if rows are activated on single click, %FALSE otherwise
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_activate_on_single_click (GtkListBox *box)
|
2013-06-12 07:40:06 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX (box), FALSE);
|
2013-06-12 07:40:06 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
return box->activate_single_click;
|
2013-06-12 07:40:06 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-12-31 04:23:16 +00:00
|
|
|
|
void
|
|
|
|
|
gtk_list_box_set_accept_unpaired_release (GtkListBox *box,
|
|
|
|
|
gboolean accept)
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->accept_unpaired_release == accept)
|
2017-12-31 04:23:16 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->accept_unpaired_release = accept;
|
2017-12-31 04:23:16 +00:00
|
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (box), properties[PROP_ACCEPT_UNPAIRED_RELEASE]);
|
|
|
|
|
}
|
2013-06-12 07:40:06 +00:00
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_list_box_add_move_binding (GtkWidgetClass *widget_class,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
guint keyval,
|
|
|
|
|
GdkModifierType modmask,
|
|
|
|
|
GtkMovementStep step,
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int count)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_widget_class_add_binding_signal (widget_class,
|
|
|
|
|
keyval, modmask,
|
|
|
|
|
"move-cursor",
|
2020-04-11 18:52:43 +00:00
|
|
|
|
"(iibb)", step, count, FALSE, FALSE);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_widget_class_add_binding_signal (widget_class,
|
2020-04-11 18:52:43 +00:00
|
|
|
|
keyval, modmask | GDK_SHIFT_MASK,
|
2018-08-12 14:47:58 +00:00
|
|
|
|
"move-cursor",
|
2020-04-11 18:52:43 +00:00
|
|
|
|
"(iibb)", step, count, TRUE, FALSE);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_widget_class_add_binding_signal (widget_class,
|
2020-04-11 18:52:43 +00:00
|
|
|
|
keyval, modmask | GDK_CONTROL_MASK,
|
2018-08-12 14:47:58 +00:00
|
|
|
|
"move-cursor",
|
2020-04-11 18:52:43 +00:00
|
|
|
|
"(iibb)", step, count, FALSE, TRUE);
|
2018-08-12 14:47:58 +00:00
|
|
|
|
gtk_widget_class_add_binding_signal (widget_class,
|
2020-04-11 18:52:43 +00:00
|
|
|
|
keyval, modmask | GDK_SHIFT_MASK | GDK_CONTROL_MASK,
|
|
|
|
|
"move-cursor",
|
|
|
|
|
"(iibb)", step, count, TRUE, TRUE);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 16:31:28 +00:00
|
|
|
|
static void
|
|
|
|
|
ensure_row_visible (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row)
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *header;
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int y, height;
|
2018-04-07 11:37:24 +00:00
|
|
|
|
graphene_rect_t rect;
|
2014-07-13 16:31:28 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (!box->adjustment)
|
2014-07-13 19:51:03 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2019-02-20 03:53:47 +00:00
|
|
|
|
if (!gtk_widget_compute_bounds (GTK_WIDGET (row), GTK_WIDGET (box), &rect))
|
|
|
|
|
return;
|
|
|
|
|
|
2018-04-07 11:37:24 +00:00
|
|
|
|
y = rect.origin.y;
|
|
|
|
|
height = rect.size.height;
|
2015-07-20 02:18:58 +00:00
|
|
|
|
|
2014-07-13 16:31:28 +00:00
|
|
|
|
/* If the row has a header, we want to ensure that it is visible as well. */
|
|
|
|
|
header = ROW_PRIV (row)->header;
|
|
|
|
|
if (GTK_IS_WIDGET (header) && gtk_widget_is_drawable (header))
|
2015-07-20 02:18:58 +00:00
|
|
|
|
{
|
2019-02-20 03:53:47 +00:00
|
|
|
|
if (gtk_widget_compute_bounds (header, GTK_WIDGET (box), &rect))
|
|
|
|
|
{
|
|
|
|
|
y = rect.origin.y;
|
|
|
|
|
height += rect.size.height;
|
|
|
|
|
}
|
2015-07-20 02:18:58 +00:00
|
|
|
|
}
|
2014-07-13 16:31:28 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
gtk_adjustment_clamp_page (box->adjustment, y, y + height);
|
2014-07-13 16:31:28 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_update_cursor (GtkListBox *box,
|
2015-02-10 19:02:08 +00:00
|
|
|
|
GtkListBoxRow *row,
|
|
|
|
|
gboolean grab_focus)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->cursor_row = row;
|
2015-02-10 19:02:08 +00:00
|
|
|
|
ensure_row_visible (box, row);
|
|
|
|
|
if (grab_focus)
|
2020-05-13 23:43:04 +00:00
|
|
|
|
{
|
|
|
|
|
GtkWidget *focus;
|
|
|
|
|
|
|
|
|
|
focus = gtk_root_get_focus (gtk_widget_get_root (GTK_WIDGET (box)));
|
|
|
|
|
if (!focus || !gtk_widget_is_ancestor (focus, GTK_WIDGET (row)))
|
|
|
|
|
gtk_widget_grab_focus (GTK_WIDGET (row));
|
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (row));
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
static GtkListBox *
|
|
|
|
|
gtk_list_box_row_get_box (GtkListBoxRow *row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 01:34:39 +00:00
|
|
|
|
GtkWidget *parent;
|
|
|
|
|
|
|
|
|
|
parent = gtk_widget_get_parent (GTK_WIDGET (row));
|
|
|
|
|
if (parent && GTK_IS_LIST_BOX (parent))
|
|
|
|
|
return GTK_LIST_BOX (parent);
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
row_is_visible (GtkListBoxRow *row)
|
|
|
|
|
{
|
|
|
|
|
return ROW_PRIV (row)->visible;
|
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
static gboolean
|
|
|
|
|
gtk_list_box_row_set_selected (GtkListBoxRow *row,
|
|
|
|
|
gboolean selected)
|
|
|
|
|
{
|
2014-07-16 03:14:28 +00:00
|
|
|
|
if (!ROW_PRIV (row)->selectable)
|
|
|
|
|
return FALSE;
|
|
|
|
|
|
2014-03-29 05:03:06 +00:00
|
|
|
|
if (ROW_PRIV (row)->selected != selected)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 05:03:06 +00:00
|
|
|
|
ROW_PRIV (row)->selected = selected;
|
2014-03-29 01:34:39 +00:00
|
|
|
|
if (selected)
|
|
|
|
|
gtk_widget_set_state_flags (GTK_WIDGET (row),
|
|
|
|
|
GTK_STATE_FLAG_SELECTED, FALSE);
|
|
|
|
|
else
|
|
|
|
|
gtk_widget_unset_state_flags (GTK_WIDGET (row),
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GTK_STATE_FLAG_SELECTED);
|
2014-03-28 23:21:57 +00:00
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
return TRUE;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
|
gtk_list_box_unselect_all_internal (GtkListBox *box)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
gboolean dirty = FALSE;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->selection_mode == GTK_SELECTION_NONE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
dirty |= gtk_list_box_row_set_selected (row, FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->selected_row = NULL;
|
2014-11-01 02:34:09 +00:00
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
return dirty;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_unselect_row_internal (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row)
|
|
|
|
|
{
|
|
|
|
|
if (!ROW_PRIV (row)->selected)
|
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->selection_mode == GTK_SELECTION_NONE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
return;
|
2020-03-28 20:52:41 +00:00
|
|
|
|
else if (box->selection_mode != GTK_SELECTION_MULTIPLE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
gtk_list_box_unselect_all_internal (box);
|
|
|
|
|
else
|
|
|
|
|
gtk_list_box_row_set_selected (row, FALSE);
|
|
|
|
|
|
2014-05-08 09:42:56 +00:00
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, NULL);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
g_signal_emit (box, signals[SELECTED_ROWS_CHANGED], 0);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-29 01:34:39 +00:00
|
|
|
|
gtk_list_box_select_row_internal (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxRow *row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-07-16 03:14:28 +00:00
|
|
|
|
if (!ROW_PRIV (row)->selectable)
|
|
|
|
|
return;
|
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
if (ROW_PRIV (row)->selected)
|
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->selection_mode == GTK_SELECTION_NONE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
return;
|
2014-07-16 03:14:28 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->selection_mode != GTK_SELECTION_MULTIPLE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
gtk_list_box_unselect_all_internal (box);
|
|
|
|
|
|
|
|
|
|
gtk_list_box_row_set_selected (row, TRUE);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->selected_row = row;
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
2014-04-08 03:17:56 +00:00
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, row);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
g_signal_emit (box, signals[SELECTED_ROWS_CHANGED], 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_select_all_between (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row1,
|
|
|
|
|
GtkListBoxRow *row2,
|
|
|
|
|
gboolean modify)
|
|
|
|
|
{
|
|
|
|
|
GSequenceIter *iter, *iter1, *iter2;
|
|
|
|
|
|
|
|
|
|
if (row1)
|
|
|
|
|
iter1 = ROW_PRIV (row1)->iter;
|
|
|
|
|
else
|
2020-03-28 20:52:41 +00:00
|
|
|
|
iter1 = g_sequence_get_begin_iter (box->children);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
|
|
|
|
if (row2)
|
|
|
|
|
iter2 = ROW_PRIV (row2)->iter;
|
|
|
|
|
else
|
2020-03-28 20:52:41 +00:00
|
|
|
|
iter2 = g_sequence_get_end_iter (box->children);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
if (g_sequence_iter_compare (iter2, iter1) < 0)
|
|
|
|
|
{
|
|
|
|
|
iter = iter1;
|
|
|
|
|
iter1 = iter2;
|
|
|
|
|
iter2 = iter;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (iter = iter1;
|
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
|
|
|
|
|
row = GTK_LIST_BOX_ROW (g_sequence_get (iter));
|
|
|
|
|
if (row_is_visible (row))
|
|
|
|
|
{
|
|
|
|
|
if (modify)
|
|
|
|
|
gtk_list_box_row_set_selected (row, !ROW_PRIV (row)->selected);
|
|
|
|
|
else
|
|
|
|
|
gtk_list_box_row_set_selected (row, TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (g_sequence_iter_compare (iter, iter2) == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-23 20:43:11 +00:00
|
|
|
|
#define gtk_list_box_update_selection(b,r,m,e) \
|
|
|
|
|
gtk_list_box_update_selection_full((b), (r), (m), (e), TRUE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
static void
|
2015-10-23 20:43:11 +00:00
|
|
|
|
gtk_list_box_update_selection_full (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row,
|
|
|
|
|
gboolean modify,
|
|
|
|
|
gboolean extend,
|
|
|
|
|
gboolean grab_cursor)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
{
|
2015-10-23 20:43:11 +00:00
|
|
|
|
gtk_list_box_update_cursor (box, row, grab_cursor);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->selection_mode == GTK_SELECTION_NONE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2014-07-16 03:14:28 +00:00
|
|
|
|
if (!ROW_PRIV (row)->selectable)
|
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->selection_mode == GTK_SELECTION_BROWSE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
{
|
|
|
|
|
gtk_list_box_unselect_all_internal (box);
|
|
|
|
|
gtk_list_box_row_set_selected (row, TRUE);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->selected_row = row;
|
2019-01-01 23:14:17 +00:00
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, row);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
}
|
2020-03-28 20:52:41 +00:00
|
|
|
|
else if (box->selection_mode == GTK_SELECTION_SINGLE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
{
|
|
|
|
|
gboolean was_selected;
|
|
|
|
|
|
|
|
|
|
was_selected = ROW_PRIV (row)->selected;
|
|
|
|
|
gtk_list_box_unselect_all_internal (box);
|
|
|
|
|
gtk_list_box_row_set_selected (row, modify ? !was_selected : TRUE);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->selected_row = ROW_PRIV (row)->selected ? row : NULL;
|
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, box->selected_row);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
}
|
|
|
|
|
else /* GTK_SELECTION_MULTIPLE */
|
|
|
|
|
{
|
|
|
|
|
if (extend)
|
|
|
|
|
{
|
2015-07-26 06:40:26 +00:00
|
|
|
|
GtkListBoxRow *selected_row;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
selected_row = box->selected_row;
|
2015-07-26 06:40:26 +00:00
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
gtk_list_box_unselect_all_internal (box);
|
2015-07-26 06:40:26 +00:00
|
|
|
|
|
|
|
|
|
if (selected_row == NULL)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
{
|
|
|
|
|
gtk_list_box_row_set_selected (row, TRUE);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->selected_row = row;
|
2014-05-08 09:42:56 +00:00
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, row);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2015-07-26 06:40:26 +00:00
|
|
|
|
gtk_list_box_select_all_between (box, selected_row, row, FALSE);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (modify)
|
|
|
|
|
{
|
|
|
|
|
gtk_list_box_row_set_selected (row, !ROW_PRIV (row)->selected);
|
2014-05-08 09:42:56 +00:00
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, ROW_PRIV (row)->selected ? row
|
|
|
|
|
: NULL);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
gtk_list_box_unselect_all_internal (box);
|
|
|
|
|
gtk_list_box_row_set_selected (row, !ROW_PRIV (row)->selected);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->selected_row = row;
|
2014-05-08 09:42:56 +00:00
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, row);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_signal_emit (box, signals[SELECTED_ROWS_CHANGED], 0);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 00:37:41 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_activate (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row)
|
|
|
|
|
{
|
2017-11-24 13:26:39 +00:00
|
|
|
|
if (!gtk_list_box_row_get_activatable (row))
|
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (ROW_PRIV (row)->action_helper)
|
|
|
|
|
gtk_action_helper_activate (ROW_PRIV (row)->action_helper);
|
2017-11-24 13:26:39 +00:00
|
|
|
|
else
|
2014-07-13 00:37:41 +00:00
|
|
|
|
g_signal_emit (box, signals[ROW_ACTIVATED], 0, row);
|
|
|
|
|
}
|
|
|
|
|
|
2015-10-23 20:43:11 +00:00
|
|
|
|
#define gtk_list_box_select_and_activate(b,r) \
|
|
|
|
|
gtk_list_box_select_and_activate_full ((b), (r), TRUE)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
static void
|
2015-10-23 20:43:11 +00:00
|
|
|
|
gtk_list_box_select_and_activate_full (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row,
|
|
|
|
|
gboolean grab_focus)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
{
|
2013-06-10 09:17:10 +00:00
|
|
|
|
if (row != NULL)
|
2014-03-29 01:34:39 +00:00
|
|
|
|
{
|
|
|
|
|
gtk_list_box_select_row_internal (box, row);
|
2015-10-23 20:43:11 +00:00
|
|
|
|
gtk_list_box_update_cursor (box, row, grab_focus);
|
2014-07-13 00:37:41 +00:00
|
|
|
|
gtk_list_box_activate (box, row);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 13:40:02 +00:00
|
|
|
|
static void
|
2019-05-29 17:10:46 +00:00
|
|
|
|
gtk_list_box_click_gesture_pressed (GtkGestureClick *gesture,
|
|
|
|
|
guint n_press,
|
2020-07-24 20:32:16 +00:00
|
|
|
|
double x,
|
|
|
|
|
double y,
|
2019-05-29 17:10:46 +00:00
|
|
|
|
GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-06-05 13:40:02 +00:00
|
|
|
|
GtkListBoxRow *row;
|
2013-06-11 13:04:02 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->active_row = NULL;
|
2014-06-05 13:40:02 +00:00
|
|
|
|
row = gtk_list_box_get_row_at_y (box, y);
|
2013-06-17 12:52:05 +00:00
|
|
|
|
|
2014-06-05 13:40:02 +00:00
|
|
|
|
if (row != NULL && gtk_widget_is_sensitive (GTK_WIDGET (row)))
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->active_row = row;
|
2013-06-17 12:52:05 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (n_press == 2 && !box->activate_single_click)
|
2014-07-13 00:37:41 +00:00
|
|
|
|
gtk_list_box_activate (box, row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-27 00:52:01 +00:00
|
|
|
|
static void
|
2019-05-29 17:10:46 +00:00
|
|
|
|
gtk_list_box_click_unpaired_release (GtkGestureClick *gesture,
|
2020-07-24 20:32:16 +00:00
|
|
|
|
double x,
|
|
|
|
|
double y,
|
2019-05-29 17:10:46 +00:00
|
|
|
|
guint button,
|
|
|
|
|
GdkEventSequence *sequence,
|
|
|
|
|
GtkListBox *box)
|
2017-11-27 00:52:01 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (!box->activate_single_click || !box->accept_unpaired_release)
|
2017-11-27 00:52:01 +00:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
row = gtk_list_box_get_row_at_y (box, y);
|
|
|
|
|
|
|
|
|
|
if (row)
|
|
|
|
|
gtk_list_box_select_and_activate (box, row);
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-05 13:40:02 +00:00
|
|
|
|
static void
|
2019-05-29 17:10:46 +00:00
|
|
|
|
gtk_list_box_click_gesture_released (GtkGestureClick *gesture,
|
|
|
|
|
guint n_press,
|
2020-07-24 20:32:16 +00:00
|
|
|
|
double x,
|
|
|
|
|
double y,
|
2019-05-29 17:10:46 +00:00
|
|
|
|
GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 01:34:39 +00:00
|
|
|
|
/* Take a ref to protect against reentrancy
|
|
|
|
|
* (the activation may destroy the widget)
|
|
|
|
|
*/
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_object_ref (box);
|
2013-06-12 11:20:15 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->active_row != NULL &&
|
|
|
|
|
box->active_row == gtk_list_box_get_row_at_y (box, y))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
gboolean focus_on_click = gtk_widget_get_focus_on_click (GTK_WIDGET (box->active_row));
|
2015-10-23 20:43:11 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (n_press == 1 && box->activate_single_click)
|
|
|
|
|
gtk_list_box_select_and_activate_full (box, box->active_row, focus_on_click);
|
2014-06-05 13:40:02 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GdkEventSequence *sequence;
|
|
|
|
|
GdkInputSource source;
|
2020-02-16 16:09:02 +00:00
|
|
|
|
GdkEvent *event;
|
2020-04-11 18:52:43 +00:00
|
|
|
|
GdkModifierType state;
|
2014-06-05 13:40:02 +00:00
|
|
|
|
gboolean extend;
|
2020-04-11 18:52:43 +00:00
|
|
|
|
gboolean modify;
|
2014-06-05 13:40:02 +00:00
|
|
|
|
|
|
|
|
|
/* With touch, we default to modifying the selection.
|
|
|
|
|
* You can still clear the selection and start over
|
|
|
|
|
* by holding Ctrl.
|
|
|
|
|
*/
|
|
|
|
|
sequence = gtk_gesture_single_get_current_sequence (GTK_GESTURE_SINGLE (gesture));
|
|
|
|
|
event = gtk_gesture_get_last_event (GTK_GESTURE (gesture), sequence);
|
2020-04-11 18:52:43 +00:00
|
|
|
|
state = gdk_event_get_modifier_state (event);
|
|
|
|
|
extend = (state & GDK_SHIFT_MASK) != 0;
|
|
|
|
|
modify = (state & GDK_CONTROL_MASK) != 0;
|
2014-06-05 13:40:02 +00:00
|
|
|
|
source = gdk_device_get_source (gdk_event_get_source_device (event));
|
|
|
|
|
|
|
|
|
|
if (source == GDK_SOURCE_TOUCHSCREEN)
|
|
|
|
|
modify = !modify;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
gtk_list_box_update_selection_full (box, box->active_row, modify, extend, focus_on_click);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
2014-06-05 13:40:02 +00:00
|
|
|
|
}
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->active_row)
|
2017-09-15 14:48:13 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->active_row = NULL;
|
2017-09-15 14:48:13 +00:00
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_object_unref (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-11-19 19:47:24 +00:00
|
|
|
|
static void
|
2019-05-29 17:10:46 +00:00
|
|
|
|
gtk_list_box_click_gesture_stopped (GtkGestureClick *gesture,
|
2017-11-19 19:47:24 +00:00
|
|
|
|
GtkListBox *box)
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->active_row)
|
2017-11-19 19:47:24 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->active_row = NULL;
|
2017-11-19 19:47:24 +00:00
|
|
|
|
gtk_widget_queue_draw (GTK_WIDGET (box));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
2013-09-28 02:00:38 +00:00
|
|
|
|
gtk_list_box_show (GtkWidget *widget)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_do_reseparate (GTK_LIST_BOX (widget));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
GTK_WIDGET_CLASS (gtk_list_box_parent_class)->show (widget);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2013-09-28 02:00:38 +00:00
|
|
|
|
gtk_list_box_focus (GtkWidget *widget,
|
|
|
|
|
GtkDirectionType direction)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
GtkListBox *box = GTK_LIST_BOX (widget);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkWidget *focus_child;
|
|
|
|
|
GtkListBoxRow *next_focus_row;
|
2015-08-22 00:51:03 +00:00
|
|
|
|
GtkWidget *row;
|
|
|
|
|
GtkWidget *header;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2017-02-08 08:00:29 +00:00
|
|
|
|
focus_child = gtk_widget_get_focus_child (widget);
|
2015-08-22 00:51:03 +00:00
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
next_focus_row = NULL;
|
|
|
|
|
if (focus_child != NULL)
|
|
|
|
|
{
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GSequenceIter *i;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
if (gtk_widget_child_focus (focus_child, direction))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
if (direction == GTK_DIR_UP || direction == GTK_DIR_TAB_BACKWARD)
|
|
|
|
|
{
|
2015-08-22 00:51:03 +00:00
|
|
|
|
if (GTK_IS_LIST_BOX_ROW (focus_child))
|
|
|
|
|
{
|
|
|
|
|
header = ROW_PRIV (GTK_LIST_BOX_ROW (focus_child))->header;
|
|
|
|
|
if (header && gtk_widget_child_focus (header, direction))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (GTK_IS_LIST_BOX_ROW (focus_child))
|
|
|
|
|
row = focus_child;
|
|
|
|
|
else
|
2020-03-28 20:52:41 +00:00
|
|
|
|
row = g_hash_table_lookup (box->header_hash, focus_child);
|
2015-08-22 00:51:03 +00:00
|
|
|
|
|
|
|
|
|
if (GTK_IS_LIST_BOX_ROW (row))
|
|
|
|
|
i = gtk_list_box_get_previous_visible (box, ROW_PRIV (GTK_LIST_BOX_ROW (row))->iter);
|
|
|
|
|
else
|
|
|
|
|
i = NULL;
|
|
|
|
|
|
2013-09-15 20:31:12 +00:00
|
|
|
|
while (i != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (gtk_widget_get_sensitive (g_sequence_get (i)))
|
|
|
|
|
{
|
|
|
|
|
next_focus_row = g_sequence_get (i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
i = gtk_list_box_get_previous_visible (box, i);
|
2013-09-15 20:31:12 +00:00
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
else if (direction == GTK_DIR_DOWN || direction == GTK_DIR_TAB_FORWARD)
|
|
|
|
|
{
|
2015-08-22 00:51:03 +00:00
|
|
|
|
if (GTK_IS_LIST_BOX_ROW (focus_child))
|
|
|
|
|
i = gtk_list_box_get_next_visible (box, ROW_PRIV (GTK_LIST_BOX_ROW (focus_child))->iter);
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
row = g_hash_table_lookup (box->header_hash, focus_child);
|
2015-08-22 00:51:03 +00:00
|
|
|
|
if (GTK_IS_LIST_BOX_ROW (row))
|
|
|
|
|
i = ROW_PRIV (GTK_LIST_BOX_ROW (row))->iter;
|
|
|
|
|
else
|
|
|
|
|
i = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-15 20:31:12 +00:00
|
|
|
|
while (!g_sequence_iter_is_end (i))
|
|
|
|
|
{
|
|
|
|
|
if (gtk_widget_get_sensitive (g_sequence_get (i)))
|
|
|
|
|
{
|
|
|
|
|
next_focus_row = g_sequence_get (i);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
i = gtk_list_box_get_next_visible (box, i);
|
2013-09-15 20:31:12 +00:00
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* No current focus row */
|
|
|
|
|
switch (direction)
|
|
|
|
|
{
|
|
|
|
|
case GTK_DIR_UP:
|
|
|
|
|
case GTK_DIR_TAB_BACKWARD:
|
2020-03-28 20:52:41 +00:00
|
|
|
|
next_focus_row = box->selected_row;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
if (next_focus_row == NULL)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
next_focus_row = gtk_list_box_get_last_focusable (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
break;
|
2017-10-06 19:19:42 +00:00
|
|
|
|
case GTK_DIR_DOWN:
|
|
|
|
|
case GTK_DIR_TAB_FORWARD:
|
|
|
|
|
case GTK_DIR_LEFT:
|
|
|
|
|
case GTK_DIR_RIGHT:
|
2013-06-10 09:17:10 +00:00
|
|
|
|
default:
|
2020-03-28 20:52:41 +00:00
|
|
|
|
next_focus_row = box->selected_row;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
if (next_focus_row == NULL)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
next_focus_row = gtk_list_box_get_first_focusable (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (next_focus_row == NULL)
|
|
|
|
|
{
|
|
|
|
|
if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN)
|
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
if (gtk_widget_keynav_failed (GTK_WIDGET (box), direction))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-22 00:51:03 +00:00
|
|
|
|
if (direction == GTK_DIR_DOWN || direction == GTK_DIR_TAB_FORWARD)
|
|
|
|
|
{
|
|
|
|
|
header = ROW_PRIV (next_focus_row)->header;
|
|
|
|
|
if (header && gtk_widget_child_focus (header, direction))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
if (gtk_widget_child_focus (GTK_WIDGET (next_focus_row), direction))
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
2015-08-22 00:51:03 +00:00
|
|
|
|
return FALSE;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 14:53:31 +00:00
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
list_box_add_visible_rows (GtkListBox *box,
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int n)
|
2013-06-11 14:53:31 +00:00
|
|
|
|
{
|
|
|
|
|
int was_zero;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
was_zero = box->n_visible_rows == 0;
|
|
|
|
|
box->n_visible_rows += n;
|
2013-06-11 14:53:31 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->placeholder &&
|
|
|
|
|
(was_zero || box->n_visible_rows == 0))
|
|
|
|
|
gtk_widget_set_child_visible (GTK_WIDGET (box->placeholder),
|
|
|
|
|
box->n_visible_rows == 0);
|
2013-06-11 14:53:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 14:13:00 +00:00
|
|
|
|
/* Children are visible if they are shown by the app (visible)
|
2013-06-15 16:13:28 +00:00
|
|
|
|
* and not filtered out (child_visible) by the listbox
|
|
|
|
|
*/
|
2013-06-11 14:13:00 +00:00
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
update_row_is_visible (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxRow *row)
|
2013-06-11 14:13:00 +00:00
|
|
|
|
{
|
2013-09-28 01:39:10 +00:00
|
|
|
|
GtkListBoxRowPrivate *row_priv = ROW_PRIV (row);
|
2013-06-11 14:20:45 +00:00
|
|
|
|
gboolean was_visible;
|
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
was_visible = row_priv->visible;
|
2013-06-11 14:20:45 +00:00
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
row_priv->visible =
|
2013-06-11 14:20:45 +00:00
|
|
|
|
gtk_widget_get_visible (GTK_WIDGET (row)) &&
|
|
|
|
|
gtk_widget_get_child_visible (GTK_WIDGET (row));
|
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
if (was_visible && !row_priv->visible)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
list_box_add_visible_rows (box, -1);
|
2013-07-02 11:44:17 +00:00
|
|
|
|
if (!was_visible && row_priv->visible)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
list_box_add_visible_rows (box, 1);
|
2013-06-11 14:13:00 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_apply_filter (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxRow *row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
gboolean do_show;
|
|
|
|
|
|
|
|
|
|
do_show = TRUE;
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->filter_func != NULL)
|
|
|
|
|
do_show = box->filter_func (row, box->filter_func_target);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
gtk_widget_set_child_visible (GTK_WIDGET (row), do_show);
|
2013-06-11 14:13:00 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
update_row_is_visible (box, row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_apply_filter_all (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
row = g_sequence_get (iter);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_apply_filter (box, row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkListBoxRow *
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_first_focusable (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
row = g_sequence_get (iter);
|
2013-09-15 20:31:12 +00:00
|
|
|
|
if (row_is_visible (row) && gtk_widget_is_sensitive (GTK_WIDGET (row)))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkListBoxRow *
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_last_focusable (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
iter = g_sequence_get_end_iter (box->children);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
while (!g_sequence_iter_is_begin (iter))
|
|
|
|
|
{
|
|
|
|
|
iter = g_sequence_iter_prev (iter);
|
|
|
|
|
row = g_sequence_get (iter);
|
2013-09-15 20:31:12 +00:00
|
|
|
|
if (row_is_visible (row) && gtk_widget_is_sensitive (GTK_WIDGET (row)))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-15 16:13:28 +00:00
|
|
|
|
static GSequenceIter *
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_previous_visible (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GSequenceIter *iter)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
|
|
|
|
|
if (g_sequence_iter_is_begin (iter))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
iter = g_sequence_iter_prev (iter);
|
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
if (row_is_visible (row))
|
|
|
|
|
return iter;
|
|
|
|
|
}
|
|
|
|
|
while (!g_sequence_iter_is_begin (iter));
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-15 16:13:28 +00:00
|
|
|
|
static GSequenceIter *
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_get_next_visible (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GSequenceIter *iter)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
|
|
|
|
|
if (g_sequence_iter_is_end (iter))
|
|
|
|
|
return iter;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
iter = g_sequence_iter_next (iter);
|
|
|
|
|
if (!g_sequence_iter_is_end (iter))
|
|
|
|
|
{
|
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
if (row_is_visible (row))
|
|
|
|
|
return iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (!g_sequence_iter_is_end (iter));
|
|
|
|
|
|
|
|
|
|
return iter;
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-01 11:25:23 +00:00
|
|
|
|
static GSequenceIter *
|
|
|
|
|
gtk_list_box_get_last_visible (GtkListBox *box,
|
|
|
|
|
GSequenceIter *iter)
|
|
|
|
|
{
|
|
|
|
|
GSequenceIter *next = NULL;
|
|
|
|
|
|
|
|
|
|
if (g_sequence_iter_is_end (iter))
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
next = gtk_list_box_get_next_visible (box, iter);
|
|
|
|
|
|
|
|
|
|
if (!g_sequence_iter_is_end (next))
|
|
|
|
|
iter = next;
|
|
|
|
|
}
|
|
|
|
|
while (!g_sequence_iter_is_end (next));
|
|
|
|
|
|
|
|
|
|
return iter;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_update_header (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GSequenceIter *iter)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
GSequenceIter *before_iter;
|
|
|
|
|
GtkListBoxRow *before_row;
|
2019-10-04 17:25:16 +00:00
|
|
|
|
GtkWidget *old_header, *new_header;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
if (iter == NULL || g_sequence_iter_is_end (iter))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
row = g_sequence_get (iter);
|
2015-07-17 22:40:11 +00:00
|
|
|
|
g_object_ref (row);
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
before_iter = gtk_list_box_get_previous_visible (box, iter);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
before_row = NULL;
|
|
|
|
|
if (before_iter != NULL)
|
|
|
|
|
{
|
|
|
|
|
before_row = g_sequence_get (before_iter);
|
|
|
|
|
if (before_row)
|
|
|
|
|
g_object_ref (before_row);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->update_header_func != NULL &&
|
2013-06-10 09:17:10 +00:00
|
|
|
|
row_is_visible (row))
|
|
|
|
|
{
|
2013-07-02 11:44:17 +00:00
|
|
|
|
old_header = ROW_PRIV (row)->header;
|
2013-06-11 12:09:19 +00:00
|
|
|
|
if (old_header)
|
|
|
|
|
g_object_ref (old_header);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->update_header_func (row,
|
2013-06-11 12:09:19 +00:00
|
|
|
|
before_row,
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->update_header_func_target);
|
2019-10-04 17:25:16 +00:00
|
|
|
|
new_header = ROW_PRIV (row)->header;
|
|
|
|
|
if (old_header != new_header)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2019-10-04 17:25:34 +00:00
|
|
|
|
if (old_header != NULL &&
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_hash_table_lookup (box->header_hash, old_header) == row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2019-10-04 17:25:34 +00:00
|
|
|
|
/* Only unparent the @old_header if it hasn’t been re-used as the
|
|
|
|
|
* header for a different row. */
|
2013-06-11 12:09:19 +00:00
|
|
|
|
gtk_widget_unparent (old_header);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_hash_table_remove (box->header_hash, old_header);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
2019-10-04 17:25:16 +00:00
|
|
|
|
if (new_header != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_hash_table_insert (box->header_hash, new_header, row);
|
2019-10-04 17:25:16 +00:00
|
|
|
|
gtk_widget_unparent (new_header);
|
|
|
|
|
gtk_widget_set_parent (new_header, GTK_WIDGET (box));
|
|
|
|
|
gtk_widget_show (new_header);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
2013-06-11 12:09:19 +00:00
|
|
|
|
if (old_header)
|
|
|
|
|
g_object_unref (old_header);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-07-02 11:44:17 +00:00
|
|
|
|
if (ROW_PRIV (row)->header != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_hash_table_remove (box->header_hash, ROW_PRIV (row)->header);
|
2013-07-02 11:44:17 +00:00
|
|
|
|
gtk_widget_unparent (ROW_PRIV (row)->header);
|
2013-06-11 12:09:19 +00:00
|
|
|
|
gtk_list_box_row_set_header (row, NULL);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_widget_queue_resize (GTK_WIDGET (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (before_row)
|
|
|
|
|
g_object_unref (before_row);
|
2015-07-17 22:40:11 +00:00
|
|
|
|
g_object_unref (row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_row_visibility_changed (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxRow *row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
update_row_is_visible (box, row);
|
2013-06-11 14:13:00 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
if (gtk_widget_get_visible (GTK_WIDGET (box)))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_update_header (box, ROW_PRIV (row)->iter);
|
|
|
|
|
gtk_list_box_update_header (box,
|
|
|
|
|
gtk_list_box_get_next_visible (box, ROW_PRIV (row)->iter));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-08 05:50:37 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_remove:
|
|
|
|
|
* @box: a #GtkListBox
|
|
|
|
|
* @child: the child to remove
|
|
|
|
|
*
|
|
|
|
|
* Removes a child from @box.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
gtk_list_box_remove (GtkListBox *box,
|
2013-09-28 02:00:38 +00:00
|
|
|
|
GtkWidget *child)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-05-08 05:50:37 +00:00
|
|
|
|
GtkWidget *widget = GTK_WIDGET (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
gboolean was_visible;
|
2014-03-29 01:34:39 +00:00
|
|
|
|
gboolean was_selected;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkListBoxRow *row;
|
2018-08-02 06:29:33 +00:00
|
|
|
|
GSequenceIter *iter;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GSequenceIter *next;
|
|
|
|
|
|
|
|
|
|
was_visible = gtk_widget_get_visible (child);
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (child == box->placeholder)
|
2017-04-07 17:02:35 +00:00
|
|
|
|
{
|
|
|
|
|
gtk_widget_unparent (child);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->placeholder = NULL;
|
2017-04-07 17:02:35 +00:00
|
|
|
|
if (was_visible && gtk_widget_get_visible (widget))
|
|
|
|
|
gtk_widget_queue_resize (widget);
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
if (!GTK_IS_LIST_BOX_ROW (child))
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
row = g_hash_table_lookup (box->header_hash, child);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
if (row != NULL)
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_hash_table_remove (box->header_hash, child);
|
2013-07-02 11:44:17 +00:00
|
|
|
|
g_clear_object (&ROW_PRIV (row)->header);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
gtk_widget_unparent (child);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
if (was_visible && gtk_widget_get_visible (widget))
|
|
|
|
|
gtk_widget_queue_resize (widget);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-02-28 16:06:25 +00:00
|
|
|
|
g_warning ("Tried to remove non-child %p", child);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
row = GTK_LIST_BOX_ROW (child);
|
2018-08-02 06:29:33 +00:00
|
|
|
|
iter = ROW_PRIV (row)->iter;
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (g_sequence_iter_get_sequence (iter) != box->children)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2016-02-28 16:06:25 +00:00
|
|
|
|
g_warning ("Tried to remove non-child %p", child);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-29 01:34:39 +00:00
|
|
|
|
was_selected = ROW_PRIV (row)->selected;
|
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
if (ROW_PRIV (row)->visible)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
list_box_add_visible_rows (box, -1);
|
2013-06-11 14:20:45 +00:00
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
if (ROW_PRIV (row)->header != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_hash_table_remove (box->header_hash, ROW_PRIV (row)->header);
|
2013-07-02 11:44:17 +00:00
|
|
|
|
gtk_widget_unparent (ROW_PRIV (row)->header);
|
|
|
|
|
g_clear_object (&ROW_PRIV (row)->header);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (row == box->selected_row)
|
|
|
|
|
box->selected_row = NULL;
|
|
|
|
|
if (row == box->cursor_row)
|
|
|
|
|
box->cursor_row = NULL;
|
|
|
|
|
if (row == box->active_row)
|
|
|
|
|
box->active_row = NULL;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (row == box->drag_highlighted_row)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_drag_unhighlight_row (box);
|
2013-06-10 13:15:22 +00:00
|
|
|
|
|
2018-08-02 06:29:33 +00:00
|
|
|
|
next = gtk_list_box_get_next_visible (box, iter);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
gtk_widget_unparent (child);
|
2018-08-02 06:29:33 +00:00
|
|
|
|
g_sequence_remove (iter);
|
|
|
|
|
|
|
|
|
|
/* After unparenting, those values are garbage */
|
|
|
|
|
iter = NULL;
|
|
|
|
|
row = NULL;
|
|
|
|
|
child = NULL;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
if (gtk_widget_get_visible (widget))
|
|
|
|
|
gtk_list_box_update_header (box, next);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
if (was_visible && gtk_widget_get_visible (GTK_WIDGET (box)))
|
|
|
|
|
gtk_widget_queue_resize (widget);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
|
2016-12-18 08:43:38 +00:00
|
|
|
|
if (was_selected && !gtk_widget_in_destruction (widget))
|
2014-04-13 20:51:49 +00:00
|
|
|
|
{
|
|
|
|
|
g_signal_emit (box, signals[ROW_SELECTED], 0, NULL);
|
|
|
|
|
g_signal_emit (box, signals[SELECTED_ROWS_CHANGED], 0);
|
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2020-05-08 12:48:00 +00:00
|
|
|
|
gtk_list_box_compute_expand (GtkWidget *widget,
|
|
|
|
|
gboolean *hexpand_p,
|
|
|
|
|
gboolean *vexpand_p)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-05-08 12:48:00 +00:00
|
|
|
|
GtkWidget *w;
|
|
|
|
|
gboolean hexpand = FALSE;
|
|
|
|
|
gboolean vexpand = FALSE;
|
2013-06-11 14:53:31 +00:00
|
|
|
|
|
2020-05-08 12:48:00 +00:00
|
|
|
|
for (w = gtk_widget_get_first_child (widget);
|
|
|
|
|
w != NULL;
|
|
|
|
|
w = gtk_widget_get_next_sibling (w))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-05-08 12:48:00 +00:00
|
|
|
|
hexpand = hexpand || gtk_widget_compute_expand (w, GTK_ORIENTATION_HORIZONTAL);
|
|
|
|
|
vexpand = vexpand || gtk_widget_compute_expand (w, GTK_ORIENTATION_VERTICAL);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-05-08 12:48:00 +00:00
|
|
|
|
*hexpand_p = hexpand;
|
|
|
|
|
*vexpand_p = vexpand;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-05-28 08:00:03 +00:00
|
|
|
|
/* We don't expand vertically beyond the minimum size */
|
2020-05-08 12:48:00 +00:00
|
|
|
|
if (*vexpand_p)
|
|
|
|
|
*vexpand_p = FALSE;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GtkSizeRequestMode
|
2013-09-28 02:00:38 +00:00
|
|
|
|
gtk_list_box_get_request_mode (GtkWidget *widget)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2017-05-03 20:16:47 +00:00
|
|
|
|
gtk_list_box_measure (GtkWidget *widget,
|
2015-12-12 04:48:34 +00:00
|
|
|
|
GtkOrientation orientation,
|
2017-05-03 20:16:47 +00:00
|
|
|
|
int for_size,
|
|
|
|
|
int *minimum,
|
|
|
|
|
int *natural,
|
|
|
|
|
int *minimum_baseline,
|
|
|
|
|
int *natural_baseline)
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
GtkListBox *box = GTK_LIST_BOX (widget);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
|
2015-12-12 04:48:34 +00:00
|
|
|
|
if (orientation == GTK_ORIENTATION_HORIZONTAL)
|
|
|
|
|
{
|
|
|
|
|
*minimum = 0;
|
|
|
|
|
*natural = 0;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->placeholder && gtk_widget_get_child_visible (box->placeholder))
|
|
|
|
|
gtk_widget_measure (box->placeholder, GTK_ORIENTATION_HORIZONTAL, -1,
|
2016-11-12 17:54:44 +00:00
|
|
|
|
minimum, natural,
|
|
|
|
|
NULL, NULL);
|
2013-06-11 14:53:31 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int row_min;
|
|
|
|
|
int row_nat;
|
2013-06-13 08:47:52 +00:00
|
|
|
|
|
2015-12-12 04:48:34 +00:00
|
|
|
|
row = g_sequence_get (iter);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2015-12-12 04:48:34 +00:00
|
|
|
|
/* We *do* take visible but filtered rows into account here so that
|
|
|
|
|
* the list width doesn't change during filtering
|
|
|
|
|
*/
|
|
|
|
|
if (!gtk_widget_get_visible (GTK_WIDGET (row)))
|
|
|
|
|
continue;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2016-11-12 17:54:44 +00:00
|
|
|
|
gtk_widget_measure (GTK_WIDGET (row), orientation, -1,
|
|
|
|
|
&row_min, &row_nat,
|
|
|
|
|
NULL, NULL);
|
|
|
|
|
|
2015-12-12 04:48:34 +00:00
|
|
|
|
*minimum = MAX (*minimum, row_min);
|
|
|
|
|
*natural = MAX (*natural, row_nat);
|
|
|
|
|
|
|
|
|
|
if (ROW_PRIV (row)->header != NULL)
|
|
|
|
|
{
|
2016-11-12 17:54:44 +00:00
|
|
|
|
gtk_widget_measure (ROW_PRIV (row)->header, orientation, -1,
|
|
|
|
|
&row_min, &row_nat,
|
|
|
|
|
NULL, NULL);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
*minimum = MAX (*minimum, row_min);
|
|
|
|
|
*natural = MAX (*natural, row_nat);
|
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-12-12 04:48:34 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (for_size < 0)
|
2017-05-03 20:16:47 +00:00
|
|
|
|
{
|
|
|
|
|
int f;
|
|
|
|
|
gtk_list_box_measure (widget, GTK_ORIENTATION_HORIZONTAL, -1,
|
|
|
|
|
&f, &for_size, NULL, NULL);
|
|
|
|
|
}
|
2015-12-12 04:48:34 +00:00
|
|
|
|
|
|
|
|
|
*minimum = 0;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->placeholder && gtk_widget_get_child_visible (box->placeholder))
|
|
|
|
|
gtk_widget_measure (box->placeholder, orientation, for_size,
|
2016-11-12 17:54:44 +00:00
|
|
|
|
minimum, NULL,
|
|
|
|
|
NULL, NULL);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int row_min = 0;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2015-12-12 04:48:34 +00:00
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
if (!row_is_visible (row))
|
|
|
|
|
continue;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2015-12-12 04:48:34 +00:00
|
|
|
|
if (ROW_PRIV (row)->header != NULL)
|
|
|
|
|
{
|
2016-11-12 17:54:44 +00:00
|
|
|
|
gtk_widget_measure (ROW_PRIV (row)->header, orientation, for_size,
|
|
|
|
|
&row_min, NULL,
|
|
|
|
|
NULL, NULL);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
*minimum += row_min;
|
|
|
|
|
}
|
2016-11-12 17:54:44 +00:00
|
|
|
|
gtk_widget_measure (GTK_WIDGET (row), orientation, for_size,
|
|
|
|
|
&row_min, NULL,
|
|
|
|
|
NULL, NULL);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
*minimum += row_min;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We always allocate the minimum height, since handling expanding rows
|
|
|
|
|
* is way too costly, and unlikely to be used, as lists are generally put
|
|
|
|
|
* inside a scrolling window anyway.
|
|
|
|
|
*/
|
|
|
|
|
*natural = *minimum;
|
|
|
|
|
}
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2018-08-16 04:53:03 +00:00
|
|
|
|
gtk_list_box_size_allocate (GtkWidget *widget,
|
|
|
|
|
int width,
|
|
|
|
|
int height,
|
|
|
|
|
int baseline)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
GtkListBox *box = GTK_LIST_BOX (widget);
|
2015-12-12 04:48:34 +00:00
|
|
|
|
GtkAllocation child_allocation;
|
|
|
|
|
GtkAllocation header_allocation;
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
int child_min;
|
|
|
|
|
|
2016-11-29 10:46:32 +00:00
|
|
|
|
|
2018-08-16 04:53:03 +00:00
|
|
|
|
child_allocation.x = 0;
|
|
|
|
|
child_allocation.y = 0;
|
|
|
|
|
child_allocation.width = width;
|
2015-12-12 04:48:34 +00:00
|
|
|
|
child_allocation.height = 0;
|
|
|
|
|
|
2018-08-16 04:53:03 +00:00
|
|
|
|
header_allocation.x = 0;
|
|
|
|
|
header_allocation.y = 0;
|
|
|
|
|
header_allocation.width = width;
|
2015-12-12 04:48:34 +00:00
|
|
|
|
header_allocation.height = 0;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->placeholder && gtk_widget_get_child_visible (box->placeholder))
|
2013-06-11 14:53:31 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
gtk_widget_measure (box->placeholder, GTK_ORIENTATION_VERTICAL,
|
2018-08-16 04:53:03 +00:00
|
|
|
|
width,
|
2016-11-12 17:54:44 +00:00
|
|
|
|
&child_min, NULL, NULL, NULL);
|
2018-08-16 04:53:03 +00:00
|
|
|
|
header_allocation.height = height;
|
2013-06-11 14:53:31 +00:00
|
|
|
|
header_allocation.y = child_allocation.y;
|
2020-03-28 20:52:41 +00:00
|
|
|
|
gtk_widget_size_allocate (box->placeholder, &header_allocation, -1);
|
2013-06-11 14:53:31 +00:00
|
|
|
|
child_allocation.y += child_min;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
if (!row_is_visible (row))
|
|
|
|
|
{
|
2013-07-02 11:44:17 +00:00
|
|
|
|
ROW_PRIV (row)->y = child_allocation.y;
|
|
|
|
|
ROW_PRIV (row)->height = 0;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
if (ROW_PRIV (row)->header != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2016-11-12 17:54:44 +00:00
|
|
|
|
gtk_widget_measure (ROW_PRIV (row)->header, GTK_ORIENTATION_VERTICAL,
|
2018-08-16 04:53:03 +00:00
|
|
|
|
width,
|
2016-11-12 17:54:44 +00:00
|
|
|
|
&child_min, NULL, NULL, NULL);
|
2013-06-11 12:09:19 +00:00
|
|
|
|
header_allocation.height = child_min;
|
|
|
|
|
header_allocation.y = child_allocation.y;
|
2017-07-11 07:58:21 +00:00
|
|
|
|
gtk_widget_size_allocate (ROW_PRIV (row)->header,
|
|
|
|
|
&header_allocation,
|
2018-03-31 19:02:28 +00:00
|
|
|
|
-1);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
child_allocation.y += child_min;
|
|
|
|
|
}
|
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
ROW_PRIV (row)->y = child_allocation.y;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2016-11-12 17:54:44 +00:00
|
|
|
|
gtk_widget_measure (GTK_WIDGET (row), GTK_ORIENTATION_VERTICAL,
|
|
|
|
|
child_allocation.width,
|
|
|
|
|
&child_min, NULL, NULL, NULL);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
child_allocation.height = child_min;
|
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
ROW_PRIV (row)->height = child_allocation.height;
|
2018-03-31 19:02:28 +00:00
|
|
|
|
gtk_widget_size_allocate (GTK_WIDGET (row), &child_allocation, -1);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
child_allocation.y += child_min;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-08-06 12:57:22 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_prepend:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-08-06 12:57:22 +00:00
|
|
|
|
* @child: the #GtkWidget to add
|
|
|
|
|
*
|
|
|
|
|
* Prepend a widget to the list. If a sort function is set, the widget will
|
|
|
|
|
* actually be inserted at the calculated position and this function has the
|
|
|
|
|
* same effect of gtk_container_add().
|
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_prepend (GtkListBox *box,
|
2013-08-06 12:57:22 +00:00
|
|
|
|
GtkWidget *child)
|
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_insert (box, child, 0);
|
2013-08-06 12:57:22 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-08-08 06:06:27 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_insert:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-08-08 06:06:27 +00:00
|
|
|
|
* @child: the #GtkWidget to add
|
|
|
|
|
* @position: the position to insert @child in
|
|
|
|
|
*
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* Insert the @child into the @box at @position. If a sort function is
|
2013-08-08 06:06:27 +00:00
|
|
|
|
* set, the widget will actually be inserted at the calculated position and
|
|
|
|
|
* this function has the same effect of gtk_container_add().
|
|
|
|
|
*
|
|
|
|
|
* If @position is -1, or larger than the total number of items in the
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box, then the @child will be appended to the end.
|
2013-08-08 06:06:27 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_insert (GtkListBox *box,
|
2013-08-08 06:06:27 +00:00
|
|
|
|
GtkWidget *child,
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int position)
|
2013-08-08 06:06:27 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
2018-06-20 10:55:14 +00:00
|
|
|
|
GSequenceIter *prev = NULL;
|
2013-08-08 06:06:27 +00:00
|
|
|
|
GSequenceIter *iter = NULL;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-09-28 01:39:10 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_WIDGET (child));
|
2013-08-08 06:06:27 +00:00
|
|
|
|
|
|
|
|
|
if (GTK_IS_LIST_BOX_ROW (child))
|
|
|
|
|
row = GTK_LIST_BOX_ROW (child);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
row = GTK_LIST_BOX_ROW (gtk_list_box_row_new ());
|
2020-05-03 04:09:18 +00:00
|
|
|
|
gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), child);
|
2013-08-08 06:06:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->sort_func != NULL)
|
|
|
|
|
iter = g_sequence_insert_sorted (box->children, row,
|
2014-03-29 03:07:41 +00:00
|
|
|
|
(GCompareDataFunc)do_sort, box);
|
2013-08-08 06:06:27 +00:00
|
|
|
|
else if (position == 0)
|
2020-03-28 20:52:41 +00:00
|
|
|
|
iter = g_sequence_prepend (box->children, row);
|
2013-08-08 06:06:27 +00:00
|
|
|
|
else if (position == -1)
|
2020-03-28 20:52:41 +00:00
|
|
|
|
iter = g_sequence_append (box->children, row);
|
2013-08-08 06:06:27 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
GSequenceIter *current_iter;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
current_iter = g_sequence_get_iter_at_pos (box->children, position);
|
2013-08-08 06:06:27 +00:00
|
|
|
|
iter = g_sequence_insert_before (current_iter, row);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ROW_PRIV (row)->iter = iter;
|
2018-06-20 10:55:14 +00:00
|
|
|
|
prev = g_sequence_iter_prev (iter);
|
|
|
|
|
gtk_widget_insert_after (GTK_WIDGET (row), GTK_WIDGET (box),
|
|
|
|
|
prev != iter ? g_sequence_get (prev) : NULL);
|
|
|
|
|
|
2013-08-08 06:06:27 +00:00
|
|
|
|
gtk_widget_set_child_visible (GTK_WIDGET (row), TRUE);
|
|
|
|
|
ROW_PRIV (row)->visible = gtk_widget_get_visible (GTK_WIDGET (row));
|
|
|
|
|
if (ROW_PRIV (row)->visible)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
list_box_add_visible_rows (box, 1);
|
|
|
|
|
gtk_list_box_apply_filter (box, row);
|
2014-07-16 03:13:43 +00:00
|
|
|
|
gtk_list_box_update_row_style (box, row);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
if (gtk_widget_get_visible (GTK_WIDGET (box)))
|
2013-08-08 06:06:27 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_update_header (box, ROW_PRIV (row)->iter);
|
|
|
|
|
gtk_list_box_update_header (box,
|
|
|
|
|
gtk_list_box_get_next_visible (box, ROW_PRIV (row)->iter));
|
2013-08-08 06:06:27 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_drag_unhighlight_row:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* If a row has previously been highlighted via gtk_list_box_drag_highlight_row()
|
|
|
|
|
* it will have the highlight removed.
|
2013-06-15 16:13:28 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_drag_unhighlight_row (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->drag_highlighted_row == NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
gtk_widget_unset_state_flags (GTK_WIDGET (box->drag_highlighted_row), GTK_STATE_FLAG_DROP_ACTIVE);
|
|
|
|
|
g_clear_object (&box->drag_highlighted_row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_drag_highlight_row:
|
2014-03-29 03:07:41 +00:00
|
|
|
|
* @box: a #GtkListBox
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @row: a #GtkListBoxRow
|
|
|
|
|
*
|
|
|
|
|
* This is a helper function for implementing DnD onto a #GtkListBox.
|
|
|
|
|
* The passed in @row will be highlighted via gtk_drag_highlight(),
|
|
|
|
|
* and any previously highlighted row will be unhighlighted.
|
|
|
|
|
*
|
|
|
|
|
* The row will also be unhighlighted when the widget gets
|
|
|
|
|
* a drag leave event.
|
2013-06-15 16:13:28 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_drag_highlight_row (GtkListBox *box,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkListBoxRow *row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
2013-09-28 01:39:10 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX_ROW (row));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->drag_highlighted_row == row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_drag_unhighlight_row (box);
|
2020-01-07 23:45:53 +00:00
|
|
|
|
gtk_widget_set_state_flags (GTK_WIDGET (row), GTK_STATE_FLAG_DROP_ACTIVE, FALSE);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->drag_highlighted_row = g_object_ref (row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_activate_cursor_row (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
gtk_list_box_select_and_activate (box, box->cursor_row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_toggle_cursor_row (GtkListBox *box)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->cursor_row == NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if ((box->selection_mode == GTK_SELECTION_SINGLE ||
|
|
|
|
|
box->selection_mode == GTK_SELECTION_MULTIPLE) &&
|
|
|
|
|
ROW_PRIV (box->cursor_row)->selected)
|
|
|
|
|
gtk_list_box_unselect_row_internal (box, box->cursor_row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
else
|
2020-03-28 20:52:41 +00:00
|
|
|
|
gtk_list_box_select_and_activate (box, box->cursor_row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_move_cursor (GtkListBox *box,
|
2013-09-28 02:00:38 +00:00
|
|
|
|
GtkMovementStep step,
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int count,
|
2020-04-11 18:52:43 +00:00
|
|
|
|
gboolean extend,
|
|
|
|
|
gboolean modify)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int page_size;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GSequenceIter *iter;
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int start_y;
|
|
|
|
|
int end_y;
|
2016-04-19 15:12:00 +00:00
|
|
|
|
int height;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
row = NULL;
|
2017-10-06 19:19:42 +00:00
|
|
|
|
switch ((guint) step)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
case GTK_MOVEMENT_BUFFER_ENDS:
|
|
|
|
|
if (count < 0)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
row = gtk_list_box_get_first_focusable (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
else
|
2014-03-29 03:07:41 +00:00
|
|
|
|
row = gtk_list_box_get_last_focusable (box);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
break;
|
|
|
|
|
case GTK_MOVEMENT_DISPLAY_LINES:
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->cursor_row != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int i = count;
|
2014-07-13 16:31:28 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
iter = ROW_PRIV (box->cursor_row)->iter;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2013-10-08 21:56:19 +00:00
|
|
|
|
while (i < 0 && iter != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
iter = gtk_list_box_get_previous_visible (box, iter);
|
2013-10-08 21:56:19 +00:00
|
|
|
|
i = i + 1;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
2013-10-08 21:56:19 +00:00
|
|
|
|
while (i > 0 && iter != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
iter = gtk_list_box_get_next_visible (box, iter);
|
2013-10-08 21:56:19 +00:00
|
|
|
|
i = i - 1;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (iter != NULL && !g_sequence_iter_is_end (iter))
|
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case GTK_MOVEMENT_PAGES:
|
|
|
|
|
page_size = 100;
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->adjustment != NULL)
|
|
|
|
|
page_size = gtk_adjustment_get_page_increment (box->adjustment);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->cursor_row != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
start_y = ROW_PRIV (box->cursor_row)->y;
|
2018-03-16 16:58:14 +00:00
|
|
|
|
height = gtk_widget_get_height (GTK_WIDGET (box));
|
2016-04-19 15:12:00 +00:00
|
|
|
|
end_y = CLAMP (start_y + page_size * count, 0, height - 1);
|
|
|
|
|
row = gtk_list_box_get_row_at_y (box, end_y);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2016-09-01 11:25:23 +00:00
|
|
|
|
if (!row)
|
|
|
|
|
{
|
|
|
|
|
GSequenceIter *cursor_iter;
|
|
|
|
|
GSequenceIter *next_iter;
|
|
|
|
|
|
2017-12-15 23:46:21 +00:00
|
|
|
|
if (count > 0)
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
cursor_iter = ROW_PRIV (box->cursor_row)->iter;
|
2017-12-15 23:46:21 +00:00
|
|
|
|
next_iter = gtk_list_box_get_last_visible (box, cursor_iter);
|
|
|
|
|
|
|
|
|
|
if (next_iter)
|
|
|
|
|
{
|
|
|
|
|
row = g_sequence_get (next_iter);
|
|
|
|
|
end_y = ROW_PRIV (row)->y;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2016-09-01 11:25:23 +00:00
|
|
|
|
{
|
2017-12-15 23:46:21 +00:00
|
|
|
|
row = gtk_list_box_get_row_at_index (box, 0);
|
2016-09-01 11:25:23 +00:00
|
|
|
|
end_y = ROW_PRIV (row)->y;
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-03-28 20:52:41 +00:00
|
|
|
|
else if (row == box->cursor_row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2016-09-01 11:23:14 +00:00
|
|
|
|
iter = ROW_PRIV (row)->iter;
|
|
|
|
|
|
2016-04-19 15:12:00 +00:00
|
|
|
|
/* Move at least one row. This is important when the cursor_row's height is
|
|
|
|
|
* greater than page_size */
|
|
|
|
|
if (count < 0)
|
|
|
|
|
iter = g_sequence_iter_prev (iter);
|
|
|
|
|
else
|
|
|
|
|
iter = g_sequence_iter_next (iter);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2016-04-19 15:12:00 +00:00
|
|
|
|
if (!g_sequence_iter_is_begin (iter) && !g_sequence_iter_is_end (iter))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2016-04-19 15:12:00 +00:00
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
end_y = ROW_PRIV (row)->y;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-19 15:12:00 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (end_y != start_y && box->adjustment != NULL)
|
|
|
|
|
gtk_adjustment_animate_to_value (box->adjustment, end_y);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (row == NULL || row == box->cursor_row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkDirectionType direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
if (!gtk_widget_keynav_failed (GTK_WIDGET (box), direction))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2019-05-20 04:47:50 +00:00
|
|
|
|
GtkWidget *toplevel = GTK_WIDGET (gtk_widget_get_root (GTK_WIDGET (box)));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
if (toplevel)
|
|
|
|
|
gtk_widget_child_focus (toplevel,
|
|
|
|
|
direction == GTK_DIR_UP ?
|
|
|
|
|
GTK_DIR_TAB_BACKWARD :
|
|
|
|
|
GTK_DIR_TAB_FORWARD);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-10 19:02:08 +00:00
|
|
|
|
gtk_list_box_update_cursor (box, row, TRUE);
|
2014-03-29 01:34:39 +00:00
|
|
|
|
if (!modify)
|
2014-03-29 03:07:41 +00:00
|
|
|
|
gtk_list_box_update_selection (box, row, FALSE, extend);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_new:
|
|
|
|
|
*
|
|
|
|
|
* Creates a new #GtkListBoxRow, to be used as a child of a #GtkListBox.
|
|
|
|
|
*
|
|
|
|
|
* Returns: a new #GtkListBoxRow
|
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
GtkWidget *
|
|
|
|
|
gtk_list_box_row_new (void)
|
|
|
|
|
{
|
|
|
|
|
return g_object_new (GTK_TYPE_LIST_BOX_ROW, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2020-05-03 04:09:18 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_set_child:
|
|
|
|
|
* @row: a #GtkListBoxRow
|
|
|
|
|
* @child: (allow-none): the child widget
|
|
|
|
|
*
|
|
|
|
|
* Sets the child widget of @self.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
gtk_list_box_row_set_child (GtkListBoxRow *row,
|
|
|
|
|
GtkWidget *child)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRowPrivate *priv = ROW_PRIV (row);
|
|
|
|
|
|
|
|
|
|
g_clear_pointer (&priv->child, gtk_widget_unparent);
|
|
|
|
|
|
|
|
|
|
priv->child = child;
|
|
|
|
|
if (child)
|
|
|
|
|
gtk_widget_set_parent (child, GTK_WIDGET (row));
|
|
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (row), row_properties[ROW_PROP_CHILD]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_get_child:
|
|
|
|
|
* @row: a #GtkListBoxRow
|
|
|
|
|
*
|
|
|
|
|
* Gets the child widget of @row.
|
|
|
|
|
*
|
|
|
|
|
* Returns: (nullable) (transfer none): the child widget of @row
|
|
|
|
|
*/
|
|
|
|
|
GtkWidget *
|
|
|
|
|
gtk_list_box_row_get_child (GtkListBoxRow *row)
|
|
|
|
|
{
|
|
|
|
|
return ROW_PRIV (row)->child;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_set_focus (GtkListBoxRow *row)
|
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
GtkListBox *box = gtk_list_box_row_get_box (row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
if (!box)
|
2013-10-03 12:00:30 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-04-11 18:52:43 +00:00
|
|
|
|
gtk_list_box_update_selection (box, row, FALSE, FALSE);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static gboolean
|
2013-09-28 01:39:10 +00:00
|
|
|
|
gtk_list_box_row_focus (GtkWidget *widget,
|
|
|
|
|
GtkDirectionType direction)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
|
|
|
|
|
gboolean had_focus = FALSE;
|
2020-05-03 04:09:18 +00:00
|
|
|
|
GtkWidget *child = ROW_PRIV (row)->child;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
g_object_get (widget, "has-focus", &had_focus, NULL);
|
|
|
|
|
if (had_focus)
|
|
|
|
|
{
|
|
|
|
|
/* If on row, going right, enter into possible container */
|
|
|
|
|
if (child &&
|
|
|
|
|
(direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD))
|
|
|
|
|
{
|
|
|
|
|
if (gtk_widget_child_focus (GTK_WIDGET (child), direction))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
2017-02-08 08:00:29 +00:00
|
|
|
|
else if (gtk_widget_get_focus_child (widget) != NULL)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
/* Child has focus, always navigate inside it first */
|
2018-01-16 14:12:24 +00:00
|
|
|
|
if (gtk_widget_child_focus (gtk_widget_get_focus_child (widget), direction))
|
2013-06-10 09:17:10 +00:00
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
|
|
/* If exiting child container to the left, select row */
|
|
|
|
|
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
|
|
|
|
|
{
|
2020-05-14 01:45:25 +00:00
|
|
|
|
/* grab focus explicitly, since gtk_list_box_row_set_focus()
|
|
|
|
|
* refuses to steal it from a child
|
|
|
|
|
*/
|
|
|
|
|
gtk_widget_grab_focus (GTK_WIDGET (row));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
gtk_list_box_row_set_focus (row);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* If coming from the left, enter into possible container */
|
|
|
|
|
if (child &&
|
|
|
|
|
(direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD))
|
|
|
|
|
{
|
|
|
|
|
if (gtk_widget_child_focus (child, direction))
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
gtk_list_box_row_set_focus (row);
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-10 12:12:07 +00:00
|
|
|
|
static void
|
2013-09-28 01:39:10 +00:00
|
|
|
|
gtk_list_box_row_activate (GtkListBoxRow *row)
|
2013-09-10 12:12:07 +00:00
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
GtkListBox *box;
|
2013-09-10 12:12:07 +00:00
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
box = gtk_list_box_row_get_box (row);
|
|
|
|
|
if (box)
|
|
|
|
|
gtk_list_box_select_and_activate (box, row);
|
2013-09-10 12:12:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
2013-09-28 01:39:10 +00:00
|
|
|
|
gtk_list_box_row_show (GtkWidget *widget)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
GtkListBox *box;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
GTK_WIDGET_CLASS (gtk_list_box_row_parent_class)->show (widget);
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
box = gtk_list_box_row_get_box (row);
|
|
|
|
|
if (box)
|
|
|
|
|
gtk_list_box_row_visibility_changed (box, row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
2013-09-28 01:39:10 +00:00
|
|
|
|
gtk_list_box_row_hide (GtkWidget *widget)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
|
2014-03-29 03:07:41 +00:00
|
|
|
|
GtkListBox *box;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
GTK_WIDGET_CLASS (gtk_list_box_row_parent_class)->hide (widget);
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
box = gtk_list_box_row_get_box (row);
|
|
|
|
|
if (box)
|
|
|
|
|
gtk_list_box_row_visibility_changed (box, row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-11 10:59:32 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_changed:
|
|
|
|
|
* @row: a #GtkListBoxRow
|
|
|
|
|
*
|
|
|
|
|
* Marks @row as changed, causing any state that depends on this
|
2013-06-24 13:58:10 +00:00
|
|
|
|
* to be updated. This affects sorting, filtering and headers.
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*
|
|
|
|
|
* Note that calls to this method must be in sync with the data
|
|
|
|
|
* used for the row functions. For instance, if the list is
|
|
|
|
|
* mirroring some external data set, and *two* rows changed in the
|
|
|
|
|
* external data set then when you call gtk_list_box_row_changed()
|
|
|
|
|
* on the first row the sort function must only read the new data
|
|
|
|
|
* for the first of the two changed rows, otherwise the resorting
|
|
|
|
|
* of the rows will be wrong.
|
|
|
|
|
*
|
2014-02-07 18:32:47 +00:00
|
|
|
|
* This generally means that if you don’t fully control the data
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* model you have to duplicate the data that affects the listbox
|
|
|
|
|
* row functions into the row widgets themselves. Another alternative
|
2013-06-15 16:13:28 +00:00
|
|
|
|
* is to call gtk_list_box_invalidate_sort() on any model change,
|
|
|
|
|
* but that is more expensive.
|
2013-06-11 10:59:32 +00:00
|
|
|
|
*/
|
2013-06-10 09:17:10 +00:00
|
|
|
|
void
|
|
|
|
|
gtk_list_box_row_changed (GtkListBoxRow *row)
|
|
|
|
|
{
|
2014-03-29 03:07:41 +00:00
|
|
|
|
GtkListBox *box;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2013-07-04 13:50:09 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX_ROW (row));
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
box = gtk_list_box_row_get_box (row);
|
|
|
|
|
if (box)
|
|
|
|
|
gtk_list_box_got_row_changed (box, row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* gtk_list_box_row_get_header:
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @row: a #GtkListBoxRow
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* Returns the current header of the @row. This can be used
|
2013-06-24 13:58:10 +00:00
|
|
|
|
* in a #GtkListBoxUpdateHeaderFunc to see if there is a header
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* set already, and if so to update the state of it.
|
|
|
|
|
*
|
2015-12-01 12:40:46 +00:00
|
|
|
|
* Returns: (transfer none) (nullable): the current header, or %NULL if none
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*/
|
|
|
|
|
GtkWidget *
|
2013-06-11 12:09:19 +00:00
|
|
|
|
gtk_list_box_row_get_header (GtkListBoxRow *row)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2013-07-04 13:50:09 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX_ROW (row), NULL);
|
|
|
|
|
|
2013-09-28 01:39:10 +00:00
|
|
|
|
return ROW_PRIV (row)->header;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* gtk_list_box_row_set_header:
|
2013-06-11 10:59:32 +00:00
|
|
|
|
* @row: a #GtkListBoxRow
|
2013-06-15 16:13:28 +00:00
|
|
|
|
* @header: (allow-none): the header, or %NULL
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* Sets the current header of the @row. This is only allowed to be called
|
2013-06-24 13:58:10 +00:00
|
|
|
|
* from a #GtkListBoxUpdateHeaderFunc. It will replace any existing
|
2013-06-11 12:09:19 +00:00
|
|
|
|
* header in the row, and be shown in front of the row in the listbox.
|
2013-06-10 09:17:10 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2013-06-11 12:09:19 +00:00
|
|
|
|
gtk_list_box_row_set_header (GtkListBoxRow *row,
|
2013-06-15 16:13:28 +00:00
|
|
|
|
GtkWidget *header)
|
2013-06-10 09:17:10 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
GtkListBoxRowPrivate *priv = ROW_PRIV (row);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2013-07-04 13:50:09 +00:00
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX_ROW (row));
|
|
|
|
|
g_return_if_fail (header == NULL || GTK_IS_WIDGET (header));
|
|
|
|
|
|
2013-07-02 11:44:17 +00:00
|
|
|
|
if (priv->header)
|
|
|
|
|
g_object_unref (priv->header);
|
|
|
|
|
|
|
|
|
|
priv->header = header;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2013-06-11 12:09:19 +00:00
|
|
|
|
if (header)
|
2013-09-04 19:00:23 +00:00
|
|
|
|
g_object_ref_sink (header);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-07-04 11:40:21 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_get_index:
|
|
|
|
|
* @row: a #GtkListBoxRow
|
|
|
|
|
*
|
|
|
|
|
* Gets the current index of the @row in its #GtkListBox container.
|
|
|
|
|
*
|
|
|
|
|
* Returns: the index of the @row, or -1 if the @row is not in a listbox
|
|
|
|
|
*/
|
2020-07-24 13:54:49 +00:00
|
|
|
|
int
|
2013-07-04 11:40:21 +00:00
|
|
|
|
gtk_list_box_row_get_index (GtkListBoxRow *row)
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
GtkListBoxRowPrivate *priv = ROW_PRIV (row);
|
2013-07-04 11:40:21 +00:00
|
|
|
|
|
2013-07-04 13:50:09 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX_ROW (row), -1);
|
|
|
|
|
|
2013-07-04 11:40:21 +00:00
|
|
|
|
if (priv->iter != NULL)
|
|
|
|
|
return g_sequence_iter_get_position (priv->iter);
|
|
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-03-28 23:21:57 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_is_selected:
|
|
|
|
|
* @row: a #GtkListBoxRow
|
|
|
|
|
*
|
|
|
|
|
* Returns whether the child is currently selected in its
|
|
|
|
|
* #GtkListBox container.
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE if @row is selected
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
gtk_list_box_row_is_selected (GtkListBoxRow *row)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX_ROW (row), FALSE);
|
|
|
|
|
|
2014-03-29 03:07:41 +00:00
|
|
|
|
return ROW_PRIV (row)->selected;
|
2014-03-28 23:21:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-07-16 02:42:29 +00:00
|
|
|
|
static void
|
2014-07-16 03:13:43 +00:00
|
|
|
|
gtk_list_box_update_row_style (GtkListBox *box,
|
|
|
|
|
GtkListBoxRow *row)
|
2014-07-16 02:42:29 +00:00
|
|
|
|
{
|
2014-07-16 03:13:43 +00:00
|
|
|
|
gboolean can_select;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box && box->selection_mode != GTK_SELECTION_NONE)
|
2014-07-16 03:13:43 +00:00
|
|
|
|
can_select = TRUE;
|
|
|
|
|
else
|
|
|
|
|
can_select = FALSE;
|
2014-07-16 02:42:29 +00:00
|
|
|
|
|
2014-07-16 03:13:43 +00:00
|
|
|
|
if (ROW_PRIV (row)->activatable ||
|
|
|
|
|
(ROW_PRIV (row)->selectable && can_select))
|
2020-02-06 16:32:26 +00:00
|
|
|
|
gtk_widget_add_css_class (GTK_WIDGET (row), "activatable");
|
2014-07-16 02:42:29 +00:00
|
|
|
|
else
|
2020-02-06 16:32:26 +00:00
|
|
|
|
gtk_widget_remove_css_class (GTK_WIDGET (row), "activatable");
|
2014-07-16 02:42:29 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-07-16 03:13:43 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_update_row_styles (GtkListBox *box)
|
|
|
|
|
{
|
|
|
|
|
GSequenceIter *iter;
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
for (iter = g_sequence_get_begin_iter (box->children);
|
2014-07-16 03:13:43 +00:00
|
|
|
|
!g_sequence_iter_is_end (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter))
|
|
|
|
|
{
|
|
|
|
|
row = g_sequence_get (iter);
|
|
|
|
|
gtk_list_box_update_row_style (box, row);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 00:37:41 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_set_activatable:
|
2015-02-18 10:57:05 +00:00
|
|
|
|
* @row: a #GtkListBoxRow
|
2014-07-16 03:18:27 +00:00
|
|
|
|
* @activatable: %TRUE to mark the row as activatable
|
2014-07-13 00:37:41 +00:00
|
|
|
|
*
|
|
|
|
|
* Set the #GtkListBoxRow:activatable property for this row.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
gtk_list_box_row_set_activatable (GtkListBoxRow *row,
|
|
|
|
|
gboolean activatable)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX_ROW (row));
|
|
|
|
|
|
|
|
|
|
activatable = activatable != FALSE;
|
|
|
|
|
|
2014-07-16 02:42:29 +00:00
|
|
|
|
if (ROW_PRIV (row)->activatable != activatable)
|
|
|
|
|
{
|
|
|
|
|
ROW_PRIV (row)->activatable = activatable;
|
2014-07-13 00:37:41 +00:00
|
|
|
|
|
2014-07-16 03:13:43 +00:00
|
|
|
|
gtk_list_box_update_row_style (gtk_list_box_row_get_box (row), row);
|
2015-09-06 14:39:57 +00:00
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (row), row_properties[ROW_PROP_ACTIVATABLE]);
|
2014-07-16 02:42:29 +00:00
|
|
|
|
}
|
2014-07-13 00:37:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_get_activatable:
|
|
|
|
|
* @row: a #GtkListBoxRow
|
|
|
|
|
*
|
|
|
|
|
* Gets the value of the #GtkListBoxRow:activatable property
|
|
|
|
|
* for this row.
|
|
|
|
|
*
|
2014-07-16 03:18:27 +00:00
|
|
|
|
* Returns: %TRUE if the row is activatable
|
2014-07-13 00:37:41 +00:00
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
gtk_list_box_row_get_activatable (GtkListBoxRow *row)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX_ROW (row), TRUE);
|
|
|
|
|
|
2014-07-16 02:42:29 +00:00
|
|
|
|
return ROW_PRIV (row)->activatable;
|
2014-07-13 00:37:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-07-16 03:10:51 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_set_selectable:
|
2016-02-08 21:49:01 +00:00
|
|
|
|
* @row: a #GtkListBoxRow
|
2014-07-16 03:10:51 +00:00
|
|
|
|
* @selectable: %TRUE to mark the row as selectable
|
|
|
|
|
*
|
|
|
|
|
* Set the #GtkListBoxRow:selectable property for this row.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
gtk_list_box_row_set_selectable (GtkListBoxRow *row,
|
|
|
|
|
gboolean selectable)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX_ROW (row));
|
|
|
|
|
|
|
|
|
|
selectable = selectable != FALSE;
|
|
|
|
|
|
|
|
|
|
if (ROW_PRIV (row)->selectable != selectable)
|
|
|
|
|
{
|
|
|
|
|
if (!selectable)
|
|
|
|
|
gtk_list_box_row_set_selected (row, FALSE);
|
|
|
|
|
|
|
|
|
|
ROW_PRIV (row)->selectable = selectable;
|
|
|
|
|
|
2014-07-16 03:13:43 +00:00
|
|
|
|
gtk_list_box_update_row_style (gtk_list_box_row_get_box (row), row);
|
2015-09-06 14:39:57 +00:00
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (row), row_properties[ROW_PROP_SELECTABLE]);
|
2014-07-16 03:10:51 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_row_get_selectable:
|
|
|
|
|
* @row: a #GtkListBoxRow
|
|
|
|
|
*
|
|
|
|
|
* Gets the value of the #GtkListBoxRow:selectable property
|
|
|
|
|
* for this row.
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE if the row is selectable
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
gtk_list_box_row_get_selectable (GtkListBoxRow *row)
|
|
|
|
|
{
|
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX_ROW (row), TRUE);
|
|
|
|
|
|
|
|
|
|
return ROW_PRIV (row)->selectable;
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-24 13:26:39 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_set_action_name (GtkActionable *actionable,
|
2020-07-24 18:40:36 +00:00
|
|
|
|
const char *action_name)
|
2017-11-24 13:26:39 +00:00
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (actionable);
|
|
|
|
|
GtkListBoxRowPrivate *priv = ROW_PRIV (row);
|
|
|
|
|
|
|
|
|
|
if (!priv->action_helper)
|
|
|
|
|
priv->action_helper = gtk_action_helper_new (actionable);
|
|
|
|
|
|
|
|
|
|
gtk_action_helper_set_action_name (priv->action_helper, action_name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_set_action_target_value (GtkActionable *actionable,
|
|
|
|
|
GVariant *action_target)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (actionable);
|
|
|
|
|
GtkListBoxRowPrivate *priv = ROW_PRIV (row);
|
|
|
|
|
|
|
|
|
|
if (!priv->action_helper)
|
|
|
|
|
priv->action_helper = gtk_action_helper_new (actionable);
|
|
|
|
|
|
|
|
|
|
gtk_action_helper_set_action_target_value (priv->action_helper, action_target);
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-13 00:37:41 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_get_property (GObject *obj,
|
|
|
|
|
guint property_id,
|
|
|
|
|
GValue *value,
|
|
|
|
|
GParamSpec *pspec)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (obj);
|
|
|
|
|
|
|
|
|
|
switch (property_id)
|
|
|
|
|
{
|
|
|
|
|
case ROW_PROP_ACTIVATABLE:
|
|
|
|
|
g_value_set_boolean (value, gtk_list_box_row_get_activatable (row));
|
|
|
|
|
break;
|
2014-07-16 03:10:51 +00:00
|
|
|
|
case ROW_PROP_SELECTABLE:
|
|
|
|
|
g_value_set_boolean (value, gtk_list_box_row_get_selectable (row));
|
|
|
|
|
break;
|
2017-11-24 13:26:39 +00:00
|
|
|
|
case ROW_PROP_ACTION_NAME:
|
|
|
|
|
g_value_set_string (value, gtk_action_helper_get_action_name (ROW_PRIV (row)->action_helper));
|
|
|
|
|
break;
|
|
|
|
|
case ROW_PROP_ACTION_TARGET:
|
|
|
|
|
g_value_set_variant (value, gtk_action_helper_get_action_target_value (ROW_PRIV (row)->action_helper));
|
|
|
|
|
break;
|
2020-05-03 04:09:18 +00:00
|
|
|
|
case ROW_PROP_CHILD:
|
|
|
|
|
g_value_set_object (value, gtk_list_box_row_get_child (row));
|
|
|
|
|
break;
|
2014-07-13 00:37:41 +00:00
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_set_property (GObject *obj,
|
|
|
|
|
guint property_id,
|
|
|
|
|
const GValue *value,
|
|
|
|
|
GParamSpec *pspec)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (obj);
|
|
|
|
|
|
|
|
|
|
switch (property_id)
|
|
|
|
|
{
|
|
|
|
|
case ROW_PROP_ACTIVATABLE:
|
|
|
|
|
gtk_list_box_row_set_activatable (row, g_value_get_boolean (value));
|
|
|
|
|
break;
|
2014-07-16 03:10:51 +00:00
|
|
|
|
case ROW_PROP_SELECTABLE:
|
|
|
|
|
gtk_list_box_row_set_selectable (row, g_value_get_boolean (value));
|
|
|
|
|
break;
|
2017-11-24 13:26:39 +00:00
|
|
|
|
case ROW_PROP_ACTION_NAME:
|
|
|
|
|
gtk_list_box_row_set_action_name (GTK_ACTIONABLE (row), g_value_get_string (value));
|
|
|
|
|
break;
|
|
|
|
|
case ROW_PROP_ACTION_TARGET:
|
|
|
|
|
gtk_list_box_row_set_action_target_value (GTK_ACTIONABLE (row), g_value_get_variant (value));
|
|
|
|
|
break;
|
2020-05-03 04:09:18 +00:00
|
|
|
|
case ROW_PROP_CHILD:
|
|
|
|
|
gtk_list_box_row_set_child (row, g_value_get_object (value));
|
|
|
|
|
break;
|
2014-07-13 00:37:41 +00:00
|
|
|
|
default:
|
|
|
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, property_id, pspec);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-07-24 18:40:36 +00:00
|
|
|
|
static const char *
|
2017-11-24 13:26:39 +00:00
|
|
|
|
gtk_list_box_row_get_action_name (GtkActionable *actionable)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (actionable);
|
|
|
|
|
|
|
|
|
|
return gtk_action_helper_get_action_name (ROW_PRIV (row)->action_helper);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static GVariant *
|
|
|
|
|
gtk_list_box_row_get_action_target_value (GtkActionable *actionable)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (actionable);
|
|
|
|
|
|
|
|
|
|
return gtk_action_helper_get_action_target_value (ROW_PRIV (row)->action_helper);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_actionable_iface_init (GtkActionableInterface *iface)
|
|
|
|
|
{
|
|
|
|
|
iface->get_action_name = gtk_list_box_row_get_action_name;
|
|
|
|
|
iface->set_action_name = gtk_list_box_row_set_action_name;
|
|
|
|
|
iface->get_action_target_value = gtk_list_box_row_get_action_target_value;
|
|
|
|
|
iface->set_action_target_value = gtk_list_box_row_set_action_target_value;
|
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_finalize (GObject *obj)
|
|
|
|
|
{
|
2013-09-28 01:39:10 +00:00
|
|
|
|
g_clear_object (&ROW_PRIV (GTK_LIST_BOX_ROW (obj))->header);
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (gtk_list_box_row_parent_class)->finalize (obj);
|
|
|
|
|
}
|
|
|
|
|
|
2017-11-24 13:26:39 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_dispose (GObject *object)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (object);
|
|
|
|
|
GtkListBoxRowPrivate *priv = ROW_PRIV (row);
|
|
|
|
|
|
|
|
|
|
g_clear_object (&priv->action_helper);
|
2020-05-03 04:09:18 +00:00
|
|
|
|
g_clear_pointer (&priv->child, gtk_widget_unparent);
|
2017-11-24 13:26:39 +00:00
|
|
|
|
|
|
|
|
|
G_OBJECT_CLASS (gtk_list_box_row_parent_class)->dispose (object);
|
|
|
|
|
}
|
|
|
|
|
|
2019-10-15 13:34:16 +00:00
|
|
|
|
static gboolean
|
2015-02-10 19:02:08 +00:00
|
|
|
|
gtk_list_box_row_grab_focus (GtkWidget *widget)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row = GTK_LIST_BOX_ROW (widget);
|
|
|
|
|
GtkListBox *box = gtk_list_box_row_get_box (row);
|
|
|
|
|
|
2019-10-15 13:34:16 +00:00
|
|
|
|
g_return_val_if_fail (box != NULL, FALSE);
|
|
|
|
|
|
2020-04-08 11:43:28 +00:00
|
|
|
|
gtk_widget_grab_focus_self (widget);
|
2015-02-26 01:15:18 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->cursor_row != row)
|
2015-02-10 19:02:08 +00:00
|
|
|
|
gtk_list_box_update_cursor (box, row, FALSE);
|
|
|
|
|
|
2019-10-15 13:34:16 +00:00
|
|
|
|
return TRUE;
|
2015-02-10 19:02:08 +00:00
|
|
|
|
}
|
|
|
|
|
|
2013-06-10 09:17:10 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_class_init (GtkListBoxRowClass *klass)
|
|
|
|
|
{
|
|
|
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
|
|
|
|
|
|
2014-07-13 00:37:41 +00:00
|
|
|
|
object_class->get_property = gtk_list_box_row_get_property;
|
|
|
|
|
object_class->set_property = gtk_list_box_row_set_property;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
object_class->finalize = gtk_list_box_row_finalize;
|
2017-11-24 13:26:39 +00:00
|
|
|
|
object_class->dispose = gtk_list_box_row_dispose;
|
2013-06-10 09:17:10 +00:00
|
|
|
|
|
2013-09-28 01:39:10 +00:00
|
|
|
|
widget_class->show = gtk_list_box_row_show;
|
|
|
|
|
widget_class->hide = gtk_list_box_row_hide;
|
|
|
|
|
widget_class->focus = gtk_list_box_row_focus;
|
2015-02-10 19:02:08 +00:00
|
|
|
|
widget_class->grab_focus = gtk_list_box_row_grab_focus;
|
2013-09-28 01:39:10 +00:00
|
|
|
|
|
|
|
|
|
klass->activate = gtk_list_box_row_activate;
|
2013-09-10 12:12:07 +00:00
|
|
|
|
|
2018-03-12 16:08:29 +00:00
|
|
|
|
/**
|
|
|
|
|
* GtkListBoxRow::activate:
|
|
|
|
|
*
|
|
|
|
|
* This is a keybinding signal, which will cause this row to be activated.
|
|
|
|
|
*
|
|
|
|
|
* If you want to be notified when the user activates a row (by key or not),
|
|
|
|
|
* use the #GtkListBox::row-activated signal on the row’s parent #GtkListBox.
|
|
|
|
|
*/
|
2013-09-10 12:12:07 +00:00
|
|
|
|
row_signals[ROW__ACTIVATE] =
|
|
|
|
|
g_signal_new (I_("activate"),
|
|
|
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
|
|
|
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
|
|
|
|
G_STRUCT_OFFSET (GtkListBoxRowClass, activate),
|
|
|
|
|
NULL, NULL,
|
2016-08-29 14:00:17 +00:00
|
|
|
|
NULL,
|
2013-09-10 12:12:07 +00:00
|
|
|
|
G_TYPE_NONE, 0);
|
2014-07-13 00:37:41 +00:00
|
|
|
|
|
2013-09-10 12:12:07 +00:00
|
|
|
|
widget_class->activate_signal = row_signals[ROW__ACTIVATE];
|
2014-07-13 00:37:41 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* GtkListBoxRow:activatable:
|
|
|
|
|
*
|
|
|
|
|
* The property determines whether the #GtkListBox::row-activated
|
|
|
|
|
* signal will be emitted for this row.
|
|
|
|
|
*/
|
|
|
|
|
row_properties[ROW_PROP_ACTIVATABLE] =
|
|
|
|
|
g_param_spec_boolean ("activatable",
|
|
|
|
|
P_("Activatable"),
|
|
|
|
|
P_("Whether this row can be activated"),
|
|
|
|
|
TRUE,
|
|
|
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
|
2014-07-16 03:10:51 +00:00
|
|
|
|
/**
|
2014-08-16 20:39:51 +00:00
|
|
|
|
* GtkListBoxRow:selectable:
|
2014-07-16 03:10:51 +00:00
|
|
|
|
*
|
|
|
|
|
* The property determines whether this row can be selected.
|
|
|
|
|
*/
|
|
|
|
|
row_properties[ROW_PROP_SELECTABLE] =
|
|
|
|
|
g_param_spec_boolean ("selectable",
|
|
|
|
|
P_("Selectable"),
|
|
|
|
|
P_("Whether this row can be selected"),
|
|
|
|
|
TRUE,
|
|
|
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
|
2020-05-03 04:09:18 +00:00
|
|
|
|
row_properties[ROW_PROP_CHILD] =
|
|
|
|
|
g_param_spec_object ("child",
|
|
|
|
|
P_("Child"),
|
|
|
|
|
P_("The child widget"),
|
|
|
|
|
GTK_TYPE_WIDGET,
|
|
|
|
|
G_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
|
|
|
|
|
|
2014-07-13 00:37:41 +00:00
|
|
|
|
g_object_class_install_properties (object_class, LAST_ROW_PROPERTY, row_properties);
|
|
|
|
|
|
2017-11-24 13:26:39 +00:00
|
|
|
|
g_object_class_override_property (object_class, ROW_PROP_ACTION_NAME, "action-name");
|
|
|
|
|
g_object_class_override_property (object_class, ROW_PROP_ACTION_TARGET, "action-target");
|
|
|
|
|
|
2020-05-03 04:09:18 +00:00
|
|
|
|
gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
|
2017-11-18 03:49:57 +00:00
|
|
|
|
gtk_widget_class_set_css_name (widget_class, I_("row"));
|
2013-06-10 09:17:10 +00:00
|
|
|
|
}
|
2014-10-12 22:54:44 +00:00
|
|
|
|
|
2015-12-10 05:43:58 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_row_init (GtkListBoxRow *row)
|
|
|
|
|
{
|
|
|
|
|
ROW_PRIV (row)->activatable = TRUE;
|
|
|
|
|
ROW_PRIV (row)->selectable = TRUE;
|
|
|
|
|
|
2020-05-05 18:57:21 +00:00
|
|
|
|
gtk_widget_set_focusable (GTK_WIDGET (row), TRUE);
|
2020-02-06 16:32:26 +00:00
|
|
|
|
gtk_widget_add_css_class (GTK_WIDGET (row), "activatable");
|
2015-12-10 05:43:58 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-12 22:54:44 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_buildable_add_child (GtkBuildable *buildable,
|
|
|
|
|
GtkBuilder *builder,
|
|
|
|
|
GObject *child,
|
2020-07-24 18:40:36 +00:00
|
|
|
|
const char *type)
|
2014-10-12 22:54:44 +00:00
|
|
|
|
{
|
|
|
|
|
if (type && strcmp (type, "placeholder") == 0)
|
|
|
|
|
gtk_list_box_set_placeholder (GTK_LIST_BOX (buildable), GTK_WIDGET (child));
|
2020-05-08 12:48:00 +00:00
|
|
|
|
else if (GTK_IS_WIDGET (child))
|
|
|
|
|
gtk_list_box_insert (GTK_LIST_BOX (buildable), GTK_WIDGET (child), -1);
|
2014-10-12 22:54:44 +00:00
|
|
|
|
else
|
2018-03-08 02:13:57 +00:00
|
|
|
|
parent_buildable_iface->add_child (buildable, builder, child, type);
|
2014-10-12 22:54:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_buildable_interface_init (GtkBuildableIface *iface)
|
|
|
|
|
{
|
2018-03-08 02:13:57 +00:00
|
|
|
|
parent_buildable_iface = g_type_interface_peek_parent (iface);
|
|
|
|
|
|
2014-10-12 22:54:44 +00:00
|
|
|
|
iface->add_child = gtk_list_box_buildable_add_child;
|
|
|
|
|
}
|
2015-02-09 10:49:31 +00:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_bound_model_changed (GListModel *list,
|
|
|
|
|
guint position,
|
|
|
|
|
guint removed,
|
|
|
|
|
guint added,
|
|
|
|
|
gpointer user_data)
|
|
|
|
|
{
|
|
|
|
|
GtkListBox *box = user_data;
|
2016-04-16 11:57:08 +00:00
|
|
|
|
guint i;
|
2015-02-09 10:49:31 +00:00
|
|
|
|
|
|
|
|
|
while (removed--)
|
|
|
|
|
{
|
|
|
|
|
GtkListBoxRow *row;
|
|
|
|
|
|
|
|
|
|
row = gtk_list_box_get_row_at_index (box, position);
|
2020-05-08 12:48:00 +00:00
|
|
|
|
gtk_list_box_remove (box, GTK_WIDGET (row));
|
2015-02-09 10:49:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < added; i++)
|
|
|
|
|
{
|
|
|
|
|
GObject *item;
|
|
|
|
|
GtkWidget *widget;
|
|
|
|
|
|
|
|
|
|
item = g_list_model_get_item (list, position + i);
|
2020-03-28 20:52:41 +00:00
|
|
|
|
widget = box->create_widget_func (item, box->create_widget_func_data);
|
2015-03-27 15:57:38 +00:00
|
|
|
|
|
|
|
|
|
/* We allow the create_widget_func to either return a full
|
|
|
|
|
* reference or a floating reference. If we got the floating
|
|
|
|
|
* reference, then turn it into a full reference now. That means
|
|
|
|
|
* that gtk_list_box_insert() will take another full reference.
|
|
|
|
|
* Finally, we'll release this full reference below, leaving only
|
|
|
|
|
* the one held by the box.
|
|
|
|
|
*/
|
|
|
|
|
if (g_object_is_floating (widget))
|
|
|
|
|
g_object_ref_sink (widget);
|
|
|
|
|
|
2015-08-08 14:33:42 +00:00
|
|
|
|
gtk_widget_show (widget);
|
2015-02-09 10:49:31 +00:00
|
|
|
|
gtk_list_box_insert (box, widget, position + i);
|
|
|
|
|
|
2015-03-27 15:57:38 +00:00
|
|
|
|
g_object_unref (widget);
|
2015-02-09 10:49:31 +00:00
|
|
|
|
g_object_unref (item);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-08-25 17:16:24 +00:00
|
|
|
|
static void
|
|
|
|
|
gtk_list_box_check_model_compat (GtkListBox *box)
|
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->bound_model &&
|
|
|
|
|
(box->sort_func || box->filter_func))
|
2015-08-25 17:16:24 +00:00
|
|
|
|
g_warning ("GtkListBox with a model will ignore sort and filter functions");
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-09 10:49:31 +00:00
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_bind_model:
|
|
|
|
|
* @box: a #GtkListBox
|
2015-09-01 08:41:42 +00:00
|
|
|
|
* @model: (nullable): the #GListModel to be bound to @box
|
|
|
|
|
* @create_widget_func: (nullable): a function that creates widgets for items
|
|
|
|
|
* or %NULL in case you also passed %NULL as @model
|
2020-01-13 01:26:50 +00:00
|
|
|
|
* @user_data: (closure): user data passed to @create_widget_func
|
2015-02-09 10:49:31 +00:00
|
|
|
|
* @user_data_free_func: function for freeing @user_data
|
|
|
|
|
*
|
|
|
|
|
* Binds @model to @box.
|
|
|
|
|
*
|
|
|
|
|
* If @box was already bound to a model, that previous binding is
|
|
|
|
|
* destroyed.
|
|
|
|
|
*
|
|
|
|
|
* The contents of @box are cleared and then filled with widgets that
|
|
|
|
|
* represent items from @model. @box is updated whenever @model changes.
|
|
|
|
|
* If @model is %NULL, @box is left empty.
|
|
|
|
|
*
|
|
|
|
|
* It is undefined to add or remove widgets directly (for example, with
|
|
|
|
|
* gtk_list_box_insert() or gtk_container_add()) while @box is bound to a
|
|
|
|
|
* model.
|
|
|
|
|
*
|
2015-08-25 17:20:53 +00:00
|
|
|
|
* Note that using a model is incompatible with the filtering and sorting
|
|
|
|
|
* functionality in GtkListBox. When using a model, filtering and sorting
|
|
|
|
|
* should be implemented by the model.
|
2015-02-09 10:49:31 +00:00
|
|
|
|
*/
|
|
|
|
|
void
|
2015-08-25 17:16:24 +00:00
|
|
|
|
gtk_list_box_bind_model (GtkListBox *box,
|
|
|
|
|
GListModel *model,
|
|
|
|
|
GtkListBoxCreateWidgetFunc create_widget_func,
|
|
|
|
|
gpointer user_data,
|
|
|
|
|
GDestroyNotify user_data_free_func)
|
2015-02-09 10:49:31 +00:00
|
|
|
|
{
|
2017-05-24 08:00:15 +00:00
|
|
|
|
GSequenceIter *iter;
|
2015-02-09 10:49:31 +00:00
|
|
|
|
|
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
|
|
|
|
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
|
|
|
|
|
g_return_if_fail (model == NULL || create_widget_func != NULL);
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->bound_model)
|
2015-02-09 10:49:31 +00:00
|
|
|
|
{
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->create_widget_func_data_destroy)
|
|
|
|
|
box->create_widget_func_data_destroy (box->create_widget_func_data);
|
2015-02-09 10:49:31 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_signal_handlers_disconnect_by_func (box->bound_model, gtk_list_box_bound_model_changed, box);
|
|
|
|
|
g_clear_object (&box->bound_model);
|
2015-02-09 10:49:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
iter = g_sequence_get_begin_iter (box->children);
|
2017-05-24 08:00:15 +00:00
|
|
|
|
while (!g_sequence_iter_is_end (iter))
|
|
|
|
|
{
|
|
|
|
|
GtkWidget *row = g_sequence_get (iter);
|
|
|
|
|
iter = g_sequence_iter_next (iter);
|
2020-05-08 12:48:00 +00:00
|
|
|
|
gtk_list_box_remove (box, row);
|
2017-05-24 08:00:15 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-02-09 10:49:31 +00:00
|
|
|
|
|
|
|
|
|
if (model == NULL)
|
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->bound_model = g_object_ref (model);
|
|
|
|
|
box->create_widget_func = create_widget_func;
|
|
|
|
|
box->create_widget_func_data = user_data;
|
|
|
|
|
box->create_widget_func_data_destroy = user_data_free_func;
|
2015-02-09 10:49:31 +00:00
|
|
|
|
|
2015-08-25 17:16:24 +00:00
|
|
|
|
gtk_list_box_check_model_compat (box);
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
g_signal_connect (box->bound_model, "items-changed", G_CALLBACK (gtk_list_box_bound_model_changed), box);
|
2015-02-09 10:49:31 +00:00
|
|
|
|
gtk_list_box_bound_model_changed (model, 0, 0, g_list_model_get_n_items (model), box);
|
|
|
|
|
}
|
2019-04-13 00:24:40 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_set_show_separators:
|
|
|
|
|
* @box: a #GtkListBox
|
|
|
|
|
* @show_separators: %TRUE to show separators
|
|
|
|
|
*
|
|
|
|
|
* Sets whether the list box should show separators
|
|
|
|
|
* between rows.
|
|
|
|
|
*/
|
|
|
|
|
void
|
|
|
|
|
gtk_list_box_set_show_separators (GtkListBox *box,
|
|
|
|
|
gboolean show_separators)
|
|
|
|
|
{
|
|
|
|
|
g_return_if_fail (GTK_IS_LIST_BOX (box));
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
if (box->show_separators == show_separators)
|
2019-04-13 00:24:40 +00:00
|
|
|
|
return;
|
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
box->show_separators = show_separators;
|
2019-04-13 00:24:40 +00:00
|
|
|
|
|
|
|
|
|
if (show_separators)
|
2020-02-06 16:32:26 +00:00
|
|
|
|
gtk_widget_add_css_class (GTK_WIDGET (box), "separators");
|
2019-04-13 00:24:40 +00:00
|
|
|
|
else
|
2020-02-06 16:32:26 +00:00
|
|
|
|
gtk_widget_remove_css_class (GTK_WIDGET (box), "separators");
|
2019-04-13 00:24:40 +00:00
|
|
|
|
|
|
|
|
|
g_object_notify_by_pspec (G_OBJECT (box), properties[PROP_SHOW_SEPARATORS]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* gtk_list_box_get_show_separators:
|
|
|
|
|
* @box: a #GtkListBox
|
|
|
|
|
*
|
|
|
|
|
* Returns whether the list box should show separators
|
|
|
|
|
* between rows.
|
|
|
|
|
*
|
|
|
|
|
* Returns: %TRUE if the list box shows separators
|
|
|
|
|
*/
|
|
|
|
|
gboolean
|
|
|
|
|
gtk_list_box_get_show_separators (GtkListBox *box)
|
|
|
|
|
{
|
2019-04-13 04:26:17 +00:00
|
|
|
|
g_return_val_if_fail (GTK_IS_LIST_BOX (box), FALSE);
|
2019-04-13 00:24:40 +00:00
|
|
|
|
|
2020-03-28 20:52:41 +00:00
|
|
|
|
return box->show_separators;
|
2019-04-13 00:24:40 +00:00
|
|
|
|
}
|