1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3  * anjuta
4  * Copyright (C) James Liggett 2010 <jrliggett@cox.net>
5  *
6  * anjuta is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * anjuta is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "anjuta-entry.h"
21 
22 /**
23  * SECTION:anjuta-entry
24  * @short_description: #GtkEntry subclass that displays help text with a button
25  *                     to clear the entry's contents.
26  * @include: libanjuta/anjuta-entry.h
27  *
28  * AnjutaEntry is a  version of a #GtkEntry that displays some text, in
29  * a lighter color, that describes what is to be entered into it. There is also
30  * a button on the left to clear the entry's content quickly. AnjutaEntry is
31  * similar to the serach boxes used in Evolution and Glade, but is more generic
32  * can can be used in almost any situation.
33  */
34 
35 enum
36 {
37 	PROP_0,
38 
39 	PROP_HELP_TEXT
40 };
41 
42 typedef enum
43 {
44 	ANJUTA_ENTRY_NORMAL,
45 	ANJUTA_ENTRY_HELP
46 } AnjutaEntryMode;
47 
48 struct _AnjutaEntryPriv
49 {
50 	gboolean showing_help_text;
51 	gchar *help_text;
52 };
53 
54 G_DEFINE_TYPE (AnjutaEntry, anjuta_entry, GTK_TYPE_ENTRY);
55 
56 static void
anjuta_entry_set_mode(AnjutaEntry * self,AnjutaEntryMode mode)57 anjuta_entry_set_mode (AnjutaEntry *self, AnjutaEntryMode mode)
58 {
59 	GtkStyleContext *style_context;
60 	GdkRGBA insensitive_color;
61 
62 	style_context = gtk_widget_get_style_context (GTK_WIDGET (self));
63 
64 	switch (mode)
65 	{
66 		case ANJUTA_ENTRY_NORMAL:
67 			/* Remove the help text from the widget */
68 			if (self->priv->showing_help_text)
69 				gtk_entry_set_text (GTK_ENTRY (self), "");
70 
71 			gtk_widget_override_color (GTK_WIDGET (self), GTK_STATE_NORMAL,
72 			                           NULL);
73 
74 			self->priv->showing_help_text = FALSE;
75 
76 			break;
77 		case ANJUTA_ENTRY_HELP:
78 			if (self->priv->help_text)
79 				gtk_entry_set_text (GTK_ENTRY (self), self->priv->help_text);
80 			else
81 				gtk_entry_set_text (GTK_ENTRY (self), "");
82 
83 			/* FIXME: Ideally we should be using CSS here, but some themes,
84 			 * like Ubuntu's, don't define insensitive_fg_color. The help text
85 			 * renders white on these themes, which is unreadable in many cases.
86 			 *
87 			 * This should make the help text readable on all themes, but the
88 			 * color won't change if the theme changes while Anjuta is running.
89 			 */
90 			gtk_style_context_get_color (style_context, GTK_STATE_FLAG_INSENSITIVE,
91 			                             &insensitive_color);
92 			gtk_widget_override_color (GTK_WIDGET (self), GTK_STATE_NORMAL,
93 			                           &insensitive_color);
94 
95 			self->priv->showing_help_text = TRUE;
96 
97 			break;
98 		default:
99 			break;
100 	}
101 }
102 
103 /* It's probably terrible practice for a subclass to be listening to the
104  * parent' class's signals, but for some reason the icon release signal
105  * doesn't have a virtual method pointer in the GtkEntry class structure */
106 static void
anjuta_entry_icon_release(GtkEntry * entry,GtkEntryIconPosition icon_pos,GdkEvent * event,gpointer user_data)107 anjuta_entry_icon_release (GtkEntry *entry, GtkEntryIconPosition icon_pos,
108                            GdkEvent *event, gpointer user_data)
109 {
110 	if (icon_pos == GTK_ENTRY_ICON_SECONDARY)
111 		gtk_entry_set_text (entry, "");
112 }
113 
114 static void
anjuta_entry_init(AnjutaEntry * self)115 anjuta_entry_init (AnjutaEntry *self)
116 {
117 	self->priv = g_new0 (AnjutaEntryPriv, 1);
118 
119 	gtk_entry_set_icon_from_stock (GTK_ENTRY (self), GTK_ENTRY_ICON_SECONDARY,
120 	                               GTK_STOCK_CLEAR);
121 	gtk_entry_set_icon_activatable (GTK_ENTRY (self), GTK_ENTRY_ICON_SECONDARY,
122 	                                TRUE);
123 
124 	g_signal_connect (G_OBJECT (self), "icon-release",
125 	                  G_CALLBACK (anjuta_entry_icon_release),
126 	                  NULL);
127 
128 	anjuta_entry_set_mode (self, ANJUTA_ENTRY_HELP);
129 }
130 
131 static void
anjuta_entry_finalize(GObject * object)132 anjuta_entry_finalize (GObject *object)
133 {
134 	AnjutaEntry *self;
135 
136 	self = ANJUTA_ENTRY (object);
137 
138 	g_free (self->priv->help_text);
139 	g_free (self->priv);
140 
141 	G_OBJECT_CLASS (anjuta_entry_parent_class)->finalize (object);
142 }
143 
144 static void
anjuta_entry_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)145 anjuta_entry_set_property (GObject *object, guint prop_id, const GValue *value,
146                            GParamSpec *pspec)
147 {
148 	AnjutaEntry *self;
149 
150 	g_return_if_fail (ANJUTA_IS_ENTRY (object));
151 
152 	self = ANJUTA_ENTRY (object);
153 
154 	switch (prop_id)
155 	{
156 		case PROP_HELP_TEXT:
157 			g_free (self->priv->help_text);
158 
159 			self->priv->help_text = g_value_dup_string (value);
160 
161 			/* Update the display */
162 			if (self->priv->showing_help_text)
163 			{
164 				if (self->priv->help_text)
165 				{
166 					gtk_entry_set_text (GTK_ENTRY (self),
167 					                    self->priv->help_text);
168 				}
169 			}
170 			break;
171 		default:
172 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
173 			break;
174 	}
175 }
176 
177 static void
anjuta_entry_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)178 anjuta_entry_get_property (GObject *object, guint prop_id, GValue *value,
179                            GParamSpec *pspec)
180 {
181 	AnjutaEntry *self;
182 
183 	g_return_if_fail (ANJUTA_IS_ENTRY (object));
184 
185 	self = ANJUTA_ENTRY (object);
186 
187 	switch (prop_id)
188 	{
189 		case PROP_HELP_TEXT:
190 			g_value_set_string (value, self->priv->help_text);
191 			break;
192 		default:
193 			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194 			break;
195 	}
196 }
197 
198 static gboolean
anjuta_entry_focus_in_event(GtkWidget * widget,GdkEventFocus * event)199 anjuta_entry_focus_in_event (GtkWidget *widget, GdkEventFocus *event)
200 {
201 	AnjutaEntry *self;
202 
203 	self = ANJUTA_ENTRY (widget);
204 
205 	if (self->priv->showing_help_text)
206 		anjuta_entry_set_mode (self, ANJUTA_ENTRY_NORMAL);
207 
208 	return GTK_WIDGET_CLASS (anjuta_entry_parent_class)->focus_in_event (widget, event);
209 }
210 
211 static gboolean
anjuta_entry_focus_out_event(GtkWidget * widget,GdkEventFocus * event)212 anjuta_entry_focus_out_event (GtkWidget *widget, GdkEventFocus *event)
213 {
214 	AnjutaEntry *self;
215 	const gchar *text;
216 
217 	self = ANJUTA_ENTRY (widget);
218 	text = gtk_entry_get_text (GTK_ENTRY (widget));
219 
220 	if (text == NULL || text[0] == '\0')
221 		anjuta_entry_set_mode (self, ANJUTA_ENTRY_HELP);
222 
223 	return GTK_WIDGET_CLASS (anjuta_entry_parent_class)->focus_out_event (widget, event);
224 }
225 
226 static void
anjuta_entry_class_init(AnjutaEntryClass * klass)227 anjuta_entry_class_init (AnjutaEntryClass *klass)
228 {
229 	GObjectClass* object_class = G_OBJECT_CLASS (klass);
230 	GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
231 
232 	object_class->finalize = anjuta_entry_finalize;
233 	object_class->set_property = anjuta_entry_set_property;
234 	object_class->get_property = anjuta_entry_get_property;
235 	widget_class->focus_in_event = anjuta_entry_focus_in_event;
236 	widget_class->focus_out_event = anjuta_entry_focus_out_event;
237 
238 	/**
239 	 * AnjutaEntry::help-text:
240 	 *
241 	 * Text that should be displayed when the entry is empty. This text should
242 	 * briefly describe what the user should enter.
243 	 */
244 	g_object_class_install_property (object_class,
245 	                                 PROP_HELP_TEXT,
246 	                                 g_param_spec_string ("help-text",
247 	                                                      _("Help text"),
248 	                                                      _("Text to show the user what to enter into the entry"),
249 	                                                      "",
250 	                                                      G_PARAM_READABLE | G_PARAM_WRITABLE));
251 }
252 
253 /**
254  * anjuta_entry_new:
255  *
256  * Creates a new AnjutaEntry.
257  */
258 GtkWidget *
anjuta_entry_new(void)259 anjuta_entry_new (void)
260 {
261 	return g_object_new (ANJUTA_TYPE_ENTRY, NULL);
262 }
263 
264 /**
265  * anjuta_entry_get_text:
266  * @self: An AnjutaEntry
267  *
268  * Returns: The contents of the entry. If the entry is empty, the help text will
269  * be displayed and an empty string will be returned.
270  */
271 const gchar *
anjuta_entry_get_text(AnjutaEntry * self)272 anjuta_entry_get_text (AnjutaEntry *self)
273 {
274 	return (self->priv->showing_help_text) ?
275 		    "" : gtk_entry_get_text (GTK_ENTRY (self)) ;
276 }
277 
278 /**
279  * anjuta_entry_dup_text:
280  * @self: An AnjutaEntry
281  *
282  * Returns: (transfer full): A copy of the contents of the entry. If the entry
283  * is empty, the returned string will be empty. The returned string must be
284  * freed when no longer needed.
285  */
286 gchar *
anjuta_entry_dup_text(AnjutaEntry * self)287 anjuta_entry_dup_text (AnjutaEntry *self)
288 {
289 	return g_strdup (anjuta_entry_get_text (self));
290 }
291 
292 /**
293  * anjuta_entry_set_text:
294  * @self: An AnjutaEntry
295  * @text: The new text
296  *
297  * Sets the text on the entry, showing the help text if the text is empty.
298  */
299 void
anjuta_entry_set_text(AnjutaEntry * self,const gchar * text)300 anjuta_entry_set_text (AnjutaEntry *self, const gchar *text)
301 {
302 	if (text != NULL && text[0] != '\0')
303 		anjuta_entry_set_mode (self, ANJUTA_ENTRY_NORMAL);
304 	else
305 		anjuta_entry_set_mode (self, ANJUTA_ENTRY_HELP);
306 
307 	gtk_entry_set_text (GTK_ENTRY (self), text);
308 }
309 
310 /**
311  * anjuta_entry_is_showing_help_text:
312  * @self: An AnjutaEntry
313  *
314  * Returns: Whether the entry is showing its help text. In practice, if this
315  * method returns %TRUE, it means that the user has not entered anything.
316  */
317 gboolean
anjuta_entry_is_showing_help_text(AnjutaEntry * self)318 anjuta_entry_is_showing_help_text (AnjutaEntry *self)
319 {
320 	return self->priv->showing_help_text;
321 }
322