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