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