~ubuntu-branches/ubuntu/vivid/liferea/vivid-proposed

« back to all changes in this revision

Viewing changes to src/ui/ui_itemlist.c

  • Committer: Package Import Robot
  • Author(s): bojo42
  • Date: 2012-03-29 14:17:21 UTC
  • mfrom: (1.3.9) (3.2.5 sid)
  • Revision ID: package-import@ubuntu.com-20120329141721-tbfopcrc5797wxt7
Tags: 1.8.3-0.1ubuntu1
* New upstream release (LP: #290666, #371754, #741543, #716688)
* Merge from Debian unstable (LP: #935147), remaining changes:
* debian/patches:
  - drop gtk-status-icon.patch & notification-append as in upstream
  - drop fix_systray_behavior as mostly upstreamed and rest seems unused
  - 01_ubuntu_feedlists: update & rename, move planets to "Open Source"  
  - add_X-Ubuntu-Gettext-Domain: rebase
  - libunity.patch: rebase, apply before indicator patch (liferea_shell.c)
  - libindicate_increase_version.patch: exclude from libindicate.patch
  - deactivate libindicate.patch, seems partly upstreamed and needs rework
* debian/control: libindicate-dev, libindicate-gtk-dev & libunity-dev
* debian/liferea.indicate & liferea.install: ship indicator desktop file
* debian/rules: enable libindicate

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/**
2
 
 * @file ui_itemlist.c item list GUI handling
3
 
 *  
4
 
 * Copyright (C) 2004-2009 Lars Lindner <lars.lindner@gmail.com>
5
 
 * Copyright (C) 2004-2006 Nathan J. Conrad <t98502@users.sourceforge.net>
6
 
 *
7
 
 * This program is free software; you can redistribute it and/or modify
8
 
 * it under the terms of the GNU General Public License as published by
9
 
 * the Free Software Foundation; either version 2 of the License, or
10
 
 * (at your option) any later version. 
11
 
 *
12
 
 * This program is distributed in the hope that it will be useful,
13
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 
 * GNU General Public License for more details.
16
 
 *
17
 
 * You should have received a copy of the GNU General Public License
18
 
 * along with this program; if not, write to the Free Software
19
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
 
 */
21
 
 
22
 
#include "ui/ui_itemlist.h"
23
 
 
24
 
#include <string.h>
25
 
#include <gdk/gdkkeysyms.h>
26
 
 
27
 
#include "browser.h"
28
 
#include "common.h"
29
 
#include "debug.h"
30
 
#include "feed.h"
31
 
#include "feedlist.h"
32
 
#include "item.h"
33
 
#include "itemlist.h"
34
 
#include "itemview.h"
35
 
#include "newsbin.h"
36
 
#include "social.h"
37
 
#include "ui/browser_tabs.h"
38
 
#include "ui/liferea_shell.h"
39
 
#include "ui/ui_common.h"
40
 
#include "ui/ui_popup.h"
41
 
 
42
 
/** Enumeration of the columns in the itemstore. */
43
 
enum is_columns {
44
 
        IS_TIME,                /**< Time of item creation */
45
 
        IS_TIME_STR,            /**< Time of item creation as a string*/
46
 
        IS_LABEL,               /**< Displayed name */
47
 
        IS_STATEICON,           /**< Pixbuf reference to the item's state icon */
48
 
        IS_NR,                  /**< Item id, to lookup item ptr from parent feed */
49
 
        IS_PARENT,              /**< Parent node pointer */
50
 
        IS_FAVICON,             /**< Pixbuf reference to the item's feed's icon */
51
 
        IS_ENCICON,             /**< Pixbuf reference to the item's enclosure icon */
52
 
        IS_ENCLOSURE,           /**< Flag wether enclosure is attached or not */
53
 
        IS_SOURCE,              /**< Source node pointer */
54
 
        IS_STATE,               /**< Original item state (unread, flagged...) for sorting */
55
 
        ITEMSTORE_UNREAD,       /**< Flag wether "unread" icon is to be shown */
56
 
        ITEMSTORE_LEN           /**< Number of columns in the itemstore */
57
 
};
58
 
 
59
 
static GHashTable       *item_id_to_iter = NULL;        /** hash table used for fast item id->tree iter lookup */
60
 
 
61
 
static GtkTreeView      *itemlist_treeview = NULL;
62
 
 
63
 
/* helper functions for item <-> iter conversion */
64
 
 
65
 
gboolean
66
 
ui_itemlist_contains_item (gulong id)
67
 
{
68
 
        return (NULL != g_hash_table_lookup (item_id_to_iter, GUINT_TO_POINTER (id)));
69
 
}
70
 
 
71
 
static gulong
72
 
ui_iter_to_item_id (GtkTreeIter *iter)
73
 
{
74
 
        gulong  id = 0;
75
 
        
76
 
        gtk_tree_model_get (gtk_tree_view_get_model (itemlist_treeview), iter, IS_NR, &id, -1);
77
 
        return id;
78
 
}
79
 
 
80
 
static gboolean
81
 
ui_item_id_to_iter (gulong id, GtkTreeIter *iter)
82
 
{
83
 
        GtkTreeIter *old_iter;
84
 
 
85
 
        old_iter = g_hash_table_lookup (item_id_to_iter, GUINT_TO_POINTER (id));
86
 
        if (!old_iter)
87
 
                return FALSE;
88
 
        
89
 
        *iter = *old_iter;
90
 
        return TRUE;
91
 
}
92
 
 
93
 
static gint
94
 
ui_itemlist_date_sort_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
95
 
{
96
 
        guint64 timea, timeb;
97
 
        double  diff;
98
 
 
99
 
        gtk_tree_model_get (model, a, IS_TIME, &timea, -1);
100
 
        gtk_tree_model_get (model, b, IS_TIME, &timeb, -1);
101
 
        diff = difftime ((time_t)timeb, (time_t)timea);
102
 
        
103
 
        if (diff < 0)
104
 
                return 1;
105
 
                
106
 
        if (diff > 0)
107
 
                return -1;
108
 
        
109
 
        return 0;
110
 
}
111
 
 
112
 
static gint
113
 
ui_itemlist_favicon_sort_func (GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b, gpointer user_data)
114
 
{
115
 
        nodePtr node1, node2;
116
 
        
117
 
        gtk_tree_model_get (model, a, IS_SOURCE, &node1, -1);
118
 
        gtk_tree_model_get (model, b, IS_SOURCE, &node2, -1);
119
 
        
120
 
        if (!node1->id || !node2->id)
121
 
                return 0;
122
 
                
123
 
        return strcmp (node1->id, node2->id);
124
 
}
125
 
 
126
 
void
127
 
ui_itemlist_set_sort_column (nodeViewSortType sortType, gboolean sortReversed)
128
 
{
129
 
        gint sortColumn;
130
 
        
131
 
        switch (sortType) {
132
 
                case NODE_VIEW_SORT_BY_TITLE:
133
 
                        sortColumn = IS_LABEL;
134
 
                        break;
135
 
                case NODE_VIEW_SORT_BY_PARENT:
136
 
                        sortColumn = IS_PARENT;
137
 
                        break;
138
 
                default:
139
 
                        sortColumn = IS_TIME;
140
 
                        break;
141
 
        }
142
 
        
143
 
        gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (gtk_tree_view_get_model (itemlist_treeview)),
144
 
                                              sortColumn, 
145
 
                                              sortReversed?GTK_SORT_DESCENDING:GTK_SORT_ASCENDING);
146
 
}
147
 
 
148
 
void
149
 
ui_itemlist_reset_tree_store (void)
150
 
{
151
 
        GtkTreeModel    *model;
152
 
        GtkTreeStore    *itemstore;
153
 
 
154
 
        /* clear items */
155
 
        ui_itemlist_clear ();
156
 
 
157
 
        /* drop old tree store */
158
 
        model = gtk_tree_view_get_model (itemlist_treeview);
159
 
        gtk_tree_view_set_model (itemlist_treeview, NULL);
160
 
        if (model)
161
 
                g_object_unref (model);
162
 
        
163
 
        /* create and assign new one */
164
 
        itemstore = gtk_tree_store_new (ITEMSTORE_LEN,
165
 
                                        G_TYPE_UINT64,  /* IS_TIME */
166
 
                                        G_TYPE_STRING,  /* IS_TIME_STR */
167
 
                                        G_TYPE_STRING,  /* IS_LABEL */
168
 
                                        GDK_TYPE_PIXBUF,        /* IS_STATEICON */
169
 
                                        G_TYPE_ULONG,   /* IS_NR */
170
 
                                        G_TYPE_POINTER, /* IS_PARENT */
171
 
                                        GDK_TYPE_PIXBUF,        /* IS_FAVICON */
172
 
                                        GDK_TYPE_PIXBUF,        /* IS_ENCICON */
173
 
                                        G_TYPE_BOOLEAN, /* IS_ENCLOSURE */
174
 
                                        G_TYPE_POINTER, /* IS_SOURCE */
175
 
                                        G_TYPE_UINT,    /* IS_STATE */
176
 
                                        G_TYPE_INT      /* ITEMSTORE_UNREAD */
177
 
                                        );
178
 
        gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (itemstore), IS_TIME, ui_itemlist_date_sort_func, NULL, NULL);
179
 
        gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (itemstore), IS_SOURCE, ui_itemlist_favicon_sort_func, NULL, NULL);
180
 
        g_signal_connect (G_OBJECT(itemstore), "sort-column-changed", G_CALLBACK (itemlist_sort_column_changed_cb), NULL);
181
 
        
182
 
        gtk_tree_view_set_model (itemlist_treeview, GTK_TREE_MODEL (itemstore));
183
 
 
184
 
        ui_itemlist_prefocus ();
185
 
}
186
 
 
187
 
void
188
 
ui_itemlist_remove_item (itemPtr item)
189
 
{
190
 
        GtkTreeIter     *iter;
191
 
 
192
 
        g_assert (NULL != item);
193
 
        iter = g_hash_table_lookup (item_id_to_iter, GUINT_TO_POINTER (item->id));
194
 
        if (iter) {
195
 
                gtk_tree_store_remove (GTK_TREE_STORE (gtk_tree_view_get_model (itemlist_treeview)), iter);
196
 
                g_hash_table_remove (item_id_to_iter, GUINT_TO_POINTER(item->id));
197
 
        } else {
198
 
                g_warning("Fatal: item to be removed not found in iter lookup hash!");
199
 
        }
200
 
}
201
 
 
202
 
/* cleans up the item list, sets up the iter hash when called for the first time */
203
 
void
204
 
ui_itemlist_clear (void)
205
 
{
206
 
        GtkAdjustment           *adj;
207
 
        GtkTreeStore            *itemstore;
208
 
 
209
 
        itemstore = GTK_TREE_STORE (gtk_tree_view_get_model (itemlist_treeview));
210
 
        
211
 
        /* unselecting all items is important to remove items
212
 
           whose removal is deferred until unselecting */
213
 
        gtk_tree_selection_unselect_all (gtk_tree_view_get_selection (itemlist_treeview));
214
 
        
215
 
        adj = gtk_tree_view_get_vadjustment (itemlist_treeview);
216
 
        gtk_adjustment_set_value (adj, 0.0);
217
 
        gtk_tree_view_set_vadjustment (itemlist_treeview, adj);
218
 
 
219
 
        if (itemstore)
220
 
                gtk_tree_store_clear (itemstore);
221
 
        if (item_id_to_iter)
222
 
                g_hash_table_destroy (item_id_to_iter);
223
 
        
224
 
        item_id_to_iter = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
225
 
}
226
 
 
227
 
void
228
 
ui_itemlist_update_item (itemPtr item)
229
 
{
230
 
        GtkTreeIter     iter;
231
 
        gchar           *title, *time_str;
232
 
        const gchar     *direction_marker;
233
 
        guint           state_icon;
234
 
        
235
 
        if (!ui_item_id_to_iter (item->id, &iter))
236
 
                return;
237
 
        
238
 
        time_str = (0 != item->time) ? itemview_format_date((time_t)item->time) : g_strdup ("");
239
 
 
240
 
        direction_marker = common_get_direction_mark (node_from_id (item->nodeId)->title);      
241
 
 
242
 
        title = item->title && strlen (item->title) ? item->title : _("*** No title ***");
243
 
        title = g_strstrip (g_strdup_printf ("%s%s", direction_marker, title));
244
 
 
245
 
        state_icon = item->flagStatus ? ICON_FLAG :
246
 
                     !item->readStatus ? ICON_UNREAD :
247
 
                     ICON_READ;
248
 
 
249
 
        gtk_tree_store_set (GTK_TREE_STORE (gtk_tree_view_get_model (itemlist_treeview)),
250
 
                            &iter,
251
 
                            IS_LABEL, title,
252
 
                            IS_TIME_STR, time_str,
253
 
                            IS_STATEICON, icons[state_icon],
254
 
                            ITEMSTORE_UNREAD, item->readStatus ? PANGO_WEIGHT_NORMAL : PANGO_WEIGHT_BOLD,
255
 
                            -1);
256
 
 
257
 
        g_free (time_str);
258
 
        g_free (title);
259
 
}
260
 
 
261
 
static void
262
 
ui_itemlist_update_item_foreach (gpointer key,
263
 
                                 gpointer value,
264
 
                                 gpointer user_data)
265
 
{
266
 
        itemPtr         item;
267
 
        
268
 
        item = item_load (GPOINTER_TO_UINT (key) /* id */);
269
 
        if (!item)
270
 
                return;
271
 
 
272
 
        ui_itemlist_update_item (item);
273
 
        
274
 
        item_unload (item);
275
 
}
276
 
 
277
 
void 
278
 
ui_itemlist_update_all_items (void) 
279
 
{
280
 
        g_hash_table_foreach (item_id_to_iter, ui_itemlist_update_item_foreach, NULL);
281
 
}
282
 
 
283
 
static gboolean
284
 
ui_itemlist_key_press_cb (GtkWidget *widget,
285
 
                          GdkEventKey *event,
286
 
                          gpointer data) 
287
 
{
288
 
        if ((event->type == GDK_KEY_PRESS) && (event->state == 0) && (event->keyval == GDK_Delete)) 
289
 
                on_remove_item_activate(NULL, NULL);
290
 
 
291
 
        return FALSE;
292
 
}
293
 
 
294
 
GtkWidget * 
295
 
ui_itemlist_new (GtkWidget *mainwindow) 
296
 
{
297
 
        GtkCellRenderer         *renderer;
298
 
        GtkTreeViewColumn       *column;
299
 
        GtkTreeSelection        *select;
300
 
        GtkWidget               *ilscrolledwindow;
301
 
        
302
 
        ilscrolledwindow = gtk_scrolled_window_new (NULL, NULL);
303
 
        gtk_widget_show (ilscrolledwindow);
304
 
        gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (ilscrolledwindow), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
305
 
        gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (ilscrolledwindow), GTK_SHADOW_IN);
306
 
 
307
 
        itemlist_treeview = GTK_TREE_VIEW (gtk_tree_view_new ());
308
 
        gtk_container_add (GTK_CONTAINER (ilscrolledwindow), GTK_WIDGET (itemlist_treeview));
309
 
        gtk_widget_show (GTK_WIDGET (itemlist_treeview));
310
 
        gtk_widget_set_name (GTK_WIDGET (itemlist_treeview), "itemlist");
311
 
        gtk_tree_view_set_rules_hint (itemlist_treeview, TRUE);
312
 
        
313
 
        g_object_set_data (G_OBJECT (mainwindow), "itemlist", itemlist_treeview);
314
 
 
315
 
        item_id_to_iter = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free);
316
 
 
317
 
        ui_itemlist_reset_tree_store ();
318
 
 
319
 
        renderer = gtk_cell_renderer_pixbuf_new ();
320
 
        column = gtk_tree_view_column_new_with_attributes ("", renderer, "pixbuf", IS_STATEICON, NULL);
321
 
        gtk_tree_view_append_column (itemlist_treeview, column);
322
 
        gtk_tree_view_column_set_sort_column_id (column, IS_STATE);     
323
 
        
324
 
        renderer = gtk_cell_renderer_pixbuf_new ();
325
 
        column = gtk_tree_view_column_new_with_attributes ("", renderer, "pixbuf", IS_ENCICON, NULL);
326
 
        gtk_tree_view_append_column (itemlist_treeview, column);
327
 
 
328
 
        renderer = gtk_cell_renderer_text_new ();
329
 
        column = gtk_tree_view_column_new_with_attributes (_("Date"), renderer, 
330
 
                                                           "text", IS_TIME_STR,
331
 
                                                           "weight", ITEMSTORE_UNREAD,
332
 
                                                           NULL);
333
 
        gtk_tree_view_append_column (itemlist_treeview, column);
334
 
        gtk_tree_view_column_set_sort_column_id(column, IS_TIME);
335
 
        g_object_set (column, "resizable", TRUE, NULL);
336
 
        
337
 
        renderer = gtk_cell_renderer_pixbuf_new ();
338
 
        column = gtk_tree_view_column_new_with_attributes ("", renderer, "pixbuf", IS_FAVICON, NULL);
339
 
        gtk_tree_view_column_set_sort_column_id (column, IS_SOURCE);
340
 
        gtk_tree_view_append_column (itemlist_treeview, column);
341
 
        
342
 
        renderer = gtk_cell_renderer_text_new ();
343
 
        column = gtk_tree_view_column_new_with_attributes (_("Headline"), renderer, 
344
 
                                                           "text", IS_LABEL,
345
 
                                                           "weight", ITEMSTORE_UNREAD,                                    
346
 
                                                           NULL);
347
 
        gtk_tree_view_append_column (itemlist_treeview, column);
348
 
        gtk_tree_view_column_set_sort_column_id (column, IS_LABEL);
349
 
        g_object_set (column, "resizable", TRUE, NULL);
350
 
        g_object_set (renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL);
351
 
 
352
 
        /* And connect signals */       
353
 
        g_signal_connect (G_OBJECT (itemlist_treeview), "key-press-event", G_CALLBACK (ui_itemlist_key_press_cb), NULL);
354
 
        
355
 
        /* Setup the selection handler */
356
 
        select = gtk_tree_view_get_selection (itemlist_treeview);
357
 
        gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);
358
 
        g_signal_connect (G_OBJECT (select), "changed",
359
 
                          G_CALLBACK (on_itemlist_selection_changed), NULL);
360
 
        g_signal_connect ((gpointer)itemlist_treeview, "button_press_event",
361
 
                          G_CALLBACK (on_itemlist_button_press_event), NULL);
362
 
        g_signal_connect ((gpointer)itemlist_treeview, "row_activated",
363
 
                          G_CALLBACK (on_Itemlist_row_activated), NULL);
364
 
                  
365
 
        return ilscrolledwindow;
366
 
}
367
 
 
368
 
void
369
 
ui_itemlist_destroy (void)
370
 
{
371
 
        g_hash_table_destroy (item_id_to_iter);
372
 
}
373
 
 
374
 
/* typically called when filling the item tree view */
375
 
void 
376
 
ui_itemlist_prefocus (void)
377
 
{
378
 
        GtkWidget               *focus_widget;
379
 
        GtkTreeSelection        *itemselection;
380
 
        
381
 
        /* the following is important to prevent setting the unread
382
 
           flag for the first item in the item list when the user does
383
 
           the first click into the treeview, if we don't do a focus and
384
 
           unselect, GTK would always (exception: clicking on first item)
385
 
           generate two selection-change events (one for the clicked and
386
 
           one for the selected item)!!! */
387
 
 
388
 
        /* we need to restore the focus after we temporarily select the itemlist */
389
 
        focus_widget = gtk_window_get_focus (GTK_WINDOW (liferea_shell_get_window ()));
390
 
 
391
 
        /* prevent marking as unread before focussing, which leads to a selection */
392
 
        gtk_widget_grab_focus (GTK_WIDGET (itemlist_treeview));
393
 
 
394
 
        itemselection = gtk_tree_view_get_selection (itemlist_treeview);
395
 
        if (itemselection)
396
 
                gtk_tree_selection_unselect_all (itemselection);
397
 
        
398
 
        if (focus_widget)
399
 
                gtk_widget_grab_focus (focus_widget);
400
 
}
401
 
 
402
 
void 
403
 
ui_itemlist_add_item (itemPtr item) 
404
 
{
405
 
        GtkTreeStore    *itemstore;
406
 
        GtkTreeIter     *iter;
407
 
        GtkTreeIter     old_iter;
408
 
        gboolean        exists;
409
 
        nodePtr         node;
410
 
        gint            state = 0;
411
 
        
412
 
        exists = ui_item_id_to_iter (item->id, &old_iter);
413
 
        iter = &old_iter;
414
 
        
415
 
        itemstore = GTK_TREE_STORE (gtk_tree_view_get_model (itemlist_treeview));
416
 
 
417
 
        node = node_from_id (item->nodeId);
418
 
        if(!node)
419
 
                return; /* comment items do cause this... maybe filtering them earlier would be a good idea... */
420
 
 
421
 
        if (!exists) 
422
 
        {
423
 
                iter = g_new0 (GtkTreeIter, 1);
424
 
                gtk_tree_store_prepend (itemstore, iter, NULL);
425
 
                g_hash_table_insert (item_id_to_iter, GUINT_TO_POINTER (item->id), (gpointer)iter);
426
 
        }
427
 
 
428
 
        if (item->flagStatus)
429
 
                state += 2;
430
 
        if (!item->readStatus)
431
 
                state += 1;
432
 
 
433
 
        gtk_tree_store_set (itemstore, iter,
434
 
                                       IS_TIME, (guint64)item->time,
435
 
                                       IS_NR, item->id,
436
 
                                       IS_PARENT, node,
437
 
                                       IS_FAVICON, node->icon,
438
 
                                       IS_ENCICON, item->hasEnclosure?icons[ICON_ENCLOSURE]:NULL,
439
 
                                       IS_ENCLOSURE, item->hasEnclosure,
440
 
                                       IS_SOURCE, node,
441
 
                                       IS_STATE, state,
442
 
                                       -1);
443
 
        ui_itemlist_update_item (item);
444
 
}
445
 
 
446
 
void
447
 
ui_itemlist_enable_favicon_column (gboolean enabled)
448
 
{
449
 
        /* we depend on the fact that the second column is the favicon column!!! 
450
 
           if we are in search mode or have a folder or vfolder we show the favicon 
451
 
           column to give a hint where the item comes from ... */
452
 
        gtk_tree_view_column_set_visible (gtk_tree_view_get_column (itemlist_treeview, 3), enabled);
453
 
}
454
 
 
455
 
void 
456
 
ui_itemlist_enable_encicon_column (gboolean enabled) 
457
 
{
458
 
        /* we depend on the fact that the third column is the enclosure icon column!!! */
459
 
        gtk_tree_view_column_set_visible (gtk_tree_view_get_column (itemlist_treeview, 1), enabled);
460
 
}
461
 
 
462
 
void
463
 
on_popup_launchitem_selected (void) 
464
 
{
465
 
        itemPtr         item;
466
 
 
467
 
        item = itemlist_get_selected ();
468
 
        if (item) {
469
 
                itemview_launch_URL (item_get_source (item), FALSE);
470
 
                                       
471
 
                item_unload (item);
472
 
        } else {
473
 
                liferea_shell_set_important_status_bar (_("No item has been selected"));
474
 
        }
475
 
}
476
 
 
477
 
void
478
 
on_popup_launchitem_in_tab_selected (void) 
479
 
{
480
 
        itemPtr         item;
481
 
        const gchar     *link;
482
 
 
483
 
        item = itemlist_get_selected ();
484
 
        if (item) {
485
 
                link = item_get_source (item);
486
 
                if (link)
487
 
                        browser_tabs_add_new (link, link, FALSE);
488
 
                else
489
 
                        ui_show_error_box (_("This item has no link specified!"));
490
 
                        
491
 
                item_unload (item);
492
 
        } else {
493
 
                liferea_shell_set_important_status_bar (_("No item has been selected"));
494
 
        }
495
 
}
496
 
 
497
 
void 
498
 
on_Itemlist_row_activated (GtkTreeView *treeview,
499
 
                           GtkTreePath *path,
500
 
                           GtkTreeViewColumn *column,
501
 
                           gpointer user_data) 
502
 
{
503
 
 
504
 
        on_popup_launchitem_selected ();
505
 
}
506
 
 
507
 
/* menu callbacks */
508
 
 
509
 
void
510
 
on_toggle_item_flag (GtkMenuItem *menuitem,
511
 
                     gpointer user_data) 
512
 
{
513
 
        itemPtr         item;
514
 
 
515
 
        item = itemlist_get_selected ();
516
 
        if (item) {
517
 
                itemlist_toggle_flag (item);
518
 
                item_unload (item);
519
 
        }
520
 
}
521
 
 
522
 
void 
523
 
on_popup_toggle_flag (void) 
524
 
{
525
 
        on_toggle_item_flag (NULL, NULL);
526
 
}
527
 
 
528
 
void 
529
 
on_toggle_unread_status (GtkMenuItem *menuitem,
530
 
                         gpointer user_data) 
531
 
{
532
 
        itemPtr         item;
533
 
 
534
 
        item = itemlist_get_selected ();
535
 
        if (item) {
536
 
                itemlist_toggle_read_status (item);
537
 
                item_unload (item);
538
 
        }
539
 
}
540
 
 
541
 
void
542
 
on_popup_toggle_read (void)
543
 
{
544
 
        on_toggle_unread_status (NULL, NULL);
545
 
}
546
 
 
547
 
void
548
 
on_remove_items_activate (GtkMenuItem *menuitem, gpointer user_data)
549
 
{
550
 
        nodePtr         node;
551
 
        
552
 
        node = feedlist_get_selected ();
553
 
        // FIXME: use node type capability check
554
 
        if (node && (IS_FEED (node) || IS_NEWSBIN (node)))
555
 
                itemlist_remove_all_items (node);
556
 
        else
557
 
                ui_show_error_box (_("You must select a feed to delete its items!"));
558
 
}
559
 
 
560
 
void
561
 
on_remove_item_activate (GtkMenuItem *menuitem, gpointer user_data)
562
 
{
563
 
        itemPtr         item;
564
 
        
565
 
        item = itemlist_get_selected ();
566
 
        if (item) {
567
 
                ui_common_treeview_move_cursor (itemlist_treeview, 1);
568
 
                itemlist_remove_item (item);
569
 
        } else {
570
 
                liferea_shell_set_important_status_bar (_("No item has been selected"));
571
 
        }
572
 
}
573
 
 
574
 
void
575
 
on_popup_remove_selected (void)
576
 
{
577
 
        on_remove_item_activate(NULL, NULL);
578
 
}
579
 
 
580
 
void
581
 
ui_itemlist_select (itemPtr item)
582
 
{
583
 
        GtkTreeSelection        *selection;
584
 
        
585
 
        selection = gtk_tree_view_get_selection (itemlist_treeview);
586
 
        
587
 
        if (item) {
588
 
                GtkTreeIter             iter;
589
 
                GtkTreePath             *path;
590
 
                
591
 
                if (!ui_item_id_to_iter(item->id, &iter))
592
 
                        /* This is an evil hack to fix SF #1870052: crash
593
 
                           upon hitting <enter> when no headline selected.
594
 
                           FIXME: This code is rotten! Rewrite it! Now! */
595
 
                        itemlist_selection_changed (NULL);
596
 
 
597
 
                path = gtk_tree_model_get_path (gtk_tree_view_get_model (itemlist_treeview), &iter);
598
 
                gtk_tree_view_scroll_to_cell (itemlist_treeview, path, NULL, FALSE, 0.0, 0.0);
599
 
                gtk_tree_view_set_cursor (itemlist_treeview, path, NULL, FALSE);
600
 
                gtk_tree_path_free (path);
601
 
        } else {
602
 
                gtk_tree_selection_unselect_all (selection);
603
 
        }
604
 
}
605
 
 
606
 
itemPtr
607
 
ui_itemlist_find_unread_item (gulong startId)
608
 
{
609
 
        GtkTreeIter             iter;
610
 
        GtkTreeModel            *model;
611
 
        gboolean                valid = TRUE;
612
 
        
613
 
        model = gtk_tree_view_get_model (itemlist_treeview);
614
 
        
615
 
        if (startId)
616
 
                valid = ui_item_id_to_iter (startId, &iter);
617
 
        else
618
 
                valid = gtk_tree_model_get_iter_first (model, &iter);
619
 
        
620
 
        while (valid) {
621
 
                itemPtr item = item_load (ui_iter_to_item_id (&iter));
622
 
                if (item) {
623
 
                        if (!item->readStatus)
624
 
                                return item;
625
 
                        item_unload (item);
626
 
                }
627
 
                valid = gtk_tree_model_iter_next(model, &iter);
628
 
        }
629
 
 
630
 
        return NULL;
631
 
}
632
 
 
633
 
void
634
 
on_next_unread_item_activate (GtkMenuItem *menuitem, gpointer user_data)
635
 
{
636
 
        itemlist_select_next_unread ();
637
 
}
638
 
 
639
 
void
640
 
on_popup_next_unread_item_selected (gpointer callback_data, guint callback_action, GtkWidget *widget)
641
 
{
642
 
        itemlist_select_next_unread ();
643
 
}
644
 
 
645
 
void
646
 
on_nextbtn_clicked (GtkButton *button, gpointer user_data)
647
 
{
648
 
        itemlist_select_next_unread ();
649
 
}
650
 
 
651
 
gboolean
652
 
on_itemlist_button_press_event (GtkWidget *treeview, GdkEventButton *event, gpointer user_data)
653
 
{
654
 
        GtkTreeViewColumn       *column;
655
 
        GtkTreePath             *path;
656
 
        GtkTreeIter             iter;
657
 
        itemPtr                 item = NULL;
658
 
        gboolean                result = FALSE;
659
 
        
660
 
        if (event->type != GDK_BUTTON_PRESS)
661
 
                return FALSE;
662
 
 
663
 
        /* avoid handling header clicks */
664
 
        if (event->window != gtk_tree_view_get_bin_window (itemlist_treeview))
665
 
                return FALSE;
666
 
 
667
 
        if (!gtk_tree_view_get_path_at_pos (itemlist_treeview, (gint)event->x, (gint)event->y, &path, NULL, NULL, NULL))
668
 
                return FALSE;
669
 
 
670
 
        if (gtk_tree_model_get_iter (gtk_tree_view_get_model (itemlist_treeview), &iter, path))
671
 
                item = item_load (ui_iter_to_item_id (&iter));
672
 
                
673
 
        gtk_tree_path_free (path);
674
 
        
675
 
        if (item) {
676
 
                GdkEventButton *eb = (GdkEventButton*)event; 
677
 
                switch (eb->button) {
678
 
                        case 1:
679
 
                                column = gtk_tree_view_get_column (itemlist_treeview, 0);
680
 
                                if (column) {
681
 
                                        /* Allow flag toggling when left clicking in the flagging column.
682
 
                                           We depend on the fact that the state column is the first!!! */
683
 
                                        if (event->x <= column->width) {
684
 
                                                itemlist_toggle_flag (item);
685
 
                                                result = TRUE;
686
 
                                        }
687
 
                                }
688
 
                                break;
689
 
                        case 2:
690
 
                                /* Middle mouse click toggles read status... */
691
 
                                itemlist_toggle_read_status (item);
692
 
                                result = TRUE;
693
 
                                break;
694
 
                        case 3:
695
 
                                ui_itemlist_select (item);
696
 
                                ui_popup_item_menu (item, eb->button, eb->time);
697
 
                                result = TRUE;
698
 
                                break;
699
 
                }
700
 
                item_unload (item);
701
 
        }
702
 
                
703
 
        return result;
704
 
}
705
 
 
706
 
void
707
 
on_popup_copy_URL_clipboard (void)
708
 
{
709
 
        itemPtr         item;
710
 
 
711
 
        item = itemlist_get_selected ();
712
 
        if (item) {
713
 
                gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_PRIMARY), item_get_source (item), -1);
714
 
                gtk_clipboard_set_text (gtk_clipboard_get (GDK_SELECTION_CLIPBOARD), item_get_source (item), -1);
715
 
                item_unload (item);
716
 
        } else {
717
 
                liferea_shell_set_important_status_bar (_("No item has been selected"));
718
 
        }
719
 
}
720
 
 
721
 
void
722
 
ui_itemlist_search_item_link (itemPtr item)
723
 
{
724
 
        gchar *url = social_get_link_search_url (item_get_source (item));
725
 
        itemview_launch_URL (url, FALSE);
726
 
        g_free (url);
727
 
}
728
 
 
729
 
void
730
 
ui_itemlist_add_item_bookmark (itemPtr item)
731
 
{
732
 
        gchar *url = social_get_bookmark_url (item_get_source (item), item_get_title (item));
733
 
        (void)browser_launch_URL_external (url);
734
 
        g_free (url);
735
 
}
736
 
 
737
 
void
738
 
on_popup_social_bm_item_selected (void)
739
 
{
740
 
        itemPtr item;
741
 
 
742
 
        item = itemlist_get_selected ();
743
 
        if (item) {
744
 
                ui_itemlist_add_item_bookmark (item);
745
 
                item_unload (item);
746
 
        }
747
 
        else
748
 
                liferea_shell_set_important_status_bar (_("No item has been selected"));
749
 
}
750
 
 
751
 
void
752
 
on_itemlist_selection_changed (GtkTreeSelection *selection, gpointer data)
753
 
{
754
 
        GtkTreeIter     iter;
755
 
        GtkTreeModel    *model;
756
 
        itemPtr         item = NULL;
757
 
 
758
 
        if (gtk_tree_selection_get_selected (selection, &model, &iter))
759
 
                item = item_load (ui_iter_to_item_id (&iter));
760
 
 
761
 
        liferea_shell_update_item_menu (NULL != item);
762
 
        if (item)
763
 
                itemlist_selection_changed (item);
764
 
}
765
 
 
766
 
void
767
 
itemlist_sort_column_changed_cb (GtkTreeSortable *treesortable, gpointer user_data)
768
 
{
769
 
        gint            sortColumn, nodeSort;
770
 
        GtkSortType     sortType;
771
 
        gboolean        sorted, changed;
772
 
        
773
 
        if (feedlist_get_selected () == NULL)
774
 
                return;
775
 
        
776
 
        sorted = gtk_tree_sortable_get_sort_column_id (treesortable, &sortColumn, &sortType);
777
 
        if (!sorted)
778
 
                return;
779
 
                
780
 
        switch (sortColumn) {
781
 
                case IS_TIME:
782
 
                default:
783
 
                        nodeSort = NODE_VIEW_SORT_BY_TIME;
784
 
                        break;
785
 
                case IS_LABEL:
786
 
                        nodeSort = NODE_VIEW_SORT_BY_TITLE;
787
 
                        break;
788
 
                case IS_PARENT:
789
 
                case IS_SOURCE:
790
 
                        nodeSort = NODE_VIEW_SORT_BY_PARENT;
791
 
                        break;
792
 
        }
793
 
 
794
 
        changed = node_set_sort_column (feedlist_get_selected (), nodeSort, sortType == GTK_SORT_DESCENDING);
795
 
        if (changed)
796
 
                feedlist_schedule_save ();
797
 
}
798
 
 
799
 
/* needed because switching does sometimes returns to the tree 
800
 
   view with a very disturbing horizontal scrolling state */
801
 
void
802
 
ui_itemlist_scroll_left (void)
803
 
{
804
 
        GtkTreeViewColumn       *column;
805
 
        GtkTreePath             *path;
806
 
 
807
 
        if (2 != itemlist_get_view_mode ()) {
808
 
                gtk_tree_view_get_cursor (itemlist_treeview, &path, &column);
809
 
                if (path) {
810
 
                        column = gtk_tree_view_get_column (itemlist_treeview, 1);
811
 
                        gtk_tree_view_set_cursor (itemlist_treeview, path, column, FALSE);
812
 
                        gtk_tree_path_free (path);
813
 
                }
814
 
        }
815
 
}