forked from AuroraMiddleware/gtk
ae7cefd97d
We document the supported style classes by name, not by macro name, and these macros don't really add any value. Drop them for GTK 4.
849 lines
24 KiB
C
849 lines
24 KiB
C
/*
|
|
* Copyright © 2019 Red Hat, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the licence, 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/>.
|
|
*
|
|
* Author: Matthias Clasen
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "gtktreepopoverprivate.h"
|
|
|
|
#include "gtktreemodel.h"
|
|
#include "gtkcellarea.h"
|
|
#include "gtkcelllayout.h"
|
|
#include "gtkcellview.h"
|
|
#include "gtkintl.h"
|
|
#include "gtkprivate.h"
|
|
#include "gtkgizmoprivate.h"
|
|
#include "gtkbuiltiniconprivate.h"
|
|
|
|
// TODO
|
|
// positioning + sizing
|
|
|
|
struct _GtkTreePopover
|
|
{
|
|
GtkPopover parent_instance;
|
|
|
|
GtkTreeModel *model;
|
|
|
|
GtkCellArea *area;
|
|
GtkCellAreaContext *context;
|
|
|
|
gulong size_changed_id;
|
|
gulong row_inserted_id;
|
|
gulong row_deleted_id;
|
|
gulong row_changed_id;
|
|
gulong row_reordered_id;
|
|
gulong apply_attributes_id;
|
|
|
|
GtkTreeViewRowSeparatorFunc row_separator_func;
|
|
gpointer row_separator_data;
|
|
GDestroyNotify row_separator_destroy;
|
|
|
|
GtkWidget *active_item;
|
|
};
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_MODEL,
|
|
PROP_CELL_AREA,
|
|
|
|
NUM_PROPERTIES
|
|
};
|
|
|
|
enum {
|
|
MENU_ACTIVATE,
|
|
NUM_SIGNALS
|
|
};
|
|
|
|
static guint signals[NUM_SIGNALS];
|
|
|
|
static void gtk_tree_popover_cell_layout_init (GtkCellLayoutIface *iface);
|
|
static void gtk_tree_popover_set_area (GtkTreePopover *popover,
|
|
GtkCellArea *area);
|
|
static void rebuild_menu (GtkTreePopover *popover);
|
|
static void context_size_changed_cb (GtkCellAreaContext *context,
|
|
GParamSpec *pspec,
|
|
GtkWidget *popover);
|
|
static GtkWidget * gtk_tree_popover_create_item (GtkTreePopover *popover,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
gboolean header_item);
|
|
static GtkWidget * gtk_tree_popover_get_path_item (GtkTreePopover *popover,
|
|
GtkTreePath *search);
|
|
static void gtk_tree_popover_set_active_item (GtkTreePopover *popover,
|
|
GtkWidget *item);
|
|
|
|
G_DEFINE_TYPE_WITH_CODE (GtkTreePopover, gtk_tree_popover, GTK_TYPE_POPOVER,
|
|
G_IMPLEMENT_INTERFACE (GTK_TYPE_CELL_LAYOUT,
|
|
gtk_tree_popover_cell_layout_init));
|
|
|
|
static void
|
|
gtk_tree_popover_constructed (GObject *object)
|
|
{
|
|
GtkTreePopover *popover = GTK_TREE_POPOVER (object);
|
|
|
|
G_OBJECT_CLASS (gtk_tree_popover_parent_class)->constructed (object);
|
|
|
|
if (!popover->area)
|
|
{
|
|
GtkCellArea *area = gtk_cell_area_box_new ();
|
|
gtk_tree_popover_set_area (popover, area);
|
|
}
|
|
|
|
popover->context = gtk_cell_area_create_context (popover->area);
|
|
|
|
popover->size_changed_id = g_signal_connect (popover->context, "notify",
|
|
G_CALLBACK (context_size_changed_cb), popover);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_dispose (GObject *object)
|
|
{
|
|
GtkTreePopover *popover = GTK_TREE_POPOVER (object);
|
|
|
|
gtk_tree_popover_set_model (popover, NULL);
|
|
gtk_tree_popover_set_area (popover, NULL);
|
|
|
|
if (popover->context)
|
|
{
|
|
g_signal_handler_disconnect (popover->context, popover->size_changed_id);
|
|
popover->size_changed_id = 0;
|
|
|
|
g_clear_object (&popover->context);
|
|
}
|
|
|
|
G_OBJECT_CLASS (gtk_tree_popover_parent_class)->dispose (object);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_finalize (GObject *object)
|
|
{
|
|
GtkTreePopover *popover = GTK_TREE_POPOVER (object);
|
|
|
|
if (popover->row_separator_destroy)
|
|
popover->row_separator_destroy (popover->row_separator_data);
|
|
|
|
G_OBJECT_CLASS (gtk_tree_popover_parent_class)->finalize (object);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_set_property (GObject *object,
|
|
guint prop_id,
|
|
const GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkTreePopover *popover = GTK_TREE_POPOVER (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_MODEL:
|
|
gtk_tree_popover_set_model (popover, g_value_get_object (value));
|
|
break;
|
|
|
|
case PROP_CELL_AREA:
|
|
gtk_tree_popover_set_area (popover, g_value_get_object (value));
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_get_property (GObject *object,
|
|
guint prop_id,
|
|
GValue *value,
|
|
GParamSpec *pspec)
|
|
{
|
|
GtkTreePopover *popover = GTK_TREE_POPOVER (object);
|
|
|
|
switch (prop_id)
|
|
{
|
|
case PROP_MODEL:
|
|
g_value_set_object (value, popover->model);
|
|
break;
|
|
|
|
case PROP_CELL_AREA:
|
|
g_value_set_object (value, popover->area);
|
|
break;
|
|
|
|
default:
|
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_class_init (GtkTreePopoverClass *class)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (class);
|
|
|
|
object_class->constructed = gtk_tree_popover_constructed;
|
|
object_class->dispose = gtk_tree_popover_dispose;
|
|
object_class->finalize = gtk_tree_popover_finalize;
|
|
object_class->set_property = gtk_tree_popover_set_property;
|
|
object_class->get_property = gtk_tree_popover_get_property;
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_MODEL,
|
|
g_param_spec_object ("model",
|
|
P_("model"),
|
|
P_("The model for the popover"),
|
|
GTK_TYPE_TREE_MODEL,
|
|
GTK_PARAM_READWRITE));
|
|
|
|
g_object_class_install_property (object_class,
|
|
PROP_CELL_AREA,
|
|
g_param_spec_object ("cell-area",
|
|
P_("Cell Area"),
|
|
P_("The GtkCellArea used to layout cells"),
|
|
GTK_TYPE_CELL_AREA,
|
|
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
signals[MENU_ACTIVATE] =
|
|
g_signal_new (I_("menu-activate"),
|
|
G_OBJECT_CLASS_TYPE (object_class),
|
|
G_SIGNAL_RUN_FIRST,
|
|
0,
|
|
NULL, NULL,
|
|
NULL,
|
|
G_TYPE_NONE, 1, G_TYPE_STRING);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_add_submenu (GtkTreePopover *popover,
|
|
GtkWidget *submenu,
|
|
const char *name)
|
|
{
|
|
GtkWidget *stack = gtk_popover_get_child (GTK_POPOVER (popover));
|
|
gtk_stack_add_named (GTK_STACK (stack), submenu, name);
|
|
}
|
|
|
|
static GtkWidget *
|
|
gtk_tree_popover_get_submenu (GtkTreePopover *popover,
|
|
const char *name)
|
|
{
|
|
GtkWidget *stack = gtk_popover_get_child (GTK_POPOVER (popover));
|
|
return gtk_stack_get_child_by_name (GTK_STACK (stack), name);
|
|
}
|
|
|
|
void
|
|
gtk_tree_popover_open_submenu (GtkTreePopover *popover,
|
|
const char *name)
|
|
{
|
|
GtkWidget *stack = gtk_popover_get_child (GTK_POPOVER (popover));
|
|
gtk_stack_set_visible_child_name (GTK_STACK (stack), name);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_init (GtkTreePopover *popover)
|
|
{
|
|
GtkWidget *stack;
|
|
|
|
stack = gtk_stack_new ();
|
|
gtk_stack_set_vhomogeneous (GTK_STACK (stack), FALSE);
|
|
gtk_stack_set_transition_type (GTK_STACK (stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
|
|
gtk_stack_set_interpolate_size (GTK_STACK (stack), TRUE);
|
|
gtk_popover_set_child (GTK_POPOVER (popover), stack);
|
|
|
|
gtk_widget_add_css_class (GTK_WIDGET (popover), "menu");
|
|
}
|
|
|
|
static GtkCellArea *
|
|
gtk_tree_popover_cell_layout_get_area (GtkCellLayout *layout)
|
|
{
|
|
return GTK_TREE_POPOVER (layout)->area;
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_cell_layout_init (GtkCellLayoutIface *iface)
|
|
{
|
|
iface->get_area = gtk_tree_popover_cell_layout_get_area;
|
|
}
|
|
|
|
static void
|
|
insert_at_position (GtkBox *box,
|
|
GtkWidget *child,
|
|
int position)
|
|
{
|
|
GtkWidget *sibling = NULL;
|
|
|
|
if (position > 0)
|
|
{
|
|
int i;
|
|
|
|
sibling = gtk_widget_get_first_child (GTK_WIDGET (box));
|
|
for (i = 1; i < position; i++)
|
|
sibling = gtk_widget_get_next_sibling (sibling);
|
|
}
|
|
|
|
gtk_box_insert_child_after (box, child, sibling);
|
|
}
|
|
|
|
static GtkWidget *
|
|
ensure_submenu (GtkTreePopover *popover,
|
|
GtkTreePath *path)
|
|
{
|
|
GtkWidget *box;
|
|
char *name;
|
|
|
|
if (path)
|
|
name = gtk_tree_path_to_string (path);
|
|
else
|
|
name = NULL;
|
|
|
|
box = gtk_tree_popover_get_submenu (popover, name ? name : "main");
|
|
if (!box)
|
|
{
|
|
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
|
|
gtk_tree_popover_add_submenu (popover, box, name ? name : "main");
|
|
if (path)
|
|
{
|
|
GtkTreeIter iter;
|
|
GtkWidget *item;
|
|
gtk_tree_model_get_iter (popover->model, &iter, path);
|
|
item = gtk_tree_popover_create_item (popover, path, &iter, TRUE);
|
|
gtk_box_append (GTK_BOX (box), item);
|
|
gtk_box_append (GTK_BOX (box), gtk_separator_new (GTK_ORIENTATION_HORIZONTAL));
|
|
}
|
|
|
|
}
|
|
|
|
g_free (name);
|
|
|
|
return box;
|
|
}
|
|
|
|
static void
|
|
row_inserted_cb (GtkTreeModel *model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
GtkTreePopover *popover)
|
|
{
|
|
int *indices, depth, index;
|
|
GtkWidget *item;
|
|
GtkWidget *box;
|
|
|
|
indices = gtk_tree_path_get_indices (path);
|
|
depth = gtk_tree_path_get_depth (path);
|
|
index = indices[depth - 1];
|
|
|
|
item = gtk_tree_popover_create_item (popover, path, iter, FALSE);
|
|
if (depth == 1)
|
|
{
|
|
box = ensure_submenu (popover, NULL);
|
|
insert_at_position (GTK_BOX (box), item, index);
|
|
}
|
|
else
|
|
{
|
|
GtkTreePath *ppath;
|
|
|
|
ppath = gtk_tree_path_copy (path);
|
|
gtk_tree_path_up (ppath);
|
|
|
|
box = ensure_submenu (popover, ppath);
|
|
insert_at_position (GTK_BOX (box), item, index + 2);
|
|
|
|
gtk_tree_path_free (ppath);
|
|
}
|
|
|
|
gtk_cell_area_context_reset (popover->context);
|
|
}
|
|
|
|
static void
|
|
row_deleted_cb (GtkTreeModel *model,
|
|
GtkTreePath *path,
|
|
GtkTreePopover *popover)
|
|
{
|
|
GtkWidget *item;
|
|
|
|
item = gtk_tree_popover_get_path_item (popover, path);
|
|
|
|
if (item)
|
|
{
|
|
gtk_widget_unparent (item);
|
|
gtk_cell_area_context_reset (popover->context);
|
|
}
|
|
}
|
|
|
|
static void
|
|
row_changed_cb (GtkTreeModel *model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
GtkTreePopover *popover)
|
|
{
|
|
gboolean is_separator = FALSE;
|
|
GtkWidget *item;
|
|
int *indices, depth, index;
|
|
|
|
item = gtk_tree_popover_get_path_item (popover, path);
|
|
|
|
if (!item)
|
|
return;
|
|
|
|
indices = gtk_tree_path_get_indices (path);
|
|
depth = gtk_tree_path_get_depth (path);
|
|
index = indices[depth - 1];
|
|
|
|
if (popover->row_separator_func)
|
|
is_separator = popover->row_separator_func (model, iter, popover->row_separator_data);
|
|
|
|
if (is_separator != GTK_IS_SEPARATOR (item))
|
|
{
|
|
GtkWidget *box = gtk_widget_get_parent (item);
|
|
|
|
gtk_box_remove (GTK_BOX (box), item);
|
|
|
|
item = gtk_tree_popover_create_item (popover, path, iter, FALSE);
|
|
|
|
if (depth == 1)
|
|
insert_at_position (GTK_BOX (box), item, index);
|
|
else
|
|
insert_at_position (GTK_BOX (box), item, index + 2);
|
|
}
|
|
}
|
|
|
|
static void
|
|
row_reordered_cb (GtkTreeModel *model,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
int *new_order,
|
|
GtkTreePopover *popover)
|
|
{
|
|
rebuild_menu (popover);
|
|
}
|
|
|
|
static void
|
|
context_size_changed_cb (GtkCellAreaContext *context,
|
|
GParamSpec *pspec,
|
|
GtkWidget *popover)
|
|
{
|
|
if (!strcmp (pspec->name, "minimum-width") ||
|
|
!strcmp (pspec->name, "natural-width") ||
|
|
!strcmp (pspec->name, "minimum-height") ||
|
|
!strcmp (pspec->name, "natural-height"))
|
|
gtk_widget_queue_resize (popover);
|
|
}
|
|
|
|
static gboolean
|
|
area_is_sensitive (GtkCellArea *area)
|
|
{
|
|
GList *cells, *list;
|
|
gboolean sensitive = FALSE;
|
|
|
|
cells = gtk_cell_layout_get_cells (GTK_CELL_LAYOUT (area));
|
|
|
|
for (list = cells; list; list = list->next)
|
|
{
|
|
g_object_get (list->data, "sensitive", &sensitive, NULL);
|
|
|
|
if (sensitive)
|
|
break;
|
|
}
|
|
g_list_free (cells);
|
|
|
|
return sensitive;
|
|
}
|
|
|
|
static GtkWidget *
|
|
gtk_tree_popover_get_path_item (GtkTreePopover *popover,
|
|
GtkTreePath *search)
|
|
{
|
|
GtkWidget *stack = gtk_popover_get_child (GTK_POPOVER (popover));
|
|
GtkWidget *item = NULL;
|
|
GtkWidget *stackchild;
|
|
GtkWidget *child;
|
|
|
|
for (stackchild = gtk_widget_get_first_child (stack);
|
|
stackchild != NULL;
|
|
stackchild = gtk_widget_get_next_sibling (stackchild))
|
|
{
|
|
for (child = gtk_widget_get_first_child (stackchild);
|
|
!item && child;
|
|
child = gtk_widget_get_next_sibling (child))
|
|
{
|
|
GtkTreePath *path = NULL;
|
|
|
|
if (GTK_IS_SEPARATOR (child))
|
|
{
|
|
GtkTreeRowReference *row = g_object_get_data (G_OBJECT (child), "gtk-tree-path");
|
|
|
|
if (row)
|
|
{
|
|
path = gtk_tree_row_reference_get_path (row);
|
|
if (!path)
|
|
item = child;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GtkWidget *view = GTK_WIDGET (g_object_get_data (G_OBJECT (child), "view"));
|
|
|
|
path = gtk_cell_view_get_displayed_row (GTK_CELL_VIEW (view));
|
|
|
|
if (!path)
|
|
item = child;
|
|
}
|
|
|
|
if (path)
|
|
{
|
|
if (gtk_tree_path_compare (search, path) == 0)
|
|
item = child;
|
|
gtk_tree_path_free (path);
|
|
}
|
|
}
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
static void
|
|
area_apply_attributes_cb (GtkCellArea *area,
|
|
GtkTreeModel *tree_model,
|
|
GtkTreeIter *iter,
|
|
gboolean is_expander,
|
|
gboolean is_expanded,
|
|
GtkTreePopover *popover)
|
|
{
|
|
GtkTreePath*path;
|
|
GtkWidget *item;
|
|
gboolean sensitive;
|
|
GtkTreeIter dummy;
|
|
gboolean has_submenu = FALSE;
|
|
|
|
if (gtk_tree_model_iter_children (popover->model, &dummy, iter))
|
|
has_submenu = TRUE;
|
|
|
|
path = gtk_tree_model_get_path (tree_model, iter);
|
|
item = gtk_tree_popover_get_path_item (popover, path);
|
|
|
|
if (item)
|
|
{
|
|
sensitive = area_is_sensitive (popover->area);
|
|
gtk_widget_set_sensitive (item, sensitive || has_submenu);
|
|
}
|
|
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_set_area (GtkTreePopover *popover,
|
|
GtkCellArea *area)
|
|
{
|
|
if (popover->area)
|
|
{
|
|
g_signal_handler_disconnect (popover->area, popover->apply_attributes_id);
|
|
popover->apply_attributes_id = 0;
|
|
g_clear_object (&popover->area);
|
|
}
|
|
|
|
popover->area = area;
|
|
|
|
if (popover->area)
|
|
{
|
|
g_object_ref_sink (popover->area);
|
|
popover->apply_attributes_id = g_signal_connect (popover->area, "apply-attributes",
|
|
G_CALLBACK (area_apply_attributes_cb), popover);
|
|
}
|
|
}
|
|
|
|
static void
|
|
item_activated_cb (GtkGesture *gesture,
|
|
guint n_press,
|
|
double x,
|
|
double y,
|
|
GtkTreePopover *popover)
|
|
{
|
|
GtkWidget *item;
|
|
GtkCellView *view;
|
|
GtkTreePath *path;
|
|
char *path_str;
|
|
gboolean is_header = FALSE;
|
|
gboolean has_submenu = FALSE;
|
|
|
|
item = gtk_event_controller_get_widget (GTK_EVENT_CONTROLLER (gesture));
|
|
is_header = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (item), "is-header"));
|
|
|
|
view = GTK_CELL_VIEW (g_object_get_data (G_OBJECT (item), "view"));
|
|
|
|
path = gtk_cell_view_get_displayed_row (view);
|
|
|
|
if (is_header)
|
|
{
|
|
gtk_tree_path_up (path);
|
|
}
|
|
else
|
|
{
|
|
GtkTreeIter iter;
|
|
GtkTreeIter dummy;
|
|
|
|
gtk_tree_model_get_iter (popover->model, &iter, path);
|
|
if (gtk_tree_model_iter_children (popover->model, &dummy, &iter))
|
|
has_submenu = TRUE;
|
|
}
|
|
|
|
path_str = gtk_tree_path_to_string (path);
|
|
|
|
if (is_header || has_submenu)
|
|
{
|
|
gtk_tree_popover_open_submenu (popover, path_str ? path_str : "main");
|
|
}
|
|
else
|
|
{
|
|
g_signal_emit (popover, signals[MENU_ACTIVATE], 0, path_str);
|
|
gtk_popover_popdown (GTK_POPOVER (popover));
|
|
}
|
|
|
|
g_free (path_str);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
|
|
static void
|
|
enter_cb (GtkEventController *controller,
|
|
double x,
|
|
double y,
|
|
GtkTreePopover *popover)
|
|
{
|
|
GtkWidget *item;
|
|
item = gtk_event_controller_get_widget (controller);
|
|
|
|
gtk_tree_popover_set_active_item (popover, item);
|
|
}
|
|
|
|
static GtkWidget *
|
|
gtk_tree_popover_create_item (GtkTreePopover *popover,
|
|
GtkTreePath *path,
|
|
GtkTreeIter *iter,
|
|
gboolean header_item)
|
|
{
|
|
GtkWidget *item, *view;
|
|
gboolean is_separator = FALSE;
|
|
|
|
if (popover->row_separator_func)
|
|
is_separator = popover->row_separator_func (popover->model, iter, popover->row_separator_data);
|
|
|
|
if (is_separator)
|
|
{
|
|
item = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
|
|
g_object_set_data_full (G_OBJECT (item), "gtk-tree-path",
|
|
gtk_tree_row_reference_new (popover->model, path),
|
|
(GDestroyNotify)gtk_tree_row_reference_free);
|
|
}
|
|
else
|
|
{
|
|
GtkEventController *controller;
|
|
GtkTreeIter dummy;
|
|
gboolean has_submenu = FALSE;
|
|
GtkWidget *indicator;
|
|
|
|
if (!header_item &&
|
|
gtk_tree_model_iter_children (popover->model, &dummy, iter))
|
|
has_submenu = TRUE;
|
|
|
|
view = gtk_cell_view_new_with_context (popover->area, popover->context);
|
|
gtk_cell_view_set_model (GTK_CELL_VIEW (view), popover->model);
|
|
gtk_cell_view_set_displayed_row (GTK_CELL_VIEW (view), path);
|
|
gtk_widget_set_hexpand (view, TRUE);
|
|
|
|
item = gtk_gizmo_new ("modelbutton", NULL, NULL, NULL, NULL, NULL, NULL);
|
|
gtk_widget_set_layout_manager (item, gtk_box_layout_new (GTK_ORIENTATION_HORIZONTAL));
|
|
gtk_widget_add_css_class (item, "flat");
|
|
|
|
if (header_item)
|
|
{
|
|
indicator = gtk_builtin_icon_new ("arrow");
|
|
gtk_widget_add_css_class (indicator, "left");
|
|
gtk_widget_set_parent (indicator, item);
|
|
}
|
|
|
|
gtk_widget_set_parent (view, item);
|
|
|
|
indicator = gtk_builtin_icon_new (has_submenu ? "arrow" : "none");
|
|
gtk_widget_add_css_class (indicator, "right");
|
|
gtk_widget_set_parent (indicator, item);
|
|
|
|
controller = GTK_EVENT_CONTROLLER (gtk_gesture_click_new ());
|
|
g_signal_connect (controller, "pressed", G_CALLBACK (item_activated_cb), popover);
|
|
gtk_widget_add_controller (item, GTK_EVENT_CONTROLLER (controller));
|
|
|
|
controller = gtk_event_controller_motion_new ();
|
|
g_signal_connect (controller, "enter", G_CALLBACK (enter_cb), popover);
|
|
gtk_widget_add_controller (item, controller);
|
|
|
|
g_object_set_data (G_OBJECT (item), "is-header", GINT_TO_POINTER (header_item));
|
|
g_object_set_data (G_OBJECT (item), "view", view);
|
|
}
|
|
|
|
return item;
|
|
}
|
|
|
|
static void
|
|
populate (GtkTreePopover *popover,
|
|
GtkTreeIter *parent)
|
|
{
|
|
GtkTreeIter iter;
|
|
gboolean valid = FALSE;
|
|
|
|
if (!popover->model)
|
|
return;
|
|
|
|
valid = gtk_tree_model_iter_children (popover->model, &iter, parent);
|
|
|
|
while (valid)
|
|
{
|
|
GtkTreePath *path;
|
|
|
|
path = gtk_tree_model_get_path (popover->model, &iter);
|
|
row_inserted_cb (popover->model, path, &iter, popover);
|
|
|
|
populate (popover, &iter);
|
|
|
|
valid = gtk_tree_model_iter_next (popover->model, &iter);
|
|
gtk_tree_path_free (path);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_populate (GtkTreePopover *popover)
|
|
{
|
|
populate (popover, NULL);
|
|
}
|
|
|
|
static void
|
|
rebuild_menu (GtkTreePopover *popover)
|
|
{
|
|
GtkWidget *stack;
|
|
GtkWidget *child;
|
|
|
|
stack = gtk_popover_get_child (GTK_POPOVER (popover));
|
|
while ((child = gtk_widget_get_first_child (stack)))
|
|
gtk_stack_remove (GTK_STACK (stack), child);
|
|
|
|
if (popover->model)
|
|
gtk_tree_popover_populate (popover);
|
|
}
|
|
|
|
void
|
|
gtk_tree_popover_set_model (GtkTreePopover *popover,
|
|
GtkTreeModel *model)
|
|
{
|
|
if (popover->model == model)
|
|
return;
|
|
|
|
if (popover->model)
|
|
{
|
|
g_signal_handler_disconnect (popover->model, popover->row_inserted_id);
|
|
g_signal_handler_disconnect (popover->model, popover->row_deleted_id);
|
|
g_signal_handler_disconnect (popover->model, popover->row_changed_id);
|
|
g_signal_handler_disconnect (popover->model, popover->row_reordered_id);
|
|
popover->row_inserted_id = 0;
|
|
popover->row_deleted_id = 0;
|
|
popover->row_changed_id = 0;
|
|
popover->row_reordered_id = 0;
|
|
|
|
g_object_unref (popover->model);
|
|
}
|
|
|
|
popover->model = model;
|
|
|
|
if (popover->model)
|
|
{
|
|
g_object_ref (popover->model);
|
|
|
|
popover->row_inserted_id = g_signal_connect (popover->model, "row-inserted",
|
|
G_CALLBACK (row_inserted_cb), popover);
|
|
popover->row_deleted_id = g_signal_connect (popover->model, "row-deleted",
|
|
G_CALLBACK (row_deleted_cb), popover);
|
|
popover->row_changed_id = g_signal_connect (popover->model, "row-changed",
|
|
G_CALLBACK (row_changed_cb), popover);
|
|
popover->row_reordered_id = g_signal_connect (popover->model, "rows-reordered",
|
|
G_CALLBACK (row_reordered_cb), popover);
|
|
}
|
|
|
|
rebuild_menu (popover);
|
|
}
|
|
|
|
void
|
|
gtk_tree_popover_set_row_separator_func (GtkTreePopover *popover,
|
|
GtkTreeViewRowSeparatorFunc func,
|
|
gpointer data,
|
|
GDestroyNotify destroy)
|
|
{
|
|
if (popover->row_separator_destroy)
|
|
popover->row_separator_destroy (popover->row_separator_data);
|
|
|
|
popover->row_separator_func = func;
|
|
popover->row_separator_data = data;
|
|
popover->row_separator_destroy = destroy;
|
|
|
|
rebuild_menu (popover);
|
|
}
|
|
|
|
static void
|
|
gtk_tree_popover_set_active_item (GtkTreePopover *popover,
|
|
GtkWidget *item)
|
|
{
|
|
if (popover->active_item == item)
|
|
return;
|
|
|
|
if (popover->active_item)
|
|
{
|
|
gtk_widget_unset_state_flags (popover->active_item, GTK_STATE_FLAG_SELECTED);
|
|
g_object_remove_weak_pointer (G_OBJECT (popover->active_item), (gpointer *)&popover->active_item);
|
|
}
|
|
|
|
popover->active_item = item;
|
|
|
|
if (popover->active_item)
|
|
{
|
|
g_object_add_weak_pointer (G_OBJECT (popover->active_item), (gpointer *)&popover->active_item);
|
|
gtk_widget_set_state_flags (popover->active_item, GTK_STATE_FLAG_SELECTED, FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
gtk_tree_popover_set_active (GtkTreePopover *popover,
|
|
int item)
|
|
{
|
|
GtkWidget *box;
|
|
GtkWidget *child;
|
|
int pos;
|
|
|
|
if (item == -1)
|
|
{
|
|
gtk_tree_popover_set_active_item (popover, NULL);
|
|
return;
|
|
}
|
|
|
|
box = gtk_tree_popover_get_submenu (popover, "main");
|
|
if (!box)
|
|
return;
|
|
|
|
for (child = gtk_widget_get_first_child (box), pos = 0;
|
|
child;
|
|
child = gtk_widget_get_next_sibling (child), pos++)
|
|
{
|
|
if (pos == item)
|
|
{
|
|
gtk_tree_popover_set_active_item (popover, child);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|