Recently Used Files Proposal



Hi all.

I've spoken with Federico at GUADEC about the recently used files
handling, and we came up with some ideas for improving the current
situation.

* What do we have?
We have a specification, at freedesktop.org, which nobody uses except
us; this is not so bad, but since we've taken the path of
interoperability, that specification should at least be improved.  The
process already started: I'm writing a new specification, which is under
review in the xdg mailing list.  Nevertheless, the specification really
handles only storaging of bookmarks - and treats the recently used items
list as a special case of bookmarks (high access rate, short life span).

We have an implementation of the recent-files spec that many projects
already use.  The implementation lives in the libegg/recent-files
module.  It's unmaintained; it's buggy (28 bugs open in bugzilla, many
with patches); it's ugly (performance-wise and API-wise).  I have
requested a CVS account in order to *at least* apply the patches lying
around in bugzilla (so that language bindings don't leak resources all
around or have to add API in order to improve sanity; and so that
application developers do not have to apply a set of patches before
using it) - but the entire recent-files module should be reviewed (or
completely rewrote, keeping the good things and scrapping the rest).

* What do users want?
Alice has worked for six hours on a document, using Abiword.  She saved
the file, but she used a generic name;  soon after she has closed the
save dialog, she realizes that she does not know where she saved that
file.  She wants to get a list of recently used files that shows the
files she has used today, where they are, and she want to be able to
change the file's location and name.

Bob has been working for two months, editing a set of chapters (each in
a single file) for a book he is writing.  While he is driving its
motorcycle, he fells trying to avoid a cat, and breaks his arm.  He can
return to his job of editing only one month later, and wishes to know
which chapters he edited one month ago, in order to avoid opening each
chapter.

* What do we want?
We want something that handles a list of recently used resources, be
that files or directories, local or remote.  We want to be able to show
a list of recently used resources to a user.  We want application
developers to be able to register a resource, and to show a list, sorted
and filtered.  We want to be able to track each item in the list, and
check if a resource has been changed, moved or deleted.

* How do we get there?
We should provide what I've called "the GtkFileChooser of recently used
files".  On the "core" side, we have a GtkRecentItem object, which is
the representation of a recently used resource.  It works basically like
a bookmark: it has a URI, a title, a description and timestamps for
operations.  It also has metadata: the MIME type of the resource pointed
by the URI; the applications that have registered it; whether it is to
be shown into all lists of recently used resources or just into the list
requested by the applications that have registered it; it has groups,
which implement classes of applications (like Editors, Graphics, Media,
etc.), so that multiple applications could share the same recently used
items.

We then have the GtkRecentManager object, for handling the list of
recently used resources; it will handle the storage for these bookmarks,
pass the list when requested, and filter, sort and purge that list.  It
will also check each item in the list, so that we could track changes in
the resources, and notify the users of this object.

On the UI side, we should have a GtkRecentChooserDialog and a
GtkRecentChooserWidget, for which I have prepared a mockup at the GUADEC
Hackfest; you might see it here:

http://www.emmanuelebassi.net/images/shots/recent-items-viewer.png

(It's also a semi-working Perl program, which uses the current
recent-files implementation and its Perl bindings).

We could provide more than the filter based on the timestamp, as we do
for the GtkFileChooser widgets.

I've considered the option of making a menu, but I decided that we
should avoid it for latency reasons; applications might just have a
"Open recently used..." item which launches the dialog (many
applications already use a submenu, there's no difference for the user's
point of view), or we could add the ability to show recently used
resources into the GtkFileChooser, near where we put the bookmarks.

* Issues
How do we store this data and its associated metadata is tricky;  the
best way to improve interoperability should be to use a common data
format, which is XBEL (XML Bookmarks Exchange Language), an XML dialect
(which is already used by the KDE people, for instance).  I've tried and
proposed a GMarkup-like storage, but it was turned down - like the
current recent-files spec.

If we're going to use XBEL, we need a full XML parser (valid XBEL uses
namespaces for storing custom metadata);  I wrote something that could
parse it, using just Glib-Object and libxml2, but it would add libxml2
to our dependency chain.  It's not that bad: we already (indirectly)
depend on a XML parser (libexpat, needed by fontconfig), libxml2 is
portable and on a modern desktop most likely libxml2 is most likely
already installed by default.  I do not want to add Yet Another Library
on the dependency chain, but I also don't see major problems adding a
library like libxml2.

I've attached a prototype API for GtkRecentItem and GtkRecentManager
(already set to live in libegg, for the time being). Let me know what
you think of it.

Kind regards,
 Emmanuele.

-- 
Emmanuele Bassi <ebassi gmail com>
Web site: http://log.emmanuelebassi.net
/* gtkrecentitem.h - Gtk Recently Used Item
 *
 * Copyright (C) 2005 Emmanuele Bassi
 * All rights reserved
 *
 * Based on EggRecentItem, originally developed by James Willcox
 * <jwillcox gnome org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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 Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifndef __EGG_RECENT_ITEM_H__
#define __EGG_RECENT_ITEM_H__

#include <glib-object.h>
#include <time.h>

G_BEGIN_DECLS

#define EGG_TYPE_RECENT_ITEM		(egg_recent_item_get_type ())
#define EGG_RECENT_ITEM(obj)		(G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_RECENT_ITEM, EggRecentItem))
#define EGG_IS_RECENT_ITEM(obj)		(G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_RECENT_ITEM))

typedef struct _EggRecentItem		EggRecentItem;

GType			egg_recent_item_get_type		(void) G_GNUC_CONST;

EggRecentItem *		egg_recent_item_new			(void);
EggRecentItem *		egg_recent_item_new_for_uri		(const gchar   *uri);

void			egg_recent_item_set_uri			(EggRecentItem *recent_item,
								 const gchar   *uri);
G_CONST_RETURN gchar *	egg_recent_item_get_uri			(EggRecentItem *recent_item);
void			egg_recent_item_set_mime_type		(EggRecentItem *recent_item,
								 const gchar   *mime_type);
G_CONST_RETURN gchar *	egg_recent_item_get_mime_type		(EggRecentItem *recent_item);
void			egg_recent_item_set_display_name	(EggRecentItem *recent_item,
								 const gchar   *display_name);
G_CONST_RETURN gchar *	egg_recent_item_get_display_name	(EggRecentItem *recent_item);
void			egg_recent_item_set_description		(EggRecentItem *recent_item,
								 const gchar   *description);
G_CONST_RETURN gchar *	egg_recent_item_get_description		(EggRecentItem *recent_item);
time_t			egg_recent_item_added			(EggRecentItem *recent_item);
time_t			egg_recent_item_modified		(EggRecentItem *recent_item);
time_t			egg_recent_item_visited			(EggRecentItem *recent_item);
gboolean		egg_recent_item_register_application	(EggRecentItem *recent_item,
								 const gchar   *application_name,
								 const gchar   *application_exec);
gboolean		egg_recent_item_get_registration_count	(EggRecentItem *recent_item,
								 const gchar   *application_name,
								 gint          *count);
gboolean		egg_recent_item_get_registration_time	(EggRecentItem *recent_item,
								 const gchar   *application_name,
								 time_t         *time);
gboolean		egg_recent_item_has_application		(EggRecentItem *recent_item,
								 const gchar   *application_name);
gchar**			egg_recent_item_get_applications	(EggRecentItem *recent_item);
void			egg_recent_item_add_group		(EggRecentItem *recent_item,
								 const gchar   *group_name);
void			egg_recent_item_remove_group		(EggRecentItem *recent_item,
								 const gchar   *group_name);
gboolean		egg_recent_item_in_group		(EggRecentItem *recent_item,
								 const gchar   *group_name);
gchar**			egg_recent_item_get_groups		(EggRecentItem *recent_item);
gboolean		egg_recent_item_match			(EggRecentItem *recent_item_1,
								 EggRecentItem *recent_item_2);

G_END_DECLS

#endif /* ! __GTK_RECENT_ITEM_H__ */
/* eggrecentmanager.h - Manager for a list of recent items
 *
 * Copyright (C) 2005 Emmanuele Bassi
 * All rights reserved
 *
 * Based on EggRecentModel, originally developed by James Willcox
 * <jwillcox gnome org>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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 Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#ifndef __EGG_RECENT_MANAGER_H__
#define __EGG_RECENT_MANAGER_H__

#include <glib-object.h>
#include "eggrecentitem.h"

G_BEGIN_DECLS

#define EGG_TYPE_RECENT_MANAGER			(egg_recent_manager_get_type ())
#define EGG_RECENT_MANAGER			(G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_RECENT_MANAGER, EggRecentManager))
#define EGG_IS_RECENT_MANAGER			(G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_RECENT_MANAGER))
#define EGG_RECENT_MANAGER_CLASS(klass)		(G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_RECENT_MANAGER, EggRecentManagerClass))
#define EGG_IS_RECENT_MANAGER_CLASS(klass)	(G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_RECENT_MANAGER))
#define EGG_RECENT_MANAGER_GET_CLASS(obj)	(G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_RECENT_MANAGER, EggRecentManagerClass))

typedef struct _EggRecentManager	EggRecentManager;
typedef struct _EggRecentManagerClass	EggRecentManagerClass;

struct _EggRecentManager
{
  /*< private >*/
  GObject parent_instance;
};

struct _EggRecentManagerClass
{
  GObjectClass parent_class;

  void (* changed) 	(EggRecentManager *recent_manager);
};

/**
 * EggRecentManagerSort:
 * @EGG_RECENT_MANAGER_SORT_NONE: no sorting done
 * @EGG_RECENT_MANAGER_SORT_MRU: sort items, mort recently used first.
 * @EGG_RECENT_MANAGER_SORT_LRU: sort items, least recently user first.
 * @EGG_RECENT_MANAGER_SORT_CUSTOM: use custom sorting; you must provide a
 *   sorting function with egg_recent_manager_set_sort_func().
 * 
 * Used to specify the sorting to be done when retrieving the list of recently
 * used items with egg_recent_manager_get_list().
 **/
typedef enum
{
  EGG_RECENT_MANAGER_SORT_NONE = 0,
  EGG_RECENT_MANAGER_SORT_MRU,
  EGG_RECENT_MANAGER_SORT_LRU,
  EGG_RECENT_MANAGER_SORT_CUSTOM
} EggRecentManagerSort;

/* sorting function */
typedef void (*EggRecentManagerSortFunc) (EggRecentManager *recent_manager,
					  EggRecentItem    *a,
					  EggRecentItem    *b,
					  gpointer          user_data);

/* filtering function */
typedef gboolean (*EggRecentManagerFilterFunc) (EggRecentManager *recent_manager,
						EggRecentItem    *recent_item,
						gpointer          user_data);

#define	EGG_RECENTLY_USED_FILE	".recently-used"

GType		     egg_recent_manager_get_type        (void) G_GNUC_CONST;

EggRecentManager *   egg_recent_manager_new	        (EggRecentManagerSort        sort_type);
void		     egg_recent_manager_set_sort_type   (EggRecentManager           *recent_manager,
						         EggRecentManagerSort        sort_type);
EggRecentManagerSort egg_recent_manager_get_sort_type   (EggRecentManager           *recent_manager);
void		     egg_recent_manager_set_sort_func   (EggRecentManager           *recent_manager,
						         EggRecentManagerSortFunc    sort_func,
						         gpointer                    sort_data,
						         GDestroyNotify              data_destroy);
void		     egg_recent_manager_set_filter_func (EggRecentManager           *recent_manager,
							 EggRecentManagerFilterFunc  filter_func,
							 gpointer                    filter_data,
							 GDestroyNotify              data_destroy);
gboolean	     egg_recent_manager_add             (EggRecentManager           *recent_manager,
							 EggRecentItem              *recent_item);
gboolean	     egg_recent_manager_add_from_uri    (EggRecentManager           *recent_manager,
							 const gchar                *uri,
							 const gchar                *mime_type,
							 const gchar                *application_name,
							 const gchar                *application_exec,
							 const gchar                *group_name);
gboolean	     egg_recent_manager_remove          (EggRecentManager           *recent_manager,
							 EggRecentItem              *recent_item);
gboolean	     egg_recent_manager_remove_from_uri (EggRecentManager           *recent_manager,
							 const gchar                *uri);
EggRecentItem *      egg_recent_manager_get_from_uri    (EggRecentManager           *recent_manager,
							 const gchar                *uri);
GList*		     egg_recent_manager_get_list        (EggRecentManager           *recent_manager,
							 gint                        limit);
gint                 egg_recent_manager_get_count       (EggRecentManager           *recent_manager);
gboolean             egg_recent_manager_purge           (EggRecentManager           *recent_manager);

/* commodity filter functions */
void 		egg_recent_manager_set_mime_filter 	  (EggRecentManager *recent_manager,
							   const gchar      *mime_type);
void 		egg_recent_manager_set_group_filter 	  (EggRecentManager *recent_manager,
							   const gchar      *group_name);
void 		egg_recent_manager_set_application_filter (EggRecentManager *recent_manager,
							   const gchar      *application_name);
void		egg_recent_manager_set_age_filter	  (EggRecentManager *recent_manager,
							   gint              days);

/* this function emits a signal */
void		egg_recent_manager_changed	(EggRecentManager *recent_manager);

G_END_DECLS

#endif /* ! __EGG_RECENT_MANAGER_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]