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