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