background: Introduce BgRecentSource
BgRecentSource is a background source implementation that keeps track of recently added backgrounds. The backgrounds are copied to $(local data dir)/backgrounds, which is monitored. When copying files to this folder, the current time is added as prefix to avoid filename collisions.
This commit is contained in:
parent
5ba721b581
commit
9e39748d16
3 changed files with 513 additions and 0 deletions
473
panels/background/bg-recent-source.c
Normal file
473
panels/background/bg-recent-source.c
Normal file
|
@ -0,0 +1,473 @@
|
|||
/* bg-recent-source.c
|
||||
*
|
||||
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#define G_LOG_DOMAIN "bg-recent-source"
|
||||
|
||||
#include "bg-recent-source.h"
|
||||
#include "cc-background-item.h"
|
||||
|
||||
#define ATTRIBUTES G_FILE_ATTRIBUTE_STANDARD_NAME "," \
|
||||
G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "," \
|
||||
G_FILE_ATTRIBUTE_TIME_MODIFIED
|
||||
|
||||
struct _BgRecentSource
|
||||
{
|
||||
BgSource parent;
|
||||
|
||||
GFile *backgrounds_folder;
|
||||
GFileMonitor *monitor;
|
||||
|
||||
GCancellable *cancellable;
|
||||
GHashTable *items;
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE (BgRecentSource, bg_recent_source, BG_TYPE_SOURCE)
|
||||
|
||||
|
||||
static const gchar * const content_types[] = {
|
||||
"image/png",
|
||||
"image/jp2",
|
||||
"image/jpeg",
|
||||
"image/bmp",
|
||||
"image/svg+xml",
|
||||
"image/x-portable-anymap",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int
|
||||
sort_func (gconstpointer a,
|
||||
gconstpointer b,
|
||||
gpointer user_data)
|
||||
{
|
||||
CcBackgroundItem *item_a;
|
||||
CcBackgroundItem *item_b;
|
||||
guint64 modified_a;
|
||||
guint64 modified_b;
|
||||
int retval;
|
||||
|
||||
item_a = (CcBackgroundItem *) a;
|
||||
item_b = (CcBackgroundItem *) b;
|
||||
modified_a = cc_background_item_get_modified (item_a);
|
||||
modified_b = cc_background_item_get_modified (item_b);
|
||||
|
||||
retval = modified_b - modified_a;
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void
|
||||
add_file_from_info (BgRecentSource *self,
|
||||
GFile *file,
|
||||
GFileInfo *info)
|
||||
{
|
||||
g_autoptr(CcBackgroundItem) item = NULL;
|
||||
CcBackgroundItemFlags flags = 0;
|
||||
g_autofree gchar *source_uri = NULL;
|
||||
g_autofree gchar *uri = NULL;
|
||||
GListStore *store;
|
||||
const gchar *content_type;
|
||||
guint64 mtime;
|
||||
|
||||
content_type = g_file_info_get_content_type (info);
|
||||
mtime = g_file_info_get_attribute_uint64 (info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
|
||||
|
||||
if (!content_type || !g_strv_contains (content_types, content_type))
|
||||
return;
|
||||
|
||||
uri = g_file_get_uri (file);
|
||||
item = cc_background_item_new (uri);
|
||||
flags |= CC_BACKGROUND_ITEM_HAS_SHADING | CC_BACKGROUND_ITEM_HAS_PLACEMENT;
|
||||
g_object_set (G_OBJECT (item),
|
||||
"flags", flags,
|
||||
"shading", G_DESKTOP_BACKGROUND_SHADING_SOLID,
|
||||
"placement", G_DESKTOP_BACKGROUND_STYLE_ZOOM,
|
||||
"modified", mtime,
|
||||
"needs-download", FALSE,
|
||||
"source-url", source_uri,
|
||||
NULL);
|
||||
|
||||
store = bg_source_get_liststore (BG_SOURCE (self));
|
||||
g_list_store_insert_sorted (store, item, sort_func, self);
|
||||
|
||||
g_hash_table_insert (self->items, g_strdup (uri), g_object_ref (item));
|
||||
}
|
||||
|
||||
static void
|
||||
remove_item (BgRecentSource *self,
|
||||
CcBackgroundItem *item)
|
||||
{
|
||||
GListStore *store;
|
||||
const gchar *uri;
|
||||
guint i;
|
||||
|
||||
g_return_if_fail (BG_IS_RECENT_SOURCE (self));
|
||||
g_return_if_fail (CC_IS_BACKGROUND_ITEM (item));
|
||||
|
||||
uri = cc_background_item_get_uri (item);
|
||||
store = bg_source_get_liststore (BG_SOURCE (self));
|
||||
|
||||
g_debug ("Removing wallpaper %s", uri);
|
||||
|
||||
for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (store)); i++)
|
||||
{
|
||||
g_autoptr(CcBackgroundItem) tmp = NULL;
|
||||
|
||||
tmp = g_list_model_get_item (G_LIST_MODEL (store), i);
|
||||
|
||||
if (tmp == item)
|
||||
{
|
||||
g_list_store_remove (store, i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
g_hash_table_remove (self->items, cc_background_item_get_uri (item));
|
||||
}
|
||||
|
||||
static void
|
||||
query_info_finished_cb (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
BgRecentSource *self;
|
||||
g_autoptr(GFileInfo) file_info = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
GFile *file = NULL;
|
||||
|
||||
file = G_FILE (source);
|
||||
file_info = g_file_query_info_finish (file, result, &error);
|
||||
if (error)
|
||||
{
|
||||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
g_warning ("Could not get pictures file information: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
self = BG_RECENT_SOURCE (user_data);
|
||||
|
||||
g_debug ("Adding wallpaper %s (%d)",
|
||||
g_file_info_get_name (file_info),
|
||||
G_IS_FILE (self->backgrounds_folder));
|
||||
|
||||
add_file_from_info (self, file, file_info);
|
||||
}
|
||||
|
||||
static void
|
||||
on_file_changed_cb (GFileMonitor *monitor,
|
||||
GFile *file,
|
||||
GFile *other_file,
|
||||
GFileMonitorEvent event_type,
|
||||
BgRecentSource *self)
|
||||
{
|
||||
g_autofree gchar *uri = NULL;
|
||||
|
||||
switch (event_type)
|
||||
{
|
||||
case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
|
||||
g_file_query_info_async (file,
|
||||
ATTRIBUTES,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
G_PRIORITY_DEFAULT,
|
||||
self->cancellable,
|
||||
query_info_finished_cb,
|
||||
self);
|
||||
break;
|
||||
|
||||
case G_FILE_MONITOR_EVENT_DELETED:
|
||||
uri = g_file_get_uri (file);
|
||||
remove_item (self, g_hash_table_lookup (self->items, uri));
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
file_sort_func (gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
GFileInfo *file_a = G_FILE_INFO (a);
|
||||
GFileInfo *file_b = G_FILE_INFO (b);
|
||||
guint64 modified_a, modified_b;
|
||||
|
||||
modified_a = g_file_info_get_attribute_uint64 (file_a, G_FILE_ATTRIBUTE_TIME_MODIFIED);
|
||||
modified_b = g_file_info_get_attribute_uint64 (file_b, G_FILE_ATTRIBUTE_TIME_MODIFIED);
|
||||
|
||||
return modified_b - modified_a;
|
||||
}
|
||||
|
||||
static void
|
||||
file_info_async_ready_cb (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
BgRecentSource *self;
|
||||
g_autolist(GFileInfo) file_infos = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
GFile *parent = NULL;
|
||||
GList *l;
|
||||
|
||||
file_infos = g_file_enumerator_next_files_finish (G_FILE_ENUMERATOR (source),
|
||||
result,
|
||||
&error);
|
||||
if (error)
|
||||
{
|
||||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
g_warning ("Could not get pictures file information: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
self = BG_RECENT_SOURCE (user_data);
|
||||
parent = g_file_enumerator_get_container (G_FILE_ENUMERATOR (source));
|
||||
|
||||
file_infos = g_list_sort (file_infos, file_sort_func);
|
||||
|
||||
for (l = file_infos; l; l = l->next)
|
||||
{
|
||||
g_autoptr(GFile) file = NULL;
|
||||
GFileInfo *info;
|
||||
|
||||
info = l->data;
|
||||
file = g_file_get_child (parent, g_file_info_get_name (info));
|
||||
|
||||
g_debug ("Found recent wallpaper %s", g_file_info_get_name (info));
|
||||
|
||||
add_file_from_info (self, file, info);
|
||||
}
|
||||
|
||||
g_file_enumerator_close (G_FILE_ENUMERATOR (source), self->cancellable, &error);
|
||||
|
||||
if (error)
|
||||
g_warning ("Error closing file enumerator: %s", error->message);
|
||||
}
|
||||
|
||||
static void
|
||||
enumerate_children_finished_cb (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
BgRecentSource *self = BG_RECENT_SOURCE (user_data);
|
||||
g_autoptr(GFileEnumerator) enumerator = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
enumerator = g_file_enumerate_children_finish (G_FILE (source), result, &error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
g_warning ("Could not fill pictures source: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
g_file_enumerator_next_files_async (enumerator,
|
||||
G_MAXINT,
|
||||
G_PRIORITY_DEFAULT,
|
||||
self->cancellable,
|
||||
file_info_async_ready_cb,
|
||||
self);
|
||||
}
|
||||
|
||||
static void
|
||||
load_backgrounds (BgRecentSource *self)
|
||||
{
|
||||
g_autofree gchar *backgrounds_path = NULL;
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
if (!g_file_make_directory_with_parents (self->backgrounds_folder, self->cancellable, &error) &&
|
||||
!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
|
||||
{
|
||||
g_critical ("Failed to create local background directory: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
backgrounds_path = g_file_get_path (self->backgrounds_folder);
|
||||
g_debug ("Enumerating wallpapers under %s", backgrounds_path);
|
||||
|
||||
g_file_enumerate_children_async (self->backgrounds_folder,
|
||||
ATTRIBUTES,
|
||||
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
|
||||
G_PRIORITY_DEFAULT,
|
||||
self->cancellable,
|
||||
enumerate_children_finished_cb,
|
||||
self);
|
||||
|
||||
self->monitor = g_file_monitor_directory (self->backgrounds_folder,
|
||||
G_FILE_MONITOR_WATCH_MOVES,
|
||||
self->cancellable,
|
||||
&error);
|
||||
|
||||
if (!self->monitor)
|
||||
{
|
||||
g_critical ("Failed to monitor background directory: %s", error->message);
|
||||
return;
|
||||
}
|
||||
|
||||
g_signal_connect (self->monitor, "changed", G_CALLBACK (on_file_changed_cb), self);
|
||||
}
|
||||
|
||||
/* Callbacks */
|
||||
|
||||
static void
|
||||
on_file_copied_cb (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
BgRecentSource *self = BG_RECENT_SOURCE (user_data);
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
g_file_copy_finish (G_FILE (source), result, &error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
g_critical ("Failed to copy file: %s", error->message);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autofree gchar *original_file = g_file_get_path (G_FILE (source));
|
||||
g_debug ("Successfully copied wallpaper: %s", original_file);
|
||||
}
|
||||
|
||||
g_object_unref (self);
|
||||
}
|
||||
|
||||
static void
|
||||
on_file_deleted_cb (GObject *source,
|
||||
GAsyncResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
BgRecentSource *self = BG_RECENT_SOURCE (user_data);
|
||||
g_autoptr(GError) error = NULL;
|
||||
|
||||
g_file_delete_finish (G_FILE (source), result, &error);
|
||||
|
||||
if (error)
|
||||
{
|
||||
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
|
||||
g_critical ("Failed to delete wallpaper: %s", error->message);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_autofree gchar *original_file = g_file_get_path (G_FILE (source));
|
||||
g_debug ("Successfully deleted wallpaper: %s", original_file);
|
||||
}
|
||||
|
||||
g_object_unref (self);
|
||||
}
|
||||
|
||||
/* GObject overrides */
|
||||
|
||||
static void
|
||||
bg_recent_source_finalize (GObject *object)
|
||||
{
|
||||
BgRecentSource *self = (BgRecentSource *)object;
|
||||
|
||||
g_cancellable_cancel (self->cancellable);
|
||||
g_clear_object (&self->cancellable);
|
||||
g_clear_object (&self->monitor);
|
||||
|
||||
G_OBJECT_CLASS (bg_recent_source_parent_class)->finalize (object);
|
||||
}
|
||||
|
||||
static void
|
||||
bg_recent_source_class_init (BgRecentSourceClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
object_class->finalize = bg_recent_source_finalize;
|
||||
}
|
||||
|
||||
static void
|
||||
bg_recent_source_init (BgRecentSource *self)
|
||||
{
|
||||
g_autofree gchar *backgrounds_path = NULL;
|
||||
|
||||
backgrounds_path = g_build_filename (g_get_user_data_dir (), "backgrounds", NULL);
|
||||
|
||||
self->items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
|
||||
self->cancellable = g_cancellable_new ();
|
||||
self->backgrounds_folder = g_file_new_for_path (backgrounds_path);
|
||||
|
||||
load_backgrounds (self);
|
||||
}
|
||||
|
||||
BgRecentSource*
|
||||
bg_recent_source_new (GtkWidget *widget)
|
||||
{
|
||||
return g_object_new (BG_TYPE_RECENT_SOURCE,
|
||||
"widget", widget,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void
|
||||
bg_recent_source_add_file (BgRecentSource *self,
|
||||
const gchar *path)
|
||||
{
|
||||
g_autoptr(GDateTime) now = NULL;
|
||||
g_autofree gchar *destination_name = NULL;
|
||||
g_autofree gchar *formatted_now = NULL;
|
||||
g_autofree gchar *basename = NULL;
|
||||
g_autoptr(GFile) destination = NULL;
|
||||
g_autoptr(GFile) file = NULL;
|
||||
|
||||
g_return_if_fail (BG_IS_RECENT_SOURCE (self));
|
||||
g_return_if_fail (path && *path);
|
||||
|
||||
g_debug ("Importing wallpaper %s", path);
|
||||
|
||||
now = g_date_time_new_now_local ();
|
||||
formatted_now = g_date_time_format (now, "%Y-%m-%d-%H-%M-%S");
|
||||
|
||||
file = g_file_new_for_path (path);
|
||||
|
||||
basename = g_file_get_basename (file);
|
||||
destination_name = g_strdup_printf ("%s-%s", formatted_now, basename);
|
||||
destination = g_file_get_child (self->backgrounds_folder, destination_name);
|
||||
|
||||
g_file_copy_async (file,
|
||||
destination,
|
||||
G_FILE_COPY_NONE,
|
||||
G_PRIORITY_DEFAULT,
|
||||
self->cancellable,
|
||||
NULL, NULL,
|
||||
on_file_copied_cb,
|
||||
g_object_ref (self));
|
||||
}
|
||||
|
||||
void
|
||||
bg_recent_source_remove_item (BgRecentSource *self,
|
||||
CcBackgroundItem *item)
|
||||
{
|
||||
g_autoptr(GFile) file = NULL;
|
||||
const gchar *uri;
|
||||
|
||||
g_return_if_fail (BG_IS_RECENT_SOURCE (self));
|
||||
g_return_if_fail (CC_IS_BACKGROUND_ITEM (item));
|
||||
|
||||
uri = cc_background_item_get_uri (item);
|
||||
file = g_file_new_for_uri (uri);
|
||||
|
||||
g_file_delete_async (file,
|
||||
G_PRIORITY_DEFAULT,
|
||||
self->cancellable,
|
||||
on_file_deleted_cb,
|
||||
g_object_ref (self));
|
||||
}
|
39
panels/background/bg-recent-source.h
Normal file
39
panels/background/bg-recent-source.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/* bg-recent-source.h
|
||||
*
|
||||
* Copyright 2019 Georges Basile Stavracas Neto <georges.stavracas@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "bg-source.h"
|
||||
#include "cc-background-item.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define BG_TYPE_RECENT_SOURCE (bg_recent_source_get_type())
|
||||
G_DECLARE_FINAL_TYPE (BgRecentSource, bg_recent_source, BG, RECENT_SOURCE, BgSource)
|
||||
|
||||
BgRecentSource* bg_recent_source_new (GtkWidget *widget);
|
||||
|
||||
void bg_recent_source_add_file (BgRecentSource *self,
|
||||
const gchar *path);
|
||||
|
||||
void bg_recent_source_remove_item (BgRecentSource *self,
|
||||
CcBackgroundItem *item);
|
||||
|
||||
G_END_DECLS
|
|
@ -77,6 +77,7 @@ common_sources += gnome.compile_resources(
|
|||
sources = common_sources + files(
|
||||
'bg-colors-source.c',
|
||||
'bg-pictures-source.c',
|
||||
'bg-recent-source.c',
|
||||
'bg-source.c',
|
||||
'bg-wallpapers-source.c',
|
||||
'cc-background-chooser.c',
|
||||
|
|
Loading…
Add table
Reference in a new issue