/* * fm-gtk-bookmarks.c * * Copyright 2009 PCMan * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. */ #include "fm-bookmarks.h" #include #include enum { CHANGED, N_SIGNALS }; static FmBookmarks* fm_bookmarks_new (void); static void fm_bookmarks_finalize (GObject *object); static GList* load_bookmarks(const char* fpath); static void free_item(FmBookmarkItem* item); static char* get_bookmarks_file(); G_DEFINE_TYPE(FmBookmarks, fm_bookmarks, G_TYPE_OBJECT); static FmBookmarks* singleton = NULL; static guint signals[N_SIGNALS]; static guint idle_handler = 0; static void fm_bookmarks_class_init(FmBookmarksClass *klass) { GObjectClass *g_object_class; g_object_class = G_OBJECT_CLASS(klass); g_object_class->finalize = fm_bookmarks_finalize; signals[CHANGED] = g_signal_new("changed", G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET(FmBookmarksClass, changed), NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0 ); } static void fm_bookmarks_finalize(GObject *object) { FmBookmarks *self; g_return_if_fail(object != NULL); g_return_if_fail(IS_FM_BOOKMARKS(object)); self = FM_BOOKMARKS(object); if(idle_handler) { g_source_remove(idle_handler); idle_handler = 0; } g_list_foreach(self->items, (GFunc)free_item, NULL); g_list_free(self->items); g_object_unref(self->mon); G_OBJECT_CLASS(fm_bookmarks_parent_class)->finalize(object); } char* get_bookmarks_file() { return g_build_filename(g_get_home_dir(), ".gtk-bookmarks", NULL); } void free_item(FmBookmarkItem* item) { if(item->name != item->path->name) g_free(item->name); fm_path_unref(item->path); g_slice_free(FmBookmarkItem, item); } static void on_changed( GFileMonitor* mon, GFile* gf, GFile* other, GFileMonitorEvent evt, FmBookmarks* bookmarks ) { char* fpath; /* reload bookmarks */ g_list_foreach(bookmarks->items, (GFunc)free_item, NULL); g_list_free(bookmarks->items); fpath = get_bookmarks_file(); bookmarks->items = load_bookmarks(fpath); g_free(fpath); g_signal_emit(bookmarks, signals[CHANGED], 0); } static FmBookmarkItem* new_item(char* line) { FmBookmarkItem* item = g_slice_new0(FmBookmarkItem); char* sep; sep = strchr(line, '\n'); if(sep) *sep = '\0'; sep = strchr(line, ' '); if(sep) *sep = '\0'; /* FIXME: this is no longer needed once fm_path_new can convert file:/// to / */ if(g_str_has_prefix(line, "file:/")) { char* fpath = g_filename_from_uri(line, NULL, NULL); item->path = fm_path_new(fpath); g_free(fpath); } else { /* FIXME: is unescape needed? */ item->path = fm_path_new(line); } if(sep) item->name = g_strdup(sep+1); else item->name = g_filename_display_name(item->path->name); return item; } GList* load_bookmarks(const char* fpath) { FILE* f; char buf[1024]; FmBookmarkItem* item; GList* items = NULL; /* load the file */ if( f = fopen(fpath, "r") ) { while(fgets(buf, 1024, f)) { item = new_item(buf); items = g_list_prepend(items, item); } } items = g_list_reverse(items); return items; } static void fm_bookmarks_init(FmBookmarks *self) { char* fpath = get_bookmarks_file(); GFile* gf = g_file_new_for_path(fpath); self->mon = g_file_monitor_file(gf, 0, NULL, NULL); g_object_unref(gf); g_signal_connect(self->mon, "changed", G_CALLBACK(on_changed), self); self->items = load_bookmarks(fpath); g_free(fpath); } FmBookmarks *fm_bookmarks_new(void) { return g_object_new(FM_BOOKMARKS_TYPE, NULL); } FmBookmarks* fm_bookmarks_get(void) { if( G_LIKELY(singleton) ) g_object_ref(singleton); else { singleton = fm_bookmarks_new(); g_object_add_weak_pointer(G_OBJECT(singleton), &singleton); } return singleton; } GList* fm_bookmarks_list_all(FmBookmarks* bookmarks) { return bookmarks->items; } gboolean save_bookmarks(FmBookmarks* bookmarks) { FmBookmarkItem* item; GList* l; GString* buf = g_string_sized_new(1024); char* fpath; for( l=bookmarks->items; l; l=l->next ) { char* uri; item = (FmBookmarkItem*)l->data; uri = fm_path_to_uri(item->path); g_string_append(buf, uri); g_free(uri); g_string_append_c(buf, ' '); g_string_append(buf, item->name); g_string_append_c(buf, '\n'); } fpath = get_bookmarks_file(); g_file_set_contents(fpath, buf->str, buf->len, NULL); g_free(fpath); g_string_free(buf, TRUE); return FALSE; } void queue_save_bookmarks(FmBookmarks* bookmarks) { if(idle_handler) g_source_remove(idle_handler); idle_handler = g_idle_add((GSourceFunc)save_bookmarks, bookmarks); } FmBookmarkItem* fm_bookmarks_insert(FmBookmarks* bookmarks, FmPath* path, const char* name, int pos) { FmBookmarkItem* item = g_slice_new0(FmBookmarkItem); item->path = fm_path_ref(path); item->name = g_strdup(name); bookmarks->items = g_list_insert(bookmarks->items, item, pos); /* g_debug("insert %s at %d", name, pos); */ queue_save_bookmarks(bookmarks); return item; } void fm_bookmarks_remove(FmBookmarks* bookmarks, FmBookmarkItem* item) { bookmarks->items = g_list_remove(bookmarks->items, item); free_item(item); queue_save_bookmarks(bookmarks); } void fm_bookmarks_rename(FmBookmarks* bookmarks, FmBookmarkItem* item, const char* new_name) { g_free(item->name); item->name = g_strdup(new_name); queue_save_bookmarks(bookmarks); }