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