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