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