All of lore.kernel.org
 help / color / mirror / Atom feed
From: Vladimir Testov <vladimir.testov@rosalab.ru>
To: grub-devel@gnu.org
Subject: RE: [PATCH] multistring support in gui_label
Date: Fri, 22 Mar 2013 19:58:50 +0400	[thread overview]
Message-ID: <1433564.XLGijaNbUn@icedphoenix> (raw)

[-- 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;
 }

             reply	other threads:[~2013-03-22 15:59 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-03-22 15:58 Vladimir Testov [this message]
2013-04-03  7:20 ` [PATCH] multistring support in gui_label Vladimir 'φ-coder/phcoder' Serbinenko
  -- strict thread matches above, loose matches on Subject: below --
2013-04-16  9:04 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-03-21 17:17 Vladimir Testov
2013-03-21 18:12 ` Gerard Butler

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1433564.XLGijaNbUn@icedphoenix \
    --to=vladimir.testov@rosalab.ru \
    --cc=grub-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.