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