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