commit 1351da1255832b104e4cff07f251ed87b9cd925b3983ac86e192b84481ed0d77 Author: OBS User unknown Date: Sun Jan 7 22:54:35 2007 +0000 OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/gnome-desktop?expand=0&rev=1 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..9b03811 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,23 @@ +## Default LFS +*.7z filter=lfs diff=lfs merge=lfs -text +*.bsp filter=lfs diff=lfs merge=lfs -text +*.bz2 filter=lfs diff=lfs merge=lfs -text +*.gem filter=lfs diff=lfs merge=lfs -text +*.gz filter=lfs diff=lfs merge=lfs -text +*.jar filter=lfs diff=lfs merge=lfs -text +*.lz filter=lfs diff=lfs merge=lfs -text +*.lzma filter=lfs diff=lfs merge=lfs -text +*.obscpio filter=lfs diff=lfs merge=lfs -text +*.oxt filter=lfs diff=lfs merge=lfs -text +*.pdf filter=lfs diff=lfs merge=lfs -text +*.png filter=lfs diff=lfs merge=lfs -text +*.rpm filter=lfs diff=lfs merge=lfs -text +*.tbz filter=lfs diff=lfs merge=lfs -text +*.tbz2 filter=lfs diff=lfs merge=lfs -text +*.tgz filter=lfs diff=lfs merge=lfs -text +*.ttf filter=lfs diff=lfs merge=lfs -text +*.txz filter=lfs diff=lfs merge=lfs -text +*.whl filter=lfs diff=lfs merge=lfs -text +*.xz filter=lfs diff=lfs merge=lfs -text +*.zip filter=lfs diff=lfs merge=lfs -text +*.zst filter=lfs diff=lfs merge=lfs -text diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57affb6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.osc diff --git a/X-KDE-SubstituteUID.dif b/X-KDE-SubstituteUID.dif new file mode 100644 index 0000000..2885fe8 --- /dev/null +++ b/X-KDE-SubstituteUID.dif @@ -0,0 +1,35 @@ +Index: libgnome-desktop/gnome-desktop-item.c +================================================================================ +--- libgnome-desktop/gnome-desktop-item.c ++++ libgnome-desktop/gnome-desktop-item.c +@@ -2085,8 +2085,16 @@ + + + /* make a new copy and get rid of spaces */ +- the_exec = g_alloca (strlen (exec) + 1); +- strcpy (the_exec, exec); ++ ++ if (gnome_desktop_item_get_boolean (item, GNOME_DESKTOP_ITEM_SUBSTITUTEUID) || ++ gnome_desktop_item_get_boolean (item, GNOME_DESKTOP_ITEM_ROOT_ONLY)) { ++ the_exec = g_alloca (strlen (exec) + sizeof ("gnomesu -- ")); ++ strcpy (the_exec, "gnomesu -- "); ++ strcat (the_exec, exec); ++ } else { ++ the_exec = g_alloca (strlen (exec) + 1); ++ strcpy (the_exec, exec); ++ } + + if ( ! strip_the_amp (the_exec)) { + g_set_error (error, +--- libgnome-desktop/libgnome/gnome-desktop-item.h ++++ libgnome-desktop/libgnome/gnome-desktop-item.h +@@ -96,7 +96,8 @@ + #define GNOME_DESKTOP_ITEM_SORT_ORDER "SortOrder" /* strings */ + #define GNOME_DESKTOP_ITEM_URL "URL" /* string */ + #define GNOME_DESKTOP_ITEM_DOC_PATH "X-GNOME-DocPath" /* string */ +- ++#define GNOME_DESKTOP_ITEM_SUBSTITUTEUID "X-KDE-SubstituteUID" /*boolean*/ ++#define GNOME_DESKTOP_ITEM_ROOT_ONLY "X-KDE-RootOnly" /*boolean*/ + /* The vfolder proposal */ + #define GNOME_DESKTOP_ITEM_CATEGORIES "Categories" /* string */ + #define GNOME_DESKTOP_ITEM_ONLY_SHOW_IN "OnlyShowIn" /* string */ diff --git a/gnome-desktop-2.16.1.tar.bz2 b/gnome-desktop-2.16.1.tar.bz2 new file mode 100644 index 0000000..5790fcb --- /dev/null +++ b/gnome-desktop-2.16.1.tar.bz2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56c7dbe56e6a9fbcd3730c55892734a9daff23a86d69f49a381a4c965732f9d1 +size 1230980 diff --git a/gnome-desktop-desktop.patch b/gnome-desktop-desktop.patch new file mode 100644 index 0000000..a13d4f8 --- /dev/null +++ b/gnome-desktop-desktop.patch @@ -0,0 +1,18 @@ +--- gnome-desktop-2.15.90/gnome-about/gnome-about.desktop.in.in ++++ gnome-desktop-2.15.90/gnome-about/gnome-about.desktop.in.in +@@ -1,12 +1,12 @@ + [Desktop Entry] + Encoding=UTF-8 + _Name=About GNOME +-_Comment=Learn more about GNOME ++_GenericName=Learn more about GNOME + Exec=gnome-about +-Icon=stock_about ++Icon=gnome-logo-icon-transparent + Terminal=false + Type=Application +-Categories=GNOME;GTK;Application;Core; ++Categories=GNOME;GTK;Application;Core;Documentation; + OnlyShowIn=GNOME; + X-GNOME-Bugzilla-Bugzilla=GNOME + X-GNOME-Bugzilla-Product=gnome-desktop diff --git a/gnome-desktop-recently-used-apps.patch b/gnome-desktop-recently-used-apps.patch new file mode 100644 index 0000000..6e359e2 --- /dev/null +++ b/gnome-desktop-recently-used-apps.patch @@ -0,0 +1,2701 @@ +diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/Makefile.am gnome-desktop-2.16.1/libgnome-desktop/Makefile.am +--- gnome-desktop-2.16.1-pristine/libgnome-desktop/Makefile.am 2006-06-04 07:53:09.000000000 -0400 ++++ gnome-desktop-2.16.1/libgnome-desktop/Makefile.am 2006-10-23 16:19:45.000000000 -0400 +@@ -17,9 +17,13 @@ lib_LTLIBRARIES = libgnome-desktop-2.la + + noinst_PROGRAMS = test-ditem test-hint test-ditem-edit + +-libgnome_desktop_2_la_SOURCES = \ +- gnome-desktop-item.c \ +- gnome-ditem-edit.c \ ++libgnome_desktop_2_la_SOURCES = \ ++ gnome-desktop-item.c \ ++ gnome-ditem-edit.c \ ++ egg-recent-item.h \ ++ egg-recent-item.c \ ++ egg-recent-model.h \ ++ egg-recent-model.c \ + gnome-hint.c + + libgnome_desktop_2_la_LIBADD = \ +diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-item.c gnome-desktop-2.16.1/libgnome-desktop/egg-recent-item.c +--- gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-item.c 1969-12-31 19:00:00.000000000 -0500 ++++ gnome-desktop-2.16.1/libgnome-desktop/egg-recent-item.c 2006-10-23 16:19:45.000000000 -0400 +@@ -0,0 +1,426 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ ++/* ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Authors: ++ * James Willcox ++ */ ++ ++ ++#include ++#include ++#include ++#include ++#include ++#include "egg-recent-item.h" ++ ++ ++ ++EggRecentItem * ++egg_recent_item_new (void) ++{ ++ EggRecentItem *item; ++ ++ item = g_new (EggRecentItem, 1); ++ ++ item->groups = NULL; ++ item->private_data = FALSE; ++ item->uri = NULL; ++ item->mime_type = NULL; ++ item->mime_type_is_explicit = FALSE; ++ ++ item->refcount = 1; ++ ++ return item; ++} ++ ++static void ++egg_recent_item_free (EggRecentItem *item) ++{ ++ if (item->uri) ++ g_free (item->uri); ++ ++ if (item->mime_type) ++ g_free (item->mime_type); ++ ++ if (item->groups) { ++ g_list_foreach (item->groups, (GFunc)g_free, NULL); ++ g_list_free (item->groups); ++ item->groups = NULL; ++ } ++ ++ g_free (item); ++} ++ ++EggRecentItem * ++egg_recent_item_ref (EggRecentItem *item) ++{ ++ item->refcount++; ++ return item; ++} ++ ++EggRecentItem * ++egg_recent_item_unref (EggRecentItem *item) ++{ ++ item->refcount--; ++ ++ if (item->refcount == 0) { ++ egg_recent_item_free (item); ++ } ++ ++ return item; ++} ++ ++ ++EggRecentItem * ++egg_recent_item_new_from_uri (const gchar *uri) ++{ ++ EggRecentItem *item; ++ ++ g_return_val_if_fail (uri != NULL, NULL); ++ ++ item = egg_recent_item_new (); ++ ++ if (!egg_recent_item_set_uri (item ,uri)) { ++ egg_recent_item_free (item); ++ return NULL; ++ } ++ ++ return item; ++} ++ ++static void ++egg_recent_item_update_mime_type (EggRecentItem *item) ++{ ++ if (!item->mime_type_is_explicit) { ++ g_free (item->mime_type); ++ item->mime_type = NULL; ++ ++ if (item->uri) ++ item->mime_type = gnome_vfs_get_mime_type (item->uri); ++ ++ if (!item->mime_type) ++ item->mime_type = g_strdup (GNOME_VFS_MIME_TYPE_UNKNOWN); ++ } ++} ++ ++gboolean ++egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri) ++{ ++ gchar *utf8_uri; ++ ++ /* if G_BROKEN_FILENAMES is not set, this should succede */ ++ if (g_utf8_validate (uri, -1, NULL)) { ++ item->uri = gnome_vfs_make_uri_from_input (uri); ++ } else { ++ utf8_uri = g_filename_to_utf8 (uri, -1, NULL, NULL, NULL); ++ ++ if (utf8_uri == NULL) { ++ g_warning ("Couldn't convert URI to UTF-8"); ++ return FALSE; ++ } ++ ++ if (g_utf8_validate (utf8_uri, -1, NULL)) { ++ item->uri = gnome_vfs_make_uri_from_input (utf8_uri); ++ } else { ++ g_free (utf8_uri); ++ return FALSE; ++ } ++ ++ g_free (utf8_uri); ++ } ++ ++ return TRUE; ++} ++ ++gchar * ++egg_recent_item_get_uri (const EggRecentItem *item) ++{ ++ return g_strdup (item->uri); ++} ++ ++G_CONST_RETURN gchar * ++egg_recent_item_peek_uri (const EggRecentItem *item) ++{ ++ return item->uri; ++} ++ ++gchar * ++egg_recent_item_get_uri_utf8 (const EggRecentItem *item) ++{ ++ /* this could fail, but it's not likely, since we've already done it ++ * once in set_uri() ++ */ ++ return g_filename_to_utf8 (item->uri, -1, NULL, NULL, NULL); ++} ++ ++gchar * ++egg_recent_item_get_uri_for_display (const EggRecentItem *item) ++{ ++ return gnome_vfs_format_uri_for_display (item->uri); ++} ++ ++/* Stolen from gnome_vfs_make_valid_utf8() */ ++static char * ++make_valid_utf8 (const char *name) ++{ ++ GString *string; ++ const char *remainder, *invalid; ++ int remaining_bytes, valid_bytes; ++ ++ string = NULL; ++ remainder = name; ++ remaining_bytes = name ? strlen (name) : 0; ++ ++ while (remaining_bytes != 0) { ++ if (g_utf8_validate (remainder, remaining_bytes, &invalid)) ++ break; ++ ++ valid_bytes = invalid - remainder; ++ ++ if (string == NULL) ++ string = g_string_sized_new (remaining_bytes); ++ ++ g_string_append_len (string, remainder, valid_bytes); ++ g_string_append_c (string, '?'); ++ ++ remaining_bytes -= valid_bytes + 1; ++ remainder = invalid + 1; ++ } ++ ++ if (string == NULL) ++ return g_strdup (name); ++ ++ g_string_append (string, remainder); ++/* g_string_append (string, _(" (invalid file name)")); */ ++ g_assert (g_utf8_validate (string->str, -1, NULL)); ++ ++ return g_string_free (string, FALSE); ++} ++ ++static gchar * ++get_uri_shortname_for_display (GnomeVFSURI *uri) ++{ ++ gchar *name; ++ gboolean validated; ++ ++ validated = FALSE; ++ name = gnome_vfs_uri_extract_short_name (uri); ++ ++ if (name == NULL) ++ { ++ name = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD); ++ } ++ else if (g_ascii_strcasecmp (uri->method_string, "file") == 0) ++ { ++ gchar *text_uri; ++ gchar *local_file; ++ text_uri = gnome_vfs_uri_to_string (uri, GNOME_VFS_URI_HIDE_PASSWORD); ++ local_file = gnome_vfs_get_local_path_from_uri (text_uri); ++ ++ if (local_file != NULL) ++ { ++ g_free (name); ++ name = g_filename_display_basename (local_file); ++ validated = TRUE; ++ } ++ ++ g_free (local_file); ++ g_free (text_uri); ++ } ++ else if (!gnome_vfs_uri_has_parent (uri)) ++ { ++ const gchar *method; ++ ++ method = uri->method_string; ++ ++ if (name == NULL || ++ strcmp (name, GNOME_VFS_URI_PATH_STR) == 0) ++ { ++ g_free (name); ++ name = g_strdup (method); ++ } ++ else ++ { ++ gchar *tmp; ++ ++ tmp = name; ++ name = g_strdup_printf ("%s: %s", method, name); ++ g_free (tmp); ++ } ++ } ++ ++ if (!validated && !g_utf8_validate (name, -1, NULL)) ++ { ++ gchar *utf8_name; ++ ++ utf8_name = make_valid_utf8 (name); ++ g_free (name); ++ name = utf8_name; ++ } ++ ++ return name; ++} ++ ++/** ++ * egg_recent_item_get_short_name: ++ * @item: an #EggRecentItem ++ * ++ * Computes a valid UTF-8 string that can be used as the name of the item in a ++ * menu or list. For example, calling this function on an item that refers to ++ * "file:///foo/bar.txt" will yield "bar.txt". ++ * ++ * Return value: A newly-allocated string in UTF-8 encoding; free it with ++ * g_free(). ++ **/ ++gchar * ++egg_recent_item_get_short_name (const EggRecentItem *item) ++{ ++ GnomeVFSURI *uri; ++ gchar *short_name; ++ ++ g_return_val_if_fail (item != NULL, NULL); ++ ++ if (item->uri == NULL) ++ return NULL; ++ ++ uri = gnome_vfs_uri_new (item->uri); ++ if (uri == NULL) ++ return NULL; ++ ++ short_name = get_uri_shortname_for_display (uri); ++ ++ gnome_vfs_uri_unref (uri); ++ ++ return short_name; ++} ++ ++void ++egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime) ++{ ++ g_free (item->mime_type); ++ item->mime_type = NULL; ++ ++ if (mime && mime[0]) { ++ item->mime_type_is_explicit = TRUE; ++ item->mime_type = g_strdup (mime); ++ } else { ++ item->mime_type_is_explicit = FALSE; ++ } ++} ++ ++gchar * ++egg_recent_item_get_mime_type (EggRecentItem *item) ++{ ++ egg_recent_item_update_mime_type (item); ++ ++ return g_strdup (item->mime_type); ++} ++ ++void ++egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp) ++{ ++ if (timestamp == (time_t) -1) ++ time (×tamp); ++ ++ item->timestamp = timestamp; ++} ++ ++time_t ++egg_recent_item_get_timestamp (const EggRecentItem *item) ++{ ++ return item->timestamp; ++} ++ ++G_CONST_RETURN GList * ++egg_recent_item_get_groups (const EggRecentItem *item) ++{ ++ return item->groups; ++} ++ ++gboolean ++egg_recent_item_in_group (const EggRecentItem *item, const gchar *group_name) ++{ ++ GList *tmp; ++ ++ tmp = item->groups; ++ while (tmp != NULL) { ++ gchar *val = (gchar *)tmp->data; ++ ++ if (strcmp (group_name, val) == 0) ++ return TRUE; ++ ++ tmp = tmp->next; ++ } ++ ++ return FALSE; ++} ++ ++void ++egg_recent_item_add_group (EggRecentItem *item, const gchar *group_name) ++{ ++ g_return_if_fail (group_name != NULL); ++ ++ if (!egg_recent_item_in_group (item, group_name)) ++ item->groups = g_list_append (item->groups, g_strdup (group_name)); ++} ++ ++void ++egg_recent_item_remove_group (EggRecentItem *item, const gchar *group_name) ++{ ++ GList *tmp; ++ ++ g_return_if_fail (group_name != NULL); ++ ++ tmp = item->groups; ++ while (tmp != NULL) { ++ gchar *val = (gchar *)tmp->data; ++ ++ if (strcmp (group_name, val) == 0) { ++ item->groups = g_list_remove (item->groups, ++ val); ++ g_free (val); ++ break; ++ } ++ ++ tmp = tmp->next; ++ } ++} ++ ++void ++egg_recent_item_set_private (EggRecentItem *item, gboolean priv) ++{ ++ item->private_data = priv; ++} ++ ++gboolean ++egg_recent_item_get_private (const EggRecentItem *item) ++{ ++ return item->private_data; ++} ++ ++GType ++egg_recent_item_get_type (void) ++{ ++ static GType boxed_type = 0; ++ ++ if (!boxed_type) { ++ boxed_type = g_boxed_type_register_static ("EggRecentItem", ++ (GBoxedCopyFunc)egg_recent_item_ref, ++ (GBoxedFreeFunc)egg_recent_item_unref); ++ } ++ ++ return boxed_type; ++} +diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-item.h gnome-desktop-2.16.1/libgnome-desktop/egg-recent-item.h +--- gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-item.h 1969-12-31 19:00:00.000000000 -0500 ++++ gnome-desktop-2.16.1/libgnome-desktop/egg-recent-item.h 2006-10-23 16:19:45.000000000 -0400 +@@ -0,0 +1,80 @@ ++ ++#ifndef __EGG_RECENT_ITEM_H__ ++#define __EGG_RECENT_ITEM_H__ ++ ++#include ++#include ++#include ++ ++G_BEGIN_DECLS ++ ++#define EGG_TYPE_RECENT_ITEM (egg_recent_item_get_type ()) ++ ++#define EGG_RECENT_ITEM_LIST_UNREF(list) \ ++ g_list_foreach (list, (GFunc)egg_recent_item_unref, NULL); \ ++ g_list_free (list); ++ ++typedef struct _EggRecentItem EggRecentItem; ++ ++struct _EggRecentItem { ++ /* do not access any of these directly */ ++ gchar *uri; ++ gchar *mime_type; ++ time_t timestamp; ++ ++ gboolean private_data; ++ ++ GList *groups; ++ ++ int refcount; ++ ++ guint mime_type_is_explicit : 1; ++}; ++ ++GType egg_recent_item_get_type (void) G_GNUC_CONST; ++ ++/* constructors */ ++EggRecentItem * egg_recent_item_new (void); ++ ++EggRecentItem * egg_recent_item_ref (EggRecentItem *item); ++EggRecentItem * egg_recent_item_unref (EggRecentItem *item); ++ ++/* automatically fetches the mime type, etc */ ++EggRecentItem * egg_recent_item_new_from_uri (const gchar *uri); ++ ++gboolean egg_recent_item_set_uri (EggRecentItem *item, const gchar *uri); ++gchar * egg_recent_item_get_uri (const EggRecentItem *item); ++gchar * egg_recent_item_get_uri_utf8 (const EggRecentItem *item); ++gchar * egg_recent_item_get_uri_for_display (const EggRecentItem *item); ++gchar * egg_recent_item_get_short_name (const EggRecentItem *item); ++ ++void egg_recent_item_set_mime_type (EggRecentItem *item, const gchar *mime); ++gchar * egg_recent_item_get_mime_type (EggRecentItem *item); ++ ++void egg_recent_item_set_timestamp (EggRecentItem *item, time_t timestamp); ++time_t egg_recent_item_get_timestamp (const EggRecentItem *item); ++ ++G_CONST_RETURN gchar *egg_recent_item_peek_uri (const EggRecentItem *item); ++ ++ ++/* groups */ ++G_CONST_RETURN GList * egg_recent_item_get_groups (const EggRecentItem *item); ++ ++gboolean egg_recent_item_in_group (const EggRecentItem *item, ++ const gchar *group_name); ++ ++void egg_recent_item_add_group (EggRecentItem *item, ++ const gchar *group_name); ++ ++void egg_recent_item_remove_group (EggRecentItem *item, ++ const gchar *group_name); ++ ++void egg_recent_item_set_private (EggRecentItem *item, ++ gboolean priv); ++ ++gboolean egg_recent_item_get_private (const EggRecentItem *item); ++ ++ ++G_END_DECLS ++ ++#endif /* __EGG_RECENT_ITEM_H__ */ +diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-model.c gnome-desktop-2.16.1/libgnome-desktop/egg-recent-model.c +--- gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-model.c 1969-12-31 19:00:00.000000000 -0500 ++++ gnome-desktop-2.16.1/libgnome-desktop/egg-recent-model.c 2006-10-23 16:19:45.000000000 -0400 +@@ -0,0 +1,1965 @@ ++/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ ++/* ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * Authors: ++ * James Willcox ++ */ ++ ++#ifdef HAVE_CONFIG_H ++#include ++#endif ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#define EGG_ENABLE_RECENT_FILES ++#include "egg-recent-model.h" ++#include "egg-recent-item.h" ++ ++#define EGG_RECENT_MODEL_FILE_PATH "/.recently-used" ++#define EGG_RECENT_MODEL_BUFFER_SIZE 8192 ++ ++#define EGG_RECENT_MODEL_MAX_ITEMS 500 ++#define EGG_RECENT_MODEL_DEFAULT_LIMIT 10 ++#define EGG_RECENT_MODEL_TIMEOUT_LENGTH 200 ++#define EGG_RECENT_MODEL_POLL_TIME 3 ++ ++/* needed for Darwin */ ++#if !HAVE_DECL_LOCKF ++int lockf (int filedes, int function, off_t size); ++#endif ++ ++#define EGG_RECENT_MODEL_KEY_DIR "/desktop/gnome/recent_files" ++#define EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY EGG_RECENT_MODEL_KEY_DIR "/default_limit" ++#define EGG_RECENT_MODEL_EXPIRE_KEY EGG_RECENT_MODEL_KEY_DIR "/expire" ++ ++struct _EggRecentModelPrivate { ++ GSList *mime_filter_values; /* list of mime types we allow */ ++ GSList *group_filter_values; /* list of groups we allow */ ++ GSList *scheme_filter_values; /* list of URI schemes we allow */ ++ ++ EggRecentModelSort sort_type; /* type of sorting to be done */ ++ ++ int limit; /* soft limit for length of the list */ ++ int expire_days; /* number of days to hold an item */ ++ ++ char *path; /* path to the file we store stuff in */ ++ ++ GHashTable *monitors; ++ ++ GnomeVFSMonitorHandle *monitor; ++ ++ GConfClient *client; ++ gboolean use_default_limit; ++ ++ guint limit_change_notify_id; ++ guint expiration_change_notify_id; ++ ++ guint changed_timeout; ++ guint poll_timeout; ++ time_t last_mtime; ++}; ++ ++/* signals */ ++enum { ++ CHANGED, ++ LAST_SIGNAL ++}; ++ ++static GType model_signals[LAST_SIGNAL] = { 0 }; ++ ++/* properties */ ++enum { ++ PROP_BOGUS, ++ PROP_MIME_FILTERS, ++ PROP_GROUP_FILTERS, ++ PROP_SCHEME_FILTERS, ++ PROP_SORT_TYPE, ++ PROP_LIMIT ++}; ++ ++typedef struct { ++ GSList *states; ++ GList *items; ++ EggRecentItem *current_item; ++} ParseInfo; ++ ++typedef enum { ++ STATE_START, ++ STATE_RECENT_FILES, ++ STATE_RECENT_ITEM, ++ STATE_URI, ++ STATE_MIME_TYPE, ++ STATE_TIMESTAMP, ++ STATE_PRIVATE, ++ STATE_GROUPS, ++ STATE_GROUP ++} ParseState; ++ ++typedef struct { ++ EggRecentModel *model; ++ GList *list; ++} ChangedData; ++ ++#define TAG_RECENT_FILES "RecentFiles" ++#define TAG_RECENT_ITEM "RecentItem" ++#define TAG_URI "URI" ++#define TAG_MIME_TYPE "Mime-Type" ++#define TAG_TIMESTAMP "Timestamp" ++#define TAG_PRIVATE "Private" ++#define TAG_GROUPS "Groups" ++#define TAG_GROUP "Group" ++ ++static void start_element_handler (GMarkupParseContext *context, ++ const gchar *element_name, ++ const gchar **attribute_names, ++ const gchar **attribute_values, ++ gpointer user_data, ++ GError **error); ++ ++static void end_element_handler (GMarkupParseContext *context, ++ const gchar *element_name, ++ gpointer user_data, ++ GError **error); ++ ++static void text_handler (GMarkupParseContext *context, ++ const gchar *text, ++ gsize text_len, ++ gpointer user_data, ++ GError **error); ++ ++static void error_handler (GMarkupParseContext *context, ++ GError *error, ++ gpointer user_data); ++ ++static GMarkupParser parser = {start_element_handler, end_element_handler, ++ text_handler, ++ NULL, ++ error_handler}; ++ ++static GObjectClass *parent_class; ++ ++static void egg_recent_model_clear_mime_filter (EggRecentModel *model); ++static void egg_recent_model_clear_group_filter (EggRecentModel *model); ++static void egg_recent_model_clear_scheme_filter (EggRecentModel *model); ++ ++static GObjectClass *parent_class; ++ ++static gboolean ++egg_recent_model_string_match (const GSList *list, const gchar *str) ++{ ++ const GSList *tmp; ++ ++ if (list == NULL || str == NULL) ++ return TRUE; ++ ++ tmp = list; ++ ++ while (tmp) { ++ if (g_pattern_match_string (tmp->data, str)) ++ return TRUE; ++ ++ tmp = tmp->next; ++ } ++ ++ return FALSE; ++} ++ ++static gboolean ++egg_recent_model_write_raw (EggRecentModel *model, FILE *file, ++ const gchar *content) ++{ ++ int len; ++ int fd; ++ struct stat sbuf; ++ ++ rewind (file); ++ ++ len = strlen (content); ++ fd = fileno (file); ++ ++ if (fstat (fd, &sbuf) < 0) ++ g_warning ("Couldn't stat XML document."); ++ ++ if ((off_t)len < sbuf.st_size) { ++ ftruncate (fd, len); ++ } ++ ++ if (fputs (content, file) == EOF) ++ return FALSE; ++ ++#ifndef G_OS_WIN32 ++ fsync (fd); ++#endif ++ rewind (file); ++ ++ return TRUE; ++} ++ ++static GList * ++egg_recent_model_delete_from_list (GList *list, ++ const gchar *uri) ++{ ++ GList *tmp; ++ ++ if (!uri) ++ return list; ++ ++ tmp = list; ++ ++ while (tmp) { ++ EggRecentItem *item = tmp->data; ++ GList *next; ++ ++ next = tmp->next; ++ ++ if (!strcmp (egg_recent_item_peek_uri (item), uri)) { ++ egg_recent_item_unref (item); ++ ++ list = g_list_remove_link (list, tmp); ++ g_list_free_1 (tmp); ++ } ++ ++ tmp = next; ++ } ++ ++ return list; ++} ++ ++static void ++egg_recent_model_add_new_groups (EggRecentItem *item, ++ EggRecentItem *upd_item) ++{ ++ const GList *tmp; ++ ++ tmp = egg_recent_item_get_groups (upd_item); ++ ++ while (tmp) { ++ char *group = tmp->data; ++ ++ if (!egg_recent_item_in_group (item, group)) ++ egg_recent_item_add_group (item, group); ++ ++ tmp = tmp->next; ++ } ++} ++ ++static gboolean ++egg_recent_model_update_item (GList *items, EggRecentItem *upd_item) ++{ ++ GList *tmp; ++ const char *uri; ++ ++ uri = egg_recent_item_peek_uri (upd_item); ++ ++ tmp = items; ++ ++ while (tmp) { ++ EggRecentItem *item = tmp->data; ++ ++ if (gnome_vfs_uris_match (egg_recent_item_peek_uri (item), uri)) { ++ egg_recent_item_set_timestamp (item, (time_t) -1); ++ ++ egg_recent_model_add_new_groups (item, upd_item); ++ ++ return TRUE; ++ } ++ ++ tmp = tmp->next; ++ } ++ ++ return FALSE; ++} ++ ++static gchar * ++egg_recent_model_read_raw (EggRecentModel *model, FILE *file) ++{ ++ GString *string; ++ char buf[EGG_RECENT_MODEL_BUFFER_SIZE]; ++ ++ rewind (file); ++ ++ string = g_string_new (NULL); ++ while (fgets (buf, EGG_RECENT_MODEL_BUFFER_SIZE, file)) { ++ string = g_string_append (string, buf); ++ } ++ ++ rewind (file); ++ ++ return g_string_free (string, FALSE); ++} ++ ++ ++ ++static ParseInfo * ++parse_info_init (void) ++{ ++ ParseInfo *retval; ++ ++ retval = g_new0 (ParseInfo, 1); ++ retval->states = g_slist_prepend (NULL, STATE_START); ++ retval->items = NULL; ++ ++ return retval; ++} ++ ++static void ++parse_info_free (ParseInfo *info) ++{ ++ g_slist_free (info->states); ++ g_free (info); ++} ++ ++static void ++push_state (ParseInfo *info, ++ ParseState state) ++{ ++ info->states = g_slist_prepend (info->states, GINT_TO_POINTER (state)); ++} ++ ++static void ++pop_state (ParseInfo *info) ++{ ++ g_return_if_fail (info->states != NULL); ++ ++ info->states = g_slist_remove (info->states, info->states->data); ++} ++ ++static ParseState ++peek_state (ParseInfo *info) ++{ ++ g_return_val_if_fail (info->states != NULL, STATE_START); ++ ++ return GPOINTER_TO_INT (info->states->data); ++} ++ ++#define ELEMENT_IS(name) (strcmp (element_name, (name)) == 0) ++ ++static gboolean ++valid_element (ParseInfo *info, ++ int valid_parent_state, ++ const gchar *element_name, ++ const gchar *valid_element, ++ GError **error) ++{ ++ if (peek_state (info) != valid_parent_state) { ++ g_set_error (error, ++ G_MARKUP_ERROR, ++ G_MARKUP_ERROR_INVALID_CONTENT, ++ "Unexpected tag '%s', tag '%s' expected", ++ element_name, valid_element); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ ++static void ++start_element_handler (GMarkupParseContext *context, ++ const gchar *element_name, ++ const gchar **attribute_names, ++ const gchar **attribute_values, ++ gpointer user_data, ++ GError **error) ++{ ++ ParseInfo *info = (ParseInfo *)user_data; ++ ++ if (ELEMENT_IS (TAG_RECENT_FILES)) ++ push_state (info, STATE_RECENT_FILES); ++ else if (ELEMENT_IS (TAG_RECENT_ITEM)) { ++ if (valid_element (info, STATE_RECENT_FILES, ++ TAG_RECENT_ITEM, TAG_RECENT_FILES, error)) { ++ info->current_item = egg_recent_item_new (); ++ push_state (info, STATE_RECENT_ITEM); ++ } ++ } else if (ELEMENT_IS (TAG_URI)) { ++ if (valid_element (info, STATE_RECENT_ITEM, ++ TAG_URI, TAG_RECENT_ITEM, error)) { ++ push_state (info, STATE_URI); ++ } ++ } else if (ELEMENT_IS (TAG_MIME_TYPE)) { ++ if (valid_element (info, STATE_RECENT_ITEM, ++ TAG_MIME_TYPE, TAG_RECENT_ITEM, error)) { ++ push_state (info, STATE_MIME_TYPE); ++ } ++ } else if (ELEMENT_IS (TAG_TIMESTAMP)) { ++ if (valid_element (info, STATE_RECENT_ITEM, ++ TAG_TIMESTAMP, TAG_RECENT_ITEM, error)) { ++ push_state (info, STATE_TIMESTAMP); ++ } ++ } else if (ELEMENT_IS (TAG_PRIVATE)) { ++ if (valid_element (info, STATE_RECENT_ITEM, ++ TAG_PRIVATE, TAG_RECENT_ITEM, error)) { ++ push_state (info, STATE_PRIVATE); ++ egg_recent_item_set_private (info->current_item, TRUE); ++ } ++ } else if (ELEMENT_IS (TAG_GROUPS)) { ++ if (valid_element (info, STATE_RECENT_ITEM, ++ TAG_GROUPS, TAG_RECENT_ITEM, error)) { ++ push_state (info, STATE_GROUPS); ++ } ++ } else if (ELEMENT_IS (TAG_GROUP)) { ++ if (valid_element (info, STATE_GROUPS, ++ TAG_GROUP, TAG_GROUPS, error)) { ++ push_state (info, STATE_GROUP); ++ } ++ } ++} ++ ++static gint ++list_compare_func_mru (gpointer a, gpointer b) ++{ ++ EggRecentItem *item_a = (EggRecentItem *)a; ++ EggRecentItem *item_b = (EggRecentItem *)b; ++ ++ return item_a->timestamp < item_b->timestamp; ++} ++ ++static gint ++list_compare_func_lru (gpointer a, gpointer b) ++{ ++ EggRecentItem *item_a = (EggRecentItem *)a; ++ EggRecentItem *item_b = (EggRecentItem *)b; ++ ++ return item_a->timestamp > item_b->timestamp; ++} ++ ++ ++ ++static void ++end_element_handler (GMarkupParseContext *context, ++ const gchar *element_name, ++ gpointer user_data, ++ GError **error) ++{ ++ ParseInfo *info = (ParseInfo *)user_data; ++ ++ switch (peek_state (info)) { ++ case STATE_RECENT_ITEM: ++ if (!info->current_item) { ++ g_warning ("No recent item found\n"); ++ break; ++ } ++ ++ if (!info->current_item->uri) { ++ g_warning ("Invalid item found\n"); ++ break; ++ } ++ ++ info->items = g_list_prepend (info->items, ++ info->current_item); ++ info->current_item = NULL; ++ break; ++ default: ++ break; ++ } ++ ++ pop_state (info); ++} ++ ++static void ++text_handler (GMarkupParseContext *context, ++ const gchar *text, ++ gsize text_len, ++ gpointer user_data, ++ GError **error) ++{ ++ ParseInfo *info = (ParseInfo *)user_data; ++ gchar *value; ++ ++ value = g_strndup (text, text_len); ++ ++ switch (peek_state (info)) { ++ case STATE_START: ++ case STATE_RECENT_FILES: ++ case STATE_RECENT_ITEM: ++ case STATE_PRIVATE: ++ case STATE_GROUPS: ++ break; ++ case STATE_URI: ++ egg_recent_item_set_uri (info->current_item, value); ++ break; ++ case STATE_MIME_TYPE: ++ egg_recent_item_set_mime_type (info->current_item, value); ++ break; ++ case STATE_TIMESTAMP: ++ egg_recent_item_set_timestamp (info->current_item, ++ (time_t)atoi (value)); ++ break; ++ case STATE_GROUP: ++ egg_recent_item_add_group (info->current_item, ++ text); ++ break; ++ } ++ ++ g_free (value); ++} ++ ++static void ++error_handler (GMarkupParseContext *context, ++ GError *error, ++ gpointer user_data) ++{ ++ g_warning ("Error in parse: %s", error->message); ++} ++ ++static void ++egg_recent_model_enforce_limit (GList *list, int limit) ++{ ++ int len; ++ GList *end; ++ ++ /* limit < 0 means unlimited */ ++ if (limit <= 0) ++ return; ++ ++ len = g_list_length (list); ++ ++ if (len > limit) { ++ GList *next; ++ ++ end = g_list_nth (list, limit-1); ++ next = end->next; ++ ++ end->next = NULL; ++ ++ EGG_RECENT_ITEM_LIST_UNREF (next); ++ } ++} ++ ++static GList * ++egg_recent_model_sort (EggRecentModel *model, GList *list) ++{ ++ switch (model->priv->sort_type) { ++ case EGG_RECENT_MODEL_SORT_MRU: ++ list = g_list_sort (list, ++ (GCompareFunc)list_compare_func_mru); ++ break; ++ case EGG_RECENT_MODEL_SORT_LRU: ++ list = g_list_sort (list, ++ (GCompareFunc)list_compare_func_lru); ++ break; ++ case EGG_RECENT_MODEL_SORT_NONE: ++ break; ++ } ++ ++ return list; ++} ++ ++static gboolean ++egg_recent_model_group_match (EggRecentItem *item, GSList *groups) ++{ ++ GSList *tmp; ++ ++ tmp = groups; ++ ++ while (tmp != NULL) { ++ const gchar * group = (const gchar *)tmp->data; ++ ++ if (egg_recent_item_in_group (item, group)) ++ return TRUE; ++ ++ tmp = tmp->next; ++ } ++ ++ return FALSE; ++} ++ ++static GList * ++egg_recent_model_filter (EggRecentModel *model, GList *list) ++{ ++ GList *newlist = NULL; ++ GList *l; ++ gchar *mime_type; ++ gchar *uri; ++ ++ g_return_val_if_fail (list != NULL, NULL); ++ ++ for (l = list; l != NULL ; l = l->next) { ++ EggRecentItem *item = (EggRecentItem *) l->data; ++ gboolean pass_mime_test = FALSE; ++ gboolean pass_group_test = FALSE; ++ gboolean pass_scheme_test = FALSE; ++ ++ g_assert (item != NULL); ++ ++ uri = egg_recent_item_get_uri (item); ++ ++ /* filter by mime type */ ++ if (model->priv->mime_filter_values != NULL) { ++ mime_type = egg_recent_item_get_mime_type (item); ++ ++ if (egg_recent_model_string_match ++ (model->priv->mime_filter_values, ++ mime_type)) ++ pass_mime_test = TRUE; ++ ++ g_free (mime_type); ++ } else ++ pass_mime_test = TRUE; ++ ++ /* filter by group */ ++ if (pass_mime_test && model->priv->group_filter_values != NULL) { ++ if (egg_recent_model_group_match ++ (item, model->priv->group_filter_values)) ++ pass_group_test = TRUE; ++ } else if (egg_recent_item_get_private (item)) { ++ pass_group_test = FALSE; ++ } else ++ pass_group_test = TRUE; ++ ++ /* filter by URI scheme */ ++ if (pass_mime_test && pass_group_test && ++ model->priv->scheme_filter_values != NULL) { ++ gchar *scheme; ++ ++ scheme = gnome_vfs_get_uri_scheme (uri); ++ ++ if (egg_recent_model_string_match ++ (model->priv->scheme_filter_values, scheme)) ++ pass_scheme_test = TRUE; ++ ++ g_free (scheme); ++ } else ++ pass_scheme_test = TRUE; ++ ++ if (pass_mime_test && pass_group_test && pass_scheme_test) ++ newlist = g_list_prepend (newlist, item); ++ else ++ egg_recent_item_unref (item); ++ ++ g_free (uri); ++ } ++ ++ g_list_free (list); ++ ++ return g_list_reverse (newlist); ++} ++ ++ ++ ++#if 0 ++static void ++egg_recent_model_monitor_list_cb (GnomeVFSMonitorHandle *handle, ++ const gchar *monitor_uri, ++ const gchar *info_uri, ++ GnomeVFSMonitorEventType event_type, ++ gpointer user_data) ++{ ++ EggRecentModel *model; ++ ++ model = EGG_RECENT_MODEL (user_data); ++ ++ if (event_type == GNOME_VFS_MONITOR_EVENT_DELETED) { ++ egg_recent_model_delete (model, monitor_uri); ++ g_hash_table_remove (model->priv->monitors, monitor_uri); ++ } ++} ++ ++ ++ ++static void ++egg_recent_model_monitor_list (EggRecentModel *model, GList *list) ++{ ++ GList *tmp; ++ ++ tmp = list; ++ while (tmp) { ++ EggRecentItem *item = (EggRecentItem *)tmp->data; ++ GnomeVFSMonitorHandle *handle; ++ GnomeVFSResult res; ++ gchar *uri; ++ ++ tmp = tmp->next; ++ ++ uri = egg_recent_item_get_uri (item); ++ if (g_hash_table_lookup (model->priv->monitors, uri)) { ++ /* already monitoring this one */ ++ g_free (uri); ++ continue; ++ } ++ ++ res = gnome_vfs_monitor_add (&handle, uri, ++ GNOME_VFS_MONITOR_FILE, ++ egg_recent_model_monitor_list_cb, ++ model); ++ ++ if (res == GNOME_VFS_OK) ++ g_hash_table_insert (model->priv->monitors, uri, handle); ++ else ++ g_free (uri); ++ } ++} ++#endif ++ ++ ++static gboolean ++egg_recent_model_changed_timeout (EggRecentModel *model) ++{ ++ model->priv->changed_timeout = 0; ++ ++ egg_recent_model_changed (model); ++ ++ return FALSE; ++} ++ ++static void ++egg_recent_model_monitor_cb (GnomeVFSMonitorHandle *handle, ++ const gchar *monitor_uri, ++ const gchar *info_uri, ++ GnomeVFSMonitorEventType event_type, ++ gpointer user_data) ++{ ++ EggRecentModel *model; ++ ++ g_return_if_fail (user_data != NULL); ++ g_return_if_fail (EGG_IS_RECENT_MODEL (user_data)); ++ model = EGG_RECENT_MODEL (user_data); ++ ++ if (event_type == GNOME_VFS_MONITOR_EVENT_CHANGED || ++ event_type == GNOME_VFS_MONITOR_EVENT_CREATED || ++ event_type == GNOME_VFS_MONITOR_EVENT_DELETED) { ++ if (model->priv->changed_timeout > 0) { ++ g_source_remove (model->priv->changed_timeout); ++ } ++ ++ model->priv->changed_timeout = g_timeout_add ( ++ EGG_RECENT_MODEL_TIMEOUT_LENGTH, ++ (GSourceFunc)egg_recent_model_changed_timeout, ++ model); ++ } ++} ++ ++static gboolean ++egg_recent_model_poll_timeout (gpointer user_data) ++{ ++ EggRecentModel *model; ++ struct stat stat_buf; ++ int stat_res; ++ ++ model = EGG_RECENT_MODEL (user_data); ++ stat_res = stat (model->priv->path, &stat_buf); ++ ++ if (!stat_res && stat_buf.st_mtime && ++ stat_buf.st_mtime != model->priv->last_mtime) { ++ model->priv->last_mtime = stat_buf.st_mtime; ++ ++ if (model->priv->changed_timeout > 0) ++ g_source_remove (model->priv->changed_timeout); ++ ++ model->priv->changed_timeout = g_timeout_add ( ++ EGG_RECENT_MODEL_TIMEOUT_LENGTH, ++ (GSourceFunc)egg_recent_model_changed_timeout, ++ model); ++ } ++ return TRUE; ++} ++ ++static void ++egg_recent_model_monitor (EggRecentModel *model, gboolean should_monitor) ++{ ++ if (should_monitor && model->priv->monitor == NULL) { ++ char *uri; ++ GnomeVFSResult result; ++ ++ uri = gnome_vfs_get_uri_from_local_path (model->priv->path); ++ ++ result = gnome_vfs_monitor_add (&model->priv->monitor, ++ uri, ++ GNOME_VFS_MONITOR_FILE, ++ egg_recent_model_monitor_cb, ++ model); ++ ++ g_free (uri); ++ ++ /* if the above fails, don't worry about it. ++ * local notifications will still happen ++ */ ++ if (result == GNOME_VFS_ERROR_NOT_SUPPORTED) { ++ if (model->priv->poll_timeout > 0) ++ g_source_remove (model->priv->poll_timeout); ++ ++ model->priv->poll_timeout = g_timeout_add ( ++ EGG_RECENT_MODEL_POLL_TIME * 1000, ++ egg_recent_model_poll_timeout, ++ model); ++ } ++ ++ } else if (!should_monitor && model->priv->monitor != NULL) { ++ gnome_vfs_monitor_cancel (model->priv->monitor); ++ model->priv->monitor = NULL; ++ } ++} ++ ++static void ++egg_recent_model_set_limit_internal (EggRecentModel *model, int limit) ++{ ++ model->priv->limit = limit; ++ ++ if (limit <= 0) ++ egg_recent_model_monitor (model, FALSE); ++ else { ++ egg_recent_model_monitor (model, TRUE); ++ egg_recent_model_changed (model); ++ } ++} ++ ++static GList * ++egg_recent_model_read (EggRecentModel *model, FILE *file) ++{ ++ GList *list=NULL; ++ gchar *content; ++ GMarkupParseContext *ctx; ++ ParseInfo *info; ++ GError *error; ++ ++ content = egg_recent_model_read_raw (model, file); ++ ++ if (strlen (content) <= 0) { ++ g_free (content); ++ return NULL; ++ } ++ ++ info = parse_info_init (); ++ ++ ctx = g_markup_parse_context_new (&parser, 0, info, NULL); ++ ++ error = NULL; ++ if (!g_markup_parse_context_parse (ctx, content, strlen (content), &error)) { ++ g_warning ("Error while parsing the .recently-used file: %s\n", ++ error->message); ++ ++ g_error_free (error); ++ parse_info_free (info); ++ ++ return NULL; ++ } ++ ++ error = NULL; ++ if (!g_markup_parse_context_end_parse (ctx, &error)) { ++ g_warning ("Unable to complete parsing of the .recently-used file: %s\n", ++ error->message); ++ ++ g_error_free (error); ++ g_markup_parse_context_free (ctx); ++ parse_info_free (info); ++ ++ return NULL; ++ } ++ ++ list = g_list_reverse (info->items); ++ ++ g_markup_parse_context_free (ctx); ++ parse_info_free (info); ++ g_free (content); ++ ++ return list; ++} ++ ++ ++static gboolean ++egg_recent_model_write (EggRecentModel *model, FILE *file, GList *list) ++{ ++ GString *string; ++ gchar *data; ++ EggRecentItem *item; ++ const GList *groups; ++ int i; ++ int ret; ++ ++ string = g_string_new ("\n"); ++ string = g_string_append (string, "<" TAG_RECENT_FILES ">\n"); ++ ++ i=0; ++ while (list) { ++ gchar *uri; ++ gchar *mime_type; ++ gchar *escaped_uri; ++ time_t timestamp; ++ item = (EggRecentItem *)list->data; ++ ++ ++ uri = egg_recent_item_get_uri_utf8 (item); ++ escaped_uri = g_markup_escape_text (uri, ++ strlen (uri)); ++ g_free (uri); ++ ++ mime_type = egg_recent_item_get_mime_type (item); ++ timestamp = egg_recent_item_get_timestamp (item); ++ ++ string = g_string_append (string, " <" TAG_RECENT_ITEM ">\n"); ++ ++ g_string_append_printf (string, ++ " <" TAG_URI ">%s\n", escaped_uri); ++ ++ if (mime_type) ++ g_string_append_printf (string, ++ " <" TAG_MIME_TYPE ">%s\n", mime_type); ++ else ++ g_string_append_printf (string, ++ " <" TAG_MIME_TYPE ">\n"); ++ ++ ++ g_string_append_printf (string, ++ " <" TAG_TIMESTAMP ">%d\n", (int)timestamp); ++ ++ if (egg_recent_item_get_private (item)) ++ string = g_string_append (string, ++ " <" TAG_PRIVATE "/>\n"); ++ ++ /* write the groups */ ++ string = g_string_append (string, ++ " <" TAG_GROUPS ">\n"); ++ groups = egg_recent_item_get_groups (item); ++ ++ if (groups == NULL && egg_recent_item_get_private (item)) ++ g_warning ("Item with URI \"%s\" marked as private, but" ++ " does not belong to any groups.\n", uri); ++ ++ while (groups) { ++ const gchar *group = (const gchar *)groups->data; ++ gchar *escaped_group; ++ ++ escaped_group = g_markup_escape_text (group, strlen(group)); ++ ++ g_string_append_printf (string, ++ " <" TAG_GROUP ">%s\n", ++ escaped_group); ++ ++ g_free (escaped_group); ++ ++ groups = groups->next; ++ } ++ ++ string = g_string_append (string, " \n"); ++ ++ string = g_string_append (string, ++ " \n"); ++ ++ g_free (mime_type); ++ g_free (escaped_uri); ++ ++ list = list->next; ++ i++; ++ } ++ ++ string = g_string_append (string, ""); ++ ++ data = g_string_free (string, FALSE); ++ ++ ret = egg_recent_model_write_raw (model, file, data); ++ ++ g_free (data); ++ ++ return ret; ++} ++ ++static FILE * ++egg_recent_model_open_file (EggRecentModel *model, ++ gboolean for_writing) ++{ ++ FILE *file; ++ mode_t prev_umask; ++ ++ file = fopen (model->priv->path, "r+"); ++ if (file == NULL && for_writing) { ++ /* be paranoid */ ++ prev_umask = umask (077); ++ ++ file = fopen (model->priv->path, "w+"); ++ ++ umask (prev_umask); ++ ++ g_return_val_if_fail (file != NULL, NULL); ++ } ++ ++ return file; ++} ++ ++static gboolean ++egg_recent_model_lock_file (FILE *file) ++{ ++#ifdef HAVE_LOCKF ++ int fd; ++ gint try = 5; ++ ++ rewind (file); ++ fd = fileno (file); ++ ++ /* Attempt to lock the file 5 times, ++ * waiting a random interval (< 1 second) ++ * in between attempts. ++ * We should really be doing asynchronous ++ * locking, but requires substantially larger ++ * changes. ++ */ ++ ++ while (try > 0) ++ { ++ int rand_interval; ++ ++ if (lockf (fd, F_TLOCK, 0) == 0) ++ return TRUE; ++ ++ rand_interval = 1 + (int) (10.0 * rand()/(RAND_MAX + 1.0)); ++ ++ g_usleep (100000 * rand_interval); ++ ++ --try; ++ } ++ ++ return FALSE; ++#else ++ return TRUE; ++#endif /* HAVE_LOCKF */ ++} ++ ++static gboolean ++egg_recent_model_unlock_file (FILE *file) ++{ ++#ifdef HAVE_LOCKF ++ int fd; ++ ++ rewind (file); ++ fd = fileno (file); ++ ++ return (lockf (fd, F_ULOCK, 0) == 0) ? TRUE : FALSE; ++#else ++ return TRUE; ++#endif /* HAVE_LOCKF */ ++} ++ ++static void ++egg_recent_model_finalize (GObject *object) ++{ ++ EggRecentModel *model = EGG_RECENT_MODEL (object); ++ ++ if (model->priv->changed_timeout > 0) { ++ g_source_remove (model->priv->changed_timeout); ++ } ++ ++ egg_recent_model_monitor (model, FALSE); ++ ++ ++ g_slist_foreach (model->priv->mime_filter_values, ++ (GFunc) g_pattern_spec_free, NULL); ++ g_slist_free (model->priv->mime_filter_values); ++ model->priv->mime_filter_values = NULL; ++ ++ g_slist_foreach (model->priv->scheme_filter_values, ++ (GFunc) g_pattern_spec_free, NULL); ++ g_slist_free (model->priv->scheme_filter_values); ++ model->priv->scheme_filter_values = NULL; ++ ++ g_slist_foreach (model->priv->group_filter_values, ++ (GFunc) g_free, NULL); ++ g_slist_free (model->priv->group_filter_values); ++ model->priv->group_filter_values = NULL; ++ ++ ++ if (model->priv->limit_change_notify_id) ++ gconf_client_notify_remove (model->priv->client, ++ model->priv->limit_change_notify_id); ++ model->priv->expiration_change_notify_id = 0; ++ ++ if (model->priv->expiration_change_notify_id) ++ gconf_client_notify_remove (model->priv->client, ++ model->priv->expiration_change_notify_id); ++ model->priv->expiration_change_notify_id = 0; ++ ++ g_object_unref (model->priv->client); ++ model->priv->client = NULL; ++ ++ ++ g_free (model->priv->path); ++ model->priv->path = NULL; ++ ++ g_hash_table_destroy (model->priv->monitors); ++ model->priv->monitors = NULL; ++ ++ if (model->priv->poll_timeout > 0) ++ g_source_remove (model->priv->poll_timeout); ++ model->priv->poll_timeout =0; ++ ++ g_free (model->priv); ++ ++ parent_class->finalize (object); ++} ++ ++static void ++egg_recent_model_set_property (GObject *object, ++ guint prop_id, ++ const GValue *value, ++ GParamSpec *pspec) ++{ ++ EggRecentModel *model = EGG_RECENT_MODEL (object); ++ ++ switch (prop_id) ++ { ++ case PROP_MIME_FILTERS: ++ if (model->priv->mime_filter_values != NULL) ++ egg_recent_model_clear_mime_filter (model); ++ ++ model->priv->mime_filter_values = ++ (GSList *)g_value_get_pointer (value); ++ break; ++ ++ case PROP_GROUP_FILTERS: ++ if (model->priv->group_filter_values != NULL) ++ egg_recent_model_clear_group_filter (model); ++ ++ model->priv->group_filter_values = ++ (GSList *)g_value_get_pointer (value); ++ break; ++ ++ case PROP_SCHEME_FILTERS: ++ if (model->priv->scheme_filter_values != NULL) ++ egg_recent_model_clear_scheme_filter (model); ++ ++ model->priv->scheme_filter_values = ++ (GSList *)g_value_get_pointer (value); ++ break; ++ ++ case PROP_SORT_TYPE: ++ model->priv->sort_type = g_value_get_int (value); ++ break; ++ ++ case PROP_LIMIT: ++ egg_recent_model_set_limit (model, ++ g_value_get_int (value)); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++egg_recent_model_get_property (GObject *object, ++ guint prop_id, ++ GValue *value, ++ GParamSpec *pspec) ++{ ++ EggRecentModel *model = EGG_RECENT_MODEL (object); ++ ++ switch (prop_id) ++ { ++ case PROP_MIME_FILTERS: ++ g_value_set_pointer (value, model->priv->mime_filter_values); ++ break; ++ ++ case PROP_GROUP_FILTERS: ++ g_value_set_pointer (value, model->priv->group_filter_values); ++ break; ++ ++ case PROP_SCHEME_FILTERS: ++ g_value_set_pointer (value, model->priv->scheme_filter_values); ++ break; ++ ++ case PROP_SORT_TYPE: ++ g_value_set_int (value, model->priv->sort_type); ++ break; ++ ++ case PROP_LIMIT: ++ g_value_set_int (value, model->priv->limit); ++ break; ++ ++ default: ++ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); ++ break; ++ } ++} ++ ++static void ++egg_recent_model_class_init (EggRecentModelClass * klass) ++{ ++ GObjectClass *object_class; ++ ++ parent_class = g_type_class_peek_parent (klass); ++ ++ parent_class = g_type_class_peek_parent (klass); ++ ++ object_class = G_OBJECT_CLASS (klass); ++ object_class->set_property = egg_recent_model_set_property; ++ object_class->get_property = egg_recent_model_get_property; ++ object_class->finalize = egg_recent_model_finalize; ++ ++ model_signals[CHANGED] = g_signal_new ("changed", ++ G_OBJECT_CLASS_TYPE (object_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (EggRecentModelClass, changed), ++ NULL, NULL, ++ g_cclosure_marshal_VOID__POINTER, ++ G_TYPE_NONE, 1, ++ G_TYPE_POINTER); ++ ++ ++ g_object_class_install_property (object_class, ++ PROP_MIME_FILTERS, ++ g_param_spec_pointer ("mime-filters", ++ "Mime Filters", ++ "List of mime types to be allowed.", ++ G_PARAM_READWRITE)); ++ ++ g_object_class_install_property (object_class, ++ PROP_GROUP_FILTERS, ++ g_param_spec_pointer ("group-filters", ++ "Group Filters", ++ "List of groups to be allowed.", ++ G_PARAM_READWRITE)); ++ ++ g_object_class_install_property (object_class, ++ PROP_SCHEME_FILTERS, ++ g_param_spec_pointer ("scheme-filters", ++ "Scheme Filters", ++ "List of URI schemes to be allowed.", ++ G_PARAM_READWRITE)); ++ ++ g_object_class_install_property (object_class, ++ PROP_SORT_TYPE, ++ g_param_spec_int ("sort-type", ++ "Sort Type", ++ "Type of sorting to be done.", ++ 0, EGG_RECENT_MODEL_SORT_NONE, ++ EGG_RECENT_MODEL_SORT_MRU, ++ G_PARAM_READWRITE)); ++ ++ g_object_class_install_property (object_class, ++ PROP_LIMIT, ++ g_param_spec_int ("limit", ++ "Limit", ++ "Max number of items allowed.", ++ -1, EGG_RECENT_MODEL_MAX_ITEMS, ++ EGG_RECENT_MODEL_DEFAULT_LIMIT, ++ G_PARAM_READWRITE)); ++ ++ klass->changed = NULL; ++} ++ ++ ++ ++static void ++egg_recent_model_limit_changed (GConfClient *client, guint cnxn_id, ++ GConfEntry *entry, gpointer user_data) ++{ ++ EggRecentModel *model; ++ GConfValue *value; ++ ++ model = EGG_RECENT_MODEL (user_data); ++ ++ g_return_if_fail (model != NULL); ++ ++ if (model->priv->use_default_limit == FALSE) ++ return; /* ignore this key */ ++ ++ /* the key was unset, and the schema has apparently failed */ ++ if (entry == NULL) ++ return; ++ ++ value = gconf_entry_get_value (entry); ++ ++ if (value->type != GCONF_VALUE_INT) { ++ g_warning ("Expected GConfValue of type integer, " ++ "got something else"); ++ } ++ ++ ++ egg_recent_model_set_limit_internal (model, gconf_value_get_int (value)); ++} ++ ++static void ++egg_recent_model_expiration_changed (GConfClient *client, guint cnxn_id, ++ GConfEntry *entry, gpointer user_data) ++{ ++ ++} ++ ++static void ++egg_recent_model_init (EggRecentModel * model) ++{ ++ if (!gnome_vfs_init ()) { ++ g_warning ("gnome-vfs initialization failed."); ++ return; ++ } ++ ++ ++ model->priv = g_new0 (EggRecentModelPrivate, 1); ++ ++ model->priv->path = g_strdup_printf ("%s" EGG_RECENT_MODEL_FILE_PATH, ++ g_get_home_dir ()); ++ ++ model->priv->mime_filter_values = NULL; ++ model->priv->group_filter_values = NULL; ++ model->priv->scheme_filter_values = NULL; ++ ++ model->priv->client = gconf_client_get_default (); ++ gconf_client_add_dir (model->priv->client, EGG_RECENT_MODEL_KEY_DIR, ++ GCONF_CLIENT_PRELOAD_ONELEVEL, NULL); ++ ++ model->priv->limit_change_notify_id = ++ gconf_client_notify_add (model->priv->client, ++ EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY, ++ egg_recent_model_limit_changed, ++ model, NULL, NULL); ++ ++ model->priv->expiration_change_notify_id = ++ gconf_client_notify_add (model->priv->client, ++ EGG_RECENT_MODEL_EXPIRE_KEY, ++ egg_recent_model_expiration_changed, ++ model, NULL, NULL); ++ ++ model->priv->expire_days = gconf_client_get_int ( ++ model->priv->client, ++ EGG_RECENT_MODEL_EXPIRE_KEY, ++ NULL); ++ ++#if 0 ++ /* keep this out, for now */ ++ model->priv->limit = gconf_client_get_int ( ++ model->priv->client, ++ EGG_RECENT_MODEL_DEFAULT_LIMIT_KEY, NULL); ++ model->priv->use_default_limit = TRUE; ++#endif ++ model->priv->limit = EGG_RECENT_MODEL_DEFAULT_LIMIT; ++ model->priv->use_default_limit = FALSE; ++ ++ model->priv->monitors = g_hash_table_new_full ( ++ g_str_hash, g_str_equal, ++ (GDestroyNotify) g_free, ++ (GDestroyNotify) gnome_vfs_monitor_cancel); ++ ++ model->priv->monitor = NULL; ++ model->priv->poll_timeout = 0; ++ model->priv->last_mtime = 0; ++ egg_recent_model_monitor (model, TRUE); ++} ++ ++ ++/** ++ * egg_recent_model_new: ++ * @sort: the type of sorting to use ++ * @limit: maximum number of items in the list ++ * ++ * This creates a new EggRecentModel object. ++ * ++ * Returns: a EggRecentModel object ++ */ ++EggRecentModel * ++egg_recent_model_new (EggRecentModelSort sort) ++{ ++ EggRecentModel *model; ++ ++ model = EGG_RECENT_MODEL (g_object_new (egg_recent_model_get_type (), ++ "sort-type", sort, NULL)); ++ ++ g_return_val_if_fail (model, NULL); ++ ++ return model; ++} ++ ++/** ++ * egg_recent_model_add_full: ++ * @model: A EggRecentModel object. ++ * @item: A EggRecentItem ++ * ++ * This function adds an item to the list of recently used URIs. ++ * ++ * Returns: gboolean ++ */ ++gboolean ++egg_recent_model_add_full (EggRecentModel * model, EggRecentItem *item) ++{ ++ FILE *file; ++ GList *list = NULL; ++ gboolean ret = FALSE; ++ gboolean updated = FALSE; ++ char *uri; ++ time_t t; ++ ++ g_return_val_if_fail (model != NULL, FALSE); ++ g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE); ++ ++ uri = egg_recent_item_get_uri (item); ++ if (strncmp (uri, "recent-files://", strlen ("recent-files://")) == 0) { ++ g_free (uri); ++ return FALSE; ++ } else { ++ g_free (uri); ++ } ++ ++ file = egg_recent_model_open_file (model, TRUE); ++ g_return_val_if_fail (file != NULL, FALSE); ++ ++ time (&t); ++ egg_recent_item_set_timestamp (item, t); ++ ++ if (egg_recent_model_lock_file (file)) { ++ ++ /* read existing stuff */ ++ list = egg_recent_model_read (model, file); ++ ++ /* if it's already there, we just update it */ ++ updated = egg_recent_model_update_item (list, item); ++ ++ if (!updated) { ++ list = g_list_prepend (list, item); ++ ++ egg_recent_model_enforce_limit (list, ++ EGG_RECENT_MODEL_MAX_ITEMS); ++ } ++ ++ /* write new stuff */ ++ if (!egg_recent_model_write (model, file, list)) ++ g_warning ("Write failed: %s", strerror (errno)); ++ ++ if (!updated) ++ list = g_list_remove (list, item); ++ ++ EGG_RECENT_ITEM_LIST_UNREF (list); ++ ret = TRUE; ++ } else { ++ g_warning ("Failed to lock: %s", strerror (errno)); ++ fclose (file); ++ return FALSE; ++ } ++ ++ if (!egg_recent_model_unlock_file (file)) ++ g_warning ("Failed to unlock: %s", strerror (errno)); ++ ++ fclose (file); ++ ++ if (model->priv->monitor == NULL) { ++ /* since monitoring isn't working, at least give a ++ * local notification ++ */ ++ egg_recent_model_changed (model); ++ } ++ ++ return ret; ++} ++ ++/** ++ * egg_recent_model_add: ++ * @model: A EggRecentModel object. ++ * @uri: A string URI ++ * ++ * This function adds an item to the list of recently used URIs. ++ * ++ * Returns: gboolean ++ */ ++gboolean ++egg_recent_model_add (EggRecentModel *model, const gchar *uri) ++{ ++ EggRecentItem *item; ++ gboolean ret = FALSE; ++ ++ g_return_val_if_fail (model != NULL, FALSE); ++ g_return_val_if_fail (uri != NULL, FALSE); ++ ++ item = egg_recent_item_new_from_uri (uri); ++ ++ g_return_val_if_fail (item != NULL, FALSE); ++ ++ ret = egg_recent_model_add_full (model, item); ++ ++ egg_recent_item_unref (item); ++ ++ return ret; ++} ++ ++ ++ ++/** ++ * egg_recent_model_delete: ++ * @model: A EggRecentModel object. ++ * @uri: The URI you want to delete. ++ * ++ * This function deletes a URI from the file of recently used URIs. ++ * ++ * Returns: gboolean ++ */ ++gboolean ++egg_recent_model_delete (EggRecentModel * model, const gchar * uri) ++{ ++ FILE *file; ++ GList *list; ++ unsigned int length; ++ gboolean ret = FALSE; ++ ++ g_return_val_if_fail (model != NULL, FALSE); ++ g_return_val_if_fail (EGG_IS_RECENT_MODEL (model), FALSE); ++ g_return_val_if_fail (uri != NULL, FALSE); ++ ++ file = egg_recent_model_open_file (model, TRUE); ++ g_return_val_if_fail (file != NULL, FALSE); ++ ++ if (egg_recent_model_lock_file (file)) { ++ list = egg_recent_model_read (model, file); ++ ++ if (list == NULL) ++ goto out; ++ ++ length = g_list_length (list); ++ ++ list = egg_recent_model_delete_from_list (list, uri); ++ ++ if (length == g_list_length (list)) { ++ /* nothing was deleted */ ++ EGG_RECENT_ITEM_LIST_UNREF (list); ++ } else { ++ egg_recent_model_write (model, file, list); ++ EGG_RECENT_ITEM_LIST_UNREF (list); ++ ret = TRUE; ++ ++ } ++ } else { ++ g_warning ("Failed to lock: %s", strerror (errno)); ++ return FALSE; ++ } ++ ++out: ++ ++ if (!egg_recent_model_unlock_file (file)) ++ g_warning ("Failed to unlock: %s", strerror (errno)); ++ ++ fclose (file); ++ ++ g_hash_table_remove (model->priv->monitors, uri); ++ ++ if (model->priv->monitor == NULL && ret) { ++ /* since monitoring isn't working, at least give a ++ * local notification ++ */ ++ egg_recent_model_changed (model); ++ } ++ ++ return ret; ++} ++ ++ ++/** ++ * egg_recent_model_get_list: ++ * @model: A EggRecentModel object. ++ * ++ * This function gets the current contents of the file ++ * ++ * Returns: a GList ++ */ ++GList * ++egg_recent_model_get_list (EggRecentModel *model) ++{ ++ FILE *file; ++ GList *list = NULL; ++ ++ file = egg_recent_model_open_file (model, FALSE); ++ if (file == NULL) ++ return NULL; ++ ++ if (egg_recent_model_lock_file (file)) ++ list = egg_recent_model_read (model, file); ++ else { ++ g_warning ("Failed to lock: %s", strerror (errno)); ++ fclose (file); ++ return NULL; ++ } ++ ++ if (!egg_recent_model_unlock_file (file)) ++ g_warning ("Failed to unlock: %s", strerror (errno)); ++ ++ if (list != NULL) { ++ list = egg_recent_model_filter (model, list); ++ list = egg_recent_model_sort (model, list); ++ ++ egg_recent_model_enforce_limit (list, model->priv->limit); ++ } ++ ++ fclose (file); ++ ++ return list; ++} ++ ++ ++ ++/** ++ * egg_recent_model_set_limit: ++ * @model: A EggRecentModel object. ++ * @limit: The maximum length of the list ++ * ++ * This function sets the maximum length of the list. Note: This only affects ++ * the length of the list emitted in the "changed" signal, not the list stored ++ * on disk. ++ * ++ * Returns: void ++ */ ++void ++egg_recent_model_set_limit (EggRecentModel *model, int limit) ++{ ++ model->priv->use_default_limit = FALSE; ++ ++ egg_recent_model_set_limit_internal (model, limit); ++} ++ ++/** ++ * egg_recent_model_get_limit: ++ * @model: A EggRecentModel object. ++ * ++ * This function gets the maximum length of the list. ++ * ++ * Returns: int ++ */ ++int ++egg_recent_model_get_limit (EggRecentModel *model) ++{ ++ return model->priv->limit; ++} ++ ++ ++/** ++ * egg_recent_model_clear: ++ * @model: A EggRecentModel object. ++ * ++ * This function clears the contents of the file ++ * ++ * Returns: void ++ */ ++void ++egg_recent_model_clear (EggRecentModel *model) ++{ ++ FILE *file; ++ int fd; ++ ++ file = egg_recent_model_open_file (model, TRUE); ++ g_return_if_fail (file != NULL); ++ ++ fd = fileno (file); ++ ++ if (egg_recent_model_lock_file (file)) { ++ ftruncate (fd, 0); ++ } else { ++ g_warning ("Failed to lock: %s", strerror (errno)); ++ return; ++ } ++ ++ if (!egg_recent_model_unlock_file (file)) ++ g_warning ("Failed to unlock: %s", strerror (errno)); ++ ++ fclose (file); ++ ++ if (model->priv->monitor == NULL) { ++ /* since monitoring isn't working, at least give a ++ * local notification ++ */ ++ egg_recent_model_changed (model); ++ } ++} ++ ++static void ++egg_recent_model_clear_mime_filter (EggRecentModel *model) ++{ ++ g_return_if_fail (model != NULL); ++ ++ if (model->priv->mime_filter_values != NULL) { ++ g_slist_foreach (model->priv->mime_filter_values, ++ (GFunc) g_pattern_spec_free, NULL); ++ g_slist_free (model->priv->mime_filter_values); ++ model->priv->mime_filter_values = NULL; ++ } ++} ++ ++/** ++ * egg_recent_model_set_filter_mime_types: ++ * @model: A EggRecentModel object. ++ * ++ * Sets which mime types are allowed in the list. ++ * ++ * Returns: void ++ */ ++void ++egg_recent_model_set_filter_mime_types (EggRecentModel *model, ++ ...) ++{ ++ va_list valist; ++ GSList *list = NULL; ++ gchar *str; ++ ++ g_return_if_fail (model != NULL); ++ ++ egg_recent_model_clear_mime_filter (model); ++ ++ va_start (valist, model); ++ ++ str = va_arg (valist, gchar*); ++ ++ while (str != NULL) { ++ list = g_slist_prepend (list, g_pattern_spec_new (str)); ++ ++ str = va_arg (valist, gchar*); ++ } ++ ++ va_end (valist); ++ ++ model->priv->mime_filter_values = list; ++} ++ ++static void ++egg_recent_model_clear_group_filter (EggRecentModel *model) ++{ ++ g_return_if_fail (model != NULL); ++ ++ if (model->priv->group_filter_values != NULL) { ++ g_slist_foreach (model->priv->group_filter_values, (GFunc)g_free, NULL); ++ g_slist_free (model->priv->group_filter_values); ++ model->priv->group_filter_values = NULL; ++ } ++} ++ ++/** ++ * egg_recent_model_set_filter_groups: ++ * @model: A EggRecentModel object. ++ * ++ * Sets which groups are allowed in the list. ++ * ++ * Returns: void ++ */ ++void ++egg_recent_model_set_filter_groups (EggRecentModel *model, ++ ...) ++{ ++ va_list valist; ++ GSList *list = NULL; ++ gchar *str; ++ ++ g_return_if_fail (model != NULL); ++ ++ egg_recent_model_clear_group_filter (model); ++ ++ va_start (valist, model); ++ ++ str = va_arg (valist, gchar*); ++ ++ while (str != NULL) { ++ list = g_slist_prepend (list, g_strdup (str)); ++ ++ str = va_arg (valist, gchar*); ++ } ++ ++ va_end (valist); ++ ++ model->priv->group_filter_values = list; ++} ++ ++static void ++egg_recent_model_clear_scheme_filter (EggRecentModel *model) ++{ ++ g_return_if_fail (model != NULL); ++ ++ if (model->priv->scheme_filter_values != NULL) { ++ g_slist_foreach (model->priv->scheme_filter_values, ++ (GFunc) g_pattern_spec_free, NULL); ++ g_slist_free (model->priv->scheme_filter_values); ++ model->priv->scheme_filter_values = NULL; ++ } ++} ++ ++/** ++ * egg_recent_model_set_filter_uri_schemes: ++ * @model: A EggRecentModel object. ++ * ++ * Sets which URI schemes (file, http, ftp, etc) are allowed in the list. ++ * ++ * Returns: void ++ */ ++void ++egg_recent_model_set_filter_uri_schemes (EggRecentModel *model, ...) ++{ ++ va_list valist; ++ GSList *list = NULL; ++ gchar *str; ++ ++ g_return_if_fail (model != NULL); ++ ++ egg_recent_model_clear_scheme_filter (model); ++ ++ va_start (valist, model); ++ ++ str = va_arg (valist, gchar*); ++ ++ while (str != NULL) { ++ list = g_slist_prepend (list, g_pattern_spec_new (str)); ++ ++ str = va_arg (valist, gchar*); ++ } ++ ++ va_end (valist); ++ ++ model->priv->scheme_filter_values = list; ++} ++ ++/** ++ * egg_recent_model_set_sort: ++ * @model: A EggRecentModel object. ++ * @sort: A EggRecentModelSort type ++ * ++ * Sets the type of sorting to be used. ++ * ++ * Returns: void ++ */ ++void ++egg_recent_model_set_sort (EggRecentModel *model, ++ EggRecentModelSort sort) ++{ ++ g_return_if_fail (model != NULL); ++ ++ model->priv->sort_type = sort; ++} ++ ++/** ++ * egg_recent_model_changed: ++ * @model: A EggRecentModel object. ++ * ++ * This function causes a "changed" signal to be emitted. ++ * ++ * Returns: void ++ */ ++void ++egg_recent_model_changed (EggRecentModel *model) ++{ ++ GList *list = NULL; ++ ++ if (model->priv->limit > 0) { ++ list = egg_recent_model_get_list (model); ++ /* egg_recent_model_monitor_list (model, list); */ ++ ++ g_signal_emit (G_OBJECT (model), model_signals[CHANGED], 0, ++ list); ++ } ++ ++ if (list) ++ EGG_RECENT_ITEM_LIST_UNREF (list); ++} ++ ++static void ++egg_recent_model_remove_expired_list (EggRecentModel *model, GList *list) ++{ ++ time_t current_time; ++ time_t day_seconds; ++ ++ time (¤t_time); ++ day_seconds = model->priv->expire_days*24*60*60; ++ ++ while (list != NULL) { ++ EggRecentItem *item = list->data; ++ time_t timestamp; ++ ++ timestamp = egg_recent_item_get_timestamp (item); ++ ++ if ((timestamp+day_seconds) < current_time) { ++ gchar *uri = egg_recent_item_get_uri (item); ++ egg_recent_model_delete (model, uri); ++ ++ g_strdup (uri); ++ } ++ ++ list = list->next; ++ } ++} ++ ++ ++/** ++ * egg_recent_model_remove_expired: ++ * @model: A EggRecentModel object. ++ * ++ * Goes through the entire list, and removes any items that are older than ++ * the user-specified expiration period. ++ * ++ * Returns: void ++ */ ++void ++egg_recent_model_remove_expired (EggRecentModel *model) ++{ ++ FILE *file; ++ GList *list=NULL; ++ ++ g_return_if_fail (model != NULL); ++ ++ file = egg_recent_model_open_file (model, FALSE); ++ if (file == NULL) ++ return; ++ ++ if (egg_recent_model_lock_file (file)) { ++ list = egg_recent_model_read (model, file); ++ ++ } else { ++ g_warning ("Failed to lock: %s", strerror (errno)); ++ return; ++ } ++ ++ if (!egg_recent_model_unlock_file (file)) ++ g_warning ("Failed to unlock: %s", strerror (errno)); ++ ++ if (list != NULL) { ++ egg_recent_model_remove_expired_list (model, list); ++ EGG_RECENT_ITEM_LIST_UNREF (list); ++ } ++ ++ fclose (file); ++} ++ ++/** ++ * egg_recent_model_get_type: ++ * ++ * This returns a GType representing a EggRecentModel object. ++ * ++ * Returns: a GType ++ */ ++GType ++egg_recent_model_get_type (void) ++{ ++ static GType egg_recent_model_type = 0; ++ ++ if(!egg_recent_model_type) { ++ static const GTypeInfo egg_recent_model_info = { ++ sizeof (EggRecentModelClass), ++ NULL, /* base init */ ++ NULL, /* base finalize */ ++ (GClassInitFunc)egg_recent_model_class_init, /* class init */ ++ NULL, /* class finalize */ ++ NULL, /* class data */ ++ sizeof (EggRecentModel), ++ 0, ++ (GInstanceInitFunc) egg_recent_model_init ++ }; ++ ++ egg_recent_model_type = g_type_register_static (G_TYPE_OBJECT, ++ "EggRecentModel", ++ &egg_recent_model_info, 0); ++ } ++ ++ return egg_recent_model_type; ++} ++ +diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-model.h gnome-desktop-2.16.1/libgnome-desktop/egg-recent-model.h +--- gnome-desktop-2.16.1-pristine/libgnome-desktop/egg-recent-model.h 1969-12-31 19:00:00.000000000 -0500 ++++ gnome-desktop-2.16.1/libgnome-desktop/egg-recent-model.h 2006-10-23 16:19:45.000000000 -0400 +@@ -0,0 +1,84 @@ ++/* vim: set sw=8: -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ ++#ifndef __EGG_RECENT_MODEL_H__ ++#define __EGG_RECENT_MODEL_H__ ++ ++#include "egg-recent-item.h" ++ ++#ifndef EGG_ENABLE_RECENT_FILES ++#error "EggRecent has been DEPRECATED by the new recent files code inside GTK 2.9.0. Use EGG_ENABLE_RECENT_FILES to compile this code, but you are using it at your own risk." ++#endif ++ ++G_BEGIN_DECLS ++ ++#define EGG_TYPE_RECENT_MODEL (egg_recent_model_get_type ()) ++#define EGG_RECENT_MODEL(obj) G_TYPE_CHECK_INSTANCE_CAST (obj, EGG_TYPE_RECENT_MODEL, EggRecentModel) ++#define EGG_RECENT_MODEL_CLASS(klass) G_TYPE_CHECK_CLASS_CAST (klass, EGG_TYPE_RECENT_MODEL, EggRecentModelClass) ++#define EGG_IS_RECENT_MODEL(obj) G_TYPE_CHECK_INSTANCE_TYPE (obj, egg_recent_model_get_type ()) ++ ++typedef struct _EggRecentModel EggRecentModel; ++typedef struct _EggRecentModelPrivate EggRecentModelPrivate; ++typedef struct _EggRecentModelClass EggRecentModelClass; ++ ++struct _EggRecentModel { ++ GObject parent_instance; ++ ++ EggRecentModelPrivate *priv; ++}; ++ ++struct _EggRecentModelClass { ++ GObjectClass parent_class; ++ ++ void (*changed) (EggRecentModel *model, GList *list); ++}; ++ ++typedef enum { ++ EGG_RECENT_MODEL_SORT_MRU, ++ EGG_RECENT_MODEL_SORT_LRU, ++ EGG_RECENT_MODEL_SORT_NONE ++} EggRecentModelSort; ++ ++ ++/* Standard group names */ ++#define EGG_RECENT_GROUP_LAUNCHERS "Launchers" ++ ++ ++GType egg_recent_model_get_type (void); ++ ++/* constructors */ ++EggRecentModel * egg_recent_model_new (EggRecentModelSort sort); ++ ++/* public methods */ ++void egg_recent_model_set_filter_mime_types (EggRecentModel *model, ++ ...); ++ ++void egg_recent_model_set_filter_groups (EggRecentModel *model, ...); ++ ++void egg_recent_model_set_filter_uri_schemes (EggRecentModel *model, ++ ...); ++ ++void egg_recent_model_set_sort (EggRecentModel *model, ++ EggRecentModelSort sort); ++ ++gboolean egg_recent_model_add_full (EggRecentModel *model, ++ EggRecentItem *item); ++ ++gboolean egg_recent_model_add (EggRecentModel *model, ++ const gchar *uri); ++ ++gboolean egg_recent_model_delete (EggRecentModel *model, ++ const gchar *uri); ++ ++void egg_recent_model_clear (EggRecentModel *model); ++ ++GList * egg_recent_model_get_list (EggRecentModel *model); ++ ++void egg_recent_model_changed (EggRecentModel *model); ++ ++void egg_recent_model_set_limit (EggRecentModel *model, int limit); ++int egg_recent_model_get_limit (EggRecentModel *model); ++ ++void egg_recent_model_remove_expired (EggRecentModel *model); ++ ++G_END_DECLS ++ ++#endif /* __EGG_RECENT_MODEL_H__ */ +diff -uprN gnome-desktop-2.16.1-pristine/libgnome-desktop/gnome-desktop-item.c gnome-desktop-2.16.1/libgnome-desktop/gnome-desktop-item.c +--- gnome-desktop-2.16.1-pristine/libgnome-desktop/gnome-desktop-item.c 2006-08-05 07:59:08.000000000 -0400 ++++ gnome-desktop-2.16.1/libgnome-desktop/gnome-desktop-item.c 2006-10-23 17:15:41.000000000 -0400 +@@ -59,6 +59,16 @@ + #include + #endif + ++#include ++#if GTK_CHECK_VERSION (2,10,0) ++# define USE_GTK_RECENT_MANAGER ++# include ++#else ++# include "egg-recent-item.h" ++# define EGG_ENABLE_RECENT_FILES ++# include "egg-recent-model.h" ++#endif ++ + #define sure_string(s) ((s)!=NULL?(s):"") + + struct _GnomeDesktopItem { +@@ -128,6 +138,8 @@ static gboolean ditem_save (Gno + const char *uri, + GError **error); + ++static void update_recently_used_apps (const GnomeDesktopItem *item); ++ + static int + readbuf_getc (ReadBuf *rb) + { +@@ -2103,6 +2115,8 @@ gnome_desktop_item_launch_on_screen_with + (flags & GNOME_DESKTOP_ITEM_LAUNCH_APPEND_PATHS), + error); + ++ update_recently_used_apps (item); ++ + return ret; + } + +@@ -4098,3 +4112,71 @@ gnome_desktop_item_error_quark (void) + + return q; + } ++ ++static void ++update_recently_used_apps (const GnomeDesktopItem *item) ++{ ++#ifdef USE_GTK_RECENT_MANAGER ++ GtkRecentManager *manager; ++ GtkRecentData recent_data; ++#else ++ EggRecentModel *model; ++ EggRecentItem *recent_item; ++#endif ++ ++ ++ if (! item) ++ return; ++ ++#ifdef USE_GTK_RECENT_MANAGER ++ manager = gtk_recent_manager_get_default (); ++ ++ if (! manager) ++ return; ++ ++ recent_data.display_name = NULL; ++ recent_data.description = NULL; ++ recent_data.mime_type = g_strdup ("application/x-desktop"); ++ recent_data.is_private = TRUE; ++ ++ recent_data.app_name = g_strdup (g_get_application_name ()); ++ if (! recent_data.app_name) ++ recent_data.app_name = g_strdup ("libgnomedesktop"); ++ ++ recent_data.app_exec = g_strdup (gnome_desktop_item_get_string (item, GNOME_DESKTOP_ITEM_EXEC)); ++ if (! recent_data.app_exec) ++ recent_data.app_exec = g_strdup ("gnome-open %u"); ++ ++ recent_data.groups = g_new0 (gchar *, 2); ++ recent_data.groups [0] = g_strdup ("recently-used-apps"); ++ recent_data.groups [1] = NULL; ++ ++ gtk_recent_manager_add_full (manager, gnome_desktop_item_get_location (item), & recent_data); ++ ++ g_free (recent_data.mime_type); ++ g_free (recent_data.app_name); ++ g_free (recent_data.app_exec); ++ g_free (recent_data.groups [0]); ++ g_free (recent_data.groups); ++ ++#else ++ ++ model = egg_recent_model_new (EGG_RECENT_MODEL_SORT_MRU); ++ ++ if (! model) ++ return; ++ ++ recent_item = egg_recent_item_new_from_uri (gnome_desktop_item_get_location (item)); ++ ++ if (recent_item) { ++ egg_recent_item_add_group (recent_item, "recently-used-apps"); ++ egg_recent_item_set_private (recent_item, TRUE); ++ ++ egg_recent_model_add_full (model, recent_item); ++ ++ egg_recent_item_unref (recent_item); ++ } ++ ++ g_object_unref (G_OBJECT (model)); ++#endif ++} diff --git a/gnome-desktop.changes b/gnome-desktop.changes new file mode 100644 index 0000000..56cedc9 --- /dev/null +++ b/gnome-desktop.changes @@ -0,0 +1,458 @@ +------------------------------------------------------------------- +Sun Jan 7 23:37:13 CET 2007 - sbrabec@suse.cz + +- Splitted gnome-desktop-doc from gnome-desktop. + +------------------------------------------------------------------- +Thu Dec 14 13:24:10 CST 2006 - maw@suse.de + +- Move to /usr +- Some specfile cleanup. + +------------------------------------------------------------------- +Tue Nov 21 16:20:50 CET 2006 - sbrabec@suse.cz + +- Do not explicitly require mDNSResponder-lib blocking avahi compat + package. + +------------------------------------------------------------------- +Wed Nov 15 22:46:58 CET 2006 - jimmyk@suse.de + +- Updated recently-used-apps.patch to be compatible with new recently-used + format, BNC #221392. + +------------------------------------------------------------------- +Fri Oct 13 01:03:08 CEST 2006 - ro@suse.de + +- added gnome-doc-utils-devel to buildreq + +------------------------------------------------------------------- +Mon Oct 2 22:50:30 CEST 2006 - jhargadon@suse.de + +- update to version 2.16.1 +- updated translations + +------------------------------------------------------------------- +Thu Sep 14 00:50:46 CEST 2006 - jhargadon@suse.de + +- update to version 2.16.0 +- updated translations + +------------------------------------------------------------------- +Tue Sep 12 20:04:35 CEST 2006 - danw@suse.de + +- update X-KDE-SubstituteUID.dif for 2.15 and re-enable it. #202076 + +------------------------------------------------------------------- +Wed Aug 30 18:11:34 CEST 2006 - jhargadon@suse.de + +- update to version 2.15.92 +- Remove gnome-workspace icon +- Doc Translations + +------------------------------------------------------------------- +Fri Aug 18 16:17:19 CEST 2006 - jhargadon@suse.de + +- update to version 2.15.91 +- Use a useful icon theme in gnome_desktop_item_find_icon() when no + icon theme is passed as argument so we get results + +------------------------------------------------------------------- +Fri Aug 11 17:53:16 CEST 2006 - jhargadon@suse.de + +- updated to version 2.15.90 +- Set GNOME_PARAM_APP_DATADIR in test application +- Save some memory in gnome-about +- Make URI canonical before using them +- Remember the added locales in GnomeDitemEdit +- Sort locales list in GnomeDitemEdit +- Use gdk_x11_display_get_user_time() to get the launch time +- Don't crash when launching a desktop file without specifying a + screen + +------------------------------------------------------------------- +Mon May 8 23:11:10 CEST 2006 - jimmyk@suse.de + +- Fix recently-used-apps patch, colliding libegg symbols (BNC #158106). + +------------------------------------------------------------------- +Mon Mar 20 23:36:26 CET 2006 - danw@suse.de + +- Fix the error message mentioned in #156801 (though not the dialog + box, which turns out to be a separate bug). + +------------------------------------------------------------------- +Tue Feb 28 23:24:48 CET 2006 - jimmyk@suse.de + +- Added .recently-used-apps support (BNC #152405) + +------------------------------------------------------------------- +Tue Feb 28 15:25:33 CET 2006 - sbrabec@suse.cz + +- Use kdelibs3-doc in BuildRequires for meinproc (#153635#c14). + +------------------------------------------------------------------- +Mon Feb 27 23:56:04 CET 2006 - ro@suse.de + +- added kdelibs3-devel-doc to BuildRequires + +------------------------------------------------------------------- +Sat Feb 4 13:23:53 CET 2006 - aj@suse.de + +- Reduce BuildRequires. + +------------------------------------------------------------------- +Wed Jan 25 21:31:42 CET 2006 - mls@suse.de + +- converted neededforbuild to BuildRequires + +------------------------------------------------------------------- +Wed Nov 30 22:17:32 CET 2005 - gekker@suse.de + +- Update to version 2.12.2 + +------------------------------------------------------------------- +Wed Nov 16 14:51:51 CET 2005 - sbrabec@suse.cz + +- Fixed X-KDE-SubstituteUID patch for commands with arguments + (#133942). + +------------------------------------------------------------------- +Thu Oct 13 00:17:32 CEST 2005 - gekker@suse.de + +- Update to version 2.12.1 + +------------------------------------------------------------------- +Mon Sep 5 12:01:16 CEST 2005 - rodrigo@suse.de + +- Update to version 2.12.0 + +------------------------------------------------------------------- +Tue Aug 23 00:19:11 CEST 2005 - gekker@suse.de + +- Update to version 2.11.92 + +------------------------------------------------------------------- +Tue Aug 2 16:36:50 CEST 2005 - gekker@suse.de + +- Update to version 2.11.90 + +------------------------------------------------------------------- +Fri Jul 22 17:43:06 CEST 2005 - gekker@suse.de + +- Update to version 2.11.5 + +------------------------------------------------------------------- +Fri Jun 17 17:16:42 CEST 2005 - gekker@suse.de + +- Update to version 2.11.3 + +------------------------------------------------------------------- +Wed Jun 1 18:15:17 CEST 2005 - sbrabec@suse.cz + +- Fixed devel requirements. + +------------------------------------------------------------------- +Mon May 23 14:24:53 CEST 2005 - sbrabec@suse.cz + +- Require libgnomesu instead of kdebase. + +------------------------------------------------------------------- +Fri May 13 17:41:36 CEST 2005 - sbrabec@suse.cz + +- Call gnomesu for root-only binaries. + +------------------------------------------------------------------- +Tue Mar 22 17:44:31 CET 2005 - sbrabec@suse.cz + +- Typo fix in desktop file (#72816). + +------------------------------------------------------------------- +Wed Mar 16 16:17:24 CET 2005 - clahey@suse.de + +- Add an icon (gnome-about-logo-transparent). + +------------------------------------------------------------------- +Thu Mar 10 09:40:53 CET 2005 - adrian@suse.de + +- kick build check workaround and apply translations to desktop file +- show gnome about dialog only in gnome + +------------------------------------------------------------------- +Wed Mar 9 19:37:31 CET 2005 - gekker@suse.de + +- Update to version 2.10.0 (GNOME 2.10). + +------------------------------------------------------------------- +Thu Mar 3 18:24:28 CET 2005 - gekker@suse.de + +- update to version 2.9.92 + +------------------------------------------------------------------- +Thu Feb 10 22:40:54 CET 2005 - gekker@suse.de + +- Update to version 2.9.91 + +------------------------------------------------------------------- +Tue Feb 8 13:52:35 CET 2005 - sbrabec@suse.cz + +- Changed Categories for gnome-about (#50440). + +------------------------------------------------------------------- +Sun Feb 6 00:10:38 CET 2005 - gekker@suse.de + +- Update to version 2.9.90.1 + +------------------------------------------------------------------- +Sat Jan 22 14:45:30 CET 2005 - gekker@suse.de + +- Fixing the broken build + +------------------------------------------------------------------- +Fri Jan 21 01:26:15 CET 2005 - gekker@suse.de + +- Update to version 2.9.4 + +------------------------------------------------------------------- +Tue Nov 2 17:13:04 CET 2004 - ro@suse.de + +- locale rename: no -> nb + +------------------------------------------------------------------- +Mon Oct 04 16:58:40 CEST 2004 - sbrabec@suse.cz + +- Updated Czech translation (#46621), + +------------------------------------------------------------------- +Wed Sep 29 12:22:00 CEST 2004 - hhetter@suse.de + +- change distributor to SUSE + +------------------------------------------------------------------- +Mon Sep 20 14:40:56 CEST 2004 - shprasad@suse.de + +- Fixes bug #65062 (Fix given by svasista@novell.com) + Fixes a crash when menu item is deleted. + +------------------------------------------------------------------- +Wed Aug 25 21:26:54 CEST 2004 - clahey@suse.de + +- Cache gnome-desktop help files. + +------------------------------------------------------------------- +Thu Aug 19 21:06:47 CEST 2004 - rml@novell.com + +- Fix bug #62316: Use kdesu not gnomesu + +------------------------------------------------------------------- +Tue Aug 17 13:57:13 CEST 2004 - sbrabec@suse.cz + +- Added gnomesu needed by X-KDE-SubstituteUID.dif to requires (#43867). + +------------------------------------------------------------------- +Wed Jul 28 20:02:52 CEST 2004 - clahey@suse.de + +- Change distributor from GNOME Project to Novell, Inc. + +------------------------------------------------------------------- +Tue May 4 17:28:20 CEST 2004 - clahey@suse.de + +- Updated to version 2.6.0.1. +- Updated X-KDE-SubstituteUID based on changes in base package. +- Added patch to provide .directory files for new GNOME menu layout. + +------------------------------------------------------------------- +Wed Mar 31 12:41:22 CEST 2004 - hhetter@suse.de + +- added X-KDE-RootOnly Interpretation (#37660) + +------------------------------------------------------------------- +Wed Mar 17 13:07:32 CET 2004 - hhetter@suse.de + +- fix gnomesu call in X-KDE-SubstituteUID + +------------------------------------------------------------------- +Mon Mar 15 14:36:21 CET 2004 - sbrabec@suse.cz + +- FHS 2.3 fix (mandir, infodir, #35821). + +------------------------------------------------------------------- +Wed Mar 10 10:35:57 CET 2004 - sbrabec@suse.cz + +- Fixed %doc attributes (#33163). + +------------------------------------------------------------------- +Sat Jan 10 16:39:26 CET 2004 - adrian@suse.de + +- add %defattr and %run_ldconfig + +------------------------------------------------------------------- +Mon Oct 27 12:31:30 CET 2003 - sbrabec@suse.cz + +- Updated to version 2.4.1.1. + +------------------------------------------------------------------- +Fri Oct 17 12:46:44 CEST 2003 - ro@suse.de + +- fix build on x86_64 running all autotools + +------------------------------------------------------------------- +Tue Oct 07 14:26:48 CEST 2003 - sbrabec@suse.cz + +- Updated to version 2.4.0 (GNOME 2.4). + +------------------------------------------------------------------- +Mon Sep 22 10:34:14 CEST 2003 - sbrabec@suse.cz + +- Obsolete gnome-core (bug #31480), + +------------------------------------------------------------------- +Mon Sep 15 14:59:29 CEST 2003 - hhetter@suse.de + +- expand iconpath for KDE crystalsvg stuff in all sizes + +------------------------------------------------------------------- +Fri Sep 12 11:45:48 CEST 2003 - hhetter@suse.de + +- enable X-KDE-SubstituteUID +- fix kdedir setting + +------------------------------------------------------------------- +Mon Jul 14 16:17:53 CEST 2003 - sbrabec@suse.cz + +- GNOME prefix change to /opt/gnome. + +------------------------------------------------------------------- +Tue Jun 24 18:34:36 CEST 2003 - sbrabec@suse.cz + +- Updated to version 2.2.2. +- Use %find_lang. +- Fixed filelist. +- Compressed man pages. +- Standard docs moved away from gnome-core2. + +------------------------------------------------------------------- +Tue Mar 4 12:43:33 CET 2003 - hhetter@suse.de + +- provide links for susewm icons in first place, making + the GNOME susewm menu more consistent + +------------------------------------------------------------------- +Mon Feb 10 16:08:09 CET 2003 - hhetter@suse.de + +- updated to version 2.2.0.1 [GNOME 2.2.0] + +------------------------------------------------------------------- +Tue Jan 28 13:36:34 CET 2003 - hhetter@suse.de + +- updated to version 2.2.0 + +------------------------------------------------------------------- +Thu Jan 16 16:28:09 CET 2003 - sbrabec@suse.cz + +- Updated to version 2.1.90. +- Updated %files and license. + +------------------------------------------------------------------- +Thu Jan 16 10:11:46 CET 2003 - sbrabec@suse.cz + +- Added libjpeg to neededforbuild. + +------------------------------------------------------------------- +Fri Nov 29 12:58:20 CET 2002 - hhetter@suse.de + +- add suse paths to the icon loader + +------------------------------------------------------------------- +Thu Nov 28 13:29:23 CET 2002 - hhetter@suse.de + +- updated to version 2.0.10 [GNOME 2.0.3] +- removed hu-translation because translations have been updated + +------------------------------------------------------------------- +Mon Nov 11 23:49:54 CET 2002 - ro@suse.de + +- changed neededforbuild to + +------------------------------------------------------------------- +Tue Sep 24 16:42:18 CEST 2002 - sbrabec@suse.cz + +- Added alsa-devel to neededforbuild, because esound-devel can require it. + +------------------------------------------------------------------- +Thu Sep 19 10:56:19 CEST 2002 - sbrabec@suse.cz + +- Added alsa to neededforbuild, because esound can depend on it. + +------------------------------------------------------------------- +Tue Sep 17 14:13:06 CEST 2002 - hhetter@suse.de + +- added corrected hu po-files + +------------------------------------------------------------------- +Tue Aug 13 10:18:46 CEST 2002 - hhetter@suse.de + +- fix neededforbuild + +------------------------------------------------------------------- +Thu Aug 8 15:44:09 CEST 2002 - hhetter@suse.de + +- updated to version 2.0.6 + * Don't stat all icons on startup + * Fix #89245, picking an .xpm over a .png + * Also read icons from our installed prefix + * Fix GConfClient leaks + +------------------------------------------------------------------- +Mon Aug 5 15:32:31 CEST 2002 - hhetter@suse.de + +- updated to version 2.0.5 + * fix bogus file reference + +------------------------------------------------------------------- +Tue Jul 30 15:22:01 CEST 2002 - hhetter@suse.de + +- updated to version 2.0.4 + * Truncate .desktop file after opening + * Remove numeric canonization + * Add APPEND_PATHS flag + * Sort out libtool versioning + * Translation updates + + +------------------------------------------------------------------- +Tue Jul 23 10:28:57 CEST 2002 - hhetter@suse.de + +- updated to version 2.0.3, req. for Nautilus 2.0.1 + +------------------------------------------------------------------- +Thu Jun 27 08:52:00 CEST 2002 - hhetter@suse.de + +- updated to version 2.0.2 (gnome 2.0 final) + +------------------------------------------------------------------- +Thu Jun 20 17:32:30 CEST 2002 - hhetter@suse.de + +- updated to version 2.0.1 + +------------------------------------------------------------------- +Mon Jun 10 15:44:23 CEST 2002 - hhetter@suse.de + +- updated to version 2.0.0 + - many translation updates + +------------------------------------------------------------------- +Tue Jun 4 17:54:19 CEST 2002 - hhetter@suse.de + +- updated to frozen source version 1.5.22 + +------------------------------------------------------------------- +Wed May 15 18:13:05 CEST 2002 - ro@suse.de + +- use libdir + +------------------------------------------------------------------- +Wed Apr 10 12:00:06 CEST 2002 - hhetter@suse.de + +- initial release for GNOME 2.0 platform + diff --git a/gnome-desktop.spec b/gnome-desktop.spec new file mode 100644 index 0000000..8e8078d --- /dev/null +++ b/gnome-desktop.spec @@ -0,0 +1,360 @@ +# +# spec file for package gnome-desktop (Version 2.16.1) +# +# Copyright (c) 2007 SUSE LINUX Products GmbH, Nuernberg, Germany. +# This file and all modifications and additions to the pristine +# package are under the same license as the package itself. +# +# Please submit bugfixes or comments via http://bugs.opensuse.org/ +# + +# norootforbuild + +Name: gnome-desktop +BuildRequires: gnome-common gnome-doc-utils gnome-doc-utils-devel gtk-doc intltool kdelibs3-doc libgnomeui-devel perl-XML-Parser scrollkeeper startup-notification-devel update-desktop-files +License: GNU Free Documentation License, Version 1.1 (GFDL), GNU General Public License (GPL) +Group: System/GUI/GNOME +Requires: libgnomesu +Autoreqprov: on +Obsoletes: gnome-core +Version: 2.16.1 +Release: 37 +Summary: The GNOME Desktop API Library +Source: %{name}-%{version}.tar.bz2 +Url: http://www.gnome.org +BuildRoot: %{_tmppath}/%{name}-%{version}-build +Patch: icon-search-path.dif +Patch1: X-KDE-SubstituteUID.dif +Patch2: gnome-desktop-desktop.patch +Patch3: gnome-desktop-recently-used-apps.patch + +%description +This package contains the libgnome-desktop library that contains APIs +that really belong in libgnome/libgnomeui but have not seen enough +testing or development to be considered stable. Use them at your own +risk. + +Also contained here are documents installed as part of the core GNOME +distribution: the GPL, GNOME's .desktop files, the gnome-about program, +some man pages, and GNOME's core graphics files and icons. + + + +Authors: +-------- + Elliot Lee + George Lebl + John Ellis + Havoc Pennington + Anders Carlsson + +%package devel +Summary: Include Files and Libraries mandatory for Development. +Group: Development/Libraries/GNOME +Autoreqprov: on +Requires: %{name} = %{version} gtk2-devel libgnomeui-devel startup-notification-devel + +%description devel +This package contains all necessary include files and libraries needed +to develop applications that require these. + + + +%package doc +Summary: Additional Package Documentation. +Group: System/GUI/GNOME +Requires: %{name} = %{version} + +%description doc +This package contains the libgnome-desktop library that contains APIs +that really belong in libgnome/libgnomeui but have not seen enough +testing or development to be considered stable. Use them at your own +risk. + +Also contained here are documents installed as part of the core GNOME +distribution: the GPL, GNOME's .desktop files, the gnome-about program, +some man pages, and GNOME's core graphics files and icons. + + + +Authors: +-------- + Elliot Lee + George Lebl + John Ellis + Havoc Pennington + Anders Carlsson + +%prep +%setup -q +%patch -p1 +%patch1 +%patch2 -p1 +%patch3 -p1 + +%build +autoreconf -f -i +CFLAGS="$RPM_OPT_FLAGS" \ + %configure \ + --localstatedir=/var/lib \ + --with-kde-datadir=/opt/kde3 \ + --with-gnome-distributor="SUSE" \ + --enable-platform-gnome2 \ + --disable-scrollkeeper +make + +%install +make install DESTDIR=$RPM_BUILD_ROOT +%find_lang %{name}-2.0 +%find_lang fdl %{name}-2.0.lang +%find_lang gnome-feedback %{name}-2.0.lang +%find_lang gpl %{name}-2.0.lang +%find_lang lgpl %{name}-2.0.lang +for xml in $RPM_BUILD_ROOT%{_datadir}/gnome/help/*/*/*.xml; do + if echo $xml | egrep 'share/gnome/help/([^/]*)/[^/]*/\1\.xml$' > /dev/null; then + meinproc --check --cache `echo $xml | sed 's/xml$/cache.bz2/'` $xml || : + fi +done +%suse_update_desktop_file gnome-about Documentation + +%clean +rm -rf $RPM_BUILD_ROOT + +%post +%run_ldconfig + +%postun +%run_ldconfig + +%files -f %{name}-2.0.lang +%defattr (-, root, root) +%doc AUTHORS COPYING COPYING-DOCS COPYING.LIB ChangeLog NEWS README +%{_bindir}/* +%{_datadir}/applications/gnome-about.desktop +%{_datadir}/gnome-about +%{_datadir}/omf/* +%{_datadir}/pixmaps/*.png +%{_datadir}/pixmaps/*.xpm +%{_libdir}/*.so.* +%doc %{_mandir}/man?/*.* + +%files devel +%defattr (-, root, root) +%{_libdir}/pkgconfig/*.pc +%{_libdir}/*.so +%{_libdir}/*.*a +%{_includedir}/gnome-desktop-2.0 + +%files doc +%defattr (-, root, root) +%{_datadir}/gtk-doc/html/gnome-desktop + +%changelog -n gnome-desktop +* Sun Jan 07 2007 - sbrabec@suse.cz +- Splitted gnome-desktop-doc from gnome-desktop. +* Thu Dec 14 2006 - maw@suse.de +- Move to /usr +- Some specfile cleanup. +* Tue Nov 21 2006 - sbrabec@suse.cz +- Do not explicitly require mDNSResponder-lib blocking avahi compat + package. +* Wed Nov 15 2006 - jimmyk@suse.de +- Updated recently-used-apps.patch to be compatible with new recently-used + format, BNC #221392. +* Fri Oct 13 2006 - ro@suse.de +- added gnome-doc-utils-devel to buildreq +* Mon Oct 02 2006 - jhargadon@suse.de +- update to version 2.16.1 +- updated translations +* Thu Sep 14 2006 - jhargadon@suse.de +- update to version 2.16.0 +- updated translations +* Tue Sep 12 2006 - danw@suse.de +- update X-KDE-SubstituteUID.dif for 2.15 and re-enable it. #202076 +* Wed Aug 30 2006 - jhargadon@suse.de +- update to version 2.15.92 +- Remove gnome-workspace icon +- Doc Translations +* Fri Aug 18 2006 - jhargadon@suse.de +- update to version 2.15.91 +- Use a useful icon theme in gnome_desktop_item_find_icon() when no + icon theme is passed as argument so we get results +* Fri Aug 11 2006 - jhargadon@suse.de +- updated to version 2.15.90 +- Set GNOME_PARAM_APP_DATADIR in test application +- Save some memory in gnome-about +- Make URI canonical before using them +- Remember the added locales in GnomeDitemEdit +- Sort locales list in GnomeDitemEdit +- Use gdk_x11_display_get_user_time() to get the launch time +- Don't crash when launching a desktop file without specifying a + screen +* Mon May 08 2006 - jimmyk@suse.de +- Fix recently-used-apps patch, colliding libegg symbols (BNC #158106). +* Mon Mar 20 2006 - danw@suse.de +- Fix the error message mentioned in #156801 (though not the dialog + box, which turns out to be a separate bug). +* Tue Feb 28 2006 - jimmyk@suse.de +- Added .recently-used-apps support (BNC #152405) +* Tue Feb 28 2006 - sbrabec@suse.cz +- Use kdelibs3-doc in BuildRequires for meinproc (#153635#c14). +* Mon Feb 27 2006 - ro@suse.de +- added kdelibs3-devel-doc to BuildRequires +* Sat Feb 04 2006 - aj@suse.de +- Reduce BuildRequires. +* Wed Jan 25 2006 - mls@suse.de +- converted neededforbuild to BuildRequires +* Wed Nov 30 2005 - gekker@suse.de +- Update to version 2.12.2 +* Wed Nov 16 2005 - sbrabec@suse.cz +- Fixed X-KDE-SubstituteUID patch for commands with arguments + (#133942). +* Thu Oct 13 2005 - gekker@suse.de +- Update to version 2.12.1 +* Mon Sep 05 2005 - rodrigo@suse.de +- Update to version 2.12.0 +* Tue Aug 23 2005 - gekker@suse.de +- Update to version 2.11.92 +* Tue Aug 02 2005 - gekker@suse.de +- Update to version 2.11.90 +* Fri Jul 22 2005 - gekker@suse.de +- Update to version 2.11.5 +* Fri Jun 17 2005 - gekker@suse.de +- Update to version 2.11.3 +* Wed Jun 01 2005 - sbrabec@suse.cz +- Fixed devel requirements. +* Mon May 23 2005 - sbrabec@suse.cz +- Require libgnomesu instead of kdebase. +* Fri May 13 2005 - sbrabec@suse.cz +- Call gnomesu for root-only binaries. +* Tue Mar 22 2005 - sbrabec@suse.cz +- Typo fix in desktop file (#72816). +* Wed Mar 16 2005 - clahey@suse.de +- Add an icon (gnome-about-logo-transparent). +* Thu Mar 10 2005 - adrian@suse.de +- kick build check workaround and apply translations to desktop file +- show gnome about dialog only in gnome +* Wed Mar 09 2005 - gekker@suse.de +- Update to version 2.10.0 (GNOME 2.10). +* Thu Mar 03 2005 - gekker@suse.de +- update to version 2.9.92 +* Thu Feb 10 2005 - gekker@suse.de +- Update to version 2.9.91 +* Tue Feb 08 2005 - sbrabec@suse.cz +- Changed Categories for gnome-about (#50440). +* Sun Feb 06 2005 - gekker@suse.de +- Update to version 2.9.90.1 +* Sat Jan 22 2005 - gekker@suse.de +- Fixing the broken build +* Fri Jan 21 2005 - gekker@suse.de +- Update to version 2.9.4 +* Tue Nov 02 2004 - ro@suse.de +- locale rename: no -> nb +* Mon Oct 04 2004 - sbrabec@suse.cz +- Updated Czech translation (#46621), +* Wed Sep 29 2004 - hhetter@suse.de +- change distributor to SUSE +* Mon Sep 20 2004 - shprasad@suse.de +- Fixes bug #65062 (Fix given by svasista@novell.com) + Fixes a crash when menu item is deleted. +* Wed Aug 25 2004 - clahey@suse.de +- Cache gnome-desktop help files. +* Thu Aug 19 2004 - rml@novell.com +- Fix bug #62316: Use kdesu not gnomesu +* Tue Aug 17 2004 - sbrabec@suse.cz +- Added gnomesu needed by X-KDE-SubstituteUID.dif to requires (#43867). +* Wed Jul 28 2004 - clahey@suse.de +- Change distributor from GNOME Project to Novell, Inc. +* Tue May 04 2004 - clahey@suse.de +- Updated to version 2.6.0.1. +- Updated X-KDE-SubstituteUID based on changes in base package. +- Added patch to provide .directory files for new GNOME menu layout. +* Wed Mar 31 2004 - hhetter@suse.de +- added X-KDE-RootOnly Interpretation (#37660) +* Wed Mar 17 2004 - hhetter@suse.de +- fix gnomesu call in X-KDE-SubstituteUID +* Mon Mar 15 2004 - sbrabec@suse.cz +- FHS 2.3 fix (mandir, infodir, #35821). +* Wed Mar 10 2004 - sbrabec@suse.cz +- Fixed %%doc attributes (#33163). +* Sat Jan 10 2004 - adrian@suse.de +- add %%defattr and %%run_ldconfig +* Mon Oct 27 2003 - sbrabec@suse.cz +- Updated to version 2.4.1.1. +* Fri Oct 17 2003 - ro@suse.de +- fix build on x86_64 running all autotools +* Tue Oct 07 2003 - sbrabec@suse.cz +- Updated to version 2.4.0 (GNOME 2.4). +* Mon Sep 22 2003 - sbrabec@suse.cz +- Obsolete gnome-core (bug #31480), +* Mon Sep 15 2003 - hhetter@suse.de +- expand iconpath for KDE crystalsvg stuff in all sizes +* Fri Sep 12 2003 - hhetter@suse.de +- enable X-KDE-SubstituteUID +- fix kdedir setting +* Mon Jul 14 2003 - sbrabec@suse.cz +- GNOME prefix change to /opt/gnome. +* Tue Jun 24 2003 - sbrabec@suse.cz +- Updated to version 2.2.2. +- Use %%find_lang. +- Fixed filelist. +- Compressed man pages. +- Standard docs moved away from gnome-core2. +* Tue Mar 04 2003 - hhetter@suse.de +- provide links for susewm icons in first place, making + the GNOME susewm menu more consistent +* Mon Feb 10 2003 - hhetter@suse.de +- updated to version 2.2.0.1 [GNOME 2.2.0] +* Tue Jan 28 2003 - hhetter@suse.de +- updated to version 2.2.0 +* Thu Jan 16 2003 - sbrabec@suse.cz +- Updated to version 2.1.90. +- Updated %%files and license. +* Thu Jan 16 2003 - sbrabec@suse.cz +- Added libjpeg to neededforbuild. +* Fri Nov 29 2002 - hhetter@suse.de +- add suse paths to the icon loader +* Thu Nov 28 2002 - hhetter@suse.de +- updated to version 2.0.10 [GNOME 2.0.3] +- removed hu-translation because translations have been updated +* Mon Nov 11 2002 - ro@suse.de +- changed neededforbuild to +* Tue Sep 24 2002 - sbrabec@suse.cz +- Added alsa-devel to neededforbuild, because esound-devel can require it. +* Thu Sep 19 2002 - sbrabec@suse.cz +- Added alsa to neededforbuild, because esound can depend on it. +* Tue Sep 17 2002 - hhetter@suse.de +- added corrected hu po-files +* Tue Aug 13 2002 - hhetter@suse.de +- fix neededforbuild +* Thu Aug 08 2002 - hhetter@suse.de +- updated to version 2.0.6 + * Don't stat all icons on startup + * Fix #89245, picking an .xpm over a .png + * Also read icons from our installed prefix + * Fix GConfClient leaks +* Mon Aug 05 2002 - hhetter@suse.de +- updated to version 2.0.5 + * fix bogus file reference +* Tue Jul 30 2002 - hhetter@suse.de +- updated to version 2.0.4 + * Truncate .desktop file after opening + * Remove numeric canonization + * Add APPEND_PATHS flag + * Sort out libtool versioning + * Translation updates +* Tue Jul 23 2002 - hhetter@suse.de +- updated to version 2.0.3, req. for Nautilus 2.0.1 +* Thu Jun 27 2002 - hhetter@suse.de +- updated to version 2.0.2 (gnome 2.0 final) +* Thu Jun 20 2002 - hhetter@suse.de +- updated to version 2.0.1 +* Mon Jun 10 2002 - hhetter@suse.de +- updated to version 2.0.0 + - many translation updates +* Tue Jun 04 2002 - hhetter@suse.de +- updated to frozen source version 1.5.22 +* Wed May 15 2002 - ro@suse.de +- use libdir +* Wed Apr 10 2002 - hhetter@suse.de +- initial release for GNOME 2.0 platform diff --git a/icon-search-path.dif b/icon-search-path.dif new file mode 100644 index 0000000..fc4f6a2 --- /dev/null +++ b/icon-search-path.dif @@ -0,0 +1,99 @@ +diff -ruN gnome-desktop-2.2.2/libgnome-desktop/gnome-desktop-item.c gnome-desktop-n/libgnome-desktop/gnome-desktop-item.c +--- gnome-desktop-2.2.2/libgnome-desktop/gnome-desktop-item.c 2003-05-14 14:40:38.000000000 +0200 ++++ gnome-desktop-n/libgnome-desktop/gnome-desktop-item.c 2003-09-15 14:55:59.000000000 +0200 +@@ -2340,6 +2340,11 @@ + static GSList *hicolor_kde_32 = NULL; + static GSList *hicolor_kde_22 = NULL; + static GSList *hicolor_kde_16 = NULL; ++static GSList *crystalsvg_kde_48 = NULL; ++static GSList *crystalsvg_kde_32 = NULL; ++static GSList *crystalsvg_kde_22 = NULL; ++static GSList *crystalsvg_kde_16 = NULL; ++ + /* XXX: maybe we don't care about locolor + static GSList *locolor_kde_48 = NULL; + static GSList *locolor_kde_32 = NULL; +@@ -2397,6 +2402,10 @@ + ADD_DIRS (hicolor, 32); + ADD_DIRS (hicolor, 22); + ADD_DIRS (hicolor, 16); ++ ADD_DIRS (crystalsvg, 48); ++ ADD_DIRS (crystalsvg, 32); ++ ADD_DIRS (crystalsvg, 22); ++ ADD_DIRS (crystalsvg, 16); + + /* XXX: maybe we don't care about locolor + ADD_DIRS (locolor, 48); +@@ -2423,6 +2432,15 @@ + g_slist_copy (hicolor_kde_22)); + list = g_slist_concat (list, + g_slist_copy (hicolor_kde_16)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_48)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_32)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_16)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_22)); ++ + } else if (size > 22) { + /* 23-32 */ + list = g_slist_concat (g_slist_copy (hicolor_kde_32), +@@ -2431,6 +2449,15 @@ + g_slist_copy (hicolor_kde_22)); + list = g_slist_concat (list, + g_slist_copy (hicolor_kde_16)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_48)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_32)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_16)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_22)); ++ + } else if (size > 16) { + /* 17-22 */ + list = g_slist_concat (g_slist_copy (hicolor_kde_22), +@@ -2439,6 +2466,15 @@ + g_slist_copy (hicolor_kde_48)); + list = g_slist_concat (list, + g_slist_copy (hicolor_kde_16)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_48)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_32)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_16)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_22)); ++ + } else { + /* 1-16 */ + list = g_slist_concat (g_slist_copy (hicolor_kde_16), +@@ -2447,6 +2483,15 @@ + g_slist_copy (hicolor_kde_32)); + list = g_slist_concat (list, + g_slist_copy (hicolor_kde_48)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_48)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_32)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_16)); ++ list = g_slist_concat (list, ++ g_slist_copy (crystalsvg_kde_22)); ++ + } + + list = g_slist_append (list, kde_icondir); +@@ -2463,7 +2508,7 @@ + char *try_prefixes[] = { + "/usr", + "/opt/kde", +- "/opt/kde2", ++ "/opt/kde3", + "/usr/local", + "/kde", + "/kde2", diff --git a/ready b/ready new file mode 100644 index 0000000..473a0f4