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