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