[haiku-commits] r35381 - in haiku/trunk: headers/private/interface src/kits/interface src/servers/app

  • From: mmlr@xxxxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Mon, 1 Feb 2010 19:43:03 +0100 (CET)

Author: mmlr
Date: 2010-02-01 19:43:03 +0100 (Mon, 01 Feb 2010)
New Revision: 35381
Changeset: http://dev.haiku-os.org/changeset/35381/haiku
Ticket: http://dev.haiku-os.org/ticket/4128

Modified:
   haiku/trunk/headers/private/interface/truncate_string.h
   haiku/trunk/src/kits/interface/Font.cpp
   haiku/trunk/src/kits/interface/InterfaceDefs.cpp
   haiku/trunk/src/servers/app/ServerFont.cpp
Log:
* Replace the truncate_string() helper function with a new, simplified version.
* Remove no longer necessary support functions.
* The new version uses a single BString as input/output parameter and only
  modifies that one by removing non-fitting chars and inserting the ellipsis
  where appropriate, so avoids copying around bytes/chars/strings in a few
  places. It uses the new Chars functions of BString so also no need for manual
  multibyte handling.
* Adjusted the BFont and ServerFont usage of truncate_string() which are both
  simplified by using the single BString. It avoids a lot of temprary
  allocations and string copying. The char * version of BFont
  GetTruncatedStrings() now uses the BString version and not the other way
  around anymore which requires us to allocate temporary BString objects, it's
  not worse than before though.
* This fixes a bunch of problems with the previous functions like always
  prepending the ellipsis for B_TRUNCATE_BEGINNING, crashing on short enough
  widths, violating the width in the B_TRUNCATE_END case when the width was
  short enough, non-optimal truncation in a few cases and sometimes truncation
  where none would've been needed. Also fixes #4128 which was a symptom of the
  broken B_TRUNCATE_BEGINNING.


Modified: haiku/trunk/headers/private/interface/truncate_string.h
===================================================================
--- haiku/trunk/headers/private/interface/truncate_string.h     2010-02-01 
17:04:49 UTC (rev 35380)
+++ haiku/trunk/headers/private/interface/truncate_string.h     2010-02-01 
18:43:03 UTC (rev 35381)
@@ -1,22 +1,14 @@
 /*
- * Copyright 2005, Stephan Aßmus <superstippi@xxxxxx>. All rights reserved.
+ * Copyright 2010, Michael Lotz <mmlr@xxxxxxxx>. All rights reserved.
  * Distributed under the terms of the MIT License.
- *
- * a helper function to truncate strings
- *
  */
-
-
 #ifndef TRUNCATE_STRING_H
 #define TRUNCATE_STRING_H
 
 #include <SupportDefs.h>
 
-// truncated_string
-void
-truncate_string(const char* string,
-                               uint32 mode, float width, char* result,
-                               const float* escapementArray, float fontSize,
-                               float ellipsisWidth, int32 length, int32 
numChars);
+void truncate_string(BString& string, uint32 mode, float width,
+       const float* escapementArray, float fontSize, float ellipsisWidth,
+       int32 numChars);
 
-#endif // STRING_TRUNCATION_H
+#endif // TRUNCATE_STRING_H

Modified: haiku/trunk/src/kits/interface/Font.cpp
===================================================================
--- haiku/trunk/src/kits/interface/Font.cpp     2010-02-01 17:04:49 UTC (rev 
35380)
+++ haiku/trunk/src/kits/interface/Font.cpp     2010-02-01 18:43:03 UTC (rev 
35381)
@@ -971,22 +971,23 @@
 BFont::GetTruncatedStrings(const char *stringArray[], int32 numStrings,
        uint32 mode, float width, BString resultArray[]) const
 {
-       if (stringArray && resultArray && numStrings > 0) {
-               // allocate storage, see BeBook for "+ 3" (make space for 
ellipsis)
-               char** truncatedStrings = new char*[numStrings];
+       if (stringArray != NULL && numStrings > 0) {
+               // the width of the "…" glyph
+               float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS);
+
                for (int32 i = 0; i < numStrings; i++) {
-                       truncatedStrings[i] = new char[strlen(stringArray[i]) + 
3];
-               }
+                       resultArray[i] = stringArray[i];
+                       int32 numChars = resultArray[i].CountChars();
 
-               GetTruncatedStrings(stringArray, numStrings, mode, width,
-                       truncatedStrings);
+                       // get the escapement of each glyph in font units
+                       float *escapementArray = new float[numChars];
+                       GetEscapements(stringArray[i], numChars, NULL, 
escapementArray);
 
-               // copy the strings into the BString array and free each one
-               for (int32 i = 0; i < numStrings; i++) {
-                       resultArray[i].SetTo(truncatedStrings[i]);
-                       delete[] truncatedStrings[i];
+                       truncate_string(resultArray[i], mode, width, 
escapementArray,
+                               fSize, ellipsisWidth, numChars);
+
+                       delete[] escapementArray;
                }
-               delete[] truncatedStrings;
        }
 }
 
@@ -995,21 +996,15 @@
 BFont::GetTruncatedStrings(const char *stringArray[], int32 numStrings,
        uint32 mode, float width, char *resultArray[]) const
 {
-       if (stringArray && numStrings > 0) {
-               // the width of the "…" glyph
-               float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS);
+       if (stringArray != NULL && numStrings > 0) {
                for (int32 i = 0; i < numStrings; i++) {
-                       int32 length = strlen(stringArray[i]);
-                       // count the individual glyphs
-                       int32 numChars = UTF8CountChars(stringArray[i], length);
-                       // get the escapement of each glyph in font units
-                       float* escapementArray = new float[numChars];
-                       GetEscapements(stringArray[i], numChars, NULL, 
escapementArray);
+                       BString *strings = new BString[numStrings];
+                       GetTruncatedStrings(stringArray, numStrings, mode, 
width, strings);
 
-                       truncate_string(stringArray[i], mode, width, 
resultArray[i],
-                               escapementArray, fSize, ellipsisWidth, length, 
numChars);
+                       for (int32 i = 0; i < numStrings; i++)
+                               strcpy(resultArray[i], strings[i].String());
 
-                       delete[] escapementArray;
+                       delete[] strings;
                }
        }
 }

Modified: haiku/trunk/src/kits/interface/InterfaceDefs.cpp
===================================================================
--- haiku/trunk/src/kits/interface/InterfaceDefs.cpp    2010-02-01 17:04:49 UTC 
(rev 35380)
+++ haiku/trunk/src/kits/interface/InterfaceDefs.cpp    2010-02-01 18:43:03 UTC 
(rev 35381)
@@ -1,11 +1,12 @@
 /*
- * Copyright 2001-2009, Haiku, Inc.
+ * Copyright 2001-2010, Haiku, Inc.
  * Distributed under the terms of the MIT License.
  *
  * Authors:
  *             DarkWyrm <bpmagic@xxxxxxxxxxxxxxx>
  *             Caz <turok2@xxxxxxxxxxxxxx>
  *             Axel Dörfler, axeld@xxxxxxxxxxxxxxxx
+ *             Michael Lotz <mmlr@xxxxxxxx>
  */
 
 
@@ -1361,259 +1362,134 @@
 //     #pragma mark - truncate string
 
 
-static char*
-write_ellipsis(char* dst)
+void
+truncate_string(BString& string, uint32 mode, float width,
+       const float* escapementArray, float fontSize, float ellipsisWidth,
+       int32 charCount)
 {
-       strcpy(dst, B_UTF8_ELLIPSIS);
-       // The UTF-8 character spans over 3 bytes
-       return dst + 3;
-}
+       // add a tiny amount to the width to make floating point inaccuracy
+       // not drop chars that would actually fit exactly
+       width += 0.00001;
 
+       switch (mode) {
+               case B_TRUNCATE_BEGINNING:
+               {
+                       float totalWidth = 0;
+                       for (int32 i = charCount - 1; i >= 0; i--) {
+                               float charWidth = escapementArray[i] * fontSize;
+                               if (totalWidth + charWidth > width) {
+                                       // we need to truncate
+                                       while (totalWidth + ellipsisWidth > 
width) {
+                                               // remove chars until there's 
enough space for the
+                                               // ellipsis
+                                               if (++i == charCount) {
+                                                       // we've reached the 
end of the string and still
+                                                       // no space, so return 
an empty string
+                                                       string.Truncate(0);
+                                                       return;
+                                               }
 
-static bool
-optional_char_fits(float escapement, float fontSize, float gap)
-{
-       const float size = escapement * fontSize;
-       if (size <= gap || fabs(size - gap) <= 0.0001)
-               return true;
-       return false;
-}
+                                               totalWidth -= 
escapementArray[i] * fontSize;
+                                       }
 
+                                       string.RemoveChars(0, i + 1);
+                                       string.PrependChars(B_UTF8_ELLIPSIS, 1);
+                                       return;
+                               }
 
-bool
-truncate_end(const char* source, char* dest, uint32 numChars,
-       const float* escapementArray, float width, float ellipsisWidth, float 
size)
-{
-       float currentWidth = 0.0;
-       ellipsisWidth /= size;
-               // test if this is as accurate as escapementArray * size
-       width /= size;
-       uint32 lastFit = 0, c;
+                               totalWidth += charWidth;
+                       }
 
-       for (c = 0; c < numChars; c++) {
-               currentWidth += escapementArray[c];
-               if (currentWidth + ellipsisWidth <= width)
-                       lastFit = c;
-
-               if (currentWidth > width)
                        break;
-       }
+               }
 
-       if (c == numChars) {
-               // string fits into width
-               return false;
-       }
+               case B_TRUNCATE_END:
+               {
+                       float totalWidth = 0;
+                       for (int32 i = 0; i < charCount; i++) {
+                               float charWidth = escapementArray[i] * fontSize;
+                               if (totalWidth + charWidth > width) {
+                                       // we need to truncate
+                                       while (totalWidth + ellipsisWidth > 
width) {
+                                               // remove chars until there's 
enough space for the
+                                               // ellipsis
+                                               if (i-- == 0) {
+                                                       // we've reached the 
start of the string and still
+                                                       // no space, so return 
an empty string
+                                                       string.Truncate(0);
+                                                       return;
+                                               }
 
-       if (c == 0) {
-               // there is no space for the ellipsis
-               strcpy(dest, "");
-               return true;
-       }
+                                               totalWidth -= 
escapementArray[i] * fontSize;
+                                       }
 
-       // copy string to destination
+                                       string.RemoveChars(i, charCount - i);
+                                       string.AppendChars(B_UTF8_ELLIPSIS, 1);
+                                       return;
+                               }
 
-       for (uint32 i = 0; i < lastFit + 1; i++) {
-               // copy one glyph
-               do {
-                       *dest++ = *source++;
-               } while (IsInsideGlyph(*source));
-       }
-
-       // write ellipsis and terminate
-
-       dest = write_ellipsis(dest);
-       *dest = '\0';
-       return true;
-}
-
-
-static char*
-copy_from_end(const char* src, char* dst, uint32 numChars, uint32 length,
-       const float* escapementArray, float width, float ellipsisWidth, float 
size)
-{
-       const char* originalStart = src;
-       src += length - 1;
-       float currentWidth = 0.0;
-       for (int32 c = numChars - 1; c > 0; c--) {
-               currentWidth += escapementArray[c] * size;
-               if (currentWidth > width) {
-                       // ups, we definitely don't fit. go back until the 
ellipsis fits
-                       currentWidth += ellipsisWidth;
-                       // go forward again until ellipsis fits (already beyond 
the target)
-                       for (uint32 c2 = c; c2 < numChars; c2++) {
-//printf(" backward: %c (%ld) (%.1f - %.1f = %.1f)\n", *dst, c2, currentWidth, 
escapementArray[c2] * size,
-//     currentWidth - escapementArray[c2] * size);
-                               currentWidth -= escapementArray[c2] * size;
-                               do {
-                                       src++;
-                               } while (IsInsideGlyph(*src));
-                               // see if we went back enough
-                               if (currentWidth <= width)
-                                       break;
+                               totalWidth += charWidth;
                        }
+
                        break;
-               } else {
-                       // go back one glyph
-                       do {
-                               src--;
-                       } while (IsInsideGlyph(*src));
                }
-       }
-       // copy from the end of the string
-       uint32 bytesToCopy = originalStart + length - src;
-       memcpy(dst, src, bytesToCopy);
-       dst += bytesToCopy;
-       return dst;
-}
 
+               case B_TRUNCATE_MIDDLE:
+               case B_TRUNCATE_SMART:
+               {
+                       float leftWidth = 0;
+                       float rightWidth = 0;
+                       int32 leftIndex = 0;
+                       int32 rightIndex = charCount - 1;
+                       bool left = true;
 
-bool
-truncate_middle(const char* source, char* dest, uint32 numChars,
-       const float* escapementArray, float width, float ellipsisWidth, float 
size)
-{
-       float mid = (width - ellipsisWidth) / 2.0;
+                       for (int32 i = 0; i < charCount; i++) {
+                               float charWidth
+                                       = escapementArray[left ? leftIndex : 
rightIndex] * fontSize;
 
-       uint32 left = 0;
-       float leftWidth = 0.0;
-       while (left < numChars && (leftWidth + (escapementArray[left] * size))
-                       < mid) {
-               leftWidth += (escapementArray[left++] * size);
-       }
+                               if (leftWidth + rightWidth + charWidth > width) 
{
+                                       // we need to truncate
+                                       while (leftWidth + rightWidth + 
ellipsisWidth > width) {
+                                               // remove chars until there's 
enough space for the
+                                               // ellipsis
+                                               if (leftIndex == 0 && 
rightIndex == charCount - 1) {
+                                                       // we've reached both 
ends of the string and still
+                                                       // no space, so return 
an empty string
+                                                       string.Truncate(0);
+                                                       return;
+                                               }
 
-       if (left == numChars)
-               return false;
+                                               if (leftIndex > 0 && 
(rightIndex == charCount - 1
+                                                               || leftWidth > 
rightWidth)) {
+                                                       // remove char on the 
left
+                                                       leftWidth -= 
escapementArray[--leftIndex]
+                                                               * fontSize;
+                                               } else {
+                                                       // remove char on the 
right
+                                                       rightWidth -= 
escapementArray[++rightIndex]
+                                                               * fontSize;
+                                               }
+                                       }
 
-       float rightWidth = 0.0;
-       uint32 right = numChars;
-       while (right > left && (rightWidth + (escapementArray[right - 1] * 
size))
-                       < mid) {
-               rightWidth += (escapementArray[--right] * size);
-       }
+                                       string.RemoveChars(leftIndex, 
rightIndex + 1 - leftIndex);
+                                       string.InsertChars(B_UTF8_ELLIPSIS, 1, 
leftIndex);
+                                       return;
+                               }
 
-       if (left >= right)
-               return false;
+                               if (left) {
+                                       leftIndex++;
+                                       leftWidth += charWidth;
+                               } else {
+                                       rightIndex--;
+                                       rightWidth += charWidth;
+                               }
 
-       float stringWidth = leftWidth + rightWidth;
-       for (uint32 i = left; i < right; ++i)
-               stringWidth += (escapementArray[i] * size);
-
-       if (stringWidth <= width)
-               return false;
-
-       // if there is no space for the ellipsis
-       if (width < ellipsisWidth) {
-               strcpy(dest, "");
-               return true;
-       }
-
-       // The ellipsis now definitely fits, but let's
-       // see if we can add another character
-       float gap = width - (leftWidth + ellipsisWidth + rightWidth);
-       if (left > numChars - right) {
-               // try right letter first
-               if (optional_char_fits(escapementArray[right - 1], size, gap))
-                       right--;
-               else if (optional_char_fits(escapementArray[left], size, gap))
-                       left++;
-       } else {
-               // try left letter first
-               if (optional_char_fits(escapementArray[left], size, gap))
-                       left++;
-               else if (optional_char_fits(escapementArray[right - 1], size, 
gap))
-                       right--;
-       }
-
-       // copy characters
-
-       for (uint32 i = 0; i < left; i++) {
-               // copy one glyph
-               do {
-                       *dest++ = *source++;
-               } while (IsInsideGlyph(*source));
-       }
-
-       dest = write_ellipsis(dest);
-
-       for (uint32 i = left; i < numChars; i++) {
-               // copy one glyph
-               do {
-                       if (i >= right)
-                               *dest++ = *source++;
-                       else
-                               source++;
-               } while (IsInsideGlyph(*source));
-       }
-
-       // terminate
-       dest[0] = '\0';
-       return true;
-}
-
-
-// TODO: put into BPrivate namespace
-void
-truncate_string(const char* string, uint32 mode, float width,
-       char* result, const float* escapementArray, float fontSize,
-       float ellipsisWidth, int32 length, int32 numChars)
-{
-       // TODO: that's actually not correct: the string could be smaller than
-       // ellipsisWidth
-       if (string == NULL /*|| width < ellipsisWidth*/) {
-               // we don't have room for a single glyph
-               strcpy(result, "");
-               return;
-       }
-
-       // iterate over glyphs and copy source into result string
-       // one glyph at a time as long as we have room for the "…" yet
-       char* dest = result;
-       const char* source = string;
-       bool truncated = true;
-
-       switch (mode) {
-               case B_TRUNCATE_BEGINNING: {
-                       dest = copy_from_end(source, dest, numChars, length,
-                               escapementArray, width, ellipsisWidth, 
fontSize);
-                       // "dst" points to the position behind the last glyph 
that
-                       // was copied.
-                       int32 dist = dest - result;
-                       // we didn't terminate yet
-                       *dest = 0;
-                       if (dist < length) {
-                               // TODO: Is there a smarter way?
-                               char* temp = new char[dist + 4];
-                               char* t = temp;
-                               // append "…"
-                               t = write_ellipsis(t);
-                               // shuffle arround strings so that "…" is 
prepended
-                               strcpy(t, result);
-                               strcpy(result, temp);
-                               delete[] temp;
-/*                                             char t = result[3];
-                               memmove(&result[3], result, dist + 1);
-                               write_ellipsis(result);
-                               result[3] = t;*/
+                               left = rightWidth > leftWidth;
                        }
-                       break;
-               }
 
-               case B_TRUNCATE_END:
-                       truncated = truncate_end(source, dest, numChars, 
escapementArray,
-                               width, ellipsisWidth, fontSize);
                        break;
-
-               case B_TRUNCATE_SMART:
-                       // TODO: implement, though it was never implemented on 
R5
-                       // FALL THROUGH (at least do something)
-               case B_TRUNCATE_MIDDLE:
-               default:
-                       truncated = truncate_middle(source, dest, numChars,
-                               escapementArray, width, ellipsisWidth, 
fontSize);
-                       break;
+               }
        }
 
-       if (!truncated) {
-               // copy string to destination verbatim
-               strlcpy(dest, source, length + 1);
-       }
+       // we've run through without the need to truncate, leave the string as 
it is
 }

Modified: haiku/trunk/src/servers/app/ServerFont.cpp
===================================================================
--- haiku/trunk/src/servers/app/ServerFont.cpp  2010-02-01 17:04:49 UTC (rev 
35380)
+++ haiku/trunk/src/servers/app/ServerFont.cpp  2010-02-01 18:43:03 UTC (rev 
35381)
@@ -867,28 +867,20 @@
 
        // the width of the "…" glyph
        float ellipsisWidth = StringWidth(B_UTF8_ELLIPSIS, 
strlen(B_UTF8_ELLIPSIS));
-       const char* string = inOut->String();
-       int32 length = inOut->Length();
 
-       // temporary array to hold result
-       char* result = new char[length + 3];
-
        // count the individual glyphs
-       int32 numChars = UTF8CountChars(string, -1);
+       int32 numChars = inOut->CountChars();
 
        // get the escapement of each glyph in font units
        float* escapementArray = new float[numChars];
        static escapement_delta delta = (escapement_delta){ 0.0, 0.0 };
-       if (GetEscapements(string, length, numChars, delta, escapementArray)
-                       == B_OK) {
-               truncate_string(string, mode, width, result, escapementArray, 
fSize,
-                       ellipsisWidth, length, numChars);
-
-               inOut->SetTo(result);
+       if (GetEscapements(inOut->String(), inOut->Length(), numChars, delta,
+               escapementArray) == B_OK) {
+               truncate_string(*inOut, mode, width, escapementArray, fSize,
+                       ellipsisWidth, numChars);
        }
 
        delete[] escapementArray;
-       delete[] result;
 }
 
 


Other related posts:

  • » [haiku-commits] r35381 - in haiku/trunk: headers/private/interface src/kits/interface src/servers/app - mmlr