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 BKE_report(op->reports, RPT_ERROR, "Python script fail, look in the console for now...");
528 return OPERATOR_CANCELLED;
532 void TEXT_OT_run_script(wmOperatorType *ot)
535 ot->name= "Run Script";
536 ot->idname= "TEXT_OT_run_script";
539 ot->exec= run_script_exec;
540 ot->poll= text_edit_poll;
544 /******************* refresh pyconstraints operator *********************/
546 static int refresh_pyconstraints_exec(bContext *C, wmOperator *op)
548 #ifndef DISABLE_PYTHON
549 Text *text= CTX_data_edit_text(C);
550 Scene *scene= CTX_data_scene(C);
555 /* check all pyconstraints */
556 for(ob= CTX_data_main(C)->object.first; ob; ob= ob->id.next) {
558 if(ob->type==OB_ARMATURE && ob->pose) {
560 for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
561 for(con = pchan->constraints.first; con; con= con->next) {
562 if(con->type==CONSTRAINT_TYPE_PYTHON) {
563 bPythonConstraint *data = con->data;
564 if(data->text==text) BPY_pyconstraint_update(ob, con);
571 for(con = ob->constraints.first; con; con= con->next) {
572 if(con->type==CONSTRAINT_TYPE_PYTHON) {
573 bPythonConstraint *data = con->data;
574 if(data->text==text) BPY_pyconstraint_update(ob, con);
580 DAG_object_flush_update(scene, ob, OB_RECALC_DATA);
585 return OPERATOR_FINISHED;
588 void TEXT_OT_refresh_pyconstraints(wmOperatorType *ot)
591 ot->name= "Refresh PyConstraints";
592 ot->idname= "TEXT_OT_refresh_pyconstraints";
595 ot->exec= refresh_pyconstraints_exec;
596 ot->poll= text_edit_poll;
599 /******************* paste operator *********************/
601 static char *txt_copy_selected(Text *text)
603 TextLine *tmp, *linef, *linel;
605 int charf, charl, length= 0;
607 if(!text) return NULL;
608 if(!text->curl) return NULL;
609 if(!text->sell) return NULL;
611 if(!txt_has_sel(text)) return NULL;
613 if(text->curl==text->sell) {
614 linef= linel= text->curl;
616 if(text->curc < text->selc) {
625 else if(txt_get_span(text->curl, text->sell)<0) {
643 buf= MEM_callocN(length+1, "cut buffera");
645 BLI_strncpy(buf, linef->line + charf, length+1);
648 length+= linef->len - charf;
650 length++; /* For the '\n' */
653 while(tmp && tmp!= linel) {
658 buf= MEM_callocN(length+1, "cut bufferb");
660 strncpy(buf, linef->line+ charf, linef->len-charf);
661 length= linef->len-charf;
666 while(tmp && tmp!=linel) {
667 strncpy(buf+length, tmp->line, tmp->len);
674 strncpy(buf+length, linel->line, charl);
683 static int paste_exec(bContext *C, wmOperator *op)
685 Text *text= CTX_data_edit_text(C);
687 int selection= RNA_boolean_get(op->ptr, "selection");
689 buf= WM_clipboard_text_get(selection);
692 return OPERATOR_CANCELLED;
694 txt_insert_buf(text, buf);
695 text_update_edited(text);
699 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
700 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
702 return OPERATOR_FINISHED;
705 void TEXT_OT_paste(wmOperatorType *ot)
709 ot->idname= "TEXT_OT_paste";
712 ot->exec= paste_exec;
713 ot->poll= text_edit_poll;
716 ot->flag= OPTYPE_REGISTER;
719 RNA_def_boolean(ot->srna, "selection", 0, "Selection", "Paste text selected elsewhere rather than copied, X11 only.");
722 /******************* copy operator *********************/
724 static void txt_copy_clipboard(Text *text)
728 buf= txt_copy_selected(text);
731 WM_clipboard_text_set(buf, 0);
736 static int copy_exec(bContext *C, wmOperator *op)
738 Text *text= CTX_data_edit_text(C);
740 txt_copy_clipboard(text);
742 return OPERATOR_FINISHED;
745 void TEXT_OT_copy(wmOperatorType *ot)
749 ot->idname= "TEXT_OT_copy";
753 ot->poll= text_edit_poll;
756 /******************* cut operator *********************/
758 static int cut_exec(bContext *C, wmOperator *op)
760 Text *text= CTX_data_edit_text(C);
762 txt_copy_clipboard(text);
763 txt_delete_selected(text);
765 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
766 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
768 return OPERATOR_FINISHED;
771 void TEXT_OT_cut(wmOperatorType *ot)
775 ot->idname= "TEXT_OT_cut";
779 ot->poll= text_edit_poll;
782 ot->flag= OPTYPE_REGISTER;
785 /******************* indent operator *********************/
787 static int indent_exec(bContext *C, wmOperator *op)
789 Text *text= CTX_data_edit_text(C);
791 if(txt_has_sel(text)) {
792 txt_order_cursors(text);
796 txt_add_char(text, '\t');
798 text_update_edited(text);
800 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
801 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
803 return OPERATOR_FINISHED;
806 void TEXT_OT_indent(wmOperatorType *ot)
810 ot->idname= "TEXT_OT_indent";
813 ot->exec= indent_exec;
814 ot->poll= text_edit_poll;
817 ot->flag= OPTYPE_REGISTER;
820 /******************* unindent operator *********************/
822 static int unindent_exec(bContext *C, wmOperator *op)
824 Text *text= CTX_data_edit_text(C);
826 if(txt_has_sel(text)) {
827 txt_order_cursors(text);
830 text_update_edited(text);
832 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
833 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
835 return OPERATOR_FINISHED;
838 return OPERATOR_CANCELLED;
841 void TEXT_OT_unindent(wmOperatorType *ot)
844 ot->name= "Unindent";
845 ot->idname= "TEXT_OT_unindent";
848 ot->exec= unindent_exec;
849 ot->poll= text_edit_poll;
852 ot->flag= OPTYPE_REGISTER;
855 /******************* line break operator *********************/
857 static int line_break_exec(bContext *C, wmOperator *op)
859 Text *text= CTX_data_edit_text(C);
862 // double check tabs before splitting the line
863 curtab= setcurr_tab(text);
864 txt_split_curline(text);
866 for(a=0; a < curtab; a++)
867 txt_add_char(text, '\t');
871 text_update_line_edited(text, text->curl->prev);
872 text_update_line_edited(text, text->curl);
875 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
876 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
878 return OPERATOR_CANCELLED;
881 void TEXT_OT_line_break(wmOperatorType *ot)
884 ot->name= "Line Break";
885 ot->idname= "TEXT_OT_line_break";
888 ot->exec= line_break_exec;
889 ot->poll= text_edit_poll;
892 ot->flag= OPTYPE_REGISTER;
895 /******************* comment operator *********************/
897 static int comment_exec(bContext *C, wmOperator *op)
899 Text *text= CTX_data_edit_text(C);
901 if(txt_has_sel(text)) {
902 txt_order_cursors(text);
904 text_update_edited(text);
906 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
907 return OPERATOR_FINISHED;
910 return OPERATOR_CANCELLED;
913 void TEXT_OT_comment(wmOperatorType *ot)
917 ot->idname= "TEXT_OT_comment";
920 ot->exec= comment_exec;
921 ot->poll= text_edit_poll;
924 ot->flag= OPTYPE_REGISTER;
927 /******************* uncomment operator *********************/
929 static int uncomment_exec(bContext *C, wmOperator *op)
931 Text *text= CTX_data_edit_text(C);
933 if(txt_has_sel(text)) {
934 txt_order_cursors(text);
936 text_update_edited(text);
938 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
940 return OPERATOR_FINISHED;
943 return OPERATOR_CANCELLED;
946 void TEXT_OT_uncomment(wmOperatorType *ot)
949 ot->name= "Uncomment";
950 ot->idname= "TEXT_OT_uncomment";
953 ot->exec= uncomment_exec;
954 ot->poll= text_edit_poll;
957 ot->flag= OPTYPE_REGISTER;
960 /******************* convert whitespace operator *********************/
962 enum { TO_SPACES, TO_TABS };
963 static EnumPropertyItem whitespace_type_items[]= {
964 {TO_SPACES, "SPACES", "To Spaces", NULL},
965 {TO_TABS, "TABS", "To Tabs", NULL},
966 {0, NULL, NULL, NULL}};
968 static int convert_whitespace_exec(bContext *C, wmOperator *op)
970 SpaceText *st= CTX_wm_space_text(C);
971 Text *text= CTX_data_edit_text(C);
975 char *text_check_line, *new_line;
976 int extra, number; //unknown for now
977 int type= RNA_enum_get(op->ptr, "type");
979 tmp = text->lines.first;
981 //first convert to all space, this make it alot easier to convert to tabs because there is no mixtures of ' ' && '\t'
983 text_check_line = tmp->line;
984 number = flatten_string(st, &fs, text_check_line)+1;
985 flatten_string_free(&fs);
986 new_line = MEM_callocN(number, "Converted_Line");
988 for(a=0; a < strlen(text_check_line); a++) { //foreach char in line
989 if(text_check_line[a] == '\t') { //checking for tabs
990 //get the number of spaces this tabs is showing
991 //i dont like doing it this way but will look into it later
993 number = flatten_string(st, &fs, new_line);
994 flatten_string_free(&fs);
996 new_line[j+1] = '\0';
997 number = flatten_string(st, &fs, new_line)-number;
998 flatten_string_free(&fs);
1000 for(extra = 0; extra < number; extra++) {
1006 new_line[j] = text_check_line[a];
1011 // put new_line in the tmp->line spot still need to try and set the curc correctly
1012 if(tmp->line) MEM_freeN(tmp->line);
1013 if(tmp->format) MEM_freeN(tmp->format);
1015 tmp->line = new_line;
1016 tmp->len = strlen(new_line);
1021 if(type == TO_TABS) // Converting to tabs
1022 { //start over from the begining
1023 tmp = text->lines.first;
1026 text_check_line = tmp->line;
1028 for(a = 0; a < strlen(text_check_line); a++) {
1030 for(j = 0; j < (size_t)st->tabnumber; j++) {
1031 if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
1032 if(text_check_line[a+j] != ' ') {
1037 if(!number) { //found all number of space to equal a tab
1038 a = a+(st->tabnumber-1);
1043 if( extra > 0 ) { //got tabs make malloc and do what you have to do
1044 new_line = MEM_callocN(strlen(text_check_line)-(((st->tabnumber*extra)-extra)-1), "Converted_Line");
1045 extra = 0; //reuse vars
1046 for(a = 0; a < strlen(text_check_line); a++) {
1048 for(j = 0; j < (size_t)st->tabnumber; j++) {
1049 if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
1050 if(text_check_line[a+j] != ' ') {
1056 if(!number) { //found all number of space to equal a tab
1057 new_line[extra] = '\t';
1058 a = a+(st->tabnumber-1);
1062 else { //not adding a tab
1063 new_line[extra] = text_check_line[a];
1067 new_line[extra] = '\0';
1068 // put new_line in the tmp->line spot still need to try and set the curc correctly
1069 if(tmp->line) MEM_freeN(tmp->line);
1070 if(tmp->format) MEM_freeN(tmp->format);
1072 tmp->line = new_line;
1073 tmp->len = strlen(new_line);
1080 text_update_edited(text);
1082 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1084 return OPERATOR_FINISHED;
1087 void TEXT_OT_convert_whitespace(wmOperatorType *ot)
1090 ot->name= "Convert Whitespace";
1091 ot->idname= "TEXT_OT_convert_whitespace";
1094 ot->exec= convert_whitespace_exec;
1095 ot->poll= text_edit_poll;
1098 ot->flag= OPTYPE_REGISTER;
1101 RNA_def_enum(ot->srna, "type", whitespace_type_items, TO_SPACES, "type", "Type of whitespace to convert to.");
1104 /******************* select all operator *********************/
1106 static int select_all_exec(bContext *C, wmOperator *op)
1108 Text *text= CTX_data_edit_text(C);
1112 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1114 return OPERATOR_FINISHED;
1117 void TEXT_OT_select_all(wmOperatorType *ot)
1120 ot->name= "Select All";
1121 ot->idname= "TEXT_OT_select_all";
1124 ot->exec= select_all_exec;
1125 ot->poll= text_edit_poll;
1128 ot->flag= OPTYPE_REGISTER;
1131 /******************* select line operator *********************/
1133 static int select_line_exec(bContext *C, wmOperator *op)
1135 Text *text= CTX_data_edit_text(C);
1139 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1141 return OPERATOR_FINISHED;
1144 void TEXT_OT_select_line(wmOperatorType *ot)
1147 ot->name= "Select Line";
1148 ot->idname= "TEXT_OT_select_line";
1150 /* api clinebacks */
1151 ot->exec= select_line_exec;
1152 ot->poll= text_edit_poll;
1155 ot->flag= OPTYPE_REGISTER;
1158 /******************* previous marker operator *********************/
1160 static int previous_marker_exec(bContext *C, wmOperator *op)
1162 Text *text= CTX_data_edit_text(C);
1166 lineno= txt_get_span(text->lines.first, text->curl);
1167 mrk= text->markers.last;
1168 while(mrk && (mrk->lineno>lineno || (mrk->lineno==lineno && mrk->end > text->curc)))
1170 if(!mrk) mrk= text->markers.last;
1172 txt_move_to(text, mrk->lineno, mrk->start, 0);
1173 txt_move_to(text, mrk->lineno, mrk->end, 1);
1176 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1178 return OPERATOR_FINISHED;
1181 void TEXT_OT_previous_marker(wmOperatorType *ot)
1184 ot->name= "Previous Marker";
1185 ot->idname= "TEXT_OT_previous_marker";
1188 ot->exec= previous_marker_exec;
1189 ot->poll= text_edit_poll;
1192 ot->flag= OPTYPE_REGISTER;
1195 /******************* next marker operator *********************/
1197 static int next_marker_exec(bContext *C, wmOperator *op)
1199 Text *text= CTX_data_edit_text(C);
1203 lineno= txt_get_span(text->lines.first, text->curl);
1204 mrk= text->markers.first;
1205 while(mrk && (mrk->lineno<lineno || (mrk->lineno==lineno && mrk->start <= text->curc)))
1207 if(!mrk) mrk= text->markers.first;
1209 txt_move_to(text, mrk->lineno, mrk->start, 0);
1210 txt_move_to(text, mrk->lineno, mrk->end, 1);
1213 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1215 return OPERATOR_FINISHED;
1218 void TEXT_OT_next_marker(wmOperatorType *ot)
1221 ot->name= "Next Marker";
1222 ot->idname= "TEXT_OT_next_marker";
1225 ot->exec= next_marker_exec;
1226 ot->poll= text_edit_poll;
1229 ot->flag= OPTYPE_REGISTER;
1232 /******************* clear all markers operator *********************/
1234 static int clear_all_markers_exec(bContext *C, wmOperator *op)
1236 Text *text= CTX_data_edit_text(C);
1238 txt_clear_markers(text, 0, 0);
1240 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1242 return OPERATOR_FINISHED;
1245 void TEXT_OT_markers_clear(wmOperatorType *ot)
1248 ot->name= "Clear All Markers";
1249 ot->idname= "TEXT_OT_markers_clear";
1252 ot->exec= clear_all_markers_exec;
1253 ot->poll= text_edit_poll;
1256 ot->flag= OPTYPE_REGISTER;
1259 /************************ move operator ************************/
1261 static EnumPropertyItem move_type_items[]= {
1262 {LINE_BEGIN, "LINE_BEGIN", "Line Begin", ""},
1263 {LINE_END, "LINE_END", "Line End", ""},
1264 {FILE_TOP, "FILE_TOP", "File Top", ""},
1265 {FILE_BOTTOM, "FILE_BOTTOM", "File Bottom", ""},
1266 {PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""},
1267 {NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""},
1268 {PREV_WORD, "PREVIOUS_WORD", "Previous Word", ""},
1269 {NEXT_WORD, "NEXT_WORD", "Next Word", ""},
1270 {PREV_LINE, "PREVIOUS_LINE", "Previous Line", ""},
1271 {NEXT_LINE, "NEXT_LINE", "Next Line", ""},
1272 {PREV_PAGE, "PREVIOUS_PAGE", "Previous Page", ""},
1273 {NEXT_PAGE, "NEXT_PAGE", "Next Page", ""},
1274 {0, NULL, NULL, NULL}};
1276 static void wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
1278 Text *text= st->text;
1279 int offl, offc, lin;
1281 lin= txt_get_span(text->lines.first, text->sell);
1282 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1285 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, -offc);
1288 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, -offc);
1294 static void wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
1296 Text *text= st->text;
1297 int offl, offc, lin, startl, c;
1299 lin= txt_get_span(text->lines.first, text->sell);
1300 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1303 while (offl==startl && text->sell->line[c]!='\0') {
1305 wrap_offset(st, ar, text->sell, c, &offl, &offc);
1306 } if (offl!=startl) c--;
1309 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, c);
1312 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, c);
1318 static void wrap_move_up(SpaceText *st, ARegion *ar, short sel)
1320 Text *text= st->text;
1321 int offl, offl_1, offc, fromline, toline, c, target;
1323 wrap_offset(st, ar, text->sell, 0, &offl_1, &offc);
1324 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1325 fromline= toline= txt_get_span(text->lines.first, text->sell);
1326 target= text->selc + offc;
1329 if (!text->sell->prev) {
1330 txt_move_bol(text, sel);
1334 c= text->sell->prev->len; /* End of prev. line */
1335 wrap_offset(st, ar, text->sell->prev, c, &offl, &offc);
1338 c= -offc-1; /* End of prev. line */
1339 wrap_offset(st, ar, text->sell, c, &offl, &offc);
1345 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
1346 if (toline<fromline) text->sell= text->sell->prev;
1348 if (c>text->sell->len) c= text->sell->len;
1352 else if(text->curl) {
1353 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
1354 if (toline<fromline) text->curl= text->curl->prev;
1356 if (c>text->curl->len) c= text->curl->len;
1363 static void wrap_move_down(SpaceText *st, ARegion *ar, short sel)
1365 Text *text= st->text;
1366 int offl, startoff, offc, fromline, toline, c, target;
1368 wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1369 fromline= toline= txt_get_span(text->lines.first, text->sell);
1370 target= text->selc + offc;
1373 while (offl==startoff && text->sell->line[c]!='\0') {
1375 wrap_offset(st, ar, text->sell, c, &offl, &offc);
1378 if (text->sell->line[c]=='\0') {
1379 if (!text->sell->next) {
1380 txt_move_eol(text, sel);
1387 if (c > text->sell->len) c= text->sell->len;
1392 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
1393 if (toline>fromline) text->sell= text->sell->next;
1395 if (c>text->sell->len) c= text->sell->len;
1399 else if(text->curl) {
1400 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
1401 if (toline>fromline) text->curl= text->curl->next;
1403 if (c > text->curl->len) c= text->curl->len;
1410 /* Moves the cursor vertically by the specified number of lines.
1411 If the destination line is shorter than the current cursor position, the
1412 cursor will be positioned at the end of this line.
1414 This is to replace screen_skip for PageUp/Down operations.
1416 static void cursor_skip(Text *text, int lines, int sel)
1419 int oldl, oldc, *charp;
1421 if (sel) linep= &text->sell, charp= &text->selc;
1422 else linep= &text->curl, charp= &text->curc;
1423 oldl= txt_get_span(text->lines.first, *linep);
1426 while (lines>0 && (*linep)->next) {
1427 *linep= (*linep)->next;
1430 while (lines<0 && (*linep)->prev) {
1431 *linep= (*linep)->prev;
1435 if (*charp > (*linep)->len) *charp= (*linep)->len;
1437 if (!sel) txt_pop_sel(text);
1438 txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
1441 static int move_cursor(bContext *C, int type, int select)
1443 SpaceText *st= CTX_wm_space_text(C);
1444 Text *text= CTX_data_edit_text(C);
1445 ARegion *ar= CTX_wm_region(C);
1447 /* ensure we have the right region, it's optional */
1448 if(ar->regiontype != RGN_TYPE_WINDOW)
1453 if(st && st->wordwrap && ar) wrap_move_bol(st, ar, select);
1454 else txt_move_bol(text, select);
1458 if(st && st->wordwrap && ar) wrap_move_eol(st, ar, select);
1459 else txt_move_eol(text, select);
1463 txt_move_bof(text, select);
1467 txt_move_eof(text, select);
1471 txt_jump_left(text, select);
1475 txt_jump_right(text, select);
1479 txt_move_left(text, select);
1483 txt_move_right(text, select);
1487 if(st && st->wordwrap && ar) wrap_move_up(st, ar, select);
1488 else txt_move_up(text, select);
1492 if(st && st->wordwrap && ar) wrap_move_down(st, ar, select);
1493 else txt_move_down(text, select);
1497 if(st) cursor_skip(text, -st->viewlines, select);
1498 else cursor_skip(text, -10, select);
1502 if(st) cursor_skip(text, st->viewlines, select);
1503 else cursor_skip(text, 10, select);
1507 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
1508 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1510 return OPERATOR_FINISHED;
1513 static int move_exec(bContext *C, wmOperator *op)
1515 int type= RNA_enum_get(op->ptr, "type");
1517 return move_cursor(C, type, 0);
1520 void TEXT_OT_move(wmOperatorType *ot)
1523 ot->name= "Move Cursor";
1524 ot->idname= "TEXT_OT_move";
1527 ot->exec= move_exec;
1528 ot->poll= text_edit_poll;
1531 ot->flag= OPTYPE_REGISTER;
1534 RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
1537 /******************* move select operator ********************/
1539 static int move_select_exec(bContext *C, wmOperator *op)
1541 int type= RNA_enum_get(op->ptr, "type");
1543 return move_cursor(C, type, 1);
1546 void TEXT_OT_move_select(wmOperatorType *ot)
1549 ot->name= "Move Select";
1550 ot->idname= "TEXT_OT_move_select";
1553 ot->exec= move_select_exec;
1554 ot->poll= text_space_edit_poll;
1557 ot->flag= OPTYPE_REGISTER;
1560 RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection.");
1563 /******************* jump operator *********************/
1565 static int jump_exec(bContext *C, wmOperator *op)
1567 Text *text= CTX_data_edit_text(C);
1568 int line= RNA_int_get(op->ptr, "line");
1569 short nlines= txt_get_span(text->lines.first, text->lines.last)+1;
1571 if(line < 1 || line > nlines)
1572 return OPERATOR_CANCELLED;
1574 txt_move_toline(text, line-1, 0);
1576 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
1578 return OPERATOR_FINISHED;
1582 // short tmp= txt_get_span(text->lines.first, text->curl)+1;
1583 // button(&tmp, 1, nlines, "Jump to line:"))
1585 void TEXT_OT_jump(wmOperatorType *ot)
1589 ot->idname= "TEXT_OT_jump";
1592 ot->exec= jump_exec;
1593 ot->poll= text_edit_poll;
1596 ot->flag= OPTYPE_REGISTER;
1599 RNA_def_int(ot->srna, "line", 1, INT_MAX, 1, "Line", "Line number to jump to.", 1, 10000);
1602 /******************* delete operator **********************/
1604 static EnumPropertyItem delete_type_items[]= {
1605 {DEL_NEXT_CHAR, "NEXT_CHARACTER", "Next Character", ""},
1606 {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", "Previous Character", ""},
1607 {DEL_NEXT_WORD, "NEXT_WORD", "Next Word", ""},
1608 {DEL_PREV_WORD, "PREVIOUS_WORD", "Previous Word", ""},
1609 {0, NULL, NULL, NULL}};
1611 static int delete_exec(bContext *C, wmOperator *op)
1613 Text *text= CTX_data_edit_text(C);
1614 int type= RNA_enum_get(op->ptr, "type");
1616 if(type == DEL_PREV_WORD)
1617 txt_backspace_word(text);
1618 else if(type == DEL_PREV_CHAR)
1619 txt_backspace_char(text);
1620 else if(type == DEL_NEXT_WORD)
1621 txt_delete_word(text);
1622 else if(type == DEL_NEXT_CHAR)
1623 txt_delete_char(text);
1625 text_update_line_edited(text, text->curl);
1627 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
1628 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1630 return OPERATOR_FINISHED;
1633 void TEXT_OT_delete(wmOperatorType *ot)
1637 ot->idname= "TEXT_OT_delete";
1640 ot->exec= delete_exec;
1641 ot->poll= text_edit_poll;
1644 ot->flag= OPTYPE_REGISTER;
1647 RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete.");
1650 /******************* toggle overwrite operator **********************/
1652 static int toggle_overwrite_exec(bContext *C, wmOperator *op)
1654 SpaceText *st= CTX_wm_space_text(C);
1656 st->overwrite= !st->overwrite;
1658 return OPERATOR_FINISHED;
1661 void TEXT_OT_overwrite_toggle(wmOperatorType *ot)
1664 ot->name= "Toggle Overwrite";
1665 ot->idname= "TEXT_OT_overwrite_toggle";
1668 ot->exec= toggle_overwrite_exec;
1669 ot->poll= text_space_edit_poll;
1672 ot->flag= OPTYPE_REGISTER;
1675 /******************* scroll operator **********************/
1677 /* Moves the view vertically by the specified number of lines */
1678 static void screen_skip(SpaceText *st, int lines)
1684 last= txt_get_span(st->text->lines.first, st->text->lines.last);
1685 last= last - (st->viewlines/2);
1687 if(st->top>last) st->top= last;
1688 if(st->top<0) st->top= 0;
1691 typedef struct TextScroll {
1702 static int scroll_exec(bContext *C, wmOperator *op)
1704 SpaceText *st= CTX_wm_space_text(C);
1705 int lines= RNA_int_get(op->ptr, "lines");
1708 return OPERATOR_CANCELLED;
1710 screen_skip(st, lines*U.wheellinescroll);
1712 ED_area_tag_redraw(CTX_wm_area(C));
1714 return OPERATOR_FINISHED;
1717 static int scroll_invoke(bContext *C, wmOperator *op, wmEvent *event)
1719 SpaceText *st= CTX_wm_space_text(C);
1722 if(RNA_property_is_set(op->ptr, "lines"))
1723 return scroll_exec(C, op);
1725 tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
1727 op->customdata= tsc;
1729 st->flags|= ST_SCROLL_SELECT;
1731 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1733 return OPERATOR_RUNNING_MODAL;
1736 static void scroll_apply(bContext *C, wmOperator *op, wmEvent *event)
1738 SpaceText *st= CTX_wm_space_text(C);
1739 TextScroll *tsc= op->customdata;
1740 short *mval= event->mval;
1743 tsc->old[0]= mval[0];
1744 tsc->old[1]= mval[1];
1745 tsc->hold[0]= mval[0];
1746 tsc->hold[1]= mval[1];
1750 if(!tsc->scrollbar) {
1751 tsc->delta[0]= (tsc->hold[0]-mval[0])/text_font_width_character(st);
1752 tsc->delta[1]= (mval[1]-tsc->hold[1])/st->lheight;
1755 tsc->delta[1]= (tsc->hold[1]-mval[1])*st->pix_per_line;
1757 if(tsc->delta[0] || tsc->delta[1]) {
1758 screen_skip(st, tsc->delta[1]);
1760 tsc->lines += tsc->delta[1];
1766 st->left+= tsc->delta[0];
1767 if(st->left<0) st->left= 0;
1770 tsc->hold[0]= mval[0];
1771 tsc->hold[1]= mval[1];
1773 ED_area_tag_redraw(CTX_wm_area(C));
1776 tsc->old[0]= mval[0];
1777 tsc->old[1]= mval[1];
1780 static void scroll_exit(bContext *C, wmOperator *op)
1782 SpaceText *st= CTX_wm_space_text(C);
1784 st->flags &= ~ST_SCROLL_SELECT;
1785 MEM_freeN(op->customdata);
1788 static int scroll_modal(bContext *C, wmOperator *op, wmEvent *event)
1790 switch(event->type) {
1792 scroll_apply(C, op, event);
1798 return OPERATOR_FINISHED;
1801 return OPERATOR_RUNNING_MODAL;
1804 static int scroll_cancel(bContext *C, wmOperator *op)
1808 return OPERATOR_CANCELLED;
1811 void TEXT_OT_scroll(wmOperatorType *ot)
1815 ot->idname= "TEXT_OT_scroll";
1818 ot->exec= scroll_exec;
1819 ot->invoke= scroll_invoke;
1820 ot->modal= scroll_modal;
1821 ot->cancel= scroll_cancel;
1822 ot->poll= text_space_edit_poll;
1825 RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
1828 /******************** scroll bar operator *******************/
1830 static int scroll_bar_invoke(bContext *C, wmOperator *op, wmEvent *event)
1832 SpaceText *st= CTX_wm_space_text(C);
1833 ARegion *ar= CTX_wm_region(C);
1835 short *mval= event->mval;
1837 if(RNA_property_is_set(op->ptr, "lines"))
1838 return scroll_exec(C, op);
1840 /* verify we are in the right zone */
1841 if(!(mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<ar->winy))
1842 return OPERATOR_PASS_THROUGH;
1844 tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
1847 op->customdata= tsc;
1849 st->flags|= ST_SCROLL_SELECT;
1851 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
1853 return OPERATOR_RUNNING_MODAL;
1856 void TEXT_OT_scroll_bar(wmOperatorType *ot)
1859 ot->name= "Scrollbar";
1860 ot->idname= "TEXT_OT_scroll_bar";
1863 ot->invoke= scroll_bar_invoke;
1864 ot->modal= scroll_modal;
1865 ot->cancel= scroll_cancel;
1866 ot->poll= text_region_edit_poll;
1869 RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
1872 /******************* set cursor operator **********************/
1874 typedef struct SetCursor {
1880 static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel)
1883 Text *text= st->text;
1888 if(sel) { linep= &text->sell; charp= &text->selc; }
1889 else { linep= &text->curl; charp= &text->curc; }
1891 y= (ar->winy - y)/st->lheight;
1894 x-= TXT_OFFSET+TEXTXLOC;
1899 x = (x/text_font_width_character(st)) + st->left;
1902 int i, j, endj, curs, max, chop, start, end, chars, loop;
1905 /* Point to first visible line */
1906 *linep= text->lines.first;
1907 for(i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;
1909 max= wrap_width(st, ar);
1912 while(loop && *linep) {
1919 for(i=0, j=0; loop; j++) {
1921 /* Mimic replacement of tabs */
1922 ch= (*linep)->line[j];
1924 chars= st->tabnumber-i%st->tabnumber;
1931 /* Gone too far, go back to last wrap point */
1936 /* Exactly at the cursor, done */
1938 else if(y==0 && i-start==x) {
1942 /* Prepare curs for next wrap */
1953 if(y==0 && i-start>=x) {
1959 else if(ch==' ' || ch=='-' || ch=='\0') {
1960 if(y==0 && i-start>=x) {
1973 if(!loop || y<0) break;
1975 if(!(*linep)->next) {
1976 *charp= (*linep)->len;
1980 /* On correct line but didn't meet cursor, must be at end */
1982 *charp= (*linep)->len;
1985 *linep= (*linep)->next;
1991 y-= txt_get_span(text->lines.first, *linep) - st->top;
1994 while(y-- != 0) if((*linep)->next) *linep= (*linep)->next;
1997 while(y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
2001 w= flatten_string(st, &fs, (*linep)->line);
2002 if(x<w) *charp= fs.accum[x];
2003 else *charp= (*linep)->len;
2004 flatten_string_free(&fs);
2006 if(!sel) txt_pop_sel(text);
2009 static void set_cursor_apply(bContext *C, wmOperator *op, wmEvent *event)
2011 SpaceText *st= CTX_wm_space_text(C);
2012 ARegion *ar= CTX_wm_region(C);
2013 SetCursor *scu= op->customdata;
2015 if(event->mval[1]<0 || event->mval[1]>ar->winy) {
2016 int d= (scu->old[1]-event->mval[1])*st->pix_per_line;
2017 if(d) screen_skip(st, d);
2019 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1]<0?0:ar->winy, 1);
2021 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2023 else if(!st->wordwrap && (event->mval[0]<0 || event->mval[0]>ar->winx)) {
2024 if(event->mval[0]>ar->winx) st->left++;
2025 else if(event->mval[0]<0 && st->left>0) st->left--;
2027 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
2029 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2030 // XXX PIL_sleep_ms(10);
2033 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
2035 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2037 scu->old[0]= event->mval[0];
2038 scu->old[1]= event->mval[1];
2042 static void set_cursor_exit(bContext *C, wmOperator *op)
2044 SpaceText *st= CTX_wm_space_text(C);
2045 Text *text= st->text;
2046 SetCursor *scu= op->customdata;
2050 if(txt_has_sel(text)) {
2051 buffer = txt_sel_to_buf(text);
2052 WM_clipboard_text_set(buffer, 1);
2056 linep2= txt_get_span(st->text->lines.first, st->text->sell);
2057 charp2= st->text->selc;
2059 if(scu->sell!=linep2 || scu->selc!=charp2)
2060 txt_undo_add_toop(st->text, UNDO_STO, scu->sell, scu->selc, linep2, charp2);
2062 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2067 static int set_cursor_invoke(bContext *C, wmOperator *op, wmEvent *event)
2069 SpaceText *st= CTX_wm_space_text(C);
2070 ARegion *ar= CTX_wm_region(C);
2073 op->customdata= MEM_callocN(sizeof(SetCursor), "SetCursor");
2074 scu= op->customdata;
2075 scu->selecting= RNA_boolean_get(op->ptr, "select");
2077 scu->old[0]= event->mval[0];
2078 scu->old[1]= event->mval[1];
2080 if(!scu->selecting) {
2081 int curl= txt_get_span(st->text->lines.first, st->text->curl);
2082 int curc= st->text->curc;
2085 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 0);
2087 linep2= txt_get_span(st->text->lines.first, st->text->curl);
2088 charp2= st->text->selc;
2090 if(curl!=linep2 || curc!=charp2)
2091 txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
2094 scu->sell= txt_get_span(st->text->lines.first, st->text->sell);
2095 scu->selc= st->text->selc;
2097 WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);
2099 set_cursor_apply(C, op, event);
2101 return OPERATOR_RUNNING_MODAL;
2104 static int set_cursor_modal(bContext *C, wmOperator *op, wmEvent *event)
2106 switch(event->type) {
2110 set_cursor_exit(C, op);
2111 return OPERATOR_FINISHED;
2113 set_cursor_apply(C, op, event);
2117 return OPERATOR_RUNNING_MODAL;
2120 static int set_cursor_cancel(bContext *C, wmOperator *op)
2122 set_cursor_exit(C, op);
2123 return OPERATOR_FINISHED;
2126 void TEXT_OT_cursor_set(wmOperatorType *ot)
2129 ot->name= "Set Cursor";
2130 ot->idname= "TEXT_OT_cursor_set";
2133 ot->invoke= set_cursor_invoke;
2134 ot->modal= set_cursor_modal;
2135 ot->cancel= set_cursor_cancel;
2136 ot->poll= text_region_edit_poll;
2139 ot->flag= OPTYPE_REGISTER;
2142 RNA_def_boolean(ot->srna, "select", 0, "Select", "Set selection end rather than cursor.");
2145 /******************* line number operator **********************/
2147 static int line_number_invoke(bContext *C, wmOperator *op, wmEvent *event)
2149 SpaceText *st= CTX_wm_space_text(C);
2150 Text *text= CTX_data_edit_text(C);
2151 ARegion *ar= CTX_wm_region(C);
2152 short *mval= event->mval;
2154 static int jump_to= 0;
2155 static double last_jump= 0;
2157 if(!st->showlinenrs)
2158 return OPERATOR_PASS_THROUGH;
2160 if(!(mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<ar->winy-2))
2161 return OPERATOR_PASS_THROUGH;
2163 if(!(event->ascii>='0' && event->ascii<='9'))
2164 return OPERATOR_PASS_THROUGH;
2166 time = PIL_check_seconds_timer();
2167 if(last_jump < time-1)
2171 jump_to += (int)(event->ascii-'0');
2173 txt_move_toline(text, jump_to-1, 0);
2176 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2178 return OPERATOR_FINISHED;
2181 void TEXT_OT_line_number(wmOperatorType *ot)
2184 ot->name= "Line Number";
2185 ot->idname= "TEXT_OT_line_number";
2188 ot->invoke= line_number_invoke;
2189 ot->poll= text_region_edit_poll;
2192 /******************* insert operator **********************/
2194 static int insert_exec(bContext *C, wmOperator *op)
2196 SpaceText *st= CTX_wm_space_text(C);
2197 Text *text= CTX_data_edit_text(C);
2201 str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
2206 return OPERATOR_CANCELLED;
2208 if(st && st->overwrite)
2209 done= txt_replace_char(text, ascii);
2211 done= txt_add_char(text, ascii);
2214 return OPERATOR_CANCELLED;
2216 text_update_line_edited(text, text->curl);
2218 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2219 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2221 return OPERATOR_FINISHED;
2224 static int insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
2228 /* XXX old code from winqreadtextspace, is it still needed somewhere? */
2229 /* smartass code to prevent the CTRL/ALT events below from not working! */
2230 /*if(qual & (LR_ALTKEY|LR_CTRLKEY))
2234 str[0]= event->ascii;
2237 RNA_string_set(op->ptr, "text", str);
2239 return insert_exec(C, op);
2242 void TEXT_OT_insert(wmOperatorType *ot)
2246 ot->idname= "TEXT_OT_insert";
2249 ot->exec= insert_exec;
2250 ot->invoke= insert_invoke;
2251 ot->poll= text_edit_poll;
2254 ot->flag= OPTYPE_REGISTER;
2257 RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
2260 /******************* find operator *********************/
2264 #define TEXT_REPLACE 1
2265 #define TEXT_MARK_ALL 2
2267 static int find_and_replace(bContext *C, wmOperator *op, short mode)
2269 SpaceText *st= CTX_wm_space_text(C);
2270 Text *start= NULL, *text= st->text;
2271 int flags, first= 1;
2274 if(!st->findstr[0] || (mode == TEXT_REPLACE && !st->replacestr[0]))
2275 return OPERATOR_CANCELLED;
2278 if(flags & ST_FIND_ALL)
2279 flags ^= ST_FIND_WRAP;
2283 txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
2287 /* Replace current */
2288 if(mode!=TEXT_FIND && txt_has_sel(text)) {
2289 tmp= txt_sel_to_buf(text);
2291 if(strcmp(st->findstr, tmp)==0) {
2292 if(mode==TEXT_REPLACE) {
2293 txt_insert_buf(text, st->replacestr);
2294 if(text->curl && text->curl->format) {
2295 MEM_freeN(text->curl->format);
2296 text->curl->format= NULL;
2298 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2300 else if(mode==TEXT_MARK_ALL) {
2302 UI_GetThemeColor4ubv(TH_SHADE2, color);
2304 if(txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
2305 if(tmp) MEM_freeN(tmp), tmp=NULL;
2309 txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
2310 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2318 if(txt_find_string(text, st->findstr, flags & ST_FIND_WRAP)) {
2319 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2321 else if(flags & ST_FIND_ALL) {
2322 if(text==start) break;
2323 if(!start) start= text;
2325 text= st->text= text->id.next;
2327 text= st->text= G.main->text.first;
2328 txt_move_toline(text, 0, 0);
2329 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2333 BKE_reportf(op->reports, RPT_INFO, "Text not found: %s", st->findstr);
2336 } while(mode==TEXT_MARK_ALL);
2338 return OPERATOR_FINISHED;
2341 static int find_exec(bContext *C, wmOperator *op)
2343 return find_and_replace(C, op, TEXT_FIND);
2346 void TEXT_OT_find(wmOperatorType *ot)
2350 ot->idname= "TEXT_OT_find";
2353 ot->exec= find_exec;
2354 ot->poll= text_space_edit_poll;
2357 /******************* replace operator *********************/
2359 static int replace_exec(bContext *C, wmOperator *op)
2361 return find_and_replace(C, op, TEXT_REPLACE);
2364 void TEXT_OT_replace(wmOperatorType *ot)
2367 ot->name= "Replace";
2368 ot->idname= "TEXT_OT_replace";
2371 ot->exec= replace_exec;
2372 ot->poll= text_space_edit_poll;
2375 /******************* mark all operator *********************/
2377 static int mark_all_exec(bContext *C, wmOperator *op)
2379 return find_and_replace(C, op, TEXT_MARK_ALL);
2382 void TEXT_OT_mark_all(wmOperatorType *ot)
2385 ot->name= "Mark All";
2386 ot->idname= "TEXT_OT_mark_all";
2389 ot->exec= mark_all_exec;
2390 ot->poll= text_space_edit_poll;
2393 /******************* find set selected *********************/
2395 static int find_set_selected_exec(bContext *C, wmOperator *op)
2397 SpaceText *st= CTX_wm_space_text(C);
2398 Text *text= CTX_data_edit_text(C);
2401 tmp= txt_sel_to_buf(text);
2402 BLI_strncpy(st->findstr, tmp, ST_MAX_FIND_STR);
2406 return OPERATOR_FINISHED;
2408 return find_and_replace(C, op, TEXT_FIND);
2411 void TEXT_OT_find_set_selected(wmOperatorType *ot)
2414 ot->name= "Find Set Selected";
2415 ot->idname= "TEXT_OT_find_set_selected";
2418 ot->exec= find_set_selected_exec;
2419 ot->poll= text_space_edit_poll;
2422 /******************* replace set selected *********************/
2424 static int replace_set_selected_exec(bContext *C, wmOperator *op)
2426 SpaceText *st= CTX_wm_space_text(C);
2427 Text *text= CTX_data_edit_text(C);
2430 tmp= txt_sel_to_buf(text);
2431 BLI_strncpy(st->replacestr, tmp, ST_MAX_FIND_STR);
2434 return OPERATOR_FINISHED;
2437 void TEXT_OT_replace_set_selected(wmOperatorType *ot)
2440 ot->name= "Replace Set Selected";
2441 ot->idname= "TEXT_OT_replace_set_selected";
2444 ot->exec= replace_set_selected_exec;
2445 ot->poll= text_space_edit_poll;
2448 /****************** resolve conflict operator ******************/
2450 enum { RESOLVE_IGNORE, RESOLVE_RELOAD, RESOLVE_SAVE, RESOLVE_MAKE_INTERNAL };
2451 static EnumPropertyItem resolution_items[]= {
2452 {RESOLVE_IGNORE, "IGNORE", "Ignore", ""},
2453 {RESOLVE_RELOAD, "RELOAD", "Reload", ""},
2454 {RESOLVE_SAVE, "SAVE", "Save", ""},
2455 {RESOLVE_MAKE_INTERNAL, "MAKE_INTERNAL", "Make Internal", ""},
2456 {0, NULL, NULL, NULL}};
2458 /* returns 0 if file on disk is the same or Text is in memory only
2459 returns 1 if file has been modified on disk since last local edit
2460 returns 2 if file on disk has been deleted
2461 -1 is returned if an error occurs */
2463 int text_file_modified(Text *text)
2467 char file[FILE_MAXDIR+FILE_MAXFILE];
2469 if(!text || !text->name)
2472 BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2473 BLI_convertstringcode(file, G.sce);
2475 if(!BLI_exists(file))
2478 result = stat(file, &st);
2483 if((st.st_mode & S_IFMT) != S_IFREG)
2486 if(st.st_mtime > text->mtime)
2492 static void text_ignore_modified(Text *text)
2496 char file[FILE_MAXDIR+FILE_MAXFILE];
2498 if(!text || !text->name) return;
2500 BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2501 BLI_convertstringcode(file, G.sce);
2503 if(!BLI_exists(file)) return;
2505 result = stat(file, &st);
2507 if(result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
2510 text->mtime= st.st_mtime;
2513 static int resolve_conflict_exec(bContext *C, wmOperator *op)
2515 Text *text= CTX_data_edit_text(C);
2516 int resolution= RNA_enum_get(op->ptr, "resolution");
2518 switch(resolution) {
2519 case RESOLVE_RELOAD:
2520 return reload_exec(C, op);
2522 return save_exec(C, op);
2523 case RESOLVE_MAKE_INTERNAL:
2524 return make_internal_exec(C, op);
2525 case RESOLVE_IGNORE:
2526 text_ignore_modified(text);
2527 return OPERATOR_FINISHED;
2530 return OPERATOR_CANCELLED;
2533 static int resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *event)
2535 Text *text= CTX_data_edit_text(C);
2539 switch(text_file_modified(text)) {
2541 if(text->flags & TXT_ISDIRTY) {
2542 /* modified locally and externally, ahhh. offer more possibilites. */
2543 pup= uiPupMenuBegin(C, "File Modified Outside and Inside Blender", 0);
2544 layout= uiPupMenuLayout(pup);
2545 uiItemEnumO(layout, "Reload from disk (ignore local changes)", 0, op->type->idname, "resolution", RESOLVE_RELOAD);
2546 uiItemEnumO(layout, "Save to disk (ignore outside changes)", 0, op->type->idname, "resolution", RESOLVE_SAVE);
2547 uiItemEnumO(layout, "Make text internal (separate copy)", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
2548 uiPupMenuEnd(C, pup);
2551 pup= uiPupMenuBegin(C, "File Modified Outside Blender", 0);
2552 layout= uiPupMenuLayout(pup);
2553 uiItemEnumO(layout, "Reload from disk", 0, op->type->idname, "resolution", RESOLVE_RELOAD);
2554 uiItemEnumO(layout, "Make text internal (separate copy)", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
2555 uiItemEnumO(layout, "Ignore", 0, op->type->idname, "resolution", RESOLVE_IGNORE);
2556 uiPupMenuEnd(C, pup);
2560 pup= uiPupMenuBegin(C, "File Deleted Outside Blender", 0);
2561 layout= uiPupMenuLayout(pup);
2562 uiItemEnumO(layout, "Make text internal", 0, op->type->idname, "resolution", RESOLVE_MAKE_INTERNAL);
2563 uiItemEnumO(layout, "Recreate file", 0, op->type->idname, "resolution", RESOLVE_SAVE);
2564 uiPupMenuEnd(C, pup);
2568 return OPERATOR_CANCELLED;
2571 void TEXT_OT_resolve_conflict(wmOperatorType *ot)
2574 ot->name= "Resolve Conflict";
2575 ot->idname= "TEXT_OT_resolve_conflict";
2576 ot->description= "When external text is out of sync, resolve the conflict.";
2579 ot->exec= resolve_conflict_exec;
2580 ot->invoke= resolve_conflict_invoke;
2581 ot->poll= save_poll;
2584 RNA_def_enum(ot->srna, "resolution", resolution_items, RESOLVE_IGNORE, "Resolution", "How to solve conflict due to different in internal and external text.");
2587 /********************** to 3d object operator *****************/
2589 static int to_3d_object_exec(bContext *C, wmOperator *op)
2591 Text *text= CTX_data_edit_text(C);
2592 int split_lines= RNA_boolean_get(op->ptr, "split_lines");
2594 ED_text_to_object(C, text, split_lines);
2596 return OPERATOR_FINISHED;
2599 void TEXT_OT_to_3d_object(wmOperatorType *ot)
2602 ot->name= "To 3D Object";
2603 ot->idname= "TEXT_OT_to_3d_object";
2606 ot->exec= to_3d_object_exec;
2607 ot->poll= text_edit_poll;
2610 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2613 RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text.");
2617 /************************ undo ******************************/
2619 void ED_text_undo_step(bContext *C, int step)
2621 Text *text= CTX_data_edit_text(C);
2631 text_update_edited(text);
2633 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2634 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);