diff -Naur grub-2.00/grub-core/gfxmenu/gui_label.c grub-new/grub-core/gfxmenu/gui_label.c --- grub-2.00/grub-core/gfxmenu/gui_label.c 2012-03-03 16:00:50.000000000 +0400 +++ grub-new/grub-core/gfxmenu/gui_label.c 2013-03-21 20:36:26.574105575 +0400 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -51,11 +52,22 @@ grub_font_t font; grub_video_rgba_color_t color; int value; + int multistring; enum align_mode align; }; typedef struct grub_gui_label *grub_gui_label_t; +static int +is_whitespace (char c) +{ + return (c == ' ' + || c == '\t' + || c == '\r' + || c == '\n' + || c == '\f'); +} + static void label_destroy (void *vself) { @@ -79,17 +91,81 @@ return grub_strcmp (type, "component") == 0; } -static void -label_paint (void *vself, const grub_video_rect_t *region) +static int +label_end_of_word (char *buf, int len, int pos) { - grub_gui_label_t self = vself; + while ((pos < len) && (!is_whitespace (buf[pos]))) + { + pos++; + } + return pos; +} - if (! self->visible) - return; +static int +label_string_width (char *buf, grub_font_t font, int beg, int end) +{ + char *tmpstr = grub_new_substring (buf, beg, end); + int result = grub_font_get_string_width (font, tmpstr); + grub_free (tmpstr); + return result; +} - if (!grub_video_have_common_points (region, &self->bounds)) - return; +static int +label_get_max_symbols(char *buf, grub_font_t font, int max_width, + int start, int end, int *result_width, int isvertical) +{ + grub_uint32_t *ptr; + grub_ssize_t logical_len; + grub_uint32_t *logical; + struct grub_unicode_glyph glyph; + + char *tmpstr = grub_new_substring (buf, start, end); + logical_len = grub_utf8_to_ucs4_alloc (tmpstr, &logical, 0); + grub_free (tmpstr); + if (logical_len < 0) + return end; + ptr = logical; + + if (isvertical) + { + ptr += grub_unicode_aglomerate_comb (ptr, + logical_len - (ptr - logical), + &glyph); + *result_width += grub_font_get_constructed_device_width (font, &glyph); + grub_free (glyph.combining); + + int result = start + grub_get_num_of_utf8_bytes (logical, 1); + grub_free (logical); + return result; + } + + int cnt = 0; + int last_width; + int str_width = *result_width; + while ((str_width <= max_width) && (ptr < logical + logical_len)) + { + cnt ++; + ptr += grub_unicode_aglomerate_comb (ptr, + logical_len - (ptr - logical), + &glyph); + last_width = str_width; + str_width += grub_font_get_constructed_device_width (font, &glyph); + grub_free (glyph.combining); + } + if (str_width > max_width) + { + cnt --; + str_width = last_width; + } + int result = start + grub_get_num_of_utf8_bytes (logical, cnt); + grub_free (logical); + *result_width = str_width; + return result; +} +static void +label_paint (grub_gui_label_t self) +{ /* Calculate the starting x coordinate. */ int left_x; if (self->align == align_left) @@ -100,19 +176,115 @@ else if (self->align == align_right) left_x = (self->bounds.width - grub_font_get_string_width (self->font, self->text)); - else - return; /* Invalid alignment. */ if (left_x < 0 || left_x > (int) self->bounds.width) left_x = 0; - grub_video_rect_t vpsave; - grub_gui_set_viewport (&self->bounds, &vpsave); grub_font_draw_string (self->text, self->font, grub_video_map_rgba_color (self->color), left_x, grub_font_get_ascent (self->font)); +} + +static void +label_paint_multistring (grub_gui_label_t self) +{ + int baseline = grub_font_get_ascent (self->font); + int text_height = grub_font_get_max_char_height (self->font); + int left_x = 0; + int l_width = self->bounds.width; + int l_lastend; + char *l_tmpstr; + + char *buf = self->text; + int pos = 0; + int end = 0; + int len = grub_strlen(buf); + grub_font_t font = self->font; + int str_width = 0; + int word_width = 0; + + int isvertical = (l_width == grub_font_get_max_char_width (self->font)); + + while (pos < len) + { + l_lastend = end; + if (is_whitespace (buf[end])) + { + end ++; + } else { + end = label_end_of_word (buf, len, end); // non-empty string + if (!isvertical && is_whitespace (buf[pos])) + { + pos = l_lastend; // remove whitespaces from the start of a string + str_width = 0; + } + } + word_width = label_string_width (buf, font, l_lastend, end); + if (isvertical || (str_width + word_width > l_width)) // can't add new word + { + if (isvertical || (word_width > l_width)) // word is too big + { // print as many symbols + end = label_get_max_symbols (buf, font, l_width, l_lastend, end, + &str_width, isvertical); + } else { // newline + end = l_lastend; + } + } else { // new word can be printed in the same line + str_width += word_width; + if (end < len) // if there are any other word + continue; + } + /* Remove whitespaces from the end of a string */ + l_lastend = end; + if (!isvertical) + { + while ((end - pos > 1) && is_whitespace (buf[end - 1])) + end --; + str_width -= label_string_width(buf, font, end, l_lastend); + } + /* Calculate the starting x coordinate. */ + if (self->align == align_center) + left_x = (l_width - str_width) / 2; + else if (self->align == align_right) + left_x = (l_width - str_width); + l_tmpstr = grub_new_substring (buf, pos, end); + grub_font_draw_string (l_tmpstr, + font, + grub_video_map_rgba_color (self->color), + left_x, + baseline); + grub_free (l_tmpstr); + baseline += text_height; + str_width = 0; + /* Return saved state of "end" */ + end = l_lastend; + pos = l_lastend; + } +} + +static void +label_paint_dispatcher (void *vself, const grub_video_rect_t *region) +{ + grub_gui_label_t self = vself; + if (! self->visible) + return; + + if (!grub_video_have_common_points (region, &self->bounds)) + return; + + if ((!self->align == align_left) && + (!self->align == align_center) && + (!self->align == align_right)) + return; /* Invalid alignment */ + + grub_video_rect_t vpsave; + grub_gui_set_viewport (&self->bounds, &vpsave); + if (self->multistring) + label_paint_multistring(self); + else + label_paint(self); grub_gui_restore_viewport (&vpsave); } @@ -148,9 +320,15 @@ label_get_minimal_size (void *vself, unsigned *width, unsigned *height) { grub_gui_label_t self = vself; - *width = grub_font_get_string_width (self->font, self->text); - *height = (grub_font_get_ascent (self->font) - + grub_font_get_descent (self->font)); + if (self->multistring) + { + *width = grub_font_get_max_char_width (self->font); + *height = grub_font_get_max_char_height (self->font); + } else { + *width = grub_font_get_string_width (self->font, self->text); + *height = (grub_font_get_ascent (self->font) + + grub_font_get_descent (self->font)); + } } static void @@ -231,6 +409,10 @@ grub_gfxmenu_timeout_register ((grub_gui_component_t) self, label_set_state); } + else if (grub_strcmp (name, "multistring") == 0) + { + self->multistring = grub_strcmp (value, "false") != 0; + } return GRUB_ERR_NONE; } @@ -239,7 +421,7 @@ .destroy = label_destroy, .get_id = label_get_id, .is_instance = label_is_instance, - .paint = label_paint, + .paint = label_paint_dispatcher, .set_parent = label_set_parent, .get_parent = label_get_parent, .set_bounds = label_set_bounds, @@ -264,5 +446,6 @@ label->color.blue = 0; label->color.alpha = 255; label->align = align_left; + label->multistring = 0; return (grub_gui_component_t) label; }