gitg/egg-list-box.c

1999 lines
60 KiB
C
Raw Normal View History

/*
* 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 <glib.h>
#include <glib-object.h>
#include <gtk/gtk.h>
#include <gdk/gdk.h>
#include <float.h>
#include <math.h>
#include <cairo.h>
#include <string.h>
#include <gobject/gvaluecollector.h>
2012-08-16 14:14:09 +00:00
#include "egg-list-box.h"
#include "egg-list-box-accessible.h"
2012-08-16 14:13:43 +00:00
/* This already exists in gtk as _gtk_marshal_VOID__ENUM_INT, inline it here for now
to avoid separate marshallers file */
static void
_egg_marshal_VOID__ENUM_INT (GClosure * closure,
GValue * return_value,
guint n_param_values,
const GValue * param_values,
gpointer invocation_hint,
gpointer marshal_data)
{
2012-08-16 12:39:26 +00:00
typedef void (*GMarshalFunc_VOID__ENUM_INT) (gpointer data1, gint arg_1, gint arg_2, gpointer data2);
register GMarshalFunc_VOID__ENUM_INT callback;
register GCClosure * cc;
register gpointer data1;
register gpointer data2;
cc = (GCClosure *) closure;
g_return_if_fail (n_param_values == 3);
if (G_CCLOSURE_SWAP_DATA (closure)) {
data1 = closure->data;
data2 = param_values->data[0].v_pointer;
} else {
data1 = param_values->data[0].v_pointer;
data2 = closure->data;
}
callback = (GMarshalFunc_VOID__ENUM_INT) (marshal_data ? marshal_data : cc->callback);
callback (data1, g_value_get_enum (param_values + 1), g_value_get_int (param_values + 2), data2);
}
typedef struct _EggListBoxChildInfo EggListBoxChildInfo;
struct _EggListBoxPrivate
{
2012-08-16 12:39:26 +00:00
GSequence *children;
GHashTable *child_hash;
GHashTable *separator_hash;
GCompareDataFunc sort_func;
gpointer sort_func_target;
GDestroyNotify sort_func_target_destroy_notify;
2012-08-16 12:39:26 +00:00
EggListBoxFilterFunc filter_func;
gpointer filter_func_target;
GDestroyNotify filter_func_target_destroy_notify;
2012-08-16 12:39:26 +00:00
EggListBoxUpdateSeparatorFunc update_separator_func;
gpointer update_separator_func_target;
GDestroyNotify update_separator_func_target_destroy_notify;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *selected_child;
EggListBoxChildInfo *prelight_child;
EggListBoxChildInfo *cursor_child;
gboolean active_child_active;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *active_child;
GtkSelectionMode selection_mode;
2012-08-16 12:39:26 +00:00
GtkAdjustment *adjustment;
gboolean activate_single_click;
/* DnD */
2012-08-16 12:39:26 +00:00
GtkWidget *drag_highlighted_widget;
guint auto_scroll_timeout_id;
};
struct _EggListBoxChildInfo
{
2012-08-16 12:39:26 +00:00
GSequenceIter *iter;
GtkWidget *widget;
GtkWidget *separator;
gint y;
gint height;
};
2012-08-16 12:51:04 +00:00
enum {
CHILD_SELECTED,
CHILD_ACTIVATED,
ACTIVATE_CURSOR_CHILD,
TOGGLE_CURSOR_CHILD,
MOVE_CURSOR,
REFILTER,
2012-08-16 12:51:04 +00:00
LAST_SIGNAL
};
enum {
PROP_0
};
G_DEFINE_TYPE (EggListBox, egg_list_box, GTK_TYPE_CONTAINER)
2012-08-16 14:05:35 +00:00
static EggListBoxChildInfo *egg_list_box_find_child_at_y (EggListBox *list_box,
gint y);
static EggListBoxChildInfo *egg_list_box_lookup_info (EggListBox *list_box,
GtkWidget *widget);
static void egg_list_box_update_selected (EggListBox *list_box,
EggListBoxChildInfo *child);
static void egg_list_box_apply_filter_all (EggListBox *list_box);
static void egg_list_box_update_separator (EggListBox *list_box,
GSequenceIter *iter);
static GSequenceIter * egg_list_box_get_next_visible (EggListBox *list_box,
GSequenceIter *_iter);
static void egg_list_box_apply_filter (EggListBox *list_box,
GtkWidget *child);
static void egg_list_box_add_move_binding (GtkBindingSet *binding_set,
guint keyval,
GdkModifierType modmask,
GtkMovementStep step,
gint count);
static void egg_list_box_update_cursor (EggListBox *list_box,
EggListBoxChildInfo *child);
static void egg_list_box_select_and_activate (EggListBox *list_box,
EggListBoxChildInfo *child);
static void egg_list_box_update_prelight (EggListBox *list_box,
EggListBoxChildInfo *child);
static void egg_list_box_update_active (EggListBox *list_box,
EggListBoxChildInfo *child);
static gboolean egg_list_box_real_enter_notify_event (GtkWidget *widget,
GdkEventCrossing *event);
static gboolean egg_list_box_real_leave_notify_event (GtkWidget *widget,
GdkEventCrossing *event);
static gboolean egg_list_box_real_motion_notify_event (GtkWidget *widget,
GdkEventMotion *event);
static gboolean egg_list_box_real_button_press_event (GtkWidget *widget,
GdkEventButton *event);
static gboolean egg_list_box_real_button_release_event (GtkWidget *widget,
GdkEventButton *event);
static void egg_list_box_real_show (GtkWidget *widget);
static gboolean egg_list_box_real_focus (GtkWidget *widget,
GtkDirectionType direction);
static GSequenceIter* egg_list_box_get_previous_visible (EggListBox *list_box,
GSequenceIter *_iter);
static EggListBoxChildInfo *egg_list_box_get_first_visible (EggListBox *list_box);
static EggListBoxChildInfo *egg_list_box_get_last_visible (EggListBox *list_box);
static gboolean egg_list_box_real_draw (GtkWidget *widget,
cairo_t *cr);
static void egg_list_box_real_realize (GtkWidget *widget);
static void egg_list_box_real_add (GtkContainer *container,
GtkWidget *widget);
static void egg_list_box_real_remove (GtkContainer *container,
GtkWidget *widget);
static void egg_list_box_real_forall_internal (GtkContainer *container,
gboolean include_internals,
GtkCallback callback,
void *callback_target);
static void egg_list_box_real_compute_expand_internal (GtkWidget *widget,
gboolean *hexpand,
gboolean *vexpand);
static GType egg_list_box_real_child_type (GtkContainer *container);
static GtkSizeRequestMode egg_list_box_real_get_request_mode (GtkWidget *widget);
static void egg_list_box_real_size_allocate (GtkWidget *widget,
GtkAllocation *allocation);
static void egg_list_box_real_drag_leave (GtkWidget *widget,
GdkDragContext *context,
guint time_);
static gboolean egg_list_box_real_drag_motion (GtkWidget *widget,
GdkDragContext *context,
gint x,
gint y,
guint time_);
static void egg_list_box_real_activate_cursor_child (EggListBox *list_box);
static void egg_list_box_real_toggle_cursor_child (EggListBox *list_box);
static void egg_list_box_real_move_cursor (EggListBox *list_box,
GtkMovementStep step,
gint count);
static void egg_list_box_real_refilter (EggListBox *list_box);
2012-08-16 14:05:35 +00:00
static void egg_list_box_finalize (GObject *obj);
2012-08-16 13:53:22 +00:00
static void egg_list_box_real_get_preferred_height (GtkWidget *widget,
2012-08-16 13:12:27 +00:00
gint *minimum_height,
gint *natural_height);
2012-08-16 13:53:22 +00:00
static void egg_list_box_real_get_preferred_height_for_width (GtkWidget *widget,
2012-08-16 13:12:27 +00:00
gint width,
gint *minimum_height,
gint *natural_height);
2012-08-16 13:53:22 +00:00
static void egg_list_box_real_get_preferred_width (GtkWidget *widget,
2012-08-16 13:12:27 +00:00
gint *minimum_width,
gint *natural_width);
2012-08-16 13:53:22 +00:00
static void egg_list_box_real_get_preferred_width_for_height (GtkWidget *widget,
2012-08-16 13:12:27 +00:00
gint height,
gint *minimum_width,
gint *natural_width);
2012-08-16 12:51:04 +00:00
static guint signals[LAST_SIGNAL] = { 0 };
2012-08-16 13:12:27 +00:00
static EggListBoxChildInfo*
egg_list_box_child_info_new (GtkWidget *widget)
{
EggListBoxChildInfo *info;
info = g_new0 (EggListBoxChildInfo, 1);
info->widget = g_object_ref (widget);
return info;
}
static void
egg_list_box_child_info_free (EggListBoxChildInfo *info)
{
2012-08-16 14:35:47 +00:00
g_clear_object (&info->widget);
g_clear_object (&info->separator);
2012-08-16 13:12:27 +00:00
g_free (info);
}
2012-08-16 13:05:10 +00:00
EggListBox*
egg_list_box_new (void)
{
return g_object_new (EGG_TYPE_LIST_BOX, NULL);
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_init (EggListBox *list_box)
2012-08-16 13:05:10 +00:00
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv;
2012-08-16 14:58:24 +00:00
list_box->priv = priv =
G_TYPE_INSTANCE_GET_PRIVATE (list_box, EGG_TYPE_LIST_BOX, EggListBoxPrivate);
2012-08-16 13:05:10 +00:00
2012-08-16 14:58:24 +00:00
gtk_widget_set_can_focus (GTK_WIDGET (list_box), TRUE);
gtk_widget_set_has_window (GTK_WIDGET (list_box), TRUE);
gtk_widget_set_redraw_on_allocate (GTK_WIDGET (list_box), TRUE);
2012-08-16 14:04:02 +00:00
priv->selection_mode = GTK_SELECTION_SINGLE;
priv->activate_single_click = TRUE;
2012-08-16 13:05:10 +00:00
2012-08-16 14:04:02 +00:00
priv->children = g_sequence_new ((GDestroyNotify)egg_list_box_child_info_free);
priv->child_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
priv->separator_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL);
2012-08-16 13:05:10 +00:00
}
static void
egg_list_box_finalize (GObject *obj)
{
2012-08-16 14:04:02 +00:00
EggListBox *list_box = EGG_LIST_BOX (obj);
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:05:10 +00:00
2012-08-16 14:04:02 +00:00
if (priv->auto_scroll_timeout_id != ((guint) 0))
g_source_remove (priv->auto_scroll_timeout_id);
2012-08-16 13:05:10 +00:00
2012-08-16 14:04:02 +00:00
if (priv->sort_func_target_destroy_notify != NULL)
priv->sort_func_target_destroy_notify (priv->sort_func_target);
if (priv->filter_func_target_destroy_notify != NULL)
priv->filter_func_target_destroy_notify (priv->filter_func_target);
if (priv->update_separator_func_target_destroy_notify != NULL)
priv->update_separator_func_target_destroy_notify (priv->update_separator_func_target);
2012-08-16 13:05:10 +00:00
2012-08-16 14:35:47 +00:00
g_clear_object (&priv->adjustment);
g_clear_object (&priv->drag_highlighted_widget);
2012-08-16 13:05:10 +00:00
2012-08-16 14:04:02 +00:00
g_sequence_free (priv->children);
g_hash_table_unref (priv->child_hash);
g_hash_table_unref (priv->separator_hash);
2012-08-16 13:05:10 +00:00
G_OBJECT_CLASS (egg_list_box_parent_class)->finalize (obj);
}
static void
egg_list_box_class_init (EggListBoxClass *klass)
{
2012-08-16 13:40:54 +00:00
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
2012-08-16 13:05:10 +00:00
GtkBindingSet *binding_set;
egg_list_box_parent_class = g_type_class_peek_parent (klass);
g_type_class_add_private (klass, sizeof (EggListBoxPrivate));
gtk_widget_class_set_accessible_type (widget_class, EGG_TYPE_LIST_BOX_ACCESSIBLE);
2012-08-16 13:40:54 +00:00
object_class->finalize = egg_list_box_finalize;
widget_class->enter_notify_event = egg_list_box_real_enter_notify_event;
widget_class->leave_notify_event = egg_list_box_real_leave_notify_event;
widget_class->motion_notify_event = egg_list_box_real_motion_notify_event;
widget_class->button_press_event = egg_list_box_real_button_press_event;
widget_class->button_release_event = egg_list_box_real_button_release_event;
widget_class->show = egg_list_box_real_show;
widget_class->focus = egg_list_box_real_focus;
widget_class->draw = egg_list_box_real_draw;
widget_class->realize = egg_list_box_real_realize;
widget_class->compute_expand = egg_list_box_real_compute_expand_internal;
widget_class->get_request_mode = egg_list_box_real_get_request_mode;
widget_class->get_preferred_height = egg_list_box_real_get_preferred_height;
widget_class->get_preferred_height_for_width = egg_list_box_real_get_preferred_height_for_width;
widget_class->get_preferred_width = egg_list_box_real_get_preferred_width;
widget_class->get_preferred_width_for_height = egg_list_box_real_get_preferred_width_for_height;
widget_class->size_allocate = egg_list_box_real_size_allocate;
widget_class->drag_leave = egg_list_box_real_drag_leave;
widget_class->drag_motion = egg_list_box_real_drag_motion;
container_class->add = egg_list_box_real_add;
container_class->remove = egg_list_box_real_remove;
container_class->forall = egg_list_box_real_forall_internal;
container_class->child_type = egg_list_box_real_child_type;
klass->activate_cursor_child = egg_list_box_real_activate_cursor_child;
klass->toggle_cursor_child = egg_list_box_real_toggle_cursor_child;
klass->move_cursor = egg_list_box_real_move_cursor;
klass->refilter = egg_list_box_real_refilter;
2012-08-16 13:05:10 +00:00
signals[CHILD_SELECTED] =
g_signal_new ("child-selected",
EGG_TYPE_LIST_BOX,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EggListBoxClass, child_selected),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
GTK_TYPE_WIDGET);
signals[CHILD_ACTIVATED] =
g_signal_new ("child-activated",
EGG_TYPE_LIST_BOX,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EggListBoxClass, child_activated),
NULL, NULL,
g_cclosure_marshal_VOID__OBJECT,
G_TYPE_NONE, 1,
GTK_TYPE_WIDGET);
signals[ACTIVATE_CURSOR_CHILD] =
g_signal_new ("activate-cursor-child",
EGG_TYPE_LIST_BOX,
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (EggListBoxClass, activate_cursor_child),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[TOGGLE_CURSOR_CHILD] =
g_signal_new ("toggle-cursor-child",
EGG_TYPE_LIST_BOX,
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (EggListBoxClass, toggle_cursor_child),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
signals[MOVE_CURSOR] =
g_signal_new ("move-cursor",
EGG_TYPE_LIST_BOX,
G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
G_STRUCT_OFFSET (EggListBoxClass, move_cursor),
NULL, NULL,
2012-08-16 14:13:43 +00:00
_egg_marshal_VOID__ENUM_INT,
2012-08-16 13:05:10 +00:00
G_TYPE_NONE, 2,
GTK_TYPE_MOVEMENT_STEP, G_TYPE_INT);
signals[REFILTER] =
g_signal_new ("refilter",
EGG_TYPE_LIST_BOX,
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (EggListBoxClass, refilter),
NULL, NULL,
g_cclosure_marshal_VOID__VOID,
G_TYPE_NONE, 0);
2012-08-16 13:05:10 +00:00
2012-08-16 13:40:54 +00:00
widget_class->activate_signal = signals[ACTIVATE_CURSOR_CHILD];
2012-08-16 13:05:10 +00:00
binding_set = gtk_binding_set_by_class (klass);
2012-08-16 13:40:54 +00:00
egg_list_box_add_move_binding (binding_set, GDK_KEY_Home, 0,
GTK_MOVEMENT_BUFFER_ENDS, -1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Home, 0,
GTK_MOVEMENT_BUFFER_ENDS, -1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_End, 0,
GTK_MOVEMENT_BUFFER_ENDS, 1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_End, 0,
GTK_MOVEMENT_BUFFER_ENDS, 1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_Up, GDK_CONTROL_MASK,
GTK_MOVEMENT_DISPLAY_LINES, -1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Up, GDK_CONTROL_MASK,
GTK_MOVEMENT_DISPLAY_LINES, -1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_Down, GDK_CONTROL_MASK,
GTK_MOVEMENT_DISPLAY_LINES, 1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Down, GDK_CONTROL_MASK,
GTK_MOVEMENT_DISPLAY_LINES, 1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_Page_Up, 0,
GTK_MOVEMENT_PAGES, -1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Up, 0,
GTK_MOVEMENT_PAGES, -1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_Page_Down, 0,
GTK_MOVEMENT_PAGES, 1);
egg_list_box_add_move_binding (binding_set, GDK_KEY_KP_Page_Down, 0,
GTK_MOVEMENT_PAGES, 1);
gtk_binding_entry_add_signal (binding_set, GDK_KEY_space, GDK_CONTROL_MASK,
"toggle-cursor-child", 0, NULL);
2012-08-16 13:05:10 +00:00
}
GtkWidget *
2012-08-16 13:56:50 +00:00
egg_list_box_get_selected_child (EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
g_return_val_if_fail (list_box != NULL, NULL);
2012-08-16 14:04:02 +00:00
if (priv->selected_child != NULL)
return priv->selected_child->widget;
return NULL;
}
GtkWidget *
2012-08-16 13:56:50 +00:00
egg_list_box_get_child_at_y (EggListBox *list_box, gint y)
{
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child;
2012-08-16 12:51:04 +00:00
2012-08-16 13:56:50 +00:00
g_return_val_if_fail (list_box != NULL, NULL);
2012-08-16 13:56:50 +00:00
child = egg_list_box_find_child_at_y (list_box, y);
if (child == NULL)
return NULL;
return child->widget;
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_select_child (EggListBox *list_box, GtkWidget *child)
{
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *info = NULL;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
if (child != NULL)
2012-08-16 13:56:50 +00:00
info = egg_list_box_lookup_info (list_box, child);
2012-08-16 13:56:50 +00:00
egg_list_box_update_selected (list_box, info);
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_set_adjustment (EggListBox *list_box,
2012-08-16 12:39:26 +00:00
GtkAdjustment *adjustment)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
g_object_ref (adjustment);
2012-08-16 14:35:47 +00:00
if (priv->adjustment)
g_object_unref (priv->adjustment);
2012-08-16 14:04:02 +00:00
priv->adjustment = adjustment;
2012-08-16 13:56:50 +00:00
gtk_container_set_focus_vadjustment (GTK_CONTAINER (list_box),
adjustment);
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_add_to_scrolled (EggListBox *list_box,
2012-08-16 12:39:26 +00:00
GtkScrolledWindow *scrolled)
{
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
g_return_if_fail (scrolled != NULL);
gtk_scrolled_window_add_with_viewport (scrolled,
2012-08-16 13:56:50 +00:00
GTK_WIDGET (list_box));
egg_list_box_set_adjustment (list_box,
gtk_scrolled_window_get_vadjustment (scrolled));
}
2012-08-16 14:04:02 +00:00
void
egg_list_box_set_selection_mode (EggListBox *list_box, GtkSelectionMode mode)
{
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
if (mode == GTK_SELECTION_MULTIPLE)
{
g_warning ("egg-list-box.vala:115: Multiple selections not supported");
return;
}
2012-08-16 14:04:02 +00:00
priv->selection_mode = mode;
if (mode == GTK_SELECTION_NONE)
2012-08-16 13:56:50 +00:00
egg_list_box_update_selected (list_box, NULL);
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_set_filter_func (EggListBox *list_box,
EggListBoxFilterFunc f,
2012-08-16 12:39:26 +00:00
void *f_target,
GDestroyNotify f_target_destroy_notify)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
2012-08-16 14:04:02 +00:00
if (priv->filter_func_target_destroy_notify != NULL)
priv->filter_func_target_destroy_notify (priv->filter_func_target);
2012-08-16 14:04:02 +00:00
priv->filter_func = f;
priv->filter_func_target = f_target;
priv->filter_func_target_destroy_notify = f_target_destroy_notify;
2012-08-16 12:51:04 +00:00
2012-08-16 13:56:50 +00:00
egg_list_box_refilter (list_box);
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_set_separator_funcs (EggListBox *list_box,
EggListBoxUpdateSeparatorFunc update_separator,
2012-08-16 12:39:26 +00:00
void *update_separator_target,
GDestroyNotify update_separator_target_destroy_notify)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
2012-08-16 14:04:02 +00:00
if (priv->update_separator_func_target_destroy_notify != NULL)
priv->update_separator_func_target_destroy_notify (priv->update_separator_func_target);
2012-08-16 14:04:02 +00:00
priv->update_separator_func = update_separator;
priv->update_separator_func_target = update_separator_target;
priv->update_separator_func_target_destroy_notify = update_separator_target_destroy_notify;
2012-08-16 13:56:50 +00:00
egg_list_box_reseparate (list_box);
}
static void
egg_list_box_real_refilter (EggListBox *list_box)
{
egg_list_box_apply_filter_all (list_box);
egg_list_box_reseparate (list_box);
gtk_widget_queue_resize (GTK_WIDGET (list_box));
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_refilter (EggListBox *list_box)
{
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
g_signal_emit (list_box, signals[REFILTER], 0);
}
static gint
2012-08-16 13:08:29 +00:00
do_sort (EggListBoxChildInfo *a,
EggListBoxChildInfo *b,
2012-08-16 13:56:50 +00:00
EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
return priv->sort_func (a->widget, b->widget,
priv->sort_func_target);
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_resort (EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
2012-08-16 14:04:02 +00:00
g_sequence_sort (priv->children,
(GCompareDataFunc)do_sort, list_box);
2012-08-16 13:56:50 +00:00
egg_list_box_reseparate (list_box);
2012-08-16 14:58:24 +00:00
gtk_widget_queue_resize (GTK_WIDGET (list_box));
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_reseparate (EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
GSequenceIter *iter;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
2012-08-16 14:04:02 +00:00
for (iter = g_sequence_get_begin_iter (priv->children);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
2012-08-16 13:56:50 +00:00
egg_list_box_update_separator (list_box, iter);
2012-08-16 14:58:24 +00:00
gtk_widget_queue_resize (GTK_WIDGET (list_box));
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_set_sort_func (EggListBox *list_box,
GCompareDataFunc f,
2012-08-16 12:39:26 +00:00
void *f_target,
GDestroyNotify f_target_destroy_notify)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
2012-08-16 14:04:02 +00:00
if (priv->sort_func_target_destroy_notify != NULL)
priv->sort_func_target_destroy_notify (priv->sort_func_target);
2012-08-16 14:04:02 +00:00
priv->sort_func = f;
priv->sort_func_target = f_target;
priv->sort_func_target_destroy_notify = f_target_destroy_notify;
2012-08-16 13:56:50 +00:00
egg_list_box_resort (list_box);
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_child_changed (EggListBox *list_box, GtkWidget *widget)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *info;
GSequenceIter *prev_next, *next;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
g_return_if_fail (widget != NULL);
2012-08-16 13:56:50 +00:00
info = egg_list_box_lookup_info (list_box, widget);
if (info == NULL)
return;
2012-08-16 13:56:50 +00:00
prev_next = egg_list_box_get_next_visible (list_box, info->iter);
2012-08-16 14:04:02 +00:00
if (priv->sort_func != NULL)
{
g_sequence_sort_changed (info->iter,
2012-08-16 13:08:29 +00:00
(GCompareDataFunc)do_sort,
2012-08-16 13:56:50 +00:00
list_box);
2012-08-16 14:58:24 +00:00
gtk_widget_queue_resize (GTK_WIDGET (list_box));
}
2012-08-16 13:56:50 +00:00
egg_list_box_apply_filter (list_box, info->widget);
2012-08-16 14:58:24 +00:00
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
{
2012-08-16 13:56:50 +00:00
next = egg_list_box_get_next_visible (list_box, info->iter);
egg_list_box_update_separator (list_box, info->iter);
egg_list_box_update_separator (list_box, next);
egg_list_box_update_separator (list_box, prev_next);
}
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_set_activate_on_single_click (EggListBox *list_box,
gboolean single)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
2012-08-16 14:04:02 +00:00
priv->activate_single_click = single;
}
static void
2012-08-16 12:39:26 +00:00
egg_list_box_add_move_binding (GtkBindingSet *binding_set,
guint keyval,
GdkModifierType modmask,
GtkMovementStep step,
gint count)
{
gtk_binding_entry_add_signal (binding_set, keyval, modmask,
"move-cursor", (guint) 2, GTK_TYPE_MOVEMENT_STEP, step, G_TYPE_INT, count, NULL);
if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
return;
gtk_binding_entry_add_signal (binding_set, keyval, GDK_CONTROL_MASK,
"move-cursor", (guint) 2, GTK_TYPE_MOVEMENT_STEP, step, G_TYPE_INT, count, NULL);
}
static EggListBoxChildInfo*
2012-08-16 13:56:50 +00:00
egg_list_box_find_child_at_y (EggListBox *list_box, gint y)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child_info;
GSequenceIter *iter;
EggListBoxChildInfo *info;
child_info = NULL;
2012-08-16 14:04:02 +00:00
for (iter = g_sequence_get_begin_iter (priv->children);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
info = (EggListBoxChildInfo*) g_sequence_get (iter);
if (y >= info->y && y < (info->y + info->height))
{
child_info = info;
break;
}
}
return child_info;
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_update_cursor (EggListBox *list_box,
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
priv->cursor_child = child;
2012-08-16 14:58:24 +00:00
gtk_widget_grab_focus (GTK_WIDGET (list_box));
gtk_widget_queue_draw (GTK_WIDGET (list_box));
2012-08-16 14:04:02 +00:00
if (child != NULL && priv->adjustment != NULL)
{
GtkAllocation allocation;
2012-08-16 14:58:24 +00:00
gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
2012-08-16 14:04:02 +00:00
gtk_adjustment_clamp_page (priv->adjustment,
priv->cursor_child->y + allocation.y,
priv->cursor_child->y + allocation.y + priv->cursor_child->height);
}
_egg_list_box_accessible_update_cursor (list_box, child ? child->widget : NULL);
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_update_selected (EggListBox *list_box,
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
if (child != priv->selected_child &&
(child == NULL || priv->selection_mode != GTK_SELECTION_NONE))
{
2012-08-16 14:04:02 +00:00
priv->selected_child = child;
2012-08-16 13:56:50 +00:00
g_signal_emit (list_box, signals[CHILD_SELECTED], 0,
2012-08-16 14:04:02 +00:00
(priv->selected_child != NULL) ? priv->selected_child->widget : NULL);
2012-08-16 14:58:24 +00:00
gtk_widget_queue_draw (GTK_WIDGET (list_box));
}
_egg_list_box_accessible_update_selected (list_box, child ? child->widget : NULL);
if (child != NULL)
2012-08-16 13:56:50 +00:00
egg_list_box_update_cursor (list_box, child);
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_select_and_activate (EggListBox *list_box, EggListBoxChildInfo *child)
{
2012-08-16 12:39:26 +00:00
GtkWidget *w = NULL;
if (child != NULL)
w = child->widget;
2012-08-16 13:56:50 +00:00
egg_list_box_update_selected (list_box, child);
if (w != NULL)
2012-08-16 13:56:50 +00:00
g_signal_emit (list_box, signals[CHILD_ACTIVATED], 0, w);
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_update_prelight (EggListBox *list_box, EggListBoxChildInfo *child)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
if (child != priv->prelight_child)
{
2012-08-16 14:04:02 +00:00
priv->prelight_child = child;
2012-08-16 14:58:24 +00:00
gtk_widget_queue_draw (GTK_WIDGET (list_box));
}
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_update_active (EggListBox *list_box, EggListBoxChildInfo *child)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
gboolean val;
2012-08-16 14:04:02 +00:00
val = priv->active_child == child;
if (priv->active_child != NULL &&
val != priv->active_child_active)
{
2012-08-16 14:04:02 +00:00
priv->active_child_active = val;
2012-08-16 14:58:24 +00:00
gtk_widget_queue_draw (GTK_WIDGET (list_box));
}
}
static gboolean
2012-08-16 13:53:22 +00:00
egg_list_box_real_enter_notify_event (GtkWidget *widget,
2012-08-16 12:39:26 +00:00
GdkEventCrossing *event)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child;
2012-08-16 14:58:24 +00:00
if (event->window != gtk_widget_get_window (GTK_WIDGET (list_box)))
return FALSE;
2012-08-16 13:56:50 +00:00
child = egg_list_box_find_child_at_y (list_box, event->y);
egg_list_box_update_prelight (list_box, child);
egg_list_box_update_active (list_box, child);
return FALSE;
}
static gboolean
2012-08-16 13:53:22 +00:00
egg_list_box_real_leave_notify_event (GtkWidget *widget,
2012-08-16 12:39:26 +00:00
GdkEventCrossing *event)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child = NULL;
2012-08-16 14:58:24 +00:00
if (event->window != gtk_widget_get_window (GTK_WIDGET (list_box)))
return FALSE;
2012-08-16 12:51:04 +00:00
if (event->detail != GDK_NOTIFY_INFERIOR)
child = NULL;
else
2012-08-16 13:56:50 +00:00
child = egg_list_box_find_child_at_y (list_box, event->y);
2012-08-16 13:56:50 +00:00
egg_list_box_update_prelight (list_box, child);
egg_list_box_update_active (list_box, child);
return FALSE;
}
static gboolean
2012-08-16 13:53:22 +00:00
egg_list_box_real_motion_notify_event (GtkWidget *widget,
2012-08-16 12:39:26 +00:00
GdkEventMotion *event)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child;
GdkWindow *window, *event_window;
gint relative_y;
gdouble parent_y;
window = gtk_widget_get_window (GTK_WIDGET (list_box));
event_window = event->window;
relative_y = event->y;
while ((event_window != NULL) && (event_window != window))
{
gdk_window_coords_to_parent (event_window, 0, relative_y, NULL, &parent_y);
relative_y = parent_y;
event_window = gdk_window_get_effective_parent (event_window);
}
child = egg_list_box_find_child_at_y (list_box, relative_y);
2012-08-16 13:56:50 +00:00
egg_list_box_update_prelight (list_box, child);
egg_list_box_update_active (list_box, child);
return FALSE;
}
static gboolean
2012-08-16 13:53:22 +00:00
egg_list_box_real_button_press_event (GtkWidget *widget,
2012-08-16 12:39:26 +00:00
GdkEventButton *event)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
if (event->button == 1)
{
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child;
2012-08-16 13:56:50 +00:00
child = egg_list_box_find_child_at_y (list_box, event->y);
if (child != NULL)
{
2012-08-16 14:04:02 +00:00
priv->active_child = child;
priv->active_child_active = TRUE;
2012-08-16 14:58:24 +00:00
gtk_widget_queue_draw (GTK_WIDGET (list_box));
if (event->type == GDK_2BUTTON_PRESS &&
!priv->activate_single_click)
2012-08-16 13:56:50 +00:00
g_signal_emit (list_box, signals[CHILD_ACTIVATED], 0,
2012-08-16 12:51:04 +00:00
child->widget);
}
/* TODO:
Should mark as active while down,
and handle grab breaks */
}
return FALSE;
}
static gboolean
2012-08-16 13:53:22 +00:00
egg_list_box_real_button_release_event (GtkWidget *widget,
2012-08-16 12:39:26 +00:00
GdkEventButton *event)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
if (event->button == 1)
{
if (priv->active_child != NULL &&
priv->active_child_active)
{
if (priv->activate_single_click)
egg_list_box_select_and_activate (list_box, priv->active_child);
else
egg_list_box_update_selected (list_box, priv->active_child);
}
priv->active_child = NULL;
priv->active_child_active = FALSE;
gtk_widget_queue_draw (GTK_WIDGET (list_box));
}
return FALSE;
}
static void
2012-08-16 13:53:22 +00:00
egg_list_box_real_show (GtkWidget *widget)
{
2012-08-16 13:56:50 +00:00
EggListBox * list_box = EGG_LIST_BOX (widget);
2012-08-16 12:51:04 +00:00
2012-08-16 13:56:50 +00:00
egg_list_box_reseparate (list_box);
2012-08-16 12:51:04 +00:00
2012-08-16 13:56:50 +00:00
GTK_WIDGET_CLASS (egg_list_box_parent_class)->show ((GtkWidget*) G_TYPE_CHECK_INSTANCE_CAST (list_box, GTK_TYPE_CONTAINER, GtkContainer));
}
static gboolean
2012-08-16 13:53:22 +00:00
egg_list_box_real_focus (GtkWidget* widget, GtkDirectionType direction)
{
2012-08-16 14:04:02 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
EggListBoxPrivate *priv = list_box->priv;
gboolean had_focus = FALSE;
gboolean focus_into = FALSE;
GtkWidget* recurse_into;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *current_focus_child;
EggListBoxChildInfo *next_focus_child;
gboolean modify_selection_pressed;
GdkModifierType state = 0;
recurse_into = NULL;
focus_into = TRUE;
2012-08-16 14:58:24 +00:00
g_object_get (GTK_WIDGET (list_box), "has-focus", &had_focus, NULL);
current_focus_child = NULL;
next_focus_child = NULL;
if (had_focus)
{
/* If on row, going right, enter into possible container */
if (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)
{
2012-08-16 14:04:02 +00:00
if (priv->cursor_child != NULL)
recurse_into = priv->cursor_child->widget;
}
2012-08-16 14:04:02 +00:00
current_focus_child = priv->cursor_child;
/* Unless we're going up/down we're always leaving
the container */
if (direction != GTK_DIR_UP && direction != GTK_DIR_DOWN)
focus_into = FALSE;
}
2012-08-16 13:56:50 +00:00
else if (gtk_container_get_focus_child ((GtkContainer*) list_box) != NULL)
{
/* There is a focus child, always navigat inside it first */
2012-08-16 13:56:50 +00:00
recurse_into = gtk_container_get_focus_child ((GtkContainer*) list_box);
current_focus_child = egg_list_box_lookup_info (list_box, recurse_into);
/* If exiting child container to the right, exit row */
if (direction == GTK_DIR_RIGHT || direction == GTK_DIR_TAB_FORWARD)
focus_into = FALSE;
/* If exiting child container to the left, select row or out */
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
next_focus_child = current_focus_child;
}
else
{
/* If coming from the left, enter into possible container */
if (direction == GTK_DIR_LEFT || direction == GTK_DIR_TAB_BACKWARD)
{
2012-08-16 14:04:02 +00:00
if (priv->selected_child != NULL)
recurse_into = priv->selected_child->widget;
}
}
if (recurse_into != NULL)
{
if (gtk_widget_child_focus (recurse_into, direction))
return TRUE;
}
if (!focus_into)
return FALSE; /* Focus is leaving us */
/* TODO: This doesn't handle up/down going into a focusable separator */
if (next_focus_child == NULL)
{
if (current_focus_child != NULL)
{
GSequenceIter* i;
if (direction == GTK_DIR_UP)
{
2012-08-16 13:56:50 +00:00
i = egg_list_box_get_previous_visible (list_box, current_focus_child->iter);
2012-08-16 12:51:04 +00:00
if (i != NULL)
next_focus_child = g_sequence_get (i);
2012-08-16 12:51:04 +00:00
}
else
{
2012-08-16 13:56:50 +00:00
i = egg_list_box_get_next_visible (list_box, current_focus_child->iter);
if (!g_sequence_iter_is_end (i))
next_focus_child = g_sequence_get (i);
2012-08-16 12:51:04 +00:00
}
}
else
{
switch (direction)
{
case GTK_DIR_UP:
case GTK_DIR_TAB_BACKWARD:
next_focus_child = priv->selected_child;
if (next_focus_child == NULL)
next_focus_child = egg_list_box_get_last_visible (list_box);
break;
default:
2012-08-16 14:04:02 +00:00
next_focus_child = priv->selected_child;
if (next_focus_child == NULL)
next_focus_child =
2012-08-16 13:56:50 +00:00
egg_list_box_get_first_visible (list_box);
break;
}
}
}
if (next_focus_child == NULL)
{
if (direction == GTK_DIR_UP || direction == GTK_DIR_DOWN)
{
if (gtk_widget_keynav_failed (GTK_WIDGET (list_box), direction))
return TRUE;
}
return FALSE;
}
modify_selection_pressed = FALSE;
if (gtk_get_current_event_state (&state))
{
GdkModifierType modify_mod_mask;
modify_mod_mask =
2012-08-16 14:58:24 +00:00
gtk_widget_get_modifier_mask (GTK_WIDGET (list_box),
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
if ((state & modify_mod_mask) == modify_mod_mask)
modify_selection_pressed = TRUE;
}
2012-08-16 13:56:50 +00:00
egg_list_box_update_cursor (list_box, next_focus_child);
if (!modify_selection_pressed)
2012-08-16 13:56:50 +00:00
egg_list_box_update_selected (list_box, next_focus_child);
return TRUE;
}
typedef struct {
EggListBoxChildInfo *child;
GtkStateFlags state;
} ChildFlags;
static ChildFlags*
child_flags_find_or_add (ChildFlags *array,
int *array_length,
EggListBoxChildInfo *to_find)
{
gint i;
for (i = 0; i < *array_length; i++)
{
if (array[i].child == to_find)
return &array[i];
}
*array_length = *array_length + 1;
array[*array_length - 1].child = to_find;
array[*array_length - 1].state = 0;
return &array[*array_length - 1];
}
static gboolean
2012-08-16 13:53:22 +00:00
egg_list_box_real_draw (GtkWidget* widget, cairo_t* cr)
{
2012-08-16 13:56:50 +00:00
EggListBox * list_box = EGG_LIST_BOX (widget);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
GtkAllocation allocation = {0};
GtkStyleContext* context;
GtkStateFlags state;
ChildFlags flags[3], *found;
gint flags_length;
gint focus_pad;
int i;
2012-08-16 14:58:24 +00:00
gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
state = gtk_widget_get_state_flags (widget);
gtk_render_background (context, cr, (gdouble) 0, (gdouble) 0, (gdouble) allocation.width, (gdouble) allocation.height);
flags_length = 0;
2012-08-16 14:04:02 +00:00
if (priv->selected_child != NULL)
{
2012-08-16 14:04:02 +00:00
found = child_flags_find_or_add (flags, &flags_length, priv->selected_child);
found->state |= (state | GTK_STATE_FLAG_SELECTED);
}
2012-08-16 14:04:02 +00:00
if (priv->prelight_child != NULL)
{
2012-08-16 14:04:02 +00:00
found = child_flags_find_or_add (flags, &flags_length, priv->prelight_child);
found->state |= (state | GTK_STATE_FLAG_PRELIGHT);
}
2012-08-16 14:04:02 +00:00
if (priv->active_child != NULL && priv->active_child_active)
{
2012-08-16 14:04:02 +00:00
found = child_flags_find_or_add (flags, &flags_length, priv->active_child);
found->state |= (state | GTK_STATE_FLAG_ACTIVE);
}
2012-08-16 12:51:04 +00:00
for (i = 0; i < flags_length; i++)
{
ChildFlags *flag = &flags[i];
gtk_style_context_save (context);
gtk_style_context_set_state (context, flag->state);
gtk_render_background (context, cr, 0, flag->child->y, allocation.width, flag->child->height);
gtk_style_context_restore (context);
}
2012-08-16 14:58:24 +00:00
if (gtk_widget_has_visible_focus (GTK_WIDGET (list_box)) && priv->cursor_child != NULL)
{
gtk_style_context_get_style (context,
"focus-padding", &focus_pad,
NULL);
gtk_render_focus (context, cr, focus_pad, priv->cursor_child->y + focus_pad,
allocation.width - 2 * focus_pad, priv->cursor_child->height - 2 * focus_pad);
}
2012-08-16 13:56:50 +00:00
GTK_WIDGET_CLASS (egg_list_box_parent_class)->draw ((GtkWidget*) G_TYPE_CHECK_INSTANCE_CAST (list_box, GTK_TYPE_CONTAINER, GtkContainer), cr);
return TRUE;
}
static void
2012-08-16 13:53:22 +00:00
egg_list_box_real_realize (GtkWidget* widget)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
GtkAllocation allocation;
GdkWindowAttr attributes = {0};
2012-08-16 12:39:26 +00:00
GdkWindow *window;
2012-08-16 14:58:24 +00:00
gtk_widget_get_allocation (GTK_WIDGET (list_box), &allocation);
gtk_widget_set_realized (GTK_WIDGET (list_box), TRUE);
attributes.x = allocation.x;
attributes.y = allocation.y;
attributes.width = allocation.width;
attributes.height = allocation.height;
attributes.window_type = GDK_WINDOW_CHILD;
2012-08-16 14:58:24 +00:00
attributes.event_mask = gtk_widget_get_events (GTK_WIDGET (list_box)) |
GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK | GDK_POINTER_MOTION_MASK |
GDK_EXPOSURE_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK;
attributes.wclass = GDK_INPUT_OUTPUT;
2012-08-16 14:58:24 +00:00
window = gdk_window_new (gtk_widget_get_parent_window (GTK_WIDGET (list_box)),
&attributes, GDK_WA_X | GDK_WA_Y);
2012-08-16 14:58:24 +00:00
gtk_style_context_set_background (gtk_widget_get_style_context (GTK_WIDGET (list_box)), window);
2012-08-16 13:56:50 +00:00
gdk_window_set_user_data (window, (GObject*) list_box);
2012-08-16 14:58:24 +00:00
gtk_widget_set_window (GTK_WIDGET (list_box), window); /* Passes ownership */
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_apply_filter (EggListBox *list_box, GtkWidget *child)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
gboolean do_show;
do_show = TRUE;
2012-08-16 14:04:02 +00:00
if (priv->filter_func != NULL)
do_show = priv->filter_func (child, priv->filter_func_target);
gtk_widget_set_child_visible (child, do_show);
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_apply_filter_all (EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child_info;
GSequenceIter *iter;
2012-08-16 14:04:02 +00:00
for (iter = g_sequence_get_begin_iter (priv->children);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
child_info = g_sequence_get (iter);
2012-08-16 13:56:50 +00:00
egg_list_box_apply_filter (list_box, child_info->widget);
}
}
2012-08-16 14:53:58 +00:00
/* Children are visible if they are shown by the app (visible)
and not filtered out (child_visible) by the listbox */
static gboolean
child_is_visible (GtkWidget *child)
{
return gtk_widget_get_visible (child) && gtk_widget_get_child_visible (child);
}
static EggListBoxChildInfo*
2012-08-16 13:56:50 +00:00
egg_list_box_get_first_visible (EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child_info;
GSequenceIter *iter;
2012-08-16 14:04:02 +00:00
for (iter = g_sequence_get_begin_iter (priv->children);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
child_info = g_sequence_get (iter);
2012-08-16 14:53:58 +00:00
if (child_is_visible (child_info->widget))
return child_info;
}
return NULL;
}
static EggListBoxChildInfo*
2012-08-16 13:56:50 +00:00
egg_list_box_get_last_visible (EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child_info;
2012-08-16 14:04:02 +00:00
GSequenceIter *iter;
2012-08-16 14:04:02 +00:00
iter = g_sequence_get_end_iter (priv->children);
while (!g_sequence_iter_is_begin (iter))
{
iter = g_sequence_iter_prev (iter);
child_info = g_sequence_get (iter);
2012-08-16 14:53:58 +00:00
if (child_is_visible (child_info->widget))
return child_info;
}
return NULL;
}
static GSequenceIter*
2012-08-16 13:56:50 +00:00
egg_list_box_get_previous_visible (EggListBox *list_box,
GSequenceIter* iter)
{
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child_info;
if (g_sequence_iter_is_begin (iter))
return NULL;
do
{
iter = g_sequence_iter_prev (iter);
child_info = g_sequence_get (iter);
2012-08-16 14:53:58 +00:00
if (child_is_visible (child_info->widget))
return iter;
}
while (!g_sequence_iter_is_begin (iter));
2012-08-16 12:51:04 +00:00
return NULL;
}
static GSequenceIter*
2012-08-16 13:56:50 +00:00
egg_list_box_get_next_visible (EggListBox *list_box, GSequenceIter* iter)
{
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child_info;
if (g_sequence_iter_is_end (iter))
return iter;
do
{
iter = g_sequence_iter_next (iter);
if (!g_sequence_iter_is_end (iter))
{
child_info = g_sequence_get (iter);
2012-08-16 14:53:58 +00:00
if (child_is_visible (child_info->widget))
return iter;
}
}
while (!g_sequence_iter_is_end (iter));
return iter;
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_update_separator (EggListBox *list_box, GSequenceIter* iter)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *info;
GSequenceIter *before_iter;
GtkWidget *child;
GtkWidget *before_child;
EggListBoxChildInfo *before_info;
GtkWidget *old_separator;
if (iter == NULL || g_sequence_iter_is_end (iter))
return;
info = g_sequence_get (iter);
2012-08-16 13:56:50 +00:00
before_iter = egg_list_box_get_previous_visible (list_box, iter);
2012-08-16 14:35:47 +00:00
child = info->widget;
if (child)
g_object_ref (child);
2012-08-16 12:39:26 +00:00
before_child = NULL;
if (before_iter != NULL)
{
before_info = g_sequence_get (before_iter);
2012-08-16 14:35:47 +00:00
before_child = before_info->widget;
if (before_child)
g_object_ref (before_child);
}
2012-08-16 12:51:04 +00:00
2012-08-16 14:04:02 +00:00
if (priv->update_separator_func != NULL &&
2012-08-16 14:53:58 +00:00
child_is_visible (child))
{
2012-08-16 14:35:47 +00:00
old_separator = info->separator;
if (old_separator)
g_object_ref (old_separator);
2012-08-16 14:04:02 +00:00
priv->update_separator_func (&info->separator,
2012-08-16 14:35:47 +00:00
child,
before_child,
priv->update_separator_func_target);
if (old_separator != info->separator)
{
if (old_separator != NULL)
{
gtk_widget_unparent (old_separator);
2012-08-16 14:04:02 +00:00
g_hash_table_remove (priv->separator_hash, old_separator);
}
if (info->separator != NULL)
{
2012-08-16 14:04:02 +00:00
g_hash_table_insert (priv->separator_hash, info->separator, info);
2012-08-16 14:58:24 +00:00
gtk_widget_set_parent (info->separator, GTK_WIDGET (list_box));
gtk_widget_show (info->separator);
}
2012-08-16 14:58:24 +00:00
gtk_widget_queue_resize (GTK_WIDGET (list_box));
}
2012-08-16 14:35:47 +00:00
if (old_separator)
g_object_unref (old_separator);
}
else
{
if (info->separator != NULL)
{
2012-08-16 14:04:02 +00:00
g_hash_table_remove (priv->separator_hash, info->separator);
gtk_widget_unparent (info->separator);
2012-08-16 14:35:47 +00:00
g_clear_object (&info->separator);
2012-08-16 14:58:24 +00:00
gtk_widget_queue_resize (GTK_WIDGET (list_box));
}
}
2012-08-16 14:35:47 +00:00
if (before_child)
g_object_unref (before_child);
if (child)
g_object_unref (child);
}
static EggListBoxChildInfo*
2012-08-16 13:56:50 +00:00
egg_list_box_lookup_info (EggListBox *list_box, GtkWidget* child)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
return g_hash_table_lookup (priv->child_hash, child);
}
static void
2012-08-16 14:10:09 +00:00
child_visibility_changed (GObject* object, GParamSpec* pspec, EggListBox *list_box)
{
2012-08-16 14:10:09 +00:00
EggListBoxChildInfo *info;
2012-08-16 14:58:24 +00:00
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
2012-08-16 14:10:09 +00:00
{
info = egg_list_box_lookup_info (list_box, GTK_WIDGET (object));
if (info != NULL)
{
egg_list_box_update_separator (list_box, info->iter);
egg_list_box_update_separator (list_box,
egg_list_box_get_next_visible (list_box, info->iter));
}
}
}
static void
2012-08-16 13:51:59 +00:00
egg_list_box_real_add (GtkContainer* container, GtkWidget* child)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (container);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *info;
GSequenceIter* iter = NULL;
2012-08-16 12:39:26 +00:00
info = egg_list_box_child_info_new (child);
2012-08-16 14:04:02 +00:00
g_hash_table_insert (priv->child_hash, child, info);
if (priv->sort_func != NULL)
iter = g_sequence_insert_sorted (priv->children, info,
2012-08-16 13:56:50 +00:00
(GCompareDataFunc)do_sort, list_box);
else
2012-08-16 14:04:02 +00:00
iter = g_sequence_append (priv->children, info);
info->iter = iter;
2012-08-16 14:58:24 +00:00
gtk_widget_set_parent (child, GTK_WIDGET (list_box));
2012-08-16 13:56:50 +00:00
egg_list_box_apply_filter (list_box, child);
2012-08-16 14:58:24 +00:00
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
{
2012-08-16 13:56:50 +00:00
egg_list_box_update_separator (list_box, iter);
egg_list_box_update_separator (list_box, egg_list_box_get_next_visible (list_box, iter));
}
2012-08-16 12:39:26 +00:00
g_signal_connect_object (child, "notify::visible",
2012-08-16 14:10:09 +00:00
(GCallback) child_visibility_changed, list_box, 0);
}
static void
2012-08-16 13:51:59 +00:00
egg_list_box_real_remove (GtkContainer* container, GtkWidget* child)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (container);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
gboolean was_visible;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *info;
GSequenceIter *next;
g_return_if_fail (child != NULL);
was_visible = gtk_widget_get_visible (child);
2012-08-16 14:10:09 +00:00
g_signal_handlers_disconnect_by_func (child, (GCallback) child_visibility_changed, list_box);
2012-08-16 12:51:04 +00:00
2012-08-16 13:56:50 +00:00
info = egg_list_box_lookup_info (list_box, child);
if (info == NULL)
{
2012-08-16 14:04:02 +00:00
info = g_hash_table_lookup (priv->separator_hash, child);
if (info != NULL)
{
2012-08-16 14:04:02 +00:00
g_hash_table_remove (priv->separator_hash, child);
2012-08-16 14:35:47 +00:00
g_clear_object (&info->separator);
2012-08-16 12:39:26 +00:00
gtk_widget_unparent (child);
2012-08-16 14:58:24 +00:00
if (was_visible && gtk_widget_get_visible (GTK_WIDGET (list_box)))
gtk_widget_queue_resize (GTK_WIDGET (list_box));
}
else
{
2012-08-16 12:39:26 +00:00
g_warning ("egg-list-box.vala:846: Tried to remove non-child %p\n", child);
}
return;
}
2012-08-16 12:51:04 +00:00
if (info->separator != NULL)
{
2012-08-16 14:04:02 +00:00
g_hash_table_remove (priv->separator_hash, info->separator);
gtk_widget_unparent (info->separator);
2012-08-16 14:35:47 +00:00
g_clear_object (&info->separator);
}
2012-08-16 14:04:02 +00:00
if (info == priv->selected_child)
2012-08-16 13:56:50 +00:00
egg_list_box_update_selected (list_box, NULL);
2012-08-16 14:04:02 +00:00
if (info == priv->prelight_child)
priv->prelight_child = NULL;
if (info == priv->cursor_child)
priv->cursor_child = NULL;
if (info == priv->active_child)
priv->active_child = NULL;
2012-08-16 13:56:50 +00:00
next = egg_list_box_get_next_visible (list_box, info->iter);
2012-08-16 12:39:26 +00:00
gtk_widget_unparent (child);
2012-08-16 14:04:02 +00:00
g_hash_table_remove (priv->child_hash, child);
g_sequence_remove (info->iter);
2012-08-16 14:58:24 +00:00
if (gtk_widget_get_visible (GTK_WIDGET (list_box)))
2012-08-16 13:56:50 +00:00
egg_list_box_update_separator (list_box, next);
2012-08-16 14:58:24 +00:00
if (was_visible && gtk_widget_get_visible (GTK_WIDGET (list_box)))
gtk_widget_queue_resize (GTK_WIDGET (list_box));
}
static void
2012-08-16 13:51:59 +00:00
egg_list_box_real_forall_internal (GtkContainer* container,
gboolean include_internals,
GtkCallback callback,
void* callback_target)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (container);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
GSequenceIter *iter;
EggListBoxChildInfo *child_info;
2012-08-16 14:04:02 +00:00
iter = g_sequence_get_begin_iter (priv->children);
while (!g_sequence_iter_is_end (iter))
{
child_info = g_sequence_get (iter);
iter = g_sequence_iter_next (iter);
if (child_info->separator != NULL && include_internals)
callback (child_info->separator, callback_target);
callback (child_info->widget, callback_target);
}
}
static void
2012-08-16 13:53:22 +00:00
egg_list_box_real_compute_expand_internal (GtkWidget* widget,
gboolean* hexpand,
gboolean* vexpand)
{
2012-08-16 13:53:22 +00:00
GTK_WIDGET_CLASS (egg_list_box_parent_class)->compute_expand (widget,
hexpand, vexpand);
/* We don't expand vertically beyound the minimum size */
if (vexpand)
*vexpand = FALSE;
}
static GType
2012-08-16 13:51:59 +00:00
egg_list_box_real_child_type (GtkContainer* container)
{
return GTK_TYPE_WIDGET;
}
static GtkSizeRequestMode
2012-08-16 13:53:22 +00:00
egg_list_box_real_get_request_mode (GtkWidget* widget)
{
return GTK_SIZE_REQUEST_HEIGHT_FOR_WIDTH;
}
static void
2012-08-16 13:53:22 +00:00
egg_list_box_real_get_preferred_height (GtkWidget* widget,
gint* minimum_height,
gint* natural_height)
{
gint natural_width;
2012-08-16 13:53:22 +00:00
egg_list_box_real_get_preferred_width (widget, NULL, &natural_width);
egg_list_box_real_get_preferred_height_for_width (widget, natural_width,
minimum_height, natural_height);
}
static void
2012-08-16 13:53:22 +00:00
egg_list_box_real_get_preferred_height_for_width (GtkWidget* widget, gint width,
gint* minimum_height_out, gint* natural_height_out)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 12:39:26 +00:00
GSequenceIter *iter;
gint minimum_height;
gint natural_height;
2012-08-16 12:39:26 +00:00
GtkStyleContext *context;
gint focus_width;
gint focus_pad;
minimum_height = 0;
2012-08-16 14:58:24 +00:00
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
gtk_style_context_get_style (context,
"focus-line-width", &focus_width,
"focus-padding", &focus_pad, NULL);
2012-08-16 14:04:02 +00:00
for (iter = g_sequence_get_begin_iter (priv->children);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child_info;
GtkWidget *child;
gint child_min = 0;
child_info = g_sequence_get (iter);
2012-08-16 12:39:26 +00:00
child = child_info->widget;
2012-08-16 14:53:58 +00:00
if (!child_is_visible (child))
continue;
if (child_info->separator != NULL)
{
gtk_widget_get_preferred_height_for_width (child_info->separator, width, &child_min, NULL);
minimum_height += child_min;
}
2012-08-16 12:39:26 +00:00
gtk_widget_get_preferred_height_for_width (child, width - 2 * (focus_width + focus_pad),
&child_min, NULL);
minimum_height += child_min + 2 * (focus_width + focus_pad);
}
/* 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_height = minimum_height;
if (minimum_height_out)
*minimum_height_out = minimum_height;
if (natural_height_out)
*natural_height_out = natural_height;
}
static void
2012-08-16 13:53:22 +00:00
egg_list_box_real_get_preferred_width (GtkWidget* widget, gint* minimum_width_out, gint* natural_width_out)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
gint minimum_width;
gint natural_width;
2012-08-16 12:39:26 +00:00
GtkStyleContext *context;
gint focus_width;
gint focus_pad;
2012-08-16 12:39:26 +00:00
GSequenceIter *iter;
EggListBoxChildInfo *child_info;
GtkWidget *child;
gint child_min;
gint child_nat;
2012-08-16 12:51:04 +00:00
2012-08-16 14:58:24 +00:00
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
gtk_style_context_get_style (context, "focus-line-width", &focus_width, "focus-padding", &focus_pad, NULL);
2012-08-16 12:51:04 +00:00
minimum_width = 0;
natural_width = 0;
2012-08-16 14:04:02 +00:00
for (iter = g_sequence_get_begin_iter (priv->children);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
child_info = g_sequence_get (iter);
2012-08-16 12:39:26 +00:00
child = child_info->widget;
2012-08-16 14:53:58 +00:00
if (!child_is_visible (child))
continue;
2012-08-16 12:39:26 +00:00
gtk_widget_get_preferred_width (child, &child_min, &child_nat);
minimum_width = MAX (minimum_width, child_min + 2 * (focus_width + focus_pad));
natural_width = MAX (natural_width, child_nat + 2 * (focus_width + focus_pad));
if (child_info->separator != NULL)
{
gtk_widget_get_preferred_width (child_info->separator, &child_min, &child_nat);
minimum_width = MAX (minimum_width, child_min);
natural_width = MAX (natural_width, child_nat);
}
}
if (minimum_width_out)
*minimum_width_out = minimum_width;
if (natural_width_out)
*natural_width_out = natural_width;
}
static void
2012-08-16 13:53:22 +00:00
egg_list_box_real_get_preferred_width_for_height (GtkWidget *widget, gint height,
2012-08-16 12:39:26 +00:00
gint *minimum_width, gint *natural_width)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 14:58:24 +00:00
egg_list_box_real_get_preferred_width (GTK_WIDGET (list_box), minimum_width, natural_width);
}
static void
2012-08-16 13:53:22 +00:00
egg_list_box_real_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
GtkAllocation child_allocation;
GtkAllocation separator_allocation;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child_info;
GdkWindow *window;
GtkWidget *child;
GSequenceIter *iter;
GtkStyleContext *context;
gint focus_width;
gint focus_pad;
int child_min;
child_allocation.x = 0;
child_allocation.y = 0;
child_allocation.width = 0;
child_allocation.height = 0;
separator_allocation.x = 0;
separator_allocation.y = 0;
separator_allocation.width = 0;
separator_allocation.height = 0;
2012-08-16 12:51:04 +00:00
2012-08-16 14:58:24 +00:00
gtk_widget_set_allocation (GTK_WIDGET (list_box), allocation);
window = gtk_widget_get_window (GTK_WIDGET (list_box));
2012-08-16 12:51:04 +00:00
if (window != NULL)
gdk_window_move_resize (window,
allocation->x, allocation->y,
allocation->width, allocation->height);
2012-08-16 14:58:24 +00:00
context = gtk_widget_get_style_context (GTK_WIDGET (list_box));
gtk_style_context_get_style (context,
"focus-line-width", &focus_width,
"focus-padding", &focus_pad,
NULL);
child_allocation.x = 0 + focus_width + focus_pad;
child_allocation.y = 0;
child_allocation.width = allocation->width - 2 * (focus_width + focus_pad);
separator_allocation.x = 0;
separator_allocation.width = allocation->width;
2012-08-16 14:04:02 +00:00
for (iter = g_sequence_get_begin_iter (priv->children);
!g_sequence_iter_is_end (iter);
iter = g_sequence_iter_next (iter))
{
child_info = g_sequence_get (iter);
2012-08-16 12:39:26 +00:00
child = child_info->widget;
2012-08-16 14:53:58 +00:00
if (!child_is_visible (child))
{
child_info->y = child_allocation.y;
child_info->height = 0;
continue;
}
2012-08-16 12:51:04 +00:00
if (child_info->separator != NULL)
{
gtk_widget_get_preferred_height_for_width (child_info->separator,
allocation->width, &child_min, NULL);
separator_allocation.height = child_min;
separator_allocation.y = child_allocation.y;
gtk_widget_size_allocate (child_info->separator,
&separator_allocation);
child_allocation.y += child_min;
}
2012-08-16 12:51:04 +00:00
child_info->y = child_allocation.y;
child_allocation.y += focus_width + focus_pad;
2012-08-16 12:51:04 +00:00
2012-08-16 12:39:26 +00:00
gtk_widget_get_preferred_height_for_width (child, child_allocation.width, &child_min, NULL);
child_allocation.height = child_min;
child_info->height = child_allocation.height + 2 * (focus_width + focus_pad);
2012-08-16 12:39:26 +00:00
gtk_widget_size_allocate (child, &child_allocation);
child_allocation.y += child_min + focus_width + focus_pad;
}
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_drag_unhighlight_widget (EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
2012-08-16 14:04:02 +00:00
if (priv->drag_highlighted_widget == NULL)
return;
2012-08-16 14:04:02 +00:00
gtk_drag_unhighlight (priv->drag_highlighted_widget);
2012-08-16 14:35:47 +00:00
g_clear_object (&priv->drag_highlighted_widget);
}
void
2012-08-16 13:56:50 +00:00
egg_list_box_drag_highlight_widget (EggListBox *list_box, GtkWidget *child)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
GtkWidget *old_highlight;
2012-08-16 13:56:50 +00:00
g_return_if_fail (list_box != NULL);
2012-08-16 12:39:26 +00:00
g_return_if_fail (child != NULL);
2012-08-16 14:04:02 +00:00
if (priv->drag_highlighted_widget == child)
return;
2012-08-16 13:56:50 +00:00
egg_list_box_drag_unhighlight_widget (list_box);
2012-08-16 12:39:26 +00:00
gtk_drag_highlight (child);
2012-08-16 12:51:04 +00:00
2012-08-16 14:04:02 +00:00
old_highlight = priv->drag_highlighted_widget;
priv->drag_highlighted_widget = g_object_ref (child);
if (old_highlight)
g_object_unref (old_highlight);
}
static void
2012-08-16 13:53:22 +00:00
egg_list_box_real_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time_)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:56:50 +00:00
egg_list_box_drag_unhighlight_widget (list_box);
2012-08-16 14:04:02 +00:00
if (priv->auto_scroll_timeout_id != 0) {
g_source_remove (priv->auto_scroll_timeout_id);
priv->auto_scroll_timeout_id = 0;
}
}
2012-08-16 13:01:56 +00:00
typedef struct
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box;
2012-08-16 13:01:56 +00:00
gint move;
} MoveData;
static void
2012-08-16 13:01:56 +00:00
move_data_free (MoveData *data)
{
g_slice_free (MoveData, data);
}
static gboolean
2012-08-16 13:01:56 +00:00
drag_motion_timeout (MoveData *data)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = data->list_box;
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:01:56 +00:00
2012-08-16 14:04:02 +00:00
gtk_adjustment_set_value (priv->adjustment,
gtk_adjustment_get_value (priv->adjustment) +
gtk_adjustment_get_step_increment (priv->adjustment) * data->move);
return TRUE;
}
static gboolean
2012-08-16 13:53:22 +00:00
egg_list_box_real_drag_motion (GtkWidget *widget, GdkDragContext *context,
gint x, gint y, guint time_)
{
2012-08-16 13:56:50 +00:00
EggListBox *list_box = EGG_LIST_BOX (widget);
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
2012-08-16 13:01:56 +00:00
int move;
MoveData *data;
gdouble size;
/* Auto-scroll during Dnd if cursor is moving into the top/bottom portion of the
* box. */
2012-08-16 14:04:02 +00:00
if (priv->auto_scroll_timeout_id != 0)
{
2012-08-16 14:04:02 +00:00
g_source_remove (priv->auto_scroll_timeout_id);
priv->auto_scroll_timeout_id = 0;
}
2012-08-16 14:04:02 +00:00
if (priv->adjustment == NULL)
return FALSE;
/* Part of the view triggering auto-scroll */
size = 30;
2012-08-16 13:01:56 +00:00
move = 0;
2012-08-16 14:04:02 +00:00
if (y < (gtk_adjustment_get_value (priv->adjustment) + size))
{
/* Scroll up */
2012-08-16 13:01:56 +00:00
move = -1;
}
2012-08-16 14:04:02 +00:00
else if (y > ((gtk_adjustment_get_value (priv->adjustment) + gtk_adjustment_get_page_size (priv->adjustment)) - size))
{
/* Scroll down */
2012-08-16 13:01:56 +00:00
move = 1;
}
2012-08-16 13:01:56 +00:00
if (move == 0)
return FALSE;
2012-08-16 13:01:56 +00:00
data = g_slice_new0 (MoveData);
2012-08-16 13:56:50 +00:00
data->list_box = list_box;
2012-08-16 14:04:02 +00:00
priv->auto_scroll_timeout_id =
2012-08-16 13:01:56 +00:00
g_timeout_add_full (G_PRIORITY_DEFAULT, 150, (GSourceFunc)drag_motion_timeout,
data, (GDestroyNotify) move_data_free);
return FALSE;
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_real_activate_cursor_child (EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
egg_list_box_select_and_activate (list_box, priv->cursor_child);
}
static void
2012-08-16 13:56:50 +00:00
egg_list_box_real_toggle_cursor_child (EggListBox *list_box)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
if (priv->cursor_child == NULL)
return;
2012-08-16 14:04:02 +00:00
if (priv->selection_mode == GTK_SELECTION_SINGLE &&
priv->selected_child == priv->cursor_child)
2012-08-16 13:56:50 +00:00
egg_list_box_update_selected (list_box, NULL);
else
2012-08-16 14:04:02 +00:00
egg_list_box_select_and_activate (list_box, priv->cursor_child);
}
static void
2012-08-16 14:04:02 +00:00
egg_list_box_real_move_cursor (EggListBox *list_box,
GtkMovementStep step,
gint count)
{
2012-08-16 14:04:02 +00:00
EggListBoxPrivate *priv = list_box->priv;
GdkModifierType state;
gboolean modify_selection_pressed;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *child;
GdkModifierType modify_mod_mask;
2012-08-16 12:39:26 +00:00
EggListBoxChildInfo *prev;
EggListBoxChildInfo *next;
gint page_size;
2012-08-16 12:39:26 +00:00
GSequenceIter *iter;
gint start_y;
gint end_y;
2012-08-16 12:51:04 +00:00
modify_selection_pressed = FALSE;
if (gtk_get_current_event_state (&state))
{
2012-08-16 14:58:24 +00:00
modify_mod_mask = gtk_widget_get_modifier_mask (GTK_WIDGET (list_box),
GDK_MODIFIER_INTENT_MODIFY_SELECTION);
if ((state & modify_mod_mask) == modify_mod_mask)
modify_selection_pressed = TRUE;
}
2012-08-16 12:51:04 +00:00
child = NULL;
switch (step)
{
case GTK_MOVEMENT_BUFFER_ENDS:
if (count < 0)
2012-08-16 13:56:50 +00:00
child = egg_list_box_get_first_visible (list_box);
else
2012-08-16 13:56:50 +00:00
child = egg_list_box_get_last_visible (list_box);
break;
case GTK_MOVEMENT_DISPLAY_LINES:
2012-08-16 14:04:02 +00:00
if (priv->cursor_child != NULL)
{
2012-08-16 14:04:02 +00:00
iter = priv->cursor_child->iter;
2012-08-16 12:51:04 +00:00
while (count < 0 && iter != NULL)
{
2012-08-16 13:56:50 +00:00
iter = egg_list_box_get_previous_visible (list_box, iter);
count = count + 1;
}
while (count > 0 && iter != NULL)
{
2012-08-16 13:56:50 +00:00
iter = egg_list_box_get_next_visible (list_box, iter);
count = count - 1;
}
2012-08-16 12:51:04 +00:00
if (iter != NULL && !g_sequence_iter_is_end (iter))
child = g_sequence_get (iter);
}
break;
case GTK_MOVEMENT_PAGES:
page_size = 100;
2012-08-16 14:04:02 +00:00
if (priv->adjustment != NULL)
page_size = gtk_adjustment_get_page_increment (priv->adjustment);
2012-08-16 14:04:02 +00:00
if (priv->cursor_child != NULL)
{
2012-08-16 14:04:02 +00:00
start_y = priv->cursor_child->y;
end_y = start_y;
2012-08-16 14:04:02 +00:00
iter = priv->cursor_child->iter;
2012-08-16 12:51:04 +00:00
2012-08-16 14:04:02 +00:00
child = priv->cursor_child;
if (count < 0)
{
/* Up */
while (iter != NULL && !g_sequence_iter_is_begin (iter))
{
2012-08-16 13:56:50 +00:00
iter = egg_list_box_get_previous_visible (list_box, iter);
if (iter == NULL)
break;
2012-08-16 12:51:04 +00:00
prev = g_sequence_get (iter);
if (prev->y < start_y - page_size)
break;
child = prev;
}
}
else
{
/* Down */
while (iter != NULL && !g_sequence_iter_is_end (iter))
{
2012-08-16 13:56:50 +00:00
iter = egg_list_box_get_next_visible (list_box, iter);
if (g_sequence_iter_is_end (iter))
break;
next = g_sequence_get (iter);
if (next->y > start_y + page_size)
break;
2012-08-16 12:51:04 +00:00
child = next;
}
}
end_y = child->y;
2012-08-16 14:04:02 +00:00
if (end_y != start_y && priv->adjustment != NULL)
gtk_adjustment_set_value (priv->adjustment,
gtk_adjustment_get_value (priv->adjustment) +
end_y - start_y);
}
break;
default:
return;
}
if (child == NULL || child == priv->cursor_child)
{
GtkDirectionType direction = count < 0 ? GTK_DIR_UP : GTK_DIR_DOWN;
if (!gtk_widget_keynav_failed (GTK_WIDGET (list_box), direction))
{
GtkWidget *toplevel = gtk_widget_get_toplevel (GTK_WIDGET (list_box));
if (toplevel)
gtk_widget_child_focus (toplevel,
direction == GTK_DIR_UP ?
GTK_DIR_TAB_BACKWARD :
GTK_DIR_TAB_FORWARD);
}
return;
}
2012-08-16 12:51:04 +00:00
2012-08-16 13:56:50 +00:00
egg_list_box_update_cursor (list_box, child);
if (!modify_selection_pressed)
2012-08-16 13:56:50 +00:00
egg_list_box_update_selected (list_box, child);
}