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 ))
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", "To Spaces", NULL},
976 {TO_TABS, "TABS", "To Tabs", NULL},
977 {0, NULL, 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", "Line Begin", ""},
1274 {LINE_END, "LINE_END", "Line End", ""},
1275 {FILE_TOP, "FILE_TOP", "File Top", ""},
1276 {FILE_BOTTOM, "FILE_BOTTOM", "File Bottom", ""},
1277 {PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""},
1278 {NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""},
1279 {PREV_WORD, "PREVIOUS_WORD", "Previous Word", ""},
1280 {NEXT_WORD, "NEXT_WORD", "Next Word", ""},
1281 {PREV_LINE, "PREVIOUS_LINE", "Previous Line", ""},
1282 {NEXT_LINE, "NEXT_LINE", "Next Line", ""},
1283 {PREV_PAGE, "PREVIOUS_PAGE", "Previous Page", ""},
1284 {NEXT_PAGE, "NEXT_PAGE", "Next Page", ""},
1285 {0, NULL, 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", "Next Character", ""},
1617 {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""},
1618 {DEL_NEXT_WORD, "NEXT_WORD", "Next Word", ""},
1619 {DEL_PREV_WORD, "PREVIOUS_WORD", "Previous Word", ""},
1620 {0, NULL, 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 RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
1843 /******************** scroll bar operator *******************/
1845 static int scroll_bar_invoke(bContext *C, wmOperator *op, wmEvent *event)
1847 SpaceText *st= CTX_wm_space_text(C);
1848 ARegion *ar= CTX_wm_region(C);
1850 short *mval= event->mval;
1852 if(RNA_property_is_set(op->ptr, "lines"))
1853 return scroll_exec(C, op);
1855 /* verify we are in the right zone */
1856 if(!(mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<ar->winy))
1857 return OPERATOR_PASS_THROUGH;
1859 tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
1862 op->customdata= tsc;
1864 st->flags|= ST_SCROLL_SELECT;
1866 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1868 return OPERATOR_RUNNING_MODAL;
1871 void TEXT_OT_scroll_bar(wmOperatorType *ot)
1874 ot->name= "Scrollbar";
1875 ot->idname= "TEXT_OT_scroll_bar";
1878 ot->invoke= scroll_bar_invoke;
1879 ot->modal= scroll_modal;
1880 ot->cancel= scroll_cancel;
1881 ot->poll= text_region_edit_poll;
1884 RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
1887 /******************* set cursor operator **********************/
1889 typedef struct SetCursor {
1895 static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
1898 Text *text= st->text;
1903 if(sel) { linep= &text->sell; charp= &text->selc; }
1904 else { linep= &text->curl; charp= &text->curc; }
1906 y= (ar->winy - y)/st->lheight;
1909 x-= TXT_OFFSET+TEXTXLOC;
1914 x = (x/text_font_width_character(st)) + st->left;
1917 int i, j, endj, curs, max, chop, start, end, chars, loop;
1920 /* Point to first visible line */
1921 *linep= text->lines.first;
1922 for(i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;
1924 max= wrap_width(st, ar);
1927 while(loop && *linep) {
1934 for(i=0, j=0; loop; j++) {
1936 /* Mimic replacement of tabs */
1937 ch= (*linep)->line[j];
1939 chars= st->tabnumber-i%st->tabnumber;
1946 /* Gone too far, go back to last wrap point */
1951 /* Exactly at the cursor, done */
1953 else if(y==0 && i-start==x) {
1957 /* Prepare curs for next wrap */
1968 if(y==0 && i-start>=x) {
1974 else if(ch==' ' || ch=='-' || ch=='\0') {
1975 if(y==0 && i-start>=x) {
1988 if(!loop || y<0) break;
1990 if(!(*linep)->next) {
1991 *charp= (*linep)->len;
1995 /* On correct line but didn't meet cursor, must be at end */
1997 *charp= (*linep)->len;
2000 *linep= (*linep)->next;
2006 y-= txt_get_span(text->lines.first, *linep) - st->top;
2009 while(y-- != 0) if((*linep)->next) *linep= (*linep)->next;
2012 while(y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
2016 w= flatten_string(st, &fs, (*linep)->line);
2017 if(x<w) *charp= fs.accum[x];
2018 else *charp= (*linep)->len;
2019 flatten_string_free(&fs);
2021 if(!sel) txt_pop_sel(text);
2024 static void set_cursor_apply(bContext *C, wmOperator *op, wmEvent *event)
2026 SpaceText *st= CTX_wm_space_text(C);
2027 ARegion *ar= CTX_wm_region(C);
2028 SetCursor *scu= op->customdata;
2030 if(event->mval[1]<0 || event->mval[1]>ar->winy) {
2031 int d= (scu->old[1]-event->mval[1])*st->pix_per_line;
2032 if(d) screen_skip(st, d);
2034 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1]<0?0:ar->winy, 1);
2036 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2038 else if(!st->wordwrap && (event->mval[0]<0 || event->mval[0]>ar->winx)) {
2039 if(event->mval[0]>ar->winx) st->left++;
2040 else if(event->mval[0]<0 && st->left>0) st->left--;
2042 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
2044 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2045 // XXX PIL_sleep_ms(10);
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);
2052 scu->old[0]= event->mval[0];
2053 scu->old[1]= event->mval[1];
2057 static void set_cursor_exit(bContext *C, wmOperator *op)
2059 SpaceText *st= CTX_wm_space_text(C);
2060 Text *text= st->text;
2061 SetCursor *scu= op->customdata;
2065 if(txt_has_sel(text)) {
2066 buffer = txt_sel_to_buf(text);
2067 WM_clipboard_text_set(buffer, 1);
2071 linep2= txt_get_span(st->text->lines.first, st->text->sell);
2072 charp2= st->text->selc;
2074 if(scu->sell!=linep2 || scu->selc!=charp2)
2075 txt_undo_add_toop(st->text, UNDO_STO, scu->sell, scu->selc, linep2, charp2);
2077 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2082 static int set_cursor_invoke(bContext *C, wmOperator *op, wmEvent *event)
2084 SpaceText *st= CTX_wm_space_text(C);
2085 ARegion *ar= CTX_wm_region(C);
2088 op->customdata= MEM_callocN(sizeof(SetCursor), "SetCursor");
2089 scu= op->customdata;
2090 scu->selecting= RNA_boolean_get(op->ptr, "select");
2092 scu->old[0]= event->mval[0];
2093 scu->old[1]= event->mval[1];
2095 if(!scu->selecting) {
2096 int curl= txt_get_span(st->text->lines.first, st->text->curl);
2097 int curc= st->text->curc;
2100 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 0);
2102 linep2= txt_get_span(st->text->lines.first, st->text->curl);
2103 charp2= st->text->selc;
2105 if(curl!=linep2 || curc!=charp2)
2106 txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
2109 scu->sell= txt_get_span(st->text->lines.first, st->text->sell);
2110 scu->selc= st->text->selc;
2112 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
2114 set_cursor_apply(C, op, event);
2116 return OPERATOR_RUNNING_MODAL;
2119 static int set_cursor_modal(bContext *C, wmOperator *op, wmEvent *event)
2121 switch(event->type) {
2125 set_cursor_exit(C, op);
2126 return OPERATOR_FINISHED;
2128 set_cursor_apply(C, op, event);
2132 return OPERATOR_RUNNING_MODAL;
2135 static int set_cursor_cancel(bContext *C, wmOperator *op)
2137 set_cursor_exit(C, op);
2138 return OPERATOR_FINISHED;
2141 void TEXT_OT_cursor_set(wmOperatorType *ot)
2144 ot->name= "Set Cursor";
2145 ot->idname= "TEXT_OT_cursor_set";
2148 ot->invoke= set_cursor_invoke;
2149 ot->modal= set_cursor_modal;
2150 ot->cancel= set_cursor_cancel;
2151 ot->poll= text_region_edit_poll;
2154 ot->flag= OPTYPE_REGISTER;
2157 RNA_def_boolean(ot->srna, "select", 0, "Select", "Set selection end rather than cursor.");
2160 /******************* line number operator **********************/
2162 static int line_number_invoke(bContext *C, wmOperator *op, wmEvent *event)
2164 SpaceText *st= CTX_wm_space_text(C);
2165 Text *text= CTX_data_edit_text(C);
2166 ARegion *ar= CTX_wm_region(C);
2167 short *mval= event->mval;
2169 static int jump_to= 0;
2170 static double last_jump= 0;
2172 if(!st->showlinenrs)
2173 return OPERATOR_PASS_THROUGH;
2175 if(!(mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<ar->winy-2))
2176 return OPERATOR_PASS_THROUGH;
2178 if(!(event->ascii>='0' && event->ascii<='9'))
2179 return OPERATOR_PASS_THROUGH;
2181 time = PIL_check_seconds_timer();
2182 if(last_jump < time-1)
2186 jump_to += (int)(event->ascii-'0');
2188 txt_move_toline(text, jump_to-1, 0);
2191 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2193 return OPERATOR_FINISHED;
2196 void TEXT_OT_line_number(wmOperatorType *ot)
2199 ot->name= "Line Number";
2200 ot->idname= "TEXT_OT_line_number";
2203 ot->invoke= line_number_invoke;
2204 ot->poll= text_region_edit_poll;
2207 /******************* insert operator **********************/
2209 static int insert_exec(bContext *C, wmOperator *op)
2211 SpaceText *st= CTX_wm_space_text(C);
2212 Text *text= CTX_data_edit_text(C);
2216 str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
2221 return OPERATOR_CANCELLED;
2223 if(st && st->overwrite)
2224 done= txt_replace_char(text, ascii);
2226 done= txt_add_char(text, ascii);
2229 return OPERATOR_CANCELLED;
2231 text_update_line_edited(text, text->curl);
2233 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2234 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2236 return OPERATOR_FINISHED;
2239 static int insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
2243 /* XXX old code from winqreadtextspace, is it still needed somewhere? */
2244 /* smartass code to prevent the CTRL/ALT events below from not working! */
2245 /*if(qual & (LR_ALTKEY|LR_CTRLKEY))
2249 str[0]= event->ascii;
2252 RNA_string_set(op->ptr, "text", str);
2253 ret = insert_exec(C, op);
2255 /* run the script while editing, evil but useful */
2256 if(ret==OPERATOR_FINISHED && CTX_wm_space_text(C)->live_edit)
2257 run_script_exec(C, op);
2262 void TEXT_OT_insert(wmOperatorType *ot)
2266 ot->idname= "TEXT_OT_insert";
2269 ot->exec= insert_exec;
2270 ot->invoke= insert_invoke;
2271 ot->poll= text_edit_poll;
2274 ot->flag= OPTYPE_REGISTER;
2277 RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
2280 /******************* find operator *********************/
2284 #define TEXT_REPLACE 1
2285 #define TEXT_MARK_ALL 2
2287 static int find_and_replace(bContext *C, wmOperator *op, short mode)
2289 SpaceText *st= CTX_wm_space_text(C);
2290 Text *start= NULL, *text= st->text;
2291 int flags, first= 1;
2294 if(!st->findstr[0] || (mode == TEXT_REPLACE && !st->replacestr[0]))
2295 return OPERATOR_CANCELLED;
2298 if(flags & ST_FIND_ALL)
2299 flags ^= ST_FIND_WRAP;
2303 txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
2307 /* Replace current */
2308 if(mode!=TEXT_FIND && txt_has_sel(text)) {
2309 tmp= txt_sel_to_buf(text);
2311 if(strcmp(st->findstr, tmp)==0) {
2312 if(mode==TEXT_REPLACE) {
2313 txt_insert_buf(text, st->replacestr);
2314 if(text->curl && text->curl->format) {
2315 MEM_freeN(text->curl->format);
2316 text->curl->format= NULL;
2318 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2320 else if(mode==TEXT_MARK_ALL) {
2322 UI_GetThemeColor4ubv(TH_SHADE2, color);
2324 if(txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
2325 if(tmp) MEM_freeN(tmp), tmp=NULL;
2329 txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
2330 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2338 if(txt_find_string(text, st->findstr, flags & ST_FIND_WRAP)) {
2339 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2341 else if(flags & ST_FIND_ALL) {
2342 if(text==start) break;
2343 if(!start) start= text;
2345 text= st->text= text->id.next;
2347 text= st->text= G.main->text.first;
2348 txt_move_toline(text, 0, 0);
2349 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2353 BKE_reportf(op->reports, RPT_INFO, "Text not found: %s", st->findstr);
2356 } while(mode==TEXT_MARK_ALL);
2358 return OPERATOR_FINISHED;
2361 static int find_exec(bContext *C, wmOperator *op)
2363 return find_and_replace(C, op, TEXT_FIND);
2366 void TEXT_OT_find(wmOperatorType *ot)
2370 ot->idname= "TEXT_OT_find";
2373 ot->exec= find_exec;
2374 ot->poll= text_space_edit_poll;
2377 /******************* replace operator *********************/
2379 static int replace_exec(bContext *C, wmOperator *op)
2381 return find_and_replace(C, op, TEXT_REPLACE);
2384 void TEXT_OT_replace(wmOperatorType *ot)
2387 ot->name= "Replace";
2388 ot->idname= "TEXT_OT_replace";
2391 ot->exec= replace_exec;
2392 ot->poll= text_space_edit_poll;
2395 /******************* mark all operator *********************/
2397 static int mark_all_exec(bContext *C, wmOperator *op)
2399 return find_and_replace(C, op, TEXT_MARK_ALL);
2402 void TEXT_OT_mark_all(wmOperatorType *ot)
2405 ot->name= "Mark All";
2406 ot->idname= "TEXT_OT_mark_all";
2409 ot->exec= mark_all_exec;
2410 ot->poll= text_space_edit_poll;
2413 /******************* find set selected *********************/
2415 static int find_set_selected_exec(bContext *C, wmOperator *op)
2417 SpaceText *st= CTX_wm_space_text(C);
2418 Text *text= CTX_data_edit_text(C);
2421 tmp= txt_sel_to_buf(text);
2422 BLI_strncpy(st->findstr, tmp, ST_MAX_FIND_STR);
2426 return OPERATOR_FINISHED;
2428 return find_and_replace(C, op, TEXT_FIND);
2431 void TEXT_OT_find_set_selected(wmOperatorType *ot)
2434 ot->name= "Find Set Selected";
2435 ot->idname= "TEXT_OT_find_set_selected";
2438 ot->exec= find_set_selected_exec;
2439 ot->poll= text_space_edit_poll;
2442 /******************* replace set selected *********************/
2444 static int replace_set_selected_exec(bContext *C, wmOperator *op)
2446 SpaceText *st= CTX_wm_space_text(C);
2447 Text *text= CTX_data_edit_text(C);
2450 tmp= txt_sel_to_buf(text);
2451 BLI_strncpy(st->replacestr, tmp, ST_MAX_FIND_STR);
2454 return OPERATOR_FINISHED;
2457 void TEXT_OT_replace_set_selected(wmOperatorType *ot)
2460 ot->name= "Replace Set Selected";
2461 ot->idname= "TEXT_OT_replace_set_selected";
2464 ot->exec= replace_set_selected_exec;
2465 ot->poll= text_space_edit_poll;
2468 /****************** resolve conflict operator ******************/
2470 enum { RESOLVE_IGNORE, RESOLVE_RELOAD, RESOLVE_SAVE, RESOLVE_MAKE_INTERNAL };
2471 static EnumPropertyItem resolution_items[]= {
2472 {RESOLVE_IGNORE, "IGNORE", "Ignore", ""},
2473 {RESOLVE_RELOAD, "RELOAD", "Reload", ""},
2474 {RESOLVE_SAVE, "SAVE", "Save", ""},
2475 {RESOLVE_MAKE_INTERNAL, "MAKE_INTERNAL", "Make Internal", ""},
2476 {0, NULL, NULL, NULL}};
2478 /* returns 0 if file on disk is the same or Text is in memory only
2479 returns 1 if file has been modified on disk since last local edit
2480 returns 2 if file on disk has been deleted
2481 -1 is returned if an error occurs */
2483 int text_file_modified(Text *text)
2487 char file[FILE_MAXDIR+FILE_MAXFILE];
2489 if(!text || !text->name)
2492 BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2493 BLI_convertstringcode(file, G.sce);
2495 if(!BLI_exists(file))
2498 result = stat(file, &st);
2503 if((st.st_mode & S_IFMT) != S_IFREG)
2506 if(st.st_mtime > text->mtime)
2512 static void text_ignore_modified(Text *text)
2516 char file[FILE_MAXDIR+FILE_MAXFILE];
2518 if(!text || !text->name) return;
2520 BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2521 BLI_convertstringcode(file, G.sce);
2523 if(!BLI_exists(file)) return;
2525 result = stat(file, &st);
2527 if(result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
2530 text->mtime= st.st_mtime;
2533 static int resolve_conflict_exec(bContext *C, wmOperator *op)
2535 Text *text= CTX_data_edit_text(C);
2536 int resolution= RNA_enum_get(op->ptr, "resolution");
2538 switch(resolution) {
2539 case RESOLVE_RELOAD:
2540 return reload_exec(C, op);
2542 return save_exec(C, op);
2543 case RESOLVE_MAKE_INTERNAL:
2544 return make_internal_exec(C, op);
2545 case RESOLVE_IGNORE:
2546 text_ignore_modified(text);
2547 return OPERATOR_FINISHED;
2550 return OPERATOR_CANCELLED;
2553 static int resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *event)
2555 Text *text= CTX_data_edit_text(C);
2559 switch(text_file_modified(text)) {
2561 if(text->flags & TXT_ISDIRTY) {
2562 /* modified locally and externally, ahhh. offer more possibilites. */
2563 pup= uiPupMenuBegin(C, "File Modified Outside and Inside Blender", 0);
2564 layout= uiPupMenuLayout(pup);
2565 uiItemEnumO(layout, "Reload from disk (ignore local changes)", 0, op->type->idname, "resolution", RESOLVE_RELOAD);
2566 uiItemEnumO(layout, "Save to disk (ignore outside changes)", 0, op->type->idname, "resolution", RESOLVE_SAVE);
2567 uiItemEnumO(layout, "Make text internal (separate copy)", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
2568 uiPupMenuEnd(C, pup);
2571 pup= uiPupMenuBegin(C, "File Modified Outside Blender", 0);
2572 layout= uiPupMenuLayout(pup);
2573 uiItemEnumO(layout, "Reload from disk", 0, op->type->idname, "resolution", RESOLVE_RELOAD);
2574 uiItemEnumO(layout, "Make text internal (separate copy)", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
2575 uiItemEnumO(layout, "Ignore", 0, op->type->idname, "resolution", RESOLVE_IGNORE);
2576 uiPupMenuEnd(C, pup);
2580 pup= uiPupMenuBegin(C, "File Deleted Outside Blender", 0);
2581 layout= uiPupMenuLayout(pup);
2582 uiItemEnumO(layout, "Make text internal", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
2583 uiItemEnumO(layout, "Recreate file", 0, op->type->idname, "resolution", RESOLVE_SAVE);
2584 uiPupMenuEnd(C, pup);
2588 return OPERATOR_CANCELLED;
2591 void TEXT_OT_resolve_conflict(wmOperatorType *ot)
2594 ot->name= "Resolve Conflict";
2595 ot->idname= "TEXT_OT_resolve_conflict";
2596 ot->description= "When external text is out of sync, resolve the conflict.";
2599 ot->exec= resolve_conflict_exec;
2600 ot->invoke= resolve_conflict_invoke;
2601 ot->poll= save_poll;
2604 RNA_def_enum(ot->srna, "resolution", resolution_items, RESOLVE_IGNORE, "Resolution", "How to solve conflict due to different in internal and external text.");
2607 /********************** to 3d object operator *****************/
2609 static int to_3d_object_exec(bContext *C, wmOperator *op)
2611 Text *text= CTX_data_edit_text(C);
2612 int split_lines= RNA_boolean_get(op->ptr, "split_lines");
2614 ED_text_to_object(C, text, split_lines);
2616 return OPERATOR_FINISHED;
2619 void TEXT_OT_to_3d_object(wmOperatorType *ot)
2622 ot->name= "To 3D Object";
2623 ot->idname= "TEXT_OT_to_3d_object";
2626 ot->exec= to_3d_object_exec;
2627 ot->poll= text_edit_poll;
2630 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2633 RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text.");
2637 /************************ undo ******************************/
2639 void ED_text_undo_step(bContext *C, int step)
2641 Text *text= CTX_data_edit_text(C);
2651 text_update_edited(text);
2653 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2654 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);