[almanah] Added import support
- From: Philip Withnall <pwithnall src gnome org>
- To: svn-commits-list gnome org
- Cc:
- Subject: [almanah] Added import support
- Date: Thu, 24 Sep 2009 15:12:43 +0000 (UTC)
commit 97530ba0ac032ef81892f090030db2aeb971e0ca
Author: Philip Withnall <philip tecnocode co uk>
Date: Thu Sep 24 16:08:46 2009 +0100
Added import support
Text files and other Almanah databases can now be imported to the
current database.
data/almanah.ui | 235 +++++++++++++
po/Makefile.in.in | 4 +-
po/POTFILES.in | 1 +
src/Makefile.am | 2 +
src/definition-manager-window.c | 14 +-
src/import-dialog.c | 694 +++++++++++++++++++++++++++++++++++++++
src/import-dialog.h | 83 +++++
src/main-window.c | 16 +-
src/main.c | 2 +
src/main.h | 1 +
src/storage-manager.c | 132 ++++++--
src/storage-manager.h | 3 +-
12 files changed, 1144 insertions(+), 43 deletions(-)
---
diff --git a/data/almanah.ui b/data/almanah.ui
index 7729b6c..6dcba63 100644
--- a/data/almanah.ui
+++ b/data/almanah.ui
@@ -10,6 +10,13 @@
</object>
</child>
<child>
+ <object class="GtkAction" id="almanah_ui_import">
+ <property name="name">file-import</property>
+ <property name="label" translatable="yes">_Importâ?¦</property>
+ <signal name="activate" handler="mw_import_activate_cb"/>
+ </object>
+ </child>
+ <child>
<object class="GtkAction" id="almanah_ui_page_setup">
<property name="stock-id">gtk-page-setup</property>
<property name="name">file-page-setup</property>
@@ -201,6 +208,8 @@
<ui>
<menubar name="almanah_mw_menu_bar">
<menu action="almanah_ui_file">
+ <menuitem action="almanah_ui_import"/>
+ <separator/>
<menuitem action="almanah_ui_page_setup"/>
<menuitem action="almanah_ui_print_preview"/>
<menuitem action="almanah_ui_print"/>
@@ -918,4 +927,230 @@
<action-widget response="-5">almanah_ded_ok_button</action-widget><!-- GTK_RESPONSE_OK -->
</action-widgets>
</object>
+
+ <object class="GtkListStore" id="almanah_id_mode_store">
+ <columns>
+ <column type="gchararray"/><!-- Name -->
+ <column type="guint"/><!-- Type -->
+ </columns>
+ </object>
+
+ <object class="AlmanahImportDialog" id="almanah_import_dialog">
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="almanah_id_internal_vbox">
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkVBox" id="almanah_id_vbox">
+ <property name="border-width">5</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkHBox" id="almanah_id_hbox">
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkLabel" id="almanah_id_mode_label">
+ <property name="xalign">0</property>
+ <property name="label" translatable="yes">Import Mode</property>
+ <accessibility>
+ <relation target="almanah_id_mode_combo_box" type="label-for"/>
+ </accessibility>
+ <attributes>
+ <attribute name="weight" value="PANGO_WEIGHT_BOLD"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkComboBox" id="almanah_id_mode_combo_box">
+ <signal name="changed" handler="id_mode_combo_box_changed_cb"/>
+ <property name="model">almanah_id_mode_store</property>
+ <accessibility>
+ <relation target="almanah_id_mode_label" type="labelled-by"/>
+ </accessibility>
+ <child>
+ <object class="GtkCellRendererText" id="almanah_id_mode_renderer"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="almanah_id_description_label">
+ <property name="wrap">True</property>
+ <attributes>
+ <attribute name="scale" value="0.83"/><!-- PANGO_SCALE_SMALL -->
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFileChooserWidget" id="almanah_id_file_chooser">
+ <signal name="selection-changed" handler="id_file_chooser_selection_changed_cb"/>
+ <signal name="file-activated" handler="id_file_chooser_file_activated_cb"/>
+ <property name="action">GTK_FILE_CHOOSER_ACTION_OPEN</property>
+ <property name="select-multiple">False</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="almanah_id_button_box">
+ <child>
+ <object class="GtkButton" id="almanah_id_cancel_button">
+ <property name="use-stock">True</property>
+ <property name="label">gtk-cancel</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="almanah_id_import_button">
+ <property name="use-underline">True</property>
+ <property name="label" translatable="yes">_Import</property>
+ <property name="can-default">True</property>
+ <property name="has-default">True</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-6">almanah_id_cancel_button</action-widget><!-- GTK_RESPONSE_CANCEL -->
+ <action-widget response="-5">almanah_id_import_button</action-widget><!-- GTK_RESPONSE_OK -->
+ </action-widgets>
+ </object>
+
+ <object class="GtkListStore" id="almanah_ird_view_store">
+ <columns>
+ <column type="gchararray"/><!-- View name -->
+ </columns>
+ <data>
+ <row>
+ <col id="0" translatable="yes">Successful Entries</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Merged Entries</col>
+ </row>
+ <row>
+ <col id="0" translatable="yes">Failed Entries</col>
+ </row>
+ </data>
+ </object>
+
+ <object class="GtkListStore" id="almanah_ird_results_store">
+ <columns>
+ <column type="guint"/><!-- Day -->
+ <column type="guint"/><!-- Month -->
+ <column type="guint"/><!-- Year -->
+ <column type="gchararray"/><!-- Formatted date -->
+ <column type="guint"/><!-- Status -->
+ <column type="gchararray"/><!-- Message -->
+ </columns>
+ </object>
+
+ <object class="GtkTreeModelFilter" id="almanah_ird_filtered_results_store">
+ <property name="child-model">almanah_ird_results_store</property>
+ </object>
+
+ <object class="AlmanahImportResultsDialog" id="almanah_import_results_dialog">
+ <child internal-child="vbox">
+ <object class="GtkVBox" id="almanah_ird_internal_vbox">
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkVBox" id="almanah_id_vbox">
+ <property name="border-width">5</property>
+ <property name="spacing">5</property>
+ <child>
+ <object class="GtkComboBox" id="almanah_ird_view_combo_box">
+ <signal name="changed" handler="ird_view_combo_box_changed_cb"/>
+ <property name="model">almanah_ird_view_store</property>
+ <accessibility>
+ <relation target="almanah_ird_results_tree_view" type="label-for"/>
+ </accessibility>
+ <child>
+ <object class="GtkCellRendererText" id="almanah_ird_view_renderer"/>
+ <attributes>
+ <attribute name="text">0</attribute>
+ </attributes>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="almanah_ird_results_scrolled_window">
+ <property name="hscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="vscrollbar-policy">GTK_POLICY_AUTOMATIC</property>
+ <property name="shadow-type">GTK_SHADOW_IN</property>
+ <child>
+ <object class="GtkTreeView" id="almanah_ird_results_tree_view">
+ <property name="model">almanah_ird_filtered_results_store</property>
+ <property name="headers-visible">False</property>
+ <signal name="row-activated" handler="ird_results_tree_view_row_activated_cb"/>
+ <accessibility>
+ <relation target="almanah_ird_view_combo_box" type="labelled-by"/>
+ </accessibility>
+ <child internal-child="accessible">
+ <object class="AtkObject" id="a11y-almanah_ird_results_tree_view">
+ <property name="AtkObject::accessible-name" translatable="yes">Import Results List</property>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="almanah_ird_results_column">
+ <child>
+ <object class="GtkCellRendererText" id="almanah_ird_results_renderer"/>
+ <attributes>
+ <attribute name="text">3</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkTreeViewColumn" id="almanah_ird_results_message_column">
+ <child>
+ <object class="GtkCellRendererText" id="almanah_ird_results_message_renderer"/>
+ <attributes>
+ <attribute name="text">5</attribute>
+ </attributes>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child internal-child="action_area">
+ <object class="GtkHButtonBox" id="almanah_ird_button_box">
+ <child>
+ <object class="GtkButton" id="almanah_ird_view_button">
+ <property name="label" translatable="yes">View Entry</property>
+ <property name="sensitive">False</property>
+ <signal name="clicked" handler="ird_view_button_clicked_cb"/>
+ </object>
+ </child>
+ <child>
+ <object class="GtkButton" id="almanah_ird_close_button">
+ <property name="use-stock">True</property>
+ <property name="label">gtk-close</property>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-7">almanah_ird_close_button</action-widget><!-- GTK_RESPONSE_CLOSE -->
+ </action-widgets>
+ </object>
</interface>
diff --git a/po/Makefile.in.in b/po/Makefile.in.in
index 57ef267..c7e8302 100644
--- a/po/Makefile.in.in
+++ b/po/Makefile.in.in
@@ -21,7 +21,7 @@ GETTEXT_PACKAGE = @GETTEXT_PACKAGE@
PACKAGE = @PACKAGE@
VERSION = @VERSION@
-SHELL = /bin/sh
+SHELL = @SHELL@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
@@ -56,7 +56,7 @@ ALL_LINGUAS = @ALL_LINGUAS@
PO_LINGUAS=$(shell if test -r $(srcdir)/LINGUAS; then grep -v "^\#" $(srcdir)/LINGUAS; else echo "$(ALL_LINGUAS)"; fi)
-USER_LINGUAS=$(shell if test -n "$(LINGUAS)"; then LLINGUAS="$(LINGUAS)"; ALINGUAS="$(ALL_LINGUAS)"; for lang in $$LLINGUAS; do if test -n "`grep ^$$lang$$ $(srcdir)/LINGUAS 2>/dev/null`" -o -n "`echo $$ALINGUAS|tr ' ' '\n'|grep ^$$lang$$`"; then printf "$$lang "; fi; done; fi)
+USER_LINGUAS=$(shell if test -n "$(LINGUAS)"; then LLINGUAS="$(LINGUAS)"; ALINGUAS="$(ALL_LINGUAS)"; for lang in $$LLINGUAS; do if test -n "`grep '^$$lang$$' $(srcdir)/LINGUAS 2>/dev/null`" -o -n "`echo $$ALINGUAS|tr ' ' '\n'|grep '^$$lang$$'`"; then printf "$$lang "; fi; done; fi)
USE_LINGUAS=$(shell if test -n "$(USER_LINGUAS)" -o -n "$(LINGUAS)"; then LLINGUAS="$(USER_LINGUAS)"; else if test -n "$(PO_LINGUAS)"; then LLINGUAS="$(PO_LINGUAS)"; else LLINGUAS="$(ALL_LINGUAS)"; fi; fi; for lang in $$LLINGUAS; do printf "$$lang "; done)
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 6e536f5..2ab85e3 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -7,6 +7,7 @@ src/add-definition-dialog.c
src/events/calendar-appointment.c
src/events/calendar-task.c
src/events/f-spot-photo.c
+src/import-dialog.c
src/interface.c
src/definition-manager-window.c
src/definition.c
diff --git a/src/Makefile.am b/src/Makefile.am
index eb99cb0..63fc1f5 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -55,6 +55,8 @@ almanah_SOURCES = \
definition.h \
date-entry-dialog.c \
date-entry-dialog.h \
+ import-dialog.c \
+ import-dialog.h \
definitions/contact.c \
definitions/contact.h \
definitions/file.c \
diff --git a/src/definition-manager-window.c b/src/definition-manager-window.c
index d507a0c..de80665 100644
--- a/src/definition-manager-window.c
+++ b/src/definition-manager-window.c
@@ -359,20 +359,20 @@ static void
populate_definition_store (AlmanahDefinitionManagerWindow *self)
{
AlmanahDefinitionManagerWindowPrivate *priv = self->priv;
- AlmanahDefinition **definitions;
- guint i = 0;
+ GSList *definitions, *i;
definitions = almanah_storage_manager_get_definitions (almanah->storage_manager);
- while (definitions[i] != NULL) {
+ for (i = definitions; i != NULL; i = i->next) {
GtkTreeIter iter;
+ AlmanahDefinition *definition = ALMANAH_DEFINITION (i->data);
gtk_list_store_append (priv->definition_store, &iter);
gtk_list_store_set (priv->definition_store, &iter,
- 0, definitions[i],
- 1, almanah_definition_get_text (definitions[i]),
+ 0, definition,
+ 1, almanah_definition_get_text (definition),
-1);
- i++;
+ g_object_unref (definition);
}
- g_free (definitions);
+ g_slist_free (definitions);
}
diff --git a/src/import-dialog.c b/src/import-dialog.c
new file mode 100644
index 0000000..561c1ba
--- /dev/null
+++ b/src/import-dialog.c
@@ -0,0 +1,694 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2009 <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 <config.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include "import-dialog.h"
+#include "interface.h"
+#include "main.h"
+#include "main-window.h"
+
+typedef enum {
+ MODE_TEXT_FILES = 0,
+ MODE_DATABASE
+} ImportMode;
+
+static const struct { ImportMode mode; const gchar *name; } import_modes[] = {
+ { MODE_TEXT_FILES, N_("Text Files") },
+ { MODE_DATABASE, N_("Database") }
+};
+
+static void response_cb (GtkDialog *dialog, gint response_id, AlmanahImportDialog *self);
+
+/* GtkBuilder callbacks */
+void id_mode_combo_box_changed_cb (GtkComboBox *combo_box, AlmanahImportDialog *self);
+void id_file_chooser_selection_changed_cb (GtkFileChooser *file_chooser, AlmanahImportDialog *self);
+void id_file_chooser_file_activated_cb (GtkFileChooser *file_chooser, AlmanahImportDialog *self);
+
+struct _AlmanahImportDialogPrivate {
+ GtkComboBox *mode_combo_box;
+ GtkListStore *mode_store;
+ ImportMode current_mode;
+ GtkFileChooser *file_chooser;
+ GtkWidget *import_button;
+ GtkLabel *description_label;
+};
+
+G_DEFINE_TYPE (AlmanahImportDialog, almanah_import_dialog, GTK_TYPE_DIALOG)
+#define ALMANAH_IMPORT_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_IMPORT_DIALOG, AlmanahImportDialogPrivate))
+
+static void
+almanah_import_dialog_class_init (AlmanahImportDialogClass *klass)
+{
+ g_type_class_add_private (klass, sizeof (AlmanahImportDialogPrivate));
+}
+
+static void
+almanah_import_dialog_init (AlmanahImportDialog *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_IMPORT_DIALOG, AlmanahImportDialogPrivate);
+ self->priv->current_mode = -1; /* no mode selected */
+
+ g_signal_connect (self, "response", G_CALLBACK (response_cb), self);
+ g_signal_connect (self, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), self);
+ gtk_dialog_set_has_separator (GTK_DIALOG (self), FALSE);
+ gtk_window_set_resizable (GTK_WINDOW (self), TRUE);
+ gtk_window_set_title (GTK_WINDOW (self), _("Import Entries"));
+ gtk_window_set_transient_for (GTK_WINDOW (self), GTK_WINDOW (almanah->main_window));
+ gtk_window_set_default_size (GTK_WINDOW (self), 600, 400);
+}
+
+static void
+populate_mode_store (GtkListStore *store)
+{
+ guint i;
+
+ /* Add all the available import modes to the combo box */
+ for (i = 0; i < G_N_ELEMENTS (import_modes); i++) {
+ GtkTreeIter iter;
+
+ gtk_list_store_append (store, &iter);
+ gtk_list_store_set (store, &iter, 0, _(import_modes[i].name), 1, import_modes[i].mode, -1);
+ }
+}
+
+AlmanahImportDialog *
+almanah_import_dialog_new (void)
+{
+ GtkBuilder *builder;
+ AlmanahImportDialog *import_dialog;
+ AlmanahImportDialogPrivate *priv;
+ GError *error = NULL;
+ const gchar *interface_filename = almanah_get_interface_filename ();
+ const gchar *object_names[] = {
+ "almanah_id_mode_store",
+ "almanah_import_dialog",
+ NULL
+ };
+
+ builder = gtk_builder_new ();
+
+ if (gtk_builder_add_objects_from_file (builder, interface_filename, (gchar**) object_names, &error) == FALSE) {
+ /* Show an error */
+ GtkWidget *dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("UI file \"%s\" could not be loaded"), interface_filename);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_error_free (error);
+ g_object_unref (builder);
+
+ return NULL;
+ }
+
+ gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE);
+ import_dialog = ALMANAH_IMPORT_DIALOG (gtk_builder_get_object (builder, "almanah_import_dialog"));
+ gtk_builder_connect_signals (builder, import_dialog);
+
+ if (import_dialog == NULL) {
+ g_object_unref (builder);
+ return NULL;
+ }
+
+ priv = import_dialog->priv;
+
+ /* Grab our child widgets */
+ priv->mode_combo_box = GTK_COMBO_BOX (gtk_builder_get_object (builder, "almanah_id_mode_combo_box"));
+ priv->file_chooser = GTK_FILE_CHOOSER (gtk_builder_get_object (builder, "almanah_id_file_chooser"));
+ priv->import_button = GTK_WIDGET (gtk_builder_get_object (builder, "almanah_id_import_button"));
+ priv->mode_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "almanah_id_mode_store"));
+ priv->description_label = GTK_LABEL (gtk_builder_get_object (builder, "almanah_id_description_label"));
+
+ /* Populate the mode combo box */
+ populate_mode_store (priv->mode_store);
+ gtk_combo_box_set_active (priv->mode_combo_box, 0);
+
+ g_object_unref (builder);
+
+ return import_dialog;
+}
+
+/**
+ * set_entry:
+ * @self: an #AlmanahImportDialog
+ * @imported_entry: an #AlmanahEntry created for an entry being imported
+ * @import_source: a string representing the source of the imported entry
+ * @message: return location for an error or informational message, or %NULL; free with g_free()
+ *
+ * Stores the given entry in the database, merging it with any existing one where appropriate.
+ *
+ * @import_source should represent where the imported entry came from, so it could be the filename of a text file which held the entry, or of
+ * the entry's previous database.
+ *
+ * If @message is non-%NULL, error or informational messages can be allocated and returned in it. They will be returned irrespective of the return
+ * value of the function, as some errors can be overcome to result in a successful import. The message should be freed with g_free() by the caller.
+ *
+ * Return value: %ALMANAH_IMPORT_STATUS_MERGED if the entry was merged with an existing one, %ALMANAH_IMPORT_STATUS_IMPORTED if it was imported
+ * without merging, %ALMANAH_IMPORT_STATUS_FAILED otherwise
+ **/
+static AlmanahImportStatus
+set_entry (AlmanahImportDialog *self, AlmanahEntry *imported_entry, const gchar *import_source, gchar **message)
+{
+ GDate entry_date;
+ AlmanahEntry *existing_entry;
+ GtkTextBuffer *existing_buffer, *imported_buffer;
+ GtkTextIter existing_end, imported_start, imported_end;
+ gchar *header_string;
+ GError *error = NULL;
+
+ /* Check to see if there's a conflict first */
+ almanah_entry_get_date (imported_entry, &entry_date);
+ existing_entry = almanah_storage_manager_get_entry (almanah->storage_manager, &entry_date);
+
+ if (existing_entry == NULL) {
+ /* Add the entry to the proper database and return, ignoring failure */
+ almanah_storage_manager_set_entry (almanah->storage_manager, imported_entry);
+ return ALMANAH_IMPORT_STATUS_IMPORTED;
+ }
+
+ /* Load both entries into buffers */
+ imported_buffer = gtk_text_buffer_new (NULL);
+ if (almanah_entry_get_content (imported_entry, imported_buffer, TRUE, &error) == FALSE) {
+ if (message != NULL)
+ *message = g_strdup_printf (_("Error deserializing imported entry into buffer: %s"), (error != NULL) ? error->message : NULL);
+
+ g_error_free (error);
+ g_object_unref (imported_buffer);
+ g_object_unref (existing_entry);
+ return ALMANAH_IMPORT_STATUS_FAILED;
+ }
+
+ /* The two buffers have to use the same tag table for gtk_text_buffer_insert_range() to work */
+ existing_buffer = gtk_text_buffer_new (gtk_text_buffer_get_tag_table (imported_buffer));
+ if (almanah_entry_get_content (existing_entry, existing_buffer, FALSE, NULL) == FALSE) {
+ /* Deserialising the existing entry failed; use the imported entry instead */
+ almanah_storage_manager_set_entry (almanah->storage_manager, imported_entry);
+
+ if (message != NULL) {
+ *message = g_strdup_printf (_("Error deserializing existing entry into buffer; overwriting with imported entry: %s"),
+ (error != NULL) ? error->message : NULL);
+ }
+
+ g_error_free (error);
+ g_object_unref (imported_buffer);
+ g_object_unref (existing_buffer);
+ g_object_unref (existing_entry);
+ return ALMANAH_IMPORT_STATUS_IMPORTED;
+ }
+
+ /* Append some header text for the imported entry */
+ header_string = g_strdup_printf (_("\n\nEntry imported from \"%s\":\n\n"), import_source);
+ gtk_text_buffer_get_end_iter (existing_buffer, &existing_end);
+ gtk_text_buffer_insert (existing_buffer, &existing_end, header_string, -1);
+ g_free (header_string);
+
+ /* Append the imported entry to the end of the existing one */
+ gtk_text_buffer_get_bounds (imported_buffer, &imported_start, &imported_end);
+ gtk_text_buffer_insert_range (existing_buffer, &existing_end, &imported_start, &imported_end);
+ g_object_unref (imported_buffer);
+
+ /* Store the buffer back in the existing entry and save the entry */
+ almanah_entry_set_content (existing_entry, existing_buffer);
+ g_object_unref (existing_buffer);
+
+ almanah_storage_manager_set_entry (almanah->storage_manager, existing_entry);
+ g_object_unref (existing_entry);
+
+ return ALMANAH_IMPORT_STATUS_MERGED;
+}
+
+static gboolean
+import_text_files (AlmanahImportDialog *self, AlmanahImportResultsDialog *results_dialog, GError **error)
+{
+ gboolean retval = FALSE;
+ GFile *folder;
+ GFileInfo *file_info;
+ GFileEnumerator *enumerator;
+ GtkTextBuffer *buffer;
+ GError *child_error = NULL;
+ AlmanahImportDialogPrivate *priv = self->priv;
+
+ /* Get the folder containing all the files to import */
+ 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);
+ g_object_unref (folder);
+ if (enumerator == NULL)
+ return FALSE;
+
+ /* Build a text buffer to use when setting all the entries */
+ buffer = gtk_text_buffer_new (NULL);
+
+ /* 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;
+ GFile *file;
+ gchar *contents, *message = NULL;
+ gsize length;
+ AlmanahImportStatus status;
+ const gchar *file_name = g_file_info_get_name (file_info);
+ const gchar *display_name = g_file_info_get_display_name (file_info);
+
+ /* Skip the file if it's hidden */
+ if (g_file_info_get_is_hidden (file_info) == TRUE || file_name[strlen (file_name) - 1] == '~') {
+ g_object_unref (file_info);
+ continue;
+ }
+
+ /* Heuristically parse the date, though we recommend using the format: yyyy-mm-dd */
+ g_date_set_parse (&parsed_date, file_name);
+ if (g_date_valid (&parsed_date) == FALSE) {
+ g_object_unref (file_info);
+ continue;
+ }
+
+ /* Get the file */
+ file = g_file_get_child (folder, file_name);
+ g_assert (file != NULL);
+
+ /* Load the content */
+ if (g_file_load_contents (file, NULL, &contents, &length, NULL, error) == FALSE) {
+ g_object_unref (file);
+ g_object_unref (file_info);
+ break; /* let the error get handled by the code just after the loop */
+ }
+ g_object_unref (file);
+
+ /* Create the relevant entry */
+ entry = almanah_entry_new (&parsed_date);
+
+ /* Set the content on the entry */
+ gtk_text_buffer_set_text (buffer, contents, length);
+ almanah_entry_set_content (entry, buffer);
+ g_free (contents);
+
+ /* Store the entry */
+ status = set_entry (self, entry, display_name, &message);
+ almanah_import_results_dialog_add_result (results_dialog, &parsed_date, status, message);
+ g_free (message);
+
+ g_object_unref (entry);
+ g_object_unref (file_info);
+ }
+
+ /* Check if the loop was broken due to an error */
+ if (child_error != NULL) {
+ g_propagate_error (error, child_error);
+ goto finish;
+ }
+
+ /* Success! */
+ retval = TRUE;
+
+finish:
+ g_object_unref (folder);
+ g_object_unref (enumerator);
+ g_object_unref (buffer);
+
+ return retval;
+}
+
+static gboolean
+import_database (AlmanahImportDialog *self, AlmanahImportResultsDialog *results_dialog, GError **error)
+{
+ GFile *file;
+ GFileInfo *file_info;
+ gchar *path;
+ const gchar *display_name;
+ GSList *entries, *i, *definitions;
+ AlmanahStorageManager *database;
+ AlmanahImportDialogPrivate *priv = self->priv;
+
+ /* Get the database file to import */
+ file = gtk_file_chooser_get_file (priv->file_chooser);
+ g_assert (file != NULL);
+
+ /* Get the display name for use with set_entry(), below */
+ file_info = g_file_query_info (file, G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+ display_name = g_file_info_get_display_name (file_info);
+
+ /* Open the database */
+ path = g_file_get_path (file);
+ database = almanah_storage_manager_new (path);
+ g_free (path);
+ g_object_unref (file);
+
+ /* Connect to the database */
+ if (almanah_storage_manager_connect (database, error) == FALSE) {
+ g_object_unref (database);
+ return FALSE;
+ }
+
+ /* Query for every entry */
+ entries = almanah_storage_manager_get_entries (database);
+ for (i = entries; i != NULL; i = i->next) {
+ GDate date;
+ gchar *message = NULL;
+ AlmanahImportStatus status;
+ AlmanahEntry *entry = ALMANAH_ENTRY (i->data);
+
+ almanah_entry_get_date (entry, &date);
+
+ status = set_entry (self, entry, display_name, &message);
+ almanah_import_results_dialog_add_result (results_dialog, &date, status, message);
+ g_free (message);
+
+ g_object_unref (entry);
+ }
+ g_slist_free (entries);
+
+ /* Query for every definition */
+ definitions = almanah_storage_manager_get_definitions (database);
+ for (i = definitions; i != NULL; i = i->next) {
+ /* Add the definition to the proper database, ignoring failure */
+ almanah_storage_manager_add_definition (almanah->storage_manager, ALMANAH_DEFINITION (i->data));
+ g_object_unref (i->data);
+ }
+ g_slist_free (definitions);
+
+ almanah_storage_manager_disconnect (database, NULL);
+ g_object_unref (database);
+ g_object_unref (file_info);
+
+ return TRUE;
+}
+
+static void
+response_cb (GtkDialog *dialog, gint response_id, AlmanahImportDialog *self)
+{
+ GError *error = NULL;
+ AlmanahImportDialogPrivate *priv = self->priv;
+ AlmanahImportResultsDialog *results_dialog;
+
+ /* Just return if the user pressed Cancel */
+ if (response_id != GTK_RESPONSE_OK) {
+ gtk_widget_hide (GTK_WIDGET (self));
+ return;
+ }
+
+ /* Import the entries according to the selected method. It's OK if we block here, since the dialogue should
+ * be running in its own main loop. */
+ results_dialog = almanah_import_results_dialog_new ();
+ switch (priv->current_mode) {
+ case MODE_TEXT_FILES:
+ import_text_files (self, results_dialog, &error);
+ break;
+ case MODE_DATABASE:
+ import_database (self, results_dialog, &error);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ /* Check for errors (e.g. errors opening databases or files; not errors importing individual entries once we have the content to import) */
+ if (error != NULL) {
+ /* Show an error */
+ GtkWidget *error_dialog = gtk_message_dialog_new (GTK_WINDOW (self), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK, _("Import failed"));
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (error_dialog), "%s", error->message);
+ gtk_dialog_run (GTK_DIALOG (error_dialog));
+ gtk_widget_destroy (error_dialog);
+
+ g_error_free (error);
+
+ gtk_widget_hide (GTK_WIDGET (self));
+ } else {
+ /* Show the results dialogue */
+ gtk_widget_hide (GTK_WIDGET (self));
+ gtk_widget_show_all (GTK_WIDGET (results_dialog));
+ gtk_dialog_run (GTK_DIALOG (results_dialog));
+ }
+
+ gtk_widget_destroy (GTK_WIDGET (results_dialog));
+}
+
+void
+id_mode_combo_box_changed_cb (GtkComboBox *combo_box, AlmanahImportDialog *self)
+{
+ gint new_mode;
+ AlmanahImportDialogPrivate *priv = self->priv;
+
+ new_mode = gtk_combo_box_get_active (combo_box);
+ if (new_mode == -1 || new_mode == (gint) priv->current_mode)
+ return;
+
+ priv->current_mode = new_mode;
+
+ /* Create the new widgets */
+ switch (priv->current_mode) {
+ case MODE_TEXT_FILES:
+ gtk_file_chooser_set_action (priv->file_chooser, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER);
+ gtk_label_set_text (priv->description_label, _("Select a folder containing text files, one per entry, with names in the "
+ "format 'yyyy-mm-dd', and no extension. Any and all such files will be "
+ "imported."));
+ break;
+ case MODE_DATABASE:
+ gtk_file_chooser_set_action (priv->file_chooser, GTK_FILE_CHOOSER_ACTION_OPEN);
+ gtk_label_set_text (priv->description_label, _("Select a database file created by Almanah Diary to import."));
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+void
+id_file_chooser_selection_changed_cb (GtkFileChooser *file_chooser, AlmanahImportDialog *self)
+{
+ GFile *current_file;
+
+ /* Change the sensitivity of the dialogue's buttons based on whether a file is selected */
+ current_file = gtk_file_chooser_get_file (file_chooser);
+ if (current_file == NULL) {
+ gtk_widget_set_sensitive (self->priv->import_button, FALSE);
+ } else {
+ gtk_widget_set_sensitive (self->priv->import_button, TRUE);
+ g_object_unref (current_file);
+ }
+}
+
+void
+id_file_chooser_file_activated_cb (GtkFileChooser *file_chooser, AlmanahImportDialog *self)
+{
+ /* Activate the dialogue's default button */
+ gtk_window_activate_default (GTK_WINDOW (self));
+}
+
+static gboolean filter_results_cb (GtkTreeModel *model, GtkTreeIter *iter, AlmanahImportResultsDialog *self);
+static void results_selection_changed_cb (GtkTreeSelection *tree_selection, GtkWidget *button);
+
+/* GtkBuilder callbacks */
+void ird_results_tree_view_row_activated_cb (GtkTreeView *self, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data);
+void ird_view_button_clicked_cb (GtkButton *button, AlmanahImportResultsDialog *self);
+void ird_view_combo_box_changed_cb (GtkComboBox *combo_box, AlmanahImportResultsDialog *self);
+
+struct _AlmanahImportResultsDialogPrivate {
+ GtkListStore *results_store;
+ GtkTreeSelection *results_selection;
+ GtkTreeModelFilter *filtered_results_store;
+ GtkComboBox *view_combo_box;
+ AlmanahImportStatus current_mode;
+};
+
+G_DEFINE_TYPE (AlmanahImportResultsDialog, almanah_import_results_dialog, GTK_TYPE_DIALOG)
+#define ALMANAH_IMPORT_RESULTS_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_IMPORT_RESULTS_DIALOG,\
+ AlmanahImportResultsDialogPrivate))
+
+static void
+almanah_import_results_dialog_class_init (AlmanahImportResultsDialogClass *klass)
+{
+ g_type_class_add_private (klass, sizeof (AlmanahImportResultsDialogPrivate));
+}
+
+static void
+almanah_import_results_dialog_init (AlmanahImportResultsDialog *self)
+{
+ self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_IMPORT_RESULTS_DIALOG, AlmanahImportResultsDialogPrivate);
+
+ g_signal_connect (self, "response", G_CALLBACK (response_cb), self);
+ g_signal_connect (self, "delete-event", G_CALLBACK (gtk_widget_hide_on_delete), self);
+ gtk_dialog_set_has_separator (GTK_DIALOG (self), FALSE);
+ gtk_window_set_resizable (GTK_WINDOW (self), TRUE);
+ gtk_window_set_title (GTK_WINDOW (self), _("Import Results"));
+ gtk_window_set_default_size (GTK_WINDOW (self), 600, 400);
+ gtk_window_set_modal (GTK_WINDOW (self), FALSE);
+}
+
+AlmanahImportResultsDialog *
+almanah_import_results_dialog_new (void)
+{
+ GtkBuilder *builder;
+ AlmanahImportResultsDialog *results_dialog;
+ AlmanahImportResultsDialogPrivate *priv;
+ GError *error = NULL;
+ const gchar *interface_filename = almanah_get_interface_filename ();
+ const gchar *object_names[] = {
+ "almanah_ird_view_store",
+ "almanah_ird_results_store",
+ "almanah_ird_filtered_results_store",
+ "almanah_import_results_dialog",
+ NULL
+ };
+
+ builder = gtk_builder_new ();
+
+ if (gtk_builder_add_objects_from_file (builder, interface_filename, (gchar**) object_names, &error) == FALSE) {
+ /* Show an error */
+ GtkWidget *dialog = gtk_message_dialog_new (NULL,
+ GTK_DIALOG_MODAL,
+ GTK_MESSAGE_ERROR,
+ GTK_BUTTONS_OK,
+ _("UI file \"%s\" could not be loaded"), interface_filename);
+ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", error->message);
+ gtk_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+
+ g_error_free (error);
+ g_object_unref (builder);
+
+ return NULL;
+ }
+
+ gtk_builder_set_translation_domain (builder, GETTEXT_PACKAGE);
+ results_dialog = ALMANAH_IMPORT_RESULTS_DIALOG (gtk_builder_get_object (builder, "almanah_import_results_dialog"));
+ gtk_builder_connect_signals (builder, results_dialog);
+
+ if (results_dialog == NULL) {
+ g_object_unref (builder);
+ return NULL;
+ }
+
+ priv = results_dialog->priv;
+
+ /* Grab our child widgets */
+ priv->results_store = GTK_LIST_STORE (gtk_builder_get_object (builder, "almanah_ird_results_store"));
+ priv->results_selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (gtk_builder_get_object (builder, "almanah_ird_results_tree_view")));
+ priv->filtered_results_store = GTK_TREE_MODEL_FILTER (gtk_builder_get_object (builder, "almanah_ird_filtered_results_store"));
+ priv->view_combo_box = GTK_COMBO_BOX (gtk_builder_get_object (builder, "almanah_ird_view_combo_box"));
+
+ g_signal_connect (priv->results_selection, "changed", G_CALLBACK (results_selection_changed_cb),
+ gtk_builder_get_object (builder, "almanah_ird_view_button"));
+
+ /* Set up the tree filter */
+ gtk_tree_model_filter_set_visible_func (priv->filtered_results_store, (GtkTreeModelFilterVisibleFunc) filter_results_cb, results_dialog, NULL);
+
+ /* Set up the combo box */
+ gtk_combo_box_set_active (priv->view_combo_box, ALMANAH_IMPORT_STATUS_MERGED);
+
+ g_object_unref (builder);
+
+ return results_dialog;
+}
+
+static gboolean
+filter_results_cb (GtkTreeModel *model, GtkTreeIter *iter, AlmanahImportResultsDialog *self)
+{
+ guint status;
+
+ /* Compare the current mode to the row's status column */
+ gtk_tree_model_get (model, iter, 4, &status, -1);
+
+ return (self->priv->current_mode == status) ? TRUE : FALSE;
+}
+
+static void
+results_selection_changed_cb (GtkTreeSelection *tree_selection, GtkWidget *button)
+{
+ gtk_widget_set_sensitive (button, gtk_tree_selection_count_selected_rows (tree_selection) == 0 ? FALSE : TRUE);
+}
+
+void
+almanah_import_results_dialog_add_result (AlmanahImportResultsDialog *self, GDate *date, AlmanahImportStatus status, const gchar *message)
+{
+ GtkTreeIter iter;
+ gchar formatted_date[100];
+
+ /* Translators: This is a strftime()-format string for the dates displayed in import results. */
+ g_date_strftime (formatted_date, sizeof (formatted_date), _("%A, %e %B %Y"), date);
+
+ gtk_list_store_append (self->priv->results_store, &iter);
+ gtk_list_store_set (self->priv->results_store, &iter,
+ 0, g_date_get_day (date),
+ 1, g_date_get_month (date),
+ 2, g_date_get_year (date),
+ 3, &formatted_date,
+ 4, status,
+ 5, message,
+ -1);
+}
+
+static void
+select_date (GtkTreeModel *model, GtkTreeIter *iter)
+{
+ guint day, month, year;
+ GDate date;
+
+ gtk_tree_model_get (model, iter,
+ 0, &day,
+ 1, &month,
+ 2, &year,
+ -1);
+
+ g_date_set_dmy (&date, day, month, year);
+ almanah_main_window_select_date (ALMANAH_MAIN_WINDOW (almanah->main_window), &date);
+}
+
+void
+ird_results_tree_view_row_activated_cb (GtkTreeView *self, GtkTreePath *path, GtkTreeViewColumn *column, gpointer user_data)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ model = gtk_tree_view_get_model (self);
+ gtk_tree_model_get_iter (model, &iter, path);
+ select_date (model, &iter);
+}
+
+void
+ird_view_button_clicked_cb (GtkButton *button, AlmanahImportResultsDialog *self)
+{
+ GtkTreeIter iter;
+ GtkTreeModel *model;
+
+ if (gtk_tree_selection_get_selected (self->priv->results_selection, &model, &iter) == TRUE)
+ select_date (model, &iter);
+}
+
+void
+ird_view_combo_box_changed_cb (GtkComboBox *combo_box, AlmanahImportResultsDialog *self)
+{
+ gint new_mode;
+ AlmanahImportResultsDialogPrivate *priv = self->priv;
+
+ new_mode = gtk_combo_box_get_active (combo_box);
+ if (new_mode == -1 || new_mode == (gint) priv->current_mode)
+ return;
+
+ priv->current_mode = new_mode;
+ gtk_tree_model_filter_refilter (self->priv->filtered_results_store);
+}
diff --git a/src/import-dialog.h b/src/import-dialog.h
new file mode 100644
index 0000000..5acd0ba
--- /dev/null
+++ b/src/import-dialog.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2009 <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_IMPORT_DIALOG_H
+#define ALMANAH_IMPORT_DIALOG_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+/* These must be kept in synchrony with the rows in almanah_ird_view_store in almanah.ui */
+typedef enum {
+ ALMANAH_IMPORT_STATUS_IMPORTED = 0,
+ ALMANAH_IMPORT_STATUS_MERGED = 1,
+ ALMANAH_IMPORT_STATUS_FAILED = 2
+} AlmanahImportStatus;
+
+#define ALMANAH_TYPE_IMPORT_DIALOG (almanah_import_dialog_get_type ())
+#define ALMANAH_IMPORT_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_IMPORT_DIALOG, AlmanahImportDialog))
+#define ALMANAH_IMPORT_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_IMPORT_DIALOG, AlmanahImportDialogClass))
+#define ALMANAH_IS_IMPORT_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_IMPORT_DIALOG))
+#define ALMANAH_IS_IMPORT_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_IMPORT_DIALOG))
+#define ALMANAH_IMPORT_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_IMPORT_DIALOG, AlmanahImportDialogClass))
+
+typedef struct _AlmanahImportDialogPrivate AlmanahImportDialogPrivate;
+
+typedef struct {
+ GtkDialog parent;
+ AlmanahImportDialogPrivate *priv;
+} AlmanahImportDialog;
+
+typedef struct {
+ GtkDialogClass parent;
+} AlmanahImportDialogClass;
+
+GType almanah_import_dialog_get_type (void) G_GNUC_CONST;
+
+AlmanahImportDialog *almanah_import_dialog_new (void) G_GNUC_WARN_UNUSED_RESULT;
+
+#define ALMANAH_TYPE_IMPORT_RESULTS_DIALOG (almanah_import_results_dialog_get_type ())
+#define ALMANAH_IMPORT_RESULTS_DIALOG(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_IMPORT_RESULTS_DIALOG, AlmanahImportResultsDialog))
+#define ALMANAH_IMPORT_RESULTS_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_IMPORT_RESULTS_DIALOG, AlmanahImportResultsDialogClass))
+#define ALMANAH_IS_IMPORT_RESULTS_DIALOG(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_IMPORT_RESULTS_DIALOG))
+#define ALMANAH_IS_IMPORT_RESULTS_DIALOG_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_IMPORT_RESULTS_DIALOG))
+#define ALMANAH_IMPORT_RESULTS_DIALOG_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_IMPORT_RESULTS_DIALOG, AlmanahImportResultsDialogClass))
+
+typedef struct _AlmanahImportResultsDialogPrivate AlmanahImportResultsDialogPrivate;
+
+typedef struct {
+ GtkDialog parent;
+ AlmanahImportResultsDialogPrivate *priv;
+} AlmanahImportResultsDialog;
+
+typedef struct {
+ GtkDialogClass parent;
+} AlmanahImportResultsDialogClass;
+
+GType almanah_import_results_dialog_get_type (void) G_GNUC_CONST;
+
+AlmanahImportResultsDialog *almanah_import_results_dialog_new (void) G_GNUC_WARN_UNUSED_RESULT;
+void almanah_import_results_dialog_add_result (AlmanahImportResultsDialog *self, GDate *date, AlmanahImportStatus status, const gchar *message);
+
+G_END_DECLS
+
+#endif /* !ALMANAH_IMPORT_DIALOG_H */
diff --git a/src/main-window.c b/src/main-window.c
index bd5ef31..eaa88af 100644
--- a/src/main-window.c
+++ b/src/main-window.c
@@ -2,7 +2,7 @@
/*
* Almanah
* Copyright (C) Philip Withnall 2008-2009 <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
@@ -40,6 +40,7 @@
#include "event.h"
#include "definition.h"
#include "definition-manager-window.h"
+#include "import-dialog.h"
static void almanah_main_window_dispose (GObject *object);
static void save_window_state (AlmanahMainWindow *self);
@@ -60,6 +61,7 @@ static void mw_definition_removed_cb (AlmanahStorageManager *storage_manager, co
/* GtkBuilder callbacks */
void mw_calendar_day_selected_cb (GtkCalendar *calendar, AlmanahMainWindow *main_window);
+void mw_import_activate_cb (GtkAction *action, AlmanahMainWindow *main_window);
void mw_page_setup_activate_cb (GtkAction *action, AlmanahMainWindow *main_window);
void mw_print_preview_activate_cb (GtkAction *action, AlmanahMainWindow *main_window);
void mw_print_activate_cb (GtkAction *action, AlmanahMainWindow *main_window);
@@ -522,7 +524,7 @@ remove_definition_from_current_entry (AlmanahMainWindow *self)
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);
@@ -696,6 +698,16 @@ mw_delete_event_cb (GtkWindow *window, gpointer user_data)
}
void
+mw_import_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
+{
+ if (almanah->import_dialog == NULL)
+ almanah->import_dialog = GTK_WIDGET (almanah_import_dialog_new ());
+
+ gtk_widget_show_all (almanah->import_dialog);
+ gtk_dialog_run (GTK_DIALOG (almanah->import_dialog));
+}
+
+void
mw_page_setup_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
{
almanah_print_page_setup ();
diff --git a/src/main.c b/src/main.c
index ac1db72..5c5e0de 100644
--- a/src/main.c
+++ b/src/main.c
@@ -73,6 +73,8 @@ almanah_quit (void)
gtk_widget_destroy (almanah->search_dialog);
if (almanah->date_entry_dialog != NULL)
gtk_widget_destroy (almanah->date_entry_dialog);
+ if (almanah->import_dialog != NULL)
+ gtk_widget_destroy (almanah->import_dialog);
#ifdef ENABLE_ENCRYPTION
if (almanah->preferences_dialog != NULL)
gtk_widget_destroy (almanah->preferences_dialog);
diff --git a/src/main.h b/src/main.h
index 08f9d50..8010a97 100644
--- a/src/main.h
+++ b/src/main.h
@@ -46,6 +46,7 @@ typedef struct {
GtkWidget *search_dialog;
GtkWidget *date_entry_dialog;
GtkWidget *definition_manager_window;
+ GtkWidget *import_dialog;
#ifdef ENABLE_ENCRYPTION
GtkWidget *preferences_dialog;
#endif /* ENABLE_ENCRYPTION */
diff --git a/src/storage-manager.c b/src/storage-manager.c
index 4b095dd..f69189f 100644
--- a/src/storage-manager.c
+++ b/src/storage-manager.c
@@ -35,6 +35,8 @@
#include "storage-manager.h"
#include "almanah-marshal.h"
+#define ENCRYPTED_SUFFIX ".encrypted"
+
static void almanah_storage_manager_finalize (GObject *object);
static void almanah_storage_manager_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
static void almanah_storage_manager_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
@@ -126,12 +128,25 @@ almanah_storage_manager_init (AlmanahStorageManager *self)
*
* Creates a new #AlmanahStorageManager, connected to the given database @filename.
*
+ * If @filename is for an encrypted database, it will automatically be changed to the canonical filename for the
+ * unencrypted database, even if that file doesn't exist, and even if Almanah was compiled without encryption support.
+ * Database filenames are always passed as the unencrypted filename.
+ *
* Return value: the new #AlmanahStorageManager
**/
AlmanahStorageManager *
almanah_storage_manager_new (const gchar *filename)
{
- return g_object_new (ALMANAH_TYPE_STORAGE_MANAGER, "filename", filename, NULL);
+ gchar *new_filename = NULL;
+ AlmanahStorageManager *sm;
+
+ if (g_str_has_suffix (filename, ENCRYPTED_SUFFIX) == TRUE)
+ filename = new_filename = g_strndup (filename, strlen (filename) - strlen (ENCRYPTED_SUFFIX));
+
+ sm = g_object_new (ALMANAH_TYPE_STORAGE_MANAGER, "filename", filename, NULL);
+ g_free (new_filename);
+
+ return sm;
}
static void
@@ -170,7 +185,7 @@ almanah_storage_manager_set_property (GObject *object, guint property_id, const
switch (property_id) {
case PROP_FILENAME:
priv->plain_filename = g_strdup (g_value_get_string (value));
- priv->filename = g_strjoin (NULL, priv->plain_filename, ".encrypted", NULL);
+ priv->filename = g_strjoin (NULL, priv->plain_filename, ENCRYPTED_SUFFIX, NULL);
break;
default:
/* We don't have any other property... */
@@ -898,6 +913,61 @@ almanah_storage_manager_search_entries (AlmanahStorageManager *self, const gchar
return result_count - 1;
}
+/**
+ * almanah_storage_manager_get_entries:
+ * @self: an #AlmanahStorageManager
+ *
+ * Returns a list of all #AlmanahEntry<!-- -->s in the database.
+ *
+ * Return value: a #GSList of #AlmanahEntry<!-- -->s, or %NULL; unref elements with g_object_unref(); free list with g_slist_free()
+ **/
+GSList *
+almanah_storage_manager_get_entries (AlmanahStorageManager *self)
+{
+ GSList *entries = NULL;
+ int result;
+ sqlite3_stmt *statement;
+
+ /* Just as with almanah_storage_manager_get_entry(), it's necessary to avoid our nice SQLite interface
+ * here. It's probably more efficient to avoid it anyway. */
+
+ /* Prepare the statement */
+ if (sqlite3_prepare_v2 (self->priv->connection,
+ "SELECT content, is_important, day, month, year FROM entries", -1,
+ &statement, NULL) != SQLITE_OK) {
+ return NULL;
+ }
+
+ /* Execute the statement */
+ while ((result = sqlite3_step (statement)) == SQLITE_ROW) {
+ GDate date;
+ AlmanahEntry *entry;
+
+ g_date_set_dmy (&date,
+ sqlite3_column_int (statement, 2),
+ sqlite3_column_int (statement, 3),
+ sqlite3_column_int (statement, 4));
+
+ /* Get the data */
+ entry = almanah_entry_new (&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);
+
+ entries = g_slist_prepend (entries, entry);
+ }
+
+ sqlite3_finalize (statement);
+
+ /* Check for errors */
+ if (result != SQLITE_DONE) {
+ g_slist_foreach (entries, (GFunc) g_object_unref, NULL);
+ g_slist_free (entries);
+ return NULL;
+ }
+
+ return g_slist_reverse (entries);
+}
+
/* NOTE: Free results with g_free. Return value is 0-based. */
gboolean *
almanah_storage_manager_get_month_marked_days (AlmanahStorageManager *self, GDateYear year, GDateMonth month, guint *num_days)
@@ -930,46 +1000,46 @@ almanah_storage_manager_get_month_marked_days (AlmanahStorageManager *self, GDat
* almanah_storage_manager_get_definitions:
* @self: an #AlmanahStorageManager
*
- * Returns a %NULL-terminated array of all the #AlmanahDefinitions in the
- * database. Each #AlmanahDefinition should be unreffed, and the array should
- * be freed with g_free().
- *
- * On error, an array with a single %NULL element will be returned. The array
- * should still be freed with g_free().
+ * Returns a list of all #AlmanahDefinition<!-- -->s in the database.
*
- * Return value: a %NULL-terminated array of #AlmanahDefinitions
+ * Return value: a #GSList of #AlmanahDefinition<!-- -->s, or %NULL; unref elements with g_object_unref(); free list with g_slist_free()
**/
-AlmanahDefinition **
+GSList *
almanah_storage_manager_get_definitions (AlmanahStorageManager *self)
{
- AlmanahQueryResults *results;
- AlmanahDefinition **definitions;
- gint i;
+ GSList *definitions = NULL;
+ int result;
+ sqlite3_stmt *statement;
- results = almanah_storage_manager_query (self, "SELECT definition_type, definition_value, definition_value2, definition_text FROM definitions", NULL);
+ /* It's more efficient to avoid our nice SQLite interface and do things manually. */
- if (results == NULL || results->rows == 0) {
- if (results != NULL)
- almanah_storage_manager_free_results (results);
-
- /* Return empty array */
- definitions = (AlmanahDefinition**) g_new (AlmanahDefinition*, 1);
- definitions[0] = NULL;
- return definitions;
+ /* Prepare the statement */
+ if (sqlite3_prepare_v2 (self->priv->connection,
+ "SELECT definition_type, definition_value, definition_value2, definition_text FROM definitions", -1,
+ &statement, NULL) != SQLITE_OK) {
+ return NULL;
}
- definitions = (AlmanahDefinition**) g_new (AlmanahDefinition*, results->rows + 1);
- for (i = 0; i < results->rows; i++) {
- 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]);
+ /* Execute the statement */
+ while ((result = sqlite3_step (statement)) == SQLITE_ROW) {
+ AlmanahDefinition *definition = almanah_definition_new (sqlite3_column_int (statement, 0));
+ almanah_definition_set_value (definition, (gchar*) sqlite3_column_text (statement, 1));
+ almanah_definition_set_value2 (definition, (gchar*) sqlite3_column_text (statement, 2));
+ almanah_definition_set_text (definition, (gchar*) sqlite3_column_text (statement, 3));
+
+ definitions = g_slist_prepend (definitions, definition);
}
- definitions[i] = NULL;
- almanah_storage_manager_free_results (results);
+ sqlite3_finalize (statement);
+
+ /* Check for errors */
+ if (result != SQLITE_DONE) {
+ g_slist_foreach (definitions, (GFunc) g_object_unref, NULL);
+ g_slist_free (definitions);
+ return NULL;
+ }
- return definitions;
+ return g_slist_reverse (definitions);
}
/* Note: this function is case-insensitive, unless the definition text contains Unicode characters
diff --git a/src/storage-manager.h b/src/storage-manager.h
index 3bb84e8..14a3cf9 100644
--- a/src/storage-manager.h
+++ b/src/storage-manager.h
@@ -82,10 +82,11 @@ gboolean almanah_storage_manager_entry_exists (AlmanahStorageManager *self, GDat
AlmanahEntry *almanah_storage_manager_get_entry (AlmanahStorageManager *self, GDate *date);
gboolean almanah_storage_manager_set_entry (AlmanahStorageManager *self, AlmanahEntry *entry);
gint almanah_storage_manager_search_entries (AlmanahStorageManager *self, const gchar *search_string, GDate *matches[]);
+GSList *almanah_storage_manager_get_entries (AlmanahStorageManager *self) G_GNUC_WARN_UNUSED_RESULT;
gboolean *almanah_storage_manager_get_month_marked_days (AlmanahStorageManager *self, GDateYear year, GDateMonth month, guint *num_days);
-AlmanahDefinition **almanah_storage_manager_get_definitions (AlmanahStorageManager *self);
+GSList *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);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]