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