1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2014 Hiroyuki Yamamoto
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
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 General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23 
24 #include <glib.h>
25 #include <glib/gi18n.h>
26 #include <gdk/gdkkeysyms.h>
27 #include <gdk/gdk.h>
28 #include <gtk/gtk.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdarg.h>
32 
33 #ifdef G_OS_WIN32
34 #  include <pango/pangowin32.h>
35 #endif
36 
37 #include "gtkutils.h"
38 #include "utils.h"
39 #include "codeconv.h"
40 #include "menu.h"
41 
gtkut_get_str_size(GtkWidget * widget,const gchar * str,gint * width,gint * height)42 gboolean gtkut_get_str_size(GtkWidget *widget, const gchar *str,
43 			    gint *width, gint *height)
44 {
45 	PangoLayout *layout;
46 
47 	g_return_val_if_fail(GTK_IS_WIDGET(widget), FALSE);
48 
49 	layout = gtk_widget_create_pango_layout(widget, str);
50 	g_return_val_if_fail(layout, FALSE);
51 	pango_layout_get_pixel_size(layout, width, height);
52 	g_object_unref(layout);
53 
54 	return TRUE;
55 }
56 
gtkut_get_font_size(GtkWidget * widget,gint * width,gint * height)57 gboolean gtkut_get_font_size(GtkWidget *widget, gint *width, gint *height)
58 {
59 	const gchar *str = "Abcdef";
60 	gboolean ret;
61 
62 	ret = gtkut_get_str_size(widget, str, width, height);
63 	if (ret && width)
64 		*width = *width / g_utf8_strlen(str, -1);
65 
66 	return ret;
67 }
68 
gtkut_get_default_font_desc(void)69 PangoFontDescription *gtkut_get_default_font_desc(void)
70 {
71 	static PangoFontDescription *font_desc = NULL;
72 
73 	if (!font_desc) {
74 		GtkWidget *window;
75 
76 		window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
77 		gtk_widget_ensure_style(window);
78 		font_desc = pango_font_description_copy
79 			(window->style->font_desc);
80 		gtk_object_sink(GTK_OBJECT(window));
81 	}
82 
83 	return pango_font_description_copy(font_desc);
84 }
85 
gtkut_widget_set_small_font_size(GtkWidget * widget)86 void gtkut_widget_set_small_font_size(GtkWidget *widget)
87 {
88 	PangoFontDescription *font_desc;
89 	gint size;
90 
91 	g_return_if_fail(widget != NULL);
92 	g_return_if_fail(widget->style != NULL);
93 
94 	font_desc = gtkut_get_default_font_desc();
95 	size = pango_font_description_get_size(font_desc);
96 	pango_font_description_set_size(font_desc, size * PANGO_SCALE_SMALL);
97 	gtk_widget_modify_font(widget, font_desc);
98 	pango_font_description_free(font_desc);
99 }
100 
gtkut_font_can_load(const gchar * str)101 gboolean gtkut_font_can_load(const gchar *str)
102 {
103 #ifdef G_OS_WIN32
104 	PangoFontDescription *desc;
105 	PangoContext *context;
106 	PangoFont *font;
107 	gboolean can_load = FALSE;
108 
109 	desc = pango_font_description_from_string(str);
110 	if (desc) {
111 		context = pango_win32_get_context();
112 		font = pango_context_load_font(context, desc);
113 		if (font) {
114 			can_load = TRUE;
115 			g_object_unref(font);
116 		}
117 		g_object_unref(context);
118 		pango_font_description_free(desc);
119 	}
120 
121 	return can_load;
122 #elif defined(__APPLE__)
123 	PangoFontDescription *desc;
124 	PangoContext *context;
125 	PangoFont *font;
126 	gboolean can_load = FALSE;
127 
128 	desc = pango_font_description_from_string(str);
129 	if (desc) {
130 		context = gdk_pango_context_get_for_screen
131 			(gdk_screen_get_default());
132 		font = pango_context_load_font(context, desc);
133 		if (font) {
134 			can_load = TRUE;
135 			g_object_unref(font);
136 		}
137 		g_object_unref(context);
138 		pango_font_description_free(desc);
139 	}
140 
141 	return can_load;
142 #else
143 	return FALSE;
144 #endif
145 }
146 
147 static gdouble system_dpi = 0.0;
148 
gtkut_get_dpi(void)149 gdouble gtkut_get_dpi(void)
150 {
151 	gdouble dp, di;
152 
153 	if (system_dpi > 0.0)
154 		return system_dpi;
155 
156 	//dpi = gdk_screen_get_resolution(gdk_screen_get_default());
157 
158 	dp = gdk_screen_get_height(gdk_screen_get_default());
159 	di = gdk_screen_get_height_mm(gdk_screen_get_default()) / 25.4;
160 	system_dpi = dp / di;
161 
162 	debug_print("gtkut_get_dpi: dpi: %f\n", system_dpi);
163 	return system_dpi;
164 }
165 
gtkut_get_dpi_multiplier(void)166 gdouble gtkut_get_dpi_multiplier(void)
167 {
168 	gdouble dpi;
169 	gdouble mul;
170 
171 	dpi = gtkut_get_dpi();
172 
173 	/* 96 / 120 / 144 dpi */
174 	if (dpi > 142)
175 		mul = 1.5;
176 	else if (dpi > 118)
177 		mul = 1.25;
178 	else
179 		mul = 1.0;
180 
181 	return mul;
182 }
183 
gtkut_set_dpi(gdouble dpi)184 void gtkut_set_dpi(gdouble dpi)
185 {
186 	system_dpi = dpi;
187 }
188 
gtkut_convert_int_to_gdk_color(gint rgbvalue,GdkColor * color)189 void gtkut_convert_int_to_gdk_color(gint rgbvalue, GdkColor *color)
190 {
191 	g_return_if_fail(color != NULL);
192 
193 	color->pixel = 0L;
194 	color->red   = (int) (((gdouble)((rgbvalue & 0xff0000) >> 16) / 255.0) * 65535.0);
195 	color->green = (int) (((gdouble)((rgbvalue & 0x00ff00) >>  8) / 255.0) * 65535.0);
196 	color->blue  = (int) (((gdouble) (rgbvalue & 0x0000ff)        / 255.0) * 65535.0);
197 }
198 
199 static gboolean reverse_order = FALSE;
200 
gtkut_stock_button_set_set_reverse(gboolean reverse)201 void gtkut_stock_button_set_set_reverse(gboolean reverse)
202 {
203 	reverse_order = reverse;
204 }
205 
gtkut_stock_button_set_create(GtkWidget ** bbox,GtkWidget ** button1,const gchar * label1,GtkWidget ** button2,const gchar * label2,GtkWidget ** button3,const gchar * label3)206 void gtkut_stock_button_set_create(GtkWidget **bbox,
207 				   GtkWidget **button1, const gchar *label1,
208 				   GtkWidget **button2, const gchar *label2,
209 				   GtkWidget **button3, const gchar *label3)
210 {
211 	g_return_if_fail(bbox != NULL);
212 	g_return_if_fail(button1 != NULL);
213 
214 	*bbox = gtk_hbutton_box_new();
215 	gtk_button_box_set_layout(GTK_BUTTON_BOX(*bbox), GTK_BUTTONBOX_END);
216 	gtk_box_set_spacing(GTK_BOX(*bbox), 6 * gtkut_get_dpi_multiplier());
217 
218 	if (button3) {
219 		*button3 = gtk_button_new_from_stock(label3);
220 		GTK_WIDGET_SET_FLAGS(*button3, GTK_CAN_DEFAULT);
221 		gtk_box_pack_start(GTK_BOX(*bbox), *button3, FALSE, FALSE, 0);
222 		gtk_widget_show(*button3);
223 	}
224 
225 	if (button2) {
226 		*button2 = gtk_button_new_from_stock(label2);
227 		GTK_WIDGET_SET_FLAGS(*button2, GTK_CAN_DEFAULT);
228 		gtk_box_pack_start(GTK_BOX(*bbox), *button2, FALSE, FALSE, 0);
229 		gtk_widget_show(*button2);
230 	}
231 
232 	*button1 = gtk_button_new_from_stock(label1);
233 	GTK_WIDGET_SET_FLAGS(*button1, GTK_CAN_DEFAULT);
234 	gtk_box_pack_start(GTK_BOX(*bbox), *button1, FALSE, FALSE, 0);
235 	gtk_widget_show(*button1);
236 
237 	if (reverse_order)
238 		gtkut_box_set_reverse_order(GTK_BOX(*bbox), TRUE);
239 }
240 
gtkut_box_set_reverse_order(GtkBox * box,gboolean reverse)241 void gtkut_box_set_reverse_order(GtkBox *box, gboolean reverse)
242 {
243 	GList *cur;
244 	GList *new_order = NULL;
245 	gint pos = 0;
246 	gboolean is_reversed;
247 
248 	g_return_if_fail(box != NULL);
249 
250 	is_reversed = GPOINTER_TO_INT
251 		(g_object_get_data(G_OBJECT(box), "reverse-order"));
252 	if (is_reversed == reverse)
253 		return;
254 	g_object_set_data(G_OBJECT(box), "reverse-order",
255 			  GINT_TO_POINTER(reverse));
256 
257 	for (cur = box->children; cur != NULL; cur = cur->next) {
258 		GtkBoxChild *cinfo = cur->data;
259 		new_order = g_list_prepend(new_order, cinfo->widget);
260 	}
261 
262 	for (cur = new_order; cur != NULL; cur = cur->next) {
263 		GtkWidget *child = cur->data;
264 		gtk_box_reorder_child(box, child, pos++);
265 	}
266 
267 	g_list_free(new_order);
268 }
269 
combo_button_size_request(GtkWidget * widget,GtkRequisition * requisition,gpointer data)270 static void combo_button_size_request(GtkWidget *widget,
271 				      GtkRequisition *requisition,
272 				      gpointer data)
273 {
274 	ComboButton *combo = (ComboButton *)data;
275 
276 	if (combo->arrow->allocation.height != requisition->height)
277 		gtk_widget_set_size_request(combo->arrow,
278 					    -1, requisition->height);
279 }
280 
combo_button_enter(GtkWidget * widget,gpointer data)281 static void combo_button_enter(GtkWidget *widget, gpointer data)
282 {
283 	ComboButton *combo = (ComboButton *)data;
284 
285 	if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_PRELIGHT) {
286 		gtk_widget_set_state(combo->arrow, GTK_STATE_PRELIGHT);
287 		gtk_widget_queue_draw(combo->arrow);
288 	}
289 	if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_PRELIGHT) {
290 		gtk_widget_set_state(combo->button, GTK_STATE_PRELIGHT);
291 		gtk_widget_queue_draw(combo->button);
292 	}
293 }
294 
combo_button_leave(GtkWidget * widget,gpointer data)295 static void combo_button_leave(GtkWidget *widget, gpointer data)
296 {
297 	ComboButton *combo = (ComboButton *)data;
298 
299 	if (GTK_WIDGET_STATE(combo->arrow) != GTK_STATE_NORMAL) {
300 		gtk_widget_set_state(combo->arrow, GTK_STATE_NORMAL);
301 		gtk_widget_queue_draw(combo->arrow);
302 	}
303 	if (GTK_WIDGET_STATE(combo->button) != GTK_STATE_NORMAL) {
304 		gtk_widget_set_state(combo->button, GTK_STATE_NORMAL);
305 		gtk_widget_queue_draw(combo->button);
306 	}
307 }
308 
combo_button_arrow_pressed(GtkWidget * widget,GdkEventButton * event,gpointer data)309 static gint combo_button_arrow_pressed(GtkWidget *widget, GdkEventButton *event,
310 				       gpointer data)
311 {
312 	ComboButton *combo = (ComboButton *)data;
313 
314 	if (!event) return FALSE;
315 
316 	gtk_menu_popup(GTK_MENU(combo->menu), NULL, NULL,
317 		       menu_button_position, combo->button,
318 		       event->button, event->time);
319 
320 	return TRUE;
321 }
322 
combo_button_destroy(GtkWidget * widget,gpointer data)323 static void combo_button_destroy(GtkWidget *widget, gpointer data)
324 {
325 	ComboButton *combo = (ComboButton *)data;
326 
327 	gtk_object_destroy(GTK_OBJECT(combo->factory));
328 	g_free(combo);
329 }
330 
gtkut_combo_button_create(GtkWidget * button,GtkItemFactoryEntry * entries,gint n_entries,const gchar * path,gpointer data)331 ComboButton *gtkut_combo_button_create(GtkWidget *button,
332 				       GtkItemFactoryEntry *entries,
333 				       gint n_entries, const gchar *path,
334 				       gpointer data)
335 {
336 	ComboButton *combo;
337 	GtkWidget *arrow;
338 
339 	combo = g_new0(ComboButton, 1);
340 
341 	combo->arrow = gtk_button_new();
342 	arrow = gtk_arrow_new(GTK_ARROW_DOWN, GTK_SHADOW_OUT);
343 	gtk_widget_set_size_request(arrow, 7, -1);
344 	gtk_container_add(GTK_CONTAINER(combo->arrow), arrow);
345 	GTK_WIDGET_UNSET_FLAGS(combo->arrow, GTK_CAN_FOCUS);
346 	gtk_widget_show_all(combo->arrow);
347 
348 	combo->button = button;
349 	combo->menu = menu_create_items(entries, n_entries, path,
350 					&combo->factory, data);
351 	combo->data = data;
352 
353 	g_signal_connect(G_OBJECT(combo->button), "size_request",
354 			 G_CALLBACK(combo_button_size_request), combo);
355 #if 0
356 	g_signal_connect(G_OBJECT(combo->button), "enter",
357 			 G_CALLBACK(combo_button_enter), combo);
358 	g_signal_connect(G_OBJECT(combo->button), "leave",
359 			 G_CALLBACK(combo_button_leave), combo);
360 #endif
361 	g_signal_connect(G_OBJECT(combo->arrow), "enter",
362 			 G_CALLBACK(combo_button_enter), combo);
363 	g_signal_connect(G_OBJECT(combo->arrow), "leave",
364 			 G_CALLBACK(combo_button_leave), combo);
365 	g_signal_connect(G_OBJECT(combo->arrow), "button_press_event",
366 			 G_CALLBACK(combo_button_arrow_pressed), combo);
367 	g_signal_connect(G_OBJECT(combo->arrow), "destroy",
368 			 G_CALLBACK(combo_button_destroy), combo);
369 
370 	return combo;
371 }
372 
gtkut_ctree_get_nth_from_node(GtkCTree * ctree,GtkCTreeNode * node)373 gint gtkut_ctree_get_nth_from_node(GtkCTree *ctree, GtkCTreeNode *node)
374 {
375 	g_return_val_if_fail(ctree != NULL, -1);
376 	g_return_val_if_fail(node != NULL, -1);
377 
378 	return g_list_position(GTK_CLIST(ctree)->row_list, (GList *)node);
379 }
380 
gtkut_ctree_set_focus_row(GtkCTree * ctree,GtkCTreeNode * node)381 void gtkut_ctree_set_focus_row(GtkCTree *ctree, GtkCTreeNode *node)
382 {
383 	gtkut_clist_set_focus_row(GTK_CLIST(ctree),
384 				  gtkut_ctree_get_nth_from_node(ctree, node));
385 }
386 
gtkut_clist_set_focus_row(GtkCList * clist,gint row)387 void gtkut_clist_set_focus_row(GtkCList *clist, gint row)
388 {
389 	clist->focus_row = row;
390 	GTKUT_CTREE_REFRESH(clist);
391 }
392 
393 #ifdef G_OS_WIN32
vadjustment_changed(GtkAdjustment * adj,gpointer data)394 static void vadjustment_changed(GtkAdjustment *adj, gpointer data)
395 {
396 	GtkWidget *widget = GTK_WIDGET(data);
397 
398 	gtk_widget_queue_draw(widget);
399 }
400 #elif defined(__APPLE__)
clist_select_row(GtkCList * clist,gint row,gint column,GdkEventButton * event,gpointer data)401 static void clist_select_row(GtkCList *clist, gint row, gint column, GdkEventButton *event, gpointer data)
402 {
403 	gtk_widget_queue_draw(GTK_WIDGET(clist));
404 }
405 #endif
406 
gtkut_clist_set_redraw(GtkCList * clist)407 void gtkut_clist_set_redraw(GtkCList *clist)
408 {
409 #ifdef G_OS_WIN32
410 	if (clist->vadjustment) {
411 		g_signal_connect(G_OBJECT(clist->vadjustment), "changed",
412 				 G_CALLBACK(vadjustment_changed), clist);
413 	}
414 #elif defined(__APPLE__)
415 	g_signal_connect(G_OBJECT(clist), "select-row",
416 			 G_CALLBACK(clist_select_row), NULL);
417 #endif
418 }
419 
gtkut_tree_model_next(GtkTreeModel * model,GtkTreeIter * iter)420 gboolean gtkut_tree_model_next(GtkTreeModel *model, GtkTreeIter *iter)
421 {
422 	GtkTreeIter iter_, parent;
423 	gboolean valid;
424 
425 	if (gtk_tree_model_iter_children(model, &iter_, iter)) {
426 		*iter = iter_;
427 		return TRUE;
428 	}
429 
430 	iter_ = *iter;
431 	if (gtk_tree_model_iter_next(model, &iter_)) {
432 		*iter = iter_;
433 		return TRUE;
434 	}
435 
436 	iter_ = *iter;
437 	valid = gtk_tree_model_iter_parent(model, &parent, &iter_);
438 	while (valid) {
439 		iter_ = parent;
440 		if (gtk_tree_model_iter_next(model, &iter_)) {
441 			*iter = iter_;
442 			return TRUE;
443 		}
444 
445 		iter_ = parent;
446 		valid = gtk_tree_model_iter_parent(model, &parent, &iter_);
447 	}
448 
449 	return FALSE;
450 }
451 
gtkut_tree_model_prev(GtkTreeModel * model,GtkTreeIter * iter)452 gboolean gtkut_tree_model_prev(GtkTreeModel *model, GtkTreeIter *iter)
453 {
454 	GtkTreeIter iter_, child, next, parent;
455 	GtkTreePath *path;
456 	gboolean found = FALSE;
457 
458 	iter_ = *iter;
459 
460 	path = gtk_tree_model_get_path(model, &iter_);
461 
462 	if (gtk_tree_path_prev(path)) {
463 		gtk_tree_model_get_iter(model, &child, path);
464 
465 		while (gtk_tree_model_iter_has_child(model, &child)) {
466 			iter_ = child;
467 			gtk_tree_model_iter_children(model, &child, &iter_);
468 			next = child;
469 			while (gtk_tree_model_iter_next(model, &next))
470 				child = next;
471 		}
472 
473 		*iter = child;
474 		found = TRUE;
475 	} else if (gtk_tree_model_iter_parent(model, &parent, &iter_)) {
476 		*iter = parent;
477 		found = TRUE;
478 	}
479 
480 	gtk_tree_path_free(path);
481 
482 	return found;
483 }
484 
gtkut_tree_model_get_iter_last(GtkTreeModel * model,GtkTreeIter * iter)485 gboolean gtkut_tree_model_get_iter_last(GtkTreeModel *model, GtkTreeIter *iter)
486 {
487 	GtkTreeIter iter_, child, next;
488 
489 	if (!gtk_tree_model_get_iter_first(model, &iter_))
490 		return FALSE;
491 
492 	for (;;) {
493 		next = iter_;
494 		while (gtk_tree_model_iter_next(model, &next))
495 			iter_ = next;
496 		if (gtk_tree_model_iter_children(model, &child, &iter_))
497 			iter_ = child;
498 		else
499 			break;
500 	}
501 
502 	*iter = iter_;
503 	return TRUE;
504 }
505 
gtkut_tree_model_find_by_column_data(GtkTreeModel * model,GtkTreeIter * iter,GtkTreeIter * start,gint col,gpointer data)506 gboolean gtkut_tree_model_find_by_column_data(GtkTreeModel *model,
507 					      GtkTreeIter *iter,
508 					      GtkTreeIter *start,
509 					      gint col, gpointer data)
510 {
511 	gboolean valid;
512 	GtkTreeIter iter_;
513 	gpointer store_data;
514 
515 	if (start) {
516 		gtk_tree_model_get(model, start, col, &store_data, -1);
517 		if (store_data == data) {
518 			*iter = *start;
519 			return TRUE;
520 		}
521 		valid = gtk_tree_model_iter_children(model, &iter_, start);
522 	} else
523 		valid = gtk_tree_model_get_iter_first(model, &iter_);
524 
525 	while (valid) {
526 		if (gtkut_tree_model_find_by_column_data
527 			(model, iter, &iter_, col, data)) {
528 			return TRUE;
529 		}
530 
531 		valid = gtk_tree_model_iter_next(model, &iter_);
532 	}
533 
534 	return FALSE;
535 }
536 
gtkut_tree_model_foreach(GtkTreeModel * model,GtkTreeIter * start,GtkTreeModelForeachFunc func,gpointer user_data)537 void gtkut_tree_model_foreach(GtkTreeModel *model, GtkTreeIter *start,
538 			      GtkTreeModelForeachFunc func, gpointer user_data)
539 {
540 	gboolean valid = TRUE;
541 	GtkTreeIter iter;
542 	GtkTreePath *path;
543 
544 	g_return_if_fail(func != NULL);
545 
546 	if (!start) {
547 		gtk_tree_model_foreach(model, func, user_data);
548 		return;
549 	}
550 
551 	path = gtk_tree_model_get_path(model, start);
552 	func(model, path, start, user_data);
553 	gtk_tree_path_free(path);
554 
555 	valid = gtk_tree_model_iter_children(model, &iter, start);
556 	while (valid) {
557 		gtkut_tree_model_foreach(model, &iter, func, user_data);
558 		valid = gtk_tree_model_iter_next(model, &iter);
559 	}
560 }
561 
gtkut_tree_row_reference_get_iter(GtkTreeModel * model,GtkTreeRowReference * ref,GtkTreeIter * iter)562 gboolean gtkut_tree_row_reference_get_iter(GtkTreeModel *model,
563 					   GtkTreeRowReference *ref,
564 					   GtkTreeIter *iter)
565 {
566 	GtkTreePath *path;
567 	gboolean valid = FALSE;
568 
569 	if (ref) {
570 		path = gtk_tree_row_reference_get_path(ref);
571 		if (path) {
572 			valid = gtk_tree_model_get_iter(model, iter, path);
573 			gtk_tree_path_free(path);
574 		}
575 	}
576 
577 	return valid;
578 }
579 
gtkut_tree_row_reference_equal(GtkTreeRowReference * ref1,GtkTreeRowReference * ref2)580 gboolean gtkut_tree_row_reference_equal(GtkTreeRowReference *ref1,
581 					GtkTreeRowReference *ref2)
582 {
583 	GtkTreePath *path1, *path2;
584 	gint result;
585 
586 	if (ref1 == NULL || ref2 == NULL)
587 		return FALSE;
588 
589 	path1 = gtk_tree_row_reference_get_path(ref1);
590 	if (!path1)
591 		return FALSE;
592 	path2 = gtk_tree_row_reference_get_path(ref2);
593 	if (!path2) {
594 		gtk_tree_path_free(path1);
595 		return FALSE;
596 	}
597 
598 	result = gtk_tree_path_compare(path1, path2);
599 
600 	gtk_tree_path_free(path2);
601 	gtk_tree_path_free(path1);
602 
603 	return (result == 0);
604 }
605 
gtkut_tree_sortable_unset_sort_column_id(GtkTreeSortable * sortable)606 void gtkut_tree_sortable_unset_sort_column_id(GtkTreeSortable *sortable)
607 {
608 #if GTK_CHECK_VERSION(2, 6, 0)
609 	gtk_tree_sortable_set_sort_column_id
610 		(sortable, GTK_TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID,
611 		 GTK_SORT_ASCENDING);
612 #else
613 	GtkTreeStore *store = GTK_TREE_STORE(sortable);
614 
615 	g_return_if_fail(GTK_IS_TREE_STORE(sortable));
616 
617 	if (store->sort_column_id == -2 && store->order == GTK_SORT_ASCENDING)
618 		return;
619 
620 	store->sort_column_id = -2;
621 	store->order = GTK_SORT_ASCENDING;
622 
623 	gtk_tree_sortable_sort_column_changed(sortable);
624 #endif
625 }
626 
gtkut_tree_view_find_collapsed_parent(GtkTreeView * treeview,GtkTreeIter * parent,GtkTreeIter * iter)627 gboolean gtkut_tree_view_find_collapsed_parent(GtkTreeView *treeview,
628 					       GtkTreeIter *parent,
629 					       GtkTreeIter *iter)
630 {
631 	GtkTreeModel *model;
632 	GtkTreeIter iter_, parent_;
633 	GtkTreePath *path;
634 	gboolean valid;
635 
636 	if (!iter) return FALSE;
637 
638 	model = gtk_tree_view_get_model(treeview);
639 	valid = gtk_tree_model_iter_parent(model, &parent_, iter);
640 
641 	while (valid) {
642 		path = gtk_tree_model_get_path(model, &parent_);
643 		if (!gtk_tree_view_row_expanded(treeview, path)) {
644 			*parent = parent_;
645 			gtk_tree_path_free(path);
646 			return TRUE;
647 		}
648 		gtk_tree_path_free(path);
649 		iter_ = parent_;
650 		valid = gtk_tree_model_iter_parent(model, &parent_, &iter_);
651 	}
652 
653 	return FALSE;
654 }
655 
gtkut_tree_view_expand_parent_all(GtkTreeView * treeview,GtkTreeIter * iter)656 void gtkut_tree_view_expand_parent_all(GtkTreeView *treeview, GtkTreeIter *iter)
657 {
658 	GtkTreeModel *model;
659 	GtkTreeIter parent;
660 	GtkTreePath *path;
661 
662 	model = gtk_tree_view_get_model(treeview);
663 
664 	if (gtk_tree_model_iter_parent(model, &parent, iter)) {
665 		path = gtk_tree_model_get_path(model, &parent);
666 		gtk_tree_view_expand_to_path(treeview, path);
667 		gtk_tree_path_free(path);
668 	}
669 }
670 
671 #define SCROLL_EDGE_SIZE 15
672 
673 /* borrowed from gtktreeview.c */
gtkut_tree_view_vertical_autoscroll(GtkTreeView * treeview)674 void gtkut_tree_view_vertical_autoscroll(GtkTreeView *treeview)
675 {
676 	GdkRectangle visible_rect;
677 	gint y, wy;
678 	gint offset;
679 	GtkAdjustment *vadj;
680 	gfloat value;
681 
682 	gdk_window_get_pointer(gtk_tree_view_get_bin_window(treeview),
683 			       NULL, &wy, NULL);
684 	gtk_tree_view_widget_to_tree_coords(treeview, 0, wy, NULL, &y);
685 
686 	gtk_tree_view_get_visible_rect(treeview, &visible_rect);
687 
688 	/* see if we are near the edge. */
689 	offset = y - (visible_rect.y + 2 * SCROLL_EDGE_SIZE);
690 	if (offset > 0) {
691 		offset = y - (visible_rect.y + visible_rect.height - 2 * SCROLL_EDGE_SIZE);
692 		if (offset < 0)
693 			return;
694 	}
695 
696 	vadj = gtk_tree_view_get_vadjustment(treeview);
697 	value = CLAMP(vadj->value + offset, 0.0, vadj->upper - vadj->page_size);
698 	gtk_adjustment_set_value(vadj, value);
699 }
700 
701 /* modified version of gtk_tree_view_scroll_to_cell */
gtkut_tree_view_scroll_to_cell(GtkTreeView * treeview,GtkTreePath * path,gboolean align_center)702 void gtkut_tree_view_scroll_to_cell(GtkTreeView *treeview, GtkTreePath *path,
703 				    gboolean align_center)
704 {
705 	GdkRectangle cell_rect;
706 	GdkRectangle vis_rect;
707 	gint dest_x, dest_y;
708 	gint margin = 0;
709 
710 	if (!path)
711 		return;
712 
713 	gtk_tree_view_get_cell_area(treeview, path, NULL, &cell_rect);
714 	gtk_tree_view_widget_to_tree_coords(treeview, cell_rect.x, cell_rect.y,
715 					    NULL, &(cell_rect.y));
716 	gtk_tree_view_get_visible_rect(treeview, &vis_rect);
717 
718 	dest_x = vis_rect.x;
719 	dest_y = vis_rect.y;
720 
721 	/* add margin */
722 	if (cell_rect.height * 2 < vis_rect.height)
723 		margin = cell_rect.height + (align_center ? 0 : 2);
724 
725 	if (cell_rect.y < vis_rect.y + margin) {
726 		if (align_center)
727 			dest_y = cell_rect.y -
728 				(vis_rect.height - cell_rect.height) / 2;
729 		else
730 			dest_y = cell_rect.y - margin;
731 	}
732 	if (cell_rect.y + cell_rect.height >
733 		vis_rect.y + vis_rect.height - margin) {
734 		if (align_center)
735 			dest_y = cell_rect.y -
736 				(vis_rect.height - cell_rect.height) / 2;
737 		else
738 			dest_y = cell_rect.y + cell_rect.height -
739 				vis_rect.height + margin;
740 	}
741 
742 	gtk_tree_view_scroll_to_point(treeview, dest_x, dest_y);
743 }
744 
gtkut_tree_view_fast_clear(GtkTreeView * treeview,GtkTreeStore * store)745 void gtkut_tree_view_fast_clear(GtkTreeView *treeview, GtkTreeStore *store)
746 {
747 #if GTK_CHECK_VERSION(2, 8, 0) && !GTK_CHECK_VERSION(2, 10, 0)
748 	gtk_tree_store_clear(store);
749 #else
750 	/* this is faster than above, but it seems to trigger crashes in
751 	   GTK+ 2.8.x */
752 	gtk_tree_view_set_model(treeview, NULL);
753 	gtk_tree_store_clear(store);
754 	gtk_tree_view_set_model(treeview, GTK_TREE_MODEL(store));
755 #endif
756 }
757 
gtkut_combo_set_items(GtkCombo * combo,const gchar * str1,...)758 void gtkut_combo_set_items(GtkCombo *combo, const gchar *str1, ...)
759 {
760 	va_list args;
761 	gchar *s;
762 	GList *combo_items = NULL;
763 
764 	g_return_if_fail(str1 != NULL);
765 
766 	combo_items = g_list_append(combo_items, (gpointer)str1);
767 	va_start(args, str1);
768 	s = va_arg(args, gchar*);
769 	while (s) {
770 		combo_items = g_list_append(combo_items, (gpointer)s);
771 		s = va_arg(args, gchar*);
772 	}
773 	va_end(args);
774 
775 	gtk_combo_set_popdown_strings(combo, combo_items);
776 
777 	g_list_free(combo_items);
778 }
779 
gtkut_editable_get_selection(GtkEditable * editable)780 gchar *gtkut_editable_get_selection(GtkEditable *editable)
781 {
782 	gint start_pos, end_pos;
783 	gboolean found;
784 
785 	g_return_val_if_fail(GTK_IS_EDITABLE(editable), NULL);
786 
787 	found = gtk_editable_get_selection_bounds(editable,
788 						  &start_pos, &end_pos);
789 	if (found)
790 		return gtk_editable_get_chars(editable, start_pos, end_pos);
791 	else
792 		return NULL;
793 }
794 
gtkut_editable_disable_im(GtkEditable * editable)795 void gtkut_editable_disable_im(GtkEditable *editable)
796 {
797 	g_return_if_fail(editable != NULL);
798 
799 #if USE_XIM
800 	if (editable->ic) {
801 		gdk_ic_destroy(editable->ic);
802 		editable->ic = NULL;
803 	}
804 	if (editable->ic_attr) {
805 		gdk_ic_attr_destroy(editable->ic_attr);
806 		editable->ic_attr = NULL;
807 	}
808 #endif
809 }
810 
gtkut_entry_strip_text(GtkEntry * entry)811 void gtkut_entry_strip_text(GtkEntry *entry)
812 {
813 	gchar *text;
814 	gint len;
815 
816 	text = gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
817 	len = strlen(text);
818 	g_strstrip(text);
819 	if (len > strlen(text))
820 		gtk_entry_set_text(entry, text);
821 	g_free(text);
822 }
823 
gtkut_container_remove(GtkContainer * container,GtkWidget * widget)824 void gtkut_container_remove(GtkContainer *container, GtkWidget *widget)
825 {
826 	gtk_container_remove(container, widget);
827 }
828 
gtkut_scrolled_window_reset_position(GtkScrolledWindow * window)829 void gtkut_scrolled_window_reset_position(GtkScrolledWindow *window)
830 {
831 	GtkAdjustment *adj;
832 
833 	adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(window));
834 	gtk_adjustment_set_value(adj, adj->lower);
835 	adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(window));
836 	gtk_adjustment_set_value(adj, adj->lower);
837 }
838 
gtkut_text_buffer_match_string(GtkTextBuffer * textbuf,const GtkTextIter * iter,gunichar * wcs,gint len,gboolean case_sens)839 gboolean gtkut_text_buffer_match_string(GtkTextBuffer *textbuf,
840 					const GtkTextIter *iter,
841 					gunichar *wcs, gint len,
842 					gboolean case_sens)
843 {
844 	GtkTextIter start_iter, end_iter;
845 	gchar *utf8str, *p;
846 	gint match_count;
847 
848 	start_iter = end_iter = *iter;
849 	gtk_text_iter_forward_chars(&end_iter, len);
850 
851 	utf8str = gtk_text_buffer_get_text(textbuf, &start_iter, &end_iter,
852 					   FALSE);
853 	if (!utf8str) return FALSE;
854 
855 	if ((gint)g_utf8_strlen(utf8str, -1) != len) {
856 		g_free(utf8str);
857 		return FALSE;
858 	}
859 
860 	for (p = utf8str, match_count = 0;
861 	     *p != '\0' && match_count < len;
862 	     p = g_utf8_next_char(p), match_count++) {
863 		gunichar wc;
864 
865 		wc = g_utf8_get_char(p);
866 
867 		if (case_sens) {
868 			if (wc != wcs[match_count])
869 				break;
870 		} else {
871 			if (g_unichar_tolower(wc) !=
872 			    g_unichar_tolower(wcs[match_count]))
873 				break;
874 		}
875 	}
876 
877 	g_free(utf8str);
878 
879 	if (match_count == len)
880 		return TRUE;
881 	else
882 		return FALSE;
883 }
884 
gtkut_text_buffer_find(GtkTextBuffer * buffer,const GtkTextIter * iter,const gchar * str,gboolean case_sens,GtkTextIter * match_pos)885 gboolean gtkut_text_buffer_find(GtkTextBuffer *buffer, const GtkTextIter *iter,
886 				const gchar *str, gboolean case_sens,
887 				GtkTextIter *match_pos)
888 {
889 	gunichar *wcs;
890 	gint len;
891 	glong items_read = 0, items_written = 0;
892 	GError *error = NULL;
893 	GtkTextIter iter_;
894 	gboolean found = FALSE;
895 
896 	wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
897 	if (error != NULL) {
898 		g_warning("An error occurred while converting a string from UTF-8 to UCS-4: %s\n", error->message);
899 		g_error_free(error);
900 	}
901 	if (!wcs || items_written <= 0) return FALSE;
902 	len = (gint)items_written;
903 
904 	iter_ = *iter;
905 	do {
906 		found = gtkut_text_buffer_match_string
907 			(buffer, &iter_, wcs, len, case_sens);
908 		if (found) {
909 			*match_pos = iter_;
910 			break;
911 		}
912 	} while (gtk_text_iter_forward_char(&iter_));
913 
914 	g_free(wcs);
915 
916 	return found;
917 }
918 
gtkut_text_buffer_find_backward(GtkTextBuffer * buffer,const GtkTextIter * iter,const gchar * str,gboolean case_sens,GtkTextIter * match_pos)919 gboolean gtkut_text_buffer_find_backward(GtkTextBuffer *buffer,
920 					 const GtkTextIter *iter,
921 					 const gchar *str, gboolean case_sens,
922 					 GtkTextIter *match_pos)
923 {
924 	gunichar *wcs;
925 	gint len;
926 	glong items_read = 0, items_written = 0;
927 	GError *error = NULL;
928 	GtkTextIter iter_;
929 	gboolean found = FALSE;
930 
931 	wcs = g_utf8_to_ucs4(str, -1, &items_read, &items_written, &error);
932 	if (error != NULL) {
933 		g_warning("An error occurred while converting a string from UTF-8 to UCS-4: %s\n", error->message);
934 		g_error_free(error);
935 	}
936 	if (!wcs || items_written <= 0) return FALSE;
937 	len = (gint)items_written;
938 
939 	iter_ = *iter;
940 	while (gtk_text_iter_backward_char(&iter_)) {
941 		found = gtkut_text_buffer_match_string
942 			(buffer, &iter_, wcs, len, case_sens);
943 		if (found) {
944 			*match_pos = iter_;
945 			break;
946 		}
947 	}
948 
949 	g_free(wcs);
950 
951 	return found;
952 }
953 
954 #define MAX_TEXT_LINE_LEN	8190
955 
gtkut_text_buffer_insert_with_tag_by_name(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * text,gint len,const gchar * tag)956 void gtkut_text_buffer_insert_with_tag_by_name(GtkTextBuffer *buffer,
957 					       GtkTextIter *iter,
958 					       const gchar *text,
959 					       gint len,
960 					       const gchar *tag)
961 {
962 	if (len < 0)
963 		len = strlen(text);
964 
965 	gtk_text_buffer_insert_with_tags_by_name
966 		(buffer, iter, text, len, tag, NULL);
967 
968 	if (len > 0 && text[len - 1] != '\n') {
969 		/* somehow returns invalid value first (bug?),
970 		   so call it twice */
971 		gtk_text_iter_get_chars_in_line(iter);
972 		if (gtk_text_iter_get_chars_in_line(iter) > MAX_TEXT_LINE_LEN) {
973 			gtk_text_buffer_insert_with_tags_by_name
974 				(buffer, iter, "\n", 1, tag, NULL);
975 		}
976 	}
977 }
978 
gtkut_text_view_get_selection(GtkTextView * textview)979 gchar *gtkut_text_view_get_selection(GtkTextView *textview)
980 {
981 	GtkTextBuffer *buffer;
982 	GtkTextIter start_iter, end_iter;
983 	gboolean found;
984 
985 	g_return_val_if_fail(GTK_IS_TEXT_VIEW(textview), NULL);
986 
987 	buffer = gtk_text_view_get_buffer(textview);
988 	found = gtk_text_buffer_get_selection_bounds(buffer,
989 						     &start_iter, &end_iter);
990 	if (found)
991 		return gtk_text_buffer_get_text(buffer, &start_iter, &end_iter,
992 						FALSE);
993 	else
994 		return NULL;
995 }
996 
gtkut_window_popup(GtkWidget * window)997 void gtkut_window_popup(GtkWidget *window)
998 {
999 	gint x, y, sx, sy, new_x, new_y;
1000 
1001 	g_return_if_fail(window != NULL);
1002 	g_return_if_fail(window->window != NULL);
1003 
1004 	sx = gdk_screen_width();
1005 	sy = gdk_screen_height();
1006 
1007 	gdk_window_get_origin(window->window, &x, &y);
1008 	new_x = x % sx; if (new_x < 0) new_x = 0;
1009 	new_y = y % sy; if (new_y < 0) new_y = 0;
1010 	if (new_x != x || new_y != y)
1011 		gdk_window_move(window->window, new_x, new_y);
1012 
1013 	gtk_window_set_skip_taskbar_hint(GTK_WINDOW(window), FALSE);
1014 	gtk_widget_show(window);
1015 	gtk_window_present(GTK_WINDOW(window));
1016 #ifdef G_OS_WIN32
1017 	/* ensure that the window is displayed at the top */
1018 	gdk_window_show(window->window);
1019 #endif
1020 }
1021 
gtkut_window_modal_exist(void)1022 gboolean gtkut_window_modal_exist(void)
1023 {
1024 	GList *window_list, *cur;
1025 	gboolean exist = FALSE;
1026 
1027 	window_list = gtk_window_list_toplevels();
1028 	for (cur = window_list; cur != NULL; cur = cur->next) {
1029 		GtkWidget *window = GTK_WIDGET(cur->data);
1030 
1031 		if (GTK_WIDGET_VISIBLE(window) &&
1032 		    gtk_window_get_modal(GTK_WINDOW(window))) {
1033 			exist = TRUE;
1034 			break;
1035 		}
1036 	}
1037 	g_list_free(window_list);
1038 
1039 	return exist;
1040 }
1041 
1042 /* ensure that the window is displayed on screen */
gtkut_window_move(GtkWindow * window,gint x,gint y)1043 void gtkut_window_move(GtkWindow *window, gint x, gint y)
1044 {
1045 	g_return_if_fail(window != NULL);
1046 
1047 	if (x < 0)
1048 		x = 0;
1049 	if (y < 0)
1050 		y = 0;
1051 	x %= gdk_screen_width();
1052 	y %= gdk_screen_height();
1053 
1054 	gtk_window_move(window, x, y);
1055 }
1056 
gtkut_widget_get_uposition(GtkWidget * widget,gint * px,gint * py)1057 void gtkut_widget_get_uposition(GtkWidget *widget, gint *px, gint *py)
1058 {
1059 	gint x, y;
1060 	gint sx, sy;
1061 
1062 	g_return_if_fail(widget != NULL);
1063 	g_return_if_fail(widget->window != NULL);
1064 
1065 	sx = gdk_screen_width();
1066 	sy = gdk_screen_height();
1067 
1068 	/* gdk_window_get_root_origin ever return *rootwindow*'s position */
1069 	gdk_window_get_root_origin(widget->window, &x, &y);
1070 
1071 	x %= sx; if (x < 0) x = 0;
1072 	y %= sy; if (y < 0) y = 0;
1073 	*px = x;
1074 	*py = y;
1075 }
1076 
gtkut_widget_draw_now(GtkWidget * widget)1077 void gtkut_widget_draw_now(GtkWidget *widget)
1078 {
1079 	if (GTK_WIDGET_VISIBLE(widget) && GTK_WIDGET_DRAWABLE(widget))
1080 		gdk_window_process_updates(widget->window, FALSE);
1081 }
1082 
gtkut_clist_bindings_add(GtkWidget * clist)1083 static void gtkut_clist_bindings_add(GtkWidget *clist)
1084 {
1085 	GtkBindingSet *binding_set;
1086 
1087 	binding_set = gtk_binding_set_by_class(GTK_CLIST_GET_CLASS(clist));
1088 
1089 	gtk_binding_entry_add_signal(binding_set, GDK_n, GDK_CONTROL_MASK,
1090 				     "scroll_vertical", 2,
1091 				     G_TYPE_ENUM, GTK_SCROLL_STEP_FORWARD,
1092 				     G_TYPE_FLOAT, 0.0);
1093 	gtk_binding_entry_add_signal(binding_set, GDK_p, GDK_CONTROL_MASK,
1094 				     "scroll_vertical", 2,
1095 				     G_TYPE_ENUM, GTK_SCROLL_STEP_BACKWARD,
1096 				     G_TYPE_FLOAT, 0.0);
1097 }
1098 
gtkut_widget_init(void)1099 void gtkut_widget_init(void)
1100 {
1101 	GtkWidget *clist;
1102 
1103 	clist = gtk_clist_new(1);
1104 	g_object_ref(G_OBJECT(clist));
1105 	gtk_object_sink(GTK_OBJECT(clist));
1106 	gtkut_clist_bindings_add(clist);
1107 	g_object_unref(G_OBJECT(clist));
1108 
1109 	clist = gtk_ctree_new(1, 0);
1110 	g_object_ref(G_OBJECT(clist));
1111 	gtk_object_sink(GTK_OBJECT(clist));
1112 	gtkut_clist_bindings_add(clist);
1113 	g_object_unref(G_OBJECT(clist));
1114 }
1115 
gtkut_events_flush(void)1116 void gtkut_events_flush(void)
1117 {
1118 	GTK_EVENTS_FLUSH();
1119 }
1120