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