2564 lines
56 KiB
C
2564 lines
56 KiB
C
|
/**
|
||
|
* $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.
|
||
|
*
|
||
|
* The Original Code is: all of this file.
|
||
|
*
|
||
|
* Contributor(s): none yet.
|
||
|
*
|
||
|
* ***** END GPL LICENSE BLOCK *****
|
||
|
*/
|
||
|
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <ctype.h> /* ispunct */
|
||
|
#include <sys/stat.h>
|
||
|
|
||
|
#include "MEM_guardedalloc.h"
|
||
|
|
||
|
#include "DNA_constraint_types.h"
|
||
|
#include "DNA_object_types.h"
|
||
|
#include "DNA_action_types.h"
|
||
|
#include "DNA_scene_types.h"
|
||
|
#include "DNA_screen_types.h"
|
||
|
#include "DNA_space_types.h"
|
||
|
#include "DNA_text_types.h"
|
||
|
#include "DNA_windowmanager_types.h"
|
||
|
|
||
|
#include "BLI_blenlib.h"
|
||
|
#include "PIL_time.h"
|
||
|
|
||
|
#include "BKE_context.h"
|
||
|
#include "BKE_depsgraph.h"
|
||
|
#include "BKE_global.h"
|
||
|
#include "BKE_main.h"
|
||
|
#include "BKE_report.h"
|
||
|
#include "BKE_suggestions.h"
|
||
|
#include "BKE_text.h"
|
||
|
|
||
|
#include "WM_api.h"
|
||
|
#include "WM_types.h"
|
||
|
|
||
|
#include "ED_curve.h"
|
||
|
#include "ED_screen.h"
|
||
|
#include "UI_interface.h"
|
||
|
#include "UI_resources.h"
|
||
|
|
||
|
#include "RNA_access.h"
|
||
|
#include "RNA_define.h"
|
||
|
|
||
|
#ifndef DISABLE_PYTHON
|
||
|
#include "BPY_extern.h"
|
||
|
#endif
|
||
|
|
||
|
#include "text_intern.h"
|
||
|
|
||
|
/************************ poll ***************************/
|
||
|
|
||
|
static int text_new_poll(bContext *C)
|
||
|
{
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int text_edit_poll(bContext *C)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(!text)
|
||
|
return 0;
|
||
|
|
||
|
if(text->id.lib) {
|
||
|
// BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int text_space_edit_poll(bContext *C)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(!st || !text)
|
||
|
return 0;
|
||
|
|
||
|
if(text->id.lib) {
|
||
|
// BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int text_region_edit_poll(bContext *C)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
ARegion *ar= CTX_wm_region(C);
|
||
|
|
||
|
if(!st || !text)
|
||
|
return 0;
|
||
|
|
||
|
if(!ar || ar->regiontype != RGN_TYPE_WINDOW)
|
||
|
return 0;
|
||
|
|
||
|
if(text->id.lib) {
|
||
|
// BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
|
||
|
/********************** updates *********************/
|
||
|
|
||
|
void text_update_line_edited(Text *text, TextLine *line)
|
||
|
{
|
||
|
if(!line)
|
||
|
return;
|
||
|
|
||
|
/* we just free format here, and let it rebuild during draw */
|
||
|
if(line->format) {
|
||
|
MEM_freeN(line->format);
|
||
|
line->format= NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void text_update_edited(Text *text)
|
||
|
{
|
||
|
TextLine *line;
|
||
|
|
||
|
for(line=text->lines.first; line; line=line->next)
|
||
|
text_update_line_edited(text, line);
|
||
|
}
|
||
|
|
||
|
/******************* new operator *********************/
|
||
|
|
||
|
static int new_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text;
|
||
|
|
||
|
text= add_empty_text("Text");
|
||
|
|
||
|
if(st) {
|
||
|
st->text= text;
|
||
|
st->top= 0;
|
||
|
}
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_new(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "New";
|
||
|
ot->idname= "TEXT_OT_new";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= new_exec;
|
||
|
ot->poll= text_new_poll;
|
||
|
}
|
||
|
|
||
|
/******************* open operator *********************/
|
||
|
|
||
|
static int open_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text;
|
||
|
char str[FILE_MAX];
|
||
|
|
||
|
RNA_string_get(op->ptr, "filename", str);
|
||
|
|
||
|
text= add_text(str, G.sce);
|
||
|
|
||
|
if(st) {
|
||
|
st->text= text;
|
||
|
st->top= 0;
|
||
|
}
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
static int open_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
char *path= (text && text->name)? text->name: G.sce;
|
||
|
|
||
|
if(RNA_property_is_set(op->ptr, "filename"))
|
||
|
return open_exec(C, op);
|
||
|
|
||
|
RNA_string_set(op->ptr, "filename", path);
|
||
|
WM_event_add_fileselect(C, op);
|
||
|
|
||
|
return OPERATOR_RUNNING_MODAL;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_open(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Open";
|
||
|
ot->idname= "TEXT_OT_open";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= open_exec;
|
||
|
ot->invoke= open_invoke;
|
||
|
ot->poll= text_new_poll;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_string_file_path(ot->srna, "filename", "", FILE_MAX, "Filename", "File path of image to open.");
|
||
|
}
|
||
|
|
||
|
/******************* reload operator *********************/
|
||
|
|
||
|
static int reload_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(!reopen_text(text)) {
|
||
|
BKE_report(op->reports, RPT_ERROR, "Could not reopen file");
|
||
|
return OPERATOR_CANCELLED;
|
||
|
}
|
||
|
|
||
|
#ifndef DISABLE_PYTHON
|
||
|
if(text->compiled)
|
||
|
BPY_free_compiled_text(text);
|
||
|
|
||
|
text->compiled = NULL;
|
||
|
#endif
|
||
|
|
||
|
text_update_edited(text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_reload(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Reload";
|
||
|
ot->idname= "TEXT_OT_reload";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= reload_exec;
|
||
|
ot->invoke= WM_operator_confirm;
|
||
|
ot->poll= text_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* make internal operator *********************/
|
||
|
|
||
|
static int make_internal_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
text->flags |= TXT_ISMEM | TXT_ISDIRTY;
|
||
|
|
||
|
if(text->name) {
|
||
|
MEM_freeN(text->name);
|
||
|
text->name= NULL;
|
||
|
}
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_make_internal(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Make Internal";
|
||
|
ot->idname= "TEXT_OT_make_internal";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= make_internal_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* save operator *********************/
|
||
|
|
||
|
static int save_poll(bContext *C)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(!text_edit_poll(C))
|
||
|
return 0;
|
||
|
|
||
|
return (text->name != NULL && !(text->flags & TXT_ISMEM));
|
||
|
}
|
||
|
|
||
|
static void txt_write_file(Text *text, ReportList *reports)
|
||
|
{
|
||
|
FILE *fp;
|
||
|
TextLine *tmp;
|
||
|
struct stat st;
|
||
|
int res;
|
||
|
char file[FILE_MAXDIR+FILE_MAXFILE];
|
||
|
|
||
|
BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
|
||
|
BLI_convertstringcode(file, G.sce);
|
||
|
|
||
|
fp= fopen(file, "w");
|
||
|
if(fp==NULL) {
|
||
|
BKE_report(reports, RPT_ERROR, "Unable to save file.");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
tmp= text->lines.first;
|
||
|
while(tmp) {
|
||
|
if(tmp->next) fprintf(fp, "%s\n", tmp->line);
|
||
|
else fprintf(fp, "%s", tmp->line);
|
||
|
|
||
|
tmp= tmp->next;
|
||
|
}
|
||
|
|
||
|
fclose (fp);
|
||
|
|
||
|
res= stat(file, &st);
|
||
|
text->mtime= st.st_mtime;
|
||
|
|
||
|
if(text->flags & TXT_ISDIRTY)
|
||
|
text->flags ^= TXT_ISDIRTY;
|
||
|
}
|
||
|
|
||
|
static int save_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
txt_write_file(text, op->reports);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_save(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Save";
|
||
|
ot->idname= "TEXT_OT_save";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= save_exec;
|
||
|
ot->poll= save_poll;
|
||
|
}
|
||
|
|
||
|
/******************* save as operator *********************/
|
||
|
|
||
|
static int save_as_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
char str[FILE_MAX];
|
||
|
|
||
|
if(!text)
|
||
|
return OPERATOR_CANCELLED;
|
||
|
|
||
|
RNA_string_get(op->ptr, "filename", str);
|
||
|
|
||
|
if(text->name) MEM_freeN(text->name);
|
||
|
text->name= BLI_strdup(str);
|
||
|
text->flags &= ~TXT_ISMEM;
|
||
|
|
||
|
txt_write_file(text, op->reports);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
static int save_as_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
char *str;
|
||
|
|
||
|
if(RNA_property_is_set(op->ptr, "filename"))
|
||
|
return save_as_exec(C, op);
|
||
|
|
||
|
if(text->name)
|
||
|
str= text->name;
|
||
|
else if(text->flags & TXT_ISMEM)
|
||
|
str= text->id.name+2;
|
||
|
else
|
||
|
str= G.sce;
|
||
|
|
||
|
RNA_string_set(op->ptr, "filename", str);
|
||
|
WM_event_add_fileselect(C, op);
|
||
|
|
||
|
return OPERATOR_RUNNING_MODAL;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_save_as(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Save As";
|
||
|
ot->idname= "TEXT_OT_save_as";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= save_as_exec;
|
||
|
ot->invoke= save_as_invoke;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_string_file_path(ot->srna, "filename", "", FILE_MAX, "Filename", "File path to save image to.");
|
||
|
}
|
||
|
|
||
|
/******************* run script operator *********************/
|
||
|
|
||
|
static int run_script_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
#ifdef DISABLE_PYTHON
|
||
|
BKE_report(op->reports, RPT_ERROR, "Python disabled in this build");
|
||
|
|
||
|
return OPERATOR_CANCELLED;
|
||
|
#else
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
char *py_filename;
|
||
|
|
||
|
if(0) { // XXX !BPY_txt_do_python_Text(text)) {
|
||
|
int lineno = 0; // XXX BPY_Err_getLinenumber();
|
||
|
// jump to error if happened in current text:
|
||
|
py_filename = (char*) NULL; // XXX BPY_Err_getFilename();
|
||
|
|
||
|
/* st->text can become NULL: user called Blender.Load(blendfile)
|
||
|
* before the end of the script. */
|
||
|
text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(!strcmp(py_filename, text->id.name+2)) {
|
||
|
// XXX error_pyscript( );
|
||
|
if(lineno >= 0) {
|
||
|
txt_move_toline(text, lineno-1, 0);
|
||
|
txt_sel_line(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
BKE_report(op->reports, RPT_ERROR, "Error in other (possibly external) file, check console.");
|
||
|
}
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_run_script(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Run Script";
|
||
|
ot->idname= "TEXT_OT_run_script";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= run_script_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* refresh pyconstraints operator *********************/
|
||
|
|
||
|
static int refresh_pyconstraints_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
#ifndef DISABLE_PYTHON
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
Scene *scene= CTX_data_scene(C);
|
||
|
Object *ob;
|
||
|
bConstraint *con;
|
||
|
short update;
|
||
|
|
||
|
/* check all pyconstraints */
|
||
|
for(ob= CTX_data_main(C)->object.first; ob; ob= ob->id.next) {
|
||
|
update = 0;
|
||
|
if(ob->type==OB_ARMATURE && ob->pose) {
|
||
|
bPoseChannel *pchan;
|
||
|
for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
|
||
|
for(con = pchan->constraints.first; con; con= con->next) {
|
||
|
if(con->type==CONSTRAINT_TYPE_PYTHON) {
|
||
|
bPythonConstraint *data = con->data;
|
||
|
if(data->text==text) BPY_pyconstraint_update(ob, con);
|
||
|
update = 1;
|
||
|
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
for(con = ob->constraints.first; con; con= con->next) {
|
||
|
if(con->type==CONSTRAINT_TYPE_PYTHON) {
|
||
|
bPythonConstraint *data = con->data;
|
||
|
if(data->text==text) BPY_pyconstraint_update(ob, con);
|
||
|
update = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(update) {
|
||
|
DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_refresh_pyconstraints(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Refresh PyConstraints";
|
||
|
ot->idname= "TEXT_OT_refresh_pyconstraints";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= refresh_pyconstraints_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* paste operator *********************/
|
||
|
|
||
|
static char *txt_copy_selected(Text *text)
|
||
|
{
|
||
|
TextLine *tmp, *linef, *linel;
|
||
|
char *buf= NULL;
|
||
|
int charf, charl, length= 0;
|
||
|
|
||
|
if(!text) return NULL;
|
||
|
if(!text->curl) return NULL;
|
||
|
if(!text->sell) return NULL;
|
||
|
|
||
|
if(!txt_has_sel(text)) return NULL;
|
||
|
|
||
|
if(text->curl==text->sell) {
|
||
|
linef= linel= text->curl;
|
||
|
|
||
|
if(text->curc < text->selc) {
|
||
|
charf= text->curc;
|
||
|
charl= text->selc;
|
||
|
}
|
||
|
else{
|
||
|
charf= text->selc;
|
||
|
charl= text->curc;
|
||
|
}
|
||
|
}
|
||
|
else if(txt_get_span(text->curl, text->sell)<0) {
|
||
|
linef= text->sell;
|
||
|
linel= text->curl;
|
||
|
|
||
|
charf= text->selc;
|
||
|
charl= text->curc;
|
||
|
}
|
||
|
else {
|
||
|
linef= text->curl;
|
||
|
linel= text->sell;
|
||
|
|
||
|
charf= text->curc;
|
||
|
charl= text->selc;
|
||
|
}
|
||
|
|
||
|
if(linef == linel) {
|
||
|
length= charl-charf;
|
||
|
|
||
|
buf= MEM_callocN(length+1, "cut buffera");
|
||
|
|
||
|
BLI_strncpy(buf, linef->line + charf, length+1);
|
||
|
}
|
||
|
else {
|
||
|
length+= linef->len - charf;
|
||
|
length+= charl;
|
||
|
length++; /* For the '\n' */
|
||
|
|
||
|
tmp= linef->next;
|
||
|
while(tmp && tmp!= linel) {
|
||
|
length+= tmp->len+1;
|
||
|
tmp= tmp->next;
|
||
|
}
|
||
|
|
||
|
buf= MEM_callocN(length+1, "cut bufferb");
|
||
|
|
||
|
strncpy(buf, linef->line+ charf, linef->len-charf);
|
||
|
length= linef->len-charf;
|
||
|
|
||
|
buf[length++]='\n';
|
||
|
|
||
|
tmp= linef->next;
|
||
|
while(tmp && tmp!=linel) {
|
||
|
strncpy(buf+length, tmp->line, tmp->len);
|
||
|
length+= tmp->len;
|
||
|
|
||
|
buf[length++]='\n';
|
||
|
|
||
|
tmp= tmp->next;
|
||
|
}
|
||
|
strncpy(buf+length, linel->line, charl);
|
||
|
length+= charl;
|
||
|
|
||
|
buf[length]=0;
|
||
|
}
|
||
|
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
static int paste_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
char *buf;
|
||
|
int selection= RNA_boolean_get(op->ptr, "selection");
|
||
|
|
||
|
buf= WM_clipboard_text_get(selection);
|
||
|
|
||
|
if(!buf)
|
||
|
return OPERATOR_CANCELLED;
|
||
|
|
||
|
txt_insert_buf(text, buf);
|
||
|
text_update_edited(text);
|
||
|
|
||
|
MEM_freeN(buf);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_paste(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Paste";
|
||
|
ot->idname= "TEXT_OT_paste";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= paste_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_boolean(ot->srna, "selection", 0, "Selection", "Paste text selected elsewhere rather than copied, X11 only.");
|
||
|
}
|
||
|
|
||
|
/******************* copy operator *********************/
|
||
|
|
||
|
static void txt_copy_clipboard(Text *text)
|
||
|
{
|
||
|
char *buf;
|
||
|
|
||
|
buf= txt_copy_selected(text);
|
||
|
|
||
|
if(buf) {
|
||
|
WM_clipboard_text_set(buf, 0);
|
||
|
MEM_freeN(buf);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int copy_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
txt_copy_clipboard(text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_copy(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Copy";
|
||
|
ot->idname= "TEXT_OT_copy";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= copy_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* cut operator *********************/
|
||
|
|
||
|
static int cut_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
txt_copy_clipboard(text);
|
||
|
txt_delete_selected(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_cut(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Cut";
|
||
|
ot->idname= "TEXT_OT_cut";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= cut_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* indent operator *********************/
|
||
|
|
||
|
static int indent_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(txt_has_sel(text)) {
|
||
|
txt_order_cursors(text);
|
||
|
indent(text);
|
||
|
}
|
||
|
else
|
||
|
txt_add_char(text, '\t');
|
||
|
|
||
|
text_update_edited(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_indent(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Indent";
|
||
|
ot->idname= "TEXT_OT_indent";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= indent_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* unindent operator *********************/
|
||
|
|
||
|
static int unindent_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(txt_has_sel(text)) {
|
||
|
txt_order_cursors(text);
|
||
|
unindent(text);
|
||
|
|
||
|
text_update_edited(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
return OPERATOR_CANCELLED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_unindent(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Unindent";
|
||
|
ot->idname= "TEXT_OT_unindent";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= unindent_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* line break operator *********************/
|
||
|
|
||
|
static int line_break_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
int a, curtab;
|
||
|
|
||
|
// double check tabs before splitting the line
|
||
|
curtab= setcurr_tab(text);
|
||
|
txt_split_curline(text);
|
||
|
|
||
|
for(a=0; a < curtab; a++)
|
||
|
txt_add_char(text, '\t');
|
||
|
|
||
|
if(text->curl) {
|
||
|
if(text->curl->prev)
|
||
|
text_update_line_edited(text, text->curl->prev);
|
||
|
text_update_line_edited(text, text->curl);
|
||
|
}
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_CANCELLED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_line_break(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Line Break";
|
||
|
ot->idname= "TEXT_OT_line_break";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= line_break_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* comment operator *********************/
|
||
|
|
||
|
static int comment_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(txt_has_sel(text)) {
|
||
|
txt_order_cursors(text);
|
||
|
comment(text);
|
||
|
text_update_edited(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
return OPERATOR_CANCELLED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_comment(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Comment";
|
||
|
ot->idname= "TEXT_OT_comment";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= comment_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* uncomment operator *********************/
|
||
|
|
||
|
static int uncomment_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(txt_has_sel(text)) {
|
||
|
txt_order_cursors(text);
|
||
|
uncomment(text);
|
||
|
text_update_edited(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
return OPERATOR_CANCELLED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_uncomment(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Uncomment";
|
||
|
ot->idname= "TEXT_OT_uncomment";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= uncomment_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* convert whitespace operator *********************/
|
||
|
|
||
|
enum { TO_SPACES, TO_TABS };
|
||
|
static EnumPropertyItem whitespace_type_items[]= {
|
||
|
{TO_SPACES, "SPACES", "To Spaces", NULL},
|
||
|
{TO_TABS, "TABS", "To Tabs", NULL},
|
||
|
{0, NULL, NULL, NULL}};
|
||
|
|
||
|
static int convert_whitespace_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
TextLine *tmp;
|
||
|
FlattenString fs;
|
||
|
size_t a, j;
|
||
|
char *text_check_line, *new_line;
|
||
|
int extra, number; //unknown for now
|
||
|
int type= RNA_enum_get(op->ptr, "type");
|
||
|
|
||
|
tmp = text->lines.first;
|
||
|
|
||
|
//first convert to all space, this make it alot easier to convert to tabs because there is no mixtures of ' ' && '\t'
|
||
|
while(tmp) {
|
||
|
text_check_line = tmp->line;
|
||
|
number = flatten_string(st, &fs, text_check_line)+1;
|
||
|
flatten_string_free(&fs);
|
||
|
new_line = MEM_callocN(number, "Converted_Line");
|
||
|
j = 0;
|
||
|
for(a=0; a < strlen(text_check_line); a++) { //foreach char in line
|
||
|
if(text_check_line[a] == '\t') { //checking for tabs
|
||
|
//get the number of spaces this tabs is showing
|
||
|
//i dont like doing it this way but will look into it later
|
||
|
new_line[j] = '\0';
|
||
|
number = flatten_string(st, &fs, new_line);
|
||
|
flatten_string_free(&fs);
|
||
|
new_line[j] = '\t';
|
||
|
new_line[j+1] = '\0';
|
||
|
number = flatten_string(st, &fs, new_line)-number;
|
||
|
flatten_string_free(&fs);
|
||
|
|
||
|
for(extra = 0; extra < number; extra++) {
|
||
|
new_line[j] = ' ';
|
||
|
j++;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
new_line[j] = text_check_line[a];
|
||
|
++j;
|
||
|
}
|
||
|
}
|
||
|
new_line[j] = '\0';
|
||
|
// put new_line in the tmp->line spot still need to try and set the curc correctly
|
||
|
if(tmp->line) MEM_freeN(tmp->line);
|
||
|
if(tmp->format) MEM_freeN(tmp->format);
|
||
|
|
||
|
tmp->line = new_line;
|
||
|
tmp->len = strlen(new_line);
|
||
|
tmp->format = NULL;
|
||
|
tmp = tmp->next;
|
||
|
}
|
||
|
|
||
|
if(type == TO_TABS) // Converting to tabs
|
||
|
{ //start over from the begining
|
||
|
tmp = text->lines.first;
|
||
|
|
||
|
while(tmp) {
|
||
|
text_check_line = tmp->line;
|
||
|
extra = 0;
|
||
|
for(a = 0; a < strlen(text_check_line); a++) {
|
||
|
number = 0;
|
||
|
for(j = 0; j < (size_t)st->tabnumber; j++) {
|
||
|
if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
|
||
|
if(text_check_line[a+j] != ' ') {
|
||
|
number = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if(!number) { //found all number of space to equal a tab
|
||
|
a = a+(st->tabnumber-1);
|
||
|
extra = extra+1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if( extra > 0 ) { //got tabs make malloc and do what you have to do
|
||
|
new_line = MEM_callocN(strlen(text_check_line)-(((st->tabnumber*extra)-extra)-1), "Converted_Line");
|
||
|
extra = 0; //reuse vars
|
||
|
for(a = 0; a < strlen(text_check_line); a++) {
|
||
|
number = 0;
|
||
|
for(j = 0; j < (size_t)st->tabnumber; j++) {
|
||
|
if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
|
||
|
if(text_check_line[a+j] != ' ') {
|
||
|
number = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if(!number) { //found all number of space to equal a tab
|
||
|
new_line[extra] = '\t';
|
||
|
a = a+(st->tabnumber-1);
|
||
|
++extra;
|
||
|
|
||
|
}
|
||
|
else { //not adding a tab
|
||
|
new_line[extra] = text_check_line[a];
|
||
|
++extra;
|
||
|
}
|
||
|
}
|
||
|
new_line[extra] = '\0';
|
||
|
// put new_line in the tmp->line spot still need to try and set the curc correctly
|
||
|
if(tmp->line) MEM_freeN(tmp->line);
|
||
|
if(tmp->format) MEM_freeN(tmp->format);
|
||
|
|
||
|
tmp->line = new_line;
|
||
|
tmp->len = strlen(new_line);
|
||
|
tmp->format = NULL;
|
||
|
}
|
||
|
tmp = tmp->next;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
text_update_edited(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_convert_whitespace(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Convert Whitespace";
|
||
|
ot->idname= "TEXT_OT_convert_whitespace";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= convert_whitespace_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_enum(ot->srna, "type", whitespace_type_items, TO_SPACES, "type", "Type of whitespace to convert to.");
|
||
|
}
|
||
|
|
||
|
/******************* select all operator *********************/
|
||
|
|
||
|
static int select_all_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
txt_sel_all(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_select_all(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Select All";
|
||
|
ot->idname= "TEXT_OT_select_all";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= select_all_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* select line operator *********************/
|
||
|
|
||
|
static int select_line_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
txt_sel_line(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_select_line(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Select Line";
|
||
|
ot->idname= "TEXT_OT_select_line";
|
||
|
|
||
|
/* api clinebacks */
|
||
|
ot->exec= select_line_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* previous marker operator *********************/
|
||
|
|
||
|
static int previous_marker_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
TextMarker *mrk;
|
||
|
int lineno;
|
||
|
|
||
|
lineno= txt_get_span(text->lines.first, text->curl);
|
||
|
mrk= text->markers.last;
|
||
|
while(mrk && (mrk->lineno>lineno || (mrk->lineno==lineno && mrk->end > text->curc)))
|
||
|
mrk= mrk->prev;
|
||
|
if(!mrk) mrk= text->markers.last;
|
||
|
if(mrk) {
|
||
|
txt_move_to(text, mrk->lineno, mrk->start, 0);
|
||
|
txt_move_to(text, mrk->lineno, mrk->end, 1);
|
||
|
}
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_previous_marker(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Previous Marker";
|
||
|
ot->idname= "TEXT_OT_previous_marker";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= previous_marker_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* next marker operator *********************/
|
||
|
|
||
|
static int next_marker_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
TextMarker *mrk;
|
||
|
int lineno;
|
||
|
|
||
|
lineno= txt_get_span(text->lines.first, text->curl);
|
||
|
mrk= text->markers.first;
|
||
|
while(mrk && (mrk->lineno<lineno || (mrk->lineno==lineno && mrk->start <= text->curc)))
|
||
|
mrk= mrk->next;
|
||
|
if(!mrk) mrk= text->markers.first;
|
||
|
if(mrk) {
|
||
|
txt_move_to(text, mrk->lineno, mrk->start, 0);
|
||
|
txt_move_to(text, mrk->lineno, mrk->end, 1);
|
||
|
}
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_next_marker(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Next Marker";
|
||
|
ot->idname= "TEXT_OT_next_marker";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= next_marker_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* clear all markers operator *********************/
|
||
|
|
||
|
static int clear_all_markers_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
txt_clear_markers(text, 0, 0);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_clear_all_markers(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Clear All Markers";
|
||
|
ot->idname= "TEXT_OT_clear_all_markers";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= clear_all_markers_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/************************ move operator ************************/
|
||
|
|
||
|
static EnumPropertyItem move_type_items[]= {
|
||
|
{LINE_BEGIN, "LINE_BEGIN", "Line Begin", ""},
|
||
|
{LINE_END, "LINE_END", "Line End", ""},
|
||
|
{FILE_TOP, "FILE_TOP", "File Top", ""},
|
||
|
{FILE_BOTTOM, "FILE_BOTTOM", "File Bottom", ""},
|
||
|
{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 void wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
|
||
|
{
|
||
|
Text *text= st->text;
|
||
|
int offl, offc, lin;
|
||
|
|
||
|
lin= txt_get_span(text->lines.first, text->sell);
|
||
|
wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
|
||
|
|
||
|
if (sel) {
|
||
|
txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, -offc);
|
||
|
text->selc= -offc;
|
||
|
} else {
|
||
|
txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, -offc);
|
||
|
text->curc= -offc;
|
||
|
txt_pop_sel(text);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
|
||
|
{
|
||
|
Text *text= st->text;
|
||
|
int offl, offc, lin, startl, c;
|
||
|
|
||
|
lin= txt_get_span(text->lines.first, text->sell);
|
||
|
wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
|
||
|
startl= offl;
|
||
|
c= text->selc;
|
||
|
while (offl==startl && text->sell->line[c]!='\0') {
|
||
|
c++;
|
||
|
wrap_offset(st, ar, text->sell, c, &offl, &offc);
|
||
|
} if (offl!=startl) c--;
|
||
|
|
||
|
if (sel) {
|
||
|
txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, c);
|
||
|
text->selc= c;
|
||
|
} else {
|
||
|
txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, c);
|
||
|
text->curc= c;
|
||
|
txt_pop_sel(text);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void wrap_move_up(SpaceText *st, ARegion *ar, short sel)
|
||
|
{
|
||
|
Text *text= st->text;
|
||
|
int offl, offl_1, offc, fromline, toline, c, target;
|
||
|
|
||
|
wrap_offset(st, ar, text->sell, 0, &offl_1, &offc);
|
||
|
wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
|
||
|
fromline= toline= txt_get_span(text->lines.first, text->sell);
|
||
|
target= text->selc + offc;
|
||
|
|
||
|
if (offl==offl_1) {
|
||
|
if (!text->sell->prev) {
|
||
|
txt_move_bol(text, sel);
|
||
|
return;
|
||
|
}
|
||
|
toline--;
|
||
|
c= text->sell->prev->len; /* End of prev. line */
|
||
|
wrap_offset(st, ar, text->sell->prev, c, &offl, &offc);
|
||
|
c= -offc+target;
|
||
|
} else {
|
||
|
c= -offc-1; /* End of prev. line */
|
||
|
wrap_offset(st, ar, text->sell, c, &offl, &offc);
|
||
|
c= -offc+target;
|
||
|
}
|
||
|
if (c<0) c=0;
|
||
|
|
||
|
if (sel) {
|
||
|
txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
|
||
|
if (toline<fromline) text->sell= text->sell->prev;
|
||
|
if(text->sell) {
|
||
|
if (c>text->sell->len) c= text->sell->len;
|
||
|
text->selc= c;
|
||
|
}
|
||
|
}
|
||
|
else if(text->curl) {
|
||
|
txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
|
||
|
if (toline<fromline) text->curl= text->curl->prev;
|
||
|
if(text->curl) {
|
||
|
if (c>text->curl->len) c= text->curl->len;
|
||
|
text->curc= c;
|
||
|
txt_pop_sel(text);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void wrap_move_down(SpaceText *st, ARegion *ar, short sel)
|
||
|
{
|
||
|
Text *text= st->text;
|
||
|
int offl, startoff, offc, fromline, toline, c, target;
|
||
|
|
||
|
wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
|
||
|
fromline= toline= txt_get_span(text->lines.first, text->sell);
|
||
|
target= text->selc + offc;
|
||
|
startoff= offl;
|
||
|
c= text->selc;
|
||
|
while (offl==startoff && text->sell->line[c]!='\0') {
|
||
|
c++;
|
||
|
wrap_offset(st, ar, text->sell, c, &offl, &offc);
|
||
|
}
|
||
|
|
||
|
if (text->sell->line[c]=='\0') {
|
||
|
if (!text->sell->next) {
|
||
|
txt_move_eol(text, sel);
|
||
|
return;
|
||
|
}
|
||
|
toline++;
|
||
|
c= target;
|
||
|
} else {
|
||
|
c += target;
|
||
|
if (c > text->sell->len) c= text->sell->len;
|
||
|
}
|
||
|
if (c<0) c=0;
|
||
|
|
||
|
if (sel) {
|
||
|
txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
|
||
|
if (toline>fromline) text->sell= text->sell->next;
|
||
|
if(text->sell) {
|
||
|
if (c>text->sell->len) c= text->sell->len;
|
||
|
text->selc= c;
|
||
|
}
|
||
|
}
|
||
|
else if(text->curl) {
|
||
|
txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
|
||
|
if (toline>fromline) text->curl= text->curl->next;
|
||
|
if(text->curl) {
|
||
|
if (c > text->curl->len) c= text->curl->len;
|
||
|
text->curc= c;
|
||
|
txt_pop_sel(text);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* Moves the cursor vertically by the specified number of lines.
|
||
|
If the destination line is shorter than the current cursor position, the
|
||
|
cursor will be positioned at the end of this line.
|
||
|
|
||
|
This is to replace screen_skip for PageUp/Down operations.
|
||
|
*/
|
||
|
static void cursor_skip(Text *text, int lines, int sel)
|
||
|
{
|
||
|
TextLine **linep;
|
||
|
int oldl, oldc, *charp;
|
||
|
|
||
|
if (sel) linep= &text->sell, charp= &text->selc;
|
||
|
else linep= &text->curl, charp= &text->curc;
|
||
|
oldl= txt_get_span(text->lines.first, *linep);
|
||
|
oldc= *charp;
|
||
|
|
||
|
while (lines>0 && (*linep)->next) {
|
||
|
*linep= (*linep)->next;
|
||
|
lines--;
|
||
|
}
|
||
|
while (lines<0 && (*linep)->prev) {
|
||
|
*linep= (*linep)->prev;
|
||
|
lines++;
|
||
|
}
|
||
|
|
||
|
if (*charp > (*linep)->len) *charp= (*linep)->len;
|
||
|
|
||
|
if (!sel) txt_pop_sel(text);
|
||
|
txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
|
||
|
}
|
||
|
|
||
|
static int move_cursor(bContext *C, int type, int select)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
ARegion *ar= CTX_wm_region(C);
|
||
|
|
||
|
/* ensure we have the right region, it's optional */
|
||
|
if(ar->regiontype != RGN_TYPE_WINDOW)
|
||
|
ar= NULL;
|
||
|
|
||
|
switch(type) {
|
||
|
case LINE_BEGIN:
|
||
|
if(st && st->wordwrap && ar) wrap_move_bol(st, ar, select);
|
||
|
else txt_move_bol(text, select);
|
||
|
break;
|
||
|
|
||
|
case LINE_END:
|
||
|
if(st && st->wordwrap && ar) wrap_move_eol(st, ar, select);
|
||
|
else txt_move_eol(text, select);
|
||
|
break;
|
||
|
|
||
|
case FILE_TOP:
|
||
|
txt_move_bof(text, select);
|
||
|
break;
|
||
|
|
||
|
case FILE_BOTTOM:
|
||
|
txt_move_eof(text, select);
|
||
|
break;
|
||
|
|
||
|
case PREV_WORD:
|
||
|
txt_jump_left(text, select);
|
||
|
break;
|
||
|
|
||
|
case NEXT_WORD:
|
||
|
txt_jump_right(text, select);
|
||
|
break;
|
||
|
|
||
|
case PREV_CHAR:
|
||
|
txt_move_left(text, select);
|
||
|
break;
|
||
|
|
||
|
case NEXT_CHAR:
|
||
|
txt_move_right(text, select);
|
||
|
break;
|
||
|
|
||
|
case PREV_LINE:
|
||
|
if(st && st->wordwrap && ar) wrap_move_up(st, ar, select);
|
||
|
else txt_move_up(text, select);
|
||
|
break;
|
||
|
|
||
|
case NEXT_LINE:
|
||
|
if(st && st->wordwrap && ar) wrap_move_down(st, ar, select);
|
||
|
else txt_move_down(text, select);
|
||
|
break;
|
||
|
|
||
|
case PREV_PAGE:
|
||
|
if(st) cursor_skip(text, -st->viewlines, select);
|
||
|
else cursor_skip(text, -10, select);
|
||
|
break;
|
||
|
|
||
|
case NEXT_PAGE:
|
||
|
if(st) cursor_skip(text, st->viewlines, select);
|
||
|
else cursor_skip(text, 10, select);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
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 TEXT_OT_move(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Move Cursor";
|
||
|
ot->idname= "TEXT_OT_move";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= move_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
|
||
|
/* 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 TEXT_OT_move_select(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Move Select";
|
||
|
ot->idname= "TEXT_OT_move_select";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= move_select_exec;
|
||
|
ot->poll= text_space_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection.");
|
||
|
}
|
||
|
|
||
|
/******************* jump operator *********************/
|
||
|
|
||
|
static int jump_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
int line= RNA_int_get(op->ptr, "line");
|
||
|
short nlines= txt_get_span(text->lines.first, text->lines.last)+1;
|
||
|
|
||
|
if(line < 1 || line > nlines)
|
||
|
return OPERATOR_CANCELLED;
|
||
|
|
||
|
txt_move_toline(text, line-1, 0);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
// XXX invoke
|
||
|
// short tmp= txt_get_span(text->lines.first, text->curl)+1;
|
||
|
// button(&tmp, 1, nlines, "Jump to line:"))
|
||
|
|
||
|
void TEXT_OT_jump(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Jump";
|
||
|
ot->idname= "TEXT_OT_jump";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= jump_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_int(ot->srna, "line", 1, INT_MAX, 1, "Line", "Line number to jump to.", 1, 10000);
|
||
|
}
|
||
|
|
||
|
/******************* delete operator **********************/
|
||
|
|
||
|
static EnumPropertyItem delete_type_items[]= {
|
||
|
{DEL_NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""},
|
||
|
{DEL_PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""},
|
||
|
{DEL_NEXT_WORD, "NEXT_WORD", "Next Word", ""},
|
||
|
{DEL_PREV_WORD, "PREVIOUS_WORD", "Previous Word", ""},
|
||
|
{0, NULL, NULL, NULL}};
|
||
|
|
||
|
static int delete_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
int type= RNA_enum_get(op->ptr, "type");
|
||
|
|
||
|
if(type == DEL_PREV_WORD)
|
||
|
txt_backspace_word(text);
|
||
|
else if(type == DEL_PREV_CHAR)
|
||
|
txt_backspace_char(text);
|
||
|
else if(type == DEL_NEXT_WORD)
|
||
|
txt_delete_word(text);
|
||
|
else if(type == DEL_NEXT_CHAR)
|
||
|
txt_delete_char(text);
|
||
|
|
||
|
text_update_line_edited(text, text->curl);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_delete(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Delete";
|
||
|
ot->idname= "TEXT_OT_delete";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= delete_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete.");
|
||
|
}
|
||
|
|
||
|
/******************* toggle overwrite operator **********************/
|
||
|
|
||
|
static int toggle_overwrite_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
|
||
|
st->overwrite= !st->overwrite;
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_toggle_overwrite(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Toggle Overwrite";
|
||
|
ot->idname= "TEXT_OT_toggle_overwrite";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= toggle_overwrite_exec;
|
||
|
ot->poll= text_space_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
}
|
||
|
|
||
|
/******************* scroll operator **********************/
|
||
|
|
||
|
/* Moves the view vertically by the specified number of lines */
|
||
|
static void screen_skip(SpaceText *st, int lines)
|
||
|
{
|
||
|
int last;
|
||
|
|
||
|
st->top += lines;
|
||
|
|
||
|
last= txt_get_span(st->text->lines.first, st->text->lines.last);
|
||
|
last= last - (st->viewlines/2);
|
||
|
|
||
|
if(st->top>last) st->top= last;
|
||
|
if(st->top<0) st->top= 0;
|
||
|
}
|
||
|
|
||
|
typedef struct TextScroll {
|
||
|
short old[2];
|
||
|
short hold[2];
|
||
|
short delta[2];
|
||
|
|
||
|
int first;
|
||
|
int characters;
|
||
|
int lines;
|
||
|
int scrollbar;
|
||
|
} TextScroll;
|
||
|
|
||
|
static int scroll_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
int lines= RNA_int_get(op->ptr, "lines");
|
||
|
|
||
|
if(lines == 0)
|
||
|
return OPERATOR_CANCELLED;
|
||
|
|
||
|
screen_skip(st, lines*U.wheellinescroll);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, st->text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
static int scroll_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
TextScroll *tsc;
|
||
|
|
||
|
if(RNA_property_is_set(op->ptr, "lines"))
|
||
|
return scroll_exec(C, op);
|
||
|
|
||
|
tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
|
||
|
tsc->first= 1;
|
||
|
op->customdata= tsc;
|
||
|
|
||
|
st->flags|= ST_SCROLL_SELECT;
|
||
|
|
||
|
WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
|
||
|
|
||
|
return OPERATOR_RUNNING_MODAL;
|
||
|
}
|
||
|
|
||
|
static void scroll_apply(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
TextScroll *tsc= op->customdata;
|
||
|
short *mval= event->mval;
|
||
|
|
||
|
if(tsc->first) {
|
||
|
tsc->old[0]= mval[0];
|
||
|
tsc->old[1]= mval[1];
|
||
|
tsc->hold[0]= mval[0];
|
||
|
tsc->hold[1]= mval[1];
|
||
|
tsc->first= 0;
|
||
|
}
|
||
|
|
||
|
if(!tsc->scrollbar) {
|
||
|
tsc->delta[0]= (tsc->hold[0]-mval[0])/text_font_width_character(st);
|
||
|
tsc->delta[1]= (mval[1]-tsc->hold[1])/st->lheight;
|
||
|
}
|
||
|
else
|
||
|
tsc->delta[1]= (tsc->hold[1]-mval[1])*st->pix_per_line;
|
||
|
|
||
|
if(tsc->delta[0] || tsc->delta[1]) {
|
||
|
screen_skip(st, tsc->delta[1]);
|
||
|
|
||
|
tsc->lines += tsc->delta[1];
|
||
|
|
||
|
if(st->wordwrap) {
|
||
|
st->left= 0;
|
||
|
}
|
||
|
else {
|
||
|
st->left+= tsc->delta[0];
|
||
|
if(st->left<0) st->left= 0;
|
||
|
}
|
||
|
|
||
|
tsc->hold[0]= mval[0];
|
||
|
tsc->hold[1]= mval[1];
|
||
|
|
||
|
ED_area_tag_redraw(CTX_wm_area(C));
|
||
|
}
|
||
|
|
||
|
tsc->old[0]= mval[0];
|
||
|
tsc->old[1]= mval[1];
|
||
|
}
|
||
|
|
||
|
static void scroll_exit(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
|
||
|
st->flags &= ~ST_SCROLL_SELECT;
|
||
|
MEM_freeN(op->customdata);
|
||
|
}
|
||
|
|
||
|
static int scroll_modal(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
switch(event->type) {
|
||
|
case MOUSEMOVE:
|
||
|
scroll_apply(C, op, event);
|
||
|
break;
|
||
|
case LEFTMOUSE:
|
||
|
case RIGHTMOUSE:
|
||
|
case MIDDLEMOUSE:
|
||
|
scroll_exit(C, op);
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
return OPERATOR_RUNNING_MODAL;
|
||
|
}
|
||
|
|
||
|
static int scroll_cancel(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
scroll_exit(C, op);
|
||
|
|
||
|
return OPERATOR_CANCELLED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_scroll(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Scroll";
|
||
|
ot->idname= "TEXT_OT_scroll";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= scroll_exec;
|
||
|
ot->invoke= scroll_invoke;
|
||
|
ot->modal= scroll_modal;
|
||
|
ot->cancel= scroll_cancel;
|
||
|
ot->poll= text_space_edit_poll;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_int(ot->srna, "lines", INT_MIN, INT_MAX, 1, "Lines", "Number of lines to scroll.", -100, 100);
|
||
|
}
|
||
|
|
||
|
/******************** scroll bar operator *******************/
|
||
|
|
||
|
static int scroll_bar_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
ARegion *ar= CTX_wm_region(C);
|
||
|
TextScroll *tsc;
|
||
|
short *mval= event->mval;
|
||
|
|
||
|
if(RNA_property_is_set(op->ptr, "lines"))
|
||
|
return scroll_exec(C, op);
|
||
|
|
||
|
/* verify we are in the right zone */
|
||
|
if(!(mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<ar->winy))
|
||
|
return OPERATOR_PASS_THROUGH;
|
||
|
|
||
|
tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
|
||
|
tsc->first= 1;
|
||
|
tsc->scrollbar= 1;
|
||
|
op->customdata= tsc;
|
||
|
|
||
|
st->flags|= ST_SCROLL_SELECT;
|
||
|
|
||
|
WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
|
||
|
|
||
|
return OPERATOR_RUNNING_MODAL;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_scroll_bar(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Scrollbar";
|
||
|
ot->idname= "TEXT_OT_scroll_bar";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->invoke= scroll_bar_invoke;
|
||
|
ot->modal= scroll_modal;
|
||
|
ot->cancel= scroll_cancel;
|
||
|
ot->poll= text_region_edit_poll;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_int(ot->srna, "lines", INT_MIN, INT_MAX, 1, "Lines", "Number of lines to scroll.", -100, 100);
|
||
|
}
|
||
|
|
||
|
/******************* set cursor operator **********************/
|
||
|
|
||
|
typedef struct SetCursor {
|
||
|
int selecting;
|
||
|
int selc, sell;
|
||
|
short old[2];
|
||
|
} SetCursor;
|
||
|
|
||
|
static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
|
||
|
{
|
||
|
FlattenString fs;
|
||
|
Text *text= st->text;
|
||
|
TextLine **linep;
|
||
|
int *charp;
|
||
|
int w;
|
||
|
|
||
|
if(sel) { linep= &text->sell; charp= &text->selc; }
|
||
|
else { linep= &text->curl; charp= &text->curc; }
|
||
|
|
||
|
y= (ar->winy - y)/st->lheight;
|
||
|
|
||
|
if(st->showlinenrs)
|
||
|
x-= TXT_OFFSET+TEXTXLOC;
|
||
|
else
|
||
|
x-= TXT_OFFSET;
|
||
|
|
||
|
if(x<0) x= 0;
|
||
|
x = (x/text_font_width_character(st)) + st->left;
|
||
|
|
||
|
if(st->wordwrap) {
|
||
|
int i, j, endj, curs, max, chop, start, end, chars, loop;
|
||
|
char ch;
|
||
|
|
||
|
/* Point to first visible line */
|
||
|
*linep= text->lines.first;
|
||
|
for(i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;
|
||
|
|
||
|
max= wrap_width(st, ar);
|
||
|
|
||
|
loop= 1;
|
||
|
while(loop && *linep) {
|
||
|
start= 0;
|
||
|
end= max;
|
||
|
chop= 1;
|
||
|
chars= 0;
|
||
|
curs= 0;
|
||
|
endj= 0;
|
||
|
for(i=0, j=0; loop; j++) {
|
||
|
|
||
|
/* Mimic replacement of tabs */
|
||
|
ch= (*linep)->line[j];
|
||
|
if(ch=='\t') {
|
||
|
chars= st->tabnumber-i%st->tabnumber;
|
||
|
ch= ' ';
|
||
|
}
|
||
|
else
|
||
|
chars= 1;
|
||
|
|
||
|
while(chars--) {
|
||
|
/* Gone too far, go back to last wrap point */
|
||
|
if(y<0) {
|
||
|
*charp= endj;
|
||
|
loop= 0;
|
||
|
break;
|
||
|
/* Exactly at the cursor, done */
|
||
|
}
|
||
|
else if(y==0 && i-start==x) {
|
||
|
*charp= curs= j;
|
||
|
loop= 0;
|
||
|
break;
|
||
|
/* Prepare curs for next wrap */
|
||
|
}
|
||
|
else if(i-end==x) {
|
||
|
curs= j;
|
||
|
}
|
||
|
if(i-start>=max) {
|
||
|
if(chop) endj= j;
|
||
|
y--;
|
||
|
start= end;
|
||
|
end += max;
|
||
|
chop= 1;
|
||
|
if(y==0 && i-start>=x) {
|
||
|
*charp= curs;
|
||
|
loop= 0;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else if(ch==' ' || ch=='-' || ch=='\0') {
|
||
|
if(y==0 && i-start>=x) {
|
||
|
*charp= curs;
|
||
|
loop= 0;
|
||
|
break;
|
||
|
}
|
||
|
end = i+1;
|
||
|
endj = j;
|
||
|
chop= 0;
|
||
|
}
|
||
|
i++;
|
||
|
}
|
||
|
if(ch=='\0') break;
|
||
|
}
|
||
|
if(!loop || y<0) break;
|
||
|
|
||
|
if(!(*linep)->next) {
|
||
|
*charp= (*linep)->len;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
/* On correct line but didn't meet cursor, must be at end */
|
||
|
if(y==0) {
|
||
|
*charp= (*linep)->len;
|
||
|
break;
|
||
|
}
|
||
|
*linep= (*linep)->next;
|
||
|
y--;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
else {
|
||
|
y-= txt_get_span(text->lines.first, *linep) - st->top;
|
||
|
|
||
|
if(y>0) {
|
||
|
while(y-- != 0) if((*linep)->next) *linep= (*linep)->next;
|
||
|
}
|
||
|
else if(y<0) {
|
||
|
while(y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
|
||
|
}
|
||
|
|
||
|
|
||
|
w= flatten_string(st, &fs, (*linep)->line);
|
||
|
if(x<w) *charp= fs.accum[x];
|
||
|
else *charp= (*linep)->len;
|
||
|
flatten_string_free(&fs);
|
||
|
}
|
||
|
if(!sel) txt_pop_sel(text);
|
||
|
}
|
||
|
|
||
|
static void set_cursor_apply(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
ARegion *ar= CTX_wm_region(C);
|
||
|
SetCursor *scu= op->customdata;
|
||
|
|
||
|
if(event->mval[1]<0 || event->mval[1]>ar->winy) {
|
||
|
int d= (scu->old[1]-event->mval[1])*st->pix_per_line;
|
||
|
if(d) screen_skip(st, d);
|
||
|
|
||
|
set_cursor_to_pos(st, ar, event->mval[0], event->mval[1]<0?0:ar->winy, 1);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
|
||
|
}
|
||
|
else if(!st->wordwrap && (event->mval[0]<0 || event->mval[0]>ar->winx)) {
|
||
|
if(event->mval[0]>ar->winx) st->left++;
|
||
|
else if(event->mval[0]<0 && st->left>0) st->left--;
|
||
|
|
||
|
set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
|
||
|
// XXX PIL_sleep_ms(10);
|
||
|
}
|
||
|
else {
|
||
|
set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
|
||
|
|
||
|
scu->old[0]= event->mval[0];
|
||
|
scu->old[1]= event->mval[1];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void set_cursor_exit(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text= st->text;
|
||
|
SetCursor *scu= op->customdata;
|
||
|
int linep2, charp2;
|
||
|
char *buffer;
|
||
|
|
||
|
if(txt_has_sel(text)) {
|
||
|
buffer = txt_sel_to_buf(text);
|
||
|
WM_clipboard_text_set(buffer, 1);
|
||
|
MEM_freeN(buffer);
|
||
|
}
|
||
|
|
||
|
linep2= txt_get_span(st->text->lines.first, st->text->sell);
|
||
|
charp2= st->text->selc;
|
||
|
|
||
|
if(scu->sell!=linep2 || scu->selc!=charp2)
|
||
|
txt_undo_add_toop(st->text, UNDO_STO, scu->sell, scu->selc, linep2, charp2);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
|
||
|
|
||
|
MEM_freeN(scu);
|
||
|
}
|
||
|
|
||
|
static int set_cursor_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
ARegion *ar= CTX_wm_region(C);
|
||
|
SetCursor *scu;
|
||
|
|
||
|
op->customdata= MEM_callocN(sizeof(SetCursor), "SetCursor");
|
||
|
scu= op->customdata;
|
||
|
scu->selecting= RNA_boolean_get(op->ptr, "select");
|
||
|
|
||
|
scu->old[0]= event->mval[0];
|
||
|
scu->old[1]= event->mval[1];
|
||
|
|
||
|
if(!scu->selecting) {
|
||
|
int curl= txt_get_span(st->text->lines.first, st->text->curl);
|
||
|
int curc= st->text->curc;
|
||
|
int linep2, charp2;
|
||
|
|
||
|
set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 0);
|
||
|
|
||
|
linep2= txt_get_span(st->text->lines.first, st->text->curl);
|
||
|
charp2= st->text->selc;
|
||
|
|
||
|
if(curl!=linep2 || curc!=charp2)
|
||
|
txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
|
||
|
}
|
||
|
|
||
|
scu->sell= txt_get_span(st->text->lines.first, st->text->sell);
|
||
|
scu->selc= st->text->selc;
|
||
|
|
||
|
WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
|
||
|
|
||
|
set_cursor_apply(C, op, event);
|
||
|
|
||
|
return OPERATOR_RUNNING_MODAL;
|
||
|
}
|
||
|
|
||
|
static int set_cursor_modal(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
switch(event->type) {
|
||
|
case LEFTMOUSE:
|
||
|
case MIDDLEMOUSE:
|
||
|
case RIGHTMOUSE:
|
||
|
set_cursor_exit(C, op);
|
||
|
return OPERATOR_FINISHED;
|
||
|
case MOUSEMOVE:
|
||
|
set_cursor_apply(C, op, event);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return OPERATOR_RUNNING_MODAL;
|
||
|
}
|
||
|
|
||
|
static int set_cursor_cancel(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
set_cursor_exit(C, op);
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_set_cursor(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Set Cursor";
|
||
|
ot->idname= "TEXT_OT_set_cursor";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->invoke= set_cursor_invoke;
|
||
|
ot->modal= set_cursor_modal;
|
||
|
ot->cancel= set_cursor_cancel;
|
||
|
ot->poll= text_region_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_boolean(ot->srna, "select", 0, "Select", "Set selection end rather than cursor.");
|
||
|
}
|
||
|
|
||
|
/******************* line number operator **********************/
|
||
|
|
||
|
static int line_number_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
ARegion *ar= CTX_wm_region(C);
|
||
|
short *mval= event->mval;
|
||
|
double time;
|
||
|
static int jump_to= 0;
|
||
|
static double last_jump= 0;
|
||
|
|
||
|
if(!st->showlinenrs)
|
||
|
return OPERATOR_PASS_THROUGH;
|
||
|
|
||
|
if(!(mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<ar->winy-2))
|
||
|
return OPERATOR_PASS_THROUGH;
|
||
|
|
||
|
if(!(event->ascii>='0' && event->ascii<='9'))
|
||
|
return OPERATOR_PASS_THROUGH;
|
||
|
|
||
|
time = PIL_check_seconds_timer();
|
||
|
if(last_jump < time-1)
|
||
|
jump_to= 0;
|
||
|
|
||
|
jump_to *= 10;
|
||
|
jump_to += (int)(event->ascii-'0');
|
||
|
|
||
|
txt_move_toline(text, jump_to-1, 0);
|
||
|
last_jump= time;
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_line_number(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Line Number";
|
||
|
ot->idname= "TEXT_OT_line_number";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->invoke= line_number_invoke;
|
||
|
ot->poll= text_region_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* insert operator **********************/
|
||
|
|
||
|
static int insert_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
char *str;
|
||
|
int done, ascii;
|
||
|
|
||
|
str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
|
||
|
ascii= str[0];
|
||
|
MEM_freeN(str);
|
||
|
|
||
|
if(!ascii)
|
||
|
return OPERATOR_CANCELLED;
|
||
|
|
||
|
if(st && st->overwrite)
|
||
|
done= txt_replace_char(text, ascii);
|
||
|
else
|
||
|
done= txt_add_char(text, ascii);
|
||
|
|
||
|
if(!done)
|
||
|
return OPERATOR_CANCELLED;
|
||
|
|
||
|
text_update_line_edited(text, text->curl);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
static int insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
char str[2];
|
||
|
|
||
|
/* XXX old code from winqreadtextspace, is it still needed somewhere? */
|
||
|
/* smartass code to prevent the CTRL/ALT events below from not working! */
|
||
|
/*if(qual & (LR_ALTKEY|LR_CTRLKEY))
|
||
|
if(!ispunct(ascii))
|
||
|
ascii= 0;*/
|
||
|
|
||
|
str[0]= event->ascii;
|
||
|
str[1]= '\0';
|
||
|
|
||
|
RNA_string_set(op->ptr, "text", str);
|
||
|
|
||
|
return insert_exec(C, op);
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_insert(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Insert";
|
||
|
ot->idname= "TEXT_OT_insert";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= insert_exec;
|
||
|
ot->invoke= insert_invoke;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
|
||
|
}
|
||
|
|
||
|
/******************* find operator *********************/
|
||
|
|
||
|
/* mode */
|
||
|
#define TEXT_FIND 0
|
||
|
#define TEXT_REPLACE 1
|
||
|
#define TEXT_MARK_ALL 2
|
||
|
|
||
|
static int find_and_replace(bContext *C, wmOperator *op, short mode)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *start= NULL, *text= st->text;
|
||
|
int flags, first= 1;
|
||
|
char *tmp;
|
||
|
|
||
|
if(!st->findstr[0] || (mode == TEXT_REPLACE && !st->replacestr[0]))
|
||
|
return OPERATOR_CANCELLED;
|
||
|
|
||
|
flags= st->flags;
|
||
|
if(flags & ST_FIND_ALL)
|
||
|
flags ^= ST_FIND_WRAP;
|
||
|
|
||
|
do {
|
||
|
if(first)
|
||
|
txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
|
||
|
|
||
|
first= 0;
|
||
|
|
||
|
/* Replace current */
|
||
|
if(mode!=TEXT_FIND && txt_has_sel(text)) {
|
||
|
tmp= txt_sel_to_buf(text);
|
||
|
|
||
|
if(strcmp(st->findstr, tmp)==0) {
|
||
|
if(mode==TEXT_REPLACE) {
|
||
|
txt_insert_buf(text, st->replacestr);
|
||
|
if(text->curl && text->curl->format) {
|
||
|
MEM_freeN(text->curl->format);
|
||
|
text->curl->format= NULL;
|
||
|
}
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
}
|
||
|
else if(mode==TEXT_MARK_ALL) {
|
||
|
char color[4];
|
||
|
UI_GetThemeColor4ubv(TH_SHADE2, color);
|
||
|
|
||
|
if(txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
|
||
|
if(tmp) MEM_freeN(tmp), tmp=NULL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
}
|
||
|
}
|
||
|
MEM_freeN(tmp);
|
||
|
tmp= NULL;
|
||
|
}
|
||
|
|
||
|
/* Find next */
|
||
|
if(txt_find_string(text, st->findstr, flags & ST_FIND_WRAP)) {
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
}
|
||
|
else if(flags & ST_FIND_ALL) {
|
||
|
if(text==start) break;
|
||
|
if(!start) start= text;
|
||
|
if(text->id.next)
|
||
|
text= st->text= text->id.next;
|
||
|
else
|
||
|
text= st->text= G.main->text.first;
|
||
|
txt_move_toline(text, 0, 0);
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
first= 1;
|
||
|
}
|
||
|
else {
|
||
|
BKE_reportf(op->reports, RPT_INFO, "Text not found: %s", st->findstr);
|
||
|
break;
|
||
|
}
|
||
|
} while(mode==TEXT_MARK_ALL);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
static int find_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
return find_and_replace(C, op, TEXT_FIND);
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_find(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Find";
|
||
|
ot->idname= "TEXT_OT_find";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= find_exec;
|
||
|
ot->poll= text_space_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* replace operator *********************/
|
||
|
|
||
|
static int replace_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
return find_and_replace(C, op, TEXT_REPLACE);
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_replace(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Replace";
|
||
|
ot->idname= "TEXT_OT_replace";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= replace_exec;
|
||
|
ot->poll= text_space_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* mark all operator *********************/
|
||
|
|
||
|
static int mark_all_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
return find_and_replace(C, op, TEXT_MARK_ALL);
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_mark_all(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Mark All";
|
||
|
ot->idname= "TEXT_OT_mark_all";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= mark_all_exec;
|
||
|
ot->poll= text_space_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* find set selected *********************/
|
||
|
|
||
|
static int find_set_selected_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
char *tmp;
|
||
|
|
||
|
tmp= txt_sel_to_buf(text);
|
||
|
BLI_strncpy(st->findstr, tmp, ST_MAX_FIND_STR);
|
||
|
MEM_freeN(tmp);
|
||
|
|
||
|
if(!st->findstr[0])
|
||
|
return OPERATOR_FINISHED;
|
||
|
|
||
|
return find_and_replace(C, op, TEXT_FIND);
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_find_set_selected(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Find Set Selected";
|
||
|
ot->idname= "TEXT_OT_find_set_selected";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= find_set_selected_exec;
|
||
|
ot->poll= text_space_edit_poll;
|
||
|
}
|
||
|
|
||
|
/******************* replace set selected *********************/
|
||
|
|
||
|
static int replace_set_selected_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
SpaceText *st= CTX_wm_space_text(C);
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
char *tmp;
|
||
|
|
||
|
tmp= txt_sel_to_buf(text);
|
||
|
BLI_strncpy(st->replacestr, tmp, ST_MAX_FIND_STR);
|
||
|
MEM_freeN(tmp);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_replace_set_selected(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Replace Set Selected";
|
||
|
ot->idname= "TEXT_OT_replace_set_selected";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= replace_set_selected_exec;
|
||
|
ot->poll= text_space_edit_poll;
|
||
|
}
|
||
|
|
||
|
/****************** resolve conflict operator ******************/
|
||
|
|
||
|
enum { RESOLVE_IGNORE, RESOLVE_RELOAD, RESOLVE_SAVE, RESOLVE_MAKE_INTERNAL };
|
||
|
static EnumPropertyItem resolution_items[]= {
|
||
|
{RESOLVE_IGNORE, "IGNORE", "Ignore", ""},
|
||
|
{RESOLVE_RELOAD, "RELOAD", "Reload", ""},
|
||
|
{RESOLVE_SAVE, "SAVE", "Save", ""},
|
||
|
{RESOLVE_MAKE_INTERNAL, "MAKE_INTERNAL", "Make Internal", ""},
|
||
|
{0, NULL, NULL, NULL}};
|
||
|
|
||
|
/* returns 0 if file on disk is the same or Text is in memory only
|
||
|
returns 1 if file has been modified on disk since last local edit
|
||
|
returns 2 if file on disk has been deleted
|
||
|
-1 is returned if an error occurs */
|
||
|
|
||
|
int text_file_modified(Text *text)
|
||
|
{
|
||
|
struct stat st;
|
||
|
int result;
|
||
|
char file[FILE_MAXDIR+FILE_MAXFILE];
|
||
|
|
||
|
if(!text || !text->name)
|
||
|
return 0;
|
||
|
|
||
|
BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
|
||
|
BLI_convertstringcode(file, G.sce);
|
||
|
|
||
|
if(!BLI_exists(file))
|
||
|
return 2;
|
||
|
|
||
|
result = stat(file, &st);
|
||
|
|
||
|
if(result == -1)
|
||
|
return -1;
|
||
|
|
||
|
if((st.st_mode & S_IFMT) != S_IFREG)
|
||
|
return -1;
|
||
|
|
||
|
if(st.st_mtime > text->mtime)
|
||
|
return 1;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void text_ignore_modified(Text *text)
|
||
|
{
|
||
|
struct stat st;
|
||
|
int result;
|
||
|
char file[FILE_MAXDIR+FILE_MAXFILE];
|
||
|
|
||
|
if(!text || !text->name) return;
|
||
|
|
||
|
BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
|
||
|
BLI_convertstringcode(file, G.sce);
|
||
|
|
||
|
if(!BLI_exists(file)) return;
|
||
|
|
||
|
result = stat(file, &st);
|
||
|
|
||
|
if(result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
|
||
|
return;
|
||
|
|
||
|
text->mtime= st.st_mtime;
|
||
|
}
|
||
|
|
||
|
static int resolve_conflict_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
int resolution= RNA_enum_get(op->ptr, "resolution");
|
||
|
|
||
|
switch(resolution) {
|
||
|
case RESOLVE_RELOAD:
|
||
|
return reload_exec(C, op);
|
||
|
case RESOLVE_SAVE:
|
||
|
return save_exec(C, op);
|
||
|
case RESOLVE_MAKE_INTERNAL:
|
||
|
return make_internal_exec(C, op);
|
||
|
case RESOLVE_IGNORE:
|
||
|
text_ignore_modified(text);
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
return OPERATOR_CANCELLED;
|
||
|
}
|
||
|
|
||
|
static int resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *event)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
uiMenuItem *head;
|
||
|
|
||
|
switch(text_file_modified(text)) {
|
||
|
case 1:
|
||
|
if(text->flags & TXT_ISDIRTY) {
|
||
|
/* modified locally and externally, ahhh. offer more possibilites. */
|
||
|
head= uiPupMenuBegin("File Modified Outside and Inside Blender", 0);
|
||
|
uiMenuItemEnumO(head, "Reload from disk (ignore local changes)", 0, op->type->idname, "resolution", RESOLVE_RELOAD);
|
||
|
uiMenuItemEnumO(head, "Save to disk (ignore outside changes)", 0, op->type->idname, "resolution", RESOLVE_SAVE);
|
||
|
uiMenuItemEnumO(head, "Make text internal (separate copy)", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
|
||
|
uiPupMenuEnd(C, head);
|
||
|
}
|
||
|
else {
|
||
|
head= uiPupMenuBegin("File Modified Outside Blender", 0);
|
||
|
uiMenuItemEnumO(head, "Reload from disk", 0, op->type->idname, "resolution", RESOLVE_RELOAD);
|
||
|
uiMenuItemEnumO(head, "Make text internal (separate copy)", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
|
||
|
uiMenuItemEnumO(head, "Ignore", 0, op->type->idname, "resolution", RESOLVE_IGNORE);
|
||
|
uiPupMenuEnd(C, head);
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
head= uiPupMenuBegin("File Deleted Outside Blender", 0);
|
||
|
uiMenuItemEnumO(head, "Make text internal", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
|
||
|
uiMenuItemEnumO(head, "Recreate file", 0, op->type->idname, "resolution", RESOLVE_SAVE);
|
||
|
uiPupMenuEnd(C, head);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return OPERATOR_CANCELLED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_resolve_conflict(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "Resolve Conflict";
|
||
|
ot->idname= "TEXT_OT_resolve_conflict";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= resolve_conflict_exec;
|
||
|
ot->invoke= resolve_conflict_invoke;
|
||
|
ot->poll= save_poll;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_enum(ot->srna, "resolution", resolution_items, RESOLVE_IGNORE, "Resolution", "How to solve conflict due to different in internal and external text.");
|
||
|
}
|
||
|
|
||
|
/********************** to 3d object operator *****************/
|
||
|
|
||
|
static int to_3d_object_exec(bContext *C, wmOperator *op)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
int split_lines= RNA_boolean_get(op->ptr, "split_lines");
|
||
|
|
||
|
ED_text_to_object(C, text, split_lines);
|
||
|
|
||
|
return OPERATOR_FINISHED;
|
||
|
}
|
||
|
|
||
|
void TEXT_OT_to_3d_object(wmOperatorType *ot)
|
||
|
{
|
||
|
/* identifiers */
|
||
|
ot->name= "To 3D Object";
|
||
|
ot->idname= "TEXT_OT_to_3d_object";
|
||
|
|
||
|
/* api callbacks */
|
||
|
ot->exec= to_3d_object_exec;
|
||
|
ot->poll= text_edit_poll;
|
||
|
|
||
|
/* flags */
|
||
|
ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
|
||
|
|
||
|
/* properties */
|
||
|
RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text.");
|
||
|
}
|
||
|
|
||
|
/************************ undo ******************************/
|
||
|
|
||
|
void ED_text_undo_step(bContext *C, int step)
|
||
|
{
|
||
|
Text *text= CTX_data_edit_text(C);
|
||
|
|
||
|
if(!text)
|
||
|
return;
|
||
|
|
||
|
if(step==1)
|
||
|
txt_do_undo(text);
|
||
|
else if(step==-1)
|
||
|
txt_do_redo(text);
|
||
|
|
||
|
text_update_edited(text);
|
||
|
|
||
|
WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
|
||
|
WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
|
||
|
}
|
||
|
|