2008-10-13 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  * <example>
39  * <title>Creating a HildonTextView with a placeholder</title>
40  * <programlisting>
41  * GtkWidget *
42  * create_text_view (void)
43  * {
44  *     GtkWidget *text_view;
45  * <!-- -->
46  *     text_view = hildon_text_view_new ();
47  *     hildon_text_view_set_placeholder (HILDON_TEXT_VIEW (text_view),
48  *                                       "Type some text here");
49  * <!-- -->
50  *     return text_view;
51  * }
52  * </programlisting>
53  * </example>
54  */
55
56 #include                                        "hildon-text-view.h"
57
58 G_DEFINE_TYPE                                   (HildonTextView, hildon_text_view, GTK_TYPE_TEXT_VIEW);
59
60 #define                                         HILDON_TEXT_VIEW_GET_PRIVATE(obj) \
61                                                 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
62                                                 HILDON_TYPE_TEXT_VIEW, HildonTextViewPrivate));
63
64 struct                                          _HildonTextViewPrivate
65 {
66     GtkTextBuffer *main_buffer;                   /* Used to show the "real" contents */
67     GtkTextBuffer *placeholder_buffer;   /* Internal, used to display the placeholder */
68     gulong changed_id;               /* ID of the main_buffer::changed signal handler */
69 };
70
71 static const gchar *placeholder_widget_name     = "hildon-text-view-placeholder";
72
73 /* Function used to decide whether to show the placeholder or not */
74 static void
75 hildon_text_view_refresh_contents               (GtkWidget *text_view)
76 {
77     HildonTextViewPrivate *priv = HILDON_TEXT_VIEW (text_view)->priv;
78     gint bufsize = gtk_text_buffer_get_char_count (priv->main_buffer);
79
80     if ((bufsize > 0) || GTK_WIDGET_HAS_FOCUS (text_view)) {
81         /* Display the main buffer if it contains text or the widget is focused */
82         gtk_widget_set_name (text_view, NULL);
83         gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), priv->main_buffer);
84     } else {
85         /* Otherwise, display the placeholder */
86         gtk_widget_set_name (text_view, placeholder_widget_name);
87         gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), priv->placeholder_buffer);
88     }
89 }
90
91 /**
92  * hildon_text_view_set_buffer:
93  * @text_view: a #HildonTextView
94  * @buffer: a #GtkTextBuffer
95  *
96  * Sets @buffer as the buffer being displayed by @text_view. The
97  * previous buffer displayed by the text view is unreferenced, and a
98  * reference is added to @buffer. If you owned a reference to @buffer
99  * before passing it to this function, you must remove that reference
100  * yourself
101  *
102  * Note that you must never use gtk_text_view_set_buffer() to set the
103  * buffer of a #HildonTextView.
104  */
105 void
106 hildon_text_view_set_buffer                     (HildonTextView *text_view,
107                                                  GtkTextBuffer  *buffer)
108 {
109     HildonTextViewPrivate *priv;
110
111     g_return_if_fail (HILDON_IS_TEXT_VIEW (text_view));
112     g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
113
114     priv = text_view->priv;
115
116     /* If this is the same buffer, don't do anything */
117     if (buffer == priv->main_buffer)
118         return;
119
120     /* Disconnect the signal handler from the old buffer */
121     g_signal_handler_disconnect (priv->main_buffer, priv->changed_id);
122
123     /* Replace the old buffer with the new one */
124     g_object_unref (priv->main_buffer);
125     priv->main_buffer = g_object_ref (buffer);
126
127     /* Attach a callback to the new text buffer */
128     priv->changed_id =
129         g_signal_connect_swapped (priv->main_buffer, "changed",
130                                   G_CALLBACK (hildon_text_view_refresh_contents), text_view);
131
132     /* Refresh textview contents */
133     hildon_text_view_refresh_contents (GTK_WIDGET (text_view));
134 }
135
136 /**
137  * hildon_text_view_get_buffer:
138  * @text_view: a #HildonTextView
139  *
140  * Returns the text buffer in @text_view. The reference count is not
141  * incremented; the caller of this function won't own a new reference.
142  *
143  * Note that you must never use gtk_text_view_get_buffer() to get the
144  * buffer from a #HildonTextView.
145  *
146  * Also note that placeholder text (set using
147  * hildon_text_view_set_placeholder()) is never contained in this
148  * buffer.
149  *
150  * Returns: a #GtkTextBuffer
151  */
152 GtkTextBuffer *
153 hildon_text_view_get_buffer                     (HildonTextView *text_view)
154 {
155     g_return_val_if_fail (HILDON_IS_TEXT_VIEW (text_view), NULL);
156
157     /* Always return priv->main_buffer even if the placeholder is
158      * being displayed */
159     return text_view->priv->main_buffer;
160 }
161
162 /**
163  * hildon_text_view_set_placeholder:
164  * @text_view: a #HildonTextView
165  * @text: the new text
166  *
167  * Sets the placeholder text in @text_view to @text.
168  */
169 void
170 hildon_text_view_set_placeholder                (HildonTextView *text_view,
171                                                  const gchar    *text)
172 {
173     g_return_if_fail (HILDON_IS_TEXT_VIEW (text_view) && text != NULL);
174
175     gtk_text_buffer_set_text (text_view->priv->placeholder_buffer, text, -1);
176 }
177
178 /**
179  * hildon_text_view_new:
180  *
181  * Creates a new text view.
182  *
183  * Returns: a new #HildonTextView
184  */
185 GtkWidget *
186 hildon_text_view_new                            (void)
187 {
188     GtkWidget *entry = g_object_new (HILDON_TYPE_TEXT_VIEW, NULL);
189
190     return entry;
191 }
192
193 static gboolean
194 hildon_text_view_focus_in_event                 (GtkWidget     *widget,
195                                                  GdkEventFocus *event)
196 {
197     hildon_text_view_refresh_contents (widget);
198
199     if (GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_in_event) {
200         return GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_in_event (widget, event);
201     } else {
202         return FALSE;
203     }
204 }
205
206 static gboolean
207 hildon_text_view_focus_out_event                (GtkWidget     *widget,
208                                                  GdkEventFocus *event)
209 {
210     hildon_text_view_refresh_contents (widget);
211
212     if (GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_out_event) {
213         return GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_out_event (widget, event);
214     } else {
215         return FALSE;
216     }
217 }
218
219 static void
220 hildon_text_view_finalize                       (GObject *object)
221 {
222     HildonTextViewPrivate *priv = HILDON_TEXT_VIEW (object)->priv;
223
224     g_signal_handler_disconnect (priv->main_buffer, priv->changed_id);
225     g_object_unref (priv->main_buffer);
226     g_object_unref (priv->placeholder_buffer);
227
228     if (G_OBJECT_CLASS (hildon_text_view_parent_class)->finalize)
229         G_OBJECT_CLASS (hildon_text_view_parent_class)->finalize (object);
230 }
231
232 static void
233 hildon_text_view_class_init                     (HildonTextViewClass *klass)
234 {
235     GObjectClass *gobject_class = (GObjectClass *)klass;
236     GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
237
238     gobject_class->finalize = hildon_text_view_finalize;
239     widget_class->focus_in_event = hildon_text_view_focus_in_event;
240     widget_class->focus_out_event = hildon_text_view_focus_out_event;
241
242     g_type_class_add_private (klass, sizeof (HildonTextViewPrivate));
243 }
244
245 static void
246 hildon_text_view_init                           (HildonTextView *self)
247 {
248     HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (self);
249
250     self->priv = priv;
251
252     priv->main_buffer = gtk_text_buffer_new (NULL);
253     priv->placeholder_buffer = gtk_text_buffer_new (NULL);
254
255     hildon_text_view_refresh_contents (GTK_WIDGET (self));
256
257     priv->changed_id =
258         g_signal_connect_swapped (priv->main_buffer, "changed",
259                                   G_CALLBACK (hildon_text_view_refresh_contents), self);
260 }