diff options
author | Harish Krishnaswamy <kharish@novell.com> | 2005-10-19 19:39:35 +0800 |
---|---|---|
committer | Harish Krishnaswamy <kharish@src.gnome.org> | 2005-10-19 19:39:35 +0800 |
commit | 458df50352e58835c3a4fadff6705307dad39ab8 (patch) | |
tree | f6afd1b9b684909657dd078e089e2c6308c3b107 /calendar | |
parent | bf5eb927b690676631a8132cd8638ed5b0948b99 (diff) | |
download | gsoc2013-evolution-458df50352e58835c3a4fadff6705307dad39ab8.tar.gz gsoc2013-evolution-458df50352e58835c3a4fadff6705307dad39ab8.tar.zst gsoc2013-evolution-458df50352e58835c3a4fadff6705307dad39ab8.zip |
Memo Component - submitted by Nathan Owens <pianocomp81@yahoo.com>
2005-10-19 Harish Krishnaswamy <kharish@novell.com>
Memo Component - submitted by Nathan Owens <pianocomp81@yahoo.com>
svn path=/trunk/; revision=30537
Diffstat (limited to 'calendar')
41 files changed, 8476 insertions, 6 deletions
diff --git a/calendar/ChangeLog b/calendar/ChangeLog index b4386e9a85..1298f5c493 100644 --- a/calendar/ChangeLog +++ b/calendar/ChangeLog @@ -1,3 +1,38 @@ +2005-10-19 Harish Krishnaswamy <kharish@novell.com> + + Committing for Nathan Owens <pianocomp81@yahoo.com> + + * calendar.error.xml: Added strings for memos + * calendar/conduits/Makefile.am: added 'memo' to SUBDIRS + * calendar/conduits/memo/*: initial memo conduit + * calendar/gui/calendar-config.[ch]: (calendar_config_get_memos_selected) + (calendar_config_set_memos_selected) (calendar_config_add_notification_memos_selected): + gconf get/set for memos lists + * calendar/gui/calendar-config-keys.h: added gconf keys for Memos component + * calendar/gui/comp-util.[ch] (cal_comp_memo_new_with_defaults): added function + to create memo component with defaults + * calendar/gui/e-cal-component-memo-preview.[ch]: initial file for preview of a memo + * calendar/gui/e-cal-model-memos.[ch]: initial model for the memos component. + * calendar/gui/e-memos.[ch]: initial memos base + * calendar/gui/e-memo-table.[ch]: initial view for memos component + * calendar/gui/e-memo-table.etspec: etspec for memos e-table + * calendar/gui/e-memo-table-config.[ch]: initial configuration for memos e-table view + * calendar/gui/GNOME_Evolution_Calendar.server.in.in: added Memos component + * calendar/gui/main.c: added initialization code for Memos component + * calendar/gui/Makefile.am: added entries for new files for Memos component + * calendar/gui/memos-component.[ch]: Memos component base code + * calendar/gui/memos-control.[ch]: initial user control code. Used for printing, + copying, etc. + * calendar/gui/migration.c: added migration for memos component - also takes care + of initialization for the first time the Memos component is run. + * calendar/gui/dialogs/calendar-setup.[ch]: added code to create a new Memo List + * calendar/gui/dialogs/Makefile.am: added files for build + * calendar/gui/dialogs/memo-editor.[ch]: initial memo editor + * calendar/gui/dialogs/memo-page.[ch]: page in editor for editing a memo + * calendar/gui/dialogs/memo-page.glade: glade file for memo-editor-page + * calendar/gui/dialogs/send-comp.c: added E_CAL_COMPONENT_JOURNAL support for Memo + component + 2005-10-18 Chenthill Palanisamy <pchenthill@novell.com> Fixes #308802 diff --git a/calendar/calendar.error.xml b/calendar/calendar.error.xml index 3fd7c71887..9fcf0f9666 100644 --- a/calendar/calendar.error.xml +++ b/calendar/calendar.error.xml @@ -194,6 +194,13 @@ <button stock="gtk-cancel" response="GTK_RESPONSE_CANCEL"/> <button stock="gtk-delete" response="GTK_RESPONSE_YES"/> </error> + + <error id="prompt-delete-memo-list" type="question" modal="true" default="GTK_RESPONSE_CANCEL"> + <_primary>Delete memo list '{0}'?</_primary> + <_secondary>This memo list will be removed permanently.</_secondary> + <button stock="gtk-cancel" response="GTK_RESPONSE_NO"/> + <button stock="gtk-delete" response="GTK_RESPONSE_YES"/> + </error> <error id="prompt-send-no-subject-calendar" type="question" default="GTK_RESPONSE_YES"> <_primary>Are you sure you want to send the appointment without a summary?</_primary> @@ -221,6 +228,12 @@ <button stock ="gtk-ok" response="GTK_RESPONSE_YES"/> </error> + <error id="prompt-no-contents-offline-memos" type="error" default="GTK_RESPONSE_YES"> + <_primary>Error loading memo list</_primary> + <_secondary>The memo list is not marked for offline usage</_secondary> + <button stock ="gtk-ok" response="GTK_RESPONSE_YES"/> + </error> + <error id="server-version" type="warning"> <title>Server Version</title> <_primary>Some features may not work properly with your current server</_primary> diff --git a/calendar/conduits/Makefile.am b/calendar/conduits/Makefile.am index 906ea61971..365053399f 100644 --- a/calendar/conduits/Makefile.am +++ b/calendar/conduits/Makefile.am @@ -1 +1 @@ -SUBDIRS = calendar todo +SUBDIRS = calendar memo todo diff --git a/calendar/conduits/memo/Makefile.am b/calendar/conduits/memo/Makefile.am new file mode 100644 index 0000000000..70494168ca --- /dev/null +++ b/calendar/conduits/memo/Makefile.am @@ -0,0 +1,37 @@ +INCLUDES = \ + -I$(top_srcdir)/e-util \ + -I$(top_builddir)/e-util \ + -I$(top_srcdir)/widgets/misc \ + -I$(top_builddir)/widgets/misc \ + $(EVOLUTION_CALENDAR_CONDUIT_CFLAGS) + +# Memo Conduit +privconduit_LTLIBRARIES = libememo_conduit.la + +libememo_conduit_la_SOURCES = \ + memo-conduit.c + +libememo_conduit_la_LDFLAGS = -module -avoid-version +libememo_conduit_la_LIBADD = \ + $(top_builddir)/e-util/libeutil.la \ + $(top_builddir)/e-util/libeconduit.la \ + $(top_builddir)/widgets/misc/libemiscwidgets.la \ + $(EVOLUTION_CALENDAR_CONDUIT_LIBS) + +e-memo-$(BASE_VERSION).conduit: e-memo.conduit.in + sed -e 's^\@privconduitdir\@^$(privconduitdir)^g' \ + -e 's^\@datadir\@^$(datadir)^g' \ + -e 's^\@BASE_VERSION\@^$(BASE_VERSION)^g' \ + $< > $@ + +conduitdir = $(datadir)/gnome-pilot/conduits/ +conduit_DATA = e-memo-$(BASE_VERSION).conduit + +BUILT_SOURCES = $(conduit_DATA) +CLEANFILES = $(BUILT_SOURCES) + +EXTRA_DIST = \ + e-memo.conduit.in + +dist-hook: + cd $(distdir); rm -f $(BUILT_SOURCES) diff --git a/calendar/conduits/memo/e-memo.conduit.in b/calendar/conduits/memo/e-memo.conduit.in new file mode 100644 index 0000000000..1cf61436bd --- /dev/null +++ b/calendar/conduits/memo/e-memo.conduit.in @@ -0,0 +1,8 @@ +<gnome-pilot-conduit version="1.0"> + <conduit id="e_memo_conduit" type="shlib" location="@privconduitdir@/libememo_conduit.so"/> + <name value="EMemos"/> + <conduit-attribute name="description" value="Synchronizes Memo List with Evolution @BASE_VERSION@"/> + <conduit-attribute name="default-synctype" value="synchronize"/> + <conduit-attribute name="valid-synctypes" value="synchronize copy_from_pilot copy_to_pilot"/> + <conduit-attribute name="settings" value="TRUE"/> +</gnome-pilot-conduit> diff --git a/calendar/conduits/memo/memo-conduit.c b/calendar/conduits/memo/memo-conduit.c new file mode 100644 index 0000000000..4ad5aab79b --- /dev/null +++ b/calendar/conduits/memo/memo-conduit.c @@ -0,0 +1,1445 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* Evolution calendar - Memo Conduit + * + * Copyright (C) 1998 Free Software Foundation + * Copyright (C) 2000 Ximian, Inc. + * + * Authors: Eskil Heyn Olsen <deity@eskil.dk> + * JP Rosevear <jpr@ximian.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#define G_LOG_DOMAIN "ememoconduit" + +#include <libecal/e-cal-types.h> +#include <libecal/e-cal.h> +#include <libecal/e-cal-time-util.h> +#include <libedataserver/e-categories.h> +#include <pi-source.h> +#include <pi-socket.h> +#include <pi-dlp.h> +#include <pi-memo.h> +#include <libical/icaltypes.h> +#include <gpilotd/gnome-pilot-conduit.h> +#include <gpilotd/gnome-pilot-conduit-sync-abs.h> +#include <libgpilotdCM/gnome-pilot-conduit-management.h> +#include <libgpilotdCM/gnome-pilot-conduit-config.h> +#include <e-pilot-map.h> +#include <e-pilot-settings.h> +#include <e-pilot-util.h> +#include <e-config-listener.h> + +GnomePilotConduit * conduit_get_gpilot_conduit (guint32); +void conduit_destroy_gpilot_conduit (GnomePilotConduit*); + +#define CONDUIT_VERSION "0.1.6" + +#define DEBUG_MEMOCONDUIT 1 +/* #undef DEBUG_MEMOCONDUIT */ + +#ifdef DEBUG_MEMOCONDUIT +#define LOG(x) x +#else +#define LOG(x) +#endif + +#define WARN g_warning +#define INFO g_message + +typedef struct _EMemoLocalRecord EMemoLocalRecord; +typedef struct _EMemoConduitCfg EMemoConduitCfg; +typedef struct _EMemoConduitGui EMemoConduitGui; +typedef struct _EMemoConduitContext EMemoConduitContext; + +/* Local Record */ +struct _EMemoLocalRecord { + /* The stuff from gnome-pilot-conduit-standard-abs.h + Must be first in the structure, or instances of this + structure cannot be used by gnome-pilot-conduit-standard-abs. + */ + GnomePilotDesktopRecord local; + + /* The corresponding Comp object */ + ECalComponent *comp; + + /* pilot-link memo structure */ + struct Memo *memo; +}; + +int lastDesktopUniqueID; + +static void +memoconduit_destroy_record (EMemoLocalRecord *local) +{ + g_object_unref (local->comp); + free_Memo (local->memo); + g_free (local->memo); + g_free (local); +} + +/* Configuration */ +struct _EMemoConduitCfg { + guint32 pilot_id; + GnomePilotConduitSyncType sync_type; + + ESourceList *source_list; + ESource *source; + gboolean secret; + gint priority; + + gchar *last_uri; +}; + +static EMemoConduitCfg * +memoconduit_load_configuration (guint32 pilot_id) +{ + EMemoConduitCfg *c; + GnomePilotConduitManagement *management; + GnomePilotConduitConfig *config; + gchar prefix[256]; + + + g_snprintf (prefix, 255, "/gnome-pilot.d/e-memo-conduit/Pilot_%u/", + pilot_id); + + c = g_new0 (EMemoConduitCfg,1); + g_assert (c != NULL); + + c->pilot_id = pilot_id; + + management = gnome_pilot_conduit_management_new ("e_memo_conduit", GNOME_PILOT_CONDUIT_MGMT_ID); + gtk_object_ref (GTK_OBJECT (management)); + gtk_object_sink (GTK_OBJECT (management)); + config = gnome_pilot_conduit_config_new (management, pilot_id); + gtk_object_ref (GTK_OBJECT (config)); + gtk_object_sink (GTK_OBJECT (config)); + if (!gnome_pilot_conduit_config_is_enabled (config, &c->sync_type)) + c->sync_type = GnomePilotConduitSyncTypeNotSet; + gtk_object_unref (GTK_OBJECT (config)); + gtk_object_unref (GTK_OBJECT (management)); + + /* Custom settings */ + gnome_config_push_prefix (prefix); + + if (!e_cal_get_sources (&c->source_list, E_CAL_SOURCE_TYPE_JOURNAL, NULL)) + c->source_list = NULL; + if (c->source_list) { + c->source = e_pilot_get_sync_source (c->source_list); + if (!c->source) + c->source = e_source_list_peek_source_any (c->source_list); + if (c->source) { + g_object_ref (c->source); + } else { + g_object_unref (c->source_list); + c->source_list = NULL; + } + } + + c->secret = gnome_config_get_bool ("secret=FALSE"); + c->priority = gnome_config_get_int ("priority=3"); + c->last_uri = gnome_config_get_string ("last_uri"); + + gnome_config_pop_prefix (); + + return c; +} + +static void +memoconduit_save_configuration (EMemoConduitCfg *c) +{ + gchar prefix[256]; + + g_snprintf (prefix, 255, "/gnome-pilot.d/e-memo-conduit/Pilot_%u/", + c->pilot_id); + + gnome_config_push_prefix (prefix); + e_pilot_set_sync_source (c->source_list, c->source); + gnome_config_set_bool ("secret", c->secret); + gnome_config_set_int ("priority", c->priority); + gnome_config_set_string ("last_uri", c->last_uri); + gnome_config_pop_prefix (); + + gnome_config_sync (); + gnome_config_drop_all (); +} + +static EMemoConduitCfg* +memoconduit_dupe_configuration (EMemoConduitCfg *c) +{ + EMemoConduitCfg *retval; + + g_return_val_if_fail (c != NULL, NULL); + + retval = g_new0 (EMemoConduitCfg, 1); + retval->sync_type = c->sync_type; + retval->pilot_id = c->pilot_id; + + if (c->source_list) + retval->source_list = g_object_ref (c->source_list); + if (c->source) + retval->source = g_object_ref (c->source); + retval->secret = c->secret; + retval->priority = c->priority; + retval->last_uri = g_strdup (c->last_uri); + + return retval; +} + +static void +memoconduit_destroy_configuration (EMemoConduitCfg *c) +{ + g_return_if_fail (c != NULL); + + g_object_unref (c->source_list); + g_object_unref (c->source); + g_free (c->last_uri); + g_free (c); +} + +/* Context */ +struct _EMemoConduitContext { + GnomePilotDBInfo *dbi; + + EMemoConduitCfg *cfg; + EMemoConduitCfg *new_cfg; + GtkWidget *ps; + + struct MemoAppInfo ai; + + ECal *client; + + icaltimezone *timezone; + ECalComponent *default_comp; + GList *comps; + GList *changed; + GHashTable *changed_hash; + GList *locals; + + EPilotMap *map; +}; + +static EMemoConduitContext * +e_memo_context_new (guint32 pilot_id) +{ + EMemoConduitContext *ctxt = g_new0 (EMemoConduitContext, 1); + + ctxt->cfg = memoconduit_load_configuration (pilot_id); + ctxt->new_cfg = memoconduit_dupe_configuration (ctxt->cfg); + ctxt->ps = NULL; + ctxt->client = NULL; + ctxt->timezone = NULL; + ctxt->default_comp = NULL; + ctxt->comps = NULL; + ctxt->changed_hash = NULL; + ctxt->changed = NULL; + ctxt->locals = NULL; + ctxt->map = NULL; + + return ctxt; +} + +static gboolean +e_memo_context_foreach_change (gpointer key, gpointer value, gpointer data) +{ + g_free (key); + + return TRUE; +} + +static void +e_memo_context_destroy (EMemoConduitContext *ctxt) +{ + GList *l; + + g_return_if_fail (ctxt != NULL); + + if (ctxt->cfg != NULL) + memoconduit_destroy_configuration (ctxt->cfg); + if (ctxt->new_cfg != NULL) + memoconduit_destroy_configuration (ctxt->new_cfg); + + if (ctxt->client != NULL) + g_object_unref (ctxt->client); + + if (ctxt->default_comp != NULL) + g_object_unref (ctxt->default_comp); + if (ctxt->comps != NULL) { + for (l = ctxt->comps; l; l = l->next) + g_object_unref (l->data); + g_list_free (ctxt->comps); + } + + if (ctxt->changed_hash != NULL) { + g_hash_table_foreach_remove (ctxt->changed_hash, e_memo_context_foreach_change, NULL); + g_hash_table_destroy (ctxt->changed_hash); + } + + if (ctxt->locals != NULL) { + for (l = ctxt->locals; l != NULL; l = l->next) + memoconduit_destroy_record (l->data); + g_list_free (ctxt->locals); + } + + if (ctxt->changed != NULL) + e_cal_free_change_list (ctxt->changed); + + if (ctxt->map != NULL) + e_pilot_map_destroy (ctxt->map); + + g_free (ctxt); +} + +/* Debug routines */ +static char * +print_local (EMemoLocalRecord *local) +{ + static char buff[ 64 ]; + + if (local == NULL) { + sprintf (buff, "[NULL]"); + return buff; + } + + if (local->memo && local->memo->text) { + g_snprintf (buff, 64, "['%s']", + local->memo->text ? + local->memo->text : ""); + return buff; + } + + return ""; +} + +static char *print_remote (GnomePilotRecord *remote) +{ + static char buff[ 64 ]; + struct Memo memo; + + if (remote == NULL) { + sprintf (buff, "[NULL]"); + return buff; + } + + memset (&memo, 0, sizeof (struct Memo)); + unpack_Memo (&memo, remote->record, remote->length); + + g_snprintf (buff, 64, "['%s']", + memo.text ? + memo.text : ""); + + free_Memo (&memo); + + return buff; +} + +static int +start_calendar_server (EMemoConduitContext *ctxt) +{ + g_return_val_if_fail (ctxt != NULL, -2); + + if (ctxt->cfg->source) { + ctxt->client = e_cal_new (ctxt->cfg->source, E_CAL_SOURCE_TYPE_JOURNAL); + if (!e_cal_open (ctxt->client, TRUE, NULL)) + return -1; + } else if (!e_cal_open_default (&ctxt->client, E_CAL_SOURCE_TYPE_JOURNAL, NULL, NULL, NULL)) { + return -1; + } + + return 0; +} + +static icaltimezone * +get_default_timezone (void) +{ + EConfigListener *listener; + icaltimezone *timezone = NULL; + char *location; + + listener = e_config_listener_new (); + + location = e_config_listener_get_string_with_default (listener, + "/apps/evolution/calendar/display/timezone", "UTC", NULL); + if (!location || !location[0]) { + g_free (location); + location = g_strdup ("UTC"); + } + + timezone = icaltimezone_get_builtin_timezone (location); + g_free (location); + + g_object_unref (listener); + + return timezone; +} + +static char * +map_name (EMemoConduitContext *ctxt) +{ + char *filename; + + filename = g_strdup_printf ("%s/.evolution/memos/local/system/pilot-map-memo-%d.xml", g_get_home_dir (), ctxt->cfg->pilot_id); + + return filename; +} + +static GList * +next_changed_item (EMemoConduitContext *ctxt, GList *changes) +{ + ECalChange *ccc; + GList *l; + + for (l = changes; l != NULL; l = l->next) { + const char *uid; + + ccc = l->data; + + e_cal_component_get_uid (ccc->comp, &uid); + if (g_hash_table_lookup (ctxt->changed_hash, uid)) + return l; + } + + return NULL; +} + +static void +compute_status (EMemoConduitContext *ctxt, EMemoLocalRecord *local, const char *uid) +{ + ECalChange *ccc; + + local->local.archived = FALSE; + local->local.secret = FALSE; + + ccc = g_hash_table_lookup (ctxt->changed_hash, uid); + + if (ccc == NULL) { + local->local.attr = GnomePilotRecordNothing; + return; + } + + switch (ccc->type) { + case E_CAL_CHANGE_ADDED: + local->local.attr = GnomePilotRecordNew; + break; + case E_CAL_CHANGE_MODIFIED: + local->local.attr = GnomePilotRecordModified; + break; + case E_CAL_CHANGE_DELETED: + local->local.attr = GnomePilotRecordDeleted; + break; + } +} + +static GnomePilotRecord +local_record_to_pilot_record (EMemoLocalRecord *local, + EMemoConduitContext *ctxt) +{ + GnomePilotRecord p; + static char record[0xffff]; + + g_assert (local->comp != NULL); + g_assert (local->memo != NULL ); + + LOG (g_message ( "local_record_to_pilot_record\n" )); + + p.ID = local->local.ID; + p.category = local->local.category; + p.attr = local->local.attr; + p.archived = local->local.archived; + p.secret = local->local.secret; + + /* Generate pilot record structure */ + p.record = record; + p.length = pack_Memo (local->memo, p.record, 0xffff); + + return p; +} + +/* + * Adds a category to the category app info structure (name and ID), + * sets category->renamed[i] to true if possible to rename. + * + * This will be packed and written to the app info block during post_sync. + */ + +static int +add_category_if_possible(char *cat_to_add, struct CategoryAppInfo *category) +{ + int i, j; + int retval = 0; /* 0 is the Unfiled category */ + LOG(fprintf(stderr, "add_category_if_possible: called\n")); + + for(i=0; i<16; i++){ + /* if strlen is 0, then the category is empty + the PalmOS doesn't let 0-length strings for + categories */ + LOG(fprintf(stderr, "add_category_if_possible: calling strlen, i==%d\n", i)); + if(strlen(category->name[i]) == 0){ + int cat_to_add_len; + LOG(fprintf(stderr, "add_category_if_possible: strlen == 0\n")); + + cat_to_add_len = strlen(cat_to_add); + LOG(fprintf(stderr, "add_category_if_possible: cat_to_add_len: %d\n", + cat_to_add_len)); + retval = i; + + /* only 15 characters for category, 16th is + * '\0' can't do direct mem transfer due to + * declaration type + */ + LOG(fprintf(stderr, "add_category_if_possible: copying first 15 of category\n")); + for(j=0; j<cat_to_add_len; j++){ + category->name[i][j] = cat_to_add[j]; + } + LOG(fprintf(stderr, + "add_category_if_possible: setting from %d to i==15 to \\0\n", + cat_to_add_len)); + + for(j=cat_to_add_len; j<16; j++) + category->name[i][j] = '\0'; + + LOG(fprintf(stderr, "add_category_if_possible: setting ID[%d] to %d\n", + category->ID[i], lastDesktopUniqueID)); + category->ID[i] = lastDesktopUniqueID; + lastDesktopUniqueID++; + + LOG(fprintf(stderr, "add_category_if_possible: setting renamed[%d] to TRUE\n", i)); + category->renamed[i] = TRUE; + + LOG(g_message("*** adding category '%s', ID %d ***", + category->name[i], category->ID[i])); + break; + } + } + + if(retval == 0){ + LOG(g_message("*** not adding category - category list already full ***")); + } + + return retval; +} + +/* + * converts a ECalComponent object to a EMemoLocalRecord + */ +static void +local_record_from_comp (EMemoLocalRecord *local, ECalComponent *comp, EMemoConduitContext *ctxt) +{ + const char *uid; + GSList *d_list = NULL; + ECalComponentText *description; + ECalComponentClassification classif; + + LOG (g_message ( "local_record_from_comp\n" )); + + g_return_if_fail (local != NULL); + g_return_if_fail (comp != NULL); + + local->comp = comp; + g_object_ref (comp); + + LOG(fprintf(stderr, "local_record_from_comp: calling e_cal_component_get_uid\n")); + e_cal_component_get_uid (local->comp, &uid); + LOG(fprintf(stderr, "local_record_from_comp: got UID - %s, calling e_pilot_map_lookup_pid\n", uid)); + local->local.ID = e_pilot_map_lookup_pid (ctxt->map, uid, TRUE); + LOG(fprintf(stderr, "local_record_from_comp: local->local.ID == %lu\n", local->local.ID)); + + compute_status (ctxt, local, uid); + + LOG(fprintf(stderr, "local_record_from_comp: local->local.attr: %d\n", local->local.attr)); + + local->memo = g_new0 (struct Memo,1); + + /* Don't overwrite the category */ + if (local->local.ID != 0) { + char record[0xffff]; + int cat = 0; + + LOG(fprintf(stderr, "local_record_from_comp: calling dlp_ReadRecordById\n")); + if (dlp_ReadRecordById (ctxt->dbi->pilot_socket, + ctxt->dbi->db_handle, + local->local.ID, &record, + NULL, NULL, NULL, &cat) > 0) { + local->local.category = cat; + } + LOG(fprintf(stderr, "local_record_from_comp: done calling dlp_ReadRecordById\n")); + } + + /* + * Grab category from existing category list in ctxt->ai.category + */ + if(local->local.category == 0){ + GSList *categ_list_head, *categ_list_cur; + int cat = -1; + int i; + + LOG(fprintf(stderr, "local_record_from_comp: trying to set category")); + LOG(fprintf(stderr, "local_record_from_comp: calling e_cal_component_get_categories_list\n")); + + e_cal_component_get_categories_list(comp, &categ_list_head); + LOG(fprintf(stderr, "local_record_from_comp: got list, setting categ_list_cur to head\n")); + + categ_list_cur = categ_list_head; + while (categ_list_cur && cat == -1) + { + LOG(fprintf(stderr, "local_record_from_comp: iterating, data == %s", + (char *)categ_list_cur->data)); + for(i=0; i<16; i++){ + LOG(fprintf(stderr, "local_record_from_comp: i == %d\n", i)); + if(strcmp((char *)categ_list_cur->data, + ctxt->ai.category.name[i]) == 0){ + cat = i; + LOG(fprintf(stderr, "local_record_from_comp: found category, name: %s\n", + ctxt->ai.category.name[i])); + break; + } + } + + LOG(fprintf(stderr, "local_record_from_comp: calling g_slist_next\n")); + categ_list_cur = g_slist_next(categ_list_cur); + } + + if(cat != -1){ + LOG(fprintf(stderr, "local_record_from_comp: setting category\n")); + local->local.category = cat; + } + else if(categ_list_head != NULL){ + local->local.category = + add_category_if_possible( + (char *)(categ_list_head->data), + &(ctxt->ai.category)); + } + } + + /* STOP: don't replace these with g_strdup, since free_Memo + uses free to deallocate */ + + e_cal_component_get_description_list (comp, &d_list); + if (d_list) { + description = (ECalComponentText *) d_list->data; + if (description && description->value){ + local->memo->text = e_pilot_utf8_to_pchar (description->value); + } + else{ + local->memo->text = NULL; + } + } else { + local->memo->text = NULL; + } + + e_cal_component_get_classification (comp, &classif); + + if (classif == E_CAL_COMPONENT_CLASS_PRIVATE) + local->local.secret = 1; + else + local->local.secret = 0; + + local->local.archived = 0; +} + +static void +local_record_from_uid (EMemoLocalRecord *local, + const char *uid, + EMemoConduitContext *ctxt) +{ + ECalComponent *comp; + icalcomponent *icalcomp; + GError *error = NULL; + + g_assert(local!=NULL); + + LOG(g_message("local_record_from_uid\n")); + + if (e_cal_get_object (ctxt->client, uid, NULL, &icalcomp, &error)) { + comp = e_cal_component_new (); + if (!e_cal_component_set_icalcomponent (comp, icalcomp)) { + g_object_unref (comp); + icalcomponent_free (icalcomp); + return; + } + + local_record_from_comp (local, comp, ctxt); + g_object_unref (comp); + } else if (error->code == E_CALENDAR_STATUS_OBJECT_NOT_FOUND) { + comp = e_cal_component_new (); + e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL); + e_cal_component_set_uid (comp, uid); + local_record_from_comp (local, comp, ctxt); + g_object_unref (comp); + } else { + INFO ("Object did not exist"); + } + + g_clear_error (&error); +} + + +static ECalComponent * +comp_from_remote_record (GnomePilotConduitSyncAbs *conduit, + GnomePilotRecord *remote, + ECalComponent *in_comp, + icaltimezone *timezone, + struct MemoAppInfo *ai) +{ + ECalComponent *comp; + struct Memo memo; + struct icaltimetype now; + icaltimezone *utc_zone; + char *txt, *txt2, *txt3; + char *category; + int i; + + g_return_val_if_fail (remote != NULL, NULL); + + memset (&memo, 0, sizeof (struct Memo)); + unpack_Memo (&memo, remote->record, remote->length); + + utc_zone = icaltimezone_get_utc_timezone (); + now = icaltime_from_timet_with_zone (time (NULL), FALSE, + utc_zone); + + if (in_comp == NULL) { + comp = e_cal_component_new (); + e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL); + e_cal_component_set_created (comp, &now); + } else { + comp = e_cal_component_clone (in_comp); + } + + e_cal_component_set_last_modified (comp, &now); + + /* The iCal description field */ + if (!memo.text) { + e_cal_component_set_comment_list (comp, NULL); + e_cal_component_set_summary(comp, NULL); + } else { + int idxToUse = -1, ntext = strlen(memo.text); + gboolean foundNL = FALSE; + GSList l; + ECalComponentText text, sumText; + + for(i = 0; i<ntext && i<50; i++){ + if(memo.text[i] == '\n'){ + idxToUse = i; + foundNL = TRUE; + break; + } + } + + if(foundNL == FALSE){ + if(ntext > 50){ + txt2 = g_strndup(memo.text, 50); + } + else{ + txt2 = g_strdup(memo.text); + + } + } + else{ + txt2 = g_strndup(memo.text, idxToUse); /* cuts off '\n' */ + + } + + sumText.value = txt3 = e_pilot_utf8_from_pchar(txt2); + sumText.altrep = NULL; + + text.value = txt = e_pilot_utf8_from_pchar (memo.text); + text.altrep = NULL; + l.data = &text; + l.next = NULL; + + e_cal_component_set_summary(comp, &sumText); + e_cal_component_set_description_list (comp, &l); + free (txt); + g_free(txt2); + free(txt3); + } + + + e_cal_component_set_transparency (comp, E_CAL_COMPONENT_TRANSP_NONE); + + if (remote->secret) + e_cal_component_set_classification (comp, E_CAL_COMPONENT_CLASS_PRIVATE); + else + e_cal_component_set_classification (comp, E_CAL_COMPONENT_CLASS_PUBLIC); + + + /* set the category properly */ + category = ai->category.name[remote->category]; + + /* TODO The Memos editor page and search bar are not updated until + a restart of the evolution client */ + if(e_categories_exist(category) == FALSE){ + /* add if it doesn't exist */ + e_categories_add(category, NULL, NULL, TRUE); + } + + e_cal_component_set_categories(comp, category); + + e_cal_component_commit_sequence (comp); + + free_Memo(&memo); + + return comp; +} + +static void +check_for_slow_setting (GnomePilotConduit *c, EMemoConduitContext *ctxt) +{ + GnomePilotConduitStandard *conduit = GNOME_PILOT_CONDUIT_STANDARD (c); + int map_count; + const char *uri; + + /* If there are no objects or objects but no log */ + map_count = g_hash_table_size (ctxt->map->pid_map); + if (map_count == 0) + gnome_pilot_conduit_standard_set_slow (conduit, TRUE); + + /* Or if the URI's don't match */ + uri = e_cal_get_uri (ctxt->client); + LOG (g_message ( " Current URI %s (%s)\n", uri, ctxt->cfg->last_uri ? ctxt->cfg->last_uri : "<NONE>" )); + if (ctxt->cfg->last_uri != NULL && (strcmp (ctxt->cfg->last_uri, uri) != 0)) { + gnome_pilot_conduit_standard_set_slow (conduit, TRUE); + e_pilot_map_clear (ctxt->map); + } + + if (gnome_pilot_conduit_standard_get_slow (conduit)) { + ctxt->map->write_touched_only = TRUE; + LOG (g_message ( " doing slow sync\n" )); + } else { + LOG (g_message ( " doing fast sync\n" )); + } +} + +/* Pilot syncing callbacks */ +static gint +pre_sync (GnomePilotConduit *conduit, + GnomePilotDBInfo *dbi, + EMemoConduitContext *ctxt) +{ + GnomePilotConduitSyncAbs *abs_conduit; + GList *l; + int len; + unsigned char *buf; + char *filename, *change_id; + icalcomponent *icalcomp; + gint num_records, add_records = 0, mod_records = 0, del_records = 0; + + abs_conduit = GNOME_PILOT_CONDUIT_SYNC_ABS (conduit); + + LOG (g_message ( "---------------------------------------------------------\n" )); + LOG (g_message ( "pre_sync: Memo Conduit v.%s", CONDUIT_VERSION )); + g_message ("Memo Conduit v.%s", CONDUIT_VERSION); + + ctxt->dbi = dbi; + ctxt->client = NULL; + + if (start_calendar_server (ctxt) != 0) { + WARN(_("Could not start evolution-data-server")); + gnome_pilot_conduit_error (conduit, _("Could not start evolution-data-server")); + return -1; + } + + /* Get the timezone */ + ctxt->timezone = get_default_timezone (); + if (ctxt->timezone == NULL) + return -1; + LOG (g_message ( " Using timezone: %s", icaltimezone_get_tzid (ctxt->timezone) )); + + /* Set the default timezone on the backend. */ + if (ctxt->timezone && !e_cal_set_default_timezone (ctxt->client, ctxt->timezone, NULL)) + return -1; + + /* Get the default component */ + if (!e_cal_get_default_object (ctxt->client, &icalcomp, NULL)) + return -1; + + ctxt->default_comp = e_cal_component_new (); + if (!e_cal_component_set_icalcomponent (ctxt->default_comp, icalcomp)) { + g_object_unref (ctxt->default_comp); + icalcomponent_free (icalcomp); + return -1; + } + + /* Load the uid <--> pilot id map */ + filename = map_name (ctxt); + e_pilot_map_read (filename, &ctxt->map); + g_free (filename); + + /* Get the local database */ + if (!e_cal_get_object_list_as_comp (ctxt->client, "#t", &ctxt->comps, NULL)) + return -1; + + /* Count and hash the changes */ + change_id = g_strdup_printf ("pilot-sync-evolution-memo-%d", ctxt->cfg->pilot_id); + if (!e_cal_get_changes (ctxt->client, change_id, &ctxt->changed, NULL)) + return -1; + + ctxt->changed_hash = g_hash_table_new (g_str_hash, g_str_equal); + g_free (change_id); + + for (l = ctxt->changed; l != NULL; l = l->next) { + ECalChange *ccc = l->data; + const char *uid; + + e_cal_component_get_uid (ccc->comp, &uid); + if (!e_pilot_map_uid_is_archived (ctxt->map, uid)) { + + g_hash_table_insert (ctxt->changed_hash, g_strdup (uid), ccc); + + switch (ccc->type) { + case E_CAL_CHANGE_ADDED: + add_records++; + break; + case E_CAL_CHANGE_MODIFIED: + mod_records++; + break; + case E_CAL_CHANGE_DELETED: + del_records++; + break; + } + } else if (ccc->type == E_CAL_CHANGE_DELETED) { + e_pilot_map_remove_by_uid (ctxt->map, uid); + } + } + + /* Set the count information */ + num_records = g_list_length (ctxt->comps); + gnome_pilot_conduit_sync_abs_set_num_local_records(abs_conduit, num_records); + gnome_pilot_conduit_sync_abs_set_num_new_local_records (abs_conduit, add_records); + gnome_pilot_conduit_sync_abs_set_num_updated_local_records (abs_conduit, mod_records); + gnome_pilot_conduit_sync_abs_set_num_deleted_local_records(abs_conduit, del_records); + + g_message("num_records: %d\nadd_records: %d\nmod_records: %d\ndel_records: %d\n", + num_records, add_records, mod_records, del_records); + + buf = (unsigned char*)g_malloc (0xffff); + len = dlp_ReadAppBlock (dbi->pilot_socket, dbi->db_handle, 0, + (unsigned char *)buf, 0xffff); + + if (len < 0) { + WARN (_("Could not read pilot's Memo application block")); + WARN ("dlp_ReadAppBlock(...) = %d", len); + gnome_pilot_conduit_error (conduit, + _("Could not read pilot's Memo application block")); + return -1; + } + unpack_MemoAppInfo (&(ctxt->ai), buf, len); + g_free (buf); + + lastDesktopUniqueID = 128; + + check_for_slow_setting (conduit, ctxt); + if (ctxt->cfg->sync_type == GnomePilotConduitSyncTypeCopyToPilot + || ctxt->cfg->sync_type == GnomePilotConduitSyncTypeCopyFromPilot) + ctxt->map->write_touched_only = TRUE; + + return 0; +} + +static gint +post_sync (GnomePilotConduit *conduit, + GnomePilotDBInfo *dbi, + EMemoConduitContext *ctxt) +{ + GList *changed; + gchar *filename, *change_id; + unsigned char *buf; + int dlpRetVal, len; + + buf = (unsigned char*)g_malloc (0xffff); + + len = pack_MemoAppInfo (&(ctxt->ai), buf, 0xffff); + + dlpRetVal = dlp_WriteAppBlock (dbi->pilot_socket, dbi->db_handle, + (unsigned char *)buf, len); + + g_free (buf); + + if (dlpRetVal < 0) { + WARN (_("Could not write pilot's Memo application block")); + WARN ("dlp_WriteAppBlock(...) = %d", dlpRetVal); + gnome_pilot_conduit_error (conduit, + _("Could not write pilot's Memo application block")); + return -1; + } + + + LOG (g_message ( "post_sync: Memo Conduit v.%s", CONDUIT_VERSION )); + + g_free (ctxt->cfg->last_uri); + ctxt->cfg->last_uri = g_strdup (e_cal_get_uri (ctxt->client)); + memoconduit_save_configuration (ctxt->cfg); + + filename = map_name (ctxt); + e_pilot_map_write (filename, ctxt->map); + g_free (filename); + + /* FIX ME ugly hack - our changes musn't count, this does introduce + * a race condition if anyone changes a record elsewhere during sycnc + */ + change_id = g_strdup_printf ("pilot-sync-evolution-memo-%d", ctxt->cfg->pilot_id); + if (e_cal_get_changes (ctxt->client, change_id, &changed, NULL)) + e_cal_free_change_list (changed); + g_free (change_id); + + LOG (g_message ( "---------------------------------------------------------\n" )); + + return 0; +} + +static gint +set_pilot_id (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord *local, + guint32 ID, + EMemoConduitContext *ctxt) +{ + const char *uid; + + LOG (g_message ( "set_pilot_id: setting to %d\n", ID )); + + e_cal_component_get_uid (local->comp, &uid); + e_pilot_map_insert (ctxt->map, ID, uid, FALSE); + + return 0; +} + +static gint +set_status_cleared (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord *local, + EMemoConduitContext *ctxt) +{ + const char *uid; + + LOG (g_message ( "set_status_cleared: clearing status\n" )); + + e_cal_component_get_uid (local->comp, &uid); + g_hash_table_remove (ctxt->changed_hash, uid); + + return 0; +} + +static gint +for_each (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord **local, + EMemoConduitContext *ctxt) +{ + static GList *comps, *iterator; + static int count; + + g_return_val_if_fail (local != NULL, -1); + + if (*local == NULL) { + LOG (g_message ( "beginning for_each" )); + + comps = ctxt->comps; + count = 0; + + if (comps != NULL) { + LOG (g_message ( "for_each: iterating over %d records", g_list_length (comps))); + + *local = g_new0 (EMemoLocalRecord, 1); + LOG(fprintf(stderr, "for_each: calling local_record_from_comp\n")); + local_record_from_comp (*local, comps->data, ctxt); + LOG(fprintf(stderr, "for_each: calling g_list_prepend\n")); + g_list_prepend (ctxt->locals, *local); + LOG(fprintf(stderr, "for_each: setting iterator = comps\n")); + iterator = comps; + } else { + LOG (g_message ( "no events" )); + (*local) = NULL; + return 0; + } + } else { + count++; + LOG(fprintf(stderr, "for_each: calling g_list_next (else part)\n")); + if (g_list_next (iterator)) { + iterator = g_list_next (iterator); + LOG(fprintf(stderr, "for_each: creating EMemoLocalRecord\n")); + *local = g_new0 (EMemoLocalRecord, 1); + LOG(fprintf(stderr, "for_each: calling local_record_from_comp\n")); + local_record_from_comp (*local, iterator->data, ctxt); + LOG(fprintf(stderr, "for_each: calling g_list_prepend\n")); + g_list_prepend (ctxt->locals, *local); + } else { + LOG (g_message ( "for_each ending" )); + + /* Tell the pilot the iteration is over */ + *local = NULL; + + return 0; + } + } + + return 0; +} + +static gint +for_each_modified (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord **local, + EMemoConduitContext *ctxt) +{ + static GList *iterator; + static int count; + + g_return_val_if_fail (local != NULL, 0); + + if (*local == NULL) { + LOG (g_message ( "for_each_modified beginning\n" )); + + iterator = ctxt->changed; + + count = 0; + + LOG (g_message ( "iterating over %d records", g_hash_table_size (ctxt->changed_hash) )); + + iterator = next_changed_item (ctxt, iterator); + if (iterator != NULL) { + ECalChange *ccc = iterator->data; + LOG(fprintf(stderr, "for_each_modified: creating EMemoLocalRecord\n")); + *local = g_new0 (EMemoLocalRecord, 1); + LOG(fprintf(stderr, "for_each_modified: calling local_record_from_comp\n")); + local_record_from_comp (*local, ccc->comp, ctxt); + LOG(fprintf(stderr, "for_each_modified: calling g_list_prepend\n")); + g_list_prepend (ctxt->locals, *local); + } else { + LOG (g_message ( "no events" )); + + *local = NULL; + } + } else { + count++; + LOG(fprintf(stderr, "for_each_modified: calling g_list_next\n")); + iterator = g_list_next (iterator); + LOG(fprintf(stderr, "for_each_modified: calling next_changed_item\n")); + if (iterator && (iterator = next_changed_item (ctxt, iterator))) { + ECalChange *ccc = iterator->data; + LOG(fprintf(stderr, "for_each_modified: calling EMemoLocalRecord\n")); + *local = g_new0 (EMemoLocalRecord, 1); + LOG(fprintf(stderr, "for_each_modified: calling local_record_from_comp\n")); + local_record_from_comp (*local, ccc->comp, ctxt); + LOG(fprintf(stderr, "for_each_modified: calling g_list_prepend\n")); + g_list_prepend (ctxt->locals, *local); + } else { + LOG (g_message ( "for_each_modified ending" )); + + /* Signal the iteration is over */ + *local = NULL; + } + } + + return 0; +} + +static gint +compare (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord *local, + GnomePilotRecord *remote, + EMemoConduitContext *ctxt) +{ + /* used by the quick compare */ + GnomePilotRecord local_pilot; + int retval = 0; + + LOG (g_message ("compare: local=%s remote=%s...\n", + print_local (local), print_remote (remote))); + + g_return_val_if_fail (local!=NULL,-1); + g_return_val_if_fail (remote!=NULL,-1); + + local_pilot = local_record_to_pilot_record (local, ctxt); + + if (remote->length != local_pilot.length + || memcmp (local_pilot.record, remote->record, remote->length)) + retval = 1; + + if (retval == 0) + LOG (g_message ( " equal" )); + else + LOG (g_message ( " not equal" )); + + return retval; +} + +static gint +add_record (GnomePilotConduitSyncAbs *conduit, + GnomePilotRecord *remote, + EMemoConduitContext *ctxt) +{ + ECalComponent *comp; + char *uid; + int retval = 0; + + g_return_val_if_fail (remote != NULL, -1); + + LOG (g_message ( "add_record: adding %s to desktop\n", print_remote (remote) )); + + comp = comp_from_remote_record (conduit, remote, ctxt->default_comp, ctxt->timezone, &(ctxt->ai)); + + /* Give it a new UID otherwise it will be the uid of the default comp */ + uid = e_cal_component_gen_uid (); + e_cal_component_set_uid (comp, uid); + + if (!e_cal_create_object (ctxt->client, e_cal_component_get_icalcomponent (comp), NULL, NULL)) + return -1; + + e_pilot_map_insert (ctxt->map, remote->ID, uid, FALSE); + + g_object_unref (comp); + + return retval; +} + +static gint +replace_record (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord *local, + GnomePilotRecord *remote, + EMemoConduitContext *ctxt) +{ + ECalComponent *new_comp; + int retval = 0; + + g_return_val_if_fail (remote != NULL, -1); + + LOG (g_message ("replace_record: replace %s with %s\n", + print_local (local), print_remote (remote))); + + new_comp = comp_from_remote_record (conduit, remote, local->comp, ctxt->timezone, &(ctxt->ai)); + g_object_unref (local->comp); + local->comp = new_comp; + + if (!e_cal_modify_object (ctxt->client, e_cal_component_get_icalcomponent (new_comp), + CALOBJ_MOD_ALL, NULL)) + return -1; + + return retval; +} + +static gint +delete_record (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord *local, + EMemoConduitContext *ctxt) +{ + const char *uid; + + g_return_val_if_fail (local != NULL, -1); + g_return_val_if_fail (local->comp != NULL, -1); + + e_cal_component_get_uid (local->comp, &uid); + + LOG (g_message ( "delete_record: deleting %s", uid )); + + e_pilot_map_remove_by_uid (ctxt->map, uid); + /* FIXME Error handling */ + e_cal_remove_object (ctxt->client, uid, NULL); + + return 0; +} + +static gint +archive_record (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord *local, + gboolean archive, + EMemoConduitContext *ctxt) +{ + const char *uid; + int retval = 0; + + g_return_val_if_fail (local != NULL, -1); + + LOG (g_message ( "archive_record: %s\n", archive ? "yes" : "no" )); + + e_cal_component_get_uid (local->comp, &uid); + e_pilot_map_insert (ctxt->map, local->local.ID, uid, archive); + + return retval; +} + +static gint +match (GnomePilotConduitSyncAbs *conduit, + GnomePilotRecord *remote, + EMemoLocalRecord **local, + EMemoConduitContext *ctxt) +{ + const char *uid; + + LOG (g_message ("match: looking for local copy of %s\n", + print_remote (remote))); + + g_return_val_if_fail (local != NULL, -1); + g_return_val_if_fail (remote != NULL, -1); + + *local = NULL; + uid = e_pilot_map_lookup_uid (ctxt->map, remote->ID, TRUE); + + if (!uid) + return 0; + + LOG (g_message ( " matched\n" )); + + *local = g_new0 (EMemoLocalRecord, 1); + local_record_from_uid (*local, uid, ctxt); + + return 0; +} + +static gint +free_match (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord *local, + EMemoConduitContext *ctxt) +{ + LOG (g_message ( "free_match: freeing\n" )); + + g_return_val_if_fail (local != NULL, -1); + + memoconduit_destroy_record (local); + + return 0; +} + +static gint +prepare (GnomePilotConduitSyncAbs *conduit, + EMemoLocalRecord *local, + GnomePilotRecord *remote, + EMemoConduitContext *ctxt) +{ + LOG (g_message ( "prepare: encoding local %s\n", print_local (local) )); + + *remote = local_record_to_pilot_record (local, ctxt); + + return 0; +} + +/* Pilot Settings Callbacks */ +static void +fill_widgets (EMemoConduitContext *ctxt) +{ + if (ctxt->cfg->source) + e_pilot_settings_set_source (E_PILOT_SETTINGS (ctxt->ps), + ctxt->cfg->source); + e_pilot_settings_set_secret (E_PILOT_SETTINGS (ctxt->ps), + ctxt->cfg->secret); +} + +static gint +create_settings_window (GnomePilotConduit *conduit, + GtkWidget *parent, + EMemoConduitContext *ctxt) +{ + LOG (g_message ( "create_settings_window" )); + + if (!ctxt->cfg->source_list) + return -1; + + ctxt->ps = e_pilot_settings_new (ctxt->cfg->source_list); + + gtk_container_add (GTK_CONTAINER (parent), ctxt->ps); + gtk_widget_show (ctxt->ps); + + fill_widgets (ctxt); + + return 0; +} + +static void +display_settings (GnomePilotConduit *conduit, EMemoConduitContext *ctxt) +{ + LOG (g_message ( "display_settings" )); + + fill_widgets (ctxt); +} + +static void +save_settings (GnomePilotConduit *conduit, EMemoConduitContext *ctxt) +{ + LOG (g_message ( "save_settings" )); + + if (ctxt->new_cfg->source) + g_object_unref (ctxt->new_cfg->source); + ctxt->new_cfg->source = e_pilot_settings_get_source (E_PILOT_SETTINGS (ctxt->ps)); + g_object_ref (ctxt->new_cfg->source); + ctxt->new_cfg->secret = e_pilot_settings_get_secret (E_PILOT_SETTINGS (ctxt->ps)); + + memoconduit_save_configuration (ctxt->new_cfg); +} + +static void +revert_settings (GnomePilotConduit *conduit, EMemoConduitContext *ctxt) +{ + LOG (g_message ( "revert_settings" )); + + memoconduit_save_configuration (ctxt->cfg); + memoconduit_destroy_configuration (ctxt->new_cfg); + ctxt->new_cfg = memoconduit_dupe_configuration (ctxt->cfg); +} + +GnomePilotConduit * +conduit_get_gpilot_conduit (guint32 pilot_id) +{ + GtkObject *retval; + EMemoConduitContext *ctxt; + + LOG (g_message ( "in memo's conduit_get_gpilot_conduit\n" )); + + retval = gnome_pilot_conduit_sync_abs_new ("MemoDB", 0x6D656D6F); + g_assert (retval != NULL); + + ctxt = e_memo_context_new (pilot_id); + gtk_object_set_data (GTK_OBJECT (retval), "memoconduit_context", ctxt); + + gtk_signal_connect (retval, "pre_sync", (GtkSignalFunc) pre_sync, ctxt); + gtk_signal_connect (retval, "post_sync", (GtkSignalFunc) post_sync, ctxt); + + gtk_signal_connect (retval, "set_pilot_id", (GtkSignalFunc) set_pilot_id, ctxt); + gtk_signal_connect (retval, "set_status_cleared", (GtkSignalFunc) set_status_cleared, ctxt); + + gtk_signal_connect (retval, "for_each", (GtkSignalFunc) for_each, ctxt); + gtk_signal_connect (retval, "for_each_modified", (GtkSignalFunc) for_each_modified, ctxt); + gtk_signal_connect (retval, "compare", (GtkSignalFunc) compare, ctxt); + + gtk_signal_connect (retval, "add_record", (GtkSignalFunc) add_record, ctxt); + gtk_signal_connect (retval, "replace_record", (GtkSignalFunc) replace_record, ctxt); + gtk_signal_connect (retval, "delete_record", (GtkSignalFunc) delete_record, ctxt); + gtk_signal_connect (retval, "archive_record", (GtkSignalFunc) archive_record, ctxt); + + gtk_signal_connect (retval, "match", (GtkSignalFunc) match, ctxt); + gtk_signal_connect (retval, "free_match", (GtkSignalFunc) free_match, ctxt); + + gtk_signal_connect (retval, "prepare", (GtkSignalFunc) prepare, ctxt); + + /* Gui Settings */ + gtk_signal_connect (retval, "create_settings_window", (GtkSignalFunc) create_settings_window, ctxt); + gtk_signal_connect (retval, "display_settings", (GtkSignalFunc) display_settings, ctxt); + gtk_signal_connect (retval, "save_settings", (GtkSignalFunc) save_settings, ctxt); + gtk_signal_connect (retval, "revert_settings", (GtkSignalFunc) revert_settings, ctxt); + + return GNOME_PILOT_CONDUIT (retval); +} + +void +conduit_destroy_gpilot_conduit (GnomePilotConduit *conduit) +{ + GtkObject *obj = GTK_OBJECT (conduit); + EMemoConduitContext *ctxt; + + ctxt = gtk_object_get_data (obj, "memoconduit_context"); + e_memo_context_destroy (ctxt); + + gtk_object_destroy (obj); +} diff --git a/calendar/gui/GNOME_Evolution_Calendar.server.in.in b/calendar/gui/GNOME_Evolution_Calendar.server.in.in index 18c71798f0..f930ff9e72 100644 --- a/calendar/gui/GNOME_Evolution_Calendar.server.in.in +++ b/calendar/gui/GNOME_Evolution_Calendar.server.in.in @@ -71,6 +71,26 @@ <oaf_attribute name="evolution:button_sort_order" type="string" value="-8"/> </oaf_server> +<oaf_server iid="OAFIID:GNOME_Evolution_Memos_Component:@VERSION@" + type="factory" + location="OAFIID:GNOME_Evolution_Calendar_Factory:@VERSION@"> + + <oaf_attribute name="repo_ids" type="stringv"> + <item value="IDL:GNOME/Evolution/Component:@VERSION@"/> + </oaf_attribute> + + <oaf_attribute name="evolution:component_alias" type="string" value="memos"/> + + <oaf_attribute name="name" type="string" _value="Evolution's Memos component"/> + + <oaf_attribute name="evolution:menu_label" type="string" _value="Memo_s"/> + <oaf_attribute name="evolution:menu_accelerator" type="string" value="*Control*F6"/> + <oaf_attribute name="evolution:button_label" type="string" _value="Memos"/> + <oaf_attribute name="evolution:button_tooltips" type="string" _value="Memos"/> + <oaf_attribute name="evolution:button_icon" type="string" value="stock_notes"/> + <oaf_attribute name="evolution:button_sort_order" type="string" value="-8"/> +</oaf_server> + <oaf_server iid="OAFIID:GNOME_Evolution_Calendar_CompEditorFactory:@VERSION@" type="factory" location="OAFIID:GNOME_Evolution_Calendar_Factory:@VERSION@"> diff --git a/calendar/gui/Makefile.am b/calendar/gui/Makefile.am index 4225642d7d..5b854fd7cc 100644 --- a/calendar/gui/Makefile.am +++ b/calendar/gui/Makefile.am @@ -75,7 +75,8 @@ glade_DATA = \ etspec_DATA = \ e-calendar-table.etspec \ e-meeting-time-sel.etspec \ - e-cal-list-view.etspec + e-cal-list-view.etspec \ + e-memo-table.etspec libevolution_calendar_la_SOURCES = \ $(IDL_GENERATED) \ @@ -103,6 +104,8 @@ libevolution_calendar_la_SOURCES = \ e-alarm-list.h \ e-cal-component-preview.c \ e-cal-component-preview.h \ + e-cal-component-memo-preview.c \ + e-cal-component-memo-preview.h \ e-cal-config.c \ e-cal-config.h \ e-cal-event.c \ @@ -124,6 +127,8 @@ libevolution_calendar_la_SOURCES = \ e-cal-list-view.h \ e-cal-list-view-config.c \ e-cal-list-view-config.h \ + e-cal-model-memos.c \ + e-cal-model-memos.h \ e-calendar-table.c \ e-calendar-table.h \ e-calendar-table-config.c \ @@ -165,6 +170,12 @@ libevolution_calendar_la_SOURCES = \ e-meeting-types.h \ e-meeting-utils.c \ e-meeting-utils.h \ + e-memo-table.c \ + e-memo-table.h \ + e-memo-table-config.c \ + e-memo-table-config.h \ + e-memos.c \ + e-memos.h \ e-mini-calendar-config.c \ e-mini-calendar-config.h \ e-pub-utils.c \ @@ -198,6 +209,10 @@ libevolution_calendar_la_SOURCES = \ itip-utils.c \ itip-utils.h \ main.c \ + memos-component.c \ + memos-component.h \ + memos-control.c \ + memos-control.h \ migration.c \ migration.h \ misc.c \ diff --git a/calendar/gui/calendar-config-keys.h b/calendar/gui/calendar-config-keys.h index eea70cfbcc..3a18e93d51 100644 --- a/calendar/gui/calendar-config-keys.h +++ b/calendar/gui/calendar-config-keys.h @@ -65,6 +65,11 @@ G_BEGIN_DECLS #define CALENDAR_CONFIG_TASKS_DUE_TODAY_COLOR CALENDAR_CONFIG_PREFIX "/tasks/colors/due_today" #define CALENDAR_CONFIG_TASKS_OVERDUE_COLOR CALENDAR_CONFIG_PREFIX "/tasks/colors/overdue" +/* Memo display settings */ +#define CALENDAR_CONFIG_MEMOS_SELECTED_MEMOS CALENDAR_CONFIG_PREFIX "/memos/selected_memos" +#define CALENDAR_CONFIG_PRIMARY_MEMOS CALENDAR_CONFIG_PREFIX "/memos/primary_memos" + + /* Prompt settings */ #define CALENDAR_CONFIG_PROMPT_DELETE CALENDAR_CONFIG_PREFIX "/prompts/confirm_delete" #define CALENDAR_CONFIG_PROMPT_PURGE CALENDAR_CONFIG_PREFIX "/prompts/confirm_purge" diff --git a/calendar/gui/calendar-config.c b/calendar/gui/calendar-config.c index 8a33f6084c..bb6cb9e379 100644 --- a/calendar/gui/calendar-config.c +++ b/calendar/gui/calendar-config.c @@ -649,6 +649,55 @@ calendar_config_set_task_vpane_pos (gint vpane_pos) gconf_client_set_int (config, CALENDAR_CONFIG_TASK_VPANE_POS, vpane_pos, NULL); } +/***************************************/ + +/* The current list of memo lists selected */ +GSList * +calendar_config_get_memos_selected (void) +{ + return gconf_client_get_list (config, CALENDAR_CONFIG_MEMOS_SELECTED_MEMOS, GCONF_VALUE_STRING, NULL); +} + +void +calendar_config_set_memos_selected (GSList *selected) +{ + gconf_client_set_list (config, CALENDAR_CONFIG_MEMOS_SELECTED_MEMOS, GCONF_VALUE_STRING, selected, NULL); +} + +guint +calendar_config_add_notification_memos_selected (GConfClientNotifyFunc func, gpointer data) +{ + guint id; + + id = gconf_client_notify_add (config, CALENDAR_CONFIG_MEMOS_SELECTED_MEMOS, func, data, NULL, NULL); + + return id; +} + +/* The primary memo list */ +char * +calendar_config_get_primary_memos (void) +{ + return gconf_client_get_string (config, CALENDAR_CONFIG_PRIMARY_MEMOS, NULL); +} + +void +calendar_config_set_primary_memos (const char *primary_uid) +{ + gconf_client_set_string (config, CALENDAR_CONFIG_PRIMARY_MEMOS, primary_uid, NULL); +} + + +guint +calendar_config_add_notification_primary_memos (GConfClientNotifyFunc func, gpointer data) +{ + guint id; + + id = gconf_client_notify_add (config, CALENDAR_CONFIG_PRIMARY_MEMOS, func, data, NULL, NULL); + + return id; +} +/***************************************/ /* Whether we compress the weekend in the week/month views. */ gboolean diff --git a/calendar/gui/calendar-config.h b/calendar/gui/calendar-config.h index 0a89b8a4fa..132e4af3ce 100644 --- a/calendar/gui/calendar-config.h +++ b/calendar/gui/calendar-config.h @@ -166,6 +166,17 @@ guint calendar_config_add_notification_primary_tasks (GConfClientNotifyFunc fu gint calendar_config_get_task_vpane_pos (void); void calendar_config_set_task_vpane_pos (gint vpane_pos); + +/* The current list of memo lists selected */ +GSList *calendar_config_get_memos_selected (void); +void calendar_config_set_memos_selected (GSList *selected); +guint calendar_config_add_notification_memos_selected (GConfClientNotifyFunc func, gpointer data); + +/* The primary calendar */ +char *calendar_config_get_primary_memos (void); +void calendar_config_set_primary_memos (const char *primary_uid); +guint calendar_config_add_notification_primary_memos (GConfClientNotifyFunc func, gpointer data); + /* Colors for the task list */ const char *calendar_config_get_tasks_due_today_color (void); void calendar_config_set_tasks_due_today_color (const char *color); diff --git a/calendar/gui/comp-util.c b/calendar/gui/comp-util.c index 2be56453c1..fad01446da 100644 --- a/calendar/gui/comp-util.c +++ b/calendar/gui/comp-util.c @@ -369,3 +369,22 @@ cal_comp_task_new_with_defaults (ECal *client) return comp; } + +ECalComponent * +cal_comp_memo_new_with_defaults (ECal *client) +{ + ECalComponent *comp; + icalcomponent *icalcomp; + + if (!e_cal_get_default_object (client, &icalcomp, NULL)) + icalcomp = icalcomponent_new (ICAL_VJOURNAL_COMPONENT); + + comp = e_cal_component_new (); + if (!e_cal_component_set_icalcomponent (comp, icalcomp)) { + icalcomponent_free (icalcomp); + + e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL); + } + + return comp; +} diff --git a/calendar/gui/comp-util.h b/calendar/gui/comp-util.h index 26c7881e99..c57de4c692 100644 --- a/calendar/gui/comp-util.h +++ b/calendar/gui/comp-util.h @@ -42,5 +42,6 @@ gboolean cal_comp_is_on_server (ECalComponent *comp, ECalComponent *cal_comp_event_new_with_defaults (ECal *client); ECalComponent *cal_comp_event_new_with_current_time (ECal *client, gboolean all_day); ECalComponent *cal_comp_task_new_with_defaults (ECal *client); +ECalComponent *cal_comp_memo_new_with_defaults (ECal *client); #endif diff --git a/calendar/gui/dialogs/Makefile.am b/calendar/gui/dialogs/Makefile.am index e6b2b45434..b38b6223a2 100644 --- a/calendar/gui/dialogs/Makefile.am +++ b/calendar/gui/dialogs/Makefile.am @@ -51,6 +51,10 @@ libcal_dialogs_la_SOURCES = \ event-editor.h \ event-page.c \ event-page.h \ + memo-editor.c \ + memo-editor.h \ + memo-page.c \ + memo-page.h \ meeting-page.c \ meeting-page.h \ recurrence-page.c \ @@ -72,7 +76,7 @@ libcal_dialogs_la_SOURCES = \ task-page.c \ task-page.h \ url-editor-dialog.c \ - url-editor-dialog.h + url-editor-dialog.h glade_DATA = \ alarm-dialog.glade \ @@ -81,6 +85,7 @@ glade_DATA = \ e-delegate-dialog.glade \ event-page.glade \ meeting-page.glade \ + memo-page.glade \ recurrence-page.glade \ schedule-page.glade \ task-details-page.glade \ diff --git a/calendar/gui/dialogs/calendar-setup.c b/calendar/gui/dialogs/calendar-setup.c index 3b6b578786..37b18d0d77 100644 --- a/calendar/gui/dialogs/calendar-setup.c +++ b/calendar/gui/dialogs/calendar-setup.c @@ -288,6 +288,8 @@ eccp_general_offline (EConfig *ec, EConfigItem *item, struct _GtkWidget *parent, else if (sdialog->source_type == E_CAL_SOURCE_TYPE_TODO) offline_setting = gtk_check_button_new_with_label (_("Copy task list contents locally for offline operation")); + else if(sdialog->source_type == E_CAL_SOURCE_TYPE_JOURNAL) + offline_setting = gtk_check_button_new_with_label(_("Copy memo list contents locally for offline operation")); gtk_widget_show (offline_setting); g_signal_connect (offline_setting, "toggled", G_CALLBACK (offline_status_changed_cb), sdialog); @@ -383,6 +385,17 @@ static ECalConfigItem ectp_items[] = { { 0 }, }; +static ECalConfigItem ecmp_items[] = { + { E_CONFIG_BOOK, "", NULL }, + { E_CONFIG_PAGE, "00.general", N_("General") }, + { E_CONFIG_SECTION_TABLE, "00.general/00.source", N_("Memos List") }, + { E_CONFIG_ITEM_TABLE, "00.general/00.source/00.type", NULL, eccp_get_source_type }, + { E_CONFIG_ITEM_TABLE, "00.general/00.source/10.name", NULL, eccp_get_source_name }, + { E_CONFIG_ITEM_TABLE, "00.general/00.source/20.color", NULL, eccp_get_source_color }, + { E_CONFIG_ITEM_TABLE, "00.general/00.source/30.offline", NULL, eccp_general_offline }, + { 0 }, +}; + /** * calendar_setup_edit_calendar: * @parent: parent window for dialog (current unused) @@ -526,3 +539,69 @@ calendar_setup_new_task_list (struct _GtkWindow *parent) { calendar_setup_edit_task_list (parent, NULL); } + +void +calendar_setup_edit_memo_list (struct _GtkWindow *parent, ESource *source) +{ + CalendarSourceDialog *sdialog = g_new0 (CalendarSourceDialog, 1); + char *xml; + ECalConfig *ec; + int i; + GSList *items = NULL; + ECalConfigTargetSource *target; + + if (source) { + guint32 color; + + sdialog->original_source = source; + g_object_ref (source); + sdialog->source_group = e_source_peek_group (source); + xml = e_source_to_standalone_xml (source); + sdialog->source = e_source_new_from_standalone_xml (xml); + g_free (xml); + + e_source_get_color (source, &color); + e_source_set_color (sdialog->source, color); + } else { + GConfClient *gconf; + GSList *l; + + sdialog->source = e_source_new ("", ""); + gconf = gconf_client_get_default (); + sdialog->source_list = e_source_list_new_for_gconf (gconf, "/apps/evolution/memos/sources"); + l = e_source_list_peek_groups (sdialog->source_list); + sdialog->menu_source_groups = g_slist_copy(l); + + sdialog->source_group = (ESourceGroup *)sdialog->menu_source_groups->data; + g_object_unref (gconf); + } + + /* HACK: doesn't work if you don't do this */ + e_source_set_absolute_uri (sdialog->source, NULL); + e_source_set_group (sdialog->source, sdialog->source_group); + + sdialog->source_type = E_CAL_SOURCE_TYPE_JOURNAL; + sdialog->config = ec = e_cal_config_new (E_CONFIG_BOOK, "org.gnome.evolution.calendar.calendarProperties"); + for (i = 0; ecmp_items[i].path; i++) + items = g_slist_prepend (items, &ecmp_items[i]); + e_config_add_items ((EConfig *) ec, items, eccp_commit, NULL, eccp_free, sdialog); + e_config_add_page_check ((EConfig *) ec, NULL, eccp_check_complete, sdialog); + + target = e_cal_config_target_new_source (ec, sdialog->source); + target->source_type = E_CAL_SOURCE_TYPE_JOURNAL; + e_config_set_target ((EConfig *) ec, (EConfigTarget *) target); + + sdialog->window = e_config_create_window ((EConfig *)ec, NULL, _("New Memo List")); + + /* forces initial validation */ + if (!sdialog->original_source) + e_config_target_changed ((EConfig *)ec, E_CONFIG_TARGET_CHANGED_STATE); + + return; +} + +void +calendar_setup_new_memo_list (struct _GtkWindow *parent) +{ + calendar_setup_edit_memo_list (parent, NULL); +} diff --git a/calendar/gui/dialogs/calendar-setup.h b/calendar/gui/dialogs/calendar-setup.h index 3e266df93c..0c4c272405 100644 --- a/calendar/gui/dialogs/calendar-setup.h +++ b/calendar/gui/dialogs/calendar-setup.h @@ -36,6 +36,9 @@ void calendar_setup_new_calendar (struct _GtkWindow *parent); void calendar_setup_edit_task_list (struct _GtkWindow *parent, struct _ESource *source); void calendar_setup_new_task_list (struct _GtkWindow *parent); +void calendar_setup_edit_memo_list (struct _GtkWindow *parent, ESource *source); +void calendar_setup_new_memo_list (struct _GtkWindow *parent); + #ifdef __cplusplus } #endif diff --git a/calendar/gui/dialogs/memo-editor.c b/calendar/gui/dialogs/memo-editor.c new file mode 100644 index 0000000000..7946198e9b --- /dev/null +++ b/calendar/gui/dialogs/memo-editor.c @@ -0,0 +1,285 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* Evolution calendar - Memo editor dialog + * + * Copyright (C) 2000 Ximian, Inc. + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Miguel de Icaza <miguel@ximian.com> + * Federico Mena-Quintero <federico@ximian.com> + * Seth Alves <alves@hungry.com> + * JP Rosevear <jpr@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <glade/glade.h> +#include <libgnome/gnome-i18n.h> + +#include "memo-page.h" +#include "cancel-comp.h" +#include "memo-editor.h" + +struct _MemoEditorPrivate { + MemoPage *memo_page; + + gboolean updating; +}; + +static void memo_editor_set_e_cal (CompEditor *editor, ECal *client); +static void memo_editor_edit_comp (CompEditor *editor, ECalComponent *comp); +static gboolean memo_editor_send_comp (CompEditor *editor, ECalComponentItipMethod method); +static void memo_editor_finalize (GObject *object); + +static void refresh_memo_cmd (GtkWidget *widget, gpointer data); +static void cancel_memo_cmd (GtkWidget *widget, gpointer data); +static void forward_cmd (GtkWidget *widget, gpointer data); + +static void model_row_change_insert_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data); +static void model_row_delete_cb (GtkTreeModel *model, GtkTreePath *path, gpointer data); + +G_DEFINE_TYPE (MemoEditor, memo_editor, TYPE_COMP_EDITOR); + + + +/** + * memo_editor_get_type: + * + * Registers the #MemoEditor class if necessary, and returns the type ID + * associated to it. + * + * Return value: The type ID of the #MemoEditor class. + **/ + +/* Class initialization function for the event editor */ +static void +memo_editor_class_init (MemoEditorClass *klass) +{ + GObjectClass *object_class; + CompEditorClass *editor_class; + + object_class = (GObjectClass *) klass; + editor_class = (CompEditorClass *) klass; + + editor_class->set_e_cal = memo_editor_set_e_cal; + editor_class->edit_comp = memo_editor_edit_comp; + editor_class->send_comp = memo_editor_send_comp; + + object_class->finalize = memo_editor_finalize; +} + +static void +init_widgets (MemoEditor *me) +{ + MemoEditorPrivate *priv; + + priv = me->priv; +} + +static void +client_changed_cb (CompEditorPage *page, ECal *client, gpointer user_data) +{ +/* set_menu_sens (MEMO_EDITOR (user_data)); */ +} + +/* Object initialization function for the memo editor */ +static void +memo_editor_init (MemoEditor *te) +{ + MemoEditorPrivate *priv; + + priv = g_new0 (MemoEditorPrivate, 1); + te->priv = priv; + + priv->updating = FALSE; + + /* TODO add help stuff */ +/* comp_editor_set_help_section (COMP_EDITOR (te), "usage-calendar-memo"); */ +} + +MemoEditor * +memo_editor_construct (MemoEditor *me, ECal *client) +{ + MemoEditorPrivate *priv; + + gboolean read_only = FALSE; + + priv = me->priv; + + priv->memo_page = memo_page_new (); + g_object_ref (priv->memo_page); + gtk_object_sink (GTK_OBJECT (priv->memo_page)); + comp_editor_append_page (COMP_EDITOR (me), + COMP_EDITOR_PAGE (priv->memo_page), + _("Memo")); + g_signal_connect (G_OBJECT (priv->memo_page), "client_changed", + G_CALLBACK (client_changed_cb), me); + + if (!e_cal_is_read_only (client, &read_only, NULL)) + read_only = TRUE; + + comp_editor_set_e_cal (COMP_EDITOR (me), client); + + init_widgets (me); + + return me; +} + +static void +memo_editor_set_e_cal (CompEditor *editor, ECal *client) +{ + MemoEditor *te; + MemoEditorPrivate *priv; + + te = MEMO_EDITOR (editor); + priv = te->priv; + + if (COMP_EDITOR_CLASS (memo_editor_parent_class)->set_e_cal) + COMP_EDITOR_CLASS (memo_editor_parent_class)->set_e_cal (editor, client); +} + +static void +memo_editor_edit_comp (CompEditor *editor, ECalComponent *comp) +{ + MemoEditor *me; + MemoEditorPrivate *priv; + ECalComponentOrganizer organizer; + ECal *client; + + me = MEMO_EDITOR (editor); + priv = me->priv; + + priv->updating = TRUE; + + if (COMP_EDITOR_CLASS (memo_editor_parent_class)->edit_comp) + COMP_EDITOR_CLASS (memo_editor_parent_class)->edit_comp (editor, comp); + + client = comp_editor_get_e_cal (COMP_EDITOR (editor)); + + priv->updating = FALSE; +} + +static gboolean +memo_editor_send_comp (CompEditor *editor, ECalComponentItipMethod method) +{ + MemoEditor *me = MEMO_EDITOR (editor); + MemoEditorPrivate *priv; + ECalComponent *comp = NULL; + + priv = me->priv; + + if (COMP_EDITOR_CLASS (memo_editor_parent_class)->send_comp) + return COMP_EDITOR_CLASS (memo_editor_parent_class)->send_comp (editor, method); + + return FALSE; +} + +/* Destroy handler for the event editor */ +static void +memo_editor_finalize (GObject *object) +{ + MemoEditor *me; + MemoEditorPrivate *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_MEMO_EDITOR (object)); + + me = MEMO_EDITOR (object); + priv = me->priv; + + if (priv->memo_page) { + g_object_unref (priv->memo_page); + priv->memo_page = NULL; + } + + g_free (priv); + + if (G_OBJECT_CLASS (memo_editor_parent_class)->finalize) + (* G_OBJECT_CLASS (memo_editor_parent_class)->finalize) (object); +} + +/** + * memo_editor_new: + * @client: an ECal + * + * Creates a new event editor dialog. + * + * Return value: A newly-created event editor dialog, or NULL if the event + * editor could not be created. + **/ +MemoEditor * +memo_editor_new (ECal *client) +{ + MemoEditor *me; + + me = g_object_new (TYPE_MEMO_EDITOR, NULL); + return memo_editor_construct (me, client); +} + +static void +refresh_memo_cmd (GtkWidget *widget, gpointer data) +{ + MemoEditor *me = MEMO_EDITOR (data); + + comp_editor_send_comp (COMP_EDITOR (me), E_CAL_COMPONENT_METHOD_REFRESH); +} + +static void +cancel_memo_cmd (GtkWidget *widget, gpointer data) +{ + MemoEditor *me = MEMO_EDITOR (data); + ECalComponent *comp; + + comp = comp_editor_get_current_comp (COMP_EDITOR (me)); + if (cancel_component_dialog ((GtkWindow *) me, + comp_editor_get_e_cal (COMP_EDITOR (me)), comp, FALSE)) { + comp_editor_send_comp (COMP_EDITOR (me), E_CAL_COMPONENT_METHOD_CANCEL); + comp_editor_delete_comp (COMP_EDITOR (me)); + } +} + +static void +forward_cmd (GtkWidget *widget, gpointer data) +{ + MemoEditor *me = MEMO_EDITOR (data); + + if (comp_editor_save_comp (COMP_EDITOR (me), TRUE)) + comp_editor_send_comp (COMP_EDITOR (me), E_CAL_COMPONENT_METHOD_PUBLISH); +} + +static void +model_changed (MemoEditor *me) +{ + if (!me->priv->updating) { + comp_editor_set_changed (COMP_EDITOR (me), TRUE); + comp_editor_set_needs_send (COMP_EDITOR (me), TRUE); + } +} + +static void +model_row_change_insert_cb (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, gpointer data) +{ + model_changed (MEMO_EDITOR (data)); +} + +static void +model_row_delete_cb (GtkTreeModel *model, GtkTreePath *path, gpointer data) +{ + model_changed (MEMO_EDITOR (data)); +} diff --git a/calendar/gui/dialogs/memo-editor.h b/calendar/gui/dialogs/memo-editor.h new file mode 100644 index 0000000000..79c86e9d4e --- /dev/null +++ b/calendar/gui/dialogs/memo-editor.h @@ -0,0 +1,59 @@ +/* Evolution calendar - Task editor dialog + * + * Copyright (C) 2000 Ximian, Inc. + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Miguel de Icaza <miguel@ximian.com> + * Federico Mena-Quintero <federico@ximian.com> + * Seth Alves <alves@hungry.com> + * Nathan Owens <pianocomp81@yahoo.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef __MEMO_EDITOR_H__ +#define __MEMO_EDITOR_H__ + +#include <gtk/gtkobject.h> +#include "comp-editor.h" + +#define TYPE_MEMO_EDITOR (memo_editor_get_type ()) +#define MEMO_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MEMO_EDITOR, MemoEditor)) +#define MEMO_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_MEMO_EDITOR, \ + MemoEditorClass)) +#define IS_MEMO_EDITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MEMO_EDITOR)) +#define IS_MEMO_EDITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_MEMO_EDITOR)) + +typedef struct _MemoEditor MemoEditor; +typedef struct _MemoEditorClass MemoEditorClass; +typedef struct _MemoEditorPrivate MemoEditorPrivate; + +struct _MemoEditor { + CompEditor parent; + + /* Private data */ + MemoEditorPrivate *priv; +}; + +struct _MemoEditorClass { + CompEditorClass parent_class; +}; + +GtkType memo_editor_get_type (void); +MemoEditor *memo_editor_construct (MemoEditor *te, + ECal *client); +MemoEditor *memo_editor_new (ECal *client); + + +#endif /* __MEMO_EDITOR_H__ */ diff --git a/calendar/gui/dialogs/memo-page.c b/calendar/gui/dialogs/memo-page.c new file mode 100644 index 0000000000..0a2644b699 --- /dev/null +++ b/calendar/gui/dialogs/memo-page.c @@ -0,0 +1,618 @@ +/* Evolution calendar - Main page of the memo editor dialog + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Federico Mena-Quintero <federico@ximian.com> + * Miguel de Icaza <miguel@ximian.com> + * Seth Alves <alves@hungry.com> + * JP Rosevear <jpr@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <gtk/gtksignal.h> +#include <gtk/gtktextview.h> +#include <gtk/gtktogglebutton.h> +#include <gtk/gtkspinbutton.h> +#include <gtk/gtkoptionmenu.h> +#include <gtk/gtkmessagedialog.h> +#include <libgnome/gnome-i18n.h> +#include <glade/glade.h> +#include <libedataserverui/e-source-option-menu.h> +#include <widgets/misc/e-dateedit.h> + +#include "common/authentication.h" +#include "e-util/e-dialog-widgets.h" +#include "e-util/e-categories-config.h" +#include "../calendar-config.h" +#include "comp-editor.h" +#include "comp-editor-util.h" +#include "e-send-options-utils.h" +#include "memo-page.h" + + +/* Private part of the TaskPage structure */ +struct _MemoPagePrivate { + /* Glade XML data */ + GladeXML *xml; + + /* Widgets from the Glade file */ + GtkWidget *main; + + GtkWidget *memo_content; + + GtkWidget *classification; + + GtkWidget *categories_btn; + GtkWidget *categories; + + GtkWidget *source_selector; + + gboolean updating; +}; + +static const int classification_map[] = { + E_CAL_COMPONENT_CLASS_PUBLIC, + E_CAL_COMPONENT_CLASS_PRIVATE, + E_CAL_COMPONENT_CLASS_CONFIDENTIAL, + -1 +}; + + + +static void memo_page_finalize (GObject *object); + +static GtkWidget *memo_page_get_widget (CompEditorPage *page); +static void memo_page_focus_main_widget (CompEditorPage *page); +static gboolean memo_page_fill_widgets (CompEditorPage *page, ECalComponent *comp); +static gboolean memo_page_fill_component (CompEditorPage *page, ECalComponent *comp); + +G_DEFINE_TYPE (MemoPage, memo_page, TYPE_COMP_EDITOR_PAGE); + + + +/** + * memo_page_get_type: + * + * Registers the #TaskPage class if necessary, and returns the type ID + * associated to it. + * + * Return value: The type ID of the #TaskPage class. + **/ + +/* Class initialization function for the memo page */ +static void +memo_page_class_init (MemoPageClass *klass) +{ + CompEditorPageClass *editor_page_class; + GObjectClass *object_class; + + editor_page_class = (CompEditorPageClass *) klass; + object_class = (GObjectClass *) klass; + + editor_page_class->get_widget = memo_page_get_widget; + editor_page_class->focus_main_widget = memo_page_focus_main_widget; + editor_page_class->fill_widgets = memo_page_fill_widgets; + editor_page_class->fill_component = memo_page_fill_component; + + object_class->finalize = memo_page_finalize; +} + +/* Object initialization function for the memo page */ +static void +memo_page_init (MemoPage *tpage) +{ + MemoPagePrivate *priv; + + priv = g_new0 (MemoPagePrivate, 1); + tpage->priv = priv; + + priv->xml = NULL; + + priv->main = NULL; + priv->memo_content = NULL; + priv->classification = NULL; + priv->categories_btn = NULL; + priv->categories = NULL; + + priv->updating = FALSE; +} + +/* Destroy handler for the memo page */ +static void +memo_page_finalize (GObject *object) +{ + MemoPage *tpage; + MemoPagePrivate *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (IS_MEMO_PAGE (object)); + + tpage = MEMO_PAGE (object); + priv = tpage->priv; + + if (priv->main) + gtk_widget_unref (priv->main); + + if (priv->xml) { + g_object_unref (priv->xml); + priv->xml = NULL; + } + + g_free (priv); + tpage->priv = NULL; + + if (G_OBJECT_CLASS (memo_page_parent_class)->finalize) + (* G_OBJECT_CLASS (memo_page_parent_class)->finalize) (object); +} + + + +/* get_widget handler for the task page */ +static GtkWidget * +memo_page_get_widget (CompEditorPage *page) +{ + MemoPage *tpage; + MemoPagePrivate *priv; + + tpage = MEMO_PAGE (page); + priv = tpage->priv; + + return priv->main; +} + +/* focus_main_widget handler for the memo page */ +static void +memo_page_focus_main_widget (CompEditorPage *page) +{ + MemoPage *tpage; + MemoPagePrivate *priv; + + tpage = MEMO_PAGE (page); + priv = tpage->priv; + + gtk_widget_grab_focus (priv->memo_content); +} + +/* Fills the widgets with default values */ +static void +clear_widgets (MemoPage *tpage) +{ + MemoPagePrivate *priv; + + priv = tpage->priv; + + /* memo content */ + gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->memo_content)), "", 0); + + /* Classification */ + e_dialog_option_menu_set (priv->classification, E_CAL_COMPONENT_CLASS_PRIVATE, classification_map); + + /* Categories */ + e_dialog_editable_set (priv->categories, NULL); +} + +/* Decode the radio button group for classifications */ +static ECalComponentClassification +classification_get (GtkWidget *widget) +{ + return e_dialog_option_menu_get (widget, classification_map); +} + +static void +sensitize_widgets (MemoPage *mpage) +{ + gboolean read_only; + MemoPagePrivate *priv; + + priv = mpage->priv; + + if (!e_cal_is_read_only (COMP_EDITOR_PAGE (mpage)->client, &read_only, NULL)) + read_only = TRUE; + + gtk_widget_set_sensitive (priv->memo_content, !read_only); + gtk_widget_set_sensitive (priv->classification, !read_only); + gtk_widget_set_sensitive (priv->categories_btn, !read_only); + gtk_entry_set_editable (GTK_ENTRY (priv->categories), !read_only); +} + +/* fill_widgets handler for the memo page */ +static gboolean +memo_page_fill_widgets (CompEditorPage *page, ECalComponent *comp) +{ + MemoPage *tpage; + MemoPagePrivate *priv; + ECalComponentClassification cl; + GSList *l; + const char *categories; + ESource *source; + + tpage = MEMO_PAGE (page); + priv = tpage->priv; + + priv->updating = TRUE; + + /* Clean the screen */ + clear_widgets (tpage); + + e_cal_component_get_description_list (comp, &l); + if (l && l->data) { + ECalComponentText *dtext; + + dtext = l->data; + gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->memo_content)), + dtext->value ? dtext->value : "", -1); + } else { + gtk_text_buffer_set_text (gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->memo_content)), + "", 0); + } + e_cal_component_free_text_list (l); + + /* Classification. */ + e_cal_component_get_classification (comp, &cl); + + switch (cl) { + case E_CAL_COMPONENT_CLASS_PUBLIC: + case E_CAL_COMPONENT_CLASS_PRIVATE: + case E_CAL_COMPONENT_CLASS_CONFIDENTIAL: + break; + default: + /* default to PUBLIC */ + cl = E_CAL_COMPONENT_CLASS_PUBLIC; + break; + } + e_dialog_option_menu_set (priv->classification, cl, classification_map); + + /* Categories */ + e_cal_component_get_categories (comp, &categories); + e_dialog_editable_set (priv->categories, categories); + + /* Source */ + source = e_cal_get_source (page->client); + e_source_option_menu_select (E_SOURCE_OPTION_MENU (priv->source_selector), source); + + priv->updating = FALSE; + + sensitize_widgets (tpage); + + return TRUE; +} + +/* fill_component handler for the memo page */ +static gboolean +memo_page_fill_component (CompEditorPage *page, ECalComponent *comp) +{ + MemoPage *tpage; + MemoPagePrivate *priv; + char *cat, *str; + int i; + GtkTextBuffer *text_buffer; + GtkTextIter text_iter_start, text_iter_end; + + tpage = MEMO_PAGE (page); + priv = tpage->priv; + text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->memo_content)); + + /* Memo Content */ + + gtk_text_buffer_get_start_iter (text_buffer, &text_iter_start); + gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end); + str = gtk_text_buffer_get_text (text_buffer, &text_iter_start, &text_iter_end, FALSE); + + if (!str || strlen (str) == 0){ + e_cal_component_set_description_list (comp, NULL); + e_cal_component_set_summary(comp, NULL); + } + else { + int idxToUse = -1, nstr = strlen(str); + gboolean foundNL = FALSE; + GSList l; + ECalComponentText text, sumText; + char *txt; + + for(i = 0; i<nstr && i<50; i++){ + if(str[i] == '\n'){ + idxToUse = i; + foundNL = TRUE; + break; + } + } + + if(foundNL == FALSE){ + if(nstr > 50){ + sumText.value = txt = g_strndup(str, 50); + } + else{ + sumText.value = txt = g_strdup(str); + } + } + else{ + sumText.value = txt = g_strndup(str, idxToUse); /* cuts off '\n' */ + } + + sumText.altrep = NULL; + + text.value = str; + text.altrep = NULL; + l.data = &text; + l.next = NULL; + + e_cal_component_set_summary(comp, &sumText); + e_cal_component_set_description_list (comp, &l); + + g_free(txt); + } + + if (str) + g_free (str); + + /* Classification. */ + e_cal_component_set_classification (comp, classification_get (priv->classification)); + + /* Categories */ + cat = e_dialog_editable_get (priv->categories); + str = comp_editor_strip_categories (cat); + if (cat) + g_free (cat); + + e_cal_component_set_categories (comp, str); + + if (str) + g_free (str); + + return TRUE; +} + + + + +/* Gets the widgets from the XML file and returns if they are all available. */ +static gboolean +get_widgets (MemoPage *tpage) +{ + CompEditorPage *page = COMP_EDITOR_PAGE (tpage); + MemoPagePrivate *priv; + GSList *accel_groups; + GtkWidget *toplevel; + + priv = tpage->priv; + +#define GW(name) glade_xml_get_widget (priv->xml, name) + + priv->main = GW ("memo-page"); + if (!priv->main){ + g_warning("couldn't find memo-page!"); + return FALSE; + } + + /* Get the GtkAccelGroup from the toplevel window, so we can install + it when the notebook page is mapped. */ + toplevel = gtk_widget_get_toplevel (priv->main); + accel_groups = gtk_accel_groups_from_object (G_OBJECT (toplevel)); + if (accel_groups) { + page->accel_group = accel_groups->data; + gtk_accel_group_ref (page->accel_group); + } + + gtk_widget_ref (priv->main); + gtk_container_remove (GTK_CONTAINER (priv->main->parent), priv->main); + + priv->memo_content = GW ("memo_content"); + + priv->classification = GW ("classification"); + + priv->categories_btn = GW ("categories-button"); + priv->categories = GW ("categories"); + + priv->source_selector = GW ("source"); + +#undef GW + + return (priv->classification + && priv->memo_content + && priv->categories_btn + && priv->categories); +} + +/* Callback used when the categories button is clicked; we must bring up the + * category list dialog. + */ +static void +categories_clicked_cb (GtkWidget *button, gpointer data) +{ + MemoPage *tpage; + MemoPagePrivate *priv; + GtkWidget *entry; + + tpage = MEMO_PAGE (data); + priv = tpage->priv; + + entry = priv->categories; + e_categories_config_open_dialog_for_entry (GTK_ENTRY (entry)); +} + +/* This is called when any field is changed; it notifies upstream. */ +static void +field_changed_cb (GtkWidget *widget, gpointer data) +{ + MemoPage *tpage; + MemoPagePrivate *priv; + + tpage = MEMO_PAGE (data); + priv = tpage->priv; + + if (!priv->updating) + comp_editor_page_notify_changed (COMP_EDITOR_PAGE (tpage)); +} + +static void +source_changed_cb (GtkWidget *widget, ESource *source, gpointer data) +{ + MemoPage *tpage; + MemoPagePrivate *priv; + + tpage = MEMO_PAGE (data); + priv = tpage->priv; + + if (!priv->updating) { + ECal *client; + + client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_JOURNAL); + if (!client || !e_cal_open (client, FALSE, NULL)) { + GtkWidget *dialog; + + if (client) + g_object_unref (client); + + e_source_option_menu_select (E_SOURCE_OPTION_MENU (priv->source_selector), + e_cal_get_source (COMP_EDITOR_PAGE (tpage)->client)); + + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, + _("Unable to open memos in '%s'."), + e_source_peek_name (source)); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + } else { + comp_editor_notify_client_changed ( + COMP_EDITOR (gtk_widget_get_toplevel (priv->main)), + client); + sensitize_widgets (tpage); + } + } +} + +/* Hooks the widget signals */ +static gboolean +init_widgets (MemoPage *tpage) +{ + MemoPagePrivate *priv; + GtkTextBuffer *text_buffer; + + priv = tpage->priv; + + /* Memo Content */ + text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->memo_content)); + + gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (priv->memo_content), GTK_WRAP_WORD); + + /* Categories button */ + g_signal_connect((priv->categories_btn), "clicked", + G_CALLBACK (categories_clicked_cb), tpage); + + /* Source selector */ + g_signal_connect((priv->source_selector), "source_selected", + G_CALLBACK (source_changed_cb), tpage); + + /* Connect the default signal handler to use to make sure the "changed" + field gets set whenever a field is changed. */ + + /* Belongs to priv->memo_content */ + g_signal_connect ((text_buffer), "changed", + G_CALLBACK (field_changed_cb), tpage); + + g_signal_connect((priv->classification), "changed", + G_CALLBACK (field_changed_cb), tpage); + g_signal_connect((priv->categories), "changed", + G_CALLBACK (field_changed_cb), tpage); + + return TRUE; +} + + +/** + * memo_page_construct: + * @tpage: An memo page. + * + * Constructs an memo page by loading its Glade data. + * + * Return value: The same object as @tpage, or NULL if the widgets could not be + * created. + **/ +MemoPage * +memo_page_construct (MemoPage *tpage) +{ + MemoPagePrivate *priv; + + priv = tpage->priv; + + priv->xml = glade_xml_new (EVOLUTION_GLADEDIR "/memo-page.glade", + NULL, NULL); + if (!priv->xml) { + g_message ("memo_page_construct(): " + "Could not load the Glade XML file!"); + return NULL; + } + + if (!get_widgets (tpage)) { + g_message ("memo_page_construct(): " + "Could not find all widgets in the XML file!"); + return NULL; + } + + if (!init_widgets (tpage)) { + g_message ("memo_page_construct(): " + "Could not initialize the widgets!"); + return NULL; + } + + return tpage; +} + +/** + * memo_page_new: + * + * Creates a new memo page. + * + * Return value: A newly-created task page, or NULL if the page could + * not be created. + **/ +MemoPage * +memo_page_new (void) +{ + MemoPage *tpage; + + tpage = gtk_type_new (TYPE_MEMO_PAGE); + if (!memo_page_construct (tpage)) { + g_object_unref (tpage); + return NULL; + } + + return tpage; +} + +GtkWidget *memo_page_create_source_option_menu (void); + +GtkWidget * +memo_page_create_source_option_menu (void) +{ + GtkWidget *menu; + GConfClient *gconf_client; + ESourceList *source_list; + + gconf_client = gconf_client_get_default (); + source_list = e_source_list_new_for_gconf (gconf_client, "/apps/evolution/memos/sources"); + + menu = e_source_option_menu_new (source_list); + g_object_unref (source_list); + + gtk_widget_show (menu); + return menu; +} diff --git a/calendar/gui/dialogs/memo-page.glade b/calendar/gui/dialogs/memo-page.glade new file mode 100644 index 0000000000..2553e1a4f2 --- /dev/null +++ b/calendar/gui/dialogs/memo-page.glade @@ -0,0 +1,323 @@ +<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*--> +<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd"> + +<glade-interface> + +<widget class="GtkWindow" id="memo-toplevel"> + <property name="title" translatable="yes">window1</property> + <property name="type">GTK_WINDOW_TOPLEVEL</property> + <property name="window_position">GTK_WIN_POS_NONE</property> + <property name="modal">False</property> + <property name="resizable">True</property> + <property name="destroy_with_parent">False</property> + <property name="decorated">True</property> + <property name="skip_taskbar_hint">False</property> + <property name="skip_pager_hint">False</property> + <property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property> + <property name="gravity">GDK_GRAVITY_NORTH_WEST</property> + + <child> + <widget class="GtkVBox" id="memo-page"> + <property name="border_width">12</property> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">6</property> + + <child> + <widget class="GtkLabel" id="label21"> + <property name="visible">True</property> + <property name="label" translatable="yes"><span weight="bold">Basics</span></property> + <property name="use_underline">False</property> + <property name="use_markup">True</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox7"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkLabel" id="label22"> + <property name="visible">True</property> + <property name="label" translatable="yes"></property> + <property name="use_underline">False</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">12</property> + <property name="ypad">0</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="GtkTable" id="table3"> + <property name="visible">True</property> + <property name="n_rows">4</property> + <property name="n_columns">2</property> + <property name="homogeneous">False</property> + <property name="row_spacing">6</property> + <property name="column_spacing">12</property> + + <child> + <widget class="GtkLabel" id="label100"> + <property name="visible">True</property> + <property name="label" translatable="yes">Classi_fication:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">classification</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkHBox" id="hbox10"> + <property name="visible">True</property> + <property name="homogeneous">False</property> + <property name="spacing">0</property> + + <child> + <widget class="GtkOptionMenu" id="classification"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="history">0</property> + + <child> + <widget class="GtkMenu" id="menu1"> + + <child> + <widget class="GtkMenuItem" id="public1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Public</property> + <property name="use_underline">True</property> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="private1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Private</property> + <property name="use_underline">True</property> + </widget> + </child> + + <child> + <widget class="GtkMenuItem" id="confidential1"> + <property name="visible">True</property> + <property name="label" translatable="yes">Confidential</property> + <property name="use_underline">True</property> + </widget> + </child> + </widget> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label23"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Group:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_LEFT</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0.5</property> + <property name="yalign">0.5</property> + <property name="xpad">12</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">source</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">False</property> + <property name="fill">False</property> + </packing> + </child> + + <child> + <widget class="Custom" id="source"> + <property name="visible">True</property> + <property name="creation_function">memo_page_create_source_option_menu</property> + <property name="int1">0</property> + <property name="int2">0</property> + <property name="last_modification_time">Thu, 13 Jan 2004 22:00:00 GMT</property> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">1</property> + <property name="bottom_attach">2</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + + <child> + <widget class="GtkButton" id="categories-button"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="label" translatable="yes">Ca_tegories...</property> + <property name="use_underline">True</property> + <property name="relief">GTK_RELIEF_NORMAL</property> + <property name="focus_on_click">True</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="x_options">fill</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkEntry" id="categories"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="visibility">True</property> + <property name="max_length">0</property> + <property name="text" translatable="yes"></property> + <property name="has_frame">True</property> + <property name="invisible_char" translatable="yes">*</property> + <property name="activates_default">False</property> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">2</property> + <property name="bottom_attach">3</property> + <property name="y_options"></property> + </packing> + </child> + + <child> + <widget class="GtkScrolledWindow" id="scrolledwindow1"> + <property name="visible">True</property> + <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property> + <property name="shadow_type">GTK_SHADOW_IN</property> + <property name="window_placement">GTK_CORNER_TOP_LEFT</property> + + <child> + <widget class="GtkTextView" id="memo_content"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="editable">True</property> + <property name="overwrite">False</property> + <property name="accepts_tab">True</property> + <property name="justification">GTK_JUSTIFY_LEFT</property> + <property name="wrap_mode">GTK_WRAP_WORD</property> + <property name="cursor_visible">True</property> + <property name="pixels_above_lines">0</property> + <property name="pixels_below_lines">0</property> + <property name="pixels_inside_wrap">0</property> + <property name="left_margin">0</property> + <property name="right_margin">0</property> + <property name="indent">0</property> + <property name="text" translatable="yes"></property> + </widget> + </child> + </widget> + <packing> + <property name="left_attach">1</property> + <property name="right_attach">2</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">expand|shrink|fill</property> + <property name="y_options">expand|shrink|fill</property> + </packing> + </child> + + <child> + <widget class="GtkLabel" id="label18"> + <property name="visible">True</property> + <property name="label" translatable="yes">_Memo Content:</property> + <property name="use_underline">True</property> + <property name="use_markup">False</property> + <property name="justify">GTK_JUSTIFY_CENTER</property> + <property name="wrap">False</property> + <property name="selectable">False</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="xpad">0</property> + <property name="ypad">0</property> + <property name="mnemonic_widget">memo content</property> + </widget> + <packing> + <property name="left_attach">0</property> + <property name="right_attach">1</property> + <property name="top_attach">3</property> + <property name="bottom_attach">4</property> + <property name="x_options">fill</property> + <property name="y_options">fill</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + <packing> + <property name="padding">0</property> + <property name="expand">True</property> + <property name="fill">True</property> + </packing> + </child> + </widget> + </child> +</widget> + +</glade-interface> diff --git a/calendar/gui/dialogs/memo-page.h b/calendar/gui/dialogs/memo-page.h new file mode 100644 index 0000000000..8df3b5ff65 --- /dev/null +++ b/calendar/gui/dialogs/memo-page.h @@ -0,0 +1,56 @@ +/* Evolution calendar - Main page of the memo editor dialog + * + * Copyright (C) 2001 Ximian, Inc. + * + * Authors: Federico Mena-Quintero <federico@ximian.com> + * Miguel de Icaza <miguel@ximian.com> + * Seth Alves <alves@hungry.com> + * JP Rosevear <jpr@ximian.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef MEMO_PAGE_H +#define MEMO_PAGE_H + +#include "comp-editor-page.h" + +G_BEGIN_DECLS + +#define TYPE_MEMO_PAGE (memo_page_get_type ()) +#define MEMO_PAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_MEMO_PAGE, MemoPage)) +#define MEMO_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_MEMO_PAGE, MemoPageClass)) +#define IS_MEMO_PAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_MEMO_PAGE)) +#define IS_MEMO_PAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), TYPE_MEMO_PAGE)) + +typedef struct _MemoPagePrivate MemoPagePrivate; + +typedef struct { + CompEditorPage page; + + /* Private data */ + MemoPagePrivate *priv; +} MemoPage; + +typedef struct { + CompEditorPageClass parent_class; +} MemoPageClass; + +GtkType memo_page_get_type (void); +MemoPage *memo_page_construct (MemoPage *epage); +MemoPage *memo_page_new (void); + +G_END_DECLS + +#endif diff --git a/calendar/gui/dialogs/send-comp.c b/calendar/gui/dialogs/send-comp.c index f685bd1014..9e88f302eb 100644 --- a/calendar/gui/dialogs/send-comp.c +++ b/calendar/gui/dialogs/send-comp.c @@ -90,7 +90,8 @@ send_component_prompt_subject (GtkWindow *parent, ECal *client, ECalComponent *c case E_CAL_COMPONENT_TODO: id = "calendar:prompt-send-no-subject-task"; break; - + case E_CAL_COMPONENT_JOURNAL: + return TRUE; /* we don't do summaries directly */ default: g_message ("send_component_prompt_subject(): " "Cannot handle object of type %d", vtype); diff --git a/calendar/gui/e-cal-component-memo-preview.c b/calendar/gui/e-cal-component-memo-preview.c new file mode 100644 index 0000000000..4210f40a4c --- /dev/null +++ b/calendar/gui/e-cal-component-memo-preview.c @@ -0,0 +1,374 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-tasks.c + * + * Copyright (C) 2001-2003 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + * + * Authors: Federico Mena Quintero <federico@ximian.com> + * Damon Chaplin <damon@ximian.com> + * Rodrigo Moya <rodrigo@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gnome.h> +#include <gtk/gtk.h> +#include <libgnomevfs/gnome-vfs-ops.h> +#include <libecal/e-cal-time-util.h> +#include <libedataserver/e-categories.h> +#include <gtkhtml/gtkhtml.h> +#include <gtkhtml/gtkhtml-stream.h> +#include <e-util/e-time-utils.h> +#include <e-util/e-categories-config.h> +#include "calendar-config.h" +#include "e-cal-component-memo-preview.h" + +struct _ECalComponentMemoPreviewPrivate { + GtkWidget *html; + + icaltimezone *zone; +}; + +G_DEFINE_TYPE (ECalComponentMemoPreview, e_cal_component_memo_preview, GTK_TYPE_TABLE); + + +static void +on_link_clicked (GtkHTML *html, const char *url, gpointer data) +{ + GError *err = NULL; + + gnome_url_show (url, &err); + + if (err) { + g_warning ("gnome_url_show: %s", err->message); + g_error_free (err); + } +} + +static void +on_url_cb (GtkHTML *html, const char *url, gpointer data) +{ +#if 0 + char *msg; + ECalComponentMemoPreview *preview = data; + + if (url && *url) { + msg = g_strdup_printf (_("Click to open %s"), url); + e_calendar_table_set_status_message (e_tasks_get_calendar_table (tasks), msg); + g_free (msg); + } else + e_calendar_table_set_status_message (e_tasks_get_calendar_table (tasks), NULL); +#endif +} + +/* Callback used when the user selects a URL in the HTML widget */ +static void +url_requested_cb (GtkHTML *html, const char *url, GtkHTMLStream *stream, gpointer data) +{ + if (!strncmp ("file:///", url, strlen ("file:///"))) { + GnomeVFSHandle *handle; + GnomeVFSResult result; + char buffer[4096]; + + if (gnome_vfs_open (&handle, url, GNOME_VFS_OPEN_READ) == GNOME_VFS_OK) { + do { + GnomeVFSFileSize bread; + + result = gnome_vfs_read (handle, buffer, sizeof (buffer), &bread); + if (result == GNOME_VFS_OK) + gtk_html_stream_write (stream, buffer, bread); + } while (result == GNOME_VFS_OK); + + gnome_vfs_close (handle); + } + } +} + +/* Converts a time_t to a string, relative to the specified timezone */ +static char * +timet_to_str_with_zone (ECalComponentDateTime *dt, ECal *ecal, icaltimezone *default_zone) +{ + struct icaltimetype itt; + icaltimezone *zone; + struct tm tm; + char buf[256]; + + if (dt->tzid) { + /* If we can't find the zone, we'll guess its "local" */ + if (!e_cal_get_timezone (ecal, dt->tzid, &zone, NULL)) + zone = NULL; + } else if (dt->value->is_utc) { + zone = icaltimezone_get_utc_timezone (); + } else { + zone = NULL; + } + + + itt = *dt->value; + if (zone) + icaltimezone_convert_time (&itt, zone, default_zone); + tm = icaltimetype_to_tm (&itt); + + e_time_format_date_and_time (&tm, calendar_config_get_24_hour_format (), + FALSE, FALSE, buf, sizeof (buf)); + + return g_locale_to_utf8 (buf, -1, NULL, NULL, NULL); +} + +static void +write_html (GtkHTMLStream *stream, ECal *ecal, ECalComponent *comp, icaltimezone *default_zone) +{ + ECalComponentText text; + ECalComponentDateTime dt; + gchar *str; + GSList *l; + icalproperty_status status; + const char *location; + int *priority_value; + gboolean one_added = FALSE; + + g_return_if_fail (E_IS_CAL_COMPONENT (comp)); + + gtk_html_stream_printf (stream, + "<HTML><BODY>"); + + /* write icons for the categories */ + e_cal_component_get_categories_list (comp, &l); + if (l) { + GSList *node; + GString *str = g_string_new (""); + + + gtk_html_stream_printf(stream, "<H3>Categories: "); + + for (node = l; node != NULL; node = node->next) { + const char *icon_file; + + icon_file = e_categories_get_icon_file_for ((const char *) node->data); + if (icon_file && g_file_test(icon_file, G_FILE_TEST_EXISTS)) { + gtk_html_stream_printf (stream, "<IMG ALT=\"%s\" SRC=\"file://%s\">", + (const char *) node->data, icon_file); + one_added = TRUE; + } + else{ + if(one_added == FALSE){ + g_string_append_printf (str, "%s", (const char *) node->data); + one_added = TRUE; + } + else{ + g_string_append_printf (str, ", %s", (const char *) node->data); + } + } + } + + gtk_html_stream_printf(stream, str->str); + + gtk_html_stream_printf(stream, "</H3>"); + + e_cal_component_free_categories_list (l); + } + + /* Start table */ + gtk_html_stream_printf (stream, "<TABLE BORDER=\"0\" WIDTH=\"80%%\">" + "<TR><TD VALIGN=\"TOP\" ALIGN=\"RIGHT\" WIDTH=\"15%%\"></TD></TR>"); + + + /* write description and URL */ + gtk_html_stream_printf (stream, "<TR><TD COLSPAN=\"2\"><HR></TD></TR>"); + + e_cal_component_get_description_list (comp, &l); + if (l) { + GSList *node; + + gtk_html_stream_printf (stream, "<TR><TD VALIGN=\"TOP\" ALIGN=\"RIGHT\"><B>%s</B></TD>", _("Memo:")); + + gtk_html_stream_printf (stream, "<TD>"); + + for (node = l; node != NULL; node = node->next) { + gint i, j; + GString *str = g_string_new (""); + + text = * (ECalComponentText *) node->data; + for (i = 0, j=0; i < strlen (text.value ? text.value : 0); i++, j++) { + if (text.value[i] == '\n'){ + str = g_string_append (str, "<BR>"); + } + else if (text.value[i] == '<') + str = g_string_append (str, "<"); + else if (text.value[i] == '>') + str = g_string_append (str, ">"); + else + str = g_string_append_c (str, text.value[i]); + } + + gtk_html_stream_printf (stream, str->str); + g_string_free (str, TRUE); + } + + gtk_html_stream_printf (stream, "</TD></TR>"); + + e_cal_component_free_text_list (l); + } + + /* URL */ + e_cal_component_get_url (comp, (const char **) &str); + if (str) { + gtk_html_stream_printf (stream, "<TR><TD VALIGN=\"TOP\" ALIGN=\"RIGHT\"><B>%s</B></TD>", _("Web Page:")); + gtk_html_stream_printf (stream, "<TD><A HREF=\"%s\">%s</A></TD></TR>", str, str); + } + + gtk_html_stream_printf (stream, "</TABLE>"); + + /* close document */ + gtk_html_stream_printf (stream, "</BODY></HTML>"); +} + +static void +e_cal_component_memo_preview_init (ECalComponentMemoPreview *preview) +{ + ECalComponentMemoPreviewPrivate *priv; + GtkWidget *scroll; + + priv = g_new0 (ECalComponentMemoPreviewPrivate, 1); + preview->priv = priv; + + priv->html = gtk_html_new (); + gtk_html_set_default_content_type (GTK_HTML (priv->html), "charset=utf-8"); + gtk_html_load_empty (GTK_HTML (priv->html)); + + g_signal_connect (G_OBJECT (priv->html), "url_requested", + G_CALLBACK (url_requested_cb), NULL); + g_signal_connect (G_OBJECT (priv->html), "link_clicked", + G_CALLBACK (on_link_clicked), preview); + g_signal_connect (G_OBJECT (priv->html), "on_url", + G_CALLBACK (on_url_cb), preview); + + scroll = gtk_scrolled_window_new (NULL, NULL); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroll), + GTK_POLICY_AUTOMATIC, + GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroll), GTK_SHADOW_IN); + + gtk_container_add (GTK_CONTAINER (scroll), priv->html); + gtk_container_add (GTK_CONTAINER (preview), scroll); + gtk_widget_show_all (scroll); + + priv->zone = icaltimezone_get_utc_timezone (); +} + +static void +e_cal_component_memo_preview_destroy (GtkObject *object) +{ + ECalComponentMemoPreview *preview; + ECalComponentMemoPreviewPrivate *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (E_IS_CAL_COMPONENT_MEMO_PREVIEW (object)); + + preview = E_CAL_COMPONENT_MEMO_PREVIEW (object); + priv = preview->priv; + + if (priv) { + + g_free (priv); + preview->priv = NULL; + } + + if (GTK_OBJECT_CLASS (e_cal_component_memo_preview_parent_class)->destroy) + (* GTK_OBJECT_CLASS (e_cal_component_memo_preview_parent_class)->destroy) (object); +} + +static void +e_cal_component_memo_preview_class_init (ECalComponentMemoPreviewClass *klass) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass *) klass; + + object_class->destroy = e_cal_component_memo_preview_destroy; +} + +GtkWidget * +e_cal_component_memo_preview_new (void) +{ + ECalComponentMemoPreview *preview; + + preview = g_object_new (e_cal_component_memo_preview_get_type (), NULL); + + return GTK_WIDGET (preview); +} + +icaltimezone * +e_cal_component_memo_preview_get_default_timezone (ECalComponentMemoPreview *preview) +{ + ECalComponentMemoPreviewPrivate *priv; + + g_return_val_if_fail (preview != NULL, NULL); + g_return_val_if_fail (E_IS_CAL_COMPONENT_MEMO_PREVIEW (preview), NULL); + + priv = preview->priv; + + return priv->zone; +} + +void +e_cal_component_memo_preview_set_default_timezone (ECalComponentMemoPreview *preview, icaltimezone *zone) +{ + ECalComponentMemoPreviewPrivate *priv; + + g_return_if_fail (preview != NULL); + g_return_if_fail (E_IS_CAL_COMPONENT_MEMO_PREVIEW (preview)); + g_return_if_fail (zone != NULL); + + priv = preview->priv; + + priv->zone = zone; +} + +void +e_cal_component_memo_preview_display (ECalComponentMemoPreview *preview, ECal *ecal, ECalComponent *comp) +{ + ECalComponentMemoPreviewPrivate *priv; + GtkHTMLStream *stream; + + g_return_if_fail (preview != NULL); + g_return_if_fail (E_IS_CAL_COMPONENT_MEMO_PREVIEW (preview)); + g_return_if_fail (comp != NULL); + g_return_if_fail (E_IS_CAL_COMPONENT (comp)); + + priv = preview->priv; + + stream = gtk_html_begin (GTK_HTML (priv->html)); + write_html (stream, ecal, comp, priv->zone); + gtk_html_stream_close (stream, GTK_HTML_STREAM_OK); +} + +void +e_cal_component_memo_preview_clear (ECalComponentMemoPreview *preview) +{ + ECalComponentMemoPreviewPrivate *priv; + + g_return_if_fail (preview != NULL); + g_return_if_fail (E_IS_CAL_COMPONENT_MEMO_PREVIEW (preview)); + + priv = preview->priv; + + gtk_html_load_empty (GTK_HTML (priv->html)); +} + diff --git a/calendar/gui/e-cal-component-memo-preview.h b/calendar/gui/e-cal-component-memo-preview.h new file mode 100644 index 0000000000..fb1716de63 --- /dev/null +++ b/calendar/gui/e-cal-component-memo-preview.h @@ -0,0 +1,66 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-cal-component-memo-preview.h + * + * Copyright (C) 2004 Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + * + * Authors: Federico Mena Quintero <federico@ximian.com> + * Damon Chaplin <damon@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + */ + +#ifndef _E_CAL_COMPONENT_MEMO_PREVIEW_H_ +#define _E_CAL_COMPONENT_MEMO_PREVIEW_H_ + +#include <gtk/gtktable.h> +#include <libecal/e-cal.h> + +#define E_TYPE_CAL_COMPONENT_MEMO_PREVIEW (e_cal_component_memo_preview_get_type ()) +#define E_CAL_COMPONENT_MEMO_PREVIEW(obj) (GTK_CHECK_CAST ((obj), E_TYPE_CAL_COMPONENT_MEMO_PREVIEW, ECalComponentMemoPreview)) +#define E_CAL_COMPONENT_MEMO_PREVIEW_CLASS(klass) (GTK_CHECK_CAST_CLASS ((klass), E_TYPE_CAL_COMPONENT_MEMO_PREVIEW, \ + ECalComponentMemoPreviewClass)) +#define E_IS_CAL_COMPONENT_MEMO_PREVIEW(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_CAL_COMPONENT_MEMO_PREVIEW)) +#define E_IS_CAL_COMPONENT_MEMO_PREVIEW_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), E_TYPE_CAL_COMPONENT_MEMO_PREVIEW)) + +typedef struct _ECalComponentMemoPreview ECalComponentMemoPreview; +typedef struct _ECalComponentMemoPreviewClass ECalComponentMemoPreviewClass; +typedef struct _ECalComponentMemoPreviewPrivate ECalComponentMemoPreviewPrivate; + +struct _ECalComponentMemoPreview { + GtkTable table; + + /* Private data */ + ECalComponentMemoPreviewPrivate *priv; +}; + +struct _ECalComponentMemoPreviewClass { + GtkTableClass parent_class; + + /* Notification signals */ + void (* selection_changed) (ECalComponentMemoPreview *preview, int n_selected); +}; + + +GtkType e_cal_component_memo_preview_get_type (void); +GtkWidget *e_cal_component_memo_preview_new (void); + +icaltimezone *e_cal_component_memo_preview_get_default_timezone (ECalComponentMemoPreview *preview); +void e_cal_component_memo_preview_set_default_timezone (ECalComponentMemoPreview *preview, icaltimezone *zone); + +void e_cal_component_memo_preview_display (ECalComponentMemoPreview *preview, ECal *ecal, ECalComponent *comp); +void e_cal_component_memo_preview_clear (ECalComponentMemoPreview *preview); + +#endif /* _E_CAL_COMPONENT_MEMO_PREVIEW_H_ */ diff --git a/calendar/gui/e-cal-model-memos.c b/calendar/gui/e-cal-model-memos.c new file mode 100644 index 0000000000..d4cf6fc2b3 --- /dev/null +++ b/calendar/gui/e-cal-model-memos.c @@ -0,0 +1,259 @@ +/* Evolution memos - Data model for ETable + * + * Copyright (C) 2000 Ximian, Inc. + * Copyright (C) 2000 Ximian, Inc. + * + * Authors: Rodrigo Moya <rodrigo@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <libgnome/gnome-i18n.h> +#include "e-cal-model-memos.h" +#include "e-cell-date-edit-text.h" +#include "misc.h" + +#define d(x) (x) + +struct _ECalModelMemosPrivate { +}; + +static void e_cal_model_memos_finalize (GObject *object); + +static int ecmm_column_count (ETableModel *etm); +static void *ecmm_value_at (ETableModel *etm, int col, int row); +static void ecmm_set_value_at (ETableModel *etm, int col, int row, const void *value); +static gboolean ecmm_is_cell_editable (ETableModel *etm, int col, int row); +static void *ecmm_duplicate_value (ETableModel *etm, int col, const void *value); +static void ecmm_free_value (ETableModel *etm, int col, void *value); +static void *ecmm_initialize_value (ETableModel *etm, int col); +static gboolean ecmm_value_is_empty (ETableModel *etm, int col, const void *value); +static char *ecmm_value_to_string (ETableModel *etm, int col, const void *value); + +static void ecmm_fill_component_from_model (ECalModel *model, ECalModelComponent *comp_data, + ETableModel *source_model, gint row); + +G_DEFINE_TYPE (ECalModelMemos, e_cal_model_memos, E_TYPE_CAL_MODEL); + +static void +e_cal_model_memos_class_init (ECalModelMemosClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + ETableModelClass *etm_class = E_TABLE_MODEL_CLASS (klass); + ECalModelClass *model_class = E_CAL_MODEL_CLASS (klass); + + object_class->finalize = e_cal_model_memos_finalize; + + etm_class->column_count = ecmm_column_count; + etm_class->value_at = ecmm_value_at; + etm_class->set_value_at = ecmm_set_value_at; + etm_class->is_cell_editable = ecmm_is_cell_editable; + etm_class->duplicate_value = ecmm_duplicate_value; + etm_class->free_value = ecmm_free_value; + etm_class->initialize_value = ecmm_initialize_value; + etm_class->value_is_empty = ecmm_value_is_empty; + etm_class->value_to_string = ecmm_value_to_string; + + model_class->fill_component_from_model = ecmm_fill_component_from_model; +} + +static void +e_cal_model_memos_init (ECalModelMemos *model) +{ + ECalModelMemosPrivate *priv; + + priv = g_new0 (ECalModelMemosPrivate, 1); + model->priv = priv; + + e_cal_model_set_component_kind (E_CAL_MODEL (model), ICAL_VJOURNAL_COMPONENT); +} + +static void +e_cal_model_memos_finalize (GObject *object) +{ + ECalModelMemosPrivate *priv; + ECalModelMemos *model = (ECalModelMemos *) object; + + g_return_if_fail (E_IS_CAL_MODEL_MEMOS (model)); + + priv = model->priv; + if (priv) { + g_free (priv); + model->priv = NULL; + } + + if (G_OBJECT_CLASS (e_cal_model_memos_parent_class)->finalize) + G_OBJECT_CLASS (e_cal_model_memos_parent_class)->finalize (object); +} + +/* ETableModel methods */ +static int +ecmm_column_count (ETableModel *etm) +{ + return E_CAL_MODEL_MEMOS_FIELD_LAST; +} + +static void * +ecmm_value_at (ETableModel *etm, int col, int row) +{ + ECalModelComponent *comp_data; + ECalModelMemosPrivate *priv; + ECalModelMemos *model = (ECalModelMemos *) etm; + + g_return_val_if_fail (E_IS_CAL_MODEL_MEMOS (model), NULL); + + priv = model->priv; + + g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_MEMOS_FIELD_LAST, NULL); + g_return_val_if_fail (row >= 0 && row < e_table_model_row_count (etm), NULL); + + if (col < E_CAL_MODEL_FIELD_LAST) + return E_TABLE_MODEL_CLASS (e_cal_model_memos_parent_class)->value_at (etm, col, row); + + comp_data = e_cal_model_get_component_at (E_CAL_MODEL (model), row); + if (!comp_data) + return ""; + + return ""; +} + + +static void +ecmm_set_value_at (ETableModel *etm, int col, int row, const void *value) +{ + ECalModelComponent *comp_data; + ECalModelMemos *model = (ECalModelMemos *) etm; + + g_return_if_fail (E_IS_CAL_MODEL_MEMOS (model)); + g_return_if_fail (col >= 0 && col < E_CAL_MODEL_MEMOS_FIELD_LAST); + g_return_if_fail (row >= 0 && row < e_table_model_row_count (etm)); + + if (col < E_CAL_MODEL_FIELD_LAST) { + E_TABLE_MODEL_CLASS (e_cal_model_memos_parent_class)->set_value_at (etm, col, row, value); + return; + } + + comp_data = e_cal_model_get_component_at (E_CAL_MODEL (model), row); + if (!comp_data){ + g_warning("couldn't get component data: row == %d", row); + return; + } + + /* TODO ask about mod type */ + if (!e_cal_modify_object (comp_data->client, comp_data->icalcomp, CALOBJ_MOD_ALL, NULL)) { + g_warning (G_STRLOC ": Could not modify the object!"); + + /* TODO Show error dialog */ + } +} + +static gboolean +ecmm_is_cell_editable (ETableModel *etm, int col, int row) +{ + ECalModelMemos *model = (ECalModelMemos *) etm; + gboolean retval = FALSE; + + g_return_val_if_fail (E_IS_CAL_MODEL_MEMOS (model), FALSE); + g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_MEMOS_FIELD_LAST, FALSE); + g_return_val_if_fail (row >= -1 || (row >= 0 && row < e_table_model_row_count (etm)), FALSE); + + if(col == E_CAL_MODEL_FIELD_SUMMARY) + retval = FALSE; + else if (col < E_CAL_MODEL_FIELD_LAST) + retval = E_TABLE_MODEL_CLASS (e_cal_model_memos_parent_class)->is_cell_editable (etm, col, row); + + return retval; +} + +static void * +ecmm_duplicate_value (ETableModel *etm, int col, const void *value) +{ + g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_MEMOS_FIELD_LAST, NULL); + + if (col < E_CAL_MODEL_FIELD_LAST) + return E_TABLE_MODEL_CLASS (e_cal_model_memos_parent_class)->duplicate_value (etm, col, value); + + return NULL; +} + +static void +ecmm_free_value (ETableModel *etm, int col, void *value) +{ + g_return_if_fail (col >= 0 && col < E_CAL_MODEL_MEMOS_FIELD_LAST); + + if (col < E_CAL_MODEL_FIELD_LAST) { + E_TABLE_MODEL_CLASS (e_cal_model_memos_parent_class)->free_value (etm, col, value); + return; + } +} + +static void * +ecmm_initialize_value (ETableModel *etm, int col) +{ + g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_MEMOS_FIELD_LAST, NULL); + + if (col < E_CAL_MODEL_FIELD_LAST) + return E_TABLE_MODEL_CLASS (e_cal_model_memos_parent_class)->initialize_value (etm, col); + + return NULL; +} + +static gboolean +ecmm_value_is_empty (ETableModel *etm, int col, const void *value) +{ + g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_MEMOS_FIELD_LAST, TRUE); + + if (col < E_CAL_MODEL_FIELD_LAST) + return E_TABLE_MODEL_CLASS (e_cal_model_memos_parent_class)->value_is_empty (etm, col, value); + + return TRUE; +} + +static char * +ecmm_value_to_string (ETableModel *etm, int col, const void *value) +{ + g_return_val_if_fail (col >= 0 && col < E_CAL_MODEL_MEMOS_FIELD_LAST, g_strdup ("")); + + if (col < E_CAL_MODEL_FIELD_LAST) + return E_TABLE_MODEL_CLASS (e_cal_model_memos_parent_class)->value_to_string (etm, col, value); + + return g_strdup (""); +} + +/* ECalModel class methods */ + +static void +ecmm_fill_component_from_model (ECalModel *model, ECalModelComponent *comp_data, + ETableModel *source_model, gint row) +{ + g_return_if_fail (E_IS_CAL_MODEL_MEMOS (model)); + g_return_if_fail (comp_data != NULL); + g_return_if_fail (E_IS_TABLE_MODEL (source_model)); + +} + +/** + * e_cal_model_memos_new + */ +ECalModelMemos * +e_cal_model_memos_new (void) +{ + return g_object_new (E_TYPE_CAL_MODEL_MEMOS, NULL); +} diff --git a/calendar/gui/e-cal-model-memos.h b/calendar/gui/e-cal-model-memos.h new file mode 100644 index 0000000000..18ae106655 --- /dev/null +++ b/calendar/gui/e-cal-model-memos.h @@ -0,0 +1,59 @@ +/* Evolution memo - Data model for ETable + * + * Copyright (C) 2000 Ximian, Inc. + * Copyright (C) 2000 Ximian, Inc. + * + * Authors: Rodrigo Moya <rodrigo@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + */ + +#ifndef E_CAL_MODEL_MEMOS_H +#define E_CAL_MODEL_MEMOS_H + +#include "e-cal-model.h" + +G_BEGIN_DECLS + +#define E_TYPE_CAL_MODEL_MEMOS (e_cal_model_memos_get_type ()) +#define E_CAL_MODEL_MEMOS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_CAL_MODEL_MEMOS, ECalModelMemo)) +#define E_CAL_MODEL_MEMOS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_CAL_MODEL_MEMOS, ECalModelMemoClass)) +#define E_IS_CAL_MODEL_MEMOS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_CAL_MODEL_MEMOS)) +#define E_IS_CAL_MODEL_MEMOS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_CAL_MODEL_MEMOS)) + +typedef struct _ECalModelMemosPrivate ECalModelMemosPrivate; + +typedef enum { + /* If you add new items here or reorder them, you have to update the + .etspec files for the tables using this model */ + E_CAL_MODEL_MEMOS_FIELD_LAST = E_CAL_MODEL_FIELD_LAST + +} ECalModelMemoField; + +typedef struct { + ECalModel model; + ECalModelMemosPrivate *priv; +} ECalModelMemos; + +typedef struct { + ECalModelClass parent_class; +} ECalModelMemosClass; + +GType e_cal_model_memos_get_type (void); +ECalModelMemos *e_cal_model_memos_new (void); + +G_END_DECLS + +#endif diff --git a/calendar/gui/e-memo-table-config.c b/calendar/gui/e-memo-table-config.c new file mode 100644 index 0000000000..cb3ef06132 --- /dev/null +++ b/calendar/gui/e-memo-table-config.c @@ -0,0 +1,241 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author : + * JP Rosevear <jpr@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + * + * Copyright 2003, Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 "calendar-config.h" +#include "e-memo-table-config.h" + +struct _EMemoTableConfigPrivate { + EMemoTable *table; + + GList *notifications; +}; + +G_DEFINE_TYPE (EMemoTableConfig, e_memo_table_config, G_TYPE_OBJECT); + +/* Property IDs */ +enum props { + PROP_0, + PROP_TABLE +}; + +static void +e_memo_table_config_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) +{ + EMemoTableConfig *table_config; + EMemoTableConfigPrivate *priv; + + table_config = E_MEMO_TABLE_CONFIG (object); + priv = table_config->priv; + + switch (property_id) { + case PROP_TABLE: + e_memo_table_config_set_table (table_config, g_value_get_object (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +e_memo_table_config_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) +{ + EMemoTableConfig *table_config; + EMemoTableConfigPrivate *priv; + + table_config = E_MEMO_TABLE_CONFIG (object); + priv = table_config->priv; + + switch (property_id) { + case PROP_TABLE: + g_value_set_object (value, e_memo_table_config_get_table (table_config)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +e_memo_table_config_dispose (GObject *object) +{ + EMemoTableConfig *table_config = E_MEMO_TABLE_CONFIG (object); + EMemoTableConfigPrivate *priv; + + priv = table_config->priv; + + e_memo_table_config_set_table (table_config, NULL); + + if (G_OBJECT_CLASS (e_memo_table_config_parent_class)->dispose) + G_OBJECT_CLASS (e_memo_table_config_parent_class)->dispose (object); +} + +static void +e_memo_table_config_finalize (GObject *object) +{ + EMemoTableConfig *table_config = E_MEMO_TABLE_CONFIG (object); + EMemoTableConfigPrivate *priv; + + priv = table_config->priv; + + g_free (priv); + + if (G_OBJECT_CLASS (e_memo_table_config_parent_class)->finalize) + G_OBJECT_CLASS (e_memo_table_config_parent_class)->finalize (object); +} + +static void +e_memo_table_config_class_init (EMemoTableConfigClass *klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GParamSpec *spec; + + /* Method override */ + gobject_class->set_property = e_memo_table_config_set_property; + gobject_class->get_property = e_memo_table_config_get_property; + gobject_class->dispose = e_memo_table_config_dispose; + gobject_class->finalize = e_memo_table_config_finalize; + + spec = g_param_spec_object ("table", NULL, NULL, e_memo_table_get_type (), + G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT); + g_object_class_install_property (gobject_class, PROP_TABLE, spec); +} + +static void +e_memo_table_config_init (EMemoTableConfig *table_config) +{ + table_config->priv = g_new0 (EMemoTableConfigPrivate, 1); + +} + +EMemoTableConfig * +e_memo_table_config_new (EMemoTable *table) +{ + EMemoTableConfig *table_config; + + table_config = g_object_new (e_memo_table_config_get_type (), "table", table, NULL); + + return table_config; +} + +EMemoTable * +e_memo_table_config_get_table (EMemoTableConfig *table_config) +{ + EMemoTableConfigPrivate *priv; + + g_return_val_if_fail (table_config != NULL, NULL); + g_return_val_if_fail (E_IS_MEMO_TABLE_CONFIG (table_config), NULL); + + priv = table_config->priv; + + return priv->table; +} + +static void +set_timezone (EMemoTable *table) +{ + ECalModel *model; + icaltimezone *zone; + + zone = calendar_config_get_icaltimezone (); + model = e_memo_table_get_model (table); + if (model) + e_cal_model_set_timezone (model, zone); +} + +static void +timezone_changed_cb (GConfClient *client, guint id, GConfEntry *entry, gpointer data) +{ + EMemoTableConfig *table_config = data; + EMemoTableConfigPrivate *priv; + + priv = table_config->priv; + + set_timezone (priv->table); +} + +static void +set_twentyfour_hour (EMemoTable *table) +{ + ECalModel *model; + gboolean use_24_hour; + + use_24_hour = calendar_config_get_24_hour_format (); + + model = e_memo_table_get_model (table); + if (model) + e_cal_model_set_use_24_hour_format (model, use_24_hour); +} + +static void +twentyfour_hour_changed_cb (GConfClient *client, guint id, GConfEntry *entry, gpointer data) +{ + EMemoTableConfig *table_config = data; + EMemoTableConfigPrivate *priv; + + priv = table_config->priv; + + set_twentyfour_hour (priv->table); +} + +void +e_memo_table_config_set_table (EMemoTableConfig *table_config, EMemoTable *table) +{ + EMemoTableConfigPrivate *priv; + guint not; + GList *l; + + g_return_if_fail (table_config != NULL); + g_return_if_fail (E_IS_MEMO_TABLE_CONFIG (table_config)); + + priv = table_config->priv; + + if (priv->table) { + g_object_unref (priv->table); + priv->table = NULL; + } + + for (l = priv->notifications; l; l = l->next) + calendar_config_remove_notification (GPOINTER_TO_UINT (l->data)); + + g_list_free (priv->notifications); + priv->notifications = NULL; + + /* If the new view is NULL, return right now */ + if (!table) + return; + + priv->table = g_object_ref (table); + + /* Time zone */ + set_timezone (table); + + not = calendar_config_add_notification_timezone (timezone_changed_cb, table_config); + priv->notifications = g_list_prepend (priv->notifications, GUINT_TO_POINTER (not)); + + /* 24 Hour format */ + set_twentyfour_hour (table); + + not = calendar_config_add_notification_24_hour_format (twentyfour_hour_changed_cb, table_config); + priv->notifications = g_list_prepend (priv->notifications, GUINT_TO_POINTER (not)); +} diff --git a/calendar/gui/e-memo-table-config.h b/calendar/gui/e-memo-table-config.h new file mode 100644 index 0000000000..f9a3f36cad --- /dev/null +++ b/calendar/gui/e-memo-table-config.h @@ -0,0 +1,56 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author : + * JP Rosevear <jpr@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + * + * Copyright 2003, Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 + */ + +#ifndef _E_MEMO_TABLE_CONFIG_H_ +#define _E_MEMO_TABLE_CONFIG_H_ + +#include "e-memo-table.h" + +G_BEGIN_DECLS + +#define E_MEMO_TABLE_CONFIG(obj) GTK_CHECK_CAST (obj, e_memo_table_config_get_type (), EMemoTableConfig) +#define E_MEMO_TABLE_CONFIG_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, e_memo_table_config_get_type (), EMemoTableConfigClass) +#define E_IS_MEMO_TABLE_CONFIG(obj) GTK_CHECK_TYPE (obj, e_memo_table_config_get_type ()) + +typedef struct _EMemoTableConfig EMemoTableConfig; +typedef struct _EMemoTableConfigClass EMemoTableConfigClass; +typedef struct _EMemoTableConfigPrivate EMemoTableConfigPrivate; + +struct _EMemoTableConfig { + GObject parent; + + EMemoTableConfigPrivate *priv; +}; + +struct _EMemoTableConfigClass { + GObjectClass parent_class; +}; + +GType e_memo_table_config_get_type (void); +EMemoTableConfig *e_memo_table_config_new (EMemoTable *table); +EMemoTable *e_memo_table_config_get_table (EMemoTableConfig *view_config); +void e_memo_table_config_set_table (EMemoTableConfig *view_config, EMemoTable *table); + +G_END_DECLS + +#endif diff --git a/calendar/gui/e-memo-table.c b/calendar/gui/e-memo-table.c new file mode 100644 index 0000000000..906d61bba9 --- /dev/null +++ b/calendar/gui/e-memo-table.c @@ -0,0 +1,1029 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * Authors : + * Damon Chaplin <damon@ximian.com> + * Rodrigo Moya <rodrigo@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + * + * Copyright 2000, 2001, 2002, 2003 Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 + */ + +/* + * EMemoTable - displays the ECalComponent objects in a table (an ETable). + * Used for memos. + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <sys/stat.h> +#include <unistd.h> +#include <gnome.h> +#include <widgets/misc/e-gui-utils.h> +#include <table/e-cell-checkbox.h> +#include <table/e-cell-toggle.h> +#include <table/e-cell-text.h> +#include <table/e-cell-combo.h> +#include <e-util/e-dialog-utils.h> +#include <widgets/misc/e-cell-date-edit.h> +#include <widgets/misc/e-cell-percent.h> + +#include "calendar-config.h" +#include "dialogs/delete-comp.h" +#include "dialogs/delete-error.h" +#include "dialogs/memo-editor.h" +#include "e-cal-model-memos.h" +#include "e-memo-table.h" +#include "e-cell-date-edit-text.h" +#include "e-comp-editor-registry.h" +#include "print.h" +#include <e-util/e-icon-factory.h> +#include "e-cal-popup.h" + + +extern ECompEditorRegistry *comp_editor_registry; + +static void e_memo_table_class_init (EMemoTableClass *klass); +static void e_memo_table_init (EMemoTable *memo_table); +static void e_memo_table_destroy (GtkObject *object); + +static void e_memo_table_on_double_click (ETable *table, + gint row, + gint col, + GdkEvent *event, + EMemoTable *memo_table); +static gint e_memo_table_show_popup_menu (ETable *table, + GdkEvent *gdk_event, + EMemoTable *memo_table); + +static gint e_memo_table_on_right_click (ETable *table, + gint row, + gint col, + GdkEvent *event, + EMemoTable *memo_table); +static gboolean e_memo_table_on_popup_menu (GtkWidget *widget, + gpointer data); + +static gint e_memo_table_on_key_press (ETable *table, + gint row, + gint col, + GdkEventKey *event, + EMemoTable *memo_table); + +static ECalModelComponent *get_selected_comp (EMemoTable *memo_table); +static void open_memo (EMemoTable *memo_table, ECalModelComponent *comp_data); + +/* Signal IDs */ +enum { + USER_CREATED, + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = { 0 }; + +/* The icons to represent the task. */ +#define E_MEMO_MODEL_NUM_ICONS 2 +static const char* icon_names[E_MEMO_MODEL_NUM_ICONS] = { + "stock_notes", "stock_insert-note" +}; +static GdkPixbuf* icon_pixbufs[E_MEMO_MODEL_NUM_ICONS] = { 0 }; + +static GdkAtom clipboard_atom = GDK_NONE; + +G_DEFINE_TYPE (EMemoTable, e_memo_table, GTK_TYPE_TABLE); + + +static void +e_memo_table_class_init (EMemoTableClass *klass) +{ + GtkObjectClass *object_class; + GtkWidgetClass *widget_class; + + object_class = (GtkObjectClass *) klass; + widget_class = (GtkWidgetClass *) klass; + + /* Method override */ + object_class->destroy = e_memo_table_destroy; + + signals[USER_CREATED] = + g_signal_new ("user_created", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (EMemoTableClass, user_created), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + /* clipboard atom */ + if (!clipboard_atom) + clipboard_atom = gdk_atom_intern ("CLIPBOARD", FALSE); +} + +/* Comparison function for the task-sort column. Sorts by due date and then by + * priority. + * + * FIXME: Does this ever get called?? It doesn't seem to. + * I specified that the table should be sorted by this column, but it still + * never calls this function. + * Also, this assumes it is passed pointers to ECalComponents, but I think it + * may just be passed pointers to the 2 cell values. + */ +/* +static gint +task_compare_cb (gconstpointer a, gconstpointer b) +{ + ECalComponent *ca, *cb; + ECalComponentDateTime due_a, due_b; + int *prio_a, *prio_b; + int retval; + + ca = E_CAL_COMPONENT (a); + cb = E_CAL_COMPONENT (b); + + e_cal_component_get_due (ca, &due_a); + e_cal_component_get_due (cb, &due_b); + e_cal_component_get_priority (ca, &prio_a); + e_cal_component_get_priority (cb, &prio_b); + + if (due_a.value && due_b.value) { + int v; + + /* FIXME: TIMEZONES. But currently we have no way to get the + ECal, so we can't get the timezone. * + v = icaltime_compare (*due_a.value, *due_b.value); + + if (v == 0) + retval = compare_priorities (prio_a, prio_b); + else + retval = v; + } else if (due_a.value) + retval = -1; + else if (due_b.value) + retval = 1; + else + retval = compare_priorities (prio_a, prio_b); + + e_cal_component_free_datetime (&due_a); + e_cal_component_free_datetime (&due_b); + + if (prio_a) + e_cal_component_free_priority (prio_a); + + if (prio_b) + e_cal_component_free_priority (prio_b); + + return retval; +} +*/ + +static void +row_appended_cb (ECalModel *model, EMemoTable *memo_table) +{ + g_signal_emit (memo_table, signals[USER_CREATED], 0); +} + +static void +e_memo_table_init (EMemoTable *memo_table) +{ + GtkWidget *table; + ETable *e_table; + ECell *cell; + ETableExtras *extras; + gint i; + AtkObject *a11y; + + /* Create the model */ + + memo_table->model = (ECalModel *) e_cal_model_memos_new (); + g_signal_connect (memo_table->model, "row_appended", G_CALLBACK (row_appended_cb), memo_table); + + /* Create the header columns */ + + extras = e_table_extras_new(); + + /* + * Normal string fields. + */ + cell = e_cell_text_new (NULL, GTK_JUSTIFY_LEFT); + g_object_set (G_OBJECT (cell), + "bg_color_column", E_CAL_MODEL_FIELD_COLOR, + NULL); + + e_table_extras_add_cell (extras, "calstring", cell); + + + + /* Create pixmaps */ + + if (!icon_pixbufs[0]) + for (i = 0; i < E_MEMO_MODEL_NUM_ICONS; i++) { + icon_pixbufs[i] = e_icon_factory_get_icon (icon_names[i], E_ICON_SIZE_LIST); + } + + cell = e_cell_toggle_new (0, E_MEMO_MODEL_NUM_ICONS, icon_pixbufs); + e_table_extras_add_cell(extras, "icon", cell); + e_table_extras_add_pixbuf(extras, "icon", icon_pixbufs[0]); + + /* Create the table */ + + table = e_table_scrolled_new_from_spec_file (E_TABLE_MODEL (memo_table->model), + extras, + EVOLUTION_ETSPECDIR "/e-memo-table.etspec", + NULL); + /* FIXME: this causes a message from GLib about 'extras' having only a floating + reference */ + /* g_object_unref (extras); */ + + memo_table->etable = table; + gtk_table_attach (GTK_TABLE (memo_table), table, 0, 1, 0, 1, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show (table); + + + e_table = e_table_scrolled_get_table (E_TABLE_SCROLLED (table)); + g_signal_connect (e_table, "double_click", G_CALLBACK (e_memo_table_on_double_click), memo_table); + g_signal_connect (e_table, "right_click", G_CALLBACK (e_memo_table_on_right_click), memo_table); + g_signal_connect (e_table, "key_press", G_CALLBACK (e_memo_table_on_key_press), memo_table); + g_signal_connect (e_table, "popup_menu", G_CALLBACK (e_memo_table_on_popup_menu), memo_table); + + a11y = gtk_widget_get_accessible (GTK_WIDGET(e_table)); + if (a11y) + atk_object_set_name (a11y, _("Memo Table")); +} + + +/** + * e_memo_table_new: + * @Returns: a new #EMemoTable. + * + * Creates a new #EMemoTable. + **/ +GtkWidget * +e_memo_table_new (void) +{ + GtkWidget *memo_table; + + memo_table = GTK_WIDGET (g_object_new (e_memo_table_get_type (), NULL)); + + return memo_table; +} + + +/** + * e_memo_table_get_model: + * @memo_table: A calendar table. + * + * Queries the calendar data model that a calendar table is using. + * + * Return value: A memo model. + **/ +ECalModel * +e_memo_table_get_model (EMemoTable *memo_table) +{ + g_return_val_if_fail (memo_table != NULL, NULL); + g_return_val_if_fail (E_IS_MEMO_TABLE (memo_table), NULL); + + return memo_table->model; +} + + +static void +e_memo_table_destroy (GtkObject *object) +{ + EMemoTable *memo_table; + + memo_table = E_MEMO_TABLE (object); + + if (memo_table->model) { + g_object_unref (memo_table->model); + memo_table->model = NULL; + } + + GTK_OBJECT_CLASS (e_memo_table_parent_class)->destroy (object); +} + +/** + * e_memo_table_get_table: + * @memo_table: A calendar table. + * + * Queries the #ETable widget that the calendar table is using. + * + * Return value: The #ETable widget that the calendar table uses to display its + * data. + **/ +ETable * +e_memo_table_get_table (EMemoTable *memo_table) +{ + g_return_val_if_fail (memo_table != NULL, NULL); + g_return_val_if_fail (E_IS_MEMO_TABLE (memo_table), NULL); + + return e_table_scrolled_get_table (E_TABLE_SCROLLED (memo_table->etable)); +} + +void +e_memo_table_open_selected (EMemoTable *memo_table) +{ + ECalModelComponent *comp_data; + + comp_data = get_selected_comp (memo_table); + if (comp_data != NULL) + open_memo (memo_table, comp_data); +} + +/* Used from e_table_selected_row_foreach(); puts the selected row number in an + * int pointed to by the closure data. + */ +static void +get_selected_row_cb (int model_row, gpointer data) +{ + int *row; + + row = data; + *row = model_row; +} + +/* + * Returns the component that is selected in the table; only works if there is + * one and only one selected row. + */ +static ECalModelComponent * +get_selected_comp (EMemoTable *memo_table) +{ + ETable *etable; + int row; + + etable = e_table_scrolled_get_table (E_TABLE_SCROLLED (memo_table->etable)); + if (e_table_selected_count (etable) != 1) + return NULL; + + row = -1; + e_table_selected_row_foreach (etable, + get_selected_row_cb, + &row); + g_assert (row != -1); + + return e_cal_model_get_component_at (memo_table->model, row); +} + +struct get_selected_uids_closure { + EMemoTable *memo_table; + GSList *objects; +}; + +/* Used from e_table_selected_row_foreach(), builds a list of the selected UIDs */ +static void +add_uid_cb (int model_row, gpointer data) +{ + struct get_selected_uids_closure *closure; + ECalModelComponent *comp_data; + + closure = data; + + comp_data = e_cal_model_get_component_at (closure->memo_table->model, model_row); + + closure->objects = g_slist_prepend (closure->objects, comp_data); +} + +static GSList * +get_selected_objects (EMemoTable *memo_table) +{ + struct get_selected_uids_closure closure; + ETable *etable; + + closure.memo_table = memo_table; + closure.objects = NULL; + + etable = e_table_scrolled_get_table (E_TABLE_SCROLLED (memo_table->etable)); + e_table_selected_row_foreach (etable, add_uid_cb, &closure); + + return closure.objects; +} + +/* Deletes all of the selected components in the table */ +static void +delete_selected_components (EMemoTable *memo_table) +{ + GSList *objs, *l; + + objs = get_selected_objects (memo_table); + + e_memo_table_set_status_message (memo_table, _("Deleting selected objects")); + + for (l = objs; l; l = l->next) { + ECalModelComponent *comp_data = (ECalModelComponent *) l->data; + GError *error = NULL; + + e_cal_remove_object (comp_data->client, + icalcomponent_get_uid (comp_data->icalcomp), &error); + delete_error_dialog (error, E_CAL_COMPONENT_JOURNAL); + g_clear_error (&error); + } + + e_memo_table_set_status_message (memo_table, NULL); + + g_slist_free (objs); +} + +/** + * e_memo_table_get_selected: + * @cal_table: + * + * Get the currently selected ECalModelComponent's on the table. + * + * Return value: A GSList of the components, which should be + * g_slist_free'd when finished with. + **/ +GSList * +e_memo_table_get_selected (EMemoTable *memo_table) +{ + return get_selected_objects(memo_table); +} + +/** + * e_memo_table_delete_selected: + * @memo_table: A memo table. + * + * Deletes the selected components in the table; asks the user first. + **/ +void +e_memo_table_delete_selected (EMemoTable *memo_table) +{ + ETable *etable; + int n_selected; + ECalModelComponent *comp_data; + ECalComponent *comp = NULL; + + g_return_if_fail (memo_table != NULL); + g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); + + etable = e_table_scrolled_get_table (E_TABLE_SCROLLED (memo_table->etable)); + + n_selected = e_table_selected_count (etable); + if (n_selected <= 0) + return; + + if (n_selected == 1) + comp_data = get_selected_comp (memo_table); + else + comp_data = NULL; + + /* FIXME: this may be something other than a TODO component */ + + if (comp_data) { + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp)); + } + + if (delete_component_dialog (comp, FALSE, n_selected, E_CAL_COMPONENT_JOURNAL, + GTK_WIDGET (memo_table))) + delete_selected_components (memo_table); + + /* free memory */ + if (comp) + g_object_unref (comp); +} + +/** + * e_memo_table_cut_clipboard: + * @memo_table: A calendar table. + * + * Cuts selected tasks in the given calendar table + */ +void +e_memo_table_cut_clipboard (EMemoTable *memo_table) +{ + g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); + + e_memo_table_copy_clipboard (memo_table); + delete_selected_components (memo_table); +} + +/* callback for e_table_selected_row_foreach */ +static void +copy_row_cb (int model_row, gpointer data) +{ + EMemoTable *memo_table; + ECalModelComponent *comp_data; + gchar *comp_str; + icalcomponent *child; + + memo_table = E_MEMO_TABLE (data); + + g_return_if_fail (memo_table->tmp_vcal != NULL); + + comp_data = e_cal_model_get_component_at (memo_table->model, model_row); + if (!comp_data) + return; + + /* add timezones to the VCALENDAR component */ + e_cal_util_add_timezones_from_component (memo_table->tmp_vcal, comp_data->icalcomp); + + /* add the new component to the VCALENDAR component */ + comp_str = icalcomponent_as_ical_string (comp_data->icalcomp); + child = icalparser_parse_string (comp_str); + if (child) { + icalcomponent_add_component (memo_table->tmp_vcal, + icalcomponent_new_clone (child)); + icalcomponent_free (child); + } +} + +/** + * e_memo_table_copy_clipboard: + * @memo_table: A calendar table. + * + * Copies selected tasks into the clipboard + */ +void +e_memo_table_copy_clipboard (EMemoTable *memo_table) +{ + ETable *etable; + char *comp_str; + + g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); + + /* create temporary VCALENDAR object */ + memo_table->tmp_vcal = e_cal_util_new_top_level (); + + etable = e_table_scrolled_get_table (E_TABLE_SCROLLED (memo_table->etable)); + e_table_selected_row_foreach (etable, copy_row_cb, memo_table); + comp_str = icalcomponent_as_ical_string (memo_table->tmp_vcal); + gtk_clipboard_set_text (gtk_widget_get_clipboard (GTK_WIDGET (memo_table), clipboard_atom), + (const char *) comp_str, + g_utf8_strlen (comp_str, -1)); + + /* free memory */ + icalcomponent_free (memo_table->tmp_vcal); + memo_table->tmp_vcal = NULL; +} + +static void +clipboard_get_text_cb (GtkClipboard *clipboard, const gchar *text, EMemoTable *memo_table) +{ + icalcomponent *icalcomp; + char *uid; + ECalComponent *comp; + ECal *client; + icalcomponent_kind kind; + + g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); + + if (!text || !*text) + return; + + icalcomp = icalparser_parse_string (text); + if (!icalcomp) + return; + + /* check the type of the component */ + kind = icalcomponent_isa (icalcomp); + if (kind != ICAL_VCALENDAR_COMPONENT && + kind != ICAL_VJOURNAL_COMPONENT) { + return; + } + + client = e_cal_model_get_default_client (memo_table->model); + + e_memo_table_set_status_message (memo_table, _("Updating objects")); + + if (kind == ICAL_VCALENDAR_COMPONENT) { + icalcomponent_kind child_kind; + icalcomponent *subcomp; + icalcomponent *vcal_comp; + + vcal_comp = icalcomp; + subcomp = icalcomponent_get_first_component ( + vcal_comp, ICAL_ANY_COMPONENT); + while (subcomp) { + child_kind = icalcomponent_isa (subcomp); + if (child_kind == ICAL_VJOURNAL_COMPONENT) { + ECalComponent *tmp_comp; + + uid = e_cal_component_gen_uid (); + tmp_comp = e_cal_component_new (); + e_cal_component_set_icalcomponent ( + tmp_comp, icalcomponent_new_clone (subcomp)); + e_cal_component_set_uid (tmp_comp, uid); + free (uid); + + /* FIXME should we convert start/due/complete times? */ + /* FIXME Error handling */ + e_cal_create_object (client, e_cal_component_get_icalcomponent (tmp_comp), NULL, NULL); + + g_object_unref (tmp_comp); + } + subcomp = icalcomponent_get_next_component ( + vcal_comp, ICAL_ANY_COMPONENT); + } + } + else { + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent (comp, icalcomp); + uid = e_cal_component_gen_uid (); + e_cal_component_set_uid (comp, (const char *) uid); + free (uid); + + e_cal_create_object (client, e_cal_component_get_icalcomponent (comp), NULL, NULL); + + g_object_unref (comp); + } + + e_memo_table_set_status_message (memo_table, NULL); +} + +/** + * e_memo_table_paste_clipboard: + * @memo_table: A calendar table. + * + * Pastes tasks currently in the clipboard into the given calendar table + */ +void +e_memo_table_paste_clipboard (EMemoTable *memo_table) +{ + g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); + + gtk_clipboard_request_text (gtk_widget_get_clipboard (GTK_WIDGET (memo_table), clipboard_atom), + (GtkClipboardTextReceivedFunc) clipboard_get_text_cb, memo_table); +} + +/* Opens a task in the task editor */ +static void +open_memo (EMemoTable *memo_table, ECalModelComponent *comp_data) +{ + CompEditor *medit; + const char *uid; + + uid = icalcomponent_get_uid (comp_data->icalcomp); + + medit = e_comp_editor_registry_find (comp_editor_registry, uid); + if (medit == NULL) { + ECalComponent *comp; + + medit = COMP_EDITOR (memo_editor_new (comp_data->client)); + + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp)); + comp_editor_edit_comp (medit, comp); + + e_comp_editor_registry_add (comp_editor_registry, medit, FALSE); + } + + comp_editor_focus (medit); +} + +/* Opens the task in the specified row */ +static void +open_memo_by_row (EMemoTable *memo_table, int row) +{ + ECalModelComponent *comp_data; + + comp_data = e_cal_model_get_component_at (memo_table->model, row); + open_memo (memo_table, comp_data); +} + +static void +e_memo_table_on_double_click (ETable *table, + gint row, + gint col, + GdkEvent *event, + EMemoTable *memo_table) +{ + open_memo_by_row (memo_table, row); +} + + +static void +e_memo_table_on_open_memo (EPopup *ep, EPopupItem *pitem, void *data) +{ + EMemoTable *memo_table = E_MEMO_TABLE (data); + ECalModelComponent *comp_data; + + comp_data = get_selected_comp (memo_table); + if (comp_data) + open_memo (memo_table, comp_data); +} + +static void +e_memo_table_on_save_as (EPopup *ep, EPopupItem *pitem, void *data) +{ + EMemoTable *memo_table = E_MEMO_TABLE (data); + ECalModelComponent *comp_data; + char *filename; + char *ical_string; + FILE *file; + + comp_data = get_selected_comp (memo_table); + if (comp_data == NULL) + return; + + filename = e_file_dialog_save (_("Save as..."), NULL); + if (filename == NULL) + return; + + ical_string = e_cal_get_component_as_string (comp_data->client, comp_data->icalcomp); + if (ical_string == NULL) { + g_warning ("Couldn't convert item to a string"); + return; + } + + file = fopen (filename, "w"); + if (file == NULL) { + g_warning ("Couldn't save item"); + return; + } + + fprintf (file, ical_string); + g_free (ical_string); + fclose (file); +} + +static void +e_memo_table_on_print_memo (EPopup *ep, EPopupItem *pitem, void *data) +{ + EMemoTable *memo_table = E_MEMO_TABLE (data); + ECalModelComponent *comp_data; + ECalComponent *comp; + + comp_data = get_selected_comp (memo_table); + if (comp_data == NULL) + return; + + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp)); + print_comp (comp, comp_data->client, FALSE); + + g_object_unref (comp); +} + +static void +e_memo_table_on_cut (EPopup *ep, EPopupItem *pitem, void *data) +{ + EMemoTable *memo_table = E_MEMO_TABLE (data); + + e_memo_table_cut_clipboard (memo_table); +} + +static void +e_memo_table_on_copy (EPopup *ep, EPopupItem *pitem, void *data) +{ + EMemoTable *memo_table = E_MEMO_TABLE (data); + + e_memo_table_copy_clipboard (memo_table); +} + +static void +e_memo_table_on_paste (EPopup *ep, EPopupItem *pitem, void *data) +{ + EMemoTable *memo_table = E_MEMO_TABLE (data); + + e_memo_table_paste_clipboard (memo_table); +} + +static void +e_memo_table_on_forward (EPopup *ep, EPopupItem *pitem, void *data) +{ + EMemoTable *memo_table = E_MEMO_TABLE (data); + ECalModelComponent *comp_data; + + comp_data = get_selected_comp (memo_table); + if (comp_data) { + ECalComponent *comp; + + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp)); + itip_send_comp (E_CAL_COMPONENT_METHOD_PUBLISH, comp, comp_data->client, NULL, NULL); + + g_object_unref (comp); + } +} + +/* Opens the URL of the memo */ +static void +open_url_cb (EPopup *ep, EPopupItem *pitem, void *data) +{ + EMemoTable *memo_table = E_MEMO_TABLE (data); + ECalModelComponent *comp_data; + icalproperty *prop; + + comp_data = get_selected_comp (memo_table); + if (!comp_data) + return; + + prop = icalcomponent_get_first_property (comp_data->icalcomp, ICAL_URL_PROPERTY); + if (!prop) + return; + + gnome_url_show (icalproperty_get_url (prop), NULL); +} + +/* Callback for the "delete tasks" menu item */ +static void +delete_cb (EPopup *ep, EPopupItem *pitem, void *data) +{ + EMemoTable *memo_table = E_MEMO_TABLE (data); + + e_memo_table_delete_selected (memo_table); +} + +static EPopupItem memos_popup_items [] = { + { E_POPUP_ITEM, "00.open", N_("_Open"), e_memo_table_on_open_memo, NULL, GTK_STOCK_OPEN, E_CAL_POPUP_SELECT_ONE }, + { E_POPUP_ITEM, "05.openweb", N_("Open _Web Page"), open_url_cb, NULL, NULL, E_CAL_POPUP_SELECT_ONE, E_CAL_POPUP_SELECT_HASURL }, + { E_POPUP_ITEM, "10.saveas", N_("_Save As..."), e_memo_table_on_save_as, NULL, GTK_STOCK_SAVE_AS, E_CAL_POPUP_SELECT_ONE }, + { E_POPUP_ITEM, "20.print", N_("_Print..."), e_memo_table_on_print_memo, NULL, GTK_STOCK_PRINT, E_CAL_POPUP_SELECT_ONE }, + + { E_POPUP_BAR, "30.bar" }, + + { E_POPUP_ITEM, "40.cut", N_("C_ut"), e_memo_table_on_cut, NULL, GTK_STOCK_CUT, 0, E_CAL_POPUP_SELECT_EDITABLE }, + { E_POPUP_ITEM, "50.copy", N_("_Copy"), e_memo_table_on_copy, NULL, GTK_STOCK_COPY, 0, 0 }, + { E_POPUP_ITEM, "60.paste", N_("_Paste"), e_memo_table_on_paste, NULL, GTK_STOCK_PASTE, 0, E_CAL_POPUP_SELECT_EDITABLE }, + + { E_POPUP_BAR, "70.bar" }, + + { E_POPUP_ITEM, "80.forward", N_("_Forward as iCalendar"), e_memo_table_on_forward, NULL, "stock_mail-forward", E_CAL_POPUP_SELECT_ONE }, + + { E_POPUP_BAR, "90.bar" }, + + { E_POPUP_ITEM, "a0.delete", N_("_Delete"), delete_cb, NULL, GTK_STOCK_DELETE, E_CAL_POPUP_SELECT_ONE, E_CAL_POPUP_SELECT_EDITABLE }, + { E_POPUP_ITEM, "b0.deletemany", N_("_Delete Selected Memos"), delete_cb, NULL, GTK_STOCK_DELETE, E_CAL_POPUP_SELECT_MANY, E_CAL_POPUP_SELECT_EDITABLE }, +}; + +static void +emt_popup_free(EPopup *ep, GSList *items, void *data) +{ + g_slist_free(items); +} + +static gint +e_memo_table_show_popup_menu (ETable *table, + GdkEvent *gdk_event, + EMemoTable *memo_table) +{ + GtkMenu *menu; + GSList *selection, *l, *menus = NULL; + GPtrArray *events; + ECalPopup *ep; + ECalPopupTargetSelect *t; + int i; + + selection = get_selected_objects (memo_table); + if (!selection) + return TRUE; + + /** @HookPoint-ECalPopup: Tasks Table Context Menu + * @Id: org.gnome.evolution.tasks.table.popup + * @Class: org.gnome.evolution.calendar.popup:1.0 + * @Target: ECalPopupTargetSelect + * + * The context menu on the tasks table. + */ + ep = e_cal_popup_new("org.gnome.evolution.memos.table.popup"); + + events = g_ptr_array_new(); + for (l=selection;l;l=g_slist_next(l)) + g_ptr_array_add(events, e_cal_model_copy_component_data((ECalModelComponent *)l->data)); + g_slist_free(selection); + + t = e_cal_popup_target_new_select(ep, memo_table->model, events); + t->target.widget = (GtkWidget *)memo_table; + + for (i=0;i<sizeof(memos_popup_items)/sizeof(memos_popup_items[0]);i++) + menus = g_slist_prepend(menus, &memos_popup_items[i]); + e_popup_add_items((EPopup *)ep, menus, NULL, emt_popup_free, memo_table); + + menu = e_popup_create_menu_once((EPopup *)ep, (EPopupTarget *)t, 0); + + gtk_menu_popup(menu, NULL, NULL, NULL, NULL, gdk_event?gdk_event->button.button:0, + gdk_event?gdk_event->button.time:gtk_get_current_event_time()); + + return TRUE; +} + +static gint +e_memo_table_on_right_click (ETable *table, + gint row, + gint col, + GdkEvent *event, + EMemoTable *memo_table) +{ + return e_memo_table_show_popup_menu (table, event, memo_table); +} + +static gboolean +e_memo_table_on_popup_menu (GtkWidget *widget, gpointer data) +{ + ETable *table = E_TABLE(widget); + g_return_val_if_fail(table, FALSE); + + return e_memo_table_show_popup_menu (table, NULL, + E_MEMO_TABLE(data)); +} + +static gint +e_memo_table_on_key_press (ETable *table, + gint row, + gint col, + GdkEventKey *event, + EMemoTable *memo_table) +{ + if (event->keyval == GDK_Delete) { + delete_cb (NULL, NULL, memo_table); + return TRUE; + } else if ((event->keyval == GDK_o) + &&(event->state & GDK_CONTROL_MASK)) { + open_memo_by_row (memo_table, row); + return TRUE; + } + + return FALSE; +} + +/* Loads the state of the table (headers shown etc.) from the given file. */ +void +e_memo_table_load_state (EMemoTable *memo_table, + gchar *filename) +{ + struct stat st; + + g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); + + if (stat (filename, &st) == 0 && st.st_size > 0 + && S_ISREG (st.st_mode)) { + e_table_load_state (e_table_scrolled_get_table (E_TABLE_SCROLLED (memo_table->etable)), filename); + } +} + + +/* Saves the state of the table (headers shown etc.) to the given file. */ +void +e_memo_table_save_state (EMemoTable *memo_table, + gchar *filename) +{ + g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); + + e_table_save_state (e_table_scrolled_get_table (E_TABLE_SCROLLED (memo_table->etable)), + filename); +} + +#ifdef TRANSLATORS_ONLY + +static char *test[] = { + N_("Click to add a memo") +}; + +#endif + +/* Displays messages on the status bar */ +#define EVOLUTION_MEMOS_PROGRESS_IMAGE "stock_notes" +static GdkPixbuf *progress_icon = NULL; + +void +e_memo_table_set_activity_handler (EMemoTable *memo_table, EActivityHandler *activity_handler) +{ + g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); + + memo_table->activity_handler = activity_handler; +} + +void +e_memo_table_set_status_message (EMemoTable *memo_table, const gchar *message) +{ + g_return_if_fail (E_IS_MEMO_TABLE (memo_table)); + + if (!memo_table->activity_handler) + return; + + if (!message || !*message) { + if (memo_table->activity_id != 0) { + e_activity_handler_operation_finished (memo_table->activity_handler, memo_table->activity_id); + memo_table->activity_id = 0; + } + } else if (memo_table->activity_id == 0) { + char *client_id = g_strdup_printf ("%p", memo_table); + + if (progress_icon == NULL) + progress_icon = e_icon_factory_get_icon (EVOLUTION_MEMOS_PROGRESS_IMAGE, E_ICON_SIZE_STATUS); + + memo_table->activity_id = e_activity_handler_operation_started (memo_table->activity_handler, client_id, + progress_icon, message, TRUE); + + g_free (client_id); + } else { + e_activity_handler_operation_progressing (memo_table->activity_handler, memo_table->activity_id, message, -1.0); + } +} diff --git a/calendar/gui/e-memo-table.etspec b/calendar/gui/e-memo-table.etspec new file mode 100644 index 0000000000..06ecc2ce4b --- /dev/null +++ b/calendar/gui/e-memo-table.etspec @@ -0,0 +1,13 @@ +<ETableSpecification click-to-add="false" _click-to-add-message="Click to add a memo" draw-grid="true"> + <ETableColumn model_col= "8" _title="Summary" expansion="3.0" minimum_width="10" resizable="true" cell="calstring" compare="string" priority="10"/> + <ETableColumn model_col="19" _title="Memo sort" cell="task-sort" compare="memo-sort" priority="-4"/> + <ETableColumn model_col="7" pixbuf="icon" _title="Type" expansion="1.0" minimum_width="16" resizable="false" cell="icon" compare="integer" priority="-4"/> + <ETableColumn model_col="0" _title="Categories" cell="calstring" compare="string" expansion="1.0" minimum_width="10" resizable="true" priority="-2"/> + + <ETableState> + <column source="1"/> + <column source="0"/> + <column source="2"/> + <grouping></grouping> + </ETableState> +</ETableSpecification> diff --git a/calendar/gui/e-memo-table.h b/calendar/gui/e-memo-table.h new file mode 100644 index 0000000000..da01405ac0 --- /dev/null +++ b/calendar/gui/e-memo-table.h @@ -0,0 +1,106 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ + +/* + * Author : + * Damon Chaplin <damon@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + * + * Copyright 2000, Ximian, Inc. + * Copyright 2000, Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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 + */ +#ifndef _E_MEMO_TABLE_H_ +#define _E_MEMO_TABLE_H_ + +#include <gtk/gtktable.h> +#include <table/e-table-scrolled.h> +#include <widgets/misc/e-cell-date-edit.h> +#include "e-activity-handler.h" +#include "e-cal-model.h" + +G_BEGIN_DECLS + +/* + * EMemoTable - displays the iCalendar objects in a table (an ETable). + * Used for memo events and tasks. + */ + + +#define E_MEMO_TABLE(obj) GTK_CHECK_CAST (obj, e_memo_table_get_type (), EMemoTable) +#define E_MEMO_TABLE_CLASS(klass) GTK_CHECK_CLASS_CAST (klass, e_memo_table_get_type (), EMemoTableClass) +#define E_IS_MEMO_TABLE(obj) GTK_CHECK_TYPE (obj, e_memo_table_get_type ()) + + +typedef struct _EMemoTable EMemoTable; +typedef struct _EMemoTableClass EMemoTableClass; + + +struct _EMemoTable { + GtkTable table; + + /* The model that we use */ + ECalModel *model; + + GtkWidget *etable; + + /* Fields used for cut/copy/paste */ + icalcomponent *tmp_vcal; + + /* Activity ID for the EActivityHandler (i.e. the status bar). */ + EActivityHandler *activity_handler; + guint activity_id; +}; + +struct _EMemoTableClass { + GtkTableClass parent_class; + + /* Notification signals */ + void (* user_created) (EMemoTable *memo_table); +}; + + +GtkType e_memo_table_get_type (void); +GtkWidget* e_memo_table_new (void); + +ECalModel *e_memo_table_get_model (EMemoTable *memo_table); + +ETable *e_memo_table_get_table (EMemoTable *memo_table); + +void e_memo_table_open_selected (EMemoTable *memo_table); +void e_memo_table_delete_selected (EMemoTable *memo_table); + +GSList *e_memo_table_get_selected (EMemoTable *memo_table); + +/* Clipboard related functions */ +void e_memo_table_cut_clipboard (EMemoTable *memo_table); +void e_memo_table_copy_clipboard (EMemoTable *memo_table); +void e_memo_table_paste_clipboard (EMemoTable *memo_table); + +/* These load and save the state of the table (headers shown etc.) to/from + the given file. */ +void e_memo_table_load_state (EMemoTable *memo_table, + gchar *filename); +void e_memo_table_save_state (EMemoTable *memo_table, + gchar *filename); + +void e_memo_table_set_activity_handler (EMemoTable *memo_table, + EActivityHandler *activity_handler); +void e_memo_table_set_status_message (EMemoTable *memo_table, + const gchar *message); + +G_END_DECLS + +#endif /* _E_MEMO_TABLE_H_ */ diff --git a/calendar/gui/e-memos.c b/calendar/gui/e-memos.c new file mode 100644 index 0000000000..5825af0a43 --- /dev/null +++ b/calendar/gui/e-memos.c @@ -0,0 +1,1174 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-memos.c + * + * Copyright (C) 2001-2003 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + * + * Authors: Federico Mena Quintero <federico@ximian.com> + * Damon Chaplin <damon@ximian.com> + * Rodrigo Moya <rodrigo@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gnome.h> +#include <libgnomevfs/gnome-vfs-ops.h> +#include <table/e-table-scrolled.h> +#include <widgets/menus/gal-view-instance.h> +#include <widgets/menus/gal-view-factory-etable.h> +#include <widgets/menus/gal-view-etable.h> + +#include "e-util/e-error.h" +#include "e-util/e-categories-config.h" +#include "e-util/e-config-listener.h" +#include "e-util/e-time-utils.h" +#include "shell/e-user-creatable-items-handler.h" +#include <libecal/e-cal-time-util.h> +#include <libedataserver/e-url.h> +#include <libedataserver/e-categories.h> +#include "widgets/menus/gal-view-menus.h" +#include "dialogs/delete-error.h" +#include "e-calendar-marshal.h" +#include "calendar-config.h" +#include "cal-search-bar.h" +#include "calendar-component.h" +#include "comp-util.h" +#include "e-memo-table-config.h" +#include "misc.h" +#include "memos-component.h" +#include "e-cal-component-memo-preview.h" +#include "e-memos.h" +#include "common/authentication.h" + + +/* Private part of the GnomeCalendar structure */ +struct _EMemosPrivate { + /* The memo lists for display */ + GHashTable *clients; + GList *clients_list; + ECal *default_client; + + ECalView *query; + + EConfigListener *config_listener; + + /* The EMemoTable showing the memos. */ + GtkWidget *memos_view; + EMemoTableConfig *memos_view_config; + + /* Calendar search bar for memos */ + GtkWidget *search_bar; + + /* Paned widget */ + GtkWidget *paned; + + /* The preview */ + GtkWidget *preview; + + gchar *current_uid; + char *sexp; + guint update_timeout; + + /* View instance and the view menus handler */ + GalViewInstance *view_instance; + GalViewMenus *view_menus; + + GList *notifications; +}; + +static void setup_widgets (EMemos *memos); +static void e_memos_destroy (GtkObject *object); +static void update_view (EMemos *memos); + +static void config_categories_changed_cb (EConfigListener *config_listener, const char *key, gpointer user_data); +static void backend_error_cb (ECal *client, const char *message, gpointer data); + +/* Signal IDs */ +enum { + SELECTION_CHANGED, + SOURCE_ADDED, + SOURCE_REMOVED, + LAST_SIGNAL +}; + +enum DndTargetType { + TARGET_VCALENDAR +}; + +static GtkTargetEntry list_drag_types[] = { + { "text/calendar", 0, TARGET_VCALENDAR }, + { "text/x-calendar", 0, TARGET_VCALENDAR } +}; +static const int num_list_drag_types = sizeof (list_drag_types) / sizeof (list_drag_types[0]); + +static guint e_memos_signals[LAST_SIGNAL] = { 0 }; + +G_DEFINE_TYPE (EMemos, e_memos, GTK_TYPE_TABLE); + +/* Callback used when the cursor changes in the table */ +static void +table_cursor_change_cb (ETable *etable, int row, gpointer data) +{ + EMemos *memos; + EMemosPrivate *priv; + ECalModel *model; + ECalModelComponent *comp_data; + ECalComponent *comp; + const char *uid; + + int n_selected; + + memos = E_MEMOS (data); + priv = memos->priv; + + n_selected = e_table_selected_count (etable); + + /* update the HTML widget */ + if (n_selected != 1) { + e_cal_component_memo_preview_clear (E_CAL_COMPONENT_MEMO_PREVIEW (priv->preview)); + + return; + } + + model = e_memo_table_get_model (E_MEMO_TABLE (priv->memos_view)); + + comp_data = e_cal_model_get_component_at (model, e_table_get_cursor_row (etable)); + comp = e_cal_component_new (); + e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (comp_data->icalcomp)); + + e_cal_component_memo_preview_display (E_CAL_COMPONENT_MEMO_PREVIEW (priv->preview), comp_data->client, comp); + + e_cal_component_get_uid (comp, &uid); + if (priv->current_uid) + g_free (priv->current_uid); + priv->current_uid = g_strdup (uid); + + g_object_unref (comp); +} + +/* Callback used when the selection changes in the table. */ +static void +table_selection_change_cb (ETable *etable, gpointer data) +{ + EMemos *memos; + int n_selected; + + memos = E_MEMOS (data); + + n_selected = e_table_selected_count (etable); + gtk_signal_emit (GTK_OBJECT (memos), e_memos_signals[SELECTION_CHANGED], + n_selected); +} + +static void +user_created_cb (GtkWidget *view, EMemos *memos) +{ + EMemosPrivate *priv; + ECal *ecal; + ECalModel *model; + + priv = memos->priv; + + model = e_memo_table_get_model (E_MEMO_TABLE (priv->memos_view)); + ecal = e_cal_model_get_default_client (model); + + e_memos_add_memo_source (memos, e_cal_get_source (ecal)); +} + +/* Callback used when the sexp in the search bar changes */ +static void +search_bar_sexp_changed_cb (CalSearchBar *cal_search, const char *sexp, gpointer data) +{ + EMemos *memos; + EMemosPrivate *priv; + + memos = E_MEMOS (data); + priv = memos->priv; + + if (priv->sexp) + g_free (priv->sexp); + + priv->sexp = g_strdup (sexp); + + update_view (memos); +} + +/* Callback used when the selected category in the search bar changes */ +static void +search_bar_category_changed_cb (CalSearchBar *cal_search, const char *category, gpointer data) +{ + EMemos *memos; + EMemosPrivate *priv; + ECalModel *model; + + memos = E_MEMOS (data); + priv = memos->priv; + + model = e_memo_table_get_model (E_MEMO_TABLE (priv->memos_view)); + e_cal_model_set_default_category (model, category); +} + +static gboolean +vpaned_resized_cb (GtkWidget *widget, GdkEventButton *event, EMemos *memos) +{ + calendar_config_set_task_vpane_pos (gtk_paned_get_position (GTK_PANED (widget))); + + return FALSE; +} + +static void +set_timezone (EMemos *memos) +{ + EMemosPrivate *priv; + icaltimezone *zone; + GList *l; + + priv = memos->priv; + + zone = calendar_config_get_icaltimezone (); + for (l = priv->clients_list; l != NULL; l = l->next) { + ECal *client = l->data; + + if (e_cal_get_load_state (client) == E_CAL_LOAD_LOADED) + /* FIXME Error checking */ + e_cal_set_default_timezone (client, zone, NULL); + } + + if (priv->default_client && e_cal_get_load_state (priv->default_client) == E_CAL_LOAD_LOADED) + /* FIXME Error checking */ + e_cal_set_default_timezone (priv->default_client, zone, NULL); + + if (priv->preview) + e_cal_component_memo_preview_set_default_timezone (E_CAL_COMPONENT_MEMO_PREVIEW (priv->preview), zone); +} + +static void +timezone_changed_cb (GConfClient *client, guint id, GConfEntry *entry, gpointer data) +{ + EMemos *memos = data; + + set_timezone (memos); +} + +static void +update_view (EMemos *memos) +{ + EMemosPrivate *priv; + ECalModel *model; + char *real_sexp = NULL; + char *new_sexp = NULL; + + priv = memos->priv; + + model = e_memo_table_get_model (E_MEMO_TABLE (priv->memos_view)); + + e_cal_model_set_search_query (model, priv->sexp); + + e_cal_component_memo_preview_clear (E_CAL_COMPONENT_MEMO_PREVIEW (priv->preview)); +} + +static gboolean +update_view_cb (EMemos *memos) +{ + update_view (memos); + + return TRUE; +} + +static void +model_row_changed_cb (ETableModel *etm, int row, gpointer data) +{ + EMemos *memos; + EMemosPrivate *priv; + ECalModelComponent *comp_data; + + memos = E_MEMOS (data); + priv = memos->priv; + + if (priv->current_uid) { + const char *uid; + + comp_data = e_cal_model_get_component_at (E_CAL_MODEL (etm), row); + if (comp_data) { + uid = icalcomponent_get_uid (comp_data->icalcomp); + if (!strcmp (uid ? uid : "", priv->current_uid)) { + ETable *etable; + + etable = e_table_scrolled_get_table ( + E_TABLE_SCROLLED (E_MEMO_TABLE (priv->memos_view)->etable)); + table_cursor_change_cb (etable, 0, memos); + } + } + } +} + +static void +setup_config (EMemos *memos) +{ + EMemosPrivate *priv; + guint not; + + priv = memos->priv; + + /* Timezone */ + set_timezone (memos); + + not = calendar_config_add_notification_timezone (timezone_changed_cb, memos); + priv->notifications = g_list_prepend (priv->notifications, GUINT_TO_POINTER (not)); +} + +static void +table_drag_data_get (ETable *table, + int row, + int col, + GdkDragContext *context, + GtkSelectionData *selection_data, + guint info, + guint time, + EMemos *memos) +{ + EMemosPrivate *priv; + ECalModelComponent *comp_data; + + priv = memos->priv; + + if (priv->current_uid) { + ECalModel *model; + + model = e_memo_table_get_model ( + E_MEMO_TABLE (priv->memos_view)); + + comp_data = e_cal_model_get_component_at (model, row); + + if (info == TARGET_VCALENDAR) { + /* we will pass an icalcalendar component for both types */ + char *comp_str; + icalcomponent *vcal; + + vcal = e_cal_util_new_top_level (); + e_cal_util_add_timezones_from_component (vcal, comp_data->icalcomp); + icalcomponent_add_component ( + vcal, + icalcomponent_new_clone (comp_data->icalcomp)); + + comp_str = icalcomponent_as_ical_string (vcal); + if (comp_str) { + gtk_selection_data_set (selection_data, selection_data->target, + 8, comp_str, strlen (comp_str)); + } + icalcomponent_free (vcal); + } + } +} + +static void +table_drag_data_delete (ETable *table, + int row, + int col, + GdkDragContext *context, + EMemos *memos) +{ + EMemosPrivate *priv; + ECalModelComponent *comp_data; + ECalModel *model; + gboolean read_only = TRUE; + + priv = memos->priv; + + model = e_memo_table_get_model ( + E_MEMO_TABLE (priv->memos_view)); + comp_data = e_cal_model_get_component_at (model, row); + + e_cal_is_read_only (comp_data->client, &read_only, NULL); + if (read_only) + return; + + e_cal_remove_object (comp_data->client, icalcomponent_get_uid (comp_data->icalcomp), NULL); +} + +#define E_MEMOS_TABLE_DEFAULT_STATE \ + "<?xml version=\"1.0\"?>" \ + "<ETableState>" \ + "<column source=\"1\"/>" \ + "<column source=\"0\"/>" \ + "<column source=\"2\"/>" \ + "<grouping></grouping>" \ + "</ETableState>" + +static void +pane_realized (GtkWidget *widget, EMemos *memos) +{ + gtk_paned_set_position ((GtkPaned *)widget, calendar_config_get_task_vpane_pos ()); +} + +static void +setup_widgets (EMemos *memos) +{ + EMemosPrivate *priv; + ETable *etable; + ECalModel *model; + + priv = memos->priv; + + priv->search_bar = cal_search_bar_new (CAL_SEARCH_TASKS_DEFAULT); + g_signal_connect (priv->search_bar, "sexp_changed", + G_CALLBACK (search_bar_sexp_changed_cb), memos); + g_signal_connect (priv->search_bar, "category_changed", + G_CALLBACK (search_bar_category_changed_cb), memos); + + /* TODO Why doesn't this work?? */ + config_categories_changed_cb (priv->config_listener, "/apps/evolution/general/category_master_list", memos); + + gtk_table_attach (GTK_TABLE (memos), priv->search_bar, 0, 1, 0, 1, + GTK_EXPAND | GTK_FILL | GTK_SHRINK, 0, 0, 0); + gtk_widget_show (priv->search_bar); + + /* add the paned widget for the memo list and memo detail areas */ + priv->paned = gtk_vpaned_new (); + g_signal_connect (priv->paned, "realize", G_CALLBACK (pane_realized), memos); + + g_signal_connect (G_OBJECT (priv->paned), "button_release_event", + G_CALLBACK (vpaned_resized_cb), memos); + gtk_table_attach (GTK_TABLE (memos), priv->paned, 0, 1, 1, 2, + GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0); + gtk_widget_show (priv->paned); + + /* create the memo list */ + priv->memos_view = e_memo_table_new (); + priv->memos_view_config = e_memo_table_config_new (E_MEMO_TABLE (priv->memos_view)); + + g_signal_connect (priv->memos_view, "user_created", G_CALLBACK (user_created_cb), memos); + + etable = e_table_scrolled_get_table ( + E_TABLE_SCROLLED (E_MEMO_TABLE (priv->memos_view)->etable)); + e_table_set_state (etable, E_MEMOS_TABLE_DEFAULT_STATE); + + gtk_paned_add1 (GTK_PANED (priv->paned), priv->memos_view); + gtk_widget_show (priv->memos_view); + + + e_table_drag_source_set (etable, GDK_BUTTON1_MASK, + list_drag_types, num_list_drag_types, + GDK_ACTION_MOVE|GDK_ACTION_COPY|GDK_ACTION_ASK); + + g_signal_connect (etable, "table_drag_data_get", + G_CALLBACK(table_drag_data_get), memos); + g_signal_connect (etable, "table_drag_data_delete", + G_CALLBACK(table_drag_data_delete), memos); + + g_signal_connect (etable, "cursor_change", G_CALLBACK (table_cursor_change_cb), memos); + g_signal_connect (etable, "selection_change", G_CALLBACK (table_selection_change_cb), memos); + + /* Timeout check to hide completed items */ + priv->update_timeout = g_timeout_add_full (G_PRIORITY_LOW, 60000, (GSourceFunc) update_view_cb, memos, NULL); + + /* create the memo detail */ + priv->preview = e_cal_component_memo_preview_new (); + e_cal_component_memo_preview_set_default_timezone (E_CAL_COMPONENT_MEMO_PREVIEW (priv->preview), calendar_config_get_icaltimezone ()); + gtk_paned_add2 (GTK_PANED (priv->paned), priv->preview); + gtk_widget_show (priv->preview); + + model = e_memo_table_get_model (E_MEMO_TABLE (priv->memos_view)); + g_signal_connect (G_OBJECT (model), "model_row_changed", + G_CALLBACK (model_row_changed_cb), memos); +} + +/* Class initialization function for the gnome calendar */ +static void +e_memos_class_init (EMemosClass *klass) +{ + GtkObjectClass *object_class; + + object_class = (GtkObjectClass *) klass; + + e_memos_signals[SELECTION_CHANGED] = + gtk_signal_new ("selection_changed", + GTK_RUN_LAST, + G_TYPE_FROM_CLASS (object_class), + GTK_SIGNAL_OFFSET (EMemosClass, selection_changed), + gtk_marshal_NONE__INT, + GTK_TYPE_NONE, 1, + GTK_TYPE_INT); + + e_memos_signals[SOURCE_ADDED] = + g_signal_new ("source_added", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EMemosClass, source_added), + NULL, NULL, + e_calendar_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + e_memos_signals[SOURCE_REMOVED] = + g_signal_new ("source_removed", + G_TYPE_FROM_CLASS (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (EMemosClass, source_removed), + NULL, NULL, + e_calendar_marshal_VOID__OBJECT, + G_TYPE_NONE, + 1, + G_TYPE_OBJECT); + + object_class->destroy = e_memos_destroy; + + klass->selection_changed = NULL; + klass->source_added = NULL; + klass->source_removed = NULL; +} + + +static void +config_categories_changed_cb (EConfigListener *config_listener, const char *key, gpointer user_data) +{ + GList *cat_list; + GPtrArray *cat_array; + EMemosPrivate *priv; + EMemos *memos = user_data; + + priv = memos->priv; + + cat_array = g_ptr_array_new (); + cat_list = e_categories_get_list (); + while (cat_list != NULL) { + if (e_categories_is_searchable ((const char *) cat_list->data)) + g_ptr_array_add (cat_array, cat_list->data); + cat_list = g_list_remove (cat_list, cat_list->data); + } + + cal_search_bar_set_categories (CAL_SEARCH_BAR(priv->search_bar), cat_array); + + g_ptr_array_free (cat_array, TRUE); +} + +/* Object initialization function for the gnome calendar */ +static void +e_memos_init (EMemos *memos) +{ + EMemosPrivate *priv; + + priv = g_new0 (EMemosPrivate, 1); + memos->priv = priv; + + setup_config (memos); + setup_widgets (memos); + + priv->config_listener = e_config_listener_new (); + g_signal_connect (priv->config_listener, "key_changed", G_CALLBACK (config_categories_changed_cb), memos); + + priv->clients = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); + priv->query = NULL; + priv->view_instance = NULL; + priv->view_menus = NULL; + priv->current_uid = NULL; + priv->sexp = g_strdup ("#t"); + priv->default_client = NULL; + update_view (memos); +} + +/* Callback used when the set of categories changes in the calendar client */ +/* TODO this was actually taken out of tasks in 2.3.x + * config_categories doesn't work, but this may - trying with memos*/ +/* +static void +client_categories_changed_cb (ECal *client, GPtrArray *categories, gpointer data) +{ + EMemos *memos; + EMemosPrivate *priv; + + memos = E_MEMOS (data); + priv = memos->priv; + + cal_search_bar_set_categories (CAL_SEARCH_BAR (priv->search_bar), categories); +} +*/ + +GtkWidget * +e_memos_new (void) +{ + EMemos *memos; + + memos = g_object_new (e_memos_get_type (), NULL); + + return GTK_WIDGET (memos); +} + + +void +e_memos_set_ui_component (EMemos *memos, + BonoboUIComponent *ui_component) +{ + g_return_if_fail (E_IS_MEMOS (memos)); + g_return_if_fail (ui_component == NULL || BONOBO_IS_UI_COMPONENT (ui_component)); + + e_search_bar_set_ui_component (E_SEARCH_BAR (memos->priv->search_bar), ui_component); +} + + +static void +e_memos_destroy (GtkObject *object) +{ + EMemos *memos; + EMemosPrivate *priv; + + g_return_if_fail (object != NULL); + g_return_if_fail (E_IS_MEMOS (object)); + + memos = E_MEMOS (object); + priv = memos->priv; + + if (priv) { + GList *l; + + /* unset the config listener */ + if (priv->config_listener) { + g_signal_handlers_disconnect_matched (priv->config_listener, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, memos); + g_object_unref (priv->config_listener); + priv->config_listener = NULL; + } + + /* disconnect from signals on all the clients */ + for (l = priv->clients_list; l != NULL; l = l->next) { + g_signal_handlers_disconnect_matched (l->data, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, memos); + } + + g_hash_table_destroy (priv->clients); + g_list_free (priv->clients_list); + + if (priv->default_client) + g_object_unref (priv->default_client); + priv->default_client = NULL; + + if (priv->current_uid) { + g_free (priv->current_uid); + priv->current_uid = NULL; + } + + if (priv->sexp) { + g_free (priv->sexp); + priv->sexp = NULL; + } + + if (priv->update_timeout) { + g_source_remove (priv->update_timeout); + priv->update_timeout = 0; + } + + if (priv->memos_view_config) { + g_object_unref (priv->memos_view_config); + priv->memos_view_config = NULL; + } + + for (l = priv->notifications; l; l = l->next) + calendar_config_remove_notification (GPOINTER_TO_UINT (l->data)); + priv->notifications = NULL; + + g_free (priv); + memos->priv = NULL; + } + + if (GTK_OBJECT_CLASS (e_memos_parent_class)->destroy) + (* GTK_OBJECT_CLASS (e_memos_parent_class)->destroy) (object); +} + +static void +set_status_message (EMemos *memos, const char *message, ...) +{ + EMemosPrivate *priv; + va_list args; + char sz[2048], *msg_string = NULL; + + if (message) { + va_start (args, message); + vsnprintf (sz, sizeof sz, message, args); + va_end (args); + msg_string = sz; + } + + priv = memos->priv; + + e_memo_table_set_status_message (E_MEMO_TABLE (priv->memos_view), msg_string); +} + +/* Callback from the calendar client when an error occurs in the backend */ +static void +backend_error_cb (ECal *client, const char *message, gpointer data) +{ + EMemos *memos; + EMemosPrivate *priv; + char *errmsg; + char *urinopwd; + + memos = E_MEMOS (data); + priv = memos->priv; + + urinopwd = get_uri_without_password (e_cal_get_uri (client)); + errmsg = g_strdup_printf (_("Error on %s:\n %s"), urinopwd, message); + gnome_error_dialog_parented (errmsg, GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (memos)))); + g_free (errmsg); + g_free (urinopwd); +} + +/* Callback from the calendar client when the backend dies */ +static void +backend_died_cb (ECal *client, gpointer data) +{ + EMemos *memos; + EMemosPrivate *priv; + ESource *source; + + memos = E_MEMOS (data); + priv = memos->priv; + + source = g_object_ref (e_cal_get_source (client)); + + priv->clients_list = g_list_remove (priv->clients_list, client); + g_hash_table_remove (priv->clients, e_source_peek_uid (source)); + + gtk_signal_emit (GTK_OBJECT (memos), e_memos_signals[SOURCE_REMOVED], source); + + e_memo_table_set_status_message (E_MEMO_TABLE (e_memos_get_calendar_table (memos)), NULL); + + e_error_run (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (memos))), + "calendar:memos-crashed", NULL); + + g_object_unref (source); +} + +/* Callback from the calendar client when the calendar is opened */ +static void +client_cal_opened_cb (ECal *ecal, ECalendarStatus status, EMemos *memos) +{ + ECalModel *model; + ESource *source; + EMemosPrivate *priv; + + priv = memos->priv; + + source = e_cal_get_source (ecal); + + switch (status) { + case E_CALENDAR_STATUS_OK : + g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, client_cal_opened_cb, NULL); + + set_status_message (memos, _("Loading memos")); + model = e_memo_table_get_model (E_MEMO_TABLE (priv->memos_view)); + e_cal_model_add_client (model, ecal); + + set_timezone (memos); + set_status_message (memos, NULL); + break; + case E_CALENDAR_STATUS_BUSY : + break; + case E_CALENDAR_STATUS_REPOSITORY_OFFLINE: + e_error_run (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (memos))), "calendar:prompt-no-contents-offline-memos", NULL); + break; + default : + /* Make sure the source doesn't disappear on us */ + g_object_ref (source); + + priv->clients_list = g_list_remove (priv->clients_list, ecal); + g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, memos); + + /* Do this last because it unrefs the client */ + g_hash_table_remove (priv->clients, e_source_peek_uid (source)); + + gtk_signal_emit (GTK_OBJECT (memos), e_memos_signals[SOURCE_REMOVED], source); + + set_status_message (memos, NULL); + g_object_unref (source); + + break; + } +} + +static void +default_client_cal_opened_cb (ECal *ecal, ECalendarStatus status, EMemos *memos) +{ + ECalModel *model; + ESource *source; + EMemosPrivate *priv; + + priv = memos->priv; + + source = e_cal_get_source (ecal); + + switch (status) { + case E_CALENDAR_STATUS_OK : + g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, default_client_cal_opened_cb, NULL); + model = e_memo_table_get_model (E_MEMO_TABLE (priv->memos_view)); + + set_timezone (memos); + e_cal_model_set_default_client (model, ecal); + set_status_message (memos, NULL); + break; + case E_CALENDAR_STATUS_BUSY: + break; + default : + /* Make sure the source doesn't disappear on us */ + g_object_ref (source); + + priv->clients_list = g_list_remove (priv->clients_list, ecal); + g_signal_handlers_disconnect_matched (ecal, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, memos); + + /* Do this last because it unrefs the client */ + g_hash_table_remove (priv->clients, e_source_peek_uid (source)); + + gtk_signal_emit (GTK_OBJECT (memos), e_memos_signals[SOURCE_REMOVED], source); + + set_status_message (memos, NULL); + g_object_unref (priv->default_client); + priv->default_client = NULL; + g_object_unref (source); + + break; + } +} + +typedef void (*open_func) (ECal *, ECalendarStatus, EMemos *); + +static gboolean +open_ecal (EMemos *memos, ECal *cal, gboolean only_if_exists, open_func of) +{ + EMemosPrivate *priv; + + priv = memos->priv; + + set_status_message (memos, _("Opening memos at %s"), e_cal_get_uri (cal)); + + g_signal_connect (G_OBJECT (cal), "cal_opened", G_CALLBACK (of), memos); + e_cal_open_async (cal, only_if_exists); + + return TRUE; +} + +void +e_memos_open_memo (EMemos *memos) +{ + EMemoTable *cal_table; + + cal_table = e_memos_get_calendar_table (memos); + e_memo_table_open_selected (cal_table); +} + +void +e_memos_new_memo (EMemos *memos) +{ + /* used for click_to_add ?? Can't figure out anything else it's used for */ +} + +gboolean +e_memos_add_memo_source (EMemos *memos, ESource *source) +{ + EMemosPrivate *priv; + ECal *client; + const char *uid; + + g_return_val_if_fail (memos != NULL, FALSE); + g_return_val_if_fail (E_IS_MEMOS (memos), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + priv = memos->priv; + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (priv->clients, uid); + if (client) { + /* We already have it */ + + return TRUE; + } else { + ESource *default_source; + + if (priv->default_client) { + default_source = e_cal_get_source (priv->default_client); + + /* We don't have it but the default client is it */ + if (!strcmp (e_source_peek_uid (default_source), uid)) + client = g_object_ref (priv->default_client); + } + + /* Create a new one */ + if (!client) { + client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_JOURNAL); + if (!client) + return FALSE; + } + } + + g_signal_connect (G_OBJECT (client), "backend_error", G_CALLBACK (backend_error_cb), memos); +/* g_signal_connect (G_OBJECT (client), "categories_changed", G_CALLBACK (client_categories_changed_cb), memos); */ + g_signal_connect (G_OBJECT (client), "backend_died", G_CALLBACK (backend_died_cb), memos); + + /* add the client to internal structure */ + g_hash_table_insert (priv->clients, g_strdup (uid) , client); + priv->clients_list = g_list_prepend (priv->clients_list, client); + + gtk_signal_emit (GTK_OBJECT (memos), e_memos_signals[SOURCE_ADDED], source); + + open_ecal (memos, client, FALSE, client_cal_opened_cb); + + return TRUE; +} + +gboolean +e_memos_remove_memo_source (EMemos *memos, ESource *source) +{ + EMemosPrivate *priv; + ECal *client; + ECalModel *model; + const char *uid; + + g_return_val_if_fail (memos != NULL, FALSE); + g_return_val_if_fail (E_IS_MEMOS (memos), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + priv = memos->priv; + + uid = e_source_peek_uid (source); + client = g_hash_table_lookup (priv->clients, uid); + if (!client) + return TRUE; + + + priv->clients_list = g_list_remove (priv->clients_list, client); + g_signal_handlers_disconnect_matched (client, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, memos); + + model = e_memo_table_get_model (E_MEMO_TABLE (priv->memos_view)); + e_cal_model_remove_client (model, client); + + g_hash_table_remove (priv->clients, uid); + + + gtk_signal_emit (GTK_OBJECT (memos), e_memos_signals[SOURCE_REMOVED], source); + + return TRUE; +} + +gboolean +e_memos_set_default_source (EMemos *memos, ESource *source) +{ + EMemosPrivate *priv; + ECal *ecal; + + g_return_val_if_fail (memos != NULL, FALSE); + g_return_val_if_fail (E_IS_MEMOS (memos), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + priv = memos->priv; + + ecal = g_hash_table_lookup (priv->clients, e_source_peek_uid (source)); + + if (priv->default_client) + g_object_unref (priv->default_client); + + if (ecal) { + priv->default_client = g_object_ref (ecal); + } else { + priv->default_client = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_JOURNAL); + if (!priv->default_client) + return FALSE; + } + + open_ecal (memos, priv->default_client, FALSE, default_client_cal_opened_cb); + + return TRUE; +} + +ECal * +e_memos_get_default_client (EMemos *memos) +{ + EMemosPrivate *priv; + + g_return_val_if_fail (memos != NULL, NULL); + g_return_val_if_fail (E_IS_MEMOS (memos), NULL); + + priv = memos->priv; + + return e_cal_model_get_default_client (e_memo_table_get_model (E_MEMO_TABLE (priv->memos_view))); +} + + +/** + * e_memos_delete_selected: + * @memos: A memos control widget. + * + * Deletes the selected memos in the memo list. + **/ +void +e_memos_delete_selected (EMemos *memos) +{ + EMemosPrivate *priv; + EMemoTable *cal_table; + + g_return_if_fail (memos != NULL); + g_return_if_fail (E_IS_MEMOS (memos)); + + priv = memos->priv; + + cal_table = E_MEMO_TABLE (priv->memos_view); + set_status_message (memos, _("Deleting selected objects...")); + e_memo_table_delete_selected (cal_table); + set_status_message (memos, NULL); + + e_cal_component_memo_preview_clear (E_CAL_COMPONENT_MEMO_PREVIEW (priv->preview)); +} + + +/* Callback used from the view collection when we need to display a new view */ +static void +display_view_cb (GalViewInstance *instance, GalView *view, gpointer data) +{ + EMemos *memos; + + memos = E_MEMOS (data); + + if (GAL_IS_VIEW_ETABLE (view)) { + gal_view_etable_attach_table (GAL_VIEW_ETABLE (view), e_table_scrolled_get_table (E_TABLE_SCROLLED (E_MEMO_TABLE (memos->priv->memos_view)->etable))); + } + + gtk_paned_set_position ((GtkPaned *)memos->priv->paned, calendar_config_get_task_vpane_pos ()); +} + +/** + * e_memos_setup_view_menus: + * @memos: A memos widget. + * @uic: UI controller to use for the menus. + * + * Sets up the #GalView menus for a memos control. This function should be + * called from the Bonobo control activation callback for this memos control. + * Also, the menus should be discarded using e_memos_discard_view_menus(). + */ +void +e_memos_setup_view_menus (EMemos *memos, BonoboUIComponent *uic) +{ + EMemosPrivate *priv; + GalViewFactory *factory; + ETableSpecification *spec; + char *dir; + static GalViewCollection *collection = NULL; + + g_return_if_fail (memos != NULL); + g_return_if_fail (E_IS_MEMOS (memos)); + g_return_if_fail (uic != NULL); + g_return_if_fail (BONOBO_IS_UI_COMPONENT (uic)); + + priv = memos->priv; + + g_return_if_fail (priv->view_instance == NULL); + + g_assert (priv->view_instance == NULL); + g_assert (priv->view_menus == NULL); + + /* Create the view instance */ + + if (collection == NULL) { + collection = gal_view_collection_new (); + + gal_view_collection_set_title (collection, _("Memos")); + + dir = g_build_filename (memos_component_peek_base_directory (memos_component_peek ()), + "memos", "views", NULL); + + gal_view_collection_set_storage_directories (collection, + EVOLUTION_GALVIEWSDIR "/memos/", + dir); + g_free (dir); + + /* Create the views */ + + spec = e_table_specification_new (); + e_table_specification_load_from_file (spec, + EVOLUTION_ETSPECDIR "/e-memo-table.etspec"); + + factory = gal_view_factory_etable_new (spec); + g_object_unref (spec); + gal_view_collection_add_factory (collection, factory); + g_object_unref (factory); + + /* Load the collection and create the menus */ + + gal_view_collection_load (collection); + } + + priv->view_instance = gal_view_instance_new (collection, NULL); + + priv->view_menus = gal_view_menus_new (priv->view_instance); + gal_view_menus_apply (priv->view_menus, uic, NULL); + g_signal_connect (priv->view_instance, "display_view", G_CALLBACK (display_view_cb), memos); + display_view_cb (priv->view_instance, gal_view_instance_get_current_view (priv->view_instance), memos); +} + +/** + * e_memos_discard_view_menus: + * @memos: A memos widget. + * + * Discards the #GalView menus used by a memos control. This function should be + * called from the Bonobo control deactivation callback for this memos control. + * The menus should have been set up with e_memos_setup_view_menus(). + **/ +void +e_memos_discard_view_menus (EMemos *memos) +{ + EMemosPrivate *priv; + + g_return_if_fail (memos != NULL); + g_return_if_fail (E_IS_MEMOS (memos)); + + priv = memos->priv; + + g_return_if_fail (priv->view_instance != NULL); + + g_assert (priv->view_instance != NULL); + g_assert (priv->view_menus != NULL); + + g_object_unref (priv->view_instance); + priv->view_instance = NULL; + + g_object_unref (priv->view_menus); + priv->view_menus = NULL; +} + +/** + * e_memos_get_calendar_table: + * @memos: A memos widget. + * + * Queries the #EMemoTable contained in a memos widget. + * + * Return value: The #EMemoTable that the memos widget uses to display its + * information. + **/ +EMemoTable * +e_memos_get_calendar_table (EMemos *memos) +{ + EMemosPrivate *priv; + + g_return_val_if_fail (memos != NULL, NULL); + g_return_val_if_fail (E_IS_MEMOS (memos), NULL); + + priv = memos->priv; + return E_MEMO_TABLE (priv->memos_view); +} diff --git a/calendar/gui/e-memos.h b/calendar/gui/e-memos.h new file mode 100644 index 0000000000..df4b6b1052 --- /dev/null +++ b/calendar/gui/e-memos.h @@ -0,0 +1,87 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* e-memos.h + * + * Copyright (C) 2001 Ximian, Inc. + * Copyright (C) 2001 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + * + * Authors: Federico Mena Quintero <federico@ximian.com> + * Damon Chaplin <damon@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + */ + +#ifndef _E_MEMOS_H_ +#define _E_MEMOS_H_ + +#include <bonobo/bonobo-ui-component.h> +#include <gtk/gtktable.h> +#include <libedataserver/e-source.h> +#include <libecal/e-cal.h> +#include "e-memo-table.h" + +#define E_TYPE_MEMOS (e_memos_get_type ()) +#define E_MEMOS(obj) (GTK_CHECK_CAST ((obj), E_TYPE_MEMOS, EMemos)) +#define E_MEMOS_CLASS(klass) (GTK_CHECK_CAST_CLASS ((klass), E_TYPE_MEMOS, \ + EMemosClass)) +#define E_IS_MEMOS(obj) (GTK_CHECK_TYPE ((obj), E_TYPE_MEMOS)) +#define E_IS_MEMOS_CLASS(klass) (GTK_CHECK_CLASS_TYPE ((klass), E_TYPE_MEMOS)) + +typedef struct _EMemos EMemos; +typedef struct _EMemosClass EMemosClass; +typedef struct _EMemosPrivate EMemosPrivate; + +struct _EMemos { + GtkTable table; + + /* Private data */ + EMemosPrivate *priv; +}; + +struct _EMemosClass { + GtkTableClass parent_class; + + /* Notification signals */ + void (* selection_changed) (EMemos *memos, int n_selected); + void (* source_added) (EMemos *memos, ESource *source); + void (* source_removed) (EMemos *memos, ESource *source); +}; + + +GtkType e_memos_get_type (void); +GtkWidget *e_memos_construct (EMemos *memos); + +GtkWidget *e_memos_new (void); + +void e_memos_set_ui_component (EMemos *memos, + BonoboUIComponent *ui_component); + +gboolean e_memos_add_memo_source (EMemos *memos, ESource *source); +gboolean e_memos_remove_memo_source (EMemos *memos, ESource *source); +gboolean e_memos_set_default_source (EMemos *memos, ESource *source); +ECal *e_memos_get_default_client (EMemos *memos); + +void e_memos_open_memo (EMemos *memos); +void e_memos_new_memo (EMemos *memos); +void e_memos_complete_selected (EMemos *memos); +void e_memos_delete_selected (EMemos *memos); + + +void e_memos_setup_view_menus (EMemos *memos, BonoboUIComponent *uic); +void e_memos_discard_view_menus (EMemos *memos); + +EMemoTable *e_memos_get_calendar_table (EMemos *memos); + +#endif /* _E_MEMOS_H_ */ diff --git a/calendar/gui/main.c b/calendar/gui/main.c index e8bfbafca3..44a880f120 100644 --- a/calendar/gui/main.c +++ b/calendar/gui/main.c @@ -44,6 +44,7 @@ #include "itip-bonobo-control.h" #include "tasks-control.h" #include "tasks-component.h" +#include "memos-component.h" #include <e-util/e-plugin.h> #include <e-util/e-import.h> @@ -57,6 +58,7 @@ #define CALENDAR_COMPONENT_ID "OAFIID:GNOME_Evolution_Calendar_Component:" BASE_VERSION #define TASKS_COMPONENT_ID "OAFIID:GNOME_Evolution_Tasks_Component:" BASE_VERSION +#define MEMOS_COMPONENT_ID "OAFIID:GNOME_Evolution_Memos_Component:" BASE_VERSION #define ITIP_CONTROL_ID "OAFIID:GNOME_Evolution_Calendar_iTip_Control:" BASE_VERSION #define CONFIG_CONTROL_ID "OAFIID:GNOME_Evolution_Calendar_ConfigControl:" BASE_VERSION #define COMP_EDITOR_FACTORY_ID "OAFIID:GNOME_Evolution_Calendar_CompEditorFactory:" BASE_VERSION @@ -179,6 +181,10 @@ factory (BonoboGenericFactory *factory, BonoboObject *object = BONOBO_OBJECT (tasks_component_peek ()); bonobo_object_ref (object); return object; + } else if (strcmp (component_id, MEMOS_COMPONENT_ID) == 0){ + BonoboObject *object = BONOBO_OBJECT (memos_component_peek ()); + bonobo_object_ref (object); + return object; } else if (strcmp (component_id, ITIP_CONTROL_ID) == 0) return BONOBO_OBJECT (itip_bonobo_control_new ()); else if (strcmp (component_id, CONFIG_CONTROL_ID) == 0) diff --git a/calendar/gui/memos-component.c b/calendar/gui/memos-component.c new file mode 100644 index 0000000000..ab9719f426 --- /dev/null +++ b/calendar/gui/memos-component.c @@ -0,0 +1,1330 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* memos-component.c + * + * Copyright (C) 2003 Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + * + * Authors: Rodrigo Moya <rodrigo@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <errno.h> +#include <string.h> +#include <bonobo/bonobo-control.h> +#include <bonobo/bonobo-i18n.h> +#include <bonobo/bonobo-exception.h> +#include <gconf/gconf-client.h> +#include <libecal/e-cal.h> +#include <libedataserverui/e-source-selector.h> +#include <shell/e-user-creatable-items-handler.h> +#include "e-cal-model.h" +#include "e-memos.h" +#include "memos-component.h" +#include "memos-control.h" +#include "e-comp-editor-registry.h" +#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" +#include "dialogs/copy-source-dialog.h" +#include "dialogs/memo-editor.h" +#include "widgets/misc/e-info-label.h" +#include "e-util/e-icon-factory.h" +#include "e-util/e-error.h" +#include "calendar-component.h" + +#define CREATE_MEMO_ID "memo" +#define CREATE_MEMO_LIST_ID "memo-list" + +enum DndTargetType { + DND_TARGET_TYPE_CALENDAR_LIST, +}; +#define CALENDAR_TYPE "text/calendar" +#define XCALENDAR_TYPE "text/x-calendar" +#define WEB_BASE_URI "webcal://" +#define PERSONAL_RELATIVE_URI "system" + +static GtkTargetEntry drag_types[] = { + { CALENDAR_TYPE, 0, DND_TARGET_TYPE_CALENDAR_LIST }, + { XCALENDAR_TYPE, 0, DND_TARGET_TYPE_CALENDAR_LIST } +}; +static gint num_drag_types = sizeof(drag_types) / sizeof(drag_types[0]); + +#define PARENT_TYPE bonobo_object_get_type () + +static BonoboObjectClass *parent_class = NULL; + +/* Memos should have their own registry */ +extern ECompEditorRegistry *comp_editor_registry; + + +typedef struct _MemosComponentView +{ + ESourceList *source_list; + + GSList *source_selection; + + EMemos *memos; + ETable *table; + ETableModel *model; + + EInfoLabel *info_label; + GtkWidget *source_selector; + + BonoboControl *view_control; + BonoboControl *sidebar_control; + BonoboControl *statusbar_control; + + GList *notifications; + + EUserCreatableItemsHandler *creatable_items_handler; + + EActivityHandler *activity_handler; +} MemosComponentView; + +struct _MemosComponentPrivate { + char *base_directory; + char *config_directory; + + ESourceList *source_list; + GSList *source_selection; + + GList *views; + + ECal *create_ecal; + + GList *notifications; +}; + +/* #define d(x) x */ +#define d(x) + +static void +ensure_sources (MemosComponent *component) +{ + GSList *groups; + ESourceList *source_list; + ESourceGroup *group; + ESourceGroup *on_this_computer; + ESourceGroup *on_the_web; + ESource *personal_source; + char *base_uri, *base_uri_proto; + + on_this_computer = NULL; + on_the_web = NULL; + personal_source = NULL; + + if (!e_cal_get_sources (&source_list, E_CAL_SOURCE_TYPE_JOURNAL, NULL)) { + g_warning ("Could not get memo source list from GConf!"); + return; + } + + base_uri = g_build_filename (memos_component_peek_base_directory (component), + "memos", "local", + NULL); + + base_uri_proto = g_strconcat ("file://", base_uri, NULL); + + groups = e_source_list_peek_groups (source_list); + if (groups) { + /* groups are already there, we need to search for things... */ + GSList *g; + + for (g = groups; g; g = g->next) { + + group = E_SOURCE_GROUP (g->data); + + /* compare only file:// part. If user home dir name changes we do not want to create + one more group */ + + if (!on_this_computer && !strncmp (base_uri_proto, e_source_group_peek_base_uri (group), 7)) + on_this_computer = group; + else if (!on_the_web && !strcmp (WEB_BASE_URI, e_source_group_peek_base_uri (group))) + on_the_web = group; + } + } + + if (on_this_computer) { + /* make sure "Personal" shows up as a source under + this group */ + GSList *sources = e_source_group_peek_sources (on_this_computer); + GSList *s; + for (s = sources; s; s = s->next) { + ESource *source = E_SOURCE (s->data); + if (!strcmp (PERSONAL_RELATIVE_URI, e_source_peek_relative_uri (source))) { + personal_source = source; + break; + } + } + /* Make sure we have the correct base uri. This can change when user's + homedir name changes */ + if (strcmp (base_uri_proto, e_source_group_peek_base_uri (on_this_computer))) { + e_source_group_set_base_uri (on_this_computer, base_uri_proto); + + /* *sigh* . We shouldn't need this sync call here as set_base_uri + call results in synching to gconf, but that happens in idle loop + and too late to prevent user seeing "Can not Open ... because of invalid uri" error.*/ + e_source_list_sync (source_list,NULL); + } + } + else { + /* create the local source group */ + group = e_source_group_new (_("On This Computer"), base_uri_proto); + e_source_list_add_group (source_list, group, -1); + + on_this_computer = group; + } + + if (!personal_source) { + /* Create the default Person addressbook */ + ESource *source = e_source_new (_("Personal"), PERSONAL_RELATIVE_URI); + e_source_group_add_source (on_this_computer, source, -1); + + if (!calendar_config_get_primary_memos () && !calendar_config_get_memos_selected ()) { + GSList selected; + + calendar_config_set_primary_memos (e_source_peek_uid (source)); + + selected.data = (gpointer)e_source_peek_uid (source); + selected.next = NULL; + calendar_config_set_memos_selected (&selected); + } + + e_source_set_color (source, 0xBECEDD); + personal_source = source; + } + + if (!on_the_web) { + /* Create the LDAP source group */ + group = e_source_group_new (_("On The Web"), WEB_BASE_URI); + e_source_list_add_group (source_list, group, -1); + + on_the_web = group; + } + + component->priv->source_list = source_list; + g_free (base_uri_proto); + g_free (base_uri); +} + +/* Utility functions. */ +/* FIXME Some of these are duplicated from calendar-component.c */ +static gboolean +is_in_selection (GSList *selection, ESource *source) +{ + GSList *l; + + for (l = selection; l; l = l->next) { + ESource *selected_source = l->data; + + if (!strcmp (e_source_peek_uid (selected_source), e_source_peek_uid (source))) + return TRUE; + } + + return FALSE; +} + +static gboolean +is_in_uids (GSList *uids, ESource *source) +{ + GSList *l; + + for (l = uids; l; l = l->next) { + const char *uid = l->data; + + if (!strcmp (uid, e_source_peek_uid (source))) + return TRUE; + } + + return FALSE; +} + +static void +update_uris_for_selection (MemosComponentView *component_view) +{ + GSList *selection, *l, *uids_selected = NULL; + + selection = e_source_selector_get_selection (E_SOURCE_SELECTOR (component_view->source_selector)); + + for (l = component_view->source_selection; l; l = l->next) { + ESource *old_selected_source = l->data; + + if (!is_in_selection (selection, old_selected_source)) + e_memos_remove_memo_source (component_view->memos, old_selected_source); + } + + for (l = selection; l; l = l->next) { + ESource *selected_source = l->data; + + e_memos_add_memo_source (component_view->memos, selected_source); + uids_selected = g_slist_append (uids_selected, (char *)e_source_peek_uid (selected_source)); + } + + e_source_selector_free_selection (component_view->source_selection); + component_view->source_selection = selection; + + /* Save the selection for next time we start up */ + calendar_config_set_memos_selected (uids_selected); + g_slist_free (uids_selected); +} + +static void +update_uri_for_primary_selection (MemosComponentView *component_view) +{ + ESource *source; + EMemoTable *cal_table; + ETable *etable; + + source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector)); + if (!source) + return; + + /* Set the default */ + e_memos_set_default_source (component_view->memos, source); + + cal_table = e_memos_get_calendar_table (component_view->memos); + etable = e_memo_table_get_table (cal_table); + + memos_control_sensitize_commands (component_view->view_control, component_view->memos, e_table_selected_count (etable)); + + /* Save the selection for next time we start up */ + calendar_config_set_primary_memos (e_source_peek_uid (source)); +} + +static void +update_selection (MemosComponentView *component_view) +{ + GSList *selection, *uids_selected, *l; + + d(g_message("memos-component.c: update_selection called");) + + /* Get the selection in gconf */ + uids_selected = calendar_config_get_memos_selected (); + + /* Remove any that aren't there any more */ + selection = e_source_selector_get_selection (E_SOURCE_SELECTOR (component_view->source_selector)); + + for (l = selection; l; l = l->next) { + ESource *source = l->data; + + if (!is_in_uids (uids_selected, source)) + e_source_selector_unselect_source (E_SOURCE_SELECTOR (component_view->source_selector), source); + } + + e_source_selector_free_selection (selection); + + /* Make sure the whole selection is there */ + for (l = uids_selected; l; l = l->next) { + char *uid = l->data; + ESource *source; + + source = e_source_list_peek_source_by_uid (component_view->source_list, uid); + if (source) + e_source_selector_select_source (E_SOURCE_SELECTOR (component_view->source_selector), source); + + g_free (uid); + } + g_slist_free (uids_selected); +} + +static void +update_primary_selection (MemosComponentView *component_view) +{ + ESource *source = NULL; + char *uid; + + uid = calendar_config_get_primary_memos (); + if (uid) { + source = e_source_list_peek_source_by_uid (component_view->source_list, uid); + g_free (uid); + } + + if (source) { + e_source_selector_set_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector), source); + } else { + /* Try to create a default if there isn't one */ + source = e_source_list_peek_source_any (component_view->source_list); + if (source) + e_source_selector_set_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector), source); + } + +} + + +/* Callbacks. */ +/* TODO: doesn't work! */ +static void +copy_memo_list_cb (EPopup *ep, EPopupItem *pitem, void *data) +{ + MemosComponentView *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(ep->target->widget)), selected_source, E_CAL_SOURCE_TYPE_JOURNAL); +} + +static void +delete_memo_list_cb (EPopup *ep, EPopupItem *pitem, void *data) +{ + MemosComponentView *component_view = data; + ESource *selected_source; + ECal *cal; + char *uri; + + selected_source = e_source_selector_peek_primary_selection (E_SOURCE_SELECTOR (component_view->source_selector)); + if (!selected_source) + return; + + if (e_error_run((GtkWindow *)gtk_widget_get_toplevel(ep->target->widget), + "calendar:prompt-delete-memo-list", e_source_peek_name(selected_source)) != GTK_RESPONSE_YES) + return; + + /* first, ask the backend to remove the memo list */ + uri = e_source_get_uri (selected_source); + cal = e_cal_model_get_client_for_uri ( + e_memo_table_get_model (E_MEMO_TABLE (e_memos_get_calendar_table (component_view->memos))), + uri); + if (!cal) + cal = e_cal_new_from_uri (uri, E_CAL_SOURCE_TYPE_JOURNAL); + 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_memos_remove_memo_source (component_view->memos, 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); + } + } +} + +static void +new_memo_list_cb (EPopup *ep, EPopupItem *pitem, void *data) +{ + calendar_setup_new_memo_list (GTK_WINDOW (gtk_widget_get_toplevel(ep->target->widget))); +} + +static void +edit_memo_list_cb (EPopup *ep, EPopupItem *pitem, void *data) +{ + MemosComponentView *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_memo_list (GTK_WINDOW (gtk_widget_get_toplevel(ep->target->widget)), selected_source); +} + +static EPopupItem emc_source_popups[] = { + { E_POPUP_ITEM, "10.new", N_("New Memo List"), new_memo_list_cb, NULL, "stock_notes", 0, 0 }, + { E_POPUP_ITEM, "15.copy", N_("Copy"), copy_memo_list_cb, NULL, "stock_folder-copy", 0, E_CAL_POPUP_SOURCE_PRIMARY }, + { E_POPUP_ITEM, "20.delete", N_("Delete"), delete_memo_list_cb, NULL, "stock_delete", 0, E_CAL_POPUP_SOURCE_USER|E_CAL_POPUP_SOURCE_PRIMARY }, + { E_POPUP_ITEM, "30.properties", N_("Properties..."), edit_memo_list_cb, NULL, "stock_folder-properties", 0, E_CAL_POPUP_SOURCE_PRIMARY }, +}; + +static void +emc_source_popup_free(EPopup *ep, GSList *list, void *data) +{ + g_slist_free(list); +} + +static gboolean +popup_event_cb(ESourceSelector *selector, ESource *insource, GdkEventButton *event, MemosComponentView *component_view) +{ + ECalPopup *ep; + ECalPopupTargetSource *t; + GSList *menus = NULL; + int i; + GtkMenu *menu; + + /** @HookPoint-ECalPopup: Memos Source Selector Context Menu + * @Id: org.gnome.evolution.memos.source.popup + * @Class: org.gnome.evolution.calendar.popup:1.0 + * @Target: ECalPopupTargetSource + * + * The context menu on the source selector in the memos window. + */ + ep = e_cal_popup_new("org.gnome.evolution.memos.source.popup"); + t = e_cal_popup_target_new_source(ep, selector); + t->target.widget = (GtkWidget *)component_view->memos; + + for (i=0;i<sizeof(emc_source_popups)/sizeof(emc_source_popups[0]);i++) + menus = g_slist_prepend(menus, &emc_source_popups[i]); + + e_popup_add_items((EPopup *)ep, menus, NULL,emc_source_popup_free, component_view); + + menu = e_popup_create_menu_once((EPopup *)ep, (EPopupTarget *)t, 0); + gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event?event->button:0, event?event->time:gtk_get_current_event_time()); + + return TRUE; +} + +static void +source_selection_changed_cb (ESourceSelector *selector, MemosComponentView *component_view) +{ + update_uris_for_selection (component_view); +} + +static void +primary_source_selection_changed_cb (ESourceSelector *selector, MemosComponentView *component_view) +{ + update_uri_for_primary_selection (component_view); +} + +static void +source_added_cb (EMemos *memos, ESource *source, MemosComponentView *component_view) +{ + e_source_selector_select_source (E_SOURCE_SELECTOR (component_view->source_selector), source); +} + +static void +source_removed_cb (EMemos *memos, ESource *source, MemosComponentView *component_view) +{ + e_source_selector_unselect_source (E_SOURCE_SELECTOR (component_view->source_selector), source); +} + +static void +set_info (MemosComponentView *component_view) +{ + GString *message = g_string_new (""); + int rows, selected_rows; + + rows = e_table_model_row_count (component_view->model); + selected_rows = e_table_selected_count (component_view->table); + + g_string_append_printf(message, ngettext("%d memo", "%d memos", rows), rows); + if (selected_rows > 0) + g_string_append_printf(message, ngettext(", %d selected", ", %d selected", selected_rows), selected_rows); + + e_info_label_set_info (component_view->info_label, _("Memos"), message->str); + + g_string_free (message, TRUE); +} + +static void +table_selection_change_cb (ETableModel *etm, MemosComponentView *component_view) +{ + set_info (component_view); +} + +static void +model_changed_cb (ETableModel *etm, MemosComponentView *component_view) +{ + set_info (component_view); +} + +static void +model_rows_inserted_cb (ETableModel *etm, int row, int count, MemosComponentView *component_view) +{ + set_info (component_view); +} + +static void +model_rows_deleted_cb (ETableModel *etm, int row, int count, MemosComponentView *component_view) +{ + set_info (component_view); +} + +/* Evolution::Component CORBA methods */ + +static void +impl_upgradeFromVersion (PortableServer_Servant servant, + CORBA_short major, + CORBA_short minor, + CORBA_short revision, + CORBA_Environment *ev) +{ + GError *err = NULL; + MemosComponent *component = MEMOS_COMPONENT (bonobo_object_from_servant (servant)); + + if (!migrate_memos(component, major, minor, revision, &err)) { + GNOME_Evolution_Component_UpgradeFailed *failedex; + + failedex = GNOME_Evolution_Component_UpgradeFailed__alloc(); + failedex->what = CORBA_string_dup(_("Failed upgrading memos.")); + failedex->why = CORBA_string_dup(err->message); + CORBA_exception_set(ev, CORBA_USER_EXCEPTION, ex_GNOME_Evolution_Component_UpgradeFailed, failedex); + } + + if (err) + g_error_free(err); +} + +static gboolean +selector_tree_drag_drop (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + guint time, + CalendarComponent *component) +{ + GtkTreeViewColumn *column; + int cell_x; + int cell_y; + GtkTreePath *path; + GtkTreeModel *model; + GtkTreeIter iter; + gpointer data; + + if (!gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (widget), x, y, &path, + &column, &cell_x, &cell_y)) + return FALSE; + + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + + if (!gtk_tree_model_get_iter (model, &iter, path)) { + gtk_tree_path_free (path); + return FALSE; + } + + gtk_tree_model_get (model, &iter, 0, &data, -1); + + if (E_IS_SOURCE_GROUP (data)) { + g_object_unref (data); + gtk_tree_path_free (path); + return FALSE; + } + + gtk_tree_path_free (path); + return TRUE; +} + +static gboolean +selector_tree_drag_motion (GtkWidget *widget, + GdkDragContext *context, + int x, + int y, + guint time, + gpointer user_data) +{ + GtkTreePath *path = NULL; + gpointer data = NULL; + GtkTreeViewDropPosition pos; + GtkTreeModel *model; + GtkTreeIter iter; + GdkDragAction action = GDK_ACTION_DEFAULT; + + if (!gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), + x, y, &path, &pos)) + goto finish; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + + if (!gtk_tree_model_get_iter (model, &iter, path)) + goto finish; + + gtk_tree_model_get (model, &iter, 0, &data, -1); + + if (E_IS_SOURCE_GROUP (data) || e_source_get_readonly (data)) + goto finish; + + gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), path, GTK_TREE_VIEW_DROP_INTO_OR_BEFORE); + action = context->suggested_action; + + finish: + if (path) + gtk_tree_path_free (path); + if (data) + g_object_unref (data); + + gdk_drag_status (context, action, time); + return TRUE; +} + +static gboolean +update_single_object (ECal *client, icalcomponent *icalcomp) +{ + char *uid; + icalcomponent *tmp_icalcomp; + + d(g_message("memos-component.c: update_single_object called");) + + uid = (char *) icalcomponent_get_uid (icalcomp); + + if (e_cal_get_object (client, uid, NULL, &tmp_icalcomp, NULL)) + return e_cal_modify_object (client, icalcomp, CALOBJ_MOD_ALL, NULL); + + return e_cal_create_object (client, icalcomp, &uid, NULL); +} + +static gboolean +update_objects (ECal *client, icalcomponent *icalcomp) +{ + icalcomponent *subcomp; + icalcomponent_kind kind; + + d(g_message("memos-component.c: update_objects called");) + + kind = icalcomponent_isa (icalcomp); + if (kind == ICAL_VJOURNAL_COMPONENT) + return update_single_object (client, icalcomp); + else if (kind != ICAL_VCALENDAR_COMPONENT) + return FALSE; + + subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT); + while (subcomp) { + gboolean success; + + kind = icalcomponent_isa (subcomp); + if (kind == ICAL_VTIMEZONE_COMPONENT) { + icaltimezone *zone; + + zone = icaltimezone_new (); + icaltimezone_set_component (zone, subcomp); + + success = e_cal_add_timezone (client, zone, NULL); + icaltimezone_free (zone, 1); + if (!success) + return success; + } else if (kind == ICAL_VJOURNAL_COMPONENT) { + success = update_single_object (client, subcomp); + if (!success) + return success; + } + + subcomp = icalcomponent_get_next_component (icalcomp, ICAL_ANY_COMPONENT); + } + + return TRUE; +} + +static void +selector_tree_drag_data_received (GtkWidget *widget, + GdkDragContext *context, + gint x, + gint y, + GtkSelectionData *data, + guint info, + guint time, + gpointer user_data) +{ + GtkTreePath *path = NULL; + GtkTreeViewDropPosition pos; + gpointer source = NULL; + GtkTreeModel *model; + GtkTreeIter iter; + gboolean success = FALSE; + icalcomponent *icalcomp = NULL; + ECal *client = NULL; + + if (!gtk_tree_view_get_dest_row_at_pos (GTK_TREE_VIEW (widget), + x, y, &path, &pos)) + goto finish; + + model = gtk_tree_view_get_model (GTK_TREE_VIEW (widget)); + + if (!gtk_tree_model_get_iter (model, &iter, path)) + goto finish; + + + gtk_tree_model_get (model, &iter, 0, &source, -1); + + if (E_IS_SOURCE_GROUP (source) || e_source_get_readonly (source)) + goto finish; + + icalcomp = icalparser_parse_string (data->data); + + if (icalcomp) { + char * uid; + + /* FIXME deal with GDK_ACTION_ASK */ + if (context->action == GDK_ACTION_COPY) { + uid = e_cal_component_gen_uid (); + icalcomponent_set_uid (icalcomp, uid); + } + + client = auth_new_cal_from_source (source, + E_CAL_SOURCE_TYPE_JOURNAL); + + if (client) { + if (e_cal_open (client, TRUE, NULL)) { + success = TRUE; + update_objects (client, icalcomp); + } + + g_object_unref (client); + } + + icalcomponent_free (icalcomp); + } + + finish: + if (source) + g_object_unref (source); + if (path) + gtk_tree_path_free (path); + + gtk_drag_finish (context, success, context->action == GDK_ACTION_MOVE, time); +} + +static void +selector_tree_drag_leave (GtkWidget *widget, GdkDragContext *context, guint time, gpointer data) +{ + gtk_tree_view_set_drag_dest_row(GTK_TREE_VIEW (widget), + NULL, GTK_TREE_VIEW_DROP_BEFORE); +} + + +static void +control_activate_cb (BonoboControl *control, gboolean activate, gpointer data) +{ + MemosComponentView *component_view = data; + + if (activate) { + BonoboUIComponent *uic; + uic = bonobo_control_get_ui_component (component_view->view_control); + + e_user_creatable_items_handler_activate (component_view->creatable_items_handler, uic); + } +} + +static void +config_create_ecal_changed_cb (GConfClient *client, guint id, GConfEntry *entry, gpointer data) +{ + MemosComponent *component = data; + MemosComponentPrivate *priv; + + priv = component->priv; + + g_object_unref (priv->create_ecal); + priv->create_ecal = NULL; + + priv->notifications = g_list_remove (priv->notifications, GUINT_TO_POINTER (id)); +} + +static ECal * +setup_create_ecal (MemosComponent *component, MemosComponentView *component_view) +{ + MemosComponentPrivate *priv; + ESource *source = NULL; + char *uid; + guint not; + + priv = component->priv; + + if (component_view) { + ECal *default_ecal; + + default_ecal = e_memos_get_default_client (component_view->memos); + if (default_ecal) + return default_ecal; + } + + if (priv->create_ecal) + return priv->create_ecal; + + /* Get the current primary calendar, or try to set one if it doesn't already exist */ + uid = calendar_config_get_primary_memos (); + if (uid) { + source = e_source_list_peek_source_by_uid (priv->source_list, uid); + g_free (uid); + + priv->create_ecal = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_JOURNAL); + } + + if (!priv->create_ecal) { + /* Try to create a default if there isn't one */ + source = e_source_list_peek_source_any (priv->source_list); + if (source) + priv->create_ecal = auth_new_cal_from_source (source, E_CAL_SOURCE_TYPE_JOURNAL); + } + + if (priv->create_ecal) { + + if (!e_cal_open (priv->create_ecal, FALSE, NULL)) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, + _("Unable to open the memo list '%s' for creating events and meetings"), + e_source_peek_name (source)); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + return NULL; + } + + } else { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, + GTK_MESSAGE_WARNING, GTK_BUTTONS_OK, + _("There is no calendar available for creating memos")); + gtk_dialog_run (GTK_DIALOG (dialog)); + gtk_widget_destroy (dialog); + + return NULL; + } + + /* Handle the fact it may change on us */ + not = calendar_config_add_notification_primary_memos (config_create_ecal_changed_cb, + component); + priv->notifications = g_list_prepend (priv->notifications, GUINT_TO_POINTER (not)); + + /* Save the primary source for use elsewhere */ + calendar_config_set_primary_memos (e_source_peek_uid (source)); + + return priv->create_ecal ; +} + +static gboolean +create_new_memo (MemosComponent *memo_component, gboolean is_assigned, MemosComponentView *component_view) +{ + ECal *ecal; + MemosComponentPrivate *priv; + ECalComponent *comp; + MemoEditor *editor; + + priv = memo_component->priv; + + ecal = setup_create_ecal (memo_component, component_view); + if (!ecal) + return FALSE; + + editor = memo_editor_new (ecal); + comp = cal_comp_memo_new_with_defaults (ecal); + + comp_editor_edit_comp (COMP_EDITOR (editor), comp); + comp_editor_focus (COMP_EDITOR (editor)); + + e_comp_editor_registry_add (comp_editor_registry, COMP_EDITOR (editor), TRUE); + + return TRUE; +} + +static void +create_local_item_cb (EUserCreatableItemsHandler *handler, const char *item_type_name, void *data) +{ + MemosComponent *memos_component = data; + MemosComponentPrivate *priv; + MemosComponentView *component_view = NULL; + GList *l; + + priv = memos_component->priv; + + for (l = priv->views; l; l = l->next) { + component_view = l->data; + + if (component_view->creatable_items_handler == handler) + break; + + component_view = NULL; + } + + if (strcmp (item_type_name, CREATE_MEMO_ID) == 0) { + create_new_memo (memos_component, FALSE, component_view); + } + else if (strcmp (item_type_name, CREATE_MEMO_LIST_ID) == 0) { + calendar_setup_new_memo_list (GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (component_view->memos)))); + } +} + +static MemosComponentView * +create_component_view (MemosComponent *memos_component) +{ + MemosComponentPrivate *priv; + MemosComponentView *component_view; + GtkWidget *selector_scrolled_window, *vbox; + GtkWidget *statusbar_widget; + AtkObject *a11y; + + priv = memos_component->priv; + + /* Create the calendar component view */ + component_view = g_new0 (MemosComponentView, 1); + + /* Add the source lists */ + component_view->source_list = g_object_ref (priv->source_list); + + /* Create sidebar selector */ + component_view->source_selector = e_source_selector_new (memos_component->priv->source_list); + e_source_selector_set_select_new ((ESourceSelector *)component_view->source_selector, TRUE); + a11y = gtk_widget_get_accessible (GTK_WIDGET (component_view->source_selector)); + atk_object_set_name (a11y, _("Memo Source Selector")); + + g_signal_connect (component_view->source_selector, "drag-motion", G_CALLBACK (selector_tree_drag_motion), + memos_component); + g_signal_connect (component_view->source_selector, "drag-leave", G_CALLBACK (selector_tree_drag_leave), + memos_component); + g_signal_connect (component_view->source_selector, "drag-drop", G_CALLBACK (selector_tree_drag_drop), + memos_component); + g_signal_connect (component_view->source_selector, "drag-data-received", + G_CALLBACK (selector_tree_drag_data_received), memos_component); + + gtk_drag_dest_set(component_view->source_selector, GTK_DEST_DEFAULT_ALL, drag_types, + num_drag_types, GDK_ACTION_COPY | GDK_ACTION_MOVE); + + gtk_widget_show (component_view->source_selector); + + selector_scrolled_window = gtk_scrolled_window_new (NULL, NULL); + gtk_container_add (GTK_CONTAINER (selector_scrolled_window), component_view->source_selector); + gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (selector_scrolled_window), + GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); + gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (selector_scrolled_window), + GTK_SHADOW_IN); + gtk_widget_show (selector_scrolled_window); + + component_view->info_label = (EInfoLabel *)e_info_label_new("stock_insert-note"); + e_info_label_set_info(component_view->info_label, _("Memos"), ""); + gtk_widget_show (GTK_WIDGET (component_view->info_label)); + + vbox = gtk_vbox_new(FALSE, 0); + gtk_box_pack_start(GTK_BOX (vbox), GTK_WIDGET (component_view->info_label), FALSE, TRUE, 0); + gtk_box_pack_start(GTK_BOX (vbox), selector_scrolled_window, TRUE, TRUE, 0); + gtk_widget_show (vbox); + + component_view->sidebar_control = bonobo_control_new (vbox); + + /* Create main view */ + component_view->view_control = memos_control_new (); + if (!component_view->view_control) { + /* FIXME free memory */ + + return NULL; + } + + component_view->memos = (EMemos *) bonobo_control_get_widget (component_view->view_control); + component_view->table = e_memo_table_get_table (e_memos_get_calendar_table (component_view->memos)); + component_view->model = E_TABLE_MODEL (e_memo_table_get_model (e_memos_get_calendar_table (component_view->memos))); + + /* This signal is thrown if backends die - we update the selector */ + g_signal_connect (component_view->memos, "source_added", + G_CALLBACK (source_added_cb), component_view); + g_signal_connect (component_view->memos, "source_removed", + G_CALLBACK (source_removed_cb), component_view); + + /* Create status bar */ + statusbar_widget = e_task_bar_new (); + component_view->activity_handler = e_activity_handler_new (); + e_activity_handler_attach_task_bar (component_view->activity_handler, E_TASK_BAR (statusbar_widget)); + gtk_widget_show (statusbar_widget); + + component_view->statusbar_control = bonobo_control_new (statusbar_widget); + + e_memo_table_set_activity_handler (e_memos_get_calendar_table (component_view->memos), component_view->activity_handler); + + /* connect after setting the initial selections, or we'll get unwanted calls + to calendar_control_sensitize_calendar_commands */ + g_signal_connect (component_view->source_selector, "selection_changed", + 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, "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 ("memos", create_local_item_cb, memos_component); + g_signal_connect (component_view->view_control, "activate", G_CALLBACK (control_activate_cb), component_view); + + /* We use this to update the component information */ + set_info (component_view); + g_signal_connect (component_view->table, "selection_change", + G_CALLBACK (table_selection_change_cb), component_view); + g_signal_connect (component_view->model, "model_changed", + G_CALLBACK (model_changed_cb), component_view); + g_signal_connect (component_view->model, "model_rows_inserted", + G_CALLBACK (model_rows_inserted_cb), component_view); + g_signal_connect (component_view->model, "model_rows_deleted", + G_CALLBACK (model_rows_deleted_cb), component_view); + + /* Load the selection from the last run */ + update_selection (component_view); + update_primary_selection (component_view); + + return component_view; +} + +static void +destroy_component_view (MemosComponentView *component_view) +{ + GList *l; + + if (component_view->source_list) + g_object_unref (component_view->source_list); + + if (component_view->source_selection) + e_source_selector_free_selection (component_view->source_selection); + + for (l = component_view->notifications; l; l = l->next) + calendar_config_remove_notification (GPOINTER_TO_UINT (l->data)); + g_list_free (component_view->notifications); + + if (component_view->creatable_items_handler) + g_object_unref (component_view->creatable_items_handler); + + if (component_view->activity_handler) + g_object_unref (component_view->activity_handler); + + g_free (component_view); +} + +static void +view_destroyed_cb (gpointer data, GObject *where_the_object_was) +{ + MemosComponent *memos_component = data; + MemosComponentPrivate *priv; + GList *l; + + priv = memos_component->priv; + + for (l = priv->views; l; l = l->next) { + MemosComponentView *component_view = l->data; + + if (G_OBJECT (component_view->view_control) == where_the_object_was) { + priv->views = g_list_remove (priv->views, component_view); + destroy_component_view (component_view); + + break; + } + } +} + +static void +impl_createControls (PortableServer_Servant servant, + Bonobo_Control *corba_sidebar_control, + Bonobo_Control *corba_view_control, + Bonobo_Control *corba_statusbar_control, + CORBA_Environment *ev) +{ + MemosComponent *component = MEMOS_COMPONENT (bonobo_object_from_servant (servant)); + MemosComponentPrivate *priv; + MemosComponentView *component_view; + + priv = component->priv; + + /* Create the calendar component view */ + component_view = create_component_view (component); + if (!component_view) { + /* FIXME Should we describe the problem in a control? */ + bonobo_exception_set (ev, ex_GNOME_Evolution_Component_Failed); + + return; + } + + g_object_weak_ref (G_OBJECT (component_view->view_control), view_destroyed_cb, component); + priv->views = g_list_append (priv->views, component_view); + + /* Return the controls */ + *corba_sidebar_control = CORBA_Object_duplicate (BONOBO_OBJREF (component_view->sidebar_control), ev); + *corba_view_control = CORBA_Object_duplicate (BONOBO_OBJREF (component_view->view_control), ev); + *corba_statusbar_control = CORBA_Object_duplicate (BONOBO_OBJREF (component_view->statusbar_control), ev); +} + +static GNOME_Evolution_CreatableItemTypeList * +impl__get_userCreatableItems (PortableServer_Servant servant, + CORBA_Environment *ev) +{ + GNOME_Evolution_CreatableItemTypeList *list = GNOME_Evolution_CreatableItemTypeList__alloc (); + + list->_length = 2; + list->_maximum = list->_length; + list->_buffer = GNOME_Evolution_CreatableItemTypeList_allocbuf (list->_length); + + CORBA_sequence_set_release (list, FALSE); + + list->_buffer[0].id = CREATE_MEMO_ID; + list->_buffer[0].description = _("New memo"); + list->_buffer[0].menuDescription = _("_Memo"); + list->_buffer[0].tooltip = _("Create a new memo"); + list->_buffer[0].menuShortcut = 'o'; + list->_buffer[0].iconName = "stock_insert-note"; + list->_buffer[0].type = GNOME_Evolution_CREATABLE_OBJECT; + + list->_buffer[1].id = CREATE_MEMO_LIST_ID; + list->_buffer[1].description = _("New memo list"); + list->_buffer[1].menuDescription = _("Memo l_ist"); + list->_buffer[1].tooltip = _("Create a new memo list"); + list->_buffer[1].menuShortcut = 'i'; + list->_buffer[1].iconName = "stock_notes"; + list->_buffer[1].type = GNOME_Evolution_CREATABLE_FOLDER; + + return list; +} + +static void +impl_requestCreateItem (PortableServer_Servant servant, + const CORBA_char *item_type_name, + CORBA_Environment *ev) +{ + MemosComponent *memos_component = MEMOS_COMPONENT (bonobo_object_from_servant (servant)); + MemosComponentPrivate *priv; + + priv = memos_component->priv; + + if (strcmp (item_type_name, CREATE_MEMO_ID) == 0) { + if (!create_new_memo (memos_component, FALSE, NULL)) + bonobo_exception_set (ev, ex_GNOME_Evolution_Component_Failed); + } + else if (strcmp (item_type_name, CREATE_MEMO_LIST_ID) == 0) { + /* FIXME Should we use the last opened window? */ + calendar_setup_new_memo_list (NULL); + } + else { + bonobo_exception_set (ev, ex_GNOME_Evolution_Component_UnknownType); + } +} + +/* GObject methods. */ + +static void +impl_dispose (GObject *object) +{ + MemosComponent *memos_component = MEMOS_COMPONENT (object); + MemosComponentPrivate *priv = memos_component->priv; + GList *l; + + if (priv->source_list != NULL) { + g_object_unref (priv->source_list); + priv->source_list = NULL; + } + if (priv->source_selection != NULL) { + e_source_selector_free_selection (priv->source_selection); + priv->source_selection = NULL; + } + + if (priv->create_ecal) { + g_object_unref (priv->create_ecal); + priv->create_ecal = NULL; + } + + for (l = priv->views; l; l = l->next) { + MemosComponentView *component_view = l->data; + + g_object_weak_unref (G_OBJECT (component_view->view_control), view_destroyed_cb, memos_component); + } + g_list_free (priv->views); + priv->views = NULL; + + for (l = priv->notifications; l; l = l->next) + calendar_config_remove_notification (GPOINTER_TO_UINT (l->data)); + g_list_free (priv->notifications); + priv->notifications = NULL; + + (* G_OBJECT_CLASS (parent_class)->dispose) (object); +} + +static void +impl_finalize (GObject *object) +{ + MemosComponentPrivate *priv = MEMOS_COMPONENT (object)->priv; + GList *l; + + for (l = priv->views; l; l = l->next) { + MemosComponentView *component_view = l->data; + + destroy_component_view (component_view); + } + g_list_free (priv->views); + + g_free (priv->base_directory); + g_free (priv->config_directory); + g_free (priv); + + (* G_OBJECT_CLASS (parent_class)->finalize) (object); +} + +static void +memos_component_class_init (MemosComponentClass *klass) +{ + POA_GNOME_Evolution_Component__epv *epv = &klass->epv; + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + epv->upgradeFromVersion = impl_upgradeFromVersion; + epv->createControls = impl_createControls; + epv->_get_userCreatableItems = impl__get_userCreatableItems; + epv->requestCreateItem = impl_requestCreateItem; + + object_class->dispose = impl_dispose; + object_class->finalize = impl_finalize; +} + +static void +memos_component_init (MemosComponent *component, MemosComponentClass *klass) +{ + MemosComponentPrivate *priv; + + priv = g_new0 (MemosComponentPrivate, 1); + + printf("priv (MemosComponentnPrivate) == %p\n", priv); + + priv->base_directory = g_build_filename (g_get_home_dir (), ".evolution", NULL); + priv->config_directory = g_build_filename (g_get_home_dir (), + ".evolution", "memos", "config", + NULL); + + component->priv = priv; + ensure_sources (component); +} + +/* Public API */ + +MemosComponent * +memos_component_peek (void) +{ + static MemosComponent *component = NULL; + + if (component == NULL) { + component = g_object_new (memos_component_get_type (), NULL); + + if (e_mkdir_hier (component->priv->config_directory, 0777) != 0) { + g_warning (G_STRLOC ": Cannot create directory %s: %s", + component->priv->config_directory, g_strerror (errno)); + g_object_unref (component); + component = NULL; + } + } + + return component; +} + +const char * +memos_component_peek_base_directory (MemosComponent *component) +{ + return component->priv->base_directory; +} + +const char * +memos_component_peek_config_directory (MemosComponent *component) +{ + return component->priv->config_directory; +} + +ESourceList * +memos_component_peek_source_list (MemosComponent *component) +{ + return component->priv->source_list; +} + +BONOBO_TYPE_FUNC_FULL (MemosComponent, GNOME_Evolution_Component, PARENT_TYPE, memos_component) diff --git a/calendar/gui/memos-component.h b/calendar/gui/memos-component.h new file mode 100644 index 0000000000..b8f60a96e4 --- /dev/null +++ b/calendar/gui/memos-component.h @@ -0,0 +1,64 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* memos-component.h + * + * Copyright (C) 2003 Novell, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + * + * Author: Rodrigo Moya <rodrigo@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + */ + +#ifndef _MEMOS_COMPONENT_H_ +#define _MEMOS_COMPONENT_H_ + +#include <bonobo/bonobo-object.h> +#include <libedataserver/e-source-list.h> +#include <widgets/misc/e-activity-handler.h> +#include "Evolution.h" + + +#define MEMOS_TYPE_COMPONENT (memos_component_get_type ()) +#define MEMOS_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MEMOS_TYPE_COMPONENT, MemosComponent)) +#define MEMOS_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MEMOS_TYPE_COMPONENT, MemosComponentClass)) +#define MEMOS_IS_COMPONENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MEMOS_TYPE_COMPONENT)) +#define MEMOS_IS_COMPONENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), MEMOS_TYPE_COMPONENT)) + + +typedef struct _MemosComponent MemosComponent; +typedef struct _MemosComponentPrivate MemosComponentPrivate; +typedef struct _MemosComponentClass MemosComponentClass; + +struct _MemosComponent { + BonoboObject parent; + + MemosComponentPrivate *priv; +}; + +struct _MemosComponentClass { + BonoboObjectClass parent_class; + + POA_GNOME_Evolution_Component__epv epv; +}; + + +GType memos_component_get_type (void); +MemosComponent *memos_component_peek (void); + +const char *memos_component_peek_base_directory (MemosComponent *component); +const char *memos_component_peek_config_directory (MemosComponent *component); +ESourceList *memos_component_peek_source_list (MemosComponent *component); + +#endif /* _MEMOS_COMPONENT_H_ */ diff --git a/calendar/gui/memos-control.c b/calendar/gui/memos-control.c new file mode 100644 index 0000000000..040865aeaf --- /dev/null +++ b/calendar/gui/memos-control.c @@ -0,0 +1,361 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* memos-control.c + * + * Copyright (C) 2000, 2001, 2002, 2003 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + * + * Authors: Damon Chaplin <damon@ximian.com> + * Ettore Perazzoli + * Nathan Owens <pianocomp81@yahoo.com> + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <gtk/gtksignal.h> +#include <gtk/gtklabel.h> +#include <gtk/gtkcheckbutton.h> +#include <gtk/gtkmessagedialog.h> +#include <libgnome/gnome-i18n.h> +#include <libgnome/gnome-util.h> +#include <libgnomeui/gnome-dialog.h> +#include <libgnomeui/gnome-dialog-util.h> +#include <libgnomeui/gnome-stock-icons.h> +#include <libgnomeprint/gnome-print.h> +#include <libgnomeprint/gnome-print-paper.h> +#include <libgnomeprint/gnome-print-job.h> +#include <libgnomeprintui/gnome-print-job-preview.h> +#include <libgnomeprintui/gnome-print-paper-selector.h> +#include <libgnomeprintui/gnome-print-preview.h> +#include <libgnomeprintui/gnome-print-dialog.h> +#include <bonobo/bonobo-control.h> +#include <bonobo/bonobo-ui-util.h> +#include <e-util/e-dialog-utils.h> +#include <e-util/e-print.h> + +#include "calendar-config.h" +#include "e-memos.h" +#include "e-memo-table.h" +#include "print.h" +#include "memos-control.h" +#include "evolution-shell-component-utils.h" + +#define FIXED_MARGIN .05 + + +static void memos_control_activate_cb (BonoboControl *control, + gboolean activate, + gpointer user_data); +static void memos_control_open_memo_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path); +static void memos_control_new_memo_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path); +static void memos_control_cut_cmd (BonoboUIComponent *uic, + gpointer data, + const gchar *path); +static void memos_control_copy_cmd (BonoboUIComponent *uic, + gpointer data, + const gchar *path); +static void memos_control_paste_cmd (BonoboUIComponent *uic, + gpointer data, + const gchar *path); +static void memos_control_delete_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path); +static void memos_control_print_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path); +static void memos_control_print_preview_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path); + + + +BonoboControl * +memos_control_new (void) +{ + BonoboControl *control; + GtkWidget *memos; + + memos = e_memos_new (); + if (!memos) + return NULL; + gtk_widget_show (memos); + + control = bonobo_control_new (memos); + if (!control) { + gtk_widget_destroy (memos); + g_message ("control_factory_fn(): could not create the control!"); + return NULL; + } + + g_signal_connect (control, "activate", G_CALLBACK (memos_control_activate_cb), memos); + + return control; +} + + +static void +memos_control_activate_cb (BonoboControl *control, + gboolean activate, + gpointer user_data) +{ + EMemos *memos; + + memos = E_MEMOS (user_data); + + if (activate) + memos_control_activate (control, memos); + else + memos_control_deactivate (control, memos); +} + +/* Sensitizes the UI Component menu/toolbar commands based on the number of + * selected memos. + */ +void +memos_control_sensitize_commands (BonoboControl *control, EMemos *memos, int n_selected) +{ + BonoboUIComponent *uic; + gboolean read_only = TRUE; + ECal *ecal; + ECalModel *model; + + uic = bonobo_control_get_ui_component (control); + g_assert (uic != NULL); + + if (bonobo_ui_component_get_container (uic) == CORBA_OBJECT_NIL) + return; + + model = e_memo_table_get_model (e_memos_get_calendar_table (memos)); + ecal = e_cal_model_get_default_client (model); + if (ecal) + e_cal_is_read_only (ecal, &read_only, NULL); + + bonobo_ui_component_set_prop (uic, "/commands/MemosOpenMemo", "sensitive", + n_selected != 1 ? "0" : "1", + NULL); + bonobo_ui_component_set_prop (uic, "/commands/MemosCut", "sensitive", + n_selected == 0 || read_only ? "0" : "1", + NULL); + bonobo_ui_component_set_prop (uic, "/commands/MemosCopy", "sensitive", + n_selected == 0 ? "0" : "1", + NULL); + bonobo_ui_component_set_prop (uic, "/commands/MemosPaste", "sensitive", + read_only ? "0" : "1", + NULL); + bonobo_ui_component_set_prop (uic, "/commands/MemosDelete", "sensitive", + n_selected == 0 || read_only ? "0" : "1", + NULL); +} + +/* Callback used when the selection in the table changes */ +static void +selection_changed_cb (EMemos *memos, int n_selected, gpointer data) +{ + BonoboControl *control; + + control = BONOBO_CONTROL (data); + + memos_control_sensitize_commands (control, memos, n_selected); +} + +static BonoboUIVerb verbs [] = { + BONOBO_UI_VERB ("MemosOpenMemo", memos_control_open_memo_cmd), + BONOBO_UI_VERB ("MemosNewMemo", memos_control_new_memo_cmd), + BONOBO_UI_VERB ("MemosCut", memos_control_cut_cmd), + BONOBO_UI_VERB ("MemosCopy", memos_control_copy_cmd), + BONOBO_UI_VERB ("MemosPaste", memos_control_paste_cmd), + BONOBO_UI_VERB ("MemosDelete", memos_control_delete_cmd), + BONOBO_UI_VERB ("MemosPrint", memos_control_print_cmd), + BONOBO_UI_VERB ("MemosPrintPreview", memos_control_print_preview_cmd), + + BONOBO_UI_VERB_END +}; + +void +memos_control_activate (BonoboControl *control, EMemos *memos) +{ + Bonobo_UIContainer remote_uih; + BonoboUIComponent *uic; + int n_selected; + EMemoTable *cal_table; + ETable *etable; + + uic = bonobo_control_get_ui_component (control); + g_assert (uic != NULL); + + remote_uih = bonobo_control_get_remote_ui_container (control, NULL); + bonobo_ui_component_set_container (uic, remote_uih, NULL); + bonobo_object_release_unref (remote_uih, NULL); + + e_memos_set_ui_component (memos, uic); + + bonobo_ui_component_add_verb_list_with_data (uic, verbs, memos); + + bonobo_ui_component_freeze (uic, NULL); + + bonobo_ui_util_set_ui (uic, PREFIX, + EVOLUTION_UIDIR "/evolution-memos.xml", + "evolution-memos", + NULL); + + e_memos_setup_view_menus (memos, uic); + + /* Signals from the memos widget; also sensitize the menu items as appropriate */ + + g_signal_connect (memos, "selection_changed", G_CALLBACK (selection_changed_cb), control); + + cal_table = e_memos_get_calendar_table (memos); + etable = e_memo_table_get_table (cal_table); + n_selected = e_table_selected_count (etable); + + memos_control_sensitize_commands (control, memos, n_selected); + + bonobo_ui_component_thaw (uic, NULL); +} + + +void +memos_control_deactivate (BonoboControl *control, EMemos *memos) +{ + BonoboUIComponent *uic = bonobo_control_get_ui_component (control); + + g_assert (uic != NULL); + + e_memos_set_ui_component (memos, NULL); + + e_memos_discard_view_menus (memos); + + /* Stop monitoring the "selection_changed" signal */ + g_signal_handlers_disconnect_matched (memos, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, control); + + bonobo_ui_component_rm (uic, "/", NULL); + bonobo_ui_component_unset_container (uic, NULL); +} + +static void memos_control_open_memo_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path) +{ + EMemos *memos; + + memos = E_MEMOS (data); + e_memos_open_memo (memos); +} + +static void +memos_control_new_memo_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path) +{ + EMemos *memos; + + memos = E_MEMOS (data); + e_memos_new_memo (memos); +} + +static void +memos_control_cut_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path) +{ + EMemos *memos; + EMemoTable *cal_table; + + memos = E_MEMOS (data); + cal_table = e_memos_get_calendar_table (memos); + e_memo_table_cut_clipboard (cal_table); +} + +static void +memos_control_copy_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path) +{ + EMemos *memos; + EMemoTable *cal_table; + + memos = E_MEMOS (data); + cal_table = e_memos_get_calendar_table (memos); + e_memo_table_copy_clipboard (cal_table); +} + +static void +memos_control_paste_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path) +{ + EMemos *memos; + EMemoTable *cal_table; + + memos = E_MEMOS (data); + cal_table = e_memos_get_calendar_table (memos); + e_memo_table_paste_clipboard (cal_table); +} + +static void +memos_control_delete_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path) +{ + EMemos *memos; + + memos = E_MEMOS (data); + e_memos_delete_selected (memos); +} + + +static void +print_memos (EMemos *memos, gboolean preview) +{ + EMemoTable *cal_table; + ETable *etable; + + cal_table = e_memos_get_calendar_table (memos); + etable = e_memo_table_get_table (E_MEMO_TABLE (cal_table)); + + print_table (etable, _("Print Memos"), _("Memos"), preview); +} + +/* File/Print callback */ +static void +memos_control_print_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path) +{ + EMemos *memos; + + memos = E_MEMOS (data); + + print_memos (memos, FALSE); +} + +static void +memos_control_print_preview_cmd (BonoboUIComponent *uic, + gpointer data, + const char *path) +{ + EMemos *memos; + + memos = E_MEMOS (data); + + print_memos (memos, TRUE); +} + diff --git a/calendar/gui/memos-control.h b/calendar/gui/memos-control.h new file mode 100644 index 0000000000..52f5da9f39 --- /dev/null +++ b/calendar/gui/memos-control.h @@ -0,0 +1,35 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* memos-control.h + * + * Copyright (C) 2000, 2001, 2002, 2003 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * 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. + * + * Authors: Federico Mena Quintero <federico@ximian.com> + * Damon Chaplin <damon@ximian.com> + * Nathan Owens <pianocomp81@yahoo.com> + */ + +#ifndef _MEMOS_CONTROL_H_ +#define _MEMOS_CONTROL_H_ + +#include "e-memos.h" + +BonoboControl *memos_control_new (void); +void memos_control_activate (BonoboControl *control, EMemos *memos); +void memos_control_deactivate (BonoboControl *control, EMemos *memos); +void memos_control_sensitize_commands (BonoboControl *control, EMemos *memos, int n_selected); + +#endif /* _MEMOS_CONTROL_H_ */ diff --git a/calendar/gui/migration.c b/calendar/gui/migration.c index bb63e8dd48..6118983130 100644 --- a/calendar/gui/migration.c +++ b/calendar/gui/migration.c @@ -997,3 +997,123 @@ fail: return retval; } + +/******************************************************************************************************** + * + * MEMOS + * + ********************************************************************************************************/ + +static void +create_memo_sources (MemosComponent *component, + ESourceList *source_list, + ESourceGroup **on_this_computer, + ESourceGroup **on_the_web, + ESource **personal_source) +{ + GSList *groups; + ESourceGroup *group; + char *base_uri, *base_uri_proto; + + *on_this_computer = NULL; + *on_the_web = NULL; + *personal_source = NULL; + + base_uri = g_build_filename (memos_component_peek_base_directory (component), + "memos", "local", NULL); + + base_uri_proto = g_strconcat ("file://", base_uri, NULL); + + groups = e_source_list_peek_groups (source_list); + if (groups) { + /* groups are already there, we need to search for things... */ + GSList *g; + + for (g = groups; g; g = g->next) { + + group = E_SOURCE_GROUP (g->data); + + if (!*on_this_computer && !strcmp (base_uri_proto, e_source_group_peek_base_uri (group))) + *on_this_computer = g_object_ref (group); + else if (!*on_the_web && !strcmp (WEBCAL_BASE_URI, e_source_group_peek_base_uri (group))) + *on_the_web = g_object_ref (group); + } + } + + if (*on_this_computer) { + /* make sure "Personal" shows up as a source under + this group */ + GSList *sources = e_source_group_peek_sources (*on_this_computer); + GSList *s; + for (s = sources; s; s = s->next) { + ESource *source = E_SOURCE (s->data); + if (!strcmp (PERSONAL_RELATIVE_URI, e_source_peek_relative_uri (source))) { + *personal_source = g_object_ref (source); + break; + } + } + } else { + /* create the local source group */ + group = e_source_group_new (_("On This Computer"), base_uri_proto); + e_source_list_add_group (source_list, group, -1); + + *on_this_computer = group; + } + + if (!*personal_source) { + /* Create the default Person task list */ + ESource *source = e_source_new (_("Personal"), PERSONAL_RELATIVE_URI); + e_source_group_add_source (*on_this_computer, source, -1); + + if (!calendar_config_get_primary_memos () && !calendar_config_get_memos_selected ()) { + GSList selected; + + calendar_config_set_primary_memos (e_source_peek_uid (source)); + + selected.data = (gpointer)e_source_peek_uid (source); + selected.next = NULL; + calendar_config_set_memos_selected (&selected); + } + + e_source_set_color (source, 0xBECEDD); + *personal_source = source; + } + + if (!*on_the_web) { + /* Create the Webcal source group */ + group = e_source_group_new (_("On The Web"), WEBCAL_BASE_URI); + e_source_list_add_group (source_list, group, -1); + + *on_the_web = group; + } + + g_free (base_uri_proto); + g_free (base_uri); +} + + +gboolean +migrate_memos (MemosComponent *component, int major, int minor, int revision, struct _GError **err) +{ + ESourceGroup *on_this_computer = NULL; + ESourceGroup *on_the_web = NULL; + ESource *personal_source = NULL; + gboolean retval = FALSE; + + /* we call this unconditionally now - create_groups either + creates the groups/sources or it finds the necessary + groups/sources. */ + create_memo_sources (component, memos_component_peek_source_list (component), &on_this_computer, &on_the_web, &personal_source); + + e_source_list_sync (memos_component_peek_source_list (component), NULL); + retval = TRUE; +fail: + if (on_this_computer) + g_object_unref (on_this_computer); + if (on_the_web) + g_object_unref (on_the_web); + if (personal_source) + g_object_unref (personal_source); + + return retval; +} diff --git a/calendar/gui/migration.h b/calendar/gui/migration.h index 1092e30c14..5207dbe3b7 100644 --- a/calendar/gui/migration.h +++ b/calendar/gui/migration.h @@ -26,10 +26,11 @@ #include <libedataserver/e-source-group.h> #include "calendar-component.h" #include "tasks-component.h" +#include "memos-component.h" struct _GError; gboolean migrate_calendars (CalendarComponent *component, int major, int minor, int revision, struct _GError **err); gboolean migrate_tasks (TasksComponent *component, int major, int minor, int revision, struct _GError **err); - +gboolean migrate_memos (MemosComponent *component, int major, int minor, int revision, struct _GError **err); #endif diff --git a/calendar/gui/print.c b/calendar/gui/print.c index 717c9d2110..3930949269 100644 --- a/calendar/gui/print.c +++ b/calendar/gui/print.c @@ -2302,11 +2302,13 @@ print_comp_item (GnomePrintContext *pc, ECalComponent *comp, ECal *client, vtype = e_cal_component_get_vtype (comp); - /* We should only be asked to print VEVENTs or VTODOs. */ + /* We should only be asked to print VEVENTs, VTODOs, or VJOURNALs. */ if (vtype == E_CAL_COMPONENT_EVENT) title = _("Appointment"); else if (vtype == E_CAL_COMPONENT_TODO) title = _("Task"); + else if (vtype == E_CAL_COMPONENT_JOURNAL) + title = _("Memo"); else return; |