2 * GNT - The GLib Ncurses Toolkit
4 * GNT is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
8 * This library is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
23 #include "gntbutton.h"
24 #include "gntcheckbox.h"
25 #include "gntcombobox.h"
29 #include "gnttextview.h"
32 #include "gntwindow.h"
41 #include <libxml/parser.h>
42 #include <libxml/tree.h>
47 void gnt_util_get_text_bound(const char *text
, int *width
, int *height
)
49 const char *s
= text
, *last
;
50 int count
= 1, max
= 0;
53 /* XXX: ew ... everyone look away */
59 if (*s
== '\n' || *s
== '\r')
62 len
= gnt_util_onscreen_width(last
, s
);
67 s
= g_utf8_next_char(s
);
70 len
= gnt_util_onscreen_width(last
, s
);
78 *width
= max
+ (count
> 1);
81 int gnt_util_onscreen_width(const char *start
, const char *end
)
86 end
= start
+ strlen(start
);
89 width
+= g_unichar_iswide(g_utf8_get_char(start
)) ? 2 : 1;
90 start
= g_utf8_next_char(start
);
95 const char *gnt_util_onscreen_width_to_pointer(const char *string
, int len
, int *w
)
99 const char *str
= string
;
102 len
= gnt_util_onscreen_width(string
, NULL
);
105 while (width
< len
&& *str
) {
106 size
= g_unichar_iswide(g_utf8_get_char(str
)) ? 2 : 1;
107 if (width
+ size
> len
)
109 str
= g_utf8_next_char(str
);
117 char *gnt_util_onscreen_fit_string(const char *string
, int maxw
)
119 const char *start
, *end
;
123 maxw
= getmaxx(stdscr
) - 4;
126 str
= g_string_new(NULL
);
129 if ((end
= strchr(start
, '\n')) != NULL
||
130 (end
= strchr(start
, '\r')) != NULL
) {
131 if (gnt_util_onscreen_width(start
, end
) > maxw
)
135 end
= gnt_util_onscreen_width_to_pointer(start
, maxw
, NULL
);
136 str
= g_string_append_len(str
, start
, end
- start
);
138 str
= g_string_append_c(str
, '\n');
139 if (*end
== '\n' || *end
== '\r')
144 return g_string_free(str
, FALSE
);
155 duplicate_values(gpointer key
, gpointer value
, gpointer data
)
157 struct duplicate_fns
*fns
= data
;
158 g_hash_table_insert(fns
->table
, fns
->key_dup
? fns
->key_dup(key
) : key
,
159 fns
->value_dup
? fns
->value_dup(value
) : value
);
162 GHashTable
*g_hash_table_duplicate(GHashTable
*src
, GHashFunc hash
,
163 GEqualFunc equal
, GDestroyNotify key_d
, GDestroyNotify value_d
,
164 GDupFunc key_dup
, GDupFunc value_dup
)
166 GHashTable
*dest
= g_hash_table_new_full(hash
, equal
, key_d
, value_d
);
167 struct duplicate_fns fns
= {key_dup
, value_dup
, dest
};
168 g_hash_table_foreach(src
, duplicate_values
, &fns
);
172 gboolean
gnt_boolean_handled_accumulator(GSignalInvocationHint
*ihint
,
174 const GValue
*handler_return
,
177 gboolean continue_emission
;
178 gboolean signal_handled
;
180 signal_handled
= g_value_get_boolean (handler_return
);
181 g_value_set_boolean (return_accu
, signal_handled
);
182 continue_emission
= !signal_handled
;
184 return continue_emission
;
193 add_binding(gpointer key
, gpointer value
, gpointer data
)
195 BindingView
*bv
= data
;
196 GntBindableActionParam
*act
= value
;
197 const char *name
= g_hash_table_lookup(bv
->hash
, act
->action
);
199 const char *k
= gnt_key_lookup(key
);
202 gnt_tree_add_row_after(bv
->tree
, (gpointer
)k
,
203 gnt_tree_create_row(bv
->tree
, k
, name
), NULL
, NULL
);
208 add_action(gpointer key
, gpointer value
, gpointer data
)
210 BindingView
*bv
= data
;
211 g_hash_table_insert(bv
->hash
, value
, key
);
214 GntWidget
*gnt_widget_bindings_view(GntWidget
*widget
)
216 GntBindable
*bind
= GNT_BINDABLE(widget
);
217 GntWidget
*tree
= gnt_tree_new_with_columns(2);
218 GntBindableClass
*klass
= GNT_BINDABLE_CLASS(GNT_BINDABLE_GET_CLASS(bind
));
219 GHashTable
*hash
= g_hash_table_new(g_direct_hash
, g_direct_equal
);
220 BindingView bv
= {hash
, GNT_TREE(tree
)};
222 gnt_tree_set_compare_func(bv
.tree
, (GCompareFunc
)g_utf8_collate
);
223 g_hash_table_foreach(klass
->actions
, add_action
, &bv
);
224 g_hash_table_foreach(klass
->bindings
, add_binding
, &bv
);
225 if (GNT_TREE(tree
)->list
== NULL
) {
226 gnt_widget_destroy(tree
);
229 gnt_tree_adjust_columns(bv
.tree
);
230 g_hash_table_destroy(hash
);
237 gnt_widget_from_xmlnode(xmlNode
*node
, GntWidget
**data
[], int max
)
239 GntWidget
*widget
= NULL
;
241 char *id
, *prop
, *content
;
244 if (node
== NULL
|| node
->name
== NULL
|| node
->type
!= XML_ELEMENT_NODE
)
247 name
= (char*)node
->name
;
248 content
= (char*)xmlNodeGetContent(node
);
249 if (strcmp(name
+ 1, "window") == 0 || strcmp(name
+ 1, "box") == 0) {
252 gboolean vert
= (*name
== 'v');
255 widget
= gnt_window_box_new(FALSE
, vert
);
257 widget
= gnt_box_new(FALSE
, vert
);
259 title
= (char*)xmlGetProp(node
, (xmlChar
*)"title");
261 gnt_box_set_title(GNT_BOX(widget
), title
);
265 prop
= (char*)xmlGetProp(node
, (xmlChar
*)"fill");
267 if (sscanf(prop
, "%d", &val
) == 1)
268 gnt_box_set_fill(GNT_BOX(widget
), !!val
);
272 prop
= (char*)xmlGetProp(node
, (xmlChar
*)"align");
274 if (sscanf(prop
, "%d", &val
) == 1)
275 gnt_box_set_alignment(GNT_BOX(widget
), val
);
279 prop
= (char*)xmlGetProp(node
, (xmlChar
*)"pad");
281 if (sscanf(prop
, "%d", &val
) == 1)
282 gnt_box_set_pad(GNT_BOX(widget
), val
);
286 for (ch
= node
->children
; ch
; ch
=ch
->next
)
287 gnt_box_add_widget(GNT_BOX(widget
), gnt_widget_from_xmlnode(ch
, data
, max
));
288 } else if (strcmp(name
, "button") == 0) {
289 widget
= gnt_button_new(content
);
290 } else if (strcmp(name
, "label") == 0) {
291 widget
= gnt_label_new(content
);
292 } else if (strcmp(name
, "entry") == 0) {
293 widget
= gnt_entry_new(content
);
294 } else if (strcmp(name
, "combobox") == 0) {
295 widget
= gnt_combo_box_new();
296 } else if (strcmp(name
, "checkbox") == 0) {
297 widget
= gnt_check_box_new(content
);
298 } else if (strcmp(name
, "tree") == 0) {
299 widget
= gnt_tree_new();
300 } else if (strcmp(name
, "textview") == 0) {
301 widget
= gnt_text_view_new();
302 } else if (strcmp(name
+ 1, "line") == 0) {
303 widget
= gnt_line_new(*name
== 'v');
308 if (widget
== NULL
) {
309 g_printerr("Invalid widget name %s\n", name
);
313 id
= (char*)xmlGetProp(node
, (xmlChar
*)"id");
316 if (sscanf(id
, "%d", &i
) == 1 && i
>= 0 && i
< max
) {
322 prop
= (char*)xmlGetProp(node
, (xmlChar
*)"border");
325 if (sscanf(prop
, "%d", &val
) == 1) {
327 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_NO_BORDER
);
329 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_NO_BORDER
);
334 prop
= (char*)xmlGetProp(node
, (xmlChar
*)"shadow");
337 if (sscanf(prop
, "%d", &val
) == 1) {
339 GNT_WIDGET_UNSET_FLAGS(widget
, GNT_WIDGET_NO_BORDER
);
341 GNT_WIDGET_SET_FLAGS(widget
, GNT_WIDGET_NO_BORDER
);
350 void gnt_util_parse_widgets(const char *string
, int num
, ...)
353 xmlParserCtxtPtr ctxt
;
360 ctxt
= xmlNewParserCtxt();
361 doc
= xmlCtxtReadDoc(ctxt
, (xmlChar
*)string
, NULL
, NULL
, XML_PARSE_NOBLANKS
);
363 data
= g_new0(GntWidget
**, num
);
366 for (id
= 0; id
< num
; id
++)
367 data
[id
] = va_arg(list
, gpointer
);
369 node
= xmlDocGetRootElement(doc
);
370 gnt_widget_from_xmlnode(node
, data
, num
);
381 util_parse_html_to_tv(xmlNode
*node
, GntTextView
*tv
, GntTextFormatFlags flag
)
386 gboolean processed
= FALSE
;
388 gboolean insert_nl_s
= FALSE
, insert_nl_e
= FALSE
;
390 if (node
== NULL
|| node
->name
== NULL
|| node
->type
!= XML_ELEMENT_NODE
)
393 name
= (char*)node
->name
;
394 if (g_ascii_strcasecmp(name
, "b") == 0 ||
395 g_ascii_strcasecmp(name
, "strong") == 0 ||
396 g_ascii_strcasecmp(name
, "i") == 0 ||
397 g_ascii_strcasecmp(name
, "blockquote") == 0) {
398 flag
|= GNT_TEXT_FLAG_BOLD
;
399 } else if (g_ascii_strcasecmp(name
, "u") == 0) {
400 flag
|= GNT_TEXT_FLAG_UNDERLINE
;
401 } else if (g_ascii_strcasecmp(name
, "br") == 0) {
403 } else if (g_ascii_strcasecmp(name
, "a") == 0) {
404 flag
|= GNT_TEXT_FLAG_UNDERLINE
;
405 url
= (char *)xmlGetProp(node
, (xmlChar
*)"href");
406 } else if (g_ascii_strcasecmp(name
, "h1") == 0 ||
407 g_ascii_strcasecmp(name
, "h2") == 0 ||
408 g_ascii_strcasecmp(name
, "h3") == 0 ||
409 g_ascii_strcasecmp(name
, "h4") == 0 ||
410 g_ascii_strcasecmp(name
, "h5") == 0 ||
411 g_ascii_strcasecmp(name
, "h6") == 0) {
414 } else if (g_ascii_strcasecmp(name
, "title") == 0) {
417 flag
|= GNT_TEXT_FLAG_BOLD
| GNT_TEXT_FLAG_UNDERLINE
;
419 /* XXX: Process other possible tags */
423 gnt_text_view_append_text_with_flags(tv
, "\n", flag
);
425 for (ch
= node
->children
; ch
; ch
= ch
->next
) {
426 if (ch
->type
== XML_ELEMENT_NODE
) {
428 util_parse_html_to_tv(ch
, tv
, flag
);
433 content
= (char*)xmlNodeGetContent(node
);
434 gnt_text_view_append_text_with_flags(tv
, content
, flag
);
439 char *href
= g_strdup_printf(" (%s)", url
);
440 gnt_text_view_append_text_with_flags(tv
, href
, flag
);
446 gnt_text_view_append_text_with_flags(tv
, "\n", flag
);
450 gboolean
gnt_util_parse_xhtml_to_textview(const char *string
, GntTextView
*tv
)
455 xmlParserCtxtPtr ctxt
;
458 GntTextFormatFlags flag
= GNT_TEXT_FLAG_NORMAL
;
459 gboolean ret
= FALSE
;
461 ctxt
= xmlNewParserCtxt();
462 doc
= xmlCtxtReadDoc(ctxt
, (xmlChar
*)string
, NULL
, NULL
, XML_PARSE_NOBLANKS
| XML_PARSE_RECOVER
);
464 node
= xmlDocGetRootElement(doc
);
465 util_parse_html_to_tv(node
, tv
, flag
);
474 /* Setup trigger widget */
481 free_trigger_button(TriggerButton
*b
)
488 key_pressed(GntWidget
*widget
, const char *text
, TriggerButton
*trig
)
490 if (text
&& trig
->text
&&
491 strcmp(text
, trig
->text
) == 0) {
492 gnt_widget_activate(trig
->button
);
498 void gnt_util_set_trigger_widget(GntWidget
*wid
, const char *text
, GntWidget
*button
)
500 TriggerButton
*tb
= g_new0(TriggerButton
, 1);
501 tb
->text
= g_strdup(text
);
503 g_signal_connect(G_OBJECT(wid
), "key_pressed", G_CALLBACK(key_pressed
), tb
);
504 g_signal_connect_swapped(G_OBJECT(button
), "destroy", G_CALLBACK(free_trigger_button
), tb
);