grub-devel.gnu.org archive mirror
 help / color / mirror / Atom feed
* Re: [PATCH] multistring support in gui_label
@ 2013-04-16  9:04 Vladimir Testov
  2013-04-16 17:10 ` Andrey Borzenkov
  2013-04-17  7:01 ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 2 replies; 14+ messages in thread
From: Vladimir Testov @ 2013-04-16  9:04 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 2157 bytes --]

>Please don't use // comments.
O.k. I won't.
>This code completely forgets the cases
>when even the first word doesn't fit in the available space.

Mmm. It can handle this case actually.

>The code as whole breaks some unicode concepts like e.g. bidi stack.

Didn't get what you mean.

>Could you reuse the already available line-vreaking algorithm in
>normal/term.c and normal/charset.c ? Since the line-breaking is
>artificially disabled for labels it should require only minor
>adjustments to be reenabled.

Yep! Here it is (patch included)!

Nevertheless, two problems appeared and I don't sure how exactly should I fix 
them.
1) Handling of some long word. If this word is not first in line and it's 
length is more than label's width
then the length of the first fragment of the word will be counted as if it will 
be drawn on the same line,
but actually it will be printed on the next line.
2) There is funny handling of UTF-8 symbols. Each symbol have "device_width" 
parameter,
which is used in calculation of string's length. But, when we are going to 
actually print a symbol,
it's length (with spaces) is calculated in other way. First, all symbols that 
have special connection with
current symbol are looked through. Then device_width is replaced with maximum 
of printed lengths in all cases.
So, for some special symbols (eg "`", used in @KEYMAP_LONG@) actual length 
will be more than pre-counted.

How should I fix these problems?
1st one - for example, I can slightly update line-breaking mechanism.
2nd one - more interesting, harder. I suggest utf-8 printing mechanism 
(charset.c unicode.c etc) should be remade. So symbol connections will be 
counted in more intelligent way (e.g. while counting spaces - take into 
consideration nearby symbols). It is interesting. :) I can do it. Would be 
happy, if someone could give me some advices.

Problem2.png 
text = "@KEYMAP_LONG@"
t is misprinted

Problem1.png
text = "short short short 
HereWeHaveSomeVeryLongWordSoItCannotBePrintedEntirelyOnOneLine"
See how the line-breaking works.

-- 
With best regards,
_______________________________
Vladimir Testov, ROSA Laboratory.
www.rosalab.ru

[-- Attachment #2: Problem1.png --]
[-- Type: image/png, Size: 2141 bytes --]

[-- Attachment #3: problem2.png --]
[-- Type: image/png, Size: 2954 bytes --]

[-- Attachment #4: grub-2.00-label-multistring.patch --]
[-- Type: text/x-patch, Size: 7526 bytes --]

diff -Naur grub-2.00/grub-core/gfxmenu/font.c grub-new/grub-core/gfxmenu/font.c
--- grub-2.00/grub-core/gfxmenu/font.c	2012-03-27 18:29:43.000000000 +0400
+++ grub-new/grub-core/gfxmenu/font.c	2013-04-15 09:55:26.978883414 +0400
@@ -75,6 +75,94 @@
   return GRUB_ERR_NONE;
 }
 
+/* Differs from grub_font_draw_line only in multiline output */
+grub_err_t
+grub_font_draw_multiline (const char *str, grub_font_t font,
+                          grub_video_color_t color,
+                          int baseline_y, int font_height,
+                          int max_width, int max_height,
+                          int align)
+{
+  int x;
+  struct grub_font_glyph *glyph;
+  grub_uint32_t *logical;
+  grub_ssize_t logical_len, visual_len;
+  struct grub_unicode_glyph *visual, *ptr, *pos;
+
+  grub_err_t print_line ()
+  {
+    while (pos != ptr)
+      {
+        grub_err_t err;
+        glyph = grub_font_construct_glyph (font, pos);
+        if (!glyph)
+          return grub_errno;
+        err = grub_font_draw_glyph (glyph, color, x, baseline_y);
+        x += glyph->device_width;
+        grub_free (glyph);
+        if (err)
+          return err;
+        pos ++;
+      }
+
+    return GRUB_ERR_NONE;
+  }
+
+  logical_len = grub_utf8_to_ucs4_alloc (str, &logical, 0);
+  if (logical_len < 0)
+    return grub_errno;
+
+  auto grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c);
+  grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c)
+  {
+    return grub_font_get_constructed_device_width (font, c);
+  }
+
+  visual_len = grub_bidi_logical_to_visual (logical, logical_len, &visual,
+					    getcharwidth, max_width, 0);
+  grub_free (logical);
+  if (visual_len < 0)
+    return grub_errno;
+
+  pos = visual;
+  for (ptr = visual, x = 0; ptr <= visual + visual_len; ptr++)
+    {
+      if (baseline_y > max_height)
+        break;
+      if (ptr->base == '\n' || ptr == visual + visual_len)
+        {
+          int left_x;
+          if (align == 1)
+            left_x = (max_width - x) / 2;
+          else if (align == 2)
+            left_x = max_width - x;
+          else
+            left_x = 0;
+
+          x = left_x;
+
+          grub_err_t err;
+          err = print_line();
+          if (err != GRUB_ERR_NONE)
+            return err;
+
+          baseline_y += font_height;
+          x = 0;
+          while ((ptr + 1 < visual + visual_len)
+                 && ((ptr + 1)->base == ' '
+                     || (ptr + 1)->base == '\t'))
+            ptr ++;
+          pos = ptr + 1;
+        }
+      else
+        x += grub_font_get_constructed_device_width (font, ptr);
+    }
+
+  grub_free (visual);
+
+  return GRUB_ERR_NONE;
+}
+
 /* Get the width in pixels of the specified UTF-8 string, when rendered in
    in the specified font (but falling back on other fonts for glyphs that
    are missing).  */
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-04-12 16:40:50.151859580 +0400
@@ -46,6 +46,7 @@
   grub_video_rect_t bounds;
   char *id;
   int visible;
+  int multiline;
   char *text;
   char *template;
   grub_font_t font;
@@ -91,28 +92,46 @@
     return;
 
   /* Calculate the starting x coordinate.  */
-  int left_x;
-  if (self->align == align_left)
-    left_x = 0;
-  else if (self->align == align_center)
-    left_x = (self->bounds.width
-	      - grub_font_get_string_width (self->font, self->text)) / 2;
-  else if (self->align == align_right)
-    left_x = (self->bounds.width
-              - grub_font_get_string_width (self->font, self->text));
-  else
-    return;   /* Invalid alignment.  */
+  int left_x = 0;
+  if (!self->multiline)
+    {
+      if (self->align == align_left)
+        left_x = 0;
+      else if (self->align == align_center)
+        left_x = (self->bounds.width
+                  - grub_font_get_string_width (self->font, self->text)) / 2;
+      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;
+      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));
+  if (!self->multiline)
+    grub_font_draw_string (self->text,
+                           self->font,
+                           grub_video_map_rgba_color (self->color),
+                           left_x,
+                           grub_font_get_ascent (self->font));
+  else
+    {
+      int font_height = grub_font_get_max_char_height (self->font);
+      int max_width = self->bounds.width;
+      int max_height = self->bounds.height;
+      grub_font_draw_multiline (self->text,
+                                self->font,
+                                grub_video_map_rgba_color (self->color),
+                                grub_font_get_ascent (self->font),
+                                font_height,
+                                max_width,
+                                max_height,
+                                self->align);
+    }
   grub_gui_restore_viewport (&vpsave);
 }
 
@@ -148,9 +167,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->multiline)
+    {
+      *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
@@ -218,6 +243,10 @@
     {
       self->visible = grub_strcmp (value, "false") != 0;
     }
+  else if (grub_strcmp (name, "multiline") == 0)
+    {
+      self->multiline = grub_strcmp (value, "false") != 0;
+    }
   else if (grub_strcmp (name, "id") == 0)
     {
       grub_gfxmenu_timeout_unregister ((grub_gui_component_t) self);
@@ -257,6 +286,7 @@
     return 0;
   label->comp.ops = &label_ops;
   label->visible = 1;
+  label->multiline = 0;
   label->text = grub_strdup ("");
   label->font = grub_font_get ("Unknown Regular 16");
   label->color.red = 0;
diff -Naur grub-2.00/include/grub/gfxmenu_view.h grub-new/include/grub/gfxmenu_view.h
--- grub-2.00/include/grub/gfxmenu_view.h	2012-02-24 14:19:45.000000000 +0400
+++ grub-new/include/grub/gfxmenu_view.h	2013-04-12 16:26:41.869448464 +0400
@@ -66,6 +66,12 @@
 				  grub_font_t font,
 				  grub_video_color_t color,
 				  int left_x, int baseline_y);
+grub_err_t grub_font_draw_multiline (const char *str,
+				     grub_font_t font,
+				     grub_video_color_t color,
+				     int baseline_y, int font_height,
+				     int max_width, int max_height,
+				     int align);
 int grub_font_get_string_width (grub_font_t font,
 				const char *str);
 

^ permalink raw reply	[flat|nested] 14+ messages in thread
* RE: [PATCH] multistring support in gui_label
@ 2013-03-22 15:58 Vladimir Testov
  2013-04-03  7:20 ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 14+ messages in thread
From: Vladimir Testov @ 2013-03-22 15:58 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 161 bytes --]

muiltistring changed to multiline
(now we have multiline)
-- 
With best regards,
_______________________________
Vladimir Testov, ROSA Laboratory.
www.rosalab.ru

[-- Attachment #2: grub-2.00-label-multistring.patch --]
[-- Type: text/x-patch, Size: 8302 bytes --]

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-22 19:33:24.187744391 +0400
@@ -21,6 +21,7 @@
 #include <grub/misc.h>
 #include <grub/gui.h>
 #include <grub/font.h>
+#include <grub/charset.h>
 #include <grub/gui_string_util.h>
 #include <grub/i18n.h>
 
@@ -51,11 +52,22 @@
   grub_font_t font;
   grub_video_rgba_color_t color;
   int value;
+  int multiline;
   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_multiline (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->multiline)
+    label_paint_multiline(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->multiline)
+    {
+      *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, "multiline") == 0)
+    {
+      self->multiline = 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->multiline = 0;
   return (grub_gui_component_t) label;
 }

^ permalink raw reply	[flat|nested] 14+ messages in thread
* [PATCH] multistring support in gui_label
@ 2013-03-21 17:17 Vladimir Testov
  2013-03-21 18:12 ` Gerard Butler
  0 siblings, 1 reply; 14+ messages in thread
From: Vladimir Testov @ 2013-03-21 17:17 UTC (permalink / raw)
  To: grub-devel

[-- Attachment #1: Type: text/plain, Size: 997 bytes --]

This time I've managed not to break backward compatibility.

New option in label - "multistring"

It is "false" by default.

when it is set to "true" (not "false") text is beeing output on multiple 
lines.

see screenshots included

let me know if I've made smth wrong.

Thanks. :)
_______________
theme.txt:

+ label {
        left = 100
        top = 20
        height = 100
        width = 600
        color = "cyan"
        align = "right"
        text = "@KEYMAP_LONG@"
       multistring = "true"
}
+ label {
        left = 100
        top = 120
        height = 300
        width = 60
        color = "cyan"
        align = "right"
        text = "@KEYMAP_LONG@"
       multistring = "true"
}
+ label {
        left = 20
        top = 50
        height = 600
        width = 1
        color = "cyan"
        align = "right"
        text = "@KEYMAP_LONG@"
       multistring = "true"
}


-- 
With best regards,
_______________________________
Vladimir Testov, ROSA Laboratory.
www.rosalab.ru

[-- Attachment #2: grub-2.00-label-multistring.patch --]
[-- Type: text/x-patch, Size: 8318 bytes --]

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 <grub/misc.h>
 #include <grub/gui.h>
 #include <grub/font.h>
+#include <grub/charset.h>
 #include <grub/gui_string_util.h>
 #include <grub/i18n.h>
 
@@ -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;
 }

[-- Attachment #3: after-label.png --]
[-- Type: image/png, Size: 13233 bytes --]

[-- Attachment #4: before-label.png --]
[-- Type: image/png, Size: 10475 bytes --]

^ permalink raw reply	[flat|nested] 14+ messages in thread

end of thread, other threads:[~2013-04-23 16:27 UTC | newest]

Thread overview: 14+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-04-16  9:04 [PATCH] multistring support in gui_label Vladimir Testov
2013-04-16 17:10 ` Andrey Borzenkov
2013-04-17  6:56   ` Vladimir Testov
2013-04-17  7:01 ` Vladimir 'φ-coder/phcoder' Serbinenko
2013-04-17  9:02   ` Vladimir Testov
2013-04-17 11:16     ` Vladimir Testov
2013-04-17 11:39     ` Vladimir Testov
2013-04-17 12:41       ` Vladimir 'φ-coder/phcoder' Serbinenko
2013-04-23 11:58       ` Vladimir Testov
2013-04-23 16:26         ` [RFC][PATCH] " Vladimir Testov
  -- strict thread matches above, loose matches on Subject: below --
2013-03-22 15:58 [PATCH] " Vladimir Testov
2013-04-03  7:20 ` Vladimir 'φ-coder/phcoder' Serbinenko
2013-03-21 17:17 Vladimir Testov
2013-03-21 18:12 ` Gerard Butler

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).