remove assignments which are unused.
[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_text_free_code(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_text_exec(C, 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, max, chop, curs, loop, endj, found, selc;
1348         char ch;
1349
1350         max= wrap_width(st, ar);
1351
1352         selc= start= endj= curs= found= 0;
1353         end= max;
1354         chop= loop= 1;
1355
1356         for(i=0, j=0; loop; j++) {
1357                 int chars;
1358                 /* Mimic replacement of tabs */
1359                 ch= linein->line[j];
1360                 if(ch=='\t') {
1361                         chars= st->tabnumber-i%st->tabnumber;
1362                         ch= ' ';
1363                 }
1364                 else chars= 1;
1365
1366                 while(chars--) {
1367                         if(rell==0 && i-start==relc) {
1368                                 /* current position could be wrapped to next line */
1369                                 /* this should be checked when end of current line would be reached */
1370                                 selc= j;
1371                                 found= 1;
1372                         }
1373                         else if(i-end==relc) {
1374                                 curs= j;
1375                         }
1376                         if(i-start>=max) {
1377                                 if(found) {
1378                                         /* exact cursor position was found, check if it's */
1379                                         /* still on needed line (hasn't been wrapped) */
1380                                         if(selc>endj && !chop) selc= endj;
1381                                         loop= 0;
1382                                         break;
1383                                 }
1384
1385                                 if(chop) endj= j;
1386
1387                                 start= end;
1388                                 end += max;
1389                                 chop= 1;
1390                                 rell--;
1391
1392                                 if(rell==0 && i-start>=relc) {
1393                                         selc= curs;
1394                                         loop= 0;
1395                                         break;
1396                                 }
1397                         }
1398                         else if (ch=='\0') {
1399                                 if(!found) selc= linein->len;
1400                                 loop= 0;
1401                                 break;
1402                         }
1403                         else if(ch==' ' || ch=='-') {
1404                                 if(found) {
1405                                         loop= 0;
1406                                         break;
1407                                 }
1408
1409                                 if(rell==0 && i-start>=relc) {
1410                                         selc= curs;
1411                                         loop= 0;
1412                                         break;
1413                                 }
1414                                 end= i+1;
1415                                 endj= j;
1416                                 chop= 0;
1417                         }
1418                         i++;
1419                 }
1420         }
1421
1422   return selc;
1423 }
1424
1425 static int cursor_skip_find_line(SpaceText* st, ARegion *ar,
1426         int lines, TextLine **linep, int *charp, int *rell, int *relc)
1427 {
1428         int offl, offc, visible_lines;
1429
1430         wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
1431         *relc= text_get_char_pos(st, (*linep)->line, *charp) + offc;
1432         *rell= lines;
1433
1434         /* handle current line */
1435         if(lines>0) {
1436                 visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
1437
1438                 if(*rell-visible_lines+offl>=0) {
1439                         if(!(*linep)->next) {
1440                                 if(offl < visible_lines-1) {
1441                                         *rell= visible_lines-1;
1442                                         return 1;
1443                                 }
1444
1445                                 *charp= (*linep)->len;
1446                                 return 0;
1447                         }
1448
1449                         *rell-= visible_lines-offl;
1450                         *linep=(*linep)->next;
1451                 } else {
1452                         *rell+= offl;
1453                         return 1;
1454                 }
1455         } else {
1456                 if(*rell+offl<=0) {
1457                         if(!(*linep)->prev) {
1458                                 if(offl) {
1459                                         *rell= 0;
1460                                         return 1;
1461                                 }
1462
1463                                 *charp= 0;
1464                                 return 0;
1465                         }
1466
1467                         *rell+= offl;
1468                         *linep=(*linep)->prev;
1469                 } else {
1470                         *rell+= offl;
1471                         return 1;
1472                 }
1473         }
1474
1475         /* skip lines and find destination line and offsets */
1476         while(*linep) {
1477                 visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
1478
1479                 if(lines<0) { /* moving top */
1480                         if(*rell+visible_lines >= 0) {
1481                                 *rell+= visible_lines;
1482                                 break;
1483                         }
1484
1485                         if(!(*linep)->prev) {
1486                                 *rell= 0;
1487                                 break;
1488                         }
1489
1490                         *rell+= visible_lines;
1491                         *linep=(*linep)->prev;
1492                 } else { /* moving bottom */
1493                         if(*rell-visible_lines < 0) break;
1494
1495                         if(!(*linep)->next) {
1496                                 *rell= visible_lines-1;
1497                                 break;
1498                         }
1499
1500                         *rell-= visible_lines;
1501                         *linep=(*linep)->next;
1502                 }
1503         }
1504
1505         return 1;
1506 }
1507
1508 static void wrap_move_bol(SpaceText *st, ARegion *ar, short sel)
1509 {
1510         Text *text= st->text;
1511         TextLine **linep;
1512         int *charp;
1513         int oldl, oldc, i, j, max, start, end, endj, chop, loop;
1514         char ch;
1515
1516         text_update_character_width(st);
1517
1518         if (sel) linep= &text->sell, charp= &text->selc;
1519         else linep= &text->curl, charp= &text->curc;
1520
1521         oldc= *charp;
1522         oldl= txt_get_span(text->lines.first, *linep);
1523
1524         max= wrap_width(st, ar);
1525
1526         start= endj= 0;
1527         end= max;
1528         chop= loop= 1;
1529         *charp= 0;
1530
1531         for(i=0, j=0; loop; j++) {
1532                 int chars;
1533                 /* Mimic replacement of tabs */
1534                 ch= (*linep)->line[j];
1535                 if(ch=='\t') {
1536                         chars= st->tabnumber-i%st->tabnumber;
1537                         ch= ' ';
1538                 }
1539                 else chars= 1;
1540
1541                 while(chars--) {
1542                         if(i-start>=max) {
1543                                 *charp= endj;
1544
1545                                 if(j>=oldc) {
1546                                         if(ch=='\0') *charp= start;
1547                                         loop= 0;
1548                                         break;
1549                                 }
1550
1551                                 if(chop) endj= j;
1552
1553                                 start= end;
1554                                 end += max;
1555                                 chop= 1;
1556                         }
1557                         else if(ch==' ' || ch=='-' || ch=='\0') {
1558                                 if(j>=oldc) {
1559                                         *charp= start;
1560                                         loop= 0;
1561                                         break;
1562                                 }
1563
1564                                 end= i+1;
1565                                 endj= j+1;
1566                                 chop= 0;
1567                         }
1568                         i++;
1569                 }
1570         }
1571
1572         if (!sel) txt_pop_sel(text);
1573         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, oldl, *charp);
1574 }
1575
1576 static void wrap_move_eol(SpaceText *st, ARegion *ar, short sel)
1577 {
1578         Text *text= st->text;
1579         TextLine **linep;
1580         int *charp;
1581         int oldl, oldc, i, j, max, start, end, endj, chop, loop;
1582         char ch;
1583
1584         text_update_character_width(st);
1585
1586         if (sel) linep= &text->sell, charp= &text->selc;
1587         else linep= &text->curl, charp= &text->curc;
1588
1589         oldc= *charp;
1590         oldl= txt_get_span(text->lines.first, *linep);
1591
1592         max= wrap_width(st, ar);
1593
1594         start= endj= 0;
1595         end= max;
1596         chop= loop= 1;
1597         *charp= 0;
1598
1599         for(i=0, j=0; loop; j++) {
1600                 int chars;
1601                 /* Mimic replacement of tabs */
1602                 ch= (*linep)->line[j];
1603                 if(ch=='\t') {
1604                         chars= st->tabnumber-i%st->tabnumber;
1605                         ch= ' ';
1606                 }
1607                 else chars= 1;
1608
1609                 while(chars--) {
1610                         if(i-start>=max) {
1611                                 if(chop) endj= j-1;
1612
1613                                 if(endj>=oldc) {
1614                                         if(ch=='\0') *charp= (*linep)->len;
1615                                         else *charp= endj;
1616                                         loop= 0;
1617                                         break;
1618                                 }
1619
1620                                 start= end;
1621                                 end += max;
1622                                 chop= 1;
1623                         } else if(ch=='\0') {
1624                                 *charp= (*linep)->len;
1625                                 loop= 0;
1626                                 break;
1627                         } else if(ch==' ' || ch=='-') {
1628                                 end= i+1;
1629                                 endj= j;
1630                                 chop= 0;
1631                         }
1632                         i++;
1633                 }
1634         }
1635
1636         if (!sel) txt_pop_sel(text);
1637         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, oldl, *charp);
1638 }
1639
1640 static void wrap_move_up(SpaceText *st, ARegion *ar, short sel)
1641 {
1642         Text *text= st->text;
1643         TextLine **linep;
1644         int *charp;
1645         int oldl, oldc, offl, offc, col, newl;
1646
1647         text_update_character_width(st);
1648
1649         if (sel) linep= &text->sell, charp= &text->selc;
1650         else linep= &text->curl, charp= &text->curc;
1651
1652         /* store previous position */
1653         oldc= *charp;
1654         newl= oldl= txt_get_span(text->lines.first, *linep);
1655
1656         wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
1657         col= text_get_char_pos(st, (*linep)->line, *charp) + offc;
1658         if(offl) {
1659                 *charp= text_get_cursor_rel(st, ar, *linep, offl-1, col);
1660         } else {
1661                 if((*linep)->prev) {
1662                         int visible_lines;
1663
1664                         *linep= (*linep)->prev;
1665                         visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
1666                         *charp= text_get_cursor_rel(st, ar, *linep, visible_lines-1, col);
1667                 } else *charp= 0;
1668         }
1669
1670         if (!sel) txt_pop_sel(text);
1671         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, newl, *charp);
1672 }
1673
1674 static void wrap_move_down(SpaceText *st, ARegion *ar, short sel)
1675 {
1676         Text *text= st->text;
1677         TextLine **linep;
1678         int *charp;
1679         int oldl, oldc, offl, offc, col, newl, visible_lines;
1680
1681         text_update_character_width(st);
1682
1683         if (sel) linep= &text->sell, charp= &text->selc;
1684         else linep= &text->curl, charp= &text->curc;
1685
1686         /* store previous position */
1687         oldc= *charp;
1688         newl= oldl= txt_get_span(text->lines.first, *linep);
1689
1690         wrap_offset_in_line(st, ar, *linep, *charp, &offl, &offc);
1691         col= text_get_char_pos(st, (*linep)->line, *charp) + offc;
1692         visible_lines= text_get_visible_lines(st, ar, (*linep)->line);
1693         if(offl<visible_lines-1) {
1694                 *charp= text_get_cursor_rel(st, ar, *linep, offl+1, col);
1695         } else {
1696                 if((*linep)->next) {
1697                         *linep= (*linep)->next;
1698                         *charp= text_get_cursor_rel(st, ar, *linep, 0, col);
1699                 } else *charp= (*linep)->len;
1700         }
1701
1702         if (!sel) txt_pop_sel(text);
1703         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, newl, *charp);
1704 }
1705
1706 /* Moves the cursor vertically by the specified number of lines.
1707  If the destination line is shorter than the current cursor position, the
1708  cursor will be positioned at the end of this line.
1709
1710  This is to replace screen_skip for PageUp/Down operations.
1711  */
1712 static void cursor_skip(SpaceText* st, ARegion *ar, Text *text, int lines, int sel)
1713 {
1714         TextLine **linep;
1715         int oldl, oldc, *charp;
1716         
1717         if (sel) linep= &text->sell, charp= &text->selc;
1718         else linep= &text->curl, charp= &text->curc;
1719         oldl= txt_get_span(text->lines.first, *linep);
1720         oldc= *charp;
1721
1722         if(st && ar && st->wordwrap) {
1723                 int rell, relc;
1724
1725                 /* find line and offsets inside it needed to set cursor position */
1726                 if(cursor_skip_find_line(st, ar, lines, linep, charp, &rell, &relc))
1727                   *charp= text_get_cursor_rel (st, ar, *linep, rell, relc);
1728         } else {
1729                 while (lines>0 && (*linep)->next) {
1730                         *linep= (*linep)->next;
1731                         lines--;
1732                 }
1733                 while (lines<0 && (*linep)->prev) {
1734                         *linep= (*linep)->prev;
1735                         lines++;
1736                 }
1737         }
1738
1739         if (*charp > (*linep)->len) *charp= (*linep)->len;
1740
1741         if (!sel) txt_pop_sel(text);
1742         txt_undo_add_toop(text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
1743 }
1744
1745 static int move_cursor(bContext *C, int type, int select)
1746 {
1747         SpaceText *st= CTX_wm_space_text(C);
1748         Text *text= CTX_data_edit_text(C);
1749         ARegion *ar= CTX_wm_region(C);
1750
1751         /* ensure we have the right region, it's optional */
1752         if(ar && ar->regiontype != RGN_TYPE_WINDOW)
1753                 ar= NULL;
1754
1755         switch(type) {
1756                 case LINE_BEGIN:
1757                         if(st && st->wordwrap && ar) wrap_move_bol(st, ar, select);
1758                         else txt_move_bol(text, select);
1759                         break;
1760                         
1761                 case LINE_END:
1762                         if(st && st->wordwrap && ar) wrap_move_eol(st, ar, select);
1763                         else txt_move_eol(text, select);
1764                         break;
1765
1766                 case FILE_TOP:
1767                         txt_move_bof(text, select);
1768                         break;
1769                         
1770                 case FILE_BOTTOM:
1771                         txt_move_eof(text, select);
1772                         break;
1773
1774                 case PREV_WORD:
1775                         txt_jump_left(text, select);
1776                         break;
1777
1778                 case NEXT_WORD:
1779                         txt_jump_right(text, select);
1780                         break;
1781
1782                 case PREV_CHAR:
1783                         txt_move_left(text, select);
1784                         break;
1785
1786                 case NEXT_CHAR: 
1787                         txt_move_right(text, select);
1788                         break;
1789
1790                 case PREV_LINE:
1791                         if(st && st->wordwrap && ar) wrap_move_up(st, ar, select);
1792                         else txt_move_up(text, select);
1793                         break;
1794                         
1795                 case NEXT_LINE:
1796                         if(st && st->wordwrap && ar) wrap_move_down(st, ar, select);
1797                         else txt_move_down(text, select);
1798                         break;
1799
1800                 case PREV_PAGE:
1801                         if(st) cursor_skip(st, ar, st->text, -st->viewlines, select);
1802                         else cursor_skip(NULL, NULL, text, -10, select);
1803                         break;
1804
1805                 case NEXT_PAGE:
1806                         if(st) cursor_skip(st, ar, st->text, st->viewlines, select);
1807                         else cursor_skip(NULL, NULL, text, 10, select);
1808                         break;
1809         }
1810
1811         text_update_cursor_moved(C);
1812         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
1813
1814         return OPERATOR_FINISHED;
1815 }
1816
1817 static int move_exec(bContext *C, wmOperator *op)
1818 {
1819         int type= RNA_enum_get(op->ptr, "type");
1820
1821         return move_cursor(C, type, 0);
1822 }
1823
1824 void TEXT_OT_move(wmOperatorType *ot)
1825 {
1826         /* identifiers */
1827         ot->name= "Move Cursor";
1828         ot->idname= "TEXT_OT_move";
1829         ot->description= "Move cursor to position type";
1830         
1831         /* api callbacks */
1832         ot->exec= move_exec;
1833         ot->poll= text_edit_poll;
1834
1835         /* properties */
1836         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to.");
1837 }
1838
1839 /******************* move select operator ********************/
1840
1841 static int move_select_exec(bContext *C, wmOperator *op)
1842 {
1843         int type= RNA_enum_get(op->ptr, "type");
1844
1845         return move_cursor(C, type, 1);
1846 }
1847
1848 void TEXT_OT_move_select(wmOperatorType *ot)
1849 {
1850         /* identifiers */
1851         ot->name= "Move Select";
1852         ot->idname= "TEXT_OT_move_select";
1853         ot->description= "Make selection from current cursor position to new cursor position type";
1854         
1855         /* api callbacks */
1856         ot->exec= move_select_exec;
1857         ot->poll= text_space_edit_poll;
1858
1859         /* properties */
1860         RNA_def_enum(ot->srna, "type", move_type_items, LINE_BEGIN, "Type", "Where to move cursor to, to make a selection.");
1861 }
1862
1863 /******************* jump operator *********************/
1864
1865 static int jump_exec(bContext *C, wmOperator *op)
1866 {
1867         Text *text= CTX_data_edit_text(C);
1868         int line= RNA_int_get(op->ptr, "line");
1869         short nlines= txt_get_span(text->lines.first, text->lines.last)+1;
1870
1871         if(line < 1)
1872                 txt_move_toline(text, 1, 0);
1873         else if(line > nlines)
1874                 txt_move_toline(text, nlines-1, 0);
1875         else
1876                 txt_move_toline(text, line-1, 0);
1877
1878         text_update_cursor_moved(C);
1879         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
1880
1881         return OPERATOR_FINISHED;
1882 }
1883
1884 static int jump_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
1885 {
1886         return WM_operator_props_dialog_popup(C,op,200,100);
1887
1888 }
1889
1890 void TEXT_OT_jump(wmOperatorType *ot)
1891 {
1892         /* identifiers */
1893         ot->name= "Jump";
1894         ot->idname= "TEXT_OT_jump";
1895         ot->description= "Jump cursor to line";
1896         
1897         /* api callbacks */
1898         ot->invoke= jump_invoke;
1899         ot->exec= jump_exec;
1900         ot->poll= text_edit_poll;
1901
1902         /* properties */
1903         RNA_def_int(ot->srna, "line", 1, 1, INT_MAX, "Line", "Line number to jump to.", 1, 10000);
1904 }
1905
1906 /******************* delete operator **********************/
1907
1908 static EnumPropertyItem delete_type_items[]= {
1909         {DEL_NEXT_CHAR, "NEXT_CHARACTER", 0, "Next Character", ""},
1910         {DEL_PREV_CHAR, "PREVIOUS_CHARACTER", 0, "Previous Character", ""},
1911         {DEL_NEXT_WORD, "NEXT_WORD", 0, "Next Word", ""},
1912         {DEL_PREV_WORD, "PREVIOUS_WORD", 0, "Previous Word", ""},
1913         {0, NULL, 0, NULL, NULL}};
1914
1915 static int delete_exec(bContext *C, wmOperator *op)
1916 {
1917         Text *text= CTX_data_edit_text(C);
1918         int type= RNA_enum_get(op->ptr, "type");
1919
1920         text_drawcache_tag_update(CTX_wm_space_text(C), 0);
1921
1922         if(type == DEL_PREV_WORD)
1923                 txt_backspace_word(text);
1924         else if(type == DEL_PREV_CHAR)
1925                 txt_backspace_char(text);
1926         else if(type == DEL_NEXT_WORD)
1927                 txt_delete_word(text);
1928         else if(type == DEL_NEXT_CHAR)
1929                 txt_delete_char(text);
1930
1931         text_update_line_edited(text->curl);
1932
1933         text_update_cursor_moved(C);
1934         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
1935
1936         /* run the script while editing, evil but useful */
1937         if(CTX_wm_space_text(C)->live_edit)
1938                 run_script_exec(C, op);
1939         
1940         return OPERATOR_FINISHED;
1941 }
1942
1943 void TEXT_OT_delete(wmOperatorType *ot)
1944 {
1945         /* identifiers */
1946         ot->name= "Delete";
1947         ot->idname= "TEXT_OT_delete";
1948         ot->description= "Delete text by cursor position";
1949         
1950         /* api callbacks */
1951         ot->exec= delete_exec;
1952         ot->poll= text_edit_poll;
1953
1954         /* properties */
1955         RNA_def_enum(ot->srna, "type", delete_type_items, DEL_NEXT_CHAR, "Type", "Which part of the text to delete.");
1956 }
1957
1958 /******************* toggle overwrite operator **********************/
1959
1960 static int toggle_overwrite_exec(bContext *C, wmOperator *UNUSED(op))
1961 {
1962         SpaceText *st= CTX_wm_space_text(C);
1963
1964         st->overwrite= !st->overwrite;
1965
1966         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
1967
1968         return OPERATOR_FINISHED;
1969 }
1970
1971 void TEXT_OT_overwrite_toggle(wmOperatorType *ot)
1972 {
1973         /* identifiers */
1974         ot->name= "Toggle Overwrite";
1975         ot->idname= "TEXT_OT_overwrite_toggle";
1976         ot->description= "Toggle overwrite while typing";
1977         
1978         /* api callbacks */
1979         ot->exec= toggle_overwrite_exec;
1980         ot->poll= text_space_edit_poll;
1981 }
1982
1983 /******************* scroll operator **********************/
1984
1985 /* Moves the view vertically by the specified number of lines */
1986 static void screen_skip(SpaceText *st, ARegion *ar, int lines)
1987 {
1988         int last;
1989
1990         st->top += lines;
1991
1992         last= text_get_total_lines(st, ar);
1993         last= last - (st->viewlines/2);
1994         
1995         if(st->top>last) st->top= last;
1996         if(st->top<0) st->top= 0;
1997 }
1998
1999 typedef struct TextScroll {
2000         short old[2];
2001         short hold[2];
2002         short delta[2];
2003
2004         int first;
2005         int characters;
2006         int lines;
2007         int scrollbar;
2008 } TextScroll;
2009
2010 static int scroll_exec(bContext *C, wmOperator *op)
2011 {
2012         SpaceText *st= CTX_wm_space_text(C);
2013         ARegion *ar= CTX_wm_region(C);
2014
2015         int lines= RNA_int_get(op->ptr, "lines");
2016
2017         if(lines == 0)
2018                 return OPERATOR_CANCELLED;
2019
2020         screen_skip(st, ar, lines*U.wheellinescroll);
2021
2022         ED_area_tag_redraw(CTX_wm_area(C));
2023
2024         return OPERATOR_FINISHED;
2025 }
2026
2027 static void scroll_apply(bContext *C, wmOperator *op, wmEvent *event)
2028 {
2029         SpaceText *st= CTX_wm_space_text(C);
2030         ARegion *ar= CTX_wm_region(C);
2031         TextScroll *tsc= op->customdata;
2032         short *mval= event->mval;
2033
2034         text_update_character_width(st);
2035
2036         if(tsc->first) {
2037                 tsc->old[0]= mval[0];
2038                 tsc->old[1]= mval[1];
2039                 tsc->hold[0]= mval[0];
2040                 tsc->hold[1]= mval[1];
2041                 tsc->first= 0;
2042         }
2043
2044         if(!tsc->scrollbar) {
2045                 tsc->delta[0]= (tsc->hold[0]-mval[0])/st->cwidth;
2046                 tsc->delta[1]= (mval[1]-tsc->hold[1])/st->lheight;
2047         }
2048         else
2049                 tsc->delta[1]= (tsc->hold[1]-mval[1])*st->pix_per_line;
2050         
2051         if(tsc->delta[0] || tsc->delta[1]) {
2052                 screen_skip(st, ar, tsc->delta[1]);
2053
2054                 tsc->lines += tsc->delta[1];
2055
2056                 if(st->wordwrap) {
2057                         st->left= 0;
2058                 }
2059                 else {
2060                         st->left+= tsc->delta[0];
2061                         if(st->left<0) st->left= 0;
2062                 }
2063                 
2064                 tsc->hold[0]= mval[0];
2065                 tsc->hold[1]= mval[1];
2066
2067                 ED_area_tag_redraw(CTX_wm_area(C));
2068         }
2069
2070         tsc->old[0]= mval[0];
2071         tsc->old[1]= mval[1];
2072 }
2073
2074 static void scroll_exit(bContext *C, wmOperator *op)
2075 {
2076         SpaceText *st= CTX_wm_space_text(C);
2077
2078         st->flags &= ~ST_SCROLL_SELECT;
2079         MEM_freeN(op->customdata);
2080 }
2081
2082 static int scroll_modal(bContext *C, wmOperator *op, wmEvent *event)
2083 {
2084         switch(event->type) {
2085                 case MOUSEMOVE:
2086                         scroll_apply(C, op, event);
2087                         break;
2088                 case LEFTMOUSE:
2089                 case RIGHTMOUSE:
2090                 case MIDDLEMOUSE:
2091                         scroll_exit(C, op);
2092                         return OPERATOR_FINISHED;
2093         }
2094
2095         return OPERATOR_RUNNING_MODAL;
2096 }
2097
2098 static int scroll_cancel(bContext *C, wmOperator *op)
2099 {
2100         scroll_exit(C, op);
2101
2102         return OPERATOR_CANCELLED;
2103 }
2104
2105 static int scroll_invoke(bContext *C, wmOperator *op, wmEvent *event)
2106 {
2107         SpaceText *st= CTX_wm_space_text(C);
2108         TextScroll *tsc;
2109         
2110         if(RNA_property_is_set(op->ptr, "lines"))
2111                 return scroll_exec(C, op);
2112         
2113         tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
2114         tsc->first= 1;
2115         op->customdata= tsc;
2116         
2117         st->flags|= ST_SCROLL_SELECT;
2118         
2119         if (event->type == MOUSEPAN) {
2120                 text_update_character_width(st);
2121                 
2122                 tsc->hold[0] = event->prevx;
2123                 tsc->hold[1] = event->prevy;
2124                 /* Sensitivity of scroll set to 4pix per line/char */
2125                 event->mval[0] = event->prevx + (event->x - event->prevx)*st->cwidth/4;
2126                 event->mval[1] = event->prevy + (event->y - event->prevy)*st->lheight/4;
2127                 tsc->first = 0;
2128                 tsc->scrollbar = 0;
2129                 scroll_apply(C, op, event);
2130                 scroll_exit(C, op);
2131                 return OPERATOR_FINISHED;
2132         }       
2133         
2134         WM_event_add_modal_handler(C, op);
2135         
2136         return OPERATOR_RUNNING_MODAL;
2137 }
2138
2139 void TEXT_OT_scroll(wmOperatorType *ot)
2140 {
2141         /* identifiers */
2142         ot->name= "Scroll";
2143         /*don't really see the difference between this and
2144           scroll_bar. Both do basically the same thing (aside 
2145           from keymaps).*/
2146         ot->idname= "TEXT_OT_scroll";
2147         ot->description= "Scroll text screen";
2148         
2149         /* api callbacks */
2150         ot->exec= scroll_exec;
2151         ot->invoke= scroll_invoke;
2152         ot->modal= scroll_modal;
2153         ot->cancel= scroll_cancel;
2154         ot->poll= text_space_edit_poll;
2155
2156         /* flags */
2157         ot->flag= OPTYPE_BLOCKING;
2158
2159         /* properties */
2160         RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
2161 }
2162
2163 /******************** scroll bar operator *******************/
2164
2165 static int scroll_bar_invoke(bContext *C, wmOperator *op, wmEvent *event)
2166 {
2167         SpaceText *st= CTX_wm_space_text(C);
2168         ARegion *ar= CTX_wm_region(C);
2169         TextScroll *tsc;
2170         short *mval= event->mval;
2171
2172         if(RNA_property_is_set(op->ptr, "lines"))
2173                 return scroll_exec(C, op);
2174         
2175         /* verify we are in the right zone */
2176         if(!(mval[0]>ar->winx-TXT_SCROLL_WIDTH && mval[0]<ar->winx-TXT_SCROLL_SPACE
2177                 && mval[1]>TXT_SCROLL_SPACE && mval[1]<ar->winy))
2178                 return OPERATOR_PASS_THROUGH;
2179
2180         tsc= MEM_callocN(sizeof(TextScroll), "TextScroll");
2181         tsc->first= 1;
2182         tsc->scrollbar= 1;
2183         op->customdata= tsc;
2184         
2185         st->flags|= ST_SCROLL_SELECT;
2186
2187         WM_event_add_modal_handler(C, op);
2188
2189         return OPERATOR_RUNNING_MODAL;
2190 }
2191
2192 void TEXT_OT_scroll_bar(wmOperatorType *ot)
2193 {
2194         /* identifiers */
2195         ot->name= "Scrollbar";
2196         /*don't really see the difference between this and
2197           scroll. Both do basically the same thing (aside 
2198           from keymaps).*/
2199         ot->idname= "TEXT_OT_scroll_bar";
2200         ot->description= "Scroll text screen";
2201         
2202         /* api callbacks */
2203         ot->invoke= scroll_bar_invoke;
2204         ot->modal= scroll_modal;
2205         ot->cancel= scroll_cancel;
2206         ot->poll= text_region_edit_poll;
2207
2208         /* flags */
2209         ot->flag= OPTYPE_BLOCKING;
2210
2211         /* properties */
2212         RNA_def_int(ot->srna, "lines", 1, INT_MIN, INT_MAX, "Lines", "Number of lines to scroll.", -100, 100);
2213 }
2214
2215 /******************* set cursor operator **********************/
2216
2217 typedef struct SetCursor {
2218         int selecting;
2219         int selc, sell;
2220         short old[2];
2221 } SetCursor;
2222
2223 static void set_cursor_to_pos(SpaceText *st, ARegion *ar, int x, int y, int sel) 
2224 {
2225         FlattenString fs;
2226         Text *text= st->text;
2227         TextLine **linep;
2228         int *charp;
2229         int w;
2230
2231         text_update_character_width(st);
2232
2233         if(sel) { linep= &text->sell; charp= &text->selc; } 
2234         else { linep= &text->curl; charp= &text->curc; }
2235         
2236         y= (ar->winy - 2 - y)/st->lheight;
2237
2238         if(st->showlinenrs)
2239                 x-= TXT_OFFSET+TEXTXLOC;
2240         else
2241                 x-= TXT_OFFSET;
2242
2243         if(x<0) x= 0;
2244         x = (x/st->cwidth) + st->left;
2245         
2246         if(st->wordwrap) {
2247                 int i, j, endj, curs, max, chop, start, end, loop, found;
2248                 char ch;
2249
2250                 /* Point to first visible line */
2251                 *linep= text->lines.first;
2252                 i= st->top;
2253                 while(i>0 && *linep) {
2254                         int lines= text_get_visible_lines(st, ar, (*linep)->line);
2255
2256                         if (i-lines<0) {
2257                                 y+= i;
2258                                 break;
2259                         } else {
2260                                 *linep= (*linep)->next;
2261                                 i-= lines;
2262                         }
2263                 }
2264
2265                 max= wrap_width(st, ar);
2266
2267                 loop= 1;
2268                 found= 0;
2269                 while(loop && *linep) {
2270                         start= 0;
2271                         end= max;
2272                         chop= 1;
2273                         curs= 0;
2274                         endj= 0;
2275                         for(i=0, j=0; loop; j++) {
2276                                 int chars;
2277
2278                                 /* Mimic replacement of tabs */
2279                                 ch= (*linep)->line[j];
2280                                 if(ch=='\t') {
2281                                         chars= st->tabnumber-i%st->tabnumber;
2282                                         ch= ' ';
2283                                 }
2284                                 else
2285                                         chars= 1;
2286
2287                                 while(chars--) {
2288                                         /* Gone too far, go back to last wrap point */
2289                                         if(y<0) {
2290                                                 *charp= endj;
2291                                                 loop= 0;
2292                                                 break;
2293                                         /* Exactly at the cursor */
2294                                         }
2295                                         else if(y==0 && i-start==x) {
2296                                                 /* current position could be wrapped to next line */
2297                                                 /* this should be checked when end of current line would be reached */
2298                                                 *charp= curs= j;
2299                                                 found= 1;
2300                                         /* Prepare curs for next wrap */
2301                                         }
2302                                         else if(i-end==x) {
2303                                                 curs= j;
2304                                         }
2305                                         if(i-start>=max) {
2306                                                 if(found) {
2307                                                         /* exact cursor position was found, check if it's */
2308                                                         /* still on needed line (hasn't been wrapped) */
2309                                                         if(*charp>endj && !chop && ch!='\0') (*charp)= endj;
2310                                                         loop= 0;
2311                                                         break;
2312                                                 }
2313
2314                                                 if(chop) endj= j;
2315                                                 start= end;
2316                                                 end += max;
2317
2318                                                 if(j<(*linep)->len)
2319                                                         y--;
2320
2321                                                 chop= 1;
2322                                                 if(y==0 && i-start>=x) {
2323                                                         *charp= curs;
2324                                                         loop= 0;
2325                                                         break;
2326                                                 }
2327                                         }
2328                                         else if(ch==' ' || ch=='-' || ch=='\0') {
2329                                                 if(found) {
2330                                                         loop= 0;
2331                                                         break;
2332                                                 }
2333
2334                                                 if(y==0 && i-start>=x) {
2335                                                         *charp= curs;
2336                                                         loop= 0;
2337                                                         break;
2338                                                 }
2339                                                 end = i+1;
2340                                                 endj = j;
2341                                                 chop= 0;
2342                                         }
2343                                         i++;
2344                                 }
2345                                 if(ch=='\0') break;
2346                         }
2347                         if(!loop || found) break;
2348
2349                         if(!(*linep)->next) {
2350                                 *charp= (*linep)->len;
2351                                 break;
2352                         }
2353
2354                         /* On correct line but didn't meet cursor, must be at end */
2355                         if(y==0) {
2356                                 *charp= (*linep)->len;
2357                                 break;
2358                         }
2359                         *linep= (*linep)->next;
2360                         y--;
2361                 }
2362
2363         }
2364         else {
2365                 y-= txt_get_span(text->lines.first, *linep) - st->top;
2366                 
2367                 if(y>0) {
2368                         while(y-- != 0) if((*linep)->next) *linep= (*linep)->next;
2369                 }
2370                 else if(y<0) {
2371                         while(y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
2372                 }
2373
2374                 
2375                 w= flatten_string(st, &fs, (*linep)->line);
2376                 if(x<w) *charp= fs.accum[x];
2377                 else *charp= (*linep)->len;
2378                 flatten_string_free(&fs);
2379         }
2380         if(!sel) txt_pop_sel(text);
2381 }
2382
2383 static void set_cursor_apply(bContext *C, wmOperator *op, wmEvent *event)
2384 {
2385         SpaceText *st= CTX_wm_space_text(C);
2386         ARegion *ar= CTX_wm_region(C);
2387         SetCursor *scu= op->customdata;
2388
2389         if(event->mval[1]<0 || event->mval[1]>ar->winy) {
2390                 int d= (scu->old[1]-event->mval[1])*st->pix_per_line;
2391                 if(d) screen_skip(st, ar, d);
2392
2393                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1]<0?0:ar->winy, 1);
2394
2395                 text_update_cursor_moved(C);
2396                 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2397         } 
2398         else if(!st->wordwrap && (event->mval[0]<0 || event->mval[0]>ar->winx)) {
2399                 if(event->mval[0]>ar->winx) st->left++;
2400                 else if(event->mval[0]<0 && st->left>0) st->left--;
2401                 
2402                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
2403                 
2404                 text_update_cursor_moved(C);
2405                 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2406                 // XXX PIL_sleep_ms(10);
2407         } 
2408         else {
2409                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 1);
2410
2411                 text_update_cursor_moved(C);
2412                 WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2413
2414                 scu->old[0]= event->mval[0];
2415                 scu->old[1]= event->mval[1];
2416         } 
2417 }
2418
2419 static void set_cursor_exit(bContext *C, wmOperator *op)
2420 {
2421         SpaceText *st= CTX_wm_space_text(C);
2422         Text *text= st->text;
2423         SetCursor *scu= op->customdata;
2424         int linep2, charp2;
2425         char *buffer;
2426
2427         if(txt_has_sel(text)) {
2428                 buffer = txt_sel_to_buf(text);
2429                 WM_clipboard_text_set(buffer, 1);
2430                 MEM_freeN(buffer);
2431         }
2432
2433         linep2= txt_get_span(st->text->lines.first, st->text->sell);
2434         charp2= st->text->selc;
2435                 
2436         if(scu->sell!=linep2 || scu->selc!=charp2)
2437                 txt_undo_add_toop(st->text, UNDO_STO, scu->sell, scu->selc, linep2, charp2);
2438
2439         text_update_cursor_moved(C);
2440         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, st->text);
2441
2442         MEM_freeN(scu);
2443 }
2444
2445 static int set_cursor_invoke(bContext *C, wmOperator *op, wmEvent *event)
2446 {
2447         SpaceText *st= CTX_wm_space_text(C);
2448         ARegion *ar= CTX_wm_region(C);
2449         SetCursor *scu;
2450
2451         op->customdata= MEM_callocN(sizeof(SetCursor), "SetCursor");
2452         scu= op->customdata;
2453         scu->selecting= RNA_boolean_get(op->ptr, "select");
2454
2455         scu->old[0]= event->mval[0];
2456         scu->old[1]= event->mval[1];
2457
2458         if(!scu->selecting) {
2459                 int curl= txt_get_span(st->text->lines.first, st->text->curl);
2460                 int curc= st->text->curc;                       
2461                 int linep2, charp2;
2462                                         
2463                 set_cursor_to_pos(st, ar, event->mval[0], event->mval[1], 0);
2464
2465                 linep2= txt_get_span(st->text->lines.first, st->text->curl);
2466                 charp2= st->text->selc;
2467                                 
2468                 if(curl!=linep2 || curc!=charp2)
2469                         txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
2470         }
2471
2472         scu->sell= txt_get_span(st->text->lines.first, st->text->sell);
2473         scu->selc= st->text->selc;
2474
2475         WM_event_add_modal_handler(C, op);
2476
2477         set_cursor_apply(C, op, event);
2478
2479         return OPERATOR_RUNNING_MODAL;
2480 }
2481
2482 static int set_cursor_modal(bContext *C, wmOperator *op, wmEvent *event)
2483 {
2484         switch(event->type) {
2485                 case LEFTMOUSE:
2486                 case MIDDLEMOUSE:
2487                 case RIGHTMOUSE:
2488                         set_cursor_exit(C, op);
2489                         return OPERATOR_FINISHED;
2490                 case MOUSEMOVE:
2491                         set_cursor_apply(C, op, event);
2492                         break;
2493         }
2494
2495         return OPERATOR_RUNNING_MODAL;
2496 }
2497
2498 static int set_cursor_cancel(bContext *C, wmOperator *op)
2499 {
2500         set_cursor_exit(C, op);
2501         return OPERATOR_FINISHED;
2502 }
2503
2504 void TEXT_OT_cursor_set(wmOperatorType *ot)
2505 {
2506         /* identifiers */
2507         ot->name= "Set Cursor";
2508         ot->idname= "TEXT_OT_cursor_set";
2509         ot->description= "Set cursor selection";
2510         
2511         /* api callbacks */
2512         ot->invoke= set_cursor_invoke;
2513         ot->modal= set_cursor_modal;
2514         ot->cancel= set_cursor_cancel;
2515         ot->poll= text_region_edit_poll;
2516
2517         /* properties */
2518         RNA_def_boolean(ot->srna, "select", 0, "Select", "Set selection end rather than cursor.");
2519 }
2520
2521 /******************* line number operator **********************/
2522
2523 static int line_number_invoke(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
2524 {
2525         SpaceText *st= CTX_wm_space_text(C);
2526         Text *text= CTX_data_edit_text(C);
2527         ARegion *ar= CTX_wm_region(C);
2528         short *mval= event->mval;
2529         double time;
2530         static int jump_to= 0;
2531         static double last_jump= 0;
2532
2533         if(!st->showlinenrs)
2534                 return OPERATOR_PASS_THROUGH;
2535
2536         if(!(mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<ar->winy-2))
2537                 return OPERATOR_PASS_THROUGH;
2538
2539         if(!(event->ascii>='0' && event->ascii<='9'))
2540                 return OPERATOR_PASS_THROUGH;
2541
2542         time = PIL_check_seconds_timer();
2543         if(last_jump < time-1)
2544                 jump_to= 0;
2545
2546         jump_to *= 10;
2547         jump_to += (int)(event->ascii-'0');
2548
2549         txt_move_toline(text, jump_to-1, 0);
2550         last_jump= time;
2551
2552         text_update_cursor_moved(C);
2553         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2554
2555         return OPERATOR_FINISHED;
2556 }
2557
2558 void TEXT_OT_line_number(wmOperatorType *ot)
2559 {
2560         /* identifiers */
2561         ot->name= "Line Number";
2562         ot->idname= "TEXT_OT_line_number";
2563         ot->description= "The current line number";
2564         
2565         /* api callbacks */
2566         ot->invoke= line_number_invoke;
2567         ot->poll= text_region_edit_poll;
2568 }
2569
2570 /******************* insert operator **********************/
2571
2572 static int insert_exec(bContext *C, wmOperator *op)
2573 {
2574         SpaceText *st= CTX_wm_space_text(C);
2575         Text *text= CTX_data_edit_text(C);
2576         char *str;
2577         int done = 0, i;
2578
2579         text_drawcache_tag_update(st, 0);
2580
2581         str= RNA_string_get_alloc(op->ptr, "text", NULL, 0);
2582
2583         if(st && st->overwrite) {
2584                 for(i=0; str[i]; i++) {
2585                         done |= txt_replace_char(text, str[i]);
2586                 }
2587         } else {
2588                 for(i=0; str[i]; i++) {
2589                         done |= txt_add_char(text, str[i]);
2590                 }
2591         }
2592
2593         MEM_freeN(str);
2594         
2595         if(!done)
2596                 return OPERATOR_CANCELLED;
2597
2598         text_update_line_edited(text->curl);
2599
2600         text_update_cursor_moved(C);
2601         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2602
2603         return OPERATOR_FINISHED;
2604 }
2605
2606 static int insert_invoke(bContext *C, wmOperator *op, wmEvent *event)
2607 {
2608         int ret;
2609
2610         // if(!RNA_property_is_set(op->ptr, "text")) { /* always set from keymap XXX */
2611         if(!RNA_string_length(op->ptr, "text")) {
2612                 /* if alt/ctrl/super are pressed pass through */
2613                 if(event->ctrl || event->oskey) {
2614                         return OPERATOR_PASS_THROUGH;
2615                 }
2616                 else {
2617                         char str[2];
2618                         str[0]= event->ascii;
2619                         str[1]= '\0';
2620                         RNA_string_set(op->ptr, "text", str);
2621                 }
2622         }
2623
2624         ret = insert_exec(C, op);
2625         
2626         /* run the script while editing, evil but useful */
2627         if(ret==OPERATOR_FINISHED && CTX_wm_space_text(C)->live_edit)
2628                 run_script_exec(C, op);
2629
2630         return ret;
2631 }
2632
2633 void TEXT_OT_insert(wmOperatorType *ot)
2634 {
2635         /* identifiers */
2636         ot->name= "Insert";
2637         ot->idname= "TEXT_OT_insert";
2638         ot->description= "Insert text at cursor position";
2639         
2640         /* api callbacks */
2641         ot->exec= insert_exec;
2642         ot->invoke= insert_invoke;
2643         ot->poll= text_edit_poll;
2644
2645         /* properties */
2646         RNA_def_string(ot->srna, "text", "", 0, "Text", "Text to insert at the cursor position.");
2647 }
2648
2649 /******************* find operator *********************/
2650
2651 /* mode */
2652 #define TEXT_FIND               0
2653 #define TEXT_REPLACE    1
2654 #define TEXT_MARK_ALL   2
2655
2656 static int find_and_replace(bContext *C, wmOperator *op, short mode)
2657 {
2658         Main *bmain= CTX_data_main(C);
2659         SpaceText *st= CTX_wm_space_text(C);
2660         Text *start= NULL, *text= st->text;
2661         int flags, first= 1;
2662         int found = 0;
2663         char *tmp;
2664
2665         if(!st->findstr[0] || (mode == TEXT_REPLACE && !st->replacestr[0]))
2666                 return OPERATOR_CANCELLED;
2667
2668         flags= st->flags;
2669         if(flags & ST_FIND_ALL)
2670                 flags ^= ST_FIND_WRAP;
2671
2672         do {
2673                 if(first)
2674                         txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
2675
2676                 first= 0;
2677                 
2678                 /* Replace current */
2679                 if(mode!=TEXT_FIND && txt_has_sel(text)) {
2680                         tmp= txt_sel_to_buf(text);
2681
2682                         if(strcmp(st->findstr, tmp)==0) {
2683                                 if(mode==TEXT_REPLACE) {
2684                                         txt_insert_buf(text, st->replacestr);
2685                                         if(text->curl && text->curl->format) {
2686                                                 MEM_freeN(text->curl->format);
2687                                                 text->curl->format= NULL;
2688                                         }
2689                                         text_update_cursor_moved(C);
2690                                         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2691                                         text_drawcache_tag_update(CTX_wm_space_text(C), 1);
2692                                 }
2693                                 else if(mode==TEXT_MARK_ALL) {
2694                                         unsigned char color[4];
2695                                         UI_GetThemeColor4ubv(TH_SHADE2, color);
2696
2697                                         if(txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
2698                                                 if(tmp) MEM_freeN(tmp), tmp=NULL;
2699                                                 break;
2700                                         }
2701
2702                                         txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
2703                                         text_update_cursor_moved(C);
2704                                         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
2705                                 }
2706                         }
2707                         MEM_freeN(tmp);
2708                         tmp= NULL;
2709                 }
2710
2711                 /* Find next */
2712                 if(txt_find_string(text, st->findstr, flags & ST_FIND_WRAP)) {
2713                         text_update_cursor_moved(C);
2714                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2715                 }
2716                 else if(flags & ST_FIND_ALL) {
2717                         if(text==start) break;
2718                         if(!start) start= text;
2719                         if(text->id.next)
2720                                 text= st->text= text->id.next;
2721                         else
2722                                 text= st->text= bmain->text.first;
2723                         txt_move_toline(text, 0, 0);
2724                         text_update_cursor_moved(C);
2725                         WM_event_add_notifier(C, NC_TEXT|ND_CURSOR, text);
2726                         first= 1;
2727                 }
2728                 else {
2729                         if(!found) BKE_reportf(op->reports, RPT_ERROR, "Text not found: %s", st->findstr);
2730                         break;
2731                 }
2732                 found = 1;
2733         } while(mode==TEXT_MARK_ALL);
2734
2735         return OPERATOR_FINISHED;
2736 }
2737
2738 static int find_exec(bContext *C, wmOperator *op)
2739 {
2740         return find_and_replace(C, op, TEXT_FIND);
2741 }
2742
2743 void TEXT_OT_find(wmOperatorType *ot)
2744 {
2745         /* identifiers */
2746         ot->name= "Find";
2747         ot->idname= "TEXT_OT_find";
2748         ot->description= "Find specified text";
2749         
2750         /* api callbacks */
2751         ot->exec= find_exec;
2752         ot->poll= text_space_edit_poll;
2753 }
2754
2755 /******************* replace operator *********************/
2756
2757 static int replace_exec(bContext *C, wmOperator *op)
2758 {
2759         return find_and_replace(C, op, TEXT_REPLACE);
2760 }
2761
2762 void TEXT_OT_replace(wmOperatorType *ot)
2763 {
2764         /* identifiers */
2765         ot->name= "Replace";
2766         ot->idname= "TEXT_OT_replace";
2767         ot->description= "Replace text with the specified text";
2768
2769         /* api callbacks */
2770         ot->exec= replace_exec;
2771         ot->poll= text_space_edit_poll;
2772 }
2773
2774 /******************* mark all operator *********************/
2775
2776 static int mark_all_exec(bContext *C, wmOperator *op)
2777 {
2778         return find_and_replace(C, op, TEXT_MARK_ALL);
2779 }
2780
2781 void TEXT_OT_mark_all(wmOperatorType *ot)
2782 {
2783         /* identifiers */
2784         ot->name= "Mark All";
2785         ot->idname= "TEXT_OT_mark_all";
2786         ot->description= "Mark all specified text";
2787         
2788         /* api callbacks */
2789         ot->exec= mark_all_exec;
2790         ot->poll= text_space_edit_poll;
2791 }
2792
2793 /******************* find set selected *********************/
2794
2795 static int find_set_selected_exec(bContext *C, wmOperator *op)
2796 {
2797         SpaceText *st= CTX_wm_space_text(C);
2798         Text *text= CTX_data_edit_text(C);
2799         char *tmp;
2800
2801         tmp= txt_sel_to_buf(text);
2802         BLI_strncpy(st->findstr, tmp, ST_MAX_FIND_STR);
2803         MEM_freeN(tmp);
2804
2805         if(!st->findstr[0])
2806                 return OPERATOR_FINISHED;
2807
2808         return find_and_replace(C, op, TEXT_FIND);
2809 }
2810
2811 void TEXT_OT_find_set_selected(wmOperatorType *ot)
2812 {
2813         /* identifiers */
2814         ot->name= "Find Set Selected";
2815         ot->idname= "TEXT_OT_find_set_selected";
2816         ot->description= "Find specified text and set as selected";
2817         
2818         /* api callbacks */
2819         ot->exec= find_set_selected_exec;
2820         ot->poll= text_space_edit_poll;
2821 }
2822
2823 /******************* replace set selected *********************/
2824
2825 static int replace_set_selected_exec(bContext *C, wmOperator *UNUSED(op))
2826 {
2827         SpaceText *st= CTX_wm_space_text(C);
2828         Text *text= CTX_data_edit_text(C);
2829         char *tmp;
2830
2831         tmp= txt_sel_to_buf(text);
2832         BLI_strncpy(st->replacestr, tmp, ST_MAX_FIND_STR);
2833         MEM_freeN(tmp);
2834
2835         return OPERATOR_FINISHED;
2836 }
2837
2838 void TEXT_OT_replace_set_selected(wmOperatorType *ot)
2839 {
2840         /* identifiers */
2841         ot->name= "Replace Set Selected";
2842         ot->idname= "TEXT_OT_replace_set_selected";
2843         ot->description= "Replace text with specified text and set as selected";
2844         
2845         /* api callbacks */
2846         ot->exec= replace_set_selected_exec;
2847         ot->poll= text_space_edit_poll;
2848 }
2849
2850 /****************** resolve conflict operator ******************/
2851
2852 enum { RESOLVE_IGNORE, RESOLVE_RELOAD, RESOLVE_SAVE, RESOLVE_MAKE_INTERNAL };
2853 static EnumPropertyItem resolution_items[]= {
2854         {RESOLVE_IGNORE, "IGNORE", 0, "Ignore", ""},
2855         {RESOLVE_RELOAD, "RELOAD", 0, "Reload", ""},
2856         {RESOLVE_SAVE, "SAVE", 0, "Save", ""},
2857         {RESOLVE_MAKE_INTERNAL, "MAKE_INTERNAL", 0, "Make Internal", ""},
2858         {0, NULL, 0, NULL, NULL}};
2859
2860 /* returns 0 if file on disk is the same or Text is in memory only
2861    returns 1 if file has been modified on disk since last local edit
2862    returns 2 if file on disk has been deleted
2863    -1 is returned if an error occurs */
2864
2865 int text_file_modified(Text *text)
2866 {
2867         struct stat st;
2868         int result;
2869         char file[FILE_MAXDIR+FILE_MAXFILE];
2870
2871         if(!text || !text->name)
2872                 return 0;
2873
2874         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2875         BLI_path_abs(file, G.main->name);
2876
2877         if(!BLI_exists(file))
2878                 return 2;
2879
2880         result = stat(file, &st);
2881         
2882         if(result == -1)
2883                 return -1;
2884
2885         if((st.st_mode & S_IFMT) != S_IFREG)
2886                 return -1;
2887
2888         if(st.st_mtime > text->mtime)
2889                 return 1;
2890
2891         return 0;
2892 }
2893
2894 static void text_ignore_modified(Text *text)
2895 {
2896         struct stat st;
2897         int result;
2898         char file[FILE_MAXDIR+FILE_MAXFILE];
2899
2900         if(!text || !text->name) return;
2901
2902         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
2903         BLI_path_abs(file, G.main->name);
2904
2905         if(!BLI_exists(file)) return;
2906
2907         result = stat(file, &st);
2908         
2909         if(result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
2910                 return;
2911
2912         text->mtime= st.st_mtime;
2913 }
2914
2915 static int resolve_conflict_exec(bContext *C, wmOperator *op)
2916 {
2917         Text *text= CTX_data_edit_text(C);
2918         int resolution= RNA_enum_get(op->ptr, "resolution");
2919
2920         switch(resolution) {
2921                 case RESOLVE_RELOAD:
2922                         return reload_exec(C, op);
2923                 case RESOLVE_SAVE:
2924                         return save_exec(C, op);
2925                 case RESOLVE_MAKE_INTERNAL:
2926                         return make_internal_exec(C, op);
2927                 case RESOLVE_IGNORE:
2928                         text_ignore_modified(text);
2929                         return OPERATOR_FINISHED;
2930         }
2931
2932         return OPERATOR_CANCELLED;
2933 }
2934
2935 static int resolve_conflict_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
2936 {
2937         Text *text= CTX_data_edit_text(C);
2938         uiPopupMenu *pup;
2939         uiLayout *layout;
2940
2941         switch(text_file_modified(text)) {
2942                 case 1:
2943                         if(text->flags & TXT_ISDIRTY) {
2944                                 /* modified locally and externally, ahhh. offer more possibilites. */
2945                                 pup= uiPupMenuBegin(C, "File Modified Outside and Inside Blender", ICON_NULL);
2946                                 layout= uiPupMenuLayout(pup);
2947                                 uiItemEnumO(layout, op->type->idname, "Reload from disk (ignore local changes)", 0, "resolution", RESOLVE_RELOAD);
2948                                 uiItemEnumO(layout, op->type->idname, "Save to disk (ignore outside changes)", 0, "resolution", RESOLVE_SAVE);
2949                                 uiItemEnumO(layout, op->type->idname, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
2950                                 uiPupMenuEnd(C, pup);
2951                         }
2952                         else {
2953                                 pup= uiPupMenuBegin(C, "File Modified Outside Blender", ICON_NULL);
2954                                 layout= uiPupMenuLayout(pup);
2955                                 uiItemEnumO(layout, op->type->idname, "Reload from disk", 0, "resolution", RESOLVE_RELOAD);
2956                                 uiItemEnumO(layout, op->type->idname, "Make text internal (separate copy)", 0, "resolution", RESOLVE_MAKE_INTERNAL);
2957                                 uiItemEnumO(layout, op->type->idname, "Ignore", 0, "resolution", RESOLVE_IGNORE);
2958                                 uiPupMenuEnd(C, pup);
2959                         }
2960                         break;
2961                 case 2:
2962                         pup= uiPupMenuBegin(C, "File Deleted Outside Blender", ICON_NULL);
2963                         layout= uiPupMenuLayout(pup);
2964                         uiItemEnumO(layout, op->type->idname, "Make text internal", 0, "resolution", RESOLVE_MAKE_INTERNAL);
2965                         uiItemEnumO(layout, op->type->idname, "Recreate file", 0, "resolution", RESOLVE_SAVE);
2966                         uiPupMenuEnd(C, pup);
2967                         break;
2968         }
2969
2970         return OPERATOR_CANCELLED;
2971 }
2972
2973 void TEXT_OT_resolve_conflict(wmOperatorType *ot)
2974 {
2975         /* identifiers */
2976         ot->name= "Resolve Conflict";
2977         ot->idname= "TEXT_OT_resolve_conflict";
2978         ot->description= "When external text is out of sync, resolve the conflict";
2979
2980         /* api callbacks */
2981         ot->exec= resolve_conflict_exec;
2982         ot->invoke= resolve_conflict_invoke;
2983         ot->poll= save_poll;
2984
2985         /* properties */
2986         RNA_def_enum(ot->srna, "resolution", resolution_items, RESOLVE_IGNORE, "Resolution", "How to solve conflict due to different in internal and external text.");
2987 }
2988
2989 /********************** to 3d object operator *****************/
2990
2991 static int to_3d_object_exec(bContext *C, wmOperator *op)
2992 {
2993         Text *text= CTX_data_edit_text(C);
2994         int split_lines= RNA_boolean_get(op->ptr, "split_lines");
2995
2996         ED_text_to_object(C, text, split_lines);
2997
2998         return OPERATOR_FINISHED;
2999 }
3000
3001 void TEXT_OT_to_3d_object(wmOperatorType *ot)
3002 {
3003         /* identifiers */
3004         ot->name= "To 3D Object";
3005         ot->idname= "TEXT_OT_to_3d_object";
3006         ot->description= "Create 3d text object from active text data block";
3007         
3008         /* api callbacks */
3009         ot->exec= to_3d_object_exec;
3010         ot->poll= text_edit_poll;
3011         
3012         /* flags */
3013         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
3014
3015         /* properties */
3016         RNA_def_boolean(ot->srna, "split_lines", 0, "Split Lines", "Create one object per line in the text.");
3017 }
3018
3019
3020 /************************ undo ******************************/
3021
3022 void ED_text_undo_step(bContext *C, int step)
3023 {
3024         Text *text= CTX_data_edit_text(C);
3025
3026         if(!text)
3027                 return;
3028
3029         if(step==1)
3030                 txt_do_undo(text);
3031         else if(step==-1)
3032                 txt_do_redo(text);
3033
3034         text_update_edited(text);
3035
3036         text_update_cursor_moved(C);
3037         text_drawcache_tag_update(CTX_wm_space_text(C), 1);
3038         WM_event_add_notifier(C, NC_TEXT|NA_EDITED, text);
3039 }
3040