/* xfce4-places-plugin
*
* Model: user bookmarks (defined in ~/.gtk-bookmarks)
*
* Copyright (c) 2007 Diego Ongaro <ongardie@gmail.com>
*
* 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
/* User bookmarks come from the file ~/.gtk-bookmarks.
*
* When changed() is first called, it returns TRUE.
*
* When get_bookmarks() is first called, they will be built using
* pbuser_build_bookmarks(). It stores the bookmarks in PBUserData.bookmarks
* and the file's mtime in PBUserData.loaded. get_bookmarks() then clones the
* bookmarks from PBUserData.bookmarks.
*
* Once that's done, a call to changed() checks the file's mtime and a couple
* other things.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "model_user.h"
#include "model.h"
#include "support.h"
#include <libxfce4util/libxfce4util.h>
#include <glib.h>
#include <glib/gstdio.h>
#define pbg_priv(pbg) ((PBUserData*) pbg->priv)
#define show_bookmark(b) ((gboolean) b->priv)
typedef struct
{
GList *bookmarks;
gchar *filename;
time_t loaded; /* 0 indicates loading the file hasn't been attempted
1 indicates the file does not exist
2+ are actual timestamps */
} PBUserData;
static inline time_t
pbuser_get_mtime(const gchar *filename)
{
struct stat buf;
if(g_stat(filename, &buf) == 0)
return MAX(buf.st_mtime, 2);
else
return 1;
}
static inline gboolean
pbuser_dir_exists(const gchar *path)
{
return g_file_test(path, G_FILE_TEST_IS_DIR);
}
static void
pbuser_free_bookmark(PlacesBookmark *bookmark)
{
g_assert(bookmark != NULL);
g_free(bookmark->uri);
g_free(bookmark->label);
g_free(bookmark);
}
static void
pbuser_destroy_bookmarks(PlacesBookmarkGroup *bookmark_group)
{
GList *bookmarks = pbg_priv(bookmark_group)->bookmarks;
if(bookmarks == NULL)
return;
DBG("destroy internal bookmarks");
while(bookmarks != NULL){
places_bookmark_free((PlacesBookmark*) bookmarks->data);
bookmarks = bookmarks->next;
}
g_list_free(bookmarks);
pbg_priv(bookmark_group)->bookmarks = NULL;
pbg_priv(bookmark_group)->loaded = 0;
}
static void
pbuser_build_bookmarks(PlacesBookmarkGroup *bookmark_group)
{
/* As of 2007-04-06, this is pretty much taken from/analogous to Thunar */
GList *bookmarks = NULL;
PlacesBookmark *bookmark;
gchar *name;
gchar *path;
gchar line[2048];
FILE *fp;
pbuser_destroy_bookmarks(bookmark_group);
fp = fopen(pbg_priv(bookmark_group)->filename, "r");
if(G_UNLIKELY(fp == NULL)){
DBG("Error opening gtk bookmarks file");
pbg_priv(bookmark_group)->loaded = 1;
return;
}
while( fgets(line, sizeof(line), fp) != NULL )
{
/* strip leading/trailing whitespace */
g_strstrip(line);
/* skip over the URI */
for (name = line; *name != '\0' && !g_ascii_isspace (*name); ++name)
/* pass */;
/* zero-terminate the URI */
*name++ = '\0';
/* check if we have a name */
for (; g_ascii_isspace (*name); ++name)
/* pass */;
/* parse the URI */ /* TODO: trash:// URI's */
path = g_filename_from_uri(line, NULL, NULL);
if (G_UNLIKELY(path == NULL || *path == '\0'))
continue;
/* if we don't have a name, find it in the path */
if(*name == '\0'){
name = g_filename_display_basename(path);
if(*name == '\0'){
g_free(path);
continue;
}
}else{
name = g_strdup(name);
}
/* create the BookmarkInfo container */
bookmark = places_bookmark_create(name); /* label needs to be freed */
bookmark->uri = path; /* uri needs to be freed */
bookmark->icon = "gnome-fs-directory";
bookmark->priv = (gpointer) pbuser_dir_exists(path);
bookmark->free = pbuser_free_bookmark;
bookmarks = g_list_prepend(bookmarks, bookmark);
}
fclose(fp);
pbg_priv(bookmark_group)->bookmarks = g_list_reverse(bookmarks);
pbg_priv(bookmark_group)->loaded = pbuser_get_mtime(pbg_priv(bookmark_group)->filename);
}
static GList*
pbuser_get_bookmarks(PlacesBookmarkGroup *bookmark_group)
{
const GList *orig_ls = pbg_priv(bookmark_group)->bookmarks;
const PlacesBookmark *orig;
GList *clone_ls = NULL;
PlacesBookmark *clone;
PlacesBookmarkAction *open, *terminal;
if(orig_ls == NULL){
pbuser_build_bookmarks(bookmark_group);
orig_ls = pbg_priv(bookmark_group)->bookmarks;
if(orig_ls == NULL)
return NULL;
}
orig_ls = g_list_last((GList*) orig_ls);
while(orig_ls != NULL){
orig = (PlacesBookmark*) orig_ls->data;
clone = places_bookmark_create(g_strdup(orig->label));
clone->uri = g_strdup(orig->uri);
clone->uri_scheme = orig->uri_scheme;
clone->icon = orig->icon;
clone->free = pbuser_free_bookmark;
terminal = places_create_open_terminal_action(clone);
clone->actions = g_list_prepend(clone->actions, terminal);
open = places_create_open_action(clone);
clone->actions = g_list_prepend(clone->actions, open);
clone->primary_action = open;
clone_ls = g_list_prepend(clone_ls, clone);
orig_ls = orig_ls->prev;
}
return clone_ls;
}
static gboolean
pbuser_changed(PlacesBookmarkGroup *bookmark_group)
{
/* If we haven't even tried, we should load the bookmarks */
if(pbg_priv(bookmark_group)->loaded == 0)
goto pbuser_did_change;
/* see if the file has changed (mtime or existence) */
time_t mtime = pbuser_get_mtime(pbg_priv(bookmark_group)->filename);
if(mtime != pbg_priv(bookmark_group)->loaded)
goto pbuser_did_change;
/* see if any directories have been created or removed */
GList *bookmarks = pbg_priv(bookmark_group)->bookmarks;
PlacesBookmark *bookmark;
gboolean ret = FALSE;
while(bookmarks != NULL){
bookmark = bookmarks->data;
if(show_bookmark(bookmark) != pbuser_dir_exists(bookmark->uri)){
bookmark->priv = (gpointer) !show_bookmark(bookmark);
ret = TRUE;
}
bookmarks = bookmarks->next;
}
if(ret == TRUE)
goto pbuser_did_change;
/* if we're still here, assume nothing changed */
return FALSE;
pbuser_did_change:
pbuser_destroy_bookmarks(bookmark_group);
return TRUE;
}
static void
pbuser_finalize(PlacesBookmarkGroup *bookmark_group)
{
pbuser_destroy_bookmarks(bookmark_group);
g_free(pbg_priv(bookmark_group)->filename);
pbg_priv(bookmark_group)->filename = NULL;
g_free(pbg_priv(bookmark_group));
g_free(bookmark_group);
}
/* external interface */
PlacesBookmarkGroup*
places_bookmarks_user_create()
{
PlacesBookmarkGroup *bookmark_group = g_new0(PlacesBookmarkGroup, 1);
bookmark_group->get_bookmarks = pbuser_get_bookmarks;
bookmark_group->changed = pbuser_changed;
bookmark_group->finalize = pbuser_finalize;
bookmark_group->priv = g_new0(PBUserData, 1);
pbg_priv(bookmark_group)->filename = xfce_get_homefile(".gtk-bookmarks", NULL);
return bookmark_group;
}
/* vim: set ai et tabstop=4: */
syntax highlighted by Code2HTML, v. 0.9.1