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