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