8 #define YUILogComponent "gtk"
9 #include <yui/Libyui_config.h>
15 static inline void skipSpace (
const char *instr,
int *i)
16 {
while (g_ascii_isspace (instr[*i])) (*i)++; }
21 unsigned int early_closer : 1;
25 tag_entry_new (GString *tag,
int tag_len)
27 static const char *early_closers[] = {
"p",
"li" };
30 entry->tag_len = tag_len;
31 entry->early_closer = FALSE;
34 for (i = 0; i < G_N_ELEMENTS (early_closers); i++)
35 if (!g_ascii_strncasecmp (tag->str, early_closers[i], tag_len))
36 entry->early_closer = TRUE;
43 if (entry && entry->tag)
44 g_string_free (entry->tag, TRUE);
49 emit_unclosed_tags_for (GString *outp, GQueue *tag_queue,
const char *tag_str,
int tag_len)
51 gboolean matched = FALSE;
54 if (g_queue_is_empty (tag_queue))
62 if (last_entry->tag_len != tag_len ||
63 g_ascii_strncasecmp (last_entry->tag->str, tag_str, tag_len)) {
65 g_string_append (outp,
"</");
66 g_string_append_len (outp, last_entry->tag->str, last_entry->tag_len);
67 g_string_append_c (outp,
'>');
71 tag_entry_free (last_entry);
76 check_early_close (GString *outp, GQueue *tag_queue,
TagEntry *entry)
81 if (!entry->early_closer)
84 last_tag = (
TagEntry *) g_queue_peek_tail (tag_queue);
85 if (!last_tag || !last_tag->early_closer)
88 if (entry->tag_len != last_tag->tag_len ||
89 g_ascii_strncasecmp (last_tag->tag->str, entry->tag->str, entry->tag_len))
94 g_string_append (outp,
"</");
95 g_string_append_len (outp, entry->tag->str, entry->tag_len);
96 g_string_append_c (outp,
'>');
103 const gchar *html, *text;
111 static const EntityMap *lookup_entity (
const char *html)
114 for (i = 0; i <
sizeof (entities) /
sizeof (
EntityMap); i++)
115 if (!g_ascii_strncasecmp (html+1, entities[i].html, strlen (entities[i].html)))
123 gchar *ygutils_convert_to_xhtml (
const char *instr)
125 GString *outp = g_string_new (
"");
126 GQueue *tag_queue = g_queue_new();
129 gboolean allow_space = FALSE, pre_mode = FALSE;
130 skipSpace (instr, &i);
133 g_string_append (outp,
"<body>");
135 for (; instr[i] !=
'\0'; i++)
138 if (instr[i] ==
'<') {
140 if (strncmp (&instr[i],
"<!--", 4) == 0) {
141 for (i += 3; instr[i] !=
'\0'; i++)
142 if (strncmp (&instr[i],
"-->", 3) == 0) {
150 gboolean is_close = FALSE;
153 GString *tag = g_string_sized_new (20);
156 skipSpace (instr, &i);
158 if (instr[i] ==
'/') {
163 skipSpace (instr, &i);
168 for (; instr[i] !=
'>' && instr[i]; i++) {
170 if (!g_ascii_isalnum(instr[i]))
175 g_string_append_c (tag, instr[i]);
179 if (!is_close && tag_len == 2 &&
180 (!g_ascii_strncasecmp (tag->str,
"hr", 2) ||
181 !g_ascii_strncasecmp (tag->str,
"br", 2)) &&
182 tag->str[tag->len - 1] !=
'/')
183 g_string_append_c (tag,
'/');
185 if (!g_ascii_strncasecmp (tag->str,
"pre", 3))
186 pre_mode = !is_close;
190 for (k = 0; k < tag->len; k++) {
191 if (tag->str[k] ==
'=') {
192 gboolean unquote = tag->str[k+1] !=
'"';
194 g_string_insert_c (tag, k+1,
'"');
197 for (k++; tag->str[k]; k++) {
198 if (unquote && g_ascii_isspace (tag->str[k]))
200 else if (!unquote && tag->str[k] ==
'"')
204 g_string_insert_c (tag, k,
'"');
207 tag->str[k] = g_ascii_tolower (tag->str[k]);
213 while (j > 0 && g_ascii_isspace (tag->str[j])) j--;
215 gboolean is_open_close = (tag->str[j] ==
'/');
219 emit_unclosed_tags_for (outp, tag_queue, tag->str, tag_len);
221 TagEntry *entry = tag_entry_new (tag, tag_len);
224 entry->tag_len = tag_len;
226 if (!check_early_close (outp, tag_queue, entry))
227 g_queue_push_tail (tag_queue, entry);
230 tag_entry_free (entry);
234 g_string_append_c (outp,
'<');
236 g_string_append_c (outp,
'/');
237 g_string_append_len (outp, tag->str, tag->len);
238 g_string_append_c (outp,
'>');
240 if (is_close || is_open_close)
241 g_string_free (tag, TRUE);
243 allow_space = is_close;
246 else if (instr[i] ==
'&') {
247 const EntityMap *entity = lookup_entity (instr+i);
249 if (!strcmp (entity->html,
"product"))
250 g_string_append (outp, YUI::app()->productName().c_str());
252 g_string_append (outp, entity->text);
253 i += strlen (entity->html);
254 if (instr[i+1] ==
';') i++;
259 for (j = i + 1; instr[j] !=
'\0'; j++) {
260 if (!g_ascii_isalnum (instr[j]) && instr[j] !=
'#')
264 g_string_append (outp,
"&");
266 g_string_append_c (outp, instr[i]);
272 if (!pre_mode && g_ascii_isspace (instr[i])) {
274 g_string_append_c (outp,
' ');
279 g_string_append_c (outp, instr[i]);
284 emit_unclosed_tags_for (outp, tag_queue,
"", 0);
285 g_queue_free (tag_queue);
286 g_string_append (outp,
"</body>");
288 gchar *ret = g_string_free (outp, FALSE);
292 std::string YGUtils::mapKBAccel (
const std::string &src)
295 std::string::size_type length = src.length(), i;
297 str.reserve (length);
298 for (i = 0; i < length; i++) {
301 else if (src[i] ==
'&') {
302 if (i+1 < length && src[i+1] ==
'&') {
315 char *ygutils_mapKBAccel (
const char *src)
317 std::string ret (YGUtils::mapKBAccel (src));
318 return strdup (ret.c_str());
321 void YGUtils::setFilter (GtkEntry *entry,
const std::string &validChars)
324 static void insert_text_cb (GtkEditable *editable,
const gchar *new_text,
325 gint new_text_length, gint *pos)
327 const gchar *valid_chars = (gchar *) g_object_get_data (G_OBJECT (editable),
331 for (i = new_text; *i; i++) {
332 for (j = valid_chars; *j; j++) {
338 g_signal_stop_emission_by_name (editable,
"insert_text");
339 gtk_widget_error_bell (GTK_WIDGET (editable));
347 if (g_object_get_data (G_OBJECT (entry),
"insert-text-set"))
348 g_object_disconnect (G_OBJECT (entry),
"insert-text",
349 G_CALLBACK (inner::insert_text_cb), NULL);
351 if (!validChars.empty()) {
352 gchar *chars = g_strdup (validChars.c_str());
353 g_object_set_data_full (G_OBJECT (entry),
"valid-chars", chars, g_free);
354 g_signal_connect (G_OBJECT (entry),
"insert-text",
355 G_CALLBACK (inner::insert_text_cb), NULL);
356 g_object_set_data (G_OBJECT (entry),
"insert-text-set", GINT_TO_POINTER (1));
359 g_object_set_data (G_OBJECT (entry),
"insert-text-set", GINT_TO_POINTER (0));
362 void ygutils_setFilter (GtkEntry *entry,
const char *validChars)
363 { YGUtils::setFilter (entry, validChars); }
365 void YGUtils::replace (std::string &str,
const char *mouth,
int mouth_len,
const char *food)
368 mouth_len = strlen (mouth);
369 std::string::size_type i = 0;
370 while ((i = str.find (mouth, i)) != std::string::npos) {
371 str.erase (i, mouth_len);
372 str.insert (i, food);
376 std::string YGUtils::truncate (
const std::string &str,
int length,
int pos)
378 std::string ret (str);
379 const char *pstr = ret.c_str();
char *pi;
380 int size = g_utf8_strlen (pstr, -1);
383 pi = g_utf8_offset_to_pointer (pstr, length-3);
388 pi = g_utf8_offset_to_pointer (pstr, size-(length-3));
389 ret.erase (0, pi-pstr);
390 ret.insert (0,
"...");
393 pi = g_utf8_offset_to_pointer (pstr, size/2);
394 int delta = size - (length-3);
395 gchar *pn = pi, *pp = pi;
397 if (i++ == delta)
break;
398 pn = g_utf8_next_char (pn);
399 if (i++ == delta)
break;
400 pp = g_utf8_prev_char (pp);
402 g_assert (pp != NULL && pn != NULL);
404 ret.erase (pp-pstr, pn-pp);
405 ret.insert (pp-pstr,
"...");
411 static gboolean scroll_down_cb (
void *pData)
413 GtkAdjustment *vadj = (GtkAdjustment *) pData;
414 gtk_adjustment_set_value (vadj, gtk_adjustment_get_upper(vadj) - gtk_adjustment_get_page_size(vadj));
418 void YGUtils::scrollWidget (GtkAdjustment *vadj,
bool top)
421 gtk_adjustment_set_value (vadj, gtk_adjustment_get_lower(vadj));
425 g_idle_add_full (G_PRIORITY_LOW, scroll_down_cb, vadj, NULL);
428 void ygutils_scrollAdj (GtkAdjustment *vadj, gboolean top)
429 { YGUtils::scrollWidget (vadj, top); }
431 std::string YGUtils::escapeMarkup (
const std::string &ori)
433 std::string::size_type length = ori.length(), i;
435 ret.reserve (length * 1.5);
436 for (i = 0; i < length; i++)
454 bool YGUtils::endsWith (
const std::string &str,
const std::string &key)
456 if (str.size() < key.size())
458 return str.compare (str.size()-key.size(), key.size(), key) == 0;
461 int YGUtils::getCharsWidth (GtkWidget *widget,
int chars_nb)
463 GtkStyleContext *style_ctx = gtk_widget_get_style_context(widget);
464 PangoContext *context = gtk_widget_get_pango_context (widget);
465 PangoFontDescription *font_desc;
466 gtk_style_context_get (style_ctx, GTK_STATE_FLAG_NORMAL,
"font", &font_desc, NULL);
467 PangoFontMetrics *metrics = pango_context_get_metrics (context, font_desc, NULL);
469 int width = pango_font_metrics_get_approximate_char_width (metrics);
470 pango_font_metrics_unref (metrics);
472 return PANGO_PIXELS (width) * chars_nb;
475 int YGUtils::getCharsHeight (GtkWidget *widget,
int chars_nb)
477 GtkStyleContext *style_ctx = gtk_widget_get_style_context(widget);
478 PangoContext *context = gtk_widget_get_pango_context (widget);
479 PangoFontDescription *font_desc;
480 gtk_style_context_get (style_ctx, GTK_STATE_FLAG_NORMAL,
"font", &font_desc, NULL);
481 PangoFontMetrics *metrics = pango_context_get_metrics (context, font_desc, NULL);
483 int height = pango_font_metrics_get_ascent (metrics) +
484 pango_font_metrics_get_descent (metrics);
485 pango_font_metrics_unref (metrics);
487 return PANGO_PIXELS (height) * chars_nb;
490 void YGUtils::setWidgetFont (GtkWidget *widget, PangoStyle style, PangoWeight weight,
493 GtkStyleContext *style_ctx = gtk_widget_get_style_context(widget);
494 PangoFontDescription *font_desc;
495 gtk_style_context_get (style_ctx, GTK_STATE_FLAG_NORMAL,
"font", &font_desc, NULL);
497 int size = pango_font_description_get_size (font_desc);
498 PangoFontDescription* font = pango_font_description_new();
499 pango_font_description_set_weight (font, weight);
500 pango_font_description_set_size (font, (
int)(size * scale));
501 pango_font_description_set_style (font, style);
502 gtk_widget_override_font (widget, font);
505 void ygutils_setWidgetFont (GtkWidget *widget, PangoStyle style, PangoWeight weight,
double scale)
506 { YGUtils::setWidgetFont (widget, style, weight, scale); }
508 static void paned_allocate_cb (GtkWidget *paned, GtkAllocation *alloc, gpointer _rel)
510 if (!g_object_get_data (G_OBJECT (paned),
"init")) {
511 gdouble rel = GPOINTER_TO_INT (_rel) / 100.;
514 gtk_widget_get_allocation(paned, &alloc);
516 if (gtk_orientable_get_orientation (GTK_ORIENTABLE (paned)) == GTK_ORIENTATION_HORIZONTAL)
517 parent_size = alloc.width;
519 parent_size = alloc.height;
520 int pos = parent_size * rel;
521 gtk_paned_set_position (GTK_PANED (paned), pos);
522 g_object_set_data (G_OBJECT (paned),
"init", GINT_TO_POINTER (1));
526 void YGUtils::setPaneRelPosition (GtkWidget *paned, gdouble rel)
528 gint _rel = rel * 100;
529 g_signal_connect_after (G_OBJECT (paned),
"size-allocate",
530 G_CALLBACK (paned_allocate_cb), GINT_TO_POINTER (_rel));
533 void ygutils_setPaneRelPosition (GtkWidget *paned, gdouble rel)
534 { YGUtils::setPaneRelPosition (paned, rel); }
536 GdkPixbuf *YGUtils::loadPixbuf (
const std::string &filename)
538 GdkPixbuf *pixbuf = NULL;
539 if (!filename.empty()) {
541 pixbuf = gdk_pixbuf_new_from_file (filename.c_str(), &error);
543 yuiWarning() <<
"Could not load icon: " << filename <<
"\n"
544 "Reason: " << error->message <<
"\n";
550 static inline guchar pixel_clamp (
int val)
551 {
return MAX (0, MIN (255, val)); }
552 GdkPixbuf *YGUtils::setOpacity (
const GdkPixbuf *src,
int opacity,
bool touchAlpha)
554 if (!src)
return NULL;
555 int shift = 255 - ((opacity * 255) / 100);
556 int rgb_shift = 0, alpha_shift = 0;
562 int width = gdk_pixbuf_get_width (src), height = gdk_pixbuf_get_height (src);
563 gboolean has_alpha = gdk_pixbuf_get_has_alpha (src);
565 GdkPixbuf *dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
566 has_alpha, gdk_pixbuf_get_bits_per_sample (src), width, height);
568 guchar *src_pixels_orig = gdk_pixbuf_get_pixels (src);
569 guchar *dest_pixels_orig = gdk_pixbuf_get_pixels (dest);
571 int src_rowstride = gdk_pixbuf_get_rowstride (src);
572 int dest_rowstride = gdk_pixbuf_get_rowstride (dest);
574 for (i = 0; i < height; i++) {
575 guchar *src_pixels = src_pixels_orig + (i * src_rowstride);
576 guchar *dest_pixels = dest_pixels_orig + (i * dest_rowstride);
577 for (j = 0; j < width; j++) {
578 *(dest_pixels++) = pixel_clamp (*(src_pixels++) + rgb_shift);
579 *(dest_pixels++) = pixel_clamp (*(src_pixels++) + rgb_shift);
580 *(dest_pixels++) = pixel_clamp (*(src_pixels++) + rgb_shift);
582 *(dest_pixels++) = pixel_clamp (*(src_pixels++) - alpha_shift);
588 GdkPixbuf *YGUtils::setGray (
const GdkPixbuf *src)
590 int width = gdk_pixbuf_get_width (src), height = gdk_pixbuf_get_height (src);
591 gboolean has_alpha = gdk_pixbuf_get_has_alpha (src);
593 GdkPixbuf *dest = gdk_pixbuf_new (gdk_pixbuf_get_colorspace (src),
594 has_alpha, gdk_pixbuf_get_bits_per_sample (src), width, height);
596 guchar *src_pixels_orig = gdk_pixbuf_get_pixels (src);
597 guchar *dest_pixels_orig = gdk_pixbuf_get_pixels (dest);
599 int src_rowstride = gdk_pixbuf_get_rowstride (src);
600 int dest_rowstride = gdk_pixbuf_get_rowstride (dest);
602 for (i = 0; i < height; i++) {
603 guchar *src_pixels = src_pixels_orig + (i * src_rowstride);
604 guchar *dest_pixels = dest_pixels_orig + (i * dest_rowstride);
605 for (j = 0; j < width; j++) {
606 int clr = (src_pixels[0] + src_pixels[1] + src_pixels[2]) / 3;
607 *(dest_pixels++) = clr;
608 *(dest_pixels++) = clr;
609 *(dest_pixels++) = clr;
612 *(dest_pixels++) = *(src_pixels++);
618 GdkPixbuf *ygutils_setOpacity (
const GdkPixbuf *src,
int opacity, gboolean useAlpha)
619 {
return YGUtils::setOpacity (src, opacity, useAlpha); }
623 const char *english, *locale, *stock;
625 static const StockMap stock_map[] = {
626 {
"Apply", _(
"Apply"),
"application-exit" },
627 {
"Accept", _(
"Accept"),
"application-exit" },
628 {
"Install", _(
"Install"),
"application-exit" },
629 {
"OK", _(
"OK"),
"document-save" },
630 {
"Cancel", _(
"Cancel"),
"application-exit" },
631 {
"Abort", _(
"Abort"),
"application-exit" },
632 {
"Close", _(
"Close"),
"window-close" },
633 {
"Yes", _(
"Yes"),
"document-save" },
634 {
"No", _(
"No"),
"application-exit" },
635 {
"Add", _(
"Add"),
"list-add" },
636 {
"Edit", _(
"Edit"),
"edit-paste" },
637 {
"Delete", _(
"Delete"),
"list-remove" },
638 {
"Up", _(
"Up"),
"go-up" },
639 {
"Down", _(
"Down"),
"go-down" },
640 {
"Enable", _(
"Enable"),
"document-save" },
641 {
"Disable", _(
"Disable"),
"application-exit" },
642 {
"Exit", _(
"Exit"),
"application-exit" },
643 {
"Back", _(
"Back"),
"go-previous" },
644 {
"Next", _(
"Next"),
"go-next" },
646 #define stock_map_length (sizeof (stock_map) / sizeof (StockMap))
648 static std::string cutUnderline (
const std::string &str)
650 std::string ret (str);
651 std::string::size_type i = 0;
652 if ((i = ret.find (
'_', i)) != std::string::npos)
657 static void stripStart (std::string &str,
char ch)
659 while (!str.empty() && str[0] == ch)
663 static void stripEnd (std::string &str,
char ch)
665 while (!str.empty() && str[str.size()-1] == ch)
666 str.erase (str.size()-1, 1);
669 const char *YGUtils::mapIconname(
const std::string &label )
671 std::map <std::string, std::string> stockMap;
673 std::string
id = cutUnderline (std::string(label));
674 stripStart (
id,
' ');
678 std::map <std::string, std::string>::const_iterator it;
679 it = stockMap.find (
id);
680 if (it != stockMap.end())
681 return it->second.c_str();
686 const char *YGUtils::setStockIcon (GtkWidget *button,
const std::string &label,
687 const char *fallbackIcon)
689 const char *icon = mapIconname (label);
691 if (!icon && label.size() < 22)
694 if (gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default(), icon, GTK_ICON_SIZE_BUTTON, GTK_ICON_LOOKUP_USE_BUILTIN )) {
696 GtkWidget *image = gtk_image_new_from_icon_name (icon, GTK_ICON_SIZE_BUTTON);
697 gtk_button_set_always_show_image(GTK_BUTTON (button),
true);
698 gtk_button_set_image (GTK_BUTTON (button), image);
702 GtkWidget *image = gtk_button_get_image (GTK_BUTTON (button));
704 gtk_widget_hide (image);
709 void YGUtils::shrinkWidget (GtkWidget *widget)
711 static bool first_time =
true;
712 GtkCssProvider *provider;
715 provider = gtk_css_provider_new ();
716 gtk_css_provider_load_from_data (provider,
717 "style \"small-widget-style\"\n"
719 " GtkWidget::focus-padding = 0\n"
720 " GtkWidget::focus-line-width = 0\n"
724 "widget \"*.small-widget\" style \"small-widget-style\"", -1, NULL);
725 gtk_style_context_add_provider (gtk_widget_get_style_context (widget),
726 GTK_STYLE_PROVIDER (provider),
727 GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
728 g_object_unref (provider);
729 gtk_widget_set_name (widget,
"small-widget");
739 ygutils_headerize_help (
const char *help_text, gboolean *cut)
741 char *text = ygutils_convert_to_xhtml (help_text);
743 GString *str = g_string_new (
"");
745 gboolean copy_word = FALSE;
746 for (i = 0; text[i]; i++) {
747 if (text[i] ==
'<') {
753 if (!strncasecmp (text+a,
"<h", 2) || !strncasecmp (text+a,
"<big>", 5) ||
754 (!str->len && !strncasecmp (text+a,
"<b>", 3))) {
755 for (i++; text[i]; i++) {
758 if (text[i] ==
'>') {
759 if (!strncasecmp (text+a,
"</h", 3) || !strncasecmp (text+a,
"</big>", 6) ||
760 !strncasecmp (text+a,
"</b>", 4))
766 else if (g_ascii_isspace (text[i])) {
768 g_string_append_c (str,
' ');
773 g_string_append_c (str, text[i]);
774 if (text[i] ==
'.') {
775 if (g_ascii_isspace (text[i+1]) || text[i+1] ==
'<') {
783 gboolean markup = FALSE;
784 for (; text[i]; i++) {
792 else if (!g_ascii_isspace (text[i])) {
799 return g_string_free (str, FALSE);
803 const char *ygutils_mapIconname (
const std::string &label)
804 {
return YGUtils::mapIconname (label); }
807 const char *ygutils_setStockIcon (GtkWidget *button,
const char *label,
const char *fallback)
808 {
return YGUtils::setStockIcon (button, label, fallback); }
814 __LEFT_PTR_WATCH = None
815 def set_busy_cursor (window):
816 global __LEFT_PTR_WATCH
817 if __LEFT_PTR_WATCH is None:
818 os.environ[
'XCURSOR_DISCOVER'] =
'1' #Turn on logging in Xlib
819 # Busy cursor code from Padraig Brady <P@draigBrady.com>
820 # cursor_data hash is 08e8e1c95fe2fc01f976f1e063a24ccd
822 \x00\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\
823 \x0c\x00\x00\x00\x1c\x00\x00\x00\x3c\x00\x00\x00\
824 \x7c\x00\x00\x00\xfc\x00\x00\x00\xfc\x01\x00\x00\
825 \xfc\x3b\x00\x00\x7c\x38\x00\x00\x6c\x54\x00\x00\
826 \xc4\xdc\x00\x00\xc0\x44\x00\x00\x80\x39\x00\x00\
827 \x80\x39\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
828 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
829 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
830 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
831 \x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
832 \x00\x00\x00\x00\x00\x00\x00\x00"
835 pix = gtk.gdk.bitmap_create_from_data(None, cursor_data, 32, 32)
836 color = gtk.gdk.Color()
837 __LEFT_PTR_WATCH = gtk.gdk.Cursor(pix, pix, color, color, 2, 2)
840 # default
"WATCH" cursor
841 __LEFT_PTR_WATCH = gtk.gdk.Cursor(gtk.gdk.WATCH)
842 window.set_cursor (__LEFT_PTR_WATCH)
846 gboolean YGUtils::empty_row_is_separator_cb (
847 GtkTreeModel *model, GtkTreeIter *iter, gpointer _text_col)
849 int text_col = GPOINTER_TO_INT (_text_col);
851 gtk_tree_model_get (model, iter, text_col, &str, -1);
852 bool ret = !str || !(*str);