~ubuntu-branches/ubuntu/saucy/libfm/saucy-proposed

« back to all changes in this revision

Viewing changes to src/gtk/fm-places-model.c

  • Committer: Bazaar Package Importer
  • Author(s): Julien Lavergne
  • Date: 2010-12-02 00:20:44 UTC
  • mfrom: (1.1.5 upstream) (0.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20101202002044-qhja03c1hk95b0q7
Tags: 0.1.14-2ubuntu1
* Sync with Debian unstable.
 - Fix crash on right click (LP: #617655)
* Ubuntu remaining changes:
 - debian/patches/90_add_gobject_link.patch:
  + From upstream, add "gobject-2.0" in configure.ac explicitly.
 - debian/patches/03_disable_deprecated_gio_module.patch:
  + Disable -DG_DISABLE_DEPRECATED for the gio module to build with glib
    2.27+.
 - debian/control:
  + Add Replaces/Conflicts for libfm0 and libfm-gtk0 (<= 0.1.11-1) to ensure
    migration from lucid.
  + Build-depends on dh-autoreconf.
 - debian/rules:
  + Use dh --with autoreconf.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *      fm-places-model.c
 
3
 *
 
4
 *      Copyright 2010 PCMan <pcman.tw@gmail.com>
 
5
 *
 
6
 *      This program is free software; you can redistribute it and/or modify
 
7
 *      it under the terms of the GNU General Public License as published by
 
8
 *      the Free Software Foundation; either version 2 of the License, or
 
9
 *      (at your option) any later version.
 
10
 *
 
11
 *      This program is distributed in the hope that it will be useful,
 
12
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 *      GNU General Public License for more details.
 
15
 *
 
16
 *      You should have received a copy of the GNU General Public License
 
17
 *      along with this program; if not, write to the Free Software
 
18
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 
19
 *      MA 02110-1301, USA.
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include <config.h>
 
24
#endif
 
25
 
 
26
#include "fm-places-model.h"
 
27
#include <glib/gi18n-lib.h>
 
28
 
 
29
static void create_trash_item(FmPlacesModel* model);
 
30
 
 
31
static void update_sep_tp(FmPlacesModel* model)
 
32
{
 
33
    gtk_tree_path_free(model->sep_tp);
 
34
    model->sep_tp = gtk_tree_model_get_path(model, &model->sep_it);
 
35
}
 
36
 
 
37
static void place_item_free(FmPlaceItem* item)
 
38
{
 
39
    switch(item->type)
 
40
    {
 
41
    case FM_PLACES_ITEM_VOL:
 
42
        g_object_unref(item->vol);
 
43
        break;
 
44
    }
 
45
    fm_file_info_unref(item->fi);
 
46
    g_slice_free(FmPlaceItem, item);
 
47
}
 
48
 
 
49
static void on_file_info_job_finished(FmFileInfoJob* job, gpointer user_data)
 
50
{
 
51
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
52
    GList* l;
 
53
    GtkTreeIter it;
 
54
    FmPlaceItem* item;
 
55
    FmFileInfo* fi;
 
56
 
 
57
    /* g_debug("file info job finished"); */
 
58
    model->jobs = g_slist_remove(model->jobs, job);
 
59
 
 
60
    if(!gtk_tree_model_get_iter_first(model, &it))
 
61
        return;
 
62
 
 
63
    if(fm_list_is_empty(job->file_infos))
 
64
        return;
 
65
 
 
66
    /* optimize for one file case */
 
67
    if(fm_list_get_length(job->file_infos) == 1)
 
68
    {
 
69
        fi = FM_FILE_INFO(fm_list_peek_head(job->file_infos));
 
70
        do {
 
71
            item = NULL;
 
72
            gtk_tree_model_get(model, &it, FM_PLACES_MODEL_COL_INFO, &item, -1);
 
73
            if( item && item->fi && item->fi->path && fm_path_equal(item->fi->path, fi->path) )
 
74
            {
 
75
                fm_file_info_unref(item->fi);
 
76
                item->fi = fm_file_info_ref(fi);
 
77
                break;
 
78
            }
 
79
        }while(gtk_tree_model_iter_next(model, &it));
 
80
    }
 
81
    else
 
82
    {
 
83
        do {
 
84
            item = NULL;
 
85
            gtk_tree_model_get(model, &it, FM_PLACES_MODEL_COL_INFO, &item, -1);
 
86
            if( item && item->fi && item->fi->path )
 
87
            {
 
88
                for(l = fm_list_peek_head_link(job->file_infos); l; l = l->next )
 
89
                {
 
90
                    fi = FM_FILE_INFO(l->data);
 
91
                    if(fm_path_equal(item->fi->path, fi->path))
 
92
                    {
 
93
                        fm_file_info_unref(item->fi);
 
94
                        item->fi = fm_file_info_ref(fi);
 
95
                        /* remove the file from list to speed up further loading.
 
96
                      * This won't cause problem since nobody else if using the list. */
 
97
                        fm_list_delete_link(job->file_infos, l);
 
98
                        break;
 
99
                    }
 
100
                }
 
101
            }
 
102
        }while(gtk_tree_model_iter_next(model, &it));
 
103
    }
 
104
}
 
105
 
 
106
static void update_vol(FmPlacesModel* model, FmPlaceItem* item, GtkTreeIter* it, FmFileInfoJob* job)
 
107
{
 
108
    FmIcon* icon;
 
109
    GIcon* gicon;
 
110
    char* name;
 
111
    GdkPixbuf* pix;
 
112
    GMount* mount;
 
113
    FmPath* path;
 
114
 
 
115
    name = g_volume_get_name(item->vol);
 
116
    if(item->fi->icon)
 
117
        fm_icon_unref(item->fi->icon);
 
118
    gicon = g_volume_get_icon(item->vol);
 
119
    icon = fm_icon_from_gicon(gicon);
 
120
    item->fi->icon = icon;
 
121
    g_object_unref(gicon);
 
122
 
 
123
    mount = g_volume_get_mount(item->vol);
 
124
    if(mount)
 
125
    {
 
126
        GFile* gf = g_mount_get_root(mount);
 
127
        path = fm_path_new_for_gfile(gf);
 
128
        g_object_unref(gf);
 
129
        g_object_unref(mount);
 
130
    }
 
131
    else
 
132
        path = NULL;
 
133
 
 
134
    if(!fm_path_equal(item->fi->path, path))
 
135
    {
 
136
        if(item->fi->path)
 
137
            fm_path_unref(item->fi->path);
 
138
        item->fi->path = path ? fm_path_ref(path) : NULL;
 
139
        if(path)
 
140
        {
 
141
            if(job)
 
142
                fm_file_info_job_add(job, path);
 
143
            else
 
144
            {
 
145
                job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_FOLLOW_SYMLINK);
 
146
                model->jobs = g_slist_prepend(model->jobs, job);
 
147
                g_signal_connect(job, "finished", G_CALLBACK(on_file_info_job_finished), model);
 
148
                fm_job_run_async(job);
 
149
            }
 
150
        }
 
151
    }
 
152
 
 
153
    pix = fm_icon_get_pixbuf(item->fi->icon, fm_config->pane_icon_size);
 
154
    gtk_list_store_set(model, it, FM_PLACES_MODEL_COL_ICON, pix, FM_PLACES_MODEL_COL_LABEL, name, -1);
 
155
    g_object_unref(pix);
 
156
    g_free(name);
 
157
}
 
158
 
 
159
static void add_vol(FmPlacesModel* model, GVolume* vol, FmFileInfoJob* job)
 
160
{
 
161
    GtkTreeIter it;
 
162
    FmPlaceItem* item;
 
163
    item = g_slice_new0(FmPlaceItem);
 
164
    item->fi = fm_file_info_new();
 
165
    item->type = FM_PLACES_ITEM_VOL;
 
166
    item->vol = (GVolume*)g_object_ref(vol);
 
167
    gtk_list_store_insert_before(model, &it, &model->sep_it);
 
168
    gtk_list_store_set(model, &it, FM_PLACES_MODEL_COL_INFO, item, -1);
 
169
    update_vol(model, item, &it, job);
 
170
}
 
171
 
 
172
static FmPlaceItem* find_vol(FmPlacesModel* model, GVolume* vol, GtkTreeIter* _it)
 
173
{
 
174
    GtkTreeIter it;
 
175
    FmPlaceItem* item;
 
176
    /* FIXME: don't need to find from the first iter */
 
177
    if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &it))
 
178
    {
 
179
        do
 
180
        {
 
181
            FmPlaceItem* item;
 
182
            gtk_tree_model_get(GTK_TREE_MODEL(model), &it, FM_PLACES_MODEL_COL_INFO, &item, -1);
 
183
 
 
184
            if(item && item->type == FM_PLACES_ITEM_VOL && item->vol == vol)
 
185
            {
 
186
                *_it = it;
 
187
                return item;
 
188
            }
 
189
        }while(it.user_data != model->sep_it.user_data && gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &it));
 
190
    }
 
191
    return NULL;
 
192
}
 
193
 
 
194
void on_vol_added(GVolumeMonitor* vm, GVolume* vol, gpointer user_data)
 
195
{
 
196
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
197
    g_debug("add vol: %p, uuid: %s, udi: %s", vol, g_volume_get_identifier(vol, "uuid"), g_volume_get_identifier(vol, "hal-udi"));
 
198
    add_vol(model, vol, NULL);
 
199
    update_sep_tp(model);
 
200
}
 
201
 
 
202
void on_vol_removed(GVolumeMonitor* vm, GVolume* vol, gpointer user_data)
 
203
{
 
204
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
205
    FmPlaceItem* item;
 
206
    GtkTreeIter it;
 
207
    item = find_vol(model, vol, &it);
 
208
    /* g_debug("remove vol: %p, uuid: %s, udi: %s", vol, g_volume_get_identifier(vol, "uuid"), g_volume_get_identifier(vol, "hal-udi")); */
 
209
    if(item)
 
210
    {
 
211
        gtk_list_store_remove(model, &it);
 
212
        place_item_free(item);
 
213
        update_sep_tp(model);
 
214
    }
 
215
}
 
216
 
 
217
void on_vol_changed(GVolumeMonitor* vm, GVolume* vol, gpointer user_data)
 
218
{
 
219
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
220
    FmPlaceItem* item;
 
221
    GtkTreeIter it;
 
222
    item = find_vol(model, vol, &it);
 
223
    if(item)
 
224
        update_vol(model, item, &it, NULL);
 
225
}
 
226
 
 
227
void on_mount_added(GVolumeMonitor* vm, GMount* mount, gpointer user_data)
 
228
{
 
229
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
230
    GVolume* vol = g_mount_get_volume(mount);
 
231
    if(vol)
 
232
    {
 
233
        FmPlaceItem *item;
 
234
        GtkTreeIter it;
 
235
        item = find_vol(model, vol, &it);
 
236
        if(item && item->type == FM_PLACES_ITEM_VOL && !item->fi->path)
 
237
        {
 
238
            GFile* gf = g_mount_get_root(mount);
 
239
            FmPath* path = fm_path_new_for_gfile(gf);
 
240
            g_debug("mount path: %s", path->name);
 
241
            g_object_unref(gf);
 
242
            item->fi->path = path;
 
243
        }
 
244
        g_object_unref(vol);
 
245
    }
 
246
}
 
247
 
 
248
static void add_bookmarks(FmPlacesModel* model, FmFileInfoJob* job)
 
249
{
 
250
    FmPlaceItem* item;
 
251
    GList *bms, *l;
 
252
    FmIcon* icon = fm_icon_from_name("folder");
 
253
    FmIcon* remote_icon = NULL;
 
254
    GdkPixbuf* folder_pix = fm_icon_get_pixbuf(icon, fm_config->pane_icon_size);
 
255
    GdkPixbuf* remote_pix = NULL;
 
256
    bms = fm_bookmarks_list_all(model->bookmarks);
 
257
    for(l=bms;l;l=l->next)
 
258
    {
 
259
        FmBookmarkItem* bm = (FmBookmarkItem*)l->data;
 
260
        GtkTreeIter it;
 
261
        GdkPixbuf* pix;
 
262
        item = g_slice_new0(FmPlaceItem);
 
263
        item->type = FM_PLACES_ITEM_PATH;
 
264
        item->fi = fm_file_info_new();
 
265
        item->fi->path = fm_path_ref(bm->path);
 
266
        fm_file_info_job_add(job, item->fi->path);
 
267
 
 
268
        if(fm_path_is_native(item->fi->path))
 
269
        {
 
270
            item->fi->icon = fm_icon_ref(icon);
 
271
            pix = folder_pix;
 
272
        }
 
273
        else
 
274
        {
 
275
            if(G_UNLIKELY(!remote_icon))
 
276
            {
 
277
                remote_icon = fm_icon_from_name("folder-remote");
 
278
                remote_pix = fm_icon_get_pixbuf(remote_icon, fm_config->pane_icon_size);
 
279
            }
 
280
            item->fi->icon = fm_icon_ref(remote_icon);
 
281
            pix = remote_pix;
 
282
        }
 
283
        item->bm_item = bm;
 
284
        gtk_list_store_append(model, &it);
 
285
        gtk_list_store_set(model, &it, FM_PLACES_MODEL_COL_ICON, pix, FM_PLACES_MODEL_COL_LABEL, bm->name, FM_PLACES_MODEL_COL_INFO, item, -1);
 
286
    }
 
287
    g_object_unref(folder_pix);
 
288
    fm_icon_unref(icon);
 
289
    if(remote_icon)
 
290
    {
 
291
        fm_icon_unref(remote_icon);
 
292
        if(remote_pix)
 
293
            g_object_unref(remote_pix);
 
294
    }
 
295
}
 
296
 
 
297
static void on_bookmarks_changed(FmBookmarks* bm, gpointer user_data)
 
298
{
 
299
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
300
    FmFileInfoJob* job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_FOLLOW_SYMLINK);
 
301
    GtkTreeIter it = model->sep_it;
 
302
    /* remove all old bookmarks */
 
303
    if(gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &it))
 
304
    {
 
305
        while(gtk_list_store_remove(model, &it))
 
306
            continue;
 
307
    }
 
308
    add_bookmarks(model, job);
 
309
 
 
310
    g_signal_connect(job, "finished", G_CALLBACK(on_file_info_job_finished), model);
 
311
    model->jobs = g_slist_prepend(model->jobs, job);
 
312
    fm_job_run_async(job);
 
313
}
 
314
 
 
315
static gboolean update_trash_item(gpointer user_data)
 
316
{
 
317
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
318
    if(fm_config->use_trash)
 
319
    {
 
320
        GFile* gf = g_file_new_for_uri("trash:///");
 
321
        GFileInfo* inf = g_file_query_info(gf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT, 0, NULL, NULL);
 
322
        g_object_unref(gf);
 
323
        if(inf)
 
324
        {
 
325
            FmIcon* icon;
 
326
            const char* icon_name;
 
327
            FmPlaceItem* item;
 
328
            GdkPixbuf* pix;
 
329
            guint32 n = g_file_info_get_attribute_uint32(inf, G_FILE_ATTRIBUTE_TRASH_ITEM_COUNT);
 
330
            g_object_unref(inf);
 
331
            icon_name = n > 0 ? "user-trash-full" : "user-trash";
 
332
            icon = fm_icon_from_name(icon_name);
 
333
            gtk_tree_model_get(GTK_TREE_MODEL(model), &model->trash_it, FM_PLACES_MODEL_COL_INFO, &item, -1);
 
334
            if(item->fi->icon)
 
335
                fm_icon_unref(item->fi->icon);
 
336
            item->fi->icon = icon;
 
337
            /* update the icon */
 
338
            pix = fm_icon_get_pixbuf(item->fi->icon, fm_config->pane_icon_size);
 
339
            gtk_list_store_set(model, &model->trash_it, FM_PLACES_MODEL_COL_ICON, pix, -1);
 
340
            g_object_unref(pix);
 
341
        }
 
342
    }
 
343
    return FALSE;
 
344
}
 
345
 
 
346
 
 
347
static void on_trash_changed(GFileMonitor *monitor, GFile *gf, GFile *other, GFileMonitorEvent evt, gpointer user_data)
 
348
{
 
349
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
350
    if(model->trash_idle)
 
351
        g_source_remove(model->trash_idle);
 
352
    model->trash_idle = g_idle_add((GSourceFunc)update_trash_item, model);
 
353
}
 
354
 
 
355
static void update_icons(FmPlacesModel* model)
 
356
{
 
357
    GtkTreeIter it;
 
358
    gtk_tree_model_get_iter_first(GTK_TREE_MODEL(model), &it);
 
359
    do{
 
360
        if(it.user_data != model->sep_it.user_data)
 
361
        {
 
362
            FmPlaceItem* item;
 
363
            gtk_tree_model_get(GTK_TREE_MODEL(model), &it, FM_PLACES_MODEL_COL_INFO, &item, -1);
 
364
            /* FIXME: get icon size from FmConfig */
 
365
            GdkPixbuf* pix = fm_icon_get_pixbuf(item->fi->icon, fm_config->pane_icon_size);
 
366
            gtk_list_store_set(model, &it, FM_PLACES_MODEL_COL_ICON, pix, -1);
 
367
            g_object_unref(pix);
 
368
        }
 
369
    }while( gtk_tree_model_iter_next(GTK_TREE_MODEL(model), &it) );
 
370
}
 
371
 
 
372
static void on_use_trash_changed(FmConfig* cfg, gpointer user_data)
 
373
{
 
374
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
375
    if(cfg->use_trash && model->trash_it.user_data == NULL)
 
376
        create_trash_item(model);
 
377
    else
 
378
    {
 
379
        FmPlaceItem *item;
 
380
        gtk_tree_model_get(GTK_TREE_MODEL(model), &model->trash_it, FM_PLACES_MODEL_COL_INFO, &item, -1);
 
381
        gtk_list_store_remove(GTK_LIST_STORE(model), &model->trash_it);
 
382
        model->trash_it.user_data = NULL;
 
383
        place_item_free(item);
 
384
 
 
385
        if(model->trash_monitor)
 
386
        {
 
387
            g_signal_handlers_disconnect_by_func(model->trash_monitor, on_trash_changed, model);
 
388
            g_object_unref(model->trash_monitor);
 
389
            model->trash_monitor = NULL;
 
390
        }
 
391
        if(model->trash_idle)
 
392
        {
 
393
            g_source_remove(model->trash_idle);
 
394
            model->trash_idle = 0;
 
395
        }
 
396
    }
 
397
    update_sep_tp(model);
 
398
}
 
399
 
 
400
static void on_pane_icon_size_changed(FmConfig* cfg, gpointer user_data)
 
401
{
 
402
    FmPlacesModel* model = FM_PLACES_MODEL(user_data);
 
403
    update_icons(model);
 
404
}
 
405
 
 
406
 
 
407
static void create_trash_item(FmPlacesModel* model)
 
408
{
 
409
    GtkTreeIter it;
 
410
    FmPlaceItem* item;
 
411
    GdkPixbuf* pix;
 
412
    GFile* gf;
 
413
 
 
414
    gf = g_file_new_for_uri("trash:///");
 
415
    model->trash_monitor = fm_monitor_directory(gf, NULL);
 
416
    g_signal_connect(model->trash_monitor, "changed", G_CALLBACK(on_trash_changed), model);
 
417
    g_object_unref(gf);
 
418
 
 
419
    item = g_slice_new0(FmPlaceItem);
 
420
    item->type = FM_PLACES_ITEM_PATH;
 
421
    item->fi = fm_file_info_new();
 
422
    item->fi->path = fm_path_ref(fm_path_get_trash());
 
423
    item->fi->icon = fm_icon_from_name("user-trash");
 
424
    gtk_list_store_insert(model, &it, 2);
 
425
    pix = fm_icon_get_pixbuf(item->fi->icon, fm_config->pane_icon_size);
 
426
    gtk_list_store_set(model, &it, FM_PLACES_MODEL_COL_ICON, pix, FM_PLACES_MODEL_COL_LABEL, _("Trash"), FM_PLACES_MODEL_COL_INFO, item, -1);
 
427
    g_object_unref(pix);
 
428
    model->trash_it = it;
 
429
 
 
430
    if(0 == model->trash_idle)
 
431
        model->trash_idle = g_idle_add((GSourceFunc)update_trash_item, model);
 
432
}
 
433
 
 
434
static void fm_places_model_init(FmPlacesModel *self)
 
435
{
 
436
    GType types[] = {GDK_TYPE_PIXBUF, G_TYPE_STRING, G_TYPE_POINTER};
 
437
    GtkTreeIter it;
 
438
    GtkTreePath* tp;
 
439
    FmPlaceItem* item;
 
440
    GList *vols, *l;
 
441
    GIcon* gicon;
 
442
    FmIcon* icon;
 
443
    GFile* gf;
 
444
    GdkPixbuf* pix;
 
445
    FmFileInfoJob* job = fm_file_info_job_new(NULL, FM_FILE_INFO_JOB_FOLLOW_SYMLINK);
 
446
    GtkListStore* model = GTK_LIST_STORE(self);
 
447
 
 
448
    gtk_list_store_set_column_types(GTK_LIST_STORE(self), FM_PLACES_MODEL_N_COLS, types);
 
449
 
 
450
    self->theme_change_handler = g_signal_connect_swapped(gtk_icon_theme_get_default(), "changed",
 
451
                                            G_CALLBACK(update_icons), self);
 
452
 
 
453
    self->use_trash_change_handler = g_signal_connect(fm_config, "changed::use_trash",
 
454
                                             G_CALLBACK(on_use_trash_changed), self);
 
455
 
 
456
    self->pane_icon_size_change_handler = g_signal_connect(fm_config, "changed::pane_icon_size",
 
457
                                             G_CALLBACK(on_pane_icon_size_changed), self);
 
458
 
 
459
    item = g_slice_new0(FmPlaceItem);
 
460
    item->type = FM_PLACES_ITEM_PATH;
 
461
    item->fi = fm_file_info_new();
 
462
    item->fi->path = fm_path_ref(fm_path_get_home());
 
463
    item->fi->icon = fm_icon_from_name("user-home");
 
464
    gtk_list_store_append(model, &it);
 
465
    pix = fm_icon_get_pixbuf(item->fi->icon, fm_config->pane_icon_size);
 
466
    gtk_list_store_set(model, &it, FM_PLACES_MODEL_COL_ICON, pix, FM_PLACES_MODEL_COL_LABEL, item->fi->path->name, FM_PLACES_MODEL_COL_INFO, item, -1);
 
467
    g_object_unref(pix);
 
468
    fm_file_info_job_add(job, item->fi->path);
 
469
 
 
470
    /* Only show desktop in side pane when the user has a desktop dir. */
 
471
    if(g_file_test(g_get_user_special_dir(G_USER_DIRECTORY_DESKTOP), G_FILE_TEST_IS_DIR))
 
472
    {
 
473
        item = g_slice_new0(FmPlaceItem);
 
474
        item->type = FM_PLACES_ITEM_PATH;
 
475
        item->fi = fm_file_info_new();
 
476
        item->fi->path = fm_path_ref(fm_path_get_desktop());
 
477
        item->fi->icon = fm_icon_from_name("user-desktop");
 
478
        gtk_list_store_append(model, &it);
 
479
        pix = fm_icon_get_pixbuf(item->fi->icon, fm_config->pane_icon_size);
 
480
        gtk_list_store_set(model, &it, FM_PLACES_MODEL_COL_ICON, pix, FM_PLACES_MODEL_COL_LABEL, _("Desktop"), FM_PLACES_MODEL_COL_INFO, item, -1);
 
481
        g_object_unref(pix);
 
482
        fm_file_info_job_add(job, item->fi->path);
 
483
    }
 
484
 
 
485
    if(fm_config->use_trash)
 
486
        create_trash_item(self); /* FIXME: how to handle trash can? */
 
487
 
 
488
    item = g_slice_new0(FmPlaceItem);
 
489
    item->type = FM_PLACES_ITEM_PATH;
 
490
    item->fi = fm_file_info_new();
 
491
    item->fi->path = fm_path_ref(fm_path_get_apps_menu());
 
492
    item->fi->icon = fm_icon_from_name("system-software-install");
 
493
    gtk_list_store_append(model, &it);
 
494
    pix = fm_icon_get_pixbuf(item->fi->icon, fm_config->pane_icon_size);
 
495
    gtk_list_store_set(model, &it, FM_PLACES_MODEL_COL_ICON, pix, FM_PLACES_MODEL_COL_LABEL, _("Applications"), FM_PLACES_MODEL_COL_INFO, item, -1);
 
496
    g_object_unref(pix);
 
497
    /* fm_file_info_job_add(job, item->fi->path); */
 
498
 
 
499
    /* volumes */
 
500
    self->vol_mon = g_volume_monitor_get();
 
501
    g_signal_connect(self->vol_mon, "volume-added", G_CALLBACK(on_vol_added), self);
 
502
    g_signal_connect(self->vol_mon, "volume-removed", G_CALLBACK(on_vol_removed), self);
 
503
    g_signal_connect(self->vol_mon, "volume-changed", G_CALLBACK(on_vol_changed), self);
 
504
    g_signal_connect(self->vol_mon, "mount-added", G_CALLBACK(on_mount_added), self);
 
505
 
 
506
    /* separator */
 
507
    gtk_list_store_append(model, &self->sep_it);
 
508
 
 
509
    /* add volumes to side-pane */
 
510
    vols = g_volume_monitor_get_volumes(self->vol_mon);
 
511
    for(l=vols;l;l=l->next)
 
512
    {
 
513
        GVolume* vol = G_VOLUME(l->data);
 
514
        add_vol(self, vol, job);
 
515
        g_object_unref(vol);
 
516
    }
 
517
    g_list_free(vols);
 
518
 
 
519
    /* get the path of separator */
 
520
    self->sep_tp = gtk_tree_model_get_path(GTK_TREE_MODEL(self), &self->sep_it);
 
521
 
 
522
    self->bookmarks = fm_bookmarks_get(); /* bookmarks */
 
523
    g_signal_connect(self->bookmarks, "changed", G_CALLBACK(on_bookmarks_changed), self);
 
524
 
 
525
    /* add bookmarks to side pane */
 
526
    add_bookmarks(self, job);
 
527
 
 
528
    g_signal_connect(job, "finished", G_CALLBACK(on_file_info_job_finished), self);
 
529
    self->jobs = g_slist_prepend(self->jobs, job);
 
530
    fm_job_run_async(job);
 
531
}
 
532
 
 
533
const GtkTreePath* fm_places_model_get_separator_path(FmPlacesModel* model)
 
534
{
 
535
    return model->sep_tp;
 
536
}
 
537
 
 
538
gboolean fm_places_model_iter_is_separator(FmPlacesModel* model, GtkTreeIter* it)
 
539
{
 
540
    return it && it->user_data == model->sep_it.user_data;
 
541
}
 
542
 
 
543
gboolean fm_places_model_path_is_separator(FmPlacesModel* model, GtkTreePath* tp)
 
544
{
 
545
    return tp && gtk_tree_path_compare(model->sep_tp, tp) == 0;
 
546
}
 
547
 
 
548
gboolean fm_places_model_path_is_bookmark(FmPlacesModel* model, GtkTreePath* tp)
 
549
{
 
550
    return tp && gtk_tree_path_compare(model->sep_tp, tp) < 0;
 
551
}
 
552
 
 
553
gboolean fm_places_model_path_is_places(FmPlacesModel* model, GtkTreePath* tp)
 
554
{
 
555
    return tp && gtk_tree_path_compare(model->sep_tp, tp) > 0;
 
556
}
 
557
 
 
558
static gboolean row_draggable(GtkTreeDragSource* drag_source, GtkTreePath* tp)
 
559
{
 
560
    FmPlacesModel* model = FM_PLACES_MODEL(drag_source);
 
561
    return fm_places_model_path_is_bookmark(model, tp);
 
562
}
 
563
 
 
564
static void fm_places_model_drag_source_init(GtkTreeDragSourceIface *iface)
 
565
{
 
566
    iface->row_draggable = row_draggable;
 
567
}
 
568
 
 
569
G_DEFINE_TYPE_WITH_CODE (FmPlacesModel, fm_places_model, GTK_TYPE_LIST_STORE,
 
570
             G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_DRAG_SOURCE,
 
571
                        fm_places_model_drag_source_init))
 
572
 
 
573
 
 
574
static void fm_places_model_finalize(GObject *object)
 
575
{
 
576
    FmPlacesModel *self;
 
577
    GtkTreeIter it;
 
578
 
 
579
    g_return_if_fail(object != NULL);
 
580
    g_return_if_fail(FM_IS_PLACES_MODEL(object));
 
581
 
 
582
    self = FM_PLACES_MODEL(object);
 
583
 
 
584
    if(self->jobs)
 
585
    {
 
586
        GSList* l;
 
587
        for(l = self->jobs; l; l=l->next)
 
588
        {
 
589
            fm_job_cancel(FM_JOB(l->data));
 
590
            g_object_unref(l->data);
 
591
        }
 
592
        g_slist_free(self->jobs);
 
593
    }
 
594
 
 
595
    if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(self), &it))
 
596
    {
 
597
        do
 
598
        {
 
599
            FmPlaceItem* item;
 
600
            gtk_tree_model_get(GTK_TREE_MODEL(self), &it, FM_PLACES_MODEL_COL_INFO, &item, -1);
 
601
            if(G_LIKELY(item))
 
602
                place_item_free(item);
 
603
        }while(gtk_tree_model_iter_next(GTK_TREE_MODEL(self), &it));
 
604
    }
 
605
 
 
606
    gtk_tree_path_free(self->sep_tp);
 
607
 
 
608
    g_signal_handler_disconnect(gtk_icon_theme_get_default(), self->theme_change_handler);
 
609
    g_signal_handler_disconnect(fm_config, self->use_trash_change_handler);
 
610
    g_signal_handler_disconnect(fm_config, self->pane_icon_size_change_handler);
 
611
 
 
612
    g_signal_handlers_disconnect_by_func(self->vol_mon, on_vol_added, self);
 
613
    g_signal_handlers_disconnect_by_func(self->vol_mon, on_vol_removed, self);
 
614
    g_signal_handlers_disconnect_by_func(self->vol_mon, on_vol_changed, self);
 
615
    g_signal_handlers_disconnect_by_func(self->vol_mon, on_mount_added, self);
 
616
    g_object_unref(self->vol_mon);
 
617
 
 
618
    if(self->trash_monitor)
 
619
    {
 
620
        g_signal_handlers_disconnect_by_func(self->trash_monitor, on_trash_changed, self);
 
621
        g_object_unref(self->trash_monitor);
 
622
    }
 
623
    if(self->trash_idle)
 
624
        g_source_remove(self->trash_idle);
 
625
 
 
626
    G_OBJECT_CLASS(fm_places_model_parent_class)->finalize(object);
 
627
}
 
628
 
 
629
static void fm_places_model_class_init(FmPlacesModelClass *klass)
 
630
{
 
631
    GObjectClass *g_object_class;
 
632
 
 
633
    g_object_class = G_OBJECT_CLASS(klass);
 
634
    g_object_class->finalize = fm_places_model_finalize;
 
635
}
 
636
 
 
637
 
 
638
GtkListStore *fm_places_model_new(void)
 
639
{
 
640
    return g_object_new(FM_TYPE_PLACES_MODEL, NULL);
 
641
}