diff options
27 files changed, 888 insertions, 183 deletions
diff --git a/extern/CMakeLists.txt b/extern/CMakeLists.txt index 6ad6bdc316f..941f69e9047 100644 --- a/extern/CMakeLists.txt +++ b/extern/CMakeLists.txt @@ -28,6 +28,7 @@ remove_strict_flags() add_subdirectory(colamd) add_subdirectory(rangetree) +add_subdirectory(wcwidth) if(WITH_BULLET) if(NOT WITH_SYSTEM_BULLET) diff --git a/extern/SConscript b/extern/SConscript index 6a0ffa3f588..e2bb1dc1f31 100644 --- a/extern/SConscript +++ b/extern/SConscript @@ -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 index 00000000000..cf8d7d6d15e --- /dev/null +++ b/extern/wcwidth/CMakeLists.txt @@ -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 index 00000000000..14fdaf3f738 --- /dev/null +++ b/extern/wcwidth/SConscript @@ -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 index 00000000000..61e822ad679 --- /dev/null +++ b/extern/wcwidth/wcwidth.c @@ -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 index 00000000000..e327921b6ea --- /dev/null +++ b/extern/wcwidth/wcwidth.h @@ -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 diff --git a/source/blender/blenfont/BLF_api.h b/source/blender/blenfont/BLF_api.h index 0ca97975d87..fd8bd196717 100644 --- a/source/blender/blenfont/BLF_api.h +++ b/source/blender/blenfont/BLF_api.h @@ -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. diff --git a/source/blender/blenfont/BLF_translation.h b/source/blender/blenfont/BLF_translation.h index cbfc7c28d8d..2f40cc3f571 100644 --- a/source/blender/blenfont/BLF_translation.h +++ b/source/blender/blenfont/BLF_translation.h @@ -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); diff --git a/source/blender/blenfont/intern/blf.c b/source/blender/blenfont/intern/blf.c index 061e8e28607..e7e277b7b34 100644 --- a/source/blender/blenfont/intern/blf.c +++ b/source/blender/blenfont/intern/blf.c @@ -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, ¶m); + 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); diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index 1900efa2dbc..f2c36475870 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -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) { diff --git a/source/blender/blenfont/intern/blf_internal.h b/source/blender/blenfont/intern/blf_internal.h index 65a54783978..b1301be46fd 100644 --- a/source/blender/blenfont/intern/blf_internal.h +++ b/source/blender/blenfont/intern/blf_internal.h @@ -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); diff --git a/source/blender/blenfont/intern/blf_translation.c b/source/blender/blenfont/intern/blf_translation.c index f7ad5c07d50..3284225c457 100644 --- a/source/blender/blenfont/intern/blf_translation.c +++ b/source/blender/blenfont/intern/blf_translation.c @@ -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 diff --git a/source/blender/blenkernel/BKE_text.h b/source/blender/blenkernel/BKE_text.h index 3d7b5d1c8e4..466adc9477e 100644 --- a/source/blender/blenkernel/BKE_text.h +++ b/source/blender/blenkernel/BKE_text.h @@ -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); diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index a155ca47d3c..e229c288a24 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -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); diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h index 30d5c28bf98..d20cbd2a91c 100644 --- a/source/blender/blenlib/BLI_string_utf8.h +++ b/source/blender/blenlib/BLI_string_utf8.h @@ -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 diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index e5851109d87..bd6e84f7ef9 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -29,6 +29,7 @@ set(INC ../makesdna ../../../intern/ghost ../../../intern/guardedalloc + ../../../extern/wcwidth ) set(INC_SYS diff --git a/source/blender/blenlib/SConscript b/source/blender/blenlib/SConscript index 2be06f7311d..41a9fe9e8b4 100644 --- a/source/blender/blenlib/SConscript +++ b/source/blender/blenlib/SConscript @@ -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'] diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index 26235de4dd2..fe8f3c20ab4 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -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, diff --git a/source/blender/editors/interface/interface_style.c b/source/blender/editors/interface/interface_style.c index 3cd7499c78e..03805dd0ef8 100644 --- a/source/blender/editors/interface/interface_style.c +++ b/source/blender/editors/interface/interface_style.c @@ -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 ); } diff --git a/source/blender/editors/space_console/console_draw.c b/source/blender/editors/space_console/console_draw.c index 73747239255..cb191d0b15e 100644 --- a/source/blender/editors/space_console/console_draw.c +++ b/source/blender/editors/space_console/console_draw.c @@ -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); diff --git a/source/blender/editors/space_info/textview.c b/source/blender/editors/space_info/textview.c index 66f4904c340..58418f2befd 100644 --- a/source/blender/editors/space_info/textview.c +++ b/source/blender/editors/space_info/textview.c @@ -31,14 +31,19 @@ #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; } diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index a146fec3dac..95fd7fce878 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -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; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index e6f95c39baf..3a7070e1a7a 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -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); diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index da0c6dd2a63..6e557647d12 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -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 diff --git a/source/blenderplayer/CMakeLists.txt b/source/blenderplayer/CMakeLists.txt index 5e65324d893..c775f7d3279 100644 --- a/source/blenderplayer/CMakeLists.txt +++ b/source/blenderplayer/CMakeLists.txt @@ -150,6 +150,7 @@ endif() bf_intern_opencolorio bf_intern_opennl extern_rangetree + extern_wcwidth ) if(WITH_MOD_CLOTH_ELTOPO) diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 24c5ccceea3..e4d37942e17 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -912,6 +912,7 @@ endif() bf_intern_raskter bf_intern_opencolorio extern_rangetree + extern_wcwidth ) if(WITH_COMPOSITOR) diff --git a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp index c1f43306eba..5d843cccf85 100644 --- a/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp +++ b/source/gameengine/GamePlayer/ghost/GPG_ghost.cpp @@ -1055,6 +1055,7 @@ int main(int argc, char** argv) #ifdef WITH_INTERNATIONAL BLF_free_unifont(); + BLF_free_unifont_mono(); BLF_lang_free(); #endif |