From 7501aec23887fe0d6e68620b00326c6f2fc3c260 Mon Sep 17 00:00:00 2001
From: Not Zed <NotZed@Ximian.com>
Date: Wed, 6 Oct 2004 03:22:39 +0000
Subject: cast warning away. (create_component_view): connect to popup_event
 rather

2004-10-01  Not Zed  <NotZed@Ximian.com>

        * gui/tasks-component.c (create_component_view): cast warning
        away.
        (create_component_view): connect to popup_event rather than
        fill_popup_menu.

        * gui/calendar-component.c (create_component_view): cast a warning
        away.

        * calendar-errors.xml: add prompt-delete-task-list.

        * gui/tasks-component.c (fill_popup_menu_cb): renamed to
        popup_event_cb, make use e-cal-popup.
        (edit_task_list_cb, new_task_list_cb, delete_task_list_cb)
        (copy_task_list_cb): deja-vu.  update for api.
        (add_popup_menu_item): killed. murdered. drawn and quatered.
        (delete_task_list_cb): use e-error for the delete prompt.

        * gui/calendar-component.c (create_component_view): hook onto
        popup event instead of fill_popup_menu.

        * calendar-errors.xml: added prompt-delete-calendar.

        * gui/calendar-component.c (fill_popup_menu_cb): rename to
        popup_event_cb, make use e-cal-popup.
        (edit_calendar_cb, new_calendar_cb, delete_calendar_cb)
        (copy_calendar_cb): fix for api changes.
        (add_popup_menu_item): removed.
        (delete_calendar_cb): use e-error for the delete thing.

        * gui/e-cal-popup.[ch]: calendar popup driver.

svn path=/trunk/; revision=27467
---
 calendar/ChangeLog                |  33 ++++++
 calendar/calendar-errors.xml      |  14 +++
 calendar/calendar-errors.xml.h    |   8 ++
 calendar/gui/Makefile.am          |   2 +
 calendar/gui/calendar-component.c | 161 +++++++++++--------------
 calendar/gui/e-cal-popup.c        | 241 ++++++++++++++++++++++++++++++++++++++
 calendar/gui/e-cal-popup.h        | 145 +++++++++++++++++++++++
 calendar/gui/tasks-component.c    | 169 ++++++++++++--------------
 8 files changed, 586 insertions(+), 187 deletions(-)
 create mode 100644 calendar/gui/e-cal-popup.c
 create mode 100644 calendar/gui/e-cal-popup.h

(limited to 'calendar')

diff --git a/calendar/ChangeLog b/calendar/ChangeLog
index 76770d4892..59b2b204dd 100644
--- a/calendar/ChangeLog
+++ b/calendar/ChangeLog
@@ -1,3 +1,36 @@
+2004-10-01  Not Zed  <NotZed@Ximian.com>
+
+	* gui/tasks-component.c (create_component_view): cast warning
+	away.
+	(create_component_view): connect to popup_event rather than
+	fill_popup_menu.
+
+	* gui/calendar-component.c (create_component_view): cast a warning
+	away.
+
+	* calendar-errors.xml: add prompt-delete-task-list.
+
+	* gui/tasks-component.c (fill_popup_menu_cb): renamed to
+	popup_event_cb, make use e-cal-popup.
+	(edit_task_list_cb, new_task_list_cb, delete_task_list_cb) 
+	(copy_task_list_cb): deja-vu.  update for api.
+	(add_popup_menu_item): killed. murdered. drawn and quatered.
+	(delete_task_list_cb): use e-error for the delete prompt.
+
+	* gui/calendar-component.c (create_component_view): hook onto
+	popup event instead of fill_popup_menu.
+
+	* calendar-errors.xml: added prompt-delete-calendar.
+
+	* gui/calendar-component.c (fill_popup_menu_cb): rename to
+	popup_event_cb, make use e-cal-popup.
+	(edit_calendar_cb, new_calendar_cb, delete_calendar_cb) 
+	(copy_calendar_cb): fix for api changes.
+	(add_popup_menu_item): removed.
+	(delete_calendar_cb): use e-error for the delete thing.
+
+	* gui/e-cal-popup.[ch]: calendar popup driver.
+
 2004-09-29  Rodrigo Moya <rodrigo@novell.com>
 
 	Fixes #64683
diff --git a/calendar/calendar-errors.xml b/calendar/calendar-errors.xml
index 3e38b7e71a..9ed07ca503 100644
--- a/calendar/calendar-errors.xml
+++ b/calendar/calendar-errors.xml
@@ -163,4 +163,18 @@
   <secondary>Your calendars will not be available until Evolution is restarted.</secondary>
  </error>
 
+ <error id="prompt-delete-calendar" type="question" modal="true" default="GTK_RESPONSE_CANCEL">
+  <primary>Delete calendar '{0}'?</primary>
+  <secondary>This calendar will be removed permanently.</secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button stock="gtk-delete" response="GTK_RESPONSE_YES"/>
+ </error>
+
+ <error id="prompt-delete-task-list" type="question" modal="true" default="GTK_RESPONSE_CANCEL">
+  <primary>Delete task list '{0}'?</primary>
+  <secondary>This task list will be removed permanently.</secondary>
+  <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/>
+  <button stock="gtk-delete" response="GTK_RESPONSE_YES"/>
+ </error>
+
 </error-list>
diff --git a/calendar/calendar-errors.xml.h b/calendar/calendar-errors.xml.h
index 85f62a501a..6e9df6221f 100644
--- a/calendar/calendar-errors.xml.h
+++ b/calendar/calendar-errors.xml.h
@@ -112,3 +112,11 @@ char *s = N_("Your tasks will not be available until Evolution is restarted.");
 char *s = N_("The Evolution calendar has quit unexpectedly.");
 /* calendar:calendar-crashed secondary */
 char *s = N_("Your calendars will not be available until Evolution is restarted.");
+/* calendar:prompt-delete-calendar primary */
+char *s = N_("Delete calendar '{0}'?");
+/* calendar:prompt-delete-calendar secondary */
+char *s = N_("This calendar will be removed permanently.");
+/* calendar:prompt-delete-task-list primary */
+char *s = N_("Delete task list '{0}'?");
+/* calendar:prompt-delete-task-list secondary */
+char *s = N_("This task list will be removed permanently.");
diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am
index 3272226955..d1b343a5c6 100644
--- a/calendar/gui/Makefile.am
+++ b/calendar/gui/Makefile.am
@@ -122,6 +122,8 @@ libevolution_calendar_la_SOURCES =		\
 	e-cal-model-tasks.h			\
 	e-cal-model.c				\
 	e-cal-model.h				\
+	e-cal-popup.h				\
+	e-cal-popup.c				\
 	e-calendar-view.c			\
 	e-calendar-view.h			\
 	e-cal-list-view.c			\
diff --git a/calendar/gui/calendar-component.c b/calendar/gui/calendar-component.c
index 41d5a0c8ab..14f7aff23a 100644
--- a/calendar/gui/calendar-component.c
+++ b/calendar/gui/calendar-component.c
@@ -51,7 +51,9 @@
 #include "dialogs/event-editor.h"
 #include "widgets/misc/e-source-selector.h"
 #include "widgets/misc/e-info-label.h"
+#include "widgets/misc/e-error.h"
 #include "e-util/e-icon-factory.h"
+#include "e-cal-popup.h"
 
 /* IDs for user creatable items */
 #define CREATE_EVENT_ID        "event"
@@ -308,133 +310,110 @@ update_primary_task_selection (CalendarComponentView *component_view)
 
 /* Callbacks.  */
 static void
-add_popup_menu_item (GtkMenu *menu, const char *label, const char *icon_name,
-		     GCallback callback, gpointer user_data, gboolean sensitive)
-{
-	GtkWidget *item, *image;
-	GdkPixbuf *pixbuf;
-
-	if (icon_name) {
-		item = gtk_image_menu_item_new_with_label (label);
-
-		/* load the image */
-		pixbuf = e_icon_factory_get_icon (icon_name, E_ICON_SIZE_MENU);
-		image = gtk_image_new_from_pixbuf (pixbuf);
-
-		if (image) {
-			gtk_widget_show (image);
-			gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
-		}
-	} else {
-		item = gtk_menu_item_new_with_label (label);
-	}
-
-	if (callback)
-		g_signal_connect (G_OBJECT (item), "activate", callback, user_data);
-
-	if (!sensitive)
-		gtk_widget_set_sensitive (item, FALSE);
-
-	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-	gtk_widget_show (item);
-}
-
-static void
-copy_calendar_cb (GtkWidget *widget, CalendarComponentView *component_view)
+copy_calendar_cb (EPopup *ep, EPopupItem *pitem, void *data)
 {
+	CalendarComponentView *component_view = data;
 	ESource *selected_source;
 	
 	selected_source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector));
 	if (!selected_source)
 		return;
 
-	copy_source_dialog (GTK_WINDOW (gtk_widget_get_toplevel (widget)), selected_source, E_CAL_SOURCE_TYPE_EVENT);
+	copy_source_dialog (GTK_WINDOW (gtk_widget_get_toplevel (ep->target->widget)), selected_source, E_CAL_SOURCE_TYPE_EVENT);
 }
 
 static void
-delete_calendar_cb (GtkWidget *widget, CalendarComponentView *component_view)
+delete_calendar_cb (EPopup *ep, EPopupItem *pitem, void *data)
 {
+	CalendarComponentView *component_view = data;
 	ESource *selected_source;
-	GtkWidget *dialog;
+	ECal *cal;
+	char *uri;
 
 	selected_source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector));
 	if (!selected_source)
 		return;
 
-	/* create the confirmation dialog */
-	dialog = gtk_message_dialog_new (
-		GTK_WINDOW (gtk_widget_get_toplevel (widget)),
-		GTK_DIALOG_MODAL,
-		GTK_MESSAGE_QUESTION,
-		GTK_BUTTONS_YES_NO,
-		_("Calendar '%s' will be removed. Are you sure you want to continue?"),
-		e_source_peek_name (selected_source));
-	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) {
-		ECal *cal;
-		char *uri;
-
-		/* first, ask the backend to remove the calendar */
-		uri = e_source_get_uri (selected_source);
-		cal = e_cal_model_get_client_for_uri (gnome_calendar_get_calendar_model (component_view->calendar), uri);
-		if (!cal)
-			cal = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_EVENT);
-		g_free (uri);
-		if (cal) {
-			if (e_cal_remove (cal, NULL)) {
-				if (e_source_selector_source_is_selected (E_SOURCE_SELECTOR (component_view->source_selector),
-									  selected_source)) {
-					gnome_calendar_remove_source (component_view->calendar, E_CAL_SOURCE_TYPE_EVENT, selected_source);
-					e_source_selector_unselect_source (E_SOURCE_SELECTOR (component_view->source_selector),
-									   selected_source);
-				}
-		
-				e_source_group_remove_source (e_source_peek_group (selected_source), selected_source);
-				e_source_list_sync (component_view->source_list, NULL);
+	if (e_error_run((GtkWindow *)gtk_widget_get_toplevel(ep->target->widget),
+			"calendar:prompt-delete-calendar", e_source_peek_name(selected_source)) != GTK_RESPONSE_YES)
+		return;
+
+	/* first, ask the backend to remove the calendar */
+	uri = e_source_get_uri (selected_source);
+	cal = e_cal_model_get_client_for_uri (gnome_calendar_get_calendar_model (component_view->calendar), uri);
+	if (!cal)
+		cal = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_EVENT);
+	g_free (uri);
+	if (cal) {
+		if (e_cal_remove (cal, NULL)) {
+			if (e_source_selector_source_is_selected (E_SOURCE_SELECTOR (component_view->source_selector),
+								  selected_source)) {
+				gnome_calendar_remove_source (component_view->calendar, E_CAL_SOURCE_TYPE_EVENT, selected_source);
+				e_source_selector_unselect_source (E_SOURCE_SELECTOR (component_view->source_selector),
+								   selected_source);
 			}
+			
+			e_source_group_remove_source (e_source_peek_group (selected_source), selected_source);
+			e_source_list_sync (component_view->source_list, NULL);
 		}
 	}
-
-	gtk_widget_destroy (dialog);
 }
 
 static void
-new_calendar_cb (GtkWidget *widget, CalendarComponentView *component_view)
+new_calendar_cb (EPopup *ep, EPopupItem *pitem, void *data)
 {
-	calendar_setup_new_calendar (GTK_WINDOW (gtk_widget_get_toplevel (widget)));
+	calendar_setup_new_calendar (GTK_WINDOW (gtk_widget_get_toplevel(ep->target->widget)));
 }
 
 static void
-edit_calendar_cb (GtkWidget *widget, CalendarComponentView *component_view)
+edit_calendar_cb (EPopup *ep, EPopupItem *pitem, void *data)
 {
+	CalendarComponentView *component_view = data;
 	ESource *selected_source;
 
 	selected_source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector));
 	if (!selected_source)
 		return;
 
-	calendar_setup_edit_calendar (GTK_WINDOW (gtk_widget_get_toplevel (widget)), selected_source);
+	calendar_setup_edit_calendar (GTK_WINDOW (gtk_widget_get_toplevel(ep->target->widget)), selected_source);
 }
 
+static EPopupItem ecc_source_popups[] = {
+	{ E_POPUP_ITEM, "10.new", N_("New Calendar"), new_calendar_cb, NULL, "stock_calendar", 0 },
+	{ E_POPUP_ITEM, "15.copy", N_("Copy"), copy_calendar_cb, NULL, "stock_folder-copy", E_CAL_POPUP_SOURCE_PRIMARY },
+	{ E_POPUP_ITEM, "20.delete", N_("Delete"), delete_calendar_cb, NULL, "stock_delete", E_CAL_POPUP_SOURCE_USER|E_CAL_POPUP_SOURCE_PRIMARY },
+	{ E_POPUP_ITEM, "30.properties", N_("Properties..."), edit_calendar_cb, NULL, NULL, E_CAL_POPUP_SOURCE_PRIMARY },
+};
+
 static void
-fill_popup_menu_cb (ESourceSelector *selector, GtkMenu *menu, CalendarComponentView *component_view)
+ecc_source_popup_free(EPopup *ep, GSList *list, void *data)
 {
-	ESource *source;
-	gboolean sensitive, system;
-	const char *source_uri;
-	
-	source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector));
-	sensitive =  source ? TRUE : FALSE;
+	g_slist_free(list);
+}
 
-	/* FIXME Gross hack, should have a property or something */
-	source_uri = e_source_peek_relative_uri (source);
-	system = source_uri && !strcmp ("system", source_uri);
-	
-	add_popup_menu_item (menu, _("New Calendar"), "stock_calendar",
-			     G_CALLBACK (new_calendar_cb), component_view, TRUE);
-	add_popup_menu_item (menu, _("Copy"), "stock_folder-copy",
-			     G_CALLBACK (copy_calendar_cb), component_view, sensitive);
-	add_popup_menu_item (menu, _("Delete"), "stock_delete", G_CALLBACK (delete_calendar_cb), component_view, sensitive && !system); 
-	add_popup_menu_item (menu, _("Properties..."), NULL, G_CALLBACK (edit_calendar_cb), component_view, sensitive);
+static gboolean
+popup_event_cb(ESourceSelector *selector, ESource *insource, GdkEventButton *event, CalendarComponentView *component_view)
+{
+	ECalPopup *ep;
+	ECalPopupTargetSource *t;
+	GSList *menus = NULL;
+	int i;
+	GtkMenu *menu;
+
+	ep = e_cal_popup_new("com.novell.evolution.calendar.source.popup");
+	t = e_cal_popup_target_new_source(ep, selector);
+	t->target.widget = (GtkWidget *)component_view->calendar;
+
+	for (i=0;i<sizeof(ecc_source_popups)/sizeof(ecc_source_popups[0]);i++)
+		menus = g_slist_prepend(menus, &ecc_source_popups[i]);
+
+	e_popup_add_items((EPopup *)ep, menus, ecc_source_popup_free, component_view);
+
+	/* visibility is disabled, we only disable menu items */
+	menu = e_popup_create_menu_once((EPopup *)ep, (EPopupTarget *)t, 0, t->target.mask);
+	gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event?event->button:0, event?event->time:gtk_get_current_event_time());
+
+	return TRUE;
 }
 
 static void
@@ -1057,7 +1036,7 @@ create_component_view (CalendarComponent *calendar_component)
 	
 	/* Create sidebar selector */
 	component_view->source_selector = e_source_selector_new (calendar_component->priv->source_list);
-	e_source_selector_set_select_new (component_view->source_selector, TRUE);
+	e_source_selector_set_select_new ((ESourceSelector *)component_view->source_selector, TRUE);
 
 	g_signal_connect (component_view->source_selector, "drag-motion", G_CALLBACK (selector_tree_drag_motion), 
 			  calendar_component);
@@ -1124,8 +1103,8 @@ create_component_view (CalendarComponent *calendar_component)
 			  G_CALLBACK (source_selection_changed_cb), component_view);
 	g_signal_connect (component_view->source_selector, "primary_selection_changed",
 			  G_CALLBACK (primary_source_selection_changed_cb), component_view);
-	g_signal_connect (component_view->source_selector, "fill_popup_menu",
-			  G_CALLBACK (fill_popup_menu_cb), component_view);
+	g_signal_connect (component_view->source_selector, "popup_event",
+			  G_CALLBACK (popup_event_cb), component_view);
 
 	/* Set up the "new" item handler */
 	component_view->creatable_items_handler = e_user_creatable_items_handler_new ("calendar", create_local_item_cb, calendar_component);
diff --git a/calendar/gui/e-cal-popup.c b/calendar/gui/e-cal-popup.c
new file mode 100644
index 0000000000..d0a751fce1
--- /dev/null
+++ b/calendar/gui/e-cal-popup.c
@@ -0,0 +1,241 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ *  Copyright 2004 Novell, Inc. (www.novell.com)
+ *
+ *  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; version 2 of the License.
+ *
+ *  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 Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <glib.h>
+
+#include "e-cal-popup.h"
+#include "widgets/misc/e-source-selector.h"
+
+static GObjectClass *ecalp_parent;
+
+static void
+ecalp_init(GObject *o)
+{
+	/*ECalPopup *eabp = (ECalPopup *)o; */
+}
+
+static void
+ecalp_finalise(GObject *o)
+{
+	((GObjectClass *)ecalp_parent)->finalize(o);
+}
+
+static void
+ecalp_target_free(EPopup *ep, EPopupTarget *t)
+{
+	switch (t->type) {
+	case E_CAL_POPUP_TARGET_SELECT: {
+		ECalPopupTargetSelect *s = (ECalPopupTargetSelect *)t;
+
+		/* FIXME: implement */
+		s = s;
+		break; }
+	case E_CAL_POPUP_TARGET_SOURCE: {
+		ECalPopupTargetSource *s = (ECalPopupTargetSource *)t;
+
+		g_object_unref(s->selector);
+		break; }
+	}
+
+	((EPopupClass *)ecalp_parent)->target_free(ep, t);
+}
+
+static void
+ecalp_class_init(GObjectClass *klass)
+{
+	klass->finalize = ecalp_finalise;
+	((EPopupClass *)klass)->target_free = ecalp_target_free;
+}
+
+GType
+e_cal_popup_get_type(void)
+{
+	static GType type = 0;
+
+	if (type == 0) {
+		static const GTypeInfo info = {
+			sizeof(ECalPopupClass),
+			NULL, NULL,
+			(GClassInitFunc)ecalp_class_init,
+			NULL, NULL,
+			sizeof(ECalPopup), 0,
+			(GInstanceInitFunc)ecalp_init
+		};
+		ecalp_parent = g_type_class_ref(e_popup_get_type());
+		type = g_type_register_static(e_popup_get_type(), "ECalPopup", &info, 0);
+	}
+
+	return type;
+}
+
+ECalPopup *e_cal_popup_new(const char *menuid)
+{
+	ECalPopup *eabp = g_object_new(e_cal_popup_get_type(), 0);
+
+	e_popup_construct(&eabp->popup, menuid);
+
+	return eabp;
+}
+
+/**
+ * e_cal_popup_target_new_select:
+ * 
+ * Create a new selection popup target.
+ * 
+ * Return value: 
+ **/
+ECalPopupTargetSelect *
+e_cal_popup_target_new_select(ECalPopup *eabp)
+{
+	ECalPopupTargetSelect *t = e_popup_target_new(&eabp->popup, E_CAL_POPUP_TARGET_SELECT, sizeof(*t));
+	guint32 mask = ~0;
+
+	/* FIXME: impelement */
+
+	t->target.mask = mask;
+
+	return t;
+}
+
+ECalPopupTargetSource *
+e_cal_popup_target_new_source(ECalPopup *eabp, ESourceSelector *selector)
+{
+	ECalPopupTargetSource *t = e_popup_target_new(&eabp->popup, E_CAL_POPUP_TARGET_SOURCE, sizeof(*t));
+	guint32 mask = ~0;
+	const char *source_uri;
+	ESource *source;
+
+	/* TODO: this is duplicated for addressbook too */
+
+	t->selector = selector;
+	g_object_ref(selector);
+
+	/* TODO: perhaps we need to copy this so it doesn't change during the lifecycle */
+	source = e_source_selector_peek_primary_selection(selector);
+	if (source)
+		mask &= ~E_CAL_POPUP_SOURCE_PRIMARY;
+
+	/* FIXME Gross hack, should have a property or something */
+	source_uri = e_source_peek_relative_uri(source);
+	if (source_uri && !strcmp("system", source_uri))
+		mask &= ~E_CAL_POPUP_SOURCE_SYSTEM;
+	else
+		mask &= ~E_CAL_POPUP_SOURCE_USER;
+
+	t->target.mask = mask;
+
+	return t;
+}
+
+/* ********************************************************************** */
+/* Popup menu plugin handler */
+
+/*
+<e-plugin
+  class="com.ximian.mail.plugin.popup:1.0"
+  id="com.ximian.mail.plugin.popup.iteab:1.0"
+  type="shlib"
+  location="/opt/gnome2/lib/camel/1.0/libcamelimap.so"
+  name="imap"
+  description="IMAP4 and IMAP4v1 mail store">
+  <hook class="com.ximian.mail.popupMenu:1.0"
+        handler="HandlePopup">
+  <menu id="any" target="select">
+   <iteab
+    type="iteab|toggle|radio|image|submenu|bar"
+    active
+    path="foo/bar"
+    label="label"
+    icon="foo"
+    mask="select_one"
+    activate="ecalp_view_eabacs"/>
+  </menu>
+  </extension>
+
+*/
+
+static void *ecalph_parent_class;
+#define ecalph ((ECalPopupHook *)eph)
+
+static const EPopupHookTargetMask ecalph_select_masks[] = {
+	{ "one", E_CAL_POPUP_SELECT_ONE },
+	{ "many", E_CAL_POPUP_SELECT_MANY },
+	{ 0 }
+};
+
+static const EPopupHookTargetMask ecalph_source_masks[] = {
+	{ "primary", E_CAL_POPUP_SOURCE_PRIMARY },
+	{ "system", E_CAL_POPUP_SOURCE_SYSTEM },
+	{ 0 }
+};
+
+static const EPopupHookTargetMap ecalph_targets[] = {
+	{ "select", E_CAL_POPUP_TARGET_SELECT, ecalph_select_masks },
+	{ "source", E_CAL_POPUP_TARGET_SOURCE, ecalph_source_masks },
+	{ 0 }
+};
+
+static void
+ecalph_finalise(GObject *o)
+{
+	/*EPluginHook *eph = (EPluginHook *)o;*/
+
+	((GObjectClass *)ecalph_parent_class)->finalize(o);
+}
+
+static void
+ecalph_class_init(EPluginHookClass *klass)
+{
+	int i;
+
+	((GObjectClass *)klass)->finalize = ecalph_finalise;
+	((EPluginHookClass *)klass)->id = "com.ximian.evolution.addressbook.popup:1.0";
+
+	for (i=0;ecalph_targets[i].type;i++)
+		e_popup_hook_class_add_target_map((EPopupHookClass *)klass, &ecalph_targets[i]);
+
+	((EPopupHookClass *)klass)->popup_class = g_type_class_ref(e_cal_popup_get_type());
+}
+
+GType
+e_cal_popup_hook_get_type(void)
+{
+	static GType type = 0;
+	
+	if (!type) {
+		static const GTypeInfo info = {
+			sizeof(ECalPopupHookClass), NULL, NULL, (GClassInitFunc) ecalph_class_init, NULL, NULL,
+			sizeof(ECalPopupHook), 0, (GInstanceInitFunc) NULL,
+		};
+
+		ecalph_parent_class = g_type_class_ref(e_popup_hook_get_type());
+		type = g_type_register_static(e_popup_hook_get_type(), "ECalPopupHook", &info, 0);
+	}
+	
+	return type;
+}
diff --git a/calendar/gui/e-cal-popup.h b/calendar/gui/e-cal-popup.h
new file mode 100644
index 0000000000..81d2d51976
--- /dev/null
+++ b/calendar/gui/e-cal-popup.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ *  Authors: Michael Zucchi <notzed@ximian.com>
+ *
+ *  Copyright 2004 Novell, Inc. (www.novell.com)
+ *
+ *  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; version 2 of the License.
+ *
+ *  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 Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __E_CAL_POPUP_H__
+#define __E_CAL_POPUP_H__
+
+#include <glib-object.h>
+
+#include "e-util/e-popup.h"
+
+#ifdef __cplusplus
+extern "C" {
+#pragma }
+#endif /* __cplusplus */
+
+typedef struct _ECalPopup ECalPopup;
+typedef struct _ECalPopupClass ECalPopupClass;
+
+/**
+ * enum _e_cal_popup_target_t - A list of mail popup target types.
+ * 
+ * @E_CAL_POPUP_TARGET_SELECT: A selection of cards
+ * @E_CAL_POPUP_TARGET_SOURCE: A source selection.
+ *
+ * Defines the value of the targetid for all ECalPopup target types.
+ **/
+enum _e_cal_popup_target_t {
+	E_CAL_POPUP_TARGET_SELECT,
+	E_CAL_POPUP_TARGET_SOURCE,
+};
+
+/**
+ * enum _e_cal_popup_target_select_t - ECalPopupTargetSelect qualifiers.
+ * 
+ * @E_CAL_POPUP_SELECT_ONE: Only one item is selected.
+ * @E_CAL_POPUP_SELECT_MANY: One ore more items are selected.
+ * 
+ **/
+enum _e_cal_popup_target_select_t {
+	E_CAL_POPUP_SELECT_ONE                = 1<<1,
+	E_CAL_POPUP_SELECT_MANY               = 1<<2,
+};
+
+/**
+ * enum _e_cal_popup_target_source_t - ECalPopupTargetSource qualifiers.
+ * 
+ * @E_CAL_POPUP_SOURCE_PRIMARY: Has a primary selection.
+ * @E_CAL_POPUP_SOURCE_SYSTEM: Is a 'system' folder.
+ * 
+ **/
+enum _e_cal_popup_target_source_t {
+	E_CAL_POPUP_SOURCE_PRIMARY = 1<<0,
+	E_CAL_POPUP_SOURCE_SYSTEM = 1<<1,	/* system folder */
+	E_CAL_POPUP_SOURCE_USER = 1<<2, /* user folder (!system) */
+};
+
+typedef struct _ECalPopupTargetSelect ECalPopupTargetSelect;
+typedef struct _ECalPopupTargetSource ECalPopupTargetSource;
+
+/**
+ * struct _ECalPopupTargetSelect - A list of address cards.
+ * 
+ * @target: Superclass.
+ *
+ * Used to represent a selection of appointments as context for a popup
+ * menu.
+ *
+ * FIXME: impelemnt me
+ **/
+struct _ECalPopupTargetSelect {
+	EPopupTarget target;
+};
+
+/**
+ * struct _ECalPopupTargetSource - A source target.
+ * 
+ * @target: Superclass.
+ * @selector: Selector holding the source selection.
+ *
+ * This target is used to represent a source selection.
+ **/
+struct _ECalPopupTargetSource {
+	EPopupTarget target;
+
+	struct _ESourceSelector *selector;
+};
+
+typedef struct _EPopupItem ECalPopupItem;
+
+/* The object */
+struct _ECalPopup {
+	EPopup popup;
+
+	struct _ECalPopupPrivate *priv;
+};
+
+struct _ECalPopupClass {
+	EPopupClass popup_class;
+};
+
+GType e_cal_popup_get_type(void);
+
+ECalPopup *e_cal_popup_new(const char *menuid);
+
+ECalPopupTargetSelect *e_cal_popup_target_new_select(ECalPopup *eabp);
+ECalPopupTargetSource *e_cal_popup_target_new_source(ECalPopup *eabp, struct _ESourceSelector *selector);
+
+/* ********************************************************************** */
+
+typedef struct _ECalPopupHook ECalPopupHook;
+typedef struct _ECalPopupHookClass ECalPopupHookClass;
+
+struct _ECalPopupHook {
+	EPopupHook hook;
+};
+
+struct _ECalPopupHookClass {
+	EPopupHookClass hook_class;
+};
+
+GType e_cal_popup_hook_get_type(void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* __E_CAL_POPUP_H__ */
diff --git a/calendar/gui/tasks-component.c b/calendar/gui/tasks-component.c
index a817ca8546..23482dc33f 100644
--- a/calendar/gui/tasks-component.c
+++ b/calendar/gui/tasks-component.c
@@ -40,6 +40,7 @@
 #include "migration.h"
 #include "comp-util.h"
 #include "calendar-config.h"
+#include "e-cal-popup.h"
 #include "common/authentication.h"
 #include "dialogs/calendar-setup.h"
 #include "dialogs/comp-editor.h"
@@ -47,6 +48,7 @@
 #include "dialogs/task-editor.h"
 #include "widgets/misc/e-source-selector.h"
 #include "widgets/misc/e-info-label.h"
+#include "widgets/misc/e-error.h"
 #include "e-util/e-icon-factory.h"
 
 #define CREATE_TASK_ID               "task"
@@ -252,137 +254,112 @@ update_primary_selection (TasksComponentView *component_view)
 
 /* Callbacks.  */
 static void
-add_popup_menu_item (GtkMenu *menu, const char *label, const char *icon_name,
-		     GCallback callback, gpointer user_data, gboolean sensitive)
-{
-	GtkWidget *item, *image;
-	GdkPixbuf *pixbuf;
-
-	if (icon_name) {
-		item = gtk_image_menu_item_new_with_label (label);
-
-		/* load the image */
-		pixbuf = e_icon_factory_get_icon (icon_name, E_ICON_SIZE_MENU);
-		image = gtk_image_new_from_pixbuf (pixbuf);
-
-		if (image) {
-			gtk_widget_show (image);
-			gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (item), image);
-		}
-	} else {
-		item = gtk_menu_item_new_with_label (label);
-	}
-
-	if (callback)
-		g_signal_connect (G_OBJECT (item), "activate", callback, user_data);
-
-	if (!sensitive)
-		gtk_widget_set_sensitive (item, FALSE);
-
-	gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
-	gtk_widget_show (item);
-}
-
-static void
-copy_task_list_cb (GtkWidget *widget, TasksComponentView *component_view)
+copy_task_list_cb (EPopup *ep, EPopupItem *pitem, void *data)
 {
+	TasksComponentView *component_view = data;
 	ESource *selected_source;
 	
 	selected_source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector));
 	if (!selected_source)
 		return;
 
-	copy_source_dialog (GTK_WINDOW (gtk_widget_get_toplevel (widget)), selected_source, E_CAL_SOURCE_TYPE_TODO);
+	copy_source_dialog (GTK_WINDOW (gtk_widget_get_toplevel(ep->target->widget)), selected_source, E_CAL_SOURCE_TYPE_TODO);
 }
 
 static void
-delete_task_list_cb (GtkWidget *widget, TasksComponentView *component_view)
+delete_task_list_cb (EPopup *ep, EPopupItem *pitem, void *data)
 {
+	TasksComponentView *component_view = data;
 	ESource *selected_source;
-	GtkWidget *dialog;
+	ECal *cal;
+	char *uri;
 
 	selected_source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector));
 	if (!selected_source)
 		return;
 
-	/* create the confirmation dialog */
-	dialog = gtk_message_dialog_new (
-		GTK_WINDOW (gtk_widget_get_toplevel (widget)),
-		GTK_DIALOG_MODAL,
-		GTK_MESSAGE_QUESTION,
-		GTK_BUTTONS_YES_NO,
-		_("Task List '%s' will be removed. Are you sure you want to continue?"),
-		e_source_peek_name (selected_source));
-	if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_YES) {
-		ECal *cal;
-		char *uri;
-
-		/* first, ask the backend to remove the task list */
-		uri = e_source_get_uri (selected_source);
-		cal = e_cal_model_get_client_for_uri (
-			e_calendar_table_get_model (E_CALENDAR_TABLE (e_tasks_get_calendar_table (component_view->tasks))),
-			uri);
-		if (!cal)
-			cal = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_TODO);
-		g_free (uri);
-		if (cal) {
-			if (e_cal_remove (cal, NULL)) {
-				if (e_source_selector_source_is_selected (E_SOURCE_SELECTOR (component_view->source_selector),
-									  selected_source)) {
-					e_tasks_remove_todo_source (component_view->tasks, selected_source);
-					e_source_selector_unselect_source (E_SOURCE_SELECTOR (component_view->source_selector),
-									   selected_source);
-				}
-
-				e_source_group_remove_source (e_source_peek_group (selected_source), selected_source);
-				e_source_list_sync (component_view->source_list, NULL);
+	if (e_error_run((GtkWindow *)gtk_widget_get_toplevel(ep->target->widget),
+			"calendar:prompt-delete-task-list", e_source_peek_name(selected_source)) != GTK_RESPONSE_YES)
+		return;
+
+	/* first, ask the backend to remove the task list */
+	uri = e_source_get_uri (selected_source);
+	cal = e_cal_model_get_client_for_uri (
+		e_calendar_table_get_model (E_CALENDAR_TABLE (e_tasks_get_calendar_table (component_view->tasks))),
+		uri);
+	if (!cal)
+		cal = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_TODO);
+	g_free (uri);
+	if (cal) {
+		if (e_cal_remove (cal, NULL)) {
+			if (e_source_selector_source_is_selected (E_SOURCE_SELECTOR (component_view->source_selector),
+								  selected_source)) {
+				e_tasks_remove_todo_source (component_view->tasks, selected_source);
+				e_source_selector_unselect_source (E_SOURCE_SELECTOR (component_view->source_selector),
+								   selected_source);
 			}
+			
+			e_source_group_remove_source (e_source_peek_group (selected_source), selected_source);
+			e_source_list_sync (component_view->source_list, NULL);
 		}
 	}
-
-	gtk_widget_destroy (dialog);
 }
 
 static void
-new_task_list_cb (GtkWidget *widget, TasksComponentView *component_view)
+new_task_list_cb (EPopup *ep, EPopupItem *pitem, void *data)
 {
-	calendar_setup_new_task_list (GTK_WINDOW (gtk_widget_get_toplevel (widget)));
+	calendar_setup_new_task_list (GTK_WINDOW (gtk_widget_get_toplevel(ep->target->widget)));
 }
 
 static void
-edit_task_list_cb (GtkWidget *widget, TasksComponentView *component_view)
+edit_task_list_cb (EPopup *ep, EPopupItem *pitem, void *data)
 {
+	TasksComponentView *component_view = data;
 	ESource *selected_source;
 
 	selected_source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector));
 	if (!selected_source)
 		return;
 
-	calendar_setup_edit_task_list (GTK_WINDOW (gtk_widget_get_toplevel (widget)), selected_source);
+	calendar_setup_edit_task_list (GTK_WINDOW (gtk_widget_get_toplevel(ep->target->widget)), selected_source);
 }
 
+static EPopupItem etc_source_popups[] = {
+	{ E_POPUP_ITEM, "10.new", N_("New Task List"), new_task_list_cb, NULL, "stock_todo", 0 },
+	{ E_POPUP_ITEM, "15.copy", N_("Copy"), copy_task_list_cb, NULL, "stock_folder-copy", E_CAL_POPUP_SOURCE_PRIMARY },
+	{ E_POPUP_ITEM, "20.delete", N_("Delete"), delete_task_list_cb, NULL, "stock_delete", E_CAL_POPUP_SOURCE_USER|E_CAL_POPUP_SOURCE_PRIMARY },
+	{ E_POPUP_ITEM, "30.properties", N_("Properties..."), edit_task_list_cb, NULL, NULL, E_CAL_POPUP_SOURCE_PRIMARY },
+};
+
 static void
-fill_popup_menu_cb (ESourceSelector *selector, GtkMenu *menu, TasksComponentView *component_view)
+etc_source_popup_free(EPopup *ep, GSList *list, void *data)
 {
-	ESource *source;
-	gboolean sensitive, system;
-	const char *source_uri;
-	
-	source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector));
-	sensitive =  source ? TRUE : FALSE;
-
-	/* FIXME Gross hack, should have a property or something */
-	source_uri = e_source_peek_relative_uri (source);
-	system = source_uri && !strcmp ("system", source_uri);
-
-	add_popup_menu_item (menu, _("New Task List"), "stock_todo",
-			     G_CALLBACK (new_task_list_cb), component_view, TRUE);
-	add_popup_menu_item (menu, _("Copy"), "stock_folder-copy",
-			     G_CALLBACK (copy_task_list_cb), component_view, sensitive);
-	add_popup_menu_item (menu, _("Delete"), "stock_delete", G_CALLBACK (delete_task_list_cb),
-			     component_view, sensitive && !system);
-	add_popup_menu_item (menu, _("Properties..."), NULL, G_CALLBACK (edit_task_list_cb),
-			     component_view, sensitive);
+	g_slist_free(list);
+}
+
+static gboolean
+popup_event_cb(ESourceSelector *selector, ESource *insource, GdkEventButton *event, TasksComponentView *component_view)
+{
+	ECalPopup *ep;
+	ECalPopupTargetSource *t;
+	GSList *menus = NULL;
+	int i;
+	GtkMenu *menu;
+
+	ep = e_cal_popup_new("com.novell.evolution.tasks.source.popup");
+	t = e_cal_popup_target_new_source(ep, selector);
+	t->target.widget = (GtkWidget *)component_view->tasks;
+
+	for (i=0;i<sizeof(etc_source_popups)/sizeof(etc_source_popups[0]);i++)
+		menus = g_slist_prepend(menus, &etc_source_popups[i]);
+
+	e_popup_add_items((EPopup *)ep, menus, etc_source_popup_free, component_view);
+
+	/* visibility is disabled, we only disable menu items */
+	menu = e_popup_create_menu_once((EPopup *)ep, (EPopupTarget *)t, 0, t->target.mask);
+	gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event?event->button:0, event?event->time:gtk_get_current_event_time());
+
+	return TRUE;
 }
 
 static void
@@ -867,7 +844,7 @@ create_component_view (TasksComponent *tasks_component)
 	
 	/* Create sidebar selector */
 	component_view->source_selector = e_source_selector_new (tasks_component->priv->source_list);
-	e_source_selector_set_select_new (component_view->source_selector, TRUE);
+	e_source_selector_set_select_new ((ESourceSelector *)component_view->source_selector, TRUE);
 
 	g_signal_connect (component_view->source_selector, "drag-motion", G_CALLBACK (selector_tree_drag_motion), 
 			  tasks_component);
@@ -936,8 +913,8 @@ create_component_view (TasksComponent *tasks_component)
 			  G_CALLBACK (source_selection_changed_cb), component_view);
 	g_signal_connect (component_view->source_selector, "primary_selection_changed",
 			  G_CALLBACK (primary_source_selection_changed_cb), component_view);
-	g_signal_connect (component_view->source_selector, "fill_popup_menu",
-			  G_CALLBACK (fill_popup_menu_cb), component_view);
+	g_signal_connect (component_view->source_selector, "popup_event",
+			  G_CALLBACK (popup_event_cb), component_view);
 
 	/* Set up the "new" item handler */
 	component_view->creatable_items_handler = e_user_creatable_items_handler_new ("tasks", create_local_item_cb, tasks_component);
-- 
cgit