2008-09-10 Alberto Garcia <agarcia@igalia.com>
[hildon] / src / hildon-text-view.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2008 Nokia Corporation, all rights reserved.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser Public License as published by
8  * the Free Software Foundation; version 2 of the license.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU Lesser Public License for more details.
14  *
15  */
16
17 /**
18  * SECTION:hildon-text-view
19  * @short_description: Widget representing a text view in the Hildon framework.
20  *
21  * The #HildonTextView is a GTK widget which represents a text
22  * view. It is derived from the #GtkTextView widget and provides
23  * additional commodities specific to the Hildon framework.
24  *
25  * Besides all the features inherited from #GtkTextView, a
26  * #HildonTextView can also have a placeholder text. This text will be
27  * shown if the text view is empty and doesn't have the input focus,
28  * but it's otherwise ignored. Thus, calls to
29  * hildon_text_view_get_buffer() will never return the placeholder
30  * text, not even when it's being displayed.
31  *
32  * Although #HildonTextView is derived from #GtkTextView,
33  * gtk_text_view_get_buffer() and gtk_text_view_set_buffer() must
34  * never be used to get/set the buffer in this
35  * widget. hildon_text_view_get_buffer() and
36  * hildon_text_view_set_buffer() must be used instead.
37  */
38
39 #include                                        "hildon-text-view.h"
40
41 G_DEFINE_TYPE                                   (HildonTextView, hildon_text_view, GTK_TYPE_TEXT_VIEW);
42
43 #define                                         HILDON_TEXT_VIEW_GET_PRIVATE(obj) \
44                                                 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
45                                                 HILDON_TYPE_TEXT_VIEW, HildonTextViewPrivate));
46
47 typedef struct                                  _HildonTextViewPrivate HildonTextViewPrivate;
48
49 struct                                          _HildonTextViewPrivate
50 {
51     GtkTextBuffer *main_buffer;                   /* Used to show the "real" contents */
52     GtkTextBuffer *placeholder_buffer;   /* Internal, used to display the placeholder */
53     gulong changed_id;               /* ID of the main_buffer::changed signal handler */
54 };
55
56 static const gchar *placeholder_widget_name     = "hildon-text-view-placeholder";
57
58 /* Function used to decide whether to show the placeholder or not */
59 static void
60 hildon_text_view_refresh_contents               (GtkWidget *text_view)
61 {
62     HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (text_view);
63     gint bufsize = gtk_text_buffer_get_char_count (priv->main_buffer);
64
65     if ((bufsize > 0) || GTK_WIDGET_HAS_FOCUS (text_view)) {
66         /* Display the main buffer if it contains text or the widget is focused */
67         gtk_widget_set_name (text_view, NULL);
68         gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), priv->main_buffer);
69     } else {
70         /* Otherwise, display the placeholder */
71         gtk_widget_set_name (text_view, placeholder_widget_name);
72         gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), priv->placeholder_buffer);
73     }
74 }
75
76 /**
77  * hildon_text_view_set_buffer:
78  * @text_view: a #HildonTextView
79  * @buffer: a #GtkTextBuffer
80  *
81  * Sets @buffer as the buffer being displayed by @text_view. The
82  * previous buffer displayed by the text view is unreferenced, and a
83  * reference is added to @buffer. If you owned a reference to @buffer
84  * before passing it to this function, you must remove that reference
85  * yourself
86  *
87  * Note that you must never use gtk_text_view_set_buffer() to set the
88  * buffer of a #HildonTextView.
89  */
90 void
91 hildon_text_view_set_buffer                     (HildonTextView *text_view,
92                                                  GtkTextBuffer  *buffer)
93 {
94     HildonTextViewPrivate *priv;
95
96     g_return_if_fail (HILDON_IS_TEXT_VIEW (text_view));
97     g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
98
99     priv = HILDON_TEXT_VIEW_GET_PRIVATE (text_view);
100
101     /* If this is the same buffer, don't do anything */
102     if (buffer == priv->main_buffer)
103         return;
104
105     /* Disconnect the signal handler from the old buffer */
106     g_signal_handler_disconnect (priv->main_buffer, priv->changed_id);
107
108     /* Replace the old buffer with the new one */
109     g_object_unref (priv->main_buffer);
110     priv->main_buffer = g_object_ref (buffer);
111
112     /* Attach a callback to the new text buffer */
113     priv->changed_id =
114         g_signal_connect_swapped (priv->main_buffer, "changed",
115                                   G_CALLBACK (hildon_text_view_refresh_contents), text_view);
116
117     /* Refresh textview contents */
118     hildon_text_view_refresh_contents (GTK_WIDGET (text_view));
119 }
120
121 /**
122  * hildon_text_view_get_buffer:
123  * @text_view: a #HildonTextView
124  *
125  * Returns the text buffer in @text_view. The reference count is not
126  * incremented; the caller of this function won't own a new reference.
127  *
128  * Note that you must never use gtk_text_view_get_buffer() to get the
129  * buffer from a #HildonTextView.
130  *
131  * Also note that placeholder text (set using
132  * hildon_text_view_set_placeholder()) is never contained in this
133  * buffer.
134  *
135  * Returns: a #GtkTextBuffer
136  */
137 GtkTextBuffer *
138 hildon_text_view_get_buffer                     (HildonTextView *text_view)
139 {
140     HildonTextViewPrivate *priv;
141
142     g_return_val_if_fail (HILDON_IS_TEXT_VIEW (text_view), NULL);
143
144     priv = HILDON_TEXT_VIEW_GET_PRIVATE (text_view);
145
146     /* Always return priv->main_buffer even if the placeholder is
147      * being displayed */
148     return priv->main_buffer;
149 }
150
151 /**
152  * hildon_text_view_set_placeholder:
153  * @text_view: a #HildonTextView
154  * @text: the new text
155  *
156  * Sets the placeholder text in @text_view to @text.
157  */
158 void
159 hildon_text_view_set_placeholder                (HildonTextView *text_view,
160                                                  const gchar    *text)
161 {
162     HildonTextViewPrivate *priv;
163
164     g_return_if_fail (HILDON_IS_TEXT_VIEW (text_view) && text != NULL);
165
166     priv = HILDON_TEXT_VIEW_GET_PRIVATE (text_view);
167
168     gtk_text_buffer_set_text (priv->placeholder_buffer, text, -1);
169 }
170
171 /**
172  * hildon_text_view_new:
173  *
174  * Creates a new text view.
175  *
176  * Returns: a new #HildonTextView
177  */
178 GtkWidget *
179 hildon_text_view_new                            (void)
180 {
181     GtkWidget *entry = g_object_new (HILDON_TYPE_TEXT_VIEW, NULL);
182
183     return entry;
184 }
185
186 static gboolean
187 hildon_text_view_focus_in_event                 (GtkWidget     *widget,
188                                                  GdkEventFocus *event)
189 {
190     hildon_text_view_refresh_contents (widget);
191
192     if (GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_in_event) {
193         return GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_in_event (widget, event);
194     } else {
195         return FALSE;
196     }
197 }
198
199 static gboolean
200 hildon_text_view_focus_out_event                (GtkWidget     *widget,
201                                                  GdkEventFocus *event)
202 {
203     hildon_text_view_refresh_contents (widget);
204
205     if (GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_out_event) {
206         return GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_out_event (widget, event);
207     } else {
208         return FALSE;
209     }
210 }
211
212 static void
213 hildon_text_view_finalize                       (GObject *object)
214 {
215     HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (object);
216
217     g_signal_handler_disconnect (priv->main_buffer, priv->changed_id);
218     g_object_unref (priv->main_buffer);
219     g_object_unref (priv->placeholder_buffer);
220
221     if (G_OBJECT_CLASS (hildon_text_view_parent_class)->finalize)
222         G_OBJECT_CLASS (hildon_text_view_parent_class)->finalize (object);
223 }
224
225 static void
226 hildon_text_view_class_init                     (HildonTextViewClass *klass)
227 {
228     GObjectClass *gobject_class = (GObjectClass *)klass;
229     GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
230
231     gobject_class->finalize = hildon_text_view_finalize;
232     widget_class->focus_in_event = hildon_text_view_focus_in_event;
233     widget_class->focus_out_event = hildon_text_view_focus_out_event;
234
235     g_type_class_add_private (klass, sizeof (HildonTextViewPrivate));
236 }
237
238 static void
239 hildon_text_view_init                           (HildonTextView *self)
240 {
241     HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (self);
242
243     priv->main_buffer = gtk_text_buffer_new (NULL);
244     priv->placeholder_buffer = gtk_text_buffer_new (NULL);
245
246     hildon_text_view_refresh_contents (GTK_WIDGET (self));
247
248     priv->changed_id =
249         g_signal_connect_swapped (priv->main_buffer, "changed",
250                                   G_CALLBACK (hildon_text_view_refresh_contents), self);
251 }