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