diff options
author | Cosimo Cecchi <cosimoc@src.gnome.org> | 2007-11-07 03:56:42 +0800 |
---|---|---|
committer | Cosimo Cecchi <cosimoc@src.gnome.org> | 2007-11-07 03:56:42 +0800 |
commit | a93d14f5580f252ff7eafea2f83aa835cca40f10 (patch) | |
tree | 28c9398f72c271357650cdecdc45c9f246187345 | |
parent | ebd3378c3fc433823cf614528745a8e66f56543a (diff) | |
download | gsoc2013-epiphany-a93d14f5580f252ff7eafea2f83aa835cca40f10.tar gsoc2013-epiphany-a93d14f5580f252ff7eafea2f83aa835cca40f10.tar.gz gsoc2013-epiphany-a93d14f5580f252ff7eafea2f83aa835cca40f10.tar.bz2 gsoc2013-epiphany-a93d14f5580f252ff7eafea2f83aa835cca40f10.tar.lz gsoc2013-epiphany-a93d14f5580f252ff7eafea2f83aa835cca40f10.tar.xz gsoc2013-epiphany-a93d14f5580f252ff7eafea2f83aa835cca40f10.tar.zst gsoc2013-epiphany-a93d14f5580f252ff7eafea2f83aa835cca40f10.zip |
Adds a column in the history window showing the date and the time of the
visit, substitutes radiobuttons with checkboxes in the View menu of the window.
Also, adds lib/ephy-time-helpers that contains a strftime() implementation.
Fix bug #380156.
svn path=/trunk/; revision=7641
-rw-r--r-- | data/ui/epiphany-history-window-ui.xml | 2 | ||||
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/ephy-time-helpers.c | 283 | ||||
-rw-r--r-- | lib/ephy-time-helpers.h | 38 | ||||
-rw-r--r-- | src/ephy-history-window.c | 190 |
5 files changed, 455 insertions, 60 deletions
diff --git a/data/ui/epiphany-history-window-ui.xml b/data/ui/epiphany-history-window-ui.xml index 228afac62..5592ec2ef 100644 --- a/data/ui/epiphany-history-window-ui.xml +++ b/data/ui/epiphany-history-window-ui.xml @@ -24,7 +24,7 @@ <menu name="ViewMenu" action="View"> <menuitem name="ViewTitleMenu" action="ViewTitle"/> <menuitem name="ViewAddressMenu" action="ViewAddress"/> - <menuitem name="ViewTitleAddressMenu" action="ViewTitleAddress"/> + <menuitem name="ViewDateTimeMenu" action="ViewDateTime"/> </menu> <menu name="HelpMenu" action="Help"> diff --git a/lib/Makefile.am b/lib/Makefile.am index 8fd4d88d3..5c3236bb6 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -27,6 +27,7 @@ NOINST_H_FILES = \ ephy-signal-accumulator.h \ ephy-string.h \ ephy-stock-icons.h \ + ephy-time-helpers.h \ ephy-zoom.h TYPES_H_FILES = \ @@ -66,6 +67,7 @@ libephymisc_la_SOURCES = \ ephy-state.c \ ephy-string.c \ ephy-stock-icons.c \ + ephy-time-helpers.c \ ephy-zoom.c \ $(INST_H_FILES) \ $(NOINST_H_FILES) diff --git a/lib/ephy-time-helpers.c b/lib/ephy-time-helpers.c new file mode 100644 index 000000000..383468def --- /dev/null +++ b/lib/ephy-time-helpers.c @@ -0,0 +1,283 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * arch-tag: File containing code cut and pasted from elsewhere + * + * Copyright © 2000 Eazel, Inc. + * Copyright © 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Authors: John Sullivan <sullivan@eazel.com> + * Jorn Baayen + */ + +/* Following code is copied from Rhythmbox rb-cut-and-paste-code.c */ + +#include <config.h> + +#include <string.h> +#include <glib/gi18n.h> +#include <glib.h> + +#include "ephy-time-helpers.h" + +/* Legal conversion specifiers, as specified in the C standard. */ +#define C_STANDARD_STRFTIME_CHARACTERS "aAbBcdHIjmMpSUwWxXyYZ" +#define C_STANDARD_NUMERIC_STRFTIME_CHARACTERS "dHIjmMSUwWyY" +#define SUS_EXTENDED_STRFTIME_MODIFIERS "EO" + +/** + * eel_strdup_strftime: + * + * Cover for standard date-and-time-formatting routine strftime that returns + * a newly-allocated string of the correct size. The caller is responsible + * for g_free-ing the returned string. + * + * Besides the buffer management, there are two differences between this + * and the library strftime: + * + * 1) The modifiers "-" and "_" between a "%" and a numeric directive + * are defined as for the GNU version of strftime. "-" means "do not + * pad the field" and "_" means "pad with spaces instead of zeroes". + * 2) Non-ANSI extensions to strftime are flagged at runtime with a + * warning, so it's easy to notice use of the extensions without + * testing with multiple versions of the library. + * + * @format: format string to pass to strftime. See strftime documentation + * for details. + * @time_pieces: date/time, in struct format. + * + * Return value: Newly allocated string containing the formatted time. + **/ + +char * +eel_strdup_strftime (const char *format, struct tm *time_pieces) +{ + GString *string; + const char *remainder, *percent; + char code[4], buffer[512]; + char *piece, *result, *converted; + size_t string_length; + gboolean strip_leading_zeros, turn_leading_zeros_to_spaces; + char modifier; + int i; + + /* Format could be translated, and contain UTF-8 chars, + * so convert to locale encoding which strftime uses */ + converted = g_locale_from_utf8 (format, -1, NULL, NULL, NULL); + g_return_val_if_fail (converted != NULL, NULL); + + string = g_string_new (""); + remainder = converted; + + /* Walk from % character to % character. */ + for (;;) { + percent = strchr (remainder, '%'); + if (percent == NULL) { + g_string_append (string, remainder); + break; + } + g_string_append_len (string, remainder, + percent - remainder); + + /* Handle the "%" character. */ + remainder = percent + 1; + switch (*remainder) { + case '-': + strip_leading_zeros = TRUE; + turn_leading_zeros_to_spaces = FALSE; + remainder++; + break; + case '_': + strip_leading_zeros = FALSE; + turn_leading_zeros_to_spaces = TRUE; + remainder++; + break; + case '%': + g_string_append_c (string, '%'); + remainder++; + continue; + case '\0': + g_warning ("Trailing %% passed to eel_strdup_strftime"); + g_string_append_c (string, '%'); + continue; + default: + strip_leading_zeros = FALSE; + turn_leading_zeros_to_spaces = FALSE; + break; + } + + modifier = 0; + if (strchr (SUS_EXTENDED_STRFTIME_MODIFIERS, *remainder) != NULL) { + modifier = *remainder; + remainder++; + + if (*remainder == 0) { + g_warning ("Unfinished %%%c modifier passed to eel_strdup_strftime", modifier); + break; + } + } + + if (strchr (C_STANDARD_STRFTIME_CHARACTERS, *remainder) == NULL) { + g_warning ("eel_strdup_strftime does not support " + "non-standard escape code %%%c", + *remainder); + } + + /* Convert code to strftime format. We have a fixed + * limit here that each code can expand to a maximum + * of 512 bytes, which is probably OK. There's no + * limit on the total size of the result string. + */ + i = 0; + code[i++] = '%'; + if (modifier != 0) { +#ifdef HAVE_STRFTIME_EXTENSION + code[i++] = modifier; +#endif + } + code[i++] = *remainder; + code[i++] = '\0'; + string_length = strftime (buffer, sizeof (buffer), + code, time_pieces); + if (string_length == 0) { + /* We could put a warning here, but there's no + * way to tell a successful conversion to + * empty string from a failure. + */ + buffer[0] = '\0'; + } + + /* Strip leading zeros if requested. */ + piece = buffer; + if (strip_leading_zeros || turn_leading_zeros_to_spaces) { + if (strchr (C_STANDARD_NUMERIC_STRFTIME_CHARACTERS, *remainder) == NULL) { + g_warning ("eel_strdup_strftime does not support " + "modifier for non-numeric escape code %%%c%c", + remainder[-1], + *remainder); + } + if (*piece == '0') { + do { + piece++; + } while (*piece == '0'); + if (!g_ascii_isdigit (*piece)) { + piece--; + } + } + if (turn_leading_zeros_to_spaces) { + memset (buffer, ' ', piece - buffer); + piece = buffer; + } + } + remainder++; + + /* Add this piece. */ + g_string_append (string, piece); + } + + /* Convert the string back into utf-8. */ + result = g_locale_to_utf8 (string->str, -1, NULL, NULL, NULL); + + g_string_free (string, TRUE); + g_free (converted); + + return result; +} + +/* Based on evolution/mail/message-list.c:filter_date() */ +char * +ephy_time_helpers_utf_friendly_time (time_t date) +{ + time_t nowdate; + time_t yesdate; + struct tm then, now, yesterday; + const char *format = NULL; + char *str = NULL; + gboolean done = FALSE; + + nowdate = time (NULL); + + if (date == 0) + return NULL; + + localtime_r (&date, &then); + localtime_r (&nowdate, &now); + + if (then.tm_mday == now.tm_mday && + then.tm_mon == now.tm_mon && + then.tm_year == now.tm_year) { + /* Translators: "friendly time" string for the current day, strftime format. like "Today 12:34 am" */ + format = _("Today %I:%M %p"); + done = TRUE; + } + + if (! done) { + yesdate = nowdate - 60 * 60 * 24; + localtime_r (&yesdate, &yesterday); + if (then.tm_mday == yesterday.tm_mday && + then.tm_mon == yesterday.tm_mon && + then.tm_year == yesterday.tm_year) { + /* Translators: "friendly time" string for the previous day, + * strftime format. e.g. "Yesterday 12:34 am" + */ + format = _("Yesterday %I:%M %p"); + done = TRUE; + } + } + + if (! done) { + int i; + for (i = 2; i < 7; i++) { + yesdate = nowdate - 60 * 60 * 24 * i; + localtime_r (&yesdate, &yesterday); + if (then.tm_mday == yesterday.tm_mday && + then.tm_mon == yesterday.tm_mon && + then.tm_year == yesterday.tm_year) { + /* Translators: "friendly time" string for a day in the current week, + * strftime format. e.g. "Wed 12:34 am" + */ + format = _("%a %I:%M %p"); + done = TRUE; + break; + } + } + } + + if (! done) { + if (then.tm_year == now.tm_year) { + /* Translators: "friendly time" string for a day in the current year, + * strftime format. e.g. "Feb 12 12:34 am" + */ + format = _("%b %d %I:%M %p"); + } else { + /* Translators: "friendly time" string for a day in a different year, + * strftime format. e.g. "Feb 12 1997" + */ + format = _("%b %d %Y"); + } + } + + if (format != NULL) { + str = eel_strdup_strftime (format, &then); + } + + if (str == NULL) { + /* impossible time or broken locale settings */ + str = g_strdup (_("Unknown")); + } + + return str; +} diff --git a/lib/ephy-time-helpers.h b/lib/ephy-time-helpers.h new file mode 100644 index 000000000..a91788479 --- /dev/null +++ b/lib/ephy-time-helpers.h @@ -0,0 +1,38 @@ +/* + * arch-tag: Header file for code cut and pasted from elsewhere + * + * Copyright © 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +/* Following code is copied from Rhythmbox rb-cut-and-paste-code.h */ + +#include <time.h> + +#ifndef EPHY_TIME_HELPERS_H +#define EPHY_TIME_HELPERS_H + +G_BEGIN_DECLS + +char *eel_strdup_strftime (const char *format, + struct tm *time_pieces); + +char * ephy_time_helpers_utf_friendly_time (time_t date); + +G_END_DECLS + +#endif /* EPHY_TIME_HELPERS_H */ diff --git a/src/ephy-history-window.c b/src/ephy-history-window.c index 214cf240f..cf2758918 100644 --- a/src/ephy-history-window.c +++ b/src/ephy-history-window.c @@ -47,7 +47,6 @@ #include <string.h> #include <time.h> -#include "ephy-node-view.h" #include "ephy-window.h" #include "ephy-history-window.h" #include "ephy-shell.h" @@ -69,6 +68,7 @@ #include "ephy-bookmarks-ui.h" #include "ephy-prefs.h" #include "ephy-gui.h" +#include "ephy-time-helpers.h" static const GtkTargetEntry page_drag_types [] = { @@ -112,6 +112,8 @@ static void cmd_select_all (GtkAction *action, EphyHistoryWindow *editor); static void cmd_help_contents (GtkAction *action, EphyHistoryWindow *editor); +static void cmd_view_columns (GtkAction *action, + EphyHistoryWindow *view); static void search_entry_search_cb (GtkWidget *entry, char *search_text, EphyHistoryWindow *editor); @@ -138,6 +140,7 @@ struct _EphyHistoryWindowPrivate EphyNode *selected_site; GtkTreeViewColumn *title_col; GtkTreeViewColumn *address_col; + GtkTreeViewColumn *datetime_col; }; enum @@ -214,23 +217,22 @@ static const GtkActionEntry ephy_history_ui_entries [] = { G_CALLBACK (window_cmd_help_about) }, }; -enum +typedef enum { - VIEW_TITLE, - VIEW_ADDRESS, - VIEW_TITLE_AND_ADDRESS -}; + VIEW_TITLE = 1 << 0, + VIEW_ADDRESS = 1 << 1, + VIEW_DATETIME = 1 << 2 +} EphyHistoryWindowColumns; -static const GtkRadioActionEntry ephy_history_radio_entries [] = +static const GtkToggleActionEntry ephy_history_toggle_entries [] = { /* View Menu */ { "ViewTitle", NULL, N_("_Title"), NULL, - N_("Show only the title column"), VIEW_TITLE }, + N_("Show the title column"), G_CALLBACK (cmd_view_columns), TRUE }, { "ViewAddress", NULL, N_("_Address"), NULL, - N_("Show only the address column"), VIEW_ADDRESS }, - { "ViewTitleAddress", NULL, N_("Title a_nd Address"), NULL, - N_("Show both the title and address columns"), - VIEW_TITLE_AND_ADDRESS } + N_("Show the address column"), G_CALLBACK (cmd_view_columns), TRUE }, + { "ViewDateTime", NULL, N_("_Date and Time"), NULL, + N_("Show the date and time column"), G_CALLBACK (cmd_view_columns), TRUE } }; static void @@ -518,45 +520,70 @@ cmd_help_contents (GtkAction *action, } static void -set_columns_visibility (EphyHistoryWindow *view, int value) +set_column_visibility (EphyHistoryWindow *view, + const char *action_name, + gboolean active) { - switch (value) + if (strcmp (action_name, "ViewTitle") == 0) { - case VIEW_TITLE: - gtk_tree_view_column_set_visible (view->priv->title_col, TRUE); - gtk_tree_view_column_set_visible (view->priv->address_col, FALSE); - break; - case VIEW_ADDRESS: - gtk_tree_view_column_set_visible (view->priv->title_col, FALSE); - gtk_tree_view_column_set_visible (view->priv->address_col, TRUE); - break; - case VIEW_TITLE_AND_ADDRESS: - gtk_tree_view_column_set_visible (view->priv->title_col, TRUE); - gtk_tree_view_column_set_visible (view->priv->address_col, TRUE); - break; + gtk_tree_view_column_set_visible (view->priv->title_col, active); + } + if (strcmp (action_name, "ViewAddress") == 0) + { + gtk_tree_view_column_set_visible (view->priv->address_col, active); + } + if (strcmp (action_name, "ViewDateTime") == 0) + { + gtk_tree_view_column_set_visible (view->priv->datetime_col, active); } } static void +set_all_columns_visibility (EphyHistoryWindow *view, + EphyHistoryWindowColumns details_value) +{ + GtkActionGroup *action_group; + GtkAction *action_title, *action_address, *action_datetime; + + action_group = view->priv->action_group; + action_title = gtk_action_group_get_action (action_group, "ViewTitle"); + action_address = gtk_action_group_get_action (action_group, "ViewAddress"); + action_datetime = gtk_action_group_get_action (action_group, "ViewDateTime"); + + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action_title), (details_value & VIEW_TITLE)); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action_address), (details_value & VIEW_ADDRESS)); + gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action_datetime), (details_value & VIEW_DATETIME)); +} + +static void cmd_view_columns (GtkAction *action, - GtkRadioAction *current, EphyHistoryWindow *view) { - int value; + gboolean active; + const char *action_name; GSList *svalues = NULL; - value = gtk_radio_action_get_current_value (current); - set_columns_visibility (view, value); + active = gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (action)); + action_name = gtk_action_get_name (action); + set_column_visibility (view, action_name, active); + + svalues = eel_gconf_get_string_list (CONF_HISTORY_VIEW_DETAILS); - switch (value) + if (active) { - case VIEW_TITLE: - svalues = g_slist_append (svalues, (gpointer)"title"); - break; - case VIEW_TITLE_AND_ADDRESS: - svalues = g_slist_append (svalues, (gpointer)"title"); - svalues = g_slist_append (svalues, (gpointer)"address"); - break; + if (!g_slist_find_custom (svalues, (gpointer) action_name, (GCompareFunc) strcmp)) + { + svalues = g_slist_append (svalues, (gpointer) action_name); + } + } + else + { + GSList *delete; + delete = g_slist_find_custom (svalues, (gpointer) action_name, (GCompareFunc) strcmp); + if (delete) + { + svalues = g_slist_delete_link (svalues, delete); + } } eel_gconf_set_string_list (CONF_HISTORY_VIEW_DETAILS, svalues); @@ -1185,36 +1212,75 @@ provide_favicon (EphyNode *node, GValue *value, gpointer user_data) } static void +convert_cell_data_func (GtkTreeViewColumn *column, + GtkCellRenderer *renderer, + GtkTreeModel *model, + GtkTreeIter *iter, + gpointer user_data) +{ + int col_id = (int) user_data; + int value; + time_t time; + char *friendly; + + gtk_tree_model_get (model, iter, + col_id, + &value, + -1); + time = (time_t) value; + + friendly = ephy_time_helpers_utf_friendly_time (time); + g_object_set (renderer, "text", friendly, NULL); + g_free (friendly); +} + +static void +parse_time_into_date (GtkTreeViewColumn *column, + int column_id) +{ + GList *renderers_list; + GtkCellRenderer *renderer; + + renderers_list = gtk_tree_view_column_get_cell_renderers (column); + renderer = GTK_CELL_RENDERER (renderers_list->data); + gtk_tree_view_column_set_cell_data_func (column, renderer, + (GtkTreeCellDataFunc) convert_cell_data_func, + (gpointer) column_id, + NULL); + g_list_free (renderers_list); +} +static void view_selection_changed_cb (GtkWidget *view, EphyHistoryWindow *editor) { ephy_history_window_update_menu (editor); } -static int +static EphyHistoryWindowColumns get_details_value (void) { - int value; + guint value = 0; GSList *svalues; svalues = eel_gconf_get_string_list (CONF_HISTORY_VIEW_DETAILS); - if (svalues == NULL) return VIEW_TITLE_AND_ADDRESS; - - if (g_slist_find_custom (svalues, "title", (GCompareFunc)strcmp) && - g_slist_find_custom (svalues, "address", (GCompareFunc)strcmp)) + if (svalues == NULL) { - value = VIEW_TITLE_AND_ADDRESS; + svalues = g_slist_append (svalues, (gpointer) "ViewAddress"); + svalues = g_slist_append (svalues, (gpointer) "ViewTitle"); + eel_gconf_set_string_list (CONF_HISTORY_VIEW_DETAILS, svalues); + return (VIEW_ADDRESS | VIEW_TITLE); } - else if (g_slist_find_custom (svalues, "title", (GCompareFunc)strcmp)) + + if (g_slist_find_custom (svalues, "ViewTitle", (GCompareFunc)strcmp)) { - value = VIEW_TITLE; + value |= VIEW_TITLE; } - else if (g_slist_find_custom (svalues, "address", (GCompareFunc)strcmp)) + if (g_slist_find_custom (svalues, "ViewAddress", (GCompareFunc)strcmp)) { - value = VIEW_ADDRESS; + value |= VIEW_ADDRESS; } - else + if (g_slist_find_custom (svalues, "ViewDateTime", (GCompareFunc)strcmp)) { - value = VIEW_TITLE_AND_ADDRESS; + value |= VIEW_DATETIME; } g_slist_foreach (svalues, (GFunc) g_free, NULL); @@ -1234,7 +1300,8 @@ ephy_history_window_construct (EphyHistoryWindow *editor) EphyNode *node; GtkUIManager *ui_merge; GtkActionGroup *action_group; - int url_col_id, title_col_id, details_value; + int url_col_id, title_col_id, datetime_col_id; + EphyHistoryWindowColumns details_value; ephy_gui_ensure_window_group (GTK_WINDOW (editor)); @@ -1258,12 +1325,10 @@ ephy_history_window_construct (EphyHistoryWindow *editor) G_N_ELEMENTS (ephy_history_ui_entries), editor); details_value = get_details_value (); - gtk_action_group_add_radio_actions (action_group, - ephy_history_radio_entries, - G_N_ELEMENTS (ephy_history_radio_entries), - details_value, - G_CALLBACK (cmd_view_columns), - editor); + gtk_action_group_add_toggle_actions (action_group, + ephy_history_toggle_entries, + G_N_ELEMENTS (ephy_history_toggle_entries), + editor); gtk_ui_manager_insert_action_group (ui_merge, action_group, 0); @@ -1374,6 +1439,13 @@ ephy_history_window_construct (EphyHistoryWindow *editor) EPHY_NODE_VIEW_SORTABLE, NULL, &col); gtk_tree_view_column_set_max_width (col, 200); editor->priv->address_col = col; + datetime_col_id = ephy_node_view_add_column (EPHY_NODE_VIEW (pages_view), _("Date"), + G_TYPE_INT, EPHY_NODE_PAGE_PROP_LAST_VISIT, + EPHY_NODE_VIEW_SORTABLE, NULL, &col); + gtk_tree_view_column_set_max_width (col, 200); + editor->priv->datetime_col = col; + parse_time_into_date (editor->priv->datetime_col, datetime_col_id); + ephy_node_view_enable_drag_source (EPHY_NODE_VIEW (pages_view), page_drag_types, G_N_ELEMENTS (page_drag_types), @@ -1410,7 +1482,7 @@ ephy_history_window_construct (EphyHistoryWindow *editor) "history_paned", 130); - set_columns_visibility (editor, details_value); + set_all_columns_visibility (editor, details_value); setup_filters (editor, TRUE, TRUE); } |