re-enable fix from r32330 but without the alt key check because this can be used...
[blender-staging.git] / source / blender / editors / space_text / text_ops.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
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.
10  *
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.
15  *
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <stdlib.h>
31 #include <string.h>
32 #include <ctype.h> /* ispunct */
33 #include <sys/stat.h>
34
35 #include "MEM_guardedalloc.h"
36
37 #include "DNA_text_types.h"
38 #include "DNA_userdef_types.h"
39
40 #include "BLI_blenlib.h"
41 #include "PIL_time.h"
42
43 #include "BKE_context.h"
44 #include "BKE_global.h"
45 #include "BKE_library.h"
46 #include "BKE_main.h"
47 #include "BKE_report.h"
48 #include "BKE_text.h"
49
50 #include "WM_api.h"
51 #include "WM_types.h"
52
53 #include "ED_curve.h"
54 #include "ED_screen.h"
55 #include "UI_interface.h"
56 #include "UI_resources.h"
57
58 #include "RNA_access.h"
59 #include "RNA_define.h"
60
61 #ifndef DISABLE_PYTHON
62 #include "BPY_extern.h"
63 #endif
64
65 #include "text_intern.h"
66
67 /************************ poll ***************************/
68
69 static int text_new_poll(bContext *C)
70 {
71         return 1;
72 }
73
74 static int text_edit_poll(bContext *C)
75 {
76         Text *text= CTX_data_edit_text(C);
77
78         if(!text)
79                 return 0;
80
81         if(text->id.lib) {
82                 // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
83                 return 0;
84         }
85
86         return 1;
87 }
88
89 static int text_space_edit_poll(bContext *C)
90 {
91         SpaceText *st= CTX_wm_space_text(C);
92         Text *text= CTX_data_edit_text(C);
93
94         if(!st || !text)
95                 return 0;
96
97         if(text->id.lib) {
98                 // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
99                 return 0;
100         }
101
102         return 1;
103 }
104
105 static int text_region_edit_poll(bContext *C)
106 {
107         SpaceText *st= CTX_wm_space_text(C);
108         Text *text= CTX_data_edit_text(C);
109         ARegion *ar= CTX_wm_region(C);
110
111         if(!st || !text)
112                 return 0;
113         
114         if(!ar || ar->regiontype != RGN_TYPE_WINDOW)
115                 return 0;
116
117         if(text->id.lib) {
118                 // BKE_report(op->reports, RPT_ERROR, "Can't edit external libdata");
119                 return 0;
120         }
121
122         return 1;
123 }
124
125
126 /********************** updates *********************/
127
128 void text_update_line_edited(Text *text, TextLine *line)
129 {
130         if(!line)
131                 return;
132
133         /* we just free format here, and let it rebuild during draw */
134         if(line->format) {
135                 MEM_freeN(line->format);
136                 line->format= NULL;
137         }
138 }
139
140 void text_update_edited(Text *text)
141 {
142         TextLine *line;
143
144         for(line=text->lines.first; line; line=line->next)
145                 text_update_line_edited(text, line);
146 }
147
148 /******************* new operator *********************/
149
150 static int new_exec(bContext *C, wmOperator *op)
151 {
152         SpaceText *st= CTX_wm_space_text(C);
153         Text *text;
154         PointerRNA ptr, idptr;
155         PropertyRNA *prop;
156
157         text= add_empty_text("Text");
158
159         /* hook into UI */
160         uiIDContextProperty(C, &ptr, &prop);
161
162         if(prop) {
163                 /* when creating new ID blocks, use is already 1, but RNA
164                  * pointer se also increases user, so this compensates it */
165                 text->id.us--;
166
167                 RNA_id_pointer_create(&text->id, &idptr);
168                 RNA_property_pointer_set(&ptr, prop, idptr);
169                 RNA_property_update(C, &ptr, prop);
170         }
171         else if(st) {
172                 st->text= text;
173                 st->top= 0;
174         }
175
176         WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
177
178         return OPERATOR_FINISHED;
179 }
180
181 void TEXT_OT_new(wmOperatorType *ot)
182 {
183         /* identifiers */
184         ot->name= "New";
185         ot->idname= "TEXT_OT_new";
186         ot->description= "Create a new text data block";
187         
188         /* api callbacks */
189         ot->exec= new_exec;
190         ot->poll= text_new_poll;
191         
192         /* flags */
193         ot->flag= OPTYPE_UNDO;
194 }
195
196 /******************* open operator *********************/
197
198 static void open_init(bContext *C, wmOperator *op)
199 {
200         PropertyPointerRNA *pprop;
201
202         op->customdata= pprop= MEM_callocN(sizeof(PropertyPointerRNA), "OpenPropertyPointerRNA");
203         uiIDContextProperty(C, &pprop->ptr, &pprop->prop);
204 }
205
206 static int open_cancel(bContext *C, wmOperator *op)
207 {
208         MEM_freeN(op->customdata);
209         return OPERATOR_CANCELLED;
210 }
211
212 static int open_exec(bContext *C, wmOperator *op)
213 {
214         SpaceText *st= CTX_wm_space_text(C);
215         Text *text;
216         PropertyPointerRNA *pprop;
217         PointerRNA idptr;
218         char str[FILE_MAX];
219         short internal = RNA_boolean_get(op->ptr, "internal");
220
221         RNA_string_get(op->ptr, "filepath", str);
222
223         text= add_text(str, G.sce);
224
225         if(!text) {
226                 if(op->customdata) MEM_freeN(op->customdata);
227                 return OPERATOR_CANCELLED;
228         }
229
230         if(!op->customdata)
231                 open_init(C, op);
232
233         /* hook into UI */
234         pprop= op->customdata;
235
236         if(pprop->prop) {
237                 /* when creating new ID blocks, use is already 1, but RNA
238                  * pointer se also increases user, so this compensates it */
239                 text->id.us--;
240
241                 RNA_id_pointer_create(&text->id, &idptr);
242                 RNA_property_pointer_set(&pprop->ptr, pprop->prop, idptr);
243                 RNA_property_update(C, &pprop->ptr, pprop->prop);
244         }
245         else if(st) {
246                 st->text= text;
247                 st->top= 0;
248         }
249         
250         if (internal) {
251                 if(text->name)
252                         MEM_freeN(text->name);
253                 
254                 text->name = NULL;
255         }
256
257         WM_event_add_notifier(C, NC_TEXT|NA_ADDED, text);
258
259         MEM_freeN(op->customdata);
260
261         return OPERATOR_FINISHED;
262 }
263
264 static int open_invoke(bContext *C, wmOperator *op, wmEvent *event)
265 {
266         Text *text= CTX_data_edit_text(C);
267         char *path= (text && text->name)? text->name: G.sce;
268
269         if(RNA_property_is_set(op->ptr, "filepath"))
270                 return open_exec(C, op);
271         
272         open_init(C, op);
273         RNA_string_set(op->ptr, "filepath", path);
274         WM_event_add_fileselect(C, op); 
275
276         return OPERATOR_RUNNING_MODAL;
277 }
278
279 void TEXT_OT_open(wmOperatorType *ot)
280 {
281         /* identifiers */
282         ot->name= "Open";
283         ot->idname= "TEXT_OT_open";
284         ot->description= "Open a new text data block";
285
286         /* api callbacks */
287         ot->exec= open_exec;
288         ot->invoke= open_invoke;
289         ot->cancel= open_cancel;
290         ot->poll= text_new_poll;
291
292         /* flags */
293         ot->flag= OPTYPE_UNDO;
294         
295         /* properties */
296         WM_operator_properties_filesel(ot, FOLDERFILE|TEXTFILE|PYSCRIPTFILE, FILE_SPECIAL, FILE_OPENFILE, WM_FILESEL_FILEPATH);  //XXX TODO, relative_path
297         RNA_def_boolean(ot->srna, "internal", 0, "Make internal", "Make text file internal after loading");
298 }
299
300 /******************* reload operator *********************/
301
302 static int reload_exec(bContext *C, wmOperator *op)
303 {
304         Text *text= CTX_data_edit_text(C);
305
306         if(!reopen_text(text)) {
307                 BKE_report(op->reports, RPT_ERROR, "Could not reopen file");
308                 return OPERATOR_CANCELLED;
309         }
310
311 #ifndef DISABLE_PYTHON
312         if(text->compiled)
313                 BPY_free_compiled_text(text);
314 #endif
315
316         text_update_edited(text);
317         text_update_cursor_moved(C);
318         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
319
320         return OPERATOR_FINISHED;
321 }
322
323 void TEXT_OT_reload(wmOperatorType *ot)
324 {
325         /* identifiers */
326         ot->name= "Reload";
327         ot->idname= "TEXT_OT_reload";
328         ot->description= "Reload active text data block from its file";
329         
330         /* api callbacks */
331         ot->exec= reload_exec;
332         ot->invoke= WM_operator_confirm;
333         ot->poll= text_edit_poll;
334 }
335
336 /******************* delete operator *********************/
337
338 static int unlink_exec(bContext *C, wmOperator *op)
339 {
340         Main *bmain= CTX_data_main(C);
341         SpaceText *st= CTX_wm_space_text(C);
342         Text *text= CTX_data_edit_text(C);
343
344         /* make the previous text active, if its not there make the next text active */
345         if(st) {
346                 if(text->id.prev) {
347                         st->text = text->id.prev;
348                         text_update_cursor_moved(C);
349                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
350                 }
351                 else if(text->id.next) {
352                         st->text = text->id.next;
353                         text_update_cursor_moved(C);
354                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
355                 }
356         }
357
358         unlink_text(bmain, text);
359         free_libblock(&bmain->text, text);
360         WM_event_add_notifier(C, NC_TEXT|NA_REMOVED, NULL);
361
362         return OPERATOR_FINISHED;
363 }
364
365 void TEXT_OT_unlink(wmOperatorType *ot)
366 {
367         /* identifiers */
368         ot->name= "Unlink";
369         ot->idname= "TEXT_OT_unlink";
370         ot->description= "Unlink active text data block";
371         
372         /* api callbacks */
373         ot->exec= unlink_exec;
374         ot->invoke= WM_operator_confirm;
375         ot->poll= text_edit_poll;
376         
377         /* flags */
378         ot->flag= OPTYPE_UNDO;
379 }
380
381 /******************* make internal operator *********************/
382
383 static int make_internal_exec(bContext *C, wmOperator *op)
384 {
385         Text *text= CTX_data_edit_text(C);
386
387         text->flags |= TXT_ISMEM | TXT_ISDIRTY;
388
389         if(text->name) {
390                 MEM_freeN(text->name);
391                 text->name= NULL;
392         }
393
394         text_update_cursor_moved(C);
395         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
396
397         return OPERATOR_FINISHED;
398 }
399
400 void TEXT_OT_make_internal(wmOperatorType *ot)
401 {
402         /* identifiers */
403         ot->name= "Make Internal";
404         ot->idname= "TEXT_OT_make_internal";
405         ot->description= "Make active text file internal";
406
407         /* api callbacks */
408         ot->exec= make_internal_exec;
409         ot->poll= text_edit_poll;
410         
411         /* flags */
412         ot->flag= OPTYPE_UNDO;
413 }
414
415 /******************* save operator *********************/
416
417 static int save_poll(bContext *C)
418 {
419         Text *text= CTX_data_edit_text(C);
420
421         if(!text_edit_poll(C))
422                 return 0;
423         
424         return (text->name != NULL && !(text->flags & TXT_ISMEM));
425 }
426
427 static void txt_write_file(Text *text, ReportList *reports) 
428 {
429         FILE *fp;
430         TextLine *tmp;
431         struct stat st;
432         int res;
433         char file[FILE_MAXDIR+FILE_MAXFILE];
434         
435         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
436         BLI_path_abs(file, G.sce);
437         
438         fp= fopen(file, "w");
439         if(fp==NULL) {
440                 BKE_report(reports, RPT_ERROR, "Unable to save file.");
441                 return;
442         }
443
444         tmp= text->lines.first;
445         while(tmp) {
446                 if(tmp->next) fprintf(fp, "%s\n", tmp->line);
447                 else fprintf(fp, "%s", tmp->line);
448                 
449                 tmp= tmp->next;
450         }
451         
452         fclose (fp);
453
454         res= stat(file, &st);
455         text->mtime= st.st_mtime;
456         
457         if(text->flags & TXT_ISDIRTY)
458                 text->flags ^= TXT_ISDIRTY;
459 }
460
461 static int save_exec(bContext *C, wmOperator *op)
462 {
463         Text *text= CTX_data_edit_text(C);
464
465         txt_write_file(text, op->reports);
466
467         text_update_cursor_moved(C);
468         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
469
470         return OPERATOR_FINISHED;
471 }
472
473 void TEXT_OT_save(wmOperatorType *ot)
474 {
475         /* identifiers */
476         ot->name= "Save";
477         ot->idname= "TEXT_OT_save";
478         ot->description= "Save active text data block";
479
480         /* api callbacks */
481         ot->exec= save_exec;
482         ot->poll= save_poll;
483 }
484
485 /******************* save as operator *********************/
486
487 static int save_as_exec(bContext *C, wmOperator *op)
488 {
489         Text *text= CTX_data_edit_text(C);
490         char str[FILE_MAX];
491
492         if(!text)
493                 return OPERATOR_CANCELLED;
494
495         RNA_string_get(op->ptr, "filepath", str);
496
497         if(text->name) MEM_freeN(text->name);
498         text->name= BLI_strdup(str);
499         text->flags &= ~TXT_ISMEM;
500
501         txt_write_file(text, op->reports);
502
503         text_update_cursor_moved(C);
504         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
505
506         return OPERATOR_FINISHED;
507 }
508
509 static int save_as_invoke(bContext *C, wmOperator *op, wmEvent *event)
510 {
511         Text *text= CTX_data_edit_text(C);
512         char *str;
513
514         if(RNA_property_is_set(op->ptr, "filepath"))
515                 return save_as_exec(C, op);
516
517         if(text->name)
518                 str= text->name;
519         else if(text->flags & TXT_ISMEM)
520                 str= text->id.name+2;
521         else
522                 str= G.sce;
523         
524         RNA_string_set(op->ptr, "filepath", str);
525         WM_event_add_fileselect(C, op); 
526
527         return OPERATOR_RUNNING_MODAL;
528 }
529
530 void TEXT_OT_save_as(wmOperatorType *ot)
531 {
532         /* identifiers */
533         ot->name= "Save As";
534         ot->idname= "TEXT_OT_save_as";
535         ot->description= "Save active text file with options";
536         
537         /* api callbacks */
538         ot->exec= save_as_exec;
539         ot->invoke= save_as_invoke;
540         ot->poll= text_edit_poll;
541
542         /* properties */
543         WM_operator_properties_filesel(ot, FOLDERFILE|TEXTFILE|PYSCRIPTFILE, FILE_SPECIAL, FILE_SAVE, WM_FILESEL_FILEPATH);  //XXX TODO, relative_path
544 }
545
546 /******************* run script operator *********************/
547
548 static int run_script_poll(bContext *C)
549 {
550         return (CTX_data_edit_text(C) != NULL);
551 }
552
553 static int run_script_exec(bContext *C, wmOperator *op)
554 {
555 #ifdef DISABLE_PYTHON
556         BKE_report(op->reports, RPT_ERROR, "Python disabled in this build");
557
558         return OPERATOR_CANCELLED;
559 #else
560         Text *text= CTX_data_edit_text(C);
561         SpaceText *st= CTX_wm_space_text(C);
562
563         if (BPY_run_python_script(C, NULL, text, op->reports))
564                 return OPERATOR_FINISHED;
565         
566         /* Dont report error messages while live editing */
567         if(!(st && st->live_edit))
568                 BKE_report(op->reports, RPT_ERROR, "Python script fail, look in the console for now...");
569         
570         return OPERATOR_CANCELLED;
571 #endif
572 }
573
574 void TEXT_OT_run_script(wmOperatorType *ot)
575 {
576         /* identifiers */
577         ot->name= "Run Script";
578         ot->idname= "TEXT_OT_run_script";
579         ot->description= "Run active script";
580         
581         /* api callbacks */
582         ot->poll= run_script_poll;
583         ot->exec= run_script_exec;
584
585         /* flags */
586         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
587 }
588
589 /******************* refresh pyconstraints operator *********************/
590
591 static int refresh_pyconstraints_exec(bContext *C, wmOperator *op)
592 {
593 #ifndef DISABLE_PYTHON
594 #if 0
595         Text *text= CTX_data_edit_text(C);
596         Object *ob;
597         bConstraint *con;
598         short update;
599         
600         /* check all pyconstraints */
601         for(ob= CTX_data_main(C)->object.first; ob; ob= ob->id.next) {
602                 update = 0;
603                 if(ob->type==OB_ARMATURE && ob->pose) {
604                         bPoseChannel *pchan;
605                         for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
606                                 for(con = pchan->constraints.first; con; con= con->next) {
607                                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
608                                                 bPythonConstraint *data = con->data;
609                                                 if(data->text==text) BPY_pyconstraint_update(ob, con);
610                                                 update = 1;
611                                                 
612                                         }
613                                 }
614                         }
615                 }
616                 for(con = ob->constraints.first; con; con= con->next) {
617                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
618                                 bPythonConstraint *data = con->data;
619                                 if(data->text==text) BPY_pyconstraint_update(ob, con);
620                                 update = 1;
621                         }
622                 }
623                 
624                 if(update) {
625                         DAG_id_flush_update(&ob->id, OB_RECALC_DATA);
626                 }
627         }
628 #endif
629 #endif
630
631         return OPERATOR_FINISHED;
632 }
633
634 void TEXT_OT_refresh_pyconstraints(wmOperatorType *ot)
635 {
636         /* identifiers */
637         ot->name= "Refresh PyConstraints";
638         ot->idname= "TEXT_OT_refresh_pyconstraints";
639         ot->description= "Refresh all pyconstraints";
640         
641         /* api callbacks */
642         ot->exec= refresh_pyconstraints_exec;
643         ot->poll= text_edit_poll;
644 }
645
646 /******************* paste operator *********************/
647
648 static char *txt_copy_selected(Text *text)
649 {
650         TextLine *tmp, *linef, *linel;
651         char *buf= NULL;
652         int charf, charl, length= 0;
653         
654         if(!text) return NULL;
655         if(!text->curl) return NULL;
656         if(!text->sell) return NULL;
657
658         if(!txt_has_sel(text)) return NULL;
659
660         if(text->curl==text->sell) {
661                 linef= linel= text->curl;
662                 
663                 if(text->curc < text->selc) {
664                         charf= text->curc;
665                         charl= text->selc;
666                 }
667                 else{
668                         charf= text->selc;
669                         charl= text->curc;
670                 }
671         }
672         else if(txt_get_span(text->curl, text->sell)<0) {
673                 linef= text->sell;
674                 linel= text->curl;
675
676                 charf= text->selc;              
677                 charl= text->curc;
678         }
679         else {
680                 linef= text->curl;
681                 linel= text->sell;
682                 
683                 charf= text->curc;
684                 charl= text->selc;
685         }
686
687         if(linef == linel) {
688                 length= charl-charf;
689
690                 buf= MEM_callocN(length+1, "cut buffera");
691                 
692                 BLI_strncpy(buf, linef->line + charf, length+1);
693         }
694         else {
695                 length+= linef->len - charf;
696                 length+= charl;
697                 length++; /* For the '\n' */
698                 
699                 tmp= linef->next;
700                 while(tmp && tmp!= linel) {
701                         length+= tmp->len+1;
702                         tmp= tmp->next;
703                 }
704                 
705                 buf= MEM_callocN(length+1, "cut bufferb");
706                 
707                 strncpy(buf, linef->line+ charf, linef->len-charf);
708                 length= linef->len-charf;
709                 
710                 buf[length++]='\n';
711                 
712                 tmp= linef->next;
713                 while(tmp && tmp!=linel) {
714                         strncpy(buf+length, tmp->line, tmp->len);
715                         length+= tmp->len;
716                         
717                         buf[length++]='\n';                     
718                         
719                         tmp= tmp->next;
720                 }
721                 strncpy(buf+length, linel->line, charl);
722                 length+= charl;
723                 
724                 buf[length]=0;
725         }
726
727         return buf;
728 }
729
730 static int paste_exec(bContext *C, wmOperator *op)
731 {
732         Text *text= CTX_data_edit_text(C);
733         char *buf;
734         int selection= RNA_boolean_get(op->ptr, "selection");
735
736         buf= WM_clipboard_text_get(selection);
737
738         if(!buf)
739                 return OPERATOR_CANCELLED;
740
741         txt_insert_buf(text, buf);
742         text_update_edited(text);
743
744         MEM_freeN(buf);
745
746         text_update_cursor_moved(C);
747         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
748
749         /* run the script while editing, evil but useful */
750         if(CTX_wm_space_text(C)->live_edit)
751                 run_script_exec(C, op);
752         
753         return OPERATOR_FINISHED;
754 }
755
756 void TEXT_OT_paste(wmOperatorType *ot)
757 {
758         /* identifiers */
759         ot->name= "Paste";
760         ot->idname= "TEXT_OT_paste";
761         ot->description= "Paste text from clipboard";
762         
763         /* api callbacks */
764         ot->exec= paste_exec;
765         ot->poll= text_edit_poll;
766         
767         /* properties */
768         RNA_def_boolean(ot->srna, "selection", 0, "Selection", "Paste text selected elsewhere rather than copied, X11 only.");
769 }
770
771 /******************* copy operator *********************/
772
773 static void txt_copy_clipboard(Text *text)
774 {
775         char *buf;
776
777         buf= txt_copy_selected(text);
778
779         if(buf) {
780                 WM_clipboard_text_set(buf, 0);
781                 MEM_freeN(buf);
782         }
783 }
784
785 static int copy_exec(bContext *C, wmOperator *op)
786 {
787         Text *text= CTX_data_edit_text(C);
788
789         txt_copy_clipboard(text);
790
791         return OPERATOR_FINISHED;
792 }
793
794 void TEXT_OT_copy(wmOperatorType *ot)
795 {
796         /* identifiers */
797         ot->name= "Copy";
798         ot->idname= "TEXT_OT_copy";
799         ot->description= "Copy selected text to clipboard";
800
801         /* api callbacks */
802         ot->exec= copy_exec;
803         ot->poll= text_edit_poll;
804 }
805
806 /******************* cut operator *********************/
807
808 static int cut_exec(bContext *C, wmOperator *op)
809 {
810         Text *text= CTX_data_edit_text(C);
811
812         txt_copy_clipboard(text);
813         txt_delete_selected(text);
814
815         text_update_cursor_moved(C);
816         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
817
818         /* run the script while editing, evil but useful */
819         if(CTX_wm_space_text(C)->live_edit)
820                 run_script_exec(C, op);
821         
822         return OPERATOR_FINISHED;
823 }
824
825 void TEXT_OT_cut(wmOperatorType *ot)
826 {
827         /* identifiers */
828         ot->name= "Cut";
829         ot->idname= "TEXT_OT_cut";
830         ot->description= "Cut selected text to clipboard";
831         
832         /* api callbacks */
833         ot->exec= cut_exec;
834         ot->poll= text_edit_poll;
835 }
836
837 /******************* indent operator *********************/
838
839 static int indent_exec(bContext *C, wmOperator *op)
840 {
841         Text *text= CTX_data_edit_text(C);
842
843         if(txt_has_sel(text)) {
844                 txt_order_cursors(text);
845                 indent(text);
846         }
847         else
848                 txt_add_char(text, '\t');
849
850         text_update_edited(text);
851
852         text_update_cursor_moved(C);
853         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
854
855         return OPERATOR_FINISHED;
856 }
857
858 void TEXT_OT_indent(wmOperatorType *ot)
859 {
860         /* identifiers */
861         ot->name= "Indent";
862         ot->idname= "TEXT_OT_indent";
863         ot->description= "Indent selected text";
864         
865         /* api callbacks */
866         ot->exec= indent_exec;
867         ot->poll= text_edit_poll;
868 }
869
870 /******************* unindent operator *********************/
871
872 static int unindent_exec(bContext *C, wmOperator *op)
873 {
874         Text *text= CTX_data_edit_text(C);
875
876         if(txt_has_sel(text)) {
877                 txt_order_cursors(text);
878                 unindent(text);
879
880                 text_update_edited(text);
881
882                 text_update_cursor_moved(C);
883                 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
884
885                 return OPERATOR_FINISHED;
886         }
887
888         return OPERATOR_CANCELLED;
889 }
890
891 void TEXT_OT_unindent(wmOperatorType *ot)
892 {
893         /* identifiers */
894         ot->name= "Unindent";
895         ot->idname= "TEXT_OT_unindent";
896         ot->description= "Unindent selected text";
897         
898         /* api callbacks */
899         ot->exec= unindent_exec;
900         ot->poll= text_edit_poll;
901 }
902
903 /******************* line break operator *********************/
904
905 static int line_break_exec(bContext *C, wmOperator *op)
906 {
907         SpaceText *st= CTX_wm_space_text(C);
908         Text *text= CTX_data_edit_text(C);
909         int a, curts;
910         int space = (text->flags & TXT_TABSTOSPACES) ? st->tabnumber : 1;
911
912         // double check tabs/spaces before splitting the line
913         curts= setcurr_tab_spaces(text, space);
914         txt_split_curline(text);
915
916         for(a=0; a < curts; a++) {
917                 if (text->flags & TXT_TABSTOSPACES) {
918                         txt_add_char(text, ' ');
919                 } else {
920                         txt_add_char(text, '\t');
921                 }
922         }
923
924         if(text->curl) {
925                 if(text->curl->prev)
926                         text_update_line_edited(text, text->curl->prev);
927                 text_update_line_edited(text, text->curl);
928         }
929
930         text_update_cursor_moved(C);
931         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
932
933         return OPERATOR_CANCELLED;
934 }
935
936 void TEXT_OT_line_break(wmOperatorType *ot)
937 {
938         /* identifiers */
939         ot->name= "Line Break";
940         ot->idname= "TEXT_OT_line_break";
941         ot->description= "Insert line break at cursor position";
942         
943         /* api callbacks */
944         ot->exec= line_break_exec;
945         ot->poll= text_edit_poll;
946 }
947
948 /******************* comment operator *********************/
949
950 static int comment_exec(bContext *C, wmOperator *op)
951 {
952         Text *text= CTX_data_edit_text(C);
953
954         if(txt_has_sel(text)) {
955                 txt_order_cursors(text);
956                 comment(text);
957                 text_update_edited(text);
958
959                 text_update_cursor_moved(C);
960                 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
961                 return OPERATOR_FINISHED;
962         }
963
964         return OPERATOR_CANCELLED;
965 }
966
967 void TEXT_OT_comment(wmOperatorType *ot)
968 {
969         /* identifiers */
970         ot->name= "Comment";
971         ot->idname= "TEXT_OT_comment";
972         ot->description= "Convert selected text to comment";
973         
974         /* api callbacks */
975         ot->exec= comment_exec;
976         ot->poll= text_edit_poll;
977 }
978
979 /******************* uncomment operator *********************/
980
981 static int uncomment_exec(bContext *C, wmOperator *op)
982 {
983         Text *text= CTX_data_edit_text(C);
984
985         if(txt_has_sel(text)) {
986                 txt_order_cursors(text);
987                 uncomment(text);
988                 text_update_edited(text);
989
990                 text_update_cursor_moved(C);
991                 WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
992
993                 return OPERATOR_FINISHED;
994         }
995
996         return OPERATOR_CANCELLED;
997 }
998
999 void TEXT_OT_uncomment(wmOperatorType *ot)
1000 {
1001         /* identifiers */
1002         ot->name= "Uncomment";
1003         ot->idname= "TEXT_OT_uncomment";
1004         ot->description= "Convert selected comment to text";
1005         
1006         /* api callbacks */
1007         ot->exec= uncomment_exec;
1008         ot->poll= text_edit_poll;
1009 }
1010
1011 /******************* convert whitespace operator *********************/
1012
1013 enum { TO_SPACES, TO_TABS };
1014 static EnumPropertyItem whitespace_type_items[]= {
1015         {TO_SPACES, "SPACES", 0, "To Spaces", NULL},
1016         {TO_TABS, "TABS", 0, "To Tabs", NULL},
1017         {0, NULL, 0, NULL, NULL}};
1018
1019 static int convert_whitespace_exec(bContext *C, wmOperator *op)
1020 {
1021         SpaceText *st= CTX_wm_space_text(C);
1022         Text *text= CTX_data_edit_text(C);
1023         TextLine *tmp;
1024         FlattenString fs;
1025         size_t a, j;
1026         char *text_check_line, *new_line;
1027         int extra, number; //unknown for now
1028         int type= RNA_enum_get(op->ptr, "type");
1029         
1030         tmp = text->lines.first;
1031         
1032         //first convert to all space, this make it a lot easier to convert to tabs because there is no mixtures of ' ' && '\t'
1033         while(tmp) {
1034                 text_check_line = tmp->line;
1035                 number = flatten_string(st, &fs, text_check_line)+1;
1036                 flatten_string_free(&fs);
1037                 new_line = MEM_callocN(number, "Converted_Line");
1038                 j = 0;
1039                 for(a=0; a < strlen(text_check_line); a++) { //foreach char in line
1040                         if(text_check_line[a] == '\t') { //checking for tabs
1041                                 //get the number of spaces this tabs is showing
1042                                 //i dont like doing it this way but will look into it later
1043                                 new_line[j] = '\0';
1044                                 number = flatten_string(st, &fs, new_line);
1045                                 flatten_string_free(&fs);
1046                                 new_line[j] = '\t';
1047                                 new_line[j+1] = '\0';
1048                                 number = flatten_string(st, &fs, new_line)-number;
1049                                 flatten_string_free(&fs);
1050
1051                                 for(extra = 0; extra < number; extra++) {
1052                                         new_line[j] = ' ';
1053                                         j++;
1054                                 }
1055                         }
1056                         else {
1057                                 new_line[j] = text_check_line[a];
1058                                 ++j;
1059                         }
1060                 }
1061                 new_line[j] = '\0';
1062                 // put new_line in the tmp->line spot still need to try and set the curc correctly
1063                 if(tmp->line) MEM_freeN(tmp->line);
1064                 if(tmp->format) MEM_freeN(tmp->format);
1065                 
1066                 tmp->line = new_line;
1067                 tmp->len = strlen(new_line);
1068                 tmp->format = NULL;
1069                 tmp = tmp->next;
1070         }
1071         
1072         if(type == TO_TABS) // Converting to tabs
1073         {       //start over from the begining
1074                 tmp = text->lines.first;
1075                 
1076                 while(tmp) {
1077                         text_check_line = tmp->line;
1078                         extra = 0;
1079                         for(a = 0; a < strlen(text_check_line); a++) {
1080                                 number = 0;
1081                                 for(j = 0; j < (size_t)st->tabnumber; j++) {
1082                                         if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
1083                                                 if(text_check_line[a+j] != ' ') {
1084                                                         number = 1;
1085                                                 }
1086                                         }
1087                                 }
1088                                 if(!number) { //found all number of space to equal a tab
1089                                         a = a+(st->tabnumber-1);
1090                                         extra = extra+1;
1091                                 }
1092                         }
1093                         
1094                         if( extra > 0 ) { //got tabs make malloc and do what you have to do
1095                                 new_line = MEM_callocN(strlen(text_check_line)-(((st->tabnumber*extra)-extra)-1), "Converted_Line");
1096                                 extra = 0; //reuse vars
1097                                 for(a = 0; a < strlen(text_check_line); a++) {
1098                                         number = 0;
1099                                         for(j = 0; j < (size_t)st->tabnumber; j++) {
1100                                                 if((a+j) <= strlen(text_check_line)) { //check to make sure we are not pass the end of the line
1101                                                         if(text_check_line[a+j] != ' ') {
1102                                                                 number = 1;
1103                                                         }
1104                                                 }
1105                                         }
1106
1107                                         if(!number) { //found all number of space to equal a tab
1108                                                 new_line[extra] = '\t';
1109                                                 a = a+(st->tabnumber-1);
1110                                                 ++extra;
1111                                                 
1112                                         }
1113                                         else { //not adding a tab
1114                                                 new_line[extra] = text_check_line[a];
1115                                                 ++extra;
1116                                         }
1117                                 }
1118                                 new_line[extra] = '\0';
1119                                 // put new_line in the tmp->line spot still need to try and set the curc correctly
1120                                 if(tmp->line) MEM_freeN(tmp->line);
1121                                 if(tmp->format) MEM_freeN(tmp->format);
1122                                 
1123                                 tmp->line = new_line;
1124                                 tmp->len = strlen(new_line);
1125                                 tmp->format = NULL;
1126                         }
1127                         tmp = tmp->next;
1128                 }
1129         }
1130
1131         text_update_edited(text);
1132         text_update_cursor_moved(C);
1133         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1134
1135         return OPERATOR_FINISHED;
1136 }
1137
1138 void TEXT_OT_convert_whitespace(wmOperatorType *ot)
1139 {
1140         /* identifiers */
1141         ot->name= "Convert Whitespace";
1142         ot->idname= "TEXT_OT_convert_whitespace";
1143         ot->description= "Convert whitespaces by type";
1144         
1145         /* api callbacks */
1146         ot->exec= convert_whitespace_exec;
1147         ot->poll= text_edit_poll;
1148
1149         /* properties */
1150         RNA_def_enum(ot->srna, "type", whitespace_type_items, TO_SPACES, "type", "Type of whitespace to convert to.");
1151 }
1152
1153 /******************* select all operator *********************/
1154
1155 static int select_all_exec(bContext *C, wmOperator *op)
1156 {
1157         Text *text= CTX_data_edit_text(C);
1158
1159         txt_sel_all(text);
1160
1161         text_update_cursor_moved(C);
1162         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1163
1164         return OPERATOR_FINISHED;
1165 }
1166
1167 void TEXT_OT_select_all(wmOperatorType *ot)
1168 {
1169         /* identifiers */
1170         ot->name= "Select All";
1171         ot->idname= "TEXT_OT_select_all";
1172         ot->description= "Select all text";
1173         
1174         /* api callbacks */
1175         ot->exec= select_all_exec;
1176         ot->poll= text_edit_poll;
1177 }
1178
1179 /******************* select line operator *********************/
1180
1181 static int select_line_exec(bContext *C, wmOperator *op)
1182 {
1183         Text *text= CTX_data_edit_text(C);
1184
1185         txt_sel_line(text);
1186
1187         text_update_cursor_moved(C);
1188         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1189
1190         return OPERATOR_FINISHED;
1191 }
1192
1193 void TEXT_OT_select_line(wmOperatorType *ot)
1194 {
1195         /* identifiers */
1196         ot->name= "Select Line";
1197         ot->idname= "TEXT_OT_select_line";
1198         ot->description= "Select text by line";
1199         
1200         /* api clinebacks */
1201         ot->exec= select_line_exec;
1202         ot->poll= text_edit_poll;
1203 }
1204
1205 /******************* previous marker operator *********************/
1206
1207 static int previous_marker_exec(bContext *C, wmOperator *op)
1208 {
1209         Text *text= CTX_data_edit_text(C);
1210         TextMarker *mrk;
1211         int lineno;
1212
1213         lineno= txt_get_span(text->lines.first, text->curl);
1214         mrk= text->markers.last;
1215         while(mrk && (mrk->lineno>lineno || (mrk->lineno==lineno && mrk->end > text->curc)))
1216                 mrk= mrk->prev;
1217         if(!mrk) mrk= text->markers.last;
1218         if(mrk) {
1219                 txt_move_to(text, mrk->lineno, mrk->start, 0);
1220                 txt_move_to(text, mrk->lineno, mrk->end, 1);
1221         }
1222
1223         text_update_cursor_moved(C);
1224         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1225
1226         return OPERATOR_FINISHED;
1227 }
1228
1229 void TEXT_OT_previous_marker(wmOperatorType *ot)
1230 {
1231         /* identifiers */
1232         ot->name= "Previous Marker";
1233         ot->idname= "TEXT_OT_previous_marker";
1234         ot->description= "Move to previous marker";
1235         
1236         /* api callbacks */
1237         ot->exec= previous_marker_exec;
1238         ot->poll= text_edit_poll;
1239 }
1240
1241 /******************* next marker operator *********************/
1242
1243 static int next_marker_exec(bContext *C, wmOperator *op)
1244 {
1245         Text *text= CTX_data_edit_text(C);
1246         TextMarker *mrk;
1247         int lineno;
1248
1249         lineno= txt_get_span(text->lines.first, text->curl);
1250         mrk= text->markers.first;
1251         while(mrk && (mrk->lineno<lineno || (mrk->lineno==lineno && mrk->start <= text->curc)))
1252                 mrk= mrk->next;
1253         if(!mrk) mrk= text->markers.first;
1254         if(mrk) {
1255                 txt_move_to(text, mrk->lineno, mrk->start, 0);
1256                 txt_move_to(text, mrk->lineno, mrk->end, 1);
1257         }
1258
1259         text_update_cursor_moved(C);
1260         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1261
1262         return OPERATOR_FINISHED;
1263 }
1264
1265 void TEXT_OT_next_marker(wmOperatorType *ot)
1266 {
1267         /* identifiers */
1268         ot->name= "Next Marker";
1269         ot->idname= "TEXT_OT_next_marker";
1270         ot->description= "Move to next marker";
1271         
1272         /* api callbacks */
1273         ot->exec= next_marker_exec;
1274         ot->poll= text_edit_poll;
1275 }
1276
1277 /******************* clear all markers operator *********************/
1278
1279 static int clear_all_markers_exec(bContext *C, wmOperator *op)
1280 {
1281         Text *text= CTX_data_edit_text(C);
1282
1283         txt_clear_markers(text, 0, 0);
1284
1285         text_update_cursor_moved(C);
1286         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1287
1288         return OPERATOR_FINISHED;
1289 }
1290
1291 void TEXT_OT_markers_clear(wmOperatorType *ot)
1292 {
1293         /* identifiers */
1294         ot->name= "Clear All Markers";
1295         ot->idname= "TEXT_OT_markers_clear";
1296         ot->description= "Clear all markers";
1297         
1298         /* api callbacks */
1299         ot->exec= clear_all_markers_exec;
1300         ot->poll= text_edit_poll;
1301 }
1302
1303 /************************ move operator ************************/
1304
1305 static EnumPropertyItem move_type_items[]= {
1306         {LINE_BEGIN, "LINE_BEGIN", 0, "Line Begin", ""},
1307         {LINE_END, "LINE_END", 0, "Line End", ""},
1308         {FILE_TOP, "FILE_TOP", 0, "File Top", ""},
1309         {FILE_BOTTOM, "FILE_BOTTOM", 0, "File Bottom", ""},
1310         {PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
1311         {NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
1312         {PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
1313         {NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
1314         {PREV_LINE, "PREVIOUS_LINE", 0, "Previous Line", ""},
1315         {NEXT_LINE, "NEXT_LINE", 0, "Next Line", ""},
1316         {PREV_PAGE, "PREVIOUS_PAGE", 0, "Previous Page", ""},
1317         {NEXT_PAGE, "NEXT_PAGE", 0, "Next Page", ""},
1318         {0, NULL, 0, NULL, NULL}};
1319
1320 static void wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
1321 {
1322         Text *text= st->text;
1323         int offl, offc, lin;
1324
1325         text_update_character_width(st);
1326
1327         lin= txt_get_span(text->lines.first, text->sell);
1328         wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1329
1330         if (sel) {
1331                 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, -offc);
1332                 text->selc= -offc;
1333         } else {
1334                 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, -offc);
1335                 text->curc= -offc;
1336                 txt_pop_sel(text);
1337         }
1338 }
1339
1340 static void wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
1341 {
1342         Text *text= st->text;
1343         int offl, offc, lin, startl, c;
1344
1345         text_update_character_width(st);
1346
1347         lin= txt_get_span(text->lines.first, text->sell);
1348         wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1349         startl= offl;
1350         c= text->selc;
1351         while (offl==startl && text->sell->line[c]!='\0') {
1352                 c++;
1353                 wrap_offset(st, ar, text->sell, c, &offl, &offc);
1354         } if (offl!=startl) c--;
1355
1356         if (sel) {
1357                 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, c);
1358                 text->selc= c;
1359         } else {
1360                 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, c);
1361                 text->curc= c;
1362                 txt_pop_sel(text);
1363         }
1364 }
1365
1366 static void wrap_move_up(SpaceText *st, ARegion *ar, short sel)
1367 {
1368         Text *text= st->text;
1369         int offl, offl_1, offc, fromline, toline, c, target;
1370
1371         text_update_character_width(st);
1372
1373         wrap_offset(st, ar, text->sell, 0, &offl_1, &offc);
1374         wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1375         fromline= toline= txt_get_span(text->lines.first, text->sell);
1376         target= text->selc + offc;
1377
1378         if (offl==offl_1) {
1379                 if (!text->sell->prev) {
1380                         txt_move_bol(text, sel);
1381                         return;
1382                 }
1383                 toline--;
1384                 c= text->sell->prev->len; /* End of prev. line */
1385                 wrap_offset(st, ar, text->sell->prev, c, &offl, &offc);
1386                 c= -offc+target;
1387         } else {
1388                 c= -offc-1; /* End of prev. line */
1389                 wrap_offset(st, ar, text->sell, c, &offl, &offc);
1390                 c= -offc+target;
1391         }
1392         if (c<0) c=0;
1393
1394         if (sel) {
1395                 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
1396                 if (toline<fromline) text->sell= text->sell->prev;
1397                 if(text->sell) {
1398                         if (c>text->sell->len) c= text->sell->len;
1399                         text->selc= c;
1400                 }
1401         } 
1402         else if(text->curl) {
1403                 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
1404                 if (toline<fromline) text->curl= text->curl->prev;
1405                 if(text->curl) {
1406                         if (c>text->curl->len) c= text->curl->len;
1407                         text->curc= c;
1408                         txt_pop_sel(text);
1409                 }
1410         }
1411 }
1412
1413 static void wrap_move_down(SpaceText *st, ARegion *ar, short sel)
1414 {
1415         Text *text= st->text;
1416         int offl, startoff, offc, fromline, toline, c, target;
1417
1418         text_update_character_width(st);
1419
1420         wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
1421         fromline= toline= txt_get_span(text->lines.first, text->sell);
1422         target= text->selc + offc;
1423         startoff= offl;
1424         c= text->selc;
1425         while (offl==startoff && text->sell->line[c]!='\0') {
1426                 c++;
1427                 wrap_offset(st, ar, text->sell, c, &offl, &offc);
1428         }
1429
1430         if (text->sell->line[c]=='\0') {
1431                 if (!text->sell->next) {
1432                         txt_move_eol(text, sel);
1433                         return;
1434                 }
1435                 toline++;
1436                 c= target;
1437         } else {
1438                 c += target;
1439                 if (c > text->sell->len) c= text->sell->len;
1440         }
1441         if (c<0) c=0;
1442
1443         if (sel) {
1444                 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
1445                 if (toline>fromline) text->sell= text->sell->next;
1446                 if(text->sell) {
1447                         if (c>text->sell->len) c= text->sell->len;
1448                         text->selc= c;
1449                 }
1450         } 
1451         else if(text->curl) {
1452                 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
1453                 if (toline>fromline) text->curl= text->curl->next;
1454                 if(text->curl) {
1455                         if (c > text->curl->len) c= text->curl->len;
1456                         text->curc= c;
1457                         txt_pop_sel(text);
1458                 }
1459         }
1460 }
1461
1462 /* Moves the cursor vertically by the specified number of lines.
1463  If the destination line is shorter than the current cursor position, the
1464  cursor will be positioned at the end of this line.
1465
1466  This is to replace screen_skip for PageUp/Down operations.
1467  */
1468 static void cursor_skip(Text *text, int lines, int sel)
1469 {
1470         TextLine **linep;
1471         int oldl, oldc, *charp;
1472         
1473         if (sel) linep= &text->sell, charp= &text->selc;
1474         else linep= &text->curl, charp= &text->curc;
1475         oldl= txt_get_span(text->lines.first, *linep);
1476         oldc= *charp;
1477
1478         while (lines>0 && (*linep)->next) {
1479                 *linep= (*linep)->next;
1480                 lines--;
1481         }
1482         while (lines<0 && (*linep)->prev) {
1483                 *linep= (*linep)->prev;
1484                 lines++;
1485         }
1486
1487         if (*charp > (*linep)->len) *charp= (*linep)->len;
1488
1489         if (!sel) txt_pop_sel(text);
1490         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
1491 }
1492
1493 static int move_cursor(bContext *C, int type, int select)
1494 {
1495         SpaceText *st= CTX_wm_space_text(C);
1496         Text *text= CTX_data_edit_text(C);
1497         ARegion *ar= CTX_wm_region(C);
1498
1499         /* ensure we have the right region, it's optional */
1500         if(ar && ar->regiontype != RGN_TYPE_WINDOW)
1501                 ar= NULL;
1502
1503         switch(type) {
1504                 case LINE_BEGIN:
1505                         if(st && st->wordwrap && ar) wrap_move_bol(st, ar, select);
1506                         else txt_move_bol(text, select);
1507                         break;
1508                         
1509                 case LINE_END:
1510                         if(st && st->wordwrap && ar) wrap_move_eol(st, ar, select);
1511                         else txt_move_eol(text, select);
1512                         break;
1513
1514                 case FILE_TOP:
1515                         txt_move_bof(text, select);
1516                         break;
1517                         
1518                 case FILE_BOTTOM:
1519                         txt_move_eof(text, select);
1520                         break;
1521
1522                 case PREV_WORD:
1523                         txt_jump_left(text, select);
1524                         break;
1525
1526                 case NEXT_WORD:
1527                         txt_jump_right(text, select);
1528                         break;
1529
1530                 case PREV_CHAR:
1531                         txt_move_left(text, select);
1532                         break;
1533
1534                 case NEXT_CHAR: 
1535                         txt_move_right(text, select);
1536                         break;
1537
1538                 case PREV_LINE:
1539                         if(st && st->wordwrap && ar) wrap_move_up(st, ar, select);
1540                         else txt_move_up(text, select);
1541                         break;
1542                         
1543                 case NEXT_LINE:
1544                         if(st && st->wordwrap && ar) wrap_move_down(st, ar, select);
1545                         else txt_move_down(text, select);
1546                         break;
1547
1548                 case PREV_PAGE:
1549                         if(st) cursor_skip(text, -st->viewlines, select);
1550                         else cursor_skip(text, -10, select);
1551                         break;
1552
1553                 case NEXT_PAGE:
1554                         if(st) cursor_skip(text, st->viewlines, select);
1555                         else cursor_skip(text, 10, select);
1556                         break;
1557         }
1558
1559         text_update_cursor_moved(C);
1560         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1561
1562         return OPERATOR_FINISHED;
1563 }
1564
1565 static int move_exec(bContext *C, wmOperator *op)
1566 {
1567         int type= RNA_enum_get(op->ptr, "type");
1568
1569         return move_cursor(C, type, 0);
1570 }
1571
1572 void TEXT_OT_move(wmOperatorType *ot)
1573 {
1574         /* identifiers */
1575         ot->name= "Move Cursor";
1576         ot->idname= "TEXT_OT_move";
1577         ot->description= "Move cursor to position type";
1578         
1579         /* api callbacks */
1580         ot->exec= move_exec;
1581         ot->poll= text_edit_poll;
1582
1583         /* properties */
1584         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
1585 }
1586
1587 /******************* move select operator ********************/
1588
1589 static int move_select_exec(bContext *C, wmOperator *op)
1590 {
1591         int type= RNA_enum_get(op->ptr, "type");
1592
1593         return move_cursor(C, type, 1);
1594 }
1595
1596 void TEXT_OT_move_select(wmOperatorType *ot)
1597 {
1598         /* identifiers */
1599         ot->name= "Move Select";
1600         ot->idname= "TEXT_OT_move_select";
1601         ot->description= "Make selection from current cursor position to new cursor position type";
1602         
1603         /* api callbacks */
1604         ot->exec= move_select_exec;
1605         ot->poll= text_space_edit_poll;
1606
1607         /* properties */
1608         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection.");
1609 }
1610
1611 /******************* jump operator *********************/
1612
1613 static int jump_exec(bContext *C, wmOperator *op)
1614 {
1615         Text *text= CTX_data_edit_text(C);
1616         int line= RNA_int_get(op->ptr, "line");
1617         short nlines= txt_get_span(text->lines.first, text->lines.last)+1;
1618
1619         if(line < 1)
1620                 txt_move_toline(text, 1, 0);
1621         else if(line > nlines)
1622                 txt_move_toline(text, nlines-1, 0);
1623         else
1624                 txt_move_toline(text, line-1, 0);
1625
1626         text_update_cursor_moved(C);
1627         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
1628
1629         return OPERATOR_FINISHED;
1630 }
1631
1632 static int jump_invoke(bContext *C, wmOperator *op, wmEvent *event)
1633 {
1634         return WM_operator_props_dialog_popup(C,op,200,100);
1635
1636 }
1637
1638 void TEXT_OT_jump(wmOperatorType *ot)
1639 {
1640         /* identifiers */
1641         ot->name= "Jump";
1642         ot->idname= "TEXT_OT_jump";
1643         ot->description= "Jump cursor to line";
1644         
1645         /* api callbacks */
1646         ot->invoke= jump_invoke;
1647         ot->exec= jump_exec;
1648         ot->poll= text_edit_poll;
1649
1650         /* properties */
1651         RNA_def_int(ot->srna, "line", 1, 1, INT_MAX, "Line", "Line number to jump to.", 1, 10000);
1652 }
1653
1654 /******************* delete operator **********************/
1655
1656 static EnumPropertyItem delete_type_items[]= {
1657         {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
1658         {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
1659         {DEL_NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
1660         {DEL_PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
1661         {0, NULL, 0, NULL, NULL}};
1662
1663 static int delete_exec(bContext *C, wmOperator *op)
1664 {
1665         Text *text= CTX_data_edit_text(C);
1666         int type= RNA_enum_get(op->ptr, "type");
1667
1668         if(type == DEL_PREV_WORD)
1669                 txt_backspace_word(text);
1670         else if(type == DEL_PREV_CHAR)
1671                 txt_backspace_char(text);
1672         else if(type == DEL_NEXT_WORD)
1673                 txt_delete_word(text);
1674         else if(type == DEL_NEXT_CHAR)
1675                 txt_delete_char(text);
1676
1677         text_update_line_edited(text, text->curl);
1678
1679         text_update_cursor_moved(C);
1680         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1681
1682         /* run the script while editing, evil but useful */
1683         if(CTX_wm_space_text(C)->live_edit)
1684                 run_script_exec(C, op);
1685         
1686         return OPERATOR_FINISHED;
1687 }
1688
1689 void TEXT_OT_delete(wmOperatorType *ot)
1690 {
1691         /* identifiers */
1692         ot->name= "Delete";
1693         ot->idname= "TEXT_OT_delete";
1694         ot->description= "Delete text by cursor position";
1695         
1696         /* api callbacks */
1697         ot->exec= delete_exec;
1698         ot->poll= text_edit_poll;
1699
1700         /* properties */
1701         RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete.");
1702 }
1703
1704 /******************* toggle overwrite operator **********************/
1705
1706 static int toggle_overwrite_exec(bContext *C, wmOperator *op)
1707 {
1708         SpaceText *st= CTX_wm_space_text(C);
1709
1710         st->overwrite= !st->overwrite;
1711
1712         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
1713
1714         return OPERATOR_FINISHED;
1715 }
1716
1717 void TEXT_OT_overwrite_toggle(wmOperatorType *ot)
1718 {
1719         /* identifiers */
1720         ot->name= "Toggle Overwrite";
1721         ot->idname= "TEXT_OT_overwrite_toggle";
1722         ot->description= "Toggle overwrite while typing";
1723         
1724         /* api callbacks */
1725         ot->exec= toggle_overwrite_exec;
1726         ot->poll= text_space_edit_poll;
1727 }
1728
1729 /******************* scroll operator **********************/
1730
1731 /* Moves the view vertically by the specified number of lines */
1732 static void screen_skip(SpaceText *st, int lines)
1733 {
1734         int last;
1735
1736          st->top += lines;
1737
1738         last= txt_get_span(st->text->lines.first, st->text->lines.last);
1739         last= last - (st->viewlines/2);
1740         
1741         if(st->top>last) st->top= last;
1742         if(st->top<0) st->top= 0;
1743 }
1744
1745 typedef struct TextScroll {
1746         short old[2];
1747         short hold[2];
1748         short delta[2];
1749
1750         int first;
1751         int characters;
1752         int lines;
1753         int scrollbar;
1754 } TextScroll;
1755
1756 static int scroll_exec(bContext *C, wmOperator *op)
1757 {
1758         SpaceText *st= CTX_wm_space_text(C);
1759         int lines= RNA_int_get(op->ptr, "lines");
1760
1761         if(lines == 0)
1762                 return OPERATOR_CANCELLED;
1763
1764         screen_skip(st, lines*U.wheellinescroll);
1765
1766         ED_area_tag_redraw(CTX_wm_area(C));
1767
1768         return OPERATOR_FINISHED;
1769 }
1770
1771 static void scroll_apply(bContext *C, wmOperator *op, wmEvent *event)
1772 {
1773         SpaceText *st= CTX_wm_space_text(C);
1774         TextScroll *tsc= op->customdata;
1775         short *mval= event->mval;
1776
1777         text_update_character_width(st);
1778
1779         if(tsc->first) {
1780                 tsc->old[0]= mval[0];
1781                 tsc->old[1]= mval[1];
1782                 tsc->hold[0]= mval[0];
1783                 tsc->hold[1]= mval[1];
1784                 tsc->first= 0;
1785         }
1786
1787         if(!tsc->scrollbar) {
1788                 tsc->delta[0]= (tsc->hold[0]-mval[0])/st->cwidth;
1789                 tsc->delta[1]= (mval[1]-tsc->hold[1])/st->lheight;
1790         }
1791         else
1792                 tsc->delta[1]= (tsc->hold[1]-mval[1])*st->pix_per_line;
1793         
1794         if(tsc->delta[0] || tsc->delta[1]) {
1795                 screen_skip(st, tsc->delta[1]);
1796
1797                 tsc->lines += tsc->delta[1];
1798
1799                 if(st->wordwrap) {
1800                         st->left= 0;
1801                 }
1802                 else {
1803                         st->left+= tsc->delta[0];
1804                         if(st->left<0) st->left= 0;
1805                 }
1806                 
1807                 tsc->hold[0]= mval[0];
1808                 tsc->hold[1]= mval[1];
1809
1810                 ED_area_tag_redraw(CTX_wm_area(C));
1811         }
1812
1813         tsc->old[0]= mval[0];
1814         tsc->old[1]= mval[1];
1815 }
1816
1817 static void scroll_exit(bContext *C, wmOperator *op)
1818 {
1819         SpaceText *st= CTX_wm_space_text(C);
1820
1821         st->flags &= ~ST_SCROLL_SELECT;
1822         MEM_freeN(op->customdata);
1823 }
1824
1825 static int scroll_modal(bContext *C, wmOperator *op, wmEvent *event)
1826 {
1827         switch(event->type) {
1828                 case MOUSEMOVE:
1829                         scroll_apply(C, op, event);
1830                         break;
1831                 case LEFTMOUSE:
1832                 case RIGHTMOUSE:
1833                 case MIDDLEMOUSE:
1834                         scroll_exit(C, op);
1835                         return OPERATOR_FINISHED;
1836         }
1837
1838         return OPERATOR_RUNNING_MODAL;
1839 }
1840
1841 static int scroll_cancel(bContext *C, wmOperator *op)
1842 {
1843         scroll_exit(C, op);
1844
1845         return OPERATOR_CANCELLED;
1846 }
1847
1848 static int scroll_invoke(bContext *C, wmOperator *op, wmEvent *event)
1849 {
1850         SpaceText *st= CTX_wm_space_text(C);
1851         TextScroll *tsc;
1852         
1853         if(RNA_property_is_set(op->ptr, "lines"))
1854                 return scroll_exec(C, op);
1855         
1856         tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
1857         tsc->first= 1;
1858         op->customdata= tsc;
1859         
1860         st->flags|= ST_SCROLL_SELECT;
1861         
1862         if (event->type == MOUSEPAN) {
1863                 text_update_character_width(st);
1864                 
1865                 tsc->hold[0] = event->prevx;
1866                 tsc->hold[1] = event->prevy;
1867                 /* Sensitivity of scroll set to 4pix per line/char */
1868                 event->mval[0] = event->prevx + (event->x - event->prevx)*st->cwidth/4;
1869                 event->mval[1] = event->prevy + (event->y - event->prevy)*st->lheight/4;
1870                 tsc->first = 0;
1871                 tsc->scrollbar = 0;
1872                 scroll_apply(C, op, event);
1873                 scroll_exit(C, op);
1874                 return OPERATOR_FINISHED;
1875         }       
1876         
1877         WM_event_add_modal_handler(C, op);
1878         
1879         return OPERATOR_RUNNING_MODAL;
1880 }
1881
1882 void TEXT_OT_scroll(wmOperatorType *ot)
1883 {
1884         /* identifiers */
1885         ot->name= "Scroll";
1886         /*don't really see the difference between this and
1887           scroll_bar. Both do basically the same thing (aside 
1888           from keymaps).*/
1889         ot->idname= "TEXT_OT_scroll";
1890         ot->description= "Scroll text screen";
1891         
1892         /* api callbacks */
1893         ot->exec= scroll_exec;
1894         ot->invoke= scroll_invoke;
1895         ot->modal= scroll_modal;
1896         ot->cancel= scroll_cancel;
1897         ot->poll= text_space_edit_poll;
1898
1899         /* flags */
1900         ot->flag= OPTYPE_BLOCKING;
1901
1902         /* properties */
1903         RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
1904 }
1905
1906 /******************** scroll bar operator *******************/
1907
1908 static int scroll_bar_invoke(bContext *C, wmOperator *op, wmEvent *event)
1909 {
1910         SpaceText *st= CTX_wm_space_text(C);
1911         ARegion *ar= CTX_wm_region(C);
1912         TextScroll *tsc;
1913         short *mval= event->mval;
1914
1915         if(RNA_property_is_set(op->ptr, "lines"))
1916                 return scroll_exec(C, op);
1917         
1918         /* verify we are in the right zone */
1919         if(!(mval[0]>ar->winx-TXT_SCROLL_WIDTH && mval[0]<ar->winx-TXT_SCROLL_SPACE
1920                 && mval[1]>TXT_SCROLL_SPACE && mval[1]<ar->winy))
1921                 return OPERATOR_PASS_THROUGH;
1922
1923         tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
1924         tsc->first= 1;
1925         tsc->scrollbar= 1;
1926         op->customdata= tsc;
1927         
1928         st->flags|= ST_SCROLL_SELECT;
1929
1930         WM_event_add_modal_handler(C, op);
1931
1932         return OPERATOR_RUNNING_MODAL;
1933 }
1934
1935 void TEXT_OT_scroll_bar(wmOperatorType *ot)
1936 {
1937         /* identifiers */
1938         ot->name= "Scrollbar";
1939         /*don't really see the difference between this and
1940           scroll. Both do basically the same thing (aside 
1941           from keymaps).*/
1942         ot->idname= "TEXT_OT_scroll_bar";
1943         ot->description= "Scroll text screen";
1944         
1945         /* api callbacks */
1946         ot->invoke= scroll_bar_invoke;
1947         ot->modal= scroll_modal;
1948         ot->cancel= scroll_cancel;
1949         ot->poll= text_region_edit_poll;
1950
1951         /* flags */
1952         ot->flag= OPTYPE_BLOCKING;
1953
1954         /* properties */
1955         RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
1956 }
1957
1958 /******************* set cursor operator **********************/
1959
1960 typedef struct SetCursor {
1961         int selecting;
1962         int selc, sell;
1963         short old[2];
1964 } SetCursor;
1965
1966 static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel) 
1967 {
1968         FlattenString fs;
1969         Text *text= st->text;
1970         TextLine **linep;
1971         int *charp;
1972         int w;
1973
1974         text_update_character_width(st);
1975
1976         if(sel) { linep= &text->sell; charp= &text->selc; } 
1977         else { linep= &text->curl; charp= &text->curc; }
1978         
1979         y= (ar->winy - y)/st->lheight;
1980
1981         if(st->showlinenrs)
1982                 x-= TXT_OFFSET+TEXTXLOC;
1983         else
1984                 x-= TXT_OFFSET;
1985
1986         if(x<0) x= 0;
1987         x = (x/st->cwidth) + st->left;
1988         
1989         if(st->wordwrap) {
1990                 int i, j, endj, curs, max, chop, start, end, chars, loop;
1991                 char ch;
1992
1993                 /* Point to first visible line */
1994                 *linep= text->lines.first;
1995                 for(i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;
1996
1997                 max= wrap_width(st, ar);
1998
1999                 loop= 1;
2000                 while(loop && *linep) {
2001                         start= 0;
2002                         end= max;
2003                         chop= 1;
2004                         chars= 0;
2005                         curs= 0;
2006                         endj= 0;
2007                         for(i=0, j=0; loop; j++) {
2008
2009                                 /* Mimic replacement of tabs */
2010                                 ch= (*linep)->line[j];
2011                                 if(ch=='\t') {
2012                                         chars= st->tabnumber-i%st->tabnumber;
2013                                         ch= ' ';
2014                                 }
2015                                 else
2016                                         chars= 1;
2017
2018                                 while(chars--) {
2019                                         /* Gone too far, go back to last wrap point */
2020                                         if(y<0) {
2021                                                 *charp= endj;
2022                                                 loop= 0;
2023                                                 break;
2024                                         /* Exactly at the cursor, done */
2025                                         }
2026                                         else if(y==0 && i-start==x) {
2027                                                 *charp= curs= j;
2028                                                 loop= 0;
2029                                                 break;
2030                                         /* Prepare curs for next wrap */
2031                                         }
2032                                         else if(i-end==x) {
2033                                                 curs= j;
2034                                         }
2035                                         if(i-start>=max) {
2036                                                 if(chop) endj= j;
2037                                                 y--;
2038                                                 start= end;
2039                                                 end += max;
2040                                                 chop= 1;
2041                                                 if(y==0 && i-start>=x) {
2042                                                         *charp= curs;
2043                                                         loop= 0;
2044                                                         break;
2045                                                 }
2046                                         }
2047                                         else if(ch==' ' || ch=='-' || ch=='\0') {
2048                                                 if(y==0 && i-start>=x) {
2049                                                         *charp= curs;
2050                                                         loop= 0;
2051                                                         break;
2052                                                 }
2053                                                 end = i+1;
2054                                                 endj = j;
2055                                                 chop= 0;
2056                                         }
2057                                         i++;
2058                                 }
2059                                 if(ch=='\0') break;
2060                         }
2061                         if(!loop || y<0) break;
2062
2063                         if(!(*linep)->next) {
2064                                 *charp= (*linep)->len;
2065                                 break;
2066                         }
2067                         
2068                         /* On correct line but didn't meet cursor, must be at end */
2069                         if(y==0) {
2070                                 *charp= (*linep)->len;
2071                                 break;
2072                         }
2073                         *linep= (*linep)->next;
2074                         y--;
2075                 }
2076
2077         }
2078         else {
2079                 y-= txt_get_span(text->lines.first, *linep) - st->top;
2080                 
2081                 if(y>0) {
2082                         while(y-- != 0) if((*linep)->next) *linep= (*linep)->next;
2083                 }
2084                 else if(y<0) {
2085                         while(y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
2086                 }
2087
2088                 
2089                 w= flatten_string(st, &fs, (*linep)->line);
2090                 if(x<w) *charp= fs.accum[x];
2091                 else *charp= (*linep)->len;
2092                 flatten_string_free(&fs);
2093         }
2094         if(!sel) txt_pop_sel(text);
2095 }
2096
2097 static void set_cursor_apply(bContext *C, wmOperator *op, wmEvent *event)
2098 {
2099         SpaceText *st= CTX_wm_space_text(C);
2100         ARegion *ar= CTX_wm_region(C);
2101         SetCursor *scu= op->customdata;
2102
2103         if(event->mval[1]<0 || event->mval[1]>ar->winy) {
2104                 int d= (scu->old[1]-event->mval[1])*st->pix_per_line;
2105                 if(d) screen_skip(st, d);
2106
2107                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1]<0?0:ar->winy, 1);
2108
2109                 text_update_cursor_moved(C);
2110                 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2111         } 
2112         else if(!st->wordwrap && (event->mval[0]<0 || event->mval[0]>ar->winx)) {
2113                 if(event->mval[0]>ar->winx) st->left++;
2114                 else if(event->mval[0]<0 && st->left>0) st->left--;
2115                 
2116                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
2117                 
2118                 text_update_cursor_moved(C);
2119                 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2120                 // XXX PIL_sleep_ms(10);
2121         } 
2122         else {
2123                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
2124
2125                 text_update_cursor_moved(C);
2126                 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2127
2128                 scu->old[0]= event->mval[0];
2129                 scu->old[1]= event->mval[1];
2130         } 
2131 }
2132
2133 static void set_cursor_exit(bContext *C, wmOperator *op)
2134 {
2135         SpaceText *st= CTX_wm_space_text(C);
2136         Text *text= st->text;
2137         SetCursor *scu= op->customdata;
2138         int linep2, charp2;
2139         char *buffer;
2140
2141         if(txt_has_sel(text)) {
2142                 buffer = txt_sel_to_buf(text);
2143                 WM_clipboard_text_set(buffer, 1);
2144                 MEM_freeN(buffer);
2145         }
2146
2147         linep2= txt_get_span(st->text->lines.first, st->text->sell);
2148         charp2= st->text->selc;
2149                 
2150         if(scu->sell!=linep2 || scu->selc!=charp2)
2151                 txt_undo_add_toop(st->text, UNDO_STO, scu->sell, scu->selc, linep2, charp2);
2152
2153         text_update_cursor_moved(C);
2154         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2155
2156         MEM_freeN(scu);
2157 }
2158
2159 static int set_cursor_invoke(bContext *C, wmOperator *op, wmEvent *event)
2160 {
2161         SpaceText *st= CTX_wm_space_text(C);
2162         ARegion *ar= CTX_wm_region(C);
2163         SetCursor *scu;
2164
2165         op->customdata= MEM_callocN(sizeof(SetCursor), "SetCursor");
2166         scu= op->customdata;
2167         scu->selecting= RNA_boolean_get(op->ptr, "select");
2168
2169         scu->old[0]= event->mval[0];
2170         scu->old[1]= event->mval[1];
2171
2172         if(!scu->selecting) {
2173                 int curl= txt_get_span(st->text->lines.first, st->text->curl);
2174                 int curc= st->text->curc;                       
2175                 int linep2, charp2;
2176                                         
2177                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 0);
2178
2179                 linep2= txt_get_span(st->text->lines.first, st->text->curl);
2180                 charp2= st->text->selc;
2181                                 
2182                 if(curl!=linep2 || curc!=charp2)
2183                         txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
2184         }
2185
2186         scu->sell= txt_get_span(st->text->lines.first, st->text->sell);
2187         scu->selc= st->text->selc;
2188
2189         WM_event_add_modal_handler(C, op);
2190
2191         set_cursor_apply(C, op, event);
2192
2193         return OPERATOR_RUNNING_MODAL;
2194 }
2195
2196 static int set_cursor_modal(bContext *C, wmOperator *op, wmEvent *event)
2197 {
2198         switch(event->type) {
2199                 case LEFTMOUSE:
2200                 case MIDDLEMOUSE:
2201                 case RIGHTMOUSE:
2202                         set_cursor_exit(C, op);
2203                         return OPERATOR_FINISHED;
2204                 case MOUSEMOVE:
2205                         set_cursor_apply(C, op, event);
2206                         break;
2207         }
2208
2209         return OPERATOR_RUNNING_MODAL;
2210 }
2211
2212 static int set_cursor_cancel(bContext *C, wmOperator *op)
2213 {
2214         set_cursor_exit(C, op);
2215         return OPERATOR_FINISHED;
2216 }
2217
2218 void TEXT_OT_cursor_set(wmOperatorType *ot)
2219 {
2220         /* identifiers */
2221         ot->name= "Set Cursor";
2222         ot->idname= "TEXT_OT_cursor_set";
2223         ot->description= "Set cursor selection";
2224         
2225         /* api callbacks */
2226         ot->invoke= set_cursor_invoke;
2227         ot->modal= set_cursor_modal;
2228         ot->cancel= set_cursor_cancel;
2229         ot->poll= text_region_edit_poll;
2230
2231         /* properties */
2232         RNA_def_boolean(ot->srna, "select", 0, "Select", "Set selection end rather than cursor.");
2233 }
2234
2235 /******************* line number operator **********************/
2236
2237 static int line_number_invoke(bContext *C, wmOperator *op, wmEvent *event)
2238 {
2239         SpaceText *st= CTX_wm_space_text(C);
2240         Text *text= CTX_data_edit_text(C);
2241         ARegion *ar= CTX_wm_region(C);
2242         short *mval= event->mval;
2243         double time;
2244         static int jump_to= 0;
2245         static double last_jump= 0;
2246
2247         if(!st->showlinenrs)
2248                 return OPERATOR_PASS_THROUGH;
2249
2250         if(!(mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<ar->winy-2))
2251                 return OPERATOR_PASS_THROUGH;
2252
2253         if(!(event->ascii>='0' && event->ascii<='9'))
2254                 return OPERATOR_PASS_THROUGH;
2255
2256         time = PIL_check_seconds_timer();
2257         if(last_jump < time-1)
2258                 jump_to= 0;
2259
2260         jump_to *= 10;
2261         jump_to += (int)(event->ascii-'0');
2262
2263         txt_move_toline(text, jump_to-1, 0);
2264         last_jump= time;
2265
2266         text_update_cursor_moved(C);
2267         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2268
2269         return OPERATOR_FINISHED;
2270 }
2271
2272 void TEXT_OT_line_number(wmOperatorType *ot)
2273 {
2274         /* identifiers */
2275         ot->name= "Line Number";
2276         ot->idname= "TEXT_OT_line_number";
2277         ot->description= "The current line number";
2278         
2279         /* api callbacks */
2280         ot->invoke= line_number_invoke;
2281         ot->poll= text_region_edit_poll;
2282 }
2283
2284 /******************* insert operator **********************/
2285
2286 static int insert_exec(bContext *C, wmOperator *op)
2287 {
2288         SpaceText *st= CTX_wm_space_text(C);
2289         Text *text= CTX_data_edit_text(C);
2290         char *str;
2291         int done = 0, i;
2292
2293         str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
2294
2295         if(st && st->overwrite) {
2296                 for(i=0; str[i]; i++) {
2297                         done |= txt_replace_char(text, str[i]);
2298                 }
2299         } else {
2300                 for(i=0; str[i]; i++) {
2301                         done |= txt_add_char(text, str[i]);
2302                 }
2303         }
2304
2305         MEM_freeN(str);
2306         
2307         if(!done)
2308                 return OPERATOR_CANCELLED;
2309
2310         text_update_line_edited(text, text->curl);
2311
2312         text_update_cursor_moved(C);
2313         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2314
2315         return OPERATOR_FINISHED;
2316 }
2317
2318 static int insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
2319 {
2320         int ret;
2321
2322         // if(!RNA_property_is_set(op->ptr, "text")) { /* always set from keymap XXX */
2323         if(!RNA_string_length(op->ptr, "text")) {
2324                 char str[2] = {event->ascii, '\0'};
2325                 /* if alt/ctrl/super are pressed pass through */
2326                 if(event->ctrl || event->oskey)
2327                         return OPERATOR_PASS_THROUGH;
2328
2329                 RNA_string_set(op->ptr, "text", str);
2330         }
2331
2332         ret = insert_exec(C, op);
2333         
2334         /* run the script while editing, evil but useful */
2335         if(ret==OPERATOR_FINISHED && CTX_wm_space_text(C)->live_edit)
2336                 run_script_exec(C, op);
2337
2338         return ret;
2339 }
2340
2341 void TEXT_OT_insert(wmOperatorType *ot)
2342 {
2343         /* identifiers */
2344         ot->name= "Insert";
2345         ot->idname= "TEXT_OT_insert";
2346         ot->description= "Insert text at cursor position";
2347         
2348         /* api callbacks */
2349         ot->exec= insert_exec;
2350         ot->invoke= insert_invoke;
2351         ot->poll= text_edit_poll;
2352
2353         /* properties */
2354         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
2355 }
2356
2357 /******************* find operator *********************/
2358
2359 /* mode */
2360 #define TEXT_FIND               0
2361 #define TEXT_REPLACE    1
2362 #define TEXT_MARK_ALL   2
2363
2364 static int find_and_replace(bContext *C, wmOperator *op, short mode)
2365 {
2366         Main *bmain= CTX_data_main(C);
2367         SpaceText *st= CTX_wm_space_text(C);
2368         Text *start= NULL, *text= st->text;
2369         int flags, first= 1;
2370         int found = 0;
2371         char *tmp;
2372
2373         if(!st->findstr[0] || (mode == TEXT_REPLACE && !st->replacestr[0]))
2374                 return OPERATOR_CANCELLED;
2375
2376         flags= st->flags;
2377         if(flags & ST_FIND_ALL)
2378                 flags ^= ST_FIND_WRAP;
2379
2380         do {
2381                 if(first)
2382                         txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
2383
2384                 first= 0;
2385                 
2386                 /* Replace current */
2387                 if(mode!=TEXT_FIND && txt_has_sel(text)) {
2388                         tmp= txt_sel_to_buf(text);
2389
2390                         if(strcmp(st->findstr, tmp)==0) {
2391                                 if(mode==TEXT_REPLACE) {
2392                                         txt_insert_buf(text, st->replacestr);
2393                                         if(text->curl && text->curl->format) {
2394                                                 MEM_freeN(text->curl->format);
2395                                                 text->curl->format= NULL;
2396                                         }
2397                                         text_update_cursor_moved(C);
2398                                         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2399                                 }
2400                                 else if(mode==TEXT_MARK_ALL) {
2401                                         char color[4];
2402                                         UI_GetThemeColor4ubv(TH_SHADE2, color);
2403
2404                                         if(txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
2405                                                 if(tmp) MEM_freeN(tmp), tmp=NULL;
2406                                                 break;
2407                                         }
2408
2409                                         txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
2410                                         text_update_cursor_moved(C);
2411                                         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2412                                 }
2413                         }
2414                         MEM_freeN(tmp);
2415                         tmp= NULL;
2416                 }
2417
2418                 /* Find next */
2419                 if(txt_find_string(text, st->findstr, flags & ST_FIND_WRAP)) {
2420                         text_update_cursor_moved(C);
2421                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2422                 }
2423                 else if(flags & ST_FIND_ALL) {
2424                         if(text==start) break;
2425                         if(!start) start= text;
2426                         if(text->id.next)
2427                                 text= st->text= text->id.next;
2428                         else
2429                                 text= st->text= bmain->text.first;
2430                         txt_move_toline(text, 0, 0);
2431                         text_update_cursor_moved(C);
2432                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2433                         first= 1;
2434                 }
2435                 else {
2436                         if(!found) BKE_reportf(op->reports, RPT_ERROR, "Text not found: %s", st->findstr);
2437                         break;
2438                 }
2439                 found = 1;
2440         } while(mode==TEXT_MARK_ALL);
2441
2442         return OPERATOR_FINISHED;
2443 }
2444
2445 static int find_exec(bContext *C, wmOperator *op)
2446 {
2447         return find_and_replace(C, op, TEXT_FIND);
2448 }
2449
2450 void TEXT_OT_find(wmOperatorType *ot)
2451 {
2452         /* identifiers */
2453         ot->name= "Find";
2454         ot->idname= "TEXT_OT_find";
2455         ot->description= "Find specified text";
2456         
2457         /* api callbacks */
2458         ot->exec= find_exec;
2459         ot->poll= text_space_edit_poll;
2460 }
2461
2462 /******************* replace operator *********************/
2463
2464 static int replace_exec(bContext *C, wmOperator *op)
2465 {
2466         return find_and_replace(C, op, TEXT_REPLACE);
2467 }
2468
2469 void TEXT_OT_replace(wmOperatorType *ot)
2470 {
2471         /* identifiers */
2472         ot->name= "Replace";
2473         ot->idname= "TEXT_OT_replace";
2474         ot->description= "Replace text with the specified text";
2475
2476         /* api callbacks */
2477         ot->exec= replace_exec;
2478         ot->poll= text_space_edit_poll;
2479 }
2480
2481 /******************* mark all operator *********************/
2482
2483 static int mark_all_exec(bContext *C, wmOperator *op)
2484 {
2485         return find_and_replace(C, op, TEXT_MARK_ALL);
2486 }
2487
2488 void TEXT_OT_mark_all(wmOperatorType *ot)
2489 {
2490         /* identifiers */
2491         ot->name= "Mark All";
2492         ot->idname= "TEXT_OT_mark_all";
2493         ot->description= "Mark all specified text";
2494         
2495         /* api callbacks */
2496         ot->exec= mark_all_exec;
2497         ot->poll= text_space_edit_poll;
2498 }
2499
2500 /******************* find set selected *********************/
2501
2502 static int find_set_selected_exec(bContext *C, wmOperator *op)
2503 {
2504         SpaceText *st= CTX_wm_space_text(C);
2505         Text *text= CTX_data_edit_text(C);
2506         char *tmp;
2507
2508         tmp= txt_sel_to_buf(text);
2509         BLI_strncpy(st->findstr, tmp, ST_MAX_FIND_STR);
2510         MEM_freeN(tmp);
2511
2512         if(!st->findstr[0])
2513                 return OPERATOR_FINISHED;
2514
2515         return find_and_replace(C, op, TEXT_FIND);
2516 }
2517
2518 void TEXT_OT_find_set_selected(wmOperatorType *ot)
2519 {
2520         /* identifiers */
2521         ot->name= "Find Set Selected";
2522         ot->idname= "TEXT_OT_find_set_selected";
2523         ot->description= "Find specified text and set as selected";
2524         
2525         /* api callbacks */
2526         ot->exec= find_set_selected_exec;
2527         ot->poll= text_space_edit_poll;
2528 }
2529
2530 /******************* replace set selected *********************/
2531
2532 static int replace_set_selected_exec(bContext *C, wmOperator *op)
2533 {
2534         SpaceText *st= CTX_wm_space_text(C);
2535         Text *text= CTX_data_edit_text(C);
2536         char *tmp;
2537
2538         tmp= txt_sel_to_buf(text);
2539         BLI_strncpy(st->replacestr, tmp, ST_MAX_FIND_STR);
2540         MEM_freeN(tmp);
2541
2542         return OPERATOR_FINISHED;
2543 }
2544
2545 void TEXT_OT_replace_set_selected(wmOperatorType *ot)
2546 {
2547         /* identifiers */
2548         ot->name= "Replace Set Selected";
2549         ot->idname= "TEXT_OT_replace_set_selected";
2550         ot->description= "Replace text with specified text and set as selected";
2551         
2552         /* api callbacks */
2553         ot->exec= replace_set_selected_exec;
2554         ot->poll= text_space_edit_poll;
2555 }
2556
2557 /****************** resolve conflict operator ******************/
2558
2559 enum { RESOLVE_IGNORE, RESOLVE_RELOAD, RESOLVE_SAVE, RESOLVE_MAKE_INTERNAL };
2560 static EnumPropertyItem resolution_items[]= {
2561         {RESOLVE_IGNORE, "IGNORE", 0, "Ignore", ""},
2562         {RESOLVE_RELOAD, "RELOAD", 0, "Reload", ""},
2563         {RESOLVE_SAVE, "SAVE", 0, "Save", ""},
2564         {RESOLVE_MAKE_INTERNAL, "MAKE_INTERNAL", 0, "Make Internal", ""},
2565         {0, NULL, 0, NULL, NULL}};
2566
2567 /* returns 0 if file on disk is the same or Text is in memory only
2568    returns 1 if file has been modified on disk since last local edit
2569    returns 2 if file on disk has been deleted
2570    -1 is returned if an error occurs */
2571
2572 int text_file_modified(Text *text)
2573 {
2574         struct stat st;
2575         int result;
2576         char file[FILE_MAXDIR+FILE_MAXFILE];
2577
2578         if(!text || !text->name)
2579                 return 0;
2580
2581         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2582         BLI_path_abs(file, G.sce);
2583
2584         if(!BLI_exists(file))
2585                 return 2;
2586
2587         result = stat(file, &st);
2588         
2589         if(result == -1)
2590                 return -1;
2591
2592         if((st.st_mode & S_IFMT) != S_IFREG)
2593                 return -1;
2594
2595         if(st.st_mtime > text->mtime)
2596                 return 1;
2597
2598         return 0;
2599 }
2600
2601 static void text_ignore_modified(Text *text)
2602 {
2603         struct stat st;
2604         int result;
2605         char file[FILE_MAXDIR+FILE_MAXFILE];
2606
2607         if(!text || !text->name) return;
2608
2609         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2610         BLI_path_abs(file, G.sce);
2611
2612         if(!BLI_exists(file)) return;
2613
2614         result = stat(file, &st);
2615         
2616         if(result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
2617                 return;
2618
2619         text->mtime= st.st_mtime;
2620 }
2621
2622 static int resolve_conflict_exec(bContext *C, wmOperator *op)
2623 {
2624         Text *text= CTX_data_edit_text(C);
2625         int resolution= RNA_enum_get(op->ptr, "resolution");
2626
2627         switch(resolution) {
2628                 case RESOLVE_RELOAD:
2629                         return reload_exec(C, op);
2630                 case RESOLVE_SAVE:
2631                         return save_exec(C, op);
2632                 case RESOLVE_MAKE_INTERNAL:
2633                         return make_internal_exec(C, op);
2634                 case RESOLVE_IGNORE:
2635                         text_ignore_modified(text);
2636                         return OPERATOR_FINISHED;
2637         }
2638
2639         return OPERATOR_CANCELLED;
2640 }
2641
2642 static int resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *event)
2643 {
2644         Text *text= CTX_data_edit_text(C);
2645         uiPopupMenu *pup;
2646         uiLayout *layout;
2647
2648         switch(text_file_modified(text)) {
2649                 case 1:
2650                         if(text->flags & TXT_ISDIRTY) {
2651                                 /* modified locally and externally, ahhh. offer more possibilites. */
2652                                 pup= uiPupMenuBegin(C, "File Modified Outside and Inside Blender", 0);
2653                                 layout= uiPupMenuLayout(pup);
2654                                 uiItemEnumO(layout, op->type->idname, "Reload from disk (ignore local changes)", 0, "resolution", RESOLVE_RELOAD);
2655                                 uiItemEnumO(layout, op->type->idname, "Save to disk (ignore outside changes)", 0, "resolution", RESOLVE_SAVE);
2656                                 uiItemEnumO(layout, op->type->idname, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
2657                                 uiPupMenuEnd(C, pup);
2658                         }
2659                         else {
2660                                 pup= uiPupMenuBegin(C, "File Modified Outside Blender", 0);
2661                                 layout= uiPupMenuLayout(pup);
2662                                 uiItemEnumO(layout, op->type->idname, "Reload from disk", 0, "resolution", RESOLVE_RELOAD);
2663                                 uiItemEnumO(layout, op->type->idname, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
2664                                 uiItemEnumO(layout, op->type->idname, "Ignore", 0, "resolution", RESOLVE_IGNORE);
2665                                 uiPupMenuEnd(C, pup);
2666                         }
2667                         break;
2668                 case 2:
2669                         pup= uiPupMenuBegin(C, "File Deleted Outside Blender", 0);
2670                         layout= uiPupMenuLayout(pup);
2671                         uiItemEnumO(layout, op->type->idname, "Make text internal", 0, "resolution", RESOLVE_MAKE_INTERNAL);
2672                         uiItemEnumO(layout, op->type->idname, "Recreate file", 0, "resolution", RESOLVE_SAVE);
2673                         uiPupMenuEnd(C, pup);
2674                         break;
2675         }
2676
2677         return OPERATOR_CANCELLED;
2678 }
2679
2680 void TEXT_OT_resolve_conflict(wmOperatorType *ot)
2681 {
2682         /* identifiers */
2683         ot->name= "Resolve Conflict";
2684         ot->idname= "TEXT_OT_resolve_conflict";
2685         ot->description= "When external text is out of sync, resolve the conflict";
2686
2687         /* api callbacks */
2688         ot->exec= resolve_conflict_exec;
2689         ot->invoke= resolve_conflict_invoke;
2690         ot->poll= save_poll;
2691
2692         /* properties */
2693         RNA_def_enum(ot->srna, "resolution", resolution_items, RESOLVE_IGNORE, "Resolution", "How to solve conflict due to different in internal and external text.");
2694 }
2695
2696 /********************** to 3d object operator *****************/
2697
2698 static int to_3d_object_exec(bContext *C, wmOperator *op)
2699 {
2700         Text *text= CTX_data_edit_text(C);
2701         int split_lines= RNA_boolean_get(op->ptr, "split_lines");
2702
2703         ED_text_to_object(C, text, split_lines);
2704
2705         return OPERATOR_FINISHED;
2706 }
2707
2708 void TEXT_OT_to_3d_object(wmOperatorType *ot)
2709 {
2710         /* identifiers */
2711         ot->name= "To 3D Object";
2712         ot->idname= "TEXT_OT_to_3d_object";
2713         ot->description= "Create 3d text object from active text data block";
2714         
2715         /* api callbacks */
2716         ot->exec= to_3d_object_exec;
2717         ot->poll= text_edit_poll;
2718         
2719         /* flags */
2720         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
2721
2722         /* properties */
2723         RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text.");
2724 }
2725
2726
2727 /************************ undo ******************************/
2728
2729 void ED_text_undo_step(bContext *C, int step)
2730 {
2731         Text *text= CTX_data_edit_text(C);
2732
2733         if(!text)
2734                 return;
2735
2736         if(step==1)
2737                 txt_do_undo(text);
2738         else if(step==-1)
2739                 txt_do_redo(text);
2740
2741         text_update_edited(text);
2742
2743         text_update_cursor_moved(C);
2744         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2745 }
2746