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