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