2 * This file is a part of hildon
4 * Copyright (C) 2008 Nokia Corporation, all rights reserved.
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.
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.
18 * SECTION:hildon-text-view
19 * @short_description: Text view within the Hildon framework.
21 * The #HildonTextView is a text
22 * view derived from the #GtkTextView widget that provides
23 * additional commodities specific to the Hildon framework.
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.
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.
39 * <title>Creating a HildonTextView with a placeholder</title>
42 * create_text_view (void)
44 * GtkWidget *text_view;
46 * text_view = hildon_text_view_new ();
47 * hildon_text_view_set_placeholder (HILDON_TEXT_VIEW (text_view),
48 * "Type some text here");
56 #include "hildon-text-view.h"
59 #define HILDON_TEXT_VIEW_DRAG_THRESHOLD 16.0
61 G_DEFINE_TYPE (HildonTextView, hildon_text_view, GTK_TYPE_TEXT_VIEW);
63 #define HILDON_TEXT_VIEW_GET_PRIVATE(obj) \
64 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
65 HILDON_TYPE_TEXT_VIEW, HildonTextViewPrivate));
67 typedef struct _HildonTextViewPrivate HildonTextViewPrivate;
69 struct _HildonTextViewPrivate
71 GtkTextBuffer *main_buffer; /* Used to show the "real" contents */
72 GtkTextBuffer *placeholder_buffer; /* Internal, used to display the placeholder */
73 gulong changed_id; /* ID of the main_buffer::changed signal handler */
74 gdouble x; /* tap x position */
75 gdouble y; /* tap y position */
76 guint showing_placeholder : 1; /* Whether the placeholder is being shown */
77 guint setting_style : 1; /* Whether the logical color is being set */
82 set_logical_color (GtkWidget *widget)
85 const gchar *colorname;
86 HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (widget);
88 colorname = priv->showing_placeholder ? "ReversedSecondaryTextColor" : "ReversedTextColor";
90 gtk_widget_ensure_style (widget);
91 if (gtk_style_lookup_color (widget->style, colorname, &color) == TRUE) {
92 priv->setting_style = TRUE;
93 gtk_widget_modify_text (widget, GTK_STATE_NORMAL, &color);
94 priv->setting_style = FALSE;
99 hildon_text_view_style_set (GtkWidget *widget,
100 GtkStyle *previous_style)
102 HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (widget);
104 if (GTK_WIDGET_CLASS (hildon_text_view_parent_class)->style_set)
105 GTK_WIDGET_CLASS (hildon_text_view_parent_class)->style_set (widget, previous_style);
107 /* Prevent infinite recursion when calling set_logical_font() and
108 * set_logical_color() */
109 if (priv->setting_style)
112 set_logical_color (widget);
115 /* Function used to decide whether to show the placeholder or not */
117 hildon_text_view_refresh_contents (GtkWidget *text_view)
119 HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (text_view);
120 gint bufsize = gtk_text_buffer_get_char_count (priv->main_buffer);
122 /* Display the main buffer if it contains text or the widget is focused */
123 priv->showing_placeholder = (bufsize <= 0) && !(GTK_WIDGET_HAS_FOCUS (text_view));
125 set_logical_color (text_view);
127 if (priv->showing_placeholder) {
128 gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), priv->placeholder_buffer);
130 gtk_text_view_set_buffer (GTK_TEXT_VIEW (text_view), priv->main_buffer);
135 * hildon_text_view_set_buffer:
136 * @text_view: a #HildonTextView
137 * @buffer: a #GtkTextBuffer
139 * Sets @buffer as the buffer being displayed by @text_view. The
140 * previous buffer displayed by the text view is unreferenced, and a
141 * reference is added to @buffer. If you owned a reference to @buffer
142 * before passing it to this function, you must remove that reference
145 * Note that you must never use gtk_text_view_set_buffer() to set the
146 * buffer of a #HildonTextView.
151 hildon_text_view_set_buffer (HildonTextView *text_view,
152 GtkTextBuffer *buffer)
154 HildonTextViewPrivate *priv;
156 g_return_if_fail (HILDON_IS_TEXT_VIEW (text_view));
157 g_return_if_fail (GTK_IS_TEXT_BUFFER (buffer));
159 priv = HILDON_TEXT_VIEW_GET_PRIVATE (text_view);
161 /* If this is the same buffer, don't do anything */
162 if (buffer == priv->main_buffer)
165 /* Disconnect the signal handler from the old buffer */
166 g_signal_handler_disconnect (priv->main_buffer, priv->changed_id);
168 /* Replace the old buffer with the new one */
169 g_object_unref (priv->main_buffer);
170 priv->main_buffer = g_object_ref (buffer);
172 /* Attach a callback to the new text buffer */
174 g_signal_connect_swapped (priv->main_buffer, "changed",
175 G_CALLBACK (hildon_text_view_refresh_contents), text_view);
177 /* Refresh textview contents */
178 hildon_text_view_refresh_contents (GTK_WIDGET (text_view));
182 * hildon_text_view_get_buffer:
183 * @text_view: a #HildonTextView
185 * Returns the text buffer in @text_view. The reference count is not
186 * incremented; the caller of this function won't own a new reference.
188 * Note that you must never use gtk_text_view_get_buffer() to get the
189 * buffer from a #HildonTextView.
191 * Also note that placeholder text (set using
192 * hildon_text_view_set_placeholder()) is never contained in this
195 * Returns: a #GtkTextBuffer
200 hildon_text_view_get_buffer (HildonTextView *text_view)
202 HildonTextViewPrivate *priv;
204 g_return_val_if_fail (HILDON_IS_TEXT_VIEW (text_view), NULL);
206 priv = HILDON_TEXT_VIEW_GET_PRIVATE (text_view);
208 /* Always return priv->main_buffer even if the placeholder is
210 return priv->main_buffer;
214 * hildon_text_view_set_placeholder:
215 * @text_view: a #HildonTextView
216 * @text: the new text
218 * Sets the placeholder text in @text_view to @text.
223 hildon_text_view_set_placeholder (HildonTextView *text_view,
226 HildonTextViewPrivate *priv;
228 g_return_if_fail (HILDON_IS_TEXT_VIEW (text_view) && text != NULL);
230 priv = HILDON_TEXT_VIEW_GET_PRIVATE (text_view);
232 gtk_text_buffer_set_text (priv->placeholder_buffer, text, -1);
236 * hildon_text_view_new:
238 * Creates a new text view.
240 * Returns: a new #HildonTextView
245 hildon_text_view_new (void)
247 GtkWidget *entry = g_object_new (HILDON_TYPE_TEXT_VIEW, NULL);
253 hildon_text_view_focus_in_event (GtkWidget *widget,
254 GdkEventFocus *event)
256 hildon_text_view_refresh_contents (widget);
258 if (GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_in_event) {
259 return GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_in_event (widget, event);
266 hildon_text_view_focus_out_event (GtkWidget *widget,
267 GdkEventFocus *event)
269 hildon_text_view_refresh_contents (widget);
271 if (GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_out_event) {
272 return GTK_WIDGET_CLASS (hildon_text_view_parent_class)->focus_out_event (widget, event);
279 hildon_text_view_button_press_event (GtkWidget *widget,
280 GdkEventButton *event)
282 HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (widget);
284 gtk_widget_grab_focus (widget);
286 if (GTK_TEXT_VIEW (widget)->editable &&
287 hildon_gtk_im_context_filter_event (GTK_TEXT_VIEW (widget)->im_context, (GdkEvent*)event)) {
288 GTK_TEXT_VIEW (widget)->need_im_reset = TRUE;
292 if (event->button == 1 && event->type == GDK_BUTTON_PRESS) {
303 hildon_text_view_button_release_event (GtkWidget *widget,
304 GdkEventButton *event)
306 GtkTextView *text_view = GTK_TEXT_VIEW (widget);
307 HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (widget);
311 if (text_view->editable &&
312 hildon_gtk_im_context_filter_event (text_view->im_context, (GdkEvent*)event)) {
313 text_view->need_im_reset = TRUE;
317 if (event->button == 1 && event->type == GDK_BUTTON_RELEASE) {
318 if (fabs (priv->x - event->x) < HILDON_TEXT_VIEW_DRAG_THRESHOLD &&
319 fabs (priv->y - event->y) < HILDON_TEXT_VIEW_DRAG_THRESHOLD) {
320 GtkTextWindowType window_type;
322 window_type = gtk_text_view_get_window_type (text_view, event->window);
323 gtk_text_view_window_to_buffer_coords (text_view,
327 gtk_text_view_get_iter_at_location (text_view, &iter, x, y);
328 if (gtk_text_buffer_get_char_count (priv->main_buffer))
329 gtk_text_buffer_place_cursor (priv->main_buffer, &iter);
331 gtk_widget_grab_focus (GTK_WIDGET (text_view));
340 hildon_text_view_finalize (GObject *object)
342 HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (object);
344 g_signal_handler_disconnect (priv->main_buffer, priv->changed_id);
345 g_object_unref (priv->main_buffer);
346 g_object_unref (priv->placeholder_buffer);
348 if (G_OBJECT_CLASS (hildon_text_view_parent_class)->finalize)
349 G_OBJECT_CLASS (hildon_text_view_parent_class)->finalize (object);
353 hildon_text_view_class_init (HildonTextViewClass *klass)
355 GObjectClass *gobject_class = (GObjectClass *)klass;
356 GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;
358 gobject_class->finalize = hildon_text_view_finalize;
359 widget_class->focus_in_event = hildon_text_view_focus_in_event;
360 widget_class->focus_out_event = hildon_text_view_focus_out_event;
361 widget_class->motion_notify_event = NULL;
362 widget_class->button_press_event = hildon_text_view_button_press_event;
363 widget_class->button_release_event = hildon_text_view_button_release_event;
364 widget_class->style_set = hildon_text_view_style_set;
366 g_type_class_add_private (klass, sizeof (HildonTextViewPrivate));
370 hildon_text_view_init (HildonTextView *self)
372 HildonTextViewPrivate *priv = HILDON_TEXT_VIEW_GET_PRIVATE (self);
374 priv->main_buffer = gtk_text_buffer_new (NULL);
375 priv->placeholder_buffer = gtk_text_buffer_new (NULL);
376 priv->showing_placeholder = FALSE;
377 priv->setting_style = FALSE;
379 hildon_text_view_refresh_contents (GTK_WIDGET (self));
382 g_signal_connect_swapped (priv->main_buffer, "changed",
383 G_CALLBACK (hildon_text_view_refresh_contents), self);