/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Copyright © 2012 Igalia S.L.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
#include "config.h"
#include "ephy-frecent-store.h"
#include "ephy-history-service.h"
#include "ephy-snapshot-service.h"
#include <libsoup/soup.h>
#define EPHY_FRECENT_STORE_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_FRECENT_STORE, EphyFrecentStorePrivate))
struct _EphyFrecentStorePrivate
{
gint history_length;
};
enum
{
PROP_0,
PROP_HISTORY_LENGTH,
};
G_DEFINE_TYPE (EphyFrecentStore, ephy_frecent_store, EPHY_TYPE_OVERVIEW_STORE)
static void
on_find_urls_cb (EphyHistoryService *service,
gboolean success,
GList *urls,
EphyFrecentStore *store)
{
EphyHistoryURL *url;
char *old_url;
GtkTreeIter treeiter;
gboolean valid;
GList *iter;
gboolean peek_snapshot;
GdkPixbuf *default_icon;
if (success != TRUE)
return;
valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store),
&treeiter);
g_object_get (store,
"default-icon", &default_icon,
NULL);
for (iter = urls; iter != NULL; iter = iter->next) {
peek_snapshot = FALSE;
url = (EphyHistoryURL *)iter->data;
if (valid) {
gtk_tree_model_get (GTK_TREE_MODEL (store), &treeiter,
EPHY_OVERVIEW_STORE_URI, &old_url,
-1);
gtk_list_store_set (GTK_LIST_STORE (store), &treeiter,
EPHY_OVERVIEW_STORE_URI, url->url,
EPHY_OVERVIEW_STORE_LAST_VISIT, url->last_visit_time,
-1);
if (g_strcmp0 (old_url, url->url) != 0) {
gtk_list_store_set (GTK_LIST_STORE (store), &treeiter,
EPHY_OVERVIEW_STORE_TITLE, url->title,
-1);
peek_snapshot = TRUE;
}
g_free (old_url);
if (ephy_overview_store_needs_snapshot (EPHY_OVERVIEW_STORE (store), &treeiter))
peek_snapshot = TRUE;
} else {
gtk_list_store_insert_with_values (GTK_LIST_STORE (store), &treeiter, -1,
EPHY_OVERVIEW_STORE_TITLE, url->title,
EPHY_OVERVIEW_STORE_URI, url->url,
EPHY_OVERVIEW_STORE_LAST_VISIT, url->last_visit_time,
EPHY_OVERVIEW_STORE_SNAPSHOT, default_icon,
-1);
peek_snapshot = TRUE;
}
if (peek_snapshot)
ephy_overview_store_peek_snapshot (EPHY_OVERVIEW_STORE (store),
NULL, &treeiter);
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &treeiter);
}
g_list_free_full (urls, (GDestroyNotify)ephy_history_url_free);
g_object_unref (default_icon);
while (valid)
valid = ephy_overview_store_remove (EPHY_OVERVIEW_STORE (store), &treeiter);
}
static void
ephy_frecent_store_fetch_urls (EphyFrecentStore *store,
EphyHistoryService *service)
{
EphyHistoryQuery *query;
query = ephy_history_query_new ();
query->sort_type = EPHY_HISTORY_SORT_MV;
query->limit = store->priv->history_length;
query->ignore_hidden = TRUE;
ephy_history_service_query_urls (service, query, NULL,
(EphyHistoryJobCallback) on_find_urls_cb,
store);
ephy_history_query_free (query);
}
static gboolean
on_urls_visited_cb (EphyHistoryService *service,
EphyFrecentStore *store)
{
ephy_frecent_store_fetch_urls (store, service);
return FALSE;
}
static void
on_cleared_cb (EphyHistoryService *service,
EphyFrecentStore *store)
{
/* Should probably emit a signal notifying that this is empty, this
signal probably should live in EphyOverviewStore. */
gtk_list_store_clear (GTK_LIST_STORE (store));
}
static void
on_url_title_changed (EphyHistoryService *service,
const char *url,
const char *title,
EphyFrecentStore *store)
{
GtkTreeIter iter;
gchar *iter_url;
if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
return;
do {
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
EPHY_OVERVIEW_STORE_URI, &iter_url,
-1);
if (g_strcmp0 (iter_url, url) == 0) {
gtk_list_store_set (GTK_LIST_STORE (store), &iter,
EPHY_OVERVIEW_STORE_TITLE, title,
-1);
g_free (iter_url);
break;
}
g_free (iter_url);
} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
}
static void
on_url_deleted (EphyHistoryService *service,
const char *url,
EphyFrecentStore *store)
{
GtkTreeIter iter;
gchar *iter_url;
gboolean needs_update = FALSE;
if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
return;
do {
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
EPHY_OVERVIEW_STORE_URI, &iter_url,
-1);
if (g_strcmp0 (iter_url, url) == 0) {
needs_update = TRUE;
ephy_overview_store_remove (EPHY_OVERVIEW_STORE (store), &iter);
g_free (iter_url);
break;
}
g_free (iter_url);
} while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
if (needs_update)
ephy_frecent_store_fetch_urls (store, service);
}
static void
on_host_deleted (EphyHistoryService *service,
const char *deleted_url,
EphyFrecentStore *store)
{
GtkTreeIter iter;
gchar *iter_url;
SoupURI *store_uri;
SoupURI *deleted_uri;
gboolean needs_update = FALSE;
gboolean remove, valid;
if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter))
return;
deleted_uri = soup_uri_new (deleted_url);
do {
gtk_tree_model_get (GTK_TREE_MODEL (store), &iter,
EPHY_OVERVIEW_STORE_URI, &iter_url,
-1);
store_uri = soup_uri_new (iter_url);
remove = g_strcmp0 (soup_uri_get_host (deleted_uri), soup_uri_get_host (store_uri)) == 0;
soup_uri_free (store_uri);
g_free (iter_url);
if (remove) {
valid = ephy_overview_store_remove (EPHY_OVERVIEW_STORE (store), &iter);
needs_update = TRUE;
} else
valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter);
} while (valid);
soup_uri_free (deleted_uri);
if (needs_update)
ephy_frecent_store_fetch_urls (store, service);
}
static void
setup_history_service (EphyFrecentStore *store)
{
EphyHistoryService *service;
g_object_get (G_OBJECT (store), "history-service", &service, NULL);
ephy_frecent_store_fetch_urls (store, service);
g_signal_connect (service, "urls-visited",
G_CALLBACK (on_urls_visited_cb), store);
g_signal_connect (service, "cleared",
G_CALLBACK (on_cleared_cb), store);
g_signal_connect (service, "url-title-changed",
G_CALLBACK (on_url_title_changed), store);
g_signal_connect (service, "url-deleted",
G_CALLBACK (on_url_deleted), store);
g_signal_connect (service, "host-deleted",
G_CALLBACK (on_host_deleted), store);
g_object_unref (service);
}
static void
ephy_frecent_store_notify (GObject *object,
GParamSpec *pspec)
{
if (g_strcmp0 (pspec->name, "history-service") == 0)
setup_history_service (EPHY_FRECENT_STORE (object));
if (G_OBJECT_CLASS (ephy_frecent_store_parent_class)->notify)
G_OBJECT_CLASS (ephy_frecent_store_parent_class)->notify (object, pspec);
}
static void
ephy_frecent_store_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
EphyFrecentStore *store = EPHY_FRECENT_STORE (object);
switch (prop_id)
{
case PROP_HISTORY_LENGTH:
ephy_frecent_store_set_history_length (store, g_value_get_int (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
ephy_frecent_store_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
EphyFrecentStore *store = EPHY_FRECENT_STORE (object);
switch (prop_id)
{
case PROP_HISTORY_LENGTH:
g_value_set_int (value, ephy_frecent_store_get_history_length (store));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
ephy_frecent_store_class_init (EphyFrecentStoreClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->notify = ephy_frecent_store_notify;
object_class->set_property = ephy_frecent_store_set_property;
object_class->get_property = ephy_frecent_store_get_property;
g_object_class_install_property (object_class,
PROP_HISTORY_LENGTH,
g_param_spec_int ("history-length",
"History length",
"Length of the history list",
0, G_MAXINT, 12,
G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_NAME | G_PARAM_STATIC_NICK | G_PARAM_STATIC_BLURB));
g_type_class_add_private (object_class, sizeof(EphyFrecentStorePrivate));
}
static void
ephy_frecent_store_init (EphyFrecentStore *self)
{
self->priv = EPHY_FRECENT_STORE_GET_PRIVATE (self);
}
EphyFrecentStore *
ephy_frecent_store_new (void)
{
return g_object_new (EPHY_TYPE_FRECENT_STORE,
NULL);
}
static void
animated_remove_cb (EphyOverviewStore *store,
GtkTreeIter *iter,
gboolean valid,
EphyHistoryService *service)
{
ephy_frecent_store_fetch_urls (EPHY_FRECENT_STORE (store), service);
}
static void
set_url_hidden_cb (EphyHistoryService *service,
gboolean success,
gpointer result_data,
GtkTreeRowReference *ref)
{
EphyOverviewStore *store;
GtkTreePath *path;
GtkTreeIter iter;
if (!success)
return;
store = EPHY_OVERVIEW_STORE (gtk_tree_row_reference_get_model (ref));
path = gtk_tree_row_reference_get_path (ref);
gtk_tree_model_get_iter (GTK_TREE_MODEL (store), &iter, path);
gtk_tree_path_free (path);
ephy_overview_store_animated_remove (store, ref,
(EphyOverviewStoreAnimRemoveFunc)animated_remove_cb,
service);
}
void
ephy_frecent_store_set_hidden (EphyFrecentStore *store,
GtkTreeIter *iter)
{
EphyHistoryService *service;
GtkTreeRowReference *ref;
GtkTreePath *path;
char *uri;
GtkTreeModel *model;
g_return_if_fail (EPHY_IS_FRECENT_STORE (store));
g_return_if_fail (iter != NULL);
model = GTK_TREE_MODEL (store);
gtk_tree_model_get (model, iter,
EPHY_OVERVIEW_STORE_URI, &uri,
-1);
g_object_get (store, "history-service", &service, NULL);
path = gtk_tree_model_get_path (model, iter);
ref = gtk_tree_row_reference_new (model, path);
gtk_tree_path_free (path);
ephy_history_service_set_url_hidden (service,
uri, TRUE, NULL,
(EphyHistoryJobCallback) set_url_hidden_cb,
ref);
g_free (uri);
g_object_unref (service);
}
void
ephy_frecent_store_set_history_length (EphyFrecentStore *store,
gint length)
{
EphyHistoryService *service;
g_return_if_fail (EPHY_IS_FRECENT_STORE (store));
g_return_if_fail (length > 0);
if (store->priv->history_length == length)
return;
store->priv->history_length = length;
g_object_get (G_OBJECT (store), "history-service", &service, NULL);
if (service) {
ephy_frecent_store_fetch_urls (store, service);
g_object_unref (service);
}
}
int
ephy_frecent_store_get_history_length (EphyFrecentStore *store)
{
g_return_val_if_fail (EPHY_IS_FRECENT_STORE (store), 0);
return store->priv->history_length;
}