/**
 * $Id: drawtext.c 16954 2008-10-06 22:08:14Z quorn $
 *
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 *
 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): none yet.
 *
 * ***** END GPL LICENSE BLOCK *****
 */

#include <stdlib.h>
#include <math.h>
#include <string.h>

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "MEM_guardedalloc.h"

#include "BMF_Api.h"

#include "BLI_blenlib.h"
#include "BLI_arithb.h"

#include "DNA_text_types.h"
#include "DNA_space_types.h"
#include "DNA_screen_types.h"
#include "DNA_userdef_types.h"

#include "BKE_utildefines.h"
#include "BKE_text.h"
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_suggestions.h"

#include "BIF_gl.h"
#include "BIF_glutil.h"
#include "BIF_keyval.h"
#include "BIF_interface.h"
#include "BIF_drawtext.h"
#include "BIF_editfont.h"
#include "BIF_spacetypes.h"
#include "BIF_usiblender.h"
#include "BIF_screen.h"
#include "BIF_toolbox.h"
#include "BIF_space.h"
#include "BIF_mywindow.h"
#include "BIF_resources.h"
#include "BIF_mainqueue.h"

#include "BSE_filesel.h"

#include "BPY_extern.h"
#include "BPY_menus.h"

#include "mydevice.h"
#include "blendef.h" 
#include "winlay.h"
#include "PIL_time.h"
#include <ctype.h> /* ispunct */
#include <sys/stat.h>

/***********************/ /*

Notes on word-wrap
--
All word-wrap functions follow the algorithm below to maintain consistency.
	line		The line to wrap (tabs converted to spaces)
	view_width	The maximum number of characters displayable in the region
				This equals region_width/font_width for the region
	wrap_chars	Characters that allow wrapping. This equals [' ', '\t', '-']

def wrap(line, view_width, wrap_chars):
	draw_start = 0
	draw_end = view_width
	pos = 0
	for c in line:
		if pos-draw_start >= view_width:
			print line[draw_start:draw_end]
			draw_start = draw_end
			draw_end += view_width
		elif c in wrap_chars:
			draw_end = pos+1
		pos += 1
	print line[draw_start:]

*/ /***********************/

#define TEXTXLOC		38

#define SUGG_LIST_SIZE	7
#define SUGG_LIST_WIDTH	20
#define DOC_WIDTH		40
#define DOC_HEIGHT		10

#define TOOL_SUGG_LIST	0x01
#define TOOL_DOCUMENT	0x02

#define TMARK_GRP_CUSTOM	0x00010000	/* Lower 2 bytes used for Python groups */
#define TMARK_GRP_FINDALL	0x00020000

/* forward declarations */

void drawtextspace(ScrArea *sa, void *spacedata);
void winqreadtextspace(struct ScrArea *sa, void *spacedata, struct BWinEvent *evt);
void redraw_alltext(void);

static void txt_copy_selectbuffer(Text *text);
static void draw_brackets(SpaceText *st);
static void get_selection_buffer(Text *text);
static int check_bracket(char ch);
static int check_delim(char ch);
static int check_digit(char ch);
static int check_identifier(char ch);
static int check_whitespace(char ch);

static int get_wrap_width(SpaceText *st);
static void get_suggest_prefix(Text *text, int offset);
static void confirm_suggestion(Text *text, int skipleft);

#define TXT_MAXFINDSTR 255
static int g_find_flags= TXT_FIND_WRAP;
static char *g_find_str= NULL;
static char *g_replace_str= NULL;

static int doc_scroll= 0;
static int jump_to= 0;
static double last_jump= 0;

static BMF_Font *spacetext_get_font(SpaceText *st) 
{
	static BMF_Font *scr12= NULL;
	static BMF_Font *scr15= NULL;
	
	switch (st->font_id) {
	default:
	case 0:
		if (!scr12)
			scr12= BMF_GetFont(BMF_kScreen12);
		return scr12;
	case 1:
		if (!scr15)
			scr15= BMF_GetFont(BMF_kScreen15);
		return scr15;
	}
}

static int spacetext_get_fontwidth(SpaceText *st) 
{
	return BMF_GetCharacterWidth(spacetext_get_font(st), ' ');
}

static char *temp_char_buf= NULL;
static int *temp_char_accum= NULL;
static int temp_char_len= 0;
static int temp_char_pos= 0;

static void temp_char_write(char c, int accum) 
{
	if (temp_char_len==0 || temp_char_pos>=temp_char_len) {
		char *nbuf; int *naccum;
		int olen= temp_char_len;
		
		if (olen) temp_char_len*= 2;
		else temp_char_len= 256;
		
		nbuf= MEM_mallocN(sizeof(*temp_char_buf)*temp_char_len, "temp_char_buf");
		naccum= MEM_mallocN(sizeof(*temp_char_accum)*temp_char_len, "temp_char_accum");
		
		if (olen) {
			memcpy(nbuf, temp_char_buf, olen);
			memcpy(naccum, temp_char_accum, olen);
			
			MEM_freeN(temp_char_buf);
			MEM_freeN(temp_char_accum);
		}
		
		temp_char_buf= nbuf;
		temp_char_accum= naccum;
	}
	
	temp_char_buf[temp_char_pos]= c;	
	temp_char_accum[temp_char_pos]= accum;
	
	if (c==0) temp_char_pos= 0;
	else temp_char_pos++;
}

void free_txt_data(void) 
{
	txt_free_cut_buffer();
	
	if (g_find_str) MEM_freeN(g_find_str);
	if (g_replace_str) MEM_freeN(g_replace_str);
	if (temp_char_buf) MEM_freeN(temp_char_buf);
	if (temp_char_accum) MEM_freeN(temp_char_accum);	
}

static int render_string (SpaceText *st, char *in) 
{
	int r = 0, i = 0;
	
	while(*in) {
		if (*in=='\t') {
			if (temp_char_pos && *(in-1)=='\t') i= st->tabnumber;
			else if (st->tabnumber > 0) i= st->tabnumber - (temp_char_pos%st->tabnumber);
			while(i--) temp_char_write(' ', r);
		} else temp_char_write(*in, r);

		r++;
		in++;
	}
	r= temp_char_pos;
	temp_char_write(0, 0);
		
	return r;
}

/* Checks the specified source string for a Python built-in function name. This
 name must start at the beginning of the source string and must be followed by
 a non-identifier (see check_identifier(char)) or null character.
 
 If a built-in function is found, the length of the matching name is returned.
 Otherwise, -1 is returned.
 */
static int find_builtinfunc(char *string)
{
	int a, i;
	char builtinfuncs[][11] = {"and", "as", "assert", "break", "class", "continue", "def",
								"del", "elif", "else", "except", "exec", "finally",
								"for", "from", "global", "if", "import", "in",
								"is", "lambda", "not", "or", "pass", "print",
								"raise", "return", "try", "while", "yield"};
	for (a=0; a<30; a++) {
		i = 0;
		while (1) {
			/* If we hit the end of a keyword... (eg. "def") */
			if (builtinfuncs[a][i]=='\0') {
				/* If we still have identifier chars in the source (eg. "definate") */
				if (check_identifier(string[i]))
					i = -1; /* No match */
				break; /* Next keyword if no match, otherwise we're done */
				
			/* If chars mismatch, move on to next keyword */
			} else if (string[i]!=builtinfuncs[a][i]) {
				i = -1;
				break; /* Break inner loop, start next keyword */
			}
			i++;
		}
		if (i>0) break; /* If we have a match, we're done */
	}
	return i;
}

/* Checks the specified source string for a Python special name. This name must
 start at the beginning of the source string and must be followed by a non-
 identifier (see check_identifier(char)) or null character.
 
 If a special name is found, the length of the matching name is returned.
 Otherwise, -1 is returned.
 */
static int find_specialvar(char *string) 
{
	int i = 0;
	/* Check for "def" */
	if (string[0]=='d' && string[1]=='e' && string[2]=='f')
		i = 3;
	/* Check for "class" */
	else if (string[0]=='c' && string[1]=='l' && string[2]=='a' && string[3]=='s' && string[4]=='s')
		i = 5;
	/* If next source char is an identifier (eg. 'i' in "definate") no match */
	if (i==0 || check_identifier(string[i]))
		return -1;
	return i;
}

/* Ensures the format string for the given line is long enough, reallocating
 as needed. Allocation is done here, alone, to ensure consitency.
 */
static int check_format_len(TextLine *line, unsigned int len)
{
	if (line->format) {
		if (strlen(line->format) < len) {
			MEM_freeN(line->format);
			line->format = MEM_mallocN(len+2, "SyntaxFormat");
			if (!line->format) return 0;
		}
	} else {
		line->format = MEM_mallocN(len+2, "SyntaxFormat");
		if (!line->format) return 0;
	}
	return 1;
}

/* Formats the specified line. If do_next is set, the process will move on to
 the succeeding line if it is affected (eg. multiline strings). Format strings
 may contain any of the following characters:
 	'_'		Whitespace
 	'#'		Comment text
 	'!'		Punctuation and other symbols
 	'n'		Numerals
 	'l'		String letters
 	'v'		Special variables (class, def)
 	'b'		Built-in names (print, for, etc.)
 	'q'		Other text (identifiers, etc.)
 It is terminated with a null-terminator '\0' followed by a continuation
 flag indicating whether the line is part of a multi-line string.
 */
void txt_format_line(SpaceText *st, TextLine *line, int do_next)
{
	char *str, *fmt, orig, cont, find, prev = ' ';
	int len, i;

	/* Get continuation from previous line */
	if (line->prev && line->prev->format != NULL) {
		fmt= line->prev->format;
		cont = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
	} else cont = 0;

	/* Get original continuation from this line */
	if (line->format != NULL) {
		fmt= line->format;
		orig = fmt[strlen(fmt)+1]; /* Just after the null-terminator */
	} else orig = 0xFF;

	render_string(st, line->line);
	str = temp_char_buf;
	len = strlen(str);
	if (!check_format_len(line, len)) return;
	fmt = line->format;

	while (*str) {
		/* Handle escape sequences by skipping both \ and next char */
		if (*str == '\\') {
			*fmt = prev; fmt++; str++;
			if (*str == '\0') break;
			*fmt = prev; fmt++; str++;
			continue;
		}
		/* Handle continuations */
		else if (cont) {
			/* Triple strings ("""...""" or '''...''') */
			if (cont & TXT_TRISTR) {
				find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
				if (*str==find && *(str+1)==find && *(str+2)==find) {
					*fmt = 'l'; fmt++; str++;
					*fmt = 'l'; fmt++; str++;
					cont = 0;
				}
			/* Handle other strings */
			} else {
				find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
				if (*str == find) cont = 0;
			}
			*fmt = 'l';
		}
		/* Not in a string... */
		else {
			/* Deal with comments first */
			if (prev == '#' || *str == '#')
				*fmt = '#';
			/* Strings */
			else if (*str == '"' || *str == '\'') {
				find = *str;
				cont = (*str== '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
				if (*(str+1) == find && *(str+2) == find) {
					*fmt = 'l'; fmt++; str++;
					*fmt = 'l'; fmt++; str++;
					cont |= TXT_TRISTR;
				}
				*fmt = 'l';
			}
			/* Whitespace (all ws. has been converted to spaces) */
			else if (*str == ' ')
				*fmt = '_';
			/* Numbers (digits not part of an identifier and periods followed by digits) */
			else if ((prev != 'q' && check_digit(*str)) || (*str == '.' && check_digit(*(str+1))))
				*fmt = 'n';
			/* Punctuation */
			else if (check_delim(*str))
				*fmt = '!';
			/* Identifiers and other text (no previous ws. or delims. so text continues) */
			else if (prev == 'q')
				*fmt = 'q';
			/* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
			else {
				/* Special vars(v) or built-in keywords(b) */
				if ((i=find_specialvar(str)) != -1)
					prev = 'v';
				else if ((i=find_builtinfunc(str)) != -1)
					prev = 'b';
				if (i>0) {
					while (i>1) {
						*fmt = prev; fmt++; str++;
						i--;
					}
					*fmt = prev;
				} else
					*fmt = 'q';
			}
		}
		prev = *fmt;
		fmt++;
		str++;
	}

	/* Terminate and add continuation char */
	*fmt = '\0'; fmt++;
	*fmt = cont;

	/* Debugging */
	//print_format(st, line);

	/* If continuation has changed and we're allowed, process the next line */
	if (cont!=orig && do_next && line->next) {
		txt_format_line(st, line->next, do_next);
	}
}

/* Formats every line of the current text */
void txt_format_text(SpaceText *st) 
{
	TextLine *linep;

	if (!st->text) return;

	for (linep=st->text->lines.first; linep; linep=linep->next)
		txt_format_line(st, linep, 0);
}

/* Sets the current drawing color based on the format character specified */
static void format_draw_color(char formatchar)
{
	switch (formatchar) {
		case '_': /* Whitespace */
			break;
		case '!': /* Symbols */
			BIF_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f);
			break;
		case '#': /* Comments */
			BIF_ThemeColor(TH_SYNTAX_C);
			break;
		case 'n': /* Numerals */
			BIF_ThemeColor(TH_SYNTAX_N);
			break;
		case 'l': /* Strings */
			BIF_ThemeColor(TH_SYNTAX_L);
			break;
		case 'v': /* Specials: class, def */
			BIF_ThemeColor(TH_SYNTAX_V);
			break;
		case 'b': /* Keywords: for, print, etc. */
			BIF_ThemeColor(TH_SYNTAX_B);
			break;
		case 'q': /* Other text (identifiers) */
		default:
			BIF_ThemeColor(TH_TEXT);
			break;
	}
}

static int text_draw_wrapped(SpaceText *st, char *str, int x, int y, int w, char *format)
{
	int basex, i, a, len, start, end, max, lines;
	
	len= render_string(st, str);
	str= temp_char_buf;
	max= w/spacetext_get_fontwidth(st);
	if (max<8) max= 8;
	basex= x;

	lines= 1;
	start= 0;
	end= max;
	for (i=0; i<len; i++) {
		if (i-start >= max) {
			/* Draw the visible portion of text on the overshot line */
			for (a=start; a<end; a++) {
				if (st->showsyntax && format) format_draw_color(format[a]);
				glRasterPos2i(x, y);
				BMF_DrawCharacter(spacetext_get_font(st), str[a]);
				x += BMF_GetCharacterWidth(spacetext_get_font(st), str[a]);
			}
			y -= st->lheight;
			x= basex;
			lines++;
			start= end;
			end += max;
		} else if (str[i]==' ' || str[i]=='-') {
			end = i+1;
		}
	}
	/* Draw the remaining text */
	for (a=start; a<len; a++) {
		if (st->showsyntax && format) format_draw_color(format[a]);
		glRasterPos2i(x, y);
		BMF_DrawCharacter(spacetext_get_font(st), str[a]);
		x += BMF_GetCharacterWidth(spacetext_get_font(st), str[a]);
	}
	return lines;
}

static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, char *format)
{
	int r=0, w= 0;
	int *acc;
	char *in;

	w= render_string(st, str);
	if(w<cshift ) return 0; /* String is shorter than shift */
	
	in= temp_char_buf+cshift;
	acc= temp_char_accum+cshift;
	w= w-cshift;

	if (draw) {
		if(st->showsyntax && format) {
			int amount, a;
			format = format+cshift;
		
			amount = strlen(in);
			
			for(a = 0; a < amount; a++) {
				format_draw_color(format[a]);
				glRasterPos2i(x, y);
				BMF_DrawCharacter(spacetext_get_font(st), in[a]);
				x = x+BMF_GetCharacterWidth(spacetext_get_font(st), in[a]);
			}
		} else {
			glRasterPos2i(x, y);
			BMF_DrawString(spacetext_get_font(st), in);
		}
	} else {
		while (w-- && *acc++ < maxwidth) {
			r+= spacetext_get_fontwidth(st);
		}
	}

	if (cshift && r==0) return 0;
	else if (st->showlinenrs)
		return r+TXT_OFFSET+TEXTXLOC;
	else
		return r+TXT_OFFSET;
}

static void set_cursor_to_pos (SpaceText *st, int x, int y, int sel) 
{
	Text *text;
	TextLine **linep;
	int *charp;
	int w;
	
	text= st->text;

	if(sel) { linep= &text->sell; charp= &text->selc; } 
	else { linep= &text->curl; charp= &text->curc; }
	
	y= (curarea->winy - y)/st->lheight;

	if(st->showlinenrs)
		x-= TXT_OFFSET+TEXTXLOC;
	else
		x-= TXT_OFFSET;

	if (x<0) x= 0;
	x = (x/spacetext_get_fontwidth(st)) + st->left;
	
	if (st->wordwrap) {
		int i, j, endj, curs, max, chop, start, end, chars, loop;
		char ch;

		/* Point to first visible line */
		*linep= text->lines.first;
		for (i=0; i<st->top && (*linep)->next; i++) *linep= (*linep)->next;

		max= get_wrap_width(st);

		loop= 1;
		while (loop && *linep) {
			start= 0;
			end= max;
			chop= 1;
			chars= 0;
			curs= 0;
			endj= 0;
			for (i=0, j=0; loop; j++) {

				/* Mimic replacement of tabs */
				ch= (*linep)->line[j];
				if (ch=='\t') {
					chars= st->tabnumber-i%st->tabnumber;
					ch= ' ';
				} else
					chars= 1;

				while (chars--) {
					/* Gone too far, go back to last wrap point */
					if (y<0) {
						*charp= endj;
						loop= 0;
						break;
					/* Exactly at the cursor, done */
					} else if (y==0 && i-start==x) {
						*charp= curs= j;
						loop= 0;
						break;
					/* Prepare curs for next wrap */
					} else if (i-end==x) {
						curs= j;
					}
					if (i-start>=max) {
						if (chop) endj= j;
						y--;
						start= end;
						end += max;
						chop= 1;
						if (y==0 && i-start>=x) {
							*charp= curs;
							loop= 0;
							break;
						}
					} else if (ch==' ' || ch=='-' || ch=='\0') {
						if (y==0 && i-start>=x) {
							*charp= curs;
							loop= 0;
							break;
						}
						end = i+1;
						endj = j;
						chop= 0;
					}
					i++;
				}
				if (ch=='\0') break;
			}
			if (!loop || y<0) break;

			if (!(*linep)->next) {
				*charp= (*linep)->len;
				break;
			}
			
			/* On correct line but didn't meet cursor, must be at end */
			if (y==0) {
				*charp= (*linep)->len;
				break;
			}
			*linep= (*linep)->next;
			y--;
		}

	} else {
		y-= txt_get_span(text->lines.first, *linep) - st->top;
		
		if (y>0) {
			while (y-- != 0) if((*linep)->next) *linep= (*linep)->next;
		} else if (y<0) {
			while (y++ != 0) if((*linep)->prev) *linep= (*linep)->prev;
		}

		
		w= render_string(st, (*linep)->line);
		if(x<w) *charp= temp_char_accum[x];
		else *charp= (*linep)->len;
	}
	if(!sel) txt_pop_sel(text);
}

static int get_wrap_width(SpaceText *st)
{
	int x, max;
	
	x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
	max= (curarea->winx-x)/spacetext_get_fontwidth(st);
	return max>8 ? max : 8;
}

/* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
static void wrap_offset(SpaceText *st, TextLine *linein, int cursin, int *offl, int *offc)
{
	Text *text;
	TextLine *linep;
	int i, j, start, end, chars, max, chop;
	char ch;

	*offl= *offc= 0;

	if (!st->text) return;
	if (!st->wordwrap) return;

	text= st->text;

	/* Move pointer to first visible line (top) */
	linep= text->lines.first;
	i= st->top;
	while (i>0 && linep) {
		if (linep == linein) return; /* Line before top */
		linep= linep->next;
		i--;
	}

	max= get_wrap_width(st);

	while (linep) {
		start= 0;
		end= max;
		chop= 1;
		chars= 0;
		*offc= 0;
		for (i=0, j=0; linep->line[j]!='\0'; j++) {

			/* Mimic replacement of tabs */
			ch= linep->line[j];
			if (ch=='\t') {
				chars= st->tabnumber-i%st->tabnumber;
				if (linep==linein && i<cursin) cursin += chars-1;
				ch= ' ';
			} else
				chars= 1;

			while (chars--) {
				if (i-start>=max) {
					if (chop && linep==linein && i >= cursin)
						return;
					(*offl)++;
					*offc -= end-start;
					start= end;
					end += max;
					chop= 1;
				} else if (ch==' ' || ch=='-') {
					end = i+1;
					chop= 0;
					if (linep==linein && i >= cursin)
						return;
				}
				i++;
			}
		}
		if (linep==linein) break;
		linep= linep->next;
	}
}

static int get_char_pos(SpaceText *st, char *line, int cur)
{
	int a=0, i;
	
	for (i=0; i<cur && line[i]; i++) {
		if (line[i]=='\t')
			a += st->tabnumber-a%st->tabnumber;
		else
			a++;
	}
	return a;
}

static void draw_markers(SpaceText *st)
{
	Text *text= st->text;
	TextMarker *marker, *next;
	TextLine *top, *bottom, *line;
	int offl, offc, i, cy, x1, x2, y1, y2, x, y;

	for (i=st->top, top= text->lines.first; top->next && i>0; i--) top= top->next;
	for (i=st->viewlines-1, bottom=top; bottom->next && i>0; i--) bottom= bottom->next;
	
	for (marker= text->markers.first; marker; marker= next) {
		next= marker->next;
		for (cy= 0, line= top; line; cy++, line= line->next) {
			if (cy+st->top==marker->lineno) {
				/* Remove broken markers */
				if (marker->end>line->len || marker->start>marker->end) {
					BLI_freelinkN(&text->markers, marker);
					break;
				}

				wrap_offset(st, line, marker->start, &offl, &offc);
				x1= get_char_pos(st, line->line, marker->start) - st->left + offc;
				y1= cy + offl;
				wrap_offset(st, line, marker->end, &offl, &offc);
				x2= get_char_pos(st, line->line, marker->end) - st->left + offc;
				y2= cy + offl;

				glColor3ub(marker->color[0], marker->color[1], marker->color[2]);
				x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
				y= curarea->winy-3;

				if (y1==y2) {
					y -= y1*st->lheight;
					glBegin(GL_LINE_LOOP);
					glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y);
					glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y);
					glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y-st->lheight);
					glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y-st->lheight);
					glEnd();
				} else {
					y -= y1*st->lheight;
					glBegin(GL_LINE_STRIP);
					glVertex2i(curarea->winx, y);
					glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y);
					glVertex2i(x+x1*spacetext_get_fontwidth(st)-2, y-st->lheight);
					glVertex2i(curarea->winx, y-st->lheight);
					glEnd();
					y-=st->lheight;
					for (i=y1+1; i<y2; i++) {
						glBegin(GL_LINES);
						glVertex2i(x, y);
						glVertex2i(curarea->winx, y);
						glVertex2i(x, y-st->lheight);
						glVertex2i(curarea->winx, y-st->lheight);
						glEnd();
						y-=st->lheight;
					}
					glBegin(GL_LINE_STRIP);
					glVertex2i(x, y);
					glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y);
					glVertex2i(x+x2*spacetext_get_fontwidth(st)+1, y-st->lheight);
					glVertex2i(x, y-st->lheight);
					glEnd();
				}

				break;
			}
			if (line==bottom) break;
		}
	}
}

static void draw_cursor(SpaceText *st)
{
	Text *text= st->text;
	int vcurl, vcurc, vsell, vselc, hidden=0;
	int offl, offc, x, y, w, i;
	
	/* Draw the selection */
	if (text->curl!=text->sell || text->curc!=text->selc) {

		/* Convert all to view space character coordinates */
		wrap_offset(st, text->curl, text->curc, &offl, &offc);
		vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
		vcurc = get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
		wrap_offset(st, text->sell, text->selc, &offl, &offc);
		vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
		vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;

		if (vcurc<0) vcurc=0;
		if (vselc<0) vselc=0, hidden=1;
		
		BIF_ThemeColor(TH_SHADE2);
		x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
		y= curarea->winy-2;

		if (vcurl==vsell) {
			y -= vcurl*st->lheight;
			if (vcurc < vselc)
				glRecti(x+vcurc*spacetext_get_fontwidth(st)-1, y, x+vselc*spacetext_get_fontwidth(st), y-st->lheight);
			else
				glRecti(x+vselc*spacetext_get_fontwidth(st)-1, y, x+vcurc*spacetext_get_fontwidth(st), y-st->lheight);
		} else {
			int froml, fromc, tol, toc;
			if (vcurl < vsell) {
				froml= vcurl; tol= vsell;
				fromc= vcurc; toc= vselc;
			} else {
				froml= vsell; tol= vcurl;
				fromc= vselc; toc= vcurc;
			}
			y -= froml*st->lheight;
			glRecti(x+fromc*spacetext_get_fontwidth(st)-1, y, curarea->winx, y-st->lheight); y-=st->lheight;
			for (i=froml+1; i<tol; i++)
				glRecti(x-4, y, curarea->winx, y-st->lheight),  y-=st->lheight;
			glRecti(x-4, y, x+toc*spacetext_get_fontwidth(st), y-st->lheight);  y-=st->lheight;
		}
	} else {
		wrap_offset(st, text->sell, text->selc, &offl, &offc);
		vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
		vselc = get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
		if (vselc<0) vselc=0, hidden=1;
	}

	if (!hidden) {
		/* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
		x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
		x += vselc*spacetext_get_fontwidth(st);
		y= curarea->winy-2 - vsell*st->lheight;
		
		if (st->overwrite) {
			char ch= text->sell->line[text->selc];
			if (!ch) ch= ' ';
			w= BMF_GetCharacterWidth(spacetext_get_font(st), ch);
			BIF_ThemeColor(TH_HILITE);
			glRecti(x, y-st->lheight-1, x+w, y-st->lheight+1);
		} else {
			BIF_ThemeColor(TH_HILITE);
			glRecti(x-1, y, x+1, y-st->lheight);
		}
	}
}

static void calc_text_rcts(SpaceText *st)
{
	int lhlstart, lhlend, ltexth;
	short barheight, barstart, hlstart, hlend, blank_lines;
	short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;

	pix_top_margin = 8;
	pix_bottom_margin = 4;
	pix_available = curarea->winy - pix_top_margin - pix_bottom_margin;
	ltexth= txt_get_span(st->text->lines.first, st->text->lines.last);
	blank_lines = st->viewlines / 2;
	
	/* when resizing a vieport with the bar at the bottom to a greater height more blank lines will be added */
	if (ltexth + blank_lines < st->top + st->viewlines) {
		blank_lines = st->top + st->viewlines - ltexth;
	}
	
	ltexth += blank_lines;

	barheight = (ltexth > 0)? (st->viewlines*pix_available)/ltexth: 0;
	pix_bardiff = 0;
	if (barheight < 20) {
		pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */	
		barheight = 20;
	}
	barstart = (ltexth > 0)? ((pix_available - pix_bardiff) * st->top)/ltexth: 0;

	st->txtbar.xmin = 5;
	st->txtbar.xmax = 17;
	st->txtbar.ymax = curarea->winy - pix_top_margin - barstart;
	st->txtbar.ymin = st->txtbar.ymax - barheight;

	CLAMP(st->txtbar.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
	CLAMP(st->txtbar.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);

	st->pix_per_line= (pix_available > 0)? (float) ltexth/pix_available: 0;
	if (st->pix_per_line<.1) st->pix_per_line=.1f;

	lhlstart = MIN2(txt_get_span(st->text->lines.first, st->text->curl), 
				txt_get_span(st->text->lines.first, st->text->sell));
	lhlend = MAX2(txt_get_span(st->text->lines.first, st->text->curl), 
				txt_get_span(st->text->lines.first, st->text->sell));

	if(ltexth > 0) {
		hlstart = (lhlstart * pix_available)/ltexth;
		hlend = (lhlend * pix_available)/ltexth;

		/* the scrollbar is non-linear sized */
		if (pix_bardiff > 0) {
			/* the start of the highlight is in the current viewport */
			if (ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) { 
				/* speed the progresion of the start of the highlight through the scrollbar */
				hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines); 	
			}
			else if (lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
				/* push hl start down */
				hlstart = barstart + barheight;
			}
			else if (lhlend > st->top  && lhlstart < st->top && hlstart > barstart) {
				/*fill out start */
				hlstart = barstart;
			}

			if (hlend <= hlstart) { 
				hlend = hlstart + 2;
			}

			/* the end of the highlight is in the current viewport */
			if (ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) { 
				/* speed the progresion of the end of the highlight through the scrollbar */
				hlend = (((pix_available - pix_bardiff )*lhlend)/ltexth) + (pix_bardiff * (lhlend - st->top)/st->viewlines); 	
			}
			else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
				/* push hl end up */
				hlend = barstart;
			}					
			else if (lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
				/* fill out end */
				hlend = barstart + barheight;
			}

			if (hlend <= hlstart) { 
				hlstart = hlend - 2;
			}	
		}	
	}
	else {
		hlstart = 0;
		hlend = 0;
	}

	if (hlend - hlstart < 2) { 
		hlend = hlstart + 2;
	}
	
	st->txtscroll.xmin= 5;
	st->txtscroll.xmax= 17;
	st->txtscroll.ymax= curarea->winy - pix_top_margin - hlstart;
	st->txtscroll.ymin= curarea->winy - pix_top_margin - hlend;

	CLAMP(st->txtscroll.ymin, pix_bottom_margin, curarea->winy - pix_top_margin);
	CLAMP(st->txtscroll.ymax, pix_bottom_margin, curarea->winy - pix_top_margin);
}

static void draw_textscroll(SpaceText *st)
{
	if (!st->text) return;

	calc_text_rcts(st);
	
	BIF_ThemeColorShade(TH_SHADE1, -20);
	glRecti(2, 2, 20, curarea->winy-6);
	uiEmboss(2, 2, 20, curarea->winy-6, 1);

	BIF_ThemeColor(TH_SHADE1);
	glRecti(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax);

	BIF_ThemeColor(TH_SHADE2);
	glRecti(st->txtscroll.xmin, st->txtscroll.ymin, st->txtscroll.xmax, st->txtscroll.ymax);

	uiEmboss(st->txtbar.xmin, st->txtbar.ymin, st->txtbar.xmax, st->txtbar.ymax, st->flags & ST_SCROLL_SELECT);
}

/* Moves the view vertically by the specified number of lines */
static void screen_skip(SpaceText *st, int lines)
{
	int last;
	
	if (!st) return;
	if (st->spacetype != SPACE_TEXT) return;
	if (!st->text) return;

 	st->top += lines;

	last= txt_get_span(st->text->lines.first, st->text->lines.last);
	last= last - (st->viewlines/2);
	
	if (st->top>last) st->top= last;
	if (st->top<0) st->top= 0;
}

/* Moves the cursor vertically by the specified number of lines.
 If the destination line is shorter than the current cursor position, the
 cursor will be positioned at the end of this line.

 This is to replace screen_skip for PageUp/Down operations.
 */
static void cursor_skip(SpaceText *st, int lines, int sel)
{
	Text *text;
	TextLine **linep;
	int oldl, oldc, *charp;
	
	if (!st) return;
	if (st->spacetype != SPACE_TEXT) return;
	if (!st->text) return;

	text= st->text;

	if (sel) linep= &text->sell, charp= &text->selc;
	else linep= &text->curl, charp= &text->curc;
	oldl= txt_get_span(text->lines.first, *linep);
	oldc= *charp;

	while (lines>0 && (*linep)->next) {
		*linep= (*linep)->next;
		lines--;
	}
	while (lines<0 && (*linep)->prev) {
		*linep= (*linep)->prev;
		lines++;
	}

	if (*charp > (*linep)->len) *charp= (*linep)->len;

	if (!sel) txt_pop_sel(st->text);
	txt_undo_add_toop(st->text, sel?UNDO_STO:UNDO_CTO, oldl, oldc, txt_get_span(text->lines.first, *linep), *charp);
}

/* Handles text scrolling via grabbing the view (MMB, mode 1) or with the
 scrollbar (mode 2)
 */
static void do_textscroll(SpaceText *st, int mode)
{
	short delta[2]= {0, 0};
	short mval[2], hold[2], old[2];
	
	if (!st->text) return;
	
	calc_text_rcts(st);

	st->flags|= ST_SCROLL_SELECT;

	scrarea_do_windraw(curarea);
	screen_swapbuffers();

	getmouseco_areawin(mval);
	old[0]= hold[0]= mval[0];
	old[1]= hold[1]= mval[1];

	while(get_mbut()&(L_MOUSE|M_MOUSE)) {
		getmouseco_areawin(mval);

		if(old[0]!=mval[0] || old[1]!=mval[1]) {
			if (mode==1) {
				delta[0]= (hold[0]-mval[0])/spacetext_get_fontwidth(st);
				delta[1]= (mval[1]-hold[1])/st->lheight;
			}
			else delta[1]= (hold[1]-mval[1])*st->pix_per_line;
			
			if (delta[0] || delta[1]) {
				screen_skip(st, delta[1]);
				if (st->wordwrap) {
					st->left= 0;
				} else {
					st->left+= delta[0];
					if (st->left<0) st->left= 0;
				}
				scrarea_do_windraw(curarea);
				screen_swapbuffers();
				
				hold[0]=mval[0];
				hold[1]=mval[1];
			}
			old[0]=mval[0];
			old[1]=mval[1];
		} else {
			BIF_wait_for_statechange();
		}
	}
	st->flags^= ST_SCROLL_SELECT;

	scrarea_do_windraw(curarea);
	screen_swapbuffers();
}

static void do_selection(SpaceText *st, int selecting)
{
	int sell, selc;
	int linep2, charp2;
	int first= 1;
	short mval[2], old[2];

	getmouseco_areawin(mval);
	old[0]= mval[0];
	old[1]= mval[1];

	if (!selecting) {
		int curl= txt_get_span(st->text->lines.first, st->text->curl);
		int curc= st->text->curc;			
		int linep2, charp2;
					
		set_cursor_to_pos(st, mval[0], mval[1], 0);

		linep2= txt_get_span(st->text->lines.first, st->text->curl);
		charp2= st->text->selc;
				
		if (curl!=linep2 || curc!=charp2)
			txt_undo_add_toop(st->text, UNDO_CTO, curl, curc, linep2, charp2);
	}

	sell= txt_get_span(st->text->lines.first, st->text->sell);
	selc= st->text->selc;

	while(get_mbut()&L_MOUSE) {
		getmouseco_areawin(mval);

		if (mval[1]<0 || mval[1]>curarea->winy) {
			int d= (old[1]-mval[1])*st->pix_per_line;
			if (d) screen_skip(st, d);

			set_cursor_to_pos(st, mval[0], mval[1]<0?0:curarea->winy, 1);

			scrarea_do_windraw(curarea);
			screen_swapbuffers();
		} 
		else if (!st->wordwrap && (mval[0]<0 || mval[0]>curarea->winx)) {
			if (mval[0]>curarea->winx) st->left++;
			else if (mval[0]<0 && st->left>0) st->left--;
			
			set_cursor_to_pos(st, mval[0], mval[1], 1);
			
			scrarea_do_windraw(curarea);
			screen_swapbuffers();
			
			PIL_sleep_ms(10);
		} 
		else if (first || old[0]!=mval[0] || old[1]!=mval[1]) {
			set_cursor_to_pos(st, mval[0], mval[1], 1);

			scrarea_do_windraw(curarea);
			screen_swapbuffers();

			old[0]= mval[0];
			old[1]= mval[1];
			first= 1;
		} 
		else {
			BIF_wait_for_statechange();
		}
	}

	linep2= txt_get_span(st->text->lines.first, st->text->sell);
	charp2= st->text->selc;
		
	if (sell!=linep2 || selc!=charp2)
		txt_undo_add_toop(st->text, UNDO_STO, sell, selc, linep2, charp2);

	pop_space_text(st);
}

static int do_suggest_select(SpaceText *st)
{
	SuggItem *item, *first, *last, *sel;
	TextLine *tmp;
	int l, x, y, w, h, i;
	int tgti, *top;
	short mval[2];
	
	if (!st || !st->text) return 0;
	if (!texttool_text_is_active(st->text)) return 0;

	first = texttool_suggest_first();
	last = texttool_suggest_last();
	sel = texttool_suggest_selected();
	top = texttool_suggest_top();

	if (!last || !first)
		return 0;

	/* Count the visible lines to the cursor */
	for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
	if (l<0) return 0;
	
	if(st->showlinenrs) {
		x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
	} else {
		x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
	}
	y = curarea->winy - st->lheight*l - 2;

	w = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
	h = SUGG_LIST_SIZE*st->lheight + 8;

	getmouseco_areawin(mval);

	if (mval[0]<x || x+w<mval[0] || mval[1]<y-h || y<mval[1])
		return 0;

	/* Work out which of the items is at the top of the visible list */
	for (i=0, item=first; i<*top && item->next; i++, item=item->next);

	/* Work out the target item index in the visible list */
	tgti = (y-mval[1]-4) / st->lheight;
	if (tgti<0 || tgti>SUGG_LIST_SIZE)
		return 1;

	for (i=tgti; i>0 && item->next; i--, item=item->next);
	if (item)
		texttool_suggest_select(item);
	return 1;
}

static void pop_suggest_list()
{
	SuggItem *item, *sel;
	int *top, i;

	item= texttool_suggest_first();
	sel= texttool_suggest_selected();
	top= texttool_suggest_top();

	i= 0;
	while (item && item != sel) {
		item= item->next;
		i++;
	}
	if (i > *top+SUGG_LIST_SIZE-1)
		*top= i-SUGG_LIST_SIZE+1;
	else if (i < *top)
		*top= i;
}

void draw_documentation(SpaceText *st)
{
	TextLine *tmp;
	char *docs, buf[DOC_WIDTH+1], *p;
	int len, i, br, lines;
	int boxw, boxh, l, x, y, top;
	
	if (!st || !st->text) return;
	if (!texttool_text_is_active(st->text)) return;
	
	docs = texttool_docs_get();

	if (!docs) return;

	/* Count the visible lines to the cursor */
	for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
	if (l<0) return;
	
	if(st->showlinenrs) {
		x= spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
	} else {
		x= spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
	}
	if (texttool_suggest_first()) {
		x += SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 50;
	}

	top= y= curarea->winy - st->lheight*l - 2;
	len= strlen(docs);
	boxw= DOC_WIDTH*spacetext_get_fontwidth(st) + 20;
	boxh= (DOC_HEIGHT+1)*st->lheight;

	/* Draw panel */
	BIF_ThemeColor(TH_BACK);
	glRecti(x, y, x+boxw, y-boxh);
	BIF_ThemeColor(TH_SHADE1);
	glBegin(GL_LINE_LOOP);
	glVertex2i(x, y);
	glVertex2i(x+boxw, y);
	glVertex2i(x+boxw, y-boxh);
	glVertex2i(x, y-boxh);
	glEnd();
	glBegin(GL_LINE_LOOP);
	glVertex2i(x+boxw-10, y-7);
	glVertex2i(x+boxw-4, y-7);
	glVertex2i(x+boxw-7, y-2);
	glEnd();
	glBegin(GL_LINE_LOOP);
	glVertex2i(x+boxw-10, y-boxh+7);
	glVertex2i(x+boxw-4, y-boxh+7);
	glVertex2i(x+boxw-7, y-boxh+2);
	glEnd();
	BIF_ThemeColor(TH_TEXT);

	i= 0; br= DOC_WIDTH; lines= -doc_scroll;
	for (p=docs; *p; p++) {
		if (*p == '\r' && *(++p) != '\n') *(--p)= '\n'; /* Fix line endings */
		if (*p == ' ' || *p == '\t')
			br= i;
		else if (*p == '\n') {
			buf[i]= '\0';
			if (lines>=0) {
				y -= st->lheight;
				text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
			}
			i= 0; br= DOC_WIDTH; lines++;
		}
		buf[i++]= *p;
		if (i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
			buf[br]= '\0';
			if (lines>=0) {
				y -= st->lheight;
				text_draw(st, buf, 0, 0, 1, x+4, y-3, NULL);
			}
			p -= i-br-1; /* Rewind pointer to last break */
			i= 0; br= DOC_WIDTH; lines++;
		}
		if (lines >= DOC_HEIGHT) break;
	}
	if (doc_scroll > 0 && lines < DOC_HEIGHT) {
		doc_scroll--;
		draw_documentation(st);
	}
}

void draw_suggestion_list(SpaceText *st)
{
	SuggItem *item, *first, *last, *sel;
	TextLine *tmp;
	char str[SUGG_LIST_WIDTH+1];
	int w, boxw=0, boxh, i, l, x, y, b, *top;
	
	if (!st || !st->text) return;
	if (!texttool_text_is_active(st->text)) return;

	first = texttool_suggest_first();
	last = texttool_suggest_last();

	if (!first || !last) return;

	pop_suggest_list();
	sel = texttool_suggest_selected();
	top = texttool_suggest_top();

	/* Count the visible lines to the cursor */
	for (tmp=st->text->curl, l=-st->top; tmp; tmp=tmp->prev, l++);
	if (l<0) return;
	
	if(st->showlinenrs) {
		x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET + TEXTXLOC - 4;
	} else {
		x = spacetext_get_fontwidth(st)*(st->text->curc-st->left) + TXT_OFFSET - 4;
	}
	y = curarea->winy - st->lheight*l - 2;

	boxw = SUGG_LIST_WIDTH*spacetext_get_fontwidth(st) + 20;
	boxh = SUGG_LIST_SIZE*st->lheight + 8;
	
	BIF_ThemeColor(TH_SHADE1);
	glRecti(x-1, y+1, x+boxw+1, y-boxh-1);
	BIF_ThemeColor(TH_BACK);
	glRecti(x, y, x+boxw, y-boxh);

	/* Set the top 'item' of the visible list */
	for (i=0, item=first; i<*top && item->next; i++, item=item->next);

	for (i=0; i<SUGG_LIST_SIZE && item; i++, item=item->next) {

		y -= st->lheight;

		strncpy(str, item->name, SUGG_LIST_WIDTH);
		str[SUGG_LIST_WIDTH] = '\0';

		w = BMF_GetStringWidth(spacetext_get_font(st), str);
		
		if (item == sel) {
			BIF_ThemeColor(TH_SHADE2);
			glRecti(x+16, y-3, x+16+w, y+st->lheight-3);
		}
		b=1; /* b=1 colour block, text is default. b=0 no block, colour text */
		switch (item->type) {
			case 'k': BIF_ThemeColor(TH_SYNTAX_B); b=0; break;
			case 'm': BIF_ThemeColor(TH_TEXT); break;
			case 'f': BIF_ThemeColor(TH_SYNTAX_L); break;
			case 'v': BIF_ThemeColor(TH_SYNTAX_N); break;
			case '?': BIF_ThemeColor(TH_TEXT); b=0; break;
		}
		if (b) {
			glRecti(x+8, y+2, x+11, y+5);
			BIF_ThemeColor(TH_TEXT);
		}
		text_draw(st, str, 0, 0, 1, x+16, y-1, NULL);

		if (item == last) break;
	}
}

static short check_blockhandler(SpaceText *st, short handler)
{
	short a;
	
	for(a=0; a<SPACE_MAXHANDLER; a+=2)
		if (st->blockhandler[a]==handler) return 1;
	return 0;
}

/* Find and replace GUI panel */
static void text_panel_find(short cntrl)	// TEXT_HANDLER_FIND
{
	uiBlock *block;

	/* Ensure that find and replace buffers have been allocated */
	if (!g_find_str || !g_replace_str) {
		g_find_str= MEM_mallocN(TXT_MAXFINDSTR+1, "find_string");
		g_replace_str= MEM_mallocN(TXT_MAXFINDSTR+1, "replace_string");
		g_find_str[0]= g_replace_str[0]= '\0';
	}
	
	block= uiNewBlock(&curarea->uiblocks, "text_panel_find", UI_EMBOSS, UI_HELV, curarea->win);
	uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl);
	uiSetPanelHandler(TEXT_HANDLER_FIND);  // for close and esc
	if(uiNewPanel(curarea, block, "Find & Replace", "Text", curarea->winx-230, curarea->winy-130, 260, 120)==0) return;
	
	uiBlockBeginAlign(block);
	uiDefButC(block, TEX, 0, "Find: ", 0,80,220,20, g_find_str, 0,(float)TXT_MAXFINDSTR, 0,0, "");
	uiDefIconBut(block, BUT, B_PASTEFIND, ICON_TEXT, 220,80,20,20, NULL, 0,0,0,0, "Copy from selection");
	uiDefButC(block, TEX, 0, "Replace: ", 0,60,220,20, g_replace_str, 0,(float)TXT_MAXFINDSTR, 0,0, "");
	uiDefIconBut(block, BUT, B_PASTEREPLACE, ICON_TEXT, 220,60,20,20, NULL, 0,0,0,0, "Copy from selection");
	uiBlockEndAlign(block);
	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");
	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");
	uiDefBut(block, BUT, B_TEXTFIND,    "Find",       0,0,50,20, NULL, 0,0,0,0, "Find next");
	uiDefBut(block, BUT, B_TEXTREPLACE, "Replace/Find", 50,0,110,20, NULL, 0,0,0,0, "Replace then find next");
	uiDefBut(block, BUT, B_TEXTMARKALL, "Mark All",   160,0,80,20, NULL, 0,0,0,0, "Mark each occurrence to edit all from one");
}

/* mode: 0 find only, 1 replace/find, 2 mark all occurrences */
void find_and_replace(SpaceText *st, short mode)
{
	Text *start= NULL, *text= st->text;
	int flags, first= 1;
	char *tmp;

	if (!check_blockhandler(st, TEXT_HANDLER_FIND)) {
		toggle_blockhandler(st->area, TEXT_HANDLER_FIND, UI_PNL_TO_MOUSE);
		return;
	}

	if (!g_find_str || !g_replace_str) return;
	if (g_find_str[0] == '\0') return;
	flags= g_find_flags;
	if (flags & TXT_FIND_ALLTEXTS) flags ^= TXT_FIND_WRAP;

	do {
		if (first)
			txt_clear_markers(text, TMARK_GRP_FINDALL, 0);
		first= 0;
		
		/* Replace current */
		if (mode && txt_has_sel(text)) {
			tmp= txt_sel_to_buf(text);
			if (strcmp(g_find_str, tmp)==0) {
				if (mode==1) {
					txt_insert_buf(text, g_replace_str);
					if (st->showsyntax) txt_format_line(st, text->curl, 1);
				} else if (mode==2) {
					char color[4];
					BIF_GetThemeColor4ubv(TH_SHADE2, color);
					if (txt_find_marker(text, text->curl, text->selc, TMARK_GRP_FINDALL, 0)) {
						if (tmp) MEM_freeN(tmp), tmp=NULL;
						break;
					}
					txt_add_marker(text, text->curl, text->curc, text->selc, color, TMARK_GRP_FINDALL, TMARK_EDITALL);
				}
			}
			MEM_freeN(tmp);
			tmp= NULL;
		}

		/* Find next */
		if (txt_find_string(text, g_find_str, flags & TXT_FIND_WRAP)) {
			pop_space_text(st);
		} else if (flags & TXT_FIND_ALLTEXTS) {
			if (text==start) break;
			if (!start) start= text;
			if (text->id.next)
				text= st->text= text->id.next;
			else
				text= st->text= G.main->text.first;
			txt_move_toline(text, 0, 0);
			pop_space_text(st);
			first= 1;
		} else {
			okee("Text not found: %s", g_find_str);
			break;
		}
	} while (mode==2);
}

static void do_find_buttons(val)
{
	Text *text;
	SpaceText *st;
	char *tmp;

	st= curarea->spacedata.first;
	if (!st || st->spacetype != SPACE_TEXT) return;
	text= st->text;
	if (!text) return;

	switch (val) {
		case B_PASTEFIND:
			if (!g_find_str) break;
			tmp= txt_sel_to_buf(text);
			strncpy(g_find_str, tmp, TXT_MAXFINDSTR);
			MEM_freeN(tmp);
			break;
		case B_PASTEREPLACE:
			if (!g_replace_str) break;
			tmp= txt_sel_to_buf(text);
			strncpy(g_replace_str, tmp, TXT_MAXFINDSTR);
			MEM_freeN(tmp);
			break;
		case B_TEXTFIND:
			find_and_replace(st, 0);
			break;
		case B_TEXTREPLACE:
			find_and_replace(st, 1);
			break;
		case B_TEXTMARKALL:
			find_and_replace(st, 2);
			break;
	}
}

static void text_blockhandlers(ScrArea *sa)
{
	SpaceText *st= sa->spacedata.first;
	short a;

	/* warning; blocks need to be freed each time, handlers dont remove */
	uiFreeBlocksWin(&sa->uiblocks, sa->win);
	
	for(a=0; a<SPACE_MAXHANDLER; a+=2) {
		/* clear action value for event */
		switch(st->blockhandler[a]) {
			case TEXT_HANDLER_FIND:
				text_panel_find(st->blockhandler[a+1]);
				break;
		}
	}
	uiDrawBlocksPanels(sa, 0);
}

void drawtextspace(ScrArea *sa, void *spacedata)
{
	SpaceText *st= curarea->spacedata.first;
	Text *text;
	TextLine *tmp;
	float col[3];
	int i, x, y;
	int linecount = 0;
	char linenr[12];

	if (st==NULL || st->spacetype != SPACE_TEXT) return;

	bwin_clear_viewmat(sa->win);	/* clear buttons view */
	glLoadIdentity();
	
	BIF_GetThemeColor3fv(TH_BACK, col);
	glClearColor(col[0], col[1], col[2], 0.0);
	glClear(GL_COLOR_BUFFER_BIT);
	myortho2(-0.375, (float)(sa->winx)-0.375, -0.375, (float)(sa->winy)-0.375);

	draw_area_emboss(sa);

	text= st->text;
	if(!text) return;
	
	/* Make sure all the positional pointers exist */
	if (!text->curl || !text->sell || !text->lines.first || !text->lines.last)
		txt_clean_text(text);
	
	if(st->lheight) st->viewlines= (int) curarea->winy/st->lheight;
	else st->viewlines= 0;
	
	if(st->showlinenrs) {
		BIF_ThemeColor(TH_GRID);
		glRecti(23,  0, (st->lheight==15)?63:59,  curarea->winy - 2);
	}

	draw_cursor(st);

	tmp= text->lines.first;
	for (i= 0; i<st->top && tmp; i++) {
		if (st->showsyntax && !tmp->format) txt_format_line(st, tmp, 0);
		tmp= tmp->next;
		linecount++;
	}

	y= curarea->winy-st->lheight;
	x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;

	BIF_ThemeColor(TH_TEXT);
	for (i=0; y>0 && i<st->viewlines && tmp; i++, tmp= tmp->next) {
		if (st->showsyntax && !tmp->format) {
			txt_format_line(st, tmp, 0);
		}
		if(st->showlinenrs) {
			/*Change the color of the current line the cursor is on*/
			if(tmp == text->curl) { 
				BIF_ThemeColor(TH_HILITE);
			} else {
				BIF_ThemeColor(TH_TEXT);
			}
			if(((float)(i + linecount + 1)/10000.0) < 1.0) {
				sprintf(linenr, "%4d", i + linecount + 1);
				glRasterPos2i(TXT_OFFSET - 7, y);
			} else {
				sprintf(linenr, "%5d", i + linecount + 1);
				glRasterPos2i(TXT_OFFSET - 11, y);
			}
			BIF_ThemeColor(TH_TEXT);
			BMF_DrawString(spacetext_get_font(st), linenr);
		}
		if (st->wordwrap) {
			int lines = text_draw_wrapped(st, tmp->line, x, y, curarea->winx-x, tmp->format);
			y -= lines*st->lheight;
		} else {
			text_draw(st, tmp->line, st->left, 0, 1, x, y, tmp->format);
			y -= st->lheight;
		}
	}
	
	draw_brackets(st);
	draw_markers(st);

	draw_textscroll(st);
	draw_documentation(st);
	draw_suggestion_list(st);
	
	bwin_scalematrix(sa->win, st->blockscale, st->blockscale, st->blockscale);
	text_blockhandlers(sa);
	
	curarea->win_swap= WIN_BACK_OK;
}

/* Moves the view to the cursor location,
  also used to make sure the view isnt outside the file */
void pop_space_text (SpaceText *st)
{
	int i, x;

	if(!st) return;
	if(!st->text) return;
	if(!st->text->curl) return;
		
	i= txt_get_span(st->text->lines.first, st->text->sell);
	if (st->top+st->viewlines <= i || st->top > i) {
		st->top= i - st->viewlines/2;
	}
	
	if (st->wordwrap) {
		st->left= 0;
	} else {
		x= text_draw(st, st->text->sell->line, st->left, st->text->selc, 0, 0, 0, NULL);

		if (x==0 || x>curarea->winx) {
			st->left= st->text->curc-0.5*(curarea->winx)/spacetext_get_fontwidth(st);
		}
	}

	if (st->top < 0) st->top= 0;
	if (st->left <0) st->left= 0;
}

void add_text_fs(char *file) /* bad but cant pass an as arg here */
{
	SpaceText *st= curarea->spacedata.first;
	Text *text;

	if (st==NULL || st->spacetype != SPACE_TEXT) return;

	text= add_text(file);

	st->text= text;

	st->top= 0;

	if (st->showsyntax) txt_format_text(st);
	allqueue(REDRAWTEXT, 0);
	allqueue(REDRAWHEADERS, 0);	
}

void free_textspace(SpaceText *st)
{
	if (!st) return;

	st->text= NULL;
}

static void save_mem_text(char *str)
{
	SpaceText *st= curarea->spacedata.first;
	Text *text;
	
	if (!str) return;
	
	if (!st) return;
	if (st->spacetype != SPACE_TEXT) return;

	text= st->text;
	if(!text) return;
	
	if (text->name) MEM_freeN(text->name);
	text->name= MEM_mallocN(strlen(str)+1, "textname");
	strcpy(text->name, str);

	text->flags ^= TXT_ISMEM;
		
	txt_write_file(text);
}

void txt_write_file(Text *text) 
{
	FILE *fp;
	TextLine *tmp;
	struct stat st;
	int res;
	char file[FILE_MAXDIR+FILE_MAXFILE];
	
	/* Do we need to get a filename? */
	if (text->flags & TXT_ISMEM) {
		if (text->name)
			activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->name, save_mem_text);
		else
			activate_fileselect(FILE_SPECIAL, "SAVE TEXT FILE", text->id.name+2, save_mem_text);
		return;
	}

	BLI_strncpy(file, text->name, FILE_MAXDIR+FILE_MAXFILE);
	BLI_convertstringcode(file, G.sce);
	
	/* Should we ask to save over? */
	if (text->flags & TXT_ISTMP) {
		if (BLI_exists(file)) {
			if (!okee("Save over")) return;
		} else if (!okee("Create new file")) return;

		text->flags ^= TXT_ISTMP;
	}
		
	fp= fopen(file, "w");
	if (fp==NULL) {
		error("Unable to save file");
		return;
	}

	tmp= text->lines.first;
	while (tmp) {
		if (tmp->next) fprintf(fp, "%s\n", tmp->line);
		else fprintf(fp, "%s", tmp->line);
		
		tmp= tmp->next;
	}
	
	fclose (fp);

	res= stat(file, &st);
	text->mtime= st.st_mtime;
	
	if (text->flags & TXT_ISDIRTY) text->flags ^= TXT_ISDIRTY;
}

void unlink_text(Text *text)
{
	bScreen *scr;
	ScrArea *area;
	SpaceLink *sl;
	
	/* check if this text was used as script link:
	 * this check function unsets the pointers and returns how many
	 * script links used this Text */
	if (BPY_check_all_scriptlinks (text)) {
		allqueue(REDRAWBUTSSCRIPT, 0);
	}
	/* equivalently for pynodes: */
	if (nodeDynamicUnlinkText ((ID*)text)) {
		allqueue(REDRAWNODE, 0);
	}

	for (scr= G.main->screen.first; scr; scr= scr->id.next) {
		for (area= scr->areabase.first; area; area= area->next) {
			for (sl= area->spacedata.first; sl; sl= sl->next) {
				if (sl->spacetype==SPACE_TEXT) {
					SpaceText *st= (SpaceText*) sl;
					
					if (st->text==text) {
						st->text= NULL;
						st->top= 0;
						
						if (st==area->spacedata.first) {
							scrarea_queue_redraw(area);
						}
					}
				}
			}
		}
	}
}

int jumptoline_interactive(SpaceText *st)
{
	short nlines= txt_get_span(st->text->lines.first, st->text->lines.last)+1;
	short tmp= txt_get_span(st->text->lines.first, st->text->curl)+1;

	if (button(&tmp, 1, nlines, "Jump to line:")) {
		txt_move_toline(st->text, tmp-1, 0);
		pop_space_text(st);
		return 1;
	} else {
		return 0;
	}
}


int bufferlength;
static char *copybuffer = NULL;

static void txt_copy_selectbuffer (Text *text)
{
	int length=0;
	TextLine *tmp, *linef, *linel;
	int charf, charl;
	
	if (!text) return;
	if (!text->curl) return;
	if (!text->sell) return;

	if (!txt_has_sel(text)) return;
	
	if (copybuffer) {
		MEM_freeN(copybuffer);
		copybuffer= NULL;
	}

	if (text->curl==text->sell) {
		linef= linel= text->curl;
		
		if (text->curc < text->selc) {
			charf= text->curc;
			charl= text->selc;
		} else{
			charf= text->selc;
			charl= text->curc;
		}
	} else if (txt_get_span(text->curl, text->sell)<0) {
		linef= text->sell;
		linel= text->curl;

		charf= text->selc;		
		charl= text->curc;
	} else {
		linef= text->curl;
		linel= text->sell;
		
		charf= text->curc;
		charl= text->selc;
	}

	if (linef == linel) {
		length= charl-charf;

		copybuffer= MEM_mallocN(length+1, "cut buffera");
		
		BLI_strncpy(copybuffer, linef->line + charf, length+1);
	} else {
		length+= linef->len - charf;
		length+= charl;
		length++; /* For the '\n' */
		
		tmp= linef->next;
		while (tmp && tmp!= linel) {
			length+= tmp->len+1;
			tmp= tmp->next;
		}
		
		copybuffer= MEM_mallocN(length+1, "cut bufferb");
		
		strncpy(copybuffer, linef->line+ charf, linef->len-charf);
		length= linef->len-charf;
		
		copybuffer[length++]='\n';
		
		tmp= linef->next;
		while (tmp && tmp!=linel) {
			strncpy(copybuffer+length, tmp->line, tmp->len);
			length+= tmp->len;
			
			copybuffer[length++]='\n';			
			
			tmp= tmp->next;
		}
		strncpy(copybuffer+length, linel->line, charl);
		length+= charl;
		
		copybuffer[length]=0;
	}

	bufferlength = length;
}

static char *unixNewLine(char *buffer)
{
	char *p, *p2, *output;
	
	/* we can afford the few extra bytes */
	output= MEM_callocN(strlen(buffer)+1, "unixnewline");
	for (p= buffer, p2= output; *p; p++)
		if (*p != '\r') *(p2++)= *p;
	
	*p2= 0;
	return(output);
}

static char *winNewLine(char *buffer)
{
	char *p, *p2, *output;
	int add= 0;
	
	for (p= buffer; *p; p++)
		if (*p == '\n') add++;
		
	bufferlength= p-buffer+add+1;
	output= MEM_callocN(bufferlength, "winnewline");
	for (p= buffer, p2= output; *p; p++, p2++) {
		if (*p == '\n') { 
			*(p2++)= '\r'; *p2= '\n';
		} else *p2= *p;
	}
	*p2= 0;
	
	return(output);
}

void txt_paste_clipboard(Text *text)
{
	char * buff;
	char *temp_buff;
	
	buff = (char*)getClipboard(0);
	if(buff) {
		temp_buff = unixNewLine(buff);
		
		txt_insert_buf(text, temp_buff);
		if(buff){free((void*)buff);}
		if(temp_buff){MEM_freeN(temp_buff);}
	}
}

void get_selection_buffer(Text *text)
{
	char *buff = getClipboard(1);
	
	txt_insert_buf(text, buff);
}

void txt_copy_clipboard(Text *text)
{
	char *temp;

	txt_copy_selectbuffer(text);

	if (copybuffer) {
		copybuffer[bufferlength] = '\0';
		temp = winNewLine(copybuffer);
		
		putClipboard(temp, 0);
		MEM_freeN(temp);
		MEM_freeN(copybuffer);
		copybuffer= NULL;
	}
}

void run_python_script(SpaceText *st)
{
	Text *text=st->text;
	char *py_filename;

	if (!BPY_txt_do_python_Text(text)) {
		int lineno = BPY_Err_getLinenumber();
		// jump to error if happened in current text:
		py_filename = (char*) BPY_Err_getFilename();

		/* st->text can become NULL: user called Blender.Load(blendfile)
		 * before the end of the script. */
		if (!st->text) return;

		if (!strcmp(py_filename, st->text->id.name+2)) {
			error_pyscript(  );
			if (lineno >= 0) {
				txt_move_toline(text, lineno-1, 0);
				txt_sel_line(text);
				pop_space_text(st);
			}	
		} else {
			error("Error in other (possibly external) file, "\
				"check console");
		}	
	}
}

static void set_tabs(Text *text)
{
	SpaceText *st = curarea->spacedata.first;
	
	st->currtab_set = setcurr_tab(text);
}

static void wrap_move_bol(SpaceText *st, short sel)
{
	Text *text= st->text;
	int offl, offc, lin;

	lin= txt_get_span(text->lines.first, text->sell);
	wrap_offset(st, text->sell, text->selc, &offl, &offc);

	if (sel) {
		txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, -offc);
		text->selc= -offc;
	} else {
		txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, -offc);
		text->curc= -offc;
		txt_pop_sel(text);
	}
}

static void wrap_move_eol(SpaceText *st, short sel)
{
	Text *text= st->text;
	int offl, offc, lin, startl, c;

	lin= txt_get_span(text->lines.first, text->sell);
	wrap_offset(st, text->sell, text->selc, &offl, &offc);
	startl= offl;
	c= text->selc;
	while (offl==startl && text->sell->line[c]!='\0') {
		c++;
		wrap_offset(st, text->sell, c, &offl, &offc);
	} if (offl!=startl) c--;

	if (sel) {
		txt_undo_add_toop(text, UNDO_STO, lin, text->selc, lin, c);
		text->selc= c;
	} else {
		txt_undo_add_toop(text, UNDO_CTO, lin, text->curc, lin, c);
		text->curc= c;
		txt_pop_sel(text);
	}
}

static void wrap_move_up(SpaceText *st, short sel)
{
	Text *text= st->text;
	int offl, offl_1, offc, fromline, toline, c, target;

	wrap_offset(st, text->sell, 0, &offl_1, &offc);
	wrap_offset(st, text->sell, text->selc, &offl, &offc);
	fromline= toline= txt_get_span(text->lines.first, text->sell);
	target= text->selc + offc;

	if (offl==offl_1) {
		if (!text->sell->prev) {
			txt_move_bol(text, sel);
			return;
		}
		toline--;
		c= text->sell->prev->len; /* End of prev. line */
		wrap_offset(st, text->sell->prev, c, &offl, &offc);
		c= -offc+target;
	} else {
		c= -offc-1; /* End of prev. line */
		wrap_offset(st, text->sell, c, &offl, &offc);
		c= -offc+target;
	}
	if (c<0) c=0;

	if (sel) {
		txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
		if (toline<fromline) text->sell= text->sell->prev;
		if(text->sell) {
			if (c>text->sell->len) c= text->sell->len;
			text->selc= c;
		}
	} 
	else if(text->curl) {
		txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
		if (toline<fromline) text->curl= text->curl->prev;
		if(text->curl) {
			if (c>text->curl->len) c= text->curl->len;
			text->curc= c;
			txt_pop_sel(text);
		}
	}
}

static void wrap_move_down(SpaceText *st, short sel)
{
	Text *text= st->text;
	int offl, startoff, offc, fromline, toline, c, target;

	wrap_offset(st, text->sell, text->selc, &offl, &offc);
	fromline= toline= txt_get_span(text->lines.first, text->sell);
	target= text->selc + offc;
	startoff= offl;
	c= text->selc;
	while (offl==startoff && text->sell->line[c]!='\0') {
		c++;
		wrap_offset(st, text->sell, c, &offl, &offc);
	}

	if (text->sell->line[c]=='\0') {
		if (!text->sell->next) {
			txt_move_eol(text, sel);
			return;
		}
		toline++;
		c= target;
	} else {
		c += target;
		if (c > text->sell->len) c= text->sell->len;
	}
	if (c<0) c=0;

	if (sel) {
		txt_undo_add_toop(text, UNDO_STO, fromline, text->selc, toline, c);
		if (toline>fromline) text->sell= text->sell->next;
		if(text->sell) {
			if (c>text->sell->len) c= text->sell->len;
			text->selc= c;
		}
	} 
	else if(text->curl) {
		txt_undo_add_toop(text, UNDO_CTO, fromline, text->curc, toline, c);
		if (toline>fromline) text->curl= text->curl->next;
		if(text->curl) {
			if (c > text->curl->len) c= text->curl->len;
			text->curc= c;
			txt_pop_sel(text);
		}
	}
}

static void get_suggest_prefix(Text *text, int offset)
{
	int i, len;
	char *line, tmp[256];

	if (!text) return;
	if (!texttool_text_is_active(text)) return;

	line= text->curl->line;
	for (i=text->curc-1+offset; i>=0; i--)
		if (!check_identifier(line[i]))
			break;
	i++;
	len= text->curc-i+offset;
	if (len > 255) {
		printf("Suggestion prefix too long\n");
		len = 255;
	}
	strncpy(tmp, line+i, len);
	tmp[len]= '\0';
	texttool_suggest_prefix(tmp);
}

static void confirm_suggestion(Text *text, int skipleft)
{
	SuggItem *sel;
	int i, over=0;
	char *line;

	if (!text) return;
	if (!texttool_text_is_active(text)) return;

	sel = texttool_suggest_selected();
	if (!sel) return;

	line= text->curl->line;
	i=text->curc-skipleft-1;
	while (i>=0) {
		if (!check_identifier(line[i]))
			break;
		over++;
		i--;
	}

	for (i=0; i<skipleft; i++)
		txt_move_left(text, 0);
	for (i=0; i<over; i++)
		txt_move_left(text, 1);

	txt_insert_buf(text, sel->name);
	
	for (i=0; i<skipleft; i++)
		txt_move_right(text, 0);

	texttool_text_clear();
}

static short do_texttools(SpaceText *st, char ascii, unsigned short evnt, short val)
{
	int draw=0, tools=0, swallow=0, scroll=1;
	if (!texttool_text_is_active(st->text)) return 0;
	if (!st->text || st->text->id.lib) return 0;

	if (st->doplugins && texttool_text_is_active(st->text)) {
		if (texttool_suggest_first()) tools |= TOOL_SUGG_LIST;
		if (texttool_docs_get()) tools |= TOOL_DOCUMENT;
	}

	if (ascii) {
		if (tools & TOOL_SUGG_LIST) {
			if ((ascii != '_' && ascii != '*' && ispunct(ascii)) || check_whitespace(ascii)) {
				confirm_suggestion(st->text, 0);
				if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
			} else if ((st->overwrite && txt_replace_char(st->text, ascii)) || txt_add_char(st->text, ascii)) {
				get_suggest_prefix(st->text, 0);
				pop_suggest_list();
				swallow= 1;
				draw= 1;
			}
		}
		if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;

	} else if (val==1 && evnt) {
		switch (evnt) {
			case LEFTMOUSE:
				if (do_suggest_select(st))
					swallow= 1;
				else {
					if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
					if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
				}
				draw= 1;
				break;
			case MIDDLEMOUSE:
				if (do_suggest_select(st)) {
					confirm_suggestion(st->text, 0);
					if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
					swallow= 1;
				} else {
					if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
					if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
				}
				draw= 1;
				break;
			case ESCKEY:
				draw= swallow= 1;
				if (tools & TOOL_SUGG_LIST) texttool_suggest_clear();
				else if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
				else draw= swallow= 0;
				break;
			case RETKEY:
				if (tools & TOOL_SUGG_LIST) {
					confirm_suggestion(st->text, 0);
					if (st->showsyntax) txt_format_line(st, st->text->curl, 1);
					swallow= 1;
					draw= 1;
				}
				if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
				break;
			case LEFTARROWKEY:
			case BACKSPACEKEY:
				if (tools & TOOL_SUGG_LIST) {
					if (G.qual)
						texttool_suggest_clear();
					else {
						/* Work out which char we are about to delete/pass */
						if (st->text->curl && st->text->curc > 0) {
							char ch= st->text->curl->line[st->text->curc-1];
							if ((ch=='_' || !ispunct(ch)) && !check_whitespace(ch)) {
								get_suggest_prefix(st->text, -1);
								pop_suggest_list();
							}
							else
								texttool_suggest_clear();
						} else
							texttool_suggest_clear();
					}
				}
				if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
				break;
			case RIGHTARROWKEY:
				if (tools & TOOL_SUGG_LIST) {
					if (G.qual)
						texttool_suggest_clear();
					else {
						/* Work out which char we are about to pass */
						if (st->text->curl && st->text->curc < st->text->curl->len) {
							char ch= st->text->curl->line[st->text->curc+1];
							if ((ch=='_' || !ispunct(ch)) && !check_whitespace(ch)) {
								get_suggest_prefix(st->text, 1);
								pop_suggest_list();
							}
							else
								texttool_suggest_clear();
						} else
							texttool_suggest_clear();
					}
				}
				if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0;
				break;
			case PAGEDOWNKEY:
				scroll= SUGG_LIST_SIZE-1;
			case WHEELDOWNMOUSE:
			case DOWNARROWKEY:
				if (tools & TOOL_DOCUMENT) {
					doc_scroll++;
					swallow= 1;
					draw= 1;
					break;
				} else if (tools & TOOL_SUGG_LIST) {
					SuggItem *sel = texttool_suggest_selected();
					if (!sel) {
						texttool_suggest_select(texttool_suggest_first());
					} else while (sel && sel!=texttool_suggest_last() && sel->next && scroll--) {
						texttool_suggest_select(sel->next);
						sel= sel->next;
					}
					pop_suggest_list();
					swallow= 1;
					draw= 1;
					break;
				}
			case PAGEUPKEY:
				scroll= SUGG_LIST_SIZE-1;
			case WHEELUPMOUSE:
			case UPARROWKEY:
				if (tools & TOOL_DOCUMENT) {
					if (doc_scroll>0) doc_scroll--;
					swallow= 1;
					draw= 1;
					break;
				} else if (tools & TOOL_SUGG_LIST) {
					SuggItem *sel = texttool_suggest_selected();
					while (sel && sel!=texttool_suggest_first() && sel->prev && scroll--) {
						texttool_suggest_select(sel->prev);
						sel= sel->prev;
					}
					pop_suggest_list();
					swallow= 1;
					draw= 1;
					break;
				}
			case RIGHTSHIFTKEY:
			case LEFTSHIFTKEY:
				break;
			default:
				if (tools & TOOL_SUGG_LIST) texttool_suggest_clear(), draw= 1;
				if (tools & TOOL_DOCUMENT) texttool_docs_clear(), doc_scroll= 0, draw= 1;
		}
	}

	if (draw)
		redraw_alltext();
	
	return swallow;
}

static short do_markers(SpaceText *st, char ascii, unsigned short evnt, short val)
{
	Text *text;
	TextMarker *marker, *mrk, *nxt;
	int c, s, draw=0, swallow=0;

	text= st->text;
	if (!text || text->id.lib || text->curl != text->sell) return 0;

	marker= txt_find_marker(text, text->sell, text->selc, 0, 0);
	if (marker && (marker->start > text->curc || marker->end < text->curc))
		marker= NULL;

	if (!marker) {
		/* Find the next temporary marker */
		if (evnt==TABKEY) {
			int lineno= txt_get_span(text->lines.first, text->curl);
			TextMarker *mrk= text->markers.first;
			while (mrk) {
				if (!marker && (mrk->flags & TMARK_TEMP)) marker= mrk;
				if ((mrk->flags & TMARK_TEMP) && (mrk->lineno > lineno || (mrk->lineno==lineno && mrk->end > text->curc))) {
					marker= mrk;
					break;
				}
				mrk= mrk->next;
			}
			if (marker) {
				txt_move_to(text, marker->lineno, marker->start, 0);
				txt_move_to(text, marker->lineno, marker->end, 1);
				pop_space_text(st);
				evnt= ascii= val= 0;
				draw= 1;
				swallow= 1;
			}
		} else if (evnt==ESCKEY) {
			if (txt_clear_markers(text, 0, TMARK_TEMP)) swallow= 1;
			else if (txt_clear_markers(text, 0, 0)) swallow= 1;
			else return 0;
			evnt= ascii= val= 0;
			draw= 1;
		}
		if (!swallow) return 0;
	}

	if (ascii) {
		if (marker->flags & TMARK_EDITALL) {
			c= text->curc-marker->start;
			s= text->selc-marker->start;
			if (s<0 || s>marker->end-marker->start) return 0;

			mrk= txt_next_marker(text, marker);
			while (mrk) {
				nxt=txt_next_marker(text, mrk); /* mrk may become invalid */
				txt_move_to(text, mrk->lineno, mrk->start+c, 0);
				if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
				if (st->overwrite) {
					if (txt_replace_char(text, ascii))
						if (st->showsyntax) txt_format_line(st, text->curl, 1);
				} else {
					if (txt_add_char(text, ascii)) {
						if (st->showsyntax) txt_format_line(st, text->curl, 1);
					}
				}

				if (mrk==marker || mrk==nxt) break;
				mrk=nxt;
			}
			swallow= 1;
			draw= 1;
		}
	} else if (val) {
		switch(evnt) {
			case BACKSPACEKEY:
				if (marker->flags & TMARK_EDITALL) {
					c= text->curc-marker->start;
					s= text->selc-marker->start;
					if (s<0 || s>marker->end-marker->start) return 0;
					
					mrk= txt_next_marker(text, marker);
					while (mrk) {
						nxt= txt_next_marker(text, mrk); /* mrk may become invalid */
						txt_move_to(text, mrk->lineno, mrk->start+c, 0);
						if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
						txt_backspace_char(text);
						if (st->showsyntax) txt_format_line(st, text->curl, 1);
						if (mrk==marker || mrk==nxt) break;
						mrk= nxt;
					}
					swallow= 1;
					draw= 1;
				}
				break;
			case DELKEY:
				if (marker->flags & TMARK_EDITALL) {
					c= text->curc-marker->start;
					s= text->selc-marker->start;
					if (s<0 || s>marker->end-marker->start) return 0;
					
					mrk= txt_next_marker(text, marker);
					while (mrk) {
						nxt= txt_next_marker(text, mrk); /* mrk may become invalid */
						txt_move_to(text, mrk->lineno, mrk->start+c, 0);
						if (s!=c) txt_move_to(text, mrk->lineno, mrk->start+s, 1);
						txt_delete_char(text);
						if (st->showsyntax) txt_format_line(st, text->curl, 1);
						if (mrk==marker || mrk==nxt) break;
						mrk= nxt;
					}
					swallow= 1;
					draw= 1;
				}
				break;
			case TABKEY:
				if (G.qual & LR_SHIFTKEY) {
					nxt= marker->prev;
					if (!nxt) nxt= text->markers.last;
				} else {
					nxt= marker->next;
					if (!nxt) nxt= text->markers.first;
				}
				if (marker->flags & TMARK_TEMP) {
					if (nxt==marker) nxt= NULL;
					BLI_freelinkN(&text->markers, marker);
				}
				mrk= nxt;
				if (mrk) {
					txt_move_to(text, mrk->lineno, mrk->start, 0);
					txt_move_to(text, mrk->lineno, mrk->end, 1);
					pop_space_text(st);
				}
				swallow= 1;
				draw= 1;
				break;

			/* Events that should clear markers */
			case UKEY: if (!(G.qual & LR_ALTKEY)) break;
			case ZKEY: if (evnt==ZKEY && !(G.qual & LR_CTRLKEY)) break;
			case RETKEY:
			case ESCKEY:
				if (marker->flags & (TMARK_EDITALL | TMARK_TEMP))
					txt_clear_markers(text, marker->group, 0);
				else
					BLI_freelinkN(&text->markers, marker);
				swallow= 1;
				draw= 1;
				break;
			case RIGHTMOUSE: /* Marker context menu? */
			case LEFTMOUSE:
				break;
			case FKEY: /* Allow find */
				if (G.qual & LR_SHIFTKEY) swallow= 1;
				break;

			default:
				if (G.qual!=0 && G.qual!=LR_SHIFTKEY)
					swallow= 1; /* Swallow all other shortcut events */
		}
	}
	
	if (draw)
		redraw_alltext();
	
	return swallow;
}


void winqreadtextspace(ScrArea *sa, void *spacedata, BWinEvent *evt)
{
	SpaceText *st= curarea->spacedata.first;
	Text *text;
	int do_draw=0, p;
	unsigned short event= evt->event;
	short val= evt->val;
	char ascii= evt->ascii;
	
	if (st==NULL || st->spacetype != SPACE_TEXT) return;
	
	/* smartass code to prevent the CTRL/ALT events below from not working! */
	if(G.qual & (LR_ALTKEY|LR_CTRLKEY))
		if(!ispunct(ascii)) 
			ascii= 0;

	text= st->text;
	
	if (!text) {
		if (event==RIGHTMOUSE) {
			switch (pupmenu("File %t|New %x0|Open... %x1")) {
			case 0:
				st->text= add_empty_text("Text");
				st->top= 0;
			
				allqueue(REDRAWTEXT, 0);
				allqueue(REDRAWHEADERS, 0);
				break;
			case 1:
				activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
				break;
			}
		}
		if (val && !ELEM(G.qual, 0, LR_SHIFTKEY)) {
			if (event==FKEY && G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
				switch (pupmenu("File %t|New %x0|Open... %x1")) {
				case 0:
					st->text= add_empty_text("Text");
					st->top= 0;
				
					allqueue(REDRAWTEXT, 0);
					allqueue(REDRAWHEADERS, 0);
					break;
				case 1:
					activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
					break;
				}
			} 
			else if (event==QKEY) {
				if (G.qual & LR_CTRLKEY) {
					if(okee("Quit Blender")) exit_usiblender();
				}
			}
			else if (event==NKEY) {
				if (G.qual & LR_ALTKEY) {
					st->text= add_empty_text("Text");
					st->top= 0;
				
					allqueue(REDRAWTEXT, 0);
					allqueue(REDRAWHEADERS, 0);
				}
			}
			else if (event==OKEY) {
				if (G.qual & LR_ALTKEY) {
					activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
				}
			}
		}
		return;
	}

	if (val && uiDoBlocks(&curarea->uiblocks, event, 1)!=UI_NOTHING) event= 0;

	if (st->doplugins && do_texttools(st, ascii, event, val)) return;
	if (do_markers(st, ascii, event, val)) return;
	
	if (event==UI_BUT_EVENT) {
		do_find_buttons(val);
		do_draw= 1;
	} else if (event==LEFTMOUSE) {
		if (val) {
			short mval[2];
			char *buffer;
			set_tabs(text);
			getmouseco_areawin(mval);
			
			if (mval[0]>2 && mval[0]<20 && mval[1]>2 && mval[1]<curarea->winy-2) {
				do_textscroll(st, 2);
			} else {
				do_selection(st, G.qual&LR_SHIFTKEY);
				if (txt_has_sel(text)) {
					buffer = txt_sel_to_buf(text);
					putClipboard(buffer, 1);
					MEM_freeN(buffer);
				}
				do_draw= 1;
			}
		}
	} else if (event==MIDDLEMOUSE) {
		if (val) {
			if (U.uiflag & USER_MMB_PASTE) {
				do_selection(st, G.qual&LR_SHIFTKEY);
				get_selection_buffer(text);
				do_draw= 1;
			} else {
				do_textscroll(st, 1);
			}
		}
	} else if (event==RIGHTMOUSE) {
		if (val) {
			if (txt_has_sel(text))
				p= pupmenu("Text %t|Cut%x10|Copy%x11|Paste%x12|New %x0|Open... %x1|Save %x2|Save As...%x3|Execute Script%x4");
			else
				p= pupmenu("Text %t|Paste%x12|New %x0|Open... %x1|Save %x2|Save As...%x3|Execute Script%x4");

			switch(p) {
				case 0:
					st->text= add_empty_text("Text");
					st->top= 0;
					
					allqueue(REDRAWTEXT, 0);
					allqueue(REDRAWHEADERS, 0);
					break;

				case 1:
					activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
					break;
					
				case 3:
					text->flags |= TXT_ISMEM;
					
				case 2:
					txt_write_file(text);
					do_draw= 1;
					break;
				case 4:
					run_python_script(st);
					do_draw= 1;
					break;
				case 10:
					if (text && text->id.lib) {
						error_libdata();
						break;
					}
					txt_copy_clipboard(text);
					txt_cut_sel(text);
					pop_space_text(st);
					do_draw= 1;
					break;
				case 11:
					//txt_copy_sel(text);
					txt_copy_clipboard(text);
					break;
				case 12:
					if (text && text->id.lib) {
						error_libdata();
						break;
					}
					txt_paste_clipboard(text);
					if (st->showsyntax) txt_format_text(st);
					do_draw= 1;
					break;
			}
		}
	} else if (ascii) {
		if (text && text->id.lib) {
			error_libdata();
		} else {
			short mval[2];
			getmouseco_areawin(mval);
			if (st->showlinenrs && mval[0]>2 && mval[0]<60 && mval[1]>2 && mval[1]<curarea->winy-2) {
				if (ascii>='0' && ascii<='9') {
					double time = PIL_check_seconds_timer();
					if (last_jump < time-1) jump_to= 0;
					jump_to *= 10; jump_to += (int)(ascii-'0');
					txt_move_toline(text, jump_to-1, 0);
					last_jump= time;
				}
			} else if ((st->overwrite && txt_replace_char(text, ascii)) || txt_add_char(text, ascii)) {
				if (st->showsyntax) txt_format_line(st, text->curl, 1);
			}
			pop_space_text(st);
			do_draw= 1;
		}
	} else if (val) {
		switch (event) {
		case AKEY:
			if (G.qual & LR_ALTKEY) {
				txt_move_bol(text, G.qual & LR_SHIFTKEY);
				do_draw= 1;
				pop_space_text(st);
			} else if (G.qual & LR_CTRLKEY) {
				txt_sel_all(text);
				do_draw= 1;
			}
			break; /* BREAK A */
		case CKEY:
			if (G.qual & LR_ALTKEY || G.qual & LR_CTRLKEY) {
				if(G.qual & LR_SHIFTKEY)
					txt_copy_clipboard(text);
				else
					txt_copy_clipboard(text);

				do_draw= 1;	
			}
			break; /* BREAK C */
		case DKEY:
			if (text && text->id.lib) {
				error_libdata();
				break;
			}
			if (G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
				//uncommenting
				txt_order_cursors(text);
				uncomment(text);
				do_draw = 1;
				if (st->showsyntax) txt_format_text(st);
				break;
			} else if (G.qual == LR_CTRLKEY) {
				txt_delete_char(text);
				if (st->showsyntax) txt_format_line(st, text->curl, 1);
				do_draw= 1;
				pop_space_text(st);
			}
			break; /* BREAK D */
		case EKEY:
			if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
				switch(pupmenu("Edit %t|Cut %x0|Copy %x1|Paste %x2|Print Cut Buffer %x3")) {
				case 0:
					if (text && text->id.lib) {
						error_libdata();
						break;
					}
					txt_copy_clipboard(text); //First copy to clipboard
					txt_cut_sel(text);
					do_draw= 1;
					break;
				case 1:
					txt_copy_clipboard(text);
					//txt_copy_sel(text);
					do_draw= 1;
					break;
				case 2:
					if (text && text->id.lib) {
						error_libdata();
						break;
					}
					//txt_paste(text);
					txt_paste_clipboard(text);
					if (st->showsyntax) txt_format_text(st);
					do_draw= 1;
					break;
				case 3:
					txt_print_cutbuffer();
					break;
				}
			}
			else if (G.qual == LR_CTRLKEY || G.qual == (LR_CTRLKEY|LR_SHIFTKEY)) {
				txt_move_eol(text, G.qual & LR_SHIFTKEY);
				do_draw= 1;
				pop_space_text(st);
			}
			break; /* BREAK E */
		case FKEY:
			if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
				switch(pupmenu("File %t|New %x0|Open... %x1|Save %x2|Save As...%x3")) {
				case 0:
					st->text= add_empty_text("Text");
					st->top= 0;
					
					allqueue(REDRAWTEXT, 0);
					allqueue(REDRAWHEADERS, 0);
					break;
				case 1:
					activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
					break;
				case 3:
					text->flags |= TXT_ISMEM;
				case 2:
					txt_write_file(text);
					do_draw= 1;
					break;
				}
			}
			else if (G.qual & (LR_ALTKEY|LR_CTRLKEY)) {
				find_and_replace(st, 0);
				do_draw= 1;
			}
			break; /* BREAK F */
		case HKEY:
			if (G.qual & (LR_ALTKEY|LR_CTRLKEY)) {
				find_and_replace(st, 1);
				do_draw= 1;
			}
			break; /* BREAK H */
		case JKEY:
			if (G.qual == LR_ALTKEY) {
				do_draw= jumptoline_interactive(st);
			}
			break; /* BREAK J */
		case MKEY:
			if (G.qual == LR_ALTKEY) {
				txt_export_to_object(text);
				do_draw= 1;
			}
			break; /* BREAK M */
		case NKEY:
			if (G.qual == LR_ALTKEY) {
				st->text= add_empty_text("Text");
				st->top= 0;
			
				allqueue(REDRAWTEXT, 0);
				allqueue(REDRAWHEADERS, 0);
			}
			break; /* BREAK N */
		case OKEY:
			if (G.qual == LR_ALTKEY) {
				activate_fileselect(FILE_SPECIAL, "Open Text File", G.sce, add_text_fs);
			}
			break; /* BREAK O */
		case PKEY:
			if (G.qual == LR_ALTKEY) {
				run_python_script(st);
				do_draw= 1;
			}
			break; /* BREAK P */
		case QKEY:
			if(okee("Quit Blender")) exit_usiblender();
			break; /* BREAK Q */
		case RKEY:
			if (G.qual == LR_ALTKEY) {
			    if (text->compiled) BPY_free_compiled_text(text);
			        text->compiled = NULL;
				if (okee("Reopen text")) {
					if (!reopen_text(text))
						error("Could not reopen file");
					if (st->showsyntax) txt_format_text(st);
				}
				do_draw= 1;	
			}
			break; /* BREAK R */
		case SKEY:
			if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
				p= pupmenu("Select %t|"
							"Select All %x0|"
							"Select Line %x1|"
							"Jump to Line %x3");
				switch(p) {
				case 0:
					txt_sel_all(text);
					do_draw= 1;
					break;
					
				case 1:
					txt_sel_line(text);
					do_draw= 1;
					break;
										
				case 3:
					do_draw= jumptoline_interactive(st);
					break;
				}
			}
			else if (G.qual & LR_ALTKEY) {
				/* Event treatment CANNOT enter this if
				if (G.qual & LR_SHIFTKEY) 
					if (text) text->flags |= TXT_ISMEM;
				*/
				txt_write_file(text);
				do_draw= 1;
			}
			break; /* BREAK S */
		case UKEY:
			//txt_print_undo(text); //debug buffer in console
			if (G.qual == (LR_ALTKEY|LR_SHIFTKEY)) {
				txt_do_redo(text);
				pop_space_text(st);
				do_draw= 1;
			}
			if (G.qual == LR_ALTKEY) {
				txt_do_undo(text);
				if (st->showsyntax) txt_format_text(st);
				pop_space_text(st);
				do_draw= 1;
			}
			break; /* BREAK U */
		case VKEY:
			if (G.qual == (LR_ALTKEY| LR_SHIFTKEY)) {
				switch(pupmenu("View %t|Top of File %x0|Bottom of File %x1|Page Up %x2|Page Down %x3")) {
				case 0:
					txt_move_bof(text, 0);
					do_draw= 1;
					pop_space_text(st);
					break;
				case 1:
					txt_move_eof(text, 0);
					do_draw= 1;
					pop_space_text(st);
					break;
				case 2:
					screen_skip(st, -st->viewlines);
					do_draw= 1;
					break;
				case 3:
					screen_skip(st, st->viewlines);
					do_draw= 1;
					break;
				}
			}
			/* Support for both Alt-V and Ctrl-V for Paste, for backward compatibility reasons */
			else if (G.qual & LR_ALTKEY || G.qual & LR_CTRLKEY) {
				if (text && text->id.lib) {
					error_libdata();
					break;
				}
				/* Throwing in the Shift modifier Paste from the OS clipboard */
				if (G.qual & LR_SHIFTKEY)
					txt_paste_clipboard(text);
				else
					txt_paste_clipboard(text);
				if (st->showsyntax) txt_format_text(st);
				do_draw= 1;	
				pop_space_text(st);
			}
			break; /* BREAK V */
		case XKEY:
			if (G.qual == LR_ALTKEY || G.qual == LR_CTRLKEY) {
				if (text && text->id.lib) {
					error_libdata();
					break;
				}
				txt_cut_sel(text);
				if (st->showsyntax) txt_format_text(st);
				do_draw= 1;	
				pop_space_text(st);
			}
			break;
		case ZKEY:
			if (G.qual & (LR_ALTKEY|LR_CTRLKEY|LR_COMMANDKEY)) {
				if (G.qual & LR_SHIFTKEY) {
					txt_do_redo(text);
				} else {
					txt_do_undo(text);
				}
				if (st->showsyntax) txt_format_text(st);
				pop_space_text(st);
				do_draw= 1;
			}
			break;
		case TABKEY:
			if (text && text->id.lib) {
				error_libdata();
				break;
			} else {
				if (txt_has_sel(text)) {
					if (G.qual & LR_SHIFTKEY) {
						txt_order_cursors(text);
						unindent(text);
						if (st->showsyntax) txt_format_text(st);
					} else {
						txt_order_cursors(text);
						indent(text);
						if (st->showsyntax) txt_format_text(st);
					}
				} else {
					txt_add_char(text, '\t');
					if (st->showsyntax) txt_format_line(st, text->curl, 1);
				}
			}
			pop_space_text(st);
			do_draw= 1;
			st->currtab_set = setcurr_tab(text);
			break;
		case RETKEY:
			if (text && text->id.lib) {
				error_libdata();
				break;
			}
			//double check tabs before splitting the line
			st->currtab_set = setcurr_tab(text);
			txt_split_curline(text);
			{
				int a = 0;
				if (a < st->currtab_set)
				{
					while ( a < st->currtab_set) {
						txt_add_char(text, '\t');
						a++;
					}
				}
			}
			if (st->showsyntax) {
				if (text->curl->prev) txt_format_line(st, text->curl->prev, 0);
				txt_format_line(st, text->curl, 1);
			}
			do_draw= 1;
			pop_space_text(st);
			break;
		case BACKSPACEKEY:
			if (text && text->id.lib) {
				error_libdata();
				break;
			}
			if (G.qual & (LR_ALTKEY | LR_CTRLKEY)) {
				txt_backspace_word(text);
			} else {
				txt_backspace_char(text);
			}
			set_tabs(text);
			if (st->showsyntax) txt_format_line(st, text->curl, 1);
			do_draw= 1;
			pop_space_text(st);
			break;
		case DELKEY:
			if (text && text->id.lib) {
				error_libdata();
				break;
			}
			if (G.qual & (LR_ALTKEY | LR_CTRLKEY)) {
				txt_delete_word(text);
			} else {
				txt_delete_char(text);
			}
			if (st->showsyntax) txt_format_line(st, text->curl, 1);
			do_draw= 1;
			pop_space_text(st);
			st->currtab_set = setcurr_tab(text);
			break;
		case INSERTKEY:
			st->overwrite= !st->overwrite;
			do_draw= 1;
			break;
		case LEFTARROWKEY:
			if (G.qual & LR_COMMANDKEY)
				txt_move_bol(text, G.qual & LR_SHIFTKEY);
			else if (G.qual & LR_ALTKEY)
				txt_jump_left(text, G.qual & LR_SHIFTKEY);
			else
				txt_move_left(text, G.qual & LR_SHIFTKEY);
			set_tabs(text);
			do_draw= 1;
			pop_space_text(st);
			break;
		case RIGHTARROWKEY:
			if (G.qual & LR_COMMANDKEY)
				txt_move_eol(text, G.qual & LR_SHIFTKEY);
			else if (G.qual & LR_ALTKEY)
				txt_jump_right(text, G.qual & LR_SHIFTKEY);
			else
				txt_move_right(text, G.qual & LR_SHIFTKEY);
			set_tabs(text);
			do_draw= 1;
			pop_space_text(st);
			break;
		case UPARROWKEY:
			if (st->wordwrap) wrap_move_up(st, G.qual & LR_SHIFTKEY);
			else txt_move_up(text, G.qual & LR_SHIFTKEY);
			set_tabs(text);
			do_draw= 1;
			pop_space_text(st);
			break;
		case DOWNARROWKEY:
			if (st->wordwrap) wrap_move_down(st, G.qual & LR_SHIFTKEY);
			else txt_move_down(text, G.qual & LR_SHIFTKEY);
			set_tabs(text);
			do_draw= 1;
			pop_space_text(st);
			break;
		case PAGEDOWNKEY:
			cursor_skip(st, st->viewlines, G.qual & LR_SHIFTKEY);
			pop_space_text(st);
			do_draw= 1;
			break;
		case PAGEUPKEY:
			cursor_skip(st, -st->viewlines, G.qual & LR_SHIFTKEY);
			pop_space_text(st);
			do_draw= 1;
			break;
		case HOMEKEY:
			if (st->wordwrap) wrap_move_bol(st, G.qual & LR_SHIFTKEY);
			else txt_move_bol(text, G.qual & LR_SHIFTKEY);
			do_draw= 1;
			pop_space_text(st);
			break;
		case ENDKEY:
			if (st->wordwrap) wrap_move_eol(st, G.qual & LR_SHIFTKEY);
			else txt_move_eol(text, G.qual & LR_SHIFTKEY);
			do_draw= 1;
			pop_space_text(st);
			break;
		case WHEELUPMOUSE:
			screen_skip(st, -U.wheellinescroll);
			do_draw= 1;
			break;
		case WHEELDOWNMOUSE:
			screen_skip(st, U.wheellinescroll);
			do_draw= 1;
			break;
		}
	}

	/* Run text plugin scripts if enabled */
	if (st->doplugins && event && val) {
		if (BPY_menu_do_shortcut(PYMENU_TEXTPLUGIN, event, G.qual)) {
			do_draw= 1;
		}
	}

	if (do_draw)
		redraw_alltext();
}

static void draw_brackets(SpaceText *st)
{
	TextLine *startl, *endl, *linep;
	Text *text = st->text;
	int b, c, startc, endc, find, stack;
	int viewc, viewl, offl, offc, x, y;
	char ch;

	if (!text || !text->curl) return;

	startl= text->curl;
	startc= text->curc;
	b= check_bracket(startl->line[startc]);
	if (b==0 && startc>0) b = check_bracket(startl->line[--startc]);
	if (b==0) return;
	
	linep= startl;
	c= startc;
	endl= NULL;
	endc= -1;
	find= -b;
	stack= 0;

	/* Opening bracket, search forward for close */
	if (b>0) {
		c++;
		while (linep) {
			while (c<linep->len) {
				b= check_bracket(linep->line[c]);
				if (b==find) {
					if (stack==0) {
						endl= linep;
						endc= c;
						break;
					}
					stack--;
				} else if (b==-find) {
					stack++;
				}
				c++;
			}
			if (endl) break;
			linep= linep->next;
			c= 0;
		}
	}
	/* Closing bracket, search backward for open */
	else {
		c--;
		while (linep) {
			while (c>=0) {
				b= check_bracket(linep->line[c]);
				if (b==find) {
					if (stack==0) {
						endl= linep;
						endc= c;
						break;
					}
					stack--;
				} else if (b==-find) {
					stack++;
				}
				c--;
			}
			if (endl) break;
			linep= linep->prev;
			if (linep) c= linep->len-1;
		}
	}

	if (!endl || endc==-1) return;

	BIF_ThemeColor(TH_HILITE);	
	x= st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
	y= curarea->winy - st->lheight;

	ch= startl->line[startc];
	wrap_offset(st, startl, startc, &offl, &offc);
	viewc= get_char_pos(st, startl->line, startc) - st->left + offc;
	if (viewc >= 0){
		viewl= txt_get_span(text->lines.first, startl) - st->top + offl;
		glRasterPos2i(x+viewc*spacetext_get_fontwidth(st), y-viewl*st->lheight);
		BMF_DrawCharacter(spacetext_get_font(st), ch);
		glRasterPos2i(x+viewc*spacetext_get_fontwidth(st)+1, y-viewl*st->lheight);
		BMF_DrawCharacter(spacetext_get_font(st), ch);
	}
	ch= endl->line[endc];
	wrap_offset(st, endl, endc, &offl, &offc);
	viewc= get_char_pos(st, endl->line, endc) - st->left + offc;
	if (viewc >= 0) {
		viewl= txt_get_span(text->lines.first, endl) - st->top + offl;
		glRasterPos2i(x+viewc*spacetext_get_fontwidth(st), y-viewl*st->lheight);
		BMF_DrawCharacter(spacetext_get_font(st), ch);
		glRasterPos2i(x+viewc*spacetext_get_fontwidth(st)+1, y-viewl*st->lheight);
		BMF_DrawCharacter(spacetext_get_font(st), ch);
	}
}

static int check_bracket(char ch)
{
	int a;
	char opens[] = "([{";
	char close[] = ")]}";
	
	for (a=0; a<3; a++) {
		if(ch==opens[a])
			return a+1;
		else if (ch==close[a])
			return -(a+1);
	}
	return 0;
}

static int check_delim(char ch) 
{
	int a;
	char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,";
	
	for (a=0; a<28; a++) {
		if (ch==delims[a])
			return 1;
	}
	return 0;
}

static int check_digit(char ch)
{
	if (ch < '0') return 0;
	if (ch <= '9') return 1;
	return 0;
}

static int check_identifier(char ch)
{
	if (ch < '0') return 0;
	if (ch <= '9') return 1;
	if (ch < 'A') return 0;
	if (ch <= 'Z' || ch == '_') return 1;
	if (ch < 'a') return 0;
	if (ch <= 'z') return 1;
	return 0;
}

static int check_whitespace(char ch)
{
	if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
		return 1;
	return 0;
}

void convert_tabs (struct SpaceText *st, int tab)
{
	Text *text = st->text;
	TextLine *tmp;
	size_t a, j;
	char *check_line, *new_line;
	int extra, number; //unknown for now
	
	if (!text) return;
	
	tmp = text->lines.first;
	
	//first convert to all space, this make it alot easier to convert to tabs because there is no mixtures of ' ' && '\t'
	while(tmp) {
		check_line = tmp->line;
		new_line = MEM_mallocN(render_string(st, check_line)+1, "Converted_Line");
		j = 0;
		for (a=0; a < strlen(check_line); a++) { //foreach char in line
			if(check_line[a] == '\t') { //checking for tabs
				//get the number of spaces this tabs is showing
				//i dont like doing it this way but will look into it later
				new_line[j] = '\0';
				number = render_string(st, new_line);
				new_line[j] = '\t';
				new_line[j+1] = '\0';
				number = render_string(st, new_line)-number;
				for(extra = 0; extra < number; extra++) {
					new_line[j] = ' ';
					j++;
				}
			} else {
				new_line[j] = check_line[a];
				++j;
			}
		}
		new_line[j] = '\0';
		// put new_line in the tmp->line spot still need to try and set the curc correctly
		if (tmp->line) MEM_freeN(tmp->line);
		if(tmp->format) MEM_freeN(tmp->format);
		
		tmp->line = new_line;
		tmp->len = strlen(new_line);
		tmp->format = NULL;
		tmp = tmp->next;
	}
	
	if (tab) // Converting to tabs
	{	//start over from the begining
		tmp = text->lines.first;
		
		while(tmp) {
			check_line = tmp->line;
			extra = 0;
			for (a = 0; a < strlen(check_line); a++) {
				number = 0;
				for (j = 0; j < (size_t)st->tabnumber; j++) {
					if ((a+j) <= strlen(check_line)) { //check to make sure we are not pass the end of the line
						if(check_line[a+j] != ' ') {
							number = 1;
						}
					}
				}
				if (!number) { //found all number of space to equal a tab
					a = a+(st->tabnumber-1);
					extra = extra+1;
				}
			}
			
			if ( extra > 0 ) { //got tabs make malloc and do what you have to do
				new_line = MEM_mallocN(strlen(check_line)-(((st->tabnumber*extra)-extra)-1), "Converted_Line");
				extra = 0; //reuse vars
				for (a = 0; a < strlen(check_line); a++) {
					number = 0;
					for (j = 0; j < (size_t)st->tabnumber; j++) {
						if ((a+j) <= strlen(check_line)) { //check to make sure we are not pass the end of the line
							if(check_line[a+j] != ' ') {
								number = 1;
							}
						}
					}
					if (!number) { //found all number of space to equal a tab
						new_line[extra] = '\t';
						a = a+(st->tabnumber-1);
						++extra;
						
					} else { //not adding a tab
						new_line[extra] = check_line[a];
						++extra;
					}
				}
				new_line[extra] = '\0';
				// put new_line in the tmp->line spot still need to try and set the curc correctly
				if (tmp->line) MEM_freeN(tmp->line);
				if(tmp->format) MEM_freeN(tmp->format);
				
				tmp->line = new_line;
				tmp->len = strlen(new_line);
				tmp->format = NULL;
			}
			tmp = tmp->next;
		}
	}

	if (st->showsyntax) txt_format_text(st);
}

void redraw_alltext(void)
{
	ScrArea *sa;
	
	if(!G.curscreen)
		return;
	
	for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
		SpaceText *st= sa->spacedata.first;
		
		if (st && st->spacetype==SPACE_TEXT) {
			scrarea_queue_redraw(sa);
		}
	}
}
