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