/* * 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 #include #include #include 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)); } void history_clear_button_clicked_cb (GtkWidget *button, HistoryDialog *dialog) { const GList *windows; Session *session; 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 (dialog->priv->gh); }