[almanah] core: Add support for hyperlinks



commit 3bfc9106408009a745d0bcfa19719911a7bd96ad
Author: Philip Withnall <philip tecnocode co uk>
Date:   Sat Apr 2 22:16:27 2011 +0100

    core: Add support for hyperlinks
    
    These replace definitions. Closes: bgo#631835

 data/almanah.ui        |   76 +++++++++++++++++
 src/Makefile.am        |    2 +
 src/main-window.c      |  167 ++++++++++++++++++++++++++++++++++---
 src/uri-entry-dialog.c |  215 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/uri-entry-dialog.h |   57 +++++++++++++
 5 files changed, 505 insertions(+), 12 deletions(-)
---
diff --git a/data/almanah.ui b/data/almanah.ui
index 85d100f..2d7ff5f 100644
--- a/data/almanah.ui
+++ b/data/almanah.ui
@@ -147,6 +147,14 @@
 					<accelerator key="U" modifiers="GDK_CONTROL_MASK"/>
 				</child>
 				<child>
+					<object class="GtkToggleAction" id="almanah_ui_hyperlink">
+						<property name="name">format-hyperlink</property>
+						<property name="label" translatable="yes">Add/Remove _Hyperlink</property>
+						<property name="tooltip" translatable="yes">Add or remove a hyperlink from the currently selected text to a specified URI.</property>
+					</object>
+					<accelerator key="H" modifiers="GDK_CONTROL_MASK"/>
+				</child>
+				<child>
 					<object class="GtkAction" id="almanah_ui_insert_time">
 						<property name="name">format-insert-time</property>
 						<property name="label" translatable="yes">Insert _Time</property>
@@ -219,6 +227,7 @@
 					<menuitem action="almanah_ui_bold"/>
 					<menuitem action="almanah_ui_italic"/>
 					<menuitem action="almanah_ui_underline"/>
+					<menuitem action="almanah_ui_hyperlink"/>
 					<separator/>
 					<menuitem action="almanah_ui_insert_time"/>
 					<menuitem action="almanah_ui_important"/>
@@ -234,6 +243,7 @@
 				<toolitem action="almanah_ui_bold"/>
 				<toolitem action="almanah_ui_italic"/>
 				<toolitem action="almanah_ui_underline"/>
+				<toolitem action="almanah_ui_hyperlink"/>
 			</toolbar>
 		</ui>
 	</object>
@@ -694,6 +704,72 @@
 		</action-widgets>
 	</object>
 
+	<object class="AlmanahUriEntryDialog" id="almanah_uri_entry_dialog">
+		<property name="border-width">5</property>
+		<child internal-child="vbox">
+			<object class="GtkVBox" id="vbox11">
+				<property name="spacing">6</property>
+				<child>
+					<object class="GtkVBox" id="almanah_ued_vbox">
+						<property name="spacing">6</property>
+						<property name="border-width">5</property>
+						<child>
+							<object class="GtkEntry" id="almanah_ued_uri_entry">
+								<property name="activates-default">True</property>
+								<signal name="notify::text" handler="ued_uri_entry_notify_text_cb"/>
+								<accessibility>
+									<relation target="almanah_ued_description_label" type="described-by"/>
+								</accessibility>
+							</object>
+							<packing>
+								<property name="expand">False</property>
+							</packing>
+						</child>
+						<child>
+							<object class="GtkLabel" id="almanah_ued_description_label">
+								<property name="xalign">0</property>
+								<!-- Translators: Use two popular URIs from your locale, preferably one on the Internet and one local file. -->
+								<property name="label" translatable="yes">e.g. â??http://google.com/â;?? or â??file:///home/me/Photos/photo.jpgâ??.</property>
+								<accessibility>
+									<relation target="almanah_ued_uri_entry" type="description-for"/>
+								</accessibility>
+								<attributes>
+									<attribute name="scale" value="0.8"/>
+								</attributes>
+							</object>
+							<packing>
+								<property name="expand">False</property>
+							</packing>
+						</child>
+					</object>
+				</child>
+				<child internal-child="action_area">
+					<object class="GtkHButtonBox" id="almanah_ued_hbuttonbox">
+						<child>
+							<object class="GtkButton" id="almanah_ued_cancel_button">
+								<property name="use-stock">True</property>
+								<property name="label">gtk-cancel</property>
+							</object>
+						</child>
+						<child>
+							<object class="GtkButton" id="almanah_ued_ok_button">
+								<property name="use-stock">True</property>
+								<property name="label">gtk-ok</property>
+								<property name="can-default">True</property>
+								<property name="has-default">True</property>
+								<property name="sensitive">False</property>
+							</object>
+						</child>
+					</object>
+				</child>
+			</object>
+		</child>
+		<action-widgets>
+			<action-widget response="-6">almanah_ued_cancel_button</action-widget><!-- GTK_RESPONSE_CANCEL -->
+			<action-widget response="-5">almanah_ued_ok_button</action-widget><!-- GTK_RESPONSE_OK -->
+		</action-widgets>
+	</object>
+
 	<object class="GtkListStore" id="almanah_ied_mode_store">
 		<columns>
 			<column type="guint"/><!-- Type -->
diff --git a/src/Makefile.am b/src/Makefile.am
index 08ddde5..778da31 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -31,6 +31,8 @@ almanah_SOURCES = \
 	import-operation.h		\
 	export-operation.c		\
 	export-operation.h		\
+	uri-entry-dialog.c		\
+	uri-entry-dialog.h		\
 	widgets/calendar.c		\
 	widgets/calendar.h		\
 	widgets/hyperlink-tag.c		\
diff --git a/src/main-window.c b/src/main-window.c
index cb264e2..5c0c4d8 100644
--- a/src/main-window.c
+++ b/src/main-window.c
@@ -38,7 +38,9 @@
 #include "storage-manager.h"
 #include "event.h"
 #include "import-export-dialog.h"
+#include "uri-entry-dialog.h"
 #include "widgets/calendar.h"
+#include "widgets/hyperlink-tag.h"
 
 static void almanah_main_window_dispose (GObject *object);
 #ifdef ENABLE_SPELL_CHECKING
@@ -55,6 +57,7 @@ static void mw_entry_buffer_has_selection_cb (GObject *object, GParamSpec *pspec
 static void mw_bold_toggled_cb (GtkToggleAction *action, AlmanahMainWindow *main_window);
 static void mw_italic_toggled_cb (GtkToggleAction *action, AlmanahMainWindow *main_window);
 static void mw_underline_toggled_cb (GtkToggleAction *action, AlmanahMainWindow *main_window);
+static void mw_hyperlink_toggled_cb (GtkToggleAction *action, AlmanahMainWindow *main_window);
 static void mw_events_updated_cb (AlmanahEventManager *event_manager, AlmanahEventFactoryType type_id, AlmanahMainWindow *main_window);
 static void mw_events_selection_changed_cb (GtkTreeSelection *tree_selection, AlmanahMainWindow *main_window);
 static void mw_events_value_data_cb (GtkTreeViewColumn *column, GtkCellRenderer *renderer, GtkTreeModel *model, GtkTreeIter *iter, gpointer user_data);
@@ -94,6 +97,7 @@ struct _AlmanahMainWindowPrivate {
 	GtkToggleAction *bold_action;
 	GtkToggleAction *italic_action;
 	GtkToggleAction *underline_action;
+	GtkToggleAction *hyperlink_action;
 	GtkAction *cut_action;
 	GtkAction *copy_action;
 	GtkAction *delete_action;
@@ -222,6 +226,7 @@ almanah_main_window_new (void)
 	priv->bold_action = GTK_TOGGLE_ACTION (gtk_builder_get_object (builder, "almanah_ui_bold"));;
 	priv->italic_action = GTK_TOGGLE_ACTION (gtk_builder_get_object (builder, "almanah_ui_italic"));
 	priv->underline_action = GTK_TOGGLE_ACTION (gtk_builder_get_object (builder, "almanah_ui_underline"));
+	priv->hyperlink_action = GTK_TOGGLE_ACTION (gtk_builder_get_object (builder, "almanah_ui_hyperlink"));
 	priv->cut_action = GTK_ACTION (gtk_builder_get_object (builder, "almanah_ui_cut"));
 	priv->copy_action = GTK_ACTION (gtk_builder_get_object (builder, "almanah_ui_copy"));
 	priv->delete_action = GTK_ACTION (gtk_builder_get_object (builder, "almanah_ui_delete"));
@@ -252,6 +257,7 @@ almanah_main_window_new (void)
 	g_signal_connect (priv->bold_action, "toggled", G_CALLBACK (mw_bold_toggled_cb), main_window);
 	g_signal_connect (priv->italic_action, "toggled", G_CALLBACK (mw_italic_toggled_cb), main_window);
 	g_signal_connect (priv->underline_action, "toggled", G_CALLBACK (mw_underline_toggled_cb), main_window);
+	g_signal_connect (priv->hyperlink_action, "toggled", (GCallback) mw_hyperlink_toggled_cb, main_window);
 
 	/* Notification for event changes */
 	g_signal_connect (almanah->event_manager, "events-updated", G_CALLBACK (mw_events_updated_cb), main_window);
@@ -612,7 +618,7 @@ mw_entry_buffer_cursor_position_cb (GObject *object, GParamSpec *pspec, AlmanahM
 	AlmanahMainWindowPrivate *priv = main_window->priv;
 	GSList *_tag_list = NULL, *tag_list = NULL;
 	gboolean range_selected = FALSE;
-	gboolean bold_toggled = FALSE, italic_toggled = FALSE, underline_toggled = FALSE;
+	gboolean bold_toggled = FALSE, italic_toggled = FALSE, underline_toggled = FALSE, hyperlink_toggled = FALSE;
 
 	/* Ensure we don't overwrite current formatting options when characters are being typed.
 	 * (Execution of this function will be sandwiched between:
@@ -633,27 +639,37 @@ mw_entry_buffer_cursor_position_cb (GObject *object, GParamSpec *pspec, AlmanahM
 
 	tag_list = _tag_list;
 	while (tag_list != NULL) {
+		GtkTextTag *tag;
 		gchar *tag_name;
 		GtkToggleAction *action = NULL;
 
-		g_object_get (tag_list->data, "name", &tag_name, NULL);
+		tag = GTK_TEXT_TAG (tag_list->data);
+		g_object_get (tag, "name", &tag_name, NULL);
 
 		/* See if we can do anything with the tag */
-		if (strcmp (tag_name, "bold") == 0) {
-			action = priv->bold_action;
-			bold_toggled = TRUE;
-		} else if (strcmp (tag_name, "italic") == 0) {
-			action = priv->italic_action;
-			italic_toggled = TRUE;
-		} else if (strcmp (tag_name, "underline") == 0) {
-			action = priv->underline_action;
-			underline_toggled = TRUE;
+		if (tag_name != NULL) {
+			if (strcmp (tag_name, "bold") == 0) {
+				action = priv->bold_action;
+				bold_toggled = TRUE;
+			} else if (strcmp (tag_name, "italic") == 0) {
+				action = priv->italic_action;
+				italic_toggled = TRUE;
+			} else if (strcmp (tag_name, "underline") == 0) {
+				action = priv->underline_action;
+				underline_toggled = TRUE;
+			}
+		}
+
+		/* Hyperlink? */
+		if (ALMANAH_IS_HYPERLINK_TAG (tag)) {
+			action = priv->hyperlink_action;
+			hyperlink_toggled = TRUE;
 		}
 
 		if (action != NULL) {
 			/* Force the toggle status on the action */
 			gtk_toggle_action_set_active (action, TRUE);
-		} else if (strcmp (tag_name, "gtkspell-misspelled") != 0) {
+		} else if (tag_name == NULL || strcmp (tag_name, "gtkspell-misspelled") != 0) {
 			/* Print a warning about the unknown tag */
 			g_warning (_("Unknown or duplicate text tag \"%s\" in entry. Ignoring."), tag_name);
 		}
@@ -672,6 +688,10 @@ mw_entry_buffer_cursor_position_cb (GObject *object, GParamSpec *pspec, AlmanahM
 			gtk_toggle_action_set_active (priv->italic_action, FALSE);
 		if (underline_toggled == FALSE)
 			gtk_toggle_action_set_active (priv->underline_action, FALSE);
+
+		if (hyperlink_toggled == FALSE) {
+			gtk_toggle_action_set_active (priv->hyperlink_action, FALSE);
+		}
 	}
 
 	/* Unblock signals */
@@ -896,6 +916,129 @@ mw_underline_toggled_cb (GtkToggleAction *action, AlmanahMainWindow *main_window
 	apply_formatting (main_window, "underline", gtk_toggle_action_get_active (action));
 }
 
+static gboolean
+hyperlink_tag_event_cb (GtkTextTag *tag, GObject *object, GdkEvent *event, GtkTextIter *iter, gpointer user_data)
+{
+	AlmanahHyperlinkTag *self = ALMANAH_HYPERLINK_TAG (tag);
+
+	/* Open the hyperlink if it's control-clicked */
+	if (event->type == GDK_BUTTON_RELEASE && event->button.state & GDK_CONTROL_MASK) {
+		GtkWindow *main_window;
+		const gchar *uri;
+		GError *error = NULL;
+
+		main_window = GTK_WINDOW (almanah->main_window);
+		uri = almanah_hyperlink_tag_get_uri (self);
+
+		/* Attempt to open the URI */
+		gtk_show_uri (gtk_widget_get_screen (GTK_WIDGET (main_window)), uri, gdk_event_get_time (event), &error);
+
+		if (error != NULL) {
+			/* Error */
+			GtkWidget *dialog = gtk_message_dialog_new (main_window,
+			                                            GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK,
+			                                            _("Error opening URI"));
+			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);
+		}
+
+		return TRUE;
+	}
+
+	return FALSE;
+}
+
+static void
+mw_hyperlink_toggled_cb (GtkToggleAction *action, AlmanahMainWindow *self)
+{
+	AlmanahMainWindowPrivate *priv = self->priv;
+	GtkTextIter start, end;
+
+	/* Make sure we don't muck up the formatting when the actions are having
+	 * their sensitivity set by the code. */
+	if (priv->updating_formatting == TRUE)
+		return;
+
+	gtk_text_buffer_get_selection_bounds (priv->entry_buffer, &start, &end);
+
+	if (gtk_toggle_action_get_active (action) == TRUE) {
+		/* Add a new hyperlink on the selected text */
+		AlmanahUriEntryDialog *uri_entry_dialog;
+
+		/* Get a URI from the user */
+		uri_entry_dialog = almanah_uri_entry_dialog_new ();
+		gtk_window_set_transient_for (GTK_WINDOW (uri_entry_dialog), GTK_WINDOW (self));
+		gtk_widget_show_all (GTK_WIDGET (uri_entry_dialog));
+
+		if (almanah_uri_entry_dialog_run (uri_entry_dialog) == TRUE) {
+			GtkTextTag *tag;
+			GtkTextTagTable *table;
+
+			/* Create and apply a new anonymous tag */
+			tag = GTK_TEXT_TAG (almanah_hyperlink_tag_new (almanah_uri_entry_dialog_get_uri (uri_entry_dialog)));
+
+			table = gtk_text_buffer_get_tag_table (priv->entry_buffer);
+			gtk_text_tag_table_add (table, tag);
+
+			gtk_text_buffer_apply_tag (priv->entry_buffer, tag, &start, &end);
+
+			/* Connect up events */
+			g_signal_connect (tag, "event", (GCallback) hyperlink_tag_event_cb, NULL);
+
+			/* The text tag table keeps a reference */
+			g_object_unref (tag);
+		}
+
+		gtk_widget_destroy (GTK_WIDGET (uri_entry_dialog));
+	} else {
+		GtkTextIter iter = start;
+		GSList *tags, *i;
+
+		/* Remove all hyperlinks which are active at the start iter. This covers the case of hyperlinks which span more than the
+		 * selected text (i.e. begin before the start iter and end after the end iter). All other spanning hyperlinks will have an end point
+		 * inside the selected text, and will be caught below. */
+		tags  = gtk_text_iter_get_tags (&start);
+
+		for (i = tags; i != NULL; i = i->next) {
+			GtkTextTag *tag = GTK_TEXT_TAG (i->data);
+
+			if (ALMANAH_IS_HYPERLINK_TAG (tag)) {
+				GtkTextIter tag_start = start, tag_end = start;
+
+				if (gtk_text_iter_backward_to_tag_toggle (&tag_start, tag) == TRUE &&
+				    gtk_text_iter_forward_to_tag_toggle (&tag_end, tag) == TRUE) {
+					gtk_text_buffer_remove_tag (priv->entry_buffer, tag, &tag_start, &tag_end);
+				}
+			}
+		}
+
+		g_slist_free (tags);
+
+		/* Remove all hyperlinks which span the selected text and have an end point inside the selected text */
+		while (gtk_text_iter_forward_to_tag_toggle (&iter, NULL) == TRUE) {
+			/* Break once we've passed the end of the selected text range */
+			if (gtk_text_iter_compare (&iter, &end) > 0) {
+				break;
+			}
+
+			tags = gtk_text_iter_get_toggled_tags (&iter, FALSE);
+
+			for (i = tags; i != NULL; i = i->next) {
+				if (ALMANAH_IS_HYPERLINK_TAG (i->data)) {
+					gtk_text_buffer_remove_tag (priv->entry_buffer, GTK_TEXT_TAG (i->data), &start, &end);
+				}
+			}
+
+			g_slist_free (tags);
+		}
+	}
+
+	gtk_text_buffer_set_modified (priv->entry_buffer, TRUE);
+}
+
 void
 mw_about_activate_cb (GtkAction *action, AlmanahMainWindow *main_window)
 {
diff --git a/src/uri-entry-dialog.c b/src/uri-entry-dialog.c
new file mode 100644
index 0000000..fe61f7b
--- /dev/null
+++ b/src/uri-entry-dialog.c
@@ -0,0 +1,215 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2011 <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 "uri-entry-dialog.h"
+#include "main.h"
+#include "interface.h"
+
+static void almanah_uri_entry_dialog_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
+static void almanah_uri_entry_dialog_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
+
+/* GtkBuilder callbacks */
+G_MODULE_EXPORT void ued_uri_entry_notify_text_cb (GObject *gobject, GParamSpec *pspec, AlmanahUriEntryDialog *self);
+
+struct _AlmanahUriEntryDialogPrivate {
+	gchar *uri;
+	GtkWidget *ok_button;
+	GtkEntry *uri_entry;
+};
+
+enum {
+	PROP_URI = 1
+};
+
+G_DEFINE_TYPE (AlmanahUriEntryDialog, almanah_uri_entry_dialog, GTK_TYPE_DIALOG)
+#define ALMANAH_URI_ENTRY_DIALOG_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ALMANAH_TYPE_URI_ENTRY_DIALOG, AlmanahUriEntryDialogPrivate))
+
+static void
+almanah_uri_entry_dialog_class_init (AlmanahUriEntryDialogClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+	g_type_class_add_private (klass, sizeof (AlmanahUriEntryDialogPrivate));
+
+	gobject_class->set_property = almanah_uri_entry_dialog_set_property;
+	gobject_class->get_property = almanah_uri_entry_dialog_get_property;
+
+	g_object_class_install_property (gobject_class, PROP_URI,
+	                                 g_param_spec_string ("uri",
+	                                                      "URI", "The current URI entered in the dialog.",
+	                                                      NULL,
+	                                                      G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+}
+
+static void
+almanah_uri_entry_dialog_init (AlmanahUriEntryDialog *self)
+{
+	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_URI_ENTRY_DIALOG, AlmanahUriEntryDialogPrivate);
+
+	g_signal_connect (self, "response", (GCallback) gtk_widget_hide, self);
+	gtk_window_set_resizable (GTK_WINDOW (self), FALSE);
+	gtk_window_set_title (GTK_WINDOW (self), _("Enter URI"));
+}
+
+static void
+almanah_uri_entry_dialog_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+{
+	AlmanahUriEntryDialogPrivate *priv = ALMANAH_URI_ENTRY_DIALOG (object)->priv;
+
+	switch (property_id) {
+		case PROP_URI:
+			g_value_set_string (value, priv->uri);
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+static void
+almanah_uri_entry_dialog_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+{
+	AlmanahUriEntryDialog *self = ALMANAH_URI_ENTRY_DIALOG (object);
+
+	switch (property_id) {
+		case PROP_URI:
+			almanah_uri_entry_dialog_set_uri (self, g_value_get_string (value));
+			break;
+		default:
+			/* We don't have any other property... */
+			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+			break;
+	}
+}
+
+AlmanahUriEntryDialog *
+almanah_uri_entry_dialog_new (void)
+{
+	GtkBuilder *builder;
+	AlmanahUriEntryDialog *uri_entry_dialog;
+	AlmanahUriEntryDialogPrivate *priv;
+	GError *error = NULL;
+	const gchar *interface_filename = almanah_get_interface_filename ();
+	const gchar *object_names[] = {
+		"almanah_uri_entry_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);
+	uri_entry_dialog = ALMANAH_URI_ENTRY_DIALOG (gtk_builder_get_object (builder, "almanah_uri_entry_dialog"));
+	gtk_builder_connect_signals (builder, uri_entry_dialog);
+
+	if (uri_entry_dialog == NULL) {
+		g_object_unref (builder);
+		return NULL;
+	}
+
+	priv = uri_entry_dialog->priv;
+
+	/* Grab widgets */
+	priv->ok_button = GTK_WIDGET (gtk_builder_get_object (builder, "almanah_ued_ok_button"));
+	priv->uri_entry = GTK_ENTRY (gtk_builder_get_object (builder, "almanah_ued_uri_entry"));
+
+	g_object_unref (builder);
+
+	return uri_entry_dialog;
+}
+
+gboolean
+almanah_uri_entry_dialog_run (AlmanahUriEntryDialog *self)
+{
+	/* Reset the URI entry and consequently the OK button */
+	gtk_entry_set_text (self->priv->uri_entry, "");
+	g_free (self->priv->uri);
+	self->priv->uri = NULL;
+
+	return (gtk_dialog_run (GTK_DIALOG (self)) == GTK_RESPONSE_OK) ? TRUE : FALSE;
+}
+
+static gboolean
+is_uri_valid (const gchar *uri)
+{
+	gchar *tmp;
+
+	/* We assume that g_uri_parse_scheme() will fail if the URI is invalid. */
+	tmp = g_uri_parse_scheme (uri);
+	g_free (tmp);
+
+	return (tmp != NULL) ? TRUE : FALSE;
+}
+
+void
+ued_uri_entry_notify_text_cb (GObject *gobject, GParamSpec *param_spec, AlmanahUriEntryDialog *self)
+{
+	AlmanahUriEntryDialogPrivate *priv = self->priv;
+
+	/* Enable/Disable the OK button based on whether the current URI is valid. */
+	if (is_uri_valid (gtk_entry_get_text (priv->uri_entry)) == TRUE) {
+		/* The URI was parsed successfully; update priv->uri and enable the OK button */
+		priv->uri = g_strdup (gtk_entry_get_text (priv->uri_entry));
+		gtk_widget_set_sensitive (priv->ok_button, TRUE);
+	} else {
+		/* Failure due to the URI entered being invalid; disable the OK button */
+		gtk_widget_set_sensitive (priv->ok_button, FALSE);
+	}
+}
+
+const gchar *
+almanah_uri_entry_dialog_get_uri (AlmanahUriEntryDialog *self)
+{
+	g_return_val_if_fail (ALMANAH_IS_URI_ENTRY_DIALOG (self), NULL);
+	return self->priv->uri;
+}
+
+void
+almanah_uri_entry_dialog_set_uri (AlmanahUriEntryDialog *self, const gchar *uri)
+{
+	g_return_if_fail (ALMANAH_IS_URI_ENTRY_DIALOG (self));
+	g_return_if_fail (uri == NULL || is_uri_valid (uri) == TRUE);
+
+	g_free (self->priv->uri);
+	self->priv->uri = g_strdup (uri);
+
+	g_object_notify (G_OBJECT (self), "uri");
+}
diff --git a/src/uri-entry-dialog.h b/src/uri-entry-dialog.h
new file mode 100644
index 0000000..c59165b
--- /dev/null
+++ b/src/uri-entry-dialog.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/*
+ * Almanah
+ * Copyright (C) Philip Withnall 2011 <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_URI_ENTRY_DIALOG_H
+#define ALMANAH_URI_ENTRY_DIALOG_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define ALMANAH_TYPE_URI_ENTRY_DIALOG		(almanah_uri_entry_dialog_get_type ())
+#define ALMANAH_URI_ENTRY_DIALOG(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), ALMANAH_TYPE_URI_ENTRY_DIALOG, AlmanahUriEntryDialog))
+#define ALMANAH_URI_ENTRY_DIALOG_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), ALMANAH_TYPE_URI_ENTRY_DIALOG, AlmanahUriEntryDialogClass))
+#define ALMANAH_IS_URI_ENTRY_DIALOG(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), ALMANAH_TYPE_URI_ENTRY_DIALOG))
+#define ALMANAH_IS_URI_ENTRY_DIALOG_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), ALMANAH_TYPE_URI_ENTRY_DIALOG))
+#define ALMANAH_URI_ENTRY_DIALOG_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), ALMANAH_TYPE_URI_ENTRY_DIALOG, AlmanahUriEntryDialogClass))
+
+typedef struct _AlmanahUriEntryDialogPrivate	AlmanahUriEntryDialogPrivate;
+
+typedef struct {
+	GtkDialog parent;
+	AlmanahUriEntryDialogPrivate *priv;
+} AlmanahUriEntryDialog;
+
+typedef struct {
+	GtkDialogClass parent;
+} AlmanahUriEntryDialogClass;
+
+GType almanah_uri_entry_dialog_get_type (void) G_GNUC_CONST;
+
+AlmanahUriEntryDialog *almanah_uri_entry_dialog_new (void) G_GNUC_WARN_UNUSED_RESULT G_GNUC_MALLOC;
+gboolean almanah_uri_entry_dialog_run (AlmanahUriEntryDialog *self);
+
+const gchar *almanah_uri_entry_dialog_get_uri (AlmanahUriEntryDialog *self) G_GNUC_PURE;
+void almanah_uri_entry_dialog_set_uri (AlmanahUriEntryDialog *self, const gchar *uri);
+
+G_END_DECLS
+
+#endif /* !ALMANAH_URI_ENTRY_DIALOG_H */



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