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