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