Added better grouping for text markers with a separate group field (instead of using...
[blender-staging.git] / source / blender / src / drawtext.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19  *
20  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
21  * All rights reserved.
22  *
23  * The Original Code is: all of this file.
24  *
25  * Contributor(s): none yet.
26  *
27  * ***** END GPL LICENSE BLOCK *****
28  */
29
30 #include <stdlib.h>
31 #include <math.h>
32 #include <string.h>
33 #include <ctype.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36
37 #ifdef HAVE_CONFIG_H
38 #include <config.h>
39 #endif
40
41 #ifndef _WIN32
42 #include <unistd.h>
43 #else
44 #include <io.h>
45 #include "BLI_winstuff.h"
46 #endif   
47 #include "MEM_guardedalloc.h"
48 #include "PIL_time.h"
49
50 #include "BMF_Api.h"
51
52 #include "BLI_blenlib.h"
53 #include "BLI_arithb.h"
54
55 #include "DNA_text_types.h"
56 #include "DNA_space_types.h"
57 #include "DNA_screen_types.h"
58 #include "DNA_userdef_types.h"
59
60 #include "BKE_utildefines.h"
61 #include "BKE_text.h"
62 #include "BKE_global.h"
63 #include "BKE_main.h"
64 #include "BKE_node.h"
65 #include "BKE_suggestions.h"
66
67 #include "BIF_gl.h"
68 #include "BIF_glutil.h"
69 #include "BIF_keyval.h"
70 #include "BIF_interface.h"
71 #include "BIF_drawtext.h"
72 #include "BIF_editfont.h"
73 #include "BIF_spacetypes.h"
74 #include "BIF_usiblender.h"
75 #include "BIF_screen.h"
76 #include "BIF_toolbox.h"
77 #include "BIF_space.h"
78 #include "BIF_mywindow.h"
79 #include "BIF_resources.h"
80 #include "BIF_mainqueue.h"
81
82 #include "BSE_filesel.h"
83
84 #include "BPY_extern.h"
85 #include "BPY_menus.h"
86
87 #include "mydevice.h"
88 #include "blendef.h" 
89 #include "winlay.h"
90
91 /***********************/ /*
92
93 Notes on word-wrap
94 --
95 All word-wrap functions follow the algorithm below to maintain consistency.
96         line            The line to wrap (tabs converted to spaces)
97         view_width      The maximum number of characters displayable in the region
98                                 This equals region_width/font_width for the region
99         wrap_chars      Characters that allow wrapping. This equals [' ', '\t', '-']
100
101 def wrap(line, view_width, wrap_chars):
102         draw_start = 0
103         draw_end = view_width
104         pos = 0
105         for c in line:
106                 if pos-draw_start >= view_width:
107                         print line[draw_start:draw_end]
108                         draw_start = draw_end
109                         draw_end += view_width
110                 elif c in wrap_chars:
111                         draw_end = pos+1
112                 pos += 1
113         print line[draw_start:]
114
115 */ /***********************/
116
117 #define TEXTXLOC                38
118
119 #define SUGG_LIST_SIZE  7
120 #define SUGG_LIST_WIDTH 20
121 #define DOC_WIDTH               40
122 #define DOC_HEIGHT              10
123
124 #define TOOL_SUGG_LIST  0x01
125 #define TOOL_DOCUMENT   0x02
126
127 #define TMARK_GRP_CUSTOM        0x00010000      /* Lower 2 bytes used for Python groups */
128 #define TMARK_GRP_FINDALL       0x00020000
129
130 /* forward declarations */
131
132 void drawtextspace(ScrArea *sa, void *spacedata);
133 void winqreadtextspace(struct ScrArea *sa, void *spacedata, struct BWinEvent *evt);
134 void txt_copy_selectbuffer (Text *text);
135 void draw_brackets(SpaceText *st);
136
137 static void get_selection_buffer(Text *text);
138 static int check_bracket(char ch);
139 static int check_delim(char ch);
140 static int check_digit(char ch);
141 static int check_builtinfuncs(char *string);
142 static int check_specialvars(char *string);
143 static int check_identifier(char ch);
144 static int check_whitespace(char ch);
145
146 static int get_wrap_width(SpaceText *st);
147 static int get_wrap_points(SpaceText *st, char *line);
148 static void get_suggest_prefix(Text *text, int offset);
149 static void confirm_suggestion(Text *text, int skipleft);
150
151 #define TXT_MAXFINDSTR 255
152 static int g_find_flags= TXT_FIND_WRAP;
153 static char *g_find_str= NULL;
154 static char *g_replace_str= NULL;
155
156 static int doc_scroll= 0;
157 static double last_check_time= 0;
158 static int jump_to= 0;
159 static double last_jump= 0;
160
161 static BMF_Font *spacetext_get_font(SpaceText *st) {
162         static BMF_Font *scr12= NULL;
163         static BMF_Font *scr15= NULL;
164         
165         switch (st->font_id) {
166         default:
167         case 0:
168                 if (!scr12)
169                         scr12= BMF_GetFont(BMF_kScreen12);
170                 return scr12;
171         case 1:
172                 if (!scr15)
173                         scr15= BMF_GetFont(BMF_kScreen15);
174                 return scr15;
175         }
176 }
177
178 static int spacetext_get_fontwidth(SpaceText *st) {
179         return BMF_GetCharacterWidth(spacetext_get_font(st), ' ');
180 }
181
182 static char *temp_char_buf= NULL;
183 static int *temp_char_accum= NULL;
184 static int temp_char_len= 0;
185 static int temp_char_pos= 0;
186
187 static void temp_char_write(char c, int accum) {
188         if (temp_char_len==0 || temp_char_pos>=temp_char_len) {
189                 char *nbuf; int *naccum;
190                 int olen= temp_char_len;
191                 
192                 if (olen) temp_char_len*= 2;
193                 else temp_char_len= 256;
194                 
195                 nbuf= MEM_mallocN(sizeof(*temp_char_buf)*temp_char_len, "temp_char_buf");
196                 naccum= MEM_mallocN(sizeof(*temp_char_accum)*temp_char_len, "temp_char_accum");
197                 
198                 if (olen) {
199                         memcpy(nbuf, temp_char_buf, olen);
200                         memcpy(naccum, temp_char_accum, olen);
201                         
202                         MEM_freeN(temp_char_buf);
203                         MEM_freeN(temp_char_accum);
204                 }
205                 
206                 temp_char_buf= nbuf;
207                 temp_char_accum= naccum;
208         }
209         
210         temp_char_buf[temp_char_pos]= c;        
211         temp_char_accum[temp_char_pos]= accum;
212         
213         if (c==0) temp_char_pos= 0;
214         else temp_char_pos++;
215 }
216
217 void free_txt_data(void) {
218         txt_free_cut_buffer();
219         
220         if (g_find_str) MEM_freeN(g_find_str);
221         if (g_replace_str) MEM_freeN(g_replace_str);
222         if (temp_char_buf) MEM_freeN(temp_char_buf);
223         if (temp_char_accum) MEM_freeN(temp_char_accum);        
224 }
225
226 static int render_string (SpaceText *st, char *in) {
227         int r = 0, i = 0;
228         
229         while(*in) {
230                 if (*in=='\t') {
231                         if (temp_char_pos && *(in-1)=='\t') i= st->tabnumber;
232                         else if (st->tabnumber > 0) i= st->tabnumber - (temp_char_pos%st->tabnumber);
233                         while(i--) temp_char_write(' ', r);
234                 } else temp_char_write(*in, r);
235
236                 r++;
237                 in++;
238         }
239         r= temp_char_pos;
240         temp_char_write(0, 0);
241                 
242         return r;
243 }
244
245 static int find_builtinfunc(char *string)
246 {
247         int a, i;
248         char builtinfuncs[][11] = {"and", "as", "assert", "break", "class", "continue", "def",
249                                                                 "del", "elif", "else", "except", "exec", "finally",
250                                                                 "for", "from", "global", "if", "import", "in",
251                                                                 "is", "lambda", "not", "or", "pass", "print",
252                                                                 "raise", "return", "try", "while", "yield"};
253         for (a=0; a<30; a++) {
254                 i = 0;
255                 while (1) {
256                         if (builtinfuncs[a][i]=='\0') {
257                                 if (check_identifier(string[i]))
258                                         i = -1;
259                                 break;
260                         } else if (string[i]!=builtinfuncs[a][i]) {
261                                 i = -1;
262                                 break;
263                         }
264                         i++;
265                 }
266                 if (i>0) break;
267         }
268         return i;
269 }
270
271 static int find_specialvar(char *string) 
272 {
273         int i = 0;
274         if (string[0]=='d' && string[1]=='e' && string[2]=='f')
275                 i = 3;
276         else if (string[0]=='c' && string[1]=='l' && string[2]=='a' && string[3]=='s' && string[4]=='s')
277                 i = 5;
278         if (i==0 || check_identifier(string[i]))
279                 return -1;
280         return i;
281 }
282
283 static void print_format(SpaceText *st, TextLine *line) {
284         int i, a;
285         char *s, *f;
286         s = line->line;
287         f = line->format;
288         for (a=0; *s; s++) {
289                 if (*s == '\t') {
290                         for (i=st->tabnumber-(a%st->tabnumber); i>0; i--)
291                                 printf(" "), f++, a++;
292                 } else
293                         printf("%c", *s), f++, a++;
294         }
295         printf("\n%s [%#x]\n", line->format, (int) (f[strlen(f)+1]));
296 }
297
298 /* Ensures the format string for the given line is long enough, reallocating as needed */
299 static int check_format_len(TextLine *line, unsigned int len) {
300         if (line->format) {
301                 if (strlen(line->format) < len) {
302                         MEM_freeN(line->format);
303                         line->format = MEM_mallocN(len+2, "SyntaxFormat");
304                         if (!line->format) return 0;
305                 }
306         } else {
307                 line->format = MEM_mallocN(len+2, "SyntaxFormat");
308                 if (!line->format) return 0;
309         }
310         return 1;
311 }
312
313 /* Formats the specified line and if allowed and needed will move on to the
314  * next line. The format string contains the following characters:
315  *              '_'             Whitespace
316  *              '#'             Comment text
317  *              '!'             Punctuation and other symbols
318  *              'n'             Numerals
319  *              'l'             String letters
320  *              'v'             Special variables (class, def)
321  *              'b'             Built-in names (print, for, etc.)
322  *              'q'             Other text (identifiers, etc.)
323  * It is terminated with a null-terminator '\0' followed by a continuation
324  * flag indicating whether the line is part of a multi-line string.
325  */
326 void txt_format_line(SpaceText *st, TextLine *line, int do_next) {
327         char *str, *fmt, orig, cont, find, prev = ' ';
328         int len, i;
329
330         /* Get continuation from previous line */
331         if (line->prev && (fmt=line->prev->format)) {
332                 cont = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
333         } else cont = 0;
334
335         /* Get original continuation from this line */
336         if (fmt=line->format) {
337                 orig = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
338         } else orig = 0xFF;
339
340         render_string(st, line->line);
341         str = temp_char_buf;
342         len = strlen(str);
343         if (!check_format_len(line, len)) return;
344         fmt = line->format;
345
346         while (*str) {
347                 /* Handle escape sequences by skipping both \ and next char */
348                 if (*str == '\\') {
349                         *fmt = prev; fmt++; str++;
350                         if (*str == '\0') break;
351                         *fmt = prev; fmt++; str++;
352                         continue;
353                 }
354                 /* Handle continuations */
355                 else if (cont) {
356                         /* Triple strings ("""...""" or '''...''') */
357                         if (cont & TXT_TRISTR) {
358                                 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
359                                 if (*str==find && *(str+1)==find && *(str+2)==find) {
360                                         *fmt = 'l'; fmt++; str++;
361                                         *fmt = 'l'; fmt++; str++;
362                                         cont = 0;
363                                 }
364                         /* Handle other strings */
365                         } else {
366                                 find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
367                                 if (*str == find) cont = 0;
368                         }
369                         *fmt = 'l';
370                 }
371                 /* Not in a string... */
372                 else {
373                         /* Deal with comments first */
374                         if (prev == '#' || *str == '#')
375                                 *fmt = '#';
376                         /* Strings */
377                         else if (*str == '"' || *str == '\'') {
378                                 find = *str;
379                                 cont = (*str== '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
380                                 if (*(str+1) == find && *(str+2) == find) {
381                                         *fmt = 'l'; fmt++; str++;
382                                         *fmt = 'l'; fmt++; str++;
383                                         cont |= TXT_TRISTR;
384                                 }
385                                 *fmt = 'l';
386                         }
387                         /* Whitespace (all ws. has been converted to spaces) */
388                         else if (*str == ' ')
389                                 *fmt = '_';
390                         /* Numbers (digits not part of an identifier and periods followed by digits) */
391                         else if ((prev != 'q' && check_digit(*str)) || (*str == '.' && check_digit(*(str+1))))
392                                 *fmt = 'n';
393                         /* Punctuation */
394                         else if (check_delim(*str))
395                                 *fmt = '!';
396                         /* Identifiers and other text (no previous ws. or delims. so text continues) */
397                         else if (prev == 'q')
398                                 *fmt = 'q';
399                         /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
400                         else {
401                                 /* Special vars(v) or built-in keywords(b) */
402                                 if ((i=find_specialvar(str)) != -1)
403                                         prev = 'v';
404                                 else if ((i=find_builtinfunc(str)) != -1)
405                                         prev = 'b';
406                                 if (i>0) {
407                                         while (i>1) {
408                                                 *fmt = prev; *fmt++; *str++;
409                                                 i--;
410                                         }
411                                         *fmt = prev;
412                                 } else
413                                         *fmt = 'q';
414                         }
415                 }
416                 prev = *fmt;
417                 fmt++;
418                 str++;
419         }
420
421         /* Terminate and add continuation char */
422         *fmt = '\0'; fmt++;
423         *fmt = cont;
424
425         /* Debugging */
426         //print_format(st, line);
427
428         /* If continuation has changed and we're allowed, process the next line */
429         if (cont!=orig && do_next && line->next) {
430                 txt_format_line(st, line->next, do_next);
431         }
432 }
433
434 void txt_format_text(SpaceText *st) 
435 {
436         TextLine *linep;
437
438         if (!st->text) return;
439
440         for (linep=st->text->lines.first; linep; linep=linep->next)
441                 txt_format_line(st, linep, 0);
442 }
443
444 static void format_draw_color(char formatchar) {
445         switch (formatchar) {
446                 case '_': /* Whitespace */
447                         break;
448                 case '!': /* Symbols */
449                         BIF_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f);
450                         break;
451                 case '#': /* Comments */
452                         BIF_ThemeColor(TH_SYNTAX_C);
453                         break;
454                 case 'n': /* Numerals */
455                         BIF_ThemeColor(TH_SYNTAX_N);
456                         break;
457                 case 'l': /* Strings */
458                         BIF_ThemeColor(TH_SYNTAX_L);
459                         break;
460                 case 'v': /* Specials: class, def */
461                         BIF_ThemeColor(TH_SYNTAX_V);
462                         break;
463                 case 'b': /* Keywords: for, print, etc. */
464                         BIF_ThemeColor(TH_SYNTAX_B);
465                         break;
466                 case 'q': /* Other text (identifiers) */
467                 default:
468                         BIF_ThemeColor(TH_TEXT);
469                         break;
470         }
471 }
472
473 static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format)
474 {
475         int basex, i, a, len, start, end, max, lines;
476         
477         len= render_string(st, str);
478         str= temp_char_buf;
479         max= w/spacetext_get_fontwidth(st);
480         if (max<8) max= 8;
481         basex= x;
482
483         lines= 1;
484         start= 0;
485         end= max;
486         for (i=0; i<len; i++) {
487                 if (i-start >= max) {
488                         /* Draw the visible portion of text on the overshot line */
489                         for (a=start; a<end; a++) {
490                                 if (st->showsyntax && format) format_draw_color(format[a]);
491                                 glRasterPos2i(x, y);
492                                 BMF_DrawCharacter(spacetext_get_font(st), str[a]);
493                                 x += BMF_GetCharacterWidth(spacetext_get_font(st), str[a]);
494                         }
495                         y -= st->lheight;
496                         x= basex;
497                         lines++;
498                         start= end;
499                         end += max;
500                 } else if (str[i]==' ' || str[i]=='-') {
501                         end = i+1;
502                 }
503         }
504         /* Draw the remaining text */
505         for (a=start; a<len; a++) {
506                 if (st->showsyntax && format) format_draw_color(format[a]);
507                 glRasterPos2i(x, y);
508                 BMF_DrawCharacter(spacetext_get_font(st), str[a]);
509                 x += BMF_GetCharacterWidth(spacetext_get_font(st), str[a]);
510         }
511         return lines;
512 }
513
514 static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format)
515 {
516         int r=0, w= 0;
517         char *in;
518         int *acc;
519
520         w= render_string(st, str);
521         if(w<cshift ) return 0; /* String is shorter than shift */
522         
523         in= temp_char_buf+cshift;
524         acc= temp_char_accum+cshift;
525         w= w-cshift;
526
527         if (draw) {
528                 if(st->showsyntax && format) {
529                         int amount, a;
530                         format = format+cshift;
531                 
532                         amount = strlen(in);
533                         
534                         for(a = 0; a < amount; a++) {
535                                 format_draw_color(format[a]);
536                                 glRasterPos2i(x, y);
537                                 BMF_DrawCharacter(spacetext_get_font(st), in[a]);
538                                 x = x+BMF_GetCharacterWidth(spacetext_get_font(st), in[a]);
539                         }
540                 } else {
541                         glRasterPos2i(x, y);
542                         BMF_DrawString(spacetext_get_font(st), in);
543                 }
544         } else {
545                 while (w-- && *acc++ < maxwidth) {
546                         r+= spacetext_get_fontwidth(st);
547                 }
548         }
549
550         if (cshift && r==0) return 0;
551         else if (st->showlinenrs)
552                 return r+TXT_OFFSET+TEXTXLOC;
553         else
554                 return r+TXT_OFFSET;
555 }
556
557 static void set_cursor_to_pos (SpaceText *st, int x, int y, int sel) 
558 {
559         Text *text;
560         TextLine **linep;
561         int *charp;
562         int w;
563         
564         text= st->text;
565
566         if(sel) { linep= &text->sell; charp= &text->selc; } 
567         else { linep= &text->curl; charp= &text->curc; }
568         
569         y= (curarea->winy - y)/st->lheight;
570
571         if(st->showlinenrs)
572                 x-= TXT_OFFSET+TEXTXLOC;
573         else
574                 x-= TXT_OFFSET;
575
576         if (x<0) x= 0;
577         x = (x/spacetext_get_fontwidth(st)) + st->left;
578         
579         if (st->wordwrap) {
580                 int i, j, endj, curs, max, chop, start, end, chars, loop;
581                 char ch;
582
583                 /* Point to first visible line */
584                 *linep= text->lines.first;
585                 for (i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;
586
587                 max= get_wrap_width(st);
588
589                 loop= 1;
590                 while (loop && *linep) {
591                         start= 0;
592                         end= max;
593                         chop= 1;
594                         chars= 0;
595                         curs= 0;
596                         for (i=0, j=0; loop; j++) {
597
598                                 /* Mimic replacement of tabs */
599                                 ch= (*linep)->line[j];
600                                 if (ch=='\t') {
601                                         chars= st->tabnumber-i%st->tabnumber;
602                                         ch= ' ';
603                                 } else
604                                         chars= 1;
605
606                                 while (chars--) {
607                                         /* Gone too far, go back to last wrap point */
608                                         if (y<0) {
609                                                 *charp= endj;
610                                                 loop= 0;
611                                                 break;
612                                         /* Exactly at the cursor, done */
613                                         } else if (y==0 && i-start==x) {
614                                                 *charp= curs= j;
615                                                 loop= 0;
616                                                 break;
617                                         /* Prepare curs for next wrap */
618                                         } else if (i-end==x) {
619                                                 curs= j;
620                                         }
621                                         if (i-start>=max) {
622                                                 if (chop) endj= j;
623                                                 y--;
624                                                 start= end;
625                                                 end += max;
626                                                 chop= 1;
627                                                 if (y==0 && i-start>=x) {
628                                                         *charp= curs;
629                                                         loop= 0;
630                                                         break;
631                                                 }
632                                         } else if (ch==' ' || ch=='-' || ch=='\0') {
633                                                 if (y==0 && i-start>=x) {
634                                                         *charp= curs;
635                                                         loop= 0;
636                                                         break;
637                                                 }
638                                                 end = i+1;
639                                                 endj = j;
640                                                 chop= 0;
641                                         }
642                                         i++;
643                                 }
644                                 if (ch=='\0') break;
645                         }
646                         if (!loop || y<0) break;
647
648                         if (!(*linep)->next) {
649                                 *charp= (*linep)->len;
650                                 break;
651                         }
652                         
653                         /* On correct line but didn't meet cursor, must be at end */
654                         if (y==0) {
655                                 *charp= (*linep)->len;
656                                 break;
657                         }
658                         *linep= (*linep)->next;
659                         y--;
660                 }
661
662         } else {
663                 y-= txt_get_span(text->lines.first, *linep) - st->top;
664                 
665                 if (y>0) {
666                         while (y-- != 0) if((*linep)->next) *linep= (*linep)->next;
667                 } else if (y<0) {
668                         while (y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
669                 }
670
671                 
672                 w= render_string(st, (*linep)->line);
673                 if(x<w) *charp= temp_char_accum[x];
674                 else *charp= (*linep)->len;
675         }
676         if(!sel) txt_pop_sel(text);
677 }
678
679 static int get_wrap_width(SpaceText *st) {
680         int x, max;
681         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
682         max= (curarea->winx-x)/spacetext_get_fontwidth(st);
683         return max>8 ? max : 8;
684 }
685
686 /* Returns the number of wrap points (or additional lines) in the given string */
687 static int get_wrap_points(SpaceText *st, char *line) {
688         int start, end, taboffs, i, max, count;
689         
690         if (!st->wordwrap) return 0;
691
692         end= max= get_wrap_width(st);
693         count= taboffs= start= 0;
694
695         for (i=0; line[i]!='\0'; i++) {
696                 if (i-start+taboffs>=max) {
697                         count++;
698                         start= end;
699                         end += max;
700                         taboffs= 0;
701                 } else if (line[i]==' ' || line[i]=='\t' || line[i]=='-') {
702                         end = i+1;
703                         if (line[i]=='\t')
704                                 taboffs += st->tabnumber-(i-start)%st->tabnumber;
705                 }
706         }
707         return count;
708 }
709
710 /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
711 static void wrap_offset(SpaceText *st, TextLine *linein, int cursin, int *offl, int *offc) {
712         Text *text;
713         TextLine *linep;
714         int i, j, start, end, chars, max, chop;
715         char ch;
716
717         *offl= *offc= 0;
718
719         if (!st->text) return;
720         if (!st->wordwrap) return;
721
722         text= st->text;
723
724         /* Move pointer to first visible line (top) */
725         linep= text->lines.first;
726         i= st->top;
727         while (i>0 && linep) {
728                 if (linep == linein) return; /* Line before top */
729                 linep= linep->next;
730                 i--;
731         }
732
733         max= get_wrap_width(st);
734
735         while (linep) {
736                 start= 0;
737                 end= max;
738                 chop= 1;
739                 chars= 0;
740                 *offc= 0;
741                 for (i=0, j=0; linep->line[j]!='\0'; j++) {
742
743                         /* Mimic replacement of tabs */
744                         ch= linep->line[j];
745                         if (ch=='\t') {
746                                 chars= st->tabnumber-i%st->tabnumber;
747                                 if (linep==linein && i<cursin) cursin += chars-1;
748                                 ch= ' ';
749                         } else
750                                 chars= 1;
751
752                         while (chars--) {
753                                 if (i-start>=max) {
754                                         if (chop && linep==linein && i >= cursin)
755                                                 return;
756                                         (*offl)++;
757                                         *offc -= end-start;
758                                         start= end;
759                                         end += max;
760                                         chop= 1;
761                                 } else if (ch==' ' || ch=='-') {
762                                         end = i+1;
763                                         chop= 0;
764                                         if (linep==linein && i >= cursin)
765                                                 return;
766                                 }
767                                 i++;
768                         }
769                 }
770                 if (linep==linein) break;
771                 linep= linep->next;
772         }
773 }
774
775 static int get_char_pos(SpaceText *st, char *line, int cur) {
776         int a=0, i;
777         for (i=0; i<cur && line[i]; i++) {
778                 if (line[i]=='\t')
779                         a += st->tabnumber-a%st->tabnumber;
780                 else
781                         a++;
782         }
783         return a;
784 }
785
786 static void draw_markers(SpaceText *st) {
787         Text *text= st->text;
788         TextMarker *marker, *next;
789         TextLine *top, *bottom, *line;
790         int offl, offc, i, cy, x1, x2, y1, y2, x, y;
791
792         for (i=st->top, top= text->lines.first; top->next && i>0; i--) top= top->next;
793         for (i=st->viewlines-1, bottom=top; bottom->next && i>0; i--) bottom= bottom->next;
794         
795         for (marker= text->markers.first; marker; marker= next) {
796                 next= marker->next;
797                 for (cy= 0, line= top; line; cy++, line= line->next) {
798                         if (cy+st->top==marker->lineno) {
799                                 /* Remove broken markers */
800                                 if (marker->end>line->len || marker->start>marker->end) {
801                                         BLI_freelinkN(&text->markers, marker);
802                                         break;
803                                 }
804
805                                 wrap_offset(st, line, marker->start, &offl, &offc);
806                                 x1= get_char_pos(st, line->line, marker->start) - st->left + offc;
807                                 y1= cy + offl;
808                                 wrap_offset(st, line, marker->end, &offl, &offc);
809                                 x2= get_char_pos(st, line->line, marker->end) - st->left + offc;
810                                 y2= cy + offl;
811
812                                 glColor3ub(marker->clr[0], marker->clr[1], marker->clr[2]);
813                                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
814                                 y= curarea->winy-3;
815
816                                 if (y1==y2) {
817                                         y -= y1*st->lheight;
818                                         glBegin(GL_LINE_LOOP);
819                                         glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y);
820                                         glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y);
821                                         glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y-st->lheight);
822                                         glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y-st->lheight);
823                                         glEnd();
824                                 } else {
825                                         y -= y1*st->lheight;
826                                         glBegin(GL_LINE_STRIP);
827                                         glVertex2i(curarea->winx, y);
828                                         glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y);
829                                         glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y-st->lheight);
830                                         glVertex2i(curarea->winx, y-st->lheight);
831                                         glEnd();
832                                         y-=st->lheight;
833                                         for (i=y1+1; i<y2; i++) {
834                                                 glBegin(GL_LINES);
835                                                 glVertex2i(x, y);
836                                                 glVertex2i(curarea->winx, y);
837                                                 glVertex2i(x, y-st->lheight);
838                                                 glVertex2i(curarea->winx, y-st->lheight);
839                                                 glEnd();
840                                                 y-=st->lheight;
841                                         }
842                                         glBegin(GL_LINE_STRIP);
843                                         glVertex2i(x, y);
844                                         glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y);
845                                         glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y-st->lheight);
846                                         glVertex2i(x, y-st->lheight);
847                                         glEnd();
848                                 }
849
850                                 break;
851                         }
852                         if (line==bottom) break;
853                 }
854         }
855 }
856
857 static void draw_cursor(SpaceText *st) {
858         Text *text= st->text;
859         int vcurl, vcurc, vsell, vselc, hidden=0;
860         int offl, offc, x, y, w, i;
861         
862         /* Draw the selection */
863         if (text->curl!=text->sell || text->curc!=text->selc) {
864
865                 /* Convert all to view space character coordinates */
866                 wrap_offset(st, text->curl, text->curc, &offl, &offc);
867                 vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
868                 vcurc = get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
869                 wrap_offset(st, text->sell, text->selc, &offl, &offc);
870                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
871                 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
872
873                 if (vcurc<0) vcurc=0;
874                 if (vselc<0) vselc=0, hidden=1;
875                 
876                 BIF_ThemeColor(TH_SHADE2);
877                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
878                 y= curarea->winy-2;
879
880                 if (vcurl==vsell) {
881                         y -= vcurl*st->lheight;
882                         if (vcurc < vselc)
883                                 glRecti(x+vcurc*spacetext_get_fontwidth(st)-1, y, x+vselc*spacetext_get_fontwidth(st), y-st->lheight);
884                         else
885                                 glRecti(x+vselc*spacetext_get_fontwidth(st)-1, y, x+vcurc*spacetext_get_fontwidth(st), y-st->lheight);
886                 } else {
887                         int froml, fromc, tol, toc;
888                         if (vcurl < vsell) {
889                                 froml= vcurl; tol= vsell;
890                                 fromc= vcurc; toc= vselc;
891                         } else {
892                                 froml= vsell; tol= vcurl;
893                                 fromc= vselc; toc= vcurc;
894                         }
895                         y -= froml*st->lheight;
896                         glRecti(x+fromc*spacetext_get_fontwidth(st)-1, y, curarea->winx, y-st->lheight); y-=st->lheight;
897                         for (i=froml+1; i<tol; i++)
898                                 glRecti(x-4, y, curarea->winx, y-st->lheight),  y-=st->lheight;
899                         glRecti(x-4, y, x+toc*spacetext_get_fontwidth(st), y-st->lheight);  y-=st->lheight;
900                 }
901         } else {
902                 wrap_offset(st, text->sell, text->selc, &offl, &offc);
903                 vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
904                 vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
905                 if (vselc<0) vselc=0, hidden=1;
906         }
907
908         if (!hidden) {
909                 /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
910                 x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
911                 x += vselc*spacetext_get_fontwidth(st);
912                 y= curarea->winy-2 - vsell*st->lheight;
913                 
914                 if (st->overwrite) {
915                         char ch= text->sell->line[text->selc];
916                         if (!ch) ch= ' ';
917                         w= BMF_GetCharacterWidth(spacetext_get_font(st), ch);
918                         BIF_ThemeColor(TH_HILITE);
919                         glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
920                 } else {
921                         BIF_ThemeColor(TH_HILITE);
922                         glRecti(x-1, y, x+1, y-st->lheight);
923                 }
924         }
925 }
926
927 static void calc_text_rcts(SpaceText *st)
928 {
929         int lhlstart, lhlend, ltexth;
930         short barheight, barstart, hlstart, hlend, blank_lines;
931         short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
932
933         pix_top_margin = 8;
934         pix_bottom_margin = 4;
935         pix_available = curarea->winy - pix_top_margin - pix_bottom_margin;
936         ltexth= txt_get_span(st->text->lines.first, st->text->lines.last);
937         blank_lines = st->viewlines / 2;
938         
939         /* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
940         if (ltexth + blank_lines < st->top + st->viewlines) {
941                 blank_lines = st->top + st->viewlines - ltexth;
942         }
943         
944         ltexth += blank_lines;
945
946         barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
947         pix_bardiff = 0;
948         if (barheight < 20) {
949                 pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */      
950                 barheight = 20;
951         }
952         barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;
953
954         st->txtbar.xmin = 5;
955         st->txtbar.xmax = 17;
956         st->txtbar.ymax = curarea->winy - pix_top_margin - barstart;
957         st->txtbar.ymin = st->txtbar.ymax - barheight;
958
959         CLAMP(st->txtbar.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
960         CLAMP(st->txtbar.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);
961
962         st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
963         if (st->pix_per_line<.1) st->pix_per_line=.1f;
964
965         lhlstart = MIN2(txt_get_span(st->text->lines.first, st->text->curl), 
966                                 txt_get_span(st->text->lines.first, st->text->sell));
967         lhlend = MAX2(txt_get_span(st->text->lines.first, st->text->curl), 
968                                 txt_get_span(st->text->lines.first, st->text->sell));
969
970         if(ltexth > 0) {
971                 hlstart = (lhlstart * pix_available)/ltexth;
972                 hlend = (lhlend * pix_available)/ltexth;
973
974                 /* the scrollbar is non-linear sized */
975                 if (pix_bardiff > 0) {
976                         /* the start of the highlight is in the current viewport */
977                         if (ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) { 
978                                 /* speed the progresion of the start of the highlight through the scrollbar */
979                                 hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);       
980                         }
981                         else if (lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
982                                 /* push hl start down */
983                                 hlstart = barstart + barheight;
984                         }
985                         else if (lhlend > st->top  && lhlstart < st->top && hlstart > barstart) {
986                                 /*fill out start */
987                                 hlstart = barstart;
988                         }
989
990                         if (hlend <= hlstart) { 
991                                 hlend = hlstart + 2;
992                         }
993
994                         /* the end of the highlight is in the current viewport */
995                         if (ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) { 
996                                 /* speed the progresion of the end of the highlight through the scrollbar */
997                                 hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines);    
998                         }
999                         else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
1000                                 /* push hl end up */
1001                                 hlend = barstart;
1002                         }                                       
1003                         else if (lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
1004                                 /* fill out end */
1005                                 hlend = barstart + barheight;
1006                         }
1007
1008                         if (hlend <= hlstart) { 
1009                                 hlstart = hlend - 2;
1010                         }       
1011                 }       
1012         }
1013         else {
1014                 hlstart = 0;
1015                 hlend = 0;
1016         }
1017
1018         if (hlend - hlstart < 2) { 
1019                 hlend = hlstart + 2;
1020         }
1021         
1022         st->txtscroll.xmin= 5;
1023         st->txtscroll.xmax= 17;
1024         st->txtscroll.ymax= curarea->winy - pix_top_margin - hlstart;
1025         st->txtscroll.ymin= curarea->winy - pix_top_margin - hlend;
1026
1027         CLAMP(st->txtscroll.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
1028         CLAMP(st->txtscroll.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);
1029 }
1030
1031 static void draw_textscroll(SpaceText *st)
1032 {
1033         if (!st->text) return;
1034
1035         calc_text_rcts(st);
1036         
1037         BIF_ThemeColorShade(TH_SHADE1, -20);
1038         glRecti(2, 2, 20, curarea->winy-6);
1039         uiEmboss(2, 2, 20, curarea->winy-6, 1);
1040
1041         BIF_ThemeColor(TH_SHADE1);
1042         glRecti(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax);
1043
1044         BIF_ThemeColor(TH_SHADE2);
1045         glRecti(st->txtscroll.xmin, st->txtscroll.ymin, st->txtscroll.xmax, st->txtscroll.ymax);
1046
1047         uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
1048 }
1049
1050 static void screen_skip(SpaceText *st, int lines)
1051 {
1052         int last;
1053         
1054         if (!st) return;
1055         if (st->spacetype != SPACE_TEXT) return;
1056         if (!st->text) return;
1057
1058         st->top += lines;
1059
1060         last= txt_get_span(st->text->lines.first, st->text->lines.last);
1061         last= last - (st->viewlines/2);
1062         
1063         if (st->top>last) st->top= last;
1064         if (st->top<0) st->top= 0;
1065 }
1066
1067 static void cursor_skip(SpaceText *st, int lines, int sel)
1068 {
1069         Text *text;
1070         TextLine **linep;
1071         int oldl, oldc, *charp;
1072         
1073         if (!st) return;
1074         if (st->spacetype != SPACE_TEXT) return;
1075         if (!st->text) return;
1076
1077         text= st->text;
1078
1079         if (sel) linep= &text->sell, charp= &text->selc;
1080         else linep= &text->curl, charp= &text->curc;
1081         oldl= txt_get_span(text->lines.first, *linep);
1082         oldc= *charp;
1083
1084         while (lines>0 && (*linep)->next) {
1085                 *linep= (*linep)->next;
1086                 lines--;
1087         }
1088         while (lines<0 && (*linep)->prev) {
1089                 *linep= (*linep)->prev;
1090                 lines++;
1091         }
1092
1093         if (*charp > (*linep)->len) *charp= (*linep)->len;
1094
1095         if (!sel) txt_pop_sel(st->text);
1096         txt_undo_add_toop(st->text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
1097 }
1098
1099 /* 
1100  * mode 1 == view scroll
1101  * mode 2 == scrollbar
1102  */
1103 static void do_textscroll(SpaceText *st, int mode)
1104 {
1105         short delta[2]= {0, 0};
1106         short mval[2], hold[2], old[2];
1107         
1108         if (!st->text) return;
1109         
1110         calc_text_rcts(st);
1111
1112         st->flags|= ST_SCROLL_SELECT;
1113
1114         scrarea_do_windraw(curarea);
1115         screen_swapbuffers();
1116
1117         getmouseco_areawin(mval);
1118         old[0]= hold[0]= mval[0];
1119         old[1]= hold[1]= mval[1];
1120
1121         while(get_mbut()&(L_MOUSE|M_MOUSE)) {
1122                 getmouseco_areawin(mval);
1123
1124                 if(old[0]!=mval[0] || old[1]!=mval[1]) {
1125                         if (mode==1) {
1126                                 delta[0]= (hold[0]-mval[0])/spacetext_get_fontwidth(st);
1127                                 delta[1]= (mval[1]-hold[1])/st->lheight;
1128                         }
1129                         else delta[1]= (hold[1]-mval[1])*st->pix_per_line;
1130                         
1131                         if (delta[0] || delta[1]) {
1132                                 screen_skip(st, delta[1]);
1133                                 if (st->wordwrap) {
1134                                         st->left= 0;
1135                                 } else {
1136                                         st->left+= delta[0];
1137                                         if (st->left<0) st->left= 0;
1138                                 }
1139                                 scrarea_do_windraw(curarea);
1140                                 screen_swapbuffers();
1141                                 
1142                                 hold[0]=mval[0];
1143                                 hold[1]=mval[1];
1144                         }
1145                         old[0]=mval[0];
1146                         old[1]=mval[1];
1147                 } else {
1148                         BIF_wait_for_statechange();
1149                 }
1150         }
1151         st->flags^= ST_SCROLL_SELECT;
1152
1153         scrarea_do_windraw(curarea);
1154         screen_swapbuffers();
1155 }
1156
1157 static void do_selection(SpaceText *st, int selecting)
1158 {
1159         short mval[2], old[2];
1160         int sell, selc;
1161         int linep2, charp2;
1162         int first= 1;
1163
1164         getmouseco_areawin(mval);
1165         old[0]= mval[0];
1166         old[1]= mval[1];
1167
1168         if (!selecting) {
1169                 int curl= txt_get_span(st->text->lines.first, st->text->curl);
1170                 int curc= st->text->curc;                       
1171                 int linep2, charp2;
1172                                         
1173                 set_cursor_to_pos(st, mval[0], mval[1], 0);
1174
1175                 linep2= txt_get_span(st->text->lines.first, st->text->curl);
1176                 charp2= st->text->selc;
1177                                 
1178                 if (curl!=linep2 || curc!=charp2)
1179                         txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
1180         }
1181
1182         sell= txt_get_span(st->text->lines.first, st->text->sell);
1183         selc= st->text->selc;
1184
1185         while(get_mbut()&L_MOUSE) {
1186                 getmouseco_areawin(mval);
1187
1188                 if (mval[1]<0 || mval[1]>curarea->winy) {
1189                         int d= (old[1]-mval[1])*st->pix_per_line;
1190                         if (d) screen_skip(st, d);
1191
1192                         set_cursor_to_pos(st, mval[0], mval[1]<0?0:curarea->winy, 1);
1193
1194                         scrarea_do_windraw(curarea);
1195                         screen_swapbuffers();
1196                 } else if (!st->wordwrap && (mval[0]<0 || mval[0]>curarea->winx)) {
1197                         if (mval[0]>curarea->winx) st->left++;
1198                         else if (mval[0]<0 && st->left>0) st->left--;
1199                         
1200                         set_cursor_to_pos(st, mval[0], mval[1], 1);
1201                         
1202                         scrarea_do_windraw(curarea);
1203                         screen_swapbuffers();
1204                         
1205                         PIL_sleep_ms(10);
1206                 } else if (first || old[0]!=mval[0] || old[1]!=mval[1]) {
1207                         set_cursor_to_pos(st, mval[0], mval[1], 1);
1208
1209                         scrarea_do_windraw(curarea);
1210                         screen_swapbuffers();
1211
1212                         old[0]= mval[0];
1213                         old[1]= mval[1];
1214                         first= 1;
1215                 } else {
1216                         BIF_wait_for_statechange();
1217                 }
1218         }
1219
1220         linep2= txt_get_span(st->text->lines.first, st->text->sell);
1221         charp2= st->text->selc;
1222                 
1223         if (sell!=linep2 || selc!=charp2)
1224                 txt_undo_add_toop(st->text, UNDO_STO, sell, selc, linep2, charp2);
1225
1226         pop_space_text(st);
1227 }
1228
1229 static int do_suggest_select(SpaceText *st)
1230 {
1231         SuggItem *item, *first, *last, *sel;
1232         short mval[2];
1233         TextLine *tmp;
1234         int l, x, y, w, h, i;
1235         int tgti, *top;
1236         
1237         if (!st || !st->text) return 0;
1238         if (!texttool_text_is_active(st->text)) return 0;
1239
1240         first = texttool_suggest_first();
1241         last = texttool_suggest_last();
1242         sel = texttool_suggest_selected();
1243         top = texttool_suggest_top();
1244
1245         if (!last || !first)
1246                 return 0;
1247
1248         /* Count the visible lines to the cursor */
1249         for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1250         if (l<0) return 0;
1251         
1252         if(st->showlinenrs) {
1253                 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1254         } else {
1255                 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1256         }
1257         y = curarea->winy - st->lheight*l - 2;
1258
1259         w = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
1260         h = SUGG_LIST_SIZE*st->lheight + 8;
1261
1262         getmouseco_areawin(mval);
1263
1264         if (mval[0]<x || x+w<mval[0] || mval[1]<y-h || y<mval[1])
1265                 return 0;
1266
1267         /* Work out which of the items is at the top of the visible list */
1268         for (i=0, item=first; i<*top && item->next; i++, item=item->next);
1269
1270         /* Work out the target item index in the visible list */
1271         tgti = (y-mval[1]-4) / st->lheight;
1272         if (tgti<0 || tgti>SUGG_LIST_SIZE)
1273                 return 1;
1274
1275         for (i=tgti; i>0 && item->next; i--, item=item->next);
1276         if (item)
1277                 texttool_suggest_select(item);
1278         return 1;
1279 }
1280
1281 static void pop_suggest_list() {
1282         SuggItem *item, *sel;
1283         int *top, i;
1284
1285         item= texttool_suggest_first();
1286         sel= texttool_suggest_selected();
1287         top= texttool_suggest_top();
1288
1289         i= 0;
1290         while (item && item != sel) {
1291                 item= item->next;
1292                 i++;
1293         }
1294         if (i > *top+SUGG_LIST_SIZE-1)
1295                 *top= i-SUGG_LIST_SIZE+1;
1296         else if (i < *top)
1297                 *top= i;
1298 }
1299
1300 void draw_documentation(SpaceText *st)
1301 {
1302         TextLine *tmp;
1303         char *docs, buf[DOC_WIDTH+1], *p;
1304         int len, i, br, lines;
1305         int boxw, boxh, l, x, y, top;
1306         
1307         if (!st || !st->text) return;
1308         if (!texttool_text_is_active(st->text)) return;
1309         
1310         docs = texttool_docs_get();
1311
1312         if (!docs) return;
1313
1314         /* Count the visible lines to the cursor */
1315         for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1316         if (l<0) return;
1317         
1318         if(st->showlinenrs) {
1319                 x= spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1320         } else {
1321                 x= spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1322         }
1323         if (texttool_suggest_first()) {
1324                 x += SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 50;
1325         }
1326
1327         top= y= curarea->winy - st->lheight*l - 2;
1328         len= strlen(docs);
1329         boxw= DOC_WIDTH*spacetext_get_fontwidth(st) + 20;
1330         boxh= (DOC_HEIGHT+1)*st->lheight;
1331
1332         /* Draw panel */
1333         BIF_ThemeColor(TH_BACK);
1334         glRecti(x, y, x+boxw, y-boxh);
1335         BIF_ThemeColor(TH_SHADE1);
1336         glBegin(GL_LINE_LOOP);
1337         glVertex2i(x, y);
1338         glVertex2i(x+boxw, y);
1339         glVertex2i(x+boxw, y-boxh);
1340         glVertex2i(x, y-boxh);
1341         glEnd();
1342         glBegin(GL_LINE_LOOP);
1343         glVertex2i(x+boxw-10, y-7);
1344         glVertex2i(x+boxw-4, y-7);
1345         glVertex2i(x+boxw-7, y-2);
1346         glEnd();
1347         glBegin(GL_LINE_LOOP);
1348         glVertex2i(x+boxw-10, y-boxh+7);
1349         glVertex2i(x+boxw-4, y-boxh+7);
1350         glVertex2i(x+boxw-7, y-boxh+2);
1351         glEnd();
1352         BIF_ThemeColor(TH_TEXT);
1353
1354         i= 0; br= DOC_WIDTH; lines= -doc_scroll;
1355         for (p=docs; *p; p++) {
1356                 if (*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */
1357                 if (*p == ' ' || *p == '\t')
1358                         br= i;
1359                 else if (*p == '\n') {
1360                         buf[i]= '\0';
1361                         if (lines>=0) {
1362                                 y -= st->lheight;
1363                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1364                         }
1365                         i= 0; br= DOC_WIDTH; lines++;
1366                 }
1367                 buf[i++]= *p;
1368                 if (i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
1369                         buf[br]= '\0';
1370                         if (lines>=0) {
1371                                 y -= st->lheight;
1372                                 text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
1373                         }
1374                         p -= i-br-1; /* Rewind pointer to last break */
1375                         i= 0; br= DOC_WIDTH; lines++;
1376                 }
1377                 if (lines >= DOC_HEIGHT) break;
1378         }
1379         if (doc_scroll > 0 && lines < DOC_HEIGHT) {
1380                 doc_scroll--;
1381                 draw_documentation(st);
1382         }
1383 }
1384
1385 void draw_suggestion_list(SpaceText *st)
1386 {
1387         SuggItem *item, *first, *last, *sel;
1388         TextLine *tmp;
1389         char str[SUGG_LIST_WIDTH+1];
1390         int w, boxw=0, boxh, i, l, x, y, b, *top;
1391         
1392         if (!st || !st->text) return;
1393         if (!texttool_text_is_active(st->text)) return;
1394
1395         first = texttool_suggest_first();
1396         last = texttool_suggest_last();
1397
1398         if (!first || !last) return;
1399
1400         pop_suggest_list();
1401         sel = texttool_suggest_selected();
1402         top = texttool_suggest_top();
1403
1404         /* Count the visible lines to the cursor */
1405         for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
1406         if (l<0) return;
1407         
1408         if(st->showlinenrs) {
1409                 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
1410         } else {
1411                 x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
1412         }
1413         y = curarea->winy - st->lheight*l - 2;
1414
1415         boxw = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
1416         boxh = SUGG_LIST_SIZE*st->lheight + 8;
1417         
1418         BIF_ThemeColor(TH_SHADE1);
1419         glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
1420         BIF_ThemeColor(TH_BACK);
1421         glRecti(x, y, x+boxw, y-boxh);
1422
1423         /* Set the top 'item' of the visible list */
1424         for (i=0, item=first; i<*top && item->next; i++, item=item->next);
1425
1426         for (i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {
1427
1428                 y -= st->lheight;
1429
1430                 strncpy(str, item->name, SUGG_LIST_WIDTH);
1431                 str[SUGG_LIST_WIDTH] = '\0';
1432
1433                 w = BMF_GetStringWidth(spacetext_get_font(st), str);
1434                 
1435                 if (item == sel) {
1436                         BIF_ThemeColor(TH_SHADE2);
1437                         glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
1438                 }
1439                 b=1; /* b=1 colour block, text is default. b=0 no block, colour text */
1440                 switch (item->type) {
1441                         case 'k': BIF_ThemeColor(TH_SYNTAX_B); b=0; break;
1442                         case 'm': BIF_ThemeColor(TH_TEXT); break;
1443                         case 'f': BIF_ThemeColor(TH_SYNTAX_L); break;
1444                         case 'v': BIF_ThemeColor(TH_SYNTAX_N); break;
1445                         case '?': BIF_ThemeColor(TH_TEXT); b=0; break;
1446                 }
1447                 if (b) {
1448                         glRecti(x+8, y+2, x+11, y+5);
1449                         BIF_ThemeColor(TH_TEXT);
1450                 }
1451                 text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);
1452
1453                 if (item == last) break;
1454         }
1455 }
1456
1457 static short check_blockhandler(SpaceText *st, short handler) {
1458         short a;
1459         for(a=0; a<SPACE_MAXHANDLER; a+=2)
1460                 if (st->blockhandler[a]==handler) return 1;
1461         return 0;
1462 }
1463
1464 static void text_panel_find(short cntrl)        // TEXT_HANDLER_FIND
1465 {
1466         uiBlock *block;
1467
1468         if (!g_find_str || !g_replace_str) {
1469                 g_find_str= MEM_mallocN(TXT_MAXFINDSTR+1, "find_string");
1470                 g_replace_str= MEM_mallocN(TXT_MAXFINDSTR+1, "replace_string");
1471                 g_find_str[0]= g_replace_str[0]= '\0';
1472         }
1473         
1474         block= uiNewBlock(&curarea->uiblocks, "text_panel_find", UI_EMBOSS, UI_HELV, curarea->win);
1475         uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
1476         uiSetPanelHandler(TEXT_HANDLER_FIND);  // for close and esc
1477         if(uiNewPanel(curarea, block, "Find & Replace", "Text", curarea->winx-230, curarea->winy-130, 260, 120)==0) return;
1478         
1479         uiBlockBeginAlign(block);
1480         uiDefButC(block, TEX, 0, "Find: ", 0,80,220,20, g_find_str, 0,(float)TXT_MAXFINDSTR, 0,0, "");
1481         uiDefIconBut(block, BUT, B_PASTEFIND, ICON_TEXT, 220,80,20,20, NULL, 0,0,0,0, "Copy from selection");
1482         uiDefButC(block, TEX, 0, "Replace: ", 0,60,220,20, g_replace_str, 0,(float)TXT_MAXFINDSTR, 0,0, "");
1483         uiDefIconBut(block, BUT, B_PASTEREPLACE, ICON_TEXT, 220,60,20,20, NULL, 0,0,0,0, "Copy from selection");
1484         uiBlockEndAlign(block);
1485         uiDefButBitI(block, TOG, TXT_FIND_WRAP,    0,"Wrap Around", 0,30,110,20,&g_find_flags,0,0,0,0,"Wrap search around current text");
1486         uiDefButBitI(block, TOG, TXT_FIND_ALLTEXTS,0,"Search All Texts",  110,30,130,20,&g_find_flags,0,0,0,0,"Search in each text");
1487         uiDefBut(block, BUT, B_TEXTFIND,    "Find",       0,0,50,20, NULL, 0,0,0,0, "Find next");
1488         uiDefBut(block, BUT, B_TEXTREPLACE, "Replace/Find", 50,0,110,20, NULL, 0,0,0,0, "Replace then find next");
1489         uiDefBut(block, BUT, B_TEXTMARKALL, "Mark All",   160,0,80,20, NULL, 0,0,0,0, "Mark each occurrence to edit all from one");
1490 }
1491
1492 /* mode: 0 find only, 1 replace/find, 2 mark all occurrences */
1493 void find_and_replace(SpaceText *st, short mode) {
1494         char *tmp;
1495         Text *start= NULL, *text= st->text;
1496         int flags, first= 1;
1497
1498         if (!check_blockhandler(st, TEXT_HANDLER_FIND)) {
1499                 toggle_blockhandler(st->area, TEXT_HANDLER_FIND, UI_PNL_TO_MOUSE);
1500                 return;
1501         }
1502
1503         if (!g_find_str || !g_replace_str) return;
1504         if (g_find_str[0] == '\0') return;
1505         flags= g_find_flags;
1506         if (flags & TXT_FIND_ALLTEXTS) flags ^= TXT_FIND_WRAP;
1507
1508         do {
1509                 if (first)
1510                         txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
1511                 first= 0;
1512                 
1513                 /* Replace current */
1514                 if (mode && txt_has_sel(text)) {
1515                         tmp= txt_sel_to_buf(text);
1516                         if (strcmp(g_find_str, tmp)==0) {
1517                                 if (mode==1) {
1518                                         txt_insert_buf(text, g_replace_str);
1519                                         if (st->showsyntax) txt_format_line(st, text->curl, 1);
1520                                 } else if (mode==2) {
1521                                         char clr[4];
1522                                         BIF_GetThemeColor4ubv(TH_SHADE2, clr);
1523                                         if (txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
1524                                                 if (tmp) MEM_freeN(tmp), tmp=NULL;
1525                                                 break;
1526                                         }
1527                                         txt_add_marker(text, text->curl, text->curc, text->selc, clr, TMARK_GRP_FINDALL, TMARK_EDITALL);
1528                                 }
1529                         }
1530                         MEM_freeN(tmp);
1531                         tmp= NULL;
1532                 }
1533
1534                 /* Find next */
1535                 if (txt_find_string(text, g_find_str, flags & TXT_FIND_WRAP)) {
1536                         pop_space_text(st);
1537                 } else if (flags & TXT_FIND_ALLTEXTS) {
1538                         if (text==start) break;
1539                         if (!start) start= text;
1540                         if (text->id.next)
1541                                 text= st->text= text->id.next;
1542                         else
1543                                 text= st->text= G.main->text.first;
1544                         txt_move_toline(text, 0, 0);
1545                         pop_space_text(st);
1546                         first= 1;
1547                 } else {
1548                         okee("Text not found: %s", g_find_str);
1549                         break;
1550                 }
1551         } while (mode==2);
1552 }
1553
1554 static void do_find_buttons(val) {
1555         Text *text;
1556         SpaceText *st;
1557         int do_draw= 0;
1558         char *tmp;
1559
1560         st= curarea->spacedata.first;
1561         if (!st || st->spacetype != SPACE_TEXT) return;
1562         text= st->text;
1563         if (!text) return;
1564
1565         switch (val) {
1566                 case B_PASTEFIND:
1567                         if (!g_find_str) break;
1568                         tmp= txt_sel_to_buf(text);
1569                         strncpy(g_find_str, tmp, TXT_MAXFINDSTR);
1570                         MEM_freeN(tmp);
1571                         do_draw= 1;
1572                         break;
1573                 case B_PASTEREPLACE:
1574                         if (!g_replace_str) break;
1575                         tmp= txt_sel_to_buf(text);
1576                         strncpy(g_replace_str, tmp, TXT_MAXFINDSTR);
1577                         MEM_freeN(tmp);
1578                         do_draw= 1;
1579                         break;
1580                 case B_TEXTFIND:
1581                         find_and_replace(st, 0);
1582                         do_draw= 1;
1583                         break;
1584                 case B_TEXTREPLACE:
1585                         find_and_replace(st, 1);
1586                         do_draw= 1;
1587                         break;
1588                 case B_TEXTMARKALL:
1589                         find_and_replace(st, 2);
1590                         do_draw= 1;
1591                         break;
1592         }
1593 }
1594
1595 static void text_blockhandlers(ScrArea *sa)
1596 {
1597         SpaceText *st= sa->spacedata.first;
1598         short a;
1599
1600         /* warning; blocks need to be freed each time, handlers dont remove */
1601         uiFreeBlocksWin(&sa->uiblocks, sa->win);
1602         
1603         for(a=0; a<SPACE_MAXHANDLER; a+=2) {
1604                 /* clear action value for event */
1605                 switch(st->blockhandler[a]) {
1606                         case TEXT_HANDLER_FIND:
1607                                 text_panel_find(st->blockhandler[a+1]);
1608                                 break;
1609                 }
1610         }
1611         uiDrawBlocksPanels(sa, 0);
1612 }
1613
1614 void drawtextspace(ScrArea *sa, void *spacedata)
1615 {
1616         SpaceText *st= curarea->spacedata.first;
1617         Text *text;
1618         int i, x, y;
1619         TextLine *tmp;
1620         char linenr[12];
1621         float col[3];
1622         int linecount = 0;
1623
1624         if (st==NULL || st->spacetype != SPACE_TEXT) return;
1625
1626         bwin_clear_viewmat(sa->win);    /* clear buttons view */
1627         glLoadIdentity();
1628         
1629         BIF_GetThemeColor3fv(TH_BACK, col);
1630         glClearColor(col[0], col[1], col[2], 0.0);
1631         glClear(GL_COLOR_BUFFER_BIT);
1632         myortho2(-0.375, (float)(sa->winx)-0.375, -0.375, (float)(sa->winy)-0.375);
1633
1634         draw_area_emboss(sa);
1635
1636         text= st->text;
1637         if(!text) return;
1638         
1639         /* Make sure all the positional pointers exist */
1640         if (!text->curl || !text->sell || !text->lines.first || !text->lines.last)
1641                 txt_clean_text(text);
1642         
1643         if(st->lheight) st->viewlines= (int) curarea->winy/st->lheight;
1644         else st->viewlines= 0;
1645         
1646         if(st->showlinenrs) {
1647                 cpack(0x8c787c);
1648                 glRecti(23,  0, (st->lheight==15)?63:59,  curarea->winy - 2);
1649         }
1650
1651         draw_cursor(st);
1652
1653         tmp= text->lines.first;
1654         for (i= 0; i<st->top && tmp; i++) {
1655                 if (st->showsyntax && !tmp->format) txt_format_line(st, tmp, 0);
1656                 tmp= tmp->next;
1657                 linecount++;
1658         }
1659
1660         y= curarea->winy-st->lheight;
1661         x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
1662
1663         BIF_ThemeColor(TH_TEXT);
1664         for (i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) {
1665                 if (st->showsyntax && !tmp->format) {
1666                         txt_format_line(st, tmp, 0);
1667                 }
1668                 if(st->showlinenrs) {
1669                         /*Change the color of the current line the cursor is on*/
1670                         if(tmp == text->curl) { 
1671                                 BIF_ThemeColor(TH_HILITE);
1672                         } else {
1673                                 BIF_ThemeColor(TH_TEXT);
1674                         }
1675                         if(((float)(i + linecount + 1)/10000.0) < 1.0) {
1676                                 sprintf(linenr, "%4d", i + linecount + 1);
1677                                 glRasterPos2i(TXT_OFFSET - 7, y);
1678                         } else {
1679                                 sprintf(linenr, "%5d", i + linecount + 1);
1680                                 glRasterPos2i(TXT_OFFSET - 11, y);
1681                         }
1682                         BIF_ThemeColor(TH_TEXT);
1683                         BMF_DrawString(spacetext_get_font(st), linenr);
1684                 }
1685                 if (st->wordwrap) {
1686                         int lines = text_draw_wrapped(st, tmp->line, x, y, curarea->winx-x, tmp->format);
1687                         y -= lines*st->lheight;
1688                 } else {
1689                         text_draw(st, tmp->line, st->left, 0, 1, x, y, tmp->format);
1690                         y -= st->lheight;
1691                 }
1692         }
1693         
1694         draw_brackets(st);
1695         draw_markers(st);
1696
1697         draw_textscroll(st);
1698         draw_documentation(st);
1699         draw_suggestion_list(st);
1700         
1701         bwin_scalematrix(sa->win, st->blockscale, st->blockscale, st->blockscale);
1702         text_blockhandlers(sa);
1703         
1704         curarea->win_swap= WIN_BACK_OK;
1705 }
1706
1707 /* Moves the view to the cursor location,
1708   also used to make sure the view isnt outside the file */
1709 void pop_space_text (SpaceText *st)
1710 {
1711         int i, x;
1712
1713         if(!st) return;
1714         if(!st->text) return;
1715         if(!st->text->curl) return;
1716                 
1717         i= txt_get_span(st->text->lines.first, st->text->sell);
1718         if (st->top+st->viewlines <= i || st->top > i) {
1719                 st->top= i - st->viewlines/2;
1720         }
1721         
1722         if (st->wordwrap) {
1723                 st->left= 0;
1724         } else {
1725                 x= text_draw(st, st->text->sell->line, st->left, st->text->selc, 0, 0, 0, NULL);
1726
1727                 if (x==0 || x>curarea->winx) {
1728                         st->left= st->text->curc-0.5*(curarea->winx)/spacetext_get_fontwidth(st);
1729                 }
1730         }
1731
1732         if (st->top < 0) st->top= 0;
1733         if (st->left <0) st->left= 0;
1734 }
1735
1736 void add_text_fs(char *file) /* bad but cant pass an as arg here */
1737 {
1738         SpaceText *st= curarea->spacedata.first;
1739         Text *text;
1740
1741         if (st==NULL || st->spacetype != SPACE_TEXT) return;
1742
1743         text= add_text(file);
1744
1745         st->text= text;
1746
1747         st->top= 0;
1748
1749         if (st->showsyntax) txt_format_text(st);
1750         allqueue(REDRAWTEXT, 0);
1751         allqueue(REDRAWHEADERS, 0);     
1752 }
1753
1754 void free_textspace(SpaceText *st)
1755 {
1756         if (!st) return;
1757
1758         st->text= NULL;
1759 }
1760
1761 /* returns 0 if file on disk is the same or Text is in memory only
1762    returns 1 if file has been modified on disk since last local edit
1763    returns 2 if file on disk has been deleted
1764    -1 is returned if an error occurs
1765 */
1766 int txt_file_modified(Text *text)
1767 {
1768         struct stat st;
1769         int result;
1770         char file[FILE_MAXDIR+FILE_MAXFILE];
1771
1772         if (!text || !text->name)
1773                 return 0;
1774
1775         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
1776         BLI_convertstringcode(file, G.sce);
1777
1778         if (!BLI_exists(file))
1779                 return 2;
1780
1781         result = stat(file, &st);
1782         
1783         if(result == -1)
1784                 return -1;
1785
1786         if((st.st_mode & S_IFMT) != S_IFREG)
1787                 return -1;
1788
1789         if (st.st_mtime > text->mtime)
1790                 return 1;
1791
1792         return 0;
1793 }
1794
1795 void txt_ignore_modified(Text *text) {
1796         struct stat st;
1797         int result;
1798         char file[FILE_MAXDIR+FILE_MAXFILE];
1799
1800         if (!text || !text->name) return;
1801
1802         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
1803         BLI_convertstringcode(file, G.sce);
1804
1805         if (!BLI_exists(file)) return;
1806
1807         result = stat(file, &st);
1808         
1809         if(result == -1 || (st.st_mode & S_IFMT) != S_IFREG)
1810                 return;
1811
1812         text->mtime= st.st_mtime;
1813 }
1814
1815 static void save_mem_text(char *str)
1816 {
1817         SpaceText *st= curarea->spacedata.first;
1818         Text *text;
1819         
1820         if (!str) return;
1821         
1822         if (!st) return;
1823         if (st->spacetype != SPACE_TEXT) return;
1824
1825         text= st->text;
1826         if(!text) return;
1827         
1828         if (text->name) MEM_freeN(text->name);
1829         text->name= MEM_mallocN(strlen(str)+1, "textname");
1830         strcpy(text->name, str);
1831
1832         text->flags ^= TXT_ISMEM;
1833                 
1834         txt_write_file(text);
1835 }
1836
1837 void txt_write_file(Text *text) 
1838 {
1839         FILE *fp;
1840         TextLine *tmp;
1841         int res;
1842         struct stat st;
1843         char file[FILE_MAXDIR+FILE_MAXFILE];
1844         
1845         /* Do we need to get a filename? */
1846         if (text->flags & TXT_ISMEM) {
1847                 if (text->name)
1848                         activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->name, save_mem_text);
1849                 else
1850                         activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->id.name+2, save_mem_text);
1851                 return;
1852         }
1853
1854         BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
1855         BLI_convertstringcode(file, G.sce);
1856         
1857         /* Should we ask to save over? */
1858         if (text->flags & TXT_ISTMP) {
1859                 if (BLI_exists(file)) {
1860                         if (!okee("Save over")) return;
1861                 } else if (!okee("Create new file")) return;
1862
1863                 text->flags ^= TXT_ISTMP;
1864         }
1865                 
1866         fp= fopen(file, "w");
1867         if (fp==NULL) {
1868                 error("Unable to save file");
1869                 return;
1870         }
1871
1872         tmp= text->lines.first;
1873         while (tmp) {
1874                 if (tmp->next) fprintf(fp, "%s\n", tmp->line);
1875                 else fprintf(fp, "%s", tmp->line);
1876                 
1877                 tmp= tmp->next;
1878         }
1879         
1880         fclose (fp);
1881
1882         res= stat(file, &st);
1883         text->mtime= st.st_mtime;
1884         
1885         if (text->flags & TXT_ISDIRTY) text->flags ^= TXT_ISDIRTY;
1886 }
1887
1888 void unlink_text(Text *text)
1889 {
1890         bScreen *scr;
1891         ScrArea *area;
1892         SpaceLink *sl;
1893         
1894         /* check if this text was used as script link:
1895          * this check function unsets the pointers and returns how many
1896          * script links used this Text */
1897         if (BPY_check_all_scriptlinks (text)) {
1898                 allqueue(REDRAWBUTSSCRIPT, 0);
1899         }
1900         /* equivalently for pynodes: */
1901         if (nodeDynamicUnlinkText ((ID*)text)) {
1902                 allqueue(REDRAWNODE, 0);
1903         }
1904
1905         for (scr= G.main->screen.first; scr; scr= scr->id.next) {
1906                 for (area= scr->areabase.first; area; area= area->next) {
1907                         for (sl= area->spacedata.first; sl; sl= sl->next) {
1908                                 if (sl->spacetype==SPACE_TEXT) {
1909                                         SpaceText *st= (SpaceText*) sl;
1910                                         
1911                                         if (st->text==text) {
1912                                                 st->text= NULL;
1913                                                 st->top= 0;
1914                                                 
1915                                                 if (st==area->spacedata.first) {
1916                                                         scrarea_queue_redraw(area);
1917                                                 }
1918                                         }
1919                                 }
1920                         }
1921                 }
1922         }
1923 }
1924
1925 int jumptoline_interactive(SpaceText *st) {
1926         short nlines= txt_get_span(st->text->lines.first, st->text->lines.last)+1;
1927         short tmp= txt_get_span(st->text->lines.first, st->text->curl)+1;
1928
1929         if (button(&tmp, 1, nlines, "Jump to line:")) {
1930                 txt_move_toline(st->text, tmp-1, 0);
1931                 pop_space_text(st);
1932                 return 1;
1933         } else {
1934                 return 0;
1935         }
1936 }
1937
1938
1939 int bufferlength;
1940 static char *copybuffer = NULL;
1941
1942 void txt_copy_selectbuffer (Text *text)
1943 {
1944         int length=0;
1945         TextLine *tmp, *linef, *linel;
1946         int charf, charl;
1947         
1948         if (!text) return;
1949         if (!text->curl) return;
1950         if (!text->sell) return;
1951
1952         if (!txt_has_sel(text)) return;
1953         
1954         if (copybuffer) {
1955                 MEM_freeN(copybuffer);
1956                 copybuffer= NULL;
1957         }
1958
1959         if (text->curl==text->sell) {
1960                 linef= linel= text->curl;
1961                 
1962                 if (text->curc < text->selc) {
1963                         charf= text->curc;
1964                         charl= text->selc;
1965                 } else{
1966                         charf= text->selc;
1967                         charl= text->curc;
1968                 }
1969         } else if (txt_get_span(text->curl, text->sell)<0) {
1970                 linef= text->sell;
1971                 linel= text->curl;
1972
1973                 charf= text->selc;              
1974                 charl= text->curc;
1975         } else {
1976                 linef= text->curl;
1977                 linel= text->sell;
1978                 
1979                 charf= text->curc;
1980                 charl= text->selc;
1981         }
1982
1983         if (linef == linel) {
1984                 length= charl-charf;
1985
1986                 copybuffer= MEM_mallocN(length+1, "cut buffera");
1987                 
1988                 BLI_strncpy(copybuffer, linef->line + charf, length+1);
1989         } else {
1990                 length+= linef->len - charf;
1991                 length+= charl;
1992                 length++; /* For the '\n' */
1993                 
1994                 tmp= linef->next;
1995                 while (tmp && tmp!= linel) {
1996                         length+= tmp->len+1;
1997                         tmp= tmp->next;
1998                 }
1999                 
2000                 copybuffer= MEM_mallocN(length+1, "cut bufferb");
2001                 
2002                 strncpy(copybuffer, linef->line+ charf, linef->len-charf);
2003                 length= linef->len-charf;
2004                 
2005                 copybuffer[length++]='\n';
2006                 
2007                 tmp= linef->next;
2008                 while (tmp && tmp!=linel) {
2009                         strncpy(copybuffer+length, tmp->line, tmp->len);
2010                         length+= tmp->len;
2011                         
2012                         copybuffer[length++]='\n';                      
2013                         
2014                         tmp= tmp->next;
2015                 }
2016                 strncpy(copybuffer+length, linel->line, charl);
2017                 length+= charl;
2018                 
2019                 copybuffer[length]=0;
2020         }
2021
2022         bufferlength = length;
2023 }
2024
2025 static char *unixNewLine(char *buffer)
2026 {
2027         char *p, *p2, *output;
2028         
2029         /* we can afford the few extra bytes */
2030         output= MEM_callocN(strlen(buffer)+1, "unixnewline");
2031         for (p= buffer, p2= output; *p; p++)
2032                 if (*p != '\r') *(p2++)= *p;
2033         
2034         *p2= 0;
2035         return(output);
2036 }
2037
2038 static char *winNewLine(char *buffer)
2039 {
2040         char *p, *p2, *output;
2041         int add= 0;
2042         
2043         for (p= buffer; *p; p++)
2044                 if (*p == '\n') add++;
2045                 
2046         bufferlength= p-buffer+add+1;
2047         output= MEM_callocN(bufferlength, "winnewline");
2048         for (p= buffer, p2= output; *p; p++, p2++) {
2049                 if (*p == '\n') { 
2050                         *(p2++)= '\r'; *p2= '\n';
2051                 } else *p2= *p;
2052         }
2053         *p2= 0;
2054         
2055         return(output);
2056 }
2057
2058 void txt_paste_clipboard(Text *text) {
2059
2060         char * buff;
2061         char *temp_buff;
2062         
2063         buff = (char*)getClipboard(0);
2064         if(buff) {
2065                 temp_buff = unixNewLine(buff);
2066                 
2067                 txt_insert_buf(text, temp_buff);
2068                 if(buff){free((void*)buff);}
2069                 if(temp_buff){MEM_freeN(temp_buff);}
2070         }
2071 }
2072
2073 void get_selection_buffer(Text *text)
2074 {
2075         char *buff = getClipboard(1);
2076         txt_insert_buf(text, buff);
2077 }
2078
2079 void txt_copy_clipboard(Text *text) {
2080         char *temp;
2081
2082         txt_copy_selectbuffer(text);
2083
2084         if (copybuffer) {
2085                 copybuffer[bufferlength] = '\0';
2086                 temp = winNewLine(copybuffer);
2087                 
2088                 putClipboard(temp, 0);
2089                 MEM_freeN(temp);
2090                 MEM_freeN(copybuffer);
2091                 copybuffer= NULL;
2092         }
2093 }
2094
2095 void run_python_script(SpaceText *st)
2096 {
2097         char *py_filename;
2098         Text *text=st->text;
2099
2100         if (!BPY_txt_do_python_Text(text)) {
2101                 int lineno = BPY_Err_getLinenumber();
2102                 // jump to error if happened in current text:
2103                 py_filename = (char*) BPY_Err_getFilename();
2104
2105                 /* st->text can become NULL: user called Blender.Load(blendfile)
2106                  * before the end of the script. */
2107                 if (!st->text) return;
2108
2109                 if (!strcmp(py_filename, st->text->id.name+2)) {
2110                         error_pyscript(  );
2111                         if (lineno >= 0) {
2112                                 txt_move_toline(text, lineno-1, 0);
2113                                 txt_sel_line(text);
2114                                 pop_space_text(st);
2115                         }       
2116                 } else {
2117                         error("Error in other (possibly external) file, "\
2118                                 "check console");
2119                 }       
2120         }
2121 }
2122
2123 static void set_tabs(Text *text)
2124 {
2125         SpaceText *st = curarea->spacedata.first;
2126         st->currtab_set = setcurr_tab(text);
2127 }
2128
2129 static void wrap_move_bol(SpaceText *st, short sel) {
2130         int offl, offc, lin;
2131         Text *text= st->text;
2132
2133         lin= txt_get_span(text->lines.first, text->sell);
2134         wrap_offset(st, text->sell, text->selc, &offl, &offc);
2135
2136         if (sel) {
2137                 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, -offc);
2138                 text->selc= -offc;
2139         } else {
2140                 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, -offc);
2141                 text->curc= -offc;
2142                 txt_pop_sel(text);
2143         }
2144 }
2145
2146 static void wrap_move_eol(SpaceText *st, short sel) {
2147         int offl, offc, lin, startl, c;
2148         Text *text= st->text;
2149
2150         lin= txt_get_span(text->lines.first, text->sell);
2151         wrap_offset(st, text->sell, text->selc, &offl, &offc);
2152         startl= offl;
2153         c= text->selc;
2154         while (offl==startl && text->sell->line[c]!='\0') {
2155                 c++;
2156                 wrap_offset(st, text->sell, c, &offl, &offc);
2157         } if (offl!=startl) c--;
2158
2159         if (sel) {
2160                 txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, c);
2161                 text->selc= c;
2162         } else {
2163                 txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, c);
2164                 text->curc= c;
2165                 txt_pop_sel(text);
2166         }
2167 }
2168
2169 static void wrap_move_up(SpaceText *st, short sel) {
2170         int offl, offl_1, offc, fromline, toline, c, target;
2171         Text *text= st->text;
2172
2173         wrap_offset(st, text->sell, 0, &offl_1, &offc);
2174         wrap_offset(st, text->sell, text->selc, &offl, &offc);
2175         fromline= toline= txt_get_span(text->lines.first, text->sell);
2176         target= text->selc + offc;
2177
2178         if (offl==offl_1) {
2179                 if (!text->sell->prev) {
2180                         txt_move_bol(text, sel);
2181                         return;
2182                 }
2183                 toline--;
2184                 c= text->sell->prev->len; /* End of prev. line */
2185                 wrap_offset(st, text->sell->prev, c, &offl, &offc);
2186                 c= -offc+target;
2187         } else {
2188                 c= -offc-1; /* End of prev. line */
2189                 wrap_offset(st, text->sell, c, &offl, &offc);
2190                 c= -offc+target;
2191         }
2192         if (c<0) c=0;
2193
2194         if (sel) {
2195                 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
2196                 if (toline<fromline) text->sell= text->sell->prev;
2197                 if (c>text->sell->len) c= text->sell->len;
2198                 text->selc= c;
2199         } else {
2200                 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
2201                 if (toline<fromline) text->curl= text->curl->prev;
2202                 if (c>text->curl->len) c= text->curl->len;
2203                 text->curc= c;
2204                 txt_pop_sel(text);
2205         }
2206 }
2207
2208 static void wrap_move_down(SpaceText *st, short sel) {
2209         int offl, startoff, offc, fromline, toline, c, target;
2210         Text *text= st->text;
2211
2212         wrap_offset(st, text->sell, text->selc, &offl, &offc);
2213         fromline= toline= txt_get_span(text->lines.first, text->sell);
2214         target= text->selc + offc;
2215         startoff= offl;
2216         c= text->selc;
2217         while (offl==startoff && text->sell->line[c]!='\0') {
2218                 c++;
2219                 wrap_offset(st, text->sell, c, &offl, &offc);
2220         }
2221
2222         if (text->sell->line[c]=='\0') {
2223                 if (!text->sell->next) {
2224                         txt_move_eol(text, sel);
2225                         return;
2226                 }
2227                 toline++;
2228                 c= target;
2229         } else {
2230                 c += target;
2231                 if (c > text->sell->len) c= text->sell->len;
2232         }
2233         if (c<0) c=0;
2234
2235         if (sel) {
2236                 txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
2237                 if (toline>fromline) text->sell= text->sell->next;
2238                 if (c>text->sell->len) c= text->sell->len;
2239                 text->selc= c;
2240         } else {
2241                 txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
2242                 if (toline>fromline) text->curl= text->curl->next;
2243                 if (c>text->curl->len) c= text->curl->len;
2244                 text->curc= c;
2245                 txt_pop_sel(text);
2246         }
2247 }
2248
2249 static void get_suggest_prefix(Text *text, int offset) {
2250         int i, len;
2251         char *line, tmp[256];
2252
2253         if (!text) return;
2254         if (!texttool_text_is_active(text)) return;
2255
2256         line= text->curl->line;
2257         for (i=text->curc-1+offset; i>=0; i--)
2258                 if (!check_identifier(line[i]))
2259                         break;
2260         i++;
2261         len= text->curc-i+offset;
2262         if (len > 255) {
2263                 printf("Suggestion prefix too long\n");
2264                 len = 255;
2265         }
2266         strncpy(tmp, line+i, len);
2267         tmp[len]= '\0';
2268         texttool_suggest_prefix(tmp);
2269 }
2270
2271 static void confirm_suggestion(Text *text, int skipleft) {
2272         int i, over=0;
2273         char *line;
2274         SuggItem *sel;
2275
2276         if (!text) return;
2277         if (!texttool_text_is_active(text)) return;
2278
2279         sel = texttool_suggest_selected();
2280         if (!sel) return;
2281
2282         line= text->curl->line;
2283         i=text->curc-skipleft-1;
2284         while (i>=0) {
2285                 if (!check_identifier(line[i]))
2286                         break;
2287                 over++;
2288                 i--;
2289         }
2290
2291         for (i=0; i<skipleft; i++)
2292                 txt_move_left(text, 0);
2293         for (i=0; i<over; i++)
2294                 txt_move_left(text, 1);
2295
2296         txt_insert_buf(text, sel->name);
2297         
2298         for (i=0; i<skipleft; i++)
2299                 txt_move_right(text, 0);
2300
2301         texttool_text_clear();
2302 }
2303
2304 static short do_texttools(SpaceText *st, char ascii, unsigned short evnt, short val) {
2305         int draw=0, tools=0, swallow=0, scroll=1;
2306         if (!texttool_text_is_active(st->text)) return 0;
2307         if (!st->text || st->text->id.lib) return 0;
2308
2309         if (st->doplugins && texttool_text_is_active(st->text)) {
2310                 if (texttool_suggest_first()) tools |= TOOL_SUGG_LIST;
2311                 if (texttool_docs_get()) tools |= TOOL_DOCUMENT;
2312         }
2313
2314         if (ascii) {
2315                 if (tools & TOOL_SUGG_LIST) {
2316                         if ((ascii != '_' && ascii != '*' && ispunct(ascii)) || check_whitespace(ascii)) {
2317                                 confirm_suggestion(st->text, 0);
2318                                 if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
2319                         } else if ((st->overwrite && txt_replace_char(st->text, ascii)) || txt_add_char(st->text, ascii)) {
2320                                 get_suggest_prefix(st->text, 0);
2321                                 pop_suggest_list();
2322                                 swallow= 1;
2323                                 draw= 1;
2324                         }
2325                 }
2326                 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
2327
2328         } else if (val==1 && evnt) {
2329                 switch (evnt) {
2330                         case LEFTMOUSE:
2331                                 if (do_suggest_select(st))
2332                                         swallow= 1;
2333                                 else {
2334                                         if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
2335                                         if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2336                                 }
2337                                 draw= 1;
2338                                 break;
2339                         case MIDDLEMOUSE:
2340                                 if (do_suggest_select(st)) {
2341                                         confirm_suggestion(st->text, 0);
2342                                         if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
2343                                         swallow= 1;
2344                                 } else {
2345                                         if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
2346                                         if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2347                                 }
2348                                 draw= 1;
2349                                 break;
2350                         case ESCKEY:
2351                                 draw= swallow= 1;
2352                                 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
2353                                 else if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2354                                 else draw= swallow= 0;
2355                                 break;
2356                         case RETKEY:
2357                                 if (tools & TOOL_SUGG_LIST) {
2358                                         confirm_suggestion(st->text, 0);
2359                                         if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
2360                                         swallow= 1;
2361                                         draw= 1;
2362                                 }
2363                                 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
2364                                 break;
2365                         case LEFTARROWKEY:
2366                         case BACKSPACEKEY:
2367                                 if (tools & TOOL_SUGG_LIST) {
2368                                         if (G.qual)
2369                                                 texttool_suggest_clear();
2370                                         else {
2371                                                 /* Work out which char we are about to delete/pass */
2372                                                 if (st->text->curl && st->text->curc > 0) {
2373                                                         char ch= st->text->curl->line[st->text->curc-1];
2374                                                         if ((ch=='_' || !ispunct(ch)) && !check_whitespace(ch)) {
2375                                                                 get_suggest_prefix(st->text, -1);
2376                                                                 pop_suggest_list();
2377                                                         }
2378                                                         else
2379                                                                 texttool_suggest_clear();
2380                                                 } else
2381                                                         texttool_suggest_clear();
2382                                         }
2383                                 }
2384                                 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2385                                 break;
2386                         case RIGHTARROWKEY:
2387                                 if (tools & TOOL_SUGG_LIST) {
2388                                         if (G.qual)
2389                                                 texttool_suggest_clear();
2390                                         else {
2391                                                 /* Work out which char we are about to pass */
2392                                                 if (st->text->curl && st->text->curc < st->text->curl->len) {
2393                                                         char ch= st->text->curl->line[st->text->curc+1];
2394                                                         if ((ch=='_' || !ispunct(ch)) && !check_whitespace(ch)) {
2395                                                                 get_suggest_prefix(st->text, 1);
2396                                                                 pop_suggest_list();
2397                                                         }
2398                                                         else
2399                                                                 texttool_suggest_clear();
2400                                                 } else
2401                                                         texttool_suggest_clear();
2402                                         }
2403                                 }
2404                                 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
2405                                 break;
2406                         case PAGEDOWNKEY:
2407                                 scroll= SUGG_LIST_SIZE-1;
2408                         case WHEELDOWNMOUSE:
2409                         case DOWNARROWKEY:
2410                                 if (tools & TOOL_DOCUMENT) {
2411                                         doc_scroll++;
2412                                         swallow= 1;
2413                                         draw= 1;
2414                                         break;
2415                                 } else if (tools & TOOL_SUGG_LIST) {
2416                                         SuggItem *sel = texttool_suggest_selected();
2417                                         if (!sel) {
2418                                                 texttool_suggest_select(texttool_suggest_first());
2419                                         } else while (sel && sel!=texttool_suggest_last() && sel->next && scroll--) {
2420                                                 texttool_suggest_select(sel->next);
2421                                                 sel= sel->next;
2422                                         }
2423                                         pop_suggest_list();
2424                                         swallow= 1;
2425                                         draw= 1;
2426                                         break;
2427                                 }
2428                         case PAGEUPKEY:
2429                                 scroll= SUGG_LIST_SIZE-1;
2430                         case WHEELUPMOUSE:
2431                         case UPARROWKEY:
2432                                 if (tools & TOOL_DOCUMENT) {
2433                                         if (doc_scroll>0) doc_scroll--;
2434                                         swallow= 1;
2435                                         draw= 1;
2436                                         break;
2437                                 } else if (tools & TOOL_SUGG_LIST) {
2438                                         SuggItem *sel = texttool_suggest_selected();
2439                                         while (sel && sel!=texttool_suggest_first() && sel->prev && scroll--) {
2440                                                 texttool_suggest_select(sel->prev);
2441                                                 sel= sel->prev;
2442                                         }
2443                                         pop_suggest_list();
2444                                         swallow= 1;
2445                                         draw= 1;
2446                                         break;
2447                                 }
2448                         case RIGHTSHIFTKEY:
2449                         case LEFTSHIFTKEY:
2450                                 break;
2451                         default:
2452                                 if (tools & TOOL_SUGG_LIST) texttool_suggest_clear(), draw= 1;
2453                                 if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
2454                 }
2455         }
2456
2457         if (draw) {
2458                 ScrArea *sa;
2459                 
2460                 for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
2461                         SpaceText *st= sa->spacedata.first;
2462                         
2463                         if (st && st->spacetype==SPACE_TEXT) {
2464                                 scrarea_queue_redraw(sa);
2465                         }
2466                 }
2467         }
2468
2469         return swallow;
2470 }
2471
2472 static short do_markers(SpaceText *st, char ascii, unsigned short evnt, short val) {
2473         Text *text;
2474         TextMarker *marker, *mrk, *nxt;
2475         int c, s, draw=0, swallow=0;
2476
2477         text= st->text;
2478         if (!text || text->id.lib || text->curl != text->sell) return 0;
2479
2480         marker= txt_find_marker(text, text->sell, text->selc, 0, 0);
2481         if (marker && (marker->start > text->curc || marker->end < text->curc))
2482                 marker= NULL;
2483
2484         if (!marker) {
2485                 /* Find the next temporary marker */
2486                 if (evnt==TABKEY) {
2487                         int lineno= txt_get_span(text->lines.first, text->curl);
2488                         TextMarker *mrk= text->markers.first;
2489                         while (mrk) {
2490                                 if (!marker && (mrk->flags & TMARK_TEMP)) marker= mrk;
2491                                 if ((mrk->flags & TMARK_TEMP) && (mrk->lineno > lineno || (mrk->lineno==lineno && mrk->end > text->curc))) {
2492                                         marker= mrk;
2493                                         break;
2494                                 }
2495                                 mrk= mrk->next;
2496                         }
2497                         if (marker) {
2498                                 txt_move_to(text, marker->lineno, marker->start, 0);
2499                                 txt_move_to(text, marker->lineno, marker->end, 1);
2500                                 pop_space_text(st);
2501                                 evnt= ascii= val= 0;
2502                                 draw= 1;
2503                                 swallow= 1;
2504                         }
2505                 } else if (evnt==ESCKEY) {
2506                         if (txt_clear_markers(text, 0, TMARK_TEMP)) swallow= 1;
2507                         else if (txt_clear_markers(text, 0, 0)) swallow= 1;
2508                         else return 0;
2509                         evnt= ascii= val= 0;
2510                         draw= 1;
2511                 }
2512                 if (!swallow) return 0;
2513         }
2514
2515         if (ascii) {
2516                 if (marker->flags & TMARK_EDITALL) {
2517                         c= text->curc-marker->start;
2518                         s= text->selc-marker->start;
2519                         if (s<0 || s>marker->end-marker->start) return 0;
2520
2521                         mrk= txt_next_marker(text, marker);
2522                         while (mrk) {
2523                                 nxt=txt_next_marker(text, mrk); /* mrk may become invalid */
2524                                 txt_move_to(text, mrk->lineno, mrk->start+c, 0);
2525                                 if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
2526                                 if (st->overwrite) {
2527                                         if (txt_replace_char(text, ascii))
2528                                                 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2529                                 } else {
2530                                         if (txt_add_char(text, ascii)) {
2531                                                 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2532                                         }
2533                                 }
2534
2535                                 if (mrk==marker || mrk==nxt) break;
2536                                 mrk=nxt;
2537                         }
2538                         swallow= 1;
2539                         draw= 1;
2540                 }
2541         } else if (val) {
2542                 switch(evnt) {
2543                         case BACKSPACEKEY:
2544                                 if (marker->flags & TMARK_EDITALL) {
2545                                         c= text->curc-marker->start;
2546                                         s= text->selc-marker->start;
2547                                         if (s<0 || s>marker->end-marker->start) return 0;
2548                                         
2549                                         mrk= txt_next_marker(text, marker);
2550                                         while (mrk) {
2551                                                 nxt= txt_next_marker(text, mrk); /* mrk may become invalid */
2552                                                 txt_move_to(text, mrk->lineno, mrk->start+c, 0);
2553                                                 if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
2554                                                 txt_backspace_char(text);
2555                                                 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2556                                                 if (mrk==marker || mrk==nxt) break;
2557                                                 mrk= nxt;
2558                                         }
2559                                         swallow= 1;
2560                                         draw= 1;
2561                                 }
2562                                 break;
2563                         case DELKEY:
2564                                 if (marker->flags & TMARK_EDITALL) {
2565                                         c= text->curc-marker->start;
2566                                         s= text->selc-marker->start;
2567                                         if (s<0 || s>marker->end-marker->start) return 0;
2568                                         
2569                                         mrk= txt_next_marker(text, marker);
2570                                         while (mrk) {
2571                                                 nxt= txt_next_marker(text, mrk); /* mrk may become invalid */
2572                                                 txt_move_to(text, mrk->lineno, mrk->start+c, 0);
2573                                                 if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
2574                                                 txt_delete_char(text);
2575                                                 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2576                                                 if (mrk==marker || mrk==nxt) break;
2577                                                 mrk= nxt;
2578                                         }
2579                                         swallow= 1;
2580                                         draw= 1;
2581                                 }
2582                                 break;
2583                         case TABKEY:
2584                                 if (G.qual & LR_SHIFTKEY) {
2585                                         nxt= marker->prev;
2586                                         if (!nxt) nxt= text->markers.last;
2587                                 } else {
2588                                         nxt= marker->next;
2589                                         if (!nxt) nxt= text->markers.first;
2590                                 }
2591                                 if (marker->flags & TMARK_TEMP) {
2592                                         if (nxt==marker) nxt= NULL;
2593                                         BLI_freelinkN(&text->markers, marker);
2594                                 }
2595                                 mrk= nxt;
2596                                 if (mrk) {
2597                                         txt_move_to(text, mrk->lineno, mrk->start, 0);
2598                                         txt_move_to(text, mrk->lineno, mrk->end, 1);
2599                                         pop_space_text(st);
2600                                 }
2601                                 swallow= 1;
2602                                 draw= 1;
2603                                 break;
2604
2605                         /* Events that should clear markers */
2606                         case UKEY: if (!(G.qual & LR_ALTKEY)) break;
2607                         case ZKEY: if (evnt==ZKEY && !(G.qual & LR_CTRLKEY)) break;
2608                         case RETKEY:
2609                         case ESCKEY:
2610                                 if (marker->flags & (TMARK_EDITALL | TMARK_TEMP))
2611                                         txt_clear_markers(text, marker->group, 0);
2612                                 else
2613                                         BLI_freelinkN(&text->markers, marker);
2614                                 swallow= 1;
2615                                 draw= 1;
2616                                 break;
2617                         case RIGHTMOUSE: /* Marker context menu? */
2618                         case LEFTMOUSE:
2619                                 break;
2620                         case FKEY: /* Allow find */
2621                                 if (G.qual & LR_SHIFTKEY) swallow= 1;
2622                                 break;
2623
2624                         default:
2625                                 if (G.qual!=0 && G.qual!=LR_SHIFTKEY)
2626                                         swallow= 1; /* Swallow all other shortcut events */
2627                 }
2628         }
2629         
2630         if (draw) {
2631                 ScrArea *sa;
2632                 
2633                 for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
2634                         SpaceText *st= sa->spacedata.first;
2635                         
2636                         if (st && st->spacetype==SPACE_TEXT) {
2637                                 scrarea_queue_redraw(sa);
2638                         }
2639                 }
2640         }
2641         return swallow;
2642 }
2643
2644 static short do_modification_check(SpaceText *st) {
2645         Text *text= st->text;
2646
2647         if (last_check_time < PIL_check_seconds_timer() - 2.0) {
2648                 switch (txt_file_modified(text)) {
2649                 case 1:
2650                         /* Modified locally and externally, ahhh. Offer more possibilites. */
2651                         if (text->flags & TXT_ISDIRTY) {
2652                                 switch (pupmenu("File Modified Outside and Inside Blender %t|Load outside changes (ignore local changes) %x0|Save local changes (ignore outside changes) %x1|Make text internal (separate copy) %x2")) {
2653                                 case 0:
2654                                         reopen_text(text);
2655                                         if (st->showsyntax) txt_format_text(st);
2656                                         return 1;
2657                                 case 1:
2658                                         txt_write_file(text);
2659                                         return 1;
2660                                 case 2:
2661                                         text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
2662                                         MEM_freeN(text->name);
2663                                         text->name= NULL;
2664                                         return 1;
2665                                 }
2666                         } else {
2667                                 switch (pupmenu("File Modified Outside Blender %t|Reload from disk %x0|Make text internal (separate copy) %x1|Ignore %x2")) {
2668                                 case 0:
2669                                         reopen_text(text);
2670                                         if (st->showsyntax) txt_format_text(st);
2671                                         return 1;
2672                                 case 1:
2673                                         text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
2674                                         MEM_freeN(text->name);
2675                                         text->name= NULL;
2676                                         return 1;
2677                                 case 2:
2678                                         txt_ignore_modified(text);
2679                                         return 1;
2680                                 }
2681                         }
2682                         break;
2683                 case 2:
2684                         switch (pupmenu("File Deleted Outside Blender %t|Make text internal %x0|Recreate file %x1")) {
2685                         case 0:
2686                                 text->flags |= TXT_ISMEM | TXT_ISDIRTY | TXT_ISTMP;
2687                                 MEM_freeN(text->name);
2688                                 text->name= NULL;
2689                                 return 1;
2690                         case 1:
2691                                 txt_write_file(text);
2692                                 return 1;
2693                         }
2694                         break;
2695                 default:
2696                         break;
2697                 }
2698                 last_check_time = PIL_check_seconds_timer();
2699         }
2700         return 0;
2701 }
2702
2703 void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
2704 {
2705         unsigned short event= evt->event;
2706         short val= evt->val;
2707         char ascii= evt->ascii;
2708         SpaceText *st= curarea->spacedata.first;
2709         Text *text;
2710         int do_draw=0, p;
2711         
2712         if (st==NULL || st->spacetype != SPACE_TEXT) return;
2713         
2714         /* smartass code to prevent the CTRL/ALT events below from not working! */
2715         if(G.qual & (LR_ALTKEY|LR_CTRLKEY))
2716                 if(!ispunct(ascii)) 
2717                         ascii= 0;
2718
2719         text= st->text;
2720         
2721         if (!text) {
2722                 if (event==RIGHTMOUSE) {
2723                         switch (pupmenu("File %t|New %x0|Open... %x1")) {
2724                         case 0:
2725                                 st->text= add_empty_text("Text");
2726                                 st->top= 0;
2727                         
2728                                 allqueue(REDRAWTEXT, 0);
2729                                 allqueue(REDRAWHEADERS, 0);
2730                                 break;
2731                         case 1:
2732                                 activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2733                                 break;
2734                         }
2735                 }
2736                 if (val && !ELEM(G.qual, 0, LR_SHIFTKEY)) {
2737                         if (event==FKEY && G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2738                                 switch (pupmenu("File %t|New %x0|Open... %x1")) {
2739                                 case 0:
2740                                         st->text= add_empty_text("Text");
2741                                         st->top= 0;
2742                                 
2743                                         allqueue(REDRAWTEXT, 0);
2744                                         allqueue(REDRAWHEADERS, 0);
2745                                         break;
2746                                 case 1:
2747                                         activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2748                                         break;
2749                                 }
2750                         } 
2751                         else if (event==QKEY) {
2752                                 if (G.qual & LR_CTRLKEY) {
2753                                         if(okee("Quit Blender")) exit_usiblender();
2754                                 }
2755                         }
2756                         else if (event==NKEY) {
2757                                 if (G.qual & LR_ALTKEY) {
2758                                         st->text= add_empty_text("Text");
2759                                         st->top= 0;
2760                                 
2761                                         allqueue(REDRAWTEXT, 0);
2762                                         allqueue(REDRAWHEADERS, 0);
2763                                 }
2764                         }
2765                         else if (event==OKEY) {
2766                                 if (G.qual & LR_ALTKEY) {
2767                                         activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2768                                 }
2769                         }
2770                 }
2771                 return;
2772         }
2773
2774         if (val && uiDoBlocks(&curarea->uiblocks, event, 1)!=UI_NOTHING) event= 0;
2775
2776         if (st->doplugins && do_texttools(st, ascii, event, val)) return;
2777         if (do_markers(st, ascii, event, val)) return;
2778         
2779         if (event==UI_BUT_EVENT) {
2780                 do_find_buttons(val);
2781                 do_draw= 1;
2782         } else if (event==LEFTMOUSE) {
2783                 if (val) {
2784                         short mval[2];
2785                         char *buffer;
2786                         set_tabs(text);
2787                         getmouseco_areawin(mval);
2788                         
2789                         if (mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<curarea->winy-2) {
2790                                 do_textscroll(st, 2);
2791                         } else {
2792                                 do_selection(st, G.qual&LR_SHIFTKEY);
2793                                 if (txt_has_sel(text)) {
2794                                         buffer = txt_sel_to_buf(text);
2795                                         putClipboard(buffer, 1);
2796                                         MEM_freeN(buffer);
2797                                 }
2798                                 do_draw= 1;
2799                         }
2800                 }
2801         } else if (event==MIDDLEMOUSE) {
2802                 if (val) {
2803                         if (U.uiflag & USER_MMB_PASTE) {
2804                                 do_selection(st, G.qual&LR_SHIFTKEY);
2805                                 get_selection_buffer(text);
2806                                 do_draw= 1;
2807                         } else {
2808                                 do_textscroll(st, 1);
2809                         }
2810                 }
2811         } else if (event==RIGHTMOUSE) {
2812                 if (val) {
2813                         if (txt_has_sel(text))
2814                                 p= pupmenu("Text %t|Cut%x10|Copy%x11|Paste%x12|New %x0|Open... %x1|Save %x2|Save As...%x3|Execute Script%x4");
2815                         else
2816                                 p= pupmenu("Text %t|Paste%x12|New %x0|Open... %x1|Save %x2|Save As...%x3|Execute Script%x4");
2817
2818                         switch(p) {
2819                                 case 0:
2820                                         st->text= add_empty_text("Text");
2821                                         st->top= 0;
2822                                         
2823                                         allqueue(REDRAWTEXT, 0);
2824                                         allqueue(REDRAWHEADERS, 0);
2825                                         break;
2826
2827                                 case 1:
2828                                         activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2829                                         break;
2830                                         
2831                                 case 3:
2832                                         text->flags |= TXT_ISMEM;
2833                                         
2834                                 case 2:
2835                                         txt_write_file(text);
2836                                         do_draw= 1;
2837                                         break;
2838                                 case 4:
2839                                         run_python_script(st);
2840                                         do_draw= 1;
2841                                         break;
2842                                 case 10:
2843                                         if (text && text->id.lib) {
2844                                                 error_libdata();
2845                                                 break;
2846                                         }
2847                                         txt_copy_clipboard(text);
2848                                         txt_cut_sel(text);
2849                                         pop_space_text(st);
2850                                         do_draw= 1;
2851                                         break;
2852                                 case 11:
2853                                         //txt_copy_sel(text);
2854                                         txt_copy_clipboard(text);
2855                                         break;
2856                                 case 12:
2857                                         if (text && text->id.lib) {
2858                                                 error_libdata();
2859                                                 break;
2860                                         }
2861                                         txt_paste_clipboard(text);
2862                                         if (st->showsyntax) txt_format_text(st);
2863                                         do_draw= 1;
2864                                         break;
2865                         }
2866                 }
2867         } else if (ascii) {
2868                 if (text && text->id.lib) {
2869                         error_libdata();
2870                 } else {
2871                         short mval[2];
2872                         getmouseco_areawin(mval);
2873                         if (st->showlinenrs && mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<curarea->winy-2) {
2874                                 if (ascii>='0' && ascii<='9') {
2875                                         double time = PIL_check_seconds_timer();
2876                                         if (last_jump < time-1) jump_to= 0;
2877                                         jump_to *= 10; jump_to += (int)(ascii-'0');
2878                                         txt_move_toline(text, jump_to-1, 0);
2879                                         last_jump= time;
2880                                 }
2881                         } else if ((st->overwrite && txt_replace_char(text, ascii)) || txt_add_char(text, ascii)) {
2882                                 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2883                         }
2884                         pop_space_text(st);
2885                         do_draw= 1;
2886                 }
2887         } else if (val) {
2888                 switch (event) {
2889                 case AKEY:
2890                         if (G.qual & LR_ALTKEY) {
2891                                 txt_move_bol(text, G.qual & LR_SHIFTKEY);
2892                                 do_draw= 1;
2893                                 pop_space_text(st);
2894                         } else if (G.qual & LR_CTRLKEY) {
2895                                 txt_sel_all(text);
2896                                 do_draw= 1;
2897                         }
2898                         break; /* BREAK A */
2899                 case CKEY:
2900                         if (G.qual & LR_ALTKEY || G.qual & LR_CTRLKEY) {
2901                                 if(G.qual & LR_SHIFTKEY)
2902                                         txt_copy_clipboard(text);
2903                                 else
2904                                         txt_copy_clipboard(text);
2905
2906                                 do_draw= 1;     
2907                         }
2908                         break; /* BREAK C */
2909                 case DKEY:
2910                         if (text && text->id.lib) {
2911                                 error_libdata();
2912                                 break;
2913                         }
2914                         if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
2915                                 //uncommenting
2916                                 txt_order_cursors(text);
2917                                 uncomment(text);
2918                                 do_draw = 1;
2919                                 if (st->showsyntax) txt_format_text(st);
2920                                 break;
2921                         } else if (G.qual == LR_CTRLKEY) {
2922                                 txt_delete_char(text);
2923                                 if (st->showsyntax) txt_format_line(st, text->curl, 1);
2924                                 do_draw= 1;
2925                                 pop_space_text(st);
2926                         }
2927                         break; /* BREAK D */
2928                 case EKEY:
2929                         if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2930                                 switch(pupmenu("Edit %t|Cut %x0|Copy %x1|Paste %x2|Print Cut Buffer %x3")) {
2931                                 case 0:
2932                                         if (text && text->id.lib) {
2933                                                 error_libdata();
2934                                                 break;
2935                                         }
2936                                         txt_copy_clipboard(text); //First copy to clipboard
2937                                         txt_cut_sel(text);
2938                                         do_draw= 1;
2939                                         break;
2940                                 case 1:
2941                                         txt_copy_clipboard(text);
2942                                         //txt_copy_sel(text);
2943                                         do_draw= 1;
2944                                         break;
2945                                 case 2:
2946                                         if (text && text->id.lib) {
2947                                                 error_libdata();
2948                                                 break;
2949                                         }
2950                                         //txt_paste(text);
2951                                         txt_paste_clipboard(text);
2952                                         if (st->showsyntax) txt_format_text(st);
2953                                         do_draw= 1;
2954                                         break;
2955                                 case 3:
2956                                         txt_print_cutbuffer();
2957                                         break;
2958                                 }
2959                         }
2960                         else if (G.qual == LR_CTRLKEY || G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
2961                                 txt_move_eol(text, G.qual & LR_SHIFTKEY);
2962                                 do_draw= 1;
2963                                 pop_space_text(st);
2964                         }
2965                         break; /* BREAK E */
2966                 case FKEY:
2967                         if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
2968                                 switch(pupmenu("File %t|New %x0|Open... %x1|Save %x2|Save As...%x3")) {
2969                                 case 0:
2970                                         st->text= add_empty_text("Text");
2971                                         st->top= 0;
2972                                         
2973                                         allqueue(REDRAWTEXT, 0);
2974                                         allqueue(REDRAWHEADERS, 0);
2975                                         break;
2976                                 case 1:
2977                                         activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
2978                                         break;
2979                                 case 3:
2980                                         text->flags |= TXT_ISMEM;
2981                                 case 2:
2982                                         txt_write_file(text);
2983                                         do_draw= 1;
2984                                         break;
2985                                 }
2986                         }
2987                         else if (G.qual & (LR_ALTKEY|LR_CTRLKEY)) {
2988                                 find_and_replace(st, 0);
2989                                 do_draw= 1;
2990                         }
2991                         break; /* BREAK F */
2992                 case HKEY:
2993                         if (G.qual & (LR_ALTKEY|LR_CTRLKEY)) {
2994                                 find_and_replace(st, 1);
2995                                 do_draw= 1;