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