/*
* Copyright (C) 2002 Jorn Baayen
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "history-dialog.h"
#include "ephy-shell.h"
#include "ephy-embed-shell.h"
#include "ephy-file-helpers.h"
#include "ephy-string.h"
#include "ephy-gui.h"
#include "ephy-dnd.h"
#include "ephy-node-filter.h"
#include "ephy-history-model.h"
#include "eggtreemodelfilter.h"
#include "eggtreemultidnd.h"
#include "ephy-tree-model-sort.h"
#include "toolbar.h"
#include <gtk/gtktreeview.h>
#include <gtk/gtktreestore.h>
#include <gtk/gtkcellrenderertext.h>
#include <bonobo/bonobo-i18n.h>
static GtkTargetEntry url_drag_types [] =
{
{ EPHY_DND_URI_LIST_TYPE, 0, 0 },
{ EPHY_DND_TEXT_TYPE, 0, 1 },
{ EPHY_DND_URL_TYPE, 0, 2 }
};
static int n_url_drag_types = G_N_ELEMENTS (url_drag_types);
#define CONF_HISTORY_SEARCH_TEXT "/apps/epiphany/history/search_text"
#define CONF_HISTORY_SEARCH_TIME "/apps/epiphany/history/search_time"
static void history_dialog_class_init (HistoryDialogClass *klass);
static void history_dialog_init (HistoryDialog *dialog);
static void history_dialog_finalize (GObject *object);
static void history_dialog_set_embedded (HistoryDialog *d,
gboolean embedded);
/* Glade callbacks */
void
history_host_checkbutton_toggled_cb (GtkWidget *widget,
HistoryDialog *dialog);
void
history_time_optionmenu_changed_cb (GtkWidget *widget,
HistoryDialog *dialog);
void
history_entry_changed_cb (GtkWidget *widget,
HistoryDialog *dialog);
void
history_go_button_clicked_cb (GtkWidget *button,
HistoryDialog *dialog);
void
history_ok_button_clicked_cb (GtkWidget *button,
HistoryDialog *dialog);
void
history_clear_button_clicked_cb (GtkWidget *button,
HistoryDialog *dialog);
static GObjectClass *parent_class = NULL;
struct HistoryDialogPrivate
{
GtkWidget *window;
EphyHistory *gh;
EphyNode *root;
EphyNode *pages;
EphyNodeFilter *filter;
GtkTreeView *treeview;
GtkTreeModel *model;
EphyHistoryModel *nodemodel;
GtkTreeModel *filtermodel;
EphyTreeModelSort *sortmodel;
GtkWidget *go_button;
gboolean group;
gboolean embedded;
};
enum
{
PROP_0,
PROP_EMBEDDED
};
enum
{
PROP_WINDOW,
PROP_TREEVIEW,
PROP_WORD,
PROP_TIME,
PROP_GO_BUTTON
};
static const
EphyDialogProperty properties [] =
{
{ PROP_WINDOW, "history_dialog", NULL, PT_NORMAL, NULL },
{ PROP_TREEVIEW, "history_treeview", NULL, PT_NORMAL, NULL },
{ PROP_WORD, "history_entry", CONF_HISTORY_SEARCH_TEXT, PT_NORMAL, NULL },
{ PROP_TIME, "history_time_optionmenu", CONF_HISTORY_SEARCH_TIME, PT_NORMAL, NULL },
{ PROP_GO_BUTTON, "history_go_button", NULL, PT_NORMAL, NULL },
{ -1, NULL, NULL }
};
GType
history_dialog_get_type (void)
{
static GType history_dialog_type = 0;
if (history_dialog_type == 0)
{
static const GTypeInfo our_info =
{
sizeof (HistoryDialogClass),
NULL, /* base_init */
NULL, /* base_finalize */
(GClassInitFunc) history_dialog_class_init,
NULL,
NULL, /* class_data */
sizeof (HistoryDialog),
0, /* n_preallocs */
(GInstanceInitFunc) history_dialog_init
};
history_dialog_type = g_type_register_static (EPHY_EMBED_DIALOG_TYPE,
"HistoryDialog",
&our_info, 0);
}
return history_dialog_type;
}
static void
history_dialog_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
HistoryDialog *d = HISTORY_DIALOG (object);
switch (prop_id)
{
case PROP_EMBEDDED:
history_dialog_set_embedded
(d, g_value_get_boolean (value));
break;
}
}
static void
history_dialog_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
HistoryDialog *d = HISTORY_DIALOG (object);
switch (prop_id)
{
case PROP_EMBEDDED:
g_value_set_boolean (value, d->priv->embedded);
break;
}
}
static void
history_dialog_class_init (HistoryDialogClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
parent_class = g_type_class_peek_parent (klass);
object_class->finalize = history_dialog_finalize;
object_class->set_property = history_dialog_set_property;
object_class->get_property = history_dialog_get_property;
g_object_class_install_property (object_class,
PROP_EMBEDDED,
g_param_spec_boolean ("embedded",
"Show embedded in another widget",
"Show embedded in another widget",
TRUE,
G_PARAM_READWRITE));
}
static void
add_column (HistoryDialog *dialog,
const char *title,
EphyHistoryModelColumn column)
{
GtkTreeViewColumn *gcolumn;
GtkCellRenderer *renderer;
gcolumn = (GtkTreeViewColumn *) gtk_tree_view_column_new ();
renderer = gtk_cell_renderer_text_new ();
gtk_tree_view_column_pack_start (gcolumn, renderer, TRUE);
gtk_tree_view_column_set_attributes (gcolumn, renderer,
"text", column,
NULL);
gtk_tree_view_column_set_sizing (gcolumn,
GTK_TREE_VIEW_COLUMN_AUTOSIZE);
gtk_tree_view_column_set_sort_column_id (gcolumn, column);
gtk_tree_view_column_set_title (gcolumn, title);
gtk_tree_view_append_column (GTK_TREE_VIEW (dialog->priv->treeview),
gcolumn);
}
static void
history_view_selection_changed_cb (GtkTreeSelection *selection,
HistoryDialog *dialog)
{
if (gtk_tree_selection_count_selected_rows (selection))
gtk_widget_set_sensitive (GTK_WIDGET (dialog->priv->go_button), TRUE);
else
gtk_widget_set_sensitive (GTK_WIDGET (dialog->priv->go_button), FALSE);
}
static void
history_view_row_activated_cb (GtkTreeView *treeview,
GtkTreePath *path,
GtkTreeViewColumn *column,
HistoryDialog *dialog)
{
GtkTreeIter iter, iter2;
EphyNode *node;
EphyEmbed *embed;
const char *location;
gtk_tree_model_get_iter (GTK_TREE_MODEL (dialog->priv->sortmodel), &iter, path);
gtk_tree_model_sort_convert_iter_to_child_iter
(GTK_TREE_MODEL_SORT (dialog->priv->sortmodel), &iter2, &iter);
egg_tree_model_filter_convert_iter_to_child_iter
(EGG_TREE_MODEL_FILTER (dialog->priv->filtermodel), &iter, &iter2);
node = ephy_history_model_node_from_iter (dialog->priv->nodemodel, &iter);
location = ephy_node_get_property_string (node, EPHY_NODE_PAGE_PROP_LOCATION);
g_return_if_fail (location != NULL);
embed = ephy_embed_dialog_get_embed (EPHY_EMBED_DIALOG (dialog));
ephy_embed_load_url (embed, location);
}
static void
node_from_sort_iter_cb (EphyTreeModelSort *model,
GtkTreeIter *iter,
void **node,
HistoryDialog *dialog)
{
GtkTreeIter filter_iter, node_iter;
gtk_tree_model_sort_convert_iter_to_child_iter (GTK_TREE_MODEL_SORT (model),
&filter_iter, iter);
egg_tree_model_filter_convert_iter_to_child_iter (EGG_TREE_MODEL_FILTER (dialog->priv->filtermodel),
&node_iter, &filter_iter);
*node = ephy_history_model_node_from_iter (EPHY_HISTORY_MODEL (dialog->priv->nodemodel), &node_iter);
g_return_if_fail (*node != NULL);
}
static void
history_dialog_setup_view (HistoryDialog *dialog)
{
dialog->priv->nodemodel = ephy_history_model_new (dialog->priv->root,
dialog->priv->pages,
dialog->priv->filter);
dialog->priv->filtermodel = egg_tree_model_filter_new (GTK_TREE_MODEL (dialog->priv->nodemodel),
NULL);
egg_tree_model_filter_set_visible_column (EGG_TREE_MODEL_FILTER (dialog->priv->filtermodel),
EPHY_HISTORY_MODEL_COL_VISIBLE);
dialog->priv->sortmodel = EPHY_TREE_MODEL_SORT (
ephy_tree_model_sort_new (GTK_TREE_MODEL (dialog->priv->filtermodel)));
ephy_tree_model_sort_set_drag_property
(dialog->priv->sortmodel, EPHY_NODE_PAGE_PROP_LOCATION);
g_signal_connect_object (G_OBJECT (dialog->priv->sortmodel),
"node_from_iter",
G_CALLBACK (node_from_sort_iter_cb),
dialog,
0);
gtk_tree_view_set_model (dialog->priv->treeview,
GTK_TREE_MODEL (dialog->priv->sortmodel));
egg_tree_multi_drag_add_drag_support (GTK_TREE_VIEW (dialog->priv->treeview));
gtk_tree_view_enable_model_drag_source (GTK_TREE_VIEW (dialog->priv->treeview),
GDK_BUTTON1_MASK,
url_drag_types, n_url_drag_types,
GDK_ACTION_COPY);
add_column (dialog, _("Title"), EPHY_HISTORY_MODEL_COL_TITLE);
add_column (dialog, _("Location"), EPHY_HISTORY_MODEL_COL_LOCATION);
add_column (dialog, _("Last Visit"), EPHY_HISTORY_MODEL_COL_LAST_VISIT);
g_signal_connect (dialog->priv->treeview,
"row_activated",
G_CALLBACK (history_view_row_activated_cb),
dialog);
g_signal_connect (gtk_tree_view_get_selection (dialog->priv->treeview),
"changed",
G_CALLBACK (history_view_selection_changed_cb),
dialog);
}
static GTime
get_date_filter (int filter_type,
GTime atime)
{
GDate date, current_date;
struct tm tm;
g_date_clear (¤t_date, 1);
g_date_set_time (¤t_date, time (NULL));
g_date_clear (&date, 1);
g_date_set_time (&date, atime);
switch (filter_type)
{
/* Always */
case 0:
return 0;
/* Today */
case 1:
break;
/* Last two days */
case 2:
g_date_subtract_days (¤t_date, 1);
break;
/* Last three days */
case 3:
g_date_subtract_days (¤t_date, 2);
break;
/* Week */
case 4:
g_date_subtract_days (¤t_date, 7);
break;
/* Two weeks */
case 5:
g_date_subtract_days (¤t_date, 14);
break;
default:
break;
}
g_date_to_struct_tm (¤t_date, &tm);
return mktime (&tm);
}
static void
history_dialog_setup_filter (HistoryDialog *dialog)
{
GValue word = {0, };
GValue atime = {0, };
const char *search_text;
GTime date_filter;
ephy_dialog_get_value (EPHY_DIALOG(dialog), PROP_WORD, &word);
search_text = g_value_get_string (&word);
ephy_dialog_get_value (EPHY_DIALOG(dialog), PROP_TIME, &atime);
date_filter = get_date_filter (g_value_get_int (&atime), time (NULL));
GDK_THREADS_ENTER ();
ephy_node_filter_empty (dialog->priv->filter);
ephy_node_filter_add_expression (dialog->priv->filter,
ephy_node_filter_expression_new (EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_CONTAINS,
EPHY_NODE_PAGE_PROP_TITLE,
search_text),
0);
ephy_node_filter_add_expression (dialog->priv->filter,
ephy_node_filter_expression_new (EPHY_NODE_FILTER_EXPRESSION_STRING_PROP_CONTAINS,
EPHY_NODE_PAGE_PROP_LOCATION,
search_text),
0);
ephy_node_filter_add_expression (dialog->priv->filter,
ephy_node_filter_expression_new (EPHY_NODE_FILTER_EXPRESSION_INT_PROP_BIGGER_THAN,
EPHY_NODE_PAGE_PROP_LAST_VISIT,
date_filter),
1);
ephy_node_filter_done_changing (dialog->priv->filter);
GDK_THREADS_LEAVE ();
}
static void
history_dialog_init (HistoryDialog *dialog)
{
EphyEmbedShell *ges;
dialog->priv = g_new0 (HistoryDialogPrivate, 1);
ges = EPHY_EMBED_SHELL (ephy_shell);
dialog->priv->gh = ephy_embed_shell_get_global_history (ges);
g_return_if_fail (dialog->priv->gh != NULL);
dialog->priv->root = ephy_history_get_hosts (dialog->priv->gh);
dialog->priv->pages = ephy_history_get_pages (dialog->priv->gh);
dialog->priv->filter = ephy_node_filter_new ();
}
static void
history_dialog_set_embedded (HistoryDialog *dialog,
gboolean embedded)
{
const char *icon_path;
dialog->priv->embedded = embedded;
ephy_dialog_construct (EPHY_DIALOG (dialog),
properties,
"epiphany.glade",
embedded ?
"history_dock_box" :
"history_dialog");
dialog->priv->window = ephy_dialog_get_control (EPHY_DIALOG (dialog), PROP_WINDOW);
dialog->priv->go_button = ephy_dialog_get_control (EPHY_DIALOG (dialog), PROP_GO_BUTTON);
dialog->priv->treeview = GTK_TREE_VIEW (
ephy_dialog_get_control (EPHY_DIALOG(dialog),
PROP_TREEVIEW));
icon_path = ephy_file ("epiphany-history.png");
gtk_window_set_icon_from_file (GTK_WINDOW(dialog->priv->window), icon_path, NULL);
history_dialog_setup_view (dialog);
history_dialog_setup_filter (dialog);
}
static void
history_dialog_finalize (GObject *object)
{
HistoryDialog *dialog;
g_return_if_fail (object != NULL);
g_return_if_fail (IS_HISTORY_DIALOG (object));
dialog = HISTORY_DIALOG (object);
g_return_if_fail (dialog->priv != NULL);
g_object_unref (G_OBJECT (dialog->priv->sortmodel));
g_object_unref (G_OBJECT (dialog->priv->filtermodel));
g_object_unref (G_OBJECT (dialog->priv->nodemodel));
g_free (dialog->priv);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
EphyDialog *
history_dialog_new (EphyEmbed *embed,
gboolean embedded)
{
HistoryDialog *dialog;
dialog = HISTORY_DIALOG (g_object_new (HISTORY_DIALOG_TYPE,
"embedded", embedded,
"EphyEmbed", embed,
NULL));
return EPHY_DIALOG(dialog);
}
EphyDialog *
history_dialog_new_with_parent (GtkWidget *window,
EphyEmbed *embed,
gboolean embedded)
{
HistoryDialog *dialog;
dialog = HISTORY_DIALOG (g_object_new (HISTORY_DIALOG_TYPE,
"embedded", embedded,
"EphyEmbed", embed,
"ParentWindow", window,
NULL));
return EPHY_DIALOG(dialog);
}
void
history_entry_changed_cb (GtkWidget *widget,
HistoryDialog *dialog)
{
if (dialog->priv->treeview == NULL) return;
history_dialog_setup_filter (dialog);
}
void
history_time_optionmenu_changed_cb (GtkWidget *widget,
HistoryDialog *dialog)
{
if (dialog->priv->treeview == NULL) return;
history_dialog_setup_filter (dialog);
}
void
history_go_button_clicked_cb (GtkWidget *button,
HistoryDialog *dialog)
{
GtkTreeSelection *selection;
GtkTreeIter iter, iter2;
EphyNode *node;
EphyEmbed *embed;
const char *location;
selection = gtk_tree_view_get_selection (dialog->priv->treeview);
g_return_if_fail (selection != NULL);
gtk_tree_selection_get_selected (selection, NULL, &iter);
gtk_tree_model_sort_convert_iter_to_child_iter
(GTK_TREE_MODEL_SORT (dialog->priv->sortmodel), &iter2, &iter);
egg_tree_model_filter_convert_iter_to_child_iter
(EGG_TREE_MODEL_FILTER (dialog->priv->filtermodel), &iter, &iter2);
node = ephy_history_model_node_from_iter (dialog->priv->nodemodel, &iter);
location = ephy_node_get_property_string (node, EPHY_NODE_PAGE_PROP_LOCATION);
g_return_if_fail (location != NULL);
embed = ephy_embed_dialog_get_embed (EPHY_EMBED_DIALOG (dialog));
ephy_embed_load_url (embed, location);
}
void
history_ok_button_clicked_cb (GtkWidget *button,
HistoryDialog *dialog)
{
g_object_unref (G_OBJECT(dialog));
}
static void
clear_history_dialog_response_cb (GtkDialog *dialog, gint response,
HistoryDialog *history_dialog)
{
const GList *windows;
Session *session;
gtk_widget_destroy (GTK_WIDGET (dialog));
if (response != GTK_RESPONSE_OK)
return;
session = ephy_shell_get_session (ephy_shell);
windows = session_get_windows (session);
for (; windows != NULL; windows = windows->next)
{
Toolbar *t;
t = ephy_window_get_toolbar (EPHY_WINDOW (windows->data));
toolbar_clear_location_history (t);
}
ephy_history_clear (history_dialog->priv->gh);
}
void
history_clear_button_clicked_cb (GtkWidget *button,
HistoryDialog *dialog)
{
GtkWidget *clear;
GtkWidget *label;
GtkWidget *vbox;
GtkWidget *hbox;
GtkWidget *image;
char *str;
clear = gtk_dialog_new_with_buttons (_("Clear history"),
GTK_WINDOW (dialog->priv->window),
GTK_DIALOG_DESTROY_WITH_PARENT |
GTK_DIALOG_NO_SEPARATOR,
GTK_STOCK_CANCEL,
GTK_RESPONSE_CANCEL,
GTK_STOCK_CLEAR,
GTK_RESPONSE_OK,
NULL);
gtk_dialog_set_default_response (GTK_DIALOG (clear), GTK_RESPONSE_OK);
gtk_container_set_border_width (GTK_CONTAINER (clear), 6);
gtk_box_set_spacing (GTK_BOX (GTK_DIALOG (clear)->vbox), 12);
hbox = gtk_hbox_new (FALSE, 6);
gtk_widget_show (hbox);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (clear)->vbox), hbox,
TRUE, TRUE, 0);
image = gtk_image_new_from_stock (GTK_STOCK_DIALOG_WARNING,
GTK_ICON_SIZE_DIALOG);
gtk_misc_set_alignment (GTK_MISC (image), 0.5, 0.0);
gtk_widget_show (image);
gtk_box_pack_start (GTK_BOX (hbox), image, TRUE, TRUE, 0);
vbox = gtk_vbox_new (FALSE, 6);
gtk_widget_show (vbox);
gtk_box_pack_start (GTK_BOX (hbox), vbox, TRUE, TRUE, 0);
label = gtk_label_new (NULL);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
str = g_strconcat ("<b>","<big>", _("Clear browsing history?"),"</big>", "</b>", NULL);
gtk_label_set_markup (GTK_LABEL (label), str);
g_free (str);
gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
gtk_widget_show (label);
label = gtk_label_new (_("Clearing the browsing history will cause all history items to be permanently deleted."));
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
gtk_box_pack_start (GTK_BOX (vbox), label, TRUE, TRUE, 0);
gtk_widget_show (label);
g_signal_connect (clear, "response",
G_CALLBACK (clear_history_dialog_response_cb), dialog);
gtk_widget_show (clear);
}