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