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