/* $Id: xfae-model.c 21496 2006-05-04 22:12:21Z benny $ */
/*-
* Copyright (c) 2005 Benedikt Meurer <benny@xfce.org>
* All rights reserved.
*
* 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, 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., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_ERRNO_H
#include <errno.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <xfce4-autostart-editor/xfae-model.h>
typedef struct _XfaeItem XfaeItem;
static void xfae_model_class_init (XfaeModelClass *klass);
static void xfae_model_init (XfaeModel *model);
static void xfae_model_tree_model_init (GtkTreeModelIface *iface);
static void xfae_model_finalize (GObject *object);
static GtkTreeModelFlags xfae_model_get_flags (GtkTreeModel *tree_model);
static gint xfae_model_get_n_columns (GtkTreeModel *tree_model);
static GType xfae_model_get_column_type (GtkTreeModel *tree_model,
gint index);
static gboolean xfae_model_get_iter (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path);
static GtkTreePath *xfae_model_get_path (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static void xfae_model_get_value (GtkTreeModel *tree_model,
GtkTreeIter *iter,
gint column,
GValue *value);
static gboolean xfae_model_iter_next (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean xfae_model_iter_children (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent);
static gboolean xfae_model_iter_has_child (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gint xfae_model_iter_n_children (GtkTreeModel *tree_model,
GtkTreeIter *iter);
static gboolean xfae_model_iter_nth_child (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n);
static gboolean xfae_model_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child);
static XfaeItem *xfae_item_new (const gchar *relpath);
static void xfae_item_free (XfaeItem *item);
static gboolean xfae_item_is_removable (XfaeItem *item);
struct _XfaeModelClass
{
GObjectClass __parent__;
};
struct _XfaeModel
{
GObject __parent__;
guint stamp;
GList *items;
};
struct _XfaeItem
{
gchar *name;
GdkPixbuf *icon;
gchar *comment;
gchar *relpath;
gboolean hidden;
};
G_DEFINE_TYPE_WITH_CODE (XfaeModel,
xfae_model,
G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL,
xfae_model_tree_model_init));
static void
xfae_model_class_init (XfaeModelClass *klass)
{
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS (klass);
gobject_class->finalize = xfae_model_finalize;
}
static void
xfae_model_init (XfaeModel *model)
{
XfaeItem *item;
gchar **files;
guint n;
model->stamp = g_random_int ();
files = xfce_resource_match (XFCE_RESOURCE_CONFIG, "autostart/*.desktop", TRUE);
for (n = 0; files[n] != NULL; ++n)
{
item = xfae_item_new (files[n]);
if (G_LIKELY (item != NULL))
model->items = g_list_append (model->items, item);
}
g_strfreev (files);
}
static void
xfae_model_tree_model_init (GtkTreeModelIface *iface)
{
iface->get_flags = xfae_model_get_flags;
iface->get_n_columns = xfae_model_get_n_columns;
iface->get_column_type = xfae_model_get_column_type;
iface->get_iter = xfae_model_get_iter;
iface->get_path = xfae_model_get_path;
iface->get_value = xfae_model_get_value;
iface->iter_next = xfae_model_iter_next;
iface->iter_children = xfae_model_iter_children;
iface->iter_has_child = xfae_model_iter_has_child;
iface->iter_n_children = xfae_model_iter_n_children;
iface->iter_nth_child = xfae_model_iter_nth_child;
iface->iter_parent = xfae_model_iter_parent;
}
static void
xfae_model_finalize (GObject *object)
{
XfaeModel *model = XFAE_MODEL (object);
/* free all items */
g_list_foreach (model->items, (GFunc) xfae_item_free, NULL);
g_list_free (model->items);
G_OBJECT_CLASS (xfae_model_parent_class)->finalize (object);
}
static GtkTreeModelFlags
xfae_model_get_flags (GtkTreeModel *tree_model)
{
return GTK_TREE_MODEL_ITERS_PERSIST | GTK_TREE_MODEL_LIST_ONLY;
}
static gint
xfae_model_get_n_columns (GtkTreeModel *tree_model)
{
return XFAE_MODEL_N_COLUMNS;
}
static GType
xfae_model_get_column_type (GtkTreeModel *tree_model,
gint index)
{
switch (index)
{
case XFAE_MODEL_COLUMN_NAME:
return G_TYPE_STRING;
case XFAE_MODEL_COLUMN_ICON:
return GDK_TYPE_PIXBUF;
case XFAE_MODEL_COLUMN_ENABLED:
return G_TYPE_BOOLEAN;
case XFAE_MODEL_COLUMN_REMOVABLE:
return G_TYPE_BOOLEAN;
}
g_assert_not_reached ();
return G_TYPE_INVALID;
}
static gboolean
xfae_model_get_iter (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreePath *path)
{
XfaeModel *model = XFAE_MODEL (tree_model);
gint index;
g_return_val_if_fail (XFAE_IS_MODEL (model), FALSE);
g_return_val_if_fail (gtk_tree_path_get_depth (path) > 0, FALSE);
index = gtk_tree_path_get_indices (path)[0];
if (G_UNLIKELY (index >= g_list_length (model->items)))
return FALSE;
iter->stamp = model->stamp;
iter->user_data = g_list_nth (model->items, index);
return TRUE;
}
static GtkTreePath*
xfae_model_get_path (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
XfaeModel *model = XFAE_MODEL (tree_model);
gint index;
g_return_val_if_fail (XFAE_IS_MODEL (model), NULL);
g_return_val_if_fail (model->stamp == iter->stamp, NULL);
index = g_list_position (model->items, iter->user_data);
if (G_UNLIKELY (index < 0))
return NULL;
return gtk_tree_path_new_from_indices (index, -1);
}
static void
xfae_model_get_value (GtkTreeModel *tree_model,
GtkTreeIter *iter,
gint column,
GValue *value)
{
XfaeModel *model = XFAE_MODEL (tree_model);
XfaeItem *item = ((GList *) iter->user_data)->data;
gchar *name;
g_return_if_fail (XFAE_IS_MODEL (model));
g_return_if_fail (iter->stamp == model->stamp);
switch (column)
{
case XFAE_MODEL_COLUMN_NAME:
g_value_init (value, G_TYPE_STRING);
if (G_LIKELY (item->comment != NULL && *item->comment != '\0'))
name = g_strdup_printf ("%s (%s)", item->name, item->comment);
else
name = g_strdup (item->name);
g_value_take_string (value, name);
break;
case XFAE_MODEL_COLUMN_ICON:
g_value_init (value, GDK_TYPE_PIXBUF);
g_value_set_object (value, item->icon);
break;
case XFAE_MODEL_COLUMN_ENABLED:
g_value_init (value, G_TYPE_BOOLEAN);
g_value_set_boolean (value, !item->hidden);
break;
case XFAE_MODEL_COLUMN_REMOVABLE:
g_value_init (value, G_TYPE_BOOLEAN);
g_value_set_boolean (value, xfae_item_is_removable (item));
break;
default:
g_assert_not_reached ();
}
}
static gboolean
xfae_model_iter_next (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
XfaeModel *model = XFAE_MODEL (tree_model);
g_return_val_if_fail (XFAE_IS_MODEL (model), FALSE);
g_return_val_if_fail (model->stamp == iter->stamp, FALSE);
iter->user_data = ((GList *) iter->user_data)->next;
return (iter->user_data != NULL);
}
static gboolean
xfae_model_iter_children (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent)
{
XfaeModel *model = XFAE_MODEL (tree_model);
g_return_val_if_fail (XFAE_IS_MODEL (model), FALSE);
if (G_LIKELY (parent == NULL && model->items != NULL))
{
iter->stamp = model->stamp;
iter->user_data = model->items;
return TRUE;
}
return FALSE;
}
static gboolean
xfae_model_iter_has_child (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
return FALSE;
}
static gint
xfae_model_iter_n_children (GtkTreeModel *tree_model,
GtkTreeIter *iter)
{
XfaeModel *model = XFAE_MODEL (tree_model);
g_return_val_if_fail (XFAE_IS_MODEL (model), 0);
return (iter == NULL) ? g_list_length (model->items) : 0;
}
static gboolean
xfae_model_iter_nth_child (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *parent,
gint n)
{
XfaeModel *model = XFAE_MODEL (tree_model);
g_return_val_if_fail (XFAE_IS_MODEL (model), FALSE);
if (G_UNLIKELY (parent == NULL && n < g_list_length (model->items)))
{
iter->stamp = model->stamp;
iter->user_data = g_list_nth (model->items, n);
return TRUE;
}
return FALSE;
}
static gboolean
xfae_model_iter_parent (GtkTreeModel *tree_model,
GtkTreeIter *iter,
GtkTreeIter *child)
{
return FALSE;
}
static XfaeItem*
xfae_item_new (const gchar *relpath)
{
const gchar *value;
XfaeItem *item = NULL;
gboolean skip = FALSE;
XfceRc *rc;
gchar **only_show_in;
gchar **not_show_in;
gchar **args;
gchar *icon_name;
gint m;
rc = xfce_rc_config_open (XFCE_RESOURCE_CONFIG, relpath, TRUE);
if (G_LIKELY (rc != NULL))
{
xfce_rc_set_group (rc, "Desktop Entry");
/* verify that we have an application here */
value = xfce_rc_read_entry (rc, "Type", NULL);
if (G_LIKELY (value != NULL && g_ascii_strcasecmp (value, "Application") == 0))
{
item = g_new0 (XfaeItem, 1);
item->relpath = g_strdup (relpath);
value = xfce_rc_read_entry (rc, "Name", NULL);
if (G_LIKELY (value != NULL))
item->name = g_strdup (value);
value = xfce_rc_read_entry (rc, "Icon", NULL);
if (G_UNLIKELY (value != NULL))
item->icon = xfce_themed_icon_load (value, 16);
else
{
/* we try to be smart here in that we try to guess
* the icon name from the "Exec" key if no "Icon"
* key is provided.
*/
value = xfce_rc_read_entry (rc, "Exec", NULL);
if (G_LIKELY (value != NULL))
{
args = g_strsplit_set (value, " \t", -1);
if (G_LIKELY (*args != NULL))
{
icon_name = g_path_get_basename (*args);
item->icon = xfce_themed_icon_load (icon_name, 16);
g_free (icon_name);
}
g_strfreev (args);
}
}
value = xfce_rc_read_entry (rc, "Comment", NULL);
if (G_LIKELY (value != NULL))
item->comment = g_strdup (value);
item->hidden = xfce_rc_read_bool_entry (rc, "Hidden", FALSE);
}
/* check the OnlyShowIn setting */
only_show_in = xfce_rc_read_list_entry (rc, "OnlyShowIn", ";");
if (G_UNLIKELY (only_show_in != NULL))
{
/* check if "Xfce" is specified */
for (m = 0, skip = TRUE; only_show_in[m] != NULL; ++m)
if (g_ascii_strcasecmp (only_show_in[m], "Xfce") == 0)
{
skip = FALSE;
break;
}
g_strfreev (only_show_in);
}
else
{
/* check the NotShowIn setting */
not_show_in = xfce_rc_read_list_entry (rc, "NotShowIn", ";");
if (G_UNLIKELY (not_show_in != NULL))
{
/* check if "Xfce" is not specified */
for (m = 0; not_show_in[m] != NULL; ++m)
if (g_ascii_strcasecmp (not_show_in[m], "Xfce") == 0)
{
skip = TRUE;
break;
}
g_strfreev (not_show_in);
}
}
xfce_rc_close (rc);
/* check if we should skip the item */
if (G_UNLIKELY (skip))
{
xfae_item_free (item);
item = NULL;
}
}
return item;
}
static void
xfae_item_free (XfaeItem *item)
{
if (item->icon != NULL)
g_object_unref (G_OBJECT (item->icon));
g_free (item->relpath);
g_free (item->comment);
g_free (item->name);
g_free (item);
}
static gboolean
xfae_item_is_removable (XfaeItem *item)
{
gboolean removable = TRUE;
gchar **files;
gchar *dir;
guint n;
/* check whether all of the containing directories are writable */
files = xfce_resource_lookup_all (XFCE_RESOURCE_CONFIG, item->relpath);
for (n = 0; files[n] != NULL; ++n)
{
dir = g_path_get_dirname (files[n]);
if (access (dir, R_OK | W_OK | X_OK) < 0)
removable = FALSE;
g_free (dir);
}
g_strfreev (files);
return removable;
}
static gboolean
xfae_item_remove (XfaeItem *item,
GError **error)
{
gboolean succeed = TRUE;
gchar **files;
guint n;
/* remove all files that belong to this item */
files = xfce_resource_lookup_all (XFCE_RESOURCE_CONFIG, item->relpath);
for (n = 0; files[n] != NULL; ++n)
{
if (unlink (files[n]) < 0 && errno != ENOENT)
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno),
_("Failed to unlink %s: %s"), files[n], g_strerror (errno));
succeed = FALSE;
break;
}
}
g_strfreev (files);
return succeed;
}
/**
* xfae_model_new:
*
* Allocates a new #XfaeModel instance.
*
* Return value: the newly allocated #XfaeModel instance.
**/
GtkTreeModel*
xfae_model_new (void)
{
return g_object_new (XFAE_TYPE_MODEL, NULL);
}
/**
* xfae_model_add:
* @model : a #XfaeModel.
* @name : the user visible name of the new item.
* @description : the description for the new item.
* @command : the command for the new item.
* @error : return locations for errors or %NULL.
*
* Attempts to add a new item with the given parameters
* to @model.
*
* Return value: %TRUE if successfull, else %FALSE.
**/
gboolean
xfae_model_add (XfaeModel *model,
const gchar *name,
const gchar *description,
const gchar *command,
GError **error)
{
GtkTreePath *path;
GtkTreeIter iter;
XfaeItem *item;
XfceRc *rc;
gchar *file;
gchar *dir;
gchar relpath[4096];
guint n;
g_return_val_if_fail (XFAE_IS_MODEL (model), FALSE);
g_return_val_if_fail (name != NULL, FALSE);
g_return_val_if_fail (description != NULL, FALSE);
g_return_val_if_fail (command != NULL, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
dir = xfce_resource_save_location (XFCE_RESOURCE_CONFIG, "autostart/", TRUE);
/* generate a unique file name */
for (n = 0;; ++n)
{
file = (n == 0)
? g_strdup_printf ("%s.desktop", name)
: g_strdup_printf ("%s-%u.desktop", name, n);
g_snprintf (relpath, 4096, "%s%s", dir, file);
if (!g_file_test (relpath, G_FILE_TEST_IS_REGULAR))
break;
g_free (file);
}
/* generate the file spec */
g_snprintf (relpath, 4096, "autostart/%s", file);
g_free (file);
g_free (dir);
/* generate the .desktop file */
rc = xfce_rc_config_open (XFCE_RESOURCE_CONFIG, relpath, FALSE);
if (G_UNLIKELY (rc == NULL))
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (EIO),
_("Failed to create file %s"), relpath);
return FALSE;
}
/* write the content */
xfce_rc_set_group (rc, "Desktop Entry");
xfce_rc_write_entry (rc, "Encoding", "UTF-8");
xfce_rc_write_entry (rc, "Version", "0.9.4");
xfce_rc_write_entry (rc, "Type", "Application");
xfce_rc_write_entry (rc, "Name", name);
xfce_rc_write_entry (rc, "Comment", description);
xfce_rc_write_entry (rc, "Exec", command);
xfce_rc_write_bool_entry (rc, "StartupNotify", FALSE);
xfce_rc_write_bool_entry (rc, "Terminal", FALSE);
xfce_rc_write_bool_entry (rc, "Hidden", FALSE);
xfce_rc_close (rc);
/* now load the matching item for the list */
item = xfae_item_new (relpath);
if (G_UNLIKELY (item == NULL))
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (EIO),
_("Failed to write file %s"), relpath);
return FALSE;
}
/* append it to our list */
model->items = g_list_append (model->items, item);
/* generate the iter for the newly appended item */
iter.stamp = model->stamp;
iter.user_data = g_list_last (model->items);
/* tell the view about it */
path = gtk_tree_path_new_from_indices (g_list_length (model->items) - 1, -1);
gtk_tree_model_row_inserted (GTK_TREE_MODEL (model), path, &iter);
gtk_tree_path_free (path);
return TRUE;
}
/**
* xfae_model_remove:
* @model : a #XfaeModel.
* @iter : the #GtkTreeIter referring to the item that should be removed.
* @error : return location for errors or %NULL.
*
* Tries to remove the item referred to by @iter from @model.
*
* Return value: %TRUE if the removal was successful.
**/
gboolean
xfae_model_remove (XfaeModel *model,
GtkTreeIter *iter,
GError **error)
{
GtkTreePath *path;
XfaeItem *item;
GList *lp;
gint index;
g_return_val_if_fail (XFAE_IS_MODEL (model), FALSE);
g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
lp = iter->user_data;
item = lp->data;
/* try to unlink the item from disk */
if (!xfae_item_remove (item, error))
return FALSE;
/* unlink the item from the list */
index = g_list_position (model->items, lp);
model->items = g_list_delete_link (model->items, lp);
xfae_item_free (item);
/* tell the view that we have just removed one item */
path = gtk_tree_path_new_from_indices (index, -1);
gtk_tree_model_row_deleted (GTK_TREE_MODEL (model), path);
gtk_tree_path_free (path);
return TRUE;
}
/**
* xfae_model_toggle:
* @model : a #XfaeModel.
* @iter : the #GtkTreeIter referring to the item that should be toggled.
* @error : return location for errors or %NULL.
*
* Attempts to toggle the state of the item referrred to by @iter.
*
* Return value: %TRUE on success, else %FALSE.
**/
gboolean
xfae_model_toggle (XfaeModel *model,
GtkTreeIter *iter,
GError **error)
{
GtkTreePath *path;
XfaeItem *item;
XfceRc *rc;
GList *lp;
g_return_val_if_fail (XFAE_IS_MODEL (model), FALSE);
g_return_val_if_fail (iter->stamp == model->stamp, FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
lp = iter->user_data;
item = lp->data;
/* try to open the resource config */
rc = xfce_rc_config_open (XFCE_RESOURCE_CONFIG, item->relpath, FALSE);
if (G_UNLIKELY (rc == NULL))
{
g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (EIO),
_("Failed to open %s for writing"), item->relpath);
return FALSE;
}
/* perform the toggle operation :-) */
item->hidden = !item->hidden;
/* write the result */
xfce_rc_set_group (rc, "Desktop Entry");
xfce_rc_write_bool_entry (rc, "Hidden", item->hidden);
xfce_rc_close (rc);
/* tell the view that we have most probably a new state */
path = gtk_tree_path_new_from_indices (g_list_position (model->items, lp), -1);
gtk_tree_model_row_changed (GTK_TREE_MODEL (model), path, iter);
gtk_tree_path_free (path);
return TRUE;
}
syntax highlighted by Code2HTML, v. 0.9.1