1 /* Helper functions for gtk-engines
2  *
3  * Copyright (C) 2006 Andrew Johnson <acjgenius@earthlink.net>
4  * Copyright (C) 2006-2007 Benjamin Berg <benjamin@sipsolutions.net>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  * Project contact: <gnome-themes-list@gnome.org>
21  *
22  *
23  * Written by Andrew Johnson <acjgenius@earthlink.net>
24  * Written by Benjamin Berg <benjamin@sipsolutions.net>
25  * modified by Andrea Cimitan <andrea.cimitan@gmail.com>
26  *
27  */
28 
29 #include <gtk/gtk.h>
30 
31 #include "general-support.h"
32 #include "widget-information.h"
33 #include "config.h"
34 #include <math.h>
35 #include <string.h>
36 
37 static gchar ge_widget_hints[] =
38 	"treeview\0"
39 	"treeview-header\0"
40 	"statusbar\0"
41 	"comboboxentry\0"
42 	"spinbutton\0"
43 	"scale\0"
44 	"vscale\0"
45 	"hscale\0"
46 	"scrollbar\0"
47 	"vscrollbar\0"
48 	"hscrollbar\0"
49 	"progressbar\0"
50 	"menubar\0";
51 
52 gboolean
ge_check_hint(GEHint hint,GQuark style_hint,GtkWidget * widget)53 ge_check_hint (GEHint      hint,
54                GQuark      style_hint,
55                GtkWidget  *widget)
56 {
57 	static GEHint quark_hint_lookup[GE_HINT_COUNT] = {0};
58 
59 	g_assert ((hint >= 0) && (hint < GE_HINT_COUNT));
60 
61 	/* Initilize lookup table */
62 	if (G_UNLIKELY (quark_hint_lookup[0] == 0))
63 	{
64 		guint i = 0;
65 		gchar *cur_hint_str = ge_widget_hints;
66 		while ((i < GE_HINT_COUNT) && cur_hint_str[0])
67 		{
68 			/* Can't use _from_static_string as the engine may be unloaded. */
69 			quark_hint_lookup[i] = g_quark_from_string (cur_hint_str);
70 			cur_hint_str += strlen(cur_hint_str) + 1;
71 			i++;
72 		}
73 		g_assert (i == GE_HINT_COUNT && cur_hint_str[0] == '\0');
74 	}
75 
76 
77 	if (quark_hint_lookup[hint] == style_hint)
78 		return TRUE;
79 
80 
81 	/* Try to decide based on other hints, eg. hscale is also a scale.  */
82 	if (hint == GE_HINT_SCALE)
83 		if (ge_check_hint (GE_HINT_VSCALE, style_hint, widget) ||
84 		    ge_check_hint (GE_HINT_HSCALE, style_hint, widget))
85 		    	return TRUE;
86 	if (hint == GE_HINT_SCROLLBAR)
87 		if (ge_check_hint (GE_HINT_VSCROLLBAR, style_hint, widget) ||
88 		    ge_check_hint (GE_HINT_HSCROLLBAR, style_hint, widget))
89 		    	return TRUE;
90 	if (hint == GE_HINT_TREEVIEW)
91 		if (ge_check_hint (GE_HINT_TREEVIEW_HEADER, style_hint, widget))
92 		    	return TRUE;
93 
94 
95 	/* These may be caused by applications so we never want to disable them.
96 	 * TODO: This does not catch the case where the theme uses appears-as-list
97 	 *       and the application turns it off again. Though this case
98 	 *       is even less likely. */
99 	switch (hint) {
100 		case GE_HINT_COMBOBOX_ENTRY:
101 			if (widget && ge_object_is_a (G_OBJECT (widget), "GtkComboBox"))
102 			{
103 				gboolean appears_as_list = FALSE;
104 
105 				gtk_widget_style_get (widget, "appears-as-list", &appears_as_list, NULL);
106 
107 				if (appears_as_list)
108 					return TRUE;
109 			}
110 		break;
111 		default:
112 		break;
113 	}
114 
115 
116 #ifdef ENABLE_WIDGET_CHECKS
117 	/* If a style_hint *was* set, and nothing matched, just give up right away.
118 	 * A theme shall either support it fully, or not at all. */
119 	if (style_hint != 0)
120 		return FALSE;
121 
122 	/* No widget? Just give up. Nothing we can do. */
123 	if (widget == NULL)
124 		return FALSE;
125 
126 	/* Try to do something based on the passed in widget pointer. */
127 	switch (hint) {
128 		case GE_HINT_TREEVIEW:
129 			if (widget->parent && (ge_object_is_a (G_OBJECT (widget->parent), "GtkTreeView")))
130 				return TRUE;
131 		break;
132 		case GE_HINT_TREEVIEW_HEADER:
133 			if (ge_object_is_a (G_OBJECT (widget), "GtkButton") && widget->parent &&
134 			    (ge_object_is_a (G_OBJECT (widget->parent), "GtkTreeView") || ge_object_is_a (G_OBJECT (widget->parent), "GtkCList") ||
135 			     ge_object_is_a (G_OBJECT (widget->parent), "GtkCTree")))
136 				return TRUE;
137 			if (widget->parent && ge_object_is_a (G_OBJECT (widget->parent), "ETreeView"))
138 				return TRUE;
139 		break;
140 		case GE_HINT_COMBOBOX_ENTRY:
141 			if (ge_is_in_combo_box (widget))
142 				return TRUE;
143 		break;
144 		case GE_HINT_SPINBUTTON:
145 			if (ge_object_is_a (G_OBJECT (widget), "GtkSpinButton"))
146 				return TRUE;
147 		break;
148 		case GE_HINT_STATUSBAR:
149 			if (widget->parent && ge_object_is_a (G_OBJECT (widget), "GtkStatusbar"))
150 				return TRUE;
151 		break;
152 		case GE_HINT_SCALE:
153 			if (ge_object_is_a (G_OBJECT (widget), "GtkScale"))
154 				return TRUE;
155 		break;
156 		case GE_HINT_HSCALE:
157 			if (ge_object_is_a (G_OBJECT (widget), "GtkHScale"))
158 				return TRUE;
159 		break;
160 		case GE_HINT_VSCALE:
161 			if (ge_object_is_a (G_OBJECT (widget), "GtkVScale"))
162 				return TRUE;
163 		break;
164 		case GE_HINT_SCROLLBAR:
165 			if (ge_object_is_a (G_OBJECT (widget), "GtkScrollbar"))
166 				return TRUE;
167 		break;
168 		case GE_HINT_HSCROLLBAR:
169 			if (ge_object_is_a (G_OBJECT (widget), "GtkHScrollbar"))
170 				return TRUE;
171 		break;
172 		case GE_HINT_VSCROLLBAR:
173 			if (ge_object_is_a (G_OBJECT (widget), "GtkVScrollbar"))
174 				return TRUE;
175 		break;
176 		case GE_HINT_PROGRESSBAR:
177 			if (ge_object_is_a (G_OBJECT (widget), "GtkProgressBar"))
178 				return TRUE;
179 		break;
180 		case GE_HINT_MENUBAR:
181 			if (ge_object_is_a (G_OBJECT (widget), "GtkMenuBar") ||
182 			    ge_object_is_a (G_OBJECT (widget->parent), "GtkMenuBar"))
183 				return TRUE;
184 		break;
185 
186 		default:
187 		break;
188 	}
189 
190 #endif
191 
192 
193 	return FALSE;
194 }
195 
196 /* Widget Type Lookups/Macros
197 
198    Based on/modified from functions in
199    Smooth-Engine.
200 */
201 gboolean
ge_object_is_a(const GObject * object,const gchar * type_name)202 ge_object_is_a (const GObject * object, const gchar * type_name)
203 {
204   gboolean result = FALSE;
205 
206   if ((object))
207     {
208       GType tmp = g_type_from_name (type_name);
209 
210       if (tmp)
211 	result = g_type_check_instance_is_a ((GTypeInstance *) object, tmp);
212     }
213 
214   return result;
215 }
216 
217 gboolean
ge_is_combo_box_entry(GtkWidget * widget)218 ge_is_combo_box_entry (GtkWidget * widget)
219 {
220   gboolean result = FALSE;
221 
222   if ((widget) && (widget->parent))
223     {
224       if (GE_IS_COMBO_BOX_ENTRY (widget->parent))
225 	result = TRUE;
226       else
227 	result = ge_is_combo_box_entry (widget->parent);
228     }
229   return result;
230 }
231 
232 static gboolean
ge_combo_box_is_using_list(GtkWidget * widget)233 ge_combo_box_is_using_list (GtkWidget * widget)
234 {
235   gboolean result = FALSE;
236 
237   if (GE_IS_COMBO_BOX (widget))
238     gtk_widget_style_get (widget, "appears-as-list", &result, NULL);
239 
240   return result;
241 }
242 
243 gboolean
ge_is_combo_box(GtkWidget * widget,gboolean as_list)244 ge_is_combo_box (GtkWidget * widget, gboolean as_list)
245 {
246   gboolean result = FALSE;
247 
248   if ((widget) && (widget->parent))
249     {
250       if (GE_IS_COMBO_BOX (widget->parent))
251         {
252           if (as_list)
253             result = (ge_combo_box_is_using_list(widget->parent));
254           else
255             result = (!ge_combo_box_is_using_list(widget->parent));
256         }
257       else
258 	result = ge_is_combo_box (widget->parent, as_list);
259     }
260   return result;
261 }
262 
263 gboolean
ge_is_combo(GtkWidget * widget)264 ge_is_combo (GtkWidget * widget)
265 {
266   gboolean result = FALSE;
267 
268   if ((widget) && (widget->parent))
269     {
270       if (GE_IS_COMBO (widget->parent))
271 	result = TRUE;
272       else
273 	result = ge_is_combo (widget->parent);
274     }
275   return result;
276 }
277 
278 gboolean
ge_is_in_combo_box(GtkWidget * widget)279 ge_is_in_combo_box (GtkWidget * widget)
280 {
281   return ((ge_is_combo (widget) || ge_is_combo_box (widget, TRUE) || ge_is_combo_box_entry (widget)));
282 }
283 
284 gboolean
ge_is_toolbar_item(GtkWidget * widget)285 ge_is_toolbar_item (GtkWidget * widget)
286 {
287   gboolean result = FALSE;
288 
289   if ((widget) && (widget->parent)) {
290     if ((GE_IS_BONOBO_TOOLBAR (widget->parent))
291 	|| (GE_IS_BONOBO_DOCK_ITEM (widget->parent))
292 	|| (GE_IS_EGG_TOOLBAR (widget->parent))
293 	|| (GE_IS_TOOLBAR (widget->parent))
294 	|| (GE_IS_HANDLE_BOX (widget->parent)))
295       result = TRUE;
296     else
297       result = ge_is_toolbar_item (widget->parent);
298   }
299   return result;
300 }
301 
302 gboolean
ge_is_panel_widget_item(GtkWidget * widget)303 ge_is_panel_widget_item (GtkWidget * widget)
304 {
305   gboolean result = FALSE;
306 
307   if ((widget) && (widget->parent))
308     {
309       if (GE_IS_PANEL_WIDGET (widget->parent))
310 	result = TRUE;
311       else
312 	result = ge_is_panel_widget_item (widget->parent);
313     }
314   return result;
315 }
316 
317 gboolean
ge_is_bonobo_dock_item(GtkWidget * widget)318 ge_is_bonobo_dock_item (GtkWidget * widget)
319 {
320   gboolean result = FALSE;
321 
322   if ((widget))
323     {
324       if (GE_IS_BONOBO_DOCK_ITEM(widget) || GE_IS_BONOBO_DOCK_ITEM (widget->parent))
325 	result = TRUE;
326       else if (GE_IS_BOX(widget) || GE_IS_BOX(widget->parent))
327         {
328           GtkContainer *box = GE_IS_BOX(widget)?GTK_CONTAINER(widget):GTK_CONTAINER(widget->parent);
329           GList *children = NULL, *child = NULL;
330 
331           children = gtk_container_get_children(box);
332 
333           for (child = g_list_first(children); child; child = g_list_next(child))
334             {
335 	      if (GE_IS_BONOBO_DOCK_ITEM_GRIP(child->data))
336 	        {
337 	          result = TRUE;
338 	          child = NULL;
339 	        }
340             }
341 
342           if (children)
343   	    g_list_free(children);
344 	}
345     }
346   return result;
347 }
348 
349 static GtkWidget *
ge_find_combo_box_entry_widget(GtkWidget * widget)350 ge_find_combo_box_entry_widget (GtkWidget * widget)
351 {
352   GtkWidget *result = NULL;
353 
354   if (widget)
355     {
356       if (GE_IS_COMBO_BOX_ENTRY (widget))
357 	result = widget;
358       else
359 	result = ge_find_combo_box_entry_widget (widget->parent);
360     }
361 
362   return result;
363 }
364 
365 static GtkWidget *
ge_find_combo_box_widget(GtkWidget * widget,gboolean as_list)366 ge_find_combo_box_widget (GtkWidget * widget, gboolean as_list)
367 {
368   GtkWidget *result = NULL;
369 
370   if (widget)
371     {
372       if (GE_IS_COMBO_BOX (widget))
373         {
374           if (as_list)
375             result = (ge_combo_box_is_using_list(widget))?widget:NULL;
376           else
377             result = (!ge_combo_box_is_using_list(widget))?widget:NULL;
378         }
379       else
380 	result = ge_find_combo_box_widget (widget->parent, as_list);
381     }
382   return result;
383 }
384 
385 static GtkWidget *
ge_find_combo_widget(GtkWidget * widget)386 ge_find_combo_widget (GtkWidget * widget)
387 {
388   GtkWidget *result = NULL;
389 
390   if (widget)
391     {
392       if (GE_IS_COMBO (widget))
393 	result = widget;
394       else
395 	result = ge_find_combo_widget(widget->parent);
396     }
397   return result;
398 }
399 
400 GtkWidget*
ge_find_combo_box_widget_parent(GtkWidget * widget)401 ge_find_combo_box_widget_parent (GtkWidget * widget)
402 {
403    GtkWidget *result = NULL;
404 
405    if (!result)
406      result = ge_find_combo_widget(widget);
407 
408    if (!result)
409      result = ge_find_combo_box_widget(widget, TRUE);
410 
411    if (!result)
412      result = ge_find_combo_box_entry_widget(widget);
413 
414   return result;
415 }
416 
417 /***********************************************
418  * option_menu_get_props -
419  *
420  *   Find Option Menu Size and Spacing
421  *
422  *   Taken from Smooth
423  ***********************************************/
424 void
ge_option_menu_get_props(GtkWidget * widget,GtkRequisition * indicator_size,GtkBorder * indicator_spacing)425 ge_option_menu_get_props (GtkWidget * widget,
426                           GtkRequisition * indicator_size,
427                           GtkBorder * indicator_spacing)
428 {
429   GtkRequisition default_size = { 9, 5 };
430   GtkBorder default_spacing = { 7, 5, 2, 2 };
431   GtkRequisition *tmp_size = NULL;
432   GtkBorder *tmp_spacing = NULL;
433 
434   if ((widget) && GE_IS_OPTION_MENU(widget))
435     gtk_widget_style_get (widget,
436 			  "indicator_size", &tmp_size,
437 			  "indicator_spacing", &tmp_spacing, NULL);
438 
439   if (tmp_size)
440     {
441       *indicator_size = *tmp_size;
442       gtk_requisition_free (tmp_size);
443     }
444   else
445     *indicator_size = default_size;
446 
447   if (tmp_spacing)
448     {
449       *indicator_spacing = *tmp_spacing;
450       gtk_border_free (tmp_spacing);
451     }
452   else
453     *indicator_spacing = default_spacing;
454 }
455 
456 void
ge_button_get_default_border(GtkWidget * widget,GtkBorder * border)457 ge_button_get_default_border (GtkWidget *widget,
458                               GtkBorder *border)
459 {
460 	GtkBorder default_border = {1, 1, 1, 1};
461 	GtkBorder *tmp_border = NULL;
462 
463 	if (widget && GE_IS_BUTTON (widget))
464 		gtk_widget_style_get (widget, "default-border", &tmp_border, NULL);
465 
466 	if (tmp_border)
467 	{
468 		*border = *tmp_border;
469 		gtk_border_free (tmp_border);
470 	}
471 	else
472 	{
473 		*border = default_border;
474 	}
475 }
476 
477 gboolean
ge_widget_is_ltr(GtkWidget * widget)478 ge_widget_is_ltr (GtkWidget *widget)
479 {
480 	GtkTextDirection dir = GTK_TEXT_DIR_NONE;
481 
482 	if (GE_IS_WIDGET (widget))
483 		dir = gtk_widget_get_direction (widget);
484 
485 	if (dir == GTK_TEXT_DIR_NONE)
486 		dir = gtk_widget_get_default_direction ();
487 
488 	if (dir == GTK_TEXT_DIR_RTL)
489 		return FALSE;
490 	else
491 		return TRUE;
492 }
493 
494 guint
ge_rc_parse_hint(GScanner * scanner,GQuark * quark)495 ge_rc_parse_hint (GScanner    *scanner,
496                   GQuark      *quark)
497 {
498 	guint token;
499 
500 	/* Skip 'hint' */
501 	token = g_scanner_get_next_token(scanner);
502 
503 	token = g_scanner_get_next_token(scanner);
504 	if (token != G_TOKEN_EQUAL_SIGN)
505 	   return G_TOKEN_EQUAL_SIGN;
506 
507 	token = g_scanner_get_next_token(scanner);
508 	if (token != G_TOKEN_STRING)
509 	   return G_TOKEN_STRING;
510 
511 	*quark = g_quark_from_string (scanner->value.v_string);
512 
513 	return G_TOKEN_NONE;
514 }
515 
516