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