code cleanup: BKE_ naming, also make bpy.data.images.load() always load a new image...
[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                 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 static int set_style(bContext *C, const int style, const int clear)
590 {
591         Object *obedit= CTX_data_edit_object(C);
592         Curve *cu= obedit->data;
593         EditFont *ef= cu->editfont;
594         int i, selstart, selend;
595
596         if (!BKE_vfont_select_get(obedit, &selstart, &selend))
597                 return OPERATOR_CANCELLED;
598
599         for (i=selstart; i<=selend; i++) {
600                 if (clear)
601                         ef->textbufinfo[i].flag &= ~style;
602                 else
603                         ef->textbufinfo[i].flag |= style;
604         }
605
606         DAG_id_tag_update(obedit->data, 0);
607         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
608
609         return OPERATOR_FINISHED;
610 }
611
612 static int set_style_exec(bContext *C, wmOperator *op)
613 {
614         const int style= RNA_enum_get(op->ptr, "style");
615         const int clear= RNA_boolean_get(op->ptr, "clear");
616
617         return set_style(C, style, clear);
618 }
619
620 void FONT_OT_style_set(wmOperatorType *ot)
621 {
622         /* identifiers */
623         ot->name = "Set Style";
624         ot->description = "Set font style";
625         ot->idname = "FONT_OT_style_set";
626         
627         /* api callbacks */
628         ot->exec = set_style_exec;
629         ot->poll = ED_operator_editfont;
630         
631         /* flags */
632         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
633
634         /* properties */
635         RNA_def_enum(ot->srna, "style", style_items, CU_CHINFO_BOLD, "Style", "Style to set selection to");
636         RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear style rather than setting it");
637 }
638
639 /******************* toggle style operator ********************/
640
641 static int toggle_style_exec(bContext *C, wmOperator *op)
642 {
643         Object *obedit= CTX_data_edit_object(C);
644         Curve *cu= obedit->data;
645         int style, clear, selstart, selend;
646
647         if (!BKE_vfont_select_get(obedit, &selstart, &selend))
648                 return OPERATOR_CANCELLED;
649         
650         style= RNA_enum_get(op->ptr, "style");
651
652         cu->curinfo.flag ^= style;
653         clear= (cu->curinfo.flag & style) == 0;
654
655         return set_style(C, style, clear);
656 }
657
658 void FONT_OT_style_toggle(wmOperatorType *ot)
659 {
660         /* identifiers */
661         ot->name = "Toggle Style";
662         ot->description = "Toggle font style";
663         ot->idname = "FONT_OT_style_toggle";
664         
665         /* api callbacks */
666         ot->exec = toggle_style_exec;
667         ot->poll = ED_operator_editfont;
668         
669         /* flags */
670         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
671
672         /* properties */
673         RNA_def_enum(ot->srna, "style", style_items, CU_CHINFO_BOLD, "Style", "Style to set selection to");
674 }
675
676 /******************* copy text operator ********************/
677
678 static void copy_selection(Object *obedit)
679 {
680         int selstart, selend;
681         
682         if (BKE_vfont_select_get(obedit, &selstart, &selend)) {
683                 Curve *cu= obedit->data;
684                 EditFont *ef= cu->editfont;
685                 
686                 memcpy(ef->copybuf, ef->textbuf+selstart, ((selend-selstart)+1)*sizeof(wchar_t));
687                 ef->copybuf[(selend-selstart)+1]=0;
688                 memcpy(ef->copybufinfo, ef->textbufinfo+selstart, ((selend-selstart)+1)*sizeof(CharInfo));      
689         }
690 }
691
692 static int copy_text_exec(bContext *C, wmOperator *UNUSED(op))
693 {
694         Object *obedit= CTX_data_edit_object(C);
695
696         copy_selection(obedit);
697
698         return OPERATOR_FINISHED;
699 }
700
701 void FONT_OT_text_copy(wmOperatorType *ot)
702 {
703         /* identifiers */
704         ot->name = "Copy Text";
705         ot->description = "Copy selected text to clipboard";
706         ot->idname = "FONT_OT_text_copy";
707         
708         /* api callbacks */
709         ot->exec = copy_text_exec;
710         ot->poll = ED_operator_editfont;
711 }
712
713 /******************* cut text operator ********************/
714
715 static int cut_text_exec(bContext *C, wmOperator *UNUSED(op))
716 {
717         Scene *scene= CTX_data_scene(C);
718         Object *obedit= CTX_data_edit_object(C);
719         int selstart, selend;
720
721         if (!BKE_vfont_select_get(obedit, &selstart, &selend))
722                 return OPERATOR_CANCELLED;
723
724         copy_selection(obedit);
725         kill_selection(obedit, 0);
726
727         text_update_edited(C, scene, obedit, 1, FO_EDIT);
728
729         return OPERATOR_FINISHED;
730 }
731
732 void FONT_OT_text_cut(wmOperatorType *ot)
733 {
734         /* identifiers */
735         ot->name = "Cut Text";
736         ot->description = "Cut selected text to clipboard";
737         ot->idname = "FONT_OT_text_cut";
738         
739         /* api callbacks */
740         ot->exec = cut_text_exec;
741         ot->poll = ED_operator_editfont;
742
743         /* flags */
744         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
745 }
746
747 /******************* paste text operator ********************/
748
749 static int paste_selection(Object *obedit, ReportList *reports)
750 {
751         Curve *cu= obedit->data;
752         EditFont *ef= cu->editfont;
753         int len= wcslen(ef->copybuf);
754
755         // Verify that the copy buffer => [copy buffer len] + cu->len < MAXTEXT
756         if (cu->len + len <= MAXTEXT) {
757                 if (len) {      
758                         int size = (cu->len * sizeof(wchar_t)) - (cu->pos*sizeof(wchar_t)) + sizeof(wchar_t);
759                         memmove(ef->textbuf+cu->pos+len, ef->textbuf+cu->pos, size);
760                         memcpy(ef->textbuf+cu->pos, ef->copybuf, len * sizeof(wchar_t));
761                 
762                         memmove(ef->textbufinfo+cu->pos+len, ef->textbufinfo+cu->pos, (cu->len-cu->pos+1)*sizeof(CharInfo));
763                         memcpy(ef->textbufinfo+cu->pos, ef->copybufinfo, len*sizeof(CharInfo)); 
764                 
765                         cu->len += len;
766                         cu->pos += len;
767
768                         return 1;
769                 }
770         }
771         else
772                 BKE_report(reports, RPT_WARNING, "Text too long");
773         
774         return 0;
775 }
776
777 static int paste_text_exec(bContext *C, wmOperator *op)
778 {
779         Scene *scene= CTX_data_scene(C);
780         Object *obedit= CTX_data_edit_object(C);
781
782         if (!paste_selection(obedit, op->reports))
783                 return OPERATOR_CANCELLED;
784
785         text_update_edited(C, scene, obedit, 1, FO_EDIT);
786
787         return OPERATOR_FINISHED;
788 }
789
790 void FONT_OT_text_paste(wmOperatorType *ot)
791 {
792         /* identifiers */
793         ot->name = "Paste Text";
794         ot->description = "Paste text from clipboard";
795         ot->idname = "FONT_OT_text_paste";
796         
797         /* api callbacks */
798         ot->exec = paste_text_exec;
799         ot->poll = ED_operator_editfont;
800
801         /* flags */
802         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
803 }
804
805 /************************ move operator ************************/
806
807 static EnumPropertyItem move_type_items[]= {
808         {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
809         {LINE_END, "LINE_END", 0, "Line End", ""},
810         {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
811         {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
812         {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
813         {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
814         {PREV_LINE, "PREVIOUS_LINE", 0, "Previous Line", ""},
815         {NEXT_LINE, "NEXT_LINE", 0, "Next Line", ""},
816         {PREV_PAGE, "PREVIOUS_PAGE", 0, "Previous Page", ""},
817         {NEXT_PAGE, "NEXT_PAGE", 0, "Next Page", ""},
818         {0, NULL, 0, NULL, NULL}};
819
820 static int move_cursor(bContext *C, int type, int select)
821 {
822         Scene *scene= CTX_data_scene(C);
823         Object *obedit= CTX_data_edit_object(C);
824         Curve *cu= obedit->data;
825         EditFont *ef= cu->editfont;
826         int cursmove= -1;
827
828         switch (type) {
829                 case LINE_BEGIN:
830                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
831                         while (cu->pos>0) {
832                                 if (ef->textbuf[cu->pos-1]=='\n') break;
833                                 if (ef->textbufinfo[cu->pos-1].flag & CU_CHINFO_WRAP) break;                            
834                                 cu->pos--;
835                         }               
836                         cursmove=FO_CURS;
837                         break;
838                         
839                 case LINE_END:
840                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;               
841                         while (cu->pos<cu->len) {
842                                 if (ef->textbuf[cu->pos]==0) break;
843                                 if (ef->textbuf[cu->pos]=='\n') break;
844                                 if (ef->textbufinfo[cu->pos].flag & CU_CHINFO_WRAP ) break;
845                                 cu->pos++;
846                         }
847                         cursmove=FO_CURS;
848                         break;
849
850                 case PREV_WORD:
851                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
852                         cu->pos= prev_word(cu);
853                         cursmove= FO_CURS;
854                         break;
855
856                 case NEXT_WORD:
857                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
858                         cu->pos= next_word(cu);
859                         cursmove= FO_CURS;                              
860                         break;
861
862                 case PREV_CHAR:
863                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
864                         cu->pos--;
865                         cursmove=FO_CURS;
866                         break;
867
868                 case NEXT_CHAR: 
869                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
870                         cu->pos++;
871                         cursmove= FO_CURS;                              
872
873                         break;
874
875                 case PREV_LINE:
876                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
877                         cursmove=FO_CURSUP;
878                         break;
879                         
880                 case NEXT_LINE:
881                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
882                         cursmove= FO_CURSDOWN;
883                         break;
884
885                 case PREV_PAGE:
886                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
887                         cursmove=FO_PAGEUP;
888                         break;
889
890                 case NEXT_PAGE:
891                         if ((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
892                         cursmove=FO_PAGEDOWN;
893                         break;
894         }
895                 
896         if (cursmove == -1)
897                 return OPERATOR_CANCELLED;
898
899         if (select == 0) {
900                 if (cu->selstart) {
901                         struct Main *bmain= CTX_data_main(C);
902                         cu->selstart = cu->selend = 0;
903                         update_string(cu);
904                         BKE_vfont_to_curve(bmain, scene, obedit, FO_SELCHANGE);
905                 }
906         }
907
908         if (cu->pos>cu->len) cu->pos= cu->len;
909         else if (cu->pos>=MAXTEXT) cu->pos= MAXTEXT;
910         else if (cu->pos<0) cu->pos= 0;
911
912         text_update_edited(C, scene, obedit, select, cursmove);
913
914         if (select)
915                 cu->selend = cu->pos;
916
917         return OPERATOR_FINISHED;
918 }
919
920 static int move_exec(bContext *C, wmOperator *op)
921 {
922         int type= RNA_enum_get(op->ptr, "type");
923
924         return move_cursor(C, type, 0);
925 }
926
927 void FONT_OT_move(wmOperatorType *ot)
928 {
929         /* identifiers */
930         ot->name = "Move Cursor";
931         ot->description = "Move cursor to position type";
932         ot->idname = "FONT_OT_move";
933         
934         /* api callbacks */
935         ot->exec = move_exec;
936         ot->poll = ED_operator_editfont;
937
938         /* flags */
939         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
940
941         /* properties */
942         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to");
943 }
944
945 /******************* move select operator ********************/
946
947 static int move_select_exec(bContext *C, wmOperator *op)
948 {
949         int type= RNA_enum_get(op->ptr, "type");
950
951         return move_cursor(C, type, 1);
952 }
953
954 void FONT_OT_move_select(wmOperatorType *ot)
955 {
956         /* identifiers */
957         ot->name = "Move Select";
958         ot->description = "Make selection from current cursor position to new cursor position type";
959         ot->idname = "FONT_OT_move_select";
960         
961         /* api callbacks */
962         ot->exec = move_select_exec;
963         ot->poll = ED_operator_editfont;
964
965         /* flags */
966         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
967
968         /* properties */
969         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection");
970 }
971
972 /************************* change spacing **********************/
973
974 static int change_spacing_exec(bContext *C, wmOperator *op)
975 {
976         Scene *scene= CTX_data_scene(C);
977         Object *obedit= CTX_data_edit_object(C);
978         Curve *cu= obedit->data;
979         EditFont *ef= cu->editfont;
980         int kern, delta= RNA_int_get(op->ptr, "delta");
981
982         kern = ef->textbufinfo[cu->pos-1].kern;
983         kern += delta;
984         CLAMP(kern, -20, 20);
985
986         if (ef->textbufinfo[cu->pos-1].kern == kern)
987                 return OPERATOR_CANCELLED;
988
989         ef->textbufinfo[cu->pos-1].kern = kern;
990
991         text_update_edited(C, scene, obedit, 1, FO_EDIT);
992
993         return OPERATOR_FINISHED;
994 }
995
996 void FONT_OT_change_spacing(wmOperatorType *ot)
997 {
998         /* identifiers */
999         ot->name = "Change Spacing";
1000         ot->description = "Change font spacing";
1001         ot->idname = "FONT_OT_change_spacing";
1002         
1003         /* api callbacks */
1004         ot->exec = change_spacing_exec;
1005         ot->poll = ED_operator_editfont;
1006
1007         /* flags */
1008         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1009
1010         /* properties */
1011         RNA_def_int(ot->srna, "delta", 1, -20, 20, "Delta", "Amount to decrease or increase character spacing with", -20, 20);
1012 }
1013
1014 /************************* change character **********************/
1015
1016 static int change_character_exec(bContext *C, wmOperator *op)
1017 {
1018         Scene *scene= CTX_data_scene(C);
1019         Object *obedit= CTX_data_edit_object(C);
1020         Curve *cu= obedit->data;
1021         EditFont *ef= cu->editfont;
1022         int character, delta= RNA_int_get(op->ptr, "delta");
1023
1024         if (cu->pos <= 0)
1025                 return OPERATOR_CANCELLED;
1026
1027         character= ef->textbuf[cu->pos - 1];
1028         character += delta;
1029         CLAMP(character, 0, 255);
1030
1031         if (character == ef->textbuf[cu->pos - 1])
1032                 return OPERATOR_CANCELLED;
1033
1034         ef->textbuf[cu->pos - 1]= character;
1035
1036         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1037
1038         return OPERATOR_FINISHED;
1039 }
1040
1041 void FONT_OT_change_character(wmOperatorType *ot)
1042 {
1043         /* identifiers */
1044         ot->name = "Change Character";
1045         ot->description = "Change font character code";
1046         ot->idname = "FONT_OT_change_character";
1047         
1048         /* api callbacks */
1049         ot->exec = change_character_exec;
1050         ot->poll = ED_operator_editfont;
1051
1052         /* flags */
1053         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1054
1055         /* properties */
1056         RNA_def_int(ot->srna, "delta", 1, -255, 255, "Delta", "Number to increase or decrease character code with", -255, 255);
1057 }
1058
1059 /******************* line break operator ********************/
1060
1061 static int line_break_exec(bContext *C, wmOperator *op)
1062 {
1063         Scene *scene= CTX_data_scene(C);
1064         Object *obedit= CTX_data_edit_object(C);
1065         Curve *cu= obedit->data;
1066         EditFont *ef= cu->editfont;
1067         const int ctrl= RNA_boolean_get(op->ptr, "ctrl");
1068
1069         if (ctrl) {
1070                 insert_into_textbuf(obedit, 1);
1071                 if (ef->textbuf[cu->pos]!='\n')
1072                         insert_into_textbuf(obedit, '\n');
1073         }
1074         else
1075                 insert_into_textbuf(obedit, '\n');
1076
1077         cu->selstart = cu->selend = 0;
1078
1079         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1080
1081         return OPERATOR_FINISHED;
1082 }
1083
1084 void FONT_OT_line_break(wmOperatorType *ot)
1085 {
1086         /* identifiers */
1087         ot->name = "Line Break";
1088         ot->description = "Insert line break at cursor position";
1089         ot->idname = "FONT_OT_line_break";
1090         
1091         /* api callbacks */
1092         ot->exec = line_break_exec;
1093         ot->poll = ED_operator_editfont;
1094
1095         /* flags */
1096         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1097
1098         /* properties */
1099         RNA_def_boolean(ot->srna, "ctrl", 0, "Ctrl", ""); // XXX what is this?
1100 }
1101
1102 /******************* delete operator **********************/
1103
1104 static EnumPropertyItem delete_type_items[]= {
1105         {DEL_ALL, "ALL", 0, "All", ""},
1106         {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
1107         {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
1108         {DEL_SELECTION, "SELECTION", 0, "Selection", ""},
1109         {DEL_NEXT_SEL, "NEXT_OR_SELECTION", 0, "Next or Selection", ""},
1110         {DEL_PREV_SEL, "PREVIOUS_OR_SELECTION", 0, "Previous or Selection", ""},
1111         {0, NULL, 0, NULL, NULL}};
1112
1113 static int delete_exec(bContext *C, wmOperator *op)
1114 {
1115         Scene *scene= CTX_data_scene(C);
1116         Object *obedit= CTX_data_edit_object(C);
1117         Curve *cu= obedit->data;
1118         EditFont *ef= cu->editfont;
1119         int x, selstart, selend, type= RNA_enum_get(op->ptr, "type");
1120
1121         if (cu->len == 0)
1122                 return OPERATOR_CANCELLED;
1123
1124         if (BKE_vfont_select_get(obedit, &selstart, &selend)) {
1125                 if (type == DEL_NEXT_SEL) type= DEL_SELECTION;
1126                 else if (type == DEL_PREV_SEL) type= DEL_SELECTION;
1127         }
1128         else {
1129                 if (type == DEL_NEXT_SEL) type= DEL_NEXT_CHAR;
1130                 else if (type == DEL_PREV_SEL) type= DEL_PREV_CHAR;
1131         }
1132
1133         switch (type) {
1134                 case DEL_ALL:
1135                         cu->len = cu->pos = 0;
1136                         ef->textbuf[0]= 0;
1137                         break;
1138                 case DEL_SELECTION:
1139                         if (!kill_selection(obedit, 0))
1140                                 return OPERATOR_CANCELLED;
1141                         break;
1142                 case DEL_PREV_CHAR:
1143                         if (cu->pos<=0)
1144                                 return OPERATOR_CANCELLED;
1145
1146                         for (x=cu->pos;x<=cu->len;x++)
1147                                 ef->textbuf[x-1]= ef->textbuf[x];
1148                         for (x=cu->pos;x<=cu->len;x++)
1149                                 ef->textbufinfo[x-1]= ef->textbufinfo[x];                                       
1150
1151                         cu->pos--;
1152                         ef->textbuf[--cu->len]='\0';
1153                         break;
1154                 case DEL_NEXT_CHAR:
1155                         if (cu->pos>=cu->len)
1156                                 return OPERATOR_CANCELLED;
1157
1158                         for (x=cu->pos;x<cu->len;x++)
1159                                 ef->textbuf[x]= ef->textbuf[x+1];
1160                         for (x=cu->pos;x<cu->len;x++)
1161                                 ef->textbufinfo[x]= ef->textbufinfo[x+1];                                       
1162
1163                         ef->textbuf[--cu->len]='\0';
1164                         break;
1165                 default:
1166                         return OPERATOR_CANCELLED;
1167         }
1168
1169         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1170
1171         return OPERATOR_FINISHED;
1172 }
1173
1174 void FONT_OT_delete(wmOperatorType *ot)
1175 {
1176         /* identifiers */
1177         ot->name = "Delete";
1178         ot->description = "Delete text by cursor position";
1179         ot->idname = "FONT_OT_delete";
1180         
1181         /* api callbacks */
1182         ot->exec = delete_exec;
1183         ot->poll = ED_operator_editfont;
1184
1185         /* flags */
1186         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1187
1188         /* properties */
1189         RNA_def_enum(ot->srna, "type", delete_type_items, DEL_ALL, "Type", "Which part of the text to delete");
1190 }
1191
1192 /*********************** insert text operator *************************/
1193
1194 static int insert_text_exec(bContext *C, wmOperator *op)
1195 {
1196         Scene *scene= CTX_data_scene(C);
1197         Object *obedit= CTX_data_edit_object(C);
1198         char *inserted_utf8;
1199         wchar_t *inserted_text;
1200         int a, len;
1201
1202         if (!RNA_struct_property_is_set(op->ptr, "text"))
1203                 return OPERATOR_CANCELLED;
1204         
1205         inserted_utf8= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
1206         len= BLI_strlen_utf8(inserted_utf8);
1207
1208         inserted_text= MEM_callocN(sizeof(wchar_t)*(len+1), "FONT_insert_text");
1209         BLI_strncpy_wchar_from_utf8(inserted_text, inserted_utf8, len+1);
1210
1211         for (a=0; a<len; a++)
1212                 insert_into_textbuf(obedit, inserted_text[a]);
1213
1214         MEM_freeN(inserted_text);
1215         MEM_freeN(inserted_utf8);
1216
1217         kill_selection(obedit, 1);
1218         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1219
1220         return OPERATOR_FINISHED;
1221 }
1222
1223 static int insert_text_invoke(bContext *C, wmOperator *op, wmEvent *evt)
1224 {
1225         Scene *scene= CTX_data_scene(C);
1226         Object *obedit= CTX_data_edit_object(C);
1227         Curve *cu= obedit->data;
1228         EditFont *ef= cu->editfont;
1229         static int accentcode= 0;
1230         uintptr_t ascii = evt->ascii;
1231         int alt= evt->alt, shift= evt->shift, ctrl= evt->ctrl;
1232         int event= evt->type, val= evt->val;
1233         wchar_t inserted_text[2]= {0};
1234
1235         if (RNA_struct_property_is_set(op->ptr, "text"))
1236                 return insert_text_exec(C, op);
1237
1238         if (RNA_struct_property_is_set(op->ptr, "accent")) {
1239                 if (cu->len!=0 && cu->pos>0)
1240                         accentcode= 1;
1241                 return OPERATOR_FINISHED;
1242         }
1243         
1244         /* tab should exit editmode, but we allow it to be typed using modifier keys */
1245         if (event==TABKEY) {
1246                 if ((alt||ctrl||shift) == 0)
1247                         return OPERATOR_PASS_THROUGH;
1248                 else
1249                         ascii= 9;
1250         }
1251         else if (event==BACKSPACEKEY)
1252                 ascii= 0;
1253
1254         if (val && (ascii || evt->utf8_buf[0])) {
1255                 /* handle case like TAB (== 9) */
1256                 if (     (ascii > 31 && ascii < 254 && ascii != 127) ||
1257                         (ascii==13) ||
1258                         (ascii==10) ||
1259                         (ascii==8)  ||
1260                         (evt->utf8_buf[0]))
1261                 {
1262
1263                         if (evt->utf8_buf[0]) {
1264                                 BLI_strncpy_wchar_from_utf8(inserted_text, evt->utf8_buf, 1);
1265                                 ascii= inserted_text[0];
1266                                 insert_into_textbuf(obedit, ascii);
1267                                 accentcode= 0;
1268                         }
1269                         else if (accentcode) {
1270                                 if (cu->pos>0) {
1271                                         inserted_text[0]= findaccent(ef->textbuf[cu->pos-1], ascii);
1272                                         ef->textbuf[cu->pos-1]= inserted_text[0];
1273                                 }
1274                                 accentcode= 0;
1275                         }
1276                         else if (cu->len<MAXTEXT-1) {
1277                                 if (alt) {
1278                                         /* might become obsolete, apple has default values for this, other OS's too? */
1279                                         if (ascii=='t') ascii= 137;
1280                                         else if (ascii=='c') ascii= 169;
1281                                         else if (ascii=='f') ascii= 164;
1282                                         else if (ascii=='g') ascii= 176;
1283                                         else if (ascii=='l') ascii= 163;
1284                                         else if (ascii=='r') ascii= 174;
1285                                         else if (ascii=='s') ascii= 223;
1286                                         else if (ascii=='y') ascii= 165;
1287                                         else if (ascii=='.') ascii= 138;
1288                                         else if (ascii=='1') ascii= 185;
1289                                         else if (ascii=='2') ascii= 178;
1290                                         else if (ascii=='3') ascii= 179;
1291                                         else if (ascii=='%') ascii= 139;
1292                                         else if (ascii=='?') ascii= 191;
1293                                         else if (ascii=='!') ascii= 161;
1294                                         else if (ascii=='x') ascii= 215;
1295                                         else if (ascii=='>') ascii= 187;
1296                                         else if (ascii=='<') ascii= 171;
1297                                 }
1298
1299                                 inserted_text[0]= ascii;
1300                                 insert_into_textbuf(obedit, ascii);
1301                         }
1302                         
1303                         kill_selection(obedit, 1);
1304                         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1305                 }
1306                 else {
1307                         inserted_text[0]= ascii;
1308                         insert_into_textbuf(obedit, ascii);
1309                         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1310                 }
1311         }
1312         else if (val && event == BACKSPACEKEY) {
1313                 if (alt && cu->len!=0 && cu->pos>0)
1314                         accentcode= 1;
1315
1316                 return OPERATOR_PASS_THROUGH;
1317         }
1318         else
1319                 return OPERATOR_PASS_THROUGH;
1320
1321         if (inserted_text[0]) {
1322                 /* store as utf8 in RNA string */
1323                 char inserted_utf8[8] = {0};
1324
1325                 BLI_strncpy_wchar_as_utf8(inserted_utf8, inserted_text, sizeof(inserted_utf8));
1326                 RNA_string_set(op->ptr, "text", inserted_utf8);
1327         }
1328
1329         /* reset property? */
1330         accentcode= 0;
1331         
1332         return OPERATOR_FINISHED;
1333 }
1334
1335 void FONT_OT_text_insert(wmOperatorType *ot)
1336 {
1337         /* identifiers */
1338         ot->name = "Insert Text";
1339         ot->description = "Insert text at cursor position";
1340         ot->idname = "FONT_OT_text_insert";
1341         
1342         /* api callbacks */
1343         ot->exec = insert_text_exec;
1344         ot->invoke = insert_text_invoke;
1345         ot->poll = ED_operator_editfont;
1346         
1347         /* flags */
1348         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1349
1350         /* properties */
1351         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position");
1352         RNA_def_boolean(ot->srna, "accent", 0, "Accent mode", "Next typed character will strike through previous, for special character input");
1353 }
1354
1355
1356 /*********************** textbox add operator *************************/
1357 static int textbox_add_exec(bContext *C, wmOperator *UNUSED(op))
1358 {
1359         Object *obedit= CTX_data_active_object(C);
1360         Curve *cu= obedit->data;
1361         int i;
1362         
1363         if (cu->totbox < 256) {
1364                 for (i = cu->totbox; i>cu->actbox; i--) cu->tb[i]= cu->tb[i-1];
1365                 cu->tb[cu->actbox]= cu->tb[cu->actbox-1];
1366                 cu->actbox++;
1367                 cu->totbox++;
1368         }
1369         
1370         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1371         return OPERATOR_FINISHED;
1372 }
1373
1374 void FONT_OT_textbox_add(wmOperatorType *ot)
1375 {
1376         /* identifiers */
1377         ot->name = "Add Textbox";
1378         ot->description = "Add a new text box";
1379         ot->idname = "FONT_OT_textbox_add";
1380         
1381         /* api callbacks */
1382         ot->exec = textbox_add_exec;
1383         ot->poll = ED_operator_object_active_editable_font;
1384
1385         /* flags */
1386         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1387         
1388         
1389 }
1390
1391
1392
1393 /*********************** textbox remove operator *************************/
1394
1395
1396
1397 static int textbox_remove_exec(bContext *C, wmOperator *op)
1398 {
1399         Object *obedit= CTX_data_active_object(C);
1400         Curve *cu= obedit->data;
1401         int i;
1402         int index = RNA_int_get(op->ptr, "index");
1403         
1404         
1405         if (cu->totbox > 1) {
1406                 for (i = index; i < cu->totbox; i++) cu->tb[i]= cu->tb[i+1];
1407                 cu->totbox--;
1408                 if (cu->actbox >= index)
1409                         cu->actbox--;
1410         }
1411         
1412         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
1413         
1414         return OPERATOR_FINISHED;
1415 }
1416
1417 void FONT_OT_textbox_remove(wmOperatorType *ot)
1418 {
1419         /* identifiers */
1420         ot->name = "Remove Textbox";
1421         ot->description = "Remove the textbox";
1422         ot->idname = "FONT_OT_textbox_remove";
1423         
1424         /* api callbacks */
1425         ot->exec = textbox_remove_exec;
1426         ot->poll = ED_operator_object_active_editable_font;
1427         
1428         /* flags */
1429         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1430         
1431         RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Index", "The current text box", 0, INT_MAX);
1432 }
1433
1434
1435
1436 /***************** editmode enter/exit ********************/
1437
1438 void make_editText(Object *obedit)
1439 {
1440         Curve *cu= obedit->data;
1441         EditFont *ef= cu->editfont;
1442         
1443         if (ef==NULL) {
1444                 ef= cu->editfont= MEM_callocN(sizeof(EditFont), "editfont");
1445         
1446                 ef->textbuf= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "texteditbuf");
1447                 ef->textbufinfo= MEM_callocN((MAXTEXT+4)*sizeof(CharInfo), "texteditbufinfo");
1448                 ef->copybuf= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "texteditcopybuf");
1449                 ef->copybufinfo= MEM_callocN((MAXTEXT+4)*sizeof(CharInfo), "texteditcopybufinfo");      
1450                 ef->oldstr= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "oldstrbuf");
1451                 ef->oldstrinfo= MEM_callocN((MAXTEXT+4)*sizeof(CharInfo), "oldstrbuf");
1452         }
1453         
1454         // Convert the original text to wchar_t
1455         BLI_strncpy_wchar_from_utf8(ef->textbuf, cu->str, MAXTEXT+4); /* length is bogus */
1456         wcscpy(ef->oldstr, ef->textbuf);
1457                 
1458         cu->len= wcslen(ef->textbuf);
1459         
1460         memcpy(ef->textbufinfo, cu->strinfo, (cu->len)*sizeof(CharInfo));
1461         memcpy(ef->oldstrinfo, cu->strinfo, (cu->len)*sizeof(CharInfo));
1462
1463         if (cu->pos>cu->len) cu->pos= cu->len;
1464
1465         if (cu->pos)
1466                 cu->curinfo = ef->textbufinfo[cu->pos-1];
1467         else
1468                 cu->curinfo = ef->textbufinfo[0];
1469         
1470         // Convert to UTF-8
1471         update_string(cu);
1472 }
1473
1474 void load_editText(Object *obedit)
1475 {
1476         Curve *cu= obedit->data;
1477         EditFont *ef= cu->editfont;
1478         
1479         MEM_freeN(ef->oldstr);
1480         ef->oldstr= NULL;
1481         MEM_freeN(ef->oldstrinfo);
1482         ef->oldstrinfo= NULL;
1483         
1484         update_string(cu);
1485         
1486         if (cu->strinfo)
1487                 MEM_freeN(cu->strinfo);
1488         cu->strinfo= MEM_callocN((cu->len+4)*sizeof(CharInfo), "texteditinfo");
1489         memcpy(cu->strinfo, ef->textbufinfo, (cu->len)*sizeof(CharInfo));
1490
1491         cu->len= strlen(cu->str);
1492         
1493         /* this memory system is weak... */
1494         
1495         if (cu->selboxes) {
1496                 MEM_freeN(cu->selboxes);
1497                 cu->selboxes= NULL;
1498         }
1499 }
1500
1501 void free_editText(Object *obedit)
1502 {
1503         BKE_curve_editfont_free((Curve *)obedit->data);
1504 }
1505
1506 /********************** set case operator *********************/
1507
1508 static EnumPropertyItem case_items[]= {
1509         {CASE_LOWER, "LOWER", 0, "Lower", ""},
1510         {CASE_UPPER, "UPPER", 0, "Upper", ""},
1511         {0, NULL, 0, NULL, NULL}};
1512
1513 static int set_case(bContext *C, int ccase)
1514 {
1515         Scene *scene= CTX_data_scene(C);
1516         Object *obedit= CTX_data_edit_object(C);
1517         Curve *cu= obedit->data;
1518         EditFont *ef= cu->editfont;
1519         wchar_t *str;
1520         int len;
1521         
1522         len= wcslen(ef->textbuf);
1523         str= ef->textbuf;
1524         while (len) {
1525                 if (*str>='a' && *str<='z')
1526                         *str-= 32;
1527                 len--;
1528                 str++;
1529         }
1530         
1531         if (ccase == CASE_LOWER) {
1532                 len= wcslen(ef->textbuf);
1533                 str= ef->textbuf;
1534                 while (len) {
1535                         if (*str>='A' && *str<='Z') {
1536                                 *str+= 32;
1537                         }
1538                         len--;
1539                         str++;
1540                 }
1541         }
1542
1543         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1544
1545         return OPERATOR_FINISHED;
1546 }
1547
1548 static int set_case_exec(bContext *C, wmOperator *op)
1549 {
1550         return set_case(C, RNA_enum_get(op->ptr, "case"));
1551 }
1552
1553 void FONT_OT_case_set(wmOperatorType *ot)
1554 {
1555         /* identifiers */
1556         ot->name = "Set Case";
1557         ot->description = "Set font case";
1558         ot->idname = "FONT_OT_case_set";
1559         
1560         /* api callbacks */
1561         ot->exec = set_case_exec;
1562         ot->poll = ED_operator_editfont;
1563
1564         /* flags */
1565         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1566
1567         /* properties */
1568         RNA_def_enum(ot->srna, "case", case_items, CASE_LOWER, "Case", "Lower or upper case");
1569 }
1570
1571 /********************** toggle case operator *********************/
1572
1573 static int toggle_case_exec(bContext *C, wmOperator *UNUSED(op))
1574 {
1575         Object *obedit= CTX_data_edit_object(C);
1576         Curve *cu= obedit->data;
1577         EditFont *ef= cu->editfont;
1578         wchar_t *str;
1579         int len, ccase= CASE_UPPER;
1580         
1581         len= wcslen(ef->textbuf);
1582         str= ef->textbuf;
1583         while (len) {
1584                 if (*str>='a' && *str<='z') {
1585                         ccase= CASE_LOWER;
1586                         break;
1587                 }
1588
1589                 len--;
1590                 str++;
1591         }
1592         
1593         return set_case(C, ccase);
1594 }
1595
1596 void FONT_OT_case_toggle(wmOperatorType *ot)
1597 {
1598         /* identifiers */
1599         ot->name = "Toggle Case";
1600         ot->description = "Toggle font case";
1601         ot->idname = "FONT_OT_case_toggle";
1602         
1603         /* api callbacks */
1604         ot->exec = toggle_case_exec;
1605         ot->poll = ED_operator_editfont;
1606
1607         /* flags */
1608         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1609 }
1610
1611 /* **************** Open Font ************** */
1612
1613 static void font_ui_template_init(bContext *C, wmOperator *op)
1614 {
1615         PropertyPointerRNA *pprop;
1616         
1617         op->customdata= pprop= MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
1618         uiIDContextProperty(C, &pprop->ptr, &pprop->prop);
1619 }
1620
1621 static int font_open_cancel(bContext *UNUSED(C), wmOperator *op)
1622 {
1623         MEM_freeN(op->customdata);
1624         op->customdata= NULL;
1625         return OPERATOR_CANCELLED;
1626 }
1627
1628 static int font_open_exec(bContext *C, wmOperator *op)
1629 {
1630         struct Main *bmain= CTX_data_main(C);
1631         VFont *font;
1632         PropertyPointerRNA *pprop;
1633         PointerRNA idptr;
1634         char filepath[FILE_MAX];
1635         RNA_string_get(op->ptr, "filepath", filepath);
1636
1637         font= BKE_vfont_load(bmain, filepath);
1638
1639         if (!font) {
1640                 if (op->customdata) MEM_freeN(op->customdata);
1641                 return OPERATOR_CANCELLED;
1642         }
1643
1644         if (!op->customdata)
1645                 font_ui_template_init(C, op);
1646         
1647         /* hook into UI */
1648         pprop= op->customdata;
1649
1650         if (pprop->prop) {
1651                 /* when creating new ID blocks, use is already 1, but RNA
1652                  * pointer se also increases user, so this compensates it */
1653                 font->id.us--;
1654         
1655                 RNA_id_pointer_create(&font->id, &idptr);
1656                 RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
1657                 RNA_property_update(C, &pprop->ptr, pprop->prop);
1658         }
1659
1660         MEM_freeN(op->customdata);
1661
1662         return OPERATOR_FINISHED;
1663 }
1664
1665 static int open_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1666 {
1667         VFont *font=NULL;
1668         char *path;
1669
1670         PointerRNA idptr;
1671         PropertyPointerRNA *pprop;
1672
1673         font_ui_template_init(C, op);
1674
1675         /* hook into UI */
1676         pprop= op->customdata;
1677
1678         if (pprop->prop) {
1679                 idptr= RNA_property_pointer_get((PointerRNA *)pprop, pprop->prop);
1680                 font= idptr.id.data;
1681         }
1682
1683         path = (font && strcmp(font->name, FO_BUILTIN_NAME) != 0)? font->name: U.fontdir;
1684
1685         if (RNA_struct_property_is_set(op->ptr, "filepath"))
1686                 return font_open_exec(C, op);
1687
1688         RNA_string_set(op->ptr, "filepath", path);
1689         WM_event_add_fileselect(C, op); 
1690
1691         return OPERATOR_RUNNING_MODAL;
1692 }
1693
1694 void FONT_OT_open(wmOperatorType *ot)
1695 {
1696         /* identifiers */
1697         ot->name = "Open Font";
1698         ot->idname = "FONT_OT_open";
1699         ot->description = "Load a new font from a file";
1700         
1701         /* api callbacks */
1702         ot->exec = font_open_exec;
1703         ot->invoke = open_invoke;
1704         ot->cancel = font_open_cancel;
1705         
1706         /* flags */
1707         ot->flag = OPTYPE_REGISTER|OPTYPE_UNDO;
1708         
1709         /* properties */
1710         WM_operator_properties_filesel(ot, FOLDERFILE|FTFONTFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH|WM_FILESEL_RELPATH, FILE_DEFAULTDISPLAY);
1711 }
1712
1713 /******************* delete operator *********************/
1714
1715 static int font_unlink_exec(bContext *C, wmOperator *op)
1716 {
1717         VFont *builtin_font;
1718
1719         PointerRNA idptr;
1720         PropertyPointerRNA pprop;
1721
1722         uiIDContextProperty(C, &pprop.ptr, &pprop.prop);
1723         
1724         if (pprop.prop==NULL) {
1725                 BKE_report(op->reports, RPT_ERROR, "Incorrect context for running font unlink");
1726                 return OPERATOR_CANCELLED;
1727         }
1728
1729         builtin_font = BKE_vfont_builtin_get();
1730
1731         RNA_id_pointer_create(&builtin_font->id, &idptr);
1732         RNA_property_pointer_set(&pprop.ptr, pprop.prop, idptr);
1733         RNA_property_update(C, &pprop.ptr, pprop.prop);
1734
1735         return OPERATOR_FINISHED;
1736 }
1737
1738 void FONT_OT_unlink(wmOperatorType *ot)
1739 {
1740         /* identifiers */
1741         ot->name = "Unlink";
1742         ot->idname = "FONT_OT_unlink";
1743         ot->description = "Unlink active font data block";
1744         
1745         /* api callbacks */
1746         ot->exec = font_unlink_exec;
1747 }
1748
1749
1750 /* **************** undo for font object ************** */
1751
1752 static void undoFont_to_editFont(void *strv, void *ecu, void *UNUSED(obdata))
1753 {
1754         Curve *cu= (Curve *)ecu;
1755         EditFont *ef= cu->editfont;
1756         char *str= strv;
1757
1758         cu->pos= *((short *)str);
1759         cu->len= *((short *)(str+2));
1760
1761         memcpy(ef->textbuf, str+4, (cu->len+1)*sizeof(wchar_t));
1762         memcpy(ef->textbufinfo, str+4 + (cu->len+1)*sizeof(wchar_t), cu->len*sizeof(CharInfo));
1763         
1764         cu->selstart = cu->selend = 0;
1765         
1766         update_string(cu);
1767 }
1768
1769 static void *editFont_to_undoFont(void *ecu, void *UNUSED(obdata))
1770 {
1771         Curve *cu= (Curve *)ecu;
1772         EditFont *ef= cu->editfont;
1773         char *str;
1774         
1775         // The undo buffer includes [MAXTEXT+6]=actual string and [MAXTEXT+4]*sizeof(CharInfo)=charinfo
1776         str= MEM_callocN((MAXTEXT+6)*sizeof(wchar_t) + (MAXTEXT+4)*sizeof(CharInfo), "string undo");
1777
1778         // Copy the string and string information
1779         memcpy(str+4, ef->textbuf, (cu->len+1)*sizeof(wchar_t));
1780         memcpy(str+4 + (cu->len+1)*sizeof(wchar_t), ef->textbufinfo, cu->len*sizeof(CharInfo));
1781
1782         *((short *)str)= cu->pos;
1783         *((short *)(str+2))= cu->len;   
1784         
1785         return str;
1786 }
1787
1788 static void free_undoFont(void *strv)
1789 {
1790         MEM_freeN(strv);
1791 }
1792
1793 static void *get_undoFont(bContext *C)
1794 {
1795         Object *obedit= CTX_data_edit_object(C);
1796         if (obedit && obedit->type==OB_FONT) {
1797                 return obedit->data;
1798         }
1799         return NULL;
1800 }
1801
1802 /* and this is all the undo system needs to know */
1803 void undo_push_font(bContext *C, const char *name)
1804 {
1805         undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
1806 }