4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
32 #include <ctype.h> /* ispunct */
35 #include "MEM_guardedalloc.h"
37 #include "DNA_constraint_types.h"
38 #include "DNA_object_types.h"
39 #include "DNA_action_types.h"
40 #include "DNA_scene_types.h"
41 #include "DNA_screen_types.h"
42 #include "DNA_space_types.h"
43 #include "DNA_text_types.h"
44 #include "DNA_windowmanager_types.h"
46 #include "BLI_blenlib.h"
49 #include "BKE_context.h"
50 #include "BKE_depsgraph.h"
51 #include "BKE_global.h"
52 #include "BKE_library.h"
54 #include "BKE_report.h"
55 #include "BKE_suggestions.h"
62 #include "ED_screen.h"
63 #include "UI_interface.h"
64 #include "UI_resources.h"
66 #include "RNA_access.h"
67 #include "RNA_define.h"
69 #ifndef DISABLE_PYTHON
70 #include "BPY_extern.h"
73 #include "text_intern.h"
75 /************************ poll ***************************/
77 static int text_new_poll(bContext *C)
82 static int text_edit_poll(bContext *C)
84 Text *text= CTX_data_edit_text(C);
90 // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
97 static int text_space_edit_poll(bContext *C)
99 SpaceText *st= CTX_wm_space_text(C);
100 Text *text= CTX_data_edit_text(C);
106 // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
113 static int text_region_edit_poll(bContext *C)
115 SpaceText *st= CTX_wm_space_text(C);
116 Text *text= CTX_data_edit_text(C);
117 ARegion *ar= CTX_wm_region(C);
122 if(!ar || ar->regiontype != RGN_TYPE_WINDOW)
126 // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
134 /********************** updates *********************/
136 void text_update_line_edited(Text *text, TextLine *line)
141 /* we just free format here, and let it rebuild during draw */
143 MEM_freeN(line->format);
148 void text_update_edited(Text *text)
152 for(line=text->lines.first; line; line=line->next)
153 text_update_line_edited(text, line);
156 /******************* new operator *********************/
158 static int new_exec(bContext *C, wmOperator *op)
160 SpaceText *st= CTX_wm_space_text(C);
163 text= add_empty_text("Text");
170 WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
172 return OPERATOR_FINISHED;
175 void TEXT_OT_new(wmOperatorType *ot)
179 ot->idname= "TEXT_OT_new";
183 ot->poll= text_new_poll;
186 /******************* open operator *********************/
188 static int open_exec(bContext *C, wmOperator *op)
190 SpaceText *st= CTX_wm_space_text(C);
194 RNA_string_get(op->ptr, "filename", str);
196 text= add_text(str, G.sce);
203 WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
205 return OPERATOR_FINISHED;
208 static int open_invoke(bContext *C, wmOperator *op, wmEvent *event)
210 Text *text= CTX_data_edit_text(C);
211 char *path= (text && text->name)? text->name: G.sce;
213 if(RNA_property_is_set(op->ptr, "filename"))
214 return open_exec(C, op);
216 RNA_string_set(op->ptr, "filename", path);
217 WM_event_add_fileselect(C, op);
219 return OPERATOR_RUNNING_MODAL;
222 void TEXT_OT_open(wmOperatorType *ot)
226 ot->idname= "TEXT_OT_open";
230 ot->invoke= open_invoke;
231 ot->poll= text_new_poll;
234 RNA_def_string_file_path(ot->srna, "filename", "", FILE_MAX, "Filename", "File path of image to open.");
237 /******************* reload operator *********************/
239 static int reload_exec(bContext *C, wmOperator *op)
241 Text *text= CTX_data_edit_text(C);
243 if(!reopen_text(text)) {
244 BKE_report(op->reports, RPT_ERROR, "Could not reopen file");
245 return OPERATOR_CANCELLED;
248 #ifndef DISABLE_PYTHON
250 BPY_free_compiled_text(text);
252 text->compiled = NULL;
255 text_update_edited(text);
256 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
258 return OPERATOR_FINISHED;
261 void TEXT_OT_reload(wmOperatorType *ot)
265 ot->idname= "TEXT_OT_reload";
268 ot->exec= reload_exec;
269 ot->invoke= WM_operator_confirm;
270 ot->poll= text_edit_poll;
273 /******************* delete operator *********************/
275 static void text_unlink(Main *bmain, Text *text)
281 /* XXX this ifdef is in fact dangerous, if python is
282 * disabled it will leave invalid pointers in files! */
284 #ifndef DISABLE_PYTHON
285 // XXX BPY_clear_bad_scriptlinks(text);
286 // XXX BPY_free_pyconstraint_links(text);
287 // XXX free_text_controllers(text);
288 // XXX free_dome_warp_text(text);
290 /* check if this text was used as script link:
291 * this check function unsets the pointers and returns how many
292 * script links used this Text */
293 if(0) // XXX BPY_text_check_all_scriptlinks (text))
294 ; // XXX notifier: allqueue(REDRAWBUTSSCRIPT, 0);
296 /* equivalently for pynodes: */
297 if(0) // XXX nodeDynamicUnlinkText ((ID*)text))
298 ; // XXX notifier: allqueue(REDRAWNODE, 0);
301 for(scr= bmain->screen.first; scr; scr= scr->id.next) {
302 for(area= scr->areabase.first; area; area= area->next) {
303 for(sl= area->spacedata.first; sl; sl= sl->next) {
304 if(sl->spacetype==SPACE_TEXT) {
305 SpaceText *st= (SpaceText*) sl;
311 if(st==area->spacedata.first)
312 ED_area_tag_redraw(area);
319 free_libblock(&bmain->text, text);
322 static int unlink_exec(bContext *C, wmOperator *op)
324 SpaceText *st= CTX_wm_space_text(C);
325 Text *text= CTX_data_edit_text(C);
327 /* make the previous text active, if its not there make the next text active */
330 st->text = text->id.prev;
331 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
333 else if(text->id.next) {
334 st->text = text->id.next;
335 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
339 text_unlink(CTX_data_main(C), text);
340 WM_event_add_notifier(C, NC_TEXT|NA_REMOVED, text);
342 return OPERATOR_FINISHED;
345 void TEXT_OT_unlink(wmOperatorType *ot)
349 ot->idname= "TEXT_OT_unlink";
352 ot->exec= unlink_exec;
353 ot->invoke= WM_operator_confirm;
354 ot->poll= text_edit_poll;
357 /******************* make internal operator *********************/
359 static int make_internal_exec(bContext *C, wmOperator *op)
361 Text *text= CTX_data_edit_text(C);
363 text->flags |= TXT_ISMEM | TXT_ISDIRTY;
366 MEM_freeN(text->name);
370 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
372 return OPERATOR_FINISHED;
375 void TEXT_OT_make_internal(wmOperatorType *ot)
378 ot->name= "Make Internal";
379 ot->idname= "TEXT_OT_make_internal";
382 ot->exec= make_internal_exec;
383 ot->poll= text_edit_poll;
386 /******************* save operator *********************/
388 static int save_poll(bContext *C)
390 Text *text= CTX_data_edit_text(C);
392 if(!text_edit_poll(C))
395 return (text->name != NULL && !(text->flags & TXT_ISMEM));
398 static void txt_write_file(Text *text, ReportList *reports)
404 char file[FILE_MAXDIR+FILE_MAXFILE];
406 BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
407 BLI_convertstringcode(file, G.sce);
409 fp= fopen(file, "w");
411 BKE_report(reports, RPT_ERROR, "Unable to save file.");
415 tmp= text->lines.first;
417 if(tmp->next) fprintf(fp, "%s\n", tmp->line);
418 else fprintf(fp, "%s", tmp->line);
425 res= stat(file, &st);
426 text->mtime= st.st_mtime;
428 if(text->flags & TXT_ISDIRTY)
429 text->flags ^= TXT_ISDIRTY;
432 static int save_exec(bContext *C, wmOperator *op)
434 Text *text= CTX_data_edit_text(C);
436 txt_write_file(text, op->reports);
438 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
440 return OPERATOR_FINISHED;
443 void TEXT_OT_save(wmOperatorType *ot)
447 ot->idname= "TEXT_OT_save";
454 /******************* save as operator *********************/
456 static int save_as_exec(bContext *C, wmOperator *op)
458 Text *text= CTX_data_edit_text(C);
462 return OPERATOR_CANCELLED;
464 RNA_string_get(op->ptr, "filename", str);
466 if(text->name) MEM_freeN(text->name);
467 text->name= BLI_strdup(str);
468 text->flags &= ~TXT_ISMEM;
470 txt_write_file(text, op->reports);
472 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
474 return OPERATOR_FINISHED;
477 static int save_as_invoke(bContext *C, wmOperator *op, wmEvent *event)
479 Text *text= CTX_data_edit_text(C);
482 if(RNA_property_is_set(op->ptr, "filename"))
483 return save_as_exec(C, op);
487 else if(text->flags & TXT_ISMEM)
488 str= text->id.name+2;
492 RNA_string_set(op->ptr, "filename", str);
493 WM_event_add_fileselect(C, op);
495 return OPERATOR_RUNNING_MODAL;
498 void TEXT_OT_save_as(wmOperatorType *ot)
502 ot->idname= "TEXT_OT_save_as";
505 ot->exec= save_as_exec;
506 ot->invoke= save_as_invoke;
507 ot->poll= text_edit_poll;
510 RNA_def_string_file_path(ot->srna, "filename", "", FILE_MAX, "Filename", "File path to save image to.");
513 /******************* run script operator *********************/
515 static int run_script_exec(bContext *C, wmOperator *op)
517 #ifdef DISABLE_PYTHON
518 BKE_report(op->reports, RPT_ERROR, "Python disabled in this build");
520 return OPERATOR_CANCELLED;
522 Text *text= CTX_data_edit_text(C);
524 if (BPY_run_python_script(C, NULL, text, op->reports))
525 return OPERATOR_FINISHED;
527 /* Dont report error messages while live editing */
528 if(!CTX_wm_space_text(C)->live_edit)
529 BKE_report(op->reports, RPT_ERROR, "Python script fail, look in the console for now...");
531 return OPERATOR_CANCELLED;
535 void TEXT_OT_run_script(wmOperatorType *ot)
538 ot->name= "Run Script";
539 ot->idname= "TEXT_OT_run_script";
542 ot->exec= run_script_exec;
543 ot->poll= text_edit_poll;
547 /******************* refresh pyconstraints operator *********************/
549 static int refresh_pyconstraints_exec(bContext *C, wmOperator *op)
551 #ifndef DISABLE_PYTHON
552 Text *text= CTX_data_edit_text(C);
553 Scene *scene= CTX_data_scene(C);
558 /* check all pyconstraints */
559 for(ob= CTX_data_main(C)->object.first; ob; ob= ob->id.next) {
561 if(ob->type==OB_ARMATURE && ob->pose) {
563 for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
564 for(con = pchan->constraints.first; con; con= con->next) {
565 if(con->type==CONSTRAINT_TYPE_PYTHON) {
566 bPythonConstraint *data = con->data;
567 if(data->text==text) BPY_pyconstraint_update(ob, con);
574 for(con = ob->constraints.first; con; con= con->next) {
575 if(con->type==CONSTRAINT_TYPE_PYTHON) {
576 bPythonConstraint *data = con->data;
577 if(data->text==text) BPY_pyconstraint_update(ob, con);
583 DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
588 return OPERATOR_FINISHED;
591 void TEXT_OT_refresh_pyconstraints(wmOperatorType *ot)
594 ot->name= "Refresh PyConstraints";
595 ot->idname= "TEXT_OT_refresh_pyconstraints";
598 ot->exec= refresh_pyconstraints_exec;
599 ot->poll= text_edit_poll;
602 /******************* paste operator *********************/
604 static char *txt_copy_selected(Text *text)
606 TextLine *tmp, *linef, *linel;
608 int charf, charl, length= 0;
610 if(!text) return NULL;
611 if(!text->curl) return NULL;
612 if(!text->sell) return NULL;
614 if(!txt_has_sel(text)) return NULL;
616 if(text->curl==text->sell) {
617 linef= linel= text->curl;
619 if(text->curc < text->selc) {
628 else if(txt_get_span(text->curl, text->sell)<0) {
646 buf= MEM_callocN(length+1, "cut buffera");
648 BLI_strncpy(buf, linef->line + charf, length+1);
651 length+= linef->len - charf;
653 length++; /* For the '\n' */
656 while(tmp && tmp!= linel) {
661 buf= MEM_callocN(length+1, "cut bufferb");
663 strncpy(buf, linef->line+ charf, linef->len-charf);
664 length= linef->len-charf;
669 while(tmp && tmp!=linel) {
670 strncpy(buf+length, tmp->line, tmp->len);
677 strncpy(buf+length, linel->line, charl);
686 static int paste_exec(bContext *C, wmOperator *op)
688 Text *text= CTX_data_edit_text(C);
690 int selection= RNA_boolean_get(op->ptr, "selection");
692 buf= WM_clipboard_text_get(selection);
695 return OPERATOR_CANCELLED;
697 txt_insert_buf(text, buf);
698 text_update_edited(text);
702 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
703 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
705 /* run the script while editing, evil but useful */
706 if(CTX_wm_space_text(C)->live_edit)
707 run_script_exec(C, op);
709 return OPERATOR_FINISHED;
712 void TEXT_OT_paste(wmOperatorType *ot)
716 ot->idname= "TEXT_OT_paste";
719 ot->exec= paste_exec;
720 ot->poll= text_edit_poll;
723 ot->flag= OPTYPE_REGISTER;
726 RNA_def_boolean(ot->srna, "selection", 0, "Selection", "Paste text selected elsewhere rather than copied, X11 only.");
729 /******************* copy operator *********************/
731 static void txt_copy_clipboard(Text *text)
735 buf= txt_copy_selected(text);
738 WM_clipboard_text_set(buf, 0);
743 static int copy_exec(bContext *C, wmOperator *op)
745 Text *text= CTX_data_edit_text(C);
747 txt_copy_clipboard(text);
749 return OPERATOR_FINISHED;
752 void TEXT_OT_copy(wmOperatorType *ot)
756 ot->idname= "TEXT_OT_copy";
760 ot->poll= text_edit_poll;
763 /******************* cut operator *********************/
765 static int cut_exec(bContext *C, wmOperator *op)
767 Text *text= CTX_data_edit_text(C);
769 txt_copy_clipboard(text);
770 txt_delete_selected(text);
772 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
773 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
775 /* run the script while editing, evil but useful */
776 if(CTX_wm_space_text(C)->live_edit)
777 run_script_exec(C, op);
779 return OPERATOR_FINISHED;
782 void TEXT_OT_cut(wmOperatorType *ot)
786 ot->idname= "TEXT_OT_cut";
790 ot->poll= text_edit_poll;
793 ot->flag= OPTYPE_REGISTER;
796 /******************* indent operator *********************/
798 static int indent_exec(bContext *C, wmOperator *op)
800 Text *text= CTX_data_edit_text(C);
802 if(txt_has_sel(text)) {
803 txt_order_cursors(text);
807 txt_add_char(text, '\t');
809 text_update_edited(text);
811 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
812 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
814 return OPERATOR_FINISHED;
817 void TEXT_OT_indent(wmOperatorType *ot)
821 ot->idname= "TEXT_OT_indent";
824 ot->exec= indent_exec;
825 ot->poll= text_edit_poll;
828 ot->flag= OPTYPE_REGISTER;
831 /******************* unindent operator *********************/
833 static int unindent_exec(bContext *C, wmOperator *op)
835 Text *text= CTX_data_edit_text(C);
837 if(txt_has_sel(text)) {
838 txt_order_cursors(text);
841 text_update_edited(text);
843 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
844 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
846 return OPERATOR_FINISHED;
849 return OPERATOR_CANCELLED;
852 void TEXT_OT_unindent(wmOperatorType *ot)
855 ot->name= "Unindent";
856 ot->idname= "TEXT_OT_unindent";
859 ot->exec= unindent_exec;
860 ot->poll= text_edit_poll;
863 ot->flag= OPTYPE_REGISTER;
866 /******************* line break operator *********************/
868 static int line_break_exec(bContext *C, wmOperator *op)
870 Text *text= CTX_data_edit_text(C);
873 // double check tabs before splitting the line
874 curtab= setcurr_tab(text);
875 txt_split_curline(text);
877 for(a=0; a < curtab; a++)
878 txt_add_char(text, '\t');
882 text_update_line_edited(text, text->curl->prev);
883 text_update_line_edited(text, text->curl);
886 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
887 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
889 return OPERATOR_CANCELLED;
892 void TEXT_OT_line_break(wmOperatorType *ot)
895 ot->name= "Line Break";
896 ot->idname= "TEXT_OT_line_break";
899 ot->exec= line_break_exec;
900 ot->poll= text_edit_poll;
903 ot->flag= OPTYPE_REGISTER;
906 /******************* comment operator *********************/
908 static int comment_exec(bContext *C, wmOperator *op)
910 Text *text= CTX_data_edit_text(C);
912 if(txt_has_sel(text)) {
913 txt_order_cursors(text);
915 text_update_edited(text);
917 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
918 return OPERATOR_FINISHED;
921 return OPERATOR_CANCELLED;
924 void TEXT_OT_comment(wmOperatorType *ot)
928 ot->idname= "TEXT_OT_comment";
931 ot->exec= comment_exec;
932 ot->poll= text_edit_poll;
935 ot->flag= OPTYPE_REGISTER;
938 /******************* uncomment operator *********************/
940 static int uncomment_exec(bContext *C, wmOperator *op)
942 Text *text= CTX_data_edit_text(C);
944 if(txt_has_sel(text)) {
945 txt_order_cursors(text);
947 text_update_edited(text);
949 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
951 return OPERATOR_FINISHED;
954 return OPERATOR_CANCELLED;
957 void TEXT_OT_uncomment(wmOperatorType *ot)
960 ot->name= "Uncomment";
961 ot->idname= "TEXT_OT_uncomment";
964 ot->exec= uncomment_exec;
965 ot->poll= text_edit_poll;
968 ot->flag= OPTYPE_REGISTER;
971 /******************* convert whitespace operator *********************/
973 enum { TO_SPACES, TO_TABS };
974 static EnumPropertyItem whitespace_type_items[]= {
975 {TO_SPACES, "SPACES", 0, "To Spaces", NULL},
976 {TO_TABS, "TABS", 0, "To Tabs", NULL},
977 {0, NULL, 0, NULL, NULL}};
979 static int convert_whitespace_exec(bContext *C, wmOperator *op)
981 SpaceText *st= CTX_wm_space_text(C);
982 Text *text= CTX_data_edit_text(C);
986 char *text_check_line, *new_line;
987 int extra, number; //unknown for now
988 int type= RNA_enum_get(op->ptr, "type");
990 tmp = text->lines.first;
992 //first convert to all space, this make it alot easier to convert to tabs because there is no mixtures of ' ' && '\t'
994 text_check_line = tmp->line;
995 number = flatten_string(st, &fs, text_check_line)+1;
996 flatten_string_free(&fs);
997 new_line = MEM_callocN(number, "Converted_Line");
999 for(a=0; a < strlen(text_check_line); a++) { //foreach char in line
1000 if(text_check_line[a] == '\t') { //checking for tabs
1001 //get the number of spaces this tabs is showing
1002 //i dont like doing it this way but will look into it later
1004 number = flatten_string(st, &fs, new_line);
1005 flatten_string_free(&fs);
1007 new_line[j+1] = '\0';
1008 number = flatten_string(st, &fs, new_line)-number;
1009 flatten_string_free(&fs);
1011 for(extra = 0; extra < number; extra++) {
1017 new_line[j] = text_check_line[a];
1022 // put new_line in the tmp->line spot still need to try and set the curc correctly
1023 if(tmp->line) MEM_freeN(tmp->line);
1024 if(tmp->format) MEM_freeN(tmp->format);
1026 tmp->line = new_line;
1027 tmp->len = strlen(new_line);
1032 if(type == TO_TABS) // Converting to tabs
1033 { //start over from the begining
1034 tmp = text->lines.first;
1037 text_check_line = tmp->line;
1039 for(a = 0; a < strlen(text_check_line); a++) {
1041 for(j = 0; j < (size_t)st->tabnumber; j++) {
1042 if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
1043 if(text_check_line[a+j] != ' ') {
1048 if(!number) { //found all number of space to equal a tab
1049 a = a+(st->tabnumber-1);
1054 if( extra > 0 ) { //got tabs make malloc and do what you have to do
1055 new_line = MEM_callocN(strlen(text_check_line)-(((st->tabnumber*extra)-extra)-1), "Converted_Line");
1056 extra = 0; //reuse vars
1057 for(a = 0; a < strlen(text_check_line); a++) {
1059 for(j = 0; j < (size_t)st->tabnumber; j++) {
1060 if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
1061 if(text_check_line[a+j] != ' ') {
1067 if(!number) { //found all number of space to equal a tab
1068 new_line[extra] = '\t';
1069 a = a+(st->tabnumber-1);
1073 else { //not adding a tab
1074 new_line[extra] = text_check_line[a];
1078 new_line[extra] = '\0';
1079 // put new_line in the tmp->line spot still need to try and set the curc correctly
1080 if(tmp->line) MEM_freeN(tmp->line);
1081 if(tmp->format) MEM_freeN(tmp->format);
1083 tmp->line = new_line;
1084 tmp->len = strlen(new_line);
1091 text_update_edited(text);
1093 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1095 return OPERATOR_FINISHED;
1098 void TEXT_OT_convert_whitespace(wmOperatorType *ot)
1101 ot->name= "Convert Whitespace";
1102 ot->idname= "TEXT_OT_convert_whitespace";
1105 ot->exec= convert_whitespace_exec;
1106 ot->poll= text_edit_poll;
1109 ot->flag= OPTYPE_REGISTER;
1112 RNA_def_enum(ot->srna, "type", whitespace_type_items, TO_SPACES, "type", "Type of whitespace to convert to.");
1115 /******************* select all operator *********************/
1117 static int select_all_exec(bContext *C, wmOperator *op)
1119 Text *text= CTX_data_edit_text(C);
1123 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1125 return OPERATOR_FINISHED;
1128 void TEXT_OT_select_all(wmOperatorType *ot)
1131 ot->name= "Select All";
1132 ot->idname= "TEXT_OT_select_all";
1135 ot->exec= select_all_exec;
1136 ot->poll= text_edit_poll;
1139 ot->flag= OPTYPE_REGISTER;
1142 /******************* select line operator *********************/
1144 static int select_line_exec(bContext *C, wmOperator *op)
1146 Text *text= CTX_data_edit_text(C);
1150 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1152 return OPERATOR_FINISHED;
1155 void TEXT_OT_select_line(wmOperatorType *ot)
1158 ot->name= "Select Line";
1159 ot->idname= "TEXT_OT_select_line";
1161 /* api clinebacks */
1162 ot->exec= select_line_exec;
1163 ot->poll= text_edit_poll;
1166 ot->flag= OPTYPE_REGISTER;
1169 /******************* previous marker operator *********************/
1171 static int previous_marker_exec(bContext *C, wmOperator *op)
1173 Text *text= CTX_data_edit_text(C);
1177 lineno= txt_get_span(text->lines.first, text->curl);
1178 mrk= text->markers.last;
1179 while(mrk && (mrk->lineno>lineno || (mrk->lineno==lineno && mrk->end > text->curc)))
1181 if(!mrk) mrk= text->markers.last;
1183 txt_move_to(text, mrk->lineno, mrk->start, 0);
1184 txt_move_to(text, mrk->lineno, mrk->end, 1);
1187 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1189 return OPERATOR_FINISHED;
1192 void TEXT_OT_previous_marker(wmOperatorType *ot)
1195 ot->name= "Previous Marker";
1196 ot->idname= "TEXT_OT_previous_marker";
1199 ot->exec= previous_marker_exec;
1200 ot->poll= text_edit_poll;
1203 ot->flag= OPTYPE_REGISTER;
1206 /******************* next marker operator *********************/
1208 static int next_marker_exec(bContext *C, wmOperator *op)
1210 Text *text= CTX_data_edit_text(C);
1214 lineno= txt_get_span(text->lines.first, text->curl);
1215 mrk= text->markers.first;
1216 while(mrk && (mrk->lineno<lineno || (mrk->lineno==lineno && mrk->start <= text->curc)))
1218 if(!mrk) mrk= text->markers.first;
1220 txt_move_to(text, mrk->lineno, mrk->start, 0);
1221 txt_move_to(text, mrk->lineno, mrk->end, 1);
1224 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1226 return OPERATOR_FINISHED;
1229 void TEXT_OT_next_marker(wmOperatorType *ot)
1232 ot->name= "Next Marker";
1233 ot->idname= "TEXT_OT_next_marker";
1236 ot->exec= next_marker_exec;
1237 ot->poll= text_edit_poll;
1240 ot->flag= OPTYPE_REGISTER;
1243 /******************* clear all markers operator *********************/
1245 static int clear_all_markers_exec(bContext *C, wmOperator *op)
1247 Text *text= CTX_data_edit_text(C);
1249 txt_clear_markers(text, 0, 0);
1251 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1253 return OPERATOR_FINISHED;
1256 void TEXT_OT_markers_clear(wmOperatorType *ot)
1259 ot->name= "Clear All Markers";
1260 ot->idname= "TEXT_OT_markers_clear";
1263 ot->exec= clear_all_markers_exec;
1264 ot->poll= text_edit_poll;
1267 ot->flag= OPTYPE_REGISTER;
1270 /************************ move operator ************************/
1272 static EnumPropertyItem move_type_items[]= {
1273 {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
1274 {LINE_END, "LINE_END", 0, "Line End", ""},
1275 {FILE_TOP, "FILE_TOP", 0, "File Top", ""},
1276 {FILE_BOTTOM, "FILE_BOTTOM", 0, "File Bottom", ""},
1277 {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
1278 {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
1279 {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
1280 {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
1281 {PREV_LINE, "PREVIOUS_LINE", 0, "Previous Line", ""},
1282 {NEXT_LINE, "NEXT_LINE", 0, "Next Line", ""},
1283 {PREV_PAGE, "PREVIOUS_PAGE", 0, "Previous Page", ""},
1284 {NEXT_PAGE, "NEXT_PAGE", 0, "Next Page", ""},
1285 {0, NULL, 0, NULL, NULL}};
1287 static void wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
1289 Text *text= st->text;
1290 int offl, offc, lin;
1292 lin= txt_get_span(text->lines.first, text->sell);
1293 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1296 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, -offc);
1299 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, -offc);
1305 static void wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
1307 Text *text= st->text;
1308 int offl, offc, lin, startl, c;
1310 lin= txt_get_span(text->lines.first, text->sell);
1311 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1314 while (offl==startl && text->sell->line[c]!='\0') {
1316 wrap_offset(st, ar, text->sell, c, &offl, &offc);
1317 } if (offl!=startl) c--;
1320 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, c);
1323 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, c);
1329 static void wrap_move_up(SpaceText *st, ARegion *ar, short sel)
1331 Text *text= st->text;
1332 int offl, offl_1, offc, fromline, toline, c, target;
1334 wrap_offset(st, ar, text->sell, 0, &offl_1, &offc);
1335 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1336 fromline= toline= txt_get_span(text->lines.first, text->sell);
1337 target= text->selc + offc;
1340 if (!text->sell->prev) {
1341 txt_move_bol(text, sel);
1345 c= text->sell->prev->len; /* End of prev. line */
1346 wrap_offset(st, ar, text->sell->prev, c, &offl, &offc);
1349 c= -offc-1; /* End of prev. line */
1350 wrap_offset(st, ar, text->sell, c, &offl, &offc);
1356 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
1357 if (toline<fromline) text->sell= text->sell->prev;
1359 if (c>text->sell->len) c= text->sell->len;
1363 else if(text->curl) {
1364 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
1365 if (toline<fromline) text->curl= text->curl->prev;
1367 if (c>text->curl->len) c= text->curl->len;
1374 static void wrap_move_down(SpaceText *st, ARegion *ar, short sel)
1376 Text *text= st->text;
1377 int offl, startoff, offc, fromline, toline, c, target;
1379 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1380 fromline= toline= txt_get_span(text->lines.first, text->sell);
1381 target= text->selc + offc;
1384 while (offl==startoff && text->sell->line[c]!='\0') {
1386 wrap_offset(st, ar, text->sell, c, &offl, &offc);
1389 if (text->sell->line[c]=='\0') {
1390 if (!text->sell->next) {
1391 txt_move_eol(text, sel);
1398 if (c > text->sell->len) c= text->sell->len;
1403 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
1404 if (toline>fromline) text->sell= text->sell->next;
1406 if (c>text->sell->len) c= text->sell->len;
1410 else if(text->curl) {
1411 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
1412 if (toline>fromline) text->curl= text->curl->next;
1414 if (c > text->curl->len) c= text->curl->len;
1421 /* Moves the cursor vertically by the specified number of lines.
1422 If the destination line is shorter than the current cursor position, the
1423 cursor will be positioned at the end of this line.
1425 This is to replace screen_skip for PageUp/Down operations.
1427 static void cursor_skip(Text *text, int lines, int sel)
1430 int oldl, oldc, *charp;
1432 if (sel) linep= &text->sell, charp= &text->selc;
1433 else linep= &text->curl, charp= &text->curc;
1434 oldl= txt_get_span(text->lines.first, *linep);
1437 while (lines>0 && (*linep)->next) {
1438 *linep= (*linep)->next;
1441 while (lines<0 && (*linep)->prev) {
1442 *linep= (*linep)->prev;
1446 if (*charp > (*linep)->len) *charp= (*linep)->len;
1448 if (!sel) txt_pop_sel(text);
1449 txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
1452 static int move_cursor(bContext *C, int type, int select)
1454 SpaceText *st= CTX_wm_space_text(C);
1455 Text *text= CTX_data_edit_text(C);
1456 ARegion *ar= CTX_wm_region(C);
1458 /* ensure we have the right region, it's optional */
1459 if(ar->regiontype != RGN_TYPE_WINDOW)
1464 if(st && st->wordwrap && ar) wrap_move_bol(st, ar, select);
1465 else txt_move_bol(text, select);
1469 if(st && st->wordwrap && ar) wrap_move_eol(st, ar, select);
1470 else txt_move_eol(text, select);
1474 txt_move_bof(text, select);
1478 txt_move_eof(text, select);
1482 txt_jump_left(text, select);
1486 txt_jump_right(text, select);
1490 txt_move_left(text, select);
1494 txt_move_right(text, select);
1498 if(st && st->wordwrap && ar) wrap_move_up(st, ar, select);
1499 else txt_move_up(text, select);
1503 if(st && st->wordwrap && ar) wrap_move_down(st, ar, select);
1504 else txt_move_down(text, select);
1508 if(st) cursor_skip(text, -st->viewlines, select);
1509 else cursor_skip(text, -10, select);
1513 if(st) cursor_skip(text, st->viewlines, select);
1514 else cursor_skip(text, 10, select);
1518 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
1519 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1521 return OPERATOR_FINISHED;
1524 static int move_exec(bContext *C, wmOperator *op)
1526 int type= RNA_enum_get(op->ptr, "type");
1528 return move_cursor(C, type, 0);
1531 void TEXT_OT_move(wmOperatorType *ot)
1534 ot->name= "Move Cursor";
1535 ot->idname= "TEXT_OT_move";
1538 ot->exec= move_exec;
1539 ot->poll= text_edit_poll;
1542 ot->flag= OPTYPE_REGISTER;
1545 RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
1548 /******************* move select operator ********************/
1550 static int move_select_exec(bContext *C, wmOperator *op)
1552 int type= RNA_enum_get(op->ptr, "type");
1554 return move_cursor(C, type, 1);
1557 void TEXT_OT_move_select(wmOperatorType *ot)
1560 ot->name= "Move Select";
1561 ot->idname= "TEXT_OT_move_select";
1564 ot->exec= move_select_exec;
1565 ot->poll= text_space_edit_poll;
1568 ot->flag= OPTYPE_REGISTER;
1571 RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection.");
1574 /******************* jump operator *********************/
1576 static int jump_exec(bContext *C, wmOperator *op)
1578 Text *text= CTX_data_edit_text(C);
1579 int line= RNA_int_get(op->ptr, "line");
1580 short nlines= txt_get_span(text->lines.first, text->lines.last)+1;
1582 if(line < 1 || line > nlines)
1583 return OPERATOR_CANCELLED;
1585 txt_move_toline(text, line-1, 0);
1587 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
1589 return OPERATOR_FINISHED;
1593 // short tmp= txt_get_span(text->lines.first, text->curl)+1;
1594 // button(&tmp, 1, nlines, "Jump to line:"))
1596 void TEXT_OT_jump(wmOperatorType *ot)
1600 ot->idname= "TEXT_OT_jump";
1603 ot->exec= jump_exec;
1604 ot->poll= text_edit_poll;
1607 ot->flag= OPTYPE_REGISTER;
1610 RNA_def_int(ot->srna, "line", 1, INT_MAX, 1, "Line", "Line number to jump to.", 1, 10000);
1613 /******************* delete operator **********************/
1615 static EnumPropertyItem delete_type_items[]= {
1616 {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
1617 {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
1618 {DEL_NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
1619 {DEL_PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
1620 {0, NULL, 0, NULL, NULL}};
1622 static int delete_exec(bContext *C, wmOperator *op)
1624 Text *text= CTX_data_edit_text(C);
1625 int type= RNA_enum_get(op->ptr, "type");
1627 if(type == DEL_PREV_WORD)
1628 txt_backspace_word(text);
1629 else if(type == DEL_PREV_CHAR)
1630 txt_backspace_char(text);
1631 else if(type == DEL_NEXT_WORD)
1632 txt_delete_word(text);
1633 else if(type == DEL_NEXT_CHAR)
1634 txt_delete_char(text);
1636 text_update_line_edited(text, text->curl);
1638 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
1639 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1641 /* run the script while editing, evil but useful */
1642 if(CTX_wm_space_text(C)->live_edit)
1643 run_script_exec(C, op);
1645 return OPERATOR_FINISHED;
1648 void TEXT_OT_delete(wmOperatorType *ot)
1652 ot->idname= "TEXT_OT_delete";
1655 ot->exec= delete_exec;
1656 ot->poll= text_edit_poll;
1659 ot->flag= OPTYPE_REGISTER;
1662 RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete.");
1665 /******************* toggle overwrite operator **********************/
1667 static int toggle_overwrite_exec(bContext *C, wmOperator *op)
1669 SpaceText *st= CTX_wm_space_text(C);
1671 st->overwrite= !st->overwrite;
1673 return OPERATOR_FINISHED;
1676 void TEXT_OT_overwrite_toggle(wmOperatorType *ot)
1679 ot->name= "Toggle Overwrite";
1680 ot->idname= "TEXT_OT_overwrite_toggle";
1683 ot->exec= toggle_overwrite_exec;
1684 ot->poll= text_space_edit_poll;
1687 ot->flag= OPTYPE_REGISTER;
1690 /******************* scroll operator **********************/
1692 /* Moves the view vertically by the specified number of lines */
1693 static void screen_skip(SpaceText *st, int lines)
1699 last= txt_get_span(st->text->lines.first, st->text->lines.last);
1700 last= last - (st->viewlines/2);
1702 if(st->top>last) st->top= last;
1703 if(st->top<0) st->top= 0;
1706 typedef struct TextScroll {
1717 static int scroll_exec(bContext *C, wmOperator *op)
1719 SpaceText *st= CTX_wm_space_text(C);
1720 int lines= RNA_int_get(op->ptr, "lines");
1723 return OPERATOR_CANCELLED;
1725 screen_skip(st, lines*U.wheellinescroll);
1727 ED_area_tag_redraw(CTX_wm_area(C));
1729 return OPERATOR_FINISHED;
1732 static int scroll_invoke(bContext *C, wmOperator *op, wmEvent *event)
1734 SpaceText *st= CTX_wm_space_text(C);
1737 if(RNA_property_is_set(op->ptr, "lines"))
1738 return scroll_exec(C, op);
1740 tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
1742 op->customdata= tsc;
1744 st->flags|= ST_SCROLL_SELECT;
1746 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1748 return OPERATOR_RUNNING_MODAL;
1751 static void scroll_apply(bContext *C, wmOperator *op, wmEvent *event)
1753 SpaceText *st= CTX_wm_space_text(C);
1754 TextScroll *tsc= op->customdata;
1755 short *mval= event->mval;
1758 tsc->old[0]= mval[0];
1759 tsc->old[1]= mval[1];
1760 tsc->hold[0]= mval[0];
1761 tsc->hold[1]= mval[1];
1765 if(!tsc->scrollbar) {
1766 tsc->delta[0]= (tsc->hold[0]-mval[0])/text_font_width_character(st);
1767 tsc->delta[1]= (mval[1]-tsc->hold[1])/st->lheight;
1770 tsc->delta[1]= (tsc->hold[1]-mval[1])*st->pix_per_line;
1772 if(tsc->delta[0] || tsc->delta[1]) {
1773 screen_skip(st, tsc->delta[1]);
1775 tsc->lines += tsc->delta[1];
1781 st->left+= tsc->delta[0];
1782 if(st->left<0) st->left= 0;
1785 tsc->hold[0]= mval[0];
1786 tsc->hold[1]= mval[1];
1788 ED_area_tag_redraw(CTX_wm_area(C));
1791 tsc->old[0]= mval[0];
1792 tsc->old[1]= mval[1];
1795 static void scroll_exit(bContext *C, wmOperator *op)
1797 SpaceText *st= CTX_wm_space_text(C);
1799 st->flags &= ~ST_SCROLL_SELECT;
1800 MEM_freeN(op->customdata);
1803 static int scroll_modal(bContext *C, wmOperator *op, wmEvent *event)
1805 switch(event->type) {
1807 scroll_apply(C, op, event);
1813 return OPERATOR_FINISHED;
1816 return OPERATOR_RUNNING_MODAL;
1819 static int scroll_cancel(bContext *C, wmOperator *op)
1823 return OPERATOR_CANCELLED;
1826 void TEXT_OT_scroll(wmOperatorType *ot)
1830 ot->idname= "TEXT_OT_scroll";
1833 ot->exec= scroll_exec;
1834 ot->invoke= scroll_invoke;
1835 ot->modal= scroll_modal;
1836 ot->cancel= scroll_cancel;
1837 ot->poll= text_space_edit_poll;
1840 ot->flag= OPTYPE_BLOCKING;
1843 RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
1846 /******************** scroll bar operator *******************/
1848 static int scroll_bar_invoke(bContext *C, wmOperator *op, wmEvent *event)
1850 SpaceText *st= CTX_wm_space_text(C);
1851 ARegion *ar= CTX_wm_region(C);
1853 short *mval= event->mval;
1855 if(RNA_property_is_set(op->ptr, "lines"))
1856 return scroll_exec(C, op);
1858 /* verify we are in the right zone */
1859 if(!(mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<ar->winy))
1860 return OPERATOR_PASS_THROUGH;
1862 tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
1865 op->customdata= tsc;
1867 st->flags|= ST_SCROLL_SELECT;
1869 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1871 return OPERATOR_RUNNING_MODAL;
1874 void TEXT_OT_scroll_bar(wmOperatorType *ot)
1877 ot->name= "Scrollbar";
1878 ot->idname= "TEXT_OT_scroll_bar";
1881 ot->invoke= scroll_bar_invoke;
1882 ot->modal= scroll_modal;
1883 ot->cancel= scroll_cancel;
1884 ot->poll= text_region_edit_poll;
1887 ot->flag= OPTYPE_BLOCKING;
1890 RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
1893 /******************* set cursor operator **********************/
1895 typedef struct SetCursor {
1901 static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
1904 Text *text= st->text;
1909 if(sel) { linep= &text->sell; charp= &text->selc; }
1910 else { linep= &text->curl; charp= &text->curc; }
1912 y= (ar->winy - y)/st->lheight;
1915 x-= TXT_OFFSET+TEXTXLOC;
1920 x = (x/text_font_width_character(st)) + st->left;
1923 int i, j, endj, curs, max, chop, start, end, chars, loop;
1926 /* Point to first visible line */
1927 *linep= text->lines.first;
1928 for(i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;
1930 max= wrap_width(st, ar);
1933 while(loop && *linep) {
1940 for(i=0, j=0; loop; j++) {
1942 /* Mimic replacement of tabs */
1943 ch= (*linep)->line[j];
1945 chars= st->tabnumber-i%st->tabnumber;
1952 /* Gone too far, go back to last wrap point */
1957 /* Exactly at the cursor, done */
1959 else if(y==0 && i-start==x) {
1963 /* Prepare curs for next wrap */
1974 if(y==0 && i-start>=x) {
1980 else if(ch==' ' || ch=='-' || ch=='\0') {
1981 if(y==0 && i-start>=x) {
1994 if(!loop || y<0) break;
1996 if(!(*linep)->next) {
1997 *charp= (*linep)->len;
2001 /* On correct line but didn't meet cursor, must be at end */
2003 *charp= (*linep)->len;
2006 *linep= (*linep)->next;
2012 y-= txt_get_span(text->lines.first, *linep) - st->top;
2015 while(y-- != 0) if((*linep)->next) *linep= (*linep)->next;
2018 while(y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
2022 w= flatten_string(st, &fs, (*linep)->line);
2023 if(x<w) *charp= fs.accum[x];
2024 else *charp= (*linep)->len;
2025 flatten_string_free(&fs);
2027 if(!sel) txt_pop_sel(text);
2030 static void set_cursor_apply(bContext *C, wmOperator *op, wmEvent *event)
2032 SpaceText *st= CTX_wm_space_text(C);
2033 ARegion *ar= CTX_wm_region(C);
2034 SetCursor *scu= op->customdata;
2036 if(event->mval[1]<0 || event->mval[1]>ar->winy) {
2037 int d= (scu->old[1]-event->mval[1])*st->pix_per_line;
2038 if(d) screen_skip(st, d);
2040 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1]<0?0:ar->winy, 1);
2042 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2044 else if(!st->wordwrap && (event->mval[0]<0 || event->mval[0]>ar->winx)) {
2045 if(event->mval[0]>ar->winx) st->left++;
2046 else if(event->mval[0]<0 && st->left>0) st->left--;
2048 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
2050 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2051 // XXX PIL_sleep_ms(10);
2054 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
2056 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2058 scu->old[0]= event->mval[0];
2059 scu->old[1]= event->mval[1];
2063 static void set_cursor_exit(bContext *C, wmOperator *op)
2065 SpaceText *st= CTX_wm_space_text(C);
2066 Text *text= st->text;
2067 SetCursor *scu= op->customdata;
2071 if(txt_has_sel(text)) {
2072 buffer = txt_sel_to_buf(text);
2073 WM_clipboard_text_set(buffer, 1);
2077 linep2= txt_get_span(st->text->lines.first, st->text->sell);
2078 charp2= st->text->selc;
2080 if(scu->sell!=linep2 || scu->selc!=charp2)
2081 txt_undo_add_toop(st->text, UNDO_STO, scu->sell, scu->selc, linep2, charp2);
2083 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2088 static int set_cursor_invoke(bContext *C, wmOperator *op, wmEvent *event)
2090 SpaceText *st= CTX_wm_space_text(C);
2091 ARegion *ar= CTX_wm_region(C);
2094 op->customdata= MEM_callocN(sizeof(SetCursor), "SetCursor");
2095 scu= op->customdata;
2096 scu->selecting= RNA_boolean_get(op->ptr, "select");
2098 scu->old[0]= event->mval[0];
2099 scu->old[1]= event->mval[1];
2101 if(!scu->selecting) {
2102 int curl= txt_get_span(st->text->lines.first, st->text->curl);
2103 int curc= st->text->curc;
2106 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 0);
2108 linep2= txt_get_span(st->text->lines.first, st->text->curl);
2109 charp2= st->text->selc;
2111 if(curl!=linep2 || curc!=charp2)
2112 txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
2115 scu->sell= txt_get_span(st->text->lines.first, st->text->sell);
2116 scu->selc= st->text->selc;
2118 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
2120 set_cursor_apply(C, op, event);
2122 return OPERATOR_RUNNING_MODAL;
2125 static int set_cursor_modal(bContext *C, wmOperator *op, wmEvent *event)
2127 switch(event->type) {
2131 set_cursor_exit(C, op);
2132 return OPERATOR_FINISHED;
2134 set_cursor_apply(C, op, event);
2138 return OPERATOR_RUNNING_MODAL;
2141 static int set_cursor_cancel(bContext *C, wmOperator *op)
2143 set_cursor_exit(C, op);
2144 return OPERATOR_FINISHED;
2147 void TEXT_OT_cursor_set(wmOperatorType *ot)
2150 ot->name= "Set Cursor";
2151 ot->idname= "TEXT_OT_cursor_set";
2154 ot->invoke= set_cursor_invoke;
2155 ot->modal= set_cursor_modal;
2156 ot->cancel= set_cursor_cancel;
2157 ot->poll= text_region_edit_poll;
2160 ot->flag= OPTYPE_REGISTER|OPTYPE_BLOCKING;
2163 RNA_def_boolean(ot->srna, "select", 0, "Select", "Set selection end rather than cursor.");
2166 /******************* line number operator **********************/
2168 static int line_number_invoke(bContext *C, wmOperator *op, wmEvent *event)
2170 SpaceText *st= CTX_wm_space_text(C);
2171 Text *text= CTX_data_edit_text(C);
2172 ARegion *ar= CTX_wm_region(C);
2173 short *mval= event->mval;
2175 static int jump_to= 0;
2176 static double last_jump= 0;
2178 if(!st->showlinenrs)
2179 return OPERATOR_PASS_THROUGH;
2181 if(!(mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<ar->winy-2))
2182 return OPERATOR_PASS_THROUGH;
2184 if(!(event->ascii>='0' && event->ascii<='9'))
2185 return OPERATOR_PASS_THROUGH;
2187 time = PIL_check_seconds_timer();
2188 if(last_jump < time-1)
2192 jump_to += (int)(event->ascii-'0');
2194 txt_move_toline(text, jump_to-1, 0);
2197 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2199 return OPERATOR_FINISHED;
2202 void TEXT_OT_line_number(wmOperatorType *ot)
2205 ot->name= "Line Number";
2206 ot->idname= "TEXT_OT_line_number";
2209 ot->invoke= line_number_invoke;
2210 ot->poll= text_region_edit_poll;
2213 /******************* insert operator **********************/
2215 static int insert_exec(bContext *C, wmOperator *op)
2217 SpaceText *st= CTX_wm_space_text(C);
2218 Text *text= CTX_data_edit_text(C);
2222 str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
2227 return OPERATOR_CANCELLED;
2229 if(st && st->overwrite)
2230 done= txt_replace_char(text, ascii);
2232 done= txt_add_char(text, ascii);
2235 return OPERATOR_CANCELLED;
2237 text_update_line_edited(text, text->curl);
2239 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2240 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2242 return OPERATOR_FINISHED;
2245 static int insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
2249 /* XXX old code from winqreadtextspace, is it still needed somewhere? */
2250 /* smartass code to prevent the CTRL/ALT events below from not working! */
2251 /*if(qual & (LR_ALTKEY|LR_CTRLKEY))
2255 str[0]= event->ascii;
2258 RNA_string_set(op->ptr, "text", str);
2259 ret = insert_exec(C, op);
2261 /* run the script while editing, evil but useful */
2262 if(ret==OPERATOR_FINISHED && CTX_wm_space_text(C)->live_edit)
2263 run_script_exec(C, op);
2268 void TEXT_OT_insert(wmOperatorType *ot)
2272 ot->idname= "TEXT_OT_insert";
2275 ot->exec= insert_exec;
2276 ot->invoke= insert_invoke;
2277 ot->poll= text_edit_poll;
2280 ot->flag= OPTYPE_REGISTER;
2283 RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
2286 /******************* find operator *********************/
2290 #define TEXT_REPLACE 1
2291 #define TEXT_MARK_ALL 2
2293 static int find_and_replace(bContext *C, wmOperator *op, short mode)
2295 SpaceText *st= CTX_wm_space_text(C);
2296 Text *start= NULL, *text= st->text;
2297 int flags, first= 1;
2300 if(!st->findstr[0] || (mode == TEXT_REPLACE && !st->replacestr[0]))
2301 return OPERATOR_CANCELLED;
2304 if(flags & ST_FIND_ALL)
2305 flags ^= ST_FIND_WRAP;
2309 txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
2313 /* Replace current */
2314 if(mode!=TEXT_FIND && txt_has_sel(text)) {
2315 tmp= txt_sel_to_buf(text);
2317 if(strcmp(st->findstr, tmp)==0) {
2318 if(mode==TEXT_REPLACE) {
2319 txt_insert_buf(text, st->replacestr);
2320 if(text->curl && text->curl->format) {
2321 MEM_freeN(text->curl->format);
2322 text->curl->format= NULL;
2324 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2326 else if(mode==TEXT_MARK_ALL) {
2328 UI_GetThemeColor4ubv(TH_SHADE2, color);
2330 if(txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
2331 if(tmp) MEM_freeN(tmp), tmp=NULL;
2335 txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
2336 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2344 if(txt_find_string(text, st->findstr, flags & ST_FIND_WRAP)) {
2345 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2347 else if(flags & ST_FIND_ALL) {
2348 if(text==start) break;
2349 if(!start) start= text;
2351 text= st->text= text->id.next;
2353 text= st->text= G.main->text.first;
2354 txt_move_toline(text, 0, 0);
2355 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2359 BKE_reportf(op->reports, RPT_INFO, "Text not found: %s", st->findstr);
2362 } while(mode==TEXT_MARK_ALL);
2364 return OPERATOR_FINISHED;
2367 static int find_exec(bContext *C, wmOperator *op)
2369 return find_and_replace(C, op, TEXT_FIND);
2372 void TEXT_OT_find(wmOperatorType *ot)
2376 ot->idname= "TEXT_OT_find";
2379 ot->exec= find_exec;
2380 ot->poll= text_space_edit_poll;
2383 /******************* replace operator *********************/
2385 static int replace_exec(bContext *C, wmOperator *op)
2387 return find_and_replace(C, op, TEXT_REPLACE);
2390 void TEXT_OT_replace(wmOperatorType *ot)
2393 ot->name= "Replace";
2394 ot->idname= "TEXT_OT_replace";
2397 ot->exec= replace_exec;
2398 ot->poll= text_space_edit_poll;
2401 /******************* mark all operator *********************/
2403 static int mark_all_exec(bContext *C, wmOperator *op)
2405 return find_and_replace(C, op, TEXT_MARK_ALL);
2408 void TEXT_OT_mark_all(wmOperatorType *ot)
2411 ot->name= "Mark All";
2412 ot->idname= "TEXT_OT_mark_all";
2415 ot->exec= mark_all_exec;
2416 ot->poll= text_space_edit_poll;
2419 /******************* find set selected *********************/
2421 static int find_set_selected_exec(bContext *C, wmOperator *op)
2423 SpaceText *st= CTX_wm_space_text(C);
2424 Text *text= CTX_data_edit_text(C);
2427 tmp= txt_sel_to_buf(text);
2428 BLI_strncpy(st->findstr, tmp, ST_MAX_FIND_STR);
2432 return OPERATOR_FINISHED;
2434 return find_and_replace(C, op, TEXT_FIND);
2437 void TEXT_OT_find_set_selected(wmOperatorType *ot)
2440 ot->name= "Find Set Selected";
2441 ot->idname= "TEXT_OT_find_set_selected";
2444 ot->exec= find_set_selected_exec;
2445 ot->poll= text_space_edit_poll;
2448 /******************* replace set selected *********************/
2450 static int replace_set_selected_exec(bContext *C, wmOperator *op)
2452 SpaceText *st= CTX_wm_space_text(C);
2453 Text *text= CTX_data_edit_text(C);
2456 tmp= txt_sel_to_buf(text);
2457 BLI_strncpy(st->replacestr, tmp, ST_MAX_FIND_STR);
2460 return OPERATOR_FINISHED;
2463 void TEXT_OT_replace_set_selected(wmOperatorType *ot)
2466 ot->name= "Replace Set Selected";
2467 ot->idname= "TEXT_OT_replace_set_selected";
2470 ot->exec= replace_set_selected_exec;
2471 ot->poll= text_space_edit_poll;
2474 /****************** resolve conflict operator ******************/
2476 enum { RESOLVE_IGNORE, RESOLVE_RELOAD, RESOLVE_SAVE, RESOLVE_MAKE_INTERNAL };
2477 static EnumPropertyItem resolution_items[]= {
2478 {RESOLVE_IGNORE, "IGNORE", 0, "Ignore", ""},
2479 {RESOLVE_RELOAD, "RELOAD", 0, "Reload", ""},
2480 {RESOLVE_SAVE, "SAVE", 0, "Save", ""},
2481 {RESOLVE_MAKE_INTERNAL, "MAKE_INTERNAL", 0, "Make Internal", ""},
2482 {0, NULL, 0, NULL, NULL}};
2484 /* returns 0 if file on disk is the same or Text is in memory only
2485 returns 1 if file has been modified on disk since last local edit
2486 returns 2 if file on disk has been deleted
2487 -1 is returned if an error occurs */
2489 int text_file_modified(Text *text)
2493 char file[FILE_MAXDIR+FILE_MAXFILE];
2495 if(!text || !text->name)
2498 BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2499 BLI_convertstringcode(file, G.sce);
2501 if(!BLI_exists(file))
2504 result = stat(file, &st);
2509 if((st.st_mode & S_IFMT) != S_IFREG)
2512 if(st.st_mtime > text->mtime)
2518 static void text_ignore_modified(Text *text)
2522 char file[FILE_MAXDIR+FILE_MAXFILE];
2524 if(!text || !text->name) return;
2526 BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2527 BLI_convertstringcode(file, G.sce);
2529 if(!BLI_exists(file)) return;
2531 result = stat(file, &st);
2533 if(result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
2536 text->mtime= st.st_mtime;
2539 static int resolve_conflict_exec(bContext *C, wmOperator *op)
2541 Text *text= CTX_data_edit_text(C);
2542 int resolution= RNA_enum_get(op->ptr, "resolution");
2544 switch(resolution) {
2545 case RESOLVE_RELOAD:
2546 return reload_exec(C, op);
2548 return save_exec(C, op);
2549 case RESOLVE_MAKE_INTERNAL:
2550 return make_internal_exec(C, op);
2551 case RESOLVE_IGNORE:
2552 text_ignore_modified(text);
2553 return OPERATOR_FINISHED;
2556 return OPERATOR_CANCELLED;
2559 static int resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *event)
2561 Text *text= CTX_data_edit_text(C);
2565 switch(text_file_modified(text)) {
2567 if(text->flags & TXT_ISDIRTY) {
2568 /* modified locally and externally, ahhh. offer more possibilites. */
2569 pup= uiPupMenuBegin(C, "File Modified Outside and Inside Blender", 0);
2570 layout= uiPupMenuLayout(pup);
2571 uiItemEnumO(layout, "Reload from disk (ignore local changes)", 0, op->type->idname, "resolution", RESOLVE_RELOAD);
2572 uiItemEnumO(layout, "Save to disk (ignore outside changes)", 0, op->type->idname, "resolution", RESOLVE_SAVE);
2573 uiItemEnumO(layout, "Make text internal (separate copy)", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
2574 uiPupMenuEnd(C, pup);
2577 pup= uiPupMenuBegin(C, "File Modified Outside Blender", 0);
2578 layout= uiPupMenuLayout(pup);
2579 uiItemEnumO(layout, "Reload from disk", 0, op->type->idname, "resolution", RESOLVE_RELOAD);
2580 uiItemEnumO(layout, "Make text internal (separate copy)", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
2581 uiItemEnumO(layout, "Ignore", 0, op->type->idname, "resolution", RESOLVE_IGNORE);
2582 uiPupMenuEnd(C, pup);
2586 pup= uiPupMenuBegin(C, "File Deleted Outside Blender", 0);
2587 layout= uiPupMenuLayout(pup);
2588 uiItemEnumO(layout, "Make text internal", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
2589 uiItemEnumO(layout, "Recreate file", 0, op->type->idname, "resolution", RESOLVE_SAVE);
2590 uiPupMenuEnd(C, pup);
2594 return OPERATOR_CANCELLED;
2597 void TEXT_OT_resolve_conflict(wmOperatorType *ot)
2600 ot->name= "Resolve Conflict";
2601 ot->idname= "TEXT_OT_resolve_conflict";
2602 ot->description= "When external text is out of sync, resolve the conflict.";
2605 ot->exec= resolve_conflict_exec;
2606 ot->invoke= resolve_conflict_invoke;
2607 ot->poll= save_poll;
2610 RNA_def_enum(ot->srna, "resolution", resolution_items, RESOLVE_IGNORE, "Resolution", "How to solve conflict due to different in internal and external text.");
2613 /********************** to 3d object operator *****************/
2615 static int to_3d_object_exec(bContext *C, wmOperator *op)
2617 Text *text= CTX_data_edit_text(C);
2618 int split_lines= RNA_boolean_get(op->ptr, "split_lines");
2620 ED_text_to_object(C, text, split_lines);
2622 return OPERATOR_FINISHED;
2625 void TEXT_OT_to_3d_object(wmOperatorType *ot)
2628 ot->name= "To 3D Object";
2629 ot->idname= "TEXT_OT_to_3d_object";
2632 ot->exec= to_3d_object_exec;
2633 ot->poll= text_edit_poll;
2636 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2639 RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text.");
2643 /************************ undo ******************************/
2645 void ED_text_undo_step(bContext *C, int step)
2647 Text *text= CTX_data_edit_text(C);
2657 text_update_edited(text);
2659 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2660 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);