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