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