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