[almanah] Add support for storing the date an entry was last edited



commit 76bcead8a663996f3b5d93ecd4cc69ac16bce27f
Author: Philip Withnall <philip tecnocode co uk>
Date:   Sun May 2 23:08:22 2010 +0100

    Add support for storing the date an entry was last edited
    
    Whenever an entry is edited, the current date is stored with it to allow
    later analysis. Imported entries have their last-edited dates preserved as
    much as possible; for example, entries imported from text files will have
    their last-edited date as the modification date of the text file.

 src/entry.c                |   60 +++++++++++++++++++++++++++++++++++++++++++-
 src/entry.h                |    3 ++
 src/import-export-dialog.c |   21 ++++++++++++---
 src/main-window.c          |   13 ++++++---
 src/storage-manager.c      |   55 +++++++++++++++++++++++++++++++++++----
 5 files changed, 136 insertions(+), 16 deletions(-)
---
diff --git a/src/entry.c b/src/entry.c
index e5a7984..5dab3a0 100644
--- a/src/entry.c
+++ b/src/entry.c
@@ -34,13 +34,17 @@ struct _AlmanahEntryPrivate {
 	gsize length;
 	gboolean is_empty;
 	gboolean is_important;
+	GDate last_edited; /* date the entry was last edited *in the database*; e.g. this isn't updated when almanah_entry_set_content() is called */
 };
 
 enum {
 	PROP_DAY = 1,
 	PROP_MONTH,
 	PROP_YEAR,
-	PROP_IS_IMPORTANT
+	PROP_IS_IMPORTANT,
+	PROP_LAST_EDITED_DAY,
+	PROP_LAST_EDITED_MONTH,
+	PROP_LAST_EDITED_YEAR
 };
 
 G_DEFINE_TYPE (AlmanahEntry, almanah_entry, G_TYPE_OBJECT)
@@ -76,6 +80,21 @@ almanah_entry_class_init (AlmanahEntryClass *klass)
 				g_param_spec_boolean ("is-important",
 					"Important?", "Whether the entry is particularly important to the user.",
 					FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property (gobject_class, PROP_LAST_EDITED_DAY,
+				g_param_spec_uint ("last-edited-day",
+					"Last Edited Day", "The day when this entry was last edited.",
+					1, 31, 1,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property (gobject_class, PROP_LAST_EDITED_MONTH,
+				g_param_spec_uint ("last-edited-month",
+					"Last Edited Month", "The month when this entry was last edited.",
+					1, 12, 1,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+	g_object_class_install_property (gobject_class, PROP_LAST_EDITED_YEAR,
+				g_param_spec_uint ("last-edited-year",
+					"Last Edited Year", "The year when this entry was last edited.",
+					1, (1 << 16) - 1, 1,
+					G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 }
 
 static void
@@ -85,6 +104,7 @@ almanah_entry_init (AlmanahEntry *self)
 	self->priv->data = NULL;
 	self->priv->length = 0;
 	g_date_clear (&(self->priv->date), 1);
+	g_date_clear (&(self->priv->last_edited), 1);
 }
 
 static void
@@ -116,6 +136,15 @@ almanah_entry_get_property (GObject *object, guint property_id, GValue *value, G
 		case PROP_IS_IMPORTANT:
 			g_value_set_boolean (value, priv->is_important);
 			break;
+		case PROP_LAST_EDITED_DAY:
+			g_value_set_uint (value, g_date_get_day (&(priv->last_edited)));
+			break;
+		case PROP_LAST_EDITED_MONTH:
+			g_value_set_uint (value, g_date_get_month (&(priv->last_edited)));
+			break;
+		case PROP_LAST_EDITED_YEAR:
+			g_value_set_uint (value, g_date_get_year (&(priv->last_edited)));
+			break;
 		default:
 			/* We don't have any other property... */
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -141,6 +170,15 @@ almanah_entry_set_property (GObject *object, guint property_id, const GValue *va
 		case PROP_IS_IMPORTANT:
 			almanah_entry_set_is_important (ALMANAH_ENTRY (object), g_value_get_boolean (value));
 			break;
+		case PROP_LAST_EDITED_DAY:
+			g_date_set_day (&(priv->last_edited), g_value_get_uint (value));
+			break;
+		case PROP_LAST_EDITED_MONTH:
+			g_date_set_month (&(priv->last_edited), g_value_get_uint (value));
+			break;
+		case PROP_LAST_EDITED_YEAR:
+			g_date_set_year (&(priv->last_edited), g_value_get_uint (value));
+			break;
 		default:
 			/* We don't have any other property... */
 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -290,3 +328,23 @@ almanah_entry_set_is_important (AlmanahEntry *self, gboolean is_important)
 		g_object_notify (G_OBJECT (self), "is-important");
 	}
 }
+
+/* NOTE: Designed for use on the stack */
+void
+almanah_entry_get_last_edited (AlmanahEntry *self, GDate *last_edited)
+{
+	g_return_if_fail (ALMANAH_IS_ENTRY (self));
+	g_return_if_fail (last_edited != NULL);
+
+	*last_edited = self->priv->last_edited;
+}
+
+/* NOTE: Designed for use on the stack */
+void
+almanah_entry_set_last_edited (AlmanahEntry *self, GDate *last_edited)
+{
+	g_return_if_fail (ALMANAH_IS_ENTRY (self));
+	g_return_if_fail (last_edited != NULL && g_date_valid (last_edited) == TRUE);
+
+	self->priv->last_edited = *last_edited;
+}
diff --git a/src/entry.h b/src/entry.h
index 95e85b1..905e75c 100644
--- a/src/entry.h
+++ b/src/entry.h
@@ -67,6 +67,9 @@ gboolean almanah_entry_is_empty (AlmanahEntry *self);
 gboolean almanah_entry_is_important (AlmanahEntry *self);
 void almanah_entry_set_is_important (AlmanahEntry *self, gboolean is_important);
 
+void almanah_entry_get_last_edited (AlmanahEntry *self, GDate *last_edited);
+void almanah_entry_set_last_edited (AlmanahEntry *self, GDate *last_edited);
+
 G_END_DECLS
 
 #endif /* !ALMANAH_ENTRY_H */
diff --git a/src/import-export-dialog.c b/src/import-export-dialog.c
index e35e680..ef98d62 100644
--- a/src/import-export-dialog.c
+++ b/src/import-export-dialog.c
@@ -214,7 +214,7 @@ almanah_import_export_dialog_new (gboolean import)
 static AlmanahImportStatus
 set_entry (AlmanahImportExportDialog *self, AlmanahEntry *imported_entry, const gchar *import_source, gchar **message)
 {
-	GDate entry_date;
+	GDate entry_date, existing_last_edited, imported_last_edited;
 	AlmanahEntry *existing_entry;
 	GtkTextBuffer *existing_buffer, *imported_buffer;
 	GtkTextIter existing_start, existing_end, imported_start, imported_end;
@@ -305,6 +305,13 @@ set_entry (AlmanahImportExportDialog *self, AlmanahEntry *imported_entry, const
 	almanah_entry_set_content (existing_entry, existing_buffer);
 	g_object_unref (existing_buffer);
 
+	/* Update the last-edited time for the merged entry to be the more recent of the last-edited times for the existing and imported entries */
+	almanah_entry_get_last_edited (existing_entry, &existing_last_edited);
+	almanah_entry_get_last_edited (imported_entry, &imported_last_edited);
+
+	if (g_date_valid (&existing_last_edited) == FALSE || g_date_compare (&existing_last_edited, &imported_last_edited) < 0)
+		almanah_entry_set_last_edited (existing_entry, &imported_last_edited);
+
 	almanah_storage_manager_set_entry (almanah->storage_manager, existing_entry);
 	g_object_unref (existing_entry);
 
@@ -326,8 +333,8 @@ import_text_files (AlmanahImportExportDialog *self, AlmanahImportResultsDialog *
 	folder = gtk_file_chooser_get_file (priv->file_chooser);
 	g_assert (folder != NULL);
 
-	enumerator = g_file_enumerate_children (folder, "standard::name,standard::display-name,standard::is-hidden", G_FILE_QUERY_INFO_NONE,
-	                                        NULL, error);
+	enumerator = g_file_enumerate_children (folder, "standard::name,standard::display-name,standard::is-hidden,time::modified",
+	                                        G_FILE_QUERY_INFO_NONE, NULL, error);
 	g_object_unref (folder);
 	if (enumerator == NULL)
 		return FALSE;
@@ -338,7 +345,8 @@ import_text_files (AlmanahImportExportDialog *self, AlmanahImportResultsDialog *
 	/* Enumerate all the children of the folder */
 	while ((file_info = g_file_enumerator_next_file (enumerator, NULL, &child_error)) != NULL) {
 		AlmanahEntry *entry;
-		GDate parsed_date;
+		GDate parsed_date, last_edited;
+		GTimeVal modification_time;
 		GFile *file;
 		gchar *contents, *message = NULL;
 		gsize length;
@@ -379,6 +387,11 @@ import_text_files (AlmanahImportExportDialog *self, AlmanahImportResultsDialog *
 		almanah_entry_set_content (entry, buffer);
 		g_free (contents);
 
+		/* Set the entry's last-edited date */
+		g_file_info_get_modification_time (file_info, &modification_time);
+		g_date_set_time_val (&last_edited, &modification_time);
+		almanah_entry_set_last_edited (entry, &last_edited);
+
 		/* Store the entry */
 		status = set_entry (self, entry, display_name, &message);
 		almanah_import_results_dialog_add_result (results_dialog, &parsed_date, status, message);
diff --git a/src/main-window.c b/src/main-window.c
index 7355181..310e8e9 100644
--- a/src/main-window.c
+++ b/src/main-window.c
@@ -361,7 +361,7 @@ static void
 save_current_entry (AlmanahMainWindow *self)
 {
 	gboolean entry_exists, entry_is_empty;
-	GDate date;
+	GDate date, last_edited;
 	AlmanahMainWindowPrivate *priv = self->priv;
 	AlmanahEntryEditability editability;
 
@@ -373,10 +373,6 @@ save_current_entry (AlmanahMainWindow *self)
 	    gtk_text_buffer_get_modified (priv->entry_buffer) == FALSE)
 		return;
 
-	/* Save the entry */
-	almanah_entry_set_content (priv->current_entry, priv->entry_buffer);
-	gtk_text_buffer_set_modified (priv->entry_buffer, FALSE);
-
 	almanah_entry_get_date (priv->current_entry, &date);
 	editability = almanah_entry_get_editability (priv->current_entry);
 	entry_exists = almanah_storage_manager_entry_exists (almanah->storage_manager, &date);
@@ -440,6 +436,13 @@ save_current_entry (AlmanahMainWindow *self)
 		gtk_widget_destroy (dialog);
 	}
 
+	/* Save the entry */
+	almanah_entry_set_content (priv->current_entry, priv->entry_buffer);
+	gtk_text_buffer_set_modified (priv->entry_buffer, FALSE);
+
+	g_date_set_time_t (&last_edited, time (NULL));
+	almanah_entry_set_last_edited (priv->current_entry, &last_edited);
+
 	/* Store the entry! */
 	almanah_storage_manager_set_entry (almanah->storage_manager, priv->current_entry);
 
diff --git a/src/storage-manager.c b/src/storage-manager.c
index 0958f95..e38f2e2 100644
--- a/src/storage-manager.c
+++ b/src/storage-manager.c
@@ -203,6 +203,9 @@ create_tables (AlmanahStorageManager *self)
 		"CREATE TABLE IF NOT EXISTS definitions (definition_text TEXT, definition_type INTEGER, definition_value TEXT, "
 		                                        "definition_value2 TEXT, PRIMARY KEY (definition_text))",
 		"ALTER TABLE entries ADD COLUMN is_important INTEGER", /* added in 0.7.0 */
+		"ALTER TABLE entries ADD COLUMN edited_year INTEGER", /* added in 0.8.0 */
+		"ALTER TABLE entries ADD COLUMN edited_month INTEGER", /* added in 0.8.0 */
+		"ALTER TABLE entries ADD COLUMN edited_day INTEGER", /* added in 0.8.0 */
 		NULL
 	};
 
@@ -743,13 +746,15 @@ almanah_storage_manager_get_entry (AlmanahStorageManager *self, GDate *date)
 {
 	AlmanahEntry *entry;
 	sqlite3_stmt *statement;
+	GDate last_edited;
 
 	/* 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, is_important FROM entries WHERE year = ? AND month = ? AND day = ?", -1,
+	                        "SELECT content, is_important, edited_day, edited_month, edited_year FROM entries "
+	                        "WHERE year = ? AND month = ? AND day = ?", -1,
 	                        &statement, NULL) != SQLITE_OK) {
 		return NULL;
 	}
@@ -770,6 +775,14 @@ almanah_storage_manager_get_entry (AlmanahStorageManager *self, GDate *date)
 	almanah_entry_set_data (entry, sqlite3_column_blob (statement, 0), sqlite3_column_bytes (statement, 0));
 	almanah_entry_set_is_important (entry, (sqlite3_column_int (statement, 1) == 1) ? TRUE : FALSE);
 
+	if (g_date_valid_dmy (sqlite3_column_int (statement, 2), sqlite3_column_int (statement, 3), sqlite3_column_int (statement, 4)) == TRUE) {
+		g_date_set_dmy (&last_edited,
+			        sqlite3_column_int (statement, 2),
+			        sqlite3_column_int (statement, 3),
+			        sqlite3_column_int (statement, 4));
+		almanah_entry_set_last_edited (entry, &last_edited);
+	}
+
 	sqlite3_finalize (statement);
 
 	return entry;
@@ -783,6 +796,8 @@ almanah_storage_manager_get_entry (AlmanahStorageManager *self, GDate *date)
  * 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 definitions' rows).
  *
+ * The entry's last-edited date should be manually updated before storing it in the database, if desired.
+ *
  * Return value: %TRUE on success, %FALSE otherwise
  **/
 gboolean
@@ -804,6 +819,7 @@ almanah_storage_manager_set_entry (AlmanahStorageManager *self, AlmanahEntry *en
 		const guint8 *data;
 		gsize length;
 		sqlite3_stmt *statement;
+		GDate last_edited;
 
 		/* 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
@@ -811,7 +827,8 @@ almanah_storage_manager_set_entry (AlmanahStorageManager *self, AlmanahEntry *en
 
 		/* Prepare the statement */
 		if (sqlite3_prepare_v2 (self->priv->connection,
-		                        "REPLACE INTO entries (year, month, day, content, is_important) VALUES (?, ?, ?, ?, ?)", -1,
+		                        "REPLACE INTO entries (year, month, day, content, is_important, edited_day, edited_month, edited_year) "
+		                        "VALUES (?, ?, ?, ?, ?, ?, ?, ?)", -1,
 		                        &statement, NULL) != SQLITE_OK) {
 			return FALSE;
 		}
@@ -826,6 +843,11 @@ almanah_storage_manager_set_entry (AlmanahStorageManager *self, AlmanahEntry *en
 
 		sqlite3_bind_int (statement, 5, almanah_entry_is_important (entry));
 
+		almanah_entry_get_last_edited (entry, &last_edited);
+		sqlite3_bind_int (statement, 6, g_date_get_day (&last_edited));
+		sqlite3_bind_int (statement, 7, g_date_get_month (&last_edited));
+		sqlite3_bind_int (statement, 8, g_date_get_year (&last_edited));
+
 		/* Execute the statement */
 		if (sqlite3_step (statement) != SQLITE_DONE) {
 			sqlite3_finalize (statement);
@@ -859,7 +881,8 @@ almanah_storage_manager_search_entries (AlmanahStorageManager *self, const gchar
 	/* Prepare the statement. Query in ascending date order, and then reverse the results by prepending them to the list as we build it,
 	 * rather than appending them. */
 	if (sqlite3_prepare_v2 (self->priv->connection,
-	                        "SELECT content, day, month, year, is_important FROM entries ORDER BY year ASC, month ASC, day ASC", -1,
+	                        "SELECT content, day, month, year, is_important, edited_day, edited_month, edited_year FROM entries "
+	                        "ORDER BY year ASC, month ASC, day ASC", -1,
 	                        &statement, NULL) != SQLITE_OK) {
 		return NULL;
 	}
@@ -869,7 +892,7 @@ almanah_storage_manager_search_entries (AlmanahStorageManager *self, const gchar
 	/* Execute the statement */
 	while (sqlite3_step (statement) == SQLITE_ROW) {
 		AlmanahEntry *entry;
-		GDate date;
+		GDate date, last_edited;
 		GtkTextIter iter;
 
 		g_date_set_dmy (&date, sqlite3_column_int (statement, 1), sqlite3_column_int (statement, 2), sqlite3_column_int (statement, 3));
@@ -877,6 +900,16 @@ almanah_storage_manager_search_entries (AlmanahStorageManager *self, const gchar
 		almanah_entry_set_data (entry, sqlite3_column_blob (statement, 0), sqlite3_column_bytes (statement, 0));
 		almanah_entry_set_is_important (entry, (sqlite3_column_int (statement, 4) == 1) ? TRUE : FALSE);
 
+		if (g_date_valid_dmy (sqlite3_column_int (statement, 2),
+		                      sqlite3_column_int (statement, 3),
+		                      sqlite3_column_int (statement, 4)) == TRUE) {
+			g_date_set_dmy (&last_edited,
+				        sqlite3_column_int (statement, 5),
+				        sqlite3_column_int (statement, 6),
+				        sqlite3_column_int (statement, 7));
+			almanah_entry_set_last_edited (entry, &last_edited);
+		}
+
 		/* Deserialise the entry into our buffer */
 		gtk_text_buffer_set_text (text_buffer, "", 0);
 		if (almanah_entry_get_content (entry, text_buffer, TRUE, NULL) == FALSE) {
@@ -923,14 +956,14 @@ almanah_storage_manager_get_entries (AlmanahStorageManager *self)
 
 	/* Prepare the statement */
 	if (sqlite3_prepare_v2 (self->priv->connection,
-	                        "SELECT content, is_important, day, month, year FROM entries", -1,
+	                        "SELECT content, is_important, day, month, year, edited_day, edited_month, edited_year FROM entries", -1,
 	                        &statement, NULL) != SQLITE_OK) {
 		return NULL;
 	}
 
 	/* Execute the statement */
 	while ((result = sqlite3_step (statement)) == SQLITE_ROW) {
-		GDate date;
+		GDate date, last_edited;
 		AlmanahEntry *entry;
 
 		g_date_set_dmy (&date,
@@ -943,6 +976,16 @@ almanah_storage_manager_get_entries (AlmanahStorageManager *self)
 		almanah_entry_set_data (entry, sqlite3_column_blob (statement, 0), sqlite3_column_bytes (statement, 0));
 		almanah_entry_set_is_important (entry, (sqlite3_column_int (statement, 1) == 1) ? TRUE : FALSE);
 
+		if (g_date_valid_dmy (sqlite3_column_int (statement, 2),
+		                      sqlite3_column_int (statement, 3),
+		                      sqlite3_column_int (statement, 4)) == TRUE) {
+			g_date_set_dmy (&last_edited,
+				        sqlite3_column_int (statement, 5),
+				        sqlite3_column_int (statement, 6),
+				        sqlite3_column_int (statement, 7));
+			almanah_entry_set_last_edited (entry, &last_edited);
+		}
+
 		entries = g_slist_prepend (entries, entry);
 	}
 



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