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