Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/curve/editfont.c')
-rw-r--r--source/blender/editors/curve/editfont.c1625
1 files changed, 1625 insertions, 0 deletions
diff --git a/source/blender/editors/curve/editfont.c b/source/blender/editors/curve/editfont.c
new file mode 100644
index 00000000000..1b2c8ea6b11
--- /dev/null
+++ b/source/blender/editors/curve/editfont.c
@@ -0,0 +1,1625 @@
+/**
+ * $Id$
+ *
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * Contributor(s): Blender Foundation
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <wchar.h>
+
+#ifndef WIN32
+#include <unistd.h>
+#else
+#include <io.h>
+#endif
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_arithb.h"
+
+#include "DNA_curve_types.h"
+#include "DNA_object_types.h"
+#include "DNA_vfont_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_text_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_userdef_types.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_depsgraph.h"
+#include "BKE_font.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_object.h"
+#include "BKE_report.h"
+#include "BKE_utildefines.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_curve.h"
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_util.h"
+
+#include "curve_intern.h"
+
+#define MAXTEXT 32766
+
+/************************* utilities ******************************/
+
+static char findaccent(char char1, unsigned int code)
+{
+ char new= 0;
+
+ if(char1=='a') {
+ if(code=='`') new= 224;
+ else if(code==39) new= 225;
+ else if(code=='^') new= 226;
+ else if(code=='~') new= 227;
+ else if(code=='"') new= 228;
+ else if(code=='o') new= 229;
+ else if(code=='e') new= 230;
+ else if(code=='-') new= 170;
+ }
+ else if(char1=='c') {
+ if(code==',') new= 231;
+ if(code=='|') new= 162;
+ }
+ else if(char1=='e') {
+ if(code=='`') new= 232;
+ else if(code==39) new= 233;
+ else if(code=='^') new= 234;
+ else if(code=='"') new= 235;
+ }
+ else if(char1=='i') {
+ if(code=='`') new= 236;
+ else if(code==39) new= 237;
+ else if(code=='^') new= 238;
+ else if(code=='"') new= 239;
+ }
+ else if(char1=='n') {
+ if(code=='~') new= 241;
+ }
+ else if(char1=='o') {
+ if(code=='`') new= 242;
+ else if(code==39) new= 243;
+ else if(code=='^') new= 244;
+ else if(code=='~') new= 245;
+ else if(code=='"') new= 246;
+ else if(code=='/') new= 248;
+ else if(code=='-') new= 186;
+ else if(code=='e') new= 143;
+ }
+ else if(char1=='s') {
+ if(code=='s') new= 167;
+ }
+ else if(char1=='u') {
+ if(code=='`') new= 249;
+ else if(code==39) new= 250;
+ else if(code=='^') new= 251;
+ else if(code=='"') new= 252;
+ }
+ else if(char1=='y') {
+ if(code==39) new= 253;
+ else if(code=='"') new= 255;
+ }
+ else if(char1=='A') {
+ if(code=='`') new= 192;
+ else if(code==39) new= 193;
+ else if(code=='^') new= 194;
+ else if(code=='~') new= 195;
+ else if(code=='"') new= 196;
+ else if(code=='o') new= 197;
+ else if(code=='e') new= 198;
+ }
+ else if(char1=='C') {
+ if(code==',') new= 199;
+ }
+ else if(char1=='E') {
+ if(code=='`') new= 200;
+ else if(code==39) new= 201;
+ else if(code=='^') new= 202;
+ else if(code=='"') new= 203;
+ }
+ else if(char1=='I') {
+ if(code=='`') new= 204;
+ else if(code==39) new= 205;
+ else if(code=='^') new= 206;
+ else if(code=='"') new= 207;
+ }
+ else if(char1=='N') {
+ if(code=='~') new= 209;
+ }
+ else if(char1=='O') {
+ if(code=='`') new= 210;
+ else if(code==39) new= 211;
+ else if(code=='^') new= 212;
+ else if(code=='~') new= 213;
+ else if(code=='"') new= 214;
+ else if(code=='/') new= 216;
+ else if(code=='e') new= 141;
+ }
+ else if(char1=='U') {
+ if(code=='`') new= 217;
+ else if(code==39) new= 218;
+ else if(code=='^') new= 219;
+ else if(code=='"') new= 220;
+ }
+ else if(char1=='Y') {
+ if(code==39) new= 221;
+ }
+ else if(char1=='1') {
+ if(code=='4') new= 188;
+ if(code=='2') new= 189;
+ }
+ else if(char1=='3') {
+ if(code=='4') new= 190;
+ }
+ else if(char1==':') {
+ if(code=='-') new= 247;
+ }
+ else if(char1=='-') {
+ if(code==':') new= 247;
+ if(code=='|') new= 135;
+ if(code=='+') new= 177;
+ }
+ else if(char1=='|') {
+ if(code=='-') new= 135;
+ if(code=='=') new= 136;
+ }
+ else if(char1=='=') {
+ if(code=='|') new= 136;
+ }
+ else if(char1=='+') {
+ if(code=='-') new= 177;
+ }
+
+ if(new) return new;
+ else return char1;
+}
+
+
+void update_string(Curve *cu)
+{
+ EditFont *ef= cu->editfont;
+ int len;
+
+ // Free the old curve string
+ MEM_freeN(cu->str);
+
+ // Calculate the actual string length in UTF-8 variable characters
+ len = wcsleninu8(ef->textbuf);
+
+ // Alloc memory for UTF-8 variable char length string
+ cu->str = MEM_callocN(len + sizeof(wchar_t), "str");
+
+ // Copy the wchar to UTF-8
+ wcs2utf8s(cu->str, ef->textbuf);
+}
+
+static int insert_into_textbuf(Object *obedit, uintptr_t c)
+{
+ Curve *cu= obedit->data;
+
+ if(cu->len<MAXTEXT-1) {
+ EditFont *ef= cu->editfont;
+ int x;
+
+ for(x= cu->len; x>cu->pos; x--) ef->textbuf[x]= ef->textbuf[x-1];
+ for(x= cu->len; x>cu->pos; x--) ef->textbufinfo[x]= ef->textbufinfo[x-1];
+ ef->textbuf[cu->pos]= c;
+ ef->textbufinfo[cu->pos] = cu->curinfo;
+ ef->textbufinfo[cu->pos].kern = 0;
+ if(obedit->actcol>0)
+ ef->textbufinfo[cu->pos].mat_nr = obedit->actcol;
+ else
+ ef->textbufinfo[cu->pos].mat_nr = 0;
+
+ cu->pos++;
+ cu->len++;
+ ef->textbuf[cu->len]='\0';
+
+ update_string(cu);
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void text_update_edited(bContext *C, Scene *scene, Object *obedit, int recalc, int mode)
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+
+ if(cu->pos)
+ cu->curinfo = ef->textbufinfo[cu->pos-1];
+ else
+ cu->curinfo = ef->textbufinfo[0];
+
+ if(obedit->totcol>0)
+ obedit->actcol= ef->textbufinfo[cu->pos-1].mat_nr;
+
+ update_string(cu);
+ BKE_text_to_curve(scene, obedit, mode);
+
+ if(recalc)
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+}
+
+/********************** insert lorem operator *********************/
+
+static int insert_lorem_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ char *p, *p2;
+ int i;
+ static char *lastlorem;
+
+ if(lastlorem)
+ p= lastlorem;
+ else
+ p= ED_lorem;
+
+ i= rand()/(RAND_MAX/6)+4;
+
+ for(p2=p; *p2 && i; p2++) {
+ insert_into_textbuf(obedit, *p2);
+
+ if(*p2=='.')
+ i--;
+ }
+
+ lastlorem = p2+1;
+ if(strlen(lastlorem)<5)
+ lastlorem = ED_lorem;
+
+ insert_into_textbuf(obedit, '\n');
+ insert_into_textbuf(obedit, '\n');
+
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_insert_lorem(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Insert Lorem";
+ ot->idname= "FONT_OT_insert_lorem";
+
+ /* api callbacks */
+ ot->exec= insert_lorem_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************* paste file operator ********************/
+
+/* note this handles both ascii and utf8 unicode, previously
+ * there were 3 functions that did effectively the same thing. */
+
+static int paste_file(bContext *C, ReportList *reports, char *filename)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ FILE *fp;
+ int filelen;
+ char *strp;
+
+ fp= fopen(filename, "r");
+
+ if(!fp) {
+ if(reports)
+ BKE_reportf(reports, RPT_ERROR, "Failed to open file %s.", filename);
+ return OPERATOR_CANCELLED;
+ }
+
+ fseek(fp, 0L, SEEK_END);
+ filelen = ftell(fp);
+ fseek(fp, 0L, SEEK_SET);
+
+ strp= MEM_callocN(filelen+4, "tempstr");
+
+ // fread() instead of read(), because windows read() converts text
+ // to DOS \r\n linebreaks, causing double linebreaks in the 3d text
+ filelen = fread(strp, 1, filelen, fp);
+ fclose(fp);
+ strp[filelen]= 0;
+
+ if(cu->len+filelen<MAXTEXT) {
+ int tmplen;
+ wchar_t *mem = MEM_callocN((sizeof(wchar_t)*filelen)+(4*sizeof(wchar_t)), "temporary");
+ tmplen = utf8towchar(mem, strp);
+ wcscat(ef->textbuf, mem);
+ MEM_freeN(mem);
+ cu->len += tmplen;
+ cu->pos= cu->len;
+ }
+ MEM_freeN(strp);
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+static int paste_file_exec(bContext *C, wmOperator *op)
+{
+ char *filename;
+ int retval;
+
+ filename= RNA_string_get_alloc(op->ptr, "filename", NULL, 0);
+ retval= paste_file(C, op->reports, filename);
+ MEM_freeN(filename);
+
+ return retval;
+}
+
+static int paste_file_invoke(bContext *C, wmOperator *op, wmEvent *event)
+{
+ if(RNA_property_is_set(op->ptr, "filename"))
+ return paste_file_exec(C, op);
+
+ WM_event_add_fileselect(C, op);
+
+ return OPERATOR_RUNNING_MODAL;
+}
+
+void FONT_OT_file_paste(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Paste File";
+ ot->idname= "FONT_OT_file_paste";
+
+ /* api callbacks */
+ ot->exec= paste_file_exec;
+ ot->invoke= paste_file_invoke;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_string_file_path(ot->srna, "filename", "", 0, "Filename", "File path of text file to load.");
+}
+
+/******************* paste buffer operator ********************/
+
+static int paste_buffer_exec(bContext *C, wmOperator *op)
+{
+ char *filename;
+
+#ifdef WIN32
+ filename= "C:\\windows\\temp\\cutbuf.txt";
+
+// The following is more likely to work on all Win32 installations.
+// suggested by Douglas Toltzman. Needs windows include files...
+/*
+ char tempFileName[MAX_PATH];
+ DWORD pathlen;
+ static const char cutbufname[]="cutbuf.txt";
+
+ if((pathlen=GetTempPath(sizeof(tempFileName),tempFileName)) > 0 &&
+ pathlen + sizeof(cutbufname) <= sizeof(tempFileName))
+ {
+ strcat(tempFileName,cutbufname);
+ filename= tempFilename;
+ }
+*/
+#else
+ filename= "/tmp/.cutbuffer";
+#endif
+
+ return paste_file(C, NULL, filename);
+}
+
+void FONT_OT_buffer_paste(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Paste Buffer";
+ ot->idname= "FONT_OT_buffer_paste";
+
+ /* api callbacks */
+ ot->exec= paste_buffer_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************* text to object operator ********************/
+
+static void txt_add_object(bContext *C, TextLine *firstline, int totline, float offset[3])
+{
+ Scene *scene= CTX_data_scene(C);
+ Curve *cu;
+ Object *obedit;
+ Base *base;
+ struct TextLine *tmp;
+ int nchars = 0, a;
+
+ obedit= add_object(scene, OB_FONT);
+ base= scene->basact;
+
+ ED_object_base_init_from_view(C, base);
+ where_is_object(scene, obedit);
+
+ obedit->loc[0] += offset[0];
+ obedit->loc[1] += offset[1];
+ obedit->loc[2] += offset[2];
+
+ cu= obedit->data;
+ cu->vfont= get_builtin_font();
+ cu->vfont->id.us++;
+
+ for(tmp=firstline, a=0; cu->len<MAXTEXT && a<totline; tmp=tmp->next, a++)
+ nchars += strlen(tmp->line) + 1;
+
+ if(cu->str) MEM_freeN(cu->str);
+ if(cu->strinfo) MEM_freeN(cu->strinfo);
+
+ cu->str= MEM_callocN(nchars+4, "str");
+ cu->strinfo= MEM_callocN((nchars+4)*sizeof(CharInfo), "strinfo");
+
+ cu->str[0]= '\0';
+ cu->len= 0;
+ cu->pos= 0;
+
+ for(tmp=firstline, a=0; cu->len<MAXTEXT && a<totline; tmp=tmp->next, a++) {
+ strcat(cu->str, tmp->line);
+ cu->len+= strlen(tmp->line);
+
+ if(tmp->next) {
+ strcat(cu->str, "\n");
+ cu->len++;
+ }
+
+ cu->pos= cu->len;
+ }
+
+ WM_event_add_notifier(C, NC_OBJECT|NA_ADDED, obedit);
+}
+
+void ED_text_to_object(bContext *C, Text *text, int split_lines)
+{
+ RegionView3D *rv3d= CTX_wm_region_view3d(C);
+ TextLine *line;
+ float offset[3];
+ int linenum= 0;
+
+ if(!text || !text->lines.first) return;
+
+ if(split_lines) {
+ for(line=text->lines.first; line; line=line->next) {
+ /* skip lines with no text, but still make space for them */
+ if(line->line[0] == '\0') {
+ linenum++;
+ continue;
+ }
+
+ /* do the translation */
+ offset[0] = 0;
+ offset[1] = -linenum;
+ offset[2] = 0;
+
+ if(rv3d)
+ Mat4Mul3Vecfl(rv3d->viewinv, offset);
+
+ txt_add_object(C, line, 1, offset);
+
+ linenum++;
+ }
+ }
+ else {
+ offset[0]= 0.0f;
+ offset[1]= 0.0f;
+ offset[2]= 0.0f;
+
+ txt_add_object(C, text->lines.first, BLI_countlist(&text->lines), offset);
+ }
+}
+
+/********************** utilities ***************************/
+
+static short next_word(Curve *cu)
+{
+ short s;
+ for(s=cu->pos; (cu->str[s]) && (cu->str[s]!=' ') && (cu->str[s]!='\n') &&
+ (cu->str[s]!=1) && (cu->str[s]!='\r'); s++);
+ if(cu->str[s]) return(s+1); else return(s);
+}
+
+static short prev_word(Curve *cu)
+{
+ short s;
+
+ if(cu->pos==0) return(0);
+ for(s=cu->pos-2; (cu->str[s]) && (cu->str[s]!=' ') && (cu->str[s]!='\n') &&
+ (cu->str[s]!=1) && (cu->str[s]!='\r'); s--);
+ if(cu->str[s]) return(s+1); else return(s);
+}
+
+static int kill_selection(Object *obedit, int ins) /* 1 == new character */
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int selend, selstart, direction;
+ int offset = 0;
+ int getfrom;
+
+ direction = BKE_font_getselection(obedit, &selstart, &selend);
+ if(direction) {
+ int size;
+ if(ins) offset = 1;
+ if(cu->pos >= selstart) cu->pos = selstart+offset;
+ if((direction == -1) && ins) {
+ selstart++;
+ selend++;
+ }
+ getfrom = selend+offset;
+ if(ins==0) getfrom++;
+ size = (cu->len * sizeof(wchar_t)) - (selstart * sizeof(wchar_t)) + (offset*sizeof(wchar_t));
+ memmove(ef->textbuf+selstart, ef->textbuf+getfrom, size);
+ memmove(ef->textbufinfo+selstart, ef->textbufinfo+getfrom, ((cu->len-selstart)+offset)*sizeof(CharInfo));
+ cu->len -= (selend-selstart)+offset;
+ cu->selstart = cu->selend = 0;
+ }
+
+ return(direction);
+}
+
+/******************* set style operator ********************/
+
+static EnumPropertyItem style_items[]= {
+ {CU_BOLD, "BOLD", "Bold", ""},
+ {CU_ITALIC, "ITALIC", "Italic", ""},
+ {CU_UNDERLINE, "UNDERLINE", "Underline", ""},
+ {0, NULL, NULL, NULL}};
+
+static int set_style(bContext *C, int style, int clear)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int i, selstart, selend;
+
+ if(!BKE_font_getselection(obedit, &selstart, &selend))
+ return OPERATOR_CANCELLED;
+
+ for(i=selstart; i<=selend; i++) {
+ if(clear)
+ ef->textbufinfo[i].flag &= ~style;
+ else
+ ef->textbufinfo[i].flag |= style;
+ }
+
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+static int set_style_exec(bContext *C, wmOperator *op)
+{
+ int style, clear;
+
+ style= RNA_enum_get(op->ptr, "style");
+ clear= RNA_enum_get(op->ptr, "clear");
+
+ return set_style(C, style, clear);
+}
+
+void FONT_OT_style_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Style";
+ ot->idname= "FONT_OT_style_set";
+
+ /* api callbacks */
+ ot->exec= set_style_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "style", style_items, CU_BOLD, "Style", "Style to set selection to.");
+ RNA_def_boolean(ot->srna, "clear", 0, "Clear", "Clear style rather than setting it.");
+}
+
+/******************* toggle style operator ********************/
+
+static int toggle_style_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ int style, clear, selstart, selend;
+
+ if(!BKE_font_getselection(obedit, &selstart, &selend))
+ return OPERATOR_CANCELLED;
+
+ style= RNA_enum_get(op->ptr, "style");
+
+ cu->curinfo.flag ^= style;
+ clear= (cu->curinfo.flag & style) == 0;
+
+ return set_style(C, style, clear);
+}
+
+void FONT_OT_style_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Toggle Style";
+ ot->idname= "FONT_OT_style_toggle";
+
+ /* api callbacks */
+ ot->exec= toggle_style_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "style", style_items, CU_BOLD, "Style", "Style to set selection to.");
+}
+
+/******************* set material operator ********************/
+
+static int set_material_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int i, mat_nr, selstart, selend;
+
+ if(!BKE_font_getselection(obedit, &selstart, &selend))
+ return OPERATOR_CANCELLED;
+
+ if(RNA_property_is_set(op->ptr, "index"))
+ mat_nr= RNA_int_get(op->ptr, "index");
+ else
+ mat_nr= obedit->actcol;
+
+ for(i=selstart; i<=selend; i++)
+ ef->textbufinfo[i].mat_nr = mat_nr;
+
+ DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
+ WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_DATA, obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_material_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Material";
+ ot->idname= "FONT_OT_material_set";
+
+ /* api callbacks */
+ ot->exec= set_material_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "index", 0, 0, INT_MAX, "Material Index", "Material slot index.", 0, INT_MAX);
+}
+
+/******************* copy text operator ********************/
+
+static void copy_selection(Object *obedit)
+{
+ int selstart, selend;
+
+ if(BKE_font_getselection(obedit, &selstart, &selend)) {
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+
+ memcpy(ef->copybuf, ef->textbuf+selstart, ((selend-selstart)+1)*sizeof(wchar_t));
+ ef->copybuf[(selend-selstart)+1]=0;
+ memcpy(ef->copybufinfo, ef->textbufinfo+selstart, ((selend-selstart)+1)*sizeof(CharInfo));
+ }
+}
+
+static int copy_text_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+
+ copy_selection(obedit);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_text_copy(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Copy Text";
+ ot->idname= "FONT_OT_text_copy";
+
+ /* api callbacks */
+ ot->exec= copy_text_exec;
+ ot->poll= ED_operator_editfont;
+}
+
+/******************* cut text operator ********************/
+
+static int cut_text_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ int selstart, selend;
+
+ if(!BKE_font_getselection(obedit, &selstart, &selend))
+ return OPERATOR_CANCELLED;
+
+ copy_selection(obedit);
+ kill_selection(obedit, 0);
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_text_cut(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Cut Text";
+ ot->idname= "FONT_OT_text_cut";
+
+ /* api callbacks */
+ ot->exec= cut_text_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/******************* paste text operator ********************/
+
+static int paste_selection(Object *obedit, ReportList *reports)
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int len= wcslen(ef->copybuf);
+
+ // Verify that the copy buffer => [copy buffer len] + cu->len < MAXTEXT
+ if(cu->len + len <= MAXTEXT) {
+ if(len) {
+ int size = (cu->len * sizeof(wchar_t)) - (cu->pos*sizeof(wchar_t)) + sizeof(wchar_t);
+ memmove(ef->textbuf+cu->pos+len, ef->textbuf+cu->pos, size);
+ memcpy(ef->textbuf+cu->pos, ef->copybuf, len * sizeof(wchar_t));
+
+ memmove(ef->textbufinfo+cu->pos+len, ef->textbufinfo+cu->pos, (cu->len-cu->pos+1)*sizeof(CharInfo));
+ memcpy(ef->textbufinfo+cu->pos, ef->copybufinfo, len*sizeof(CharInfo));
+
+ cu->len += len;
+ cu->pos += len;
+
+ return 1;
+ }
+ }
+ else
+ BKE_report(reports, RPT_WARNING, "Text too long.");
+
+ return 0;
+}
+
+static int paste_text_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+
+ if(!paste_selection(obedit, op->reports))
+ return OPERATOR_CANCELLED;
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_text_paste(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Paste Text";
+ ot->idname= "FONT_OT_text_paste";
+
+ /* api callbacks */
+ ot->exec= paste_text_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/************************ move operator ************************/
+
+static EnumPropertyItem move_type_items[]= {
+ {LINE_BEGIN, "LINE_BEGIN", "Line Begin", ""},
+ {LINE_END, "LINE_END", "Line End", ""},
+ {PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""},
+ {NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""},
+ {PREV_WORD, "PREVIOUS_WORD", "Previous Word", ""},
+ {NEXT_WORD, "NEXT_WORD", "Next Word", ""},
+ {PREV_LINE, "PREVIOUS_LINE", "Previous Line", ""},
+ {NEXT_LINE, "NEXT_LINE", "Next Line", ""},
+ {PREV_PAGE, "PREVIOUS_PAGE", "Previous Page", ""},
+ {NEXT_PAGE, "NEXT_PAGE", "Next Page", ""},
+ {0, NULL, NULL, NULL}};
+
+static int move_cursor(bContext *C, int type, int select)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int cursmove= 0;
+
+ switch(type) {
+ case LINE_BEGIN:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ while(cu->pos>0) {
+ if(ef->textbuf[cu->pos-1]=='\n') break;
+ if(ef->textbufinfo[cu->pos-1].flag & CU_WRAP ) break;
+ cu->pos--;
+ }
+ cursmove=FO_CURS;
+ break;
+
+ case LINE_END:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ while(cu->pos<cu->len) {
+ if(ef->textbuf[cu->pos]==0) break;
+ if(ef->textbuf[cu->pos]=='\n') break;
+ if(ef->textbufinfo[cu->pos].flag & CU_WRAP ) break;
+ cu->pos++;
+ }
+ cursmove=FO_CURS;
+ break;
+
+ case PREV_WORD:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cu->pos= prev_word(cu);
+ cursmove= FO_CURS;
+ break;
+
+ case NEXT_WORD:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cu->pos= next_word(cu);
+ cursmove= FO_CURS;
+ break;
+
+ case PREV_CHAR:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cu->pos--;
+ cursmove=FO_CURS;
+ break;
+
+ case NEXT_CHAR:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cu->pos++;
+ cursmove= FO_CURS;
+
+ break;
+
+ case PREV_LINE:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cursmove=FO_CURSUP;
+ break;
+
+ case NEXT_LINE:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cursmove= FO_CURSDOWN;
+ break;
+
+ case PREV_PAGE:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cursmove=FO_PAGEUP;
+ break;
+
+ case NEXT_PAGE:
+ if((select) && (cu->selstart==0)) cu->selstart = cu->selend = cu->pos+1;
+ cursmove=FO_PAGEDOWN;
+ break;
+ }
+
+ if(!cursmove)
+ return OPERATOR_CANCELLED;
+
+ if(select == 0) {
+ if(cu->selstart) {
+ cu->selstart = cu->selend = 0;
+ update_string(cu);
+ BKE_text_to_curve(scene, obedit, FO_SELCHANGE);
+ }
+ }
+
+ if(cu->pos>cu->len) cu->pos= cu->len;
+ else if(cu->pos>=MAXTEXT) cu->pos= MAXTEXT;
+ else if(cu->pos<0) cu->pos= 0;
+
+ text_update_edited(C, scene, obedit, select, cursmove);
+
+ if(select)
+ cu->selend = cu->pos;
+
+ return OPERATOR_FINISHED;
+}
+
+static int move_exec(bContext *C, wmOperator *op)
+{
+ int type= RNA_enum_get(op->ptr, "type");
+
+ return move_cursor(C, type, 0);
+}
+
+void FONT_OT_move(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Move Cursor";
+ ot->idname= "FONT_OT_move";
+
+ /* api callbacks */
+ ot->exec= move_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
+}
+
+/******************* move select operator ********************/
+
+static int move_select_exec(bContext *C, wmOperator *op)
+{
+ int type= RNA_enum_get(op->ptr, "type");
+
+ return move_cursor(C, type, 1);
+}
+
+void FONT_OT_move_select(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Move Select";
+ ot->idname= "FONT_OT_move_select";
+
+ /* api callbacks */
+ ot->exec= move_select_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection.");
+}
+
+/************************* change spacing **********************/
+
+static int change_spacing_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int kern, delta= RNA_int_get(op->ptr, "delta");
+
+ kern = ef->textbufinfo[cu->pos-1].kern;
+ kern += delta;
+ CLAMP(kern, -20, 20);
+
+ if(ef->textbufinfo[cu->pos-1].kern == kern)
+ return OPERATOR_CANCELLED;
+
+ ef->textbufinfo[cu->pos-1].kern = kern;
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_change_spacing(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Change Spacing";
+ ot->idname= "FONT_OT_change_spacing";
+
+ /* api callbacks */
+ ot->exec= change_spacing_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "delta", 1, -20, 20, "Delta", "Amount to decrease or increasing character spacing with.", -20, 20);
+}
+
+/************************* change character **********************/
+
+static int change_character_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int character, delta= RNA_int_get(op->ptr, "delta");
+
+ if(cu->pos <= 0)
+ return OPERATOR_CANCELLED;
+
+ character= ef->textbuf[cu->pos - 1];
+ character += delta;
+ CLAMP(character, 0, 255);
+
+ if(character == ef->textbuf[cu->pos - 1])
+ return OPERATOR_CANCELLED;
+
+ ef->textbuf[cu->pos - 1]= character;
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_change_character(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Change Character";
+ ot->idname= "FONT_OT_change_character";
+
+ /* api callbacks */
+ ot->exec= change_character_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_int(ot->srna, "delta", 1, -255, 255, "Delta", "Number to increase or decrease character code with.", -255, 255);
+}
+
+/******************* line break operator ********************/
+
+static int line_break_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int ctrl= RNA_enum_get(op->ptr, "ctrl");
+
+ if(ctrl) {
+ insert_into_textbuf(obedit, 1);
+ if(ef->textbuf[cu->pos]!='\n')
+ insert_into_textbuf(obedit, '\n');
+ }
+ else
+ insert_into_textbuf(obedit, '\n');
+
+ cu->selstart = cu->selend = 0;
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_line_break(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Line Break";
+ ot->idname= "FONT_OT_line_break";
+
+ /* api callbacks */
+ ot->exec= line_break_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "ctrl", 0, "Ctrl", ""); // XXX what is this?
+}
+
+/******************* delete operator **********************/
+
+static EnumPropertyItem delete_type_items[]= {
+ {DEL_ALL, "ALL", "All", ""},
+ {DEL_NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""},
+ {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""},
+ {DEL_SELECTION, "SELECTION", "Selection", ""},
+ {DEL_NEXT_SEL, "NEXT_OR_SELECTION", "Next or Selection", ""},
+ {DEL_PREV_SEL, "PREVIOUS_OR_SELECTION", "Previous or Selection", ""},
+ {0, NULL, NULL, NULL}};
+
+static int delete_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ int x, selstart, selend, type= RNA_enum_get(op->ptr, "type");
+
+ if(cu->len == 0)
+ return OPERATOR_CANCELLED;
+
+ if(BKE_font_getselection(obedit, &selstart, &selend)) {
+ if(type == DEL_NEXT_SEL) type= DEL_SELECTION;
+ else if(type == DEL_PREV_SEL) type= DEL_SELECTION;
+ }
+ else {
+ if(type == DEL_NEXT_SEL) type= DEL_NEXT_CHAR;
+ else if(type == DEL_PREV_SEL) type= DEL_PREV_CHAR;
+ }
+
+ switch(type) {
+ case DEL_ALL:
+ cu->len = cu->pos = 0;
+ ef->textbuf[0]= 0;
+ break;
+ case DEL_SELECTION:
+ if(!kill_selection(obedit, 0))
+ return OPERATOR_CANCELLED;
+ break;
+ case DEL_PREV_CHAR:
+ if(cu->pos<=0)
+ return OPERATOR_CANCELLED;
+
+ for(x=cu->pos;x<=cu->len;x++)
+ ef->textbuf[x-1]= ef->textbuf[x];
+ for(x=cu->pos;x<=cu->len;x++)
+ ef->textbufinfo[x-1]= ef->textbufinfo[x];
+
+ cu->pos--;
+ ef->textbuf[--cu->len]='\0';
+ break;
+ case DEL_NEXT_CHAR:
+ if(cu->pos>=cu->len)
+ return OPERATOR_CANCELLED;
+
+ for(x=cu->pos;x<cu->len;x++)
+ ef->textbuf[x]= ef->textbuf[x+1];
+ for(x=cu->pos;x<cu->len;x++)
+ ef->textbufinfo[x]= ef->textbufinfo[x+1];
+
+ ef->textbuf[--cu->len]='\0';
+ break;
+ default:
+ return OPERATOR_CANCELLED;
+ }
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_delete(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Delete";
+ ot->idname= "FONT_OT_delete";
+
+ /* api callbacks */
+ ot->exec= delete_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "type", delete_type_items, DEL_ALL, "Type", "Which part of the text to delete.");
+}
+
+/*********************** insert text operator *************************/
+
+static int insert_text_exec(bContext *C, wmOperator *op)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ char *inserted_utf8;
+ wchar_t *inserted_text, first;
+ int len;
+
+ if(!RNA_property_is_set(op->ptr, "text"))
+ return OPERATOR_CANCELLED;
+
+ inserted_utf8= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
+ len= strlen(inserted_utf8);
+
+ inserted_text= MEM_callocN(sizeof(wchar_t)*(len+1), "FONT_insert_text");
+ utf8towchar(inserted_text, inserted_utf8);
+ first= inserted_text[0];
+
+ MEM_freeN(inserted_text);
+ MEM_freeN(inserted_utf8);
+
+ if(!first)
+ return OPERATOR_CANCELLED;
+
+ insert_into_textbuf(obedit, first);
+ kill_selection(obedit, 1);
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+static int insert_text_invoke(bContext *C, wmOperator *op, wmEvent *evt)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ static int accentcode= 0;
+ uintptr_t ascii = evt->ascii;
+ int alt= evt->alt, shift= evt->shift, ctrl= evt->ctrl;
+ int event= evt->type, val= evt->val;
+ wchar_t inserted_text[2]= {0};
+
+ if(RNA_property_is_set(op->ptr, "text"))
+ return insert_text_exec(C, op);
+
+ /* tab should exit editmode, but we allow it to be typed using modifier keys */
+ if(event==TABKEY) {
+ if((alt||ctrl||shift) == 0)
+ return OPERATOR_PASS_THROUGH;
+ else
+ ascii= 9;
+ }
+ else if(event==BACKSPACEKEY)
+ ascii= 0;
+
+ if(val && ascii) {
+ /* handle case like TAB (== 9) */
+ if((ascii > 31 && ascii < 254 && ascii != 127) || (ascii==13) || (ascii==10) || (ascii==8)) {
+ if(accentcode) {
+ if(cu->pos>0) {
+ inserted_text[0]= findaccent(ef->textbuf[cu->pos-1], ascii);
+ ef->textbuf[cu->pos-1]= inserted_text[0];
+ }
+ accentcode= 0;
+ }
+ else if(cu->len<MAXTEXT-1) {
+ if(alt) {
+ /* might become obsolete, apple has default values for this, other OS's too? */
+ if(ascii=='t') ascii= 137;
+ else if(ascii=='c') ascii= 169;
+ else if(ascii=='f') ascii= 164;
+ else if(ascii=='g') ascii= 176;
+ else if(ascii=='l') ascii= 163;
+ else if(ascii=='r') ascii= 174;
+ else if(ascii=='s') ascii= 223;
+ else if(ascii=='y') ascii= 165;
+ else if(ascii=='.') ascii= 138;
+ else if(ascii=='1') ascii= 185;
+ else if(ascii=='2') ascii= 178;
+ else if(ascii=='3') ascii= 179;
+ else if(ascii=='%') ascii= 139;
+ else if(ascii=='?') ascii= 191;
+ else if(ascii=='!') ascii= 161;
+ else if(ascii=='x') ascii= 215;
+ else if(ascii=='>') ascii= 187;
+ else if(ascii=='<') ascii= 171;
+ }
+
+ inserted_text[0]= ascii;
+ insert_into_textbuf(obedit, ascii);
+ }
+
+ kill_selection(obedit, 1);
+ text_update_edited(C, scene, obedit, 1, 0);
+ }
+ else {
+ inserted_text[0]= ascii;
+ insert_into_textbuf(obedit, ascii);
+ text_update_edited(C, scene, obedit, 1, 0);
+ }
+ }
+ else if(val && event == BACKSPACEKEY) {
+ if(alt && cu->len!=0 && cu->pos>0)
+ accentcode= 1;
+
+ return OPERATOR_PASS_THROUGH;
+ }
+ else
+ return OPERATOR_PASS_THROUGH;
+
+ if(inserted_text[0]) {
+ /* store as utf8 in RNA string */
+ char inserted_utf8[8] = {0};
+
+ wcs2utf8s(inserted_utf8, inserted_text);
+ RNA_string_set(op->ptr, "text", inserted_utf8);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void FONT_OT_text_insert(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Insert Text";
+ ot->idname= "FONT_OT_text_insert";
+
+ /* api callbacks */
+ ot->exec= insert_text_exec;
+ ot->invoke= insert_text_invoke;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
+}
+
+/***************** editmode enter/exit ********************/
+
+void make_editText(Object *obedit)
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+
+ if(ef==NULL) {
+ ef= cu->editfont= MEM_callocN(sizeof(EditFont), "editfont");
+
+ ef->textbuf= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "texteditbuf");
+ ef->textbufinfo= MEM_callocN((MAXTEXT+4)*sizeof(CharInfo), "texteditbufinfo");
+ ef->copybuf= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "texteditcopybuf");
+ ef->copybufinfo= MEM_callocN((MAXTEXT+4)*sizeof(CharInfo), "texteditcopybufinfo");
+ ef->oldstr= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "oldstrbuf");
+ ef->oldstrinfo= MEM_callocN((MAXTEXT+4)*sizeof(wchar_t), "oldstrbuf");
+ }
+
+ // Convert the original text to wchar_t
+ utf8towchar(ef->textbuf, cu->str);
+ wcscpy(ef->oldstr, ef->textbuf);
+
+ cu->len= wcslen(ef->textbuf);
+
+ memcpy(ef->textbufinfo, cu->strinfo, (cu->len)*sizeof(CharInfo));
+ memcpy(ef->oldstrinfo, cu->strinfo, (cu->len)*sizeof(CharInfo));
+
+ if(cu->pos>cu->len) cu->pos= cu->len;
+
+ if(cu->pos)
+ cu->curinfo = ef->textbufinfo[cu->pos-1];
+ else
+ cu->curinfo = ef->textbufinfo[0];
+
+ // Convert to UTF-8
+ update_string(cu);
+}
+
+void load_editText(Object *obedit)
+{
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+
+ MEM_freeN(ef->oldstr);
+ ef->oldstr= NULL;
+ MEM_freeN(ef->oldstrinfo);
+ ef->oldstrinfo= NULL;
+
+ update_string(cu);
+
+ if(cu->strinfo)
+ MEM_freeN(cu->strinfo);
+ cu->strinfo= MEM_callocN((cu->len+4)*sizeof(CharInfo), "texteditinfo");
+ memcpy(cu->strinfo, ef->textbufinfo, (cu->len)*sizeof(CharInfo));
+
+ cu->len= strlen(cu->str);
+
+ /* this memory system is weak... */
+
+ if(cu->selboxes) {
+ MEM_freeN(cu->selboxes);
+ cu->selboxes= NULL;
+ }
+}
+
+void free_editText(Object *obedit)
+{
+ BKE_free_editfont((Curve *)obedit->data);
+}
+
+/********************** set case operator *********************/
+
+static EnumPropertyItem case_items[]= {
+ {CASE_LOWER, "LOWER", "Lower", ""},
+ {CASE_UPPER, "UPPER", "Upper", ""},
+ {0, NULL, NULL, NULL}};
+
+static int set_case(bContext *C, int ccase)
+{
+ Scene *scene= CTX_data_scene(C);
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ wchar_t *str;
+ int len;
+
+ len= wcslen(ef->textbuf);
+ str= ef->textbuf;
+ while(len) {
+ if(*str>='a' && *str<='z')
+ *str-= 32;
+ len--;
+ str++;
+ }
+
+ if(ccase == CASE_LOWER) {
+ len= wcslen(ef->textbuf);
+ str= ef->textbuf;
+ while(len) {
+ if(*str>='A' && *str<='Z') {
+ *str+= 32;
+ }
+ len--;
+ str++;
+ }
+ }
+
+ text_update_edited(C, scene, obedit, 1, 0);
+
+ return OPERATOR_FINISHED;
+}
+
+static int set_case_exec(bContext *C, wmOperator *op)
+{
+ return set_case(C, RNA_enum_get(op->ptr, "case"));
+}
+
+void FONT_OT_case_set(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Set Case";
+ ot->idname= "FONT_OT_case_set";
+
+ /* api callbacks */
+ ot->exec= set_case_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_enum(ot->srna, "case", case_items, CASE_LOWER, "Case", "Lower or upper case.");
+}
+
+/********************** toggle case operator *********************/
+
+static int toggle_case_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ Curve *cu= obedit->data;
+ EditFont *ef= cu->editfont;
+ wchar_t *str;
+ int len, ccase= CASE_UPPER;
+
+ len= wcslen(ef->textbuf);
+ str= ef->textbuf;
+ while(len) {
+ if(*str>='a' && *str<='z') {
+ ccase= CASE_LOWER;
+ break;
+ }
+
+ len--;
+ str++;
+ }
+
+ return set_case(C, ccase);
+}
+
+void FONT_OT_case_toggle(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name= "Toggle Case";
+ ot->idname= "FONT_OT_case_toggle";
+
+ /* api callbacks */
+ ot->exec= toggle_case_exec;
+ ot->poll= ED_operator_editfont;
+
+ /* flags */
+ ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+}
+
+/* **************** undo for font object ************** */
+
+static void undoFont_to_editFont(void *strv, void *ecu)
+{
+ Curve *cu= (Curve *)ecu;
+ EditFont *ef= cu->editfont;
+ char *str= strv;
+
+ cu->pos= *((short *)str);
+ cu->len= *((short *)(str+2));
+
+ memcpy(ef->textbuf, str+4, (cu->len+1)*sizeof(wchar_t));
+ memcpy(ef->textbufinfo, str+4 + (cu->len+1)*sizeof(wchar_t), cu->len*sizeof(CharInfo));
+
+ cu->selstart = cu->selend = 0;
+
+ update_string(cu);
+}
+
+static void *editFont_to_undoFont(void *ecu)
+{
+ Curve *cu= (Curve *)ecu;
+ EditFont *ef= cu->editfont;
+ char *str;
+
+ // The undo buffer includes [MAXTEXT+6]=actual string and [MAXTEXT+4]*sizeof(CharInfo)=charinfo
+ str= MEM_callocN((MAXTEXT+6)*sizeof(wchar_t) + (MAXTEXT+4)*sizeof(CharInfo), "string undo");
+
+ // Copy the string and string information
+ memcpy(str+4, ef->textbuf, (cu->len+1)*sizeof(wchar_t));
+ memcpy(str+4 + (cu->len+1)*sizeof(wchar_t), ef->textbufinfo, cu->len*sizeof(CharInfo));
+
+ *((short *)str)= cu->pos;
+ *((short *)(str+2))= cu->len;
+
+ return str;
+}
+
+static void free_undoFont(void *strv)
+{
+ MEM_freeN(strv);
+}
+
+static void *get_undoFont(bContext *C)
+{
+ Object *obedit= CTX_data_edit_object(C);
+ if(obedit && obedit->type==OB_FONT) {
+ return obedit->data;
+ }
+ return NULL;
+}
+
+/* and this is all the undo system needs to know */
+void undo_push_font(bContext *C, char *name)
+{
+ undo_editmode_push(C, name, get_undoFont, free_undoFont, undoFont_to_editFont, editFont_to_undoFont, NULL);
+}
+