split BKE_utildefines.h, now it only has blender specific defines like GS() MAKE_ID...
[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_object.h"
66 #include "ED_screen.h"
67 #include "ED_util.h"
68
69 #include "UI_interface.h"
70
71 #include "curve_intern.h"
72
73 #define MAXTEXT 32766
74
75 /************************* utilities ******************************/
76
77 static char findaccent(char char1, unsigned int code)
78 {
79         char new= 0;
80         
81         if(char1=='a') {
82                 if(code=='`') new= 224;
83                 else if(code==39) new= 225;
84                 else if(code=='^') new= 226;
85                 else if(code=='~') new= 227;
86                 else if(code=='"') new= 228;
87                 else if(code=='o') new= 229;
88                 else if(code=='e') new= 230;
89                 else if(code=='-') new= 170;
90         }
91         else if(char1=='c') {
92                 if(code==',') new= 231;
93                 if(code=='|') new= 162;
94         }
95         else if(char1=='e') {
96                 if(code=='`') new= 232;
97                 else if(code==39) new= 233;
98                 else if(code=='^') new= 234;
99                 else if(code=='"') new= 235;
100         }
101         else if(char1=='i') {
102                 if(code=='`') new= 236;
103                 else if(code==39) new= 237;
104                 else if(code=='^') new= 238;
105                 else if(code=='"') new= 239;
106         }
107         else if(char1=='n') {
108                 if(code=='~') new= 241;
109         }
110         else if(char1=='o') {
111                 if(code=='`') new= 242;
112                 else if(code==39) new= 243;
113                 else if(code=='^') new= 244;
114                 else if(code=='~') new= 245;
115                 else if(code=='"') new= 246;
116                 else if(code=='/') new= 248;
117                 else if(code=='-') new= 186;
118                 else if(code=='e') new= 143;
119         }
120         else if(char1=='s') {
121                 if(code=='s') new= 167;
122         }
123         else if(char1=='u') {
124                 if(code=='`') new= 249;
125                 else if(code==39) new= 250;
126                 else if(code=='^') new= 251;
127                 else if(code=='"') new= 252;
128         }
129         else if(char1=='y') {
130                 if(code==39) new= 253;
131                 else if(code=='"') new= 255;
132         }
133         else if(char1=='A') {
134                 if(code=='`') new= 192;
135                 else if(code==39) new= 193;
136                 else if(code=='^') new= 194;
137                 else if(code=='~') new= 195;
138                 else if(code=='"') new= 196;
139                 else if(code=='o') new= 197;
140                 else if(code=='e') new= 198;
141         }
142         else if(char1=='C') {
143                 if(code==',') new= 199;
144         }
145         else if(char1=='E') {
146                 if(code=='`') new= 200;
147                 else if(code==39) new= 201;
148                 else if(code=='^') new= 202;
149                 else if(code=='"') new= 203;
150         }
151         else if(char1=='I') {
152                 if(code=='`') new= 204;
153                 else if(code==39) new= 205;
154                 else if(code=='^') new= 206;
155                 else if(code=='"') new= 207;
156         }
157         else if(char1=='N') {
158                 if(code=='~') new= 209;
159         }
160         else if(char1=='O') {
161                 if(code=='`') new= 210;
162                 else if(code==39) new= 211;
163                 else if(code=='^') new= 212;
164                 else if(code=='~') new= 213;
165                 else if(code=='"') new= 214;
166                 else if(code=='/') new= 216;
167                 else if(code=='e') new= 141;
168         }
169         else if(char1=='U') {
170                 if(code=='`') new= 217;
171                 else if(code==39) new= 218;
172                 else if(code=='^') new= 219;
173                 else if(code=='"') new= 220;
174         }
175         else if(char1=='Y') {
176                 if(code==39) new= 221;
177         }
178         else if(char1=='1') {
179                 if(code=='4') new= 188;
180                 if(code=='2') new= 189;
181         }
182         else if(char1=='3') {
183                 if(code=='4') new= 190;
184         }
185         else if(char1==':') {
186                 if(code=='-') new= 247;
187         }
188         else if(char1=='-') {
189                 if(code==':') new= 247;
190                 if(code=='|') new= 135;
191                 if(code=='+') new= 177;
192         }
193         else if(char1=='|') {
194                 if(code=='-') new= 135;
195                 if(code=='=') new= 136;
196         }
197         else if(char1=='=') {
198                 if(code=='|') new= 136;
199         }
200         else if(char1=='+') {
201                 if(code=='-') new= 177;
202         }
203         
204         if(new) return new;
205         else return char1;
206 }
207
208
209 void update_string(Curve *cu)
210 {
211         EditFont *ef= cu->editfont;
212         int len;
213
214         // Free the old curve string    
215         MEM_freeN(cu->str);
216
217         // Calculate the actual string length in UTF-8 variable characters
218         len = wcsleninu8(ef->textbuf);
219
220         // Alloc memory for UTF-8 variable char length string
221         cu->str = MEM_callocN(len + sizeof(wchar_t), "str");
222
223         // Copy the wchar to UTF-8
224         wcs2utf8s(cu->str, ef->textbuf);
225 }
226
227 static int insert_into_textbuf(Object *obedit, uintptr_t c)
228 {
229         Curve *cu= obedit->data;
230         
231         if(cu->len<MAXTEXT-1) {
232                 EditFont *ef= cu->editfont;
233                 int x;
234
235                 for(x= cu->len; x>cu->pos; x--) ef->textbuf[x]= ef->textbuf[x-1];
236                 for(x= cu->len; x>cu->pos; x--) ef->textbufinfo[x]= ef->textbufinfo[x-1];               
237                 ef->textbuf[cu->pos]= c;
238                 ef->textbufinfo[cu->pos] = cu->curinfo;
239                 ef->textbufinfo[cu->pos].kern = 0;
240                 ef->textbufinfo[cu->pos].mat_nr = obedit->actcol;
241                                         
242                 cu->pos++;
243                 cu->len++;
244                 ef->textbuf[cu->len]='\0';
245
246                 update_string(cu);
247
248                 return 1;
249         }
250         else
251                 return 0;
252 }
253
254 static void text_update_edited(bContext *C, Scene *scene, Object *obedit, int recalc, int mode)
255 {
256         Curve *cu= obedit->data;
257         EditFont *ef= cu->editfont;
258         cu->curinfo = ef->textbufinfo[cu->pos?cu->pos-1:0];
259         
260         if(obedit->totcol>0)
261                 obedit->actcol= ef->textbufinfo[cu->pos?cu->pos-1:0].mat_nr;
262
263         if(mode == FO_EDIT)
264                 update_string(cu);
265
266         BKE_text_to_curve(scene, obedit, mode);
267
268         if(recalc)
269                 DAG_id_tag_update(obedit->data, 0);
270         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
271 }
272
273 /********************** insert lorem operator *********************/
274
275 static int insert_lorem_exec(bContext *C, wmOperator *UNUSED(op))
276 {
277         Object *obedit= CTX_data_edit_object(C);
278         const char *p, *p2;
279         int i;
280         static const char *lastlorem;
281         
282         if(lastlorem)
283                 p= lastlorem;
284         else
285                 p= ED_lorem;
286         
287         i= rand()/(RAND_MAX/6)+4;       
288                 
289         for(p2=p; *p2 && i; p2++) {
290                 insert_into_textbuf(obedit, *p2);
291
292                 if(*p2=='.')
293                         i--;
294         }
295
296         lastlorem = p2+1;
297         if(strlen(lastlorem)<5)
298                 lastlorem = ED_lorem;
299         
300         insert_into_textbuf(obedit, '\n');
301         insert_into_textbuf(obedit, '\n');      
302
303         DAG_id_tag_update(obedit->data, 0);
304         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
305
306         return OPERATOR_FINISHED;
307 }
308
309 void FONT_OT_insert_lorem(wmOperatorType *ot)
310 {
311         /* identifiers */
312         ot->name= "Insert Lorem";
313         ot->description= "Insert placeholder text";
314         ot->idname= "FONT_OT_insert_lorem";
315         
316         /* api callbacks */
317         ot->exec= insert_lorem_exec;
318         ot->poll= ED_operator_editfont;
319         
320         /* flags */
321         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
322 }
323
324 /******************* paste file operator ********************/
325
326 /* note this handles both ascii and utf8 unicode, previously
327  * there were 3 functions that did effectively the same thing. */
328
329 static int paste_file(bContext *C, ReportList *reports, const char *filename)
330 {
331         Scene *scene= CTX_data_scene(C);
332         Object *obedit= CTX_data_edit_object(C);
333         Curve *cu= obedit->data;
334         EditFont *ef= cu->editfont;
335         FILE *fp;
336         int filelen;
337         char *strp;
338
339         fp= fopen(filename, "r");
340
341         if(!fp) {
342                 if(reports)
343                         BKE_reportf(reports, RPT_ERROR, "Failed to open file %s.", filename);
344                 return OPERATOR_CANCELLED;
345         }
346
347         fseek(fp, 0L, SEEK_END);
348         filelen = ftell(fp);
349         fseek(fp, 0L, SEEK_SET);
350
351         strp= MEM_callocN(filelen+4, "tempstr");
352
353         // fread() instead of read(), because windows read() converts text
354         // to DOS \r\n linebreaks, causing double linebreaks in the 3d text
355         filelen = fread(strp, 1, filelen, fp);
356         fclose(fp);
357         strp[filelen]= 0;
358
359         if(cu->len+filelen<MAXTEXT) {
360                 int tmplen;
361                 wchar_t *mem = MEM_callocN((sizeof(wchar_t)*filelen)+(4*sizeof(wchar_t)), "temporary");
362                 tmplen = utf8towchar(mem, strp);
363                 wcscat(ef->textbuf, mem);
364                 MEM_freeN(mem);
365                 cu->len += tmplen;
366                 cu->pos= cu->len;
367         }
368         MEM_freeN(strp);
369
370         text_update_edited(C, scene, obedit, 1, FO_EDIT);
371
372         return OPERATOR_FINISHED;
373 }
374
375 static int paste_file_exec(bContext *C, wmOperator *op)
376 {
377         char *path;
378         int retval;
379         
380         path= RNA_string_get_alloc(op->ptr, "filepath", NULL, 0);
381         retval= paste_file(C, op->reports, path);
382         MEM_freeN(path);
383
384         return retval;
385 }
386
387 static int paste_file_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
388 {
389         if(RNA_property_is_set(op->ptr, "filepath"))
390                 return paste_file_exec(C, op);
391
392         WM_event_add_fileselect(C, op); 
393
394         return OPERATOR_RUNNING_MODAL;
395 }
396
397 void FONT_OT_file_paste(wmOperatorType *ot)
398 {
399         /* identifiers */
400         ot->name= "Paste File";
401         ot->description= "Paste contents from file";
402         ot->idname= "FONT_OT_file_paste";
403         
404         /* api callbacks */
405         ot->exec= paste_file_exec;
406         ot->invoke= paste_file_invoke;
407         ot->poll= ED_operator_editfont;
408         
409         /* flags */
410         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
411
412         /* properties */
413         WM_operator_properties_filesel(ot, FOLDERFILE|TEXTFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH);
414 }
415
416 /******************* paste buffer operator ********************/
417
418 static int paste_buffer_exec(bContext *C, wmOperator *UNUSED(op))
419 {
420         const char *filename;
421
422 #ifdef WIN32
423         filename= "C:\\windows\\temp\\cutbuf.txt";
424
425 //      The following is more likely to work on all Win32 installations.
426 //      suggested by Douglas Toltzman. Needs windows include files...
427 /*
428         char tempFileName[MAX_PATH];
429         DWORD pathlen;
430         static const char cutbufname[]="cutbuf.txt";
431
432         if((pathlen=GetTempPath(sizeof(tempFileName),tempFileName)) > 0 &&
433                 pathlen + sizeof(cutbufname) <= sizeof(tempFileName))
434         {
435                 strcat(tempFileName,cutbufname);
436                 filename= tempFilename;
437         }
438 */
439 #else
440         filename= "/tmp/.cutbuffer";
441 #endif
442
443         return paste_file(C, NULL, filename);
444 }
445
446 void FONT_OT_buffer_paste(wmOperatorType *ot)
447 {
448         /* identifiers */
449         ot->name= "Paste Buffer";
450         ot->description= "Paste text from OS buffer";
451         ot->idname= "FONT_OT_buffer_paste";
452         
453         /* api callbacks */
454         ot->exec= paste_buffer_exec;
455         ot->poll= ED_operator_editfont;
456         
457         /* flags */
458         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
459 }
460
461 /******************* text to object operator ********************/
462
463 static void txt_add_object(bContext *C, TextLine *firstline, int totline, float offset[3])
464 {
465         Scene *scene= CTX_data_scene(C);
466         Curve *cu;
467         Object *obedit;
468         Base *base;
469         struct TextLine *tmp;
470         int nchars = 0, a;
471         float rot[3] = {0.f, 0.f, 0.f};
472         
473         obedit= add_object(scene, OB_FONT);
474         base= scene->basact;
475
476         
477         ED_object_base_init_transform(C, base, NULL, rot); /* seems to assume view align ? TODO - look into this, could be an operator option */
478         where_is_object(scene, obedit);
479
480         obedit->loc[0] += offset[0];
481         obedit->loc[1] += offset[1];
482         obedit->loc[2] += offset[2];
483
484         cu= obedit->data;
485         cu->vfont= get_builtin_font();
486         cu->vfont->id.us++;
487
488         for(tmp=firstline, a=0; cu->len<MAXTEXT && a<totline; tmp=tmp->next, a++)
489                 nchars += strlen(tmp->line) + 1;
490
491         if(cu->str) MEM_freeN(cu->str);
492         if(cu->strinfo) MEM_freeN(cu->strinfo); 
493
494         cu->str= MEM_callocN(nchars+4, "str");
495         cu->strinfo= MEM_callocN((nchars+4)*sizeof(CharInfo), "strinfo");
496
497         cu->str[0]= '\0';
498         cu->len= 0;
499         cu->pos= 0;
500         
501         for(tmp=firstline, a=0; cu->len<MAXTEXT && a<totline; tmp=tmp->next, a++) {
502                 strcat(cu->str, tmp->line);
503                 cu->len+= strlen(tmp->line);
504
505                 if(tmp->next) {
506                         strcat(cu->str, "\n");
507                         cu->len++;
508                 }
509
510                 cu->pos= cu->len;
511         }
512
513         WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, obedit);
514 }
515
516 void ED_text_to_object(bContext *C, Text *text, int split_lines)
517 {
518         RegionView3D *rv3d= CTX_wm_region_view3d(C);
519         TextLine *line;
520         float offset[3];
521         int linenum= 0;
522
523         if(!text || !text->lines.first) return;
524
525         if(split_lines) {
526                 for(line=text->lines.first; line; line=line->next) {
527                         /* skip lines with no text, but still make space for them */
528                         if(line->line[0] == '\0') {
529                                 linenum++;
530                                 continue;
531                         }
532         
533                         /* do the translation */
534                         offset[0] = 0;
535                         offset[1] = -linenum;
536                         offset[2] = 0;
537         
538                         if(rv3d)
539                                 mul_mat3_m4_v3(rv3d->viewinv, offset);
540
541                         txt_add_object(C, line, 1, offset);
542
543                         linenum++;
544                 }
545         }
546         else {
547                 offset[0]= 0.0f;
548                 offset[1]= 0.0f;
549                 offset[2]= 0.0f;
550
551                 txt_add_object(C, text->lines.first, BLI_countlist(&text->lines), offset);
552         }
553 }
554
555 /********************** utilities ***************************/
556
557 static short next_word(Curve *cu)
558 {
559         short s;
560         for(s=cu->pos; (cu->str[s]) && (cu->str[s]!=' ') && (cu->str[s]!='\n') &&
561                                         (cu->str[s]!=1) && (cu->str[s]!='\r'); s++);
562         if(cu->str[s]) return(s+1); else return(s);
563 }
564
565 static short prev_word(Curve *cu)
566 {
567         short s;
568         
569         if(cu->pos==0) return(0);
570         for(s=cu->pos-2; (cu->str[s]) && (cu->str[s]!=' ') && (cu->str[s]!='\n') &&
571                                         (cu->str[s]!=1) && (cu->str[s]!='\r'); s--);
572         if(cu->str[s]) return(s+1); else return(s);
573 }
574
575 static int kill_selection(Object *obedit, int ins)      /* 1 == new character */
576 {
577         Curve *cu= obedit->data;
578         EditFont *ef= cu->editfont;
579         int selend, selstart, direction;
580         int offset = 0;
581         int getfrom;
582
583         direction = BKE_font_getselection(obedit, &selstart, &selend);
584         if(direction) {
585                 int size;
586                 if(ins) offset = 1;
587                 if(cu->pos >= selstart) cu->pos = selstart+offset;
588                 if((direction == -1) && ins) {
589                         selstart++;
590                         selend++;
591                 }
592                 getfrom = selend+offset;
593                 if(ins==0) getfrom++;
594                 size = (cu->len * sizeof(wchar_t)) - (selstart * sizeof(wchar_t)) + (offset*sizeof(wchar_t));
595                 memmove(ef->textbuf+selstart, ef->textbuf+getfrom, size);
596                 memmove(ef->textbufinfo+selstart, ef->textbufinfo+getfrom, ((cu->len-selstart)+offset)*sizeof(CharInfo));
597                 cu->len -= (selend-selstart)+offset;
598                 cu->selstart = cu->selend = 0;
599         }
600
601         return(direction);
602 }
603
604 /******************* set style operator ********************/
605
606 static EnumPropertyItem style_items[]= {
607         {CU_CHINFO_BOLD, "BOLD", 0, "Bold", ""},
608         {CU_CHINFO_ITALIC, "ITALIC", 0, "Italic", ""},
609         {CU_CHINFO_UNDERLINE, "UNDERLINE", 0, "Underline", ""},
610         {CU_CHINFO_SMALLCAPS, "SMALL_CAPS", 0, "Small Caps", ""},
611         {0, NULL, 0, NULL, NULL}};
612
613 static int set_style(bContext *C, int style, int clear)
614 {
615         Object *obedit= CTX_data_edit_object(C);
616         Curve *cu= obedit->data;
617         EditFont *ef= cu->editfont;
618         int i, selstart, selend;
619
620         if(!BKE_font_getselection(obedit, &selstart, &selend))
621                 return OPERATOR_CANCELLED;
622
623         for(i=selstart; i<=selend; i++) {
624                 if(clear)
625                         ef->textbufinfo[i].flag &= ~style;
626                 else
627                         ef->textbufinfo[i].flag |= style;
628         }
629
630         DAG_id_tag_update(obedit->data, 0);
631         WM_event_add_notifier(C, NC_GEOM|ND_DATA, obedit->data);
632
633         return OPERATOR_FINISHED;
634 }
635
636 static int set_style_exec(bContext *C, wmOperator *op)
637 {
638         int style, clear;
639
640         style= RNA_enum_get(op->ptr, "style");
641         clear= RNA_enum_get(op->ptr, "clear");
642
643         return set_style(C, style, clear);
644 }
645
646 void FONT_OT_style_set(wmOperatorType *ot)
647 {
648         /* identifiers */
649         ot->name= "Set Style";
650         ot->description= "Set font style";
651         ot->idname= "FONT_OT_style_set";
652         
653         /* api callbacks */
654         ot->exec= set_style_exec;
655         ot->poll= ED_operator_editfont;
656         
657         /* flags */
658         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
659
660         /* properties */
661         RNA_def_enum(ot->srna, "style", style_items, CU_CHINFO_BOLD, "Style", "Style to set selection to.");
662         RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear style rather than setting it.");
663 }
664
665 /******************* toggle style operator ********************/
666
667 static int toggle_style_exec(bContext *C, wmOperator *op)
668 {
669         Object *obedit= CTX_data_edit_object(C);
670         Curve *cu= obedit->data;
671         int style, clear, selstart, selend;
672
673         if(!BKE_font_getselection(obedit, &selstart, &selend))
674                 return OPERATOR_CANCELLED;
675         
676         style= RNA_enum_get(op->ptr, "style");
677
678         cu->curinfo.flag ^= style;
679         clear= (cu->curinfo.flag & style) == 0;
680
681         return set_style(C, style, clear);
682 }
683
684 void FONT_OT_style_toggle(wmOperatorType *ot)
685 {
686         /* identifiers */
687         ot->name= "Toggle Style";
688         ot->description= "Toggle font style";
689         ot->idname= "FONT_OT_style_toggle";
690         
691         /* api callbacks */
692         ot->exec= toggle_style_exec;
693         ot->poll= ED_operator_editfont;
694         
695         /* flags */
696         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
697
698         /* properties */
699         RNA_def_enum(ot->srna, "style", style_items, CU_CHINFO_BOLD, "Style", "Style to set selection to.");
700 }
701
702 /******************* copy text operator ********************/
703
704 static void copy_selection(Object *obedit)
705 {
706         int selstart, selend;
707         
708         if(BKE_font_getselection(obedit, &selstart, &selend)) {
709                 Curve *cu= obedit->data;
710                 EditFont *ef= cu->editfont;
711                 
712                 memcpy(ef->copybuf, ef->textbuf+selstart, ((selend-selstart)+1)*sizeof(wchar_t));
713                 ef->copybuf[(selend-selstart)+1]=0;
714                 memcpy(ef->copybufinfo, ef->textbufinfo+selstart, ((selend-selstart)+1)*sizeof(CharInfo));      
715         }
716 }
717
718 static int copy_text_exec(bContext *C, wmOperator *UNUSED(op))
719 {
720         Object *obedit= CTX_data_edit_object(C);
721
722         copy_selection(obedit);
723
724         return OPERATOR_FINISHED;
725 }
726
727 void FONT_OT_text_copy(wmOperatorType *ot)
728 {
729         /* identifiers */
730         ot->name= "Copy Text";
731         ot->description= "Copy selected text to clipboard";
732         ot->idname= "FONT_OT_text_copy";
733         
734         /* api callbacks */
735         ot->exec= copy_text_exec;
736         ot->poll= ED_operator_editfont;
737 }
738
739 /******************* cut text operator ********************/
740
741 static int cut_text_exec(bContext *C, wmOperator *UNUSED(op))
742 {
743         Scene *scene= CTX_data_scene(C);
744         Object *obedit= CTX_data_edit_object(C);
745         int selstart, selend;
746
747         if(!BKE_font_getselection(obedit, &selstart, &selend))
748                 return OPERATOR_CANCELLED;
749
750         copy_selection(obedit);
751         kill_selection(obedit, 0);
752
753         text_update_edited(C, scene, obedit, 1, FO_EDIT);
754
755         return OPERATOR_FINISHED;
756 }
757
758 void FONT_OT_text_cut(wmOperatorType *ot)
759 {
760         /* identifiers */
761         ot->name= "Cut Text";
762         ot->description= "Cut selected text to clipboard";
763         ot->idname= "FONT_OT_text_cut";
764         
765         /* api callbacks */
766         ot->exec= cut_text_exec;
767         ot->poll= ED_operator_editfont;
768
769         /* flags */
770         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
771 }
772
773 /******************* paste text operator ********************/
774
775 static int paste_selection(Object *obedit, ReportList *reports)
776 {
777         Curve *cu= obedit->data;
778         EditFont *ef= cu->editfont;
779         int len= wcslen(ef->copybuf);
780
781         // Verify that the copy buffer => [copy buffer len] + cu->len < MAXTEXT
782         if(cu->len + len <= MAXTEXT) {
783                 if(len) {       
784                         int size = (cu->len * sizeof(wchar_t)) - (cu->pos*sizeof(wchar_t)) + sizeof(wchar_t);
785                         memmove(ef->textbuf+cu->pos+len, ef->textbuf+cu->pos, size);
786                         memcpy(ef->textbuf+cu->pos, ef->copybuf, len * sizeof(wchar_t));
787                 
788                         memmove(ef->textbufinfo+cu->pos+len, ef->textbufinfo+cu->pos, (cu->len-cu->pos+1)*sizeof(CharInfo));
789                         memcpy(ef->textbufinfo+cu->pos, ef->copybufinfo, len*sizeof(CharInfo)); 
790                 
791                         cu->len += len;
792                         cu->pos += len;
793
794                         return 1;
795                 }
796         }
797         else
798                 BKE_report(reports, RPT_WARNING, "Text too long.");
799         
800         return 0;
801 }
802
803 static int paste_text_exec(bContext *C, wmOperator *op)
804 {
805         Scene *scene= CTX_data_scene(C);
806         Object *obedit= CTX_data_edit_object(C);
807
808         if(!paste_selection(obedit, op->reports))
809                 return OPERATOR_CANCELLED;
810
811         text_update_edited(C, scene, obedit, 1, FO_EDIT);
812
813         return OPERATOR_FINISHED;
814 }
815
816 void FONT_OT_text_paste(wmOperatorType *ot)
817 {
818         /* identifiers */
819         ot->name= "Paste Text";
820         ot->description= "Paste text from clipboard";
821         ot->idname= "FONT_OT_text_paste";
822         
823         /* api callbacks */
824         ot->exec= paste_text_exec;
825         ot->poll= ED_operator_editfont;
826
827         /* flags */
828         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
829 }
830
831 /************************ move operator ************************/
832
833 static EnumPropertyItem move_type_items[]= {
834         {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
835         {LINE_END, "LINE_END", 0, "Line End", ""},
836         {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
837         {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
838         {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
839         {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
840         {PREV_LINE, "PREVIOUS_LINE", 0, "Previous Line", ""},
841         {NEXT_LINE, "NEXT_LINE", 0, "Next Line", ""},
842         {PREV_PAGE, "PREVIOUS_PAGE", 0, "Previous Page", ""},
843         {NEXT_PAGE, "NEXT_PAGE", 0, "Next Page", ""},
844         {0, NULL, 0, NULL, NULL}};
845
846 static int move_cursor(bContext *C, int type, int select)
847 {
848         Scene *scene= CTX_data_scene(C);
849         Object *obedit= CTX_data_edit_object(C);
850         Curve *cu= obedit->data;
851         EditFont *ef= cu->editfont;
852         int cursmove= -1;
853
854         switch(type) {
855                 case LINE_BEGIN:
856                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
857                         while(cu->pos>0) {
858                                 if(ef->textbuf[cu->pos-1]=='\n') break;
859                                 if(ef->textbufinfo[cu->pos-1].flag & CU_CHINFO_WRAP) break;                             
860                                 cu->pos--;
861                         }               
862                         cursmove=FO_CURS;
863                         break;
864                         
865                 case LINE_END:
866                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;                
867                         while(cu->pos<cu->len) {
868                                 if(ef->textbuf[cu->pos]==0) break;
869                                 if(ef->textbuf[cu->pos]=='\n') break;
870                                 if(ef->textbufinfo[cu->pos].flag & CU_CHINFO_WRAP ) break;
871                                 cu->pos++;
872                         }
873                         cursmove=FO_CURS;
874                         break;
875
876                 case PREV_WORD:
877                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
878                         cu->pos= prev_word(cu);
879                         cursmove= FO_CURS;
880                         break;
881
882                 case NEXT_WORD:
883                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
884                         cu->pos= next_word(cu);
885                         cursmove= FO_CURS;                              
886                         break;
887
888                 case PREV_CHAR:
889                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
890                         cu->pos--;
891                         cursmove=FO_CURS;
892                         break;
893
894                 case NEXT_CHAR: 
895                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
896                         cu->pos++;
897                         cursmove= FO_CURS;                              
898
899                         break;
900
901                 case PREV_LINE:
902                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
903                         cursmove=FO_CURSUP;
904                         break;
905                         
906                 case NEXT_LINE:
907                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
908                         cursmove= FO_CURSDOWN;
909                         break;
910
911                 case PREV_PAGE:
912                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
913                         cursmove=FO_PAGEUP;
914                         break;
915
916                 case NEXT_PAGE:
917                         if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
918                         cursmove=FO_PAGEDOWN;
919                         break;
920         }
921                 
922         if(cursmove == -1)
923                 return OPERATOR_CANCELLED;
924
925         if(select == 0) {
926                 if(cu->selstart) {
927                         cu->selstart = cu->selend = 0;
928                         update_string(cu);
929                         BKE_text_to_curve(scene, obedit, FO_SELCHANGE);
930                 }
931         }
932
933         if(cu->pos>cu->len) cu->pos= cu->len;
934         else if(cu->pos>=MAXTEXT) cu->pos= MAXTEXT;
935         else if(cu->pos<0) cu->pos= 0;
936
937         text_update_edited(C, scene, obedit, select, cursmove);
938
939         if(select)
940                 cu->selend = cu->pos;
941
942         return OPERATOR_FINISHED;
943 }
944
945 static int move_exec(bContext *C, wmOperator *op)
946 {
947         int type= RNA_enum_get(op->ptr, "type");
948
949         return move_cursor(C, type, 0);
950 }
951
952 void FONT_OT_move(wmOperatorType *ot)
953 {
954         /* identifiers */
955         ot->name= "Move Cursor";
956         ot->description= "Move cursor to position type";
957         ot->idname= "FONT_OT_move";
958         
959         /* api callbacks */
960         ot->exec= move_exec;
961         ot->poll= ED_operator_editfont;
962
963         /* flags */
964         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
965
966         /* properties */
967         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
968 }
969
970 /******************* move select operator ********************/
971
972 static int move_select_exec(bContext *C, wmOperator *op)
973 {
974         int type= RNA_enum_get(op->ptr, "type");
975
976         return move_cursor(C, type, 1);
977 }
978
979 void FONT_OT_move_select(wmOperatorType *ot)
980 {
981         /* identifiers */
982         ot->name= "Move Select";
983         ot->description= "Make selection from current cursor position to new cursor position type";
984         ot->idname= "FONT_OT_move_select";
985         
986         /* api callbacks */
987         ot->exec= move_select_exec;
988         ot->poll= ED_operator_editfont;
989
990         /* flags */
991         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
992
993         /* properties */
994         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection.");
995 }
996
997 /************************* change spacing **********************/
998
999 static int change_spacing_exec(bContext *C, wmOperator *op)
1000 {
1001         Scene *scene= CTX_data_scene(C);
1002         Object *obedit= CTX_data_edit_object(C);
1003         Curve *cu= obedit->data;
1004         EditFont *ef= cu->editfont;
1005         int kern, delta= RNA_int_get(op->ptr, "delta");
1006
1007         kern = ef->textbufinfo[cu->pos-1].kern;
1008         kern += delta;
1009         CLAMP(kern, -20, 20);
1010
1011         if(ef->textbufinfo[cu->pos-1].kern == kern)
1012                 return OPERATOR_CANCELLED;
1013
1014         ef->textbufinfo[cu->pos-1].kern = kern;
1015
1016         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1017
1018         return OPERATOR_FINISHED;
1019 }
1020
1021 void FONT_OT_change_spacing(wmOperatorType *ot)
1022 {
1023         /* identifiers */
1024         ot->name= "Change Spacing";
1025         ot->description= "Change font spacing";
1026         ot->idname= "FONT_OT_change_spacing";
1027         
1028         /* api callbacks */
1029         ot->exec= change_spacing_exec;
1030         ot->poll= ED_operator_editfont;
1031
1032         /* flags */
1033         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1034
1035         /* properties */
1036         RNA_def_int(ot->srna, "delta", 1, -20, 20, "Delta", "Amount to decrease or increasing character spacing with.", -20, 20);
1037 }
1038
1039 /************************* change character **********************/
1040
1041 static int change_character_exec(bContext *C, wmOperator *op)
1042 {
1043         Scene *scene= CTX_data_scene(C);
1044         Object *obedit= CTX_data_edit_object(C);
1045         Curve *cu= obedit->data;
1046         EditFont *ef= cu->editfont;
1047         int character, delta= RNA_int_get(op->ptr, "delta");
1048
1049         if(cu->pos <= 0)
1050                 return OPERATOR_CANCELLED;
1051
1052         character= ef->textbuf[cu->pos - 1];
1053         character += delta;
1054         CLAMP(character, 0, 255);
1055
1056         if(character == ef->textbuf[cu->pos - 1])
1057                 return OPERATOR_CANCELLED;
1058
1059         ef->textbuf[cu->pos - 1]= character;
1060
1061         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1062
1063         return OPERATOR_FINISHED;
1064 }
1065
1066 void FONT_OT_change_character(wmOperatorType *ot)
1067 {
1068         /* identifiers */
1069         ot->name= "Change Character";
1070         ot->description= "Change font character code";
1071         ot->idname= "FONT_OT_change_character";
1072         
1073         /* api callbacks */
1074         ot->exec= change_character_exec;
1075         ot->poll= ED_operator_editfont;
1076
1077         /* flags */
1078         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1079
1080         /* properties */
1081         RNA_def_int(ot->srna, "delta", 1, -255, 255, "Delta", "Number to increase or decrease character code with.", -255, 255);
1082 }
1083
1084 /******************* line break operator ********************/
1085
1086 static int line_break_exec(bContext *C, wmOperator *op)
1087 {
1088         Scene *scene= CTX_data_scene(C);
1089         Object *obedit= CTX_data_edit_object(C);
1090         Curve *cu= obedit->data;
1091         EditFont *ef= cu->editfont;
1092         int ctrl= RNA_enum_get(op->ptr, "ctrl");
1093
1094         if(ctrl) {
1095                 insert_into_textbuf(obedit, 1);
1096                 if(ef->textbuf[cu->pos]!='\n')
1097                         insert_into_textbuf(obedit, '\n');
1098         }
1099         else
1100                 insert_into_textbuf(obedit, '\n');
1101
1102         cu->selstart = cu->selend = 0;
1103
1104         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1105
1106         return OPERATOR_FINISHED;
1107 }
1108
1109 void FONT_OT_line_break(wmOperatorType *ot)
1110 {
1111         /* identifiers */
1112         ot->name= "Line Break";
1113         ot->description= "Insert line break at cursor position";
1114         ot->idname= "FONT_OT_line_break";
1115         
1116         /* api callbacks */
1117         ot->exec= line_break_exec;
1118         ot->poll= ED_operator_editfont;
1119
1120         /* flags */
1121         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1122
1123         /* properties */
1124         RNA_def_boolean(ot->srna, "ctrl", 0, "Ctrl", ""); // XXX what is this?
1125 }
1126
1127 /******************* delete operator **********************/
1128
1129 static EnumPropertyItem delete_type_items[]= {
1130         {DEL_ALL, "ALL", 0, "All", ""},
1131         {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
1132         {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
1133         {DEL_SELECTION, "SELECTION", 0, "Selection", ""},
1134         {DEL_NEXT_SEL, "NEXT_OR_SELECTION", 0, "Next or Selection", ""},
1135         {DEL_PREV_SEL, "PREVIOUS_OR_SELECTION", 0, "Previous or Selection", ""},
1136         {0, NULL, 0, NULL, NULL}};
1137
1138 static int delete_exec(bContext *C, wmOperator *op)
1139 {
1140         Scene *scene= CTX_data_scene(C);
1141         Object *obedit= CTX_data_edit_object(C);
1142         Curve *cu= obedit->data;
1143         EditFont *ef= cu->editfont;
1144         int x, selstart, selend, type= RNA_enum_get(op->ptr, "type");
1145
1146         if(cu->len == 0)
1147                 return OPERATOR_CANCELLED;
1148
1149         if(BKE_font_getselection(obedit, &selstart, &selend)) {
1150                 if(type == DEL_NEXT_SEL) type= DEL_SELECTION;
1151                 else if(type == DEL_PREV_SEL) type= DEL_SELECTION;
1152         }
1153         else {
1154                 if(type == DEL_NEXT_SEL) type= DEL_NEXT_CHAR;
1155                 else if(type == DEL_PREV_SEL) type= DEL_PREV_CHAR;
1156         }
1157
1158         switch(type) {
1159                 case DEL_ALL:
1160                         cu->len = cu->pos = 0;
1161                         ef->textbuf[0]= 0;
1162                         break;
1163                 case DEL_SELECTION:
1164                         if(!kill_selection(obedit, 0))
1165                                 return OPERATOR_CANCELLED;
1166                         break;
1167                 case DEL_PREV_CHAR:
1168                         if(cu->pos<=0)
1169                                 return OPERATOR_CANCELLED;
1170
1171                         for(x=cu->pos;x<=cu->len;x++)
1172                                 ef->textbuf[x-1]= ef->textbuf[x];
1173                         for(x=cu->pos;x<=cu->len;x++)
1174                                 ef->textbufinfo[x-1]= ef->textbufinfo[x];                                       
1175
1176                         cu->pos--;
1177                         ef->textbuf[--cu->len]='\0';
1178                         break;
1179                 case DEL_NEXT_CHAR:
1180                         if(cu->pos>=cu->len)
1181                                 return OPERATOR_CANCELLED;
1182
1183                         for(x=cu->pos;x<cu->len;x++)
1184                                 ef->textbuf[x]= ef->textbuf[x+1];
1185                         for(x=cu->pos;x<cu->len;x++)
1186                                 ef->textbufinfo[x]= ef->textbufinfo[x+1];                                       
1187
1188                         ef->textbuf[--cu->len]='\0';
1189                         break;
1190                 default:
1191                         return OPERATOR_CANCELLED;
1192         }
1193
1194         text_update_edited(C, scene, obedit, 1, FO_EDIT);
1195
1196         return OPERATOR_FINISHED;
1197 }
1198
1199 void FONT_OT_delete(wmOperatorType *ot)
1200 {
1201         /* identifiers */
1202         ot->name= "Delete";
1203         ot->description= "Delete text by cursor position";
1204         ot->idname= "FONT_OT_delete";
1205         
1206         /* api callbacks */
1207         ot->exec= delete_exec;
1208         ot->poll= ED_operator_editfont;
1209
1210         /* flags */
1211         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
1212
1213         /* properties */
1214         RNA_def_enum(ot->srna, "type", delete_type_items, DEL_ALL, "Type", "Which part of the text to delete.");
1215 }
1216
1217 /*********************** insert text operator *************************/
1218
1219 static int insert_text_exec(bContext *C, wmOperator *op)
1220 {
1221         Scene *scene= CTX_data_scene(C);
1222         Object *obedit= CTX_data_edit_object(C);
1223         char *inserted_utf8;
1224         wchar_t *inserted_text, first;
1225         int a, len;
1226
1227         if(!RNA_property_is_set(op->ptr, "text"))
1228                 return OPERATOR_CANCELLED;
1229         
1230         inserted_utf8= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
1231         len= strlen(inserted_utf8);
1232
1233         inserted_text= MEM_callocN(sizeof(wchar_t)*(len+1), "FONT_insert_text");
1234         utf8towchar(inserted_text, inserted_utf8);
1235         first= inserted_text[0];
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 }