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