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