aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPeter Williams <peterw@src.gnome.org>2000-08-16 02:16:39 +0800
committerPeter Williams <peterw@src.gnome.org>2000-08-16 02:16:39 +0800
commit7a12fa1fe21ca189e2800a27682bc2cc44396b6a (patch)
tree6914ce670ab90b549869dfdc97bbc67df0d3378c
parent52af76d0932bfc2b1acd622a681328c01c946382 (diff)
downloadgsoc2013-evolution-7a12fa1fe21ca189e2800a27682bc2cc44396b6a.tar
gsoc2013-evolution-7a12fa1fe21ca189e2800a27682bc2cc44396b6a.tar.gz
gsoc2013-evolution-7a12fa1fe21ca189e2800a27682bc2cc44396b6a.tar.bz2
gsoc2013-evolution-7a12fa1fe21ca189e2800a27682bc2cc44396b6a.tar.lz
gsoc2013-evolution-7a12fa1fe21ca189e2800a27682bc2cc44396b6a.tar.xz
gsoc2013-evolution-7a12fa1fe21ca189e2800a27682bc2cc44396b6a.tar.zst
gsoc2013-evolution-7a12fa1fe21ca189e2800a27682bc2cc44396b6a.zip
Allow filtering / vfoldering based on time or date
svn path=/trunk/; revision=4843
-rw-r--r--filter/Makefile.am2
-rw-r--r--filter/filter-datespec.c703
-rw-r--r--filter/filter-datespec.h64
-rw-r--r--filter/filter-element.c3
-rw-r--r--filter/filtertypes.xml72
-rw-r--r--filter/vfoldertypes.xml64
6 files changed, 908 insertions, 0 deletions
diff --git a/filter/Makefile.am b/filter/Makefile.am
index e2f81c4a07..a581462665 100644
--- a/filter/Makefile.am
+++ b/filter/Makefile.am
@@ -26,6 +26,8 @@ libfilter_la_SOURCES = \
filter-colour.h \
filter-context.c \
filter-context.h \
+ filter-datespec.c \
+ filter-datespec.h \
filter-driver.c \
filter-driver.h \
filter-editor.c \
diff --git a/filter/filter-datespec.c b/filter/filter-datespec.c
new file mode 100644
index 0000000000..c087401ee2
--- /dev/null
+++ b/filter/filter-datespec.c
@@ -0,0 +1,703 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <config.h>
+#include <time.h>
+#include <gtk/gtk.h>
+#include <gnome.h>
+
+#include "filter-datespec.h"
+#include "e-util/e-sexp.h"
+
+#define d(x) x
+
+static void xml_create(FilterElement *fe, xmlNodePtr node);
+static xmlNodePtr xml_encode(FilterElement *fe);
+static int xml_decode(FilterElement *fe, xmlNodePtr node);
+static GtkWidget *get_widget(FilterElement *fe);
+static void build_code(FilterElement *fe, GString *out, struct _FilterPart *fds);
+static void format_sexp(FilterElement *, GString *);
+static void filter_datespec_class_init (FilterDatespecClass *class);
+static void filter_datespec_init (FilterDatespec *gspaper);
+static void filter_datespec_finalise (GtkObject *obj);
+
+static void make_span_editor (FilterDatespec *fds);
+static void adj_value_changed (GtkAdjustment *adj, gpointer user_data);
+static gchar *describe_button (FilterDatespec *fds);
+static gchar *stringify_agoness (FilterDatespec *fds);
+static void set_adjustments (FilterDatespec *fds);
+
+static void cal_day_selected (GtkCalendar *cal, gpointer user_data);
+static void cal_day_selected_double_click (GtkCalendar *cal, gpointer user_data);
+
+#define PRIV(x) (((FilterDatespec *)(x))->priv)
+
+typedef struct _timespan
+{
+ guint32 seconds;
+ const gchar *singular;
+ const gchar *plural;
+ gfloat max;
+} timespan;
+
+static const timespan timespans[] = {
+ { 31557600, N_("year"), N_("years"), 1000.0 },
+ { 2419200, N_("month"), N_("months"), 12.0 },
+ { 604800, N_("week"), N_("weeks"), 52.0 },
+ { 86400, N_("day"), N_("days"), 31.0 },
+ { 3600, N_("hour"), N_("hours"), 23.0 },
+ { 60, N_("minute"), N_("minutes"), 59.0 },
+ { 1, N_("second"), N_("seconds"), 59.0 }
+};
+
+#define N_TIMECHUNKS 3
+
+static const guint timechunks[N_TIMECHUNKS] = { 2, 2, 3 };
+#define MAX_CHUNK 3
+
+#define N_TIMESPANS (sizeof (timespans) / sizeof (timespans[0]))
+
+struct _FilterDatespecPrivate {
+ GnomeDialog *gd;
+ GtkWidget *descriptive_label;
+ GtkWidget *cur_extra_widget;
+ FilterDatespec_type selected_type;
+
+ GtkWidget *date_chooser;
+ GtkWidget *span_chooser;
+ gboolean double_click;
+ GtkWidget **spinbuttons;
+};
+
+static FilterElementClass *parent_class;
+
+guint
+filter_datespec_get_type (void)
+{
+ static guint type = 0;
+
+ if (!type) {
+ GtkTypeInfo type_info = {
+ "FilterDatespec",
+ sizeof(FilterDatespec),
+ sizeof(FilterDatespecClass),
+ (GtkClassInitFunc)filter_datespec_class_init,
+ (GtkObjectInitFunc)filter_datespec_init,
+ (GtkArgSetFunc)NULL,
+ (GtkArgGetFunc)NULL
+ };
+
+ type = gtk_type_unique(filter_element_get_type (), &type_info);
+ }
+
+ return type;
+}
+
+static void
+filter_datespec_class_init (FilterDatespecClass *class)
+{
+ GtkObjectClass *object_class;
+ FilterElementClass *filter_element = (FilterElementClass *)class;
+
+ object_class = (GtkObjectClass *)class;
+ parent_class = gtk_type_class(filter_element_get_type ());
+
+ object_class->finalize = filter_datespec_finalise;
+
+ /* override methods */
+ filter_element->xml_create = xml_create;
+ filter_element->xml_encode = xml_encode;
+ filter_element->xml_decode = xml_decode;
+ filter_element->get_widget = get_widget;
+ filter_element->build_code = build_code;
+ filter_element->format_sexp = format_sexp;
+}
+
+static void
+filter_datespec_init (FilterDatespec *o)
+{
+ o->priv = g_malloc0(sizeof(*o->priv));
+ o->type = FDST_UNKNOWN;
+ PRIV(o)->selected_type = FDST_UNKNOWN;
+ PRIV(o)->spinbuttons = g_new (GtkWidget *, N_TIMESPANS );
+}
+
+static void
+filter_datespec_finalise(GtkObject *obj)
+{
+ FilterDatespec *o = (FilterDatespec *)obj;
+
+ if (o->priv) {
+ g_free (PRIV(o)->spinbuttons);
+ g_free (o->priv);
+ }
+
+ ((GtkObjectClass *)(parent_class))->finalize(obj);
+}
+
+/**
+ * filter_datespec_new:
+ *
+ * Create a new FilterDatespec object.
+ *
+ * Return value: A new #FilterDatespec object.
+ **/
+FilterDatespec *
+filter_datespec_new(void)
+{
+ FilterDatespec *o = (FilterDatespec *)gtk_type_new(filter_datespec_get_type ());
+ return o;
+}
+
+static void xml_create(FilterElement *fe, xmlNodePtr node)
+{
+ /* parent implementation */
+ ((FilterElementClass *)(parent_class))->xml_create(fe, node);
+}
+
+static xmlNodePtr xml_encode(FilterElement *fe)
+{
+ xmlNodePtr value, work;
+ FilterDatespec *fds = (FilterDatespec *)fe;
+ gchar str[32];
+
+ d(printf("Encoding datespec as xml\n"));
+
+ value = xmlNewNode(NULL, "value");
+ xmlSetProp(value, "name", fe->name);
+ xmlSetProp(value, "type", "datespec");
+
+ work = xmlNewChild(value, NULL, "datespec", NULL);
+ sprintf (str, "%d", fds->type);
+ xmlSetProp(work, "type", str);
+ sprintf (str, "%d", (int)fds->value);
+ xmlSetProp(work, "value", str);
+
+ return value;
+}
+
+static int xml_decode(FilterElement *fe, xmlNodePtr node)
+{
+ FilterDatespec *fds = (FilterDatespec *)fe;
+ xmlNodePtr n;
+ gchar *val;
+
+ d(printf("Decoding datespec from xml %p\n", fe));
+
+ fe->name = xmlGetProp(node, "name");
+
+ n = node->childs;
+ while (n) {
+ if (!strcmp(n->name, "datespec")) {
+ val = xmlGetProp(n, "type");
+ fds->type = atoi (val);
+ g_free (val);
+ val = xmlGetProp(n, "value");
+ fds->value = atoi (val);
+ g_free (val);
+ break;
+ }
+ n = n->next;
+ }
+ return 0;
+}
+
+static void activate_now(GtkMenuItem *item, FilterDatespec *fds)
+{
+ if (PRIV(fds)->cur_extra_widget) {
+ gtk_container_remove (GTK_CONTAINER (PRIV(fds)->gd->vbox),
+ PRIV(fds)->cur_extra_widget);
+ PRIV (fds)->cur_extra_widget = NULL;
+ }
+
+
+ gtk_label_set_text (GTK_LABEL (PRIV(fds)->descriptive_label),
+ _("The message's date will be compared against\n"
+ "whatever the time is when the filter is run\n"
+ "or vfolder is opened."));
+
+ PRIV(fds)->selected_type = FDST_NOW;
+}
+
+static void activate_specified(GtkMenuItem *item, FilterDatespec *fds)
+{
+ struct tm *seltime;
+
+ /* Remove other widget if it exists */
+
+ if (PRIV(fds)->cur_extra_widget) {
+ gtk_container_remove (GTK_CONTAINER (PRIV(fds)->gd->vbox),
+ PRIV(fds)->cur_extra_widget);
+ PRIV (fds)->cur_extra_widget = NULL;
+ }
+
+ /* Set description */
+
+ gtk_label_set_text (GTK_LABEL (PRIV(fds)->descriptive_label),
+ _("The message's date will be compared against\n"
+ "the time that you specify here."));
+
+ /* Reset if going from one type to another */
+ if (PRIV(fds)->selected_type != FDST_SPECIFIED)
+ fds->value = 0;
+
+ PRIV(fds)->selected_type = FDST_SPECIFIED;
+
+ /* Set the calendar's time */
+
+ if (fds->value > 0) {
+ /* gmtime? */
+ seltime = localtime (&(fds->value));
+
+ gtk_calendar_select_month (GTK_CALENDAR (PRIV(fds)->date_chooser),
+ seltime->tm_mon,
+ seltime->tm_year + 1900);
+ gtk_calendar_select_day (GTK_CALENDAR (PRIV(fds)->date_chooser),
+ seltime->tm_mday);
+ /* free seltime?? */
+ }
+
+ gtk_box_pack_start (GTK_BOX (PRIV(fds)->gd->vbox),
+ PRIV(fds)->date_chooser,
+ TRUE, TRUE, 3);
+ gtk_widget_show (PRIV(fds)->date_chooser);
+ PRIV(fds)->cur_extra_widget = PRIV(fds)->date_chooser;
+}
+
+static void activate_x_ago(GtkMenuItem *item, FilterDatespec *fds)
+{
+ if (PRIV(fds)->cur_extra_widget) {
+ gtk_container_remove (GTK_CONTAINER (PRIV(fds)->gd->vbox),
+ PRIV(fds)->cur_extra_widget);
+ PRIV (fds)->cur_extra_widget = NULL;
+ }
+
+ gtk_label_set_text (GTK_LABEL (PRIV(fds)->descriptive_label),
+ _("The message's date will be compared against\n"
+ "a time relative to when the filter is run;\n"
+ "\"a week ago\", for example."));
+
+ /* Reset if going from one type to another */
+ if (PRIV(fds)->selected_type != FDST_X_AGO)
+ fds->value = 0;
+
+ PRIV(fds)->selected_type = FDST_X_AGO;
+
+ if (fds->value > 0)
+ set_adjustments (fds);
+
+ gtk_box_pack_start (GTK_BOX (PRIV(fds)->gd->vbox),
+ PRIV(fds)->span_chooser,
+ TRUE, TRUE, 3);
+ gtk_widget_show (PRIV(fds)->span_chooser);
+ PRIV(fds)->cur_extra_widget = PRIV(fds)->span_chooser;
+
+}
+
+typedef void (*my_menu_callback) (GtkMenuItem *, FilterDatespec *);
+
+static void button_clicked(GtkButton *button, FilterDatespec *fds)
+{
+ GnomeDialog *gd;
+ GtkWidget *box;
+ GtkWidget *label;
+ GtkWidget *menu;
+ GtkWidget *selectomatic;
+ GtkWidget *sep;
+ int i;
+ gchar *desc;
+
+ /* keep in sync with FilterDatespec_type! */
+ const char *items[] = { N_("the current time"), N_("a time you specify"),
+ N_("a time relative to the current time"), NULL };
+ const my_menu_callback callbacks[]
+ = { activate_now, activate_specified, activate_x_ago };
+
+ PRIV(fds)->descriptive_label = gtk_label_new("");
+ PRIV(fds)->cur_extra_widget = NULL;
+ PRIV(fds)->double_click = FALSE;
+
+ /* The calendar */
+
+ PRIV(fds)->date_chooser = gtk_calendar_new ();
+ gtk_object_ref (GTK_OBJECT (PRIV(fds)->date_chooser));
+ gtk_signal_connect (GTK_OBJECT (PRIV(fds)->date_chooser), "day_selected",
+ cal_day_selected, fds);
+ gtk_signal_connect (GTK_OBJECT (PRIV(fds)->date_chooser), "day_selected_double_click",
+ cal_day_selected_double_click, fds);
+
+ /* The span editor thingie */
+
+ make_span_editor (fds);
+ gtk_object_ref (GTK_OBJECT (PRIV(fds)->span_chooser));
+
+ /* The dialog */
+
+ gd = (GnomeDialog *) gnome_dialog_new ("Select a time to compare against",
+ GNOME_STOCK_BUTTON_OK,
+ GNOME_STOCK_BUTTON_CANCEL,
+ NULL);
+ PRIV(fds)->gd = gd;
+
+ /* The menu */
+
+ menu = gtk_menu_new ();
+
+ for (i = 0; items[i]; i++) {
+ GtkWidget *item;
+
+ item = gtk_menu_item_new_with_label (gettext (items[i]));
+ gtk_signal_connect (GTK_OBJECT (item), "activate",
+ callbacks[i], fds);
+ gtk_menu_append (GTK_MENU (menu), item);
+ gtk_widget_show (item);
+ }
+
+ gtk_widget_show (menu);
+
+ /* The selector */
+
+ selectomatic = gtk_option_menu_new();
+ gtk_option_menu_set_menu (GTK_OPTION_MENU (selectomatic), GTK_WIDGET (menu));
+ if (fds->type != FDST_UNKNOWN)
+ /* Keep in sync with FilterDatespec_type! */
+ gtk_option_menu_set_history (GTK_OPTION_MENU (selectomatic), fds->type);
+
+ gtk_widget_show ((GtkWidget *)selectomatic);
+
+ /* The label */
+
+ label = gtk_label_new (_("Compare against"));
+ gtk_widget_show (label);
+
+ /* The hbox */
+
+ box = gtk_hbox_new (FALSE, 3);
+ gtk_box_pack_start (GTK_BOX (box), label,
+ TRUE, TRUE, 2);
+ gtk_box_pack_start (GTK_BOX (box), selectomatic,
+ TRUE, TRUE, 2);
+ gtk_widget_show (box);
+ gtk_box_pack_start ((GtkBox *)gd->vbox, (GtkWidget *)box, TRUE, TRUE, 3);
+
+ /* The separator */
+
+ sep = gtk_hseparator_new ();
+ gtk_widget_show (sep);
+ gtk_box_pack_start (GTK_BOX (gd->vbox), sep, TRUE, TRUE, 3);
+
+ /* The descriptive label */
+
+ gtk_box_pack_start (GTK_BOX (gd->vbox), PRIV(fds)->descriptive_label, TRUE, TRUE, 3);
+ gtk_misc_set_alignment (GTK_MISC (PRIV(fds)->descriptive_label), 0.5, 0.5);
+ gtk_widget_show (PRIV(fds)->descriptive_label);
+
+ /* Set up the current view */
+
+ if (fds->type == FDST_UNKNOWN)
+ fds->type = FDST_NOW;
+
+ (callbacks[fds->type]) (NULL, fds);
+
+ /* go go gadget gnomedialog! */
+
+ switch (gnome_dialog_run_and_close(gd)) {
+ case -1: /*wm close*/
+ if (PRIV(fds)->double_click == FALSE)
+ break;
+ /* else fall */
+ case 0:
+ fds->type = PRIV(fds)->selected_type;
+
+ PRIV(fds)->descriptive_label = NULL;
+
+ desc = describe_button (fds);
+ gtk_label_set_text (GTK_LABEL (GTK_BIN (button)->child), desc);
+ g_free (desc);
+ /* falllllll */
+ case 1:
+ /* cancel */
+ break;
+ }
+
+ gtk_widget_destroy (PRIV(fds)->date_chooser);
+ gtk_widget_destroy (PRIV(fds)->span_chooser);
+}
+
+static GtkWidget *
+get_widget(FilterElement *fe)
+{
+ FilterDatespec *fds = (FilterDatespec *)fe;
+ GtkWidget *button;
+ GtkWidget *label;
+ gchar *desc;
+
+ desc = describe_button (fds);
+ label = gtk_label_new (desc);
+ gtk_misc_set_alignment (GTK_MISC (label), 0.5, 0.5);
+ g_free (desc);
+
+ button = gtk_button_new();
+ gtk_container_add (GTK_CONTAINER (button), label);
+ gtk_signal_connect(GTK_OBJECT (button), "clicked", button_clicked, fds);
+
+ gtk_widget_show(button);
+ gtk_widget_show(label);
+ return button;
+}
+
+static void
+build_code(FilterElement *fe, GString *out, struct _FilterPart *fp)
+{
+ return;
+}
+
+static void
+format_sexp(FilterElement *fe, GString *out)
+{
+ FilterDatespec *fds = (FilterDatespec *)fe;
+
+ switch (fds->type) {
+ case FDST_UNKNOWN:
+ g_warning ("user hasn't selected a datespec yet!");
+ /* fall through */
+ case FDST_NOW:
+ g_string_append (out, "(get-current-date)");
+ break;
+ case FDST_SPECIFIED:
+ g_string_sprintfa (out, "%d", (int) fds->value);
+ break;
+ case FDST_X_AGO:
+ g_string_sprintfa (out, "(- (get-current-date) %d)", (int) fds->value);
+ break;
+ }
+}
+
+static gchar *
+stringify_agoness (FilterDatespec *fds)
+{
+ time_t val;
+ GString *str;
+ gchar *ret;
+
+ str = g_string_new("");
+ val = fds->value;
+
+ if (val == 0) {
+ g_string_append (str, _("now"));
+ } else {
+ int where;
+
+ where = 0;
+
+ while (val) {
+ int count;
+
+ count = 0;
+
+ while (timespans[where].seconds <= val) {
+ count++;
+ val -= timespans[where].seconds;
+ }
+
+ if (count != 0 ) {
+ if (count > 1)
+ g_string_sprintfa (str, "%d %s", (int) count, gettext (timespans[where].plural));
+ else
+ g_string_sprintfa (str, "%d %s", (int) count, gettext (timespans[where].singular));
+
+ if (val)
+ g_string_append (str, ", ");
+ }
+
+ where++;
+ }
+
+ g_string_append (str, " ago");
+ }
+
+ ret = str->str;
+ g_string_free (str, FALSE);
+ return ret;
+}
+
+static void
+make_span_editor (FilterDatespec *fds)
+{
+ int i;
+ int chunk;
+ int delta;
+ GtkWidget *table;
+
+ /*PRIV(fds)->span_chooser = gtk_vbox_new (TRUE, 3);*/
+ table = gtk_table_new (N_TIMECHUNKS, MAX_CHUNK * 2, FALSE);
+
+ i = 0;
+
+ for (chunk = 0; chunk < N_TIMECHUNKS; chunk++ ) {
+ /*GtkWidget *hbox;*/
+
+ /*hbox = gtk_hbox_new (FALSE, 1);*/
+ /*gtk_box_pack_start (GTK_BOX (PRIV(fds)->span_chooser),
+ * hbox, TRUE, TRUE, 1);
+ */
+ /*gtk_table_attach (GTK_TABLE (PRIV(fds)->span_chooser),
+ * hbox,
+ * 0, 1, chunk, chunk + 1,
+ * 0, GTK_EXPAND | GTK_FILL,
+ * 3, 3);
+ *gtk_widget_show (hbox);
+ */
+
+ for (delta = 0; delta < timechunks[chunk]; delta++, i++ ) {
+ gchar *text;
+ GtkObject *adj;
+ GtkWidget *sb;
+ GtkWidget *label;
+
+ adj = gtk_adjustment_new (0.0, 0.0,
+ timespans[i].max,
+ 1.0, 10.0, 0.0);
+
+ sb = gtk_spin_button_new (GTK_ADJUSTMENT (adj),
+ 0, 0);
+
+ /*gtk_box_pack_start (GTK_BOX (hbox), sb, FALSE, FALSE, 1);*/
+ gtk_table_attach (GTK_TABLE (table), sb,
+ delta * 2, delta * 2 + 1,
+ chunk, chunk + 1,
+ 0, GTK_EXPAND | GTK_FILL,
+ 2, 4);
+ PRIV(fds)->spinbuttons[i] = sb;
+
+ gtk_widget_show (GTK_WIDGET (sb));
+
+ if (delta + 1 < timechunks[chunk])
+ text = g_strdup_printf ("%s, ", gettext (timespans[i].plural));
+ else
+ text = g_strdup_printf ("%s ago", gettext (timespans[i].plural));
+
+ label = gtk_label_new (text);
+ g_free (text);
+
+ /*gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 3);*/
+ gtk_table_attach (GTK_TABLE (table), label,
+ delta * 2 + 1, (delta + 1) * 2,
+ chunk, chunk + 1,
+ 0, GTK_EXPAND | GTK_FILL,
+ 2, 4);
+ gtk_widget_show (label);
+
+ gtk_signal_connect (adj, "value_changed",
+ adj_value_changed, fds);
+ }
+ }
+
+ PRIV(fds)->span_chooser = table;
+}
+
+static void
+adj_value_changed (GtkAdjustment *adj, gpointer user_data)
+{
+ FilterDatespec *fds = (FilterDatespec *) user_data;
+ int i;
+
+ fds->value = 0;
+
+ for (i = 0; i < N_TIMESPANS; i++)
+ fds->value += timespans[i].seconds *
+ (gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (PRIV(fds)->spinbuttons[i])));
+}
+
+static void
+set_adjustments (FilterDatespec *fds)
+{
+ time_t val;
+ int where;
+
+ val = fds->value;
+ where = 0;
+
+ while (val) {
+ int count;
+
+ count = 0;
+
+ while (timespans[where].seconds <= val) {
+ count++;
+ val -= timespans[where].seconds;
+ }
+
+ gtk_spin_button_set_value (GTK_SPIN_BUTTON (PRIV(fds)->spinbuttons[where]),
+ (gfloat) count);
+ where++;
+ }
+}
+
+static gchar *
+describe_button (FilterDatespec *fds)
+{
+ gchar *desc = NULL;
+
+ switch (fds->type) {
+ case FDST_UNKNOWN:
+ desc = g_strdup (_("<click here to select a date>"));
+ break;
+ case FDST_NOW:
+ desc = g_strdup (_("now"));
+ break;
+ case FDST_SPECIFIED:
+ desc = g_strdup (ctime (&(fds->value)));
+ break;
+ case FDST_X_AGO:
+ desc = stringify_agoness (fds);
+ break;
+ }
+
+ return desc;
+}
+
+static void
+cal_day_selected (GtkCalendar *cal, gpointer user_data)
+{
+ FilterDatespec *fds = (FilterDatespec *)user_data;
+ extern int daylight;
+ struct tm seltime;
+
+ seltime.tm_sec = 0;
+ seltime.tm_min = 0;
+ seltime.tm_hour = 0;
+ seltime.tm_mday = cal->selected_day;
+ seltime.tm_mon = cal->month;
+ seltime.tm_year = cal->year - 1900;
+ seltime.tm_isdst = daylight;
+
+ fds->value = mktime (&seltime);
+}
+
+static void
+cal_day_selected_double_click (GtkCalendar *cal, gpointer user_data)
+{
+ FilterDatespec *fds = (FilterDatespec *)user_data;
+
+ cal_day_selected (cal, user_data);
+ PRIV(fds)->double_click = TRUE;
+ gnome_dialog_close (PRIV(fds)->gd);
+}
diff --git a/filter/filter-datespec.h b/filter/filter-datespec.h
new file mode 100644
index 0000000000..1292a94e36
--- /dev/null
+++ b/filter/filter-datespec.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2000 Helix Code Inc.
+ *
+ * Authors: Not Zed <notzed@lostzed.mmc.com.au>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public License
+ * as published by the Free Software Foundation; either version 2 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 Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _FILTER_DATESPEC_H
+#define _FILTER_DATESPEC_H
+
+#include <gtk/gtk.h>
+#include "filter-element.h"
+
+#define FILTER_DATESPEC(obj) GTK_CHECK_CAST (obj, filter_datespec_get_type (), FilterDatespec)
+#define FILTER_DATESPEC_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, filter_datespec_get_type (), FilterDatespecClass)
+#define IS_FILTER_DATESPEC(obj) GTK_CHECK_TYPE (obj, filter_datespec_get_type ())
+
+typedef struct _FilterDatespec FilterDatespec;
+typedef struct _FilterDatespecClass FilterDatespecClass;
+
+typedef enum _FilterDatespec_type { FDST_NOW, FDST_SPECIFIED, FDST_X_AGO, FDST_UNKNOWN } FilterDatespec_type;
+
+struct _FilterDatespec {
+ FilterElement parent;
+ struct _FilterDatespecPrivate *priv;
+
+ FilterDatespec_type type;
+
+ /* either a timespan, an absolute time, or 0
+ * depending on type -- the above mapping to
+ * (X_AGO, SPECIFIED, NOW)
+ */
+
+ time_t value;
+};
+
+struct _FilterDatespecClass {
+ FilterElementClass parent_class;
+
+ /* virtual methods */
+
+ /* signals */
+};
+
+guint filter_datespec_get_type (void);
+FilterDatespec *filter_datespec_new (void);
+
+/* methods */
+
+#endif /* ! _FILTER_DATESPEC_H */
+
diff --git a/filter/filter-element.c b/filter/filter-element.c
index 049b15727b..e0b896daf2 100644
--- a/filter/filter-element.c
+++ b/filter/filter-element.c
@@ -26,6 +26,7 @@
#include "filter-option.h"
#include "filter-code.h"
#include "filter-colour.h"
+#include "filter-datespec.h"
#include "filter-folder.h"
static void xml_create(FilterElement *fe, xmlNodePtr node);
@@ -237,6 +238,8 @@ FilterElement *filter_element_new_type_name (const char *type)
return (FilterElement *)filter_colour_new();
} else if (!strcmp(type, "optionlist")) {
return (FilterElement *)filter_option_new();
+ } else if (!strcmp(type, "datespec")) {
+ return (FilterElement *)filter_datespec_new();
} else {
g_warning("Unknown filter type '%s'", type);
return 0;
diff --git a/filter/filtertypes.xml b/filter/filtertypes.xml
index db5ceaa3ac..0c38ce575c 100644
--- a/filter/filtertypes.xml
+++ b/filter/filtertypes.xml
@@ -76,7 +76,74 @@
<title>Expression</title>
<input type="code" name="code"/>
</part>
+
+ <part name="sent-date">
+ <title>Message was sent</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="before">
+ <title>before</title>
+ <code>
+ (match-all (&lt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="on-or-before">
+ <title>on or before</title>
+ <code>
+ (match-all (not (&gt; (get-sent-date) ${versus})))
+ </code>
+ </option>
+ <option value="after">
+ <title>after</title>
+ <code>
+ (match-all (&gt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="on-or-after">
+ <title>on or after</title>
+ <code>
+ (match-all (not (&lt; (get-sent-date) ${versus})))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus">
+ </input>
+ </part>
+
+ <part name="recv-date">
+ <title>Message was received</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="before">
+ <title>before</title>
+ <code>
+ (match-all (&lt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="on-or-before">
+ <title>on or before</title>
+ <code>
+ (match-all (not (&gt; (get-received-date) ${versus})))
+ </code>
+ </option>
+ <option value="after">
+ <title>after</title>
+ <code>
+ (match-all (&gt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="on-or-after">
+ <title>on or after</title>
+ <code>
+ (match-all (not (&lt; (get-received-date) ${versus})))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus">
+ </input>
+ </part>
+
</partset>
+
+
<actionset>
<part name="copy-to-folder">
<title>Copy to Folder</title>
@@ -103,3 +170,8 @@
</part>
</actionset>
</filterdescription>
+
+
+
+
+
diff --git a/filter/vfoldertypes.xml b/filter/vfoldertypes.xml
index af763aed2f..52c2c0e8ca 100644
--- a/filter/vfoldertypes.xml
+++ b/filter/vfoldertypes.xml
@@ -76,5 +76,69 @@
<title>Expression</title>
<input type="code" name="code"/>
</part>
+
+ <part name="sent-date">
+ <title>Message was sent</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="before">
+ <title>before</title>
+ <code>
+ (match-all (&lt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="on-or-before">
+ <title>on or before</title>
+ <code>
+ (match-all (not (&gt; (get-sent-date) ${versus})))
+ </code>
+ </option>
+ <option value="after">
+ <title>after</title>
+ <code>
+ (match-all (&gt; (get-sent-date) ${versus}))
+ </code>
+ </option>
+ <option value="on-or-after">
+ <title>on or after</title>
+ <code>
+ (match-all (not (&lt; (get-sent-date) ${versus})))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus">
+ </input>
+ </part>
+
+ <part name="recv-date">
+ <title>Message was received</title>
+ <input type="optionlist" name="date-spec-type">
+ <option value="before">
+ <title>before</title>
+ <code>
+ (match-all (&lt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="on-or-before">
+ <title>on or before</title>
+ <code>
+ (match-all (not (&gt; (get-received-date) ${versus})))
+ </code>
+ </option>
+ <option value="after">
+ <title>after</title>
+ <code>
+ (match-all (&gt; (get-received-date) ${versus}))
+ </code>
+ </option>
+ <option value="on-or-after">
+ <title>on or after</title>
+ <code>
+ (match-all (not (&lt; (get-received-date) ${versus})))
+ </code>
+ </option>
+ </input>
+ <input type="datespec" name="versus">
+ </input>
+ </part>
</partset>
</filterdescription>