Merge branch 'blender2.7'
[blender.git] / source / blender / blenlib / intern / string_cursor_utf8.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2011 Blender Foundation.
17  * All rights reserved.
18  */
19
20 /** \file \ingroup bli
21  */
22
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include "BLI_utildefines.h"
27 #include "BLI_string_utf8.h"
28
29 #include "BLI_string_cursor_utf8.h" /* own include */
30
31 #ifdef __GNUC__
32 #  pragma GCC diagnostic error "-Wsign-conversion"
33 #endif
34
35 typedef enum eStrCursorDelimType {
36         STRCUR_DELIM_NONE,
37         STRCUR_DELIM_ALPHANUMERIC,
38         STRCUR_DELIM_PUNCT,
39         STRCUR_DELIM_BRACE,
40         STRCUR_DELIM_OPERATOR,
41         STRCUR_DELIM_QUOTE,
42         STRCUR_DELIM_WHITESPACE,
43         STRCUR_DELIM_OTHER
44 } eStrCursorDelimType;
45
46 static eStrCursorDelimType cursor_delim_type_unicode(const uint uch)
47 {
48         switch (uch) {
49                 case ',':
50                 case '.':
51                         return STRCUR_DELIM_PUNCT;
52
53                 case '{':
54                 case '}':
55                 case '[':
56                 case ']':
57                 case '(':
58                 case ')':
59                         return STRCUR_DELIM_BRACE;
60
61                 case '+':
62                 case '-':
63                 case '=':
64                 case '~':
65                 case '%':
66                 case '/':
67                 case '<':
68                 case '>':
69                 case '^':
70                 case '*':
71                 case '&':
72                 case '|':
73                         return STRCUR_DELIM_OPERATOR;
74
75                 case '\'':
76                 case '\"':
77                         return STRCUR_DELIM_QUOTE;
78
79                 case ' ':
80                 case '\t':
81                 case '\n':
82                         return STRCUR_DELIM_WHITESPACE;
83
84                 case '\\':
85                 case '@':
86                 case '#':
87                 case '$':
88                 case ':':
89                 case ';':
90                 case '?':
91                 case '!':
92                 case 0xA3:  /* pound */
93                 case 0x80:  /* euro */
94                         /* case '_': *//* special case, for python */
95                         return STRCUR_DELIM_OTHER;
96
97                 default:
98                         break;
99         }
100         return STRCUR_DELIM_ALPHANUMERIC; /* Not quite true, but ok for now */
101 }
102
103 static eStrCursorDelimType cursor_delim_type_utf8(const char *ch_utf8)
104 {
105         /* for full unicode support we really need to have large lookup tables to figure
106          * out whats what in every possible char set - and python, glib both have these. */
107         uint uch = BLI_str_utf8_as_unicode(ch_utf8);
108         return cursor_delim_type_unicode(uch);
109 }
110
111 bool BLI_str_cursor_step_next_utf8(const char *str, size_t maxlen, int *pos)
112 {
113         const char *str_end = str + (maxlen + 1);
114         const char *str_pos = str + (*pos);
115         const char *str_next = BLI_str_find_next_char_utf8(str_pos, str_end);
116         if (str_next) {
117                 (*pos) += (str_next - str_pos);
118                 if ((*pos) > (int)maxlen) {
119                         (*pos) = (int)maxlen;
120                 }
121                 return true;
122         }
123
124         return false;
125 }
126
127 bool BLI_str_cursor_step_prev_utf8(const char *str, size_t UNUSED(maxlen), int *pos)
128 {
129         if ((*pos) > 0) {
130                 const char *str_pos = str + (*pos);
131                 const char *str_prev = BLI_str_find_prev_char_utf8(str, str_pos);
132                 if (str_prev) {
133                         (*pos) -= (str_pos - str_prev);
134                         return true;
135                 }
136         }
137
138         return false;
139 }
140
141 void BLI_str_cursor_step_utf8(
142         const char *str, size_t maxlen,
143         int *pos, eStrCursorJumpDirection direction,
144         eStrCursorJumpType jump, bool use_init_step)
145 {
146         const int pos_orig = *pos;
147
148         if (direction == STRCUR_DIR_NEXT) {
149                 if (use_init_step) {
150                         BLI_str_cursor_step_next_utf8(str, maxlen, pos);
151                 }
152                 else {
153                         BLI_assert(jump == STRCUR_JUMP_DELIM);
154                 }
155
156                 if (jump != STRCUR_JUMP_NONE) {
157                         const eStrCursorDelimType delim_type =
158                                 (*pos) < maxlen ? cursor_delim_type_utf8(&str[*pos]) : STRCUR_DELIM_NONE;
159                         /* jump between special characters (/,\,_,-, etc.),
160                          * look at function cursor_delim_type() for complete
161                          * list of special character, ctr -> */
162                         while ((*pos) < maxlen) {
163                                 if (BLI_str_cursor_step_next_utf8(str, maxlen, pos)) {
164                                         if ((jump != STRCUR_JUMP_ALL) &&
165                                             (delim_type != cursor_delim_type_utf8(&str[*pos])))
166                                         {
167                                                 break;
168                                         }
169                                 }
170                                 else {
171                                         break; /* unlikely but just in case */
172                                 }
173                         }
174                 }
175         }
176         else if (direction == STRCUR_DIR_PREV) {
177                 if (use_init_step) {
178                         BLI_str_cursor_step_prev_utf8(str, maxlen, pos);
179                 }
180                 else {
181                         BLI_assert(jump == STRCUR_JUMP_DELIM);
182                 }
183
184                 if (jump != STRCUR_JUMP_NONE) {
185                         const eStrCursorDelimType delim_type =
186                                 (*pos) > 0 ? cursor_delim_type_utf8(&str[(*pos) - 1]) : STRCUR_DELIM_NONE;
187                         /* jump between special characters (/,\,_,-, etc.),
188                          * look at function cursor_delim_type() for complete
189                          * list of special character, ctr -> */
190                         while ((*pos) > 0) {
191                                 const int pos_prev = *pos;
192                                 if (BLI_str_cursor_step_prev_utf8(str, maxlen, pos)) {
193                                         if ((jump != STRCUR_JUMP_ALL) &&
194                                             (delim_type != cursor_delim_type_utf8(&str[*pos])))
195                                         {
196                                                 /* left only: compensate for index/change in direction */
197                                                 if ((pos_orig - (*pos)) >= 1) {
198                                                         *pos = pos_prev;
199                                                 }
200                                                 break;
201                                         }
202                                 }
203                                 else {
204                                         break;
205                                 }
206                         }
207                 }
208         }
209         else {
210                 BLI_assert(0);
211         }
212 }
213
214 /* wchar_t version of BLI_str_cursor_step_utf8 (keep in sync!)
215  * less complex since it doesn't need to do multi-byte stepping.
216  */
217
218 /* helper funcs so we can match BLI_str_cursor_step_utf8 */
219 static bool wchar_t_step_next(const wchar_t *UNUSED(str), size_t maxlen, int *pos)
220 {
221         if ((*pos) >= (int)maxlen) {
222                 return false;
223         }
224         (*pos)++;
225         return true;
226 }
227
228 static bool wchar_t_step_prev(const wchar_t *UNUSED(str), size_t UNUSED(maxlen), int *pos)
229 {
230         if ((*pos) <= 0) {
231                 return false;
232         }
233         (*pos)--;
234         return true;
235 }
236
237 void BLI_str_cursor_step_wchar(
238         const wchar_t *str, size_t maxlen,
239         int *pos, eStrCursorJumpDirection direction,
240         eStrCursorJumpType jump, bool use_init_step)
241 {
242         const int pos_orig = *pos;
243
244         if (direction == STRCUR_DIR_NEXT) {
245                 if (use_init_step) {
246                         wchar_t_step_next(str, maxlen, pos);
247                 }
248                 else {
249                         BLI_assert(jump == STRCUR_JUMP_DELIM);
250                 }
251
252                 if (jump != STRCUR_JUMP_NONE) {
253                         const eStrCursorDelimType delim_type =
254                                 (*pos) < maxlen ? cursor_delim_type_unicode((uint)str[*pos]) : STRCUR_DELIM_NONE;
255                         /* jump between special characters (/,\,_,-, etc.),
256                          * look at function cursor_delim_type_unicode() for complete
257                          * list of special character, ctr -> */
258                         while ((*pos) < maxlen) {
259                                 if (wchar_t_step_next(str, maxlen, pos)) {
260                                         if ((jump != STRCUR_JUMP_ALL) &&
261                                             (delim_type != cursor_delim_type_unicode((uint)str[*pos])))
262                                         {
263                                                 break;
264                                         }
265                                 }
266                                 else {
267                                         break; /* unlikely but just in case */
268                                 }
269                         }
270                 }
271         }
272         else if (direction == STRCUR_DIR_PREV) {
273                 if (use_init_step) {
274                         wchar_t_step_prev(str, maxlen, pos);
275                 }
276                 else {
277                         BLI_assert(jump == STRCUR_JUMP_DELIM);
278                 }
279
280                 if (jump != STRCUR_JUMP_NONE) {
281                         const eStrCursorDelimType delim_type =
282                                 (*pos) > 0 ? cursor_delim_type_unicode((uint)str[(*pos) - 1]) : STRCUR_DELIM_NONE;
283                         /* jump between special characters (/,\,_,-, etc.),
284                          * look at function cursor_delim_type() for complete
285                          * list of special character, ctr -> */
286                         while ((*pos) > 0) {
287                                 const int pos_prev = *pos;
288                                 if (wchar_t_step_prev(str, maxlen, pos)) {
289                                         if ((jump != STRCUR_JUMP_ALL) &&
290                                             (delim_type != cursor_delim_type_unicode((uint)str[*pos])))
291                                         {
292                                                 /* left only: compensate for index/change in direction */
293                                                 if ((pos_orig - (*pos)) >= 1) {
294                                                         *pos = pos_prev;
295                                                 }
296                                                 break;
297                                         }
298                                 }
299                                 else {
300                                         break;
301                                 }
302                         }
303                 }
304         }
305         else {
306                 BLI_assert(0);
307         }
308 }