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