diary r91 - in trunk: . src



Author: pwithnall
Date: Sun Oct 19 11:08:00 2008
New Revision: 91
URL: http://svn.gnome.org/viewvc/diary?rev=91&view=rev

Log:
2008-10-19  Philip Withnall  <philip tecnocode co uk>

	* src/entry.c (almanah_entry_class_init), (almanah_entry_init),
	(almanah_entry_finalize), (almanah_entry_get_property),
	(almanah_entry_set_property), (almanah_entry_get_data),
	(almanah_entry_set_data), (almanah_entry_get_content),
	(almanah_entry_set_content), (almanah_entry_is_empty):
	* src/entry.h:
	* src/main-window.c (save_current_entry), 
(mw_about_activate_cb),
	(mw_calendar_day_selected_cb):
	* src/printing.c (get_iter_attrs), (is_empty_line),
	(lay_out_entry), (print_entry), (custom_widget_apply_cb),
	(diary_print_entries):
	* src/storage-manager.c 
(almanah_storage_manager_get_statistics),
	(almanah_storage_manager_get_entry),
	(almanah_storage_manager_set_entry),
	(almanah_storage_manager_search_entries):
	* src/storage-manager.h: Serialise and deserialise entries when
	writing them to/from the database to enable persistence of 
formatting
	tags. Modify the printing code to also be able to deal with
	formatting tags.



Modified:
   trunk/ChangeLog
   trunk/src/entry.c
   trunk/src/entry.h
   trunk/src/main-window.c
   trunk/src/printing.c
   trunk/src/storage-manager.c
   trunk/src/storage-manager.h

Modified: trunk/src/entry.c
==============================================================================
--- trunk/src/entry.c	(original)
+++ trunk/src/entry.c	Sun Oct 19 11:08:00 2008
@@ -17,7 +17,9 @@
  * along with Almanah.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
 #include <glib.h>
+#include <gtk/gtk.h>
 
 #include "entry.h"
 
@@ -28,14 +30,15 @@
 
 struct _AlmanahEntryPrivate {
 	GDate date;
-	gchar *content;
+	guint8 *data;
+	gsize length;
+	gboolean is_empty;
 };
 
 enum {
 	PROP_DAY = 1,
 	PROP_MONTH,
-	PROP_YEAR,
-	PROP_CONTENT
+	PROP_YEAR
 };
 
 G_DEFINE_TYPE (AlmanahEntry, almanah_entry, G_TYPE_OBJECT)
@@ -67,17 +70,14 @@
 					"Year", "The year for which this is the entry.",
 					1, (1 << 16) - 1, 1,
 					G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
-	g_object_class_install_property (gobject_class, PROP_CONTENT,
-				g_param_spec_string ("content",
-					"Content", "The textual content of the entry.",
-					NULL,
-					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 static void
 almanah_entry_init (AlmanahEntry *self)
 {
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_ENTRY, AlmanahEntryPrivate);
+	self->priv->data = NULL;
+	self->priv->length = 0;
 	g_date_clear (&(self->priv->date), 1);
 }
 
@@ -86,8 +86,8 @@
 {
 	AlmanahEntryPrivate *priv = ALMANAH_ENTRY (object)->priv;
 
-	g_free (priv->content);
-	priv->content = NULL;
+	g_free (priv->data);
+	priv->data = NULL;
 
 	/* Chain up to the parent class */
 	G_OBJECT_CLASS (almanah_entry_parent_class)->finalize (object);
@@ -108,9 +108,6 @@
 		case PROP_YEAR:
 			g_value_set_uint (value, g_date_get_year (&(priv->date)));
 			break;
-		case PROP_CONTENT:
-			g_value_set_string (value, priv->content);
-			break;
 		default:
 			/* We don't have any other property... */
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -133,9 +130,6 @@
 		case PROP_YEAR:
 			g_date_set_year (&(priv->date), g_value_get_uint (value));
 			break;
-		case PROP_CONTENT:
-			almanah_entry_set_content (ALMANAH_ENTRY (object), g_value_get_string (value));
-			break;
 		default:
 			/* We don't have any other property... */
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -153,18 +147,78 @@
 			     NULL);
 }
 
+/* NOTE: There's a difference between content and data, as recognised by AlmanahEntry.
+ * Content is deserialized, and handled in terms of GtkTextBuffers.
+ * Data is serialized, and handled in terms of a guint8 *data and gsize length. */
+const guint8 *
+almanah_entry_get_data (AlmanahEntry *self, gsize *length)
+{
+	if (length != NULL)
+		*length = self->priv->length;
+	return self->priv->data;
+}
+
 void
-almanah_entry_set_content (AlmanahEntry *self, const gchar *content)
+almanah_entry_set_data (AlmanahEntry *self, const guint8 *data, gsize length)
+{
+	AlmanahEntryPrivate *priv = self->priv;
+
+	g_free (priv->data);
+
+	priv->data = g_memdup (data, length * sizeof (*data));
+	priv->length = length;
+	priv->is_empty = FALSE;
+}
+
+gboolean
+almanah_entry_get_content (AlmanahEntry *self, GtkTextBuffer *text_buffer, GError **error)
 {
-	g_free (self->priv->content);
-	self->priv->content = g_strdup (content);
-	g_object_notify (G_OBJECT (self), "content");
+	GdkAtom format_atom;
+	GtkTextIter start_iter;
+	AlmanahEntryPrivate *priv = self->priv;
+	GError *deserialise_error = NULL;
+
+	format_atom = gtk_text_buffer_register_deserialize_tagset (text_buffer, PACKAGE_NAME);
+	gtk_text_buffer_get_start_iter (text_buffer, &start_iter);
+
+	/* Try deserializing the (hopefully) serialized data first */
+	if (gtk_text_buffer_deserialize (text_buffer, text_buffer,
+					 format_atom,
+					 &start_iter,
+					 priv->data, priv->length,
+					 &deserialise_error) == FALSE) {
+		/* Since that failed, check the data's in the old format, and try to just load it as text */
+		if (g_strcmp0 (priv->data, "GTKTEXTBUFFERCONTENTS-0001") != 0) {
+			gtk_text_buffer_set_text (text_buffer, (gchar*) priv->data, priv->length);
+			g_error_free (deserialise_error);
+			return TRUE;
+		}
+
+		g_propagate_error (error, deserialise_error);
+		return FALSE;
+	}
+
+	return TRUE;
 }
 
-gchar *
-almanah_entry_get_content (AlmanahEntry *self)
+void
+almanah_entry_set_content (AlmanahEntry *self, GtkTextBuffer *text_buffer)
 {
-	return g_strdup (self->priv->content);
+	GtkTextIter start, end;
+	GdkAtom format_atom;
+	AlmanahEntryPrivate *priv = self->priv;
+
+	/* Update our cached empty status */
+	self->priv->is_empty = (gtk_text_buffer_get_char_count (text_buffer) == 0) ? TRUE : FALSE;
+
+	g_free (priv->data);
+
+	gtk_text_buffer_get_bounds (text_buffer, &start, &end);
+	format_atom = gtk_text_buffer_register_serialize_tagset (text_buffer, PACKAGE_NAME);
+	priv->data = gtk_text_buffer_serialize (text_buffer, text_buffer,
+						format_atom,
+						&start, &end,
+						&(priv->length));
 }
 
 /* NOTE: Designed for use on the stack */
@@ -199,5 +253,8 @@
 gboolean
 almanah_entry_is_empty (AlmanahEntry *self)
 {
-	return (self->priv->content == NULL || self->priv->content[0] == '\0') ? TRUE : FALSE;
+	return (self->priv->is_empty == TRUE ||
+		self->priv->length == 0 ||
+		self->priv->data == NULL ||
+		self->priv->data[0] == '\0') ? TRUE : FALSE;
 }

Modified: trunk/src/entry.h
==============================================================================
--- trunk/src/entry.h	(original)
+++ trunk/src/entry.h	Sun Oct 19 11:08:00 2008
@@ -54,8 +54,10 @@
 
 GType almanah_entry_get_type (void);
 AlmanahEntry *almanah_entry_new (GDate *date);
-void almanah_entry_set_content (AlmanahEntry *self, const gchar *content);
-gchar *almanah_entry_get_content (AlmanahEntry *self);
+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, 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/main-window.c
==============================================================================
--- trunk/src/main-window.c	(original)
+++ trunk/src/main-window.c	Sun Oct 19 11:08:00 2008
@@ -215,8 +215,6 @@
 static void
 save_current_entry (AlmanahMainWindow *self)
 {
-	GtkTextIter start_iter, end_iter;
-	gchar *entry_text;
 	gboolean entry_exists, entry_is_empty;
 	GDate date;
 	AlmanahMainWindowPrivate *priv = self->priv;
@@ -231,18 +229,9 @@
 		return;
 
 	/* Save the entry */
-	gtk_text_buffer_get_bounds (priv->entry_buffer, &start_iter, &end_iter);
-	entry_text = gtk_text_buffer_get_text (priv->entry_buffer, &start_iter, &end_iter, FALSE);
-	almanah_entry_set_content (priv->current_entry, entry_text);
-	g_free (entry_text);
-
+	almanah_entry_set_content (priv->current_entry, priv->entry_buffer);
 	gtk_text_buffer_set_modified (priv->entry_buffer, FALSE);
 
-	/* TODO: Serialise the buffer contents instead of just grabbing the text, so we can
-	 * keep tags between saves. Could use gtk_text_buffer_register_serialize_tagset() et al
-	 * for this, but could also add a more general framework for serialisation, which would aid
-	 * output/export to other formats from the program. */
-
 	almanah_entry_get_date (priv->current_entry, &date);
 	editability = almanah_entry_get_editability (priv->current_entry);
 	entry_exists = almanah_storage_manager_entry_exists (diary->storage_manager, &date);
@@ -309,10 +298,12 @@
 		gtk_widget_destroy (dialog);
 	}
 
+	/* Store the entry! */
+	almanah_storage_manager_set_entry (diary->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. */
-	if (almanah_storage_manager_set_entry (diary->storage_manager, priv->current_entry) == FALSE) {
-		/* TODO: This sort of thing should be done by connecting to signals from the storage manager */
+	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);
@@ -569,7 +560,7 @@
 mw_about_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
 {
 	gchar *license, *description;
-	guint entry_count, link_count, character_count;
+	guint entry_count, link_count;
 
 	const gchar *authors[] =
 	{
@@ -595,11 +586,10 @@
 			  _(license_parts[2]),
 			  NULL);
 
-	almanah_storage_manager_get_statistics (diary->storage_manager, &entry_count, &link_count, &character_count);
-	description = g_strdup_printf (_("A helpful diary keeper, storing %u entries with %u links and a total of %u characters."),
+	almanah_storage_manager_get_statistics (diary->storage_manager, &entry_count, &link_count);
+	description = g_strdup_printf (_("A helpful diary keeper, storing %u entries and %u links."),
 				      entry_count,
-				      link_count,
-				      character_count);
+				      link_count);
 
 	gtk_show_about_dialog (GTK_WINDOW (main_window),
 				"version", VERSION,
@@ -677,23 +667,37 @@
 	gtk_text_view_set_editable (priv->entry_view, almanah_entry_get_editability (priv->current_entry) != ALMANAH_ENTRY_FUTURE ? TRUE : FALSE);
 	gtk_text_buffer_set_modified (priv->entry_buffer, FALSE);
 
+	/* 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);
+
 	if (almanah_entry_is_empty (priv->current_entry) == FALSE) {
-		gchar *entry_content = almanah_entry_get_content (priv->current_entry);
-		gtk_text_buffer_set_text (priv->entry_buffer, entry_content, -1);
-		g_free (entry_content);
+		GError *error = NULL;
+
+		gtk_text_buffer_set_text (priv->entry_buffer, "", 0);
+		if (almanah_entry_get_content (priv->current_entry, priv->entry_buffer, &error) == FALSE) {
+			gchar *error_message = g_strdup_printf (_("The entry content could not be loaded: %s"), error->message);
+			diary_interface_error (error_message, NULL);
+			g_free (error_message);
+			g_error_free (error);
+
+			/* Make sure the interface is left in a decent state before we return */
+			gtk_text_view_set_editable (priv->entry_view, FALSE);
+
+			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);
-		gtk_widget_set_sensitive (GTK_WIDGET (priv->add_button), FALSE);
-		gtk_action_set_sensitive (priv->add_action, FALSE);
 	}
 
-	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);
-
 #ifdef ENABLE_SPELL_CHECKING
 	/* Ensure the spell-checking is updated */
 	gtkspell = gtkspell_get_from_text_view (priv->entry_view);
@@ -702,7 +706,6 @@
 #endif /* ENABLE_SPELL_CHECKING */
 
 	/* List the entry's links */
-	gtk_list_store_clear (priv->link_store);
 	links = almanah_storage_manager_get_entry_links (diary->storage_manager, &calendar_date);
 
 	i = 0;

Modified: trunk/src/printing.c
==============================================================================
--- trunk/src/printing.c	(original)
+++ trunk/src/printing.c	Sun Oct 19 11:08:00 2008
@@ -30,6 +30,7 @@
 #define MAX_ORPHANS 3 /* maximum number of orphan lines to be forced to the next page */
 
 typedef struct {
+	GtkTextBuffer *buffer;
 	GDate *start_date;
 	GDate *end_date;
 	GDate *current_date;
@@ -40,6 +41,197 @@
 	gdouble y;
 } DiaryPrintOperation;
 
+/* Adapted from code in GtkSourceView's gtksourceprintcompositor.c, previously LGPL >= 2.1
+ * Copyright (C) 2000, 2001 Chema Celorio  
+ * Copyright (C) 2003  Gustavo GirÃldez
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 2001-2007  Paolo Maggi
+ * Copyright (C) 2008  Paolo Maggi, Paolo Borelli and Yevgen Muntyan */
+static GSList *
+get_iter_attrs (GtkTextIter *iter, GtkTextIter *limit)
+{
+	GSList *attrs = NULL;
+	GSList *tags;
+	PangoAttribute *bg = NULL, *fg = NULL, *style = NULL, *ul = NULL;
+	PangoAttribute *weight = NULL, *st = NULL;
+
+	tags = gtk_text_iter_get_tags (iter);
+	gtk_text_iter_forward_to_tag_toggle (iter, NULL);
+
+	if (gtk_text_iter_compare (iter, limit) > 0)
+		*iter = *limit;
+
+	while (tags)
+	{
+		GtkTextTag *tag;
+		gboolean bg_set, fg_set, style_set, ul_set, weight_set, st_set;
+
+		tag = tags->data;
+		tags = g_slist_delete_link (tags, tags);
+
+		g_object_get (tag,
+			     "background-set", &bg_set,
+			     "foreground-set", &fg_set,
+			     "style-set", &style_set,
+			     "underline-set", &ul_set,
+			     "weight-set", &weight_set,
+			     "strikethrough-set", &st_set,
+			     NULL);
+
+		if (bg_set)
+		{
+			GdkColor *color = NULL;
+			if (bg) pango_attribute_destroy (bg);
+			g_object_get (tag, "background-gdk", &color, NULL);
+			bg = pango_attr_background_new (color->red, color->green, color->blue);
+			gdk_color_free (color);
+		}
+
+		if (fg_set)
+		{
+			GdkColor *color = NULL;
+			if (fg) pango_attribute_destroy (fg);
+			g_object_get (tag, "foreground-gdk", &color, NULL);
+			fg = pango_attr_foreground_new (color->red, color->green, color->blue);
+			gdk_color_free (color);
+		}
+
+		if (style_set)
+		{
+			PangoStyle style_value;
+			if (style) pango_attribute_destroy (style);
+			g_object_get (tag, "style", &style_value, NULL);
+			style = pango_attr_style_new (style_value);
+		}
+
+		if (ul_set)
+		{
+			PangoUnderline underline;
+			if (ul) pango_attribute_destroy (ul);
+			g_object_get (tag, "underline", &underline, NULL);
+			ul = pango_attr_underline_new (underline);
+		}
+
+		if (weight_set)
+		{
+			PangoWeight weight_value;
+			if (weight) pango_attribute_destroy (weight);
+			g_object_get (tag, "weight", &weight_value, NULL);
+			weight = pango_attr_weight_new (weight_value);
+		}
+
+		if (st_set)
+		{
+			gboolean strikethrough;
+			if (st) pango_attribute_destroy (st);
+			g_object_get (tag, "strikethrough", &strikethrough, NULL);
+			st = pango_attr_strikethrough_new (strikethrough);
+		}
+	}
+
+	if (bg)
+		attrs = g_slist_prepend (attrs, bg);
+	if (fg)
+		attrs = g_slist_prepend (attrs, fg);
+	if (style)
+		attrs = g_slist_prepend (attrs, style);
+	if (ul)
+		attrs = g_slist_prepend (attrs, ul);
+	if (weight)
+		attrs = g_slist_prepend (attrs, weight);
+	if (st)
+		attrs = g_slist_prepend (attrs, st);
+
+	return attrs;
+}
+
+/* Adapted from code in GtkSourceView's gtksourceprintcompositor.c, previously LGPL >= 2.1
+ * Copyright (C) 2000, 2001 Chema Celorio  
+ * Copyright (C) 2003  Gustavo GirÃldez
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 2001-2007  Paolo Maggi
+ * Copyright (C) 2008  Paolo Maggi, Paolo Borelli and Yevgen Muntyan */
+static gboolean
+is_empty_line (const gchar *text)
+{
+	if (*text != '\0') {
+		const gchar *p;
+
+		for (p = text; p != NULL; p = g_utf8_next_char (p)) {
+			if (!g_unichar_isspace (*p))
+				return FALSE;
+		}
+	}
+
+	return TRUE;
+}
+
+/* Adapted from code in GtkSourceView's gtksourceprintcompositor.c, previously LGPL >= 2.1
+ * Copyright (C) 2000, 2001 Chema Celorio  
+ * Copyright (C) 2003  Gustavo GirÃldez
+ * Copyright (C) 2004  Red Hat, Inc.
+ * Copyright (C) 2001-2007  Paolo Maggi
+ * Copyright (C) 2008  Paolo Maggi, Paolo Borelli and Yevgen Muntyan */
+static void
+lay_out_entry (PangoLayout *layout, GtkTextIter *start, GtkTextIter *end)
+{
+	gchar *text;
+	PangoAttrList *attr_list = NULL;
+	GtkTextIter segm_start, segm_end;
+	int start_index;
+
+	text = gtk_text_iter_get_slice (start, end);
+
+	/* If it is an empty line (or it just contains tabs) pango has problems:
+	 * see for instance comment #22 and #23 on bug #143874 and bug #457990.
+	 * We just hack around it by inserting a space... not elegant but
+	 * works :-) */
+	if (gtk_text_iter_ends_line (start) || is_empty_line (text)) {
+		pango_layout_set_text (layout, " ", 1);
+		g_free (text);
+		return;
+	}
+
+	pango_layout_set_text (layout, text, -1);
+	g_free (text);
+
+	segm_start = *start;
+	start_index = gtk_text_iter_get_line_index (start);
+
+	while (gtk_text_iter_compare (&segm_start, end) < 0) {
+		GSList *attrs;
+		int si, ei;
+
+		segm_end = segm_start;
+		attrs = get_iter_attrs (&segm_end, end);
+		if (attrs) {
+			si = gtk_text_iter_get_line_index (&segm_start) - start_index;
+			ei = gtk_text_iter_get_line_index (&segm_end) - start_index;
+		}
+
+		while (attrs) {
+			PangoAttribute *a = attrs->data;
+
+			a->start_index = si;
+			a->end_index = ei;
+
+			if (!attr_list)
+				attr_list = pango_attr_list_new ();
+
+			pango_attr_list_insert (attr_list, a);
+
+			attrs = g_slist_delete_link (attrs, attrs);
+		}
+
+		segm_start = segm_end;
+	}
+
+	pango_layout_set_attributes (layout, attr_list);
+
+	if (attr_list)
+		pango_attr_list_unref (attr_list);
+}
+
 /* TRUE if the entry was printed OK on the current page, FALSE if it needs to be moved to a new page/is split across pages */
 static gboolean
 print_entry (GtkPrintOperation *operation, GtkPrintContext *context, DiaryPrintOperation *diary_operation)
@@ -77,16 +269,21 @@
 
 	entry = almanah_storage_manager_get_entry (diary->storage_manager, diary_operation->current_date);
 
-	if (almanah_entry_is_empty (entry)) {
+	if (entry == NULL || almanah_entry_is_empty (entry)) {
 		gchar *entry_text = g_strdup_printf ("<i>%s</i>", _("No entry for this date."));
 		pango_layout_set_markup (entry_layout, entry_text, -1);
 	} else {
-		gchar *entry_text = almanah_entry_get_content (entry);
-		pango_layout_set_text (entry_layout, entry_text, -1);
-		g_free (entry_text);
+		GtkTextIter start, end;
+
+		gtk_text_buffer_set_text (diary_operation->buffer, "", 0);
+		if (almanah_entry_get_content (entry, diary_operation->buffer, NULL) == TRUE) {
+			gtk_text_buffer_get_bounds (diary_operation->buffer, &start, &end);
+			lay_out_entry (entry_layout, &start, &end);
+		}
 	}
 
-	g_object_unref (entry);
+	if (entry != NULL)
+		g_object_unref (entry);
 
 	/* Check we're not orphaning things */
 	entry_line = pango_layout_get_line_readonly (entry_layout, MIN (pango_layout_get_line_count (entry_layout), diary_operation->current_line + MAX_ORPHANS) - 1);
@@ -244,11 +441,20 @@
 	/* Start date */
 	gtk_calendar_get_date (diary_operation->start_calendar, &year, &month, &day);
 	diary_operation->start_date = g_date_new_dmy (day, month + 1, year);
-	diary_operation->current_date = g_memdup (diary_operation->start_date, sizeof (*diary_operation->start_date));
 
 	/* End date */
 	gtk_calendar_get_date (diary_operation->end_calendar, &year, &month, &day);
 	diary_operation->end_date = g_date_new_dmy (day, month + 1, year);
+
+	/* Ensure they're in order */
+	if (g_date_compare (diary_operation->start_date, diary_operation->end_date) > 0) {
+		GDate *temp;
+		temp = diary_operation->start_date;
+		diary_operation->start_date = diary_operation->end_date;
+		diary_operation->end_date = temp;
+	}
+
+	diary_operation->current_date = g_memdup (diary_operation->start_date, sizeof (*(diary_operation->start_date)));
 }
 
 void
@@ -265,6 +471,17 @@
 	diary_operation.y = 0;
 	diary_operation.current_line = 0;
 
+	diary_operation.buffer = gtk_text_buffer_new (NULL);
+	gtk_text_buffer_create_tag (diary_operation.buffer, "bold", 
+				    "weight", PANGO_WEIGHT_BOLD, 
+				    NULL);
+	gtk_text_buffer_create_tag (diary_operation.buffer, "italic",
+				    "style", PANGO_STYLE_ITALIC,
+				    NULL);
+	gtk_text_buffer_create_tag (diary_operation.buffer, "underline",
+				    "underline", PANGO_UNDERLINE_SINGLE,
+				    NULL);
+
 	if (settings != NULL) 
 		gtk_print_operation_set_print_settings (operation, settings);
 
@@ -285,6 +502,7 @@
 	}
 
 	if (diary_operation.current_date != NULL) {
+		g_object_unref (diary_operation.buffer);
 		g_date_free (diary_operation.current_date);
 		g_date_free (diary_operation.start_date);
 		g_date_free (diary_operation.end_date);

Modified: trunk/src/storage-manager.c
==============================================================================
--- trunk/src/storage-manager.c	(original)
+++ trunk/src/storage-manager.c	Sun Oct 19 11:08:00 2008
@@ -596,16 +596,15 @@
 }
 
 gboolean
-almanah_storage_manager_get_statistics (AlmanahStorageManager *self, guint *entry_count, guint *link_count, guint *character_count)
+almanah_storage_manager_get_statistics (AlmanahStorageManager *self, guint *entry_count, guint *link_count)
 {
 	AlmanahQueryResults *results;
 
 	*entry_count = 0;
-	*character_count = 0;
 	*link_count = 0;
 
 	/* Get the number of entries and the number of letters */
-	results = almanah_storage_manager_query (self, "SELECT COUNT (year), SUM (LENGTH (content)) FROM entries", NULL);
+	results = almanah_storage_manager_query (self, "SELECT COUNT (year) FROM entries", NULL);
 	if (results == NULL) {
 		return FALSE;
 	} else if (results->rows != 1) {
@@ -614,12 +613,9 @@
 	} else {
 		*entry_count = atoi (results->data[2]);
 		if (*entry_count == 0) {
-			*character_count = 0;
 			*link_count = 0;
 			return TRUE;
 		}
-
-		*character_count = atoi (results->data[3]);
 	}
 	almanah_storage_manager_free_results (results);
 
@@ -674,25 +670,37 @@
 AlmanahEntry *
 almanah_storage_manager_get_entry (AlmanahStorageManager *self, GDate *date)
 {
-	AlmanahQueryResults *results;
 	AlmanahEntry *entry;
+	sqlite3_stmt *statement;
 
-	results = almanah_storage_manager_query (self, "SELECT content FROM entries 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));
-
-	if (results == NULL)
+	/* It's necessary to avoid our nice SQLite interface and use the sqlite3 API directly here
+	 * as we can't otherwise reliably bind the data blob to the query --- if we pass it in as
+	 * a string, it gets cut off at the first nul character, which could occur anywhere in
+	 * the blob. */
+
+	/* Prepare the statement */
+	if (sqlite3_prepare_v2 (self->priv->connection,
+				"SELECT content FROM entries WHERE year = ? AND month = ? AND day = ?", -1,
+				&statement, NULL) != SQLITE_OK) {
 		return NULL;
-	if (results->rows != 1) {
-		/* Invalid number of rows returned. */
-		almanah_storage_manager_free_results (results);
+	}
+
+	/* Bind parameters */
+	sqlite3_bind_int (statement, 1, g_date_get_year (date));
+	sqlite3_bind_int (statement, 2, g_date_get_month (date));
+	sqlite3_bind_int (statement, 3, g_date_get_day (date));
+
+	/* Execute the statement */
+	if (sqlite3_step (statement) != SQLITE_ROW) {
+		sqlite3_finalize (statement);
 		return NULL;
 	}
 
+	/* Get the data */
 	entry = almanah_entry_new (date);
-	almanah_entry_set_content (entry, results->data[1]);
-	almanah_storage_manager_free_results (results);
+	almanah_entry_set_data (entry, sqlite3_column_blob (statement, 0), sqlite3_column_bytes (statement, 0));
+
+	sqlite3_finalize (statement);
 
 	return entry;
 }
@@ -702,12 +710,11 @@
  * @self: a #AlmanahStorageManager
  * @entry: an #AlmanahEntry
  *
- * Saves the specified @entry in the database. If the @entry
- * content is empty or %NULL, it will ask if the user wants to delete
- * the entry for that date. It will return %TRUE if the content is
- * non-empty, and %FALSE otherwise.
+ * 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).
  *
- * Return value: %TRUE if the entry is non-empty
+ * Return value: %TRUE on success, %FALSE otherwise
  **/
 gboolean
 almanah_storage_manager_set_entry (AlmanahStorageManager *self, AlmanahEntry *entry)
@@ -727,17 +734,40 @@
 						     g_date_get_month (&date),
 						     g_date_get_day (&date));
 
-		return FALSE;
+		return TRUE;
 	} else {
-		/* Update the entry */
-		gchar *content = almanah_entry_get_content (entry);
+		const guint8 *data;
+		gsize length;
+		sqlite3_stmt *statement;
+
+		/* It's necessary to avoid our nice SQLite interface and use the sqlite3 API directly here
+		 * as we can't otherwise reliably bind the data blob to the query --- if we pass it in as
+		 * a string, it gets cut off at the first nul character, which could occur anywhere in
+		 * the blob. */
+
+		/* Prepare the statement */
+		if (sqlite3_prepare_v2 (self->priv->connection,
+					"REPLACE INTO entries (year, month, day, content) VALUES (?, ?, ?, ?)", -1,
+					&statement, NULL) != SQLITE_OK) {
+			return FALSE;
+		}
+
+		/* Bind parameters */
+		sqlite3_bind_int (statement, 1, g_date_get_year (&date));
+		sqlite3_bind_int (statement, 2, g_date_get_month (&date));
+		sqlite3_bind_int (statement, 3, g_date_get_day (&date));
+
+		data = almanah_entry_get_data (entry, &length);
+		sqlite3_bind_blob (statement, 4, data, length, SQLITE_TRANSIENT);
+
+		/* Execute the statement */
+		if (sqlite3_step (statement) != SQLITE_DONE) {
+			sqlite3_finalize (statement);
+			return FALSE;
+		}
+
+		sqlite3_finalize (statement);
 
-		almanah_storage_manager_query_async (self, "REPLACE INTO entries (year, month, day, content) VALUES (%u, %u, %u, '%q')", NULL, NULL, NULL,
-						     g_date_get_year (&date),
-						     g_date_get_month (&date),
-						     g_date_get_day (&date),
-						     content);
-		g_free (content);
 		return TRUE;
 	}
 }
@@ -763,6 +793,7 @@
 	AlmanahQueryResults *results;
 	guint i;
 
+	/* TODO: Won't really work with serialized data */
 	results = almanah_storage_manager_query (self, "SELECT day, month, year FROM entries WHERE content LIKE '%%%q%%'", NULL,
 						 search_string);
 

Modified: trunk/src/storage-manager.h
==============================================================================
--- trunk/src/storage-manager.h	(original)
+++ trunk/src/storage-manager.h	Sun Oct 19 11:08:00 2008
@@ -76,7 +76,7 @@
 void almanah_storage_manager_free_results (AlmanahQueryResults *results);
 gboolean almanah_storage_manager_query_async (AlmanahStorageManager *self, const gchar *query, const AlmanahQueryCallback callback, gpointer user_data, GError **error, ...);
 
-gboolean almanah_storage_manager_get_statistics (AlmanahStorageManager *self, guint *entry_count, guint *link_count, guint *character_count);
+gboolean almanah_storage_manager_get_statistics (AlmanahStorageManager *self, guint *entry_count, guint *link_count);
 
 gboolean almanah_storage_manager_entry_exists (AlmanahStorageManager *self, GDate *date);
 AlmanahEntry *almanah_storage_manager_get_entry (AlmanahStorageManager *self, GDate *date);



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