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