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