aboutsummaryrefslogtreecommitdiff
path: root/src/gtksctree.c
diff options
context:
space:
mode:
authorhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2005-01-12 11:22:08 +0000
committerhiro <hiro@ee746299-78ed-0310-b773-934348b2243d>2005-01-12 11:22:08 +0000
commitb9ca7b1ef5cd1f96ae6e28ae78d12c1e3258c23f (patch)
tree1203adec5f70af1ddd49868528d8d3a5b9004329 /src/gtksctree.c
Initial import of Sylpheed (GTK2 version).
git-svn-id: svn://sylpheed.sraoss.jp/sylpheed/trunk@1 ee746299-78ed-0310-b773-934348b2243d
Diffstat (limited to 'src/gtksctree.c')
-rw-r--r--src/gtksctree.c573
1 files changed, 573 insertions, 0 deletions
diff --git a/src/gtksctree.c b/src/gtksctree.c
new file mode 100644
index 00000000..eddb52fc
--- /dev/null
+++ b/src/gtksctree.c
@@ -0,0 +1,573 @@
+/*
+ * This program is based on gtkflist.c
+ */
+
+#include "gtksctree.h"
+#include "sylpheed-marshal.h"
+
+enum {
+ ROW_POPUP_MENU,
+ EMPTY_POPUP_MENU,
+ OPEN_ROW,
+ START_DRAG,
+ LAST_SIGNAL
+};
+
+
+static void gtk_sctree_class_init (GtkSCTreeClass *class);
+static void gtk_sctree_init (GtkSCTree *sctree);
+
+static gint gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event);
+static gint gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event);
+static gint gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event);
+static void gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context);
+static void gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context);
+static void gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
+ GtkSelectionData *data, guint info, guint time);
+static void gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time);
+static gboolean gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, guint time);
+static gboolean gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, guint time);
+static void gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, GtkSelectionData *data,
+ guint info, guint time);
+
+static void gtk_sctree_clear (GtkCList *clist);
+static void gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node);
+
+static GtkCTreeClass *parent_class;
+
+static guint sctree_signals[LAST_SIGNAL];
+
+
+/**
+ * gtk_sctree_get_type:
+ * @void:
+ *
+ * Creates the GtkSCTree class and its type information
+ *
+ * Return value: The type ID for GtkSCTreeClass
+ **/
+GType
+gtk_sctree_get_type (void)
+{
+ static GType sctree_type = 0;
+
+ if (!sctree_type) {
+ GTypeInfo sctree_info = {
+ sizeof (GtkSCTreeClass),
+
+ (GBaseInitFunc) NULL,
+ (GBaseFinalizeFunc) NULL,
+
+ (GClassInitFunc) gtk_sctree_class_init,
+ (GClassFinalizeFunc) NULL,
+ NULL, /* class_data */
+
+ sizeof (GtkSCTree),
+ 0, /* n_preallocs */
+ (GInstanceInitFunc) gtk_sctree_init,
+ };
+
+ sctree_type = g_type_register_static (GTK_TYPE_CTREE, "GtkSCTree", &sctree_info, (GTypeFlags)0);
+ }
+
+ return sctree_type;
+}
+
+/* Standard class initialization function */
+static void
+gtk_sctree_class_init (GtkSCTreeClass *klass)
+{
+ GtkObjectClass *object_class;
+ GtkWidgetClass *widget_class;
+ GtkCListClass *clist_class;
+ GtkCTreeClass *ctree_class;
+
+ object_class = (GtkObjectClass *) klass;
+ widget_class = (GtkWidgetClass *) klass;
+ clist_class = (GtkCListClass *) klass;
+ ctree_class = (GtkCTreeClass *) klass;
+
+ parent_class = gtk_type_class (gtk_ctree_get_type ());
+
+ sctree_signals[ROW_POPUP_MENU] =
+ g_signal_new ("row_popup_menu",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkSCTreeClass, row_popup_menu),
+ NULL, NULL,
+ sylpheed_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ GDK_TYPE_EVENT);
+ sctree_signals[EMPTY_POPUP_MENU] =
+ g_signal_new ("empty_popup_menu",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkSCTreeClass, empty_popup_menu),
+ NULL, NULL,
+ sylpheed_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1,
+ GDK_TYPE_EVENT);
+ sctree_signals[OPEN_ROW] =
+ g_signal_new ("open_row",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkSCTreeClass, open_row),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ sctree_signals[START_DRAG] =
+ g_signal_new ("start_drag",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (GtkSCTreeClass, start_drag),
+ NULL, NULL,
+ sylpheed_marshal_VOID__INT_POINTER,
+ G_TYPE_NONE, 2,
+ G_TYPE_INT,
+ GDK_TYPE_EVENT);
+
+ /* gtk_object_class_add_signals (object_class, sctree_signals, LAST_SIGNAL); */
+
+ clist_class->clear = gtk_sctree_clear;
+ ctree_class->tree_collapse = gtk_sctree_collapse;
+
+ widget_class->button_press_event = gtk_sctree_button_press;
+ widget_class->button_release_event = gtk_sctree_button_release;
+ widget_class->motion_notify_event = gtk_sctree_motion;
+ widget_class->drag_begin = gtk_sctree_drag_begin;
+ widget_class->drag_end = gtk_sctree_drag_end;
+ widget_class->drag_data_get = gtk_sctree_drag_data_get;
+ widget_class->drag_leave = gtk_sctree_drag_leave;
+ widget_class->drag_motion = gtk_sctree_drag_motion;
+ widget_class->drag_drop = gtk_sctree_drag_drop;
+ widget_class->drag_data_received = gtk_sctree_drag_data_received;
+}
+
+/* Standard object initialization function */
+static void
+gtk_sctree_init (GtkSCTree *sctree)
+{
+ sctree->anchor_row = NULL;
+
+ /* GtkCTree does not specify pointer motion by default */
+ gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
+ gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
+}
+
+/* Get information the specified row is selected. */
+
+static gboolean
+row_is_selected(GtkSCTree *sctree, gint row)
+{
+ GtkCListRow *clist_row;
+ clist_row = g_list_nth (GTK_CLIST(sctree)->row_list, row)->data;
+ return clist_row ? clist_row->state == GTK_STATE_SELECTED : FALSE;
+}
+
+/* Selects the rows between the anchor to the specified row, inclusive. */
+static void
+select_range (GtkSCTree *sctree, gint row)
+{
+ gint prev_row;
+ gint min, max;
+ gint i;
+
+ if (sctree->anchor_row == NULL) {
+ prev_row = row;
+ sctree->anchor_row = gtk_ctree_node_nth(GTK_CTREE(sctree), row);
+ } else
+ prev_row = g_list_position(GTK_CLIST(sctree)->row_list,
+ (GList *)sctree->anchor_row);
+
+ if (row < prev_row) {
+ min = row;
+ max = prev_row;
+ } else {
+ min = prev_row;
+ max = row;
+ }
+ for (i = min; i <= max; i++)
+ gtk_clist_select_row (GTK_CLIST (sctree), i, -1);
+}
+
+/* Handles row selection according to the specified modifier state */
+static void
+select_row (GtkSCTree *sctree, gint row, gint col, guint state)
+{
+ gboolean range, additive;
+ g_return_if_fail (sctree != NULL);
+ g_return_if_fail (GTK_IS_SCTREE (sctree));
+
+ range = ((state & GDK_SHIFT_MASK) != 0) &&
+ (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
+ (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
+ additive = ((state & GDK_CONTROL_MASK) != 0) &&
+ (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
+ (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
+
+ gtk_clist_freeze (GTK_CLIST (sctree));
+ GTK_CLIST(sctree)->focus_row = row;
+ GTK_CLIST_GET_CLASS(sctree)->refresh(GTK_CLIST(sctree));
+ if (!additive)
+ gtk_clist_unselect_all (GTK_CLIST (sctree));
+
+ if (!range) {
+ GtkCTreeNode *node;
+
+ node = gtk_ctree_node_nth (GTK_CTREE(sctree), row);
+
+ /*No need to manage overlapped list*/
+ if (additive) {
+ if (row_is_selected(sctree, row))
+ gtk_clist_unselect_row (GTK_CLIST (sctree), row, col);
+ else
+ g_signal_emit_by_name
+ (G_OBJECT (sctree),
+ "tree_select_row", node, col);
+ } else {
+ g_signal_emit_by_name
+ (G_OBJECT (sctree),
+ "tree_select_row", node, col);
+ }
+ sctree->anchor_row = node;
+ } else
+ select_range (sctree, row);
+ gtk_clist_thaw (GTK_CLIST (sctree));
+}
+
+/* Our handler for button_press events. We override all of GtkCList's broken
+ * behavior.
+ */
+static gint
+gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event)
+{
+ GtkSCTree *sctree;
+ GtkCList *clist;
+ gboolean on_row;
+ gint row;
+ gint col;
+ gint retval;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ sctree = GTK_SCTREE (widget);
+ clist = GTK_CLIST (widget);
+ retval = FALSE;
+
+ if (event->window != clist->clist_window)
+ return (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event);
+
+ on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
+
+ if (on_row && !GTK_WIDGET_HAS_FOCUS(widget))
+ gtk_widget_grab_focus (widget);
+
+ if (gtk_ctree_is_hot_spot (GTK_CTREE(sctree), event->x, event->y)) {
+ gtk_ctree_toggle_expansion
+ (GTK_CTREE(sctree),
+ gtk_ctree_node_nth(GTK_CTREE(sctree), row));
+ return TRUE;
+ }
+
+ switch (event->type) {
+ case GDK_BUTTON_PRESS:
+ if (event->button == 1 || event->button == 2) {
+ if (event->button == 2)
+ event->state &= ~(GDK_SHIFT_MASK | GDK_CONTROL_MASK);
+ if (on_row) {
+ /* Save the mouse info for DnD */
+ sctree->dnd_press_button = event->button;
+ sctree->dnd_press_x = event->x;
+ sctree->dnd_press_y = event->y;
+
+ /* Handle selection */
+ if ((row_is_selected (sctree, row)
+ && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
+ || ((event->state & GDK_CONTROL_MASK)
+ && !(event->state & GDK_SHIFT_MASK))) {
+ sctree->dnd_select_pending = TRUE;
+ sctree->dnd_select_pending_state = event->state;
+ sctree->dnd_select_pending_row = row;
+ } else
+ select_row (sctree, row, col, event->state);
+ } else
+ gtk_clist_unselect_all (clist);
+
+ retval = TRUE;
+ } else if (event->button == 3) {
+ /* Emit *_popup_menu signal*/
+ if (on_row) {
+ if (!row_is_selected(sctree,row))
+ select_row (sctree, row, col, 0);
+ g_signal_emit (G_OBJECT (sctree),
+ sctree_signals[ROW_POPUP_MENU],
+ 0, event);
+ } else {
+ gtk_clist_unselect_all(clist);
+ g_signal_emit (G_OBJECT (sctree),
+ sctree_signals[EMPTY_POPUP_MENU],
+ 0, event);
+ }
+ retval = TRUE;
+ }
+
+ break;
+
+ case GDK_2BUTTON_PRESS:
+ if (event->button != 1)
+ break;
+
+ sctree->dnd_select_pending = FALSE;
+ sctree->dnd_select_pending_state = 0;
+
+ if (on_row)
+ g_signal_emit (G_OBJECT (sctree),
+ sctree_signals[OPEN_ROW], 0);
+
+ retval = TRUE;
+ break;
+
+ default:
+ break;
+ }
+
+ return retval;
+}
+
+/* Our handler for button_release events. We override all of GtkCList's broken
+ * behavior.
+ */
+static gint
+gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event)
+{
+ GtkSCTree *sctree;
+ GtkCList *clist;
+ gint on_row;
+ gint row, col;
+ gint retval;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ sctree = GTK_SCTREE (widget);
+ clist = GTK_CLIST (widget);
+ retval = FALSE;
+
+ if (event->window != clist->clist_window)
+ return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
+
+ on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
+
+ if (!(event->button == 1 || event->button == 2))
+ return FALSE;
+
+ sctree->dnd_press_button = 0;
+ sctree->dnd_press_x = 0;
+ sctree->dnd_press_y = 0;
+
+ if (on_row) {
+ if (sctree->dnd_select_pending) {
+ select_row (sctree, row, col, sctree->dnd_select_pending_state);
+ sctree->dnd_select_pending = FALSE;
+ sctree->dnd_select_pending_state = 0;
+ }
+
+ retval = TRUE;
+ }
+
+ return retval;
+}
+
+/* Our handler for motion_notify events. We override all of GtkCList's broken
+ * behavior.
+ */
+static gint
+gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event)
+{
+ GtkSCTree *sctree;
+ GtkCList *clist;
+
+ g_return_val_if_fail (widget != NULL, FALSE);
+ g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
+ g_return_val_if_fail (event != NULL, FALSE);
+
+ sctree = GTK_SCTREE (widget);
+ clist = GTK_CLIST (widget);
+
+ if (event->window != clist->clist_window)
+ return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
+
+ if (!((sctree->dnd_press_button == 1 && (event->state & GDK_BUTTON1_MASK))
+ || (sctree->dnd_press_button == 2 && (event->state & GDK_BUTTON2_MASK))))
+ return FALSE;
+
+ /* This is the same threshold value that is used in gtkdnd.c */
+
+ if (MAX (ABS (sctree->dnd_press_x - event->x),
+ ABS (sctree->dnd_press_y - event->y)) <= 3)
+ return FALSE;
+
+ /* Handle any pending selections */
+
+ if (sctree->dnd_select_pending) {
+ if (!row_is_selected(sctree,sctree->dnd_select_pending_row))
+ select_row (sctree,
+ sctree->dnd_select_pending_row,
+ -1,
+ sctree->dnd_select_pending_state);
+
+ sctree->dnd_select_pending = FALSE;
+ sctree->dnd_select_pending_state = 0;
+ }
+
+ g_signal_emit (G_OBJECT (sctree),
+ sctree_signals[START_DRAG],
+ 0,
+ sctree->dnd_press_button,
+ event);
+ return TRUE;
+}
+
+/* We override the drag_begin signal to do nothing */
+static void
+gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context)
+{
+ /* nothing */
+}
+
+/* We override the drag_end signal to do nothing */
+static void
+gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context)
+{
+ /* nothing */
+}
+
+/* We override the drag_data_get signal to do nothing */
+static void
+gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
+ GtkSelectionData *data, guint info, guint time)
+{
+ /* nothing */
+}
+
+/* We override the drag_leave signal to do nothing */
+static void
+gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time)
+{
+ /* nothing */
+}
+
+/* We override the drag_motion signal to do nothing */
+static gboolean
+gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, guint time)
+{
+ return FALSE;
+}
+
+/* We override the drag_drop signal to do nothing */
+static gboolean
+gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, guint time)
+{
+ return FALSE;
+}
+
+/* We override the drag_data_received signal to do nothing */
+static void
+gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
+ gint x, gint y, GtkSelectionData *data,
+ guint info, guint time)
+{
+ /* nothing */
+}
+
+/* Our handler for the clear signal of the clist. We have to reset the anchor
+ * to null.
+ */
+static void
+gtk_sctree_clear (GtkCList *clist)
+{
+ GtkSCTree *sctree;
+
+ g_return_if_fail (clist != NULL);
+ g_return_if_fail (GTK_IS_SCTREE (clist));
+
+ sctree = GTK_SCTREE (clist);
+ sctree->anchor_row = NULL;
+
+ if (((GtkCListClass *)parent_class)->clear)
+ (* ((GtkCListClass *)parent_class)->clear) (clist);
+}
+
+/* Our handler for the change_focus_row_expansion signal of the ctree.
+ We have to set the anchor to parent visible node.
+ */
+static void
+gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node)
+{
+ g_return_if_fail (ctree != NULL);
+ g_return_if_fail (GTK_IS_SCTREE (ctree));
+
+ (* parent_class->tree_collapse) (ctree, node);
+ GTK_SCTREE(ctree)->anchor_row =
+ gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->focus_row);
+}
+
+GtkWidget *gtk_sctree_new_with_titles (gint columns, gint tree_column,
+ gchar *titles[])
+{
+#if 0
+ GtkSCTree* sctree;
+
+ sctree = gtk_type_new (gtk_sctree_get_type ());
+ gtk_ctree_construct (GTK_CTREE (sctree), columns, tree_column, titles);
+ gtk_clist_set_selection_mode(GTK_CLIST(sctree), GTK_SELECTION_EXTENDED);
+
+ return GTK_WIDGET (sctree);
+#else
+ GtkWidget *widget;
+
+ g_return_val_if_fail (columns > 0, NULL);
+ g_return_val_if_fail (tree_column >= 0 && tree_column < columns, NULL);
+
+ widget = gtk_widget_new (TYPE_GTK_SCTREE,
+ "n_columns", columns,
+ "tree_column", tree_column,
+ NULL);
+ if (titles) {
+ GtkCList *clist = GTK_CLIST (widget);
+ guint i;
+
+ for (i = 0; i < columns; i++)
+ gtk_clist_set_column_title (clist, i, titles[i]);
+ gtk_clist_column_titles_show (clist);
+ }
+
+ return widget;
+#endif
+}
+
+void gtk_sctree_select (GtkSCTree *sctree, GtkCTreeNode *node)
+{
+ select_row(sctree,
+ g_list_position(GTK_CLIST(sctree)->row_list, (GList *)node),
+ -1, 0);
+}
+
+void gtk_sctree_unselect_all (GtkSCTree *sctree)
+{
+ gtk_clist_unselect_all(GTK_CLIST(sctree));
+ sctree->anchor_row = NULL;
+}
+
+void gtk_sctree_set_anchor_row (GtkSCTree *sctree, GtkCTreeNode *node)
+{
+ sctree->anchor_row = node;
+}