almanah r117 - in trunk: . data src src/definitions src/link-factories src/links



Author: pwithnall
Date: Sat Dec  6 20:53:07 2008
New Revision: 117
URL: http://svn.gnome.org/viewvc/almanah?rev=117&view=rev

Log:
2008-12-06  Philip Withnall  <philip tecnocode co uk>

	* configure.ac:
	* data/almanah.ui:
	* src/Makefile.am:
	* src/add-definition-dialog.c
	(almanah_add_definition_dialog_class_init),
	(almanah_add_definition_dialog_init),
	(almanah_add_definition_dialog_dispose),
	(almanah_add_definition_dialog_finalize),
	(almanah_add_definition_dialog_get_property),
	(almanah_add_definition_dialog_set_property),
	(almanah_add_definition_dialog_new), (destroy_extra_widgets),
	(response_cb), (add_type_combo_box_changed_cb),
	(almanah_add_definition_dialog_get_text),
	(almanah_add_definition_dialog_set_text),
	(almanah_add_definition_dialog_get_definition):
	* src/add-definition-dialog.h:
	* src/add-link-dialog.c:
	* src/add-link-dialog.h:
	* src/definition.c (almanah_definition_class_init),
	(almanah_definition_init), (almanah_definition_finalize),
	(almanah_definition_get_property),
	(almanah_definition_set_property), (almanah_definition_new),
	(almanah_definition_get_type_id), (almanah_definition_get_name),
	(almanah_definition_get_description),
	(almanah_definition_get_icon_name), (almanah_definition_view),
	(almanah_definition_build_dialog),
	(almanah_definition_close_dialog), (almanah_definition_parse_text),
	(almanah_definition_get_text), (almanah_definition_set_text),
	(almanah_definition_get_value), (almanah_definition_set_value),
	(almanah_definition_get_value2), (almanah_definition_set_value2),
	(almanah_definition_populate_model):
	* src/definition.h:
	* src/definitions/file.c (almanah_file_definition_class_init),
	(almanah_file_definition_init), (file_view), (file_build_dialog),
	(file_close_dialog), (file_parse_text):
	* src/definitions/file.h:
	* src/definitions/note.c (almanah_note_definition_class_init),
	(almanah_note_definition_init), (note_view), (note_build_dialog),
	(note_close_dialog), (note_parse_text):
	* src/definitions/note.h:
	* src/definitions/uri.c (almanah_uri_definition_class_init),
	(almanah_uri_definition_init), (uri_view), (uri_build_dialog),
	(uri_close_dialog), (uri_parse_text):
	* src/definitions/uri.h:
	* src/entry.h:
	* src/interface.c (almanah_create_interface),
	(definition_tag_event_cb), (almanah_interface_create_text_tags):
	* src/interface.h:
	* src/link-factories/calendar-client.c (calendar_client_get_type),
	(calendar_client_class_init),
	(calendar_client_config_get_timezone),
	(calendar_client_config_get_icaltimezone),
	(calendar_client_set_timezone),
	(calendar_client_timezone_changed_cb), (cal_opened_cb),
	(load_calendars), (calendar_client_init),
	(calendar_client_finalize), (calendar_client_set_property),
	(calendar_client_get_property), (calendar_client_new),
	(make_time_for_day_begin), (make_isodate_for_day_begin),
	(get_time_from_property), (get_ical_uid), (get_ical_rid),
	(get_ical_summary), (get_ical_description), (get_ical_start_time),
	(get_ical_end_time), (get_ical_is_all_day), (get_ical_due_time),
	(get_ical_percent_complete), (get_ical_completed_time),
	(get_ical_priority), (get_source_color), (get_source_uri),
	(null_safe_strcmp), (calendar_appointment_equal),
	(calendar_appointment_copy), (calendar_appointment_finalize),
	(calendar_appointment_init), (resolve_timezone_id),
	(calendar_appointment_collect_occurrence),
	(calendar_appointment_generate_ocurrences), (calendar_task_equal),
	(calendar_task_copy), (calendar_task_finalize),
	(calendar_task_init), (calendar_event_free), (calendar_event_new),
	(calendar_event_copy), (calendar_event_get_uid),
	(calendar_event_equal), (calendar_event_generate_ocurrences),
	(calendar_event_debug_dump), (goddamn_this_is_crack),
	(calendar_client_handle_query_completed),
	(calendar_client_handle_query_result), (check_object_remove),
	(calendar_client_handle_objects_removed),
	(calendar_client_query_finalize), (calendar_client_stop_query),
	(calendar_client_start_query),
	(calendar_client_update_appointments),
	(calendar_client_update_tasks), (calendar_client_source_finalize),
	(compare_calendar_sources), (calendar_client_update_sources_list),
	(calendar_client_appointment_sources_changed),
	(calendar_client_task_sources_changed), (calendar_client_get_date),
	(calendar_client_select_month), (calendar_client_select_day),
	(filter_appointment), (filter_task),
	(calendar_client_filter_events), (calendar_client_get_events),
	(day_from_time_t), (calendar_client_foreach_appointment_day),
	(calendar_client_set_task_completed):
	* src/link-factories/calendar-client.h:
	* src/link-factories/calendar-debug.h:
	* src/link-factories/calendar-sources.c
	(calendar_sources_get_type), (calendar_sources_class_init),
	(calendar_sources_init), (calendar_sources_finalize_source_data),
	(calendar_sources_finalize), (calendar_sources_get),
	(is_source_selected), (auth_func_cb), (get_ecal_from_source),
	(compare_ecal_lists), (debug_dump_selected_sources),
	(debug_dump_ecal_list), (backend_restart), (backend_died_cb),
	(calendar_sources_load_esource_list),
	(calendar_sources_esource_list_changed),
	(calendar_sources_selected_sources_notify),
	(calendar_sources_load_sources),
	(calendar_sources_get_appointment_sources),
	(calendar_sources_get_task_sources):
	* src/link-factories/calendar-sources.h:
	* src/link-factories/calendar.c
	(almanah_calendar_link_factory_class_init),
	(almanah_calendar_link_factory_init),
	(almanah_calendar_link_factory_dispose), (query_links),
	(events_changed_cb), (date_to_time), (get_links):
	* src/link-factories/calendar.h:
	* src/link-factory.c (almanah_link_factory_class_init),
	(almanah_link_factory_init), (almanah_link_factory_get_property),
	(almanah_link_factory_get_type_id),
	(almanah_link_factory_query_links),
	(almanah_link_factory_get_links):
	* src/link-factory.h:
	* src/link-manager.c (almanah_link_manager_class_init),
	(almanah_link_manager_init), (almanah_link_manager_dispose),
	(almanah_link_manager_new), (links_updated_cb),
	(almanah_link_manager_query_links),
	(almanah_link_manager_get_links):
	* src/link-manager.h:
	* src/link.c (almanah_link_class_init), (almanah_link_init),
	(almanah_link_get_property), (almanah_link_view),
	(almanah_link_get_icon_name):
	* src/link.h:
	* src/links/calendar-appointment.c
	(almanah_calendar_appointment_link_class_init),
	(almanah_calendar_appointment_link_init),
	(almanah_calendar_appointment_link_finalize),
	(almanah_calendar_appointment_link_new),
	(almanah_calendar_appointment_link_format_value),
	(almanah_calendar_appointment_link_view):
	* src/links/calendar-appointment.h:
	* src/links/calendar-task.c
	(almanah_calendar_task_link_class_init),
	(almanah_calendar_task_link_init),
	(almanah_calendar_task_link_finalize),
	(almanah_calendar_task_link_new),
	(almanah_calendar_task_link_format_value),
	(almanah_calendar_task_link_view):
	* src/links/calendar-task.h:
	* src/links/file.c:
	* src/links/file.h:
	* src/links/note.c:
	* src/links/note.h:
	* src/links/uri.c:
	* src/links/uri.h:
	* src/main-window.c (almanah_main_window_new),
	(save_current_entry), (add_definition_to_current_entry),
	(remove_definition_from_current_entry),
	(mw_entry_buffer_cursor_position_cb), (apply_formatting),
	(mw_about_activate_cb), (mw_add_definition_activate_cb),
	(mw_remove_definition_activate_cb), (clear_factory_links),
	(mw_links_updated_cb), (mw_calendar_day_selected_cb),
	(mw_links_selection_changed_cb), (mw_links_value_data_cb),
	(mw_links_tree_view_row_activated_cb), (mw_view_button_clicked_cb):
	* src/main.c (almanah_quit), (main):
	* src/main.h:
	* src/printing.c (almanah_print_entries):
	* src/storage-manager.c (create_tables),
	(almanah_storage_manager_get_statistics),
	(almanah_storage_manager_set_entry),
	(almanah_storage_manager_get_definitions),
	(almanah_storage_manager_get_definition),
	(almanah_storage_manager_add_definition),
	(almanah_storage_manager_remove_definition):
	* src/storage-manager.h: Radically rearrange the "links" system so
	that "links" are now dynamic objects listed per-day, and immutable by
	the user. To replace the old "links", "definitions" have been added,
	whereby the user can define some information to be associated with a
	string across all the entries in the diary. For example, a person or
	project could be defined, and then the same associated data referenced
	from multiple entries.
	There isn't currently an interface for viewing a list of definitions,
	but one is planned.
	The old "link" types have been ported to being definition types, and
	two *new* link types have been added to complement the new system of
	link factories, which allow for dynamic link listing. The new link
	types are calendar appointments and tasks, via Evolution. Some of the
	code for this (src/link-factories/calendar-*.[ch]) is taken from the
	clock applet in gnome-panel, under the GPLv2+. It hasn't been
	modified, and should be kept in sync with the originals in
	gnome-panel.



Added:
   trunk/src/add-definition-dialog.c   (contents, props changed)
      - copied, changed from r113, /trunk/src/add-link-dialog.c
   trunk/src/add-definition-dialog.h   (contents, props changed)
      - copied, changed from r113, /trunk/src/add-link-dialog.h
   trunk/src/definition.c
   trunk/src/definition.h
   trunk/src/definitions/   (props changed)
      - copied from r113, /trunk/src/links/
   trunk/src/link-factories/
   trunk/src/link-factories/calendar-client.c
   trunk/src/link-factories/calendar-client.h
   trunk/src/link-factories/calendar-debug.h
   trunk/src/link-factories/calendar-sources.c
   trunk/src/link-factories/calendar-sources.h
   trunk/src/link-factories/calendar.c
   trunk/src/link-factories/calendar.h
   trunk/src/link-factory.c
   trunk/src/link-factory.h
   trunk/src/link-manager.c
   trunk/src/link-manager.h
   trunk/src/links/
   trunk/src/links/calendar-appointment.c
   trunk/src/links/calendar-appointment.h
   trunk/src/links/calendar-task.c
   trunk/src/links/calendar-task.h
Removed:
   trunk/src/add-link-dialog.c
   trunk/src/add-link-dialog.h
Modified:
   trunk/   (props changed)
   trunk/ChangeLog
   trunk/configure.ac
   trunk/data/almanah.ui
   trunk/src/   (props changed)
   trunk/src/Makefile.am
   trunk/src/definitions/file.c
   trunk/src/definitions/file.h
   trunk/src/definitions/note.c
   trunk/src/definitions/note.h
   trunk/src/definitions/uri.c
   trunk/src/definitions/uri.h
   trunk/src/entry.h
   trunk/src/interface.c
   trunk/src/interface.h
   trunk/src/link.c
   trunk/src/link.h
   trunk/src/main-window.c
   trunk/src/main.c
   trunk/src/main.h
   trunk/src/printing.c
   trunk/src/storage-manager.c
   trunk/src/storage-manager.h

Modified: trunk/configure.ac
==============================================================================
--- trunk/configure.ac	(original)
+++ trunk/configure.ac	Sat Dec  6 20:53:07 2008
@@ -56,7 +56,7 @@
 AM_CONDITIONAL([SPELL_CHECKING], [test x$spell_checking = xtrue])
 
 dnl ***************************************************************************
-dnl Internationalisation
+dnl Dependencies
 dnl ***************************************************************************
 
 GETTEXT_PACKAGE=almanah
@@ -67,8 +67,9 @@
 
 AM_PROG_LIBTOOL
 AM_GCONF_SOURCE_2
+AC_PATH_PROG([GLIB_MKENUMS],[glib-mkenums])
 
-PKG_CHECK_MODULES(STANDARD, glib-2.0 gtk+-2.0 >= 2.14 gmodule-2.0 gio-2.0 sqlite3 cairo gconf-2.0 atk)
+PKG_CHECK_MODULES(STANDARD, glib-2.0 gtk+-2.0 >= 2.14 gmodule-2.0 gio-2.0 sqlite3 cairo gconf-2.0 atk libecal-1.2 libedataserver-1.2 libedataserverui-1.2)
 AC_SUBST(STANDARD_CFLAGS)
 AC_SUBST(STANDARD_LIBS)
 

Modified: trunk/data/almanah.ui
==============================================================================
--- trunk/data/almanah.ui	(original)
+++ trunk/data/almanah.ui	Sat Dec  6 20:53:07 2008
@@ -119,24 +119,39 @@
 					<object class="GtkAction" id="dry_ui_jump_to_today">
 						<property name="stock-id">gtk-jump-to</property>
 						<property name="name">jump-to-today</property>
-						<property name="label" translatable="yes">Go To Today</property>
+						<property name="label" translatable="yes">Go to _Today</property>
 						<signal name="activate" handler="mw_jump_to_today_activate_cb"/>
 					</object>
 				</child>
 				<child>
-					<object class="GtkAction" id="dry_ui_add_link">
+					<object class="GtkAction" id="dry_ui_definitions">
+						<property name="name">definitions</property>
+						<property name="label" translatable="yes">_Definitions</property>
+					</object>
+				</child>
+				<child>
+					<object class="GtkAction" id="dry_ui_add_definition">
 						<property name="stock-id">gtk-add</property>
-						<property name="name">add-link</property>
-						<property name="label" translatable="yes">Add Link</property>
-						<signal name="activate" handler="mw_add_link_activate_cb"/>
+						<property name="name">add-definition</property>
+						<property name="label" translatable="yes">_Add Definition</property>
+						<property name="sensitive">False</property><!-- Disabled until a selection range is made -->
+						<signal name="activate" handler="mw_add_definition_activate_cb"/>
 					</object>
 				</child>
 				<child>
-					<object class="GtkAction" id="dry_ui_remove_link">
+					<object class="GtkAction" id="dry_ui_remove_definition">
 						<property name="stock-id">gtk-remove</property>
-						<property name="name">remove-link</property>
-						<property name="label" translatable="yes">Remove Link</property>
-						<signal name="activate" handler="mw_remove_link_activate_cb"/>
+						<property name="name">remove-definition</property>
+						<property name="label" translatable="yes">_Remove Definition</property>
+						<property name="sensitive">False</property><!-- Disabled until a selection range is made -->
+						<signal name="activate" handler="mw_remove_definition_activate_cb"/>
+					</object>
+				</child>
+				<child>
+					<object class="GtkAction" id="dry_ui_view_definitions">
+						<property name="name">view-definitions</property>
+						<property name="label" translatable="yes">_View Definitions</property>
+						<signal name="activate" handler="mw_remove_definition_activate_cb"/>
 					</object>
 				</child>
 			</object>
@@ -162,6 +177,12 @@
 					<menuitem action="dry_ui_italic"/>
 					<menuitem action="dry_ui_underline"/>
 				</menu>
+				<menu action="dry_ui_definitions">
+					<menuitem action="dry_ui_add_definition"/>
+					<menuitem action="dry_ui_remove_definition"/>
+					<separator/>
+					<menuitem action="dry_ui_view_definitions"/>
+				</menu>
 				<menu action="dry_ui_help">
 					<menuitem action="dry_ui_about"/>
 				</menu>
@@ -169,22 +190,21 @@
 			<toolbar name="dry_mw_toolbar">
 				<toolitem action="dry_ui_jump_to_today"/>
 				<separator/>
-				<toolitem action="dry_ui_add_link"/>
-				<toolitem action="dry_ui_remove_link"/>
-				<separator/>
 				<toolitem action="dry_ui_bold"/>
 				<toolitem action="dry_ui_italic"/>
 				<toolitem action="dry_ui_underline"/>
+				<separator/>
+				<toolitem action="dry_ui_add_definition"/>
+				<toolitem action="dry_ui_remove_definition"/>
 			</toolbar>
 		</ui>
 	</object>
 
 	<object class="GtkListStore" id="dry_mw_link_store">
 		<columns>
-			<column type="gchararray"/><!-- Type -->
-			<column type="gchararray"/><!-- Value -->
-			<column type="gchararray"/><!-- Value 2 -->
+			<column type="AlmanahLink"/><!-- Link object -->
 			<column type="gchararray"/><!-- Icon name -->
+			<column type="guint"/><!-- Factory type ID -->
 		</columns>
 	</object>
 
@@ -283,7 +303,7 @@
 								</child>
 								<child>
 									<object class="GtkLabel" id="dry_mw_attached_links_label">
-										<property name="label" translatable="yes">Attached Links</property>
+										<property name="label" translatable="yes">Past Events</property>
 										<accessibility>
 											<relation target="dry_mw_links_tree_view" type="label-for"/>
 										</accessibility>
@@ -309,7 +329,7 @@
 												</accessibility>
 												<child internal-child="accessible">
 													<object class="AtkObject" id="a11y-dry_mw_links_tree_view">
-														<property name="AtkObject::accessible-name" translatable="yes">Link List</property>
+														<property name="AtkObject::accessible-name" translatable="yes">Past Event List</property>
 													</object>
 												</child>
 												<child>
@@ -317,7 +337,7 @@
 														<child>
 															<object class="GtkCellRendererPixbuf" id="renderer3"/>
 															<attributes>
-																<attribute name="icon-name">3</attribute>
+																<attribute name="icon-name">1</attribute>
 															</attributes>
 														</child>
 													</object>
@@ -336,37 +356,13 @@
 								<child>
 									<object class="GtkHButtonBox" id="buttonbox1">
 										<child>
-											<object class="GtkButton" id="dry_mw_add_button">
-												<property name="use-stock">True</property>
-												<property name="label">gtk-add</property>
-												<signal name="clicked" handler="mw_add_button_clicked_cb"/>
-												<child internal-child="accessible">
-													<object class="AtkObject" id="a11y-dry_mw_add_button">
-														<property name="AtkObject::accessible-name" translatable="yes">Add Link</property>
-													</object>
-												</child>
-											</object>
-										</child>
-										<child>
-											<object class="GtkButton" id="dry_mw_remove_button">
-												<property name="use-stock">True</property>
-												<property name="label">gtk-remove</property>
-												<signal name="clicked" handler="mw_remove_button_clicked_cb"/>
-												<child internal-child="accessible">
-													<object class="AtkObject" id="a11y-dry_mw_remove_button">
-														<property name="AtkObject::accessible-name" translatable="yes">Remove Link</property>
-													</object>
-												</child>
-											</object>
-										</child>
-										<child>
 											<object class="GtkButton" id="dry_mw_view_button">
 												<property name="image">dry_mw_view_button_image</property>
 												<property name="label" translatable="yes">View</property>
 												<signal name="clicked" handler="mw_view_button_clicked_cb"/>
 												<child internal-child="accessible">
 													<object class="AtkObject" id="a11y-dry_mw_view_button">
-														<property name="AtkObject::accessible-name" translatable="yes">View Link</property>
+														<property name="AtkObject::accessible-name" translatable="yes">View Event</property>
 													</object>
 												</child>
 											</object>
@@ -388,31 +384,31 @@
 		</child>
 	</object>
 
-	<object class="GtkListStore" id="dry_ald_type_store">
+	<object class="GtkListStore" id="dry_add_type_store">
 		<columns>
 			<column type="gchararray"/><!-- Name -->
-			<column type="gchararray"/><!-- Type -->
+			<column type="guint"/><!-- Type -->
 			<column type="gchararray"/><!-- Icon name -->
 		</columns>
 	</object>
 
-	<!-- TODO: Use the description of link types somewhere -->
-	<object class="AlmanahAddLinkDialog" id="dry_add_link_dialog">
+	<!-- TODO: Use the description of definition types somewhere -->
+	<object class="AlmanahAddDefinitionDialog" id="dry_add_definition_dialog">
 		<child internal-child="vbox">
 			<object class="GtkVBox" id="vbox1">
 				<child>
-					<object class="GtkTable" id="dry_ald_table">
+					<object class="GtkTable" id="dry_add_table">
 						<property name="n-rows">2</property>
 						<property name="n-columns">2</property>
 						<property name="column-spacing">5</property>
 						<property name="row-spacing">5</property>
 						<property name="border-width">5</property>
 						<child>
-							<object class="GtkLabel" id="dry_ald_type_label">
+							<object class="GtkLabel" id="dry_add_type_label">
 								<property name="xalign">0</property>
-								<property name="label" translatable="yes">Link Type</property>
+								<property name="label" translatable="yes">Definition Type</property>
 								<accessibility>
-									<relation target="dry_ald_type_combo_box" type="label-for"/>
+									<relation target="dry_add_type_combo_box" type="label-for"/>
 								</accessibility>
 							</object>
 							<packing>
@@ -424,11 +420,11 @@
 							</packing>
 						</child>
 						<child>
-							<object class="GtkComboBox" id="dry_ald_type_combo_box">
-								<property name="model">dry_ald_type_store</property>
-								<signal name="changed" handler="ald_type_combo_box_changed_cb"/>
+							<object class="GtkComboBox" id="dry_add_type_combo_box">
+								<property name="model">dry_add_type_store</property>
+								<signal name="changed" handler="add_type_combo_box_changed_cb"/>
 								<accessibility>
-									<relation target="dry_ald_type_label" type="labelled-by"/>
+									<relation target="dry_add_type_label" type="labelled-by"/>
 								</accessibility>
 								<child>
 									<object class="GtkCellRendererPixbuf" id="renderer5"/>
@@ -452,7 +448,7 @@
 							</packing>
 						</child>
 						<child>
-							<object class="GtkVBox" id="dry_ald_vbox"/>
+							<object class="GtkVBox" id="dry_add_vbox"/>
 							<packing>
 								<property name="left-attach">1</property>
 								<property name="right-attach">3</property>
@@ -465,13 +461,13 @@
 				<child internal-child="action_area">
 					<object class="GtkHButtonBox" id="hbuttonbox1">
 						<child>
-							<object class="GtkButton" id="dry_ald_cancel_button">
+							<object class="GtkButton" id="dry_add_cancel_button">
 								<property name="use-stock">True</property>
 								<property name="label">gtk-cancel</property>
 							</object>
 						</child>
 						<child>
-							<object class="GtkButton" id="dry_ald_add_button">
+							<object class="GtkButton" id="dry_add_add_button">
 								<property name="use-stock">True</property>
 								<property name="label">gtk-add</property>
 								<property name="can-default">True</property>
@@ -483,8 +479,8 @@
 			</object>
 		</child>
 		<action-widgets>
-			<action-widget response="-6">dry_ald_cancel_button</action-widget><!-- GTK_RESPONSE_CANCEL -->
-			<action-widget response="-5">dry_ald_add_button</action-widget><!-- GTK_RESPONSE_OK -->
+			<action-widget response="-6">dry_add_cancel_button</action-widget><!-- GTK_RESPONSE_CANCEL -->
+			<action-widget response="-5">dry_add_add_button</action-widget><!-- GTK_RESPONSE_OK -->
 		</action-widgets>
 	</object>
 

Modified: trunk/src/Makefile.am
==============================================================================
--- trunk/src/Makefile.am	(original)
+++ trunk/src/Makefile.am	Sat Dec  6 20:53:07 2008
@@ -2,7 +2,9 @@
 	-DPACKAGE_LOCALE_DIR=\""$(prefix)/$(DATADIRNAME)/locale"\"	\
 	-DPACKAGE_SRC_DIR=\""$(srcdir)"\"				\
 	-DPACKAGE_DATA_DIR=\""$(datadir)"\"				\
+	-I$(srcdir)/definitions						\
 	-I$(srcdir)/links						\
+	-I$(srcdir)/link-factories					\
 	$(STANDARD_CFLAGS)						\
 	$(ENCRYPTION_CFLAGS)						\
 	$(SPELL_CHECKING_CFLAGS)
@@ -20,6 +22,10 @@
 bin_PROGRAMS = almanah
 
 almanah_SOURCES = \
+	definition-builtins.c	\
+	definition-builtins.h	\
+	link-factory-builtins.c	\
+	link-factory-builtins.h	\
 	main.c			\
 	main.h			\
 	interface.c		\
@@ -28,8 +34,8 @@
 	main-window.h		\
 	storage-manager.c	\
 	storage-manager.h	\
-	add-link-dialog.c	\
-	add-link-dialog.h	\
+	add-definition-dialog.c	\
+	add-definition-dialog.h	\
 	search-dialog.c		\
 	search-dialog.h		\
 	printing.c		\
@@ -38,12 +44,29 @@
 	entry.h			\
 	link.c			\
 	link.h			\
-	links/file.c		\
-	links/file.h		\
-	links/note.c		\
-	links/note.h		\
-	links/uri.c		\
-	links/uri.h
+	link-factory.c		\
+	link-factory.h		\
+	link-manager.c		\
+	link-manager.h		\
+	definition.c		\
+	definition.h		\
+	definitions/file.c		\
+	definitions/file.h		\
+	definitions/note.c		\
+	definitions/note.h		\
+	definitions/uri.c		\
+	definitions/uri.h		\
+	links/calendar-appointment.c	\
+	links/calendar-appointment.h	\
+	links/calendar-task.c		\
+	links/calendar-task.h		\
+	link-factories/calendar.c	\
+	link-factories/calendar.h	\
+	link-factories/calendar-client.c	\
+	link-factories/calendar-client.h	\
+	link-factories/calendar-debug.h		\
+	link-factories/calendar-sources.c	\
+	link-factories/calendar-sources.h
 
 if ENCRYPTION
 almanah_SOURCES += \
@@ -56,3 +79,59 @@
 	$(STANDARD_LIBS)	\
 	$(ENCRYPTION_LIBS)	\
 	$(SPELL_CHECKING_LIBS)
+
+definition-builtins.h: stamp-definition-builtins.h
+	@true
+
+stamp-definition-builtins.h: definition.h Makefile
+	(cd $(srcdir) && $(GLIB_MKENUMS) \
+			--fhead "#ifndef __ALMANAH_DEFINITION_BUILTINS_H__\n#define __ALMANAH_DEFINITION_BUILTINS_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+			--fprod "/* enumerations from \"@filename \" */\n" \
+			--vhead "GType @enum_name _get_type (void) G_GNUC_CONST;\n#define ALMANAH_TYPE_ ENUMSHORT@ (@enum_name _get_type())\n" \
+			--ftail "G_END_DECLS\n\n#endif /* __ALMANAH_DEFINITION_BUILTINS_H__ */" definition.h) >> xgen-gtbh \
+	&& (cmp -s xgen-gtbh definition-builtins.h || cp xgen-gtbh definition-builtins.h ) \
+	&& rm -f xgen-gtbh \
+	&& echo timestamp > $(@F)
+
+definition-builtins.c: definition.h Makefile definition-builtins.h
+	(cd $(srcdir) && $(GLIB_MKENUMS) \
+			--fhead "#include \"definition.h\"\n#include \"definition-builtins.h\"" \
+			--fprod "\n/* enumerations from \"@filename \" */" \
+			--vhead "GType\n enum_name@_get_type (void)\n{\n  static GType etype = 0;\n  if (etype == 0) {\n    static const G Type@Value values[] = {" \
+			--vprod "      { @VALUENAME@, \"@VALUENAME \", \"@valuenick \" }," \
+			--vtail "      { 0, NULL, NULL }\n    };\n    etype = g_ type@_register_static (\"@EnumName \", values);\n  }\n  return etype;\n}\n" \
+		definition.h ) > xgen-gtbc \
+	&& cp xgen-gtbc definition-builtins.c  \
+	&& rm -f xgen-gtbc
+
+link-factory-builtins.h: stamp-link-factory-builtins.h
+	@true
+
+stamp-link-factory-builtins.h: link-factory.h Makefile
+	(cd $(srcdir) && $(GLIB_MKENUMS) \
+			--fhead "#ifndef __ALMANAH_LINK_FACTORY_BUILTINS_H__\n#define __ALMANAH_LINK_FACTORY_BUILTINS_H__\n\n#include <glib-object.h>\n\nG_BEGIN_DECLS\n" \
+			--fprod "/* enumerations from \"@filename \" */\n" \
+			--vhead "GType @enum_name _get_type (void) G_GNUC_CONST;\n#define ALMANAH_TYPE_ ENUMSHORT@ (@enum_name _get_type())\n" \
+			--ftail "G_END_DECLS\n\n#endif /* __ALMANAH_LINK_FACTORY_BUILTINS_H__ */" link-factory.h) >> xgen-gtbh \
+	&& (cmp -s xgen-gtbh link-factory-builtins.h || cp xgen-gtbh link-factory-builtins.h ) \
+	&& rm -f xgen-gtbh \
+	&& echo timestamp > $(@F)
+
+link-factory-builtins.c: link-factory.h Makefile link-factory-builtins.h
+	(cd $(srcdir) && $(GLIB_MKENUMS) \
+			--fhead "#include \"link-factory.h\"\n#include \"link-factory-builtins.h\"" \
+			--fprod "\n/* enumerations from \"@filename \" */" \
+			--vhead "GType\n enum_name@_get_type (void)\n{\n  static GType etype = 0;\n  if (etype == 0) {\n    static const G Type@Value values[] = {" \
+			--vprod "      { @VALUENAME@, \"@VALUENAME \", \"@valuenick \" }," \
+			--vtail "      { 0, NULL, NULL }\n    };\n    etype = g_ type@_register_static (\"@EnumName \", values);\n  }\n  return etype;\n}\n" \
+		link-factory.h ) > xgen-gtbc \
+	&& cp xgen-gtbc link-factory-builtins.c  \
+	&& rm -f xgen-gtbc
+
+CLEANFILES = \
+	definition-builtins.h		\
+	definition-builtins.c		\
+	stamp-definition-builtins.h	\
+	link-factory-builtins.h		\
+	link-factory-builtins.c		\
+	stamp-link-factory-builtins.h

Copied: trunk/src/add-definition-dialog.c (from r113, /trunk/src/add-link-dialog.c)
==============================================================================
--- /trunk/src/add-link-dialog.c	(original)
+++ trunk/src/add-definition-dialog.c	Sat Dec  6 20:53:07 2008
@@ -22,70 +22,132 @@
 #include <glib/gi18n.h>
 #include <gtk/gtk.h>
 
-#include "add-link-dialog.h"
-#include "link.h"
+#include "add-definition-dialog.h"
+#include "definition.h"
 #include "interface.h"
+#include "main.h"
+#include "storage-manager.h"
 
-static void almanah_add_link_dialog_init (AlmanahAddLinkDialog *self);
-static void almanah_add_link_dialog_dispose (GObject *object);
-static void ald_response_cb (GtkDialog *dialog, gint response_id, AlmanahAddLinkDialog *add_link_dialog);
-static void ald_show_cb (GtkWidget *widget, AlmanahAddLinkDialog *add_link_dialog);
-
-struct _AlmanahAddLinkDialogPrivate {
-	GtkComboBox *ald_type_combo_box;
-	GtkVBox *ald_vbox;
-	GtkListStore *ald_type_store;
-	AlmanahLink *link;
+static void almanah_add_definition_dialog_init (AlmanahAddDefinitionDialog *self);
+static void almanah_add_definition_dialog_dispose (GObject *object);
+static void almanah_add_definition_dialog_finalize (GObject *object);
+static void almanah_add_definition_dialog_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void almanah_add_definition_dialog_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+static void response_cb (GtkDialog *dialog, gint response_id, AlmanahAddDefinitionDialog *self);
+
+struct _AlmanahAddDefinitionDialogPrivate {
+	GtkComboBox *type_combo_box;
+	GtkVBox *vbox;
+	GtkListStore *type_store;
+	AlmanahDefinition *definition;
+	gchar *text;
 };
 
-G_DEFINE_TYPE (AlmanahAddLinkDialog, almanah_add_link_dialog, GTK_TYPE_DIALOG)
-#define ALMANAH_ADD_LINK_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_ADD_LINK_DIALOG, AlmanahAddLinkDialogPrivate))
+enum {
+	PROP_TEXT = 1
+};
+
+G_DEFINE_TYPE (AlmanahAddDefinitionDialog, almanah_add_definition_dialog, GTK_TYPE_DIALOG)
+#define ALMANAH_ADD_DEFINITION_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_ADD_DEFINITION_DIALOG, AlmanahAddDefinitionDialogPrivate))
 
 static void
-almanah_add_link_dialog_class_init (AlmanahAddLinkDialogClass *klass)
+almanah_add_definition_dialog_class_init (AlmanahAddDefinitionDialogClass *klass)
 {
 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
-	g_type_class_add_private (klass, sizeof (AlmanahAddLinkDialogPrivate));
-	gobject_class->dispose = almanah_add_link_dialog_dispose;
+
+	g_type_class_add_private (klass, sizeof (AlmanahAddDefinitionDialogPrivate));
+
+	gobject_class->set_property = almanah_add_definition_dialog_set_property;
+	gobject_class->get_property = almanah_add_definition_dialog_get_property;
+	gobject_class->dispose = almanah_add_definition_dialog_dispose;
+	gobject_class->finalize = almanah_add_definition_dialog_finalize;
+
+	g_object_class_install_property (gobject_class, PROP_TEXT,
+				g_param_spec_string ("text",
+					"Text", "The text for which this dialog is getting a definition.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 static void
-almanah_add_link_dialog_init (AlmanahAddLinkDialog *self)
+almanah_add_definition_dialog_init (AlmanahAddDefinitionDialog *self)
 {
-	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_ADD_LINK_DIALOG, AlmanahAddLinkDialogPrivate);
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_ADD_DEFINITION_DIALOG, AlmanahAddDefinitionDialogPrivate);
 
-	g_signal_connect (self, "response", G_CALLBACK (ald_response_cb), self);
-	g_signal_connect (self, "show", G_CALLBACK (ald_show_cb), self);
+	g_signal_connect (self, "response", G_CALLBACK (response_cb), self);
 	gtk_dialog_set_has_separator (GTK_DIALOG (self), FALSE);
 	gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
-	gtk_window_set_title (GTK_WINDOW (self), _("Add Link"));
+	gtk_window_set_title (GTK_WINDOW (self), _("Add Definition"));
+	gtk_window_set_transient_for (GTK_WINDOW (self), GTK_WINDOW (almanah->main_window));
 }
 
 static void
-almanah_add_link_dialog_dispose (GObject *object)
+almanah_add_definition_dialog_dispose (GObject *object)
 {
-	AlmanahAddLinkDialogPrivate *priv = ALMANAH_ADD_LINK_DIALOG (object)->priv;
+	AlmanahAddDefinitionDialogPrivate *priv = ALMANAH_ADD_DEFINITION_DIALOG (object)->priv;
 
-	if (priv->link != NULL) {
-		g_object_unref (priv->link);
-		priv->link = NULL;
-	}
+	if (priv->definition != NULL)
+		g_object_unref (priv->definition);
+	priv->definition = NULL;
 
 	/* Chain up to the parent class */
-	G_OBJECT_CLASS (almanah_add_link_dialog_parent_class)->dispose (object);
+	G_OBJECT_CLASS (almanah_add_definition_dialog_parent_class)->dispose (object);
+}
+
+static void
+almanah_add_definition_dialog_finalize (GObject *object)
+{
+	AlmanahAddDefinitionDialogPrivate *priv = ALMANAH_ADD_DEFINITION_DIALOG (object)->priv;
+
+	g_free (priv->text);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (almanah_add_definition_dialog_parent_class)->finalize (object);
+}
+
+static void
+almanah_add_definition_dialog_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	AlmanahAddDefinitionDialogPrivate *priv = ALMANAH_ADD_DEFINITION_DIALOG (object)->priv;
+
+	switch (property_id) {
+		case PROP_TEXT:
+			g_value_set_string (value, priv->text);
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
 }
 
-AlmanahAddLinkDialog *
-almanah_add_link_dialog_new (void)
+static void
+almanah_add_definition_dialog_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	AlmanahAddDefinitionDialog *self = ALMANAH_ADD_DEFINITION_DIALOG (object);
+
+	switch (property_id) {
+		case PROP_TEXT:
+			almanah_add_definition_dialog_set_text (self, g_value_get_string (value));
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+AlmanahAddDefinitionDialog *
+almanah_add_definition_dialog_new (void)
 {
 	GtkBuilder *builder;
-	AlmanahAddLinkDialog *add_link_dialog;
-	AlmanahAddLinkDialogPrivate *priv;
+	AlmanahAddDefinitionDialog *add_definition_dialog;
+	AlmanahAddDefinitionDialogPrivate *priv;
 	GError *error = NULL;
 	const gchar *interface_filename = almanah_get_interface_filename ();
 	const gchar *object_names[] = {
-		"dry_add_link_dialog",
-		"dry_ald_type_store",
+		"dry_add_definition_dialog",
+		"dry_add_type_store",
 		NULL
 	};
 
@@ -109,79 +171,113 @@
 	}
 
 	gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE);
-	add_link_dialog = ALMANAH_ADD_LINK_DIALOG (gtk_builder_get_object (builder, "dry_add_link_dialog"));
-	gtk_builder_connect_signals (builder, add_link_dialog);
+	add_definition_dialog = ALMANAH_ADD_DEFINITION_DIALOG (gtk_builder_get_object (builder, "dry_add_definition_dialog"));
+	gtk_builder_connect_signals (builder, add_definition_dialog);
 
-	if (add_link_dialog == NULL) {
+	if (add_definition_dialog == NULL) {
 		g_object_unref (builder);
 		return NULL;
 	}
 
-	priv = add_link_dialog->priv;
+	priv = add_definition_dialog->priv;
 
 	/* Grab our child widgets */
-	priv->ald_type_combo_box = GTK_COMBO_BOX (gtk_builder_get_object (builder, "dry_ald_type_combo_box"));
-	priv->ald_vbox = GTK_VBOX (gtk_builder_get_object (builder, "dry_ald_vbox"));
-	priv->ald_type_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "dry_ald_type_store"));
+	priv->type_combo_box = GTK_COMBO_BOX (gtk_builder_get_object (builder, "dry_add_type_combo_box"));
+	priv->vbox = GTK_VBOX (gtk_builder_get_object (builder, "dry_add_vbox"));
+	priv->type_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "dry_add_type_store"));
 
-	almanah_link_populate_model (priv->ald_type_store, 1, 0, 2);
-	gtk_combo_box_set_active (priv->ald_type_combo_box, 0);
+	almanah_definition_populate_model (priv->type_store, 1, 0, 2);
+	gtk_combo_box_set_active (priv->type_combo_box, 0);
 
 	g_object_unref (builder);
 
-	return add_link_dialog;
+	return add_definition_dialog;
 }
 
 static void
-destroy_extra_widgets (AlmanahAddLinkDialog *self)
+destroy_extra_widgets (AlmanahAddDefinitionDialog *self)
 {
-	gtk_container_foreach (GTK_CONTAINER (self->priv->ald_vbox), (GtkCallback) gtk_widget_destroy, NULL);
+	gtk_container_foreach (GTK_CONTAINER (self->priv->vbox), (GtkCallback) gtk_widget_destroy, NULL);
 }
 
 static void
-ald_response_cb (GtkDialog *dialog, gint response_id, AlmanahAddLinkDialog *add_link_dialog)
+response_cb (GtkDialog *dialog, gint response_id, AlmanahAddDefinitionDialog *self)
 {
+	AlmanahAddDefinitionDialogPrivate *priv = self->priv;
+
 	/* Save the entered data */
-	g_assert (add_link_dialog->priv->link != NULL);
-	almanah_link_get_values (add_link_dialog->priv->link);
+	g_assert (priv->definition != NULL);
+	almanah_definition_close_dialog (priv->definition, priv->vbox);
 
-	/* Make sure to remove all the custom widgets for the currently-selected link type */
+	/* Make sure to remove all the custom widgets for the currently-selected definition type */
 	gtk_widget_hide_all (GTK_WIDGET (dialog));
-	destroy_extra_widgets (add_link_dialog);
+	destroy_extra_widgets (self);
 }
 
 void
-ald_type_combo_box_changed_cb (GtkComboBox *self, AlmanahAddLinkDialog *add_link_dialog)
+add_type_combo_box_changed_cb (GtkComboBox *combo_box, AlmanahAddDefinitionDialog *self)
 {
 	GtkTreeIter iter;
-	gchar *type_id;
-	AlmanahAddLinkDialogPrivate *priv = add_link_dialog->priv;
+	AlmanahDefinitionType type_id;
+	AlmanahAddDefinitionDialogPrivate *priv = self->priv;
 
-	destroy_extra_widgets (add_link_dialog);
+	destroy_extra_widgets (self);
 
-	if (gtk_combo_box_get_active_iter (self, &iter) == FALSE)
+	if (gtk_combo_box_get_active_iter (combo_box, &iter) == FALSE)
 		return;
+	gtk_tree_model_get (gtk_combo_box_get_model (combo_box), &iter, 1, &type_id, -1);
 
-	gtk_tree_model_get (gtk_combo_box_get_model (self), &iter, 1, &type_id, -1);
+	/* Create a new definition of the correct type if necessary */
+	if (priv->definition == NULL || almanah_definition_get_type_id (priv->definition) != type_id) {
+		if (priv->definition != NULL)
+			g_object_unref (priv->definition);
 
-	if (priv->link != NULL)
-		g_object_unref (priv->link);
+		priv->definition = almanah_definition_new (type_id);
+	}
 
-	priv->link = almanah_link_new (type_id);
-	almanah_link_build_dialog (priv->link, priv->ald_vbox);
+	almanah_definition_build_dialog (priv->definition, priv->vbox);
+	if (priv->text != NULL)
+		almanah_definition_parse_text (priv->definition, priv->text);
+}
 
-	g_free (type_id);
+const gchar *
+almanah_add_definition_dialog_get_text (AlmanahAddDefinitionDialog *self)
+{
+	return self->priv->text;
 }
 
-static void
-ald_show_cb (GtkWidget *widget, AlmanahAddLinkDialog *add_link_dialog)
+void
+almanah_add_definition_dialog_set_text (AlmanahAddDefinitionDialog *self, const gchar *text)
 {
-	/* Select a default link type */
-	ald_type_combo_box_changed_cb (add_link_dialog->priv->ald_type_combo_box, add_link_dialog);
+	AlmanahAddDefinitionDialogPrivate *priv = self->priv;
+	AlmanahDefinition *definition;
+
+	g_free (self->priv->text);
+	self->priv->text = g_strdup (text);
+
+	/* See if a definition for this text already exists... */
+	definition = almanah_storage_manager_get_definition (almanah->storage_manager, text);
+	if (definition != NULL) {
+		/* Use the definition we've got from the DB */
+		if (priv->definition != NULL)
+			g_object_unref (priv->definition);
+		priv->definition = definition;
+
+		/* Rebuild the UI so it contains all the data from the new definition */
+		gtk_combo_box_set_active (priv->type_combo_box, -1); /* just to make sure the selection does actually change */
+		gtk_combo_box_set_active (priv->type_combo_box, almanah_definition_get_type_id (definition) - 1);
+	} else {
+		/* Select a default definition type */
+		gtk_combo_box_set_active (priv->type_combo_box, -1); /* just to make sure the selection does actually change */
+		gtk_combo_box_set_active (priv->type_combo_box, 0);
+	}
+
+	g_object_notify (G_OBJECT (self), "text");
 }
 
-AlmanahLink *
-almanah_add_link_dialog_get_link (AlmanahAddLinkDialog *self)
+AlmanahDefinition *
+almanah_add_definition_dialog_get_definition (AlmanahAddDefinitionDialog *self)
 {
-	return g_object_ref (self->priv->link);
+	g_assert (self->priv->text != NULL);
+	return self->priv->definition;
 }

Copied: trunk/src/add-definition-dialog.h (from r113, /trunk/src/add-link-dialog.h)
==============================================================================
--- /trunk/src/add-link-dialog.h	(original)
+++ trunk/src/add-definition-dialog.h	Sat Dec  6 20:53:07 2008
@@ -17,39 +17,44 @@
  * along with Almanah.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ALMANAH_ADD_LINK_DIALOG_H
-#define ALMANAH_ADD_LINK_DIALOG_H
+#ifndef ALMANAH_ADD_DEFINITION_DIALOG_H
+#define ALMANAH_ADD_DEFINITION_DIALOG_H
 
 #include <glib.h>
 #include <glib-object.h>
 #include <gtk/gtk.h>
 
-#include "link.h"
+#include "definition.h"
 
 G_BEGIN_DECLS
 
-#define ALMANAH_TYPE_ADD_LINK_DIALOG		(almanah_add_link_dialog_get_type ())
-#define ALMANAH_ADD_LINK_DIALOG(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_ADD_LINK_DIALOG, AlmanahAddLinkDialog))
-#define ALMANAH_ADD_LINK_DIALOG_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_ADD_LINK_DIALOG, AlmanahAddLinkDialogClass))
-#define ALMANAH_IS_ADD_LINK_DIALOG(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_ADD_LINK_DIALOG))
-#define ALMANAH_IS_ADD_LINK_DIALOG_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_ADD_LINK_DIALOG))
-#define ALMANAH_ADD_LINK_DIALOG_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_ADD_LINK_DIALOG, AlmanahAddLinkDialogClass))
+#define ALMANAH_TYPE_ADD_DEFINITION_DIALOG		(almanah_add_definition_dialog_get_type ())
+#define ALMANAH_ADD_DEFINITION_DIALOG(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_ADD_DEFINITION_DIALOG, AlmanahAddDefinitionDialog))
+#define ALMANAH_ADD_DEFINITION_DIALOG_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_ADD_DEFINITION_DIALOG, AlmanahAddDefinitionDialogClass))
+#define ALMANAH_IS_ADD_DEFINITION_DIALOG(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_ADD_DEFINITION_DIALOG))
+#define ALMANAH_IS_ADD_DEFINITION_DIALOG_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_ADD_DEFINITION_DIALOG))
+#define ALMANAH_ADD_DEFINITION_DIALOG_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_ADD_DEFINITION_DIALOG, AlmanahAddDefinitionDialogClass))
 
-typedef struct _AlmanahAddLinkDialogPrivate	AlmanahAddLinkDialogPrivate;
+typedef struct _AlmanahAddDefinitionDialogPrivate	AlmanahAddDefinitionDialogPrivate;
 
 typedef struct {
 	GtkDialog parent;
-	AlmanahAddLinkDialogPrivate *priv;
-} AlmanahAddLinkDialog;
+	AlmanahAddDefinitionDialogPrivate *priv;
+} AlmanahAddDefinitionDialog;
 
 typedef struct {
 	GtkDialogClass parent;
-} AlmanahAddLinkDialogClass;
+} AlmanahAddDefinitionDialogClass;
 
-GType almanah_add_link_dialog_get_type (void);
-AlmanahAddLinkDialog *almanah_add_link_dialog_new (void);
-AlmanahLink *almanah_add_link_dialog_get_link (AlmanahAddLinkDialog *self);
+GType almanah_add_definition_dialog_get_type (void);
+
+AlmanahAddDefinitionDialog *almanah_add_definition_dialog_new ();
+
+const gchar *almanah_add_definition_dialog_get_text (AlmanahAddDefinitionDialog *self);
+void almanah_add_definition_dialog_set_text (AlmanahAddDefinitionDialog *self, const gchar *text);
+
+AlmanahDefinition *almanah_add_definition_dialog_get_definition (AlmanahAddDefinitionDialog *self);
 
 G_END_DECLS
 
-#endif /* !ALMANAH_ADD_LINK_DIALOG_H */
+#endif /* !ALMANAH_ADD_DEFINITION_DIALOG_H */

Added: trunk/src/definition.c
==============================================================================
--- (empty file)
+++ trunk/src/definition.c	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,323 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <math.h>
+#include <string.h>
+
+#include "definition.h"
+#include "definition-builtins.h"
+
+typedef struct {
+	AlmanahDefinitionType type_id;
+	GType (*type_function) (void);
+} DefinitionType;
+
+/* TODO: This is still a little hacky */
+
+#include "file.h"
+#include "note.h"
+#include "uri.h"
+
+const DefinitionType definition_types[] = {
+	{ ALMANAH_DEFINITION_FILE, almanah_file_definition_get_type },
+	{ ALMANAH_DEFINITION_NOTE, almanah_note_definition_get_type },
+	{ ALMANAH_DEFINITION_URI, almanah_uri_definition_get_type }
+};
+
+static void almanah_definition_init (AlmanahDefinition *self);
+static void almanah_definition_finalize (GObject *object);
+static void almanah_definition_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void almanah_definition_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+
+struct _AlmanahDefinitionPrivate {
+	gchar *text;
+	gchar *value;
+	gchar *value2;
+};
+
+enum {
+	PROP_TYPE_ID = 1,
+	PROP_NAME,
+	PROP_DESCRIPTION,
+	PROP_ICON_NAME,
+	PROP_TEXT,
+	PROP_VALUE,
+	PROP_VALUE2
+};
+
+G_DEFINE_ABSTRACT_TYPE (AlmanahDefinition, almanah_definition, G_TYPE_OBJECT)
+#define ALMANAH_DEFINITION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_DEFINITION, AlmanahDefinitionPrivate))
+
+static void
+almanah_definition_class_init (AlmanahDefinitionClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (AlmanahDefinitionPrivate));
+
+	gobject_class->set_property = almanah_definition_set_property;
+	gobject_class->get_property = almanah_definition_get_property;
+	gobject_class->finalize = almanah_definition_finalize;
+
+	g_object_class_install_property (gobject_class, PROP_TYPE_ID,
+				g_param_spec_enum ("type-id",
+					"Type ID", "The type ID of this definition.",
+					ALMANAH_TYPE_DEFINITION_TYPE, ALMANAH_DEFINITION_UNKNOWN,
+					G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property (gobject_class, PROP_NAME,
+				g_param_spec_string ("name",
+					"Name", "The human-readable name for this definition type.",
+					NULL,
+					G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property (gobject_class, PROP_DESCRIPTION,
+				g_param_spec_string ("description",
+					"Description", "The human-readable description for this definition type.",
+					NULL,
+					G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property (gobject_class, PROP_ICON_NAME,
+				g_param_spec_string ("icon-name",
+					"Icon Name", "The icon name for this definition type.",
+					NULL,
+					G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property (gobject_class, PROP_TEXT,
+				g_param_spec_string ("text",
+					"Text", "The text this definition defines.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property (gobject_class, PROP_VALUE,
+				g_param_spec_string ("value",
+					"Value", "The first value of this definition.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property (gobject_class, PROP_VALUE2,
+				g_param_spec_string ("value2",
+					"Value 2", "The second value of this definition.",
+					NULL,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+almanah_definition_init (AlmanahDefinition *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_DEFINITION, AlmanahDefinitionPrivate);
+}
+
+static void
+almanah_definition_finalize (GObject *object)
+{
+	AlmanahDefinitionPrivate *priv = ALMANAH_DEFINITION (object)->priv;
+
+	g_free (priv->value);
+	g_free (priv->value2);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (almanah_definition_parent_class)->finalize (object);
+}
+
+static void
+almanah_definition_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	AlmanahDefinitionPrivate *priv = ALMANAH_DEFINITION (object)->priv;
+	AlmanahDefinitionClass *klass = ALMANAH_DEFINITION_GET_CLASS (object);
+
+	switch (property_id) {
+		case PROP_TYPE_ID:
+			g_value_set_enum (value, klass->type_id);
+			break;
+		case PROP_NAME:
+			g_value_set_string (value, klass->name);
+			break;
+		case PROP_DESCRIPTION:
+			g_value_set_string (value, klass->description);
+			break;
+		case PROP_ICON_NAME:
+			g_value_set_string (value, klass->icon_name);
+			break;
+		case PROP_VALUE:
+			g_value_set_string (value, priv->value);
+			break;
+		case PROP_VALUE2:
+			g_value_set_string (value, priv->value2);
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static void
+almanah_definition_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	AlmanahDefinition *self = ALMANAH_DEFINITION (object);
+
+	switch (property_id) {
+		case PROP_VALUE:
+			almanah_definition_set_value (self, g_value_get_string (value));
+			break;
+		case PROP_VALUE2:
+			almanah_definition_set_value2 (self, g_value_get_string (value));
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+AlmanahDefinition *
+almanah_definition_new (AlmanahDefinitionType type_id)
+{
+	guint i;
+
+	for (i = 0; i < G_N_ELEMENTS (definition_types); i++) {
+		if (definition_types[i].type_id == type_id)
+			return g_object_new (definition_types[i].type_function (), NULL);
+	}
+
+	return NULL;
+}
+
+AlmanahDefinitionType
+almanah_definition_get_type_id (AlmanahDefinition *self)
+{
+	AlmanahDefinitionClass *klass = ALMANAH_DEFINITION_GET_CLASS (self);
+	return klass->type_id;
+}
+
+const gchar *
+almanah_definition_get_name (AlmanahDefinition *self)
+{
+	AlmanahDefinitionClass *klass = ALMANAH_DEFINITION_GET_CLASS (self);
+	return klass->name;
+}
+
+const gchar *
+almanah_definition_get_description (AlmanahDefinition *self)
+{
+	AlmanahDefinitionClass *klass = ALMANAH_DEFINITION_GET_CLASS (self);
+	return klass->description;
+}
+
+const gchar *
+almanah_definition_get_icon_name (AlmanahDefinition *self)
+{
+	AlmanahDefinitionClass *klass = ALMANAH_DEFINITION_GET_CLASS (self);
+	return klass->icon_name;
+}
+
+gboolean
+almanah_definition_view (AlmanahDefinition *self)
+{
+	AlmanahDefinitionClass *klass = ALMANAH_DEFINITION_GET_CLASS (self);
+	g_assert (klass->view != NULL);
+	return klass->view (self);
+}
+
+void
+almanah_definition_build_dialog (AlmanahDefinition *self, GtkVBox *parent_vbox)
+{
+	AlmanahDefinitionClass *klass = ALMANAH_DEFINITION_GET_CLASS (self);
+	g_assert (klass->build_dialog != NULL);
+	return klass->build_dialog (self, parent_vbox);
+}
+
+void
+almanah_definition_close_dialog (AlmanahDefinition *self, GtkVBox *parent_vbox)
+{
+	AlmanahDefinitionClass *klass = ALMANAH_DEFINITION_GET_CLASS (self);
+	g_assert (klass->close_dialog != NULL);
+	return klass->close_dialog (self, parent_vbox);
+}
+
+void
+almanah_definition_parse_text (AlmanahDefinition *self, const gchar *text)
+{
+	AlmanahDefinitionClass *klass = ALMANAH_DEFINITION_GET_CLASS (self);
+
+	almanah_definition_set_text (self, text);
+
+	g_assert (klass->parse_text != NULL);
+	return klass->parse_text (self, text);
+}
+
+const gchar *
+almanah_definition_get_text (AlmanahDefinition *self)
+{
+	return self->priv->text;
+}
+
+void
+almanah_definition_set_text (AlmanahDefinition *self, const gchar *text)
+{
+	g_free (self->priv->text);
+	self->priv->text = g_strdup (text);
+	g_object_notify (G_OBJECT (self), "text");
+}
+
+const gchar *
+almanah_definition_get_value (AlmanahDefinition *self)
+{
+	return self->priv->value;
+}
+
+void
+almanah_definition_set_value (AlmanahDefinition *self, const gchar *value)
+{
+	g_free (self->priv->value);
+	self->priv->value = g_strdup (value);
+	g_object_notify (G_OBJECT (self), "value");
+}
+
+const gchar *
+almanah_definition_get_value2 (AlmanahDefinition *self)
+{
+	return self->priv->value2;
+}
+
+void
+almanah_definition_set_value2 (AlmanahDefinition *self, const gchar *value)
+{
+	g_free (self->priv->value2);
+	self->priv->value2 = g_strdup (value);
+	g_object_notify (G_OBJECT (self), "value2");
+}
+
+void
+almanah_definition_populate_model (GtkListStore *list_store, guint type_id_column, guint name_column, guint icon_name_column)
+{
+	GtkTreeIter iter;
+	guint i;
+
+	for (i = 0; i < G_N_ELEMENTS (definition_types); i++) {
+		AlmanahDefinition *definition = g_object_new (definition_types[i].type_function (), NULL);
+
+		if (definition == NULL)
+			continue;
+
+		gtk_list_store_append (list_store, &iter);
+		gtk_list_store_set (list_store, &iter,
+				    type_id_column, definition_types[i].type_id,
+				    name_column, almanah_definition_get_name (definition),
+				    icon_name_column, almanah_definition_get_icon_name (definition),
+				    -1);
+	}
+}

Added: trunk/src/definition.h
==============================================================================
--- (empty file)
+++ trunk/src/definition.h	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,90 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALMANAH_DEFINITION_H
+#define ALMANAH_DEFINITION_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	ALMANAH_DEFINITION_UNKNOWN = 0,
+	ALMANAH_DEFINITION_FILE = 1,
+	ALMANAH_DEFINITION_NOTE,
+	ALMANAH_DEFINITION_URI
+} AlmanahDefinitionType;
+
+#define ALMANAH_TYPE_DEFINITION			(almanah_definition_get_type ())
+#define ALMANAH_DEFINITION(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_DEFINITION, AlmanahDefinition))
+#define ALMANAH_DEFINITION_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_DEFINITION, AlmanahDefinitionClass))
+#define ALMANAH_IS_DEFINITION(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_DEFINITION))
+#define ALMANAH_IS_DEFINITION_CLASS(k)		(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_DEFINITION))
+#define ALMANAH_DEFINITION_GET_CLASS(o)		(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_DEFINITION, AlmanahDefinitionClass))
+
+typedef struct _AlmanahDefinitionPrivate	AlmanahDefinitionPrivate;
+
+typedef struct {
+	GObject parent;
+	AlmanahDefinitionPrivate *priv;
+} AlmanahDefinition;
+
+typedef struct {
+	GObjectClass parent;
+
+	AlmanahDefinitionType type_id;
+	gchar *name;
+	gchar *description;
+	gchar *icon_name;
+
+	gboolean (*view) (AlmanahDefinition *definition);
+	void (*build_dialog) (AlmanahDefinition *definition, GtkVBox *parent_vbox);
+	void (*close_dialog) (AlmanahDefinition *definition, GtkVBox *parent_vbox);
+	void (*parse_text) (AlmanahDefinition *definition, const gchar *text);
+} AlmanahDefinitionClass;
+
+GType almanah_definition_get_type (void);
+
+AlmanahDefinition *almanah_definition_new (AlmanahDefinitionType type_id);
+
+AlmanahDefinitionType almanah_definition_get_type_id (AlmanahDefinition *self);
+const gchar *almanah_definition_get_name (AlmanahDefinition *self);
+const gchar *almanah_definition_get_description (AlmanahDefinition *self);
+const gchar *almanah_definition_get_icon_name (AlmanahDefinition *self);
+
+gboolean almanah_definition_view (AlmanahDefinition *self);
+void almanah_definition_build_dialog (AlmanahDefinition *self, GtkVBox *parent_vbox);
+void almanah_definition_close_dialog (AlmanahDefinition *self, GtkVBox *parent_vbox);
+void almanah_definition_parse_text (AlmanahDefinition *self, const gchar *text);
+
+const gchar *almanah_definition_get_text (AlmanahDefinition *self);
+void almanah_definition_set_text (AlmanahDefinition *self, const gchar *text);
+
+const gchar *almanah_definition_get_value (AlmanahDefinition *self);
+void almanah_definition_set_value (AlmanahDefinition *self, const gchar *value);
+const gchar *almanah_definition_get_value2 (AlmanahDefinition *self);
+void almanah_definition_set_value2 (AlmanahDefinition *self, const gchar *value);
+
+void almanah_definition_populate_model (GtkListStore *list_store, guint type_id_column, guint name_column, guint icon_name_column);
+
+G_END_DECLS
+
+#endif /* !ALMANAH_DEFINITION_H */

Modified: trunk/src/definitions/file.c
==============================================================================
--- /trunk/src/links/file.c	(original)
+++ trunk/src/definitions/file.c	Sat Dec  6 20:53:07 2008
@@ -21,58 +21,51 @@
 #include <glib/gi18n.h>
 
 #include "file.h"
-#include "../link.h"
-#include "../interface.h"
+#include "../definition.h"
 #include "../main.h"
 
-static void almanah_file_link_init (AlmanahFileLink *self);
-static gchar *file_format_value (AlmanahLink *link);
-static gboolean file_view (AlmanahLink *link);
-static void file_build_dialog (AlmanahLink *link, GtkVBox *parent_vbox);
-static void file_get_values (AlmanahLink *link);
+static void almanah_file_definition_init (AlmanahFileDefinition *self);
+static gboolean file_view (AlmanahDefinition *definition);
+static void file_build_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox);
+static void file_close_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox);
+static void file_parse_text (AlmanahDefinition *definition, const gchar *text);
 
-struct _AlmanahFileLinkPrivate {
+struct _AlmanahFileDefinitionPrivate {
 	GtkWidget *chooser;
 };
 
-G_DEFINE_TYPE (AlmanahFileLink, almanah_file_link, ALMANAH_TYPE_LINK)
-#define ALMANAH_FILE_LINK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_FILE_LINK, AlmanahFileLinkPrivate))
+G_DEFINE_TYPE (AlmanahFileDefinition, almanah_file_definition, ALMANAH_TYPE_DEFINITION)
+#define ALMANAH_FILE_DEFINITION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_FILE_DEFINITION, AlmanahFileDefinitionPrivate))
 
 static void
-almanah_file_link_class_init (AlmanahFileLinkClass *klass)
+almanah_file_definition_class_init (AlmanahFileDefinitionClass *klass)
 {
-	AlmanahLinkClass *link_class = ALMANAH_LINK_CLASS (klass);
+	AlmanahDefinitionClass *definition_class = ALMANAH_DEFINITION_CLASS (klass);
 
-	g_type_class_add_private (klass, sizeof (AlmanahFileLinkPrivate));
+	g_type_class_add_private (klass, sizeof (AlmanahFileDefinitionPrivate));
 
-	link_class->type_id = "file";
-	link_class->name = _("File");
-	link_class->description = _("An attached file.");
-	link_class->icon_name = "system-file-manager";
+	definition_class->type_id = ALMANAH_DEFINITION_FILE;
+	definition_class->name = _("File");
+	definition_class->description = _("An attached file.");
+	definition_class->icon_name = "system-file-manager";
 
-	link_class->format_value = file_format_value;
-	link_class->view = file_view;
-	link_class->build_dialog = file_build_dialog;
-	link_class->get_values = file_get_values;
+	definition_class->view = file_view;
+	definition_class->build_dialog = file_build_dialog;
+	definition_class->close_dialog = file_close_dialog;
+	definition_class->parse_text = file_parse_text;
 }
 
 static void
-almanah_file_link_init (AlmanahFileLink *self)
+almanah_file_definition_init (AlmanahFileDefinition *self)
 {
-	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_FILE_LINK, AlmanahFileLinkPrivate);
-}
-
-static gchar *
-file_format_value (AlmanahLink *link)
-{
-	return g_strdup (almanah_link_get_value (link));
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_FILE_DEFINITION, AlmanahFileDefinitionPrivate);
 }
 
 static gboolean
-file_view (AlmanahLink *link)
+file_view (AlmanahDefinition *definition)
 {
 	GError *error = NULL;
-	const gchar *value = almanah_link_get_value (link);
+	const gchar *value = almanah_definition_get_value (definition);
 
 	if (gtk_show_uri (gtk_widget_get_screen (almanah->main_window), value, GDK_CURRENT_TIME, &error) == FALSE) {
 		GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (almanah->main_window),
@@ -91,24 +84,35 @@
 }
 
 static void
-file_build_dialog (AlmanahLink *link, GtkVBox *parent_vbox)
+file_build_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox)
 {
-	AlmanahFileLinkPrivate *priv = ALMANAH_FILE_LINK (link)->priv;
+	AlmanahFileDefinitionPrivate *priv = ALMANAH_FILE_DEFINITION (definition)->priv;
+	const gchar *value = almanah_definition_get_value (definition);
 
 	priv->chooser = gtk_file_chooser_button_new (_("Select File"), GTK_FILE_CHOOSER_ACTION_OPEN);
 
+	/* Initialise the dialogue with the definition's current values */
+	if (value != NULL)
+		gtk_file_chooser_set_uri (GTK_FILE_CHOOSER (priv->chooser), value);
+
 	gtk_box_pack_start (GTK_BOX (parent_vbox), priv->chooser, TRUE, TRUE, 0);
 	gtk_widget_show_all (GTK_WIDGET (parent_vbox));
 }
 
 static void
-file_get_values (AlmanahLink *link)
+file_close_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox)
 {
-	AlmanahFileLinkPrivate *priv = ALMANAH_FILE_LINK (link)->priv;
+	AlmanahFileDefinitionPrivate *priv = ALMANAH_FILE_DEFINITION (definition)->priv;
 	gchar *value = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER (priv->chooser));
 
-	almanah_link_set_value (link, value);
+	almanah_definition_set_value (definition, value);
 	g_free (value);
 
-	almanah_link_set_value2 (link, NULL);
+	almanah_definition_set_value2 (definition, NULL);
+}
+
+static void
+file_parse_text (AlmanahDefinition *definition, const gchar *text)
+{
+	/* TODO */
 }

Modified: trunk/src/definitions/file.h
==============================================================================
--- /trunk/src/links/file.h	(original)
+++ trunk/src/definitions/file.h	Sat Dec  6 20:53:07 2008
@@ -17,36 +17,36 @@
  * along with Almanah.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ALMANAH_FILE_LINK_H
-#define ALMANAH_FILE_LINK_H
+#ifndef ALMANAH_FILE_DEFINITION_H
+#define ALMANAH_FILE_DEFINITION_H
 
 #include <glib.h>
 #include <glib-object.h>
 
-#include "../link.h"
+#include "../definition.h"
 
 G_BEGIN_DECLS
 
-#define ALMANAH_TYPE_FILE_LINK		(almanah_file_link_get_type ())
-#define ALMANAH_FILE_LINK(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_FILE_LINK, AlmanahFileLink))
-#define ALMANAH_FILE_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_FILE_LINK, AlmanahFileLinkClass))
-#define ALMANAH_IS_FILE_LINK(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_FILE_LINK))
-#define ALMANAH_IS_FILE_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_FILE_LINK))
-#define ALMANAH_FILE_LINK_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_FILE_LINK, AlmanahFileLinkClass))
+#define ALMANAH_TYPE_FILE_DEFINITION		(almanah_file_definition_get_type ())
+#define ALMANAH_FILE_DEFINITION(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_FILE_DEFINITION, AlmanahFileDefinition))
+#define ALMANAH_FILE_DEFINITION_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_FILE_DEFINITION, AlmanahFileDefinitionClass))
+#define ALMANAH_IS_FILE_DEFINITION(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_FILE_DEFINITION))
+#define ALMANAH_IS_FILE_DEFINITION_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_FILE_DEFINITION))
+#define ALMANAH_FILE_DEFINITION_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_FILE_DEFINITION, AlmanahFileDefinitionClass))
 
-typedef struct _AlmanahFileLinkPrivate	AlmanahFileLinkPrivate;
+typedef struct _AlmanahFileDefinitionPrivate	AlmanahFileDefinitionPrivate;
 
 typedef struct {
-	AlmanahLink parent;
-	AlmanahFileLinkPrivate *priv;
-} AlmanahFileLink;
+	AlmanahDefinition parent;
+	AlmanahFileDefinitionPrivate *priv;
+} AlmanahFileDefinition;
 
 typedef struct {
-	AlmanahLinkClass parent;
-} AlmanahFileLinkClass;
+	AlmanahDefinitionClass parent;
+} AlmanahFileDefinitionClass;
 
-GType almanah_file_link_get_type (void);
+GType almanah_file_definition_get_type (void);
 
 G_END_DECLS
 
-#endif /* !ALMANAH_FILE_LINK_H */
+#endif /* !ALMANAH_FILE_DEFINITION_H */

Modified: trunk/src/definitions/note.c
==============================================================================
--- /trunk/src/links/note.c	(original)
+++ trunk/src/definitions/note.c	Sat Dec  6 20:53:07 2008
@@ -21,57 +21,51 @@
 #include <glib/gi18n.h>
 
 #include "note.h"
-#include "../link.h"
+#include "../definition.h"
 #include "../interface.h"
 #include "../main.h"
 
-static void almanah_note_link_init (AlmanahNoteLink *self);
-static gchar *note_format_value (AlmanahLink *link);
-static gboolean note_view (AlmanahLink *link);
-static void note_build_dialog (AlmanahLink *link, GtkVBox *parent_vbox);
-static void note_get_values (AlmanahLink *link);
+static void almanah_note_definition_init (AlmanahNoteDefinition *self);
+static gboolean note_view (AlmanahDefinition *definition);
+static void note_build_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox);
+static void note_close_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox);
+static void note_parse_text (AlmanahDefinition *definition, const gchar *text);
 
-struct _AlmanahNoteLinkPrivate {
+struct _AlmanahNoteDefinitionPrivate {
 	GtkWidget *text_view;
 };
 
-G_DEFINE_TYPE (AlmanahNoteLink, almanah_note_link, ALMANAH_TYPE_LINK)
-#define ALMANAH_NOTE_LINK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_NOTE_LINK, AlmanahNoteLinkPrivate))
+G_DEFINE_TYPE (AlmanahNoteDefinition, almanah_note_definition, ALMANAH_TYPE_DEFINITION)
+#define ALMANAH_NOTE_DEFINITION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_NOTE_DEFINITION, AlmanahNoteDefinitionPrivate))
 
 static void
-almanah_note_link_class_init (AlmanahNoteLinkClass *klass)
+almanah_note_definition_class_init (AlmanahNoteDefinitionClass *klass)
 {
-	AlmanahLinkClass *link_class = ALMANAH_LINK_CLASS (klass);
+	AlmanahDefinitionClass *definition_class = ALMANAH_DEFINITION_CLASS (klass);
 
-	g_type_class_add_private (klass, sizeof (AlmanahNoteLinkPrivate));
+	g_type_class_add_private (klass, sizeof (AlmanahNoteDefinitionPrivate));
 
-	link_class->type_id = "note";
-	link_class->name = _("Note");
-	link_class->description = _("A note about an important event.");
-	link_class->icon_name = "emblem-important";
+	definition_class->type_id = ALMANAH_DEFINITION_NOTE;
+	definition_class->name = _("Note");
+	definition_class->description = _("A note about an important event.");
+	definition_class->icon_name = "emblem-important";
 
-	link_class->format_value = note_format_value;
-	link_class->view = note_view;
-	link_class->build_dialog = note_build_dialog;
-	link_class->get_values = note_get_values;
+	definition_class->view = note_view;
+	definition_class->build_dialog = note_build_dialog;
+	definition_class->close_dialog = note_close_dialog;
+	definition_class->parse_text = note_parse_text;
 }
 
 static void
-almanah_note_link_init (AlmanahNoteLink *self)
+almanah_note_definition_init (AlmanahNoteDefinition *self)
 {
-	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_NOTE_LINK, AlmanahNoteLinkPrivate);
-}
-
-static gchar *
-note_format_value (AlmanahLink *link)
-{
-	return g_strdup (almanah_link_get_value (link));
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_NOTE_DEFINITION, AlmanahNoteDefinitionPrivate);
 }
 
 static gboolean
-note_view (AlmanahLink *link)
+note_view (AlmanahDefinition *definition)
 {
-	const gchar *value = almanah_link_get_value (link);
+	const gchar *value = almanah_definition_get_value (definition);
 
 	GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (almanah->main_window),
 				GTK_DIALOG_DESTROY_WITH_PARENT,
@@ -85,10 +79,11 @@
 }
 
 static void
-note_build_dialog (AlmanahLink *link, GtkVBox *parent_vbox)
+note_build_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox)
 {
 	GtkWidget *scrolled_window;
-	AlmanahNoteLinkPrivate *priv = ALMANAH_NOTE_LINK (link)->priv;
+	AlmanahNoteDefinitionPrivate *priv = ALMANAH_NOTE_DEFINITION (definition)->priv;
+	const gchar *value = almanah_definition_get_value (definition);
 
 	scrolled_window = gtk_scrolled_window_new (NULL, NULL);
 	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled_window),
@@ -99,24 +94,36 @@
 	priv->text_view = gtk_text_view_new ();
 	gtk_container_add (GTK_CONTAINER (scrolled_window), priv->text_view);
 
+	/* Initialise the dialogue with the definition's current values */
+	if (value != NULL) {
+		GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
+		gtk_text_buffer_set_text (buffer, value, -1);
+	}
+
 	gtk_box_pack_start (GTK_BOX (parent_vbox), scrolled_window, TRUE, TRUE, 0);
 	gtk_widget_show_all (GTK_WIDGET (parent_vbox));
 }
 
 static void
-note_get_values (AlmanahLink *link)
+note_close_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox)
 {
+	AlmanahNoteDefinitionPrivate *priv = ALMANAH_NOTE_DEFINITION (definition)->priv;
 	gchar *value;
 	GtkTextBuffer *buffer;
 	GtkTextIter start_iter, end_iter;
-	AlmanahNoteLinkPrivate *priv = ALMANAH_NOTE_LINK (link)->priv;
 
 	buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (priv->text_view));
 	gtk_text_buffer_get_bounds (buffer, &start_iter, &end_iter);
 
 	value = gtk_text_buffer_get_text (buffer, &start_iter, &end_iter, FALSE);
-	almanah_link_set_value (link, value);
+	almanah_definition_set_value (definition, value);
 	g_free (value);
 
-	almanah_link_set_value2 (link, NULL);
+	almanah_definition_set_value2 (definition, NULL);
+}
+
+static void
+note_parse_text (AlmanahDefinition *definition, const gchar *text)
+{
+	/* TODO */
 }

Modified: trunk/src/definitions/note.h
==============================================================================
--- /trunk/src/links/note.h	(original)
+++ trunk/src/definitions/note.h	Sat Dec  6 20:53:07 2008
@@ -17,36 +17,36 @@
  * along with Almanah.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ALMANAH_NOTE_LINK_H
-#define ALMANAH_NOTE_LINK_H
+#ifndef ALMANAH_NOTE_DEFINITION_H
+#define ALMANAH_NOTE_DEFINITION_H
 
 #include <glib.h>
 #include <glib-object.h>
 
-#include "../link.h"
+#include "../definition.h"
 
 G_BEGIN_DECLS
 
-#define ALMANAH_TYPE_NOTE_LINK		(almanah_note_link_get_type ())
-#define ALMANAH_NOTE_LINK(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_NOTE_LINK, AlmanahNoteLink))
-#define ALMANAH_NOTE_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_NOTE_LINK, AlmanahNoteLinkClass))
-#define ALMANAH_IS_NOTE_LINK(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_NOTE_LINK))
-#define ALMANAH_IS_NOTE_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_NOTE_LINK))
-#define ALMANAH_NOTE_LINK_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_NOTE_LINK, AlmanahNoteLinkClass))
+#define ALMANAH_TYPE_NOTE_DEFINITION		(almanah_note_definition_get_type ())
+#define ALMANAH_NOTE_DEFINITION(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_NOTE_DEFINITION, AlmanahNoteDefinition))
+#define ALMANAH_NOTE_DEFINITION_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_NOTE_DEFINITION, AlmanahNoteDefinitionClass))
+#define ALMANAH_IS_NOTE_DEFINITION(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_NOTE_DEFINITION))
+#define ALMANAH_IS_NOTE_DEFINITION_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_NOTE_DEFINITION))
+#define ALMANAH_NOTE_DEFINITION_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_NOTE_DEFINITION, AlmanahNoteDefinitionClass))
 
-typedef struct _AlmanahNoteLinkPrivate	AlmanahNoteLinkPrivate;
+typedef struct _AlmanahNoteDefinitionPrivate	AlmanahNoteDefinitionPrivate;
 
 typedef struct {
-	AlmanahLink parent;
-	AlmanahNoteLinkPrivate *priv;
-} AlmanahNoteLink;
+	AlmanahDefinition parent;
+	AlmanahNoteDefinitionPrivate *priv;
+} AlmanahNoteDefinition;
 
 typedef struct {
-	AlmanahLinkClass parent;
-} AlmanahNoteLinkClass;
+	AlmanahDefinitionClass parent;
+} AlmanahNoteDefinitionClass;
 
-GType almanah_note_link_get_type (void);
+GType almanah_note_definition_get_type (void);
 
 G_END_DECLS
 
-#endif /* !ALMANAH_NOTE_LINK_H */
+#endif /* !ALMANAH_NOTE_DEFINITION_H */

Modified: trunk/src/definitions/uri.c
==============================================================================
--- /trunk/src/links/uri.c	(original)
+++ trunk/src/definitions/uri.c	Sat Dec  6 20:53:07 2008
@@ -22,58 +22,52 @@
 #include <atk/atk.h>
 
 #include "uri.h"
-#include "../link.h"
+#include "../definition.h"
 #include "../interface.h"
 #include "../main.h"
 
-static void almanah_uri_link_init (AlmanahURILink *self);
-static gchar *uri_format_value (AlmanahLink *link);
-static gboolean uri_view (AlmanahLink *link);
-static void uri_build_dialog (AlmanahLink *link, GtkVBox *parent_vbox);
-static void uri_get_values (AlmanahLink *link);
+static void almanah_uri_definition_init (AlmanahURIDefinition *self);
+static gboolean uri_view (AlmanahDefinition *definition);
+static void uri_build_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox);
+static void uri_close_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox);
+static void uri_parse_text (AlmanahDefinition *definition, const gchar *text);
 
-struct _AlmanahURILinkPrivate {
+struct _AlmanahURIDefinitionPrivate {
 	GtkWidget *entry;
 };
 
-G_DEFINE_TYPE (AlmanahURILink, almanah_uri_link, ALMANAH_TYPE_LINK)
-#define ALMANAH_URI_LINK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_URI_LINK, AlmanahURILinkPrivate))
+G_DEFINE_TYPE (AlmanahURIDefinition, almanah_uri_definition, ALMANAH_TYPE_DEFINITION)
+#define ALMANAH_URI_DEFINITION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_URI_DEFINITION, AlmanahURIDefinitionPrivate))
 
 static void
-almanah_uri_link_class_init (AlmanahURILinkClass *klass)
+almanah_uri_definition_class_init (AlmanahURIDefinitionClass *klass)
 {
-	AlmanahLinkClass *link_class = ALMANAH_LINK_CLASS (klass);
+	AlmanahDefinitionClass *definition_class = ALMANAH_DEFINITION_CLASS (klass);
 
-	g_type_class_add_private (klass, sizeof (AlmanahURILinkPrivate));
+	g_type_class_add_private (klass, sizeof (AlmanahURIDefinitionPrivate));
 
-	link_class->type_id = "uri";
-	link_class->name = _("URI");
-	link_class->description = _("A URI of a file or web page.");
-	link_class->icon_name = "applications-internet";
+	definition_class->type_id = ALMANAH_DEFINITION_URI;
+	definition_class->name = _("URI");
+	definition_class->description = _("A URI of a file or web page.");
+	definition_class->icon_name = "applications-internet";
 
-	link_class->format_value = uri_format_value;
-	link_class->view = uri_view;
-	link_class->build_dialog = uri_build_dialog;
-	link_class->get_values = uri_get_values;
+	definition_class->view = uri_view;
+	definition_class->build_dialog = uri_build_dialog;
+	definition_class->close_dialog = uri_close_dialog;
+	definition_class->parse_text = uri_parse_text;
 }
 
 static void
-almanah_uri_link_init (AlmanahURILink *self)
+almanah_uri_definition_init (AlmanahURIDefinition *self)
 {
-	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_URI_LINK, AlmanahURILinkPrivate);
-}
-
-static gchar *
-uri_format_value (AlmanahLink *link)
-{
-	return g_strdup (almanah_link_get_value (link));
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_URI_DEFINITION, AlmanahURIDefinitionPrivate);
 }
 
 static gboolean
-uri_view (AlmanahLink *link)
+uri_view (AlmanahDefinition *definition)
 {
 	GError *error = NULL;
-	const gchar *value = almanah_link_get_value (link);
+	const gchar *value = almanah_definition_get_value (definition);
 
 	if (gtk_show_uri (gtk_widget_get_screen (almanah->main_window), value, GDK_CURRENT_TIME, &error) == FALSE) {
 		GtkWidget *dialog = gtk_message_dialog_new (GTK_WINDOW (almanah->main_window),
@@ -92,11 +86,12 @@
 }
 
 static void
-uri_build_dialog (AlmanahLink *link, GtkVBox *parent_vbox)
+uri_build_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox)
 {
 	GtkWidget *label, *hbox;
 	AtkObject *a11y_label, *a11y_entry;
-	AlmanahURILinkPrivate *priv = ALMANAH_URI_LINK (link)->priv;
+	AlmanahURIDefinitionPrivate *priv = ALMANAH_URI_DEFINITION (definition)->priv;
+	const gchar *value = almanah_definition_get_value (definition);
 
 	hbox = gtk_hbox_new (FALSE, 5);
 
@@ -114,15 +109,25 @@
 	atk_object_add_relationship (a11y_label, ATK_RELATION_LABEL_FOR, a11y_entry);
 	atk_object_add_relationship (a11y_entry, ATK_RELATION_LABELLED_BY, a11y_label);
 
+	/* Initialise the dialogue with the definition's current values */
+	if (value != NULL)
+		gtk_entry_set_text (GTK_ENTRY (priv->entry), value);
+
 	gtk_box_pack_start (GTK_BOX (parent_vbox), hbox, TRUE, TRUE, 0);
 	gtk_widget_show_all (GTK_WIDGET (parent_vbox));
 }
 
 static void
-uri_get_values (AlmanahLink *link)
+uri_close_dialog (AlmanahDefinition *definition, GtkVBox *parent_vbox)
 {
-	AlmanahURILinkPrivate *priv = ALMANAH_URI_LINK (link)->priv;
+	AlmanahURIDefinitionPrivate *priv = ALMANAH_URI_DEFINITION (definition)->priv;
 
-	almanah_link_set_value (link, gtk_entry_get_text (GTK_ENTRY (priv->entry)));
-	almanah_link_set_value2 (link, NULL);
+	almanah_definition_set_value (definition, gtk_entry_get_text (GTK_ENTRY (priv->entry)));
+	almanah_definition_set_value2 (definition, NULL);
+}
+
+static void
+uri_parse_text (AlmanahDefinition *definition, const gchar *text)
+{
+	/* TODO */
 }

Modified: trunk/src/definitions/uri.h
==============================================================================
--- /trunk/src/links/uri.h	(original)
+++ trunk/src/definitions/uri.h	Sat Dec  6 20:53:07 2008
@@ -17,36 +17,36 @@
  * along with Almanah.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#ifndef ALMANAH_URI_LINK_H
-#define ALMANAH_URI_LINK_H
+#ifndef ALMANAH_URI_DEFINITION_H
+#define ALMANAH_URI_DEFINITION_H
 
 #include <glib.h>
 #include <glib-object.h>
 
-#include "../link.h"
+#include "../definition.h"
 
 G_BEGIN_DECLS
 
-#define ALMANAH_TYPE_URI_LINK		(almanah_uri_link_get_type ())
-#define ALMANAH_URI_LINK(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_URI_LINK, AlmanahURILink))
-#define ALMANAH_URI_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_URI_LINK, AlmanahURILinkClass))
-#define ALMANAH_IS_URI_LINK(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_URI_LINK))
-#define ALMANAH_IS_URI_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_URI_LINK))
-#define ALMANAH_URI_LINK_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_URI_LINK, AlmanahURILinkClass))
+#define ALMANAH_TYPE_URI_DEFINITION		(almanah_uri_definition_get_type ())
+#define ALMANAH_URI_DEFINITION(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_URI_DEFINITION, AlmanahURIDefinition))
+#define ALMANAH_URI_DEFINITION_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_URI_DEFINITION, AlmanahURIDefinitionClass))
+#define ALMANAH_IS_URI_DEFINITION(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_URI_DEFINITION))
+#define ALMANAH_IS_URI_DEFINITION_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_URI_DEFINITION))
+#define ALMANAH_URI_DEFINITION_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_URI_DEFINITION, AlmanahURIDefinitionClass))
 
-typedef struct _AlmanahURILinkPrivate	AlmanahURILinkPrivate;
+typedef struct _AlmanahURIDefinitionPrivate	AlmanahURIDefinitionPrivate;
 
 typedef struct {
-	AlmanahLink parent;
-	AlmanahURILinkPrivate *priv;
-} AlmanahURILink;
+	AlmanahDefinition parent;
+	AlmanahURIDefinitionPrivate *priv;
+} AlmanahURIDefinition;
 
 typedef struct {
-	AlmanahLinkClass parent;
-} AlmanahURILinkClass;
+	AlmanahDefinitionClass parent;
+} AlmanahURIDefinitionClass;
 
-GType almanah_uri_link_get_type (void);
+GType almanah_uri_definition_get_type (void);
 
 G_END_DECLS
 
-#endif /* !ALMANAH_URI_LINK_H */
+#endif /* !ALMANAH_URI_DEFINITION_H */

Modified: trunk/src/entry.h
==============================================================================
--- trunk/src/entry.h	(original)
+++ trunk/src/entry.h	Sat Dec  6 20:53:07 2008
@@ -54,10 +54,12 @@
 
 GType almanah_entry_get_type (void);
 AlmanahEntry *almanah_entry_new (GDate *date);
+
 const guint8 *almanah_entry_get_data (AlmanahEntry *self, gsize *length);
 void almanah_entry_set_data (AlmanahEntry *self, const guint8 *data, gsize length);
 gboolean almanah_entry_get_content (AlmanahEntry *self, GtkTextBuffer *text_buffer, gboolean create_tags, GError **error);
 void almanah_entry_set_content (AlmanahEntry *self, GtkTextBuffer *text_buffer);
+
 void almanah_entry_get_date (AlmanahEntry *self, GDate *date);
 AlmanahEntryEditability almanah_entry_get_editability (AlmanahEntry *self);
 gboolean almanah_entry_is_empty (AlmanahEntry *self);

Modified: trunk/src/interface.c
==============================================================================
--- trunk/src/interface.c	(original)
+++ trunk/src/interface.c	Sat Dec  6 20:53:07 2008
@@ -23,7 +23,7 @@
 
 #include "main.h"
 #include "main-window.h"
-#include "add-link-dialog.h"
+#include "add-definition-dialog.h"
 #include "search-dialog.h"
 #ifdef ENABLE_ENCRYPTION
 #include "preferences-dialog.h"
@@ -43,7 +43,7 @@
 almanah_create_interface (void)
 {
 	almanah->main_window = GTK_WIDGET (almanah_main_window_new ());
-	almanah->add_link_dialog = GTK_WIDGET (almanah_add_link_dialog_new ());
+	almanah->add_definition_dialog = GTK_WIDGET (almanah_add_definition_dialog_new ());
 	almanah->search_dialog = GTK_WIDGET (almanah_search_dialog_new ());
 #ifdef ENABLE_ENCRYPTION
 	almanah->preferences_dialog = GTK_WIDGET (almanah_preferences_dialog_new ());
@@ -62,6 +62,63 @@
 	g_free (markup);
 }
 
+static gboolean
+definition_tag_event_cb (GtkTextTag *tag, GObject *object, GdkEvent *event, GtkTextIter *iter, gpointer user_data)
+{
+	AlmanahDefinition *definition;
+	gchar *text;
+	GtkTextIter start_iter, end_iter;
+
+	/* TODO: Display a popup menu on right-clicking? Display a list of definitions, or allow this one to be edited, when Ctrl clicking? */
+	/* Handle only double- or control-click events on any definition tags, so they can act like hyperlinks */
+	if ((event->type != GDK_BUTTON_RELEASE && event->type != GDK_2BUTTON_PRESS) ||
+	    (event->type == GDK_BUTTON_RELEASE && !(event->button.state & GDK_CONTROL_MASK))) {
+		return FALSE;
+	}
+
+	/* Get the start and end iters for this tag instance */
+	start_iter = *iter;
+	if (gtk_text_iter_backward_to_tag_toggle (&start_iter, tag) == FALSE)
+		start_iter = *iter;
+
+	end_iter = start_iter;
+	if (gtk_text_iter_forward_to_tag_toggle (&end_iter, tag) == FALSE)
+		end_iter = *iter;
+
+	/* Get the tag's text */
+	text = gtk_text_iter_get_text (&start_iter, &end_iter);
+	definition = almanah_storage_manager_get_definition (almanah->storage_manager, text);
+	g_free (text);
+
+	if (definition == NULL)
+		return FALSE;
+
+	return almanah_definition_view (definition);
+}
+
+void
+almanah_interface_create_text_tags (GtkTextBuffer *text_buffer, gboolean connect_events)
+{
+	GtkTextTag *tag;
+
+	gtk_text_buffer_create_tag (text_buffer, "bold", 
+				    "weight", PANGO_WEIGHT_BOLD, 
+				    NULL);
+	gtk_text_buffer_create_tag (text_buffer, "italic",
+				    "style", PANGO_STYLE_ITALIC,
+				    NULL);
+	gtk_text_buffer_create_tag (text_buffer, "underline",
+				    "underline", PANGO_UNDERLINE_SINGLE,
+				    NULL);
+	tag = gtk_text_buffer_create_tag (text_buffer, "definition",
+					  "foreground", "blue",
+					  "underline", PANGO_UNDERLINE_SINGLE,
+					  NULL);
+
+	if (connect_events == TRUE)
+		g_signal_connect (tag, "event", G_CALLBACK (definition_tag_event_cb), NULL);
+}
+
 /* TODO: This exists so that different calendars can be highlighted according to which days have entries
  * (i.e. the ones on the print dialogue). This should eventually be replaced by a custom calendar widget. */
 void

Modified: trunk/src/interface.h
==============================================================================
--- trunk/src/interface.h	(original)
+++ trunk/src/interface.h	Sat Dec  6 20:53:07 2008
@@ -27,6 +27,7 @@
 const gchar *almanah_get_interface_filename (void);
 GtkWidget *almanah_create_interface (void);
 void almanah_interface_embolden_label (GtkLabel *label);
+void almanah_interface_create_text_tags (GtkTextBuffer *text_buffer, gboolean connect_events);
 void almanah_calendar_month_changed_cb (GtkCalendar *calendar, gpointer user_data);
 
 G_END_DECLS

Added: trunk/src/link-factories/calendar-client.c
==============================================================================
--- (empty file)
+++ trunk/src/link-factories/calendar-client.c	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,2170 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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:
+ *     Mark McLoughlin  <mark skynet ie>
+ *     William Jon McCann  <mccann jhu edu>
+ *     Martin Grimme  <martin pycage de>
+ *     Christian Kellner  <gicmo xatom net>
+ */
+
+#include <config.h>
+
+#include "calendar-client.h"
+
+#include <libintl.h>
+#include <string.h>
+#define HANDLE_LIBICAL_MEMORY
+#include <libecal/e-cal.h>
+#include <libecal/e-cal-time-util.h>
+#include <libecal/e-cal-recur.h>
+
+#include "calendar-sources.h"
+
+#undef CALENDAR_ENABLE_DEBUG
+#include "calendar-debug.h"
+
+#define CALENDAR_CONFIG_PREFIX   "/apps/evolution/calendar"
+#define CALENDAR_CONFIG_TIMEZONE CALENDAR_CONFIG_PREFIX "/display/timezone"
+
+#ifndef _
+#define _(x) gettext(x)
+#endif
+
+#ifndef N_
+#define N_(x) x
+#endif
+
+#define CALENDAR_CLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CALENDAR_TYPE_CLIENT, CalendarClientPrivate))
+
+typedef struct _CalendarClientQuery  CalendarClientQuery;
+typedef struct _CalendarClientSource CalendarClientSource;
+
+struct _CalendarClientQuery
+{
+  ECalView   *view;
+  GHashTable *events;
+};
+
+struct _CalendarClientSource
+{
+  CalendarClient      *client;
+  ECal                *source;
+
+  CalendarClientQuery  completed_query;
+  CalendarClientQuery  in_progress_query;
+
+  guint                changed_signal_id;
+
+  guint                query_completed : 1;
+  guint                query_in_progress : 1;
+};
+
+struct _CalendarClientPrivate
+{
+  CalendarSources     *calendar_sources;
+
+  GSList              *appointment_sources;
+  GSList              *task_sources;
+
+  icaltimezone        *zone;
+
+  guint                zone_listener;
+  GConfClient         *gconf_client;
+
+  guint                day;
+  guint                month;
+  guint                year;
+};
+
+static void calendar_client_class_init   (CalendarClientClass *klass);
+static void calendar_client_init         (CalendarClient      *client);
+static void calendar_client_finalize     (GObject             *object);
+static void calendar_client_set_property (GObject             *object,
+					  guint                prop_id,
+					  const GValue        *value,
+					  GParamSpec          *pspec);
+static void calendar_client_get_property (GObject             *object,
+					  guint                prop_id,
+					  GValue              *value,
+					  GParamSpec          *pspec);
+
+static GSList *calendar_client_update_sources_list         (CalendarClient       *client,
+							    GSList               *sources,
+							    GSList               *esources,
+							    guint                 changed_signal_id);
+static void    calendar_client_appointment_sources_changed (CalendarClient       *client);
+static void    calendar_client_task_sources_changed        (CalendarClient       *client);
+
+static void calendar_client_stop_query  (CalendarClient       *client,
+					 CalendarClientSource *source,
+					 CalendarClientQuery  *query);
+static void calendar_client_start_query (CalendarClient       *client,
+					 CalendarClientSource *source,
+					 const char           *query);
+
+static void calendar_client_source_finalize (CalendarClientSource *source);
+static void calendar_client_query_finalize  (CalendarClientQuery  *query);
+
+static void
+calendar_client_update_appointments (CalendarClient *client);
+static void
+calendar_client_update_tasks (CalendarClient *client);
+
+enum
+{
+  PROP_O,
+  PROP_DAY,
+  PROP_MONTH,
+  PROP_YEAR
+};
+
+enum
+{
+  APPOINTMENTS_CHANGED,
+  TASKS_CHANGED,
+  LAST_SIGNAL
+};
+
+static GObjectClass *parent_class = NULL;
+static guint         signals [LAST_SIGNAL] = { 0, };
+
+GType
+calendar_client_get_type (void)
+{
+  static GType client_type = 0;
+  
+  if (!client_type)
+    {
+      static const GTypeInfo client_info =
+      {
+	sizeof (CalendarClientClass),
+	NULL,		/* base_init */
+	NULL,		/* base_finalize */
+	(GClassInitFunc) calendar_client_class_init,
+	NULL,           /* class_finalize */
+	NULL,		/* class_data */
+	sizeof (CalendarClient),
+	0,		/* n_preallocs */
+	(GInstanceInitFunc) calendar_client_init,
+      };
+      
+      client_type = g_type_register_static (G_TYPE_OBJECT,
+					    "CalendarClient",
+					    &client_info, 0);
+    }
+  
+  return client_type;
+}
+
+static void
+calendar_client_class_init (CalendarClientClass *klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->finalize     = calendar_client_finalize;
+  gobject_class->set_property = calendar_client_set_property;
+  gobject_class->get_property = calendar_client_get_property;
+
+  g_type_class_add_private (klass, sizeof (CalendarClientPrivate));
+
+  g_object_class_install_property (gobject_class,
+				   PROP_DAY,
+				   g_param_spec_uint ("day",
+						      "Day",
+						      "The currently monitored day between 1 and 31 (0 denotes unset)",
+						      0, G_MAXUINT, 0,
+						      G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+				   PROP_MONTH,
+				   g_param_spec_uint ("month",
+						      "Month",
+						      "The currently monitored month between 0 and 11",
+						      0, G_MAXUINT, 0,
+						      G_PARAM_READWRITE));
+
+  g_object_class_install_property (gobject_class,
+				   PROP_YEAR,
+				   g_param_spec_uint ("year",
+						      "Year",
+						      "The currently monitored year",
+						      0, G_MAXUINT, 0,
+						      G_PARAM_READWRITE));
+
+  signals [APPOINTMENTS_CHANGED] =
+    g_signal_new ("appointments-changed",
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (CalendarClientClass, tasks_changed),
+		  NULL,
+		  NULL,
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE,
+		  0);
+
+  signals [TASKS_CHANGED] =
+    g_signal_new ("tasks-changed",
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (CalendarClientClass, tasks_changed),
+		  NULL,
+		  NULL,
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE,
+		  0);
+}
+
+/* Timezone code adapted from evolution/calendar/gui/calendar-config.c */
+/* The current timezone, e.g. "Europe/London". It may be NULL, in which case
+   you should assume UTC. */
+static gchar *
+calendar_client_config_get_timezone (GConfClient *gconf_client)
+{
+  char *location;
+
+  location = gconf_client_get_string (gconf_client,
+                                      CALENDAR_CONFIG_TIMEZONE,
+                                      NULL);
+
+  return location;
+}
+
+static icaltimezone *
+calendar_client_config_get_icaltimezone (GConfClient *gconf_client)
+{
+  char         *location;
+  icaltimezone *zone = NULL;
+	
+  location = calendar_client_config_get_timezone (gconf_client);
+  if (!location)
+    return icaltimezone_get_utc_timezone ();
+
+  zone = icaltimezone_get_builtin_timezone (location);
+  g_free (location);
+	
+  return zone;
+}
+
+static void
+calendar_client_set_timezone (CalendarClient *client) 
+{
+  GSList *l;
+  GSList *esources;
+
+  client->priv->zone = calendar_client_config_get_icaltimezone (client->priv->gconf_client);
+
+  esources = calendar_sources_get_appointment_sources (client->priv->calendar_sources);
+  for (l = esources; l; l = l->next) {
+    ECal *source = l->data;
+			
+    e_cal_set_default_timezone (source, client->priv->zone, NULL);
+  }
+}
+
+static void
+calendar_client_timezone_changed_cb (GConfClient    *gconf_client,
+                                     guint           id,
+                                     GConfEntry     *entry,
+                                     CalendarClient *client)
+{
+  calendar_client_set_timezone (client);
+}
+
+static void
+cal_opened_cb (ECal                 *ecal,
+               ECalendarStatus       status,
+               CalendarClientSource *cl_source)
+{
+  ECalSourceType  s_type;
+  CalendarClient *client = cl_source->client;
+
+  s_type = e_cal_get_source_type (ecal);
+
+  if (status == E_CALENDAR_STATUS_BUSY &&
+      e_cal_get_load_state (ecal) == E_CAL_LOAD_NOT_LOADED)
+    {
+      e_cal_open_async (ecal, FALSE);
+      return;
+    }
+  
+  g_signal_handlers_disconnect_by_func (ecal, cal_opened_cb, cl_source);
+
+  if (status != E_CALENDAR_STATUS_OK)
+    {
+      if (s_type == E_CAL_SOURCE_TYPE_EVENT)
+        client->priv->appointment_sources = g_slist_remove (client->priv->appointment_sources,
+                                                            cl_source);
+      else
+        client->priv->task_sources = g_slist_remove (client->priv->task_sources,
+                                                     cl_source);
+
+      calendar_client_source_finalize (cl_source);
+      g_free (cl_source);
+
+      return;
+    }
+
+  if (s_type == E_CAL_SOURCE_TYPE_EVENT)
+    calendar_client_update_appointments (client);
+  else
+    calendar_client_update_tasks (client);
+}
+
+static void
+load_calendars (CalendarClient    *client,
+                CalendarEventType  type) 
+{
+  GSList *l, *clients;
+
+  switch (type)
+    {
+      case CALENDAR_EVENT_APPOINTMENT:
+        clients = client->priv->appointment_sources;
+        break;
+      case CALENDAR_EVENT_TASK:
+        clients = client->priv->task_sources;
+        break;
+      default:
+        g_assert_not_reached ();
+    }
+
+  for (l = clients; l != NULL; l = l->next)
+    {
+      ECal *ecal;	
+      CalendarClientSource *cl_source = l->data;
+
+      ecal = cl_source->source;
+
+      if (e_cal_get_load_state (ecal) == E_CAL_LOAD_LOADED)
+        continue;
+
+      g_signal_connect (G_OBJECT (ecal), "cal_opened",
+                        G_CALLBACK (cal_opened_cb), cl_source);
+      e_cal_open_async (ecal, TRUE);
+    }
+}
+
+static void
+calendar_client_init (CalendarClient *client)
+{
+  GSList *esources;
+
+  client->priv = CALENDAR_CLIENT_GET_PRIVATE (client);
+
+  client->priv->calendar_sources = calendar_sources_get ();
+  client->priv->gconf_client = gconf_client_get_default ();
+
+  esources = calendar_sources_get_appointment_sources (client->priv->calendar_sources);
+  client->priv->appointment_sources =
+    calendar_client_update_sources_list (client, NULL, esources, signals [APPOINTMENTS_CHANGED]);
+
+  esources = calendar_sources_get_task_sources (client->priv->calendar_sources);
+  client->priv->task_sources =
+    calendar_client_update_sources_list (client, NULL, esources, signals [TASKS_CHANGED]);
+ 
+  /* set the timezone before loading the clients */ 
+  calendar_client_set_timezone (client);
+  load_calendars (client, CALENDAR_EVENT_APPOINTMENT);
+  load_calendars (client, CALENDAR_EVENT_TASK);
+
+  g_signal_connect_swapped (client->priv->calendar_sources,
+			    "appointment-sources-changed",
+			    G_CALLBACK (calendar_client_appointment_sources_changed),
+			    client);
+  g_signal_connect_swapped (client->priv->calendar_sources,
+			    "task-sources-changed",
+			    G_CALLBACK (calendar_client_task_sources_changed),
+			    client);
+
+  gconf_client_add_dir (client->priv->gconf_client,
+			CALENDAR_CONFIG_PREFIX,
+			GCONF_CLIENT_PRELOAD_NONE,
+			NULL);
+
+  client->priv->zone_listener = gconf_client_notify_add (client->priv->gconf_client,
+                                                         CALENDAR_CONFIG_TIMEZONE,
+                                                         (GConfClientNotifyFunc) calendar_client_timezone_changed_cb,
+                                                         client, NULL, NULL);
+
+  client->priv->day   = -1;
+  client->priv->month = -1;
+  client->priv->year  = -1;
+}
+
+static void
+calendar_client_finalize (GObject *object)
+{
+  CalendarClient *client = CALENDAR_CLIENT (object);
+  GSList         *l;
+
+  if (client->priv->zone_listener)
+    {
+      gconf_client_notify_remove (client->priv->gconf_client,
+                                  client->priv->zone_listener);
+      client->priv->zone_listener = 0;
+    }
+
+  gconf_client_remove_dir (client->priv->gconf_client,
+                           CALENDAR_CONFIG_PREFIX,
+                           NULL);
+
+  if (client->priv->gconf_client)
+    g_object_unref (client->priv->gconf_client);
+  client->priv->gconf_client = NULL;
+
+  for (l = client->priv->appointment_sources; l; l = l->next)
+    {
+      calendar_client_source_finalize (l->data);
+      g_free (l->data);
+    }
+  g_slist_free (client->priv->appointment_sources);
+  client->priv->appointment_sources = NULL;
+
+  for (l = client->priv->task_sources; l; l = l->next)
+    {
+      calendar_client_source_finalize (l->data);
+      g_free (l->data);
+    }
+  g_slist_free (client->priv->task_sources);
+  client->priv->task_sources = NULL;
+
+  if (client->priv->calendar_sources)
+    g_object_unref (client->priv->calendar_sources);
+  client->priv->calendar_sources = NULL;
+
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+calendar_client_set_property (GObject      *object,
+			      guint         prop_id,
+			      const GValue *value,
+			      GParamSpec   *pspec)
+{
+  CalendarClient *client = CALENDAR_CLIENT (object);
+
+  switch (prop_id)
+    {
+    case PROP_DAY:
+      calendar_client_select_day (client, g_value_get_uint (value));
+      break;
+    case PROP_MONTH:
+      calendar_client_select_month (client,
+				    g_value_get_uint (value),
+				    client->priv->year);
+      break;
+    case PROP_YEAR:
+      calendar_client_select_month (client,
+				    client->priv->month,
+				    g_value_get_uint (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+calendar_client_get_property (GObject    *object,
+			      guint       prop_id,
+			      GValue     *value,
+			      GParamSpec *pspec)
+{
+  CalendarClient *client = CALENDAR_CLIENT (object);
+
+  switch (prop_id)
+    {
+    case PROP_DAY:
+      g_value_set_uint (value, client->priv->day);
+      break;
+    case PROP_MONTH:
+      g_value_set_uint (value, client->priv->month);
+      break;
+    case PROP_YEAR:
+      g_value_set_uint (value, client->priv->year);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+CalendarClient *
+calendar_client_new (void)
+{
+  return g_object_new (CALENDAR_TYPE_CLIENT, NULL);
+}
+
+/* @day and @month can happily be out of range as
+ * mktime() will normalize them correctly. From mktime(3):
+ *
+ * "If structure members are outside their legal interval,
+ *  they will be normalized (so that, e.g., 40 October is
+ *  changed into 9 November)."
+ *
+ * "What?", you say, "Something useful in libc?"
+ */
+static inline GTime
+make_time_for_day_begin (int day,
+			 int month,
+			 int year)
+{
+  struct tm localtime_tm = { 0, };
+
+  localtime_tm.tm_mday  = day;
+  localtime_tm.tm_mon   = month;
+  localtime_tm.tm_year  = year - 1900;
+  localtime_tm.tm_isdst = -1;
+
+  return mktime (&localtime_tm);
+}
+
+static inline char *
+make_isodate_for_day_begin (int day,
+			    int month,
+			    int year)
+{
+  GTime utctime;
+
+  utctime = make_time_for_day_begin (day, month, year);
+
+  return utctime != -1 ? isodate_from_time_t (utctime) : NULL;
+}
+
+static GTime
+get_time_from_property (icalcomponent         *ical,
+			icalproperty_kind      prop_kind,
+			struct icaltimetype (* get_prop_func) (const icalproperty *prop),
+                        icaltimezone          *default_zone)
+{
+  icalproperty        *prop;
+  struct icaltimetype  ical_time;
+  icalparameter       *param;
+  icaltimezone        *timezone = NULL;
+  
+  prop = icalcomponent_get_first_property (ical, prop_kind);
+  if (!prop)
+    return 0;
+
+  ical_time = get_prop_func (prop);
+
+  param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
+  if (param)
+    timezone = icaltimezone_get_builtin_timezone_from_tzid (icalparameter_get_tzid (param));
+  else if (icaltime_is_utc (ical_time))
+    timezone = icaltimezone_get_utc_timezone ();
+  else 
+    timezone = default_zone;
+
+  return icaltime_as_timet_with_zone (ical_time, timezone);
+}
+
+static char *
+get_ical_uid (icalcomponent *ical)
+{
+  return g_strdup (icalcomponent_get_uid (ical));
+}
+
+static char *
+get_ical_rid (icalcomponent *ical)
+{
+  icalproperty        *prop;
+  struct icaltimetype  ical_time;
+  
+  prop = icalcomponent_get_first_property (ical, ICAL_RECURRENCEID_PROPERTY);
+  if (!prop)
+    return NULL;
+
+  ical_time = icalproperty_get_recurrenceid (prop);
+
+  return icaltime_is_valid_time (ical_time) && !icaltime_is_null_time (ical_time) ? 
+    icaltime_as_ical_string (ical_time) : NULL;
+}
+
+static char *
+get_ical_summary (icalcomponent *ical)
+{
+  icalproperty *prop;
+
+  prop = icalcomponent_get_first_property (ical, ICAL_SUMMARY_PROPERTY);
+  if (!prop)
+    return NULL;
+
+  return g_strdup (icalproperty_get_summary (prop));
+}
+
+static char *
+get_ical_description (icalcomponent *ical)
+{
+  icalproperty *prop;
+
+  prop = icalcomponent_get_first_property (ical, ICAL_DESCRIPTION_PROPERTY);
+  if (!prop)
+    return NULL;
+
+  return g_strdup (icalproperty_get_description (prop));
+}
+
+static inline GTime
+get_ical_start_time (icalcomponent *ical,
+                     icaltimezone  *default_zone)
+{
+  return get_time_from_property (ical,
+				 ICAL_DTSTART_PROPERTY,
+				 icalproperty_get_dtstart,
+                                 default_zone);
+}
+
+static inline GTime
+get_ical_end_time (icalcomponent *ical,
+                   icaltimezone  *default_zone)
+{
+  return get_time_from_property (ical,
+				 ICAL_DTEND_PROPERTY,
+				 icalproperty_get_dtend,
+                                 default_zone);
+}
+
+static gboolean
+get_ical_is_all_day (icalcomponent *ical,
+                     GTime          start_gtime,
+                     icaltimezone  *default_zone)
+{
+  icalproperty            *prop;
+  time_t                   start_time;
+  struct tm               *start_tm;
+  GTime                    end_time;
+  struct icaldurationtype  duration;
+  struct icaltimetype      start_icaltime;
+
+  start_icaltime = icalcomponent_get_dtstart (ical);
+  if (start_icaltime.is_date)
+    return TRUE;
+
+  start_time = (time_t) start_gtime;
+  start_tm = gmtime (&start_time);
+  if (start_tm->tm_sec  != 0 ||
+      start_tm->tm_min  != 0 ||
+      start_tm->tm_hour != 0)
+    return FALSE;
+
+  if ((end_time = get_ical_end_time (ical, default_zone)))
+    return (end_time - start_time) % 86400 == 0;
+
+  prop = icalcomponent_get_first_property (ical, ICAL_DURATION_PROPERTY);
+  if (!prop)
+    return FALSE;
+
+  duration = icalproperty_get_duration (prop);
+
+  return icaldurationtype_as_int (duration) % 86400 == 0;
+}
+
+static inline GTime
+get_ical_due_time (icalcomponent *ical,
+                   icaltimezone  *default_zone)
+{
+  return get_time_from_property (ical,
+				 ICAL_DUE_PROPERTY,
+				 icalproperty_get_due,
+                                 default_zone);
+}
+
+static guint
+get_ical_percent_complete (icalcomponent *ical)
+{
+  icalproperty *prop;
+  icalproperty_status status;
+  int           percent_complete;
+
+  status = icalcomponent_get_status (ical);
+  if (status == ICAL_STATUS_COMPLETED)
+    return 100;
+
+  prop = icalcomponent_get_first_property (ical, ICAL_COMPLETED_PROPERTY);
+  if (prop)
+    return 100;
+
+  prop = icalcomponent_get_first_property (ical, ICAL_PERCENTCOMPLETE_PROPERTY);
+  if (!prop)
+    return 0;
+
+  percent_complete = icalproperty_get_percentcomplete (prop);
+
+  return CLAMP (percent_complete, 0, 100);
+}
+
+static inline GTime
+get_ical_completed_time (icalcomponent *ical,
+                         icaltimezone  *default_zone)
+{
+  return get_time_from_property (ical,
+				 ICAL_COMPLETED_PROPERTY,
+				 icalproperty_get_completed,
+                                 default_zone);
+}
+
+static int
+get_ical_priority (icalcomponent *ical)
+{
+  icalproperty *prop;
+
+  prop = icalcomponent_get_first_property (ical, ICAL_PRIORITY_PROPERTY);
+  if (!prop)
+    return -1;
+
+  return icalproperty_get_priority (prop);
+}
+
+static char *
+get_source_color (ECal *esource)
+{
+  ESource *source;
+
+  g_return_val_if_fail (E_IS_CAL (esource), NULL);
+
+  source = e_cal_get_source (esource);
+
+  return g_strdup (e_source_peek_color_spec (source));
+}
+
+static gchar *
+get_source_uri (ECal *esource)
+{
+    ESource *source;
+    gchar   *string;
+    gchar  **list;
+
+    g_return_val_if_fail (E_IS_CAL (esource), NULL);
+
+    source = e_cal_get_source (esource);
+    string = g_strdup (e_source_get_uri (source));
+    if (string) {
+        list = g_strsplit (string, ":", 2);
+        g_free (string);
+
+        if (list[0]) {
+            string = g_strdup (list[0]);
+            g_strfreev (list);
+            return string;
+        }
+	g_strfreev (list);
+    }
+    return NULL;
+}
+
+static inline int
+null_safe_strcmp (const char *a,
+		  const char *b)
+{
+  return (!a && !b) ? 0 : (a && !b) || (!a && b) ? 1 : strcmp (a, b);
+}
+
+static inline gboolean
+calendar_appointment_equal (CalendarAppointment *a,
+			    CalendarAppointment *b)
+{
+  GSList *la, *lb;
+
+  if (g_slist_length (a->occurrences) != g_slist_length (b->occurrences))
+      return FALSE;
+
+  for (la = a->occurrences, lb = b->occurrences; la && lb; la = la->next, lb = lb->next)
+    {
+      CalendarOccurrence *oa = la->data;
+      CalendarOccurrence *ob = lb->data;
+
+      if (oa->start_time != ob->start_time ||
+	  oa->end_time   != ob->end_time)
+	return FALSE;
+    }
+
+  return
+    null_safe_strcmp (a->uid,          b->uid)          == 0 &&
+    null_safe_strcmp (a->uri,          b->uri)          == 0 &&
+    null_safe_strcmp (a->summary,      b->summary)      == 0 &&
+    null_safe_strcmp (a->description,  b->description)  == 0 &&
+    null_safe_strcmp (a->color_string, b->color_string) == 0 &&
+    a->start_time == b->start_time                         &&
+    a->end_time   == b->end_time                           &&
+    a->is_all_day == b->is_all_day;
+}
+
+static void
+calendar_appointment_copy (CalendarAppointment *appointment,
+			   CalendarAppointment *appointment_copy)
+{
+  GSList *l;
+
+  g_assert (appointment != NULL);
+  g_assert (appointment_copy != NULL);
+
+  appointment_copy->occurrences = g_slist_copy (appointment->occurrences);
+  for (l = appointment_copy->occurrences; l; l = l->next)
+    {
+      CalendarOccurrence *occurrence = l->data;
+      CalendarOccurrence *occurrence_copy;
+
+      occurrence_copy             = g_new0 (CalendarOccurrence, 1);
+      occurrence_copy->start_time = occurrence->start_time;
+      occurrence_copy->end_time   = occurrence->end_time;
+
+      l->data = occurrence_copy;
+    }
+
+  appointment_copy->uid          = g_strdup (appointment->uid);
+  appointment_copy->uri          = g_strdup (appointment->uri);
+  appointment_copy->summary      = g_strdup (appointment->summary);
+  appointment_copy->description  = g_strdup (appointment->description);
+  appointment_copy->color_string = g_strdup (appointment->color_string);
+  appointment_copy->start_time   = appointment->start_time;
+  appointment_copy->end_time     = appointment->end_time;
+  appointment_copy->is_all_day   = appointment->is_all_day;
+}
+
+static void
+calendar_appointment_finalize (CalendarAppointment *appointment)
+{
+  GSList *l;
+
+  for (l = appointment->occurrences; l; l = l->next)
+    g_free (l->data);
+  g_slist_free (appointment->occurrences);
+  appointment->occurrences = NULL;
+
+  g_free (appointment->uid);
+  appointment->uid = NULL;
+
+  g_free (appointment->rid);
+  appointment->rid = NULL;
+
+  g_free (appointment->uri);
+  appointment->uri = NULL;
+
+  g_free (appointment->summary);
+  appointment->summary = NULL;
+
+  g_free (appointment->description);
+  appointment->description = NULL;
+
+  g_free (appointment->color_string);
+  appointment->color_string = NULL;
+
+  appointment->start_time = 0;
+  appointment->is_all_day = FALSE;
+}
+
+static void
+calendar_appointment_init (CalendarAppointment  *appointment,
+			   icalcomponent        *ical,
+                           CalendarClientSource *source,
+                           icaltimezone         *default_zone)
+{
+  appointment->uid          = get_ical_uid (ical);
+  appointment->rid          = get_ical_rid (ical);
+  appointment->uri          = get_source_uri (source->source);
+  appointment->summary      = get_ical_summary (ical);
+  appointment->description  = get_ical_description (ical);
+  appointment->color_string = get_source_color (source->source);
+  appointment->start_time   = get_ical_start_time (ical, default_zone);
+  appointment->end_time     = get_ical_end_time (ical, default_zone);
+  appointment->is_all_day   = get_ical_is_all_day (ical,
+                                                   appointment->start_time,
+                                                   default_zone);
+}
+
+static icaltimezone *
+resolve_timezone_id (const char *tzid,
+		     ECal       *source)
+{
+  icaltimezone *retval;
+
+  retval = icaltimezone_get_builtin_timezone_from_tzid (tzid);
+  if (!retval)
+    {
+      e_cal_get_timezone (source, tzid, &retval, NULL);
+    }
+
+  return retval;
+}
+
+static gboolean
+calendar_appointment_collect_occurrence (ECalComponent  *component,
+					 GTime           occurrence_start,
+					 GTime           occurrence_end,
+					 GSList        **collect_loc)
+{
+  CalendarOccurrence *occurrence;
+
+  occurrence             = g_new0 (CalendarOccurrence, 1);
+  occurrence->start_time = occurrence_start;
+  occurrence->end_time   = occurrence_end;
+
+  *collect_loc = g_slist_prepend (*collect_loc, occurrence);
+
+  return TRUE;
+}
+
+static void
+calendar_appointment_generate_ocurrences (CalendarAppointment *appointment,
+					  icalcomponent       *ical,
+					  ECal                *source,
+					  GTime                start,
+					  GTime                end,
+                                          icaltimezone        *default_zone)
+{
+  ECalComponent *ecal;
+
+  g_assert (appointment->occurrences == NULL);
+
+  ecal = e_cal_component_new ();
+  e_cal_component_set_icalcomponent (ecal,
+				     icalcomponent_new_clone (ical));
+
+  e_cal_recur_generate_instances (ecal,
+				  start,
+				  end,
+				  (ECalRecurInstanceFn) calendar_appointment_collect_occurrence,
+				  &appointment->occurrences,
+				  (ECalRecurResolveTimezoneFn) resolve_timezone_id,
+				  source,
+				  default_zone);
+
+  g_object_unref (ecal);
+
+  appointment->occurrences = g_slist_reverse (appointment->occurrences);
+}
+
+static inline gboolean
+calendar_task_equal (CalendarTask *a,
+		     CalendarTask *b)
+{
+  return
+    null_safe_strcmp (a->uid,          b->uid)          == 0 &&
+    null_safe_strcmp (a->summary,      b->summary)      == 0 &&
+    null_safe_strcmp (a->description,  b->description)  == 0 &&
+    null_safe_strcmp (a->color_string, b->color_string) == 0 &&
+    a->start_time       == b->start_time                   &&
+    a->due_time         == b->due_time                     &&
+    a->percent_complete == b->percent_complete             &&
+    a->completed_time   == b->completed_time               &&
+    a->priority         == b->priority;
+}
+
+static void
+calendar_task_copy (CalendarTask *task,
+		    CalendarTask *task_copy)
+{
+  g_assert (task != NULL);
+  g_assert (task_copy != NULL);
+
+  task_copy->uid              = g_strdup (task->uid);
+  task_copy->summary          = g_strdup (task->summary);
+  task_copy->description      = g_strdup (task->description);
+  task_copy->color_string     = g_strdup (task->color_string);
+  task_copy->start_time       = task->start_time;
+  task_copy->due_time         = task->due_time;
+  task_copy->percent_complete = task->percent_complete;
+  task_copy->completed_time   = task->completed_time;
+  task_copy->priority         = task->priority;
+}
+
+static void
+calendar_task_finalize (CalendarTask *task)
+{
+  g_free (task->uid);
+  task->uid = NULL;
+
+  g_free (task->summary);
+  task->summary = NULL;
+
+  g_free (task->description);
+  task->description = NULL;
+
+  g_free (task->color_string);
+  task->color_string = NULL;
+
+  task->percent_complete = 0;
+}
+
+static void
+calendar_task_init (CalendarTask         *task,
+		    icalcomponent        *ical,
+                    CalendarClientSource *source,
+                    icaltimezone         *default_zone)
+{
+  task->uid              = get_ical_uid (ical);
+  task->summary          = get_ical_summary (ical);
+  task->description      = get_ical_description (ical);
+  task->color_string     = get_source_color (source->source);
+  task->start_time       = get_ical_start_time (ical, default_zone);
+  task->due_time         = get_ical_due_time (ical, default_zone);
+  task->percent_complete = get_ical_percent_complete (ical);
+  task->completed_time   = get_ical_completed_time (ical, default_zone);
+  task->priority         = get_ical_priority (ical);
+}
+
+void
+calendar_event_free (CalendarEvent *event)
+{
+  switch (event->type)
+    {
+    case CALENDAR_EVENT_APPOINTMENT:
+      calendar_appointment_finalize (CALENDAR_APPOINTMENT (event));
+      break;
+    case CALENDAR_EVENT_TASK:
+      calendar_task_finalize (CALENDAR_TASK (event));
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  g_free (event);
+}
+
+static CalendarEvent *
+calendar_event_new (icalcomponent        *ical,
+                    CalendarClientSource *source,
+                    icaltimezone         *default_zone)
+{
+  CalendarEvent *event;
+
+  event = g_new0 (CalendarEvent, 1);
+
+  switch (icalcomponent_isa (ical))
+    {
+    case ICAL_VEVENT_COMPONENT:
+      event->type = CALENDAR_EVENT_APPOINTMENT;
+      calendar_appointment_init (CALENDAR_APPOINTMENT (event),
+                                 ical,
+                                 source,
+                                 default_zone);
+      break;
+    case ICAL_VTODO_COMPONENT:
+      event->type = CALENDAR_EVENT_TASK;
+      calendar_task_init (CALENDAR_TASK (event),
+                          ical,
+                          source,
+                          default_zone);
+      break;
+    default:
+      g_warning ("Unknown calendar component type: %d\n",
+                 icalcomponent_isa (ical));
+      g_free (event);
+      return NULL;
+    }
+
+  return event;
+}
+
+static CalendarEvent *
+calendar_event_copy (CalendarEvent *event)
+{
+  CalendarEvent *retval;
+
+  if (!event)
+    return NULL;
+
+  retval = g_new0 (CalendarEvent, 1);
+
+  retval->type = event->type;
+
+  switch (event->type)
+    {
+    case CALENDAR_EVENT_APPOINTMENT:
+      calendar_appointment_copy (CALENDAR_APPOINTMENT (event),
+				 CALENDAR_APPOINTMENT (retval));
+      break;
+    case CALENDAR_EVENT_TASK:
+      calendar_task_copy (CALENDAR_TASK (event),
+			  CALENDAR_TASK (retval));
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  return retval;
+}
+
+static char *
+calendar_event_get_uid (CalendarEvent *event)
+{
+  switch (event->type)
+    {
+    case CALENDAR_EVENT_APPOINTMENT:
+      return g_strdup_printf ("%s%s", CALENDAR_APPOINTMENT (event)->uid, CALENDAR_APPOINTMENT (event)->rid ? CALENDAR_APPOINTMENT (event)->rid : ""); 
+      break;
+    case CALENDAR_EVENT_TASK:
+      return g_strdup (CALENDAR_TASK (event)->uid);
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  return NULL;
+}
+
+static gboolean
+calendar_event_equal (CalendarEvent *a,
+		      CalendarEvent *b)
+{
+  if (!a && !b)
+    return TRUE;
+
+  if ((a && !b) || (!a && b))
+    return FALSE;
+
+  if (a->type != b->type)
+    return FALSE;
+
+  switch (a->type)
+    {
+    case CALENDAR_EVENT_APPOINTMENT:
+      return calendar_appointment_equal (CALENDAR_APPOINTMENT (a),
+					 CALENDAR_APPOINTMENT (b));
+    case CALENDAR_EVENT_TASK:
+      return calendar_task_equal (CALENDAR_TASK (a),
+				  CALENDAR_TASK (b));
+    default:
+      break;
+    }
+ 
+  g_assert_not_reached ();
+
+  return FALSE;
+}
+
+static void
+calendar_event_generate_ocurrences (CalendarEvent *event,
+				    icalcomponent *ical,
+				    ECal          *source,
+				    GTime          start,
+				    GTime          end,
+                                    icaltimezone  *default_zone)
+{
+  if (event->type != CALENDAR_EVENT_APPOINTMENT)
+    return;
+
+  calendar_appointment_generate_ocurrences (CALENDAR_APPOINTMENT (event),
+					    ical,
+					    source,
+					    start,
+					    end,
+                                            default_zone);
+}
+
+static inline void
+calendar_event_debug_dump (CalendarEvent *event)
+{
+#ifdef CALENDAR_ENABLE_DEBUG
+  switch (event->type)
+    {
+    case CALENDAR_EVENT_APPOINTMENT:
+      {
+	char   *start_str;
+	char   *end_str;
+	GSList *l;
+
+	start_str = CALENDAR_APPOINTMENT (event)->start_time ?
+	                    isodate_from_time_t (CALENDAR_APPOINTMENT (event)->start_time) :
+	                    g_strdup ("(undefined)");
+	end_str = CALENDAR_APPOINTMENT (event)->end_time ?
+	                    isodate_from_time_t (CALENDAR_APPOINTMENT (event)->end_time) :
+	                    g_strdup ("(undefined)");
+	  
+	dprintf ("Appointment: uid '%s', summary '%s', description '%s', "
+		 "start_time '%s', end_time '%s', is_all_day %s\n",
+		 CALENDAR_APPOINTMENT (event)->uid,
+		 CALENDAR_APPOINTMENT (event)->summary,
+		 CALENDAR_APPOINTMENT (event)->description,
+		 start_str,
+		 end_str,
+		 CALENDAR_APPOINTMENT (event)->is_all_day ? "(true)" : "(false)");
+
+	g_free (start_str);
+	g_free (end_str);
+
+	dprintf ("  Occurrences:\n");
+	for (l = CALENDAR_APPOINTMENT (event)->occurrences; l; l = l->next)
+	  {
+	    CalendarOccurrence *occurrence = l->data;
+
+	    start_str = occurrence->start_time ?
+	      isodate_from_time_t (occurrence->start_time) :
+	      g_strdup ("(undefined)");
+	    
+	    end_str = occurrence->end_time ?
+	      isodate_from_time_t (occurrence->end_time) :
+	      g_strdup ("(undefined)");
+
+	    dprintf ("    start_time '%s', end_time '%s'\n",
+		     start_str, end_str);
+
+	    g_free (start_str);
+	    g_free (end_str);
+	  }
+      }
+      break;
+    case CALENDAR_EVENT_TASK:
+      {
+	char *start_str;
+	char *due_str;
+	char *completed_str;
+
+	start_str = CALENDAR_TASK (event)->start_time ?
+	                    isodate_from_time_t (CALENDAR_TASK (event)->start_time) :
+	                    g_strdup ("(undefined)");
+	due_str = CALENDAR_TASK (event)->due_time ?
+	                    isodate_from_time_t (CALENDAR_TASK (event)->due_time) :
+	                    g_strdup ("(undefined)");
+	completed_str = CALENDAR_TASK (event)->completed_time ?
+	                    isodate_from_time_t (CALENDAR_TASK (event)->completed_time) :
+	                    g_strdup ("(undefined)");
+
+	dprintf ("Task: uid '%s', summary '%s', description '%s', "
+		 "start_time '%s', due_time '%s', percent_complete %d, completed_time '%s'\n",
+		 CALENDAR_TASK (event)->uid,
+		 CALENDAR_TASK (event)->summary,
+		 CALENDAR_TASK (event)->description,
+		 start_str,
+		 due_str,
+		 CALENDAR_TASK (event)->percent_complete,
+		 completed_str);
+
+	g_free (completed_str);
+      }
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+#endif
+}
+
+static inline CalendarClientQuery *
+goddamn_this_is_crack (CalendarClientSource *source,
+		       ECalView             *view,
+		       gboolean             *emit_signal)
+{
+  g_assert (view != NULL);
+
+  if (source->completed_query.view == view)
+    {
+      if (emit_signal)
+	*emit_signal = TRUE;
+      return &source->completed_query;
+    }
+  else if (source->in_progress_query.view == view)
+    {
+      if (emit_signal)
+	*emit_signal = FALSE;
+      return &source->in_progress_query;
+    }
+
+  g_assert_not_reached ();
+
+  return NULL;
+}
+
+static void
+calendar_client_handle_query_completed (CalendarClientSource *source,
+					ECalendarStatus       status,
+					ECalView             *view)
+{
+  CalendarClientQuery *query;
+
+  query = goddamn_this_is_crack (source, view, NULL);
+  
+  dprintf ("Query %p completed: %s\n", query, e_cal_get_error_message (status));
+
+  if (status != E_CALENDAR_STATUS_OK)
+    {
+      g_warning ("Calendar query failed: %s\n",
+		 e_cal_get_error_message (status));
+      calendar_client_stop_query (source->client, source, query);
+      return;
+    }
+
+  g_assert (source->query_in_progress != FALSE);
+  g_assert (query == &source->in_progress_query);
+
+  calendar_client_query_finalize (&source->completed_query);
+
+  source->completed_query = source->in_progress_query;
+  source->query_completed = TRUE;
+
+  source->query_in_progress        = FALSE;
+  source->in_progress_query.view   = NULL;
+  source->in_progress_query.events = NULL;
+
+  g_signal_emit (source->client, source->changed_signal_id, 0);
+}
+
+static void
+calendar_client_handle_query_result (CalendarClientSource *source,
+				     GList                *objects,
+				     ECalView             *view)
+{
+  CalendarClientQuery *query;
+  CalendarClient      *client;
+  gboolean             emit_signal;
+  gboolean             events_changed;
+  GList               *l;
+  GTime                month_begin;
+  GTime                month_end;
+
+  client = source->client;
+
+  query = goddamn_this_is_crack (source, view, &emit_signal);
+
+  dprintf ("Query %p result: %d objects:\n",
+	   query, g_list_length (objects));
+
+  month_begin = make_time_for_day_begin (1,
+					 client->priv->month,
+					 client->priv->year);
+
+  month_end = make_time_for_day_begin (1,
+				       client->priv->month + 1,
+				       client->priv->year);
+
+  events_changed = FALSE;
+  for (l = objects; l; l = l->next)
+    {
+      CalendarEvent *event;
+      CalendarEvent *old_event;
+      icalcomponent *ical = l->data;
+      char          *uid;
+      
+      event = calendar_event_new (ical, source, client->priv->zone);
+      if (!event)
+	      continue;
+
+      calendar_event_generate_ocurrences (event,
+					  ical,
+					  source->source,
+					  month_begin,
+					  month_end,
+                                          client->priv->zone);
+
+      uid = calendar_event_get_uid (event);
+      
+      old_event = g_hash_table_lookup (query->events, uid);
+
+      if (!calendar_event_equal (event, old_event))
+	{
+ 	  dprintf ("Event %s: ", old_event ? "modified" : "added");
+
+	  calendar_event_debug_dump (event);
+
+	  g_hash_table_replace (query->events, uid, event);
+
+	  events_changed = TRUE;
+	}
+      else
+	{
+	  g_free (uid);
+	}		
+    }
+
+  if (emit_signal && events_changed)
+    {
+      g_signal_emit (source->client, source->changed_signal_id, 0);
+    }
+}
+
+static gboolean
+check_object_remove (gpointer key,
+                     gpointer value,
+                     gpointer data)
+{
+  char             *uid = data;
+  ssize_t           len;
+
+  len = strlen (uid);
+  
+  if (len <= strlen (key) && strncmp (uid, key, len) == 0)
+    {
+      dprintf ("Event removed: ");
+
+      calendar_event_debug_dump (value);
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+static void
+calendar_client_handle_objects_removed (CalendarClientSource *source,
+					GList                *ids,
+					ECalView             *view)
+{
+  CalendarClientQuery *query;
+  gboolean             emit_signal;
+  gboolean             events_changed;
+  GList               *l;
+
+  query = goddamn_this_is_crack (source, view, &emit_signal);
+
+  events_changed = FALSE;
+  for (l = ids; l; l = l->next)
+    {
+      CalendarEvent   *event;
+      ECalComponentId *id = l->data;
+      char            *uid = g_strdup_printf ("%s%s", id->uid, id->rid ? id->rid : "");
+
+      if (!id->rid || !(*id->rid))
+	{
+	  int size = g_hash_table_size (query->events);
+
+	  g_hash_table_foreach_remove (query->events, check_object_remove, id->uid);
+
+		if (size != g_hash_table_size (query->events))
+			events_changed = TRUE;		
+	}
+      else if ((event = g_hash_table_lookup (query->events, uid)))
+	{
+	  dprintf ("Event removed: ");
+
+	  calendar_event_debug_dump (event);
+
+	  g_assert (g_hash_table_remove (query->events, uid));
+
+	  events_changed = TRUE;
+	}
+      g_free (uid);
+    }
+
+  if (emit_signal && events_changed)
+    {
+      g_signal_emit (source->client, source->changed_signal_id, 0);
+    }
+}
+
+static void
+calendar_client_query_finalize (CalendarClientQuery *query)
+{
+  if (query->view)
+    g_object_unref (query->view);
+  query->view = NULL;
+
+  if (query->events)
+    g_hash_table_destroy (query->events);
+  query->events = NULL;
+}
+
+static void
+calendar_client_stop_query (CalendarClient       *client,
+			    CalendarClientSource *source,
+			    CalendarClientQuery  *query)
+{
+  if (query == &source->in_progress_query)
+    {
+      dprintf ("Stopping in progress query %p\n", query);
+
+      g_assert (source->query_in_progress != FALSE);
+
+      source->query_in_progress = FALSE;
+    }
+  else if (query == &source->completed_query)
+    {
+      dprintf ("Stopping completed query %p\n", query);
+
+      g_assert (source->query_completed != FALSE);
+
+      source->query_completed = FALSE;
+    }
+  else
+    g_assert_not_reached ();
+  
+  calendar_client_query_finalize (query);
+}
+
+static void
+calendar_client_start_query (CalendarClient       *client,
+			     CalendarClientSource *source,
+			     const char           *query)
+{
+  ECalView *view = NULL;
+  GError   *error = NULL;
+
+  if (!e_cal_get_query (source->source, query, &view, &error))
+    {
+      g_warning ("Error preparing the query: '%s': %s\n",
+		 query, error->message);
+      g_error_free (error);
+      return;
+    }
+
+  g_assert (view != NULL);
+
+  if (source->query_in_progress)
+    calendar_client_stop_query (client, source, &source->in_progress_query);
+  
+  dprintf ("Starting query %p: '%s'\n", &source->in_progress_query, query);
+
+  source->query_in_progress        = TRUE;
+  source->in_progress_query.view   = view;
+  source->in_progress_query.events =
+    g_hash_table_new_full (g_str_hash,
+			   g_str_equal,
+			   g_free,
+			   (GDestroyNotify) calendar_event_free);
+
+  g_signal_connect_swapped (view, "objects-added",
+			    G_CALLBACK (calendar_client_handle_query_result),
+			    source);
+  g_signal_connect_swapped (view, "objects-modified",
+			    G_CALLBACK (calendar_client_handle_query_result),
+			    source);
+  g_signal_connect_swapped (view, "objects-removed",
+			    G_CALLBACK (calendar_client_handle_objects_removed),
+			    source);
+  g_signal_connect_swapped (view, "view-done",
+			    G_CALLBACK (calendar_client_handle_query_completed),
+			    source);
+
+  e_cal_view_start (view);
+}
+
+static void
+calendar_client_update_appointments (CalendarClient *client)
+{
+  GSList *l;
+  char   *query;
+  char   *month_begin;
+  char   *month_end;
+
+  if (client->priv->month == -1 ||
+      client->priv->year  == -1)
+    return;
+
+  month_begin = make_isodate_for_day_begin (1,
+					    client->priv->month,
+					    client->priv->year);
+
+  month_end = make_isodate_for_day_begin (1,
+					  client->priv->month + 1,
+					  client->priv->year);
+
+  query = g_strdup_printf ("occur-in-time-range? (make-time \"%s\") "
+			                        "(make-time \"%s\")",
+			   month_begin, month_end);
+
+  for (l = client->priv->appointment_sources; l; l = l->next)
+    {
+      CalendarClientSource *cs = l->data;
+                  
+      if (e_cal_get_load_state (cs->source) != E_CAL_LOAD_LOADED)  
+        continue;
+
+      calendar_client_start_query (client, cs, query);
+    }
+
+  g_free (month_begin);
+  g_free (month_end);
+  g_free (query);
+}
+
+/* FIXME:
+ * perhaps we should use evo's "hide_completed_tasks" pref?
+ */
+static void
+calendar_client_update_tasks (CalendarClient *client)
+{
+  GSList *l;
+  char   *query;
+
+#ifdef FIX_BROKEN_TASKS_QUERY
+  /* FIXME: this doesn't work for tasks without a start or
+   *        due date
+   *        Look at filter_task() to see the behaviour we
+   *        want.
+   */
+  
+  char   *day_begin;
+  char   *day_end;
+
+  if (client->priv->day   == -1 ||
+      client->priv->month == -1 ||
+      client->priv->year  == -1)
+    return;
+
+  day_begin = make_isodate_for_day_begin (client->priv->day,
+					  client->priv->month,
+					  client->priv->year);
+
+  day_end = make_isodate_for_day_begin (client->priv->day + 1,
+					client->priv->month,
+					client->priv->year);
+  if (!day_begin || !day_end)
+    {
+      g_warning ("Cannot run query with invalid date: %dd %dy %dm\n",
+		 client->priv->day,
+		 client->priv->month,
+		 client->priv->year);
+      g_free (day_begin);
+      g_free (day_end);
+      return;
+    }
+  
+  query = g_strdup_printf ("(and (occur-in-time-range? (make-time \"%s\") "
+                                                      "(make-time \"%s\")) "
+                             "(or (not is-completed?) "
+                               "(and (is-completed?) "
+                                    "(not (completed-before? (make-time \"%s\"))))))",
+			   day_begin, day_end, day_begin);
+#else
+  query = g_strdup ("#t");
+#endif /* FIX_BROKEN_TASKS_QUERY */
+
+  for (l = client->priv->task_sources; l; l = l->next)
+    {
+      CalendarClientSource *cs = l->data;
+
+      if (e_cal_get_load_state (cs->source) != E_CAL_LOAD_LOADED)  
+        continue;
+
+      calendar_client_start_query (client, cs, query);
+    }
+
+#ifdef FIX_BROKEN_TASKS_QUERY
+  g_free (day_begin);
+  g_free (day_end);
+#endif
+  g_free (query);
+}
+
+static void
+calendar_client_source_finalize (CalendarClientSource *source)
+{
+  source->client = NULL;
+
+  if (source->source) {
+    g_signal_handlers_disconnect_by_func (source->source,
+                                          cal_opened_cb, source);
+    g_object_unref (source->source);
+  }
+  source->source = NULL;
+
+  calendar_client_query_finalize (&source->completed_query);
+  calendar_client_query_finalize (&source->in_progress_query);
+  
+  source->query_completed   = FALSE;
+  source->query_in_progress = FALSE;
+}
+
+static int
+compare_calendar_sources (CalendarClientSource *s1,
+			  CalendarClientSource *s2)
+{
+  return (s1->source == s2->source) ? 0 : 1;
+}
+
+static GSList *
+calendar_client_update_sources_list (CalendarClient *client,
+				     GSList         *sources,
+				     GSList         *esources,
+				     guint           changed_signal_id)
+{
+  GSList *retval, *l;
+
+  retval = NULL;
+
+  for (l = esources; l; l = l->next)
+    {
+      CalendarClientSource  dummy_source;
+      CalendarClientSource *new_source;
+      GSList               *s;
+      ECal                 *esource = l->data;
+
+      dummy_source.source = esource;
+
+      dprintf ("update_sources_list: adding client %s: ",
+	       e_source_peek_uid (e_cal_get_source (esource)));
+
+      if ((s = g_slist_find_custom (sources,
+				    &dummy_source,
+				    (GCompareFunc) compare_calendar_sources)))
+	{
+	  dprintf ("already on list\n");
+	  new_source = s->data;
+	  sources = g_slist_delete_link (sources, s);
+	}
+      else
+	{
+	  dprintf ("added\n");
+	  new_source                    = g_new0 (CalendarClientSource, 1);
+	  new_source->client            = client;
+	  new_source->source            = g_object_ref (esource);
+	  new_source->changed_signal_id = changed_signal_id;
+	}
+
+      retval = g_slist_prepend (retval, new_source);
+    }
+
+  for (l = sources; l; l = l->next)
+    {
+      CalendarClientSource *source = l->data;
+
+      dprintf ("Removing client %s from list\n",
+	       e_source_peek_uid (e_cal_get_source (source->source)));
+
+      calendar_client_source_finalize (source);
+      g_free (source);
+    }
+  g_slist_free (sources);
+
+  return retval;
+}
+
+static void
+calendar_client_appointment_sources_changed (CalendarClient  *client)
+{
+  GSList *esources;
+
+  dprintf ("appointment_sources_changed: updating ...\n");
+
+  esources = calendar_sources_get_appointment_sources (client->priv->calendar_sources);
+
+  client->priv->appointment_sources = 
+    calendar_client_update_sources_list (client,
+					 client->priv->appointment_sources,
+					 esources,
+					 signals [APPOINTMENTS_CHANGED]);
+
+  load_calendars (client, CALENDAR_EVENT_APPOINTMENT);
+  calendar_client_update_appointments (client);
+}
+
+static void
+calendar_client_task_sources_changed (CalendarClient  *client)
+{
+  GSList *esources;
+
+  dprintf ("task_sources_changed: updating ...\n");
+
+  esources = calendar_sources_get_task_sources (client->priv->calendar_sources);
+
+  client->priv->task_sources = 
+    calendar_client_update_sources_list (client,
+					 client->priv->task_sources,
+					 esources,
+					 signals [TASKS_CHANGED]);
+
+  load_calendars (client, CALENDAR_EVENT_TASK);
+  calendar_client_update_tasks (client);
+}
+
+void
+calendar_client_get_date (CalendarClient *client,
+                          guint          *year,
+                          guint          *month,
+                          guint          *day)
+{
+  g_return_if_fail (CALENDAR_IS_CLIENT (client));
+
+  if (year)
+    *year = client->priv->year;
+
+  if (month)
+    *month = client->priv->month;
+
+  if (day)
+    *day = client->priv->day;
+}
+
+void
+calendar_client_select_month (CalendarClient *client,
+			      guint           month,
+			      guint           year)
+{
+  g_return_if_fail (CALENDAR_IS_CLIENT (client));
+  g_return_if_fail (month <= 11);
+
+  if (client->priv->year != year || client->priv->month != month)
+    {
+      client->priv->month = month;
+      client->priv->year  = year;
+
+      calendar_client_update_appointments (client);
+      calendar_client_update_tasks (client);
+
+      g_object_freeze_notify (G_OBJECT (client));
+      g_object_notify (G_OBJECT (client), "month");
+      g_object_notify (G_OBJECT (client), "year");
+      g_object_thaw_notify (G_OBJECT (client));
+    }
+}
+
+void
+calendar_client_select_day (CalendarClient *client,
+			    guint           day)
+{
+  g_return_if_fail (CALENDAR_IS_CLIENT (client));
+  g_return_if_fail (day <= 31);
+
+  if (client->priv->day != day)
+    {
+      client->priv->day = day;
+
+      /* don't need to update appointments unless
+       * the selected month changes
+       */
+#ifdef FIX_BROKEN_TASKS_QUERY
+      calendar_client_update_tasks (client);
+#endif
+
+      g_object_notify (G_OBJECT (client), "day");
+    }
+}
+
+typedef struct
+{
+  CalendarClient *client;
+  GSList         *events;
+  GTime           start_time;
+  GTime           end_time;
+} FilterData;
+
+typedef void (* CalendarEventFilterFunc) (const char    *uid,
+					  CalendarEvent *event,
+					  FilterData    *filter_data);
+
+static void
+filter_appointment (const char    *uid,
+		    CalendarEvent *event,
+		    FilterData    *filter_data)
+{
+  GSList *occurrences, *l;
+
+  if (event->type != CALENDAR_EVENT_APPOINTMENT)
+    return;
+
+  occurrences = CALENDAR_APPOINTMENT (event)->occurrences;
+  CALENDAR_APPOINTMENT (event)->occurrences = NULL;
+
+  for (l = occurrences; l; l = l->next)
+    {
+      CalendarOccurrence *occurrence = l->data;
+      GTime start_time = occurrence->start_time;
+      GTime end_time   = occurrence->end_time;
+
+      if ((start_time >= filter_data->start_time &&
+           start_time < filter_data->end_time) ||
+          (start_time <= filter_data->start_time &&
+           (end_time - 1) > filter_data->start_time))
+	{
+	  CalendarEvent *new_event;
+
+	  new_event = calendar_event_copy (event);
+	      
+	  CALENDAR_APPOINTMENT (new_event)->start_time = occurrence->start_time;
+	  CALENDAR_APPOINTMENT (new_event)->end_time   = occurrence->end_time;
+	      
+	  filter_data->events = g_slist_prepend (filter_data->events, new_event);
+	}
+    }
+
+  CALENDAR_APPOINTMENT (event)->occurrences = occurrences;
+}
+
+static void
+filter_task (const char    *uid,
+	     CalendarEvent *event,
+	     FilterData    *filter_data)
+{
+#ifdef FIX_BROKEN_TASKS_QUERY
+  CalendarTask *task;
+#endif
+
+  if (event->type != CALENDAR_EVENT_TASK)
+    return;
+
+#ifdef FIX_BROKEN_TASKS_QUERY
+  task = CALENDAR_TASK (event);
+
+  if (task->start_time && task->start_time > filter_data->start_time)
+    return;
+
+  if (task->completed_time && 
+      (task->completed_time < filter_data->start_time ||
+       task->completed_time > filter_data->end_time))
+    return;
+#endif /* FIX_BROKEN_TASKS_QUERY */
+
+  filter_data->events = g_slist_prepend (filter_data->events,
+					 calendar_event_copy (event));
+}
+
+static GSList *
+calendar_client_filter_events (CalendarClient          *client,
+			       GSList                  *sources,
+			       CalendarEventFilterFunc  filter_func,
+			       GTime                    start_time,
+			       GTime                    end_time)
+{
+  FilterData  filter_data;
+  GSList     *l;
+  GSList     *retval;
+
+  if (!sources)
+    return NULL;
+
+  filter_data.client     = client;
+  filter_data.events     = NULL;
+  filter_data.start_time = start_time;
+  filter_data.end_time   = end_time;
+
+  retval = NULL;
+  for (l = sources; l; l = l->next)
+    {
+      CalendarClientSource *source = l->data;
+
+      if (source->query_completed)
+	{
+	  filter_data.events = NULL;
+	  g_hash_table_foreach (source->completed_query.events,
+				(GHFunc) filter_func,
+				&filter_data);
+
+	  filter_data.events = g_slist_reverse (filter_data.events);
+
+	  retval = g_slist_concat (retval, filter_data.events);
+	}
+    }
+
+  return retval;
+}
+
+GSList *
+calendar_client_get_events (CalendarClient    *client,
+			    CalendarEventType  event_mask)
+{
+  GSList *appointments;
+  GSList *tasks;
+  GTime   day_begin;
+  GTime   day_end;
+
+  g_return_val_if_fail (CALENDAR_IS_CLIENT (client), NULL);
+  g_return_val_if_fail (client->priv->day   != -1 &&
+			client->priv->month != -1 &&
+			client->priv->year  != -1, NULL);
+
+  day_begin = make_time_for_day_begin (client->priv->day,
+				       client->priv->month,
+				       client->priv->year);
+  day_end   = make_time_for_day_begin (client->priv->day + 1,
+				       client->priv->month,
+				       client->priv->year);
+
+  appointments = NULL;
+  if (event_mask & CALENDAR_EVENT_APPOINTMENT)
+    {
+      appointments = calendar_client_filter_events (client,
+						    client->priv->appointment_sources,
+						    filter_appointment,
+						    day_begin,
+						    day_end);
+    }
+
+  tasks = NULL;
+  if (event_mask & CALENDAR_EVENT_TASK)
+    {
+      tasks = calendar_client_filter_events (client,
+					     client->priv->task_sources,
+					     filter_task,
+					     day_begin,
+					     day_end);
+    }
+
+  return g_slist_concat (appointments, tasks);
+}
+
+static inline int
+day_from_time_t (time_t t)
+{
+  struct tm *tm = localtime (&t);
+
+  g_assert (tm == NULL || (tm->tm_mday >=1 && tm->tm_mday <= 31));
+
+  return tm ? tm->tm_mday : 0;
+}
+
+void
+calendar_client_foreach_appointment_day (CalendarClient  *client,
+					 CalendarDayIter  iter_func,
+					 gpointer         user_data)
+{
+  GSList   *appointments, *l;
+  gboolean  marked_days [32] = { FALSE, };
+  GTime     month_begin;
+  GTime     month_end;
+  int       i;
+
+  g_return_if_fail (CALENDAR_IS_CLIENT (client));
+  g_return_if_fail (iter_func != NULL);
+  g_return_if_fail (client->priv->month != -1 &&
+		    client->priv->year  != -1);
+
+  month_begin = make_time_for_day_begin (1,
+					 client->priv->month,
+					 client->priv->year);
+  month_end   = make_time_for_day_begin (1,
+					 client->priv->month + 1,
+					 client->priv->year);
+  
+  appointments = calendar_client_filter_events (client,
+						client->priv->appointment_sources,
+						filter_appointment,
+						month_begin,
+						month_end);
+  for (l = appointments; l; l = l->next)
+    {
+      CalendarAppointment *appointment = l->data;
+
+      if (appointment->start_time)
+        {
+          GTime day_time = appointment->start_time;
+
+          if (day_time >= month_begin)
+            marked_days [day_from_time_t (day_time)] = TRUE;
+      
+          if (appointment->end_time)
+            {
+              int day_offset;
+              int duration = appointment->end_time - appointment->start_time;
+	      /* mark the days for the appointment, no need to add an extra one when duration is a multiple of 86400 */
+              for (day_offset = 1; day_offset <= duration / 86400 && duration != day_offset * 86400; day_offset++)
+                {
+                  GTime day_tm = appointment->start_time + day_offset * 86400;
+
+                  if (day_tm > month_end)
+                    break;
+                  if (day_tm >= month_begin)
+                    marked_days [day_from_time_t (day_tm)] = TRUE;
+                }
+            }
+        }
+      calendar_event_free (CALENDAR_EVENT (appointment));
+    }
+
+  g_slist_free (appointments);
+
+  for (i = 1; i < 32; i++)
+    {
+      if (marked_days [i])
+	iter_func (client, i, user_data);
+    }
+}
+
+void
+calendar_client_set_task_completed (CalendarClient *client,
+				    char           *task_uid,
+				    gboolean        task_completed,
+				    guint           percent_complete)
+{
+  GSList              *l;
+  ECal                *esource;
+  icalcomponent       *ical;
+  icalproperty        *prop;
+  icalproperty_status  status;
+
+  g_return_if_fail (CALENDAR_IS_CLIENT (client));
+  g_return_if_fail (task_uid != NULL);
+  g_return_if_fail (task_completed == FALSE || percent_complete == 100);
+
+  ical = NULL;
+  esource = NULL;
+  for (l = client->priv->task_sources; l; l = l->next)
+    {
+      CalendarClientSource *source = l->data;
+
+      esource = source->source;
+      e_cal_get_object (esource, task_uid, NULL, &ical, NULL);
+      if (ical)
+	break;
+    }
+
+  if (!ical)
+    {
+      g_warning ("Cannot locate task with uid = '%s'\n", task_uid);
+      return;
+    }
+
+  g_assert (esource != NULL);
+
+  /* Completed time */
+  prop = icalcomponent_get_first_property (ical,
+					   ICAL_COMPLETED_PROPERTY);
+  if (task_completed)
+    {
+      struct icaltimetype  completed_time;
+
+      completed_time = icaltime_current_time_with_zone (client->priv->zone);
+      if (!prop)
+	{
+	  icalcomponent_add_property (ical,
+				      icalproperty_new_completed (completed_time));
+	}
+      else
+	{
+	  icalproperty_set_completed (prop, completed_time);
+	}
+    }
+  else if (prop)
+    {
+      icalcomponent_remove_property (ical, prop);
+    }
+
+  /* Percent complete */
+  prop = icalcomponent_get_first_property (ical,
+					   ICAL_PERCENTCOMPLETE_PROPERTY);
+  if (!prop)
+    {
+      icalcomponent_add_property (ical,
+				  icalproperty_new_percentcomplete (percent_complete));
+    }
+  else
+    {
+      icalproperty_set_percentcomplete (prop, percent_complete);
+    }
+
+  /* Status */
+  status = task_completed ? ICAL_STATUS_COMPLETED : ICAL_STATUS_NEEDSACTION;
+  prop = icalcomponent_get_first_property (ical, ICAL_STATUS_PROPERTY);
+  if (prop)
+    {
+      icalproperty_set_status (prop, status);
+    }
+  else
+    {
+      icalcomponent_add_property (ical,
+				  icalproperty_new_status (status));
+    }
+
+  e_cal_modify_object (esource, ical, CALOBJ_MOD_ALL, NULL);
+}

Added: trunk/src/link-factories/calendar-client.h
==============================================================================
--- (empty file)
+++ trunk/src/link-factories/calendar-client.h	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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:
+ *     Mark McLoughlin  <mark skynet ie>
+ *     William Jon McCann  <mccann jhu edu>
+ *     Martin Grimme  <martin pycage de>
+ *     Christian Kellner  <gicmo xatom net>
+ */
+
+#ifndef __CALENDAR_CLIENT_H__
+#define __CALENDAR_CLIENT_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+  CALENDAR_EVENT_APPOINTMENT = 1 << 0,
+  CALENDAR_EVENT_TASK        = 1 << 1,
+  CALENDAR_EVENT_ALL         = (1 << 2) - 1
+} CalendarEventType;
+
+#define CALENDAR_TYPE_CLIENT        (calendar_client_get_type ())
+#define CALENDAR_CLIENT(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CALENDAR_TYPE_CLIENT, CalendarClient))
+#define CALENDAR_CLIENT_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST ((k), CALENDAR_TYPE_CLIENT, CalendarClientClass))
+#define CALENDAR_IS_CLIENT(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), CALENDAR_TYPE_CLIENT))
+#define CALENDAR_IS_CLIENT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CALENDAR_TYPE_CLIENT))
+#define CALENDAR_CLIENT_GET_CLASS(o)(G_TYPE_INSTANCE_GET_CLASS ((o), CALENDAR_TYPE_CLIENT, CalendarClientClass))
+
+typedef struct _CalendarClient        CalendarClient;
+typedef struct _CalendarClientClass   CalendarClientClass;
+typedef struct _CalendarClientPrivate CalendarClientPrivate;
+
+struct _CalendarClient
+{
+  GObject                parent;
+  CalendarClientPrivate *priv;
+};
+
+struct _CalendarClientClass
+{
+  GObjectClass    parent_class;
+
+  void         (* appointments_changed) (CalendarClient *client);
+  void         (* tasks_changed)        (CalendarClient *client);
+};
+
+
+typedef struct
+{
+  GTime start_time;
+  GTime end_time;
+} CalendarOccurrence;
+
+typedef struct
+{
+  char   *uid;
+  char   *rid;
+  char   *uri;
+  char   *summary;
+  char   *description;
+  char   *color_string;
+  GTime   start_time;
+  GTime   end_time;
+  guint   is_all_day : 1;
+
+  /* Only used internally */
+  GSList *occurrences;
+} CalendarAppointment;
+
+typedef struct
+{
+  char  *uid;
+  char  *summary;
+  char  *description;
+  char  *color_string;
+  char  *url;
+  GTime  start_time;
+  GTime  due_time;
+  guint  percent_complete;
+  GTime  completed_time;
+  int    priority;
+} CalendarTask;
+
+typedef struct
+{
+  union
+  {
+    CalendarAppointment appointment;
+    CalendarTask        task;
+  } event;
+  CalendarEventType type;
+} CalendarEvent;
+
+#define CALENDAR_EVENT(e)       ((CalendarEvent *)(e))
+#define CALENDAR_APPOINTMENT(e) ((CalendarAppointment *)(e))
+#define CALENDAR_TASK(e)        ((CalendarTask *)(e))
+
+typedef void (* CalendarDayIter) (CalendarClient *client,
+				  guint           day,
+				  gpointer        user_data);
+
+
+GType           calendar_client_get_type                (void) G_GNUC_CONST;
+CalendarClient *calendar_client_new                     (void);
+
+void            calendar_client_get_date                (CalendarClient      *client,
+							 guint               *year,
+                                                         guint               *month,
+							 guint               *day);
+void            calendar_client_select_month            (CalendarClient      *client,
+							 guint                month,
+							 guint                year);
+void            calendar_client_select_day              (CalendarClient      *client,
+							 guint                day);
+
+GSList         *calendar_client_get_events              (CalendarClient      *client,
+							 CalendarEventType    event_mask);
+void            calendar_client_foreach_appointment_day (CalendarClient      *client,
+							 CalendarDayIter      iter_func,
+							 gpointer             user_data);
+
+void            calendar_client_set_task_completed       (CalendarClient     *client,
+							  char               *task_uid,
+							  gboolean            task_completed,
+							  guint               percent_complete);
+
+void calendar_event_free (CalendarEvent *event);
+
+G_END_DECLS
+
+#endif /* __CALENDAR_CLIENT_H__ */

Added: trunk/src/link-factories/calendar-debug.h
==============================================================================
--- (empty file)
+++ trunk/src/link-factories/calendar-debug.h	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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:
+ *     Mark McLoughlin  <mark skynet ie>
+ */
+
+#ifndef __CALENDAR_DEBUG_H__
+#define __CALENDAR_DEBUG_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#ifdef CALENDAR_ENABLE_DEBUG
+
+#include <stdio.h>
+
+#ifdef G_HAVE_ISO_VARARGS
+#  define dprintf(...) fprintf (stderr, __VA_ARGS__);
+#elif defined(G_HAVE_GNUC_VARARGS)
+#  define dprintf(args...) fprintf (stderr, args);
+#endif
+
+#else /* if !defined (CALENDAR_DEBUG) */
+
+#ifdef G_HAVE_ISO_VARARGS
+#  define dprintf(...)
+#elif defined(G_HAVE_GNUC_VARARGS)
+#  define dprintf(args...)
+#endif
+
+#endif /* CALENDAR_ENABLE_DEBUG */
+
+G_END_DECLS
+
+#endif /* __CALENDAR_DEBUG_H__ */

Added: trunk/src/link-factories/calendar-sources.c
==============================================================================
--- (empty file)
+++ trunk/src/link-factories/calendar-sources.c	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,657 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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:
+ *     Mark McLoughlin  <mark skynet ie>
+ *     William Jon McCann  <mccann jhu edu>
+ *     Martin Grimme  <martin pycage de>
+ *     Christian Kellner  <gicmo xatom net>
+ */
+
+#include <config.h>
+
+#include "calendar-sources.h"
+
+#include <libintl.h>
+#include <string.h>
+#include <gconf/gconf-client.h>
+#include <libecal/e-cal.h>
+#include <libedataserver/e-source-list.h>
+#include <libedataserverui/e-passwords.h>
+
+#undef CALENDAR_ENABLE_DEBUG
+#include "calendar-debug.h"
+
+#ifndef _
+#define _(x) gettext(x)
+#endif
+
+#ifndef N_
+#define N_(x) x
+#endif
+
+#define CALENDAR_SOURCES_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesPrivate))
+
+#define CALENDAR_SOURCES_EVO_DIR                          "/apps/evolution"
+#define CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY          CALENDAR_SOURCES_EVO_DIR "/calendar/sources"
+#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR CALENDAR_SOURCES_EVO_DIR "/calendar/display"
+#define CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR "/selected_calendars"
+#define CALENDAR_SOURCES_TASK_SOURCES_KEY                 CALENDAR_SOURCES_EVO_DIR "/tasks/sources"
+#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR        CALENDAR_SOURCES_EVO_DIR "/calendar/tasks"
+#define CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY        CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR "/selected_tasks"
+
+typedef struct _CalendarSourceData CalendarSourceData;
+
+struct _CalendarSourceData
+{
+  ECalSourceType   source_type;
+  CalendarSources *sources;
+  guint            changed_signal;
+
+  GSList          *clients;
+  GSList          *selected_sources;
+  ESourceList     *esource_list;
+
+  guint            selected_sources_listener;
+  char            *selected_sources_dir;
+
+  guint            timeout_id;
+
+  guint            loaded : 1;
+};
+
+struct _CalendarSourcesPrivate
+{
+  CalendarSourceData  appointment_sources;
+  CalendarSourceData  task_sources;
+
+  GConfClient        *gconf_client;
+};
+
+static void calendar_sources_class_init (CalendarSourcesClass *klass);
+static void calendar_sources_init       (CalendarSources      *sources);
+static void calendar_sources_finalize   (GObject             *object);
+
+static void backend_died_cb (ECal *client, CalendarSourceData *source_data);
+static void calendar_sources_esource_list_changed (ESourceList        *source_list,
+                                                   CalendarSourceData *source_data);
+
+enum
+{
+  APPOINTMENT_SOURCES_CHANGED,
+  TASK_SOURCES_CHANGED,
+  LAST_SIGNAL
+};
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static GObjectClass    *parent_class = NULL;
+static CalendarSources *calendar_sources_singleton = NULL;
+
+GType
+calendar_sources_get_type (void)
+{
+  static GType sources_type = 0;
+  
+  if (!sources_type)
+    {
+      static const GTypeInfo sources_info =
+      {
+	sizeof (CalendarSourcesClass),
+	NULL,		/* base_init */
+	NULL,		/* base_finalize */
+	(GClassInitFunc) calendar_sources_class_init,
+	NULL,           /* class_finalize */
+	NULL,		/* class_data */
+	sizeof (CalendarSources),
+	0,		/* n_preallocs */
+	(GInstanceInitFunc) calendar_sources_init,
+      };
+      
+      sources_type = g_type_register_static (G_TYPE_OBJECT,
+					     "CalendarSources",
+					     &sources_info, 0);
+    }
+  
+  return sources_type;
+}
+
+static void
+calendar_sources_class_init (CalendarSourcesClass *klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->finalize = calendar_sources_finalize;
+
+  g_type_class_add_private (klass, sizeof (CalendarSourcesPrivate));
+
+  signals [APPOINTMENT_SOURCES_CHANGED] =
+    g_signal_new ("appointment-sources-changed",
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (CalendarSourcesClass,
+				   appointment_sources_changed),
+		  NULL,
+		  NULL,
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE,
+		  0);
+
+  signals [TASK_SOURCES_CHANGED] =
+    g_signal_new ("task-sources-changed",
+		  G_TYPE_FROM_CLASS (gobject_class),
+		  G_SIGNAL_RUN_LAST,
+		  G_STRUCT_OFFSET (CalendarSourcesClass,
+				   task_sources_changed),
+		  NULL,
+		  NULL,
+		  g_cclosure_marshal_VOID__VOID,
+		  G_TYPE_NONE,
+		  0);
+}
+
+static void
+calendar_sources_init (CalendarSources *sources)
+{
+  sources->priv = CALENDAR_SOURCES_GET_PRIVATE (sources);
+
+  sources->priv->appointment_sources.source_type    = E_CAL_SOURCE_TYPE_EVENT;
+  sources->priv->appointment_sources.sources        = sources;
+  sources->priv->appointment_sources.changed_signal = signals [APPOINTMENT_SOURCES_CHANGED];
+  sources->priv->appointment_sources.timeout_id     = 0;
+
+  sources->priv->task_sources.source_type    = E_CAL_SOURCE_TYPE_TODO;
+  sources->priv->task_sources.sources        = sources;
+  sources->priv->task_sources.changed_signal = signals [TASK_SOURCES_CHANGED];
+  sources->priv->task_sources.timeout_id     = 0;
+
+  sources->priv->gconf_client = gconf_client_get_default ();
+}
+
+static void
+calendar_sources_finalize_source_data (CalendarSources    *sources,
+				       CalendarSourceData *source_data)
+{
+  if (source_data->loaded)
+    {
+      GSList *l;
+
+      if (source_data->selected_sources_dir)
+	{
+	  gconf_client_remove_dir (sources->priv->gconf_client,
+				   source_data->selected_sources_dir,
+				   NULL);
+
+	  g_free (source_data->selected_sources_dir);
+	  source_data->selected_sources_dir = NULL;
+	}
+
+      if (source_data->selected_sources_listener)
+	{
+	  gconf_client_notify_remove (sources->priv->gconf_client,
+				      source_data->selected_sources_listener);
+	  source_data->selected_sources_listener = 0;
+	}
+
+      for (l = source_data->clients; l; l = l->next)
+        {
+          g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
+                                                G_CALLBACK (backend_died_cb),
+                                                source_data);
+          g_object_unref (l->data);
+        }
+      g_slist_free (source_data->clients);
+      source_data->clients = NULL;
+
+      if (source_data->esource_list)
+        {
+          g_signal_handlers_disconnect_by_func (source_data->esource_list,
+                                                G_CALLBACK (calendar_sources_esource_list_changed),
+                                                source_data);
+          g_object_unref (source_data->esource_list);
+	}
+      source_data->esource_list = NULL;
+
+      for (l = source_data->selected_sources; l; l = l->next)
+	g_free (l->data);
+      g_slist_free (source_data->selected_sources);
+      source_data->selected_sources = NULL;
+
+      if (source_data->timeout_id != 0)
+        {
+          g_source_remove (source_data->timeout_id);
+          source_data->timeout_id = 0;
+        }
+
+      source_data->loaded = FALSE;
+    }
+}
+
+static void
+calendar_sources_finalize (GObject *object)
+{
+  CalendarSources *sources = CALENDAR_SOURCES (object);
+
+  calendar_sources_finalize_source_data (sources, &sources->priv->appointment_sources);
+  calendar_sources_finalize_source_data (sources, &sources->priv->task_sources);
+
+  if (sources->priv->gconf_client)
+    g_object_unref (sources->priv->gconf_client);
+  sources->priv->gconf_client = NULL;
+
+  if (G_OBJECT_CLASS (parent_class)->finalize)
+    G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+CalendarSources *
+calendar_sources_get (void)
+{
+  gpointer singleton_location = &calendar_sources_singleton;
+
+  if (calendar_sources_singleton)
+    return g_object_ref (calendar_sources_singleton);
+
+  calendar_sources_singleton = g_object_new (CALENDAR_TYPE_SOURCES, NULL);
+  g_object_add_weak_pointer (G_OBJECT (calendar_sources_singleton),
+			     singleton_location);
+
+  return calendar_sources_singleton;
+}
+
+static gboolean
+is_source_selected (ESource *esource,
+		    GSList  *selected_sources)
+{
+  const char *uid;
+  GSList     *l;
+
+  uid = e_source_peek_uid (esource);
+
+  for (l = selected_sources; l; l = l->next)
+    {
+      const char *source = l->data;
+
+      if (!strcmp (source, uid))
+	return TRUE;
+    }
+
+  return FALSE;
+}
+
+static char *
+auth_func_cb (ECal       *ecal,
+	      const char *prompt,
+	      const char *key,
+	      gpointer    user_data)
+{
+	ESource *source;
+	const gchar *auth_domain;
+	const gchar *component_name;
+
+	source = e_cal_get_source (ecal);
+	auth_domain = e_source_get_property (source, "auth-domain");
+	component_name = auth_domain ? auth_domain : "Calendar";
+
+	return e_passwords_get_password (component_name, key);
+}
+
+/* The clients are just created here but not loaded */
+static ECal *
+get_ecal_from_source (ESource        *esource,
+		      ECalSourceType  source_type,
+		      GSList         *existing_clients)
+{
+  ECal *retval;
+
+  if (existing_clients)
+    {
+      GSList *l;
+
+      for (l = existing_clients; l; l = l->next)
+	{
+	  ECal *client = E_CAL (l->data);
+
+	  if (e_source_equal (esource, e_cal_get_source (client)))
+	    {
+	      dprintf ("        load_esource: found existing source ... returning that\n");
+
+	      return g_object_ref (client);
+	    }
+	}
+    }
+
+  retval = e_cal_new (esource, source_type);
+  if (!retval)
+    {
+      g_warning ("Could not load source '%s' from '%s'\n",
+		 e_source_peek_name (esource),
+		 e_source_peek_relative_uri (esource));
+      return NULL;
+    }
+
+  e_cal_set_auth_func (retval, auth_func_cb, NULL);
+
+  return retval;
+}
+
+/* - Order doesn't matter
+ * - Can just compare object pointers since we
+ *   re-use client connections
+ */
+static gboolean
+compare_ecal_lists (GSList *a,
+		    GSList *b)
+{
+  GSList *l;
+
+  if (g_slist_length (a) != g_slist_length (b))
+    return FALSE;
+
+  for (l = a; l; l = l->next)
+    {
+      if (!g_slist_find (b, l->data))
+	return FALSE;
+    }
+
+  return TRUE;
+}
+
+static inline void
+debug_dump_selected_sources (GSList *selected_sources)
+{
+#ifdef CALENDAR_ENABLE_DEBUG
+  GSList *l;
+
+  dprintf ("Selected sources:\n");
+  for (l = selected_sources; l; l = l->next)
+    {
+      char *source = l->data;
+
+      dprintf ("  %s\n", source);
+    }
+  dprintf ("\n");
+#endif
+}
+
+static inline void
+debug_dump_ecal_list (GSList *ecal_list)
+{
+#ifdef CALENDAR_ENABLE_DEBUG
+  GSList *l;
+
+  dprintf ("Loaded clients:\n");
+  for (l = ecal_list; l; l = l->next)
+    {
+      ECal    *client = l->data;
+      ESource *source = e_cal_get_source (client);
+
+      dprintf ("  %s %s %s\n",
+	       e_source_peek_uid (source),
+	       e_source_peek_name (source),
+	       e_cal_get_uri (client));
+    }
+#endif
+}
+
+static void
+calendar_sources_load_esource_list (CalendarSourceData *source_data);
+
+static gboolean
+backend_restart (gpointer data)
+{
+  CalendarSourceData *source_data = data;
+
+  calendar_sources_load_esource_list (source_data);
+
+  source_data->timeout_id = 0;
+    
+  return FALSE;
+}
+
+static void
+backend_died_cb (ECal *client, CalendarSourceData *source_data)
+{
+  const char *uristr;
+
+  source_data->clients = g_slist_remove (source_data->clients, client);
+  if (g_slist_length (source_data->clients) < 1) 
+    {
+      g_slist_free (source_data->clients);
+      source_data->clients = NULL;
+    }
+  uristr = e_cal_get_uri (client);
+  g_warning ("The calendar backend for %s has crashed.", uristr);
+
+  if (source_data->timeout_id != 0)
+    {
+      g_source_remove (source_data->timeout_id);
+      source_data->timeout_id = 0;
+    }
+
+  source_data->timeout_id = g_timeout_add_seconds (2, backend_restart,
+		  				   source_data);
+}
+
+static void
+calendar_sources_load_esource_list (CalendarSourceData *source_data)
+{
+  GSList  *clients = NULL;
+  GSList  *groups, *l;
+  gboolean emit_signal = FALSE;
+
+  g_return_if_fail (source_data->esource_list != NULL);
+
+  debug_dump_selected_sources (source_data->selected_sources);
+
+  dprintf ("Source groups:\n");
+  groups = e_source_list_peek_groups (source_data->esource_list);
+  for (l = groups; l; l = l->next)
+    {
+      GSList *esources, *s;
+
+      dprintf ("  %s\n", e_source_group_peek_uid (l->data));
+      dprintf ("    sources:\n");
+
+      esources = e_source_group_peek_sources (l->data);
+      for (s = esources; s; s = s->next)
+	{
+	  ESource *esource = E_SOURCE (s->data);
+	  ECal    *client;
+
+	  dprintf ("      type = '%s' uid = '%s', name = '%s', relative uri = '%s': \n",
+                   source_data->source_type == E_CAL_SOURCE_TYPE_EVENT ? "appointment" : "task",
+		   e_source_peek_uid (esource),
+		   e_source_peek_name (esource),
+		   e_source_peek_relative_uri (esource));
+
+	  if (is_source_selected (esource, source_data->selected_sources) &&
+	      (client = get_ecal_from_source (esource, source_data->source_type, source_data->clients)))
+	    {
+	      clients = g_slist_prepend (clients, client);
+	    }
+	}
+    }
+  dprintf ("\n");
+
+  if (source_data->loaded && 
+      !compare_ecal_lists (source_data->clients, clients))
+    emit_signal = TRUE;
+
+  for (l = source_data->clients; l; l = l->next)
+    {
+      g_signal_handlers_disconnect_by_func (G_OBJECT (l->data),
+                                            G_CALLBACK (backend_died_cb),
+                                            source_data);
+
+      g_object_unref (l->data);
+    }
+  g_slist_free (source_data->clients);
+  source_data->clients = g_slist_reverse (clients);
+
+  /* connect to backend_died after we disconnected the previous signal
+   * handlers. If we do it before, we'll lose some handlers (for clients that
+   * were already there before) */
+  for (l = source_data->clients; l; l = l->next)
+    {
+      g_signal_connect (G_OBJECT (l->data), "backend_died",
+                        G_CALLBACK (backend_died_cb), source_data);
+    }
+
+  if (emit_signal) 
+    {
+      dprintf ("Emitting %s-sources-changed signal\n",
+	       source_data->source_type == E_CAL_SOURCE_TYPE_EVENT ? "appointment" : "task");
+      g_signal_emit (source_data->sources, source_data->changed_signal, 0);
+    }
+
+  debug_dump_ecal_list (source_data->clients);
+}
+
+static void
+calendar_sources_esource_list_changed (ESourceList        *source_list,
+				       CalendarSourceData *source_data)
+				       
+{
+  dprintf ("ESourceList changed, reloading\n");
+
+  calendar_sources_load_esource_list (source_data);
+}
+
+static void
+calendar_sources_selected_sources_notify (GConfClient        *client,
+					  guint               cnx_id,
+					  GConfEntry         *entry,
+					  CalendarSourceData *source_data)
+{
+  GSList *l;
+
+  if (!entry->value ||
+      entry->value->type != GCONF_VALUE_LIST ||
+      gconf_value_get_list_type (entry->value) != GCONF_VALUE_STRING)
+    return;
+
+  dprintf ("Selected sources key (%s) changed, reloading\n", entry->key);
+
+  for (l = source_data->selected_sources; l; l = l->next)
+    g_free (l->data);
+  source_data->selected_sources = NULL;
+
+  for (l = gconf_value_get_list (entry->value); l; l = l->next)
+    {
+      const char *source = gconf_value_get_string (l->data);
+
+      source_data->selected_sources = 
+	g_slist_prepend (source_data->selected_sources,
+			 g_strdup (source));
+    }
+  source_data->selected_sources =
+    g_slist_reverse (source_data->selected_sources);
+
+  calendar_sources_load_esource_list (source_data);
+}
+
+static void
+calendar_sources_load_sources (CalendarSources    *sources,
+			       CalendarSourceData *source_data,
+			       const char         *sources_key,
+			       const char         *selected_sources_key,
+			       const char         *selected_sources_dir)
+{
+  GConfClient *gconf_client;
+  GError      *error;
+
+  dprintf ("---------------------------\n");
+  dprintf ("Loading sources:\n");
+  dprintf ("  sources_key: %s\n", sources_key);
+  dprintf ("  selected_sources_key: %s\n", selected_sources_key);
+  dprintf ("  selected_sources_dir: %s\n", selected_sources_dir);
+
+  gconf_client = sources->priv->gconf_client;
+
+  error = NULL;
+  source_data->selected_sources = gconf_client_get_list (gconf_client,
+							 selected_sources_key,
+							 GCONF_VALUE_STRING,
+							 &error);
+  if (error)
+    {
+      g_warning ("Failed to get selected sources from '%s': %s\n",
+		 selected_sources_key,
+		 error->message);
+      g_error_free (error);
+      return;
+    }
+
+  gconf_client_add_dir (gconf_client,
+			selected_sources_dir,
+			GCONF_CLIENT_PRELOAD_NONE,
+			NULL);
+  source_data->selected_sources_dir = g_strdup (selected_sources_dir);
+
+  source_data->selected_sources_listener =
+    gconf_client_notify_add (gconf_client,
+			     selected_sources_dir,
+			     (GConfClientNotifyFunc) calendar_sources_selected_sources_notify,
+			     source_data, NULL, NULL);
+
+  source_data->esource_list = e_source_list_new_for_gconf (gconf_client, sources_key);
+  g_signal_connect (source_data->esource_list, "changed",
+		    G_CALLBACK (calendar_sources_esource_list_changed),
+		    source_data);
+
+  calendar_sources_load_esource_list (source_data);
+
+  source_data->loaded = TRUE;
+
+  dprintf ("---------------------------\n");
+}
+
+GSList *
+calendar_sources_get_appointment_sources (CalendarSources *sources)
+{
+  g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
+
+  if (!sources->priv->appointment_sources.loaded)
+    {
+      calendar_sources_load_sources (sources,
+				     &sources->priv->appointment_sources,
+				     CALENDAR_SOURCES_APPOINTMENT_SOURCES_KEY,
+				     CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_KEY,
+				     CALENDAR_SOURCES_SELECTED_APPOINTMENT_SOURCES_DIR);
+    }
+  
+  return sources->priv->appointment_sources.clients;
+}
+
+GSList *
+calendar_sources_get_task_sources (CalendarSources *sources)
+{
+  g_return_val_if_fail (CALENDAR_IS_SOURCES (sources), NULL);
+
+  if (!sources->priv->task_sources.loaded)
+    {
+      calendar_sources_load_sources (sources,
+				     &sources->priv->task_sources,
+				     CALENDAR_SOURCES_TASK_SOURCES_KEY,
+				     CALENDAR_SOURCES_SELECTED_TASK_SOURCES_KEY,
+				     CALENDAR_SOURCES_SELECTED_TASK_SOURCES_DIR);
+    }
+
+  return sources->priv->task_sources.clients;
+}

Added: trunk/src/link-factories/calendar-sources.h
==============================================================================
--- (empty file)
+++ trunk/src/link-factories/calendar-sources.h	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * 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:
+ *     Mark McLoughlin  <mark skynet ie>
+ *     William Jon McCann  <mccann jhu edu>
+ *     Martin Grimme  <martin pycage de>
+ *     Christian Kellner  <gicmo xatom net>
+ */
+
+#ifndef __CALENDAR_SOURCES_H__
+#define __CALENDAR_SOURCES_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define CALENDAR_TYPE_SOURCES        (calendar_sources_get_type ())
+#define CALENDAR_SOURCES(o)          (G_TYPE_CHECK_INSTANCE_CAST ((o), CALENDAR_TYPE_SOURCES, CalendarSources))
+#define CALENDAR_SOURCES_CLASS(k)    (G_TYPE_CHECK_CLASS_CAST ((k), CALENDAR_TYPE_SOURCES, CalendarSourcesClass))
+#define CALENDAR_IS_SOURCES(o)       (G_TYPE_CHECK_INSTANCE_TYPE ((o), CALENDAR_TYPE_SOURCES))
+#define CALENDAR_IS_SOURCES_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CALENDAR_TYPE_SOURCES))
+#define CALENDAR_SOURCES_GET_CLASS(o)(G_TYPE_INSTANCE_GET_CLASS ((o), CALENDAR_TYPE_SOURCES, CalendarSourcesClass))
+
+typedef struct _CalendarSources        CalendarSources;
+typedef struct _CalendarSourcesClass   CalendarSourcesClass;
+typedef struct _CalendarSourcesPrivate CalendarSourcesPrivate;
+
+struct _CalendarSources
+{
+  GObject                 parent;
+  CalendarSourcesPrivate *priv;
+};
+
+struct _CalendarSourcesClass
+{
+  GObjectClass    parent_class;
+
+  void         (* appointment_sources_changed) (CalendarSources *sources);
+  void         (* task_sources_changed)        (CalendarSources *sources);
+};
+
+
+GType            calendar_sources_get_type                (void) G_GNUC_CONST;
+CalendarSources *calendar_sources_get                     (void);
+GSList          *calendar_sources_get_appointment_sources (CalendarSources *sources);
+GSList          *calendar_sources_get_task_sources        (CalendarSources *sources);
+
+G_END_DECLS
+
+#endif /* __CALENDAR_SOURCES_H__ */

Added: trunk/src/link-factories/calendar.c
==============================================================================
--- (empty file)
+++ trunk/src/link-factories/calendar.c	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,149 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include "../link.h"
+#include "calendar.h"
+#include "calendar-client.h"
+#include "../link-factory.h"
+#include "../links/calendar-appointment.h"
+#include "../links/calendar-task.h"
+
+static void almanah_calendar_link_factory_init (AlmanahCalendarLinkFactory *self);
+static void almanah_calendar_link_factory_dispose (GObject *object);
+static void query_links (AlmanahLinkFactory *link_factory, GDate *date);
+static GSList *get_links (AlmanahLinkFactory *link_factory, GDate *date);
+static void events_changed_cb (CalendarClient *client, AlmanahCalendarLinkFactory *self);
+
+struct _AlmanahCalendarLinkFactoryPrivate {
+	CalendarClient *client;
+};
+
+G_DEFINE_TYPE (AlmanahCalendarLinkFactory, almanah_calendar_link_factory, ALMANAH_TYPE_LINK_FACTORY)
+#define ALMANAH_CALENDAR_LINK_FACTORY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_CALENDAR_LINK_FACTORY, AlmanahCalendarLinkFactoryPrivate))
+
+static void
+almanah_calendar_link_factory_class_init (AlmanahCalendarLinkFactoryClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	AlmanahLinkFactoryClass *link_factory_class = ALMANAH_LINK_FACTORY_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (AlmanahCalendarLinkFactoryPrivate));
+
+	gobject_class->dispose = almanah_calendar_link_factory_dispose;
+
+	link_factory_class->type_id = ALMANAH_LINK_FACTORY_CALENDAR;
+	link_factory_class->query_links = query_links;
+	link_factory_class->get_links = get_links;
+}
+
+static void
+almanah_calendar_link_factory_init (AlmanahCalendarLinkFactory *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_CALENDAR_LINK_FACTORY, AlmanahCalendarLinkFactoryPrivate);
+	self->priv->client = calendar_client_new ();
+
+	g_signal_connect (self->priv->client, "tasks-changed", G_CALLBACK (events_changed_cb), self);
+	g_signal_connect (self->priv->client, "appointments-changed", G_CALLBACK (events_changed_cb), self);
+}
+
+static void
+almanah_calendar_link_factory_dispose (GObject *object)
+{
+	AlmanahCalendarLinkFactoryPrivate *priv = ALMANAH_CALENDAR_LINK_FACTORY_GET_PRIVATE (object);
+
+	if (priv->client != NULL)
+		g_object_unref (priv->client);
+	priv->client = NULL;
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (almanah_calendar_link_factory_parent_class)->dispose (object);
+}
+
+static void
+query_links (AlmanahLinkFactory *link_factory, GDate *date)
+{
+	AlmanahCalendarLinkFactory *self = ALMANAH_CALENDAR_LINK_FACTORY (link_factory);
+
+	calendar_client_select_day (self->priv->client, g_date_get_day (date));
+	calendar_client_select_month (self->priv->client, g_date_get_month (date) - 1, g_date_get_year (date));
+	g_signal_emit_by_name (self, "links-updated");
+}
+
+static void
+events_changed_cb (CalendarClient *client, AlmanahCalendarLinkFactory *self)
+{
+	g_signal_emit_by_name (self, "links-updated");
+}
+
+static inline GTime
+date_to_time (GDate *date)
+{
+	struct tm localtime_tm = { 0, };
+
+	localtime_tm.tm_mday = g_date_get_day (date);
+	localtime_tm.tm_mon = g_date_get_month (date) - 1;
+	localtime_tm.tm_year = g_date_get_year (date) - 1900;
+	localtime_tm.tm_isdst = -1;
+
+	return mktime (&localtime_tm);
+}
+
+static GSList *
+get_links (AlmanahLinkFactory *link_factory, GDate *date)
+{
+	GSList *events, *e, *links = NULL;
+	AlmanahCalendarLinkFactoryPrivate *priv = ALMANAH_CALENDAR_LINK_FACTORY_GET_PRIVATE (link_factory);
+
+	events = calendar_client_get_events (priv->client, CALENDAR_EVENT_ALL);
+
+	for (e = events; e != NULL; e = g_slist_next (e)) {
+		CalendarEvent *event = e->data;
+		AlmanahLink *link;
+
+		/* Create a new link and use it to replace the CalendarEvent */
+		if (event->type == CALENDAR_EVENT_TASK) {
+			/* Task */
+			GTime today_time, yesterday_time;
+
+			today_time = date_to_time (date);
+			yesterday_time = today_time - (24 * 60 * 60);
+
+			/* Have to filter out tasks by date */
+			if (event->event.task.start_time <= today_time &&
+			    (event->event.task.completed_time == 0 || event->event.task.completed_time >= yesterday_time)) {
+				link = ALMANAH_LINK (almanah_calendar_task_link_new (event->event.task.uid, event->event.task.summary));
+			} else {
+				link = NULL;
+			}
+		} else {
+			/* Appointment */
+			link = ALMANAH_LINK (almanah_calendar_appointment_link_new (event->event.appointment.uid, event->event.appointment.summary));
+		}
+
+		if (link != NULL)
+			links = g_slist_prepend (links, link);
+
+		calendar_event_free (event);
+	}
+	g_slist_free (events);
+
+	return links;
+}

Added: trunk/src/link-factories/calendar.h
==============================================================================
--- (empty file)
+++ trunk/src/link-factories/calendar.h	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,52 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALMANAH_CALENDAR_LINK_FACTORY_H
+#define ALMANAH_CALENDAR_LINK_FACTORY_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "../link-factory.h"
+
+G_BEGIN_DECLS
+
+#define ALMANAH_TYPE_CALENDAR_LINK_FACTORY		(almanah_calendar_link_factory_get_type ())
+#define ALMANAH_CALENDAR_LINK_FACTORY(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_CALENDAR_LINK_FACTORY, AlmanahCalendarLinkFactory))
+#define ALMANAH_CALENDAR_LINK_FACTORY_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_CALENDAR_LINK_FACTORY, AlmanahCalendarLinkFactoryClass))
+#define ALMANAH_IS_CALENDAR_LINK_FACTORY(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_CALENDAR_LINK_FACTORY))
+#define ALMANAH_IS_CALENDAR_LINK_FACTORY_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_CALENDAR_LINK_FACTORY))
+#define ALMANAH_CALENDAR_LINK_FACTORY_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_CALENDAR_LINK_FACTORY, AlmanahCalendarLinkFactoryClass))
+
+typedef struct _AlmanahCalendarLinkFactoryPrivate	AlmanahCalendarLinkFactoryPrivate;
+
+typedef struct {
+	AlmanahLinkFactory parent;
+	AlmanahCalendarLinkFactoryPrivate *priv;
+} AlmanahCalendarLinkFactory;
+
+typedef struct {
+	AlmanahLinkFactoryClass parent;
+} AlmanahCalendarLinkFactoryClass;
+
+GType almanah_calendar_link_factory_get_type (void);
+
+G_END_DECLS
+
+#endif /* !ALMANAH_CALENDAR_LINK_FACTORY_H */

Added: trunk/src/link-factory.c
==============================================================================
--- (empty file)
+++ trunk/src/link-factory.c	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,112 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include "link-factory.h"
+#include "link-factory-builtins.h"
+
+static void almanah_link_factory_init (AlmanahLinkFactory *self);
+static void almanah_link_factory_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+
+struct _AlmanahLinkFactoryPrivate {
+	GDate date;
+};
+
+enum {
+	PROP_TYPE_ID = 1
+};
+
+enum {
+	SIGNAL_LINKS_UPDATED,
+	LAST_SIGNAL
+};
+
+static guint link_factory_signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_ABSTRACT_TYPE (AlmanahLinkFactory, almanah_link_factory, G_TYPE_OBJECT)
+#define ALMANAH_LINK_FACTORY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_LINK_FACTORY, AlmanahLinkFactoryPrivate))
+
+static void
+almanah_link_factory_class_init (AlmanahLinkFactoryClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (AlmanahLinkFactoryPrivate));
+
+	gobject_class->get_property = almanah_link_factory_get_property;
+
+	g_object_class_install_property (gobject_class, PROP_TYPE_ID,
+				g_param_spec_enum ("type-id",
+					"Type ID", "The type ID of this link factory.",
+					ALMANAH_TYPE_LINK_FACTORY_TYPE, ALMANAH_LINK_FACTORY_UNKNOWN,
+					G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
+
+	link_factory_signals[SIGNAL_LINKS_UPDATED] = g_signal_new ("links-updated",
+				G_TYPE_FROM_CLASS (klass),
+				G_SIGNAL_RUN_LAST,
+				0, NULL, NULL,
+				g_cclosure_marshal_VOID__VOID,
+				G_TYPE_NONE, 0);
+}
+
+static void
+almanah_link_factory_init (AlmanahLinkFactory *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_LINK_FACTORY, AlmanahLinkFactoryPrivate);
+}
+
+static void
+almanah_link_factory_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	AlmanahLinkFactoryClass *klass = ALMANAH_LINK_FACTORY_GET_CLASS (object);
+
+	switch (property_id) {
+		case PROP_TYPE_ID:
+			g_value_set_enum (value, klass->type_id);
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+AlmanahLinkFactoryType
+almanah_link_factory_get_type_id (AlmanahLinkFactory *self)
+{
+	AlmanahLinkFactoryClass *klass = ALMANAH_LINK_FACTORY_GET_CLASS (self);
+	return klass->type_id;
+}
+
+void
+almanah_link_factory_query_links (AlmanahLinkFactory *self, GDate *date)
+{
+	AlmanahLinkFactoryClass *klass = ALMANAH_LINK_FACTORY_GET_CLASS (self);
+	g_assert (klass->query_links != NULL);
+	return klass->query_links (self, date);
+}
+
+GSList *
+almanah_link_factory_get_links (AlmanahLinkFactory *self, GDate *date)
+{
+	AlmanahLinkFactoryClass *klass = ALMANAH_LINK_FACTORY_GET_CLASS (self);
+	g_assert (klass->get_links != NULL);
+	return klass->get_links (self, date);
+}

Added: trunk/src/link-factory.h
==============================================================================
--- (empty file)
+++ trunk/src/link-factory.h	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,65 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALMANAH_LINK_FACTORY_H
+#define ALMANAH_LINK_FACTORY_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	ALMANAH_LINK_FACTORY_UNKNOWN = 0,
+	ALMANAH_LINK_FACTORY_CALENDAR = 1
+} AlmanahLinkFactoryType;
+
+#define ALMANAH_TYPE_LINK_FACTORY		(almanah_link_factory_get_type ())
+#define ALMANAH_LINK_FACTORY(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_LINK_FACTORY, AlmanahLinkFactory))
+#define ALMANAH_LINK_FACTORY_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_LINK_FACTORY, AlmanahLinkFactoryClass))
+#define ALMANAH_IS_LINK_FACTORY(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_LINK_FACTORY))
+#define ALMANAH_IS_LINK_FACTORY_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_LINK_FACTORY))
+#define ALMANAH_LINK_FACTORY_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_LINK_FACTORY, AlmanahLinkFactoryClass))
+
+typedef struct _AlmanahLinkFactoryPrivate	AlmanahLinkFactoryPrivate;
+
+typedef struct {
+	GObject parent;
+	AlmanahLinkFactoryPrivate *priv;
+} AlmanahLinkFactory;
+
+typedef struct {
+	GObjectClass parent;
+
+	AlmanahLinkFactoryType type_id;
+
+	void (*query_links) (AlmanahLinkFactory *link_factory, GDate *date);
+	GSList *(*get_links) (AlmanahLinkFactory *link_factory, GDate *date);
+} AlmanahLinkFactoryClass;
+
+GType almanah_link_factory_get_type (void);
+
+AlmanahLinkFactoryType almanah_link_factory_get_type_id (AlmanahLinkFactory *self);
+
+void almanah_link_factory_query_links (AlmanahLinkFactory *self, GDate *date);
+GSList *almanah_link_factory_get_links (AlmanahLinkFactory *self, GDate *date);
+
+G_END_DECLS
+
+#endif /* !ALMANAH_LINK_FACTORY_H */

Added: trunk/src/link-manager.c
==============================================================================
--- (empty file)
+++ trunk/src/link-manager.c	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,178 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+
+#include "link-manager.h"
+#include "link-factory.h"
+#include "link-factory-builtins.h"
+
+typedef struct {
+	AlmanahLinkFactoryType type_id;
+	GType (*type_function) (void);
+} LinkFactoryType;
+
+/* TODO: This is still a little hacky */
+
+#include "calendar.h"
+#include "main.h"
+
+const LinkFactoryType link_factory_types[] = {
+	{ ALMANAH_LINK_FACTORY_CALENDAR, almanah_calendar_link_factory_get_type }
+};
+
+static void almanah_link_manager_init (AlmanahLinkManager *self);
+static void almanah_link_manager_dispose (GObject *object);
+static void links_updated_cb (AlmanahLinkFactory *factory, AlmanahLinkManager *self);
+
+struct _AlmanahLinkManagerPrivate {
+	AlmanahLinkFactory **factories;
+};
+
+enum {
+	SIGNAL_LINKS_UPDATED,
+	LAST_SIGNAL
+};
+
+static guint link_manager_signals[LAST_SIGNAL] = { 0, };
+
+G_DEFINE_TYPE (AlmanahLinkManager, almanah_link_manager, G_TYPE_OBJECT)
+#define ALMANAH_LINK_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_LINK_MANAGER, AlmanahLinkManagerPrivate))
+
+static void
+almanah_link_manager_class_init (AlmanahLinkManagerClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (AlmanahLinkManagerPrivate));
+
+	gobject_class->dispose = almanah_link_manager_dispose;
+
+	link_manager_signals[SIGNAL_LINKS_UPDATED] = g_signal_new ("links-updated",
+				G_TYPE_FROM_CLASS (klass),
+				G_SIGNAL_RUN_LAST,
+				0, NULL, NULL,
+				g_cclosure_marshal_VOID__ENUM,
+				G_TYPE_NONE, 1, ALMANAH_TYPE_LINK_FACTORY_TYPE);
+}
+
+static void
+almanah_link_manager_init (AlmanahLinkManager *self)
+{
+	guint i;
+
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_LINK_MANAGER, AlmanahLinkManagerPrivate);
+
+	/* Set up the list of AlmanahLinkFactories */
+	self->priv->factories = g_new (AlmanahLinkFactory*, G_N_ELEMENTS (link_factory_types) + 1);
+	for (i = 0; i < G_N_ELEMENTS (link_factory_types); i++) {
+		self->priv->factories[i] = g_object_new (link_factory_types[i].type_function (), NULL);
+		g_signal_connect (self->priv->factories[i], "links-updated", G_CALLBACK (links_updated_cb), self);
+	}
+	self->priv->factories[i] = NULL;
+}
+
+static void
+almanah_link_manager_dispose (GObject *object)
+{
+	guint i = 0;
+	AlmanahLinkManagerPrivate *priv = ALMANAH_LINK_MANAGER_GET_PRIVATE (object);
+
+	/* Free the factories */
+	if (priv->factories != NULL) {
+		for (i = 0; priv->factories[i] != NULL; i++)
+			g_object_unref (priv->factories[i]);
+		g_free (priv->factories);
+	}
+	priv->factories = NULL;
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (almanah_link_manager_parent_class)->dispose (object);
+}
+
+AlmanahLinkManager *
+almanah_link_manager_new (void)
+{
+	return g_object_new (ALMANAH_TYPE_LINK_MANAGER, NULL);
+}
+
+static void
+links_updated_cb (AlmanahLinkFactory *factory, AlmanahLinkManager *self)
+{
+	g_signal_emit (self, link_manager_signals[SIGNAL_LINKS_UPDATED], 0, almanah_link_factory_get_type_id (factory));
+}
+
+void
+almanah_link_manager_query_links (AlmanahLinkManager *self, AlmanahLinkFactoryType type_id, GDate *date)
+{
+	AlmanahLinkManagerPrivate *priv = ALMANAH_LINK_MANAGER_GET_PRIVATE (self);
+	guint i;
+
+	if (almanah->debug == TRUE)
+		g_debug ("almanah_link_manager_query_links called for factory %u and date %u-%u-%u.", type_id, g_date_get_year (date), g_date_get_month (date), g_date_get_day (date));
+
+	if (type_id != ALMANAH_LINK_FACTORY_UNKNOWN) {
+		/* Just query that factory */
+		for (i = 0; priv->factories[i] != NULL; i++) {
+			if (almanah_link_factory_get_type_id (priv->factories[i]) == type_id)
+				almanah_link_factory_query_links (priv->factories[i], date);
+		}
+
+		return;
+	}
+
+	/* Otherwise, query all factories */
+	for (i = 0; priv->factories[i] != NULL; i++)
+		almanah_link_factory_query_links (priv->factories[i], date);
+}
+
+GSList *
+almanah_link_manager_get_links (AlmanahLinkManager *self, AlmanahLinkFactoryType type_id, GDate *date)
+{
+	AlmanahLinkManagerPrivate *priv = ALMANAH_LINK_MANAGER_GET_PRIVATE (self);
+	GSList *list = NULL, *end = NULL;
+	guint i;
+
+	if (almanah->debug == TRUE)
+		g_debug ("almanah_link_manager_get_links called for factory %u and date %u-%u-%u.", type_id, g_date_get_year (date), g_date_get_month (date), g_date_get_day (date));
+
+	if (type_id != ALMANAH_LINK_FACTORY_UNKNOWN) {
+		/* Just return the links for the specified link factory */
+		for (i = 0; priv->factories[i] != NULL; i++) {
+			if (almanah_link_factory_get_type_id (priv->factories[i]) == type_id)
+				return almanah_link_factory_get_links (priv->factories[i], date);
+		}
+
+		return NULL;
+	}
+
+	/* Otherwise, return a concatenation of all factories' links */
+	for (i = 0; priv->factories[i] != NULL; i++) {
+		GSList *end2;
+
+		end2 = almanah_link_factory_get_links (priv->factories[i], date);
+		end = g_slist_concat (end, end2); /* assignment's only to shut gcc up */
+		end = end2;
+
+		if (list == NULL)
+			list = end;
+	}
+
+	return list;
+}

Added: trunk/src/link-manager.h
==============================================================================
--- (empty file)
+++ trunk/src/link-manager.h	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,57 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALMANAH_LINK_MANAGER_H
+#define ALMANAH_LINK_MANAGER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "link-factory.h"
+
+G_BEGIN_DECLS
+
+#define ALMANAH_TYPE_LINK_MANAGER		(almanah_link_manager_get_type ())
+#define ALMANAH_LINK_MANAGER(o)			(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_LINK_MANAGER, AlmanahLinkManager))
+#define ALMANAH_LINK_MANAGER_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_LINK_MANAGER, AlmanahLinkManagerClass))
+#define ALMANAH_IS_LINK_MANAGER(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_LINK_MANAGER))
+#define ALMANAH_IS_LINK_MANAGER_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_LINK_MANAGER))
+#define ALMANAH_LINK_MANAGER_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_LINK_MANAGER, AlmanahLinkManagerClass))
+
+typedef struct _AlmanahLinkManagerPrivate	AlmanahLinkManagerPrivate;
+
+typedef struct {
+	GObject parent;
+	AlmanahLinkManagerPrivate *priv;
+} AlmanahLinkManager;
+
+typedef struct {
+	GObjectClass parent;
+} AlmanahLinkManagerClass;
+
+GType almanah_link_manager_get_type (void);
+
+AlmanahLinkManager *almanah_link_manager_new (void);
+
+void almanah_link_manager_query_links (AlmanahLinkManager *self, AlmanahLinkFactoryType type_id, GDate *date);
+GSList *almanah_link_manager_get_links (AlmanahLinkManager *self, AlmanahLinkFactoryType type_id, GDate *date);
+
+G_END_DECLS
+
+#endif /* !ALMANAH_LINK_MANAGER_H */

Modified: trunk/src/link.c
==============================================================================
--- trunk/src/link.c	(original)
+++ trunk/src/link.c	Sat Dec  6 20:53:07 2008
@@ -25,40 +25,13 @@
 #include "link.h"
 #include "main.h"
 
-typedef struct {
-	gchar *type_id;
-	GType (*type_function) (void);
-} AlmanahLinkType;
-
-/* TODO: This is still a little hacky */
-
-#include "file.h"
-#include "note.h"
-#include "uri.h"
-
-static const AlmanahLinkType link_types[] = {
-	{ "file", &almanah_file_link_get_type },
-	{ "note", &almanah_note_link_get_type },
-	{ "uri", &almanah_uri_link_get_type }
-};
-
 static void almanah_link_init (AlmanahLink *self);
-static void almanah_link_finalize (GObject *object);
 static void almanah_link_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
-static void almanah_link_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
-
-struct _AlmanahLinkPrivate {
-	gchar *value;
-	gchar *value2;
-};
 
 enum {
-	PROP_TYPE_ID = 1,
-	PROP_NAME,
+	PROP_NAME = 1,
 	PROP_DESCRIPTION,
-	PROP_ICON_NAME,
-	PROP_VALUE,
-	PROP_VALUE2
+	PROP_ICON_NAME
 };
 
 G_DEFINE_ABSTRACT_TYPE (AlmanahLink, almanah_link, G_TYPE_OBJECT)
@@ -69,17 +42,8 @@
 {
 	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
 
-	g_type_class_add_private (klass, sizeof (AlmanahLinkPrivate));
-
-	gobject_class->set_property = almanah_link_set_property;
 	gobject_class->get_property = almanah_link_get_property;
-	gobject_class->finalize = almanah_link_finalize;
 
-	g_object_class_install_property (gobject_class, PROP_TYPE_ID,
-				g_param_spec_string ("type-id",
-					"Type ID", "The type ID of this link.",
-					NULL,
-					G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 	g_object_class_install_property (gobject_class, PROP_NAME,
 				g_param_spec_string ("name",
 					"Name", "The human-readable name for this link type.",
@@ -95,60 +59,27 @@
 					"Icon Name", "The icon name for this link type.",
 					NULL,
 					G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
-	g_object_class_install_property (gobject_class, PROP_VALUE,
-				g_param_spec_string ("value",
-					"Value", "The first value of this link.",
-					NULL,
-					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-	g_object_class_install_property (gobject_class, PROP_VALUE2,
-				g_param_spec_string ("value2",
-					"Value 2", "The second value of this link.",
-					NULL,
-					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 static void
 almanah_link_init (AlmanahLink *self)
 {
-	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_LINK, AlmanahLinkPrivate);
-}
-
-static void
-almanah_link_finalize (GObject *object)
-{
-	AlmanahLinkPrivate *priv = ALMANAH_LINK (object)->priv;
-
-	g_free (priv->value);
-	g_free (priv->value2);
-
-	/* Chain up to the parent class */
-	G_OBJECT_CLASS (almanah_link_parent_class)->finalize (object);
 }
 
 static void
 almanah_link_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
 {
-	AlmanahLinkPrivate *priv = ALMANAH_LINK (object)->priv;
 	AlmanahLinkClass *klass = ALMANAH_LINK_GET_CLASS (object);
 
 	switch (property_id) {
-		case PROP_TYPE_ID:
-			g_value_set_string (value, g_strdup (klass->type_id));
-			break;
 		case PROP_NAME:
-			g_value_set_string (value, g_strdup (klass->name));
+			g_value_set_string (value, klass->name);
 			break;
 		case PROP_DESCRIPTION:
-			g_value_set_string (value, g_strdup (klass->description));
+			g_value_set_string (value, klass->description);
 			break;
 		case PROP_ICON_NAME:
-			g_value_set_string (value, g_strdup (klass->icon_name));
-			break;
-		case PROP_VALUE:
-			g_value_set_string (value, g_strdup (priv->value));
-			break;
-		case PROP_VALUE2:
-			g_value_set_string (value, g_strdup (priv->value2));
+			g_value_set_string (value, klass->icon_name);
 			break;
 		default:
 			/* We don't have any other property... */
@@ -157,62 +88,7 @@
 	}
 }
 
-static void
-almanah_link_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
-{
-	AlmanahLinkPrivate *priv = ALMANAH_LINK (object)->priv;
-
-	switch (property_id) {
-		case PROP_VALUE:
-			g_free (priv->value);
-			priv->value = g_value_dup_string (value);
-			break;
-		case PROP_VALUE2:
-			g_free (priv->value2);
-			priv->value2 = g_value_dup_string (value);
-			break;
-		default:
-			/* We don't have any other property... */
-			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-			break;
-	}
-}
-
-AlmanahLink *
-almanah_link_new (const gchar *type_id)
-{
-	guint lower_limit, upper_limit;
-
-	/* Do a binary search */
-	lower_limit = 0;
-	upper_limit = G_N_ELEMENTS (link_types) - 1;
-
-	/* TODO: Use GQuarks to make things less heavy on the strcmp()s? */
-	while (TRUE) {
-		guint temp;
-		gint comparison;
-
-		temp = ceil ((lower_limit + upper_limit) / 2);
-		comparison = strcmp (type_id, link_types[temp].type_id);
-
-		/* Exit condition */
-		if (lower_limit == upper_limit && comparison != 0)
-			return NULL;
-
-		if (comparison < 0) {
-			upper_limit = temp - 1; /* It's in the lower half */
-		} else if (comparison > 0) {
-			lower_limit = temp + 1; /* It's in the upper half */
-		} else {
-			/* Match! */
-			return g_object_new (link_types[temp].type_function (), NULL);
-		}
-	};
-
-	return NULL;
-}
-
-gchar *
+const gchar *
 almanah_link_format_value (AlmanahLink *self)
 {
 	AlmanahLinkClass *klass = ALMANAH_LINK_GET_CLASS (self);
@@ -225,57 +101,9 @@
 {
 	AlmanahLinkClass *klass = ALMANAH_LINK_GET_CLASS (self);
 	g_assert (klass->view != NULL);
-
-	if (almanah->debug)
-		g_debug ("Viewing %s link ('%s', '%s')", klass->type_id, self->priv->value, self->priv->value2);
-
 	return klass->view (self);
 }
 
-void
-almanah_link_build_dialog (AlmanahLink *self, GtkVBox *parent_vbox)
-{
-	AlmanahLinkClass *klass = ALMANAH_LINK_GET_CLASS (self);
-	g_assert (klass->build_dialog != NULL);
-	return klass->build_dialog (self, parent_vbox);
-}
-
-void
-almanah_link_get_values (AlmanahLink *self)
-{
-	AlmanahLinkClass *klass = ALMANAH_LINK_GET_CLASS (self);
-	g_assert (klass->get_values != NULL);
-	return klass->get_values (self);
-}
-
-void
-almanah_link_populate_model (GtkListStore *list_store, guint type_id_column, guint name_column, guint icon_name_column)
-{
-	GtkTreeIter iter;
-	guint i;
-
-	for (i = 0; i < G_N_ELEMENTS (link_types); i++) {
-		AlmanahLink *link = g_object_new (link_types[i].type_function (), NULL);
-
-		if (link == NULL)
-			continue;
-
-		gtk_list_store_append (list_store, &iter);
-		gtk_list_store_set (list_store, &iter,
-				    type_id_column, link_types[i].type_id,
-				    name_column, almanah_link_get_name (link),
-				    icon_name_column, almanah_link_get_icon_name (link),
-				    -1);
-	}
-}
-
-const gchar *
-almanah_link_get_type_id (AlmanahLink *self)
-{
-	AlmanahLinkClass *klass = ALMANAH_LINK_GET_CLASS (self);
-	return klass->type_id;
-}
-
 const gchar *
 almanah_link_get_name (AlmanahLink *self)
 {
@@ -296,30 +124,3 @@
 	AlmanahLinkClass *klass = ALMANAH_LINK_GET_CLASS (self);
 	return klass->icon_name;
 }
-
-const gchar *
-almanah_link_get_value (AlmanahLink *self)
-{
-	return self->priv->value;
-}
-
-void
-almanah_link_set_value (AlmanahLink *self, const gchar *value)
-{
-	g_free (self->priv->value);
-	self->priv->value = g_strdup (value);
-}
-
-const gchar *
-almanah_link_get_value2 (AlmanahLink *self)
-{
-	return self->priv->value2;
-}
-
-void
-almanah_link_set_value2 (AlmanahLink *self, const gchar *value)
-{
-	g_free (self->priv->value2);
-	self->priv->value2 = g_strdup (value);
-}
-

Modified: trunk/src/link.h
==============================================================================
--- trunk/src/link.h	(original)
+++ trunk/src/link.h	Sat Dec  6 20:53:07 2008
@@ -37,38 +37,26 @@
 
 typedef struct {
 	GObject parent;
-	AlmanahLinkPrivate *priv;
 } AlmanahLink;
 
 typedef struct {
 	GObjectClass parent;
 
-	gchar *type_id;
 	gchar *name;
 	gchar *description;
 	gchar *icon_name;
 
-	gchar *(*format_value) (AlmanahLink *link);
+	const gchar *(*format_value) (AlmanahLink *link);
 	gboolean (*view) (AlmanahLink *link);
-	void (*build_dialog) (AlmanahLink *link, GtkVBox *parent_vbox);
-	void (*get_values) (AlmanahLink *link);
 } AlmanahLinkClass;
 
 GType almanah_link_get_type (void);
-AlmanahLink *almanah_link_new (const gchar *type_id);
-gchar *almanah_link_format_value (AlmanahLink *self);
+
+const gchar *almanah_link_format_value (AlmanahLink *self);
 gboolean almanah_link_view (AlmanahLink *self);
-void almanah_link_build_dialog (AlmanahLink *self, GtkVBox *parent_vbox);
-void almanah_link_get_values (AlmanahLink *self);
-void almanah_link_populate_model (GtkListStore *list_store, guint type_id_column, guint name_column, guint icon_name_column);
-const gchar *almanah_link_get_type_id (AlmanahLink *self);
 const gchar *almanah_link_get_name (AlmanahLink *self);
 const gchar *almanah_link_get_description (AlmanahLink *self);
 const gchar *almanah_link_get_icon_name (AlmanahLink *self);
-const gchar *almanah_link_get_value (AlmanahLink *self);
-void almanah_link_set_value (AlmanahLink *self, const gchar *value);
-const gchar *almanah_link_get_value2 (AlmanahLink *self);
-void almanah_link_set_value2 (AlmanahLink *self, const gchar *value);
 
 G_END_DECLS
 

Added: trunk/src/links/calendar-appointment.c
==============================================================================
--- (empty file)
+++ trunk/src/links/calendar-appointment.c	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,96 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "link.h"
+#include "calendar-appointment.h"
+
+static void almanah_calendar_appointment_link_init (AlmanahCalendarAppointmentLink *self);
+static void almanah_calendar_appointment_link_finalize (GObject *object);
+static const gchar *almanah_calendar_appointment_link_format_value (AlmanahLink *link);
+static gboolean almanah_calendar_appointment_link_view (AlmanahLink *link);
+
+struct _AlmanahCalendarAppointmentLinkPrivate {
+	gchar *uid;
+	gchar *summary;
+};
+
+G_DEFINE_TYPE (AlmanahCalendarAppointmentLink, almanah_calendar_appointment_link, ALMANAH_TYPE_LINK)
+#define ALMANAH_CALENDAR_APPOINTMENT_LINK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_CALENDAR_APPOINTMENT_LINK, AlmanahCalendarAppointmentLinkPrivate))
+
+static void
+almanah_calendar_appointment_link_class_init (AlmanahCalendarAppointmentLinkClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	AlmanahLinkClass *link_class = ALMANAH_LINK_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (AlmanahCalendarAppointmentLinkPrivate));
+
+	gobject_class->finalize = almanah_calendar_appointment_link_finalize;
+
+	link_class->name = _("Calendar Appointment");
+	link_class->description = _("An appointment on an Evolution calendar.");
+	link_class->icon_name = "appointment-new";
+
+	link_class->format_value = almanah_calendar_appointment_link_format_value;
+	link_class->view = almanah_calendar_appointment_link_view;
+}
+
+static void
+almanah_calendar_appointment_link_init (AlmanahCalendarAppointmentLink *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_CALENDAR_APPOINTMENT_LINK, AlmanahCalendarAppointmentLinkPrivate);
+}
+
+static void
+almanah_calendar_appointment_link_finalize (GObject *object)
+{
+	AlmanahCalendarAppointmentLinkPrivate *priv = ALMANAH_CALENDAR_APPOINTMENT_LINK_GET_PRIVATE (object);
+
+	g_free (priv->uid);
+	g_free (priv->summary);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (almanah_calendar_appointment_link_parent_class)->finalize (object);
+}
+
+AlmanahCalendarAppointmentLink *
+almanah_calendar_appointment_link_new (const gchar *uid, const gchar *summary)
+{
+	AlmanahCalendarAppointmentLink *link = g_object_new (ALMANAH_TYPE_CALENDAR_APPOINTMENT_LINK, NULL);
+	link->priv->uid = g_strdup (uid);
+	link->priv->summary = g_strdup (summary);
+	return link;
+}
+
+static const gchar *
+almanah_calendar_appointment_link_format_value (AlmanahLink *link)
+{
+	/* TODO */
+	return ALMANAH_CALENDAR_APPOINTMENT_LINK (link)->priv->summary;
+}
+
+static gboolean
+almanah_calendar_appointment_link_view (AlmanahLink *link)
+{
+	/* TODO */
+	return TRUE;
+}

Added: trunk/src/links/calendar-appointment.h
==============================================================================
--- (empty file)
+++ trunk/src/links/calendar-appointment.h	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,53 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALMANAH_CALENDAR_APPOINTMENT_LINK_H
+#define ALMANAH_CALENDAR_APPOINTMENT_LINK_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "link.h"
+
+G_BEGIN_DECLS
+
+#define ALMANAH_TYPE_CALENDAR_APPOINTMENT_LINK		(almanah_calendar_appointment_link_get_type ())
+#define ALMANAH_CALENDAR_APPOINTMENT_LINK(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_CALENDAR_APPOINTMENT_LINK, AlmanahCalendarAppointmentLink))
+#define ALMANAH_CALENDAR_APPOINTMENT_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_CALENDAR_APPOINTMENT_LINK, AlmanahCalendarAppointmentLinkClass))
+#define ALMANAH_IS_CALENDAR_APPOINTMENT_LINK(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_CALENDAR_APPOINTMENT_LINK))
+#define ALMANAH_IS_CALENDAR_APPOINTMENT_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_CALENDAR_APPOINTMENT_LINK))
+#define ALMANAH_CALENDAR_APPOINTMENT_LINK_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_CALENDAR_APPOINTMENT_LINK, AlmanahCalendarAppointmentLinkClass))
+
+typedef struct _AlmanahCalendarAppointmentLinkPrivate	AlmanahCalendarAppointmentLinkPrivate;
+
+typedef struct {
+	AlmanahLink parent;
+	AlmanahCalendarAppointmentLinkPrivate *priv;
+} AlmanahCalendarAppointmentLink;
+
+typedef struct {
+	AlmanahLinkClass parent;
+} AlmanahCalendarAppointmentLinkClass;
+
+GType almanah_calendar_appointment_link_get_type (void);
+AlmanahCalendarAppointmentLink *almanah_calendar_appointment_link_new (const gchar *uid, const gchar *summary);
+
+G_END_DECLS
+
+#endif /* !ALMANAH_CALENDAR_APPOINTMENT_LINK_H */

Added: trunk/src/links/calendar-task.c
==============================================================================
--- (empty file)
+++ trunk/src/links/calendar-task.c	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,96 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "link.h"
+#include "calendar-task.h"
+
+static void almanah_calendar_task_link_init (AlmanahCalendarTaskLink *self);
+static void almanah_calendar_task_link_finalize (GObject *object);
+static const gchar *almanah_calendar_task_link_format_value (AlmanahLink *link);
+static gboolean almanah_calendar_task_link_view (AlmanahLink *link);
+
+struct _AlmanahCalendarTaskLinkPrivate {
+	gchar *uid;
+	gchar *summary;
+};
+
+G_DEFINE_TYPE (AlmanahCalendarTaskLink, almanah_calendar_task_link, ALMANAH_TYPE_LINK)
+#define ALMANAH_CALENDAR_TASK_LINK_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_CALENDAR_TASK_LINK, AlmanahCalendarTaskLinkPrivate))
+
+static void
+almanah_calendar_task_link_class_init (AlmanahCalendarTaskLinkClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	AlmanahLinkClass *link_class = ALMANAH_LINK_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (AlmanahCalendarTaskLinkPrivate));
+
+	gobject_class->finalize = almanah_calendar_task_link_finalize;
+
+	link_class->name = _("Calendar Task");
+	link_class->description = _("A task on an Evolution calendar.");
+	link_class->icon_name = "stock_task";
+
+	link_class->format_value = almanah_calendar_task_link_format_value;
+	link_class->view = almanah_calendar_task_link_view;
+}
+
+static void
+almanah_calendar_task_link_init (AlmanahCalendarTaskLink *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_CALENDAR_TASK_LINK, AlmanahCalendarTaskLinkPrivate);
+}
+
+static void
+almanah_calendar_task_link_finalize (GObject *object)
+{
+	AlmanahCalendarTaskLinkPrivate *priv = ALMANAH_CALENDAR_TASK_LINK_GET_PRIVATE (object);
+
+	g_free (priv->uid);
+	g_free (priv->summary);
+
+	/* Chain up to the parent class */
+	G_OBJECT_CLASS (almanah_calendar_task_link_parent_class)->finalize (object);
+}
+
+AlmanahCalendarTaskLink *
+almanah_calendar_task_link_new (const gchar *uid, const gchar *summary)
+{
+	AlmanahCalendarTaskLink *link = g_object_new (ALMANAH_TYPE_CALENDAR_TASK_LINK, NULL);
+	link->priv->uid = g_strdup (uid);
+	link->priv->summary = g_strdup (summary);
+	return link;
+}
+
+static const gchar *
+almanah_calendar_task_link_format_value (AlmanahLink *link)
+{
+	/* TODO */
+	return ALMANAH_CALENDAR_TASK_LINK (link)->priv->summary;
+}
+
+static gboolean
+almanah_calendar_task_link_view (AlmanahLink *link)
+{
+	/* TODO */
+	return TRUE;
+}

Added: trunk/src/links/calendar-task.h
==============================================================================
--- (empty file)
+++ trunk/src/links/calendar-task.h	Sat Dec  6 20:53:07 2008
@@ -0,0 +1,53 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2008 <philip tecnocode co uk>
+ * 
+ * Almanah is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Almanah 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 Almanah.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef ALMANAH_CALENDAR_TASK_LINK_H
+#define ALMANAH_CALENDAR_TASK_LINK_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "link.h"
+
+G_BEGIN_DECLS
+
+#define ALMANAH_TYPE_CALENDAR_TASK_LINK		(almanah_calendar_task_link_get_type ())
+#define ALMANAH_CALENDAR_TASK_LINK(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_CALENDAR_TASK_LINK, AlmanahCalendarTaskLink))
+#define ALMANAH_CALENDAR_TASK_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_CALENDAR_TASK_LINK, AlmanahCalendarTaskLinkClass))
+#define ALMANAH_IS_CALENDAR_TASK_LINK(o)	(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_CALENDAR_TASK_LINK))
+#define ALMANAH_IS_CALENDAR_TASK_LINK_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_CALENDAR_TASK_LINK))
+#define ALMANAH_CALENDAR_TASK_LINK_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_CALENDAR_TASK_LINK, AlmanahCalendarTaskLinkClass))
+
+typedef struct _AlmanahCalendarTaskLinkPrivate	AlmanahCalendarTaskLinkPrivate;
+
+typedef struct {
+	AlmanahLink parent;
+	AlmanahCalendarTaskLinkPrivate *priv;
+} AlmanahCalendarTaskLink;
+
+typedef struct {
+	AlmanahLinkClass parent;
+} AlmanahCalendarTaskLinkClass;
+
+GType almanah_calendar_task_link_get_type (void);
+AlmanahCalendarTaskLink *almanah_calendar_task_link_new (const gchar *uid, const gchar *summary);
+
+G_END_DECLS
+
+#endif /* !ALMANAH_CALENDAR_TASK_LINK_H */

Modified: trunk/src/main-window.c
==============================================================================
--- trunk/src/main-window.c	(original)
+++ trunk/src/main-window.c	Sat Dec  6 20:53:07 2008
@@ -29,10 +29,12 @@
 #include "main-window.h"
 #include "main.h"
 #include "interface.h"
-#include "add-link-dialog.h"
+#include "add-definition-dialog.h"
 #include "printing.h"
 #include "entry.h"
 #include "storage-manager.h"
+#include "link.h"
+#include "definition.h"
 
 static void almanah_main_window_init (AlmanahMainWindow *self);
 static void almanah_main_window_dispose (GObject *object);
@@ -46,6 +48,7 @@
 static void mw_bold_toggled_cb (GtkToggleAction *action, AlmanahMainWindow *main_window);
 static void mw_italic_toggled_cb (GtkToggleAction *action, AlmanahMainWindow *main_window);
 static void mw_underline_toggled_cb (GtkToggleAction *action, AlmanahMainWindow *main_window);
+static void mw_links_updated_cb (AlmanahLinkManager *link_manager, AlmanahLinkFactoryType type_id, AlmanahMainWindow *main_window);
 void mw_calendar_day_selected_cb (GtkCalendar *calendar, AlmanahMainWindow *main_window);
 static void mw_links_selection_changed_cb (GtkTreeSelection *tree_selection, AlmanahMainWindow *main_window);
 static void mw_links_value_data_cb (GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
@@ -55,8 +58,6 @@
 	GtkTextBuffer *entry_buffer;
 	GtkCalendar *calendar;
 	GtkLabel *date_label;
-	GtkButton *add_button;
-	GtkButton *remove_button;
 	GtkButton *view_button;
 	GtkAction *add_action;
 	GtkAction *remove_action;
@@ -168,11 +169,9 @@
 	priv->entry_buffer = gtk_text_view_get_buffer (priv->entry_view);
 	priv->calendar = GTK_CALENDAR (gtk_builder_get_object (builder, "dry_mw_calendar"));
 	priv->date_label = GTK_LABEL (gtk_builder_get_object (builder, "dry_mw_date_label"));
-	priv->add_button = GTK_BUTTON (gtk_builder_get_object (builder, "dry_mw_add_button"));
-	priv->remove_button = GTK_BUTTON (gtk_builder_get_object (builder, "dry_mw_remove_button"));
 	priv->view_button = GTK_BUTTON (gtk_builder_get_object (builder, "dry_mw_view_button"));
-	priv->add_action = GTK_ACTION (gtk_builder_get_object (builder, "dry_ui_add_link"));
-	priv->remove_action = GTK_ACTION (gtk_builder_get_object (builder, "dry_ui_remove_link"));
+	priv->add_action = GTK_ACTION (gtk_builder_get_object (builder, "dry_ui_add_definition"));
+	priv->remove_action = GTK_ACTION (gtk_builder_get_object (builder, "dry_ui_remove_definition"));
 	priv->link_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "dry_mw_link_store"));
 	priv->links_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (gtk_builder_get_object (builder, "dry_mw_links_tree_view")));
 	priv->link_value_column = GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (builder, "dry_mw_link_value_column"));
@@ -185,15 +184,7 @@
 	priv->delete_action = GTK_ACTION (gtk_builder_get_object (builder, "dry_ui_delete"));
 
 	/* Set up text formatting */
-	gtk_text_buffer_create_tag (priv->entry_buffer, "bold", 
-				    "weight", PANGO_WEIGHT_BOLD, 
-				    NULL);
-	gtk_text_buffer_create_tag (priv->entry_buffer, "italic",
-				    "style", PANGO_STYLE_ITALIC,
-				    NULL);
-	gtk_text_buffer_create_tag (priv->entry_buffer, "underline",
-				    "underline", PANGO_UNDERLINE_SINGLE,
-				    NULL);
+	almanah_interface_create_text_tags (priv->entry_buffer, TRUE);
 
 #ifdef ENABLE_SPELL_CHECKING
 	/* Set up spell checking */
@@ -236,6 +227,9 @@
 	g_signal_connect (priv->italic_action, "toggled", G_CALLBACK (mw_italic_toggled_cb), main_window);
 	g_signal_connect (priv->underline_action, "toggled", G_CALLBACK (mw_underline_toggled_cb), main_window);
 
+	/* Notification for link changes */
+	g_signal_connect (almanah->link_manager, "links-updated", G_CALLBACK (mw_links_updated_cb), main_window);
+
 	/* Select the current day and month */
 	almanah_calendar_month_changed_cb (priv->calendar, NULL);
 	mw_calendar_day_selected_cb (priv->calendar, main_window);
@@ -409,20 +403,14 @@
 	almanah_storage_manager_set_entry (almanah->storage_manager, priv->current_entry);
 
 	/* Mark the day on the calendar if the entry was non-empty (and deleted)
-	 * and update the state of the add link button. */
+	 * and update the state of the add definition action. */
 	if (entry_is_empty == TRUE) {
 		gtk_calendar_unmark_day (priv->calendar, g_date_get_day (&date));
 
-		gtk_widget_set_sensitive (GTK_WIDGET (priv->add_button), FALSE);
-		gtk_action_set_sensitive (priv->add_action, FALSE);
-
 		/* Since the entry is empty, remove all the links from the treeview */
 		gtk_list_store_clear (priv->link_store);
 	} else {
 		gtk_calendar_mark_day (priv->calendar, g_date_get_day (&date));
-
-		gtk_widget_set_sensitive (GTK_WIDGET (priv->add_button), TRUE);
-		gtk_action_set_sensitive (priv->add_action, TRUE);
 	}
 }
 
@@ -437,68 +425,78 @@
 }
 
 static void
-add_link_to_current_entry (AlmanahMainWindow *self)
+add_definition_to_current_entry (AlmanahMainWindow *self)
 {
-	GtkTreeIter iter;
 	AlmanahMainWindowPrivate *priv = self->priv;
+	GtkTextIter start_iter, end_iter;
+	gchar *text;
 
 	g_assert (priv->entry_buffer != NULL);
 	g_assert (gtk_text_buffer_get_char_count (priv->entry_buffer) != 0);
 
-	/* Ensure that something is selected and its widgets displayed */
-	gtk_widget_show_all (almanah->add_link_dialog);
+	if (gtk_text_buffer_get_selection_bounds (priv->entry_buffer, &start_iter, &end_iter) == FALSE)
+		return;
+
+	text = gtk_text_buffer_get_text (priv->entry_buffer, &start_iter, &end_iter, FALSE);
 
-	if (gtk_dialog_run (GTK_DIALOG (almanah->add_link_dialog)) == GTK_RESPONSE_OK) {
-		GDate date;
-		AlmanahLink *link = almanah_add_link_dialog_get_link (ALMANAH_ADD_LINK_DIALOG (almanah->add_link_dialog));
+	/* Ensure that something is selected and its widget's displayed */
+	almanah_add_definition_dialog_set_text (ALMANAH_ADD_DEFINITION_DIALOG (almanah->add_definition_dialog), text);
+	g_free (text);
+	gtk_widget_show_all (almanah->add_definition_dialog);
 
-		if (link == NULL)
+	if (gtk_dialog_run (GTK_DIALOG (almanah->add_definition_dialog)) == GTK_RESPONSE_OK) {
+		AlmanahDefinition *definition;
+
+		definition = almanah_add_definition_dialog_get_definition (ALMANAH_ADD_DEFINITION_DIALOG (almanah->add_definition_dialog));
+		if (definition == NULL)
 			return;
 
 		/* Add to the DB */
-		get_selected_date (self, &date);
-		almanah_storage_manager_add_entry_link (almanah->storage_manager, &date, link);
-
-		/* Add to the treeview */
-		gtk_list_store_append (priv->link_store, &iter);
-		gtk_list_store_set (priv->link_store, &iter,
-				    0, almanah_link_get_type_id (link),
-				    1, almanah_link_get_value (link),
-				    2, almanah_link_get_value2 (link),
-				    3, almanah_link_get_icon_name (link),
-				    -1);
+		almanah_storage_manager_add_definition (almanah->storage_manager, definition);
 
-		g_object_unref (link);
+		/* Add a GtkTextTag to the GtkTextBuffer to mark the definition */
+		gtk_text_buffer_apply_tag_by_name (priv->entry_buffer, "definition", &start_iter, &end_iter);
+		gtk_text_buffer_set_modified (priv->entry_buffer, TRUE);
 	}
 }
 
 static void
-remove_link_from_current_entry (AlmanahMainWindow *self)
+remove_definition_from_current_entry (AlmanahMainWindow *self)
 {
-	gchar *link_type;
-	GDate date;
-	GtkTreeIter iter;
-	GtkTreeModel *model;
-	GList *links;
+	/* We don't actually remove the definition from the database, since other entries may use it;
+	 * we simply remove the formatting. */
 	AlmanahMainWindowPrivate *priv = self->priv;
-
-	links = gtk_tree_selection_get_selected_rows (priv->links_selection, &model);
-	get_selected_date (self, &date);
-
-	for (; links != NULL; links = links->next) {
-		gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) links->data);
-		gtk_tree_model_get (model, &iter, 0, &link_type, -1);
-
-		/* Remove it from the DB */
-		almanah_storage_manager_remove_entry_link (almanah->storage_manager, &date, link_type);
-
-		/* Remove it from the treeview */
-		gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
-
-		gtk_tree_path_free (links->data);
-		g_free (link_type);
-	}
-	g_list_free (links);
+	GtkTextIter start_iter, end_iter;
+	GtkTextTag *tag;
+	GtkTextTagTable *tag_table;
+
+	/* Find the tag in the tag table */
+	tag_table = gtk_text_buffer_get_tag_table (priv->entry_buffer);
+	tag = gtk_text_tag_table_lookup (tag_table, "definition");
+	g_assert (tag != NULL);
+
+	/* Find the boundaries of the tag (ignoring everything except the position
+	 * of the start iter of any selection the user's made). */
+	gtk_text_buffer_get_selection_bounds (priv->entry_buffer, &start_iter, NULL);
+	end_iter = start_iter;
+
+	if (gtk_text_iter_begins_tag (&start_iter, tag) == TRUE) {
+		/* We're at the start of the tag */
+		gtk_text_iter_forward_to_tag_toggle (&end_iter, tag);
+	} else if (gtk_text_iter_ends_tag (&start_iter, tag) == TRUE) {
+		/* We're at the end of the tag */
+		end_iter = start_iter;
+		gtk_text_iter_backward_to_tag_toggle (&start_iter, tag);
+	} else {
+		/* We're somewhere in the middle of the tag */
+		gtk_text_iter_backward_to_tag_toggle (&start_iter, tag);
+		end_iter = start_iter;
+		gtk_text_iter_forward_to_tag_toggle (&end_iter, tag);
+	}
+		
+	/* Remove the tag */
+	gtk_text_buffer_remove_tag (priv->entry_buffer, tag, &start_iter, &end_iter);
+	gtk_text_buffer_set_modified (priv->entry_buffer, TRUE);
 }
 
 void
@@ -515,7 +513,7 @@
 	AlmanahMainWindowPrivate *priv = main_window->priv;
 	GSList *_tag_list = NULL, *tag_list = NULL;
 	gboolean range_selected = FALSE;
-	gboolean bold_toggled = FALSE, italic_toggled = FALSE, underline_toggled = FALSE;
+	gboolean bold_toggled = FALSE, italic_toggled = FALSE, underline_toggled = FALSE, remove_definition_toggled = FALSE;
 
 	/* Ensure we don't overwrite current formatting options when characters are being typed.
 	 * (Execution of this function will be sandwiched between:
@@ -530,6 +528,9 @@
 	if (range_selected == FALSE)
 		_tag_list = gtk_text_iter_get_tags (&iter);
 
+	/* We can only have the "add definition" action sensitive if there's a range selected */
+	gtk_action_set_sensitive (priv->add_action, (range_selected == TRUE) ? TRUE : FALSE);
+
 	/* Block signal handlers for the formatting actions while we're executing,
 	 * so formatting doesn't get unwittingly changed. */
 	priv->updating_formatting = TRUE;
@@ -553,7 +554,11 @@
 			underline_toggled = TRUE;
 		}
 
-		if (action) {
+		if (strcmp (tag_name, "definition") == 0) {
+			/* Deal with definition tags slightly differently --- just toggle the sensitivity of the "remove definition" action */
+			gtk_action_set_sensitive (priv->remove_action, TRUE);
+			remove_definition_toggled = TRUE;
+		} else if (action != NULL) {
 			/* Force the toggle status on the action */
 			gtk_toggle_action_set_active (action, TRUE);
 		} else {
@@ -575,6 +580,8 @@
 			gtk_toggle_action_set_active (priv->italic_action, FALSE);
 		if (underline_toggled == FALSE)
 			gtk_toggle_action_set_active (priv->underline_action, FALSE);
+		if (remove_definition_toggled == FALSE)
+			gtk_action_set_sensitive (priv->remove_action, FALSE);
 	}
 
 	/* Unblock signals */
@@ -691,18 +698,20 @@
 static void
 apply_formatting (AlmanahMainWindow *self, const gchar *tag_name, gboolean applying)
 {
+	AlmanahMainWindowPrivate *priv = self->priv;
 	GtkTextIter start, end;
 
 	/* Make sure we don't muck up the formatting when the actions are having
 	 * their sensitivity set by the code. */
-	if (self->priv->updating_formatting == TRUE)
+	if (priv->updating_formatting == TRUE)
 		return;
 
-	gtk_text_buffer_get_selection_bounds (self->priv->entry_buffer, &start, &end);
+	gtk_text_buffer_get_selection_bounds (priv->entry_buffer, &start, &end);
 	if (applying == TRUE)
-		gtk_text_buffer_apply_tag_by_name (self->priv->entry_buffer, tag_name, &start, &end);
+		gtk_text_buffer_apply_tag_by_name (priv->entry_buffer, tag_name, &start, &end);
 	else
-		gtk_text_buffer_remove_tag_by_name (self->priv->entry_buffer, tag_name, &start, &end);
+		gtk_text_buffer_remove_tag_by_name (priv->entry_buffer, tag_name, &start, &end);
+	gtk_text_buffer_set_modified (priv->entry_buffer, TRUE);
 }
 
 static void
@@ -727,7 +736,7 @@
 mw_about_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
 {
 	gchar *license, *description;
-	guint entry_count, link_count;
+	guint entry_count, definition_count;
 
 	const gchar *authors[] =
 	{
@@ -753,10 +762,10 @@
 			  _(license_parts[2]),
 			  NULL);
 
-	almanah_storage_manager_get_statistics (almanah->storage_manager, &entry_count, &link_count);
-	description = g_strdup_printf (_("A helpful diary keeper, storing %u entries and %u links."),
+	almanah_storage_manager_get_statistics (almanah->storage_manager, &entry_count, &definition_count);
+	description = g_strdup_printf (_("A helpful diary keeper, storing %u entries and %u definitions."),
 				      entry_count,
-				      link_count);
+				      definition_count);
 
 	gtk_show_about_dialog (GTK_WINDOW (main_window),
 				"version", VERSION,
@@ -789,15 +798,89 @@
 }
 
 void
-mw_add_link_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
+mw_add_definition_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
 {
-	add_link_to_current_entry (main_window);
+	add_definition_to_current_entry (main_window);
 }
 
 void
-mw_remove_link_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
+mw_remove_definition_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
 {
-	remove_link_from_current_entry (main_window);
+	remove_definition_from_current_entry (main_window);
+}
+
+static void
+clear_factory_links (AlmanahMainWindow *self, AlmanahLinkFactoryType type_id)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model = GTK_TREE_MODEL (self->priv->link_store);
+
+	if (almanah->debug == TRUE)
+		g_debug ("Removing links belonging to factory %u from the list store...", type_id);
+
+	if (gtk_tree_model_get_iter_first (model, &iter) == FALSE)
+		return;
+
+	while (TRUE) {
+		AlmanahLinkFactoryType row_type_id;
+
+		gtk_tree_model_get (model, &iter, 2, &row_type_id, -1);
+
+		/* TODO: Make sure the links are unreffed appropriately */
+		if (row_type_id == type_id) {
+			if (almanah->debug == TRUE) {
+				AlmanahLink *link;
+				gtk_tree_model_get (model, &iter, 0, &link, -1);
+				g_debug ("\t%s", almanah_link_format_value (link));
+			}
+
+			if (gtk_list_store_remove (GTK_LIST_STORE (model), &iter) == FALSE)
+				break;
+		} else if (gtk_tree_model_iter_next (model, &iter) == FALSE) {
+			/* Come to the end of the list */
+			break;
+		}
+	}
+
+	if (almanah->debug == TRUE)
+		g_debug ("Finished removing links.");
+}
+
+static void
+mw_links_updated_cb (AlmanahLinkManager *link_manager, AlmanahLinkFactoryType type_id, AlmanahMainWindow *main_window)
+{
+	AlmanahMainWindowPrivate *priv = main_window->priv;
+	GSList *_links, *links;
+	GDate date;
+
+	get_selected_date (main_window, &date);
+	_links = almanah_link_manager_get_links (link_manager, type_id, &date);
+
+	/* Clear all the links generated by this factory out of the list store first */
+	clear_factory_links (main_window, type_id);
+
+	if (almanah->debug == TRUE)
+		g_debug ("Adding links from factory %u to the list store...", type_id);
+
+	for (links = _links; links != NULL; links = g_slist_next (links)) {
+		GtkTreeIter iter;
+		AlmanahLink *link = links->data;
+
+		if (almanah->debug == TRUE)
+			g_debug ("\t%s", almanah_link_format_value (link));
+
+		gtk_list_store_append (priv->link_store, &iter);
+		gtk_list_store_set (priv->link_store, &iter,
+				    0, link,
+				    1, almanah_link_get_icon_name (link),
+				    2, type_id,
+				    -1);
+	}
+
+	if (almanah->debug == TRUE)
+		g_debug ("Finished adding links.");
+
+	g_slist_free (_links);
 }
 
 void
@@ -805,9 +888,6 @@
 {
 	GDate calendar_date;
 	gchar calendar_string[100];
-	AlmanahLink **links;
-	guint i;
-	GtkTreeIter iter;
 #ifdef ENABLE_SPELL_CHECKING
 	GtkSpell *gtkspell;
 #endif /* ENABLE_SPELL_CHECKING */
@@ -833,11 +913,9 @@
 
 	/* Prepare for the possibility of failure --- do as much of the general interface changes as possible first */
 	gtk_list_store_clear (priv->link_store);
-	gtk_widget_set_sensitive (GTK_WIDGET (priv->remove_button), FALSE); /* Only sensitive if something's selected */
-	gtk_action_set_sensitive (priv->remove_action, FALSE);
-	gtk_widget_set_sensitive (GTK_WIDGET (priv->view_button), FALSE);
-	gtk_widget_set_sensitive (GTK_WIDGET (priv->add_button), FALSE);
 	gtk_action_set_sensitive (priv->add_action, FALSE);
+	gtk_action_set_sensitive (priv->remove_action, FALSE); /* Only sensitive if something's selected */
+	gtk_widget_set_sensitive (GTK_WIDGET (priv->view_button), FALSE);
 
 	if (almanah_entry_is_empty (priv->current_entry) == FALSE) {
 		GError *error = NULL;
@@ -858,9 +936,6 @@
 
 			return;
 		}
-
-		gtk_widget_set_sensitive (GTK_WIDGET (priv->add_button), TRUE);
-		gtk_action_set_sensitive (priv->add_action, TRUE);
 	} else {
 		/* Set the buffer to be empty */
 		gtk_text_buffer_set_text (priv->entry_buffer, "", -1);
@@ -874,100 +949,45 @@
 #endif /* ENABLE_SPELL_CHECKING */
 
 	/* List the entry's links */
-	links = almanah_storage_manager_get_entry_links (almanah->storage_manager, &calendar_date);
-
-	i = 0;
-	while (links[i] != NULL) {
-		const gchar *value, *value2;
-
-		value = almanah_link_get_value (links[i]);
-		value2 = almanah_link_get_value2 (links[i]);
-
-		gtk_list_store_append (priv->link_store, &iter);
-		gtk_list_store_set (priv->link_store, &iter,
-				    0, almanah_link_get_type_id (links[i]),
-				    1, value,
-				    2, value2,
-				    3, almanah_link_get_icon_name (links[i]),
-				    -1);
-
-		g_object_unref (links[i]);
-
-		i++;
-	}
-
-	g_free (links);
+	almanah_link_manager_query_links (almanah->link_manager, ALMANAH_LINK_FACTORY_UNKNOWN, &calendar_date);
 }
 
 static void
 mw_links_selection_changed_cb (GtkTreeSelection *tree_selection, AlmanahMainWindow *main_window)
 {
 	AlmanahMainWindowPrivate *priv = main_window->priv;
-
-	if (gtk_tree_selection_count_selected_rows (tree_selection) == 0) {
-		gtk_widget_set_sensitive (GTK_WIDGET (priv->remove_button), FALSE);
-		gtk_widget_set_sensitive (GTK_WIDGET (priv->view_button), FALSE);
-		gtk_action_set_sensitive (priv->remove_action, FALSE);
-	} else {
-		gtk_widget_set_sensitive (GTK_WIDGET (priv->remove_button), TRUE);
-		gtk_widget_set_sensitive (GTK_WIDGET (priv->view_button), TRUE);
-		gtk_action_set_sensitive (priv->remove_action, TRUE);
-	}
+	gtk_widget_set_sensitive (GTK_WIDGET (priv->view_button), (gtk_tree_selection_count_selected_rows (tree_selection) == 0) ? FALSE : TRUE);
 }
 
 static void
 mw_links_value_data_cb (GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data)
 {
-	gchar *new_value, *value, *value2, *type;
+	const gchar *new_value;
 	AlmanahLink *link;
 
-	/* TODO: Should really create a new model to render AlmanahLinks --- or at least attach the appropriate AlmanahLink to each tree model row */
+	/* TODO: Should really create a new model to render AlmanahLinks */
 
 	gtk_tree_model_get (model, iter,
-			    0, &type,
-			    1, &value,
-			    2, &value2,
+			    0, &link,
 			    -1);
 
-	link = almanah_link_new (type);
-	almanah_link_set_value (link, value);
-	almanah_link_set_value2 (link, value2);
-
 	new_value = almanah_link_format_value (link);
 	g_object_set (renderer, "text", new_value, NULL);
-	g_free (new_value);
-
-	g_free (type);
-	g_free (value);
-	g_free (value2);
-	g_object_unref (link);
 }
 
 void
 mw_links_tree_view_row_activated_cb (GtkTreeView *tree_view, GtkTreePath *path, GtkTreeViewColumn *column, AlmanahMainWindow *main_window)
 {
 	AlmanahLink *link;
-	gchar *value, *value2, *type;
 	GtkTreeIter iter;
 
 	gtk_tree_model_get_iter (GTK_TREE_MODEL (main_window->priv->link_store), &iter, path);
 	gtk_tree_model_get (GTK_TREE_MODEL (main_window->priv->link_store), &iter,
-			    0, &type,
-			    1, &value,
-			    2, &value2,
+			    0, &link,
 			    -1);
 
-	link = almanah_link_new (type);
-	almanah_link_set_value (link, value);
-	almanah_link_set_value2 (link, value2);
-
 	/* NOTE: Link types should display their own errors, so one won't be displayed here. */
 	almanah_link_view (link);
-
-	g_free (type);
-	g_free (value);
-	g_free (value2);
-	g_object_unref (link);
 }
 
 gboolean
@@ -978,18 +998,6 @@
 }
 
 void
-mw_add_button_clicked_cb (GtkButton *button, AlmanahMainWindow *main_window)
-{
-	add_link_to_current_entry (main_window);
-}
-
-void
-mw_remove_button_clicked_cb (GtkButton *button, AlmanahMainWindow *main_window)
-{
-	remove_link_from_current_entry (main_window);
-}
-
-void
 mw_view_button_clicked_cb (GtkButton *button, AlmanahMainWindow *main_window)
 {
 	GList *links;
@@ -999,28 +1007,16 @@
 
 	for (; links != NULL; links = links->next) {
 		AlmanahLink *link;
-		gchar *value, *value2, *type;
 		GtkTreeIter iter;
 
 		gtk_tree_model_get_iter (model, &iter, (GtkTreePath*) links->data);
 		gtk_tree_model_get (model, &iter,
-				    0, &type,
-				    1, &value,
-				    2, &value2,
+				    0, &link,
 				    -1);
 
-		link = almanah_link_new (type);
-		almanah_link_set_value (link, value);
-		almanah_link_set_value2 (link, value2);
-
 		/* NOTE: Link types should display their own errors, so one won't be displayed here. */
 		almanah_link_view (link);
 
-		g_free (type);
-		g_free (value);
-		g_free (value2);
-		g_object_unref (link);
-
 		gtk_tree_path_free (links->data);
 	}
 

Modified: trunk/src/main.c
==============================================================================
--- trunk/src/main.c	(original)
+++ trunk/src/main.c	Sat Dec  6 20:53:07 2008
@@ -25,6 +25,7 @@
 
 #include "main.h"
 #include "storage-manager.h"
+#include "link-manager.h"
 #include "interface.h"
 
 static void
@@ -56,13 +57,15 @@
 		gtk_widget_destroy (dialog);
 	}
 
-	gtk_widget_destroy (almanah->add_link_dialog);
+	gtk_widget_destroy (almanah->add_definition_dialog);
 	gtk_widget_destroy (almanah->search_dialog);
 #ifdef ENABLE_ENCRYPTION
 	gtk_widget_destroy (almanah->preferences_dialog);
 #endif /* ENABLE_ENCRYPTION */
 	gtk_widget_destroy (almanah->main_window);
 
+	g_object_unref (almanah->link_manager);
+
 	/* Quitting is actually done in storage_manager_disconnected_cb, which is called once
 	 * the storage manager has encrypted the DB and disconnected from it.
 	 * Unless, that is, disconnection failed. */
@@ -145,6 +148,9 @@
 		almanah_quit ();
 	}
 
+	/* Create the link manager */
+	almanah->link_manager = almanah_link_manager_new ();
+
 	/* Create and show the interface */
 	almanah_create_interface ();
 	gtk_widget_show_all (almanah->main_window);

Modified: trunk/src/main.h
==============================================================================
--- trunk/src/main.h	(original)
+++ trunk/src/main.h	Sat Dec  6 20:53:07 2008
@@ -23,6 +23,7 @@
 #include <gconf/gconf-client.h>
 
 #include "storage-manager.h"
+#include "link-manager.h"
 
 #ifndef ALMANAH_MAIN_H
 #define ALMANAH_MAIN_H
@@ -35,10 +36,11 @@
 
 typedef struct {
 	AlmanahStorageManager *storage_manager;
+	AlmanahLinkManager *link_manager;
 	GConfClient *gconf_client;
 
 	GtkWidget *main_window;
-	GtkWidget *add_link_dialog;
+	GtkWidget *add_definition_dialog;
 	GtkWidget *search_dialog;
 #ifdef ENABLE_ENCRYPTION
 	GtkWidget *preferences_dialog;

Modified: trunk/src/printing.c
==============================================================================
--- trunk/src/printing.c	(original)
+++ trunk/src/printing.c	Sat Dec  6 20:53:07 2008
@@ -472,15 +472,7 @@
 	almanah_operation.current_line = 0;
 
 	almanah_operation.buffer = gtk_text_buffer_new (NULL);
-	gtk_text_buffer_create_tag (almanah_operation.buffer, "bold", 
-				    "weight", PANGO_WEIGHT_BOLD, 
-				    NULL);
-	gtk_text_buffer_create_tag (almanah_operation.buffer, "italic",
-				    "style", PANGO_STYLE_ITALIC,
-				    NULL);
-	gtk_text_buffer_create_tag (almanah_operation.buffer, "underline",
-				    "underline", PANGO_UNDERLINE_SINGLE,
-				    NULL);
+	almanah_interface_create_text_tags (almanah_operation.buffer, FALSE);
 
 	if (settings != NULL) 
 		gtk_print_operation_set_print_settings (operation, settings);

Modified: trunk/src/storage-manager.c
==============================================================================
--- trunk/src/storage-manager.c	(original)
+++ trunk/src/storage-manager.c	Sat Dec  6 20:53:07 2008
@@ -31,7 +31,7 @@
 
 #include "main.h"
 #include "entry.h"
-#include "link.h"
+#include "definition.h"
 #include "storage-manager.h"
 
 static void almanah_storage_manager_init (AlmanahStorageManager *self);
@@ -56,7 +56,6 @@
 
 static guint storage_manager_signals[LAST_SIGNAL] = { 0, };
 
-
 G_DEFINE_TYPE (AlmanahStorageManager, almanah_storage_manager, G_TYPE_OBJECT)
 #define ALMANAH_STORAGE_MANAGER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_STORAGE_MANAGER, AlmanahStorageManagerPrivate))
 
@@ -162,12 +161,16 @@
 static void
 create_tables (AlmanahStorageManager *self)
 {
+	/* Old queries:
+		"CREATE TABLE IF NOT EXISTS entry_links (year INTEGER, month INTEGER, day INTEGER, link_type TEXT, link_value TEXT, link_value2 TEXT, PRIMARY KEY (year, month, day, link_type, link_value, link_value2))"
+		"CREATE TABLE IF NOT EXISTS entry_attachments (year INTEGER, month INTEGER, day INTEGER, attachment_type TEXT, attachment_data BLOB, PRIMARY KEY (year, month, day, attachment_type))"
+	 */
+
 	/* Dates are stored in ISO 8601 format...sort of */
 	guint i;
 	const gchar *queries[] = {
 		"CREATE TABLE IF NOT EXISTS entries (year INTEGER, month INTEGER, day INTEGER, content TEXT, PRIMARY KEY (year, month, day))",
-		"CREATE TABLE IF NOT EXISTS entry_links (year INTEGER, month INTEGER, day INTEGER, link_type TEXT, link_value TEXT, link_value2 TEXT, PRIMARY KEY (year, month, day, link_type, link_value, link_value2))",
-		"CREATE TABLE IF NOT EXISTS entry_attachments (year INTEGER, month INTEGER, day INTEGER, attachment_type TEXT, attachment_data BLOB, PRIMARY KEY (year, month, day, attachment_type))",
+		"CREATE TABLE IF NOT EXISTS definitions (definition_text TEXT, definition_type INTEGER, definition_value TEXT, definition_value2 TEXT, PRIMARY KEY (definition_text))",
 		NULL
 	};
 
@@ -596,12 +599,12 @@
 }
 
 gboolean
-almanah_storage_manager_get_statistics (AlmanahStorageManager *self, guint *entry_count, guint *link_count)
+almanah_storage_manager_get_statistics (AlmanahStorageManager *self, guint *entry_count, guint *definition_count)
 {
 	AlmanahQueryResults *results;
 
 	*entry_count = 0;
-	*link_count = 0;
+	*definition_count = 0;
 
 	/* Get the number of entries and the number of letters */
 	results = almanah_storage_manager_query (self, "SELECT COUNT (year) FROM entries", NULL);
@@ -613,23 +616,23 @@
 	} else {
 		*entry_count = atoi (results->data[1]);
 		if (*entry_count == 0) {
-			*link_count = 0;
+			*definition_count = 0;
 			almanah_storage_manager_free_results (results);
 			return TRUE;
 		}
 	}
 	almanah_storage_manager_free_results (results);
 
-	/* Get the number of links */
-	results = almanah_storage_manager_query (self, "SELECT COUNT (year) FROM entry_links", NULL);
+	/* Get the number of definitions */
+	results = almanah_storage_manager_query (self, "SELECT COUNT (year) FROM definitions", NULL);
 	if (results == NULL) {
 		return FALSE;
 	} else if (results->rows != 1) {
-		*link_count = 0;
+		*definition_count = 0;
 		almanah_storage_manager_free_results (results);
 		return FALSE;
 	} else {
-		*link_count = atoi (results->data[1]);
+		*definition_count = atoi (results->data[1]);
 	}
 	almanah_storage_manager_free_results (results);
 
@@ -712,7 +715,7 @@
  *
  * Saves the specified @entry in the database synchronously.
  * If the @entry's content is empty, it will delete @entry's rows
- * in the database (as well as its links' rows).
+ * in the database (as well as its definitions' rows).
  *
  * Return value: %TRUE on success, %FALSE otherwise
  **/
@@ -729,10 +732,6 @@
 						     g_date_get_year (&date),
 						     g_date_get_month (&date),
 						     g_date_get_day (&date));
-		almanah_storage_manager_query_async (self, "DELETE FROM entry_links WHERE year = %u AND month = %u AND day = %u", NULL, NULL, NULL,
-						     g_date_get_year (&date),
-						     g_date_get_month (&date),
-						     g_date_get_day (&date));
 
 		return TRUE;
 	} else {
@@ -869,77 +868,95 @@
 }
 
 /* NOTE: Free array with g_free and each element with g_object_unref */
-AlmanahLink **
-almanah_storage_manager_get_entry_links (AlmanahStorageManager *self, GDate *date)
+AlmanahDefinition **
+almanah_storage_manager_get_definitions (AlmanahStorageManager *self)
 {
 	AlmanahQueryResults *results;
-	AlmanahLink **links;
+	AlmanahDefinition **definitions;
 	guint i;
 
-	results = almanah_storage_manager_query (self, "SELECT link_type, link_value, link_value2 FROM entry_links WHERE year = %u AND month = %u AND day = %u", NULL,
-						 g_date_get_year (date),
-						 g_date_get_month (date),
-						 g_date_get_day (date));
+	results = almanah_storage_manager_query (self, "SELECT definition_type, definition_value, definition_value2, definition_text FROM definitions", NULL);
 
 	if (results == NULL || results->rows == 0) {
 		if (results != NULL)
 			almanah_storage_manager_free_results (results);
 
 		/* Return empty array */
-		links = (AlmanahLink**) g_new (AlmanahLink*, 1);
-		links[0] = NULL;
-		return links;
+		definitions = (AlmanahDefinition**) g_new (AlmanahDefinition*, 1);
+		definitions[0] = NULL;
+		return definitions;
 	}
 
-	links = (AlmanahLink**) g_new (AlmanahLink*, results->rows + 1);
+	definitions = (AlmanahDefinition**) g_new (AlmanahDefinition*, results->rows + 1);
 	for (i = 0; i < results->rows; i++) {
-		links[i] = almanah_link_new (results->data[(i + 1) * results->columns]);
-		almanah_link_set_value (links[i], results->data[(i + 1) * results->columns + 1]);
-		almanah_link_set_value2 (links[i], results->data[(i + 1) * results->columns + 2]);
+		definitions[i] = almanah_definition_new (atoi (results->data[(i + 1) * results->columns]));
+		almanah_definition_set_value (definitions[i], results->data[(i + 1) * results->columns + 1]);
+		almanah_definition_set_value2 (definitions[i], results->data[(i + 1) * results->columns + 2]);
+		almanah_definition_set_text (definitions[i], results->data[(i + 1) * results->columns + 3]);
+	}
+	definitions[i] = NULL;
+
+	almanah_storage_manager_free_results (results);
+
+	return definitions;
+}
+
+AlmanahDefinition *
+almanah_storage_manager_get_definition (AlmanahStorageManager *self, const gchar *definition_text)
+{
+	AlmanahQueryResults *results;
+	AlmanahDefinition *definition;
+
+	results = almanah_storage_manager_query (self, "SELECT definition_type, definition_value, definition_value2, definition_text FROM definitions WHERE definition_text = '%q' LIMIT 1", NULL,
+						 definition_text);
+
+	if (results == NULL || results->rows == 0) {
+		if (results != NULL)
+			almanah_storage_manager_free_results (results);
+		return NULL;
 	}
-	links[i] = NULL;
+
+	definition = almanah_definition_new (atoi (results->data[results->columns]));
+	almanah_definition_set_value (definition, results->data[results->columns + 1]);
+	almanah_definition_set_value2 (definition, results->data[results->columns + 2]);
+	almanah_definition_set_text (definition, results->data[results->columns + 3]);
 
 	almanah_storage_manager_free_results (results);
 
-	return links;
+	return definition;
 }
 
 gboolean
-almanah_storage_manager_add_entry_link (AlmanahStorageManager *self, GDate *date, AlmanahLink *link)
+almanah_storage_manager_add_definition (AlmanahStorageManager *self, AlmanahDefinition *definition)
 {
 	gboolean return_value;
-	const gchar *type_id, *value, *value2;
+	const gchar *value, *value2, *text;
+	AlmanahDefinitionType type_id;
 
-	type_id = almanah_link_get_type_id (link);
-	value = almanah_link_get_value (link);
-	value2 = almanah_link_get_value2 (link);
+	type_id = almanah_definition_get_type_id (definition);
+	value = almanah_definition_get_value (definition);
+	value2 = almanah_definition_get_value2 (definition);
+	text = almanah_definition_get_text (definition);
 
 	if (value2 == NULL) {
-		return_value = almanah_storage_manager_query_async (self, "REPLACE INTO entry_links (year, month, day, link_type, link_value) VALUES (%u, %u, %u, '%q', '%q')", NULL, NULL, NULL,
-								    g_date_get_year (date),
-								    g_date_get_month (date),
-								    g_date_get_day (date),
+		return_value = almanah_storage_manager_query_async (self, "REPLACE INTO definitions (definition_type, definition_value, definition_text) VALUES (%u, '%q', '%q')", NULL, NULL, NULL,
 								    type_id,
-								    value);
+								    value,
+								    text);
 	} else {
-		return_value = almanah_storage_manager_query_async (self, "REPLACE INTO entry_links (year, month, day, link_type, link_value, link_value2) VALUES (%u, %u, %u, '%q', '%q', '%q')", NULL, NULL, NULL,
-								    g_date_get_year (date),
-								    g_date_get_month (date),
-								    g_date_get_day (date),
+		return_value = almanah_storage_manager_query_async (self, "REPLACE INTO definitions (definition_type, definition_value, definition_value2, definition_text) VALUES (%u, '%q', '%q', '%q')", NULL, NULL, NULL,
 								    type_id,
 								    value,
-								    value2);
+								    value2,
+								    text);
 	}
 
 	return return_value;
 }
 
 gboolean
-almanah_storage_manager_remove_entry_link (AlmanahStorageManager *self, GDate *date, const gchar *link_type_id)
+almanah_storage_manager_remove_definition (AlmanahStorageManager *self, const gchar *definition_text)
 {
-	return almanah_storage_manager_query_async (self, "DELETE FROM entry_links WHERE year = %u AND month = %u AND day = %u AND link_type = '%q'", NULL, NULL, NULL,
-						    g_date_get_year (date),
-						    g_date_get_month (date),
-						    g_date_get_day (date),
-						    link_type_id);
+	return almanah_storage_manager_query_async (self, "DELETE FROM definitions WHERE definition_text = '%q'", NULL, NULL, NULL,
+						    definition_text);
 }

Modified: trunk/src/storage-manager.h
==============================================================================
--- trunk/src/storage-manager.h	(original)
+++ trunk/src/storage-manager.h	Sat Dec  6 20:53:07 2008
@@ -24,7 +24,7 @@
 #include <glib-object.h>
 
 #include "entry.h"
-#include "link.h"
+#include "definition.h"
 
 G_BEGIN_DECLS
 
@@ -85,9 +85,10 @@
 
 gboolean *almanah_storage_manager_get_month_marked_days (AlmanahStorageManager *self, GDateYear year, GDateMonth month, guint *num_days);
 
-AlmanahLink **almanah_storage_manager_get_entry_links (AlmanahStorageManager *self, GDate *date);
-gboolean almanah_storage_manager_add_entry_link (AlmanahStorageManager *self, GDate *date, AlmanahLink *link);
-gboolean almanah_storage_manager_remove_entry_link (AlmanahStorageManager *self, GDate *date, const gchar *link_type_id);
+AlmanahDefinition **almanah_storage_manager_get_definitions (AlmanahStorageManager *self);
+AlmanahDefinition *almanah_storage_manager_get_definition (AlmanahStorageManager *self, const gchar *definition_text);
+gboolean almanah_storage_manager_add_definition (AlmanahStorageManager *self, AlmanahDefinition *definition);
+gboolean almanah_storage_manager_remove_definition (AlmanahStorageManager *self, const gchar *definition_text);
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]