2b5d2c5f9fbaec75ae2d95bc92c8ee2a6b885c87
[hildon] / src / hildon-helper.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-helper
27  * @short_description: A collection of usefull utilities and functions.
28  *
29  * Hildon provides some helper functions that can be used for commonly 
30  * performed tasks and functionality blocks. This includes operations 
31  * on widget styles and probing functions for touch events.
32  *
33  */
34
35 #ifdef                                          HAVE_CONFIG_H
36 #include                                        <config.h>
37 #endif
38
39 #include                                        <gtk/gtk.h>
40 #include                                        "hildon-helper.h"
41 #include                                        "hildon-banner.h"
42
43 #define                                         HILDON_FINGER_PRESSURE_THRESHOLD 0.4
44
45 #define                                         HILDON_FINGER_BUTTON 8
46
47 #define                                         HILDON_FINGER_ALT_BUTTON 1
48
49 #define                                         HILDON_FINGER_ALT_MASK GDK_MOD4_MASK
50
51 #define                                         HILDON_FINGER_SIMULATE_BUTTON 2
52
53 struct                                          _HildonLogicalData
54 {
55     GtkRcFlags rcflags;
56     GtkStateType state;
57     gchar *logicalcolorstring;
58     gchar *logicalfontstring;
59 } typedef                                       HildonLogicalData;
60
61 static void 
62 hildon_change_style_recursive_from_ld           (GtkWidget *widget, 
63                                                  GtkStyle *prev_style, 
64                                                  HildonLogicalData *ld)
65 {
66     g_assert (GTK_IS_WIDGET (widget));
67
68     /* Change the style for child widgets */
69     if (GTK_IS_CONTAINER (widget)) {
70         GList *iterator = gtk_container_get_children (GTK_CONTAINER (widget));
71         for (iterator = iterator; iterator != NULL; iterator = g_list_next (iterator))
72             hildon_change_style_recursive_from_ld (GTK_WIDGET (iterator->data), prev_style, ld);
73     }
74
75     /* gtk_widget_modify_*() emit "style_set" signals, so if we got here from
76        "style_set" signal, we need to block this function from being called
77        again or we get into inifinite loop.
78
79     FIXME: Compiling with gcc > 3.3 and -pedantic won't allow
80     conversion between function and object pointers. GLib API however
81     requires an object pointer for a function, so we have to work
82     around this.
83     See http://bugzilla.gnome.org/show_bug.cgi?id=310175
84     */
85
86     G_GNUC_EXTENSION
87         g_signal_handlers_block_matched (G_OBJECT (widget), G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC,
88                 g_signal_lookup ("style_set", G_TYPE_FROM_INSTANCE (widget)),
89                 0, NULL,
90                 (gpointer) hildon_change_style_recursive_from_ld,
91                 NULL);
92
93     if (ld->logicalcolorstring != NULL)
94     {
95         /* Changing logical color */
96         GdkColor color;
97         gtk_widget_ensure_style (widget);
98         if (gtk_style_lookup_color (widget->style, ld->logicalcolorstring, &color) == TRUE)
99             switch (ld->rcflags)
100             {
101                 case GTK_RC_FG:
102                     gtk_widget_modify_fg (widget, ld->state, &color);
103                     break;
104
105                 case GTK_RC_BG:
106                     gtk_widget_modify_bg (widget, ld->state, &color);
107                     break;
108
109                 case GTK_RC_TEXT:
110                     gtk_widget_modify_text (widget, ld->state, &color);
111                     break;
112
113                 case GTK_RC_BASE:
114                     gtk_widget_modify_base (widget, ld->state, &color);
115                     break;
116
117             } else {
118                 g_warning ("Failed to lookup '%s' color!", ld->logicalcolorstring);
119             }
120     }
121
122     if (ld->logicalfontstring != NULL)
123     {
124         /* Changing logical font */
125         GtkStyle *fontstyle = gtk_rc_get_style_by_paths (gtk_settings_get_default (), ld->logicalfontstring, NULL, G_TYPE_NONE);
126         if (fontstyle != NULL)
127         {
128             PangoFontDescription *fontdesc = fontstyle->font_desc;
129
130             if (fontdesc != NULL)
131                 gtk_widget_modify_font (widget, fontdesc);
132         }
133     }
134
135     /* FIXME: Compilation workaround for gcc > 3.3 + -pedantic again */
136
137     G_GNUC_EXTENSION
138         g_signal_handlers_unblock_matched (G_OBJECT (widget), G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC,
139                 g_signal_lookup ("style_set", G_TYPE_FROM_INSTANCE (widget)),
140                 0, NULL,
141                 (gpointer) hildon_change_style_recursive_from_ld,
142                 NULL);
143 }
144
145 static void 
146 hildon_logical_data_free                        (HildonLogicalData *ld)
147 {
148     g_return_if_fail (ld != NULL);
149
150     if (ld->logicalcolorstring)
151         g_free (ld->logicalcolorstring);
152
153     if (ld->logicalfontstring)
154         g_free (ld->logicalfontstring);
155
156     g_free (ld);
157 }
158
159 /**
160  * hildon_helper_event_button_is_finger:
161  * @event: A @gtkeventbutton to check
162  *
163  * Checks if the given button event is a finger event.
164  * 
165  * return value : TRUE if the event is a finger event.
166  **/
167 gboolean 
168 hildon_helper_event_button_is_finger (GdkEventButton *event)
169 {
170     gdouble pressure;
171
172     if (gdk_event_get_axis ((GdkEvent*) event, GDK_AXIS_PRESSURE, &pressure) &&
173         pressure > HILDON_FINGER_PRESSURE_THRESHOLD)
174         return TRUE;
175
176     if (event->button == HILDON_FINGER_BUTTON)
177         return TRUE;
178
179     if (event->button == HILDON_FINGER_ALT_BUTTON &&
180         event->state & HILDON_FINGER_ALT_MASK)
181         return TRUE;
182
183     if (event->button == HILDON_FINGER_SIMULATE_BUTTON)
184         return TRUE;
185
186     return FALSE;
187 }
188
189 /**
190  * hildon_helper_set_logical_font:
191  * @widget : a @gtkwidget to assign this logical font for.
192  * @logicalfontname : a gchar* with the logical font name to assign to the widget.
193  * 
194  * This function assigns a defined logical font to the @widget and all its child widgets.
195  * it also connects to the "style_set" signal which will retrieve & assign the new font 
196  * for the given logical name each time the theme is changed.
197  * The returned signal id can be used to disconnect the signal. 
198  * When calling multiple times the previous signal (obtained by calling this function) is disconnected 
199  * automatically and should not be used. 
200  * 
201  * return value : the signal id that is triggered every time theme is changed. 0 if font set failed.
202  **/
203 gulong
204 hildon_helper_set_logical_font                  (GtkWidget *widget, 
205                                                  const gchar *logicalfontname)
206 {
207     HildonLogicalData *ld;
208     gulong signum = 0;
209
210     g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
211     g_return_val_if_fail (logicalfontname != NULL, 0);
212
213     ld = g_malloc (sizeof (HildonLogicalData));
214
215     ld->rcflags = 0;
216     ld->state = 0;
217     ld->logicalcolorstring = NULL;
218     ld->logicalfontstring = g_strdup(logicalfontname);
219
220     /* Disconnects the previously connected signals. That calls the closure notify
221      * and effectively disposes the allocated data (hildon_logical_data_free) */
222     g_signal_handlers_disconnect_matched (G_OBJECT (widget), G_SIGNAL_MATCH_FUNC, 
223             0, 0, NULL, 
224             G_CALLBACK (hildon_change_style_recursive_from_ld), NULL);
225
226     /* Change the font now */
227     hildon_change_style_recursive_from_ld (widget, NULL, ld);
228
229     /* Connect to "style_set" so that the font gets changed whenever theme changes. */
230     signum = g_signal_connect_data (G_OBJECT (widget), "style_set",
231             G_CALLBACK (hildon_change_style_recursive_from_ld),
232             ld, (GClosureNotify) hildon_logical_data_free, 0);
233
234     return signum;
235 }
236
237 static GQuark
238 hildon_helper_insensitive_message_quark (void)
239 {
240   static GQuark quark = 0;
241
242   if (G_UNLIKELY (quark == 0))
243     quark = g_quark_from_static_string ("hildon-insensitive-message");
244
245   return quark;
246 }
247
248
249 static void
250 show_insensitive_message (GtkWidget *widget, gpointer user_data)
251 {
252   gchar *message = NULL;
253
254   g_assert (GTK_IS_WIDGET (widget));
255
256   message = (gchar*) g_object_get_qdata (G_OBJECT (widget),
257                                          hildon_helper_insensitive_message_quark ());
258
259   if (message)
260     hildon_banner_show_information (widget, NULL, message);
261 }
262
263
264 /**
265  * hildon_helper_set_insensitive_message
266  * @widget : A @GtkWidget to assign a banner to
267  * @message: A message to display to the user
268  *
269  * This function assigns an insensitive message to a @widget. When the @widget is 
270  * in an insensitive state and the user activates it, the @message will be displayed
271  * using a standard @HildonBanner. 
272  *
273  **/
274
275 void
276 hildon_helper_set_insensitive_message           (GtkWidget *widget,
277                                                  const gchar *message)
278 {
279     g_return_if_fail (GTK_IS_WIDGET (widget));
280     g_return_if_fail (message != NULL);
281
282     gpointer stored_message;
283
284     /* Clean up any previous instance of the insensitive message */
285     g_signal_handlers_disconnect_matched (G_OBJECT (widget), G_SIGNAL_MATCH_FUNC,
286                                           0, 0, NULL,
287                                           G_CALLBACK (show_insensitive_message), NULL);
288     
289     stored_message = g_object_get_qdata (G_OBJECT (widget), hildon_helper_insensitive_message_quark ());
290     if (stored_message)
291       g_free (stored_message);
292
293     /* We need to dup the string because the pointer might not be valid when the
294      insensitive-press signal callback is executed */
295     g_object_set_qdata_full (G_OBJECT (widget), hildon_helper_insensitive_message_quark (), 
296                              (gpointer)g_strdup (message),
297                              g_free);
298
299     if (message != NULL) {
300       g_signal_connect (G_OBJECT (widget), "insensitive-press",
301                         G_CALLBACK (show_insensitive_message), NULL);
302     }
303 }
304
305 /**
306  * hildon_helper_set_insensitive_messagef
307  * @widget : A @GtkWidget to assign a banner to
308  * @format : a printf-like format string
309  * @varargs : arguments for the format string
310  *
311  * A version of hildon_helper_set_insensitive_message with string formatting.
312  *
313  **/
314
315 void
316 hildon_helper_set_insensitive_messagef        (GtkWidget *widget,
317                                                const gchar *format,
318                                                ...)
319 {
320   g_return_if_fail (GTK_IS_WIDGET (widget));
321
322   gchar *message;
323   va_list args;
324
325   va_start (args, format);
326   message = g_strdup_vprintf (format, args);
327   va_end (args);
328
329   hildon_helper_set_insensitive_message (widget, message);
330
331   g_free (message);
332 }
333
334 /**
335  * hildon_helper_set_logical_color:
336  * @widget : A @GtkWidget to assign this logical font for.
337  * @rcflags : @GtkRcFlags enumeration defining whether to assign to FG, BG, TEXT or BASE style.
338  * @state : @GtkStateType indicating to which state to assign the logical color
339  * @logicalcolorname : A gchar* with the logical font name to assign to the widget.
340  * 
341  * This function assigns a defined logical color to the @widget and all it's child widgets.
342  * It also connects to the "style_set" signal which will retrieve & assign the new color 
343  * for the given logical name each time the theme is changed.
344  * The returned signal id can be used to disconnect the signal.
345  * When calling multiple times the previous signal (obtained by calling this function) is disconnected 
346  * automatically and should not be used. 
347  * 
348  * Example : If the style you want to modify is bg[NORMAL] then set rcflags to GTK_RC_BG and state to GTK_STATE_NORMAL.
349  * 
350  * Return value : The signal id that is triggered every time theme is changed. 0 if color set failed.
351  **/
352 gulong 
353 hildon_helper_set_logical_color                 (GtkWidget *widget, 
354                                                  GtkRcFlags rcflags,
355                                                  GtkStateType state, 
356                                                  const gchar *logicalcolorname)
357 {
358     HildonLogicalData *ld;
359     gulong signum = 0;
360
361     g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
362     g_return_val_if_fail (logicalcolorname != NULL, 0);
363
364     ld = g_malloc (sizeof (HildonLogicalData));
365
366     ld->rcflags = rcflags;
367     ld->state = state;
368     ld->logicalcolorstring = g_strdup (logicalcolorname);
369     ld->logicalfontstring = NULL;
370
371     /* Disconnects the previously connected signals. That calls the closure notify
372      * and effectively disposes the allocated data (hildon_logical_data_free) */
373     g_signal_handlers_disconnect_matched (G_OBJECT (widget), G_SIGNAL_MATCH_FUNC, 
374             0, 0, NULL, 
375             G_CALLBACK (hildon_change_style_recursive_from_ld), NULL);
376
377     /* Change the colors now */
378     hildon_change_style_recursive_from_ld (widget, NULL, ld);
379
380     /* Connect to "style_set" so that the colors gets changed whenever theme */
381     signum = g_signal_connect_data (G_OBJECT (widget), "style_set",
382             G_CALLBACK (hildon_change_style_recursive_from_ld),
383             ld, (GClosureNotify) hildon_logical_data_free, 0);
384
385     return signum;
386 }
387
388