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