Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / curve / editfont.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file blender/editors/curve/editfont.c
27  *  \ingroup edcurve
28  */
29
30
31 #include <stdlib.h>
32 #include <string.h>
33 #include <fcntl.h>
34 #include <wchar.h>
35 #include <errno.h>
36
37 #include "MEM_guardedalloc.h"
38
39 #include "BLI_blenlib.h"
40 #include "BLI_math.h"
41 #include "BLI_string_cursor_utf8.h"
42 #include "BLI_utildefines.h"
43
44 #include "DNA_curve_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_vfont_types.h"
47 #include "DNA_scene_types.h"
48 #include "DNA_text_types.h"
49
50 #include "BKE_context.h"
51 #include "BKE_curve.h"
52 #include "BKE_font.h"
53 #include "BKE_library.h"
54 #include "BKE_main.h"
55 #include "BKE_object.h"
56 #include "BKE_report.h"
57
58 #include "DEG_depsgraph.h"
59
60 #include "RNA_access.h"
61 #include "RNA_define.h"
62
63 #include "WM_api.h"
64 #include "WM_types.h"
65
66 #include "ED_curve.h"
67 #include "ED_object.h"
68 #include "ED_screen.h"
69 #include "ED_util.h"
70 #include "ED_view3d.h"
71
72 #include "UI_interface.h"
73
74 #include "curve_intern.h"
75
76 #define MAXTEXT 32766
77
78 static int kill_selection(Object *obedit, int ins);
79
80 /************************* utilities ******************************/
81
82 static char findaccent(char char1, unsigned int code)
83 {
84         char new = 0;
85
86         if (char1 == 'a') {
87                 if (code == '`') new = 224;
88                 else if (code == 39) new = 225;
89                 else if (code == '^') new = 226;
90                 else if (code == '~') new = 227;
91                 else if (code == '"') new = 228;
92                 else if (code == 'o') new = 229;
93                 else if (code == 'e') new = 230;
94                 else if (code == '-') new = 170;
95         }
96         else if (char1 == 'c') {
97                 if (code == ',') new = 231;
98                 else if (code == '|') new = 162;
99                 else if (code == 'o') new = 169;
100         }
101         else if (char1 == 'e') {
102                 if (code == '`') new = 232;
103                 else if (code == 39) new = 233;
104                 else if (code == '^') new = 234;
105                 else if (code == '"') new = 235;
106         }
107         else if (char1 == 'i') {
108                 if (code == '`') new = 236;
109                 else if (code == 39) new = 237;
110                 else if (code == '^') new = 238;
111                 else if (code == '"') new = 239;
112         }
113         else if (char1 == 'n') {
114                 if (code == '~') new = 241;
115         }
116         else if (char1 == 'o') {
117                 if (code == '`') new = 242;
118                 else if (code == 39) new = 243;
119                 else if (code == '^') new = 244;
120                 else if (code == '~') new = 245;
121                 else if (code == '"') new = 246;
122                 else if (code == '/') new = 248;
123                 else if (code == '-') new = 186;
124                 else if (code == 'e') new = 143;
125                 else if (code == 'c') new = 169;
126                 else if (code == 'r') new = 174;
127         }
128         else if (char1 == 'r') {
129                 if (code == 'o') new = 174;
130         }
131         else if (char1 == 's') {
132                 if (code == 's') new = 167;
133         }
134         else if (char1 == 't') {
135                 if (code == 'm') new = 153;
136         }
137         else if (char1 == 'u') {
138                 if (code == '`') new = 249;
139                 else if (code == 39) new = 250;
140                 else if (code == '^') new = 251;
141                 else if (code == '"') new = 252;
142         }
143         else if (char1 == 'y') {
144                 if (code == 39) new = 253;
145                 else if (code == '"') new = 255;
146         }
147         else if (char1 == 'A') {
148                 if (code == '`') new = 192;
149                 else if (code == 39) new = 193;
150                 else if (code == '^') new = 194;
151                 else if (code == '~') new = 195;
152                 else if (code == '"') new = 196;
153                 else if (code == 'o') new = 197;
154                 else if (code == 'e') new = 198;
155         }
156         else if (char1 == 'C') {
157                 if (code == ',') new = 199;
158         }
159         else if (char1 == 'E') {
160                 if (code == '`') new = 200;
161                 else if (code == 39) new = 201;
162                 else if (code == '^') new = 202;
163                 else if (code == '"') new = 203;
164         }
165         else if (char1 == 'I') {
166                 if (code == '`') new = 204;
167                 else if (code == 39) new = 205;
168                 else if (code == '^') new = 206;
169                 else if (code == '"') new = 207;
170         }
171         else if (char1 == 'N') {
172                 if (code == '~') new = 209;
173         }
174         else if (char1 == 'O') {
175                 if (code == '`') new = 210;
176                 else if (code == 39) new = 211;
177                 else if (code == '^') new = 212;
178                 else if (code == '~') new = 213;
179                 else if (code == '"') new = 214;
180                 else if (code == '/') new = 216;
181                 else if (code == 'e') new = 141;
182         }
183         else if (char1 == 'U') {
184                 if (code == '`') new = 217;
185                 else if (code == 39) new = 218;
186                 else if (code == '^') new = 219;
187                 else if (code == '"') new = 220;
188         }
189         else if (char1 == 'Y') {
190                 if (code == 39) new = 221;
191         }
192         else if (char1 == '1') {
193                 if (code == '4') new = 188;
194                 if (code == '2') new = 189;
195         }
196         else if (char1 == '3') {
197                 if (code == '4') new = 190;
198         }
199         else if (char1 == ':') {
200                 if (code == '-') new = 247;
201         }
202         else if (char1 == '-') {
203                 if (code == ':') new = 247;
204                 if (code == '|') new = 135;
205                 if (code == '+') new = 177;
206         }
207         else if (char1 == '|') {
208                 if (code == '-') new = 135;
209                 if (code == '=') new = 136;
210         }
211         else if (char1 == '=') {
212                 if (code == '|') new = 136;
213         }
214         else if (char1 == '+') {
215                 if (code == '-') new = 177;
216         }
217
218         if (new) return new;
219         else return char1;
220 }
221
222 static int insert_into_textbuf(Object *obedit, uintptr_t c)
223 {
224         Curve *cu = obedit->data;
225         EditFont *ef = cu->editfont;
226
227         if (ef->len < MAXTEXT - 1) {
228                 int x;
229
230                 for (x = ef->len; x > ef->pos; x--) ef->textbuf[x] = ef->textbuf[x - 1];
231                 for (x = ef->len; x > ef->pos; x--) ef->textbufinfo[x] = ef->textbufinfo[x - 1];
232                 ef->textbuf[ef->pos] = c;
233                 ef->textbufinfo[ef->pos] = cu->curinfo;
234                 ef->textbufinfo[ef->pos].kern = 0;
235                 ef->textbufinfo[ef->pos].mat_nr = obedit->actcol;
236
237                 ef->pos++;
238                 ef->len++;
239                 ef->textbuf[ef->len] = '\0';
240
241                 return 1;
242         }
243         else
244                 return 0;
245 }
246
247 static void text_update_edited(bContext *C, Object *obedit, int mode)
248 {
249         struct Main *bmain = CTX_data_main(C);
250         Curve *cu = obedit->data;
251         EditFont *ef = cu->editfont;
252
253         BLI_assert(ef->len >= 0);
254
255         /* run update first since it can move the cursor */
256         if (mode == FO_EDIT) {
257                 /* re-tesselllate */
258                 DEG_id_tag_update(obedit->data, 0);
259         }
260         else {
261                 /* depsgraph runs above, but since we're not tagging for update, call direct */
262                 BKE_vfont_to_curve(bmain, obedit, mode);
263         }
264
265         cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0];
266
267         if (obedit->totcol > 0) {
268                 obedit->actcol = cu->curinfo.mat_nr;
269
270                 /* since this array is calloc'd, it can be 0 even though we try ensure
271                  * (mat_nr > 0) almost everywhere */
272                 if (obedit->actcol < 1) {
273                         obedit->actcol = 1;
274                 }
275         }
276
277         BKE_curve_batch_cache_dirty(cu, BKE_CURVE_BATCH_DIRTY_SELECT);
278
279         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
280 }
281
282 /* -------------------------------------------------------------------- */
283 /* Generic Paste Functions */
284
285 /* text_update_edited(C, scene, obedit, 1, FO_EDIT); */
286 static bool font_paste_wchar(Object *obedit, const wchar_t *str, const size_t str_len,
287                              /* optional */
288                              struct CharInfo *str_info)
289 {
290         Curve *cu = obedit->data;
291         EditFont *ef = cu->editfont;
292         int selend, selstart;
293
294         if (BKE_vfont_select_get(obedit, &selstart, &selend) == 0) {
295                 selstart = selend = 0;
296         }
297
298         /* Verify that the copy buffer => [copy buffer len] + ef->len < MAXTEXT */
299         if ((ef->len + str_len) - (selend - selstart) <= MAXTEXT) {
300
301                 kill_selection(obedit, 0);
302
303                 if (str_len) {
304                         int size = (ef->len * sizeof(wchar_t)) - (ef->pos * sizeof(wchar_t)) + sizeof(wchar_t);
305                         memmove(ef->textbuf + ef->pos + str_len, ef->textbuf + ef->pos, size);
306                         memcpy(ef->textbuf + ef->pos, str, str_len * sizeof(wchar_t));
307
308                         memmove(ef->textbufinfo + ef->pos + str_len, ef->textbufinfo + ef->pos, (ef->len - ef->pos + 1) * sizeof(CharInfo));
309                         if (str_info) {
310                                 memcpy(ef->textbufinfo + ef->pos, str_info, str_len * sizeof(CharInfo));
311                         }
312                         else {
313                                 memset(ef->textbufinfo + ef->pos, '\0', str_len * sizeof(CharInfo));
314                         }
315
316                         ef->len += str_len;
317                         ef->pos += str_len;
318                 }
319
320                 return true;
321         }
322
323         return false;
324 }
325
326 static bool font_paste_utf8(bContext *C, const char *str, const size_t str_len)
327 {
328         Object *obedit = CTX_data_edit_object(C);
329         bool retval;
330
331         int tmplen;
332
333         wchar_t *mem = MEM_mallocN((sizeof(wchar_t) * (str_len + 1)), __func__);
334
335         tmplen = BLI_strncpy_wchar_from_utf8(mem, str, str_len + 1);
336
337         retval = font_paste_wchar(obedit, mem, tmplen, NULL);
338
339         MEM_freeN(mem);
340
341         return retval;
342 }
343
344
345 /* -------------------------------------------------------------------- */
346 /* Paste From File*/
347
348 static int paste_from_file(bContext *C, ReportList *reports, const char *filename)
349 {
350         Object *obedit = CTX_data_edit_object(C);
351         char *strp;
352         size_t filelen;
353         int retval;
354
355         strp = BLI_file_read_text_as_mem(filename, 1, &filelen);
356         if (strp == NULL) {
357                 BKE_reportf(reports, RPT_ERROR, "Failed to open file '%s'", filename);
358                 return OPERATOR_CANCELLED;
359         }
360         strp[filelen] = 0;
361
362         if (font_paste_utf8(C, strp, filelen)) {
363                 text_update_edited(C, obedit, FO_EDIT);
364                 retval = OPERATOR_FINISHED;
365
366         }
367         else {
368                 BKE_reportf(reports, RPT_ERROR, "File too long %s", filename);
369                 retval = OPERATOR_CANCELLED;
370         }
371
372         MEM_freeN(strp);
373
374         return retval;
375 }
376
377 static int paste_from_file_exec(bContext *C, wmOperator *op)
378 {
379         char *path;
380         int retval;
381
382         path = RNA_string_get_alloc(op->ptr, "filepath", NULL, 0);
383         retval = paste_from_file(C, op->reports, path);
384         MEM_freeN(path);
385
386         return retval;
387 }
388
389 static int paste_from_file_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
390 {
391         if (RNA_struct_property_is_set(op->ptr, "filepath"))
392                 return paste_from_file_exec(C, op);
393
394         WM_event_add_fileselect(C, op);
395
396         return OPERATOR_RUNNING_MODAL;
397 }
398
399 void FONT_OT_text_paste_from_file(wmOperatorType *ot)
400 {
401         /* identifiers */
402         ot->name = "Paste File";
403         ot->description = "Paste contents from file";
404         ot->idname = "FONT_OT_text_paste_from_file";
405
406         /* api callbacks */
407         ot->exec = paste_from_file_exec;
408         ot->invoke = paste_from_file_invoke;
409         ot->poll = ED_operator_editfont;
410
411         /* flags */
412         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
413
414         /* properties */
415         WM_operator_properties_filesel(
416                 ot, FILE_TYPE_FOLDER | FILE_TYPE_TEXT, FILE_SPECIAL, FILE_OPENFILE,
417                 WM_FILESEL_FILEPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
418 }
419
420 /******************* text to object operator ********************/
421
422 static void txt_add_object(bContext *C, TextLine *firstline, int totline, const float offset[3])
423 {
424         Main *bmain = CTX_data_main(C);
425         Depsgraph *depsgraph = CTX_data_depsgraph(C);
426         Scene *scene = CTX_data_scene(C);
427         ViewLayer *view_layer = CTX_data_view_layer(C);
428         Curve *cu;
429         Object *obedit;
430         Base *base;
431         struct TextLine *tmp;
432         int nchars = 0, nbytes = 0;
433         char *s;
434         int a;
435         float rot[3] = {0.f, 0.f, 0.f};
436
437         obedit = BKE_object_add(bmain, scene, view_layer, OB_FONT, NULL);
438         base = view_layer->basact;
439
440         /* seems to assume view align ? TODO - look into this, could be an operator option */
441         ED_object_base_init_transform(C, base, NULL, rot);
442
443         BKE_object_where_is_calc(depsgraph, scene, obedit);
444
445         add_v3_v3(obedit->loc, offset);
446
447         cu = obedit->data;
448         cu->vfont = BKE_vfont_builtin_get();
449         id_us_plus(&cu->vfont->id);
450
451         for (tmp = firstline, a = 0; nbytes < MAXTEXT && a < totline; tmp = tmp->next, a++) {
452                 size_t nchars_line, nbytes_line;
453                 nchars_line = BLI_strlen_utf8_ex(tmp->line, &nbytes_line);
454                 nchars += nchars_line + 1;
455                 nbytes += nbytes_line + 1;
456         }
457
458         if (cu->str) MEM_freeN(cu->str);
459         if (cu->strinfo) MEM_freeN(cu->strinfo);
460
461         cu->str = MEM_mallocN(nbytes + 4, "str");
462         cu->strinfo = MEM_callocN((nchars + 4) * sizeof(CharInfo), "strinfo");
463
464         cu->len = 0;
465         cu->len_wchar = nchars - 1;
466         cu->pos = 0;
467
468         s = cu->str;
469
470         for (tmp = firstline, a = 0; cu->len < MAXTEXT && a < totline; tmp = tmp->next, a++) {
471                 size_t nbytes_line;
472
473                 nbytes_line = BLI_strcpy_rlen(s, tmp->line);
474
475                 s += nbytes_line;
476                 cu->len += nbytes_line;
477
478                 if (tmp->next) {
479                         nbytes_line = BLI_strcpy_rlen(s, "\n");
480
481                         s += nbytes_line;
482                         cu->len += nbytes_line;
483                 }
484
485         }
486
487         cu->pos = cu->len_wchar;
488         *s = '\0';
489
490         WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, obedit);
491 }
492
493 void ED_text_to_object(bContext *C, Text *text, const bool split_lines)
494 {
495         RegionView3D *rv3d = CTX_wm_region_view3d(C);
496         TextLine *line;
497         float offset[3];
498         int linenum = 0;
499
500         if (!text || !text->lines.first) return;
501
502         if (split_lines) {
503                 for (line = text->lines.first; line; line = line->next) {
504                         /* skip lines with no text, but still make space for them */
505                         if (line->line[0] == '\0') {
506                                 linenum++;
507                                 continue;
508                         }
509
510                         /* do the translation */
511                         offset[0] = 0;
512                         offset[1] = -linenum;
513                         offset[2] = 0;
514
515                         if (rv3d)
516                                 mul_mat3_m4_v3(rv3d->viewinv, offset);
517
518                         txt_add_object(C, line, 1, offset);
519
520                         linenum++;
521                 }
522         }
523         else {
524                 offset[0] = 0.0f;
525                 offset[1] = 0.0f;
526                 offset[2] = 0.0f;
527
528                 txt_add_object(C, text->lines.first, BLI_listbase_count(&text->lines), offset);
529         }
530 }
531
532 /********************** utilities ***************************/
533
534 static int kill_selection(Object *obedit, int ins)  /* 1 == new character */
535 {
536         Curve *cu = obedit->data;
537         EditFont *ef = cu->editfont;
538         int selend, selstart, direction;
539         int offset = 0;
540         int getfrom;
541
542         direction = BKE_vfont_select_get(obedit, &selstart, &selend);
543         if (direction) {
544                 int size;
545                 if (ins) offset = 1;
546                 if (ef->pos >= selstart) ef->pos = selstart + offset;
547                 if ((direction == -1) && ins) {
548                         selstart++;
549                         selend++;
550                 }
551                 getfrom = selend + offset;
552                 if (ins == 0) getfrom++;
553                 size = (ef->len * sizeof(wchar_t)) - (selstart * sizeof(wchar_t)) + (offset * sizeof(wchar_t));
554                 memmove(ef->textbuf + selstart, ef->textbuf + getfrom, size);
555                 memmove(ef->textbufinfo + selstart, ef->textbufinfo + getfrom, ((ef->len - selstart) + offset) * sizeof(CharInfo));
556                 ef->len -= ((selend - selstart) + 1);
557                 ef->selstart = ef->selend = 0;
558         }
559
560         return(direction);
561 }
562
563 /******************* set style operator ********************/
564
565 static const EnumPropertyItem style_items[] = {
566         {CU_CHINFO_BOLD, "BOLD", 0, "Bold", ""},
567         {CU_CHINFO_ITALIC, "ITALIC", 0, "Italic", ""},
568         {CU_CHINFO_UNDERLINE, "UNDERLINE", 0, "Underline", ""},
569         {CU_CHINFO_SMALLCAPS, "SMALL_CAPS", 0, "Small Caps", ""},
570         {0, NULL, 0, NULL, NULL}
571 };
572
573 static int set_style(bContext *C, const int style, const bool clear)
574 {
575         Object *obedit = CTX_data_edit_object(C);
576         Curve *cu = obedit->data;
577         EditFont *ef = cu->editfont;
578         int i, selstart, selend;
579
580         if (!BKE_vfont_select_get(obedit, &selstart, &selend))
581                 return OPERATOR_CANCELLED;
582
583         for (i = selstart; i <= selend; i++) {
584                 if (clear)
585                         ef->textbufinfo[i].flag &= ~style;
586                 else
587                         ef->textbufinfo[i].flag |= style;
588         }
589
590         DEG_id_tag_update(obedit->data, 0);
591         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
592
593         return OPERATOR_FINISHED;
594 }
595
596 static int set_style_exec(bContext *C, wmOperator *op)
597 {
598         const int style = RNA_enum_get(op->ptr, "style");
599         const bool clear = RNA_boolean_get(op->ptr, "clear");
600
601         return set_style(C, style, clear);
602 }
603
604 void FONT_OT_style_set(wmOperatorType *ot)
605 {
606         /* identifiers */
607         ot->name = "Set Style";
608         ot->description = "Set font style";
609         ot->idname = "FONT_OT_style_set";
610
611         /* api callbacks */
612         ot->exec = set_style_exec;
613         ot->poll = ED_operator_editfont;
614
615         /* flags */
616         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
617
618         /* properties */
619         RNA_def_enum(ot->srna, "style", style_items, CU_CHINFO_BOLD, "Style", "Style to set selection to");
620         RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear style rather than setting it");
621 }
622
623 /******************* toggle style operator ********************/
624
625 static int toggle_style_exec(bContext *C, wmOperator *op)
626 {
627         Object *obedit = CTX_data_edit_object(C);
628         Curve *cu = obedit->data;
629         int style, clear, selstart, selend;
630
631         if (!BKE_vfont_select_get(obedit, &selstart, &selend))
632                 return OPERATOR_CANCELLED;
633
634         style = RNA_enum_get(op->ptr, "style");
635
636         cu->curinfo.flag ^= style;
637         clear = (cu->curinfo.flag & style) == 0;
638
639         return set_style(C, style, clear);
640 }
641
642 void FONT_OT_style_toggle(wmOperatorType *ot)
643 {
644         /* identifiers */
645         ot->name = "Toggle Style";
646         ot->description = "Toggle font style";
647         ot->idname = "FONT_OT_style_toggle";
648
649         /* api callbacks */
650         ot->exec = toggle_style_exec;
651         ot->poll = ED_operator_editfont;
652
653         /* flags */
654         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
655
656         /* properties */
657         RNA_def_enum(ot->srna, "style", style_items, CU_CHINFO_BOLD, "Style", "Style to set selection to");
658 }
659
660
661 /* -------------------------------------------------------------------- */
662 /* Select All */
663
664 static int font_select_all_exec(bContext *C, wmOperator *UNUSED(op))
665 {
666         Object *obedit = CTX_data_edit_object(C);
667         Curve *cu = obedit->data;
668         EditFont *ef = cu->editfont;
669
670         if (ef->len) {
671                 ef->selstart = 1;
672                 ef->selend = ef->len;
673                 ef->pos = ef->len;
674
675                 text_update_edited(C, obedit, FO_SELCHANGE);
676
677                 return OPERATOR_FINISHED;
678         }
679         else {
680                 return OPERATOR_CANCELLED;
681         }
682
683 }
684
685 void FONT_OT_select_all(wmOperatorType *ot)
686 {
687         /* identifiers */
688         ot->name = "Select All";
689         ot->description = "Select all text";
690         ot->idname = "FONT_OT_select_all";
691
692         /* api callbacks */
693         ot->exec = font_select_all_exec;
694         ot->poll = ED_operator_editfont;
695
696         /* flags */
697         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
698 }
699
700
701 /******************* copy text operator ********************/
702
703 static void copy_selection(Object *obedit)
704 {
705         int selstart, selend;
706
707         if (BKE_vfont_select_get(obedit, &selstart, &selend)) {
708                 Curve *cu = obedit->data;
709                 EditFont *ef = cu->editfont;
710                 char *buf = NULL;
711                 wchar_t *text_buf;
712                 size_t len_utf8;
713
714                 /* internal clipboard (for style) */
715                 BKE_vfont_clipboard_set(ef->textbuf + selstart, ef->textbufinfo + selstart, selend - selstart + 1);
716                 BKE_vfont_clipboard_get(&text_buf, NULL, &len_utf8, NULL);
717
718                 /* system clipboard */
719                 buf = MEM_mallocN(len_utf8 + 1, __func__);
720                 if (buf) {
721                         BLI_strncpy_wchar_as_utf8(buf, text_buf, len_utf8 + 1);
722                         WM_clipboard_text_set(buf, false);
723                         MEM_freeN(buf);
724                 }
725         }
726 }
727
728 static int copy_text_exec(bContext *C, wmOperator *UNUSED(op))
729 {
730         Object *obedit = CTX_data_edit_object(C);
731
732         copy_selection(obedit);
733
734         return OPERATOR_FINISHED;
735 }
736
737 void FONT_OT_text_copy(wmOperatorType *ot)
738 {
739         /* identifiers */
740         ot->name = "Copy Text";
741         ot->description = "Copy selected text to clipboard";
742         ot->idname = "FONT_OT_text_copy";
743
744         /* api callbacks */
745         ot->exec = copy_text_exec;
746         ot->poll = ED_operator_editfont;
747 }
748
749 /******************* cut text operator ********************/
750
751 static int cut_text_exec(bContext *C, wmOperator *UNUSED(op))
752 {
753         Object *obedit = CTX_data_edit_object(C);
754         int selstart, selend;
755
756         if (!BKE_vfont_select_get(obedit, &selstart, &selend))
757                 return OPERATOR_CANCELLED;
758
759         copy_selection(obedit);
760         kill_selection(obedit, 0);
761
762         text_update_edited(C, obedit, FO_EDIT);
763
764         return OPERATOR_FINISHED;
765 }
766
767 void FONT_OT_text_cut(wmOperatorType *ot)
768 {
769         /* identifiers */
770         ot->name = "Cut Text";
771         ot->description = "Cut selected text to clipboard";
772         ot->idname = "FONT_OT_text_cut";
773
774         /* api callbacks */
775         ot->exec = cut_text_exec;
776         ot->poll = ED_operator_editfont;
777
778         /* flags */
779         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
780 }
781
782 /******************* paste text operator ********************/
783
784 static bool paste_selection(Object *obedit, ReportList *reports)
785 {
786         wchar_t *text_buf;
787         CharInfo *info_buf;
788         size_t len;
789
790         BKE_vfont_clipboard_get(&text_buf, &info_buf, NULL, &len);
791
792         if (font_paste_wchar(obedit, text_buf, len, info_buf)) {
793                 return true;
794         }
795         else {
796                 BKE_report(reports, RPT_WARNING, "Text too long");
797                 return false;
798         }
799 }
800
801 static int paste_text_exec(bContext *C, wmOperator *op)
802 {
803         Object *obedit = CTX_data_edit_object(C);
804         int retval;
805         size_t len_utf8;
806         wchar_t *text_buf;
807
808         /* Store both clipboards as utf8 for comparison,
809          * Give priority to the internal 'vfont' clipboard with its 'CharInfo' text styles
810          * as long as its synchronized with the systems clipboard. */
811         struct {
812             char *buf;
813             int len;
814         } clipboard_system = {NULL}, clipboard_vfont =  {NULL};
815
816         clipboard_system.buf = WM_clipboard_text_get(false, &clipboard_system.len);
817
818         if (clipboard_system.buf == NULL) {
819                 return OPERATOR_CANCELLED;
820         }
821
822         BKE_vfont_clipboard_get(&text_buf, NULL, &len_utf8, NULL);
823
824         if (text_buf) {
825                 clipboard_vfont.buf = MEM_mallocN(len_utf8 + 1, __func__);
826
827                 if (clipboard_vfont.buf == NULL) {
828                         MEM_freeN(clipboard_system.buf);
829                         return OPERATOR_CANCELLED;
830                 }
831
832                 BLI_strncpy_wchar_as_utf8(clipboard_vfont.buf, text_buf, len_utf8 + 1);
833         }
834
835         if (clipboard_vfont.buf && STREQ(clipboard_vfont.buf, clipboard_system.buf)) {
836                 retval = paste_selection(obedit, op->reports) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
837         }
838         else {
839                 if ((clipboard_system.len <= MAXTEXT) &&
840                     font_paste_utf8(C, clipboard_system.buf, clipboard_system.len))
841                 {
842                         text_update_edited(C, obedit, FO_EDIT);
843                         retval = OPERATOR_FINISHED;
844                 }
845                 else {
846                         BKE_report(op->reports, RPT_ERROR, "Clipboard too long");
847                         retval = OPERATOR_CANCELLED;
848                 }
849
850                 /* free the existent clipboard buffer */
851                 BKE_vfont_clipboard_free();
852         }
853
854         if (retval != OPERATOR_CANCELLED) {
855                 text_update_edited(C, obedit, FO_EDIT);
856         }
857
858         /* cleanup */
859         if (clipboard_vfont.buf) {
860                 MEM_freeN(clipboard_vfont.buf);
861         }
862
863         MEM_freeN(clipboard_system.buf);
864
865         return retval;
866 }
867
868 void FONT_OT_text_paste(wmOperatorType *ot)
869 {
870         /* identifiers */
871         ot->name = "Paste Text";
872         ot->description = "Paste text from clipboard";
873         ot->idname = "FONT_OT_text_paste";
874
875         /* api callbacks */
876         ot->exec = paste_text_exec;
877         ot->poll = ED_operator_editfont;
878
879         /* flags */
880         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
881 }
882
883 /************************ move operator ************************/
884
885 static const EnumPropertyItem move_type_items[] = {
886         {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
887         {LINE_END, "LINE_END", 0, "Line End", ""},
888         {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
889         {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
890         {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
891         {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
892         {PREV_LINE, "PREVIOUS_LINE", 0, "Previous Line", ""},
893         {NEXT_LINE, "NEXT_LINE", 0, "Next Line", ""},
894         {PREV_PAGE, "PREVIOUS_PAGE", 0, "Previous Page", ""},
895         {NEXT_PAGE, "NEXT_PAGE", 0, "Next Page", ""},
896         {0, NULL, 0, NULL, NULL}};
897
898 static int move_cursor(bContext *C, int type, const bool select)
899 {
900         Object *obedit = CTX_data_edit_object(C);
901         Curve *cu = obedit->data;
902         EditFont *ef = cu->editfont;
903         int cursmove = -1;
904
905         if ((select) && (ef->selstart == 0)) {
906                 ef->selstart = ef->selend = ef->pos + 1;
907         }
908
909         switch (type) {
910                 case LINE_BEGIN:
911                         while (ef->pos > 0) {
912                                 if (ef->textbuf[ef->pos - 1] == '\n') break;
913                                 if (ef->textbufinfo[ef->pos - 1].flag & CU_CHINFO_WRAP) break;
914                                 ef->pos--;
915                         }
916                         cursmove = FO_CURS;
917                         break;
918
919                 case LINE_END:
920                         while (ef->pos < ef->len) {
921                                 if (ef->textbuf[ef->pos] == 0) break;
922                                 if (ef->textbuf[ef->pos] == '\n') break;
923                                 if (ef->textbufinfo[ef->pos].flag & CU_CHINFO_WRAP) break;
924                                 ef->pos++;
925                         }
926                         cursmove = FO_CURS;
927                         break;
928
929                 case PREV_WORD:
930                 {
931                         int pos = ef->pos;
932                         BLI_str_cursor_step_wchar(ef->textbuf, ef->len, &pos, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, true);
933                         ef->pos = pos;
934                         cursmove = FO_CURS;
935                         break;
936                 }
937
938                 case NEXT_WORD:
939                 {
940                         int pos = ef->pos;
941                         BLI_str_cursor_step_wchar(ef->textbuf, ef->len, &pos, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, true);
942                         ef->pos = pos;
943                         cursmove = FO_CURS;
944                         break;
945                 }
946
947                 case PREV_CHAR:
948                         ef->pos--;
949                         cursmove = FO_CURS;
950                         break;
951
952                 case NEXT_CHAR:
953                         ef->pos++;
954                         cursmove = FO_CURS;
955
956                         break;
957
958                 case PREV_LINE:
959                         cursmove = FO_CURSUP;
960                         break;
961
962                 case NEXT_LINE:
963                         cursmove = FO_CURSDOWN;
964                         break;
965
966                 case PREV_PAGE:
967                         cursmove = FO_PAGEUP;
968                         break;
969
970                 case NEXT_PAGE:
971                         cursmove = FO_PAGEDOWN;
972                         break;
973         }
974
975         if (cursmove == -1)
976                 return OPERATOR_CANCELLED;
977
978         if      (ef->pos > ef->len)  ef->pos = ef->len;
979         else if (ef->pos >= MAXTEXT) ef->pos = MAXTEXT;
980         else if (ef->pos < 0)        ef->pos = 0;
981
982         /* apply virtical cursor motion to position immediately
983          * otherwise the selection will lag behind */
984         if (FO_CURS_IS_MOTION(cursmove)) {
985                 struct Main *bmain = CTX_data_main(C);
986                 BKE_vfont_to_curve(bmain, obedit, cursmove);
987                 cursmove = FO_CURS;
988         }
989
990         if (select == 0) {
991                 if (ef->selstart) {
992                         struct Main *bmain = CTX_data_main(C);
993                         ef->selstart = ef->selend = 0;
994                         BKE_vfont_to_curve(bmain, obedit, FO_SELCHANGE);
995                 }
996         }
997
998         if (select)
999                 ef->selend = ef->pos;
1000
1001         text_update_edited(C, obedit, cursmove);
1002
1003         return OPERATOR_FINISHED;
1004 }
1005
1006 static int move_exec(bContext *C, wmOperator *op)
1007 {
1008         int type = RNA_enum_get(op->ptr, "type");
1009
1010         return move_cursor(C, type, false);
1011 }
1012
1013 void FONT_OT_move(wmOperatorType *ot)
1014 {
1015         /* identifiers */
1016         ot->name = "Move Cursor";
1017         ot->description = "Move cursor to position type";
1018         ot->idname = "FONT_OT_move";
1019
1020         /* api callbacks */
1021         ot->exec = move_exec;
1022         ot->poll = ED_operator_editfont;
1023
1024         /* flags */
1025         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1026
1027         /* properties */
1028         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to");
1029 }
1030
1031 /******************* move select operator ********************/
1032
1033 static int move_select_exec(bContext *C, wmOperator *op)
1034 {
1035         int type = RNA_enum_get(op->ptr, "type");
1036
1037         return move_cursor(C, type, true);
1038 }
1039
1040 void FONT_OT_move_select(wmOperatorType *ot)
1041 {
1042         /* identifiers */
1043         ot->name = "Move Select";
1044         ot->description = "Move the cursor while selecting";
1045         ot->idname = "FONT_OT_move_select";
1046
1047         /* api callbacks */
1048         ot->exec = move_select_exec;
1049         ot->poll = ED_operator_editfont;
1050
1051         /* flags */
1052         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1053
1054         /* properties */
1055         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection");
1056 }
1057
1058 /************************* change spacing **********************/
1059
1060 static int change_spacing_exec(bContext *C, wmOperator *op)
1061 {
1062         Object *obedit = CTX_data_edit_object(C);
1063         Curve *cu = obedit->data;
1064         EditFont *ef = cu->editfont;
1065         int kern, delta = RNA_int_get(op->ptr, "delta");
1066
1067         kern = ef->textbufinfo[ef->pos - 1].kern;
1068         kern += delta;
1069         CLAMP(kern, -20, 20);
1070
1071         if (ef->textbufinfo[ef->pos - 1].kern == kern)
1072                 return OPERATOR_CANCELLED;
1073
1074         ef->textbufinfo[ef->pos - 1].kern = kern;
1075
1076         text_update_edited(C, obedit, FO_EDIT);
1077
1078         return OPERATOR_FINISHED;
1079 }
1080
1081 void FONT_OT_change_spacing(wmOperatorType *ot)
1082 {
1083         /* identifiers */
1084         ot->name = "Change Spacing";
1085         ot->description = "Change font spacing";
1086         ot->idname = "FONT_OT_change_spacing";
1087
1088         /* api callbacks */
1089         ot->exec = change_spacing_exec;
1090         ot->poll = ED_operator_editfont;
1091
1092         /* flags */
1093         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1094
1095         /* properties */
1096         RNA_def_int(ot->srna, "delta", 1, -20, 20, "Delta", "Amount to decrease or increase character spacing with", -20, 20);
1097 }
1098
1099 /************************* change character **********************/
1100
1101 static int change_character_exec(bContext *C, wmOperator *op)
1102 {
1103         Object *obedit = CTX_data_edit_object(C);
1104         Curve *cu = obedit->data;
1105         EditFont *ef = cu->editfont;
1106         int character, delta = RNA_int_get(op->ptr, "delta");
1107
1108         if (ef->pos <= 0)
1109                 return OPERATOR_CANCELLED;
1110
1111         character = ef->textbuf[ef->pos - 1];
1112         character += delta;
1113         CLAMP(character, 0, 255);
1114
1115         if (character == ef->textbuf[ef->pos - 1])
1116                 return OPERATOR_CANCELLED;
1117
1118         ef->textbuf[ef->pos - 1] = character;
1119
1120         text_update_edited(C, obedit, FO_EDIT);
1121
1122         return OPERATOR_FINISHED;
1123 }
1124
1125 void FONT_OT_change_character(wmOperatorType *ot)
1126 {
1127         /* identifiers */
1128         ot->name = "Change Character";
1129         ot->description = "Change font character code";
1130         ot->idname = "FONT_OT_change_character";
1131
1132         /* api callbacks */
1133         ot->exec = change_character_exec;
1134         ot->poll = ED_operator_editfont;
1135
1136         /* flags */
1137         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1138
1139         /* properties */
1140         RNA_def_int(ot->srna, "delta", 1, -255, 255, "Delta", "Number to increase or decrease character code with", -255, 255);
1141 }
1142
1143 /******************* line break operator ********************/
1144
1145 static int line_break_exec(bContext *C, wmOperator *UNUSED(op))
1146 {
1147         Object *obedit = CTX_data_edit_object(C);
1148         Curve *cu = obedit->data;
1149         EditFont *ef = cu->editfont;
1150
1151         insert_into_textbuf(obedit, '\n');
1152
1153         ef->selstart = ef->selend = 0;
1154
1155         text_update_edited(C, obedit, FO_EDIT);
1156
1157         return OPERATOR_FINISHED;
1158 }
1159
1160 void FONT_OT_line_break(wmOperatorType *ot)
1161 {
1162         /* identifiers */
1163         ot->name = "Line Break";
1164         ot->description = "Insert line break at cursor position";
1165         ot->idname = "FONT_OT_line_break";
1166
1167         /* api callbacks */
1168         ot->exec = line_break_exec;
1169         ot->poll = ED_operator_editfont;
1170
1171         /* flags */
1172         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1173 }
1174
1175 /******************* delete operator **********************/
1176
1177 static const EnumPropertyItem delete_type_items[] = {
1178         {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
1179         {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
1180         {DEL_NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
1181         {DEL_PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
1182         {DEL_SELECTION, "SELECTION", 0, "Selection", ""},
1183         {DEL_NEXT_SEL, "NEXT_OR_SELECTION", 0, "Next or Selection", ""},
1184         {DEL_PREV_SEL, "PREVIOUS_OR_SELECTION", 0, "Previous or Selection", ""},
1185         {0, NULL, 0, NULL, NULL}};
1186
1187 static int delete_exec(bContext *C, wmOperator *op)
1188 {
1189         Object *obedit = CTX_data_edit_object(C);
1190         Curve *cu = obedit->data;
1191         EditFont *ef = cu->editfont;
1192         int selstart, selend, type = RNA_enum_get(op->ptr, "type");
1193         int range[2] = {0, 0};
1194         bool has_select = false;
1195
1196         if (ef->len == 0)
1197                 return OPERATOR_CANCELLED;
1198
1199         if (BKE_vfont_select_get(obedit, &selstart, &selend)) {
1200                 if (type == DEL_NEXT_SEL) type = DEL_SELECTION;
1201                 else if (type == DEL_PREV_SEL) type = DEL_SELECTION;
1202                 has_select = true;
1203         }
1204         else {
1205                 if (type == DEL_NEXT_SEL) type = DEL_NEXT_CHAR;
1206                 else if (type == DEL_PREV_SEL) type = DEL_PREV_CHAR;
1207         }
1208
1209         switch (type) {
1210                 case DEL_SELECTION:
1211                         if (!kill_selection(obedit, 0))
1212                                 return OPERATOR_CANCELLED;
1213                         break;
1214                 case DEL_PREV_CHAR:
1215                         if (ef->pos <= 0)
1216                                 return OPERATOR_CANCELLED;
1217
1218                         range[0] = ef->pos - 1;
1219                         range[1] = ef->pos;
1220
1221                         ef->pos--;
1222                         break;
1223                 case DEL_NEXT_CHAR:
1224                         if (ef->pos >= ef->len)
1225                                 return OPERATOR_CANCELLED;
1226
1227                         range[0] = ef->pos;
1228                         range[1] = ef->pos + 1;
1229                         break;
1230                 case DEL_NEXT_WORD:
1231                 {
1232                         int pos = ef->pos;
1233                         BLI_str_cursor_step_wchar(ef->textbuf, ef->len, &pos, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, true);
1234                         range[0] = ef->pos;
1235                         range[1] = pos;
1236                         break;
1237                 }
1238
1239                 case DEL_PREV_WORD:
1240                 {
1241                         int pos = ef->pos;
1242                         BLI_str_cursor_step_wchar(ef->textbuf, ef->len, &pos, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, true);
1243                         range[0] = pos;
1244                         range[1] = ef->pos;
1245                         ef->pos = pos;
1246                         break;
1247                 }
1248                 default:
1249                         return OPERATOR_CANCELLED;
1250         }
1251
1252         if (range[0] != range[1]) {
1253                 BLI_assert(range[0] < range[1]);
1254                 int len_remove = range[1] - range[0];
1255                 int len_tail = ef->len - range[1];
1256                 if (has_select) {
1257                         for (int i = 0; i < 2; i++) {
1258                                 int *sel = i ? &ef->selend : &ef->selstart;
1259                                 if (*sel <= range[0]) {
1260                                         /* pass */
1261                                 }
1262                                 else if (*sel >= range[1]) {
1263                                         *sel -= len_remove;
1264                                 }
1265                                 else if (*sel < range[1]) {
1266                                         /* pass */
1267                                         *sel = range[0];
1268                                 }
1269                         }
1270                 }
1271
1272                 memmove(&ef->textbuf[range[0]], &ef->textbuf[range[1]], sizeof(*ef->textbuf) * len_tail);
1273                 memmove(&ef->textbufinfo[range[0]], &ef->textbufinfo[range[1]], sizeof(*ef->textbufinfo) * len_tail);
1274
1275                 ef->len -= len_remove;
1276                 ef->textbuf[ef->len] = '\0';
1277
1278                 BKE_vfont_select_clamp(obedit);
1279         }
1280
1281         text_update_edited(C, obedit, FO_EDIT);
1282
1283         return OPERATOR_FINISHED;
1284 }
1285
1286 void FONT_OT_delete(wmOperatorType *ot)
1287 {
1288         /* identifiers */
1289         ot->name = "Delete";
1290         ot->description = "Delete text by cursor position";
1291         ot->idname = "FONT_OT_delete";
1292
1293         /* api callbacks */
1294         ot->exec = delete_exec;
1295         ot->poll = ED_operator_editfont;
1296
1297         /* flags */
1298         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1299
1300         /* properties */
1301         RNA_def_enum(ot->srna, "type", delete_type_items, DEL_PREV_CHAR, "Type", "Which part of the text to delete");
1302 }
1303
1304 /*********************** insert text operator *************************/
1305
1306 static int insert_text_exec(bContext *C, wmOperator *op)
1307 {
1308         Object *obedit = CTX_data_edit_object(C);
1309         char *inserted_utf8;
1310         wchar_t *inserted_text;
1311         int a, len;
1312
1313         if (!RNA_struct_property_is_set(op->ptr, "text"))
1314                 return OPERATOR_CANCELLED;
1315
1316         inserted_utf8 = RNA_string_get_alloc(op->ptr, "text", NULL, 0);
1317         len = BLI_strlen_utf8(inserted_utf8);
1318
1319         inserted_text = MEM_callocN(sizeof(wchar_t) * (len + 1), "FONT_insert_text");
1320         BLI_strncpy_wchar_from_utf8(inserted_text, inserted_utf8, len + 1);
1321
1322         for (a = 0; a < len; a++)
1323                 insert_into_textbuf(obedit, inserted_text[a]);
1324
1325         MEM_freeN(inserted_text);
1326         MEM_freeN(inserted_utf8);
1327
1328         kill_selection(obedit, 1);
1329         text_update_edited(C, obedit, FO_EDIT);
1330
1331         return OPERATOR_FINISHED;
1332 }
1333
1334 static int insert_text_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1335 {
1336         Object *obedit = CTX_data_edit_object(C);
1337         Curve *cu = obedit->data;
1338         EditFont *ef = cu->editfont;
1339         static int accentcode = 0;
1340         uintptr_t ascii = event->ascii;
1341         int alt = event->alt, shift = event->shift, ctrl = event->ctrl;
1342         int event_type = event->type, event_val = event->val;
1343         wchar_t inserted_text[2] = {0};
1344
1345         if (RNA_struct_property_is_set(op->ptr, "text"))
1346                 return insert_text_exec(C, op);
1347
1348         if (RNA_struct_property_is_set(op->ptr, "accent")) {
1349                 if (ef->len != 0 && ef->pos > 0)
1350                         accentcode = 1;
1351                 return OPERATOR_FINISHED;
1352         }
1353
1354         /* tab should exit editmode, but we allow it to be typed using modifier keys */
1355         if (event_type == TABKEY) {
1356                 if ((alt || ctrl || shift) == 0)
1357                         return OPERATOR_PASS_THROUGH;
1358                 else
1359                         ascii = 9;
1360         }
1361
1362         if (event_type == BACKSPACEKEY) {
1363                 if (alt && ef->len != 0 && ef->pos > 0)
1364                         accentcode = 1;
1365                 return OPERATOR_PASS_THROUGH;
1366         }
1367
1368         if (event_val && (ascii || event->utf8_buf[0])) {
1369                 /* handle case like TAB (== 9) */
1370                 if (     (ascii > 31 && ascii < 254 && ascii != 127) ||
1371                          (ascii == 13) ||
1372                          (ascii == 10) ||
1373                          (ascii == 8)  ||
1374                          (event->utf8_buf[0]))
1375                 {
1376
1377                         if (accentcode) {
1378                                 if (ef->pos > 0) {
1379                                         inserted_text[0] = findaccent(ef->textbuf[ef->pos - 1], ascii);
1380                                         ef->textbuf[ef->pos - 1] = inserted_text[0];
1381                                 }
1382                                 accentcode = 0;
1383                         }
1384                         else if (event->utf8_buf[0]) {
1385                                 inserted_text[0] = BLI_str_utf8_as_unicode(event->utf8_buf);
1386                                 ascii = inserted_text[0];
1387                                 insert_into_textbuf(obedit, ascii);
1388                                 accentcode = 0;
1389                         }
1390                         else if (ascii) {
1391                                 insert_into_textbuf(obedit, ascii);
1392                                 accentcode = 0;
1393                         }
1394                         else {
1395                                 BLI_assert(0);
1396                         }
1397
1398                         kill_selection(obedit, 1);
1399                         text_update_edited(C, obedit, FO_EDIT);
1400                 }
1401                 else {
1402                         inserted_text[0] = ascii;
1403                         insert_into_textbuf(obedit, ascii);
1404                         text_update_edited(C, obedit, FO_EDIT);
1405                 }
1406         }
1407         else
1408                 return OPERATOR_PASS_THROUGH;
1409
1410         if (inserted_text[0]) {
1411                 /* store as utf8 in RNA string */
1412                 char inserted_utf8[8] = {0};
1413
1414                 BLI_strncpy_wchar_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8));
1415                 RNA_string_set(op->ptr, "text", inserted_utf8);
1416         }
1417
1418         /* reset property? */
1419         if (event_val == 0)
1420                 accentcode = 0;
1421
1422         return OPERATOR_FINISHED;
1423 }
1424
1425 void FONT_OT_text_insert(wmOperatorType *ot)
1426 {
1427         /* identifiers */
1428         ot->name = "Insert Text";
1429         ot->description = "Insert text at cursor position";
1430         ot->idname = "FONT_OT_text_insert";
1431
1432         /* api callbacks */
1433         ot->exec = insert_text_exec;
1434         ot->invoke = insert_text_invoke;
1435         ot->poll = ED_operator_editfont;
1436
1437         /* flags */
1438         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1439
1440         /* properties */
1441         RNA_def_string(ot->srna, "text", NULL, 0, "Text", "Text to insert at the cursor position");
1442         RNA_def_boolean(ot->srna, "accent", 0, "Accent mode", "Next typed character will strike through previous, for special character input");
1443 }
1444
1445
1446 /*********************** textbox add operator *************************/
1447 static int textbox_add_exec(bContext *C, wmOperator *UNUSED(op))
1448 {
1449         Object *obedit = CTX_data_active_object(C);
1450         Curve *cu = obedit->data;
1451         int i;
1452
1453         if (cu->totbox < 256) {
1454                 for (i = cu->totbox; i > cu->actbox; i--) cu->tb[i] = cu->tb[i - 1];
1455                 cu->tb[cu->actbox] = cu->tb[cu->actbox - 1];
1456                 cu->actbox++;
1457                 cu->totbox++;
1458         }
1459
1460         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1461         return OPERATOR_FINISHED;
1462 }
1463
1464 void FONT_OT_textbox_add(wmOperatorType *ot)
1465 {
1466         /* identifiers */
1467         ot->name = "Add Textbox";
1468         ot->description = "Add a new text box";
1469         ot->idname = "FONT_OT_textbox_add";
1470
1471         /* api callbacks */
1472         ot->exec = textbox_add_exec;
1473         ot->poll = ED_operator_object_active_editable_font;
1474
1475         /* flags */
1476         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1477
1478
1479 }
1480
1481
1482
1483 /*********************** textbox remove operator *************************/
1484
1485
1486
1487 static int textbox_remove_exec(bContext *C, wmOperator *op)
1488 {
1489         Object *obedit = CTX_data_active_object(C);
1490         Curve *cu = obedit->data;
1491         int i;
1492         int index = RNA_int_get(op->ptr, "index");
1493
1494
1495         if (cu->totbox > 1) {
1496                 for (i = index; i < cu->totbox; i++) cu->tb[i] = cu->tb[i + 1];
1497                 cu->totbox--;
1498                 if (cu->actbox >= index)
1499                         cu->actbox--;
1500         }
1501
1502         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1503
1504         return OPERATOR_FINISHED;
1505 }
1506
1507 void FONT_OT_textbox_remove(wmOperatorType *ot)
1508 {
1509         /* identifiers */
1510         ot->name = "Remove Textbox";
1511         ot->description = "Remove the textbox";
1512         ot->idname = "FONT_OT_textbox_remove";
1513
1514         /* api callbacks */
1515         ot->exec = textbox_remove_exec;
1516         ot->poll = ED_operator_object_active_editable_font;
1517
1518         /* flags */
1519         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1520
1521         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "The current text box", 0, INT_MAX);
1522 }
1523
1524
1525
1526 /***************** editmode enter/exit ********************/
1527
1528 void ED_curve_editfont_make(Object *obedit)
1529 {
1530         Curve *cu = obedit->data;
1531         EditFont *ef = cu->editfont;
1532         int len_wchar;
1533
1534         if (ef == NULL) {
1535                 ef = cu->editfont = MEM_callocN(sizeof(EditFont), "editfont");
1536
1537                 ef->textbuf = MEM_callocN((MAXTEXT + 4) * sizeof(wchar_t), "texteditbuf");
1538                 ef->textbufinfo = MEM_callocN((MAXTEXT + 4) * sizeof(CharInfo), "texteditbufinfo");
1539         }
1540
1541         /* Convert the original text to wchar_t */
1542         len_wchar = BLI_strncpy_wchar_from_utf8(ef->textbuf, cu->str, MAXTEXT + 4);
1543         BLI_assert(len_wchar == cu->len_wchar);
1544         ef->len = len_wchar;
1545         BLI_assert(ef->len >= 0);
1546
1547         memcpy(ef->textbufinfo, cu->strinfo, ef->len * sizeof(CharInfo));
1548
1549         if (ef->pos > ef->len) ef->pos = ef->len;
1550
1551         cu->curinfo = ef->textbufinfo[ef->pos ? ef->pos - 1 : 0];
1552
1553         /* Other vars */
1554         ef->pos = cu->pos;
1555         ef->selstart = cu->selstart;
1556         ef->selend = cu->selend;
1557
1558         /* text may have been modified by Python */
1559         BKE_vfont_select_clamp(obedit);
1560 }
1561
1562 void ED_curve_editfont_load(Object *obedit)
1563 {
1564         Curve *cu = obedit->data;
1565         EditFont *ef = cu->editfont;
1566
1567         /* Free the old curve string */
1568         MEM_freeN(cu->str);
1569
1570         /* Calculate the actual string length in UTF-8 variable characters */
1571         cu->len_wchar = ef->len;
1572         cu->len = BLI_wstrlen_utf8(ef->textbuf);
1573
1574         /* Alloc memory for UTF-8 variable char length string */
1575         cu->str = MEM_mallocN(cu->len + sizeof(wchar_t), "str");
1576
1577         /* Copy the wchar to UTF-8 */
1578         BLI_strncpy_wchar_as_utf8(cu->str, ef->textbuf, cu->len + 1);
1579
1580         if (cu->strinfo)
1581                 MEM_freeN(cu->strinfo);
1582         cu->strinfo = MEM_callocN((cu->len_wchar + 4) * sizeof(CharInfo), "texteditinfo");
1583         memcpy(cu->strinfo, ef->textbufinfo, cu->len_wchar * sizeof(CharInfo));
1584
1585         /* Other vars */
1586         cu->pos = ef->pos;
1587         cu->selstart = ef->selstart;
1588         cu->selend = ef->selend;
1589 }
1590
1591 void ED_curve_editfont_free(Object *obedit)
1592 {
1593         BKE_curve_editfont_free((Curve *)obedit->data);
1594 }
1595
1596 /********************** set case operator *********************/
1597
1598 static const EnumPropertyItem case_items[] = {
1599         {CASE_LOWER, "LOWER", 0, "Lower", ""},
1600         {CASE_UPPER, "UPPER", 0, "Upper", ""},
1601         {0, NULL, 0, NULL, NULL}};
1602
1603 static int set_case(bContext *C, int ccase)
1604 {
1605         Object *obedit = CTX_data_edit_object(C);
1606         Curve *cu = obedit->data;
1607         EditFont *ef = cu->editfont;
1608         wchar_t *str;
1609         int len;
1610         int selstart, selend;
1611
1612         if (BKE_vfont_select_get(obedit, &selstart, &selend)) {
1613                 len = (selend - selstart) + 1;
1614                 str = &ef->textbuf[selstart];
1615                 while (len) {
1616                         if (*str >= 'a' && *str <= 'z')
1617                                 *str -= 32;
1618                         len--;
1619                         str++;
1620                 }
1621
1622                 if (ccase == CASE_LOWER) {
1623                         len = (selend - selstart) + 1;
1624                         str = &ef->textbuf[selstart];
1625                         while (len) {
1626                                 if (*str >= 'A' && *str <= 'Z') {
1627                                         *str += 32;
1628                                 }
1629                                 len--;
1630                                 str++;
1631                         }
1632                 }
1633
1634                 text_update_edited(C, obedit, FO_EDIT);
1635         }
1636
1637         return OPERATOR_FINISHED;
1638 }
1639
1640 static int set_case_exec(bContext *C, wmOperator *op)
1641 {
1642         return set_case(C, RNA_enum_get(op->ptr, "case"));
1643 }
1644
1645 void FONT_OT_case_set(wmOperatorType *ot)
1646 {
1647         /* identifiers */
1648         ot->name = "Set Case";
1649         ot->description = "Set font case";
1650         ot->idname = "FONT_OT_case_set";
1651
1652         /* api callbacks */
1653         ot->exec = set_case_exec;
1654         ot->poll = ED_operator_editfont;
1655
1656         /* flags */
1657         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1658
1659         /* properties */
1660         RNA_def_enum(ot->srna, "case", case_items, CASE_LOWER, "Case", "Lower or upper case");
1661 }
1662
1663 /********************** toggle case operator *********************/
1664
1665 static int toggle_case_exec(bContext *C, wmOperator *UNUSED(op))
1666 {
1667         Object *obedit = CTX_data_edit_object(C);
1668         Curve *cu = obedit->data;
1669         EditFont *ef = cu->editfont;
1670         wchar_t *str;
1671         int len, ccase = CASE_UPPER;
1672
1673         len = wcslen(ef->textbuf);
1674         str = ef->textbuf;
1675         while (len) {
1676                 if (*str >= 'a' && *str <= 'z') {
1677                         ccase = CASE_LOWER;
1678                         break;
1679                 }
1680
1681                 len--;
1682                 str++;
1683         }
1684
1685         return set_case(C, ccase);
1686 }
1687
1688 void FONT_OT_case_toggle(wmOperatorType *ot)
1689 {
1690         /* identifiers */
1691         ot->name = "Toggle Case";
1692         ot->description = "Toggle font case";
1693         ot->idname = "FONT_OT_case_toggle";
1694
1695         /* api callbacks */
1696         ot->exec = toggle_case_exec;
1697         ot->poll = ED_operator_editfont;
1698
1699         /* flags */
1700         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1701 }
1702
1703 /* **************** Open Font ************** */
1704
1705 static void font_ui_template_init(bContext *C, wmOperator *op)
1706 {
1707         PropertyPointerRNA *pprop;
1708
1709         op->customdata = pprop = MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
1710         UI_context_active_but_prop_get_templateID(C, &pprop->ptr, &pprop->prop);
1711 }
1712
1713 static void font_open_cancel(bContext *UNUSED(C), wmOperator *op)
1714 {
1715         MEM_freeN(op->customdata);
1716         op->customdata = NULL;
1717 }
1718
1719 static int font_open_exec(bContext *C, wmOperator *op)
1720 {
1721         struct Main *bmain = CTX_data_main(C);
1722         VFont *font;
1723         PropertyPointerRNA *pprop;
1724         PointerRNA idptr;
1725         char filepath[FILE_MAX];
1726         RNA_string_get(op->ptr, "filepath", filepath);
1727
1728         font = BKE_vfont_load(bmain, filepath);
1729
1730         if (!font) {
1731                 if (op->customdata) MEM_freeN(op->customdata);
1732                 return OPERATOR_CANCELLED;
1733         }
1734
1735         if (!op->customdata)
1736                 font_ui_template_init(C, op);
1737
1738         /* hook into UI */
1739         pprop = op->customdata;
1740
1741         if (pprop->prop) {
1742                 /* when creating new ID blocks, use is already 1, but RNA
1743                  * pointer use also increases user, so this compensates it */
1744                 id_us_min(&font->id);
1745
1746                 RNA_id_pointer_create(&font->id, &idptr);
1747                 RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
1748                 RNA_property_update(C, &pprop->ptr, pprop->prop);
1749         }
1750
1751         MEM_freeN(op->customdata);
1752
1753         return OPERATOR_FINISHED;
1754 }
1755
1756 static int open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
1757 {
1758         VFont *vfont = NULL;
1759         const char *path;
1760
1761         PointerRNA idptr;
1762         PropertyPointerRNA *pprop;
1763
1764         font_ui_template_init(C, op);
1765
1766         /* hook into UI */
1767         pprop = op->customdata;
1768
1769         if (pprop->prop) {
1770                 idptr = RNA_property_pointer_get((PointerRNA *)pprop, pprop->prop);
1771                 vfont = idptr.id.data;
1772         }
1773
1774         path = (vfont && !BKE_vfont_is_builtin(vfont)) ? vfont->name : U.fontdir;
1775
1776         if (RNA_struct_property_is_set(op->ptr, "filepath"))
1777                 return font_open_exec(C, op);
1778
1779         RNA_string_set(op->ptr, "filepath", path);
1780         WM_event_add_fileselect(C, op);
1781
1782         return OPERATOR_RUNNING_MODAL;
1783 }
1784
1785 void FONT_OT_open(wmOperatorType *ot)
1786 {
1787         /* identifiers */
1788         ot->name = "Open Font";
1789         ot->idname = "FONT_OT_open";
1790         ot->description = "Load a new font from a file";
1791
1792         /* api callbacks */
1793         ot->exec = font_open_exec;
1794         ot->invoke = open_invoke;
1795         ot->cancel = font_open_cancel;
1796
1797         /* flags */
1798         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1799
1800         /* properties */
1801         WM_operator_properties_filesel(
1802                 ot, FILE_TYPE_FOLDER | FILE_TYPE_FTFONT, FILE_SPECIAL, FILE_OPENFILE,
1803                 WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY, FILE_SORT_ALPHA);
1804 }
1805
1806 /******************* delete operator *********************/
1807
1808 static int font_unlink_exec(bContext *C, wmOperator *op)
1809 {
1810         VFont *builtin_font;
1811
1812         PointerRNA idptr;
1813         PropertyPointerRNA pprop;
1814
1815         UI_context_active_but_prop_get_templateID(C, &pprop.ptr, &pprop.prop);
1816
1817         if (pprop.prop == NULL) {
1818                 BKE_report(op->reports, RPT_ERROR, "Incorrect context for running font unlink");
1819                 return OPERATOR_CANCELLED;
1820         }
1821
1822         builtin_font = BKE_vfont_builtin_get();
1823
1824         RNA_id_pointer_create(&builtin_font->id, &idptr);
1825         RNA_property_pointer_set(&pprop.ptr, pprop.prop, idptr);
1826         RNA_property_update(C, &pprop.ptr, pprop.prop);
1827
1828         return OPERATOR_FINISHED;
1829 }
1830
1831 void FONT_OT_unlink(wmOperatorType *ot)
1832 {
1833         /* identifiers */
1834         ot->name = "Unlink";
1835         ot->idname = "FONT_OT_unlink";
1836         ot->description = "Unlink active font data-block";
1837
1838         /* api callbacks */
1839         ot->exec = font_unlink_exec;
1840 }
1841
1842 /**
1843  * TextBox selection
1844  */
1845 bool ED_curve_editfont_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle)
1846 {
1847         Object *obedit = CTX_data_edit_object(C);
1848         Curve *cu = obedit->data;
1849         ViewContext vc;
1850         /* bias against the active, in pixels, allows cycling */
1851         const float active_bias_px = 4.0f;
1852         const float mval_fl[2] = {UNPACK2(mval)};
1853         const int i_actbox = max_ii(0, cu->actbox - 1);
1854         int i_iter, actbox_select = -1;
1855         const float dist = ED_view3d_select_dist_px();
1856         float dist_sq_best = dist * dist;
1857
1858         ED_view3d_viewcontext_init(C, &vc);
1859
1860         ED_view3d_init_mats_rv3d(vc.obedit, vc.rv3d);
1861
1862         /* currently only select active */
1863         (void)extend;
1864         (void)deselect;
1865         (void)toggle;
1866
1867         for (i_iter = 0; i_iter < cu->totbox; i_iter++) {
1868                 int i = (i_iter + i_actbox) % cu->totbox;
1869                 float dist_sq_min;
1870                 int j, j_prev;
1871
1872                 float obedit_co[4][3];
1873                 float screen_co[4][2];
1874                 rctf rect;
1875                 int project_ok = 0;
1876
1877
1878                 BKE_curve_rect_from_textbox(cu, &cu->tb[i], &rect);
1879
1880                 copy_v3_fl3(obedit_co[0], rect.xmin, rect.ymin, 0.0f);
1881                 copy_v3_fl3(obedit_co[1], rect.xmin, rect.ymax, 0.0f);
1882                 copy_v3_fl3(obedit_co[2], rect.xmax, rect.ymax, 0.0f);
1883                 copy_v3_fl3(obedit_co[3], rect.xmax, rect.ymin, 0.0f);
1884
1885                 for (j = 0; j < 4; j++) {
1886                         if (ED_view3d_project_float_object(vc.ar, obedit_co[j], screen_co[j],
1887                                                            V3D_PROJ_TEST_CLIP_BB) == V3D_PROJ_RET_OK)
1888                         {
1889                                 project_ok |= (1 << j);
1890                         }
1891                 }
1892
1893                 dist_sq_min = dist_sq_best;
1894                 for (j = 0, j_prev = 3; j < 4; j_prev = j++) {
1895                         if ((project_ok & (1 << j)) &&
1896                             (project_ok & (1 << j_prev)))
1897                         {
1898                                 const float dist_test_sq = dist_squared_to_line_segment_v2(mval_fl, screen_co[j_prev], screen_co[j]);
1899                                 if (dist_sq_min > dist_test_sq) {
1900                                         dist_sq_min = dist_test_sq;
1901                                 }
1902                         }
1903                 }
1904
1905                 /* bias in pixels to cycle seletion */
1906                 if (i_iter == 0) {
1907                         dist_sq_min += active_bias_px;
1908                 }
1909
1910                 if (dist_sq_min < dist_sq_best) {
1911                         dist_sq_best = dist_sq_min;
1912                         actbox_select = i + 1;
1913                 }
1914         }
1915
1916         if (actbox_select != -1) {
1917                 if (cu->actbox != actbox_select) {
1918                         cu->actbox = actbox_select;
1919                         WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
1920                 }
1921                 return true;
1922         }
1923         else {
1924                 return false;
1925         }
1926 }