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