Patch [#34373] Use i18n monospace font in Text editor and Python console
authorIrie Shinsuke <irieshinsuke@yahoo.co.jp>
Tue, 12 Mar 2013 07:25:53 +0000 (07:25 +0000)
committerIrie Shinsuke <irieshinsuke@yahoo.co.jp>
Tue, 12 Mar 2013 07:25:53 +0000 (07:25 +0000)
This patch allows Blender to display i18n monospace font in the text
editor and the Python interactive console. Wide characters that occupy
multiple columns such as CJK characters can be displayed correctly.
Furthermore, wrapping, selection, suggestion, cursor drawing, and
syntax highlighting should work.

Also fixes a bug [#34543]: In Text Editor false color in comment on cyrillic

To estimate how many columns each character occupies, this patch uses
wcwidth.c written by Markus Kuhn and distributed under MIT-style license:

  http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c

wcwidth.c is stored in extern/wcwidth and used as a static library.

This patch adds new API to blenfont, blenlib and blenkernel:

BLF_get_unifont_mono()
BLF_free_unifont_mono()
BLF_draw_mono()
BLI_wcwidth()
BLI_wcswidth()
BLI_str_utf8_char_width()
BLI_str_utf8_char_width_safe()
txt_utf8_offset_to_column()
txt_utf8_column_to_offset()

27 files changed:
extern/CMakeLists.txt
extern/SConscript
extern/wcwidth/CMakeLists.txt [new file with mode: 0644]
extern/wcwidth/SConscript [new file with mode: 0644]
extern/wcwidth/wcwidth.c [new file with mode: 0644]
extern/wcwidth/wcwidth.h [new file with mode: 0644]
source/blender/blenfont/BLF_api.h
source/blender/blenfont/BLF_translation.h
source/blender/blenfont/intern/blf.c
source/blender/blenfont/intern/blf_font.c
source/blender/blenfont/intern/blf_internal.h
source/blender/blenfont/intern/blf_translation.c
source/blender/blenkernel/BKE_text.h
source/blender/blenkernel/intern/text.c
source/blender/blenlib/BLI_string_utf8.h
source/blender/blenlib/CMakeLists.txt
source/blender/blenlib/SConscript
source/blender/blenlib/intern/string_utf8.c
source/blender/editors/interface/interface_style.c
source/blender/editors/space_console/console_draw.c
source/blender/editors/space_info/textview.c
source/blender/editors/space_text/text_draw.c
source/blender/editors/space_text/text_ops.c
source/blender/windowmanager/intern/wm_init_exit.c
source/blenderplayer/CMakeLists.txt
source/creator/CMakeLists.txt
source/gameengine/GamePlayer/ghost/GPG_ghost.cpp

index 6ad6bdc..941f69e 100644 (file)
@@ -28,6 +28,7 @@ remove_strict_flags()
 
 add_subdirectory(colamd)
 add_subdirectory(rangetree)
+add_subdirectory(wcwidth)
 
 if(WITH_BULLET)
        if(NOT WITH_SYSTEM_BULLET)
index 6a0ffa3..e2bb1dc 100644 (file)
@@ -5,6 +5,7 @@ Import('env')
 SConscript(['glew/SConscript'])
 SConscript(['colamd/SConscript'])
 SConscript(['rangetree/SConscript'])
+SConscript(['wcwidth/SConscript'])
 
 if env['WITH_BF_GAMEENGINE']:
     SConscript(['recastnavigation/SConscript'])
diff --git a/extern/wcwidth/CMakeLists.txt b/extern/wcwidth/CMakeLists.txt
new file mode 100644 (file)
index 0000000..cf8d7d6
--- /dev/null
@@ -0,0 +1,38 @@
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# The Original Code is Copyright (C) 2013, Blender Foundation
+# All rights reserved.
+#
+# The Original Code is: all of this file.
+#
+# Contributor(s): none yet.
+#
+# ***** END GPL LICENSE BLOCK *****
+
+set(INC
+       .
+)
+
+set(INC_SYS
+
+)
+
+set(SRC
+       wcwidth.c
+)
+
+blender_add_lib(extern_wcwidth "${SRC}" "${INC}" "${INC_SYS}")
diff --git a/extern/wcwidth/SConscript b/extern/wcwidth/SConscript
new file mode 100644 (file)
index 0000000..14fdaf3
--- /dev/null
@@ -0,0 +1,9 @@
+#!/usr/bin/python
+
+Import('env')
+
+sources = env.Glob('*.c')
+
+incs = '.'
+
+env.BlenderLib ( 'extern_wcwidth', sources, Split(incs), [], libtype=['extern','player'], priority=[10, 185])
diff --git a/extern/wcwidth/wcwidth.c b/extern/wcwidth/wcwidth.c
new file mode 100644 (file)
index 0000000..61e822a
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * This is an implementation of wcwidth() and wcswidth() (defined in
+ * IEEE Std 1002.1-2001) for Unicode.
+ *
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcwidth.html
+ * http://www.opengroup.org/onlinepubs/007904975/functions/wcswidth.html
+ *
+ * In fixed-width output devices, Latin characters all occupy a single
+ * "cell" position of equal width, whereas ideographic CJK characters
+ * occupy two such cells. Interoperability between terminal-line
+ * applications and (teletype-style) character terminals using the
+ * UTF-8 encoding requires agreement on which character should advance
+ * the cursor by how many cell positions. No established formal
+ * standards exist at present on which Unicode character shall occupy
+ * how many cell positions on character terminals. These routines are
+ * a first attempt of defining such behavior based on simple rules
+ * applied to data provided by the Unicode Consortium.
+ *
+ * For some graphical characters, the Unicode standard explicitly
+ * defines a character-cell width via the definition of the East Asian
+ * FullWidth (F), Wide (W), Half-width (H), and Narrow (Na) classes.
+ * In all these cases, there is no ambiguity about which width a
+ * terminal shall use. For characters in the East Asian Ambiguous (A)
+ * class, the width choice depends purely on a preference of backward
+ * compatibility with either historic CJK or Western practice.
+ * Choosing single-width for these characters is easy to justify as
+ * the appropriate long-term solution, as the CJK practice of
+ * displaying these characters as double-width comes from historic
+ * implementation simplicity (8-bit encoded characters were displayed
+ * single-width and 16-bit ones double-width, even for Greek,
+ * Cyrillic, etc.) and not any typographic considerations.
+ *
+ * Much less clear is the choice of width for the Not East Asian
+ * (Neutral) class. Existing practice does not dictate a width for any
+ * of these characters. It would nevertheless make sense
+ * typographically to allocate two character cells to characters such
+ * as for instance EM SPACE or VOLUME INTEGRAL, which cannot be
+ * represented adequately with a single-width glyph. The following
+ * routines at present merely assign a single-cell width to all
+ * neutral characters, in the interest of simplicity. This is not
+ * entirely satisfactory and should be reconsidered before
+ * establishing a formal standard in this area. At the moment, the
+ * decision which Not East Asian (Neutral) characters should be
+ * represented by double-width glyphs cannot yet be answered by
+ * applying a simple rule from the Unicode database content. Setting
+ * up a proper standard for the behavior of UTF-8 character terminals
+ * will require a careful analysis not only of each Unicode character,
+ * but also of each presentation form, something the author of these
+ * routines has avoided to do so far.
+ *
+ * http://www.unicode.org/unicode/reports/tr11/
+ *
+ * Markus Kuhn -- 2007-05-26 (Unicode 5.0)
+ *
+ * Permission to use, copy, modify, and distribute this software
+ * for any purpose and without fee is hereby granted. The author
+ * disclaims all warranties with regard to this software.
+ *
+ * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c
+ */
+
+#include <wchar.h>
+
+struct interval {
+  int first;
+  int last;
+};
+
+/* auxiliary function for binary search in interval table */
+static int bisearch(wchar_t ucs, const struct interval *table, int max) {
+  int min = 0;
+  int mid;
+
+  if (ucs < table[0].first || ucs > table[max].last)
+    return 0;
+  while (max >= min) {
+    mid = (min + max) / 2;
+    if (ucs > table[mid].last)
+      min = mid + 1;
+    else if (ucs < table[mid].first)
+      max = mid - 1;
+    else
+      return 1;
+  }
+
+  return 0;
+}
+
+
+/* The following two functions define the column width of an ISO 10646
+ * character as follows:
+ *
+ *    - The null character (U+0000) has a column width of 0.
+ *
+ *    - Other C0/C1 control characters and DEL will lead to a return
+ *      value of -1.
+ *
+ *    - Non-spacing and enclosing combining characters (general
+ *      category code Mn or Me in the Unicode database) have a
+ *      column width of 0.
+ *
+ *    - SOFT HYPHEN (U+00AD) has a column width of 1.
+ *
+ *    - Other format characters (general category code Cf in the Unicode
+ *      database) and ZERO WIDTH SPACE (U+200B) have a column width of 0.
+ *
+ *    - Hangul Jamo medial vowels and final consonants (U+1160-U+11FF)
+ *      have a column width of 0.
+ *
+ *    - Spacing characters in the East Asian Wide (W) or East Asian
+ *      Full-width (F) category as defined in Unicode Technical
+ *      Report #11 have a column width of 2.
+ *
+ *    - All remaining characters (including all printable
+ *      ISO 8859-1 and WGL4 characters, Unicode control characters,
+ *      etc.) have a column width of 1.
+ *
+ * This implementation assumes that wchar_t characters are encoded
+ * in ISO 10646.
+ */
+
+int mk_wcwidth(wchar_t ucs)
+{
+  /* sorted list of non-overlapping intervals of non-spacing characters */
+  /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */
+  static const struct interval combining[] = {
+    { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
+    { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
+    { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
+    { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
+    { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
+    { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
+    { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
+    { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
+    { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
+    { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
+    { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
+    { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
+    { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
+    { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
+    { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
+    { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
+    { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
+    { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
+    { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
+    { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
+    { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
+    { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
+    { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
+    { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
+    { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
+    { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
+    { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
+    { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
+    { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
+    { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
+    { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
+    { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
+    { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
+    { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
+    { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
+    { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
+    { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
+    { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
+    { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
+    { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F },
+    { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B },
+    { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F },
+    { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
+    { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
+    { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
+    { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
+    { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
+    { 0xE0100, 0xE01EF }
+  };
+
+  /* test for 8-bit control characters */
+  if (ucs == 0)
+    return 0;
+  if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0))
+    return -1;
+
+  /* binary search in table of non-spacing characters */
+  if (bisearch(ucs, combining,
+              sizeof(combining) / sizeof(struct interval) - 1))
+    return 0;
+
+  /* if we arrive here, ucs is not a combining or C0/C1 control character */
+
+  return 1 + 
+    (ucs >= 0x1100 &&
+     (ucs <= 0x115f ||                    /* Hangul Jamo init. consonants */
+      ucs == 0x2329 || ucs == 0x232a ||
+      (ucs >= 0x2e80 && ucs <= 0xa4cf &&
+       ucs != 0x303f) ||                  /* CJK ... Yi */
+      (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */
+      (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */
+      (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */
+      (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */
+      (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */
+      (ucs >= 0xffe0 && ucs <= 0xffe6) ||
+      (ucs >= 0x20000 && ucs <= 0x2fffd) ||
+      (ucs >= 0x30000 && ucs <= 0x3fffd)));
+}
+
+
+int mk_wcswidth(const wchar_t *pwcs, size_t n)
+{
+  int w, width = 0;
+
+  for (;*pwcs && n-- > 0; pwcs++)
+    if ((w = mk_wcwidth(*pwcs)) < 0)
+      return -1;
+    else
+      width += w;
+
+  return width;
+}
+
+
+/*
+ * The following functions are the same as mk_wcwidth() and
+ * mk_wcswidth(), except that spacing characters in the East Asian
+ * Ambiguous (A) category as defined in Unicode Technical Report #11
+ * have a column width of 2. This variant might be useful for users of
+ * CJK legacy encodings who want to migrate to UCS without changing
+ * the traditional terminal character-width behaviour. It is not
+ * otherwise recommended for general use.
+ */
+int mk_wcwidth_cjk(wchar_t ucs)
+{
+  /* sorted list of non-overlapping intervals of East Asian Ambiguous
+   * characters, generated by "uniset +WIDTH-A -cat=Me -cat=Mn -cat=Cf c" */
+  static const struct interval ambiguous[] = {
+    { 0x00A1, 0x00A1 }, { 0x00A4, 0x00A4 }, { 0x00A7, 0x00A8 },
+    { 0x00AA, 0x00AA }, { 0x00AE, 0x00AE }, { 0x00B0, 0x00B4 },
+    { 0x00B6, 0x00BA }, { 0x00BC, 0x00BF }, { 0x00C6, 0x00C6 },
+    { 0x00D0, 0x00D0 }, { 0x00D7, 0x00D8 }, { 0x00DE, 0x00E1 },
+    { 0x00E6, 0x00E6 }, { 0x00E8, 0x00EA }, { 0x00EC, 0x00ED },
+    { 0x00F0, 0x00F0 }, { 0x00F2, 0x00F3 }, { 0x00F7, 0x00FA },
+    { 0x00FC, 0x00FC }, { 0x00FE, 0x00FE }, { 0x0101, 0x0101 },
+    { 0x0111, 0x0111 }, { 0x0113, 0x0113 }, { 0x011B, 0x011B },
+    { 0x0126, 0x0127 }, { 0x012B, 0x012B }, { 0x0131, 0x0133 },
+    { 0x0138, 0x0138 }, { 0x013F, 0x0142 }, { 0x0144, 0x0144 },
+    { 0x0148, 0x014B }, { 0x014D, 0x014D }, { 0x0152, 0x0153 },
+    { 0x0166, 0x0167 }, { 0x016B, 0x016B }, { 0x01CE, 0x01CE },
+    { 0x01D0, 0x01D0 }, { 0x01D2, 0x01D2 }, { 0x01D4, 0x01D4 },
+    { 0x01D6, 0x01D6 }, { 0x01D8, 0x01D8 }, { 0x01DA, 0x01DA },
+    { 0x01DC, 0x01DC }, { 0x0251, 0x0251 }, { 0x0261, 0x0261 },
+    { 0x02C4, 0x02C4 }, { 0x02C7, 0x02C7 }, { 0x02C9, 0x02CB },
+    { 0x02CD, 0x02CD }, { 0x02D0, 0x02D0 }, { 0x02D8, 0x02DB },
+    { 0x02DD, 0x02DD }, { 0x02DF, 0x02DF }, { 0x0391, 0x03A1 },
+    { 0x03A3, 0x03A9 }, { 0x03B1, 0x03C1 }, { 0x03C3, 0x03C9 },
+    { 0x0401, 0x0401 }, { 0x0410, 0x044F }, { 0x0451, 0x0451 },
+    { 0x2010, 0x2010 }, { 0x2013, 0x2016 }, { 0x2018, 0x2019 },
+    { 0x201C, 0x201D }, { 0x2020, 0x2022 }, { 0x2024, 0x2027 },
+    { 0x2030, 0x2030 }, { 0x2032, 0x2033 }, { 0x2035, 0x2035 },
+    { 0x203B, 0x203B }, { 0x203E, 0x203E }, { 0x2074, 0x2074 },
+    { 0x207F, 0x207F }, { 0x2081, 0x2084 }, { 0x20AC, 0x20AC },
+    { 0x2103, 0x2103 }, { 0x2105, 0x2105 }, { 0x2109, 0x2109 },
+    { 0x2113, 0x2113 }, { 0x2116, 0x2116 }, { 0x2121, 0x2122 },
+    { 0x2126, 0x2126 }, { 0x212B, 0x212B }, { 0x2153, 0x2154 },
+    { 0x215B, 0x215E }, { 0x2160, 0x216B }, { 0x2170, 0x2179 },
+    { 0x2190, 0x2199 }, { 0x21B8, 0x21B9 }, { 0x21D2, 0x21D2 },
+    { 0x21D4, 0x21D4 }, { 0x21E7, 0x21E7 }, { 0x2200, 0x2200 },
+    { 0x2202, 0x2203 }, { 0x2207, 0x2208 }, { 0x220B, 0x220B },
+    { 0x220F, 0x220F }, { 0x2211, 0x2211 }, { 0x2215, 0x2215 },
+    { 0x221A, 0x221A }, { 0x221D, 0x2220 }, { 0x2223, 0x2223 },
+    { 0x2225, 0x2225 }, { 0x2227, 0x222C }, { 0x222E, 0x222E },
+    { 0x2234, 0x2237 }, { 0x223C, 0x223D }, { 0x2248, 0x2248 },
+    { 0x224C, 0x224C }, { 0x2252, 0x2252 }, { 0x2260, 0x2261 },
+    { 0x2264, 0x2267 }, { 0x226A, 0x226B }, { 0x226E, 0x226F },
+    { 0x2282, 0x2283 }, { 0x2286, 0x2287 }, { 0x2295, 0x2295 },
+    { 0x2299, 0x2299 }, { 0x22A5, 0x22A5 }, { 0x22BF, 0x22BF },
+    { 0x2312, 0x2312 }, { 0x2460, 0x24E9 }, { 0x24EB, 0x254B },
+    { 0x2550, 0x2573 }, { 0x2580, 0x258F }, { 0x2592, 0x2595 },
+    { 0x25A0, 0x25A1 }, { 0x25A3, 0x25A9 }, { 0x25B2, 0x25B3 },
+    { 0x25B6, 0x25B7 }, { 0x25BC, 0x25BD }, { 0x25C0, 0x25C1 },
+    { 0x25C6, 0x25C8 }, { 0x25CB, 0x25CB }, { 0x25CE, 0x25D1 },
+    { 0x25E2, 0x25E5 }, { 0x25EF, 0x25EF }, { 0x2605, 0x2606 },
+    { 0x2609, 0x2609 }, { 0x260E, 0x260F }, { 0x2614, 0x2615 },
+    { 0x261C, 0x261C }, { 0x261E, 0x261E }, { 0x2640, 0x2640 },
+    { 0x2642, 0x2642 }, { 0x2660, 0x2661 }, { 0x2663, 0x2665 },
+    { 0x2667, 0x266A }, { 0x266C, 0x266D }, { 0x266F, 0x266F },
+    { 0x273D, 0x273D }, { 0x2776, 0x277F }, { 0xE000, 0xF8FF },
+    { 0xFFFD, 0xFFFD }, { 0xF0000, 0xFFFFD }, { 0x100000, 0x10FFFD }
+  };
+
+  /* binary search in table of non-spacing characters */
+  if (bisearch(ucs, ambiguous,
+              sizeof(ambiguous) / sizeof(struct interval) - 1))
+    return 2;
+
+  return mk_wcwidth(ucs);
+}
+
+
+int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n)
+{
+  int w, width = 0;
+
+  for (;*pwcs && n-- > 0; pwcs++)
+    if ((w = mk_wcwidth_cjk(*pwcs)) < 0)
+      return -1;
+    else
+      width += w;
+
+  return width;
+}
diff --git a/extern/wcwidth/wcwidth.h b/extern/wcwidth/wcwidth.h
new file mode 100644 (file)
index 0000000..e327921
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2013 Blender Foundation.
+ * All rights reserved.
+ *
+ * Contributor(s): none yet.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#ifndef __WCWIDTH_H__
+#define __WCWIDTH_H__
+
+#include <wchar.h>
+
+int mk_wcwidth(wchar_t ucs);
+int mk_wcswidth(const wchar_t *pwcs, size_t n);
+int mk_wcwidth_cjk(wchar_t ucs);
+int mk_wcswidth_cjk(const wchar_t *pwcs, size_t n);
+
+#endif
index 0ca9797..fd8bd19 100644 (file)
@@ -77,6 +77,7 @@ void BLF_draw_default_ascii(float x, float y, float z, const char *str, size_t l
 /* Draw the string using the current font. */
 void BLF_draw(int fontid, const char *str, size_t len);
 void BLF_draw_ascii(int fontid, const char *str, size_t len);
+int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth);
 
 /* This function return the bounding box of the string
  * and are not multiplied by the aspect.
index cbfc7c2..2f40cc3 100644 (file)
@@ -68,6 +68,8 @@ struct EnumPropertyItem *BLF_RNA_lang_enum_properties(void);
 
 unsigned char *BLF_get_unifont(int *unifont_size);
 void BLF_free_unifont(void);
+unsigned char *BLF_get_unifont_mono(int *unifont_size);
+void BLF_free_unifont_mono(void);
 
 const char *BLF_pgettext(const char *msgctxt, const char *msgid);
 
index 061e8e2..e7e277b 100644 (file)
@@ -576,6 +576,21 @@ void BLF_draw_ascii(int fontid, const char *str, size_t len)
        }
 }
 
+int BLF_draw_mono(int fontid, const char *str, size_t len, int cwidth)
+{
+       FontBLF *font = blf_get(fontid);
+       GLint mode, param;
+       int columns = 0;
+
+       if (font && font->glyph_cache) {
+               blf_draw__start(font, &mode, &param);
+               columns = blf_font_draw_mono(font, str, len, cwidth);
+               blf_draw__end(mode, param);
+       }
+
+       return columns;
+}
+
 void BLF_boundbox(int fontid, const char *str, rctf *box)
 {
        FontBLF *font = blf_get(fontid);
index 1900efa..f2c3647 100644 (file)
@@ -219,6 +219,40 @@ void blf_font_draw_ascii(FontBLF *font, const char *str, size_t len)
        }
 }
 
+/* use fixed column width, but an utf8 character may occupy multiple columns */
+int blf_font_draw_mono(FontBLF *font, const char *str, size_t len, int cwidth)
+{
+       unsigned int c;
+       GlyphBLF *g;
+       int col, columns = 0;
+       int pen_x = 0, pen_y = 0;
+       size_t i = 0;
+       GlyphBLF **glyph_ascii_table = font->glyph_cache->glyph_ascii_table;
+
+       blf_font_ensure_ascii_table(font);
+
+       while ((i < len) && str[i]) {
+               BLF_UTF8_NEXT_FAST(font, g, str, i, c, glyph_ascii_table);
+
+               if (c == BLI_UTF8_ERR)
+                       break;
+               if (g == NULL)
+                       continue;
+
+               /* do not return this loop if clipped, we want every character tested */
+               blf_glyph_render(font, g, (float)pen_x, (float)pen_y);
+
+               col = BLI_wcwidth((wchar_t)c);
+               if (col < 0)
+                       col = 1;
+
+               columns += col;
+               pen_x += cwidth * col;
+       }
+
+       return columns;
+}
+
 /* Sanity checks are done by BLF_draw_buffer() */
 void blf_font_buffer(FontBLF *font, const char *str)
 {
index 65a5478..b1301be 100644 (file)
@@ -53,6 +53,7 @@ void blf_font_attach_from_mem(struct FontBLF *font, const unsigned char *mem, in
 void blf_font_size(struct FontBLF *font, int size, int dpi);
 void blf_font_draw(struct FontBLF *font, const char *str, size_t len);
 void blf_font_draw_ascii(struct FontBLF *font, const char *str, size_t len);
+int blf_font_draw_mono(struct FontBLF *font, const char *str, size_t len, int cwidth);
 void blf_font_buffer(struct FontBLF *font, const char *str);
 void blf_font_boundbox(struct FontBLF *font, const char *str, struct rctf *box);
 void blf_font_width_and_height(struct FontBLF *font, const char *str, float *width, float *height);
index f7ad5c0..3284225 100644 (file)
@@ -50,6 +50,9 @@
 static const char unifont_filename[] = "droidsans.ttf.gz";
 static unsigned char *unifont_ttf = NULL;
 static int unifont_size = 0;
+static const char unifont_mono_filename[] = "bmonofont-i18n.ttf.gz";
+static unsigned char *unifont_mono_ttf = NULL;
+static int unifont_mono_size = 0;
 #endif  /* WITH_INTERNATIONAL */
 
 unsigned char *BLF_get_unifont(int *unifont_size_r)
@@ -87,6 +90,41 @@ void BLF_free_unifont(void)
 #endif
 }
 
+unsigned char *BLF_get_unifont_mono(int *unifont_size_r)
+{
+#ifdef WITH_INTERNATIONAL
+       if(unifont_mono_ttf == NULL) {
+               char *fontpath = BLI_get_folder(BLENDER_DATAFILES, "fonts");
+               if (fontpath) {
+                       char unifont_path[1024];
+
+                       BLI_snprintf(unifont_path, sizeof(unifont_path), "%s/%s", fontpath, unifont_mono_filename);
+
+                       unifont_mono_ttf = (unsigned char*)BLI_file_ungzip_to_mem(unifont_path, &unifont_mono_size);
+               }
+               else {
+                       printf("%s: 'fonts' data path not found for international monospace font, continuing\n", __func__);
+               }
+       }
+
+       *unifont_size_r = unifont_mono_size;
+
+       return unifont_mono_ttf;
+#else
+       (void)unifont_size_r;
+       return NULL;
+#endif
+}
+
+void BLF_free_unifont_mono(void)
+{
+#ifdef WITH_INTERNATIONAL
+       if(unifont_mono_ttf)
+               MEM_freeN(unifont_mono_ttf);
+#else
+#endif
+}
+
 const char *BLF_pgettext(const char *msgctxt, const char *msgid)
 {
 #ifdef WITH_INTERNATIONAL
index 3d7b5d1..466adc9 100644 (file)
@@ -62,6 +62,8 @@ int           txt_has_sel                     (struct Text *text);
 int            txt_get_span            (struct TextLine *from, struct TextLine *to);
 int            txt_utf8_offset_to_index(const char *str, int offset);
 int            txt_utf8_index_to_offset(const char *str, int index);
+int            txt_utf8_offset_to_column(const char *str, int offset);
+int            txt_utf8_column_to_offset(const char *str, int column);
 void   txt_move_up                     (struct Text *text, short sel);
 void   txt_move_down           (struct Text *text, short sel);
 void   txt_move_left           (struct Text *text, short sel);
index a155ca4..e229c28 100644 (file)
@@ -791,6 +791,29 @@ int txt_utf8_index_to_offset(const char *str, int index)
        return offset;
 }
 
+int txt_utf8_offset_to_column(const char *str, int offset)
+{
+       int column = 0, pos = 0;
+       while (pos < offset) {
+               column += BLI_str_utf8_char_width_safe(str + pos);
+               pos += BLI_str_utf8_size_safe(str + pos);
+       }
+       return column;
+}
+
+int txt_utf8_column_to_offset(const char *str, int column)
+{
+       int offset = 0, pos = 0, col;
+       while (pos < column) {
+               col = BLI_str_utf8_char_width_safe(str + offset);
+               if (pos + col > column)
+                       break;
+               offset += BLI_str_utf8_size_safe(str + offset);
+               pos += col;
+       }
+       return offset;
+}
+
 /* returns the real number of characters in string */
 /* not the same as BLI_strlen_utf8, which returns length for wide characters */
 static int txt_utf8_len(const char *src)
@@ -804,6 +827,17 @@ static int txt_utf8_len(const char *src)
        return len;
 }
 
+static int txt_utf8_width(const char *src)
+{
+       int col = 0;
+
+       for (; *src; src += BLI_str_utf8_size(src)) {
+               col += BLI_str_utf8_char_width(src);
+       }
+
+       return col;
+}
+
 void txt_move_up(Text *text, short sel)
 {
        TextLine **linep;
@@ -815,10 +849,10 @@ void txt_move_up(Text *text, short sel)
        if (!*linep) return;
 
        if ((*linep)->prev) {
-               int index = txt_utf8_offset_to_index((*linep)->line, *charp);
+               int column = txt_utf8_offset_to_column((*linep)->line, *charp);
                *linep = (*linep)->prev;
-               if (index > txt_utf8_len((*linep)->line)) *charp = (*linep)->len;
-               else *charp = txt_utf8_index_to_offset((*linep)->line, index);
+               if (column > txt_utf8_width((*linep)->line)) *charp = (*linep)->len;
+               else *charp = txt_utf8_column_to_offset((*linep)->line, column);
                
        }
        else {
@@ -839,10 +873,10 @@ void txt_move_down(Text *text, short sel)
        if (!*linep) return;
 
        if ((*linep)->next) {
-               int index = txt_utf8_offset_to_index((*linep)->line, *charp);
+               int column = txt_utf8_offset_to_column((*linep)->line, *charp);
                *linep = (*linep)->next;
-               if (index > txt_utf8_len((*linep)->line)) *charp = (*linep)->len;
-               else *charp = txt_utf8_index_to_offset((*linep)->line, index);
+               if (column > txt_utf8_width((*linep)->line)) *charp = (*linep)->len;
+               else *charp = txt_utf8_column_to_offset((*linep)->line, column);
        }
        else {
                txt_move_eol(text, sel);
index 30d5c28..d20cbd2 100644 (file)
@@ -56,7 +56,14 @@ size_t       BLI_strnlen_utf8(const char *start, const size_t maxlen);
 size_t       BLI_strncpy_wchar_as_utf8(char *__restrict dst, const wchar_t *__restrict src, const size_t maxcpy);
 size_t       BLI_strncpy_wchar_from_utf8(wchar_t *__restrict dst, const char *__restrict src, const size_t maxcpy);
 
-#define      BLI_UTF8_MAX 6
+/* count columns that character/string occupies, based on wcwidth.c */
+int          BLI_wcwidth(wchar_t ucs);
+int          BLI_wcswidth(const wchar_t *pwcs, size_t n);
+int          BLI_str_utf8_char_width(const char *p); /* warning, can return -1 on bad chars */
+int          BLI_str_utf8_char_width_safe(const char *p);
+
+#define      BLI_UTF8_MAX 6        /* mem */
+#define      BLI_UTF8_WIDTH_MAX 2  /* columns */
 #define      BLI_UTF8_ERR ((unsigned int)-1)
 
 #ifdef __cplusplus
index e585110..bd6e84f 100644 (file)
@@ -29,6 +29,7 @@ set(INC
        ../makesdna
        ../../../intern/ghost
        ../../../intern/guardedalloc
+       ../../../extern/wcwidth
 )
 
 set(INC_SYS
index 2be06f7..41a9fe9 100644 (file)
@@ -31,7 +31,7 @@ sources = env.Glob('intern/*.c')
 
 cflags=''
 # don't add ../blenkernel back!
-incs = '. ../makesdna #/intern/guardedalloc #/intern/ghost'
+incs = '. ../makesdna #/intern/guardedalloc #/intern/ghost #/extern/wcwidth'
 
 incs += ' ' + env['BF_FREETYPE_INC']
 incs += ' ' + env['BF_ZLIB_INC']
index 26235de..fe8f3c2 100644 (file)
@@ -33,6 +33,7 @@
 #include <string.h>
 #include <wchar.h>
 #include <wctype.h>
+#include <wcwidth.h>
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -317,6 +318,42 @@ size_t BLI_strncpy_wchar_from_utf8(wchar_t *__restrict dst_w, const char *__rest
 /* end wchar_t / utf8 functions  */
 /* --------------------------------------------------------------------------*/
 
+/* count columns that character/string occupies, based on wcwidth.c */
+
+int BLI_wcwidth(wchar_t ucs)
+{
+       return mk_wcwidth(ucs);
+}
+
+int BLI_wcswidth(const wchar_t *pwcs, size_t n)
+{
+       return mk_wcswidth(pwcs, n);
+}
+
+int BLI_str_utf8_char_width(const char *p)
+{
+       unsigned int unicode = BLI_str_utf8_as_unicode(p);
+       if (unicode == BLI_UTF8_ERR)
+               return -1;
+
+       return BLI_wcwidth((wchar_t)unicode);
+}
+
+int BLI_str_utf8_char_width_safe(const char *p)
+{
+       int columns;
+
+       unsigned int unicode = BLI_str_utf8_as_unicode(p);
+       if (unicode == BLI_UTF8_ERR)
+               return 1;
+
+       columns = BLI_wcwidth((wchar_t)unicode);
+
+       return (columns < 0) ? 1 : columns;
+}
+
+/* --------------------------------------------------------------------------*/
+
 /* copied from glib's gutf8.c, added 'Err' arg */
 
 /* note, glib uses unsigned int for unicode, best we do the same,
index 3cd7499..03805dd 100644 (file)
@@ -330,6 +330,8 @@ void uiStyleInit(void)
 {
        uiFont *font = U.uifonts.first;
        uiStyle *style = U.uistyles.first;
+       int monofont_size = datatoc_bmonofont_ttf_size;
+       unsigned char *monofont_ttf = (unsigned char*)datatoc_bmonofont_ttf;
        
        /* recover from uninitialized dpi */
        if (U.dpi == 0)
@@ -400,15 +402,33 @@ void uiStyleInit(void)
                ui_style_new(&U.uistyles, "Default Style", UIFONT_DEFAULT);
        }
        
+#ifdef WITH_INTERNATIONAL
+       /* use unicode font for text editor and interactive console */
+       if (U.transopts & USER_DOTRANSLATE) {
+               monofont_ttf = BLF_get_unifont_mono(&monofont_size);
+
+               if (!monofont_ttf) {
+                       /* fall back if not found */
+                       monofont_size = datatoc_bmonofont_ttf_size;
+                       monofont_ttf = (unsigned char*)datatoc_bmonofont_ttf;
+               }
+       }
+
+       /* reload */
+       BLF_unload("monospace");
+       blf_mono_font = -1;
+       blf_mono_font_render = -1;
+#endif
+
        /* XXX, this should be moved into a style, but for now best only load the monospaced font once. */
        if (blf_mono_font == -1)
-               blf_mono_font = BLF_load_mem_unique("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
+               blf_mono_font = BLF_load_mem_unique("monospace", monofont_ttf, monofont_size);
 
        BLF_size(blf_mono_font, 12 * U.pixelsize, 72);
        
        /* second for rendering else we get threading problems */
        if (blf_mono_font_render == -1)
-               blf_mono_font_render = BLF_load_mem_unique("monospace", (unsigned char *)datatoc_bmonofont_ttf, datatoc_bmonofont_ttf_size);
+               blf_mono_font_render = BLF_load_mem_unique("monospace", monofont_ttf, monofont_size);
 
        BLF_size(blf_mono_font_render, 12 * U.pixelsize, 72 );
 }
index 7374723..cb191d0 100644 (file)
@@ -150,6 +150,26 @@ static int console_textview_line_get(struct TextViewContext *tvc, const char **l
        return 1;
 }
 
+static void console_cursor_wrap_offset(const char *str, int width, int *row, int *column, const char *end)
+{
+       int col;
+
+       for (; *str; str += BLI_str_utf8_size_safe(str)) {
+               col = BLI_str_utf8_char_width_safe(str);
+
+               if (*column + col > width) {
+                       (*row)++;
+                       *column = 0;
+               }
+
+               if (end && str >= end)
+                       break;
+
+               *column += col;
+       }
+       return;
+}
+
 static int console_textview_line_color(struct TextViewContext *tvc, unsigned char fg[3], unsigned char UNUSED(bg[3]))
 {
        ConsoleLine *cl_iter = (ConsoleLine *)tvc->iter;
@@ -158,24 +178,18 @@ static int console_textview_line_color(struct TextViewContext *tvc, unsigned cha
        if (tvc->iter_index == 0) {
                const SpaceConsole *sc = (SpaceConsole *)tvc->arg1;
                const ConsoleLine *cl = (ConsoleLine *)sc->history.last;
-               const int prompt_len = BLI_strlen_utf8(sc->prompt);
-               const int cursor_loc = BLI_strnlen_utf8(cl->line, cl->cursor) + prompt_len;
-               const int line_len = BLI_strlen_utf8(cl->line) + prompt_len;
+               int offl = 0, offc = 0;
                int xy[2] = {CONSOLE_DRAW_MARGIN, CONSOLE_DRAW_MARGIN};
                int pen[2];
                xy[1] += tvc->lheight / 6;
 
-               /* account for wrapping */
-               if (line_len < tvc->console_width) {
-                       /* simple case, no wrapping */
-                       pen[0] = tvc->cwidth * cursor_loc;
-                       pen[1] = -2;
-               }
-               else {
-                       /* wrap */
-                       pen[0] = tvc->cwidth * (cursor_loc % tvc->console_width);
-                       pen[1] = -2 + (((line_len / tvc->console_width) - (cursor_loc / tvc->console_width)) * tvc->lheight);
-               }
+               console_cursor_wrap_offset(sc->prompt, tvc->console_width, &offl, &offc, NULL);
+               console_cursor_wrap_offset(cl->line, tvc->console_width, &offl, &offc, cl->line + cl->cursor);
+               pen[0] = tvc->cwidth * offc;
+               pen[1] = -2 - tvc->lheight * offl;
+
+               console_cursor_wrap_offset(cl->line + cl->cursor, tvc->console_width, &offl, &offc, NULL);
+               pen[1] += tvc->lheight * offl;
 
                /* cursor */
                UI_GetThemeColor3ubv(TH_CONSOLE_CURSOR, fg);
index 66f4904..58418f2 100644 (file)
 #include <limits.h>
 #include <assert.h>
 
+#include "MEM_guardedalloc.h"
+
 #include "BLF_api.h"
 
 #include "BLI_math.h"
 #include "BLI_utildefines.h"
+#include "BLI_string_utf8.h"
 
 #include "BIF_gl.h"
 #include "BIF_glutil.h"
 
+#include "BKE_text.h"
+
 #include "ED_datafiles.h"
 
 #include "textview.h"
@@ -68,12 +73,12 @@ BLI_INLINE void console_step_sel(ConsoleDrawContext *cdc, const int step)
        cdc->sel[1] += step;
 }
 
-static void console_draw_sel(const int sel[2], const int xy[2], const int str_len_draw, int cwidth, int lheight,
-                             const unsigned char bg_sel[4])
+static void console_draw_sel(const char *str, const int sel[2], const int xy[2], const int str_len_draw,
+                            int cwidth, int lheight, const unsigned char bg_sel[4])
 {
        if (sel[0] <= str_len_draw && sel[1] >= 0) {
-               const int sta = max_ii(sel[0], 0);
-               const int end = min_ii(sel[1], str_len_draw);
+               const int sta = txt_utf8_offset_to_column(str, max_ii(sel[0], 0));
+               const int end = txt_utf8_offset_to_column(str, min_ii(sel[1], str_len_draw));
 
                glEnable(GL_BLEND);
                glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@@ -85,38 +90,72 @@ static void console_draw_sel(const int sel[2], const int xy[2], const int str_le
        }
 }
 
+/* warning: allocated memory for 'offsets' must be freed by caller */
+static int console_wrap_offsets(const char *str, int len, int width, int *lines, int **offsets)
+{
+       int i, end;  /* column */
+       int j;       /* mem */
+
+       *lines = 1;
+
+       *offsets = MEM_callocN(sizeof(**offsets) * (len * BLI_UTF8_WIDTH_MAX / MAX2(1, width - (BLI_UTF8_WIDTH_MAX - 1)) + 1),
+                              "console_wrap_offsets");
+       (*offsets)[0] = 0;
+
+       for (i = 0, end = width, j = 0; j < len && str[j]; j += BLI_str_utf8_size_safe(str + j)) {
+               int columns = BLI_str_utf8_char_width_safe(str + j);
+
+               if (i + columns > end) {
+                       (*offsets)[*lines] = j;
+                       (*lines)++;
+
+                       end = i + width;
+               }
+               i += columns;
+       }
+       return j; /* return actual length */
+}
+
 /* return 0 if the last line is off the screen
  * should be able to use this for any string type */
 
-static int console_draw_string(ConsoleDrawContext *cdc, const char *str, const int str_len,
+static int console_draw_string(ConsoleDrawContext *cdc, const char *str, int str_len,
                                const unsigned char fg[3], const unsigned char bg[3], const unsigned char bg_sel[4])
 {
        int rct_ofs = cdc->lheight / 4;
-       int tot_lines = (str_len / cdc->console_width) + 1; /* total number of lines for wrapping */
-       int y_next = (str_len > cdc->console_width) ? cdc->xy[1] + cdc->lheight * tot_lines : cdc->xy[1] + cdc->lheight;
+       int tot_lines;            /* total number of lines for wrapping */
+       int *offsets;             /* offsets of line beginnings for wrapping */
+       int y_next;
        const int mono = blf_mono_font;
 
+       str_len = console_wrap_offsets(str, str_len, cdc->console_width, &tot_lines, &offsets);
+       y_next = cdc->xy[1] + cdc->lheight * tot_lines;
+
        /* just advance the height */
        if (cdc->draw == 0) {
-               if (cdc->pos_pick && (cdc->mval[1] != INT_MAX)) {
-                       if (cdc->xy[1] <= cdc->mval[1]) {
-                               if ((y_next >= cdc->mval[1])) {
-                                       int ofs = (int)floor(((float)cdc->mval[0] / (float)cdc->cwidth));
-
-                                       /* wrap */
-                                       if (str_len > cdc->console_width)
-                                               ofs += cdc->console_width * ((int)((((float)(y_next - cdc->mval[1]) /
-                                                                                    (float)(y_next - cdc->xy[1])) * tot_lines)));
-       
-                                       CLAMP(ofs, 0, str_len);
-                                       *cdc->pos_pick += str_len - ofs;
+               if (cdc->pos_pick && cdc->mval[1] != INT_MAX && cdc->xy[1] <= cdc->mval[1]) {
+                       if (y_next >= cdc->mval[1]) {
+                               int ofs = 0;
+
+                               /* wrap */
+                               if (tot_lines > 1) {
+                                       int iofs = (int)((float)(y_next - cdc->mval[1]) / cdc->lheight);
+                                       ofs += offsets[MIN2(iofs, tot_lines - 1)];
                                }
-                               else
-                                       *cdc->pos_pick += str_len + 1;
+
+                               /* last part */
+                               ofs += txt_utf8_column_to_offset(str + ofs,
+                                                                (int)floor((float)cdc->mval[0] / cdc->cwidth));
+
+                               CLAMP(ofs, 0, str_len);
+                               *cdc->pos_pick += str_len - ofs;
                        }
+                       else
+                               *cdc->pos_pick += str_len + 1;
                }
 
                cdc->xy[1] = y_next;
+               MEM_freeN(offsets);
                return 1;
        }
        else if (y_next - cdc->lheight < cdc->ymin) {
@@ -128,12 +167,15 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, const i
                        console_step_sel(cdc, -(str_len + 1));
                }
 
+               MEM_freeN(offsets);
                return 1;
        }
 
        if (tot_lines > 1) { /* wrap? */
-               const int initial_offset = ((tot_lines - 1) * cdc->console_width);
-               const char *line_stride = str + initial_offset;  /* advance to the last line and draw it first */
+               const int initial_offset = offsets[tot_lines - 1];
+               size_t len = str_len - initial_offset;
+               const char *s = str + initial_offset;
+               int i;
                
                int sel_orig[2];
                copy_v2_v2_int(sel_orig, cdc->sel);
@@ -151,36 +193,38 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, const i
 
                /* last part needs no clipping */
                BLF_position(mono, cdc->xy[0], cdc->xy[1], 0);
-               BLF_draw(mono, line_stride, str_len - initial_offset);
+               BLF_draw_mono(mono, s, len, cdc->cwidth);
 
                if (cdc->sel[0] != cdc->sel[1]) {
                        console_step_sel(cdc, -initial_offset);
                        // glColor4ub(255, 0, 0, 96); // debug
-                       console_draw_sel(cdc->sel, cdc->xy, str_len % cdc->console_width, cdc->cwidth, cdc->lheight, bg_sel);
-                       console_step_sel(cdc, cdc->console_width);
+                       console_draw_sel(s, cdc->sel, cdc->xy, len, cdc->cwidth, cdc->lheight, bg_sel);
                        glColor3ubv(fg);
                }
 
                cdc->xy[1] += cdc->lheight;
 
-               line_stride -= cdc->console_width;
-               
-               for (; line_stride >= str; line_stride -= cdc->console_width) {
+               for (i = tot_lines - 1; i > 0; i--) {
+                       len = offsets[i] - offsets[i - 1];
+                       s = str + offsets[i - 1];
+
                        BLF_position(mono, cdc->xy[0], cdc->xy[1], 0);
-                       BLF_draw(mono, line_stride, cdc->console_width);
+                       BLF_draw_mono(mono, s, len, cdc->cwidth);
                        
                        if (cdc->sel[0] != cdc->sel[1]) {
+                               console_step_sel(cdc, len);
                                // glColor4ub(0, 255, 0, 96); // debug
-                               console_draw_sel(cdc->sel, cdc->xy, cdc->console_width, cdc->cwidth, cdc->lheight, bg_sel);
-                               console_step_sel(cdc, cdc->console_width);
+                               console_draw_sel(s, cdc->sel, cdc->xy, len, cdc->cwidth, cdc->lheight, bg_sel);
                                glColor3ubv(fg);
                        }
 
                        cdc->xy[1] += cdc->lheight;
                        
                        /* check if were out of view bounds */
-                       if (cdc->xy[1] > cdc->ymax)
+                       if (cdc->xy[1] > cdc->ymax) {
+                               MEM_freeN(offsets);
                                return 0;
+                       }
                }
 
                copy_v2_v2_int(cdc->sel, sel_orig);
@@ -196,7 +240,7 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, const i
                glColor3ubv(fg);
 
                BLF_position(mono, cdc->xy[0], cdc->xy[1], 0);
-               BLF_draw(mono, str, str_len);
+               BLF_draw_mono(mono, str, str_len, cdc->cwidth);
                
                if (cdc->sel[0] != cdc->sel[1]) {
                        int isel[2];
@@ -205,16 +249,19 @@ static int console_draw_string(ConsoleDrawContext *cdc, const char *str, const i
                        isel[1] = str_len - cdc->sel[0];
 
                        // glColor4ub(255, 255, 0, 96); // debug
-                       console_draw_sel(isel, cdc->xy, str_len, cdc->cwidth, cdc->lheight, bg_sel);
+                       console_draw_sel(str, isel, cdc->xy, str_len, cdc->cwidth, cdc->lheight, bg_sel);
                        console_step_sel(cdc, -(str_len + 1));
                }
 
                cdc->xy[1] += cdc->lheight;
 
-               if (cdc->xy[1] > cdc->ymax)
+               if (cdc->xy[1] > cdc->ymax) {
+                       MEM_freeN(offsets);
                        return 0;
+               }
        }
 
+       MEM_freeN(offsets);
        return 1;
 }
 
index a146fec..95fd7fc 100644 (file)
@@ -67,12 +67,14 @@ static void text_font_end(SpaceText *UNUSED(st))
 {
 }
 
-static int text_font_draw(SpaceText *UNUSED(st), int x, int y, const char *str)
+static int text_font_draw(SpaceText *st, int x, int y, const char *str)
 {
+       int columns;
+
        BLF_position(mono, x, y, 0);
-       BLF_draw(mono, str, BLF_DRAW_STR_DUMMY_MAX);
+       columns = BLF_draw_mono(mono, str, BLF_DRAW_STR_DUMMY_MAX, st->cwidth);
 
-       return BLF_width(mono, str);
+       return st->cwidth * columns;
 }
 
 static int text_font_draw_character(SpaceText *st, int x, int y, char c)
@@ -85,10 +87,13 @@ static int text_font_draw_character(SpaceText *st, int x, int y, char c)
 
 static int text_font_draw_character_utf8(SpaceText *st, int x, int y, const char *c)
 {
+       int columns;
+
        const size_t len = BLI_str_utf8_size_safe(c);
        BLF_position(mono, x, y, 0);
-       BLF_draw(mono, c, len);
-       return st->cwidth;
+       columns = BLF_draw_mono(mono, c, len, st->cwidth);
+
+       return st->cwidth * columns;
 }
 
 #if 0
@@ -216,7 +221,7 @@ void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *
        }
 
        max = wrap_width(st, ar);
-       cursin = txt_utf8_offset_to_index(linein->line, cursin);
+       cursin = txt_utf8_offset_to_column(linein->line, cursin);
 
        while (linep) {
                start = 0;
@@ -225,6 +230,7 @@ void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *
                *offc = 0;
                for (i = 0, j = 0; linep->line[j]; j += BLI_str_utf8_size_safe(linep->line + j)) {
                        int chars;
+                       int columns = BLI_str_utf8_char_width_safe(linep->line + j); /* = 1 for tab */
 
                        /* Mimic replacement of tabs */
                        ch = linep->line[j];
@@ -238,7 +244,9 @@ void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *
                        }
 
                        while (chars--) {
-                               if (i - start >= max) {
+                               if (i + columns - start > max) {
+                                       end = MIN2(end, i);
+
                                        if (chop && linep == linein && i >= cursin) {
                                                if (i == cursin) {
                                                        (*offl)++;
@@ -261,7 +269,7 @@ void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *
                                        if (linep == linein && i >= cursin)
                                                return;
                                }
-                               i++;
+                               i += columns;
                        }
                }
                if (linep == linein) break;
@@ -286,9 +294,10 @@ void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursi
        end = max;
        chop = 1;
        *offc = 0;
-       cursin = txt_utf8_offset_to_index(linein->line, cursin);
+       cursin = txt_utf8_offset_to_column(linein->line, cursin);
 
        for (i = 0, j = 0; linein->line[j]; j += BLI_str_utf8_size_safe(linein->line + j)) {
+               int columns = BLI_str_utf8_char_width_safe(linein->line + j); /* = 1 for tab */
 
                /* Mimic replacement of tabs */
                ch = linein->line[j];
@@ -301,7 +310,9 @@ void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursi
                        chars = 1;
 
                while (chars--) {
-                       if (i - start >= max) {
+                       if (i + columns - start > max) {
+                               end = MIN2(end, i);
+
                                if (chop && i >= cursin) {
                                        if (i == cursin) {
                                                (*offl)++;
@@ -324,7 +335,7 @@ void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursi
                                if (i >= cursin)
                                        return;
                        }
-                       i++;
+                       i += columns;
                }
        }
 }
@@ -337,24 +348,35 @@ int text_get_char_pos(SpaceText *st, const char *line, int cur)
                if (line[i] == '\t')
                        a += st->tabnumber - a % st->tabnumber;
                else
-                       a++;
+                       a += BLI_str_utf8_char_width_safe(line + i);
        }
        return a;
 }
 
-static const char *txt_utf8_get_nth(const char *str, int n)
+static const char *txt_utf8_forward_columns(const char *str, int columns, int *padding)
 {
-       int pos = 0;
-       while (str[pos] && n--) {
-               pos += BLI_str_utf8_size_safe(str + pos);
+       int col;
+       const char *p = str;
+       while (*p) {
+               col = BLI_str_utf8_char_width(p);
+               if (columns - col < 0)
+                       break;
+               columns -= col;
+               p += BLI_str_utf8_size_safe(p);
+               if (columns == 0)
+                       break;
        }
-       return str + pos;
+       if (padding)
+               *padding = *p ? columns : 0;
+       return p;
 }
 
 static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w, const char *format, int skip)
 {
        FlattenString fs;
-       int basex, i, a, start, end, max, lines; /* view */
+       int basex, lines;
+       int i, wrap, end, max, columns, padding; /* column */
+       int a, fstart, fpos;                     /* utf8 chars */
        int mi, ma, mstart, mend;                /* mem */
        char fmt_prev = 0xff;
        
@@ -365,41 +387,46 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w
        basex = x;
        lines = 1;
        
-       start = 0; mstart = 0;
-       end = max; mend = txt_utf8_get_nth(str, max) - str;
+       fpos = fstart = 0; mstart = 0;
+       mend = txt_utf8_forward_columns(str, max, &padding) - str;
+       end = wrap = max - padding;
        
-       for (i = 0, mi = 0; str[mi]; i++, mi += BLI_str_utf8_size_safe(str + mi)) {
-               if (i - start >= max) {
+       for (i = 0, mi = 0; str[mi]; i += columns, mi += BLI_str_utf8_size_safe(str + mi)) {
+               columns = BLI_str_utf8_char_width_safe(str + mi);
+               if (i + columns > end) {
                        /* skip hidden part of line */
                        if (skip) {
                                skip--;
-                               start = end; mstart = mend;
-                               end += max; mend = txt_utf8_get_nth(str + mend, max) - str;
+                               fstart = fpos; mstart = mend;
+                               mend = txt_utf8_forward_columns(str + mend, max, &padding) - str;
+                               end = (wrap += max - padding);
                                continue;
                        }
 
                        /* Draw the visible portion of text on the overshot line */
-                       for (a = start, ma = mstart; a < end; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
+                       for (a = fstart, ma = mstart; ma < mend; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
                                if (st->showsyntax && format) {
                                        if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]);
                                }
                                x += text_font_draw_character_utf8(st, x, y, str + ma);
+                               fpos++;
                        }
                        y -= st->lheight_dpi + TXT_LINE_SPACING;
                        x = basex;
                        lines++;
-                       start = end; mstart = mend;
-                       end += max; mend = txt_utf8_get_nth(str + mend, max) - str;
+                       fstart = fpos; mstart = mend;
+                       mend = txt_utf8_forward_columns(str + mend, max, &padding) - str;
+                       end = (wrap += max - padding);
 
                        if (y <= 0) break;
                }
                else if (str[mi] == ' ' || str[mi] == '-') {
-                       end = i + 1; mend = mi + 1;
+                       wrap = i + 1; mend = mi + 1;
                }
        }
 
        /* Draw the remaining text */
-       for (a = start, ma = mstart; str[ma] && y > 0; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
+       for (a = fstart, ma = mstart; str[ma] && y > 0; a++, ma += BLI_str_utf8_size_safe(str + ma)) {
                if (st->showsyntax && format) {
                        if (fmt_prev != format[a]) format_draw_color(fmt_prev = format[a]);
                }
@@ -412,53 +439,55 @@ static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w
        return lines;
 }
 
-static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, const char *format)
+static void text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int x, int y, const char *format)
 {
        FlattenString fs;
-       int *acc, r = 0;
-       const char *in;
+       int columns, size, n, w = 0, padding, amount = 0;
+       const char *in = NULL;
 
-       int w = flatten_string(st, &fs, str);
-       if (w < cshift) {
-               flatten_string_free(&fs);
-               return 0; /* String is shorter than shift */
-       }
-       
-       in = txt_utf8_get_nth(fs.buf, cshift);
-       acc = fs.accum + cshift;
-       w = w - cshift;
+       for (n = flatten_string(st, &fs, str), str = fs.buf; n > 0; n--) {
+               columns = BLI_str_utf8_char_width_safe(str);
+               size = BLI_str_utf8_size_safe(str);
 
-       if (draw) {
-               int amount = maxwidth ? MIN2(w, maxwidth) : w;
-               
-               if (st->showsyntax && format) {
-                       int a, str_shift = 0;
-                       char fmt_prev = 0xff;
-                       format = format + cshift;
-
-                       for (a = 0; a < amount; a++) {
-                               if (format[a] != fmt_prev) format_draw_color(fmt_prev = format[a]);
-                               x += text_font_draw_character_utf8(st, x, y, in + str_shift);
-                               str_shift += BLI_str_utf8_size_safe(in + str_shift);
+               if (!in) {
+                       if (w >= cshift) {
+                               padding = w - cshift;
+                               in = str;
                        }
+                       else if (format)
+                               format++;
                }
-               else {
-                       text_font_draw(st, x, y, in);
+               if (in) {
+                       if (maxwidth && w + columns > cshift + maxwidth)
+                               break;
+                       amount++;
+               }
+
+               w += columns;
+               str += size;
+       }
+       if (!in) {
+               flatten_string_free(&fs);
+               return; /* String is shorter than shift or ends with a padding */
+       }
+
+       x += st->cwidth * padding;
+
+       if (st->showsyntax && format) {
+               int a, str_shift = 0;
+               char fmt_prev = 0xff;
+
+               for (a = 0; a < amount; a++) {
+                       if (format[a] != fmt_prev) format_draw_color(fmt_prev = format[a]);
+                       x += text_font_draw_character_utf8(st, x, y, in + str_shift);
+                       str_shift += BLI_str_utf8_size_safe(in + str_shift);
                }
        }
        else {
-               while (w-- && *acc++ < maxwidth)
-                       r += st->cwidth;
+               text_font_draw(st, x, y, in);
        }
 
        flatten_string_free(&fs);
-
-       if (cshift && r == 0)
-               return 0;
-       else if (st->showlinenrs)
-               return r + TXT_OFFSET + TEXTXLOC;
-       else
-               return r + TXT_OFFSET;
 }
 
 /************************ cache utilities *****************************/
@@ -672,6 +701,8 @@ int text_get_visible_lines(SpaceText *st, ARegion *ar, const char *str)
        start = 0;
        end = max;
        for (i = 0, j = 0; str[j]; j += BLI_str_utf8_size_safe(str + j)) {
+               int columns = BLI_str_utf8_char_width_safe(str + j); /* = 1 for tab */
+
                /* Mimic replacement of tabs */
                ch = str[j];
                if (ch == '\t') {
@@ -683,16 +714,16 @@ int text_get_visible_lines(SpaceText *st, ARegion *ar, const char *str)
                }
 
                while (chars--) {
-                       if (i - start >= max) {
+                       if (i + columns - start > max) {
                                lines++;
-                               start = end;
+                               start = MIN2(end, i);
                                end += max;
                        }
                        else if (ch == ' ' || ch == '-') {
                                end = i + 1;
                        }
 
-                       i++;
+                       i += columns;
                }
        }
 
@@ -931,7 +962,7 @@ static void draw_documentation(SpaceText *st, ARegion *ar)
                        buf[i] = '\0';
                        if (lines >= 0) {
                                y -= st->lheight_dpi;
-                               text_draw(st, buf, 0, 0, 1, x + 4, y - 3, NULL);
+                               text_draw(st, buf, 0, 0, x + 4, y - 3, NULL);
                        }
                        i = 0; br = DOC_WIDTH; lines++;
                }
@@ -940,7 +971,7 @@ static void draw_documentation(SpaceText *st, ARegion *ar)
                        buf[br] = '\0';
                        if (lines >= 0) {
                                y -= st->lheight_dpi;
-                               text_draw(st, buf, 0, 0, 1, x + 4, y - 3, NULL);
+                               text_draw(st, buf, 0, 0, x + 4, y - 3, NULL);
                        }
                        p -= i - br - 1; /* Rewind pointer to last break */
                        i = 0; br = DOC_WIDTH; lines++;
@@ -959,9 +990,9 @@ static void draw_documentation(SpaceText *st, ARegion *ar)
 static void draw_suggestion_list(SpaceText *st, ARegion *ar)
 {
        SuggItem *item, *first, *last, *sel;
-       TextLine *tmp;
-       char str[SUGG_LIST_WIDTH + 1];
-       int w, boxw = 0, boxh, i, l, x, y, *top;
+       char str[SUGG_LIST_WIDTH * BLI_UTF8_MAX + 1];
+       int offl, offc, vcurl, vcurc;
+       int w, boxw = 0, boxh, i, x, y, *top;
        const int lheight = st->lheight_dpi + TXT_LINE_SPACING;
        const int margin_x = 2;
        
@@ -977,24 +1008,24 @@ static void draw_suggestion_list(SpaceText *st, ARegion *ar)
        sel = texttool_suggest_selected();
        top = texttool_suggest_top();
 
-       /* Count the visible lines to the cursor */
-       for (tmp = st->text->curl, l = -st->top; tmp; tmp = tmp->prev, l++) ;
-       if (l < 0) return;
-       
-       if (st->showlinenrs) {
-               x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET + TEXTXLOC - 4;
-       }
-       else {
-               x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET - 4;
-       }
+       wrap_offset(st, ar, st->text->curl, st->text->curc, &offl, &offc);
+       vcurl = txt_get_span(st->text->lines.first, st->text->curl) - st->top + offl;
+       vcurc = text_get_char_pos(st, st->text->curl->line, st->text->curc) - st->left + offc;
+
+       x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
+       x += vcurc * st->cwidth - 4;
+       y = ar->winy - (vcurl + 1) * lheight - 2;
+
        /* offset back so the start of the text lines up with the suggestions,
         * not essential but makes suggestions easier to follow */
        x -= st->cwidth * (st->text->curc - text_find_identifier_start(st->text->curl->line, st->text->curc));
-       y = ar->winy - lheight * l - 2;
 
        boxw = SUGG_LIST_WIDTH * st->cwidth + 20;
        boxh = SUGG_LIST_SIZE * lheight + 8;
        
+       if (x + boxw > ar->winx)
+               x = MAX2(0, ar->winx - boxw);
+
        /* not needed but stands out nicer */
        uiDrawBoxShadow(220, x, y - boxh, x + boxw, y);
 
@@ -1007,12 +1038,13 @@ static void draw_suggestion_list(SpaceText *st, ARegion *ar)
        for (i = 0, item = first; i < *top && item->next; i++, item = item->next) ;
 
        for (i = 0; i < SUGG_LIST_SIZE && item; i++, item = item->next) {
+               int len = txt_utf8_forward_columns(item->name, SUGG_LIST_WIDTH, NULL) - item->name;
 
                y -= lheight;
 
-               BLI_strncpy(str, item->name, SUGG_LIST_WIDTH);
+               BLI_strncpy(str, item->name, len + 1);
 
-               w = BLF_width(mono, str);
+               w = st->cwidth * text_get_char_pos(st, str, len);
                
                if (item == sel) {
                        UI_ThemeColor(TH_SHADE2);
@@ -1020,7 +1052,7 @@ static void draw_suggestion_list(SpaceText *st, ARegion *ar)
                }
 
                format_draw_color(item->type);
-               text_draw(st, str, 0, 0, 1, x + margin_x, y - 1, NULL);
+               text_draw(st, str, 0, 0, x + margin_x, y - 1, NULL);
 
                if (item == last) break;
        }
@@ -1382,7 +1414,7 @@ void draw_text_main(SpaceText *st, ARegion *ar)
                }
                else {
                        /* draw unwrapped text */
-                       text_draw(st, tmp->line, st->left, ar->winx / st->cwidth, 1, x, y, tmp->format);
+                       text_draw(st, tmp->line, st->left, ar->winx / st->cwidth, x, y, tmp->format);
                        y -= st->lheight_dpi + TXT_LINE_SPACING;
                }
                
@@ -1439,8 +1471,6 @@ void text_scroll_to_cursor(SpaceText *st, ScrArea *sa)
                        winx = ar->winx;
                        break;
                }
-       
-       winx -= TXT_SCROLL_WIDTH;
 
        text_update_character_width(st);
 
@@ -1458,10 +1488,11 @@ void text_scroll_to_cursor(SpaceText *st, ScrArea *sa)
                st->left = 0;
        }
        else {
-               x = text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL);
+               x = st->cwidth * (text_get_char_pos(st, text->sell->line, text->selc) - st->left);
+               winx -= TXT_OFFSET + (st->showlinenrs ? TEXTXLOC : 0) + TXT_SCROLL_WIDTH;
 
-               if (x == 0 || x > winx)
-                       st->left = text->curc - 0.5 * winx / st->cwidth;
+               if (x <= 0 || x > winx)
+                       st->left += (x - winx / 2) / st->cwidth;
        }
 
        if (st->top < 0) st->top = 0;
index e6f95c3..3a7070e 100644 (file)
@@ -72,7 +72,7 @@
 /************************ poll ***************************/
 
 
-BLI_INLINE int text_pixel_x_to_index(SpaceText *st, const int x)
+BLI_INLINE int text_pixel_x_to_column(SpaceText *st, const int x)
 {
        /* add half the char width so mouse cursor selection is inbetween letters */
        return (x + (st->cwidth / 2)) / st->cwidth;
@@ -1407,6 +1407,8 @@ static int text_get_cursor_rel(SpaceText *st, ARegion *ar, TextLine *linein, int
 
        for (i = 0, j = 0; loop; j += BLI_str_utf8_size_safe(linein->line + j)) {
                int chars;
+               int columns = BLI_str_utf8_char_width_safe(linein->line + j); /* = 1 for tab */
+
                /* Mimic replacement of tabs */
                ch = linein->line[j];
                if (ch == '\t') {
@@ -1418,16 +1420,18 @@ static int text_get_cursor_rel(SpaceText *st, ARegion *ar, TextLine *linein, int
                }
 
                while (chars--) {
-                       if (rell == 0 && i - start == relc) {
+                       if (rell == 0 && i - start <= relc && i + columns - start > relc) {
                                /* current position could be wrapped to next line */
                                /* this should be checked when end of current line would be reached */
                                selc = j;
                                found = 1;
                        }
-                       else if (i - end == relc) {
+                       else if (i - end <= relc && i + columns - end > relc) {
                                curs = j;
                        }
-                       if (i - start >= max) {
+                       if (i + columns - start > max) {
+                               end = MIN2(end, i);
+
                                if (found) {
                                        /* exact cursor position was found, check if it's */
                                        /* still on needed line (hasn't been wrapped) */
@@ -1443,7 +1447,7 @@ static int text_get_cursor_rel(SpaceText *st, ARegion *ar, TextLine *linein, int
                                chop = 1;
                                rell--;
 
-                               if (rell == 0 && i - start >= relc) {
+                               if (rell == 0 && i + columns - start > relc) {
                                        selc = curs;
                                        loop = 0;
                                        break;
@@ -1460,7 +1464,7 @@ static int text_get_cursor_rel(SpaceText *st, ARegion *ar, TextLine *linein, int
                                        break;
                                }
 
-                               if (rell == 0 && i - start >= relc) {
+                               if (rell == 0 && i + columns - start > relc) {
                                        selc = curs;
                                        loop = 0;
                                        break;
@@ -1469,7 +1473,7 @@ static int text_get_cursor_rel(SpaceText *st, ARegion *ar, TextLine *linein, int
                                endj = j;
                                chop = 0;
                        }
-                       i++;
+                       i += columns;
                }
        }
 
@@ -1587,6 +1591,8 @@ static void txt_wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
 
        for (i = 0, j = 0; loop; j += BLI_str_utf8_size_safe((*linep)->line + j)) {
                int chars;
+               int columns = BLI_str_utf8_char_width_safe((*linep)->line + j); /* = 1 for tab */
+
                /* Mimic replacement of tabs */
                ch = (*linep)->line[j];
                if (ch == '\t') {
@@ -1598,11 +1604,13 @@ static void txt_wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
                }
 
                while (chars--) {
-                       if (i - start >= max) {
+                       if (i + columns - start > max) {
+                               end = MIN2(end, i);
+
                                *charp = endj;
 
                                if (j >= oldc) {
-                                       if (ch == '\0') *charp = txt_utf8_index_to_offset((*linep)->line, start);
+                                       if (ch == '\0') *charp = txt_utf8_column_to_offset((*linep)->line, start);
                                        loop = 0;
                                        break;
                                }
@@ -1615,7 +1623,7 @@ static void txt_wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
                        }
                        else if (ch == ' ' || ch == '-' || ch == '\0') {
                                if (j >= oldc) {
-                                       *charp = txt_utf8_index_to_offset((*linep)->line, start);
+                                       *charp = txt_utf8_column_to_offset((*linep)->line, start);
                                        loop = 0;
                                        break;
                                }
@@ -1624,7 +1632,7 @@ static void txt_wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
                                endj = j + 1;
                                chop = 0;
                        }
-                       i++;
+                       i += columns;
                }
        }
 
@@ -1655,6 +1663,8 @@ static void txt_wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
 
        for (i = 0, j = 0; loop; j += BLI_str_utf8_size_safe((*linep)->line + j)) {
                int chars;
+               int columns = BLI_str_utf8_char_width_safe((*linep)->line + j); /* = 1 for tab */
+
                /* Mimic replacement of tabs */
                ch = (*linep)->line[j];
                if (ch == '\t') {
@@ -1666,7 +1676,9 @@ static void txt_wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
                }
 
                while (chars--) {
-                       if (i - start >= max) {
+                       if (i + columns - start > max) {
+                               end = MIN2(end, i);
+
                                if (chop) endj = BLI_str_prev_char_utf8((*linep)->line + j) - (*linep)->line;
 
                                if (endj >= oldc) {
@@ -1690,7 +1702,7 @@ static void txt_wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
                                endj = j;
                                chop = 0;
                        }
-                       i++;
+                       i += columns;
                }
        }
 
@@ -2352,7 +2364,7 @@ typedef struct SetSelection {
        short old[2];
 } SetSelection;
 
-static int flatten_len(SpaceText *st, const char *str)
+static int flatten_width(SpaceText *st, const char *str)
 {
        int i, total = 0;
 
@@ -2361,21 +2373,29 @@ static int flatten_len(SpaceText *st, const char *str)
                        total += st->tabnumber - total % st->tabnumber;
                }
                else {
-                       total++;
+                       total += BLI_str_utf8_char_width_safe(str + i);
                }
        }
        
        return total;
 }
 
-static int flatten_index_to_offset(SpaceText *st, const char *str, int index)
+static int flatten_column_to_offset(SpaceText *st, const char *str, int index)
 {
-       int i, j;
-       for (i = 0, j = 0; i < index; j += BLI_str_utf8_size_safe(str + j))
+       int i = 0, j = 0, col;
+
+       while (*(str + j)) {
                if (str[j] == '\t')
-                       i += st->tabnumber - i % st->tabnumber;
+                       col = st->tabnumber - i % st->tabnumber;
                else
-                       i++;
+                       col = BLI_str_utf8_char_width_safe(str + j);
+               
+               if (i + col > index)
+                       break;
+               
+               i += col;
+               j += BLI_str_utf8_size_safe(str + j);
+       }
        
        return j;
 }
@@ -2402,7 +2422,7 @@ static TextLine *get_first_visible_line(SpaceText *st, ARegion *ar, int *y)
 static void text_cursor_set_to_pos_wrapped(SpaceText *st, ARegion *ar, int x, int y, int sel)
 {
        Text *text = st->text;
-       int max = wrap_width(st, ar); /* view */
+       int max = wrap_width(st, ar); /* column */
        int charp = -1;               /* mem */
        int loop = 1, found = 0;      /* flags */
        char ch;
@@ -2411,12 +2431,13 @@ static void text_cursor_set_to_pos_wrapped(SpaceText *st, ARegion *ar, int x, in
        TextLine *linep = get_first_visible_line(st, ar, &y);
        
        while (loop && linep) {
-               int i = 0, start = 0, end = max; /* view */
+               int i = 0, start = 0, end = max; /* column */
                int j = 0, curs = 0, endj = 0;   /* mem */
                int chop = 1;                    /* flags */
                
                for (; loop; j += BLI_str_utf8_size_safe(linep->line + j)) {
                        int chars;
+                       int columns = BLI_str_utf8_char_width_safe(linep->line + j); /* = 1 for tab */
                        
                        /* Mimic replacement of tabs */
                        ch = linep->line[j];
@@ -2436,17 +2457,19 @@ static void text_cursor_set_to_pos_wrapped(SpaceText *st, ARegion *ar, int x, in
                                        break;
                                        /* Exactly at the cursor */
                                }
-                               else if (y == 0 && i - start == x) {
+                               else if (y == 0 && i - start <= x && i + columns - start > x) {
                                        /* current position could be wrapped to next line */
                                        /* this should be checked when end of current line would be reached */
                                        charp = curs = j;
                                        found = 1;
                                        /* Prepare curs for next wrap */
                                }
-                               else if (i - end == x) {
+                               else if (i - end <= x && i + columns - end > x) {
                                        curs = j;
                                }
-                               if (i - start >= max) {
+                               if (i + columns - start > max) {
+                                       end = MIN2(end, i);
+                                       
                                        if (found) {
                                                /* exact cursor position was found, check if it's */
                                                /* still on needed line (hasn't been wrapped) */
@@ -2463,7 +2486,7 @@ static void text_cursor_set_to_pos_wrapped(SpaceText *st, ARegion *ar, int x, in
                                                y--;
                                        
                                        chop = 1;
-                                       if (y == 0 && i - start >= x) {
+                                       if (y == 0 && i + columns - start > x) {
                                                charp = curs;
                                                loop = 0;
                                                break;
@@ -2475,7 +2498,7 @@ static void text_cursor_set_to_pos_wrapped(SpaceText *st, ARegion *ar, int x, in
                                                break;
                                        }
                                        
-                                       if (y == 0 && i - start >= x) {
+                                       if (y == 0 && i + columns - start > x) {
                                                charp = curs;
                                                loop = 0;
                                                break;
@@ -2484,7 +2507,7 @@ static void text_cursor_set_to_pos_wrapped(SpaceText *st, ARegion *ar, int x, in
                                        endj = j;
                                        chop = 0;
                                }
-                               i++;
+                               i += columns;
                        }
                        
                        if (ch == '\0') break;
@@ -2523,7 +2546,7 @@ static void text_cursor_set_to_pos(SpaceText *st, ARegion *ar, int x, int y, int
        else x -= TXT_OFFSET;
 
        if (x < 0) x = 0;
-       x = text_pixel_x_to_index(st, x) + st->left;
+       x = text_pixel_x_to_column(st, x) + st->left;
        
        if (st->wordwrap) {
                text_cursor_set_to_pos_wrapped(st, ar, x, y, sel);
@@ -2546,8 +2569,8 @@ static void text_cursor_set_to_pos(SpaceText *st, ARegion *ar, int x, int y, int
                }
 
                
-               w = flatten_len(st, (*linep)->line);
-               if (x < w) *charp = flatten_index_to_offset(st, (*linep)->line, x);
+               w = flatten_width(st, (*linep)->line);
+               if (x < w) *charp = flatten_column_to_offset(st, (*linep)->line, x);
                else *charp = (*linep)->len;
        }
        if (!sel) txt_pop_sel(text);
index da0c6dd..6e55764 100644 (file)
@@ -449,6 +449,7 @@ void WM_exit_ext(bContext *C, const short do_python)
 
 #ifdef WITH_INTERNATIONAL
        BLF_free_unifont();
+       BLF_free_unifont_mono();
        BLF_lang_free();
 #endif
        
index 5e65324..c775f7d 100644 (file)
@@ -150,6 +150,7 @@ endif()
                bf_intern_opencolorio
                bf_intern_opennl
                extern_rangetree
+               extern_wcwidth
        )
 
        if(WITH_MOD_CLOTH_ELTOPO)
index 24c5ccc..e4d3794 100644 (file)
@@ -912,6 +912,7 @@ endif()
                bf_intern_raskter
                bf_intern_opencolorio
                extern_rangetree
+               extern_wcwidth
        )
 
        if(WITH_COMPOSITOR)
index c1f4330..5d843cc 100644 (file)
@@ -1055,6 +1055,7 @@ int main(int argc, char** argv)
 
 #ifdef WITH_INTERNATIONAL
        BLF_free_unifont();
+       BLF_free_unifont_mono();
        BLF_lang_free();
 #endif