1 /* Copyright (c) 2007, Nokia Corporation
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the Nokia Corporation nor the names of its
14 * contributors may be used to endorse or promote products derived from
15 * this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
18 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
20 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
21 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
22 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
23 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
24 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 #include <glib/gi18n-lib.h>
37 #include "modest-recpt-view.h"
39 static GObjectClass *parent_class = NULL;
47 typedef struct _ModestRecptViewPriv ModestRecptViewPriv;
49 struct _ModestRecptViewPriv
51 gboolean button_pressed;
52 gdouble pressed_x, pressed_y;
55 #define MODEST_RECPT_VIEW_GET_PRIVATE(o) \
56 (G_TYPE_INSTANCE_GET_PRIVATE ((o), MODEST_TYPE_RECPT_VIEW, ModestRecptViewPriv))
58 static guint signals[LAST_SIGNAL] = {0};
61 * modest_recpt_view_new:
63 * Return value: a new #ModestRecptView instance implemented for Gtk+
66 modest_recpt_view_new (void)
68 ModestRecptView *self = g_object_new (MODEST_TYPE_RECPT_VIEW, NULL);
70 return GTK_WIDGET (self);
74 address_bounds_at_position (const gchar *recipients_list, gint position, gint *start, gint *end)
76 gchar *current = NULL;
80 gboolean is_quoted = FALSE;
83 for (current = (gchar *) recipients_list; *current != '\0'; current = g_utf8_find_next_char (current, NULL)) {
84 gunichar c = g_utf8_get_char (current);
86 if ((c == ',') && (!is_quoted)) {
87 if (index < position) {
88 range_start = index + 1;
92 } else if (c == '\"') {
93 is_quoted = !is_quoted;
94 } else if ((c == ' ') &&(range_start == index)) {
102 *start = range_start;
108 button_press_event (GtkWidget *widget,
109 GdkEventButton *event,
112 ModestRecptViewPriv *priv = MODEST_RECPT_VIEW_GET_PRIVATE (MODEST_RECPT_VIEW (widget));
114 if (!gtk_label_get_selectable (GTK_LABEL (widget)))
117 if (event->type == GDK_BUTTON_PRESS) {
118 priv->button_pressed = TRUE;
119 priv->pressed_x = event->x;
120 priv->pressed_y = event->y;
126 button_release_event (GtkWidget *widget,
127 GdkEventButton *event,
130 ModestRecptViewPriv *priv = MODEST_RECPT_VIEW_GET_PRIVATE (MODEST_RECPT_VIEW (widget));
132 if (!gtk_label_get_selectable (GTK_LABEL (widget)))
135 if (event->type != GDK_BUTTON_RELEASE)
138 if ((priv->button_pressed) &&
139 (event->type == GDK_BUTTON_RELEASE) &&
140 (priv->pressed_x == event->x) &&
141 (priv->pressed_y == event->y)) {
142 priv->button_pressed = FALSE;
143 if (event->button == 1) {
144 PangoLayout *layout = NULL;
146 layout = gtk_label_get_layout (GTK_LABEL (widget));
147 if (pango_layout_xy_to_index (layout, event->x*PANGO_SCALE, event->y*PANGO_SCALE, &index, NULL)) {
148 int selection_start, selection_end;
149 gboolean selected = FALSE;
150 if (gtk_label_get_selection_bounds (GTK_LABEL (widget),
153 (index >= selection_start)&&(index < selection_end)) {
157 address_bounds_at_position (gtk_label_get_text (GTK_LABEL (widget)),
159 &selection_start, &selection_end);
160 /* TODO: now gtk label tries to select more than the label as usual,
161 * and we force it to recover the selected region for the defined area.
162 * It should be fixed (maybe preventing gtklabel to manage selections
163 * in parallel with us
165 gtk_label_select_region (GTK_LABEL (widget),
170 g_signal_emit (G_OBJECT (widget), signals[ACTIVATE_SIGNAL], 0);
176 priv->button_pressed = FALSE;
181 modest_recpt_view_instance_init (GTypeInstance *instance, gpointer g_class)
184 gtk_label_set_justify (GTK_LABEL (instance), GTK_JUSTIFY_LEFT);
185 gtk_label_set_line_wrap (GTK_LABEL (instance), TRUE);
186 gtk_label_set_selectable (GTK_LABEL (instance), TRUE);
188 g_signal_connect (G_OBJECT (instance), "button-press-event", G_CALLBACK(button_press_event), NULL);
189 g_signal_connect (G_OBJECT (instance), "button-release-event", G_CALLBACK(button_release_event), NULL);
195 modest_recpt_view_finalize (GObject *object)
197 (*parent_class->finalize) (object);
203 modest_recpt_view_class_init (ModestRecptViewClass *klass)
205 GObjectClass *object_class;
207 parent_class = g_type_class_peek_parent (klass);
208 object_class = (GObjectClass*) klass;
210 object_class->finalize = modest_recpt_view_finalize;
212 klass->activate = NULL;
214 g_type_class_add_private (object_class, sizeof (ModestRecptViewPriv));
216 signals[ACTIVATE_SIGNAL] =
217 g_signal_new ("activate",
218 G_TYPE_FROM_CLASS (object_class),
219 G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
220 G_STRUCT_OFFSET(ModestRecptViewClass, activate),
222 g_cclosure_marshal_VOID__VOID,
229 modest_recpt_view_get_type (void)
231 static GType type = 0;
233 if (G_UNLIKELY(type == 0))
235 static const GTypeInfo info =
237 sizeof (ModestRecptViewClass),
238 NULL, /* base_init */
239 NULL, /* base_finalize */
240 (GClassInitFunc) modest_recpt_view_class_init, /* class_init */
241 NULL, /* class_finalize */
242 NULL, /* class_data */
243 sizeof (ModestRecptView),
245 modest_recpt_view_instance_init /* instance_init */
248 type = g_type_register_static (GTK_TYPE_LABEL,