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