2 * This file is a part of hildon
4 * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
6 * Contact: Rodrigo Novo <rodrigo.novo@nokia.com>
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, or (at your option) any later version.
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.
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
26 * SECTION:hildon-helper
27 * @short_description: A collection of useful utilities and functions.
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.
34 #undef HILDON_DISABLE_DEPRECATED
40 #include "hildon-helper.h"
41 #include "hildon-banner.h"
43 #define HILDON_FINGER_PRESSURE_THRESHOLD 0.4
45 #define HILDON_FINGER_BUTTON 8
47 #define HILDON_FINGER_ALT_BUTTON 1
49 #define HILDON_FINGER_ALT_MASK GDK_MOD4_MASK
51 #define HILDON_FINGER_SIMULATE_BUTTON 2
53 struct _HildonLogicalElement
55 gboolean is_color; /* If FALSE, it's a logical font def */
58 gchar *logical_color_name;
59 gchar *logical_font_name;
60 } typedef HildonLogicalElement;
63 hildon_logical_element_list_free (GSList *list)
65 GSList *iterator = list;
68 HildonLogicalElement *element = (HildonLogicalElement *) iterator->data;
70 g_free (element->logical_color_name);
71 g_free (element->logical_font_name);
72 g_slice_free (HildonLogicalElement, element);
74 iterator = iterator->next;
77 /* Free the list itself */
82 hildon_helper_logical_data_quark (void)
84 static GQuark quark = 0;
86 if (G_UNLIKELY (quark == 0))
87 quark = g_quark_from_static_string ("hildon-logical-data");
92 static HildonLogicalElement*
93 attach_blank_element (GtkWidget *widget,
96 gboolean first = (*style_list == NULL) ? TRUE : FALSE;
98 HildonLogicalElement *element = g_slice_new (HildonLogicalElement);
100 element->is_color = FALSE;
101 element->rc_flags = 0;
103 element->logical_color_name = NULL;
104 element->logical_font_name = NULL;
106 *style_list = g_slist_append (*style_list, element);
109 g_object_set_qdata_full (G_OBJECT (widget), hildon_helper_logical_data_quark (), *style_list, (GDestroyNotify) hildon_logical_element_list_free);
115 attach_new_font_element (GtkWidget *widget,
116 const gchar *font_name)
118 GSList *style_list = g_object_get_qdata (G_OBJECT (widget), hildon_helper_logical_data_quark ());
119 HildonLogicalElement *element = NULL;
121 /* Try to find an element that already sets a font */
122 GSList *iterator = style_list;
124 element = (HildonLogicalElement *) iterator->data;
126 if (element->is_color == FALSE) {
128 g_free (element->logical_font_name);
129 element->logical_font_name = g_strdup (font_name);
133 iterator = iterator->next;
136 /* It was not found so we need to create a new one and attach it */
137 element = attach_blank_element (widget, &style_list);
138 element->is_color = FALSE;
139 element->logical_font_name = g_strdup (font_name);
144 attach_new_color_element (GtkWidget *widget,
147 const gchar *color_name)
149 GSList *style_list = g_object_get_qdata (G_OBJECT (widget), hildon_helper_logical_data_quark ());
150 HildonLogicalElement *element = NULL;
152 /* Try to find an element that has same flags and state */
153 GSList *iterator = style_list;
155 element = (HildonLogicalElement *) iterator->data;
157 if (element->rc_flags == flags &&
158 element->state == state &&
159 element->is_color == TRUE) {
161 g_free (element->logical_color_name);
162 element->logical_color_name = g_strdup (color_name);
166 iterator = iterator->next;
169 /* It was not found so we need to create a new one and attach it */
170 element = attach_blank_element (widget, &style_list);
171 element->is_color = TRUE;
172 element->state = state;
173 element->rc_flags = flags;
174 element->logical_color_name = g_strdup (color_name);
179 hildon_change_style_recursive_from_list (GtkWidget *widget,
180 GtkStyle *prev_style,
183 g_assert (GTK_IS_WIDGET (widget));
185 /* Change the style for child widgets */
186 if (GTK_IS_CONTAINER (widget)) {
187 GList *iterator, *children;
188 children = gtk_container_get_children (GTK_CONTAINER (widget));
189 for (iterator = children; iterator != NULL; iterator = g_list_next (iterator))
190 hildon_change_style_recursive_from_list (GTK_WIDGET (iterator->data), prev_style, list);
191 g_list_free (children);
194 /* gtk_widget_modify_*() emit "style_set" signals, so if we got here from
195 "style_set" signal, we need to block this function from being called
196 again or we get into inifinite loop.
198 FIXME: Compiling with gcc > 3.3 and -pedantic won't allow
199 conversion between function and object pointers. GLib API however
200 requires an object pointer for a function, so we have to work
202 See http://bugzilla.gnome.org/show_bug.cgi?id=310175
206 g_signal_handlers_block_matched (G_OBJECT (widget), G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC,
207 g_signal_lookup ("style_set", G_TYPE_FROM_INSTANCE (widget)),
209 (gpointer) hildon_change_style_recursive_from_list,
212 /* We iterate over all list elements and apply each style
215 GSList *iterator = list;
218 HildonLogicalElement *element = (HildonLogicalElement *) iterator->data;
220 if (element->is_color == TRUE) {
222 /* Changing logical color */
224 gtk_widget_ensure_style (widget);
225 if (gtk_style_lookup_color (widget->style, element->logical_color_name, &color) == TRUE) {
227 switch (element->rc_flags)
230 gtk_widget_modify_fg (widget, element->state, &color);
234 gtk_widget_modify_bg (widget, element->state, &color);
238 gtk_widget_modify_text (widget, element->state, &color);
242 gtk_widget_modify_base (widget, element->state, &color);
248 /* Changing logical font */
249 GtkStyle *font_style = gtk_rc_get_style_by_paths (gtk_settings_get_default (), element->logical_font_name, NULL, G_TYPE_NONE);
250 if (font_style != NULL) {
251 PangoFontDescription *font_desc = font_style->font_desc;
253 if (font_desc != NULL)
254 gtk_widget_modify_font (widget, font_desc);
258 iterator = iterator->next;
261 /* FIXME: Compilation workaround for gcc > 3.3 + -pedantic again */
264 g_signal_handlers_unblock_matched (G_OBJECT (widget), G_SIGNAL_MATCH_ID | G_SIGNAL_MATCH_FUNC,
265 g_signal_lookup ("style_set", G_TYPE_FROM_INSTANCE (widget)),
267 (gpointer) hildon_change_style_recursive_from_list,
272 * hildon_helper_event_button_is_finger:
273 * @event: A #GtkEventButton to check
275 * Checks if the given button event is a finger event.
277 * Return value: TRUE if the event is a finger event.
280 hildon_helper_event_button_is_finger (GdkEventButton *event)
284 if (gdk_event_get_axis ((GdkEvent*) event, GDK_AXIS_PRESSURE, &pressure) &&
285 pressure > HILDON_FINGER_PRESSURE_THRESHOLD)
288 if (event->button == HILDON_FINGER_BUTTON)
291 if (event->button == HILDON_FINGER_ALT_BUTTON &&
292 event->state & HILDON_FINGER_ALT_MASK)
295 if (event->button == HILDON_FINGER_SIMULATE_BUTTON)
302 * hildon_helper_set_logical_font:
303 * @widget: a #GtkWidget to assign this logical font for.
304 * @logicalfontname: a gchar* with the logical font name to assign to the widget.
306 * This function assigns a defined logical font to the @widget and all its child widgets.
307 * it also connects to the "style_set" signal which will retrieve & assign the new font
308 * for the given logical name each time the theme is changed
309 * The returned signal id can be used to disconnect the signal.
310 * When calling multiple times the previous signal (obtained by calling this function) is disconnected
311 * automatically and should not be used.
313 * Return value: the signal id that is triggered every time theme is changed. 0 if font set failed.
316 hildon_helper_set_logical_font (GtkWidget *widget,
317 const gchar *logicalfontname)
322 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
323 g_return_val_if_fail (logicalfontname != NULL, 0);
325 list = attach_new_font_element (widget, logicalfontname);
327 /* Disconnects the previously connected signals. That calls the closure notify
328 * and effectively disposes the allocated data (hildon_logical_data_free) */
329 g_signal_handlers_disconnect_matched (G_OBJECT (widget), G_SIGNAL_MATCH_FUNC,
331 G_CALLBACK (hildon_change_style_recursive_from_list), NULL);
333 /* Change the font now */
334 hildon_change_style_recursive_from_list (widget, NULL, list);
336 /* Connect to "style_set" so that the font gets changed whenever theme changes. */
337 signum = g_signal_connect_data (G_OBJECT (widget), "style_set",
338 G_CALLBACK (hildon_change_style_recursive_from_list),
345 hildon_helper_insensitive_message_quark (void)
347 static GQuark quark = 0;
349 if (G_UNLIKELY (quark == 0))
350 quark = g_quark_from_static_string ("hildon-insensitive-message");
356 show_insensitive_message (GtkWidget *widget,
359 gchar *message = NULL;
361 g_assert (GTK_IS_WIDGET (widget));
363 message = (gchar*) g_object_get_qdata (G_OBJECT (widget),
364 hildon_helper_insensitive_message_quark ());
367 hildon_banner_show_information (widget, NULL, message);
372 * hildon_helper_set_insensitive_message:
373 * @widget: A #GtkWidget to assign a banner to
374 * @message: A message to display to the user
376 * This function assigns an insensitive message to a @widget. When the @widget is
377 * in an insensitive state and the user activates it, the @message will be displayed
378 * using a standard #HildonBanner.
380 * Deprecated: As of hildon 2.2, it is strongly discouraged to use insensitive messages.
383 hildon_helper_set_insensitive_message (GtkWidget *widget,
384 const gchar *message)
386 g_return_if_fail (GTK_IS_WIDGET (widget));
388 /* Clean up any previous instance of the insensitive message */
389 g_signal_handlers_disconnect_matched (G_OBJECT (widget), G_SIGNAL_MATCH_FUNC,
391 G_CALLBACK (show_insensitive_message), NULL);
393 /* We need to dup the string because the pointer might not be valid when the
394 insensitive-press signal callback is executed */
395 g_object_set_qdata_full (G_OBJECT (widget), hildon_helper_insensitive_message_quark (),
396 (gpointer)g_strdup (message),
399 if (message != NULL) {
400 g_signal_connect (G_OBJECT (widget), "insensitive-press",
401 G_CALLBACK (show_insensitive_message), NULL);
406 * hildon_helper_set_insensitive_messagef:
407 * @widget: A #GtkWidget to assign a banner to
408 * @format: a printf-like format string
409 * @Varargs: arguments for the format string
411 * A version of hildon_helper_set_insensitive_message with string formatting.
413 * Deprecated: As of hildon 2.2, it is strongly discouraged to use insensitive messages.
416 hildon_helper_set_insensitive_messagef (GtkWidget *widget,
420 g_return_if_fail (GTK_IS_WIDGET (widget));
425 va_start (args, format);
426 message = g_strdup_vprintf (format, args);
429 hildon_helper_set_insensitive_message (widget, message);
435 * hildon_helper_set_logical_color:
436 * @widget: A #GtkWidget to assign this logical font for.
437 * @rcflags: #GtkRcFlags enumeration defining whether to assign to FG, BG, TEXT or BASE style.
438 * @state: #GtkStateType indicating to which state to assign the logical color
439 * @logicalcolorname: A gchar* with the logical font name to assign to the widget.
441 * This function assigns a defined logical color to the @widget and all it's child widgets.
442 * It also connects to the "style_set" signal which will retrieve & assign the new color
443 * for the given logical name each time the theme is changed.
444 * The returned signal id can be used to disconnect the signal.
445 * When calling multiple times the previous signal (obtained by calling this function) is disconnected
446 * automatically and should not be used.
448 * Example : If the style you want to modify is bg[NORMAL] then set rcflags to GTK_RC_BG and state to GTK_STATE_NORMAL.
450 * Return value: The signal id that is triggered every time theme is changed. 0 if color set failed.
453 hildon_helper_set_logical_color (GtkWidget *widget,
456 const gchar *logicalcolorname)
461 g_return_val_if_fail (GTK_IS_WIDGET (widget), 0);
462 g_return_val_if_fail (logicalcolorname != NULL, 0);
464 list = attach_new_color_element (widget, rcflags, state, logicalcolorname);
466 /* Disconnects the previously connected signals. */
467 g_signal_handlers_disconnect_matched (G_OBJECT (widget), G_SIGNAL_MATCH_FUNC,
469 G_CALLBACK (hildon_change_style_recursive_from_list), NULL);
471 /* Change the colors now */
472 hildon_change_style_recursive_from_list (widget, NULL, list);
474 /* Connect to "style_set" so that the colors gets changed whenever theme */
475 signum = g_signal_connect_data (G_OBJECT (widget), "style_set",
476 G_CALLBACK (hildon_change_style_recursive_from_list),
484 * hildon_helper_set_thumb_scrollbar:
485 * @win: A #GtkScrolledWindow to use as target
486 * @thumb: TRUE to enable the thumb scrollbar, FALSE to disable
488 * This function enables a thumb scrollbar on a given scrolled window. It'll convert the
489 * existing normal scrollbar into a larger, finger-usable scrollbar that works without a stylus.
490 * As fingerable list rows are fairly high, consider using the whole available vertical space
491 * of your application for the content in order to have as many rows as possible
492 * visible on the screen at once.
494 * Finger-Sized scrollbar should always be used together with finger-sized content.
497 hildon_helper_set_thumb_scrollbar (GtkScrolledWindow *win,
500 g_return_if_fail (GTK_IS_SCROLLED_WINDOW (win));
503 gtk_widget_set_name (win->vscrollbar, (thumb) ? "hildon-thumb-scrollbar" : NULL);
507 * hildon_format_file_size_for_display:
508 * @size: a size in bytes
510 * Formats a file size in bytes for display in applications.
512 * This function is similar to g_format_file_size_for_display() but the
513 * translations are from Maemo so might differ slightly.
518 hildon_format_file_size_for_display (goffset size)
520 #define _HFM(string) g_dgettext ("hildon-fm", string)
523 return g_strdup_printf (_HFM ("ckdg_va_properties_size_kb"),
525 else if (size < 100 * 1024)
526 return g_strdup_printf (_HFM ("ckdg_va_properties_size_1kb_99kb"),
528 else if (size < 1024 * 1024)
529 return g_strdup_printf (_HFM ("ckdg_va_properties_size_100kb_1mb"),
531 else if (size < 10 * 1024 * 1024)
532 return g_strdup_printf (_HFM ("ckdg_va_properties_size_1mb_10mb"),
533 size / (1024.0f * 1024.0f));
534 else if (size < 1024 * 1024 * 1024)
535 return g_strdup_printf (_HFM ("ckdg_va_properties_size_10mb_1gb"),
536 size / (1024.0f * 1024.0f));
538 return g_strdup_printf (_HFM ("ckdg_va_properties_size_1gb_or_greater"),
539 size / (1024.0f * 1024.0f * 1024.0f));