From 89cdeabb890038ba7547da72deca2f36a39595e8 Mon Sep 17 00:00:00 2001 From: Pierre Pronchery Date: Tue, 7 Jan 2014 01:12:23 -0600 Subject: [PATCH] Introduced a new "trash" plug-in (currently able to list the content of the trash) --- Makefile | 1 + src/plugins/Makefile | 17 ++- src/plugins/project.conf | 10 +- src/plugins/trash.c | 284 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 309 insertions(+), 3 deletions(-) create mode 100644 src/plugins/trash.c diff --git a/Makefile b/Makefile index 80f90b6..1b5b557 100644 --- a/Makefile +++ b/Makefile @@ -91,6 +91,7 @@ dist: $(PACKAGE)-$(VERSION)/src/plugins/selection.c \ $(PACKAGE)-$(VERSION)/src/plugins/subversion.c \ $(PACKAGE)-$(VERSION)/src/plugins/template.c \ + $(PACKAGE)-$(VERSION)/src/plugins/trash.c \ $(PACKAGE)-$(VERSION)/src/plugins/volumes.c \ $(PACKAGE)-$(VERSION)/src/plugins/Makefile \ $(PACKAGE)-$(VERSION)/src/plugins/common.c \ diff --git a/src/plugins/Makefile b/src/plugins/Makefile index 988a662..4c0e62d 100644 --- a/src/plugins/Makefile +++ b/src/plugins/Makefile @@ -1,4 +1,4 @@ -TARGETS = cvs.so dirtree.so favorites.so git.so make.so preview.so properties.so selection.so subversion.so template.so volumes.so +TARGETS = cvs.so dirtree.so favorites.so git.so make.so preview.so properties.so selection.so subversion.so template.so trash.so volumes.so PREFIX = /usr/local DESTDIR = LIBDIR = $(PREFIX)/lib @@ -89,6 +89,13 @@ template_LDFLAGS = $(LDFLAGSF) $(LDFLAGS) template.so: $(template_OBJS) $(CCSHARED) -o template.so $(template_OBJS) $(template_LDFLAGS) +trash_OBJS = trash.o +trash_CFLAGS = $(CPPFLAGSF) $(CPPFLAGS) $(CFLAGSF) $(CFLAGS) +trash_LDFLAGS = $(LDFLAGSF) $(LDFLAGS) + +trash.so: $(trash_OBJS) + $(CCSHARED) -o trash.so $(trash_OBJS) $(trash_LDFLAGS) + volumes_OBJS = volumes.o volumes_CFLAGS = $(CPPFLAGSF) $(CPPFLAGS) $(CFLAGSF) $(CFLAGS) volumes_LDFLAGS = $(LDFLAGSF) $(LDFLAGS) @@ -126,11 +133,14 @@ subversion.o: subversion.c common.c ../../include/Browser.h template.o: template.c ../../include/Browser.h $(CC) $(template_CFLAGS) -c template.c +trash.o: trash.c ../../include/Browser.h + $(CC) $(trash_CFLAGS) -c trash.c + volumes.o: volumes.c ../../include/Browser.h $(CC) $(volumes_CFLAGS) -c volumes.c clean: - $(RM) -- $(cvs_OBJS) $(dirtree_OBJS) $(favorites_OBJS) $(git_OBJS) $(make_OBJS) $(preview_OBJS) $(properties_OBJS) $(selection_OBJS) $(subversion_OBJS) $(template_OBJS) $(volumes_OBJS) + $(RM) -- $(cvs_OBJS) $(dirtree_OBJS) $(favorites_OBJS) $(git_OBJS) $(make_OBJS) $(preview_OBJS) $(properties_OBJS) $(selection_OBJS) $(subversion_OBJS) $(template_OBJS) $(trash_OBJS) $(volumes_OBJS) distclean: clean $(RM) -- $(TARGETS) @@ -153,6 +163,8 @@ install: $(TARGETS) $(MKDIR) $(DESTDIR)$(LIBDIR)/Browser/plugins $(INSTALL) -m 0644 subversion.so $(DESTDIR)$(LIBDIR)/Browser/plugins/subversion.so $(MKDIR) $(DESTDIR)$(LIBDIR)/Browser/plugins + $(INSTALL) -m 0644 trash.so $(DESTDIR)$(LIBDIR)/Browser/plugins/trash.so + $(MKDIR) $(DESTDIR)$(LIBDIR)/Browser/plugins $(INSTALL) -m 0644 volumes.so $(DESTDIR)$(LIBDIR)/Browser/plugins/volumes.so uninstall: @@ -164,6 +176,7 @@ uninstall: $(RM) -- $(DESTDIR)$(LIBDIR)/Browser/plugins/preview.so $(RM) -- $(DESTDIR)$(LIBDIR)/Browser/plugins/properties.so $(RM) -- $(DESTDIR)$(LIBDIR)/Browser/plugins/subversion.so + $(RM) -- $(DESTDIR)$(LIBDIR)/Browser/plugins/trash.so $(RM) -- $(DESTDIR)$(LIBDIR)/Browser/plugins/volumes.so .PHONY: all clean distclean install uninstall diff --git a/src/plugins/project.conf b/src/plugins/project.conf index def0963..427dcd6 100644 --- a/src/plugins/project.conf +++ b/src/plugins/project.conf @@ -1,4 +1,4 @@ -targets=cvs,dirtree,favorites,git,make,preview,properties,selection,subversion,template,volumes +targets=cvs,dirtree,favorites,git,make,preview,properties,selection,subversion,template,trash,volumes cppflags_force=-I ../../include cppflags= cflags_force=-W `pkg-config --cflags libDesktop` @@ -84,6 +84,14 @@ sources=template.c [template.c] depends=../../include/Browser.h +[trash] +type=plugin +sources=trash.c +install=$(LIBDIR)/Browser/plugins + +[trash.c] +depends=../../include/Browser.h + [volumes] type=plugin sources=volumes.c diff --git a/src/plugins/trash.c b/src/plugins/trash.c new file mode 100644 index 0000000..c6bc0b8 --- /dev/null +++ b/src/plugins/trash.c @@ -0,0 +1,284 @@ +/* $Id$ */ +/* Copyright (c) 2014 Pierre Pronchery */ +/* This file is part of DeforaOS Desktop Browser */ +/* 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, version 3 of the License. + * + * 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, see . */ + + + +#include +#include +#include +#include +#include +#include +#include +#include "Browser.h" +#define _(string) gettext(string) +#define N_(string) (string) + + +/* Trash */ +/* private */ +/* types */ +typedef struct _BrowserPlugin +{ + BrowserPluginHelper * helper; + + guint source; + + /* widgets */ + GtkWidget * widget; + GtkWidget * view; + GtkListStore * store; +} Trash; + + +/* prototypes */ +static Trash * _trash_init(BrowserPluginHelper * helper); +static void _trash_destroy(Trash * trash); +static GtkWidget * _trash_get_widget(Trash * trash); +static void _trash_refresh(Trash * trash, GList * selection); + +static void _trash_refresh_trash(Trash * trash); + +/* callbacks */ +static void _trash_on_select_all(gpointer data); +static gboolean _trash_on_timeout(gpointer data); + + +/* public */ +/* variables */ +BrowserPluginDefinition plugin = +{ + N_("Trash"), + "user-trash", + NULL, + _trash_init, + _trash_destroy, + _trash_get_widget, + _trash_refresh +}; + + +/* private */ +/* functions */ +/* trash_init */ +static Trash * _trash_init(BrowserPluginHelper * helper) +{ + Trash * trash; + GtkWidget * widget; + GtkToolItem * toolitem; + GtkTreeSelection * treesel; + GtkCellRenderer * renderer; + GtkTreeViewColumn * column; + + if((trash = object_new(sizeof(*trash))) == NULL) + return NULL; + trash->helper = helper; + trash->source = 0; +#if GTK_CHECK_VERSION(3, 0, 0) + trash->widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); +#else + trash->widget = gtk_vbox_new(FALSE, 0); +#endif + widget = gtk_toolbar_new(); + /* FIXME handle sensitiveness */ + toolitem = gtk_tool_button_new(NULL, _("Move to trash")); +#if GTK_CHECK_VERSION(2, 8, 0) + gtk_tool_button_set_icon_name(GTK_TOOL_BUTTON(toolitem), "user-trash"); +#else + /* FIXME implement */ +#endif + /* FIXME handle the signal */ + gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); + toolitem = gtk_separator_tool_item_new(); + gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); + toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_SELECT_ALL); + g_signal_connect_swapped(toolitem, "clicked", G_CALLBACK( + _trash_on_select_all), trash); + gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); + toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_DELETE); + /* FIXME handle the signal */ + gtk_toolbar_insert(GTK_TOOLBAR(widget), toolitem, -1); + gtk_box_pack_start(GTK_BOX(trash->widget), widget, FALSE, TRUE, 0); + widget = gtk_scrolled_window_new(NULL, NULL); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(widget), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + trash->store = gtk_list_store_new(5, GDK_TYPE_PIXBUF, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_UINT, G_TYPE_STRING); + trash->view = gtk_tree_view_new_with_model(GTK_TREE_MODEL( + trash->store)); + gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(trash->view), TRUE); + treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(trash->view)); + gtk_tree_selection_set_mode(treesel, GTK_SELECTION_MULTIPLE); + /* icon */ + renderer = gtk_cell_renderer_pixbuf_new(); + column = gtk_tree_view_column_new_with_attributes("", renderer, + "pixbuf", 0, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(trash->view), column); + /* path to the original file */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Filename"), + renderer, "text", 2, NULL); + gtk_tree_view_column_set_resizable(column, TRUE); + gtk_tree_view_append_column(GTK_TREE_VIEW(trash->view), column); + /* timestamp */ + renderer = gtk_cell_renderer_text_new(); + column = gtk_tree_view_column_new_with_attributes(_("Deleted"), + renderer, "text", 4, NULL); + gtk_tree_view_append_column(GTK_TREE_VIEW(trash->view), column); + gtk_container_add(GTK_CONTAINER(widget), trash->view); + gtk_box_pack_start(GTK_BOX(trash->widget), widget, TRUE, TRUE, 0); + gtk_widget_show_all(trash->widget); + trash->source = g_idle_add(_trash_on_timeout, trash); + return trash; +} + + +/* trash_destroy */ +static void _trash_destroy(Trash * trash) +{ + if(trash->source != 0) + g_source_remove(trash->source); + object_delete(trash); +} + + +/* trash_get_widget */ +static GtkWidget * _trash_get_widget(Trash * trash) +{ + return trash->widget; +} + + +/* trash_refresh */ +static void _trash_refresh(Trash * trash, GList * selection) +{ + /* FIXME implement (save a copy of the selection) */ +} + + +/* trash_refresh_trash */ +static char * _refresh_path(void); + +static void _trash_refresh_trash(Trash * trash) +{ + const char ext[] = ".trashinfo"; + const char section[] = "Trash Info"; + BrowserPluginHelper * helper = trash->helper; + Config * config; + char * path; + DIR * dir; + struct dirent * de; + size_t len; + GtkTreeIter iter; + GdkPixbuf * pixbuf; + char * p; + char const * q; + struct tm tm; + time_t t; + char const * u; + + /* FIXME report errors */ + if((path = _refresh_path()) == NULL) + return; + if((config = config_new()) == NULL) + { + free(path); + return; + } + /* FIXME should try to create the directory */ + if((dir = opendir(path)) == NULL) + { + config_delete(config); + free(path); + return; + } + gtk_list_store_clear(trash->store); + while((de = readdir(dir)) != NULL) + { + if((len = strlen(de->d_name)) <= sizeof(ext)) + continue; + if(strncmp(&de->d_name[len - sizeof(ext) + 1], ext, + sizeof(ext)) != 0) + continue; + config_reset(config); + p = g_strdup_printf("%s/%s", path, de->d_name); + if(config_load(config, p) != 0 + || (q = config_get(config, section, "Path")) + == NULL) + { + g_free(p); + continue; + } + pixbuf = helper->get_icon(helper->browser, q, NULL, NULL, NULL, + 24); + t = -1; + if((u = config_get(config, section, "DeletionDate")) != NULL + && strptime(u, "%Y-%m-%dT%H:%M:%S", &tm) + != NULL) + /* XXX also format u in a nicer way */ + t = mktime(&tm); + else + u = ""; + gtk_list_store_append(trash->store, &iter); + gtk_list_store_set(trash->store, &iter, 0, pixbuf, 1, p, 2, q, + 3, t, 4, u, -1); + g_free(p); + } + config_delete(config); + free(path); +} + +static char * _refresh_path(void) +{ + const char fallback[] = ".local/share"; + const char trash[] = "Trash/info"; + char * ret; + char const * homedir; + size_t len; + + /* FIXME check $XDG_DATA_HOME first */ + if((homedir = getenv("HOME")) == NULL) + homedir = g_get_home_dir(); + len = strlen(homedir) + 1 + sizeof(fallback) + 1 + sizeof(trash); + if((ret = malloc(len)) == NULL) + return NULL; + snprintf(ret, len, "%s/%s/%s", homedir, fallback, trash); + return ret; +} + + +/* callbacks */ +/* trash_on_select_all */ +static void _trash_on_select_all(gpointer data) +{ + Trash * trash = data; + GtkTreeSelection * treesel; + + treesel = gtk_tree_view_get_selection(GTK_TREE_VIEW(trash->view)); + gtk_tree_selection_select_all(treesel); +} + + +/* trash_on_timeout */ +static gboolean _trash_on_timeout(gpointer data) +{ + Trash * trash = data; + + trash->source = 0; + _trash_refresh_trash(trash); + /* FIXME refresh only if necessary */ + trash->source = g_timeout_add(5000, _trash_on_timeout, trash); + return FALSE; +}