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