Statistics
| Revision:

root / src / gtksctree.c @ 1

History | View | Annotate | Download (15.2 KB)

1
/*
2
 * This program is based on gtkflist.c
3
 */
4

    
5
#include "gtksctree.h"
6
#include "sylpheed-marshal.h"
7

    
8
enum {
9
        ROW_POPUP_MENU,
10
        EMPTY_POPUP_MENU,
11
        OPEN_ROW,
12
        START_DRAG,
13
        LAST_SIGNAL
14
};
15

    
16

    
17
static void gtk_sctree_class_init (GtkSCTreeClass *class);
18
static void gtk_sctree_init (GtkSCTree *sctree);
19

    
20
static gint gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event);
21
static gint gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event);
22
static gint gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event);
23
static void gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context);
24
static void gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context);
25
static void gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
26
                                     GtkSelectionData *data, guint info, guint time);
27
static void gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time);
28
static gboolean gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
29
                                       gint x, gint y, guint time);
30
static gboolean gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
31
                                     gint x, gint y, guint time);
32
static void gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
33
                                          gint x, gint y, GtkSelectionData *data,
34
                                          guint info, guint time);
35

    
36
static void gtk_sctree_clear (GtkCList *clist);
37
static void gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node);
38
       
39
static GtkCTreeClass *parent_class;
40

    
41
static guint sctree_signals[LAST_SIGNAL];
42

    
43

    
44
/**
45
 * gtk_sctree_get_type:
46
 * @void: 
47
 * 
48
 * Creates the GtkSCTree class and its type information
49
 * 
50
 * Return value: The type ID for GtkSCTreeClass
51
 **/
52
GType
53
gtk_sctree_get_type (void)
54
{
55
        static GType sctree_type = 0;
56

    
57
        if (!sctree_type) {
58
                GTypeInfo sctree_info = {
59
                        sizeof (GtkSCTreeClass),
60

    
61
                        (GBaseInitFunc) NULL,
62
                        (GBaseFinalizeFunc) NULL,
63

    
64
                        (GClassInitFunc) gtk_sctree_class_init,
65
                        (GClassFinalizeFunc) NULL,
66
                        NULL,        /* class_data */
67

    
68
                        sizeof (GtkSCTree),
69
                        0,        /* n_preallocs */
70
                        (GInstanceInitFunc) gtk_sctree_init,
71
                };
72

    
73
                sctree_type = g_type_register_static (GTK_TYPE_CTREE, "GtkSCTree", &sctree_info, (GTypeFlags)0);
74
        }
75

    
76
        return sctree_type;
77
}
78

    
79
/* Standard class initialization function */
80
static void
81
gtk_sctree_class_init (GtkSCTreeClass *klass)
82
{
83
        GtkObjectClass *object_class;
84
        GtkWidgetClass *widget_class;
85
        GtkCListClass *clist_class;
86
        GtkCTreeClass *ctree_class;
87

    
88
        object_class = (GtkObjectClass *) klass;
89
        widget_class = (GtkWidgetClass *) klass;
90
        clist_class = (GtkCListClass *) klass;
91
        ctree_class = (GtkCTreeClass *) klass;
92

    
93
        parent_class = gtk_type_class (gtk_ctree_get_type ());
94

    
95
        sctree_signals[ROW_POPUP_MENU] =
96
                g_signal_new ("row_popup_menu",
97
                              G_TYPE_FROM_CLASS (klass),
98
                              G_SIGNAL_RUN_FIRST,
99
                              G_STRUCT_OFFSET (GtkSCTreeClass, row_popup_menu),
100
                              NULL, NULL,
101
                              sylpheed_marshal_VOID__POINTER,
102
                              G_TYPE_NONE, 1,
103
                              GDK_TYPE_EVENT);
104
        sctree_signals[EMPTY_POPUP_MENU] =
105
                g_signal_new ("empty_popup_menu",
106
                              G_TYPE_FROM_CLASS (klass),
107
                              G_SIGNAL_RUN_FIRST,
108
                              G_STRUCT_OFFSET (GtkSCTreeClass, empty_popup_menu),
109
                              NULL, NULL,
110
                              sylpheed_marshal_VOID__POINTER,
111
                              G_TYPE_NONE, 1,
112
                              GDK_TYPE_EVENT);
113
        sctree_signals[OPEN_ROW] =
114
                g_signal_new ("open_row",
115
                              G_TYPE_FROM_CLASS (klass),
116
                              G_SIGNAL_RUN_FIRST,
117
                              G_STRUCT_OFFSET (GtkSCTreeClass, open_row),
118
                              NULL, NULL,
119
                              g_cclosure_marshal_VOID__VOID,
120
                              G_TYPE_NONE, 0);
121
        sctree_signals[START_DRAG] =
122
                g_signal_new ("start_drag",
123
                              G_TYPE_FROM_CLASS (klass),
124
                              G_SIGNAL_RUN_FIRST,
125
                              G_STRUCT_OFFSET (GtkSCTreeClass, start_drag),
126
                              NULL, NULL,
127
                              sylpheed_marshal_VOID__INT_POINTER,
128
                              G_TYPE_NONE, 2,
129
                              G_TYPE_INT,
130
                              GDK_TYPE_EVENT);
131

    
132
        /* gtk_object_class_add_signals (object_class, sctree_signals, LAST_SIGNAL); */
133

    
134
        clist_class->clear = gtk_sctree_clear;
135
        ctree_class->tree_collapse = gtk_sctree_collapse;
136
        
137
        widget_class->button_press_event = gtk_sctree_button_press;
138
        widget_class->button_release_event = gtk_sctree_button_release;
139
        widget_class->motion_notify_event = gtk_sctree_motion;
140
        widget_class->drag_begin = gtk_sctree_drag_begin;
141
        widget_class->drag_end = gtk_sctree_drag_end;
142
        widget_class->drag_data_get = gtk_sctree_drag_data_get;
143
        widget_class->drag_leave = gtk_sctree_drag_leave;
144
        widget_class->drag_motion = gtk_sctree_drag_motion;
145
        widget_class->drag_drop = gtk_sctree_drag_drop;
146
        widget_class->drag_data_received = gtk_sctree_drag_data_received;
147
}
148

    
149
/* Standard object initialization function */
150
static void
151
gtk_sctree_init (GtkSCTree *sctree)
152
{
153
        sctree->anchor_row = NULL;
154

    
155
        /* GtkCTree does not specify pointer motion by default */
156
        gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
157
        gtk_widget_add_events (GTK_WIDGET (sctree), GDK_POINTER_MOTION_MASK);
158
}
159

    
160
/* Get information the specified row is selected. */
161

    
162
static gboolean
163
row_is_selected(GtkSCTree *sctree, gint row)
164
{
165
        GtkCListRow *clist_row;
166
        clist_row =  g_list_nth (GTK_CLIST(sctree)->row_list, row)->data;
167
        return clist_row ? clist_row->state == GTK_STATE_SELECTED : FALSE;
168
}
169

    
170
/* Selects the rows between the anchor to the specified row, inclusive.  */
171
static void
172
select_range (GtkSCTree *sctree, gint row)
173
{
174
        gint prev_row;
175
        gint min, max;
176
        gint i;
177

    
178
        if (sctree->anchor_row == NULL) {
179
                prev_row = row;
180
                sctree->anchor_row = gtk_ctree_node_nth(GTK_CTREE(sctree), row);
181
        } else
182
                prev_row = g_list_position(GTK_CLIST(sctree)->row_list,
183
                                           (GList *)sctree->anchor_row);
184

    
185
        if (row < prev_row) {
186
                min = row;
187
                max = prev_row;
188
        } else {
189
                min = prev_row;
190
                max = row;
191
        }
192
        for (i = min; i <= max; i++)
193
                gtk_clist_select_row (GTK_CLIST (sctree), i, -1);
194
}
195

    
196
/* Handles row selection according to the specified modifier state */
197
static void
198
select_row (GtkSCTree *sctree, gint row, gint col, guint state)
199
{
200
        gboolean range, additive;
201
        g_return_if_fail (sctree != NULL);
202
        g_return_if_fail (GTK_IS_SCTREE (sctree));
203
    
204
        range = ((state & GDK_SHIFT_MASK) != 0) &&
205
                (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
206
                (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
207
        additive = ((state & GDK_CONTROL_MASK) != 0) &&
208
                   (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_SINGLE) &&
209
                   (GTK_CLIST(sctree)->selection_mode != GTK_SELECTION_BROWSE);
210

    
211
        gtk_clist_freeze (GTK_CLIST (sctree));
212
        GTK_CLIST(sctree)->focus_row = row;
213
        GTK_CLIST_GET_CLASS(sctree)->refresh(GTK_CLIST(sctree));
214
        if (!additive)
215
                gtk_clist_unselect_all (GTK_CLIST (sctree));
216

    
217
        if (!range) {
218
                GtkCTreeNode *node;
219

    
220
                node = gtk_ctree_node_nth (GTK_CTREE(sctree), row);
221

    
222
                /*No need to manage overlapped list*/
223
                if (additive) {
224
                        if (row_is_selected(sctree, row))
225
                                gtk_clist_unselect_row (GTK_CLIST (sctree), row, col);
226
                        else
227
                                g_signal_emit_by_name
228
                                        (G_OBJECT (sctree),
229
                                         "tree_select_row", node, col);
230
                } else {
231
                        g_signal_emit_by_name
232
                                (G_OBJECT (sctree),
233
                                 "tree_select_row", node, col);
234
                }
235
                sctree->anchor_row = node;
236
        } else
237
                select_range (sctree, row);
238
        gtk_clist_thaw (GTK_CLIST (sctree));
239
}
240

    
241
/* Our handler for button_press events.  We override all of GtkCList's broken
242
 * behavior.
243
 */
244
static gint
245
gtk_sctree_button_press (GtkWidget *widget, GdkEventButton *event)
246
{
247
        GtkSCTree *sctree;
248
        GtkCList *clist;
249
        gboolean on_row;
250
        gint row;
251
        gint col;
252
        gint retval;
253

    
254
        g_return_val_if_fail (widget != NULL, FALSE);
255
        g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
256
        g_return_val_if_fail (event != NULL, FALSE);
257

    
258
        sctree = GTK_SCTREE (widget);
259
        clist = GTK_CLIST (widget);
260
        retval = FALSE;
261

    
262
        if (event->window != clist->clist_window)
263
                return (* GTK_WIDGET_CLASS (parent_class)->button_press_event) (widget, event);
264

    
265
        on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
266

    
267
        if (on_row && !GTK_WIDGET_HAS_FOCUS(widget))
268
                gtk_widget_grab_focus (widget);
269

    
270
        if (gtk_ctree_is_hot_spot (GTK_CTREE(sctree), event->x, event->y)) {
271
                gtk_ctree_toggle_expansion
272
                        (GTK_CTREE(sctree), 
273
                         gtk_ctree_node_nth(GTK_CTREE(sctree), row));
274
                return TRUE;
275
        }
276

    
277
        switch (event->type) {
278
        case GDK_BUTTON_PRESS:
279
                if (event->button == 1 || event->button == 2) {
280
                        if (event->button == 2)
281
                                event->state &= ~(GDK_SHIFT_MASK | GDK_CONTROL_MASK);
282
                        if (on_row) {
283
                                /* Save the mouse info for DnD */
284
                                sctree->dnd_press_button = event->button;
285
                                sctree->dnd_press_x = event->x;
286
                                sctree->dnd_press_y = event->y;
287

    
288
                                /* Handle selection */
289
                                if ((row_is_selected (sctree, row)
290
                                     && !(event->state & (GDK_CONTROL_MASK | GDK_SHIFT_MASK)))
291
                                    || ((event->state & GDK_CONTROL_MASK)
292
                                        && !(event->state & GDK_SHIFT_MASK))) {
293
                                        sctree->dnd_select_pending = TRUE;
294
                                        sctree->dnd_select_pending_state = event->state;
295
                                        sctree->dnd_select_pending_row = row;
296
                                } else
297
                                        select_row (sctree, row, col, event->state);
298
                        } else
299
                                gtk_clist_unselect_all (clist);
300

    
301
                        retval = TRUE;
302
                } else if (event->button == 3) {
303
                        /* Emit *_popup_menu signal*/
304
                        if (on_row) {
305
                                if (!row_is_selected(sctree,row))
306
                                        select_row (sctree, row, col, 0);
307
                                g_signal_emit (G_OBJECT (sctree),
308
                                               sctree_signals[ROW_POPUP_MENU],
309
                                               0, event);
310
                        } else {
311
                                gtk_clist_unselect_all(clist);
312
                                g_signal_emit (G_OBJECT (sctree),
313
                                               sctree_signals[EMPTY_POPUP_MENU],
314
                                               0, event);
315
                        }
316
                        retval = TRUE;
317
                }
318

    
319
                break;
320

    
321
        case GDK_2BUTTON_PRESS:
322
                if (event->button != 1)
323
                        break;
324

    
325
                sctree->dnd_select_pending = FALSE;
326
                sctree->dnd_select_pending_state = 0;
327

    
328
                if (on_row)
329
                        g_signal_emit (G_OBJECT (sctree),
330
                                       sctree_signals[OPEN_ROW], 0);
331

    
332
                retval = TRUE;
333
                break;
334

    
335
        default:
336
                break;
337
        }
338

    
339
        return retval;
340
}
341

    
342
/* Our handler for button_release events.  We override all of GtkCList's broken
343
 * behavior.
344
 */
345
static gint
346
gtk_sctree_button_release (GtkWidget *widget, GdkEventButton *event)
347
{
348
        GtkSCTree *sctree;
349
        GtkCList *clist;
350
        gint on_row;
351
        gint row, col;
352
        gint retval;
353

    
354
        g_return_val_if_fail (widget != NULL, FALSE);
355
        g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
356
        g_return_val_if_fail (event != NULL, FALSE);
357

    
358
        sctree = GTK_SCTREE (widget);
359
        clist = GTK_CLIST (widget);
360
        retval = FALSE;
361

    
362
        if (event->window != clist->clist_window)
363
                return (* GTK_WIDGET_CLASS (parent_class)->button_release_event) (widget, event);
364

    
365
        on_row = gtk_clist_get_selection_info (clist, event->x, event->y, &row, &col);
366

    
367
        if (!(event->button == 1 || event->button == 2))
368
                return FALSE;
369

    
370
        sctree->dnd_press_button = 0;
371
        sctree->dnd_press_x = 0;
372
        sctree->dnd_press_y = 0;
373

    
374
        if (on_row) {
375
                if (sctree->dnd_select_pending) {
376
                        select_row (sctree, row, col, sctree->dnd_select_pending_state);
377
                        sctree->dnd_select_pending = FALSE;
378
                        sctree->dnd_select_pending_state = 0;
379
                }
380

    
381
                retval = TRUE;
382
        }
383

    
384
        return retval;
385
}
386

    
387
/* Our handler for motion_notify events.  We override all of GtkCList's broken
388
 * behavior.
389
 */
390
static gint
391
gtk_sctree_motion (GtkWidget *widget, GdkEventMotion *event)
392
{
393
        GtkSCTree *sctree;
394
        GtkCList *clist;
395

    
396
        g_return_val_if_fail (widget != NULL, FALSE);
397
        g_return_val_if_fail (GTK_IS_SCTREE (widget), FALSE);
398
        g_return_val_if_fail (event != NULL, FALSE);
399

    
400
        sctree = GTK_SCTREE (widget);
401
        clist = GTK_CLIST (widget);
402

    
403
        if (event->window != clist->clist_window)
404
                return (* GTK_WIDGET_CLASS (parent_class)->motion_notify_event) (widget, event);
405

    
406
        if (!((sctree->dnd_press_button == 1 && (event->state & GDK_BUTTON1_MASK))
407
              || (sctree->dnd_press_button == 2 && (event->state & GDK_BUTTON2_MASK))))
408
                return FALSE;
409

    
410
        /* This is the same threshold value that is used in gtkdnd.c */
411

    
412
        if (MAX (ABS (sctree->dnd_press_x - event->x),
413
                 ABS (sctree->dnd_press_y - event->y)) <= 3)
414
                return FALSE;
415

    
416
        /* Handle any pending selections */
417

    
418
        if (sctree->dnd_select_pending) {
419
                if (!row_is_selected(sctree,sctree->dnd_select_pending_row))
420
                        select_row (sctree,
421
                                    sctree->dnd_select_pending_row,
422
                                    -1,
423
                                    sctree->dnd_select_pending_state);
424

    
425
                sctree->dnd_select_pending = FALSE;
426
                sctree->dnd_select_pending_state = 0;
427
        }
428

    
429
        g_signal_emit (G_OBJECT (sctree),
430
                       sctree_signals[START_DRAG],
431
                       0,
432
                       sctree->dnd_press_button,
433
                       event);
434
        return TRUE;
435
}
436

    
437
/* We override the drag_begin signal to do nothing */
438
static void
439
gtk_sctree_drag_begin (GtkWidget *widget, GdkDragContext *context)
440
{
441
        /* nothing */
442
}
443

    
444
/* We override the drag_end signal to do nothing */
445
static void
446
gtk_sctree_drag_end (GtkWidget *widget, GdkDragContext *context)
447
{
448
        /* nothing */
449
}
450

    
451
/* We override the drag_data_get signal to do nothing */
452
static void
453
gtk_sctree_drag_data_get (GtkWidget *widget, GdkDragContext *context,
454
                                     GtkSelectionData *data, guint info, guint time)
455
{
456
        /* nothing */
457
}
458

    
459
/* We override the drag_leave signal to do nothing */
460
static void
461
gtk_sctree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time)
462
{
463
        /* nothing */
464
}
465

    
466
/* We override the drag_motion signal to do nothing */
467
static gboolean
468
gtk_sctree_drag_motion (GtkWidget *widget, GdkDragContext *context,
469
                                   gint x, gint y, guint time)
470
{
471
        return FALSE;
472
}
473

    
474
/* We override the drag_drop signal to do nothing */
475
static gboolean
476
gtk_sctree_drag_drop (GtkWidget *widget, GdkDragContext *context,
477
                                 gint x, gint y, guint time)
478
{
479
        return FALSE;
480
}
481

    
482
/* We override the drag_data_received signal to do nothing */
483
static void
484
gtk_sctree_drag_data_received (GtkWidget *widget, GdkDragContext *context,
485
                                          gint x, gint y, GtkSelectionData *data,
486
                                          guint info, guint time)
487
{
488
        /* nothing */
489
}
490

    
491
/* Our handler for the clear signal of the clist.  We have to reset the anchor
492
 * to null.
493
 */
494
static void
495
gtk_sctree_clear (GtkCList *clist)
496
{
497
        GtkSCTree *sctree;
498

    
499
        g_return_if_fail (clist != NULL);
500
        g_return_if_fail (GTK_IS_SCTREE (clist));
501

    
502
        sctree = GTK_SCTREE (clist);
503
        sctree->anchor_row = NULL;
504

    
505
        if (((GtkCListClass *)parent_class)->clear)
506
                (* ((GtkCListClass *)parent_class)->clear) (clist);
507
}
508

    
509
/* Our handler for the change_focus_row_expansion signal of the ctree.  
510
 We have to set the anchor to parent visible node.
511
 */
512
static void 
513
gtk_sctree_collapse (GtkCTree *ctree, GtkCTreeNode *node)
514
{
515
        g_return_if_fail (ctree != NULL);
516
        g_return_if_fail (GTK_IS_SCTREE (ctree));
517

    
518
        (* parent_class->tree_collapse) (ctree, node);
519
        GTK_SCTREE(ctree)->anchor_row =
520
                gtk_ctree_node_nth(ctree, GTK_CLIST(ctree)->focus_row);
521
}
522

    
523
GtkWidget *gtk_sctree_new_with_titles (gint columns, gint tree_column, 
524
                                       gchar *titles[])
525
{
526
#if 0
527
        GtkSCTree* sctree;
528

529
        sctree = gtk_type_new (gtk_sctree_get_type ());
530
        gtk_ctree_construct (GTK_CTREE (sctree), columns, tree_column, titles);
531
        gtk_clist_set_selection_mode(GTK_CLIST(sctree), GTK_SELECTION_EXTENDED);
532

533
        return GTK_WIDGET (sctree);
534
#else
535
        GtkWidget *widget;
536

    
537
        g_return_val_if_fail (columns > 0, NULL);
538
        g_return_val_if_fail (tree_column >= 0 && tree_column < columns, NULL);
539

    
540
        widget = gtk_widget_new (TYPE_GTK_SCTREE,
541
                                 "n_columns", columns,
542
                                 "tree_column", tree_column,
543
                                 NULL);
544
        if (titles) {
545
                GtkCList *clist = GTK_CLIST (widget);
546
                guint i;
547

    
548
                for (i = 0; i < columns; i++)
549
                        gtk_clist_set_column_title (clist, i, titles[i]);
550
                gtk_clist_column_titles_show (clist);
551
        }
552

    
553
        return widget;
554
#endif
555
}
556

    
557
void gtk_sctree_select (GtkSCTree *sctree, GtkCTreeNode *node)
558
{
559
        select_row(sctree, 
560
                   g_list_position(GTK_CLIST(sctree)->row_list, (GList *)node),
561
                   -1, 0);
562
}
563

    
564
void gtk_sctree_unselect_all (GtkSCTree *sctree)
565
{
566
        gtk_clist_unselect_all(GTK_CLIST(sctree));
567
        sctree->anchor_row = NULL;
568
}
569

    
570
void gtk_sctree_set_anchor_row (GtkSCTree *sctree, GtkCTreeNode *node)
571
{
572
        sctree->anchor_row = node;
573
}