/* 
CCIDE
Copyright 2001-2011 David Lindauer.

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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

You may contact the author at:
	mailto::camille@bluegrass.net
 */
// assumes tabs aren't going to get reset yet
#define STRICT 
#include <windows.h>
#include <commctrl.h>
#include <stdio.h>
#include "header.h"
#include <richedit.h>
#include <limits.h>
#include "c_types.h"
#include "codecomp.h"

// This defines the maximum range for the horizontal scroll bar on the window
#define MAX_HSCROLL 256

// The KEYLIST structure holds information about text which should be
// colorized.  It gives the name of a keyword (e.g. 'int') and the color
// to display it in.

typedef struct
{
    char *text;
	unsigned char Color;
//    COLORREF *color;
} KEYLIST;

// The next few variables hold the configuration information from the
// editor properties screen.  IT is mostly set to defaults here,
// and will be overwritten from the registry and later from the project
// file
extern int editFlags;

COLORREF keywordColor = 0xff8000;
COLORREF numberColor = 0x0000ff;
COLORREF commentColor = 0x00c000;
COLORREF stringColor = 0x8000ff;
COLORREF escapeColor = 0xff0000;
COLORREF backgroundColor = 0xffffff;
COLORREF textColor = 0;
COLORREF highlightColor = 0x80ffff;

#define COLUMNBAR_COLOR 0xccccff
#define COLUMNBAR_COLUMNS 80

#define C_BACKGROUND 0
#define C_TEXT 1
#define C_HIGHLIGHT 2
#define C_KEYWORD 3
#define C_COMMENT 4
#define C_NUMBER 5
#define C_STRING 6
#define C_ESCAPE 7
#define C_SYS_WINDOWBACKGROUND 15
static COLORREF *colors[] = { &backgroundColor, &textColor, &highlightColor, &keywordColor,
						&commentColor, &numberColor, &stringColor, &escapeColor };
LOGFONT EditFont = 
{
     - 16, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
         OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, FIXED_PITCH 
         | FF_DONTCARE, "Courier New"
};

static LOGFONT FuncFont = 
{
     - 12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
         OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 
         FF_DONTCARE, "Courier New"
};
static LOGFONT CompleteFont = 
{
     - 12, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET,
         OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, 
         FF_DONTCARE, "Courier New"
};

int tabs = 4;

// COLORREF selcolor = 0 ;

// For drawing, put a little margin at the left
#define LEFTMARGIN 5

// well we aren't 'totally' independent of the rest of CCIDE...
// the following is used for the tooltip
extern enum DebugState uState;
extern HINSTANCE hInstance;

extern int completionEnabled;

// and the next is highlight data filled in by the find
char highlightText[256] ;
int highlightCaseSensitive;
int highlightWholeWord;
int parenthesisCheck = TRUE;

static int page_size = -1;
static HWND codecompleteBox;
static int codecompletemode;
static HWND hwndShowFunc;

void SendUpdate(HWND hwnd);
// The C_keywordList is a list of all keywords, with colorization info
static KEYLIST C_keywordList[] = 
{
    #include "c_kw.h"
};
static KEYLIST ASM_keywordList[] = 
{
    #include "asm_kw.h"
};
static KEYLIST RC_keywordList[] = 
{
    #include "rc_kw.h"
};

/* using windows to allocate memory because the borland runtime gets confused
 * with large amounts of data
 */
#define SYSALLOC
void getPageSize()
{
    if (page_size == -1)
    {
        SYSTEM_INFO info ;
        GetSystemInfo(&info) ;
        page_size = info.dwPageSize ;                    
    }
}
int freemem(EDITDATA *p)
{
#ifdef SYSALLOC
	VirtualFree(p->cd->text, p->cd->textmaxlen, MEM_DECOMMIT);
    VirtualFree(p->cd->text, 0, MEM_RELEASE ) ;
#else
    free(p->cd->text);
#endif
	return 0;
}
int allocmem(EDITDATA *p, int size)
{
    size *= sizeof(INTERNAL_CHAR) ;
    size += 64 * 1024 ;
    size = size - size % (64 * 1024) ;
    if (!p->cd->text)
    {
#ifdef SYSALLOC
        p->cd->text = VirtualAlloc(0, size, MEM_RESERVE, PAGE_NOACCESS);
#else
        p->cd->text = calloc(1, size);
#endif
        if (!p->cd->text) 
        {
            ExtendedMessageBox("Error",0,"Out Of Memory");
            return 0 ;
        }
        p->cd->textmaxlen = size ;
        return 1 ;
    }
    else
    {
        char *x ;
#ifdef SYSALLOC
       	x = VirtualAlloc(0, size, MEM_RESERVE, PAGE_NOACCESS) ;
		if (!x || !VirtualAlloc(x, (p->cd->textlen + 1) * sizeof(INTERNAL_CHAR), MEM_COMMIT, PAGE_READWRITE)) 
#else
        x = calloc(1, size);
        if (!x)
#endif
		{
            ExtendedMessageBox("Error",0,"Out Of Memory");
   	        return 0 ;
		}
		memcpy(x, p->cd->text, (p->cd->textlen + 1) * sizeof(INTERNAL_CHAR));
		freemem(p);
		p->cd->text = x;
		p->cd->textmaxlen = size;
        return 1 ;
    }
}
int commitmem(EDITDATA *p, int size)
{
    int size1 = size * sizeof(INTERNAL_CHAR);
    getPageSize() ;
    size1 += page_size ;
    size1 = size1 - size1 % page_size ;
    if (size1 > p->cd->textmaxlen)
    {
        if (!allocmem(p,size))
            return 0 ;
    }
#ifdef SYSALLOC
    if (!VirtualAlloc(p->cd->text, size1, MEM_COMMIT, PAGE_READWRITE)) 
    {
        ExtendedMessageBox("Error",0,"Cannot commit memory") ;
        return 0 ;
    }
#endif
    return 1 ;
}
void reparse(HWND hwnd, EDITDATA *p)
{
	if (p->cd->reparseTimerID)
		KillTimer(hwnd,p->cd->reparseTimerID);
	p->cd->reparseTimerID = SetTimer(hwnd, 2, 3000, 0);
}
/**********************************************************************
 * SaveColors saves the color and font settings to the registry
 **********************************************************************/
void saveColors(void)
{
#ifdef XXXXX
    IntToProfile("KeywordColor", keywordColor);
    IntToProfile("NumberColor", numberColor);
    IntToProfile("CommentColor", commentColor);
    IntToProfile("StringColor", stringColor);
    IntToProfile("EscapeColor", escapeColor);
    IntToProfile("BackgroundColor", backgroundColor);
    IntToProfile("TextColor", textColor);
    IntToProfile("lfHeight", EditFont.lfHeight);
    IntToProfile("lfWidth", EditFont.lfWidth);
    IntToProfile("lfEscapement", EditFont.lfEscapement);
    IntToProfile("lfOrientation", EditFont.lfOrientation);
    IntToProfile("lfWeight", EditFont.lfWeight);
    IntToProfile("lfItalic", EditFont.lfItalic);
    IntToProfile("lfUnderline", EditFont.lfUnderline);
    IntToProfile("lfStrikeOut", EditFont.lfStrikeOut);
    IntToProfile("lfCharSet", EditFont.lfCharSet);
    IntToProfile("lfOutPrecision", EditFont.lfOutPrecision);
    IntToProfile("lfClipPrecision", EditFont.lfClipPrecision);
    IntToProfile("lfQuality", EditFont.lfQuality);
    IntToProfile("lfPitchAndFamily", EditFont.lfPitchAndFamily);
    StringToProfile("lfFaceName", EditFont.lfFaceName);
#endif
}

/**********************************************************************
 * RestoreColors restores the color and font settings from the registry
 **********************************************************************/
void restoreColors(void)
{
#ifdef XXXXX
    keywordColor = ProfileToInt("KeywordColor", keywordColor);
    numberColor = ProfileToInt("NumberColor", numberColor);
    commentColor = ProfileToInt("CommentColor", commentColor);
    stringColor = ProfileToInt("StringColor", stringColor);
    escapeColor = ProfileToInt("EscapeColor", escapeColor);
    backgroundColor = ProfileToInt("BackgroundColor", backgroundColor);
    textColor = ProfileToInt("TextColor", textColor);
    EditFont.lfHeight = ProfileToInt("lfHeight", EditFont.lfHeight);
    EditFont.lfWidth = ProfileToInt("lfWidth", EditFont.lfWidth);
    EditFont.lfEscapement = ProfileToInt("lfEscapement", EditFont.lfEscapement);
    EditFont.lfOrientation = ProfileToInt("lfOrientation",
        EditFont.lfOrientation);
    EditFont.lfWeight = ProfileToInt("lfWeight", EditFont.lfWeight);
    EditFont.lfItalic = ProfileToInt("lfItalic", EditFont.lfItalic);
    EditFont.lfUnderline = ProfileToInt("lfUnderline", EditFont.lfUnderline);
    EditFont.lfStrikeOut = ProfileToInt("lfStrikeOut", EditFont.lfStrikeOut);
    EditFont.lfCharSet = ProfileToInt("lfCharSet", EditFont.lfCharSet);
    EditFont.lfOutPrecision = ProfileToInt("lfOutPrecision",
        EditFont.lfOutPrecision);
    EditFont.lfClipPrecision = ProfileToInt("lfClipPrecision",
        EditFont.lfClipPrecision);
    EditFont.lfQuality = ProfileToInt("lfQuality", EditFont.lfQuality);
    EditFont.lfPitchAndFamily = ProfileToInt("lfPitchAndFamily",
        EditFont.lfPitchAndFamily);
    strcpy(EditFont.lfFaceName, ProfileToString("lfFaceName",
        EditFont.lfFaceName));
#endif
}
/**********************************************************************
 * Colorize marks a range of text with a specific color and attributes
 **********************************************************************/
static void Colorize(INTERNAL_CHAR *buf, int start, int len, int color, int
    italic)
{
    int dwEffects = 0, i;
    INTERNAL_CHAR *p = buf + start;
    if (italic)
        dwEffects |= CFE_ITALIC;
    if (!color)
        dwEffects |= CFE_AUTOCOLOR;
    //   else 
    //   {
    //		if (!italic)
    //         dwEffects |= CFE_BOLD ;
    //	}
    for (i = 0; i < len; i++, p++)
    {
        p->Color = color;
        p->effect = dwEffects;
    }
}

/**********************************************************************
 * keysym returns true if it is a symbol that can be used in a keyword
 **********************************************************************/
int keysym(char x)
{
    return isalnum(x) || x == '_';
}

/**********************************************************************
 * strpstr  finds a text string within a string organized as internal
 * characters.  Returns 0 if it couldn't find the string
 **********************************************************************/
INTERNAL_CHAR *strpstr(INTERNAL_CHAR *t, char *text, int len)
{
    while (t->ch && len)
    {
        if (t->ch == text[0])
        {
            char *t1 = text;
            INTERNAL_CHAR *it1 = t;
            while (*t1 && it1->ch ==  *t1)
            {
                t1++;
                it1++;
            }
            if (! *t1)
                return t;
        }
        t++;
        len--;
    }
    return 0;

}

/**********************************************************************
 * strplen  finds the length of an internal_char array
 **********************************************************************/

int strplen(INTERNAL_CHAR *t)
{
    int rv = 0;
    while (t->ch)
        rv++, t++;
    return rv;
}

/**********************************************************************
 * backalpha goes backwards to find out if the current (numeric)
 * character is part of an identifier, or if it is a standalone number.
 * returns TRUE if part of an identifier.  Used in colorizing numbers
 **********************************************************************/

static int backalpha(INTERNAL_CHAR *buf, int i)
{
    while (i >= 0)
    {
        if (isalpha(buf[i].ch))
            return TRUE;
        if (buf[i].ch == '_')
            return TRUE;
        if (!isdigit(buf[i].ch))
            return FALSE;
        i--;
    }
    return FALSE;

}

//-------------------------------------------------------------------------

int pcmp(INTERNAL_CHAR *s, char *t, int preproc, int *retlen, int
    caseinsensitive, int bykey)
{
    *retlen = 0;
    while (*t && s->ch)
    {
        int val = s->ch;
		int ch = *t;
        if (caseinsensitive)
		{
            val = tolower(val);
			ch = tolower(ch);
		}
        if (val <  ch)
            return  - 1;
        else if (val >  ch)
            return 1;
        else
        {
            if (ch == preproc)
            {
                while (isspace(s[1].ch) && s[1].ch != '\n')
                    s++, (*retlen)++;
            }
            s++, t++, (*retlen)++;
        }
    }
    if (*t)
        return 1;
    return bykey && keysym(s->ch) ;
}

/**********************************************************************
 * See if a keyword matches the current text location
 **********************************************************************/
KEYLIST *matchkeyword(KEYLIST *table, int tabsize, int preproc, INTERNAL_CHAR
    *t, int *retlen, int insensitive)
{
    int top = tabsize;
    int bottom =  - 1;
    int v;
    while (top - bottom > 1)
    {
        int mid = (top + bottom) / 2;
        v = pcmp(t, table[mid].text, preproc, retlen, insensitive, TRUE);
        if (v < 0)
        {
            top = mid;
        }
        else
        {
            bottom = mid;
        }
    }
    if (bottom ==  - 1)
        return 0;
    v = pcmp(t, table[bottom].text, preproc, retlen, insensitive, TRUE);
    if (v)
        return 0;
    return  &table[bottom];
}

/**********************************************************************
 * SearchKeywords searches a range of INTERNAL_CHARs for keywords,
 * numbers, and strings, and colorizes them
 **********************************************************************/

static void SearchKeywords(INTERNAL_CHAR *buf, int chars, int start, int type)
{
    int i;
    KEYLIST *sr = C_keywordList;
    int size = sizeof(C_keywordList) / sizeof(KEYLIST);
    int preproc = '#';
    int hexflg = FALSE;
    int xchars = chars;
    INTERNAL_CHAR *t = buf + start;
    if (type == COLORIZE_ASM)
    {
        sr = ASM_keywordList;
        size = sizeof(ASM_keywordList) / sizeof(KEYLIST);
        preproc = '%';
    }
	else if (type == COLORIZE_RC)
	{
        sr = RC_keywordList;
        size = sizeof(RC_keywordList) / sizeof(KEYLIST);
        preproc = '#';
	}
    while (t->ch && xchars > 0)
    {
        while (t->ch && t->Color == C_COMMENT && xchars > 0)
            t++, xchars--;
        if (xchars > 0 && (t == buf || !keysym(t[ - 1].ch) && (keysym(t->ch) ||
            t->ch == preproc)))
        {
			int len;
            KEYLIST *p = matchkeyword(sr, size, preproc, t, &len, type ==
	            COLORIZE_ASM || type == COLORIZE_RC);
			if (p)
    	    {
        	    Colorize(buf, t - buf, len,  p->Color, FALSE);
            	t += len;
            	xchars -= len;
        	}
			else
			{
				if (t->Color == (C_HIGHLIGHT << 4) + C_TEXT)
					t->Color = C_TEXT;
            	t++, xchars--;
			}
        }
        else
		{
			if (t->Color == (C_HIGHLIGHT << 4) + C_TEXT)
				t->Color = C_TEXT;
            t++, xchars--;
		}
    }

    for (i = 0; i < chars; i++)
        if (buf[start + i].Color != C_COMMENT)
		{
	        int len;
	        if (highlightText[0] && 
				!pcmp(buf + start + i, highlightText, preproc, &len, !highlightCaseSensitive, FALSE)
				&& (!highlightWholeWord || (i == buf || !isalnum(buf[i-1].ch)) && !isalnum(buf[i+len].ch)))
			{
	            Colorize(buf, start + i, len,  (C_HIGHLIGHT << 4) + C_TEXT, FALSE);
	            i += len - 1;
			}
			else if (type == COLORIZE_ASM && buf[start+i].ch == '$')
			{
				len = 1;
				while (isxdigit(buf[start + i + len].ch))
					len++;
	            Colorize(buf, start + i, len, C_NUMBER, FALSE);
	            i += len - 1;
			}
		    else if (isdigit(buf[start + i].ch))
		    {
		        if (!backalpha(buf, start + i - 1))
		        {
		            int j = i;
		            char ch = buf[start + i++].ch;
		            if (type == COLORIZE_C || type == COLORIZE_RC)
		            {
		                if (isdigit(ch) || ch == '.')
		                {
		                    char oc = ch;
		                    ch = buf[start + i].ch;
		                    hexflg = oc == '0' && (ch == 'x' || ch == 'X');
		                    if (hexflg)
		                        ch = buf [start + ++i].ch;
		                    while (ch == '.' || isdigit(ch) 
		                        || hexflg && (isxdigit(ch) || ch =='p' || ch == 'P') ||
		                          !hexflg && (ch == 'e' || ch== 'E'))
		                    {
		                            i++;
		                            if (!hexflg && ch >= 'A' || hexflg && (ch =='p' || ch == 'P'))
		                            {
		                                ch = buf[start+i].ch;
		                                if (ch == '-' || ch == '+')
		                                    i++;
		                            }
		                            ch = buf[start+i].ch ;
		                    }
		                    while (ch== 'L' || ch == 'l'
		                        || ch == 'U' || ch == 'u'
		                        || ch == 'F' || ch == 'f')
		                        ch = buf[start + ++i].ch;
		                }
		            }
		            else
		            {
		                while (isxdigit(buf[start + i].ch))
		                    i++;
		                if (buf[start + i].ch != 'H' && buf[start + i].ch != 'h')
		                {
		                    i = j;
		                    while (isdigit(buf[start + i].ch))
		                        i++;
		                }
		                else
		                    i++;
		            }
		            hexflg = FALSE;
		            Colorize(buf, start + j, i - j, C_NUMBER, FALSE);
		            i--;
		        }
		    }
		    else if ((buf[start + i].ch == '"' 
		        || buf[start + i].ch == '\'') && (start + i <2 || buf[start + i - 1].ch != '\\' || buf[start + i - 2].ch == '\\'))
		    {
		        int ch = buf[start + i].ch;
		        int j = i++;
		        while (buf[start + i].ch && (buf[start + i].ch != ch && buf[start +
		            i].ch != '\n' || buf[start + i - 1].ch == '\\' && buf[start + i -
		            2].ch != '\\') && i < chars)
		            i++;
		        Colorize(buf, start + j + 1, i - j - 1, C_STRING, FALSE);
		    }
		}


}

/**********************************************************************
 * FormatBuffer colorizes comments over a range of text, 
 * then calls SearchKeywords to colorize keywords
 **********************************************************************/
static int instring(INTERNAL_CHAR *buf, INTERNAL_CHAR *t1)
{
    INTERNAL_CHAR *t2 = t1;
    int quotechar = 0;
    while (t2 != buf && t2[ - 1].ch != '\n')
        t2--;
    while (t2 != t1)
    {
        if (quotechar)
        {
            if (t2->ch == quotechar && t2[ - 1].ch != '\\')
                quotechar = 0;
        }
        else
        {
            if (t2->ch == '\'' || t2->ch == '"')
                quotechar = t2->ch;
        }
        t2++;
    }
    return !!quotechar;
}

//-------------------------------------------------------------------------

static void FormatBuffer(INTERNAL_CHAR *buf, int start, int end, int type)
{
    if (type == COLORIZE_C || type == COLORIZE_RC)
    {
        INTERNAL_CHAR *t = buf + start;
        INTERNAL_CHAR *t1;
        while (TRUE)
        {
            t1 = strpstr(t, "/*", end - (t - buf));

            if (t1)
            {
                if ((t1 == buf || t1[ - 1].ch != '/') && !instring(buf, t1))
                {

                    t = strpstr(t1, "*/",  - 1);
                    if (!t)
                        t = t1 + strplen(t1);
                    else
                    {
                        t += 2;
                    }
                    Colorize(buf, t1 - buf, t - t1, C_COMMENT, TRUE);
                }
                else
                    t = t1 + 1;
            }
            else
                break;
        }
        t = buf + start;
        t1 = strpstr(t, "//", end - (t - buf));
        while (t1)
        {
            if (!instring(buf, t1) && t1->Color != C_COMMENT)
            {

                t = strpstr(t1, "\n",  - 1);
                while (1)
                {
                    if (!t)
                    {
                        t = t1 + strplen(t1);
                        break;
                    }
                    else
                    {
                        INTERNAL_CHAR *t2 = t;
                        while (t2 > buf && isspace(t2[ - 1].ch))
                            t2--;
                        if (t2[ - 1].ch != '\\')
                            break;
                        t = strpstr(t + 1, "\n",  - 1);
                    }
                }
                Colorize(buf, t1 - buf, t - t1 + 1, C_COMMENT, TRUE);
            }
            else
                t = t1 + 1;
            t1 = strpstr(t, "//", end - (t - buf));
        }
    }
    else if (type == COLORIZE_ASM)
    {
        INTERNAL_CHAR *t = buf + start;
        int type;
        INTERNAL_CHAR *t1;
        t1 = strpstr(t, ";", end - (t - buf));
        while (t1)
        {
            t = strpstr(t1, "\n",  - 1);
            if (!t)
            {
                t = t1 + strplen(t1);
            }
            Colorize(buf, t1 - buf, t - t1 + 1, C_COMMENT, TRUE);
            t1 = strpstr(t, ";", end - (t - buf));
        }
    }
    SearchKeywords(buf, end - start, start, type);
}

//-------------------------------------------------------------------------

static void FormatBufferFromScratch(INTERNAL_CHAR *buf, int start, int end, int
    type)
{
    int xend, xstart;
    xend = end;
    if (start < 0)
        start = 0;
    xstart = start;
    while (xstart && (buf[xstart - 1].ch != '\n' || buf[xstart - 1].Color ==
        C_COMMENT))
        xstart--;
    while (buf[xend].ch && (buf[xend].ch != '\n' || buf[xend].Color ==
                C_COMMENT))
            xend++;

    Colorize(buf, xstart, xend - xstart, C_TEXT, FALSE);
    FormatBuffer(buf, xstart, xend, type);
}

/**********************************************************************
 * FormatLine is an optimized colorizer that just colorizes the current
 * line
 **********************************************************************/

static void FormatLine(HWND hwnd, INTERNAL_CHAR *buf, int type)
{
    int start, end;
    SendMessage(hwnd, EM_GETSEL, (WPARAM) &start, (LPARAM) &end);
    FormatBufferFromScratch(buf, start, start, type);

}
static void UpdateSiblings(EDITDATA *p, int pos, int insert)
{
	EDLIST *list = p->cd->siblings;
	while (list)
	{
		if (list->data != p)
			SendMessage(list->data->self, EM_UPDATESIBLING, insert, pos);
		list = list->next ;
	}
}
/**********************************************************************
 * GetWordFromPos is a utility to find out what the word under the
 * cursor is, and various information about it
 **********************************************************************/
int GetWordFromPos(HWND hwnd, char *outputbuf, int charpos, int *linenum, int
    *startoffs, int *endoffs)
{
    int linepos;
    int linecharpos;
    int linecharindex;
    char buf[1000];
    charinfo charrange;

    EDITDATA *p = (EDITDATA*)GetWindowLong(hwnd, 0);

    if (charpos ==  - 1)
    {
	    SendMessage(hwnd, EM_EXGETSEL, (WPARAM)0, (LPARAM) &charrange);
        charpos = charrange.min;
    }
    linepos = SendMessage(hwnd, EM_EXLINEFROMCHAR, 0, (LPARAM)charpos);
    linecharindex = SendMessage(hwnd, EM_LINEINDEX, linepos, 0);
    linecharpos = charpos - linecharindex;
    *(short*)buf = 1000;
    outputbuf[0] = 0;
    if (linecharpos >= SendMessage(hwnd, EM_GETLINE, linepos, (LPARAM)buf))
	{
		return FALSE;
	}

	/* if there is a selection, attempt to use it */
	if (p->selstartcharpos != p->selendcharpos)
	{
		int start = p->selstartcharpos;
		int end = p->selendcharpos;
		if (start > end)
		{
			int temp = start;
			start = end;
			end = temp;
		}
		if (end - start < 511)
		{
			int i = 0;
			while (start < end)
				outputbuf[i++] = p->cd->text[start++].ch;
			outputbuf[i] = 0;
	        if (linenum)
	            *linenum = linepos;
	        if (startoffs)
	            *startoffs = start;
	        if (endoffs)
	            *endoffs = end;
			return TRUE;
		}
	}
	
		
	// otherwise get word under cursor
    while (linecharpos && (buf[linecharpos] == ' ' || buf[linecharpos] == '\n' 
        || buf[linecharpos] == '\t' || buf[linecharpos] == 0))
        linecharpos--;
    {
        char *start = buf + linecharpos,  *end = start;
        if (start > buf)
        {
            start--;

            if (start == buf)
            {
                if (!keysym(*start))
                    start++;
            }
            else
            while (start > buf)
            {
                if (!keysym(*start))
                {
                    start++;
                    break;
                }
                start--;
            }
            if (!keysym(*start))
                start++;
        }
        while (*end && keysym(*end))
            end++;
        *end = 0;
        if (start > end)
            start = end;
        if (linenum)
            *linenum = linepos;
        if (startoffs)
            *startoffs = start - buf + linecharindex;
        if (endoffs)
            *endoffs = end - buf + linecharindex;
        strcpy(outputbuf, start);
        return TRUE;
    }
    return FALSE;
}

/**********************************************************************
 * DoHelp handles the F1 key functionality - it gets the word under the
 * cursor then tries a lookup from the favorite help engine.  Again this
 * is kind of linked to the rest of CCIDE
 **********************************************************************/

static void DoHelp(HWND edwin, int speced)
{
    char buf[512];
    if (!GetWordFromPos(edwin, buf,  - 1, 0, 0, 0))
        return ;
    else
		if (speced)
			SpecifiedHelp(buf);
		else
			RTLHelp(buf);
}

/**********************************************************************
 * RunToolTip starts the tooltip running when the cursor moves
 **********************************************************************/
static void RunToolTip(EDITDATA *p, HWND edwin, int x, int y)
{
    static int semaphore;
    static POINT oldpt;
    int charpos;
    int linepos;
    int left, right;
    int start, end;
    TOOLINFO t;
    POINT pt, lpt, rpt;

    pt.x = x;
    pt.y = y;

    if (pt.x == oldpt.x && pt.y == oldpt.y)
        return ;
    if (semaphore)
        return ;
    semaphore = TRUE;

    oldpt = pt;

    if (p->cd->ttup)
    {
        SendMessage(p->cd->tooltip, TTM_ACTIVATE, FALSE, 0);
        p->cd->ttup = FALSE;
    }
    if (uState == notDebugging || uState == Running)
    {
        semaphore = FALSE;
        return ;
    }
    #ifdef XXXXX
        if (p->cd->ttrect.left <= x && p->cd->ttrect.right > x && p->cd->ttrect.top <= y &&
            p->cd->ttrect.bottom > y)
        {
            semaphore = FALSE;
            return ;
        }
    #endif 
    charpos = SendMessage(edwin, EM_CHARFROMPOS, 0, (LPARAM) &pt);
    if (GetWordFromPos(edwin, p->cd->ttident, charpos, &linepos, &start, &end))
    {
        p->cd->ttup = TRUE;
        p->cd->ttlineno = linepos + 1;
        memset(&t, 0, sizeof(t));
        t.cbSize = sizeof(TOOLINFO);
        t.hwnd = edwin;
        t.uFlags = 0;
        t.uId = 10000;
        t.rect.left = x - 8;
        t.rect.right = x + 8;
        t.rect.top = y;
        t.rect.bottom = y + p->cd->txtFontHeight;
        SendMessage(p->cd->tooltip, TTM_NEWTOOLRECT, 0, (LPARAM) &t);
        SendMessage(p->cd->tooltip, TTM_ACTIVATE, TRUE, 0);
    }
    semaphore = FALSE;
}

/**********************************************************************
 * relays a mouse event to the tooltip
 **********************************************************************/
void RelayToolTipEvent(EDITDATA *p, HWND hwnd, UINT iMessage, WPARAM wParam,
    LPARAM lParam)
{
    MSG msg;
    memset(&msg, 0, sizeof(msg));
    msg.hwnd = hwnd;
    msg.message = iMessage;
    msg.wParam = wParam;
    msg.lParam = lParam;
    //   GetCursorPos(&msg.pt) ;
    SendMessage(p->cd->tooltip, TTM_RELAYEVENT, 0, (LPARAM) &msg);
}

/**********************************************************************
 * getundo creates an UNDO structure based on the user's last operation
 **********************************************************************/
UNDO *getundo(EDITDATA *p, int type)
{
    int x;
    UNDO *u;
    if (p->cd->undoing)
        return 0;
	if (p->cd->undohead != p->cd->undotail)
	{
		int x = p->cd->undohead - 1;
		if (x < 0)
			x += UNDO_MAX;
		u = &p->cd->undolist[x];
		if (u->undotemp)
		{
			switch(u->type)
			{
				case UNDO_INSERT:
					u->len -= u->undotemp;
					u->postselstart -= u->undotemp;
					u->postselend -= u->undotemp;
					break;
				case UNDO_DELETE:
				case UNDO_BACKSPACE:
				case UNDO_MODIFY:
					memcpy(u->data, u->data + u->undotemp, u->len - u->undotemp);
					u->postselstart += u->undotemp;
					u->postselend += u->undotemp;
					u->len -=u->undotemp;
					break;
			}
			u->undotemp = 0;
		}
	}
    if (type != UNDO_DELETESELECTION && type != UNDO_INSERTSELECTION && type !=
        UNDO_CASECHANGE && type != UNDO_AUTOBEGIN && type != UNDO_AUTOEND &&
        type != UNDO_AUTOCHAINBEGIN && p->cd->undohead != p->cd->undotail)
    {
        x = p->cd->undohead - 1;
        if (x < 0)
            x += UNDO_MAX;
        if (p->cd->undolist[x].type == type)
        if (type != UNDO_BACKSPACE)
        {
            if (p->selstartcharpos == p->cd->undolist[x].postselstart)
			{
				p->cd->modified = TRUE;
				p->cd->redotop = p->cd->undohead;
                return  &p->cd->undolist[x];
			}
        }
        else
        {
            if (p->selstartcharpos + 1 == p->cd->undolist[x].postselstart)
			{
				p->cd->modified = TRUE;
				p->cd->redotop = p->cd->undohead;
                return  &p->cd->undolist[x];
			}
        }
    }
    u = &p->cd->undolist[p->cd->undohead];
    if (++p->cd->undohead >= UNDO_MAX)
        p->cd->undohead = 0;
    if (p->cd->undohead == p->cd->undotail)
        if (++p->cd->undotail >= UNDO_MAX)
            p->cd->undotail = 0;
	p->cd->redotop = p->cd->undohead;
    u->len = 0;
    u->preselstart = p->selstartcharpos;
    u->preselend = p->selendcharpos;
	u->charpos = p->textshowncharpos;
    u->modified = p->cd->modified;
    p->cd->modified = TRUE;
    u->type = type;
    return u;
}

//-------------------------------------------------------------------------

int insertautoundo(EDITDATA *p, int type)
{
    UNDO *u = getundo(p, type);
	if (u)
	{
	    u->postselstart = p->selstartcharpos;
    	u->postselend = p->selendcharpos;
	}
	return u;
}

/**********************************************************************
 * undo_deletesel gets the undo structure for a CUT operation
 **********************************************************************/
UNDO *undo_deletesel(EDITDATA *p)
{
    UNDO *u;
    int start, end;
    int i = 0;
    if (p->selstartcharpos == p->selendcharpos)
        return 0;
    u = getundo(p, UNDO_DELETESELECTION);

    if (!u)
        return u;
    if (p->selstartcharpos < p->selendcharpos)
    {
        start = p->selstartcharpos;
        end = p->selendcharpos;
    }
    else
    {
        start = p->selendcharpos;
        end = p->selstartcharpos;
    }
    if (end - start > u->max)
    {
        char *temp = realloc(u->data, end - start);
        if (!temp)
            return 0;
        u->data = temp;
        u->max = end - start;
    }
    u->len = end - start;
    while (start < end)
    {
        u->data[i++] = p->cd->text[start++].ch;
    }
    return u;
}

//-------------------------------------------------------------------------

UNDO *undo_casechange(EDITDATA *p)
{
    UNDO *x = undo_deletesel(p);
    x->type = UNDO_CASECHANGE;
    return x;
}

/**********************************************************************
 * undo_insertsel gets the undo structure for an operation which pasts
 **********************************************************************/
UNDO *undo_insertsel(EDITDATA *p, char *s)
{
    UNDO *u = getundo(p, UNDO_INSERTSELECTION);
    if (!u)
        return u;
    u->len = strlen(s);
	if (u->max < u->len)
	{
		u->data = realloc(u->data, u->len);
	}
	memcpy(u->data, s, u->len);
    return u;
}

/**********************************************************************
 * undo_deletechar gets the undo structure for a character deletion
 **********************************************************************/
UNDO *undo_deletechar(EDITDATA *p, int ch, int type)
{
    UNDO *u = getundo(p, type);
    if (!u)
        return u;
    if (u->max <= u->len)
    {
        char *temp = realloc(u->data, u->max + 64);
        if (!temp)
            return 0;
        u->data = temp;
        u->max += 64;
    }
    memmove(u->data + 1, u->data, u->len++);
    u->data[0] = ch;
    u->postselstart = p->selstartcharpos;
    u->postselend = p->selendcharpos;
    return u;
}

/**********************************************************************
 * undo_deletechar gets the undo structure for typing over a character
 **********************************************************************/
UNDO *undo_modifychar(EDITDATA *p)
{
    UNDO *u = getundo(p, UNDO_MODIFY);
    if (!u)
        return u;
    if (u->max <= u->len)
    {
        char *temp = realloc(u->data, u->max + 64);
        if (!temp)
            return 0;
        u->data = temp;
        u->max += 64;
    }
    memmove(u->data + 1, u->data, u->len++);
    u->data[0] = p->cd->text[p->selstartcharpos].ch;
    return u;
}

/**********************************************************************
 * undo_deletechar gets the undo structure for inserting a character
 **********************************************************************/
UNDO *undo_insertchar(EDITDATA *p, int ch)
{
    UNDO *u = getundo(p, UNDO_INSERT);
    if (!u)
        return u;
    if (u->max <= u->len)
    {
        char *temp = realloc(u->data, u->max + 64);
        if (!temp)
            return 0;
        u->data = temp;
        u->max += 64;
    }
    u->data[u->len++] = ch;
    return u;
}

/**********************************************************************
 * ClientArea gets the client area.  We are leaving space at the bottom
 * because it would be overlayed with the scroll bar
 **********************************************************************/
void ClientArea(HWND hwnd, EDITDATA *p, RECT *r)
{
    GetClientRect(hwnd, r);
    //   r->bottom -= GetSystemMetrics(SM_CYHSCROLL) ;
    r->bottom -= r->bottom % p->cd->txtFontHeight;
}

/**********************************************************************
 * posfromchar determines the screen position of a given offset in
 * the buffer
 **********************************************************************/
int posfromchar(HWND hwnd, EDITDATA *p, POINTL *point, int pos)
{
    char buf[256],  *x = buf;
    SIZE size;
    RECT r;
    HDC dc;
    int spos = p->textshowncharpos, xcol;
    int i = 0, j;
    point->x = point->y = 0;
    if (spos > pos)
        return 0;
    while (spos + i < pos && spos + i < p->cd->textlen)
    {
        if (p->cd->text[spos + i].ch == '\n')
        {
            point->y += p->cd->txtFontHeight;
            spos += i + 1;
            i = 0;
        }
        else
            i++;
    }
    i = 0;
    xcol = 0;
    while (spos + i < pos && spos + i < p->cd->textlen)
    {
        if (p->cd->text[spos + i].ch == '\n')
                break;
        if (p->cd->text[spos + i].ch == '\t')
        {
            xcol += p->cd->tabs;
            xcol /= p->cd->tabs;
            xcol *= p->cd->tabs;
        }
        else
            xcol++;
        i++;
    }
    if (xcol >= p->leftshownindex)
        point->x = (xcol - p->leftshownindex) *p->cd->txtFontWidth;
    else
        return 0;
    ClientArea(hwnd, p, &r);
    if (point->x >= r.right || point->y >= r.bottom)
        return 0;
    return 1;
}

/**********************************************************************
 * charfrompos determines the buffer offset from the screen position
 **********************************************************************/
int charfrompos(HWND hwnd, EDITDATA *p, POINTL *point)
{
    RECT r;
    int row, col, xcol = 0;
    int pos = p->textshowncharpos, i = 0;
    char buf[256],  *x = buf;
    ClientArea(hwnd, p, &r);
    if (point->x > r.right || point->y > r.bottom)
        return 0;
    row = point->y / p->cd->txtFontHeight;
    col = point->x / p->cd->txtFontWidth;
    while (row && pos < p->cd->textlen)
    {
        if (p->cd->text[pos].ch == '\n')
            row--;
        pos++;
    }

    if (pos == p->cd->textlen)
        return pos;
    while (pos + i < p->cd->textlen && xcol < p->leftshownindex)
    {
        if (p->cd->text[pos + i].ch == '\n')
                return pos + i - 1;
        if (p->cd->text[pos + i].ch == '\t')
        {
            xcol += p->cd->tabs;
            xcol /= p->cd->tabs;
            xcol *= p->cd->tabs;
        }
        else
            xcol++;
        i++;
    }
    pos += i;
    i = 0;
    xcol = 0;
    while (xcol < col && pos + i < p->cd->textlen)
    {
        if (p->cd->text[pos + i].ch == '\n')
                break;
        if (p->cd->text[pos + i].ch == '\t')
        {
            xcol += p->cd->tabs;
            xcol /= p->cd->tabs;
            xcol *= p->cd->tabs;
        }
        else
            xcol++;
        i++;
    }
    return pos + i /*-1*/;
}

/**********************************************************************
 *  vscrolllen sets the limits for the vertical scroll bar
 **********************************************************************/
void VScrollLen(HWND hwnd, int count, int set)
{
    int count1 = count;
    int base = 0;
    if (!set)
    {
        GetScrollRange(hwnd, SB_VERT, &base, &count1);
        count1 += count;
    }
    SetScrollRange(hwnd, SB_VERT, base, count1, TRUE);
}

/**********************************************************************
 *  vscrolllen sets the position for the vertical scroll bar
 **********************************************************************/
void VScrollPos(HWND hwnd, int count, int set)
{
    int count1 = count;
    if (!set)
    {
        count1 = GetScrollPos(hwnd, SB_VERT);
        count1 += count;
    }
    //   ExtendedMessageBox("hi",0,"%d",count1) ;
    SetScrollPos(hwnd, SB_VERT, count1, TRUE);
}

/**********************************************************************
 * curcol finds the screen column number corresponding to a text position
 * (zero based)
 **********************************************************************/
int curcol(EDITDATA *p, INTERNAL_CHAR *text, int pos)
{
    int rv = 0;
    int opos = pos;
        while (pos && text[pos - 1].ch != '\n')
        {
            pos--;
        }
    while (pos < opos)
    {
        if (text[pos].ch == '\t')
        {
            rv += p->cd->tabs;
            rv = (rv / p->cd->tabs) *p->cd->tabs;
        }
        else
            rv++;
        pos++;
    }
    return rv;
}

//-------------------------------------------------------------------------

void setcurcol(EDITDATA *p)
{
    int pos;
    if (p->selstartcharpos != p->selendcharpos)
        pos = p->selendcharpos;
    else
        pos = p->selstartcharpos;
    p->cd->updowncol = curcol(p, p->cd->text, pos);
}

/**********************************************************************
 *  MoveCaret moves the caret to the position of the selection.  IF 
 *  the caret is offscreen, it gets hidden
 **********************************************************************/

void MoveCaret(HWND hwnd, EDITDATA *p)
{
    int x = 0, y = 0;
    POINTL pt;
    if (posfromchar(hwnd, p, &pt, p->selstartcharpos != p->selendcharpos 
			? p->selendcharpos: p->selstartcharpos))
    {
        if (p->hasfocus)
        {
            SetCaretPos(pt.x, pt.y);
            ShowCaret(hwnd);
        }
        p->hiddenCaret = FALSE;
    }
    else
    {
        if (!p->hiddenCaret && p->hasfocus)
            HideCaret(hwnd);
        p->hiddenCaret = TRUE;
    }
}

/**********************************************************************
 *  Scroll Left scrolls left or right, depending on the sign of 'cols'
 **********************************************************************/
void scrollleft(HWND hwnd, EDITDATA *p, int cols)
{
    p->leftshownindex += cols;
    if (p->leftshownindex < 0)
        p->leftshownindex = 0;
    SendUpdate(hwnd);
    InvalidateRect(hwnd, 0, 0);
}

/**********************************************************************
 *  Scrollup scrolls up or down, depending on the sign of 'lines'
 **********************************************************************/

void scrollup(HWND hwnd, EDITDATA *p, int lines)
{
    RECT r, update;
    int totallines, movelines = lines;
    int pos = p->textshowncharpos, len = 0;
    ClientArea(hwnd, p, &r);
    totallines = r.bottom / p->cd->txtFontHeight;
    if (lines < 0)
    {
        lines =  - lines;
        while (lines && pos > 0)
        {
            --pos;
            if (p->cd->text[pos].ch == '\n')
            {
                lines--;
                len--;
            }
        }
        while (pos)
        {
            --pos;
            if (p->cd->text[pos].ch == '\n')
            {
                pos++;
                break;
            }
        }
        SendUpdate(hwnd);
        if (lines >= totallines)
        {
            InvalidateRect(hwnd, 0, 0);
        }
        else
        {
            if ( - movelines - lines)
                ScrollWindowEx(hwnd, 0, ( - movelines - lines) *p
                    ->cd->txtFontHeight, &r, &r, 0, &update, SW_INVALIDATE);
        }
        p->textshowncharpos = pos;
    }
    else
    {
        while (lines && pos < p->cd->textlen)
        {
            if (p->cd->text[pos].ch == '\n')
            {
                lines--;
                len++;
            }
            pos++;
        }
        SendUpdate(hwnd);
        if (lines >= totallines)
        {
            InvalidateRect(hwnd, 0, 0);
        }
        else
        {
            if (movelines - lines)
                ScrollWindowEx(hwnd, 0,  - (movelines - lines) *p
                    ->cd->txtFontHeight, &r, &r, 0, &update, SW_INVALIDATE);
        }
        p->textshowncharpos = pos;
    }
    SendUpdate(hwnd);
    VScrollPos(hwnd, len, FALSE);
}

/**********************************************************************
 *  ScrollCaretIntoView moves the text in the window around in such a way
 *  that the caret is in the window.
 **********************************************************************/
void ScrollCaretIntoView(HWND hwnd, EDITDATA *p, BOOL middle)
{
    POINTL pt;
    int lines, cols, colpos = 0;
    RECT r;
    int pos1, pos;
    pos1 = p->selendcharpos;
    if (posfromchar(hwnd, p, &pt, pos1))
    {
        MoveCaret(hwnd, p);
        return ;
    }
    ClientArea(hwnd, p, &r);
    lines = r.bottom / p->cd->txtFontHeight;
    cols = r.right / p->cd->txtFontWidth;
    if (pos1 > p->textshowncharpos)
    {
        int xlines = 0;
        pos = p->textshowncharpos;
        while (pos < pos1 && pos < p->cd->textlen)
        {
            if (p->cd->text[pos].ch == '\n')
                xlines++;
            pos++;
        }
        if (xlines >= lines)
		{
			if (middle)
	            scrollup(hwnd, p, xlines - lines/2);
			else
	            scrollup(hwnd, p, xlines - lines + 1);
		}

    }
    else
    {
        int xlines = 0;
        if (pos1 != p->textshowncharpos)
        {
            pos = p->textshowncharpos;
            while (pos > 0 && pos != pos1)
            {
                --pos;
                if (p->cd->text[pos].ch == '\n')
                    xlines++;
            }
            if (xlines > 0)
			{
				if (middle)
	                scrollup(hwnd, p,  - xlines - lines / 2 );
				else
	                scrollup(hwnd, p,  - xlines );
			}
        }
    }
    pos = pos1;
    while (pos > 0 && p->cd->text[pos - 1].ch != '\n')
        pos--;
    while (pos != pos1)
    {
        if (pos && p->cd->text[pos - 1].ch == '\t')
            colpos = ((colpos + p->cd->tabs) / p->cd->tabs) *p->cd->tabs;
        else
            colpos++;
        pos++;
    }
    if (colpos < p->leftshownindex)
        scrollleft(hwnd, p, colpos - p->leftshownindex - 10);
    else if (colpos >= p->leftshownindex + cols)
        scrollleft(hwnd, p, colpos - p->leftshownindex - cols + 1);
    MoveCaret(hwnd, p);


}

/**********************************************************************
 * TrackVScroll handles tracking messages and updating the display when
 * the user moves the vertical scroll bar
 **********************************************************************/
void TrackVScroll(HWND hwnd, EDITDATA *p, int end)
{
    SCROLLINFO si;
    int count;
    memset(&si, 0, sizeof(si));
    si.cbSize = sizeof(si);
    si.fMask = SIF_TRACKPOS;
    GetScrollInfo(hwnd, SB_VERT, &si);
    count = SendMessage(hwnd, EM_LINEINDEX, si.nTrackPos, 0);
    p->textshowncharpos = count;
    //   if (end)
    SetScrollPos(hwnd, SB_VERT, si.nTrackPos, 0);
    SendUpdate(hwnd);
    InvalidateRect(hwnd, 0, 0);
    MoveCaret(hwnd, p);

}

/**********************************************************************
 * TrackHScroll handles tracking messages and updating the display when
 * the user moves the horizontal scroll bar
 **********************************************************************/
void TrackHScroll(HWND hwnd, EDITDATA *p, int end)
{
    SCROLLINFO si;
    int count;
    memset(&si, 0, sizeof(si));
    si.cbSize = sizeof(si);
    si.fMask = SIF_TRACKPOS;
    GetScrollInfo(hwnd, SB_HORZ, &si);
    p->leftshownindex = si.nTrackPos;
    if (end)
        SetScrollPos(hwnd, SB_HORZ, si.nTrackPos, 0);
    SendUpdate(hwnd);

}

/**********************************************************************
 * lfchars counts the number of times we switch from one line to another
 * within a range of chars
 **********************************************************************/
int lfchars(INTERNAL_CHAR *c, int start, int end)
{
    int rv = 0;
    while (start < end)
    {
        if (c[start].ch == '\n')
            rv++;
        start++;
    }
    return rv;
}

/**********************************************************************
 * Line from char takes a character pos and turns it into a line number
 **********************************************************************/
int LineFromChar(EDITDATA *p, int pos)
{
    int rv = 0;
    INTERNAL_CHAR *ic = p->cd->text;
    while (ic < p->cd->text + p->cd->textlen && pos)
    {
        if (ic->ch == '\n')
            rv++;
        pos--;
        ic++;
    }
    return rv;
}

/**********************************************************************
 * SelLine is the Same as lineFromChar, but counts a partial (unterminated) 
 * line at the end of the buffer
 **********************************************************************/
static int SelLine(EDITDATA *p, int pos)
{
    int rv = LineFromChar(p, pos);
    if (pos && p->cd->text[pos - 1].ch != '\n')
            rv++;
    return rv;
}

/**********************************************************************
 * Replace handles pasting.  Will also cut a previous selection, if there
 * was one
 **********************************************************************/
void Replace(HWND hwnd, EDITDATA *p, char *s, int lens)
{
    UNDO *u = 0;
    int i, temp;
    char *buf,  *s1;
    int len = 0, linepos;

    i = p->selendcharpos - p->selstartcharpos;
    u = undo_deletesel(p);
    if (i > 0)
    {
        len -= lfchars(p->cd->text, p->selstartcharpos, p->selendcharpos);
        SendMessage(GetParent(hwnd), EN_LINECHANGE, SelLine(p, p
            ->selstartcharpos), len);
        memcpy(p->cd->text + p->selstartcharpos, p->cd->text + p->selendcharpos, (p
            ->cd->textlen - p->selendcharpos + 1) *sizeof(INTERNAL_CHAR));
        p->selendcharpos = p->selstartcharpos;
        p->cd->textlen -= i;
		UpdateSiblings(p, p->selstartcharpos, -i);
    }
    else if (i < 0)
    {
        temp = lfchars(p->cd->text, p->selendcharpos, p->selstartcharpos);
        SendMessage(GetParent(hwnd), EN_LINECHANGE, SelLine(p, p->selendcharpos)
            ,  - temp);
        VScrollPos(hwnd,  - temp, FALSE);
        len -= temp;
        memcpy(p->cd->text + p->selendcharpos, p->cd->text + p->selstartcharpos, (p
            ->cd->textlen - p->selstartcharpos + 1) *sizeof(INTERNAL_CHAR));
        p->selstartcharpos = p->selendcharpos;
        p->cd->textlen += i;
		UpdateSiblings(p, p->selstartcharpos, i);
    }
    if (u)
    {
        u->postselstart = u->postselend = p->selstartcharpos;
    }
	if (!commitmem(p,p->cd->textlen + lens))
	{
        p->selendcharpos = p->selstartcharpos;
        SendUpdate(hwnd);
        p->cd->sendchangeonpaint = TRUE;
        InvalidateRect(hwnd, 0, 0);
        return ;
    }
    if (lens)
    {
        u = undo_insertsel(p, s);
        if (u)
        {
            u->preselstart = u->preselend = p->selstartcharpos;
            u->postselstart = u->preselstart;
            u->postselend = u->preselstart + lens;
        }
    }
    temp = 0;
    s1 = s;
    i = 0;
    while (*s1 && i++ < lens)
        if (*s1++ == '\n')
            temp++;
    SendMessage(GetParent(hwnd), EN_LINECHANGE, SelLine(p, p->selstartcharpos),
        temp);
    memmove(p->selstartcharpos + lens + p->cd->text, p->cd->text + p->selstartcharpos, 
        (p->cd->textlen - p->selstartcharpos + 1) *sizeof(INTERNAL_CHAR));
    memset(p->selstartcharpos + p->cd->text, 0, (lens) *sizeof(INTERNAL_CHAR));
	UpdateSiblings(p, p->selstartcharpos, lens);
    i = 0;
    while (*s && i < lens)
    {
        if (*s == '\n')
            len++;
        p->cd->text[p->selstartcharpos + i].ch =  *s++;
        p->cd->text[p->selstartcharpos + i].Color = p->cd->defforeground;
        i++;
        p->cd->textlen++;
    }
    VScrollLen(hwnd, len, FALSE);
    if (p->cd->colorize)
        FormatBufferFromScratch(p->cd->text, p->selstartcharpos - 1, p
            ->selstartcharpos + lens + 1, p->cd->colorize);
    SendUpdate(hwnd);
    p->cd->sendchangeonpaint = TRUE;
    InvalidateRect(hwnd, 0, 0);
	reparse(hwnd, p);
}

/**********************************************************************
 * GetLineOffset returns the line number, this time as an offset from
 * the first line shown at the top of the window
 **********************************************************************/
int GetLineOffset(HWND hwnd, EDITDATA *p, int chpos)
{
    int pos = p->textshowncharpos;
    int line = 0;
    while (pos != chpos && pos < p->cd->textlen)
    {
        if (p->cd->text[pos].ch == '\n')
            line++;
        pos++;
    }
    return line;
}

/**********************************************************************
 * drawline draws the current line and everything below it(by invalidating
 * the selection)
 **********************************************************************/
void drawline(HWND hwnd, EDITDATA *p, int chpos)
{
    RECT r;
    int pos;
    pos = p->selendcharpos;
    ClientArea(hwnd, p, &r);
    r.top = GetLineOffset(hwnd, p, pos) *p->cd->txtFontHeight;
    SendUpdate(hwnd);
    InvalidateRect(hwnd, &r, 1);
    MoveCaret(hwnd, p);
}

/**********************************************************************
 * insertchar handles the functionality of inserting a character
 * will also cut a previous selection
 **********************************************************************/
void insertchar(HWND hwnd, EDITDATA *p, int ch)
{
    int len = 0, temp;
    UNDO *u = 0;
    if (p->cd->inserting)
    {
        int i = p->selendcharpos - p->selstartcharpos;
        u = undo_deletesel(p);
        if (i > 0)
        {
            len -= lfchars(p->cd->text, p->selstartcharpos, p->selendcharpos);
            SendMessage(GetParent(hwnd), EN_LINECHANGE, SelLine(p, p
                ->selstartcharpos), len);
            memcpy(p->cd->text + p->selstartcharpos, p->cd->text + p->selendcharpos, (p
                ->cd->textlen - p->selendcharpos + 1) *sizeof(INTERNAL_CHAR));
            p->cd->textlen -= i;
			UpdateSiblings(p, p->selstartcharpos, -i);
        }
        else if (i < 0)
        {
            temp = lfchars(p->cd->text, p->selendcharpos, p->selstartcharpos);
            SendMessage(GetParent(hwnd), EN_LINECHANGE, SelLine(p, p
                ->selendcharpos),  - temp);
            VScrollPos(hwnd,  - temp, FALSE);
            len -= temp;
            memcpy(p->cd->text + p->selendcharpos, p->cd->text + p->selstartcharpos, (p
                ->cd->textlen - p->selendcharpos + 1) *sizeof(INTERNAL_CHAR));
            p->selstartcharpos = p->selendcharpos;
            p->cd->textlen += i;
			UpdateSiblings(p, p->selstartcharpos, i);
        }
        p->selendcharpos = p->selstartcharpos;
        if (u)
        {
            u->postselstart = u->postselend = p->selstartcharpos;
        }
        if (ch == '\n')
        {
            len++;
            SendMessage(GetParent(hwnd), EN_LINECHANGE, SelLine(p, p
                ->selstartcharpos), 1);
        }
        if (!commitmem(p,p->cd->textlen + 2))
        {
            SendUpdate(hwnd);
            p->cd->sendchangeonpaint = TRUE;
            InvalidateRect(hwnd, 0, 0);
            return;
        }
        u = undo_insertchar(p, ch);
        memmove(p->cd->text + p->selstartcharpos + 1, p->cd->text + p->selstartcharpos,
            (p->cd->textlen - p->selstartcharpos + 1) *sizeof(INTERNAL_CHAR));
        p->cd->text[p->selstartcharpos].ch = ch;
		if (p->cd->text[p->selstartcharpos].Color == 0)
			p->cd->text[p->selstartcharpos].Color = C_TEXT;
        p->cd->textlen++;
		UpdateSiblings(p, p->selstartcharpos, 1);		
        VScrollLen(hwnd, len, FALSE);
    }
    else
    {
        p->selendcharpos = p->selstartcharpos;
        if (p->cd->text[p->selstartcharpos].ch == '\n')
                p->selendcharpos = p->selstartcharpos++;
        u = undo_modifychar(p);
        p->cd->text[p->selstartcharpos].ch = ch;
    }
    p->selendcharpos = ++p->selstartcharpos;
    if (u)
    {
        u->postselstart = p->selstartcharpos;
        u->postselend = p->selendcharpos;
    }
    p->cd->sendchangeonpaint = TRUE;
    ScrollCaretIntoView(hwnd, p, FALSE);
	reparse(hwnd, p);
}

/**********************************************************************
 * insertcr inserts a cr/lf pair
 **********************************************************************/
void insertcr(HWND hwnd, EDITDATA *p)
{
    RECT r, update;
    int totallines;
    int temp;
    drawline(hwnd, p, p->selstartcharpos);
    if (p->selstartcharpos > p->selendcharpos)
    {
        temp =  - p->selstartcharpos;
        p->selstartcharpos = p->selendcharpos;
    }
    else
    {
        temp = p->selendcharpos;
        p->selendcharpos = p->selstartcharpos;
    }
    insertchar(hwnd, p, '\n');
        if (temp < 0)
        {
            p->selstartcharpos =  - temp + 1;
        }
        else
        {
            p->selendcharpos = temp + 1;
        }
    //   ClientArea(hwnd,p,&r) ;
    //   r.top = GetLineOffset(hwnd,p,p->selstartcharpos)*p->cd->txtFontHeight ;
    //   ScrollWindowEx(hwnd,0,1*p->cd->txtFontHeight,&r,0,0,&update,SW_ERASE) ;
    VScrollLen(hwnd, 1, FALSE);
    VScrollPos(hwnd, 1, FALSE);
}

/**********************************************************************
 * inserttab inserts a tab, or types in a bunch of spaces to take
 * us to the next tab position
 **********************************************************************/
void inserttab(HWND hwnd, EDITDATA *p)
{
    if (!(editFlags &TABS_AS_SPACES))
        insertchar(hwnd, p, '\t');
    else
    {
        int pos = p->selstartcharpos, col = 0, col2;
        while (pos && --pos)
        {
            if (p->cd->text[pos].ch == '\n')
            {
                pos++;
                break;
            }
        }
        while (pos != p->selstartcharpos)
        {
            if (p->cd->text[pos].ch == '\t')
            {
                col = col + p->cd->tabs;
                col /= p->cd->tabs;
                col *= p->cd->tabs;
            }
            else
                col++;
            pos++;
        }
        col2 = col + p->cd->tabs;
        col2 /= p->cd->tabs;
        col2 *= p->cd->tabs;
        while (col < col2)
        {
            insertchar(hwnd, p, ' ');
            col++;
        }
    }
	reparse(hwnd, p);
}

int firstword(INTERNAL_CHAR *pos, char *name)
{
	int l = strlen(name);
	int i;
	for (i=0; i < l; i++)
		if (!pos[i].ch || pos[i].ch != name[i])
			return FALSE ;
	if (isalnum(pos[i].ch) || pos[i].ch == '_')
		return FALSE;
	return TRUE ;
}
/**********************************************************************
 * tab to the current line position
 **********************************************************************/
void insertcrtabs(HWND hwnd, EDITDATA *p)
{
    int pos, n;
    int oldinsert = p->cd->inserting;
	int storepos, parencount = 0;
    if (!p->cd->colorize)
        return ;
    if (editFlags &AUTO_INDENT)
        return ;
    p->cd->inserting = TRUE;
    pos = p->selstartcharpos - 1;
    while (1)
    {
		int pos2 = pos ;
        while (pos && p->cd->text[pos - 1].ch != '\n')
            pos--;
        while (p->cd->text[pos].ch && isspace(p->cd->text[pos].ch) && p->cd->text[pos].ch 
            != '\n')
            pos++;
        if (p->cd->text[pos].ch != '#')
		{
			while (pos2-- > pos &&
				   p->cd->text[pos2].ch && p->cd->text[pos2].ch != '\n')
			{
				if (p->cd->text[pos2].ch == '(')
				{
					storepos = pos2 ;
					if (++parencount > 0)
						break;
				}
				else
					if (p->cd->text[pos2].ch == ')')
						parencount--;
			} 
			if (parencount > 0)
			{
				pos = storepos + 1 ;
				break ;
			}
			else if (parencount == 0)
	            break;
		}
        while (pos && p->cd->text[pos - 1].ch != '\n')
            pos--;
        if (!pos)
            break;
        pos--;
    }
    insertautoundo(p, UNDO_AUTOCHAINEND);
    n = curcol(p, p->cd->text, pos);
	if (firstword(p->cd->text +pos, "if") || firstword(p->cd->text + pos, "else")
		||firstword(p->cd->text + pos, "do") || firstword(p->cd->text + pos, "for")
		||firstword(p->cd->text + pos, "while"))
		n += p->cd->tabs;
    while (n >= p->cd->tabs)
    {
        inserttab(hwnd, p);
        n -= p->cd->tabs;
    }
    while (n--)
        insertchar(hwnd, p, ' ');
    p->cd->inserting = oldinsert;
    insertautoundo(p, UNDO_AUTOCHAINBEGIN);
}

//-------------------------------------------------------------------------

int spacedend(EDITDATA *p, int pos)
{
    int rv = 0;
    while (pos && p->cd->text[pos - 1].ch != '\n')
        if (!isspace(p->cd->text[--pos].ch))
            return 0;
    rv = pos;
    while (p->cd->text[pos].ch && isspace(p->cd->text[pos].ch) && p->cd->text[pos].ch !=
        '\n')
        pos++;
    if (p->cd->text[pos].ch == '}')
        return rv;
    else
        return 0;
}

//-------------------------------------------------------------------------

int preprocline(EDITDATA *p, int pos)
{
    int rv;
    while (pos && p->cd->text[pos - 1].ch != '\n')
        pos--;
    rv = pos;
    while (isspace(p->cd->text[pos].ch))
        pos++;
    if (p->cd->text[pos].ch == '#')
        return rv;
    else
        return  - 1;
}

/**********************************************************************
 * tab to the current line position
 **********************************************************************/
void InsertBeginTabs(HWND hwnd, EDITDATA *p)
{
    int pos, n;
	int storepos, parencount = 0;
	int solpos, eospos;
    int oldinsert = p->cd->inserting;
    if (p->cd->colorize != COLORIZE_C && p->cd->colorize != COLORIZE_RC)
        return ;
    if (editFlags &AUTO_FORMAT)
        return ;
    p->cd->inserting = TRUE;
    pos = p->selstartcharpos - 1;
	solpos = pos;
	while (solpos && p->cd->text[solpos - 1].ch != '\n')
	{
		if (!isspace(p->cd->text[solpos - 1].ch))
			return ;
		solpos--;
	}
	if (solpos)
		pos = solpos-1 ;
	eospos = solpos;
	while (eospos < p->cd->textlen && isspace(p->cd->text[eospos].ch) &&
		   	p->cd->text[eospos].ch != '\n')
		eospos++;
    while (1)
    {
		int pos2 = pos ;
        while (pos && p->cd->text[pos - 1].ch != '\n')
            pos--;
        while (p->cd->text[pos].ch && isspace(p->cd->text[pos].ch) && p->cd->text[pos].ch 
            != '\n')
            pos++;
        if (p->cd->text[pos].ch != '#')
		{
			while (pos2-- > pos &&
				   p->cd->text[pos2].ch && p->cd->text[pos2].ch != '\n')
			{
				if (p->cd->text[pos2].ch == '(')
				{
					storepos = pos2 ;
					if (++parencount == 0)
						break;
				}
				else
					if (p->cd->text[pos2].ch == ')')
						if (--parencount == 0)
							break;
			} 
			if (parencount >= 0)
	            break;
		}
        while (pos && p->cd->text[pos - 1].ch != '\n')
            pos--;
        if (!pos)
            break;
        pos--;
    }
	while (pos && p->cd->text[pos-1].ch != '\n')
		pos--;
	while (isspace(p->cd->text[pos].ch) && p->cd->text[pos].ch && pos < p->cd->textlen)
		pos++;
    insertautoundo(p, UNDO_AUTOCHAINEND);
    n = curcol(p, p->cd->text, pos);
    p->selstartcharpos = solpos;
    p->selendcharpos = eospos;
    Replace(hwnd, p, "", 0);
    while (n >= p->cd->tabs)
    {
        inserttab(hwnd, p);
        n -= p->cd->tabs;
    }
    while (n--)
        insertchar(hwnd, p, ' ');
    p->selstartcharpos = ++p->selendcharpos; // skip past '}'
    insertautoundo(p, UNDO_AUTOCHAINBEGIN);
	p->cd->inserting = oldinsert;
}
/**********************************************************************
 * tab to the current line position
 **********************************************************************/
void InsertEndTabs(HWND hwnd, EDITDATA *p, int newend)
{
    int pos, n;
    int solpos, eospos;
    int lsolpos, leospos;
    int oldinsert = p->cd->inserting;
    if (p->cd->colorize != COLORIZE_C && p->cd->colorize != COLORIZE_RC)
        return ;
    if (!newend)
        return ;
    if (editFlags &AUTO_FORMAT)
        return ;
    p->cd->inserting = TRUE;
    leospos = pos = p->selstartcharpos - 1;
    while (isspace(p->cd->text[leospos].ch) && p->cd->text[leospos].ch != '\n')
        leospos++;
    if (lsolpos = spacedend(p, pos))
    {
        int indentlevel = 0;
        eospos = 0;
        pos--;
        while (pos > 0)
        {
            int pos1 = preprocline(p, pos);
            if (pos1 !=  - 1)
                pos = pos1;
            else if (p->cd->text[pos].Color != C_COMMENT)
                if (p->cd->text[pos].ch == '{')
            if (!indentlevel)
            {
                while (pos && p->cd->text[pos - 1].ch != '\n')
                        pos--;
                while (isspace(p->cd->text[pos].ch))
                    pos++;
                eospos = pos;
                break;
            }
            else
                indentlevel--;
            else if (p->cd->text[pos].ch == '}')
                indentlevel++;
            pos--;
        }
        insertautoundo(p, UNDO_AUTOCHAINEND);
        n = curcol(p, p->cd->text, eospos);
        p->selstartcharpos = lsolpos;
        p->selendcharpos = leospos;
        Replace(hwnd, p, "", 0);
        while (n >= p->cd->tabs)
        {
            inserttab(hwnd, p);
            n -= p->cd->tabs;
        }
        while (n--)
            insertchar(hwnd, p, ' ');
        p->selstartcharpos = ++p->selendcharpos; // skip past '}'
        insertautoundo(p, UNDO_AUTOCHAINBEGIN);
    }
    p->cd->inserting = oldinsert;
}

//-------------------------------------------------------------------------

void SelectIndent(HWND hwnd, EDITDATA *p, int insert)
{
    int olds = p->selstartcharpos;
    int olde = p->selendcharpos;
    int start = p->selstartcharpos;
    int end = p->selendcharpos;
    int oldinsert = p->cd->inserting;
	int oldselect = p->cd->selecting;
	int oldte = p->cd->textlen;
	int decd = FALSE;
	int inverted = FALSE ;
    if (start == end)
        return ;
    if (end < start)
    {
		inverted = TRUE ;
        start = p->selendcharpos;
        end = p->selstartcharpos;
    }
    if (end && p->cd->text[end - 1].ch == '\n')
        end--, decd++;
    p->cd->inserting = TRUE;
    while (start && p->cd->text[start - 1].ch != '\n')
        start--;
    while (p->cd->text[end].ch && p->cd->text[end].ch != '\n')
        end++;
    olds = start;
    insertautoundo(p, UNDO_AUTOEND);
    while (start < end)
    {
		int x = p->textshowncharpos;
        if (insert)
        {
            p->selstartcharpos = p->selendcharpos = start;
            inserttab(hwnd, p);
            end += p->cd->textlen - oldte;
			if (start < x)
				p->textshowncharpos = x + p->cd->textlen - oldte;
        }
        else
        {
            if (isspace(p->cd->text[start].ch) && p->cd->text[start].ch != '\n')
            {
                int count = 0;
                if (p->cd->text[start + count].ch == ' ')
                    while (count < p->cd->tabs)
                        if (p->cd->text[start + count].ch != ' ')
                            break;
                        else
                            count++;
                else
                    count++;
                p->selstartcharpos = start;
                p->selendcharpos = start + count;
                Replace(hwnd, p, "", 0);
                end -= count;
				if (start < p->textshowncharpos)
					p->textshowncharpos = x - count;
            }
        }
		oldte = p->cd->textlen;
        while (p->cd->text[start].ch && p->cd->text[start].ch != '\n')
            start++;
        if (p->cd->text[start].ch)
            start++;
    }
	if (inverted)
	{
	    p->selstartcharpos = end + decd;
    	p->selendcharpos = olds;
	}
	else
	{
	    p->selendcharpos = end + decd;
    	p->selstartcharpos = olds;
	}
    p->cd->inserting = oldinsert;
	p->cd->selecting = TRUE;
	MoveCaret(hwnd, p);
	p->cd->selecting = oldselect;
    insertautoundo(p, UNDO_AUTOBEGIN);
    InvalidateRect(hwnd, 0, 0);
}

//-------------------------------------------------------------------------

void DeletePound(HWND hwnd, EDITDATA *p)
{
    int n, m;
    if (p->cd->colorize != COLORIZE_C && p->cd->colorize != COLORIZE_RC)
        return ;
    if (editFlags &AUTO_FORMAT)
        return ;
    if (p->selstartcharpos && p->cd->text[p->selstartcharpos - 1].ch != '#')
        return ;
    n = p->selstartcharpos - 1;
    while (n && p->cd->text[n - 1].ch != '\n')
        n--;
    m = n;
    while (isspace(p->cd->text[m].ch))
        m++;
    if (p->cd->text[m].ch != '#' || m == n)
        return ;
    insertautoundo(p, UNDO_AUTOCHAINEND);
    p->selstartcharpos = n;
    p->selendcharpos = m;
    Replace(hwnd, p, "", 0);
    p->selstartcharpos = p->selendcharpos = n + 1;
    insertautoundo(p, UNDO_AUTOCHAINBEGIN);
    ScrollCaretIntoView(hwnd, p, FALSE);
}

//-------------------------------------------------------------------------

void DeletePercent(HWND hwnd, EDITDATA *p)
{
    int n, m;
    if (p->cd->colorize != COLORIZE_ASM)
        return ;
    if (editFlags &AUTO_FORMAT)
        return ;
    if (p->selstartcharpos && p->cd->text[p->selstartcharpos - 1].ch != '%')
        return ;
    n = p->selstartcharpos - 1;
    while (n && p->cd->text[n - 1].ch != '\n')
        n--;
    m = n;
    while (isspace(p->cd->text[m].ch))
        m++;
    if (p->cd->text[m].ch != '%' || m == n)
        return ;
    insertautoundo(p, UNDO_AUTOCHAINEND);
    p->selstartcharpos = n;
    p->selendcharpos = m;
    Replace(hwnd, p, "", 0);
    p->selstartcharpos = p->selendcharpos = n + 1;
    insertautoundo(p, UNDO_AUTOCHAINBEGIN);
    ScrollCaretIntoView(hwnd, p, FALSE);
}

void CancelParenMatch(HWND hwnd, EDITDATA *p)
{
	if (p->matchingEnd != 0)
	{
		p->matchingEnd = p->matchingStart = 0;
		InvalidateRect(hwnd,NULL,0);
	}
}
int FindParenMatchBackward(HWND hwnd, EDITDATA *p, int dec)
{
	int skip,match;
	int level = 1;
	int s = p->selstartcharpos;
	int quotechar = 0;
	if (s != p->selendcharpos || s == p->cd->textlen && !dec)
		return FALSE;
	if (dec && s)
		s--;
	if (p->cd->text[s].ch == ')')
		skip = ')', match = '(';
	else if (p->cd->text[s].ch == '}')
		skip = '}', match = '{';
	else if (p->cd->text[s].ch == ']')
		skip = ']', match = '[';
	else
		return FALSE;
	while (--s)
	{
		if (quotechar == p->cd->text[s].ch && (!s || p->cd->text[s-1].ch != '\\' 
											   || s < 2 || p->cd->text[s-2].ch == '\\'))
			quotechar = 0;
		else if (!quotechar)
			if ((p->cd->text[s].ch == '\'' || p->cd->text[s].ch == '"')
				&& (!s || p->cd->text[s-1].ch != '\\'
					|| s < 2 || p->cd->text[s-2].ch == '\\'))
				quotechar = p->cd->text[s].ch;
			else if (p->cd->text[s].ch == skip)
				level++;
			else if (p->cd->text[s].ch == match)
				if (!--level)
					break;
	}
	if (level)
		return FALSE;
	p->matchingStart = p->selstartcharpos - dec;
	p->matchingEnd = s;
	InvalidateRect(hwnd, NULL, FALSE);
	return TRUE;
}
int FindParenMatchForward(HWND hwnd, EDITDATA *p, int dec)
{
	int skip,match;
	int level = 1;
	int s = p->selstartcharpos;
	int quotechar = 0;
	if (s != p->selendcharpos || s == p->cd->textlen && !dec)
		return FALSE;
	if (dec && s)
		s--;
	if (p->cd->text[s].ch == '(')
		skip = '(', match = ')';
	else if (p->cd->text[s].ch == '{')
		skip = '{', match = '}';
	else if (p->cd->text[s].ch == '[')
		skip = '[', match = ']';
	else
		return FALSE;
	while (++s != p->cd->textlen)
	{
		if (quotechar == p->cd->text[s].ch && (p->cd->text[s-1].ch != '\\'
											   || s < 2 || p->cd->text[s-2].ch == '\\'))
			quotechar = 0; 
		else if (!quotechar)
			if ((p->cd->text[s].ch == '\'' || p->cd->text[s].ch == '"')
				&& (p->cd->text[s-1].ch != '\\' || s < 2 || p->cd->text[s-2].ch == '\\'))
				quotechar = p->cd->text[s].ch;
			else if (p->cd->text[s].ch == skip)
				level++;
			else if (p->cd->text[s].ch == match)
				if (!--level)
					break;
	}
	if (level)
		return FALSE;
	p->matchingStart = p->selstartcharpos - dec;
	p->matchingEnd = s;
	InvalidateRect(hwnd, NULL, FALSE);
	return TRUE;
}
void FindParenMatch(HWND hwnd, EDITDATA *p)
{
	if (parenthesisCheck)
		if (!FindParenMatchForward(hwnd, p ,TRUE ))
			FindParenMatchBackward(hwnd, p, TRUE);
}
/**********************************************************************
 * go backwards to the last tab position
 **********************************************************************/
void backtab(HWND hwnd, EDITDATA *p)
{
    int pos = p->selstartcharpos, col = 0, col2;
    if (pos)
    {
        if (pos && p->cd->text[pos - 1].ch == '\t')
            p->selstartcharpos--;
        else
        {
            int sol;
            if (p->cd->text[pos].ch == '\n')
                pos--;
            while (pos)
            {
                if (p->cd->text[pos].ch == '\n')
                {
                    pos++;
                    break;
                }
                pos--;
            }
            sol = pos;
            while (pos != p->selstartcharpos)
            {
                if (p->cd->text[pos].ch == '\t')
                {
                    col = col + p->cd->tabs;
                    col /= p->cd->tabs;
                    col *= p->cd->tabs;
                }
                else
                    col++;
                pos++;
            }
            col2 = col - 1;
            col2 /= p->cd->tabs;
            col2 *= p->cd->tabs;
            col = 0;
            while (col < col2)
            {
                if (p->cd->text[pos].ch == '\t')
                {
                    col = col + p->cd->tabs;
                    col /= p->cd->tabs;
                    col *= p->cd->tabs;
                }
                else
                    col++;
                sol++;
            }
            p->selstartcharpos = sol;
        }
        Replace(hwnd, p, "", 0);
    }

}

/**********************************************************************
 * removecr cuts a CR/LF pair
 **********************************************************************/
void removecr(HWND hwnd, EDITDATA *p, int utype)
{
    int del = 0;
    int sel = SelLine(p, p->selstartcharpos);

    if (p->cd->text[p->selstartcharpos].ch == '\n')
            del++, sel++;
    SendMessage(GetParent(hwnd), EN_LINECHANGE, sel,  - 1);
    undo_deletechar(p, CRLF_DUMMY, utype);
    memcpy(p->cd->text + p->selstartcharpos, p->cd->text + p->selstartcharpos + del, (p
        ->cd->textlen - p->selstartcharpos - del + 1) *sizeof(INTERNAL_CHAR));
    p->cd->textlen -= del;
	UpdateSiblings(p, p->selstartcharpos, -del);
    SendUpdate(hwnd);
    p->cd->sendchangeonpaint = TRUE;
    VScrollLen(hwnd,  - 1, FALSE);
    VScrollPos(hwnd,  - 1, FALSE);
	reparse(hwnd, p);
}

/**********************************************************************
 * removechar cuts a character from the text (delete or back arrow)
 **********************************************************************/
void removechar(HWND hwnd, EDITDATA *p, int utype)
{
    if (p->cd->inserting && p->selstartcharpos != p->selendcharpos)
    {
        Replace(hwnd, p, "", 0);
        ScrollCaretIntoView(hwnd, p, FALSE);
    }
    else
    {
        int del;
        if (p->selstartcharpos == p->cd->textlen)
            return ;
		if (p->cd->text[p->selstartcharpos].ch == '\f')
		{
			del = 2;
		    insertautoundo(p, UNDO_AUTOEND);
			if (utype == UNDO_BACKSPACE)
			{
				p->selstartcharpos++;
	            undo_deletechar(p, '\n', utype);
				p->selstartcharpos--;
	            undo_deletechar(p, '\f', utype);
			}
			else
			{
	            undo_deletechar(p, '\f', utype);
	            undo_deletechar(p, '\n', utype);
			}
		    insertautoundo(p, UNDO_AUTOBEGIN);
		}
		else
		{
			del = 1;
            undo_deletechar(p, p->cd->text[p->selstartcharpos].ch, utype);
		}
        memcpy(p->cd->text + p->selstartcharpos, p->cd->text + p
	        ->selstartcharpos + del, (p->cd->textlen - p->selstartcharpos - del + 1)
    	    *sizeof(INTERNAL_CHAR));
        p->cd->textlen-=del;
		UpdateSiblings(p, p->selstartcharpos, -del);
        ScrollCaretIntoView(hwnd, p, FALSE);
    	drawline(hwnd, p, p->selstartcharpos);
		reparse(hwnd, p);
    }
    p->selendcharpos = p->selstartcharpos;
}
    /**********************************************************************
     * SelToClipboard copies the current selection to the clipboard
     **********************************************************************/
    void SelToClipboard(HWND hwnd, EDITDATA *p)
    {
        int start, end, lf = 0, i;
        if (p->selendcharpos - p->selstartcharpos < 0)
        {
            end = p->selstartcharpos;
            start = p->selendcharpos;
        }
        else
        {
            end = p->selendcharpos;
            start = p->selstartcharpos;
        }
        if (end == start)
            return ;
        for (i = start; i < end; i++)
            if (p->cd->text[i].ch == '\n')
                lf++;
        if (OpenClipboard(hwnd))
        {
            HGLOBAL glmem = GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE, end -
                start + 1+lf);
            if (glmem != NULL)
            {
                char *data = GlobalLock(glmem),  *q = data;
                for (i = start; i < end; i++)
                {
                    if (p->cd->text[i].ch == '\n')
                        *q++ = '\r';
                    *q++ = p->cd->text[i].ch;
                }
                *q++ = 0;
                GlobalUnlock(data);
                EmptyClipboard();
                SetClipboardData(CF_TEXT, glmem);
            }
            CloseClipboard();
        }
    }
    /**********************************************************************
     * ClipboardToSel pastes the clipboard into the text
     **********************************************************************/
    void ClipboardToSel(HWND hwnd, EDITDATA *ptr)
    {
        if (!IsClipboardFormatAvailable(CF_TEXT))
            return ;
        if (OpenClipboard(hwnd))
        {
            HANDLE clh = GetClipboardData(CF_TEXT);
            char *data = GlobalLock(clh);
            int l = GlobalSize(clh);
            if (l)
            {
                char *mem = calloc(1,l),  *p = mem,  *q = mem;
                if (mem)
                {
                    memcpy(mem, data, l);
                    mem[l - 1] = 0;
                    while (*p)
                        if (*p == '\r')
                            p++;
                        else
                            *q++ =  *p++;
                    *q = 0;
                    if (mem[0])
                    {
                        Replace(hwnd, ptr, mem, q - mem);
                        ScrollCaretIntoView(hwnd, ptr, FALSE);
                        if (ptr->cd->colorize)
                            FormatBufferFromScratch(ptr->cd->text, ptr
                                ->selstartcharpos - 1, ptr->selendcharpos + 1,
                                ptr->cd->colorize);
                        SendUpdate(hwnd);
                        ptr->cd->sendchangeonpaint = TRUE;
                        ptr->selendcharpos = ptr->selstartcharpos = ptr
                            ->selstartcharpos + strlen(mem);
                        MoveCaret(hwnd, ptr);
                        InvalidateRect(hwnd, 0, 0);
                    }
                    free(mem);
                }
            }
            GlobalUnlock(data);
            CloseClipboard();
        }
    }
    /**********************************************************************
     * upline scrolls the display down one line
     **********************************************************************/
    void upline(HWND hwnd, EDITDATA *p, int lines)
    {
        RECT r;
        int ilines = lines;
        int curline;
        int pos, oldsel;
        int col, index = 0;
        pos = p->selendcharpos;
        //   oldsel = pos ;
        col = p->cd->updowncol;
        if (lines > 0)
        {
            while (lines && pos < p->cd->textlen)
            {
                if (p->cd->text[pos].ch == '\n')
                    lines--;
                pos++;
            }
        }
        else
        {
                if (pos)
                    pos--;
            while (lines && pos)
            {
                if (p->cd->text[pos].ch == '\n')
                    lines++;
                pos--;
            }
            while (pos)
            {
                if (p->cd->text[pos].ch == '\n')
                {
                    pos++;
                    break;
                }
                pos--;
            }
        }
        while (index < col && pos < p->cd->textlen && p->cd->text[pos].ch != '\n')
        {
            if (p->cd->text[pos].ch == '\t')
            {
                index += p->cd->tabs;
                index = (index / p->cd->tabs) *p->cd->tabs;
            }
            else
                index++;
            pos++;
        }
		if (pos && p->cd->text[pos-1].ch == '\f')
			pos--;
        if (!p->cd->selecting)
            p->selendcharpos = p->selstartcharpos = pos;
        else
            p->selendcharpos = pos;
        ScrollCaretIntoView(hwnd, p, FALSE);
    }
	int lineParsed(EDITDATA *p, int line)
	{
		if (completionEnabled)
		{
			if (p->cd->lineData)
			{
				if (!(p->cd->lineData[line/8] & (1 << (line & 7))))
				{
					return FALSE;
				}
			}
		}
		return TRUE;
	}
    int getfragment(EDITDATA *p, int pos, char *buf, int *fcolor, int *bcolor,
        HFONT *font, int *col, int line)
    {
        int count = 0;
        int found = FALSE;
        int attribs;
        int color = C_TEXT;
        int selecting;
        int start, end;
		int matched = FALSE;
        int taboffs = p->leftshownindex % p->cd->tabs;
		int parsed = lineParsed(p, line);
        if (p->cd->nosel)
            start = end = 0;
        else if (p->selstartcharpos <= p->selendcharpos)
        {
            start = p->selstartcharpos;
            end = p->selendcharpos;
        }
        else
        {
            start = p->selendcharpos;
            end = p->selstartcharpos;
        }
        selecting = pos >= start && pos < end;
        while (TRUE)
        {
            if (pos >= p->cd->textlen || p->cd->text[pos].ch == '\n')
				break;
			if (p->cd->text[pos].ch == '\f')
			{
				buf[count++] = '\f';
				pos++;
                break;
			}
            if (selecting)
            {
                if (pos >= end)
                    break;
            }
            else
            {
				if (p->matchingEnd && parsed)
					if (pos == p->matchingStart || pos == p->matchingEnd)
					{	
						buf[count++] = p->cd->text[pos++].ch;
						(*col)++;
						matched = TRUE;
						break;
					}
                if (pos >= start && pos < end)
                    break;
            }
            if (found)
            {
                if (!selecting && parsed)
                {
                    if (p->cd->text[pos].effect != attribs)
                        break;
                    if (!(attribs &CFE_AUTOCOLOR) && p->cd->text[pos].Color
                        != color)
                        break;
                }
            }
            if (p->cd->text[pos].ch == '\t')
            {
                int newpos = ((*col + p->cd->tabs) / p->cd->tabs) *p->cd->tabs;
                int i;
                for (i =  *col; i < newpos; i++)
                    buf[count++] = ' ';
                pos++;
                *col = newpos;
				found = TRUE;
            }
            else
            {
                if (p->cd->text[pos].ch == ' ')
                {
                    buf[count++] = ' ';
                    pos++;
                    (*col)++;
					found = TRUE;
                }
                else
                {
                    found = TRUE;
                    attribs = p->cd->text[pos].effect;
                    color = p->cd->text[pos].Color;
                    buf[count++] = p->cd->text[pos++].ch;
                    (*col)++;
                }
            }
			if (p->matchingEnd && parsed)
				if (pos == p->matchingStart || pos == p->matchingEnd)
				{
					break;
				}
        }
		if (!parsed)
		{
            *fcolor = 0x80808080;
			if (p->cd->defbackground == C_SYS_WINDOWBACKGROUND)
			{
				*bcolor = GetSysColor(COLOR_WINDOW);
			}
			else
	            *bcolor = *(colors[color >> 4]);
		}
        else if (selecting)
        {
            *fcolor = GetSysColor(COLOR_HIGHLIGHTTEXT);
            *bcolor = GetSysColor(COLOR_HIGHLIGHT);
        }
        else
        {
            if (attribs &CFE_AUTOCOLOR)
                *fcolor = colors[p->cd->defforeground];
            else
                *fcolor = *(colors[color & 0xf]);
			if (p->cd->defbackground == C_SYS_WINDOWBACKGROUND)
			{
				*bcolor = GetSysColor(COLOR_WINDOW);
			}
			else
	            *bcolor = *(colors[color >> 4]);
        }
        switch (attribs &~CFE_AUTOCOLOR)
        {
            case CFE_BOLD:
                *font = p->cd->hBoldFont;
                break;
            case CFE_ITALIC:
                *font = p->cd->hItalicFont;
                if (pos >= p->cd->textlen || p->cd->text[pos].ch == '\n')
                    buf[count++] = ' ';
                break;
            case CFE_BOLD | CFE_ITALIC: default:
                *font = p->cd->hFont;
                break;
        }
		if (matched)
		{
			*font = p->cd->hBoldFont;
			*fcolor = keywordColor;
		}
        buf[count] = 0;
        return pos;
    }
    /**********************************************************************
     * EditPaint is the paint procedure for the window.  It selectively
     * paints only those lines that have been marked as changed
     **********************************************************************/
    void EditPaint(HWND hwnd, EDITDATA *p)
    {
        char buf[4000];
        PAINTSTRUCT ps;
        HDC dc;
        RECT r, b1, client;
        int lines, i, pos;
		int baseline;
        HBRUSH selBrush = CreateSolidBrush(GetSysColor(COLOR_HIGHLIGHT));
		HPEN columnbarPen =	CreatePen(PS_SOLID, 0, COLUMNBAR_COLOR);
        ClientArea(hwnd, p, &r);
        GetClientRect(hwnd, &client);
        lines = r.bottom / p->cd->txtFontHeight;
        dc = BeginPaint(hwnd, &ps);
        SetBkMode(dc, OPAQUE);
        pos = p->textshowncharpos;
		baseline = LineFromChar(p, pos)+1;
        for (i = 0; i < lines; i++)
        {
            int col = 0, leftcol = 0;
            r.top = i * p->cd->txtFontHeight;
            r.bottom = r.top + p->cd->txtFontHeight;
            if (IntersectRect(&b1, &r, &ps.rcPaint))
            {
                while (leftcol < p->leftshownindex && p->cd->text[pos].ch != '\n'
					&& p->cd->text[pos].ch != '\f'
                    && pos < p->cd->textlen)
                {
                    if (p->cd->text[pos].ch == '\t')
                    {
                        int v = leftcol;
                        v = v + p->cd->tabs;
                        v = (v / p->cd->tabs) *p->cd->tabs;
                        leftcol = v;
                        if (v > p->leftshownindex)
                            break;
                        pos++;
                    }
                    else
                    {
                        pos++;
                        leftcol++;
                    }
                }
                if (leftcol > p->leftshownindex)
                    leftcol = p->leftshownindex;
				while (p->cd->text[pos].ch != '\n' && pos < p->cd->textlen)
                {
                    int selection = FALSE;
                    int fcolor, bcolor;
                    HFONT font;
                    pos = getfragment(p, pos, buf, &fcolor, &bcolor, &font,
                        &leftcol, baseline + i);
					if (buf[0] == '\f')
					{
						strcpy(buf,"+--------- Page Break ---------+");
					}
                    SetTextColor(dc, fcolor);
   	                SetBkColor(dc, bcolor);
       	            SelectObject(dc, font);
           	        TextOut(dc, col, r.top, buf, strlen(buf));
               	    col += (p->cd->txtFontWidth) *strlen(buf);
                }
            }
            while (pos < p->cd->textlen)
            {
                if (p->cd->text[pos].ch == '\n')
                    break;
                pos++;
            }
            r.left = col;
            if (pos < p->selendcharpos && pos > p->selstartcharpos
                    || pos >= p->selendcharpos && pos < p->selstartcharpos) {
                FillRect(dc, &r, selBrush);
            }
            else
                FillRect(dc, &r, p->cd->hbrBackground);
			if (p->leftshownindex < COLUMNBAR_COLUMNS)
			{
				HPEN oldpen = SelectObject(dc, columnbarPen);
				int x = (COLUMNBAR_COLUMNS - p->leftshownindex) * (p->cd->txtFontWidth);
				MoveToEx(dc, x, r.top, NULL);
						 
				LineTo(dc, x, r.bottom);
				SelectObject(dc, oldpen);
			}
            r.left = 0;
            if (pos >= p->cd->textlen)
                break;
            pos++;
        }
        if (r.bottom < client.bottom)
        {
            r.top = r.bottom;
            r.bottom = client.bottom;
            FillRect(dc, &r, p->cd->hbrBackground);
			if (p->leftshownindex < COLUMNBAR_COLUMNS)
			{
				HPEN oldpen = SelectObject(dc, columnbarPen);
				int x = (COLUMNBAR_COLUMNS - p->leftshownindex) * (p->cd->txtFontWidth);
				MoveToEx(dc, x, r.top, NULL);
						 
				LineTo(dc, x, r.bottom);
				SelectObject(dc, oldpen);
			}
        }
        EndPaint(hwnd, &ps);
        if (p->cd->sendchangeonpaint)
        {
            p->cd->sendchangeonpaint = FALSE;
            SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)(EN_CHANGE | 
                (GetWindowLong(hwnd, GWL_ID) << 16)), (LPARAM)hwnd);
        }
        DeleteObject(selBrush);
		DeleteObject(columnbarPen);
    }
    /**********************************************************************
     * eol indexes to the last character in a line
     **********************************************************************/
    void eol(HWND hwnd, EDITDATA *p)
    {
        int pos;
        pos = p->selendcharpos;
        while (pos < p->cd->textlen && p->cd->text[pos].ch != '\n')
            pos++;
        if (!p->cd->selecting)
            p->selendcharpos = p->selstartcharpos = pos;
        else
            p->selendcharpos = pos;
        ScrollCaretIntoView(hwnd, p, FALSE);
        MoveCaret(hwnd, p);
    }
    /**********************************************************************
     * sol indexes to the first character in a line
     **********************************************************************/
    void sol(HWND hwnd, EDITDATA *p)
    {
        int pos;
        int encns = FALSE;
        pos = p->selendcharpos;
        if (pos && p->cd->text[pos - 1].ch == '\n')
        {
            pastspace: 
            while (p->cd->text[pos].ch == ' ' || p->cd->text[pos].ch == '\t')
                pos++;
        }
        else
        {
            while (pos && p->cd->text[pos - 1].ch != '\n')
            {
                if (pos && p->cd->text[pos - 1].ch != ' ' && p->cd->text[pos - 1].ch != '\t')
                    encns = TRUE;
                pos--;
            }
            if (encns)
                goto pastspace;
        }
        if (!p->cd->selecting)
            p->selendcharpos = p->selstartcharpos = pos;
        else
            p->selendcharpos = pos;
        ScrollCaretIntoView(hwnd, p, FALSE);
        MoveCaret(hwnd, p);


    }
    /**********************************************************************
     * left moves the cursor left one character
     **********************************************************************/
    void left(HWND hwnd, EDITDATA *p)
    {
        int pos;
        pos = p->selendcharpos;
        if (pos)
		{
            pos--;
			if (pos && p->cd->text[pos-1].ch == '\f')
	            pos--;
		}
        if (!p->cd->selecting)
            p->selendcharpos = p->selstartcharpos = pos;
        else
            p->selendcharpos = pos;
        MoveCaret(hwnd, p);
        ScrollCaretIntoView(hwnd, p, FALSE);
    }
    /**********************************************************************
     * right moves the cursor right one character
     **********************************************************************/
    void right(HWND hwnd, EDITDATA *p)
    {
        int pos;
        pos = p->selendcharpos;
        if (pos < p->cd->textlen)
		{
			if (p->cd->text[pos].ch == '\f')
	            pos++;
	        if (pos < p->cd->textlen)
				pos++;
		}
        if (!p->cd->selecting)
            p->selendcharpos = p->selstartcharpos = pos;
        else
            p->selendcharpos = pos;
        MoveCaret(hwnd, p);
        ScrollCaretIntoView(hwnd, p, FALSE);
    }
    void leftword(HWND hwnd, EDITDATA *p)
    {
        int pos;
        int flag = 0;
        pos = p->selendcharpos;
        if (pos)
            pos--;
        while (pos && isspace(p->cd->text[pos].ch))
            pos--;
        while (pos && (isalnum(p->cd->text[pos].ch) || p->cd->text[pos].ch == '_'))
        {
            flag = 1;
            pos--;
        }
        if (pos && flag)
            pos++;
        if (!p->cd->selecting)
            p->selendcharpos = p->selstartcharpos = pos;
        else
            p->selendcharpos = pos;
        MoveCaret(hwnd, p);
        ScrollCaretIntoView(hwnd, p, FALSE);
    }
    void rightword(HWND hwnd, EDITDATA *p)
    {
        int pos;
        int flag = 0;
        pos = p->selendcharpos;
        while (pos < p->cd->textlen && (isalnum(p->cd->text[pos].ch) || p->cd->text[pos].ch
            == '_'))
        {
            flag = TRUE;
            pos++;
        }
        while (pos < p->cd->textlen && isspace(p->cd->text[pos].ch))
        {
            flag = TRUE;
            pos++;
        }
        if (!flag && pos < p->cd->textlen)
        {
            pos++;
            while (pos < p->cd->textlen && isspace(p->cd->text[pos].ch))
            {
                pos++;
            }
        }
        if (!p->cd->selecting)
            p->selendcharpos = p->selstartcharpos = pos;
        else
            p->selendcharpos = pos;
        MoveCaret(hwnd, p);
        ScrollCaretIntoView(hwnd, p, FALSE);
    }
    /**********************************************************************
     * AutoTimeProc fires When the cursor is moved out of the window during 
     * a select operation.  It is used to keep the text scrolling.
     **********************************************************************/
    void CALLBACK AutoTimeProc(HWND hwnd, UINT message, UINT event, DWORD timex)
    {
        EDITDATA *p = (EDITDATA*)GetWindowLong(hwnd, 0);
        RECT r;
        ClientArea(hwnd, p, &r);
        scrollup(hwnd, p, p->cd->autoscrolldir);
        if (p->cd->autoscrolldir < 0)
        {
            p->selendcharpos = p->textshowncharpos;
        }
        else
        {
            int lines, pos = p->textshowncharpos;
            lines = r.bottom / p->cd->txtFontHeight;
            while (lines && pos < p->cd->textlen)
            {
                if (p->cd->text[pos].ch == '\n')
                    lines--;
                pos++;
            }
            p->selendcharpos = pos;
        }
        MoveCaret(hwnd, p);
    }
    /**********************************************************************
     * StartAutoScroll is called to start the timer which keeps the screen
     * scrolling while the cursor is out of the window during a select
     **********************************************************************/
    void StartAutoScroll(HWND hwnd, EDITDATA *p, int dir)
    {
        if (!p->cd->autoscrolldir)
        {
            p->cd->autoscrolldir = dir;
            SetTimer(hwnd, 1, 120, AutoTimeProc);
        }
    }
    /**********************************************************************
     * EndAutoScroll stops the above timer
     **********************************************************************/
    void EndAutoScroll(HWND hwnd, EDITDATA *p)
    {
        if (p->cd->autoscrolldir)
        {
            KillTimer(hwnd, 1);
            p->cd->autoscrolldir = 0;
        }
    }
    /**********************************************************************
     * HilightWord selects the word under the cursor
     **********************************************************************/
    void HilightWord(HWND hwnd, EDITDATA *p)
    {
        if (keysym(p->cd->text[p->selstartcharpos].ch))
        {
            while (p->selstartcharpos && keysym(p->cd->text[p->selstartcharpos -
                1].ch))
                p->selstartcharpos--;
            while (p->selendcharpos < p->cd->textlen && keysym(p->cd->text[p
                ->selendcharpos].ch))
                p->selendcharpos++;
        }
    }
    /**********************************************************************
     * undo_pchar undoes a delete character operation
     **********************************************************************/
    void undo_pchar(HWND hwnd, EDITDATA *p, int ch)
    {
        if (ch == CRLF_DUMMY)
        {
            insertcr(hwnd, p);
            if (p->cd->colorize)
                FormatBufferFromScratch(p->cd->text, p->selstartcharpos - 1, p
                    ->selstartcharpos, p->cd->colorize);
        }
        else
        {
            insertchar(hwnd, p, ch);
            if (p->cd->colorize)
                FormatLine(hwnd, p->cd->text, p->cd->colorize);
        }
    }
    /**********************************************************************
     * doundo is the primary routine to traverse the undo list and perform
     * an undo operation
     **********************************************************************/

    int doundo(HWND hwnd, EDITDATA *p)
    {
        int rv = 0;
        if (p->cd->undohead != p->cd->undotail || p->cd->undolist[p->cd->undotail].undotemp)
        {
            UNDO *u;
            int oldinsert = p->cd->inserting;
            int start, end;
            char *s;
            int x;
			if (p->cd->undohead == p->cd->undotail)
			{
				x = p->cd->undohead;
			}
			else
			{
	            x = p->cd->undohead - 1;
    	        if (x < 0)
        	        x += UNDO_MAX;
			}
            u = &p->cd->undolist[x];
            p->cd->undoing++;
            p->cd->inserting = TRUE;
            switch (u->type)
            {
                case UNDO_INSERT:
					if (u->undotemp == 0)
						u->undotemp = u->len;
                    start = end = u->undotemp;
                    start--;
                    if (keysym(u->data[start]))
                        while (start > 0 && keysym(u->data[start-1]))
                            start--;
                    p->selstartcharpos = u->postselstart - u->len + start;
                    p->selendcharpos = u->postselstart - u->len + end;
					u->undotemp = start;
                    if (u->undotemp <= 0)
                    {
                        p->cd->modified = u->modified;
                        p->cd->undohead = x;
                    }
                    Replace(hwnd, p, "", 0);
                    break;
                case UNDO_DELETE:
                    start = u->undotemp;
                    if (keysym(u->data[start]))
	                    while (keysym(u->data[start]) && u->len > start)
	                    {
	                        p->selstartcharpos = p->selendcharpos = u->preselstart;
	                        undo_pchar(hwnd, p, u->data[start++]);
	                    }
                    else
                    {
                        p->selstartcharpos = p->selendcharpos = u->preselstart;
                        undo_pchar(hwnd, p, u->data[start++]);
                    }
					u->undotemp = start;
                    if (u->len <= start)
                    {
						u->undotemp = 0;
                        p->cd->modified = u->modified;
                        p->cd->undohead = x;
                    }
                    p->selstartcharpos = p->selendcharpos = u->preselstart;
                    break;
                case UNDO_BACKSPACE:
                    start = u->undotemp;
                    p->selstartcharpos = p->selendcharpos = u->postselstart + start;
                    if (keysym(u->data[start]))
	                    while (keysym(u->data[start]) && u->len > start)
	                    {
	                        undo_pchar(hwnd, p, u->data[start++]);
	                    }
                    else
                    {
                        undo_pchar(hwnd, p, u->data[start++]);
                        if (u->data[start] == '\n')
                        {
                            undo_pchar(hwnd, p, u->data[start++]);
                        }
                    }
					u->undotemp = start;
                    if (u->len <= start)
                    {
						u->undotemp = 0;
                        p->cd->modified = u->modified;
                        p->cd->undohead = x;
                    }
                    break;
                case UNDO_MODIFY:
                    p->cd->inserting = FALSE;
                    start = u->undotemp;
                    if (keysym(u->data[start]))
	                    while (keysym(u->data[start]) && u->len > start)
	                    {
							int n;
	                        p->selstartcharpos = p->selendcharpos = u->postselstart - start-1;
							n = p->cd->text[p->selstartcharpos].ch;
	                        undo_pchar(hwnd, p, u->data[start]);
							u->data[start++] = n;
	                    }
                    else
                    {
						int n;
                        p->selstartcharpos = p->selendcharpos = u->postselstart - start-1;
						n = p->cd->text[p->selstartcharpos].ch;
                        undo_pchar(hwnd, p, u->data[start]);
						u->data[start++] = n;
                        if (u->data[start] == '\n')
                        {
                            p->selstartcharpos = p->selendcharpos = u
                                ->postselstart - start;
							n = p->cd->text[p->selstartcharpos].ch;
                            undo_pchar(hwnd, p, u->data[start]);
							u->data[start++] = n;
                        }
                    }
					u->undotemp = start;
                    if (u->len <= start)
                    {
						u->undotemp = 0;
                        p->cd->modified = u->modified;
                        p->cd->undohead = x;
                    }
                    p->selstartcharpos = p->selendcharpos = u->postselstart - start;
                    break;
                case UNDO_INSERTSELECTION:
                    p->selstartcharpos = u->postselstart;
                    p->selendcharpos = u->postselend;
                    Replace(hwnd, p, "", 0);
                    p->cd->modified = u->modified;
                    p->cd->undohead = x;
                    p->selstartcharpos = u->preselstart;
                    p->selendcharpos = u->preselend;
                    break;
                case UNDO_DELETESELECTION:
                    p->selstartcharpos = u->postselstart;
                    p->selendcharpos = u->postselend;
                    Replace(hwnd, p, u->data, u->len);
                    p->cd->modified = u->modified;
                    p->cd->undohead = x;
                    p->selstartcharpos = u->preselstart;
                    p->selendcharpos = u->preselend;
                    break;
                case UNDO_CASECHANGE:
                    p->selstartcharpos = u->postselstart;
                    p->selendcharpos = u->postselend;
                    p->cd->modified = u->modified;
                    p->cd->undohead = x;
                    for (start = 0; start < u->len; start++)
					{
						int n = p->cd->text[p->selstartcharpos + start].ch;
                        p->cd->text[p->selstartcharpos + start].ch = u->data[start];
						u->data[start] = n;
					}
                    p->selstartcharpos = u->preselstart;
                    p->selendcharpos = u->preselend;
                    break;
                case UNDO_AUTOBEGIN:
    	            p->cd->undohead = x;
        	        while (p->cd->undohead != p->cd->undotail && !doundo(hwnd, p))
            	        ;
                    break;
                case UNDO_AUTOCHAINBEGIN:
                    p->cd->undohead = x;
                    while (p->cd->undohead != p->cd->undotail && !doundo(hwnd, p))
                        ;
                    doundo(hwnd, p); 
                        // will undo things recursively if there are more auto-begins
                    break;
                case UNDO_AUTOEND:
				case UNDO_AUTOCHAINEND:
                    p->cd->undohead = x;
                    p->selstartcharpos = u->preselstart;
                    p->selendcharpos = u->preselend;
					p->textshowncharpos = u->charpos;
                    p->cd->modified = u->modified;
                    rv = 1;
                    break;
            }
            p->cd->inserting = oldinsert;
            p->cd->undoing--;
            p->cd->sendchangeonpaint = TRUE;
            InvalidateRect(hwnd, 0, 0);
            ScrollCaretIntoView(hwnd, p, FALSE);
        }
        return rv;
    }
    /**********************************************************************
     * doundo is the primary routine to traverse the undo list and perform
     * an undo operation
     **********************************************************************/

    int doredo(HWND hwnd, EDITDATA *p)
    {
        int rv = 0;
        int x = p->cd->undohead - 1;
        if (x < 0)
           x += UNDO_MAX;
        if (p->cd->undohead != p->cd->redotop || p->cd->undotail != p->cd->undohead &&
			p->cd->undolist[x].undotemp)
        {
            UNDO *u;
            int oldinsert = p->cd->inserting;
            int start, end;
            char *s;
			char buf[512];
			if (p->cd->undohead == p->cd->redotop)
			{
				x = p->cd->undohead;
			}
			else
			{
	            x = p->cd->undohead + 1;
    	        if (x >= UNDO_MAX)
        	        x -= UNDO_MAX;
			}
            u = &p->cd->undolist[p->cd->undohead];
            p->cd->undoing++;
            p->cd->inserting = TRUE;
            switch (u->type)
            {
                case UNDO_INSERT:
                    p->selstartcharpos = p->selendcharpos = u->preselstart + u->undotemp;
					start = u->undotemp;
                    if (keysym(u->data[start]))
	                    while (keysym(u->data[start]) && u->len > start)
	                    {
	                        undo_pchar(hwnd, p, u->data[start++]);
	                    }
                    else
                    {
                        undo_pchar(hwnd, p, u->data[start++]);
                        if (u->data[start] == '\n')
                        {
                            undo_pchar(hwnd, p, u->data[start++]);
                        }
                    }
					u->undotemp = start;
                    if (u->len <= u->undotemp)
                    {
						u->undotemp = 0;
                        p->cd->modified = p->cd->undolist[x].modified;
                        p->cd->undohead = x;
                    }
                    p->selstartcharpos = p->selendcharpos = u->preselstart + start;
                    break;
                case UNDO_DELETE:
					if (u->undotemp == 0)
						u->undotemp = u->len;
					start = u->undotemp;
                    if (keysym(u->data[start-1]))
					{
	                    while (end >0 && keysym(u->data[start-1]))
	                    {
							start--;
	                    }
					}
                    else
                    {
						start--;
                    }
                    p->selstartcharpos = u->preselstart ;
					p->selendcharpos = u->preselstart + u->undotemp - start;
					Replace(hwnd, p, "", 0);
					u->undotemp = start;
                    if (u->undotemp == 0)
                    {
						u->undotemp = 0;
                        p->cd->modified = p->cd->undolist[x].modified;
                        p->cd->undohead = x;
                    }
                    break;
                case UNDO_BACKSPACE:
					if (u->undotemp == 0)
						u->undotemp = u->len;
					start = u->undotemp;
                    if (keysym(u->data[start-1]))
					{
	                    while (end >0 && keysym(u->data[start-1]))
	                    {
							start--;
	                    }
					}
                    else
                    {
						start--;
                    }
                    p->selendcharpos = u->preselstart - u->len + u->undotemp-1 ;
					p->selstartcharpos = p->selendcharpos + u->undotemp - start;
					Replace(hwnd, p, "", 0);
					u->undotemp = start;
                    if (u->undotemp == 0)
                    {
						u->undotemp = 0;
                        p->cd->modified = p->cd->undolist[x].modified;
                        p->cd->undohead = x;
                    }
                    break;
                case UNDO_MODIFY:
                    p->cd->inserting = FALSE;
					if (u->undotemp == 0)
						u->undotemp = u->len;
                    start = u->undotemp;
					p->selendcharpos = p->selstartcharpos = u->preselstart + u->len - start;
					
                    if (keysym(u->data[start-1]))
	                    while (start > 0 && keysym(u->data[start-1]))
    	                {
							int n = p->cd->text[p->selstartcharpos].ch;
                    	    undo_pchar(hwnd, p, u->data[--start]);
							u->data[start] = n;
                    	}
                    else
                    {
						int n = p->cd->text[p->selstartcharpos].ch;
                        undo_pchar(hwnd, p, u->data[--start]);
						u->data[start] = n;
                        if (u->data[start-1] == '\n')
                        {
							n = p->cd->text[p->selstartcharpos].ch;
                            undo_pchar(hwnd, p, u->data[--start]);
							u->data[start] = n;
                        }
                    }
					u->undotemp = start;
                    if (start == 0)
                    {
                        p->cd->modified = p->cd->undolist[x].modified;
                        p->cd->undohead = x;
                    }
                    break;
                case UNDO_INSERTSELECTION:
                    p->selstartcharpos = u->preselstart;
                    p->selendcharpos = u->preselend;
                    Replace(hwnd, p, u->data, u->len);
                    p->cd->modified = p->cd->undolist[x].modified;
                    p->cd->undohead = x;
                    p->selstartcharpos = u->postselstart;
                    p->selendcharpos = u->postselend;

                    break;
                case UNDO_DELETESELECTION:
                    p->selstartcharpos = u->preselstart;
                    p->selendcharpos = u->preselend;
                    Replace(hwnd, p, "", 0);
                    p->cd->modified = p->cd->undolist[x].modified;
                    p->cd->undohead = x;
                    p->selstartcharpos = u->postselstart;
                    p->selendcharpos = u->postselend;
                    break;
                case UNDO_CASECHANGE:
                    p->selstartcharpos = u->preselstart;
                    p->selendcharpos = u->preselend;
                    p->cd->modified = p->cd->undolist[x].modified;
                    p->cd->undohead = x;
                    for (start = 0; start < u->len; start++)
					{
						int n = p->cd->text[p->selstartcharpos + start].ch;
                        p->cd->text[p->selstartcharpos + start].ch = u->data[start];
						u->data[start] = n;
					}
                    p->selstartcharpos = u->postselstart;
                    p->selendcharpos = u->postselend;
                    break;
                case UNDO_AUTOEND:
    	            p->cd->undohead = x;
        	        while (p->cd->undohead != p->cd->redotop && !doredo(hwnd, p))
            	        ;
                    break;
                case UNDO_AUTOCHAINEND:
                    p->cd->undohead = x;
                    while (p->cd->undohead != p->cd->redotop && !doredo(hwnd, p))
                        ;
                    doredo(hwnd, p); 
                        // will undo things recursively if there are more auto-ends
                    break;
                case UNDO_AUTOBEGIN:
				case UNDO_AUTOCHAINBEGIN:
                    p->cd->undohead = x;
                    p->cd->modified = p->cd->undolist[x].modified;
                    p->selstartcharpos = u->postselstart;
                    p->selendcharpos = u->postselend;
					p->textshowncharpos = u->charpos;
                    rv = 1;
                    break;
            }
            p->cd->inserting = oldinsert;
            p->cd->undoing--;
            p->cd->sendchangeonpaint = TRUE;
            InvalidateRect(hwnd, 0, 0);
            ScrollCaretIntoView(hwnd, p, FALSE);
        }
        return rv;
    }
	void verticalCenter(HWND hwnd, EDITDATA *p)
	{
		RECT bounds;
		int rows, pos, lines=0;
		ClientArea(hwnd, p, &bounds);
		rows = (bounds.bottom - bounds.top) / p->cd->txtFontHeight;
		rows = rows ; /* find center */
		if (p->selstartcharpos <= p->selendcharpos)
			pos = p->selendcharpos;
		else
			pos = p->selstartcharpos;
		while(--pos > p->textshowncharpos)
		{
			if (p->cd->text[pos].ch == '\n')
				lines++;
		}

		pos = p->textshowncharpos;
		
		lines = rows/2 - lines ;
		
		if (lines > 0)
		{
			while (lines > 0 && pos > 0)
			{
				pos--;
				if (p->cd->text[pos].ch == '\n')
					lines--;
			}
			if (pos)
				pos++;
		}
		else
		{
			while (lines < 0 && p->cd->text[pos].ch)
			{
				if (p->cd->text[pos].ch == '\n')
					lines++;
				pos++;
			}
		}
        p->textshowncharpos = pos;
        InvalidateRect(hwnd, 0, 0);
		MoveCaret(hwnd, p);
	}
    void upperlowercase(HWND hwnd, EDITDATA *p, int ucase)
    {
        int i;
        int s, e;
        UNDO *u;
        if (p->selstartcharpos == p->selendcharpos)
            return ;
        u = undo_casechange(p);
        s = p->selstartcharpos;
        e = p->selendcharpos;
        if (e < s)
        {
            int v = s;
            s = e;
            e = v;
        }
        u->postselstart = s;
        u->postselend = e;
        for (i = s; i < e; i++)
        {
            p->cd->text[i].ch = ucase ? toupper(p->cd->text[i].ch): tolower(p
                ->cd->text[i].ch);
        }
        InvalidateRect(hwnd, 0, 0);
    }
    void SendUpdate(HWND hwnd)
    {
        SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)(EN_UPDATE | 
            (GetWindowLong(hwnd, GWL_ID) << 16)), (LPARAM)hwnd);
    }
    typedef struct
    {
        EDITDATA *ed;
        HWND wnd;
        int creation;
    } CINFO;
    void colorizeThread(CINFO *info)
    {
        if (info->creation)
        {
            ShowWindow(info->wnd, SW_SHOW);
            SendMessage(info->wnd, EM_GETLINECOUNT, 0, 0); // update scroll bar
            info->ed->cd->sendchangeonpaint = TRUE;
            SendUpdate(info->wnd);
        }
        if (info->ed->cd->colorize)
            FormatBuffer(info->ed->cd->text, 0, info->ed->cd->textlen, info->ed->cd
                ->colorize);
        InvalidateRect(info->wnd, 0, 0);
        free(info);
    }
    void FullColorize(HWND hwnd, EDITDATA *p, int creation)
    {
        CINFO *info = calloc(1,sizeof(CINFO));
        if (info)
        {
            DWORD hand;
            info->ed = p;
            info->wnd = hwnd;
            info->creation = creation;
            SetLastError(0);
            CloseHandle(CreateThread(0, 0, (LPTHREAD_START_ROUTINE)
                colorizeThread, (void*)info, 0, &hand));
        }
    }
	struct llist 
	{
		struct llist *next;
		char name[256];
	};
    LRESULT CALLBACK codecompProc(HWND hwnd, UINT iMessage, WPARAM wParam, LPARAM
        lParam)
	{
		static HWND parent;
		static HWND hwndLB;
		static char buf[256];
		static int len;
        static int begin_inserted;
		static int selected;
		static struct llist *stringdata;
        PAINTSTRUCT ps;
        HDC dc;
        char buf2[256];
        EDITDATA *p ;
		int rv;
		DRAWITEMSTRUCT *lpdis;
		MEASUREITEMSTRUCT *lpmis;
		TEXTMETRIC tm;
		HFONT font;
		RECT r;
		LOGFONT lf;
		char *temp;
		int i = selected;
		switch(iMessage)
		{
	        case WM_MEASUREITEM: 
	            lpmis = (LPMEASUREITEMSTRUCT) lParam; 
	 
				dc = GetDC(hwnd);
				font = CreateFontIndirect(&CompleteFont);
				font = SelectObject(dc, font);
                GetTextMetrics(dc, &tm);
				font = SelectObject(dc, font);
				DeleteObject(font);
				ReleaseDC(hwnd, dc);
	            /* Set the height of the list box items. */ 
	 
	            lpmis->itemHeight = tm.tmHeight; 
	            return TRUE; 
	 
	        case WM_DRAWITEM: 
	 
	            lpdis = (LPDRAWITEMSTRUCT) lParam; 
	 
	            /* If there are no list box items, skip this message. */ 
	 
	            /* 
	             * Draw the bitmap and text for the list box item. Draw a 
	
	             * rectangle around the bitmap if it is selected. 
	             */ 
	            switch (lpdis->itemAction) { 
	 
	                case ODA_SELECT: 
	                case ODA_DRAWENTIRE: 
	  					FillRect(lpdis->hDC, &lpdis->rcItem, GetStockObject(WHITE_BRUSH));
						
						temp =  (char *)SendMessage(lpdis->hwndItem, LB_GETITEMDATA, lpdis->itemID, 0);
						strcpy(buf2, temp);
	                    GetTextMetrics(lpdis->hDC, &tm); 
	 
	                    if (lpdis->itemState == ODS_SELECTED) 
						{
							lf = CompleteFont; 
							lf.lfWeight = FW_BOLD;
							font = CreateFontIndirect(&lf);
		 					font = SelectObject(lpdis->hDC, font);
						}
	                    TextOut(lpdis->hDC, 
	                        6, 
	                        (lpdis->rcItem.bottom + lpdis->rcItem.top - tm.tmHeight) / 2,
	                        buf2, 
	                        strlen(buf2)); 

	                    if (lpdis->itemState == ODS_SELECTED) 
						{ 
		 					font = SelectObject(lpdis->hDC, font);
							DeleteObject(font); 
						}
	                    break; 
	 
	                case ODA_FOCUS: 
	 
	                    /* 
	                     * Do not process focus changes. The focus caret 
	
	                     * (outline rectangle) indicates the selection. 
	                     * The Which one? (IDOK) button indicates the final 
	                     * selection. 
	                     */ 
	                    break; 
				}
				return TRUE;
			case WM_CREATE:
				hwndLB = CreateWindow("LISTBOX","", 
									   WS_CHILD | WS_VISIBLE | LBS_NOTIFY |
									   LBS_OWNERDRAWFIXED | WS_VSCROLL |
									   LBS_WANTKEYBOARDINPUT | WS_CLIPSIBLINGS,
									   CW_USEDEFAULT, CW_USEDEFAULT,
									   CW_USEDEFAULT, CW_USEDEFAULT,
									   hwnd, (HMENU)100, hInstance,0);
				
				font = CreateFontIndirect(&CompleteFont);
				SendMessage(hwndLB, WM_SETFONT, (WPARAM)font, FALSE);
				break;
			case WM_DESTROY:
				SendMessage(hwnd, LB_RESETCONTENT, 0, 0);
				DestroyWindow(hwndLB);
				break;
			case WM_PAINT:
		        dc = BeginPaint(hwnd, &ps);
				GetClientRect(hwnd, &r);
				RoundRect(dc, r.left, r.top, r.right, r.bottom, 5, 5);
		        EndPaint(hwnd, &ps);
				return 0;
			case WM_USER:
				parent = (HWND)lParam;
                p= (EDITDATA *)GetWindowLong(parent, 0);
                insertautoundo(p, UNDO_AUTOEND);
				len = 0;
				selected = 0;
                begin_inserted = FALSE;
				break;
			case WM_USER+1:
				selected = SendMessage(hwndLB, LB_GETCURSEL, 0, 0);
				break;
			case WM_SETFOCUS:
				SetFocus(hwndLB);
				break;
            case WM_SHOWWINDOW:
                if (!wParam && !begin_inserted)
                {
                    p= (EDITDATA *)GetWindowLong(parent, 0);
                    begin_inserted = TRUE;
                    insertautoundo(p, UNDO_AUTOBEGIN);
                    if (len == 0)
                        doundo(parent, p);
                }
				else if (wParam)
					ShowWindow(hwndLB, SW_SHOW);
                break ;
			case WM_COMMAND:
				if (HIWORD(wParam) == LBN_KILLFOCUS)
				{
					ShowWindow(hwnd, SW_HIDE); 
				} 
				else if (HIWORD(wParam) == LBN_DBLCLK)
				{
                    PostMessage(hwndLB, WM_KEYDOWN, VK_RETURN, 0);
				}
				else if (HIWORD(wParam) == LBN_SELCHANGE)
				{
					selected = SendMessage(hwndLB, LB_GETCURSEL, 0, 0);
				}
				break;
			case WM_SIZE:
	            MoveWindow(hwndLB, 2, 2, (lParam &65535)-4, (lParam >> 16)-4, 1);
				break;
			case WM_VKEYTOITEM:
				switch(LOWORD(wParam))
				{				
					case VK_RETURN:
						if (selected != LB_ERR)
						{
							EDITDATA *p = (EDITDATA *)GetWindowLong(parent, 0);
							int count, i;
                            begin_inserted = TRUE;
                            insertautoundo(p, UNDO_AUTOBEGIN);
                            doundo(parent, p);

							buf[0] = 0;
							temp =  (char *)SendMessage(hwndLB, LB_GETITEMDATA, selected, 0);
							strcpy(buf, temp);
						    count = strlen(buf);
							Replace(parent, p, buf, count);
							p->selstartcharpos = p->selendcharpos = p->selstartcharpos + count;
							MoveCaret(parent, p);
						}
						// fallthrough
						
					case VK_ESCAPE:
						codecompletemode = FALSE;
						ShowWindow(hwnd, SW_HIDE);
						return -2;
					case VK_UP:
					case VK_DOWN:
					case VK_PRIOR:
					case VK_NEXT:
					case VK_HOME:
					case VK_END:
						len = 0;
						PostMessage(hwnd, WM_USER + 1, 0, 0);
						return -1;
					case VK_BACK:
						if (len)
						{
							len--;
    						buf[len] = 0;
	    					if (len == 0)
							{
								selected = 0;
		    					PostMessage(hwndLB, LB_SETCURSEL, -1, 0);
							}
			    			else
				    			selected = SendMessage(hwnd, LB_SELECTSTRING, -1, (LPARAM)buf);
                        }
                        else
                        {
                            p= (EDITDATA *)GetWindowLong(parent, 0);
                            begin_inserted = TRUE;
                            insertautoundo(p, UNDO_AUTOBEGIN);
                            doundo(parent, p);
							ShowWindow(hwnd, SW_HIDE);
                        }
                        SendMessage(parent, WM_KEYDOWN, VK_BACK, lParam);
						return -2;
					case VK_DELETE:
						len = 0;
						selected = 0;
						buf[len] = 0;
						SendMessage(hwndLB, LB_SETCURSEL, -1, 0);
                        p= (EDITDATA *)GetWindowLong(parent, 0);
                        insertautoundo(p, UNDO_AUTOBEGIN);
                        doundo(parent, p);
                        insertautoundo(p, UNDO_AUTOEND);
						return -2;
					default:
						break;
				}
				return -1;
			case WM_CHARTOITEM:
				wParam = LOWORD(wParam);
				buf[len++] = wParam;
				buf[len] = 0;
                SendMessage(parent, WM_CHAR, wParam, 0);
				selected = SendMessage(hwnd, LB_SELECTSTRING, -1, (LPARAM)buf);
                if (selected != LB_ERR)
                {
					temp =  (char *)SendMessage(hwndLB, LB_GETITEMDATA, selected, 0);
					strcpy(buf2, temp);
                    if (!stricmp(buf, buf2))
                    {
                        PostMessage(hwndLB, WM_KEYDOWN, VK_RETURN, 0);
                    }
                }
				return -1;
			case LB_RESETCONTENT:
				rv = SendMessage(hwndLB, LB_RESETCONTENT, 0, 0);
				while (stringdata)
				{
					struct llist *next = stringdata->next;
					free(stringdata);
					stringdata = next;
				}
				return rv;
			case LB_ADDSTRING:
				{
					struct llist *ll = malloc(sizeof(struct llist)), **find = &stringdata;
					if (ll)
					{
						int i = 0;
						strcpy(ll->name, (char *)lParam);
						while ((*find) && strcmp(ll->name, (*find)->name) > 0)
						{
							i++;
							find = &(*find)->next;
						}
						ll->next = *find;
						*find = ll;
						return SendMessage(hwndLB, LB_INSERTSTRING, i, (LPARAM)ll->name);
					}
				}
				return 0;
			case LB_SELECTSTRING:
			{
				int i = wParam +1;
				do
				{
					temp =  (char *)SendMessage(hwndLB, LB_GETITEMDATA, i, 0);
					if (temp != (char *)LB_ERR)
						if (!strnicmp(temp, (char *)lParam, strlen((char *)lParam)))
						{
	    					SendMessage(hwndLB, LB_SETCURSEL, i, 0);
							return i;
						}
					i++;
				} while (temp != (char *)LB_ERR);
				if (wParam != -1)
				{
					for (i=0; i <= wParam; i++)
					{
						temp =  (char *)SendMessage(hwndLB, LB_GETITEMDATA, selected, 0);
						if (temp != (char *)LB_ERR)
							if (!strnicmp(temp, (char *)lParam, strlen((char *)lParam)))
							{
		    					SendMessage(hwndLB, LB_SETCURSEL, i, 0);
								return i;
							}
					}
				}
		    	SendMessage(hwndLB, LB_SETCURSEL, -1, 0);
				return LB_ERR;
			}
			default:
				if (iMessage >= LB_ADDSTRING && iMessage <= LB_SETCOUNT)
					return SendMessage(hwndLB, iMessage, wParam, lParam);
				break;
				
		}
        return DefWindowProc(hwnd, iMessage, wParam, lParam);
	}
	void GetCompletionPos(HWND hwnd, EDITDATA *p, LPPOINT pt, int width, int height)
	
	{
    	if (posfromchar(hwnd, p, pt, p->selstartcharpos))
		{
			RECT rect, frame;
			GetWindowRect(hwnd, &rect);
			GetFrameWindowRect(&frame);
			pt->x -= width/2;
			pt->x += rect.left;
//			pt->x -= frame.left;
			pt->y += p->cd->txtFontHeight ; //+ GetSystemMetrics(SM_CYCAPTION) * 2;
			pt->y += rect.top ;
//			pt->y -= frame.top;
			if (pt->y + height > frame.bottom - frame.top)
			{
				pt->y -= p->cd->txtFontHeight;
				pt->y -= height;
			}			
		}
	}
	TYPE *find_type(HWND hwnd, EDITDATA *p, int ch)
	{
		TYPE *type = NULL;
		char buf[4096], *q = buf+4095;
		DWINFO *info = (DWINFO *)GetWindowLong(GetParent(hwnd), 0);
	    int lineno = SendMessage(hwnd, EM_EXLINEFROMCHAR, 0, p->selstartcharpos);
		int pos = p->selstartcharpos-1;
		int done = FALSE;
		int wading = FALSE;
		if (!info || !completionEnabled)
			return NULL;
		*q = 0;
		if (ch != '.')
		{
			if (!pos)
				return NULL;
			pos--;
		}
		if (pos <= 0)
			return NULL;
		while (pos && isspace (p->cd->text[pos].ch))
			pos--;
		while (pos && !done)
		{
			switch(p->cd->text[pos].ch)
			{
				case ';':
				case '}':
				case '{':
					done = TRUE;
					break;
				case ']':
					*--q = ']';
					wading++ ;
					break;
				case '[':
					if (wading)
					{
						wading --;
						if (!wading)
							*--q = '[';
					}
					else
						done = TRUE;
					break;
				case ' ':
				case '\t':
				case '\f':
				case '\n':
					if (wading || *q == '-' || *q == '.')
					{
						while (pos && isspace(p->cd->text[pos].ch))
						{
							pos--;
						}
						pos++;
					}
					else
					{
						while (pos && isspace(p->cd->text[pos].ch))
						{
							pos--;
						}
						if (p->cd->text[pos].ch != '.' && p->cd->text[pos].ch != '>')
							done = TRUE;
						else
							pos++;
					}
					break;
				case '>':
					if (pos && p->cd->text[pos-1].ch == '-')
					{
						*--q = '>';
						*--q = '-';
						pos--;
					}
					else
						done = TRUE;
					break;
				case '.':
					if (!wading)
						*--q = '.';
					break;
				default:
					if (!wading)
						if (!isalnum(p->cd->text[pos].ch) && 
							p->cd->text[pos].ch != '_')
							done = TRUE;
						else
							*--q = p->cd->text[pos].ch;
					break ;
			}
			pos--;
		}
		while (*q)
		{
			int indir = 0;
			char name[256], *s = name;
			while (*q && (isalnum(*q) || *q == '_'))
				*s++ = *q++;
			*s = 0;
			if (type == NULL)
				type = parse_lookup_symtype( name, info->dwName, lineno);
			else
			{
				SYM *sym = type->syms;
				while (sym)
				{
					if (!stricmp(sym->name, name))
					{
						break ;
					}
					sym = sym->link;
				}
				if (!sym)
					return NULL;
				type = sym->type;
			}
			if (!type)
				return NULL;
			while (*q == '[' && *(q+1) == ']')
			{
				q += 2;
				(indir)++;
			}
			if (*q)
				if (*q == '.')
					q++;
				else if (*q == '-' && *(q+1) == '>')
				{
					q += 2;
					(indir)++;
				}
				else
					return NULL;
			else
				if (ch == '>')
					(indir)++;
			while (type->type == BT_POINTER && (indir))
			{
				(indir)--;
				type = type->link;
			}
			if (type->type == BT_POINTER || indir)
				return NULL;
			if (type->type == BT_FUNC)
				return NULL;
		}
		return type;
	}
	int codecomplete(HWND hwnd, EDITDATA *p, int ch)
	{
		TYPE *type = find_type(hwnd, p, ch);
		if (type)
		{
			if (!codecompleteBox)
			{
				codecompleteBox = CreateWindowEx(WS_EX_TOPMOST, "xcccodeclass","", 
									   WS_POPUP | WS_CLIPCHILDREN,
									   CW_USEDEFAULT, CW_USEDEFAULT,
									   CW_USEDEFAULT, CW_USEDEFAULT,
									   hwnd,0, hInstance,0);
			}
			if (codecompleteBox)
			{
				int count = 0;
				int max = 0;
				POINT siz1;
				SYM *sym = type->syms;
				int width, height;
				POINT cpos;
				TEXTMETRIC t;
				HDC dc = GetDC(codecompleteBox);
				HFONT font = CreateFontIndirect(&CompleteFont);
				font = SelectObject(dc, font);
                GetTextMetrics(dc, &t);
				SendMessage(codecompleteBox, LB_RESETCONTENT, 0, 0); 
				p->cd->selecting = FALSE;
				width = 0;
				while (sym)
				{
					count++;
					GetTextExtentPoint32(dc, sym->name, strlen(sym->name), &siz1);
					if (siz1.x > width)
						width = siz1.x;
					SendMessage(codecompleteBox, LB_ADDSTRING, 0, (LPARAM)sym->name);
					sym = sym->link;
				}
				font = SelectObject(dc, font);
				DeleteObject(font);
				ReleaseDC(codecompleteBox, dc);
				if (count > 8)
					count = 8;
				if (width < 70)
					width = 70;
				if (count)
				{
					height = t.tmHeight * count;
					width += 10 + GetSystemMetrics(SM_CXVSCROLL) + 2 * GetSystemMetrics(SM_CXFRAME);
					GetCompletionPos(hwnd, p, &cpos, width, height);
                    insertchar(hwnd, p, ch);
					SendMessage(codecompleteBox, WM_USER, 0, (LPARAM)hwnd);
					MoveWindow(codecompleteBox, cpos.x, cpos.y, width+4, height+4, TRUE);
					ShowWindow(codecompleteBox, SW_SHOW);
					codecompletemode = TRUE;
                    return TRUE;
				}
			}
		}
        return FALSE;
	}
	void SetFuncWindowSize(HWND hwnd, HFONT normal, HFONT bold, char *proto, 
						   int arg, int max, int *offsets)
	{
		HDC dc = GetDC(hwnd);
		SIZE size1, size2, size3;
		HFONT oldfont;
		RECT r;
		oldfont = SelectObject(dc, normal);
		GetTextExtentPoint32(dc, proto, offsets[arg], &size1);
		SelectObject(dc, bold);
		GetTextExtentPoint32(dc, proto+ offsets[arg], offsets[arg+1]-offsets[arg], 
							 &size2);
		SelectObject(dc, normal);
		GetTextExtentPoint32(dc, proto+ offsets[arg+1], 
							 strlen(proto)- offsets[arg+1], &size3);
		SelectObject(dc, oldfont);
		ReleaseDC(hwnd, dc);
		GetWindowRect(hwnd, &r);
		MoveWindow(hwnd, r.left, r.top, size1.cx + size2.cx + size3.cx 
				   + 4 + 4 * GetSystemMetrics(SM_CXBORDER), 
				   size1.cy + 4 + 2 * GetSystemMetrics(SM_CYBORDER), TRUE);
	}  
	int getFuncPos(EDITDATA *p, char *funcname)
	{
		int l = p->selstartcharpos;
		int slen = strlen(funcname);
		int count = 0;
		if (p->selstartcharpos != p->selendcharpos)
			return INT_MAX;
		while (l >= 0)
		{
			if (funcname[0] == p->cd->text[l].ch)
			{
				int i;
				for (i=0; i < slen; i++)
				{
					if (funcname[i] != p->cd->text[i+l].ch)
						break;
				}
				if (i >= slen)
					break;
			}
			l--;
		}
		if (l < 0)
			return INT_MAX;
		l += slen;
		while (isspace(p->cd->text[l].ch) && l < p->selstartcharpos)
			l++;
		if (p->cd->text[l].ch != '(')
			return INT_MAX;
		l++;
		while (l < p->selstartcharpos)
		{
			if (p->cd->text[l].ch == ',')
				count++, l++;
			else if (p->cd->text[l].ch == '[')
			{
				slen = 0;
				do
				{
					if (p->cd->text[l].ch == '[')
						slen++;
					else if (p->cd->text[l].ch == ']')
						slen--;
					l++;
				} while (slen && l < p->selstartcharpos);
			}
			else if (p->cd->text[l].ch == '(')
			{
				slen = 0;
				do
				{
					if (p->cd->text[l].ch == '(')
						slen++;
					else if (p->cd->text[l].ch == ')')
						slen--;
					l++;
				} while (slen && l < p->selstartcharpos);				
			}
			else if (p->cd->text[l].ch == ')')
				return INT_MAX;
			else
				l++;
				
		}
		return count;
	}
    LRESULT CALLBACK funcProc(HWND hwnd, UINT iMessage, WPARAM wParam, LPARAM
        lParam)
	{
		static char name[256];
		static char proto[4096];
		static int offsets[400];
		static int argcount, currentArg;
		static HFONT normal, bold, oldfont;
		int offset;
		EDITDATA *p;
		LOGFONT lf;
		TEXTMETRIC t;
        PAINTSTRUCT ps;
		HPEN pen, oldpen;
        HDC dc;
		SIZE size1, size2, size3;
		RECT r;
		switch(iMessage)
		{
			case WM_CREATE:
			
				normal = CreateFontIndirect(&FuncFont);
				lf = FuncFont;
				lf.lfWeight = FW_BOLD;
				bold = CreateFontIndirect(&lf);
				break;
			case WM_USER:
				strcpy(name, (char *)wParam);
				strcpy(proto, (char *)lParam);
				currentArg = 0;
				SetFuncWindowSize(hwnd, normal, bold, proto, 
								  currentArg, argcount, offsets);
				break;
			case WM_USER + 1:
				memcpy(offsets, (int *)lParam, (wParam +1 )* sizeof(int));
				argcount = wParam;
				currentArg = 0;
				SetFuncWindowSize(hwnd, normal, bold, proto, 
								  currentArg, argcount, offsets);
				break;
			case WM_USER + 2:
				currentArg = wParam;
				if (currentArg >= argcount)
					currentArg = argcount - 1;
				SetFuncWindowSize(hwnd, normal, bold, proto, 
								  currentArg, argcount, offsets);
				InvalidateRect(hwnd, 0, 0);
				break;
			case WM_USER + 3:
				if (IsWindowVisible(hwnd))
				{
					currentArg = getFuncPos((EDITDATA *)lParam, name);
					if (currentArg == INT_MAX)
					{
						currentArg = 0;
						ShowWindow(hwnd, SW_HIDE);
					}
					else
					{
						if (currentArg >= argcount)
							currentArg = argcount-1;
						InvalidateRect(hwnd, 0, 0);
					}
				}
				break;
			case WM_PAINT:
		        dc = BeginPaint(hwnd, &ps);
				GetClientRect(hwnd, &r);
				pen = CreatePen(PS_SOLID, 1, 0);
				pen = SelectObject(dc, pen); 
				RoundRect(dc, r.left, r.top, r.right, r.bottom, 5, 5);
				offset = 2 + 2 * GetSystemMetrics(SM_CXBORDER);
				oldfont = SelectObject(dc, normal);
				GetTextExtentPoint32(dc, proto, offsets[currentArg], &size1);
				TextOut(dc, offset, 3, proto, offsets[currentArg]);
				SelectObject(dc, oldfont);

				oldfont = SelectObject(dc, bold);
				GetTextExtentPoint32(dc, proto+ offsets[currentArg], 
									 offsets[currentArg+1]-offsets[currentArg], 
									 &size2);
				TextOut(dc, offset + size1.cx, 3, proto + offsets[currentArg], 
						offsets[currentArg + 1 ] - offsets[currentArg]);
				SelectObject(dc, oldfont);


				oldfont = SelectObject(dc, normal);
				TextOut(dc, offset + size1.cx + size2.cx, 3, 
						proto + offsets[currentArg+1], 
						strlen(proto) - offsets[currentArg + 1]);
				SelectObject(dc, oldfont);
				pen = SelectObject(dc, pen);
				DeleteObject(pen);
		        EndPaint(hwnd, &ps);
				return 0;
		}
        return DefWindowProc(hwnd, iMessage, wParam, lParam);
	}
	void showFunction(HWND hwnd, EDITDATA *p)
	{
		int args;
		int offsets[400];
		char proto[4096];
		char name[256], *q = name;
		int end = p->selstartcharpos;
		int pos = p->selstartcharpos - 1;
		POINT cpos, size;
		p->cd->selecting = FALSE;
		if (pos <= 0 || !completionEnabled)
			return ;
		while (pos && isspace(p->cd->text[pos].ch))
		{
			pos--;
			end--;
		}
		while (pos && (isalnum(p->cd->text[pos].ch)
			    || p->cd->text[pos].ch == '_'))
			pos--;
		pos++;
		while (pos < end)
			*q++ = p->cd->text[pos++].ch;
		*q = 0;
		args = parse_lookup_prototype(proto, name, &offsets);
		if (args)
		{
			HDC dc;
			if (!hwndShowFunc)
			{
				hwndShowFunc = CreateWindowEx(WS_EX_TOPMOST, "xccfuncclass", "",
											WS_POPUP,
											CW_USEDEFAULT, CW_USEDEFAULT,
											CW_USEDEFAULT, CW_USEDEFAULT,
											hwnd, 0, GetModuleHandle(0), 0);
			}
			SendMessage(hwndShowFunc, WM_USER, (WPARAM)name, (LPARAM)proto);
			SendMessage(hwndShowFunc, WM_USER + 1, args, (LPARAM)offsets);
	        dc = GetDC(hwndShowFunc);
			GetTextExtentPoint32(dc, proto, strlen(proto), &size);
			ReleaseDC(hwndShowFunc, dc);
			GetCompletionPos(hwnd, p, &cpos, size.x, size.y);
//						SetParent(codecompleteBox, hwnd);
			MoveWindow(hwndShowFunc, cpos.x, cpos.y, size.x, size.y, TRUE);
			SendMessage(hwndShowFunc, WM_USER + 2, 0, 0);
			ShowWindow(hwndShowFunc, SW_SHOW);
			SetFocus(hwnd);
		}
	}
	static BOOL multilineSelect(EDITDATA *p)
	{
		int st = p->selstartcharpos;
		int en = p->selendcharpos;
		if (st == en)
			return FALSE;
		if (en < st)
		{
			int temp = st;
			st = en;
			en = temp;
		}
		while (st < en)
			if (p->cd->text[st++].ch == '\n')
				return TRUE;
		return FALSE;
	}
    /**********************************************************************
     * exeditProc is the window proc for the edit control
     **********************************************************************/

    LRESULT CALLBACK exeditProc(HWND hwnd, UINT iMessage, WPARAM wParam, LPARAM
        lParam)
    {
		CREATESTRUCT *lpCreate;
        PROJLIST *selectedProject ;
        static char buffer[1024];
        char face[256];
        EDITDATA *p;
        DWINFO *x;
        int stop;
        char *data,  *buf;
        LRESULT rv;
        int i, linecount, start, end, line, chars;
        TOOLTIPTEXT *lpnhead;
        char *types,  *syms;
        int offset, l;
        DEBUG_INFO *dbg;
        VARINFO *var;
        NMHDR nm;
        int charpos;
        RECT r;
        HDC dc;
        HFONT xfont;
        TEXTMETRIC t;
        INTERNAL_CHAR *ic;
        POINTL pt;
        TOOLINFO t1;
        LOGFONT lf;
        charinfo *ci;
        switch (iMessage)
        {
			case WM_TIMER:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
				KillTimer(hwnd, p->cd->reparseTimerID);
				p->cd->reparseTimerID = 0;
				InstallForParse(GetParent(hwnd));
				break;
            case WM_ERASEBKGND:
                return 0;
            case WM_PAINT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                EditPaint(hwnd, p);
                return 0;
            case EM_GETINSERTSTATUS:
                return ((EDITDATA*)GetWindowLong(hwnd, 0))->cd->inserting;
            case WM_NOTIFY:
                lpnhead = (TOOLTIPTEXT*)lParam;
                switch (lpnhead->hdr.code)
                {
                case TTN_SHOW:
                case TTN_POP:
                    break;
                case TTN_NEEDTEXT:
                    // tooltip
                    selectedProject = ProjectFindSelectedEXE(0);
                    if (selectedProject && selectedProject->buildFlags &BF_DEBUGTOOLTIPS)
                    {
                        int level;
                        int ebp;
                        int offset1;
                        int lineno;
                        THREAD *thread;
                        p = (EDITDATA*)GetWindowLong(hwnd, 0);
                        x = (DWINFO*)GetWindowLong(GetParent(hwnd), 0);
                        lineno = TagOldLine(x->dwName, p->cd->ttlineno);
                        offset = GetBreakpointAddress(x->dwName, &lineno, TRUE);
                        var = EvalExpr(&types, &syms, &dbg, offset, &offset,
                            p->cd->ttident, FALSE);
                        if (var)
                        {
                            if (!var->udt)
                            {
                                sprintf(buffer, "%s: ", p->cd->ttident);
                                HintValue(types, var, buffer + strlen
                                    (buffer));
                                lpnhead->lpszText = buffer;
                            }
                            FreeVarInfo(var);
                        }
                    }
                    break;
                }
                break;
            case WM_COMMAND:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                p->cd->selecting = FALSE;
                if (LOWORD(wParam) == IDM_RTLHELP)
                    DoHelp(hwnd, 0);
                if (LOWORD(wParam) == IDM_SPECIFIEDHELP)
                    DoHelp(hwnd, 1);
                break;
            case WM_KEYDOWN:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
				CancelParenMatch(hwnd, p);
                switch (wParam)
                {
				case VK_ESCAPE:
					if (hwndShowFunc)
						ShowWindow(hwndShowFunc, SW_HIDE);
					break;
                case VK_INSERT:
                    if (!p->cd->readonly)
                    {
	                    if (GetKeyState(VK_SHIFT) &0x80000000)
	                    {
	                        SendMessage(hwnd, WM_PASTE, 0, 0);
	                    }
	                    else if (GetKeyState(VK_CONTROL) &0x80000000)
	                    {
	                        SendMessage(hwnd, WM_COPY, 0, 0);
	                    }
						else
						{
	                        p->cd->inserting = !p->cd->inserting;
	                        if (p->hasfocus)
	                        {
	                            SendMessage(hwnd, WM_KILLFOCUS, 0, 0);
	                            SendMessage(hwnd, WM_SETFOCUS, 0, 0);
	                        }
	                        SendMessage(GetParent(hwnd), EN_SETCURSOR, 0, 0);
						}
                    }
                    break;
                case VK_BACK:
                    if (p->selstartcharpos == p->selendcharpos)
                    {
                        if (p->selstartcharpos == 0)
                            break;
                        left(hwnd, p);
                    }
                case VK_DELETE:
                    if (!p->cd->readonly)
                    {
	                    if (GetKeyState(VK_SHIFT) &0x80000000)
	                    {
	                        SendMessage(hwnd, WM_CUT, 0, 0);
	                    }
                        else if (p->selstartcharpos != p->selendcharpos)
                        {
                            Replace(hwnd, p, "", 0);
                            ScrollCaretIntoView(hwnd, p, FALSE);
                        }
                        else
                        {
                            removechar(hwnd, p, wParam == VK_DELETE ?
                                UNDO_DELETE : UNDO_BACKSPACE);
                            if (p->cd->colorize)
                                FormatBufferFromScratch(p->cd->text, p
                                    ->selstartcharpos, p->selstartcharpos, p
                                    ->cd->colorize);
                        }
                        setcurcol(p);
                    }
                    break;
                case VK_RETURN:
                    if (!p->cd->readonly)
                    {
                        insertcr(hwnd, p);
                        insertcrtabs(hwnd, p);
                        if (p->cd->colorize)
                            FormatBufferFromScratch(p->cd->text, p->selstartcharpos
                                - 1, p->selstartcharpos, p->cd->colorize);
                        setcurcol(p);
						if (IsWindowVisible(hwndShowFunc))
							scrollup(hwnd, p, 1);
	                    }
					
                    break;
                case VK_TAB:
                    if (GetKeyState(VK_SHIFT) &0x80000000)
                    {
                        if (!p->cd->readonly)
                        {
                            if (multilineSelect(p))
                                SelectIndent(hwnd, p, FALSE);
                            else
                            {
                                backtab(hwnd, p);
                                drawline(hwnd, p, p->selstartcharpos);
                            }
                        }
                        else
                            left(hwnd, p);
                        setcurcol(p);
                    }
                    else
                    {
                        if (!p->cd->readonly)
                        {
                            if (multilineSelect(p))
                                SelectIndent(hwnd, p, TRUE);
                            else
                            {
                                inserttab(hwnd, p);
                                InsertEndTabs(hwnd, p, FALSE);
                                drawline(hwnd, p, p->selstartcharpos);
                            }
                        }
                        else
                            right(hwnd, p);
                        setcurcol(p);
                    }
                    break;
                case VK_DOWN:
                    upline(hwnd, p, 1);
                    break;
                case VK_UP:
                    upline(hwnd, p,  - 1);
                    break;
                case VK_PRIOR:
                    if (GetKeyState(VK_CONTROL) &0x80000000)
                    {
                        VScrollPos(hwnd, 0, TRUE);
                        p->textshowncharpos = p->selendcharpos = 0;
                        if (!p->cd->selecting)
                            p->selstartcharpos = p->selendcharpos;
                        MoveCaret(hwnd, p);
                        InvalidateRect(hwnd, 0, 0);
                    }
                    else
                    {
                        ClientArea(hwnd, p, &r);
                        i = r.bottom / p->cd->txtFontHeight;
                        upline(hwnd, p, 2-i);
                    }

                    break;
                case VK_NEXT:
                    ClientArea(hwnd, p, &r);
                    if (GetKeyState(VK_CONTROL) &0x80000000)
                    {
                        i = lfchars(p->cd->text, p->textshowncharpos, p->cd->textlen);
                        upline(hwnd, p, i);
                    }
                    else
                    {
                        i = r.bottom / p->cd->txtFontHeight;
                        upline(hwnd, p, i - 2);
                    }
                    break;
                case VK_END:
                    if (GetKeyState(VK_CONTROL) &0x80000000)
                    {
                        i = lfchars(p->cd->text, p->textshowncharpos, p->cd->textlen);
                        upline(hwnd, p, i);
                    }
                    else
                    {
                        eol(hwnd, p);
                        setcurcol(p);
                    }
                    break;
                case VK_HOME:
                    if (GetKeyState(VK_CONTROL) &0x80000000)
                    {
                        VScrollPos(hwnd, 0, TRUE);
                        p->textshowncharpos = 0;
                        if (p->cd->selecting)
                            p->selstartcharpos = 0;
                        else
                            p->selstartcharpos = p->selendcharpos = 0;
                        MoveCaret(hwnd, p);
                        SendUpdate(hwnd);
                        InvalidateRect(hwnd, 0, 0);
                    }
                    else
                    {
                        sol(hwnd, p);
                        setcurcol(p);
                    }
                    break;
                case VK_LEFT:
                    if (GetKeyState(VK_CONTROL) &0x80000000)
                        leftword(hwnd, p);
                    else
                        left(hwnd, p);
                    setcurcol(p);
                    break;
                case VK_RIGHT:
                    if (GetKeyState(VK_CONTROL) &0x80000000)
                        rightword(hwnd, p);
                    else
                        right(hwnd, p);
                    setcurcol(p);
                    break;
                case 'X':
                    if (!p->cd->readonly && GetKeyState(VK_CONTROL) &0x80000000)
                    {
                        SendMessage(hwnd, WM_CUT, 0, 0);
                    }
                    break;
                case 'C':
                    if (GetKeyState(VK_CONTROL) &0x80000000)
                    {
                        SendMessage(hwnd, WM_COPY, 0, 0);
                    }
                    break;
                case 'V':
                    if (!p->cd->readonly && GetKeyState(VK_CONTROL) &0x80000000)
                    {
                        SendMessage(hwnd, WM_PASTE, 0, 0);
                    }
                    break;
				case 'Y':
                    if (!p->cd->readonly)
						if (GetKeyState(VK_CONTROL) &0x80000000)
	                    {
	                        SendMessage(hwnd, WM_REDO, 0, 0);
	                    }
						break;
                case 'Z':
                    if (!p->cd->readonly)
						if (GetKeyState(VK_CONTROL) &0x80000000)
	                    {
	                        SendMessage(hwnd, WM_UNDO, 0, 0);
	                    }
                    break;
                case 'S':
                    if (!p->cd->readonly && GetKeyState(VK_CONTROL) &0x80000000)
                    {
                        if (p->cd->modified)
                            SendMessage(GetParent(hwnd), WM_COMMAND, IDM_SAVE,
                                0);
                    }
                    break;
				case 'T':
                    if (GetKeyState(VK_CONTROL) &0x80000000)
						verticalCenter(hwnd, p);
					break;
				case 'L':
                    if (!(GetKeyState(VK_CONTROL) &0x80000000))
						break;
                    if (!p->cd->readonly && p->cd->inserting)
					{
					    insertautoundo(p, UNDO_AUTOEND);
						insertchar(hwnd,p , '\f');
						insertcr(hwnd,p);	
					    insertautoundo(p, UNDO_AUTOBEGIN);
					}
					break;
				case 'W':
                    if (!(GetKeyState(VK_CONTROL) &0x80000000))
						break;
					InstallForParse(GetParent(hwnd));
					break ;
                case VK_SHIFT:
                    p->cd->selecting = TRUE;
                    break;
                }
                PostMessage(GetParent(hwnd), WM_COMMAND, ID_REDRAWSTATUS, 0);
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
				SendMessage(hwndShowFunc, WM_USER+3, 0, (LPARAM)p);
				FindParenMatch(hwnd, p);
                break;
            case WM_KEYUP:
                if (wParam == VK_SHIFT)
                {
                    p = (EDITDATA*)GetWindowLong(hwnd, 0);
                    p->cd->selecting = FALSE;
                    break;
                }
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
				SendMessage(hwndShowFunc, WM_USER+3, 0, (LPARAM)p);
                break;
            case WM_CHAR:
                if (wParam >= ' ' && wParam < 256)
                {
                    p = (EDITDATA*)GetWindowLong(hwnd, 0);
					CancelParenMatch(hwnd, p);
                    if (!p->cd->readonly)
                    {
						if (wParam == '.' || wParam == '>')
                        {
							if (codecomplete(hwnd, p, wParam))
                                break;
                        }
						else if (wParam == '(')
							showFunction(hwnd, p);
                        insertchar(hwnd, p, wParam);
						if (wParam == '{')
							InsertBeginTabs(hwnd, p);
                        else if (wParam == ' ' || wParam == '}')
                            InsertEndTabs(hwnd, p, wParam == '}');
                        else if (wParam == '#')
                            DeletePound(hwnd, p);
                        if (!p->cd->inserting)
                            SendMessage(hwnd, WM_CLEAR, 0, 0);
                        if (p->cd->colorize)
                            FormatLine(hwnd, p->cd->text, p->cd->colorize);
                        drawline(hwnd, p, p->selstartcharpos);
                    }
                    else
                        right(hwnd, p);
					FindParenMatch(hwnd, p);
                    setcurcol(p);
                    PostMessage(GetParent(hwnd), WM_COMMAND, ID_REDRAWSTATUS, 0)
                        ;
                    break;
                }
                break;
            case WM_SETFOCUS:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                p->hasfocus = TRUE;
                p->hiddenCaret = TRUE;
                CreateCaret(hwnd, 0, p->cd->inserting ? 2 : p->cd->txtFontWidth, p
                    ->cd->txtFontHeight);
                MoveCaret(hwnd, p);
                SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)(EN_SETFOCUS |
                    (GetWindowLong(hwnd, GWL_ID) << 16)), (LPARAM)hwnd);
                return 0;
            case WM_KILLFOCUS:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                p->hasfocus = FALSE;
                DestroyCaret();
                SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)(EN_KILLFOCUS 
                    | (GetWindowLong(hwnd, GWL_ID) << 16)), (LPARAM)hwnd);
                return 0;
            case WM_SETTEXT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                freemem(p);
				p->cd->text = 0;
                p->cd->textlen = 0;
                p->cd->textmaxlen = 0;
                p->textshowncharpos = p->selstartcharpos = p->selendcharpos = 0;
                if (p->cd->colorize)
                    SendMessage(hwnd, EM_SETBKGNDCOLOR, 0, backgroundColor);
                i = strlen((char*)lParam) ;
				
                if (allocmem(p, i * 5) && commitmem(p, i+1))
                {
                    p->cd->textlen = i;
                    for (i = 0; i < p->cd->textlen; i++)
                    {
                        p->cd->text[i].ch = ((char*)lParam)[i];
                        p->cd->text[i].Color = p->cd->defforeground;
                    }
                    FullColorize(hwnd, p, TRUE);
                }
                break;
			case EM_GETEDITDATA:
                return (LRESULT) (EDITDATA*)GetWindowLong(hwnd, 0);
			case EM_UPDATESIBLING:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
				if (lParam <= p->textshowncharpos)
					if (wParam > 0)
						p->textshowncharpos += wParam;
					else
						if (lParam - wParam < p->textshowncharpos)
							p->textshowncharpos += wParam;
						else
							p->textshowncharpos -= p->textshowncharpos - lParam;
				if (lParam <= p->selstartcharpos)
					p->selstartcharpos += wParam;
				if (lParam <= p->selendcharpos)
					p->selendcharpos += wParam;
				InvalidateRect(hwnd, 0, 0);
				break ;
            case WM_CREATE:
                p = (EDITDATA*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                    sizeof(EDITDATA));
                SetWindowLong(hwnd, 0, (int)p);
				p->self = hwnd;
				lpCreate = (LPCREATESTRUCT)lParam;
				if (lpCreate->lpCreateParams)
				{
					p->cd = (COREDATA *)((EDITDATA *)(lpCreate->lpCreateParams))->cd;
				}
				else 
				{
					p->cd = (COREDATA*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
												 sizeof(COREDATA));
	                p->cd->inserting = TRUE;
	                p->cd->tooltip = CreateWindowEx(0, TOOLTIPS_CLASS, NULL, WS_POPUP |
	                    TTS_NOPREFIX | TTS_ALWAYSTIP, CW_USEDEFAULT, CW_USEDEFAULT,
	                    CW_USEDEFAULT, CW_USEDEFAULT, hwnd, 0, hInstance, 0);
	                SetWindowPos(p->cd->tooltip, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE |
	                    SWP_NOACTIVATE | SWP_NOMOVE);
	                //         SendMessage(p->cd->tooltip, TTM_SETDELAYTIME, TTDT_AUTOMATIC, 5000) ;
	                t1.cbSize = sizeof(TOOLINFO);
	                t1.uFlags = 0;
	                t1.hwnd = hwnd;
	                t1.uId = 10000;
	                t1.rect.left = 0;
	                t1.rect.right = 0;
	                t1.rect.top = 8;
	                t1.rect.bottom = 8;
	                t1.hinst = hInstance;
	                t1.lpszText = LPSTR_TEXTCALLBACK;
	                SendMessage(p->cd->tooltip, TTM_ADDTOOL, 0, (LPARAM) &t1);
	                SendMessage(p->cd->tooltip, TTM_ACTIVATE, TRUE, 0);
	
	                xfont = CreateFontIndirect(&EditFont);
	                SendMessage(hwnd, WM_SETFONT, (WPARAM)xfont, 0);
	                p->cd->defbackground = C_BACKGROUND;
	                p->cd->defforeground = C_TEXT;
	                p->cd->hbrBackground = CreateSolidBrush(backgroundColor);
	                p->cd->tabs = tabs;
	                p->cd->leftmargin = EC_LEFTMARGIN;
					
	                getPageSize() ;
	                if (allocmem(p, page_size * 20) && commitmem(p, page_size))
	                {
	                    p->cd->textlen = 0;
	                    for (i = 0; i < p->cd->textlen; i++)
	                    {
	                        p->cd->text[i].Color = p->cd->defforeground;
	                    }
	                }
				}
				{
					EDLIST *l = (EDLIST*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
                    		sizeof(EDLIST));
					if (l)
					{
						EDLIST **l1 = &p->cd->siblings;
						while (*l1) 
						{
							if (p->id <= (*l1)->data->id)
								p->id = (*l1)->data->id + 1;
							l1 = &(*l1)->next ;
						}
						*l1 = l ;
						l->data = p;
					}
				}
                SetScrollRange(hwnd, SB_HORZ, 0, MAX_HSCROLL, TRUE);
                break;
            case WM_SETEDITORSETTINGS:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (p->cd->colorize)
                {
                    SendMessage(hwnd, EM_SETBKGNDCOLOR, 0, backgroundColor);
                    stop = p->cd->tabs *4;
                    SendMessage(hwnd, EM_SETTABSTOPS, 1, (LPARAM) &stop);
                    Colorize(p->cd->text, 0, p->cd->textlen, p->cd->defforeground, 0);
                    FullColorize(hwnd, p, FALSE);
                }
                return 0;
            case WM_DESTROY:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
				{
					EDLIST **l = &p->cd->siblings;
					while (*l)
					{
						if ((*l)->data == p)
						{
							EDLIST *l2 = (*l) ;
							(*l) = (*l)->next;
							HeapFree(GetProcessHeap(), 0, (void *)l2);
							break ;
						}
						l = &(*l)->next;
					}
					if (!(p->cd->siblings))
					{	
		                for (i = 0; i < UNDO_MAX; i++)
		                    free(p->cd->undolist[i].data);
		                DestroyWindow(p->cd->tooltip);
		                DeleteObject(p->cd->hFont);
		                DeleteObject(p->cd->hItalicFont);
		                DeleteObject(p->cd->hBoldFont);
		                DeleteObject(p->cd->hbrBackground);
		                freemem(p);
						HeapFree(GetProcessHeap(), 0, (void*)p->cd);
					}
				}
                HeapFree(GetProcessHeap(), 0, (void*)p);
                break;
            case WM_VSCROLL:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)(EN_VSCROLL | 
                    (GetWindowLong(hwnd, GWL_ID) << 16)), (LPARAM)hwnd);
                switch (LOWORD(wParam))
                {
                case SB_BOTTOM:
                    SendMessage(hwnd, WM_KEYDOWN, VK_NEXT, 0);
                    break;
                case SB_TOP:
                    SendMessage(hwnd, WM_KEYDOWN, VK_PRIOR, 0);
                    break;
                case SB_LINEDOWN:
                    scrollup(hwnd, p, 1);
                    InvalidateRect(hwnd, 0, 0);
                    MoveCaret(hwnd, p);
                    break;
                case SB_LINEUP:
                    scrollup(hwnd, p,  - 1);
                    InvalidateRect(hwnd, 0, 0);
                    MoveCaret(hwnd, p);
                    break;
                case SB_PAGEDOWN:
                    ClientArea(hwnd, p, &r);
                    i = r.bottom / p->cd->txtFontHeight;
                    scrollup(hwnd, p, i - 1);
                    InvalidateRect(hwnd, 0, 0);
                    MoveCaret(hwnd, p);
                    break;
                case SB_PAGEUP:
                    ClientArea(hwnd, p, &r);
                    i = r.bottom / p->cd->txtFontHeight;
                    scrollup(hwnd, p, 1-i);
                    InvalidateRect(hwnd, 0, 0);
                    MoveCaret(hwnd, p);
                    break;
                case SB_ENDSCROLL:
                    return 0;
                case SB_THUMBPOSITION:
                    p = (EDITDATA*)GetWindowLong(hwnd, 0);
                    TrackVScroll(hwnd, p, TRUE);
                    break;
                case SB_THUMBTRACK:
                    p = (EDITDATA*)GetWindowLong(hwnd, 0);
                    TrackVScroll(hwnd, p, FALSE);
                    break;
                default:
                    return 0;
                }
                InvalidateRect(GetParent(hwnd), 0, 0);
                return 0;
            case WM_HSCROLL:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)(EN_HSCROLL | 
                    (GetWindowLong(hwnd, GWL_ID) << 16)), (LPARAM)hwnd);
                switch (LOWORD(wParam))
                {
                case SB_LEFT:
                    p->leftshownindex = 0;
                    break;
                case SB_RIGHT:
                    ClientArea(hwnd, p, &r);
                    i = r.right / p->cd->txtFontWidth;
                    p->leftshownindex = MAX_HSCROLL - i;
                    break;
                case SB_LINELEFT:
                    scrollleft(hwnd, p,  - 1);
                    break;
                case SB_LINERIGHT:
                    scrollleft(hwnd, p, 1);
                    break;
                case SB_PAGERIGHT:
                    ClientArea(hwnd, p, &r);
                    i = r.right / p->cd->txtFontWidth;
                    scrollleft(hwnd, p, i - 1);
                    break;
                case SB_PAGELEFT:
                    ClientArea(hwnd, p, &r);
                    i = r.right / p->cd->txtFontWidth;
                    scrollleft(hwnd, p, 1-i);
                    break;
                case SB_ENDSCROLL:
                    return 0;
                case SB_THUMBPOSITION:
                    p = (EDITDATA*)GetWindowLong(hwnd, 0);
                    TrackHScroll(hwnd, p, TRUE);
                    InvalidateRect(hwnd, 0, 0);
                    MoveCaret(hwnd, p);
                    return 0;
                case SB_THUMBTRACK:
                    p = (EDITDATA*)GetWindowLong(hwnd, 0);
                    TrackHScroll(hwnd, p, FALSE);
                    InvalidateRect(hwnd, 0, 0);
                    MoveCaret(hwnd, p);
                    return 0;
                }
                SetScrollPos(hwnd, SB_HORZ, p->leftshownindex, TRUE);
                SendUpdate(hwnd);
                InvalidateRect(hwnd, 0, 0);
                MoveCaret(hwnd, p);
                return 0;
			case WM_MOUSEWHEEL:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                SendMessage(GetParent(hwnd), WM_COMMAND, (WPARAM)(EN_VSCROLL | 
                    (GetWindowLong(hwnd, GWL_ID) << 16)), (LPARAM)hwnd);
				p->wheelIncrement -= GET_WHEEL_DELTA_WPARAM(wParam);
				if (p->wheelIncrement < 0)
				{
					while (p->wheelIncrement <= - WHEEL_DELTA)
					{
	                    scrollup(hwnd, p,  - 1);
						p->wheelIncrement += WHEEL_DELTA;
					}
				}
				else
				{
					while (p->wheelIncrement >= WHEEL_DELTA)
					{
	                    scrollup(hwnd, p,  1);
						p->wheelIncrement -= WHEEL_DELTA;
					}
				}
                InvalidateRect(hwnd, 0, 0);
                SendUpdate(hwnd);
                MoveCaret(hwnd, p);
                InvalidateRect(GetParent(hwnd), 0, 0);
				break;
            case WM_MOUSEMOVE:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                ClientArea(hwnd, p, &r);
                pt.x = LOWORD(lParam);
                pt.y = (int)(short)HIWORD(lParam);
                if (p->cd->buttondown)
                {
                    if ((signed)pt.y < 0)
                        StartAutoScroll(hwnd, p,  - 1);
                    else if (pt.y > r.bottom)
                    {
                        StartAutoScroll(hwnd, p, 1);
                    }
                    else
                    {
                        EndAutoScroll(hwnd, p);
                        p->selendcharpos = charfrompos(hwnd, p, &pt);
                        InvalidateRect(hwnd, 0, 0);
                    }
                    MoveCaret(hwnd, p);
                }
                RunToolTip(p, hwnd, LOWORD(lParam), HIWORD(lParam));
                RelayToolTipEvent(p, hwnd, iMessage, wParam, lParam);
				SendMessage(hwndShowFunc, WM_USER+3, 0, (LPARAM)p);
                return 0;
            case WM_RBUTTONDOWN:
            case WM_MBUTTONDOWN:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (GetFocus() != hwnd)
                    SetFocus(hwnd);
                nm.code = NM_RCLICK;
                SendMessage(GetParent(hwnd), WM_NOTIFY, 0, (LPARAM) &nm);
                RelayToolTipEvent(p, hwnd, iMessage, wParam, lParam);
                return 0;
            case WM_RBUTTONUP:
            case WM_MBUTTONUP:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                RelayToolTipEvent(p, hwnd, iMessage, wParam, lParam);
                return 0;
            case WM_LBUTTONDOWN:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
				CancelParenMatch(hwnd, p);
                if (GetFocus() != hwnd)
                    SetFocus(hwnd);
                p->cd->selecting = TRUE;
                if (p->selstartcharpos != p->selendcharpos)
                {
                    InvalidateRect(hwnd, 0, 0);
                }
                p->cd->buttondown = TRUE;
                pt.x = LOWORD(lParam);
                pt.y = HIWORD(lParam);
                p->selstartcharpos = p->selendcharpos = charfrompos(hwnd, p,
                    &pt);
                MoveCaret(hwnd, p);
                setcurcol(p);
                SetCapture(hwnd);
                RelayToolTipEvent(p, hwnd, iMessage, wParam, lParam);
				FindParenMatch(hwnd, p);
                break;
            case WM_LBUTTONUP:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (p->cd->buttondown)
                {
                    p->cd->selecting = !!(GetKeyState(VK_SHIFT) &0x80000000);
                    p->cd->buttondown = FALSE;
                    ReleaseCapture();
                    EndAutoScroll(hwnd, p);
                }
                RelayToolTipEvent(p, hwnd, iMessage, wParam, lParam);
                SendMessage(GetParent(hwnd), EN_SETCURSOR, 0, 0);
                break;
            case WM_LBUTTONDBLCLK:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                /* have the pos from the lbutton down */
                HilightWord(hwnd, p);
                drawline(hwnd, p, 0);
                break;
            case WM_WORDUNDERCURSOR:
				if (wParam != 0)
				{
					POINT pos = *(POINT *)wParam;
					int charpos;
					ScreenToClient(hwnd, &pos);
					charpos = SendMessage(hwnd, EM_CHARFROMPOS, 0, (LPARAM) &pos);
	   	            return GetWordFromPos(hwnd, (char*)lParam,  charpos, 0, 0, 0);
				}
				else
				{
	   	            return GetWordFromPos(hwnd, (char*)lParam,  -1, 0, 0, 0);
				}
            case EM_SETBKGNDCOLOR:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (wParam)
                {
                    p->cd->defbackground = C_SYS_WINDOWBACKGROUND;
                }
                else
                {

                    p->cd->defbackground = lParam;
                }
                DeleteObject(p->cd->hbrBackground);
                p->cd->hbrBackground = CreateSolidBrush(lParam);
                break;
            case WM_GETTEXT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (!p->cd->textlen)
                    return 0;
                i = p->cd->textlen;
                if (wParam <= i)
                    i = wParam - 1;
                {
                    INTERNAL_CHAR *x = p->cd->text;
                    while (i--)
                        *((char*)lParam)++ = x++->ch;
                    *(char*)lParam = 0;
                }
                return i;
            case WM_GETTEXTLENGTH:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return p->cd->textlen + 1;
            case EM_EXSETSEL:
                ci = (charinfo*)lParam;
                wParam = ci->min;
                lParam = ci->max;
                // fall through
            case EM_SETSEL:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (wParam == 0 && lParam ==  - 1)
                {
                    p->selstartcharpos = 0;
                    p->selendcharpos = p->cd->textlen;
                }
                else if (wParam ==  - 1)
                {
                    p->selendcharpos = p->selstartcharpos;
                }
                else
                {
                    p->selstartcharpos = wParam;
                    p->selendcharpos = lParam;
                }
                if (!p->cd->nosel)
                {
                    //            VScrollPos(hwnd,LineFromChar(p,p->selstartcharpos),TRUE) ;
                    ScrollCaretIntoView(hwnd, p, FALSE);
                    InvalidateRect(hwnd, 0, 0);
                }
                return 0;
            case EM_GETSEL:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (wParam)
                    *(int*)wParam = p->selstartcharpos;
                if (lParam)
                    *(int*)lParam = p->selendcharpos;
                return  - 1;
            case EM_EXGETSEL:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                ((CHARRANGE*)lParam)->cpMin = p->selstartcharpos;
                ((CHARRANGE*)lParam)->cpMax = p->selendcharpos;
                return 0;
            case EM_HIDESELECTION:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                p->cd->nosel = wParam;
                if (p->selstartcharpos != p->selendcharpos)
                    InvalidateRect(hwnd, 0, 0);
                return 0;
            case EM_REPLACESEL:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                Replace(hwnd, p, (char*)lParam, strlen((char*)lParam));
                return 0;
            case EM_SCROLLCARET:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                ScrollCaretIntoView(hwnd, p, lParam);
                return 0;
            case EM_LINEFROMCHAR:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return LineFromChar(p, wParam);
            case EM_EXLINEFROMCHAR:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return LineFromChar(p, lParam);
            case EM_LINEINDEX:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                rv = 0;
                ic = p->cd->text;
                while (ic < p->cd->text + p->cd->textlen && wParam)
                {
                    if (ic->ch == '\n')
                        wParam--;
                    ic++;
                    rv++;
                }
                return rv;
            case EM_GETLINE:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                rv = 0;
                ic = p->cd->text;
                while (ic < p->cd->text + p->cd->textlen && wParam)
                {
                    if (ic->ch == '\n')
                        wParam--;
                    ic++;
                    rv++;
                }
                if (ic >= p->cd->text + p->cd->textlen)
                    return 0;
                l = *(short*)lParam;
                for (i = 0; i < l && ic < p->cd->text + p->cd->textlen; i++, ic++)
                {
                    *(char*)(lParam + i) = ic->ch;
                    if (ic->ch == '\n')
                    {
                        break;
                    }
                }
                *(char*)(lParam + i) = 0;

                return i;
            case EM_CHARFROMPOS:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return charfrompos(hwnd, p, (POINTL*)lParam);
            case EM_POSFROMCHAR:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                {
                    int x, y;
                    posfromchar(hwnd, p, (POINTL*)wParam, lParam);
                    return 0;
                }

            case EM_GETLINECOUNT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                rv = 0;
                if (!p->cd->text)
                        return 0;
                ic = p->cd->text + 1;
                while (ic->ch)
                {
                    if (ic->ch == '\n')
                        rv++;
                    ic++;
                }
                if (ic[ - 1].ch != '\n')
                    rv++;
                VScrollLen(hwnd, rv - 1, TRUE);
                return rv;
            case EM_SETTABSTOPS:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                p->cd->tabs = (*(int*)lParam) / 4;
                return 0;
            case EM_SETMARGINS:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                p->cd->leftmargin = lParam &0xffff;
                return 0;
            case EM_SETEVENTMASK:
                return 0;
            case EM_SETLIMITTEXT:
                return 0;
            case EM_GETMODIFY:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return p->cd->modified;
            case EM_SETMODIFY:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                p->cd->modified = wParam;
                for (i = 0; i < UNDO_MAX; i++)
                    p->cd->undolist[i].modified = TRUE;
                return 0;
            case EM_GETFIRSTVISIBLELINE:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return SendMessage(hwnd, EM_EXLINEFROMCHAR, 0, p
                    ->textshowncharpos);
            case EM_GETRECT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                ClientArea(hwnd, p, (LPRECT)lParam);
                ((LPRECT)lParam)->left += p->cd->leftmargin;
                return 0;
            case EM_CANUNDO:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return p->cd->undohead != p->cd->undotail;
			case EM_CANREDO:
				{
					int x;
	                p = (EDITDATA*)GetWindowLong(hwnd, 0);
			        x = p->cd->undohead - 1;
			        if (x < 0)
           				x += UNDO_MAX;
        			if (p->cd->undohead != p->cd->redotop || p->cd->undotail != p->cd->undohead &&
						p->cd->undolist[x].undotemp)
						return TRUE;
					return FALSE;
				}
            case WM_UNDO:
            case EM_UNDO:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                doundo(hwnd, p);
                return 0;
			case WM_REDO:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                doredo(hwnd, p);
				return 0;
            case WM_CUT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                SelToClipboard(hwnd, p);
                Replace(hwnd, p, "", 0);
                ScrollCaretIntoView(hwnd, p, FALSE);
                setcurcol(p);
                p->cd->selecting = FALSE;
                break;
            case WM_COPY:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                SelToClipboard(hwnd, p);
                break;
            case WM_PASTE:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                ClipboardToSel(hwnd, p);
                p->cd->selecting = FALSE;
                setcurcol(p);
                break;
			case WM_GETFONT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
				return p->cd->hFont;
            case WM_SETFONT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                DeleteObject(p->cd->hFont);
                DeleteObject(p->cd->hBoldFont);
                DeleteObject(p->cd->hItalicFont);
                p->cd->hFont = (HFONT)wParam;
                dc = GetDC(hwnd);
                xfont = SelectObject(dc, p->cd->hFont);
                GetTextMetrics(dc, &t);
                SelectObject(dc, xfont);

                ReleaseDC(hwnd, dc);
                p->cd->txtFontHeight = t.tmHeight;
                p->cd->txtFontWidth = t.tmAveCharWidth ;
                GetObject(p->cd->hFont, sizeof(LOGFONT), &lf);
                lf.lfItalic = TRUE;
                p->cd->hItalicFont = CreateFontIndirect(&lf);
                lf.lfItalic = FALSE;
                lf.lfWeight = FW_BOLD;
                p->cd->hBoldFont = CreateFontIndirect(&lf);
                if (lParam)
                {
                    SendUpdate(hwnd);
                    InvalidateRect(hwnd, 0, 0);
                }
                break;
            case EM_GETTEXTHEIGHT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return p->cd->txtFontHeight;
            case EM_COLORIZE:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                p->cd->colorize = lParam;
                if (lParam == COLORIZE_NONE)
                {
                    Colorize(p->cd->text, 0, p->cd->textlen, C_TEXT, FALSE);
                    InvalidateRect(hwnd, 0, 0);
                }
                else
                {
                    FullColorize(hwnd, p, FALSE);
                }
                break;
            case EM_GETCOLUMN:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return curcol(p, p->cd->text, p->selstartcharpos);
            case EM_SETREADONLY:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                p->cd->readonly = !!wParam;
                return 1;
            case EM_GETREADONLY:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                return p->cd->readonly;
            case EM_TOUPPER:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (!p->cd->readonly)
                    upperlowercase(hwnd, p, TRUE);
                break;
            case EM_TOLOWER:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (!p->cd->readonly)
                    upperlowercase(hwnd, p, FALSE);
                break;
            case EM_SELECTINDENT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
                if (!p->cd->readonly)
                    SelectIndent(hwnd, p, !!lParam);
                break;
            case WM_SIZE:
                InvalidateRect(hwnd, 0, 0);
                break;
			case EM_GETSELTEXT:
                p = (EDITDATA*)GetWindowLong(hwnd, 0);
				if (p->selstartcharpos != p->selendcharpos)
				{
					int start, end ;
					char *pt = (char *)lParam;
					INTERNAL_CHAR *q ;
					if (p->selstartcharpos < p->selendcharpos)
					{
						start = p->selstartcharpos;
						end = p->selendcharpos;
					}
					else
					{
						start = p->selendcharpos;
						end = p->selstartcharpos;
					}
					q = p->cd->text + start ;
					while (q < p->cd->text + end)
						*pt++ = q++->ch;
					*pt = 0;
				}				
				break ;
            default:
                //         if (iMessage >= WM_USER)
                //            ExtendedMessageBox("XEDIT",0,"Diag - message %x received",iMessage) ;
                break;

        }
        return DefWindowProc(hwnd, iMessage, wParam, lParam);
    }
    /**********************************************************************
     * RegisterXeditWindow registers the edit window
     **********************************************************************/
    void RegisterXeditWindow(void)
    {
        WNDCLASS wc;
        memset(&wc, 0, sizeof(wc));
        wc.lpfnWndProc = &exeditProc;
        wc.lpszClassName = "XEDIT";
        wc.hInstance = hInstance;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 4;
        wc.style = CS_DBLCLKS;
        wc.hIcon = LoadIcon(0, IDI_APPLICATION);
        wc.hCursor = LoadCursor(0, IDC_IBEAM);
        wc.hbrBackground = 0;
        wc.lpszMenuName = 0;
        RegisterClass(&wc);

        wc.lpfnWndProc = &funcProc;
        wc.lpszClassName = "xccfuncclass";
        wc.hInstance = hInstance;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.style = 0;
        wc.hIcon = LoadIcon(0, IDI_APPLICATION);
        wc.hCursor = LoadCursor(0, IDC_ARROW);
        wc.hbrBackground = 0;
        wc.lpszMenuName = 0;
        RegisterClass(&wc);

        wc.lpfnWndProc = &codecompProc;
        wc.lpszClassName = "xcccodeclass";
        wc.hInstance = hInstance;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.style = 0;
        wc.hIcon = LoadIcon(0, IDI_APPLICATION);
        wc.hCursor = LoadCursor(0, IDC_ARROW);
        wc.hbrBackground = 0;
        wc.lpszMenuName = 0;
        RegisterClass(&wc);
        backgroundColor = GetSysColor(COLOR_WINDOW);
        textColor = GetSysColor(COLOR_WINDOWTEXT);
    }
