This commit was manufactured by cvs2svn to create tag 'LAST_STABLE'.
[claws.git] / src / gtksctree.c
blobe1713ba99e675ad146ff9fd90ba2c44086c53a79
1 /*
2 * This program is based on gtkflist.c
3 */
5 #include "utils.h"
6 #include "gtkutils.h"
7 #include "gtksctree.h"
10 enum {
11 ROW_POPUP_MENU,
12 EMPTY_POPUP_MENU,
13 OPEN_ROW,
14 START_DRAG,
15 LAST_SIGNAL
19 static void gtk_sctree_class_init (GtkSCTreeClass *class);
20 static void gtk_sctree_init (GtkSCTree *sctree);
22 static gint gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event);
23 static gint gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event);
24 static gint gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event);
25 static void gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context);
26 static void gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context);
27 static void gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
28 GtkSelectionData *data, guint info, guint time);
29 static void gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time);
30 static gboolean gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
31 gint x, gint y, guint time);
32 static gboolean gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
33 gint x, gint y, guint time);
34 static void gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
35 gint x, gint y, GtkSelectionData *data,
36 guint info, guint time);
38 static void gtk_sctree_clear (GtkCList *clist);
39 static void gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node);
41 static GtkCTreeClass *parent_class;
43 static guint sctree_signals[LAST_SIGNAL];
46 /**
47 * gtk_sctree_get_type:
48 * @void:
50 * Creates the GtkSCTree class and its type information
52 * Return value: The type ID for GtkSCTreeClass
53 **/
54 GtkType
55 gtk_sctree_get_type (void)
57 static GtkType sctree_type = 0;
59 if (!sctree_type) {
60 GtkTypeInfo sctree_info = {
61 "GtkSCTree",
62 sizeof (GtkSCTree),
63 sizeof (GtkSCTreeClass),
64 (GtkClassInitFunc) gtk_sctree_class_init,
65 (GtkObjectInitFunc) gtk_sctree_init,
66 NULL, /* reserved_1 */
67 NULL, /* reserved_2 */
68 (GtkClassInitFunc) NULL
71 sctree_type = gtk_type_unique (gtk_ctree_get_type (), &sctree_info);
74 return sctree_type;
77 /* Standard class initialization function */
78 static void
79 gtk_sctree_class_init (GtkSCTreeClass *klass)
81 GtkObjectClass *object_class;
82 GtkWidgetClass *widget_class;
83 GtkCListClass *clist_class;
84 GtkCTreeClass *ctree_class;
86 object_class = (GtkObjectClass *) klass;
87 widget_class = (GtkWidgetClass *) klass;
88 clist_class = (GtkCListClass *) klass;
89 ctree_class = (GtkCTreeClass *) klass;
91 parent_class = gtk_type_class (gtk_ctree_get_type ());
93 sctree_signals[ROW_POPUP_MENU] =
94 gtk_signal_new ("row_popup_menu",
95 GTK_RUN_FIRST,
96 object_class->type,
97 GTK_SIGNAL_OFFSET (GtkSCTreeClass, row_popup_menu),
98 gtk_marshal_NONE__POINTER,
99 GTK_TYPE_NONE, 1,
100 GTK_TYPE_GDK_EVENT);
101 sctree_signals[EMPTY_POPUP_MENU] =
102 gtk_signal_new ("empty_popup_menu",
103 GTK_RUN_FIRST,
104 object_class->type,
105 GTK_SIGNAL_OFFSET (GtkSCTreeClass, empty_popup_menu),
106 gtk_marshal_NONE__POINTER,
107 GTK_TYPE_NONE, 1,
108 GTK_TYPE_GDK_EVENT);
109 sctree_signals[OPEN_ROW] =
110 gtk_signal_new ("open_row",
111 GTK_RUN_FIRST,
112 object_class->type,
113 GTK_SIGNAL_OFFSET (GtkSCTreeClass, open_row),
114 gtk_marshal_NONE__NONE,
115 GTK_TYPE_NONE, 0);
116 sctree_signals[START_DRAG] =
117 gtk_signal_new ("start_drag",
118 GTK_RUN_FIRST,
119 object_class->type,
120 GTK_SIGNAL_OFFSET (GtkSCTreeClass, start_drag),
121 gtk_marshal_NONE__INT_POINTER,
122 GTK_TYPE_NONE, 2,
123 GTK_TYPE_INT,
124 GTK_TYPE_GDK_EVENT);
126 gtk_object_class_add_signals (object_class, sctree_signals, LAST_SIGNAL);
128 clist_class->clear = gtk_sctree_clear;
129 ctree_class->tree_collapse = gtk_sctree_collapse;
131 widget_class->button_press_event = gtk_sctree_button_press;
132 widget_class->button_release_event = gtk_sctree_button_release;
133 widget_class->motion_notify_event = gtk_sctree_motion;
134 widget_class->drag_begin = gtk_sctree_drag_begin;
135 widget_class->drag_end = gtk_sctree_drag_end;
136 widget_class->drag_data_get = gtk_sctree_drag_data_get;
137 widget_class->drag_leave = gtk_sctree_drag_leave;
138 widget_class->drag_motion = gtk_sctree_drag_motion;
139 widget_class->drag_drop = gtk_sctree_drag_drop;
140 widget_class->drag_data_received = gtk_sctree_drag_data_received;
143 /* Standard object initialization function */
144 static void
145 gtk_sctree_init (GtkSCTree *sctree)
147 sctree->anchor_row = -1;
149 /* GtkCTree does not specify pointer motion by default */
150 gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
151 gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
154 /* Get information the specified row is selected. */
156 static gboolean
157 row_is_selected(GtkSCTree *sctree, gint row)
159 GtkCListRow *clist_row;
160 clist_row = g_list_nth (GTK_CLIST(sctree)->row_list, row)->data;
161 return clist_row ? clist_row->state == GTK_STATE_SELECTED : FALSE;
164 /* Selects the rows between the anchor to the specified row, inclusive. */
165 static void
166 select_range (GtkSCTree *sctree, gint row)
168 gint min, max;
169 gint i;
171 if (sctree->anchor_row == -1)
172 sctree->anchor_row = row;
174 if (row < sctree->anchor_row) {
175 min = row;
176 max = sctree->anchor_row;
177 } else {
178 min = sctree->anchor_row;
179 max = row;
181 for (i = min; i <= max; i++)
182 gtk_clist_select_row (GTK_CLIST (sctree), i, -1);
185 /* Handles row selection according to the specified modifier state */
186 static void
187 select_row (GtkSCTree *sctree, gint row, gint col, guint state)
189 gboolean range, additive;
190 g_return_if_fail (sctree != NULL);
191 g_return_if_fail (GTK_IS_SCTREE (sctree));
193 range = ((state & GDK_SHIFT_MASK) != 0) &&
194 (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
195 (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
196 additive = ((state & GDK_CONTROL_MASK) != 0) &&
197 (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
198 (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
200 gtk_clist_freeze (GTK_CLIST (sctree));
201 gtkut_clist_set_focus_row(GTK_CLIST(sctree), row);
202 if (!additive)
203 gtk_clist_unselect_all (GTK_CLIST (sctree));
205 if (!range) {
206 /*No need to manage overlapped list*/
207 if (additive) {
208 if (row_is_selected(sctree, row))
209 gtk_clist_unselect_row (GTK_CLIST (sctree), row, col);
210 else
211 gtk_signal_emit_by_name
212 (GTK_OBJECT (sctree),
213 "tree_select_row",
214 gtk_ctree_node_nth (GTK_CTREE(sctree), row),
215 col);
216 } else {
217 gtk_signal_emit_by_name
218 (GTK_OBJECT (sctree),
219 "tree_select_row",
220 gtk_ctree_node_nth (GTK_CTREE(sctree), row),
221 col);
223 sctree->anchor_row = row;
224 } else
225 select_range (sctree, row);
226 gtk_clist_thaw (GTK_CLIST (sctree));
229 /* Our handler for button_press events. We override all of GtkCList's broken
230 * behavior.
232 static gint
233 gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event)
235 GtkSCTree *sctree;
236 GtkCList *clist;
237 gboolean on_row;
238 gint row;
239 gint col;
240 gint retval;
242 g_return_val_if_fail (widget != NULL, FALSE);
243 g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
244 g_return_val_if_fail (event != NULL, FALSE);
246 sctree = GTK_SCTREE (widget);
247 clist = GTK_CLIST (widget);
248 retval = FALSE;
250 if (event->window != clist->clist_window)
251 return (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event);
253 on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
255 if (on_row && !GTK_WIDGET_HAS_FOCUS(widget))
256 gtk_widget_grab_focus (widget);
258 if (gtk_ctree_is_hot_spot (GTK_CTREE(sctree), event->x, event->y)) {
259 gtk_ctree_toggle_expansion
260 (GTK_CTREE(sctree),
261 gtk_ctree_node_nth(GTK_CTREE(sctree), row));
262 return TRUE;
265 switch (event->type) {
266 case GDK_BUTTON_PRESS:
267 if (event->button == 1 || event->button == 2) {
268 if (event->button == 2)
269 event->state &= ~(GDK_SHIFT_MASK | GDK_CONTROL_MASK);
270 if (on_row) {
271 /* Save the mouse info for DnD */
272 sctree->dnd_press_button = event->button;
273 sctree->dnd_press_x = event->x;
274 sctree->dnd_press_y = event->y;
276 /* Handle selection */
277 if ((row_is_selected (sctree, row)
278 && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
279 || ((event->state & GDK_CONTROL_MASK)
280 && !(event->state & GDK_SHIFT_MASK))) {
281 sctree->dnd_select_pending = TRUE;
282 sctree->dnd_select_pending_state = event->state;
283 sctree->dnd_select_pending_row = row;
284 } else
285 select_row (sctree, row, col, event->state);
286 } else
287 gtk_clist_unselect_all (clist);
289 retval = TRUE;
290 } else if (event->button == 3) {
291 /* Emit *_popup_menu signal*/
292 if (on_row) {
293 if (!row_is_selected(sctree,row))
294 select_row (sctree, row, col, 0);
295 gtk_signal_emit (GTK_OBJECT (sctree),
296 sctree_signals[ROW_POPUP_MENU],
297 event);
298 } else {
299 gtk_clist_unselect_all(clist);
300 gtk_signal_emit (GTK_OBJECT (sctree),
301 sctree_signals[EMPTY_POPUP_MENU],
302 event);
304 retval = TRUE;
307 break;
309 case GDK_2BUTTON_PRESS:
310 if (event->button != 1)
311 break;
313 sctree->dnd_select_pending = FALSE;
314 sctree->dnd_select_pending_state = 0;
316 if (on_row)
317 gtk_signal_emit (GTK_OBJECT (sctree),
318 sctree_signals[OPEN_ROW]);
320 retval = TRUE;
321 break;
323 default:
324 break;
327 return retval;
330 /* Our handler for button_release events. We override all of GtkCList's broken
331 * behavior.
333 static gint
334 gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event)
336 GtkSCTree *sctree;
337 GtkCList *clist;
338 gint on_row;
339 gint row, col;
340 gint retval;
342 g_return_val_if_fail (widget != NULL, FALSE);
343 g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
344 g_return_val_if_fail (event != NULL, FALSE);
346 sctree = GTK_SCTREE (widget);
347 clist = GTK_CLIST (widget);
348 retval = FALSE;
350 if (event->window != clist->clist_window)
351 return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
353 on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
355 if (!(event->button == 1 || event->button == 2))
356 return FALSE;
358 sctree->dnd_press_button = 0;
359 sctree->dnd_press_x = 0;
360 sctree->dnd_press_y = 0;
362 if (on_row) {
363 if (sctree->dnd_select_pending) {
364 select_row (sctree, row, col, sctree->dnd_select_pending_state);
365 sctree->dnd_select_pending = FALSE;
366 sctree->dnd_select_pending_state = 0;
369 retval = TRUE;
372 return retval;
375 /* Our handler for motion_notify events. We override all of GtkCList's broken
376 * behavior.
378 static gint
379 gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event)
381 GtkSCTree *sctree;
382 GtkCList *clist;
384 g_return_val_if_fail (widget != NULL, FALSE);
385 g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
386 g_return_val_if_fail (event != NULL, FALSE);
388 sctree = GTK_SCTREE (widget);
389 clist = GTK_CLIST (widget);
391 if (event->window != clist->clist_window)
392 return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
394 if (!((sctree->dnd_press_button == 1 && (event->state & GDK_BUTTON1_MASK))
395 || (sctree->dnd_press_button == 2 && (event->state & GDK_BUTTON2_MASK))))
396 return FALSE;
398 /* This is the same threshold value that is used in gtkdnd.c */
400 if (MAX (abs (sctree->dnd_press_x - event->x),
401 abs (sctree->dnd_press_y - event->y)) <= 3)
402 return FALSE;
404 /* Handle any pending selections */
406 if (sctree->dnd_select_pending) {
407 if (!row_is_selected(sctree,sctree->dnd_select_pending_row))
408 select_row (sctree,
409 sctree->dnd_select_pending_row,
411 sctree->dnd_select_pending_state);
413 sctree->dnd_select_pending = FALSE;
414 sctree->dnd_select_pending_state = 0;
417 gtk_signal_emit (GTK_OBJECT (sctree),
418 sctree_signals[START_DRAG],
419 sctree->dnd_press_button,
420 event);
421 return TRUE;
424 /* We override the drag_begin signal to do nothing */
425 static void
426 gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context)
428 /* nothing */
431 /* We override the drag_end signal to do nothing */
432 static void
433 gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context)
435 /* nothing */
438 /* We override the drag_data_get signal to do nothing */
439 static void
440 gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
441 GtkSelectionData *data, guint info, guint time)
443 /* nothing */
446 /* We override the drag_leave signal to do nothing */
447 static void
448 gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time)
450 /* nothing */
453 /* We override the drag_motion signal to do nothing */
454 static gboolean
455 gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
456 gint x, gint y, guint time)
458 return FALSE;
461 /* We override the drag_drop signal to do nothing */
462 static gboolean
463 gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
464 gint x, gint y, guint time)
466 return FALSE;
469 /* We override the drag_data_received signal to do nothing */
470 static void
471 gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
472 gint x, gint y, GtkSelectionData *data,
473 guint info, guint time)
475 /* nothing */
478 /* Our handler for the clear signal of the clist. We have to reset the anchor
479 * to null.
481 static void
482 gtk_sctree_clear (GtkCList *clist)
484 GtkSCTree *sctree;
486 g_return_if_fail (clist != NULL);
487 g_return_if_fail (GTK_IS_SCTREE (clist));
489 sctree = GTK_SCTREE (clist);
490 sctree->anchor_row = -1;
492 if (((GtkCListClass *)parent_class)->clear)
493 (* ((GtkCListClass *)parent_class)->clear) (clist);
496 /* Our handler for the change_focus_row_expansion signal of the ctree.
497 We have to set the anchor to parent visible node.
499 static void
500 gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node)
502 GtkSCTree *sctree;
504 g_return_if_fail (ctree != NULL);
505 g_return_if_fail (GTK_IS_SCTREE (ctree));
507 (* parent_class->tree_collapse) (ctree, node);
508 sctree = GTK_SCTREE (ctree);
509 sctree->anchor_row = GTK_CLIST(ctree)->focus_row;
512 GtkWidget *gtk_sctree_new_with_titles (gint columns,
513 gint tree_column,
514 gchar *titles[])
516 GtkSCTree* sctree;
518 sctree = gtk_type_new (gtk_sctree_get_type ());
519 gtk_ctree_construct (GTK_CTREE (sctree), columns, tree_column, titles);
520 gtk_clist_set_selection_mode(GTK_CLIST(sctree), GTK_SELECTION_EXTENDED);
522 return GTK_WIDGET (sctree);
525 void gtk_sctree_select (GtkSCTree *sctree,
526 GtkCTreeNode *node)
528 select_row(sctree,
529 gtkut_ctree_get_nth_from_node(GTK_CTREE(sctree),node),
530 -1, 0);
533 void gtk_sctree_unselect_all (GtkSCTree *sctree)
535 gtk_clist_unselect_all(GTK_CLIST(sctree));
536 sctree->anchor_row = -1;