[almanah/tagsupport: 2/2] Bug 684412 - Add support for tagging diary entries



commit f58538ddcc37fb8b7e624892d743098ace8dc7e7
Author: Ãlvaro PeÃa <alvaropg gmail com>
Date:   Fri Jan 25 14:25:57 2013 +0100

    Bug 684412 - Add support for tagging diary entries
    
    The "remove" button in the tag turns highlighted when the user moves the pointer on.
    
    The "remove" button now removes the tag from the entry, in the tags area and in the storage.
    
    The user can't append a tag in an Entry if already has been added.

 src/storage-manager.c         |   58 ++++++++++++++++-
 src/storage-manager.h         |    1 +
 src/widgets/entry-tags-area.c |   42 +++++++++---
 src/widgets/tag.c             |  149 ++++++++++++++++++++++++++++++++++++++---
 src/widgets/tag.h             |    5 +-
 5 files changed, 232 insertions(+), 23 deletions(-)
---
diff --git a/src/storage-manager.c b/src/storage-manager.c
index 2708a5d..d66f187 100644
--- a/src/storage-manager.c
+++ b/src/storage-manager.c
@@ -1430,7 +1430,7 @@ almanah_storage_manager_entry_get_tags (AlmanahStorageManager *self, AlmanahEntr
 
 	almanah_entry_get_date (entry, &date);
 	if (g_date_valid (&date) != TRUE) {
-		g_debug ("Invalid entry date.");
+		g_debug ("Invalid entry date");
 		return NULL;
 	}
 
@@ -1461,6 +1461,62 @@ almanah_storage_manager_entry_get_tags (AlmanahStorageManager *self, AlmanahEntr
 }
 
 /**
+ * almanah_storage_manager_entry_check_tag:
+ * @self: an #AlmanahStorageManager
+ * @entry: an #AlmanahEntry to check into it
+ * @tag: the tag to be checked
+ *
+ * Check if a tag has been added to an entry
+ *
+ * Return value: TRUE if the tag already added to the entry, FALSE otherwise
+ */
+gboolean
+almanah_storage_manager_entry_check_tag (AlmanahStorageManager *self, AlmanahEntry *entry, const gchar *tag)
+{
+	gboolean result, q_result;
+	sqlite3_stmt *statement;
+	GDate date;
+
+	g_return_val_if_fail (ALMANAH_IS_STORAGE_MANAGER (self), FALSE);
+	g_return_val_if_fail (ALMANAH_IS_ENTRY (entry), FALSE);
+	g_return_val_if_fail (g_utf8_strlen (tag, 1) == 1, FALSE);
+
+	result = FALSE;
+
+	almanah_entry_get_date (entry, &date);
+	if (g_date_valid (&date) != TRUE) {
+		g_debug ("Invalid entry date");
+		return FALSE;
+	}
+
+	if (sqlite3_prepare_v2 (self->priv->connection, 
+				"SELECT count(1) FROM entry_tag WHERE year = ? AND month = ? AND day = ? AND tag = ?",
+				-1, &statement, NULL) != SQLITE_OK) {
+		g_debug ("Can't prepare statement");
+		return FALSE;
+	}
+
+	sqlite3_bind_int (statement, 1, g_date_get_year (&date));
+	sqlite3_bind_int (statement, 2, g_date_get_month (&date));
+	sqlite3_bind_int (statement, 3, g_date_get_day (&date));
+	sqlite3_bind_text (statement, 4, tag, -1, SQLITE_STATIC);
+
+	if ((q_result  = sqlite3_step (statement)) == SQLITE_ROW) {
+		if (sqlite3_column_int (statement, 0) > 0)
+			result = TRUE;
+	}
+
+	if (q_result != SQLITE_DONE) {
+		g_debug ("Error quering for a tag from database: %s", sqlite3_errmsg (self->priv->connection));
+	}
+
+	sqlite3_finalize (statement);
+
+	return result;
+}
+
+
+/**
  * almanah_storage_manager_get_tags:
  * @self: an #AlmanahStorageManager
  *
diff --git a/src/storage-manager.h b/src/storage-manager.h
index 0c47097..51aad3d 100644
--- a/src/storage-manager.h
+++ b/src/storage-manager.h
@@ -98,6 +98,7 @@ const gchar *almanah_storage_manager_get_filename (AlmanahStorageManager *self,
 gboolean almanah_storage_manager_entry_add_tag (AlmanahStorageManager *self, AlmanahEntry *entry, const gchar *tag);
 gboolean almanah_storage_manager_entry_remove_tag (AlmanahStorageManager *self, AlmanahEntry *entry, const gchar *tag);
 GList *almanah_storage_manager_entry_get_tags (AlmanahStorageManager *self, AlmanahEntry *entry);
+gboolean almanah_storage_manager_entry_check_tag (AlmanahStorageManager *self, AlmanahEntry *entry, const gchar *tag);
 GList *almanah_storage_manager_get_tags (AlmanahStorageManager *self);
 
 G_END_DECLS
diff --git a/src/widgets/entry-tags-area.c b/src/widgets/entry-tags-area.c
index c73e39d..7dc01fb 100644
--- a/src/widgets/entry-tags-area.c
+++ b/src/widgets/entry-tags-area.c
@@ -41,11 +41,13 @@ static void almanah_entry_tags_area_finalize          (GObject *object);
 static void almanah_entry_tags_area_load_tags         (AlmanahEntryTagsArea *self);
 static void almanah_entry_tags_area_update            (AlmanahEntryTagsArea *self);
 static gint almanah_entry_tags_area_draw              (GtkWidget *widget, cairo_t *cr);
+static void almanah_entry_tags_area_add_tag                (AlmanahEntryTagsArea *self, const gchar *tag);
 
 /* Signals */
 void tag_entry_activate_cb              (GtkEntry *entry, AlmanahEntryTagsArea *self);
 void entry_tags_area_remove_foreach_cb  (GtkWidget *tag_widget, AlmanahEntryTagsArea *self);
 void storage_manager_entry_tag_added_cb (AlmanahEntry *entry, gchar *tag,  AlmanahEntryTagsArea *self);
+static void tag_remove                  (AlmanahTag *tag_widget, AlmanahEntryTagsArea *self);
 
 G_DEFINE_TYPE (AlmanahEntryTagsArea, almanah_entry_tags_area, GTK_TYPE_GRID)
 
@@ -152,9 +154,7 @@ almanah_entry_tags_area_load_tags (AlmanahEntryTagsArea *self)
 
 	tags = almanah_storage_manager_entry_get_tags (self->priv->storage_manager, self->priv->entry);
 	while (tags) {
-		GtkWidget *tag_widget = almanah_tag_new (tags->data);
-		gtk_container_add (GTK_CONTAINER (self), tag_widget);
-		self->priv->tags_number++;
+		almanah_entry_tags_area_add_tag (self, (const gchar*) tags->data);
 
 		g_free (tags->data);
 		tags = g_list_next (tags);
@@ -185,6 +185,19 @@ almanah_entry_tags_area_draw (GtkWidget *widget, cairo_t *cr)
 	return GTK_WIDGET_CLASS (almanah_entry_tags_area_parent_class)->draw (widget, cr);
 }
 
+static void
+almanah_entry_tags_area_add_tag (AlmanahEntryTagsArea *self, const gchar *tag)
+{
+	GtkWidget *tag_widget;
+
+	tag_widget = almanah_tag_new (tag);
+	gtk_container_add (GTK_CONTAINER (self), tag_widget);
+	gtk_widget_show (tag_widget);
+	g_signal_connect (tag_widget, "remove", G_CALLBACK (tag_remove), self);
+
+	self->priv->tags_number++;
+}
+
 void
 tag_entry_activate_cb (GtkEntry *entry, AlmanahEntryTagsArea *self)
 {
@@ -193,15 +206,13 @@ tag_entry_activate_cb (GtkEntry *entry, AlmanahEntryTagsArea *self)
 
 	tag = g_strdup (gtk_entry_get_text (entry));
 	gtk_entry_set_text (entry, "");
-	if (almanah_storage_manager_entry_add_tag (self->priv->storage_manager, self->priv->entry, tag)) {
-		GtkWidget *tag_widget = almanah_tag_new (tag);
-		gtk_container_add (GTK_CONTAINER (self), tag_widget);
-		self->priv->tags_number++;
-		gtk_widget_show (tag_widget);
+	/* Check fist if the tag as already added to the entry */
+	if (almanah_storage_manager_entry_check_tag (self->priv->storage_manager, self->priv->entry, tag) == FALSE) {
+		if (almanah_storage_manager_entry_add_tag (self->priv->storage_manager, self->priv->entry, tag)) {
+			almanah_entry_tags_area_add_tag (self, (const gchar*) tag);
+		}
 	}
 	g_free (tag);
-
-	/* @TODO: Return the focus to the GtkTextView */
 }
 
 void
@@ -241,6 +252,17 @@ storage_manager_entry_tag_added_cb (AlmanahEntry *entry, gchar *tag, AlmanahEntr
 
 }
 
+static void
+tag_remove (AlmanahTag *tag_widget, AlmanahEntryTagsArea *self)
+{
+	if (almanah_storage_manager_entry_remove_tag (self->priv->storage_manager, self->priv->entry, almanah_tag_get_tag (tag_widget))) {
+		gtk_widget_destroy (GTK_WIDGET (tag_widget));
+		self->priv->tags_number--;
+	} else {
+		g_debug ("Can't remove the tag");
+	}
+}
+
 void
 almanah_entry_tags_area_set_storage_manager (AlmanahEntryTagsArea *entry_tags_area, AlmanahStorageManager *storage_manager)
 {
diff --git a/src/widgets/tag.c b/src/widgets/tag.c
index 8340f1e..95be00a 100644
--- a/src/widgets/tag.c
+++ b/src/widgets/tag.c
@@ -17,6 +17,7 @@
  * along with Almanah.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <glib/gi18n.h>
 #include <pango/pangocairo.h>
 #include <math.h>
 
@@ -38,18 +39,39 @@ enum {
 struct _AlmanahTagPrivate {
         gchar *tag;
         PangoLayout *layout;
+
+	/* Tag colors */
 	GdkRGBA text_color;
 	GdkRGBA strock_color;
 	GdkRGBA fill_a_color;
 	GdkRGBA fill_b_color;
+
+	/* Some coordinates */
+	gint close_x;
+	gint close_y;
+
+	/* The close button state */
+	gboolean close_highlighted;
+	gboolean close_pressed;
+};
+
+enum {
+	SIGNAL_REMOVE,
+	LAST_SIGNAL
 };
 
+static guint tag_signals[LAST_SIGNAL] = { 0, };
+
 static void almanah_tag_get_property         (GObject *object, guint property_id, GValue *value, GParamSpec *pspec);
 static void almanah_tag_set_property         (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec);
 void        almanah_tag_ensure_layout        (AlmanahTag *self);
 void        almanah_tag_get_preferred_width  (GtkWidget *widget, gint *minimum_width, gint *natural_width);
 void        almanah_tag_get_preferred_height (GtkWidget *widget, gint *minimum_height, gint *natural_height);
+gboolean    almanah_tag_motion_notify_event  (GtkWidget *widget, GdkEventMotion *event);
+gboolean    almanah_tag_button_press_event   (GtkWidget *widget, GdkEventButton *event);
+gboolean    almanah_tag_button_release_event (GtkWidget *widget, GdkEventButton *event);
 gboolean    almanah_tag_draw                 (GtkWidget *widget, cairo_t *cr, gpointer data);
+gboolean    almanah_tag_query_tooltip        (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip);
 
 G_DEFINE_TYPE (AlmanahTag, almanah_tag, GTK_TYPE_DRAWING_AREA)
 
@@ -66,11 +88,22 @@ almanah_tag_class_init (AlmanahTagClass *klass)
 
 	widget_class->get_preferred_width = almanah_tag_get_preferred_width;
 	widget_class->get_preferred_height = almanah_tag_get_preferred_height;
+	widget_class->motion_notify_event = almanah_tag_motion_notify_event;
+	widget_class->button_release_event = almanah_tag_button_release_event;
+	widget_class->button_press_event = almanah_tag_button_press_event;
+	widget_class->query_tooltip = almanah_tag_query_tooltip;
 
         g_object_class_install_property (gobject_class, PROP_TAG,
                                          g_param_spec_string ("tag",
                                                               "Tag", "The tag name.",
                                                               NULL, G_PARAM_READWRITE));
+
+	tag_signals[SIGNAL_REMOVE] = g_signal_new ("remove",
+						   G_TYPE_FROM_CLASS (klass),
+						   G_SIGNAL_RUN_LAST,
+						   0, NULL, NULL,
+						   g_cclosure_marshal_VOID__VOID,
+						   G_TYPE_NONE, 0);
 }
 
 static void
@@ -79,10 +112,18 @@ almanah_tag_init (AlmanahTag *self)
 	self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, ALMANAH_TYPE_TAG, AlmanahTagPrivate);
         g_signal_connect (G_OBJECT (self), "draw", G_CALLBACK (almanah_tag_draw), NULL);
 
+	gtk_widget_add_events (GTK_WIDGET  (self), GDK_POINTER_MOTION_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK);
+
 	gdk_rgba_parse (&self->priv->text_color, "#936835");
 	gdk_rgba_parse (&self->priv->strock_color, "#ECB447");
 	gdk_rgba_parse (&self->priv->fill_a_color, "#FFDB73");
 	gdk_rgba_parse (&self->priv->fill_b_color, "#FCBC4E");
+
+	gtk_widget_set_has_tooltip (GTK_WIDGET (self), TRUE);
+	gtk_widget_set_tooltip_text (GTK_WIDGET (self), _("Remove tag"));
+
+	self->priv->close_highlighted = FALSE;
+	self->priv->close_pressed = FALSE;
 }
 
 static void
@@ -164,6 +205,65 @@ almanah_tag_get_preferred_width (GtkWidget *widget, gint *minimum_width, gint *n
 }
 
 gboolean
+almanah_tag_motion_notify_event  (GtkWidget *widget, GdkEventMotion *event)
+{
+	gint close_x = ALMANAH_TAG (widget)->priv->close_x;
+	gint close_y = ALMANAH_TAG (widget)->priv->close_y;
+	gboolean close_highlighted;
+
+	/* Close button */
+	if (event->x >= close_x && event->x <= close_x + CLOSE_BUTTON
+	    && event->y >= close_y && event->y <= close_y + CLOSE_BUTTON) {
+		close_highlighted = TRUE;
+	} else {
+		close_highlighted = FALSE;
+	}
+
+	if (ALMANAH_TAG (widget)->priv->close_highlighted != close_highlighted) {
+		ALMANAH_TAG (widget)->priv->close_highlighted = close_highlighted;
+		gtk_widget_queue_draw (widget);
+	}
+
+	return FALSE;
+}
+
+gboolean
+almanah_tag_button_press_event (GtkWidget *widget, GdkEventButton *event)
+{
+	AlmanahTagPrivate *priv = ALMANAH_TAG (widget)->priv;
+	gint close_x = priv->close_x;
+	gint close_y = priv->close_y;
+
+	if (event->x >= close_x && event->x <= close_x + CLOSE_BUTTON
+	    && event->y >= close_y && event->y <= close_y + CLOSE_BUTTON
+	    && event->button == 1) {
+		priv->close_pressed = TRUE;
+	} else {
+		priv->close_pressed = FALSE;
+	}
+
+	return FALSE;
+}
+
+gboolean
+almanah_tag_button_release_event (GtkWidget *widget, GdkEventButton *event)
+{
+	AlmanahTagPrivate *priv = ALMANAH_TAG (widget)->priv;
+	gint close_x = priv->close_x;
+	gint close_y = priv->close_y;
+
+	if (event->x >= close_x && event->x <= close_x + CLOSE_BUTTON
+	    && event->y >= close_y && event->y <= close_y + CLOSE_BUTTON
+	    && priv->close_pressed
+	    && event->button == 1) {
+		g_signal_emit (ALMANAH_TAG (widget), tag_signals[SIGNAL_REMOVE], 0);
+	}
+	priv->close_pressed = FALSE;
+
+	return FALSE;
+}
+
+gboolean
 almanah_tag_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
 {
         AlmanahTagPrivate *priv = ALMANAH_TAG (widget)->priv;
@@ -182,7 +282,7 @@ almanah_tag_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
 	middle_height = height / 2;
 	middle_padding_left = PADDING_LEFT / 2;
 
-	/* The tag must the vertical centered */
+	/* The tag must be vertical centered */
 	allocated_height = gtk_widget_get_allocated_height (widget);
 	y_origin = (allocated_height / 2) - middle_height;
 
@@ -240,35 +340,55 @@ almanah_tag_draw (GtkWidget *widget, cairo_t *cr, gpointer data)
 	pango_cairo_show_layout (cr, priv->layout);
 
 	/* Remove button, it's a "x" */
+	priv->close_x = PADDING_LEFT + text_width + CLOSE_BUTTON_SPACING;
+	priv->close_y = y_origin + middle_height - (CLOSE_BUTTON / 2);
 
 	/* First the shadow */
 	cairo_set_source_rgba (cr, 1, 1, 1, 0.5);
 	/* Line from left up */
-	cairo_move_to (cr, PADDING_LEFT + text_width + CLOSE_BUTTON_SPACING, y_origin + middle_height - (CLOSE_BUTTON / 2) + 1);
+	cairo_move_to (cr, priv->close_x, priv->close_y + 1);
 	/* To right down */
-	cairo_line_to (cr, PADDING_LEFT + text_width + CLOSE_BUTTON_SPACING + CLOSE_BUTTON, y_origin + middle_height + (CLOSE_BUTTON / 2) + 1);
+	cairo_line_to (cr, priv->close_x + CLOSE_BUTTON, y_origin + middle_height + (CLOSE_BUTTON / 2) + 1);
 	cairo_stroke (cr);
 	/* From right left */
-	cairo_move_to (cr, PADDING_LEFT + text_width + CLOSE_BUTTON_SPACING + CLOSE_BUTTON, y_origin + middle_height - (CLOSE_BUTTON / 2) + 1);
+	cairo_move_to (cr, priv->close_x + CLOSE_BUTTON, priv->close_y + 1);
 	/* To left down */
-	cairo_line_to (cr, PADDING_LEFT + text_width + CLOSE_BUTTON_SPACING, y_origin + middle_height + (CLOSE_BUTTON / 2) + 1);
+	cairo_line_to (cr, priv->close_x, y_origin + middle_height + (CLOSE_BUTTON / 2) + 1);
 	cairo_stroke (cr);
 
-	gdk_cairo_set_source_rgba (cr, &priv->text_color);
+	if (priv->close_highlighted) {
+		gdk_cairo_set_source_rgba (cr, &priv->fill_a_color);
+	} else {
+		gdk_cairo_set_source_rgba (cr, &priv->text_color);
+	}
 	/* Line from left up */
-	cairo_move_to (cr, PADDING_LEFT + text_width + CLOSE_BUTTON_SPACING, y_origin + middle_height - (CLOSE_BUTTON / 2));
+	cairo_move_to (cr, priv->close_x, priv->close_y);
 	/* To right down */
-	cairo_line_to (cr, PADDING_LEFT + text_width + CLOSE_BUTTON_SPACING + CLOSE_BUTTON, y_origin + middle_height + (CLOSE_BUTTON / 2));
+	cairo_line_to (cr, priv->close_x + CLOSE_BUTTON, y_origin + middle_height + (CLOSE_BUTTON / 2));
 	cairo_stroke (cr);
 	/* From right left */
-	cairo_move_to (cr, PADDING_LEFT + text_width + CLOSE_BUTTON_SPACING + CLOSE_BUTTON, y_origin + middle_height - (CLOSE_BUTTON / 2));
+	cairo_move_to (cr, priv->close_x + CLOSE_BUTTON, priv->close_y);
 	/* To left down */
-	cairo_line_to (cr, PADDING_LEFT + text_width + CLOSE_BUTTON_SPACING, y_origin + middle_height + (CLOSE_BUTTON / 2));
+	cairo_line_to (cr, priv->close_x, y_origin + middle_height + (CLOSE_BUTTON / 2));
 	cairo_stroke (cr);
 
         return FALSE;
 }
 
+gboolean
+almanah_tag_query_tooltip (GtkWidget *widget, gint x, gint y, gboolean keyboard_mode, GtkTooltip *tooltip)
+{
+	gint close_x = ALMANAH_TAG (widget)->priv->close_x;
+	gint close_y = ALMANAH_TAG (widget)->priv->close_y;
+
+	if (x >= close_x && x <= close_x + CLOSE_BUTTON
+	    && y >= close_y && y <= close_y + CLOSE_BUTTON) {
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
 GtkWidget *
 almanah_tag_new (const gchar *tag)
 {
@@ -276,3 +396,12 @@ almanah_tag_new (const gchar *tag)
                                          "tag", tag,
                                          NULL));
 }
+
+
+const gchar *
+almanah_tag_get_tag (AlmanahTag *tag_widget)
+{
+	g_return_val_if_fail (ALMANAH_IS_TAG (tag_widget), NULL);
+
+	return tag_widget->priv->tag;
+}
diff --git a/src/widgets/tag.h b/src/widgets/tag.h
index 8908f99..8311626 100644
--- a/src/widgets/tag.h
+++ b/src/widgets/tag.h
@@ -42,8 +42,9 @@ typedef struct {
 	GtkDrawingAreaClass parent;
 } AlmanahTagClass;
 
-GType      almanah_tag_get_type (void) G_GNUC_CONST;
-GtkWidget *almanah_tag_new      (const gchar *tag);
+GType      almanah_tag_get_type  (void) G_GNUC_CONST;
+GtkWidget *almanah_tag_new       (const gchar *tag);
+const gchar *almanah_tag_get_tag (AlmanahTag *tag_widget);
 
 G_END_DECLS
 



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