/* 
   Copyright 2001-2003 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
   02111-1307, USA.  

   You may contact the author at:

   mailto::camille@bluegrass.net

   or by snail mail at:

   David Lindauer
   850 Washburn Ave Apt 99
   Louisville, KY 40222

**********************************************************************

XEDIT is an edit control, similar in some respects to rich edit but
not having the full functionality of the rich edit control.  It was written
in order to speed up the process of drawing colored text and get around
various bugs inherent in the rich edit control.  This module forms
the basic functionality for the edit windows, however the windows created
with the class in this module are superclassed with the routines in the
EDITOR module, to add various functionality such as loading and storing
files to disk, processing hints, putting up the right-click menu, and
interacting with other windows in the CCIDE program.

**********************************************************************

*/
// 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>

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

// The TTF and TTM constants are extended contents not found in the very old
// borland compiler I'm using to test this.  They would be in the headers
// with any modern compiler
#ifndef TTF_TRACK
#define TTF_TRACK               0x0020
#define TTF_ABSOLUTE            0x0080
#define TTM_TRACKACTIVATE       (WM_USER + 17)  
#define TTM_TRACKPOSITION       (WM_USER + 18)  
#endif

// The INTERNAL_CHAR structure is used to store one character that is displayed.
// It has all the information needed to render the character.
typedef struct {
   COLORREF color ;
   short effect ;
   char ch ;
   char pad ;
} INTERNAL_CHAR;

#define CRLF_DUMMY 255

// The next few constants control the UNDO mechanism
#define UNDO_MAX 100 
#define UNDO_INSERT 1
#define UNDO_DELETE 2
#define UNDO_BACKSPACE 3
#define UNDO_MODIFY 4
#define UNDO_DELETESELECTION 5
#define UNDO_INSERTSELECTION 6
#define UNDO_CASECHANGE 7

// a list of UNDO structures describe each operation the user performs.
// The list is traversed backwards if 'undo' is pressed.
typedef struct {
   char type ;
   char modified ;
   int preselstart ;
   int preselend ;
   int postselstart ;
   int postselend ;
   int len ;
   int max ;
   unsigned char *data ;
} UNDO ;

// the EDITDATA structure holds all information the edit control has
// available on a per window basis.  Note that this isn't all the information
// there is about the window; things that aren't related to editing
// may be held in structures in the EDITOR module
typedef struct {
   HWND tooltip ;
   RECT ttrect ;
   char ttident[256] ;
   int ttlineno ;
   HFONT hFont,hBoldFont, hItalicFont, hItalicBoldFont ;
   HBRUSH hbrBackground ;
   COLORREF defforeground ;
   COLORREF defbackground ;
   char *lastgottext ;
   INTERNAL_CHAR *text ;
   int textlen ;
   int textmaxlen ;
   int selstartcharpos ;
   int selendcharpos ;
   int textshowncharpos ;
   int leftshownindex ;
   int tabs ;
   int leftmargin ;
   UNDO undolist[UNDO_MAX] ;
   char undohead ;
   char undotail ;
   char undoing ;
   char nocolorize ;
   char modified ;
   char txtFontHeight ;
   char txtFontWidth ;
   char nosel ;
	char inserting ;
	char selecting ;
   char buttondown ;
   char autoscrolldir ;
   char hasfocus ;
   char hiddenCaret ;
   char ttup ;
   char sendchangeonpaint ;
   char readonly ;
} EDITDATA ;

// 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;
	int len ;
	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 = 0xff0000 ;
COLORREF numberColor = 0x0000ff ;
COLORREF commentColor = 0x00c000 ;
COLORREF stringColor = 0xffff00 ;
COLORREF escapeColor = 0x0000ff ;
COLORREF backgroundColor = 0xffffff ;
COLORREF textColor = 0 ;

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"
} ;

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 CONTEXT StoppedRegs ;
extern PROJLIST *selectedProject ;

void SendUpdate(HWND hwnd) ;
// The C_keywordList is a list of all keywords, with colorization info
static KEYLIST C_keywordList[] = { 
	{ "int", 3, &keywordColor },
	{ "char", 4, &keywordColor },
	{ "long", 4, &keywordColor },
	{ "float" , 5 , &keywordColor },
	{ "double", 6, &keywordColor } ,
	{ "return", 6, &keywordColor },
	{ "struct", 6, &keywordColor },
	{ "union", 5, &keywordColor },
	{ "typedef",7, &keywordColor },
	{ "enum" , 4, &keywordColor },
	{ "static",6,&keywordColor },
	{ "auto", 4, &keywordColor },
	{ "sizeof", 6, &keywordColor },
	{ "do",2,&keywordColor },
	{ "if",2,&keywordColor },
	{ "else",4,&keywordColor },
	{ "for",3,&keywordColor },
	{ "switch",6,&keywordColor },
	{ "while",5,&keywordColor },
	{ "short",5,&keywordColor },
	{ "extern",6,&keywordColor },
	{ "case",4,&keywordColor },
	{ "goto",4,&keywordColor },
	{ "default",7,&keywordColor },
	{ "register",8,&keywordColor },
	{ "unsigned",8,&keywordColor },
	{ "break",5,&keywordColor },
	{ "continue",8,&keywordColor } ,
	{ "void",4,&keywordColor },
	{ "volatile",8,&keywordColor },
	{ "const",5,&keywordColor },
	{ "virtual",7,&keywordColor },
	{ "public",6,&keywordColor },
	{ "private",7,&keywordColor },
	{ "protected",9,&keywordColor },
	{ "class",5,&keywordColor },
	{ "friend",6,&keywordColor },
	{ "this",4,&keywordColor },
	{ "operator",8,&keywordColor },
	{ "new",3,&keywordColor },
	{ "delete",6,&keywordColor },
	{ "inline",6,&keywordColor },
	{ "try",3,&keywordColor },
	{ "catch", 5, &keywordColor },
	{ "template",8,&keywordColor },
	{ "throw",5,&keywordColor } ,
	{ "namespace",9,&keywordColor },
	{ "bool", 4, &keywordColor },
	{ "true", 4, &numberColor } ,
	{ "false", 5, &numberColor },
	{ "#include",8,&escapeColor },
	{ "#define", 7,&escapeColor },
	{ "#if",3,&escapeColor },
	{ "#else",5,&escapeColor },
	{ "#elseif",7,&escapeColor },
	{ "#endif",6,&escapeColor },
	{ "#ifdef",6,&escapeColor },
	{ "#ifndef",7,&escapeColor },
	{ "defined",7,&keywordColor },
	{ 0, 0}
} ;

/**********************************************************************
 * SaveColors saves the color and font settings to the registry
 **********************************************************************/
void saveColors(void)
{
   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) ;
}
/**********************************************************************
 * RestoreColors restores the color and font settings from the registry
 **********************************************************************/
void restoreColors(void)
{
   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)) ;
}
/**********************************************************************
 * 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)
{
   while (t->ch) {
      if (t->ch == text[0]) {
         char *t1 = text ;
         INTERNAL_CHAR *it1 = t;
         while (it1->ch == *t1) {
            t1++ ;
            it1++ ;
         }
         if (!*t1)
            return t ;
      }
      t++ ;
   }
   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 ;

}
/**********************************************************************
 * 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)
{
	KEYLIST *p = C_keywordList ;
	int i ;
   int hexflg = FALSE ;
	while (p->text) {
      INTERNAL_CHAR *t = buf+start ;
      while ((t = strpstr(t,p->text)) && t < buf + start + chars) {
         if (t->color != commentColor && !keysym(t[-1].ch) && !keysym(t[p->len].ch))
            Colorize(buf,t-buf,p->len,*p->color,FALSE) ;
			t += p->len ;
		}
		p++ ;
	}
	for (i=0; i < chars; i++)
      if (buf[start+i].color != commentColor)
         if (isdigit(buf[start+i].ch)) {
            if (!backalpha(buf,start+i-1)) {
               int j = i ;
               char c = buf[start+i++].ch ;
               if (isdigit(buf[start+i].ch) || (hexflg |= (buf[start+i].ch == 'x' && c == '0'))) {
                  i++ ;
                  while (isdigit(buf[start+i].ch) || hexflg && isxdigit(buf[start+i].ch)) 
                     i++ ;
               }
               hexflg = FALSE ;
               Colorize(buf,start+j,i-j,numberColor,FALSE) ;
               i-- ;
            }
         } else
            if ((buf[start+i].ch == '"' || buf[start+i].ch == '\'') &&
                  (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-1].ch =='\\' && buf[start+i-2].ch != '\\' )&& i < chars)
                  i++ ;
               Colorize(buf,start+j+1,i-j-1,stringColor,FALSE) ;
            }
            
				
}
/**********************************************************************
 * FormatBuffer colorizes comments over a range of text, 
 * then calls SearchKeywords to colorize keywords
 **********************************************************************/
static void FormatBuffer(INTERNAL_CHAR *buf, int start, int len)
{
      INTERNAL_CHAR *t=buf+start ;
      int type ;
		while (t < buf + len) {
         INTERNAL_CHAR *t1 = strpstr(t,"/*") ;
         INTERNAL_CHAR *t2 = strpstr(t,"//") ;
			if (!t1 || t1 > buf + len)
				t1 = buf + len;
         if (!t2 || t2 > buf + len)
            t2 = buf + len ;
         if (t2 < t1) {
            t1 = t2 ;
            type = 1 ;
         } else
            type = 0 ;
         if (t1 != buf + len) {
            if (type) {
               t = strpstr(t1+2,"\n") ;
               if (!t || t > buf+len-1)
                  t = buf + len-1 ;
               else
                  t = t - 1 ;
            } else {
               t = strpstr(t1+2,"*/") ;
               if (!t || t > buf+len-2)
                  t = buf + len-2 ;
            }
            Colorize(buf,t1-buf,t-t1+2,commentColor,TRUE) ;
            if (type)
               t++ ;
            else
               t += 2 ;
			} else
				t = buf + len ;
		}
      SearchKeywords(buf,len-start,start) ;
}
/**********************************************************************
 * FormatLine is an optimized colorizer that just colorizes the current
 * line
 **********************************************************************/

static void FormatLine(HWND hwnd, INTERNAL_CHAR *buf)
{
	char *s ;
		int start,end,xstart,xend,ystart,found = FALSE ;
		SendMessage(hwnd, EM_GETSEL, (WPARAM) &start,(LPARAM) &end) ;

		xend = start ;
		xstart = start ;
      while (xstart && buf[xstart-1].ch != '\n')
			xstart -- ;
#ifdef OLD_EDIT_FORMAT
      while (buf[xend].ch && buf[xend].ch != '\r')
#else
      while (buf[xend].ch && buf[xend].ch != '\n')
#endif
			xend++ ;
		ystart = xstart ;
		while (xstart) {
         while (xstart && buf[--xstart].ch != '*') ;
			if (!xstart)
				break ;
         if (buf[xstart+1].ch == '/')
				xstart = 0 ;
         else if (buf[xstart-1].ch == '/') {
				xstart-- ;
				found = TRUE ;
				break ;
			}
		}
		if (!found)
			xstart = ystart ;
      Colorize(buf,xstart,xend-xstart,textColor,FALSE) ;
      FormatBuffer(buf,xstart,xend) ;
}
/**********************************************************************
 * CuttingComment returns true if we are cutting part of a C- style
 * comment
 **********************************************************************/
static int CuttingComment(HWND hwnd,EDITDATA *p,INTERNAL_CHAR *buf)
{
		int start,end ;
		char *x,*y;
      start = p->selstartcharpos ;
      end = p->selendcharpos ;
	 	start--;
      x =strpstr(buf+start,"/*") ;
      y =strpstr(buf+start,"*/") ;
		return x && x < buf+end || y && y < buf+ end ;
}
/**********************************************************************
 * CutComment handles recolorization when part of a comment is cut
 **********************************************************************/
static void CutComment(HWND hwnd, EDITDATA *p, INTERNAL_CHAR *buf, int key, int iscomment)
{
      if (p->nocolorize)
         return ;
		if (!iscomment)
         FormatLine(hwnd,buf) ;
		else {
			int start,end ;
			int xstart,xend,ystart,yend ;
         start = p->selstartcharpos ;
         end = p->selendcharpos ;

			xstart = xend = start ;
         ystart = xstart ;
			while (TRUE) {
            while (xstart && buf[--xstart].ch != '*')  ;
            if (xstart == 0 || buf[xstart+1].ch == '/') {
					xstart = ystart ;
					break ;	
            } else if (xstart && buf[xstart-1].ch == '/') {
					xstart-- ;
					break ;
				}
			}
         yend = xend ;
			while (TRUE) {
            while (xend < p->textlen-1 && buf[++xend].ch != '*') ;
            if (xend >= p->textlen-1 || buf[xend+1].ch == '/') {
					xend++ ;
					break ;
				}
            if (buf[xend-1].ch == '/') {
					xend-- ;
					break ;
				}
			}
         Colorize(buf,xstart,xend-xstart,textColor,FALSE) ;
         FormatBuffer(buf,xstart,xend) ;
		}
}
/**********************************************************************
 * AddedComment recolorizes when part or all of a comment is pasted
 **********************************************************************/
static int AddedComment(HWND hwnd, EDITDATA *p, INTERNAL_CHAR *buf, int key) 
{
	int starting = -1, ending = -1 ;
		int start,end ;
      if (p->nocolorize)
         return ;
		SendMessage(hwnd,EM_GETSEL,(WPARAM)&start,(LPARAM)&end) ;
      if (buf[start-1].ch == '*') {
         if (buf[start-2].ch == '/')
				starting = start-2 ;
         else if (buf[start].ch == '/')
				ending = start-1 ;
      } else if (buf[start-1].ch == '/') {
         if (buf[start-2].ch == '*')
				ending = start - 2 ;
         else if (buf[start].ch == '*')
				starting = start-1 ;
		}
				
		if (starting != -1) {
         INTERNAL_CHAR *t = strpstr(buf+starting+2,"*/") ;
			if (!t)
            t = buf + strplen(buf) ;
         FormatBuffer(buf, starting, t-buf) ;
		} else if (ending != -1) {
         INTERNAL_CHAR *t = strpstr(buf+ending+2,"/*") ;
			if (!t)
            t = buf + strplen(buf) ;
         Colorize(buf,ending,t-buf-ending,textColor,FALSE) ;
         Colorize(buf,ending,2,commentColor,2) ;
         FormatBuffer(buf, ending,t- buf) ;
		}
	return !(starting == ending) ;
	
}
/**********************************************************************
 * 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] ;   
   struct {
      long min,max ;
   } charrange ;
   if (charpos == -1) {
      SendMessage(hwnd,EM_EXGETSEL,(WPARAM) 0, (LPARAM) &charrange) ;
      charpos = charrange.min ;
   }
   linepos = SendMessage(hwnd,EM_LINEFROMCHAR,(WPARAM)charpos,0) ;
   linecharindex = SendMessage(hwnd, EM_LINEINDEX,linepos,0) ;
   linecharpos = charpos - linecharindex;
   *(short *)buf = 1000 ;
   SendMessage(hwnd,EM_GETLINE,linepos,(LPARAM)buf) ;
   outputbuf[0] = 0 ;
   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)
{
   char buf[256] ;   
   if (!GetWordFromPos(edwin,buf,-1,0,0,0)) 
      return ;
   else
      ShowHelp(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->ttup) {
      memset(&t,0,sizeof(t)) ;
      t.cbSize = sizeof(TOOLINFO) ;
      t.hwnd = edwin ;
      t.uId = 10000 ;
      SendMessage(p->tooltip,TTM_TRACKACTIVATE,FALSE,(LPARAM)&t) ;
      p->ttup = FALSE ;
   }
   if (uState == notDebugging || uState == Running) {
      semaphore = FALSE ;
      return ;
   }
#ifdef XXXXX
   if (p->ttrect.left <= x && p->ttrect.right > x && p->ttrect.top <= y &&
         p->ttrect.bottom > y) {
      semaphore = FALSE ;
      return ;
   }
#endif
   charpos = SendMessage(edwin,EM_CHARFROMPOS,0,(LPARAM)&pt) ;
   if (GetWordFromPos(edwin,p->ttident,charpos,&linepos,&start,&end)) {
      RECT r ;
      GetWindowRect(edwin,&r) ;
      p->ttup = TRUE ;
      p->ttlineno = linepos + 1;
      SendMessage(p->tooltip,TTM_TRACKPOSITION,0,MAKELONG(x+r.left,y + r.top + p->txtFontHeight)) ;
      memset(&t,0,sizeof(t)) ;
      t.cbSize = sizeof(TOOLINFO) ;
      t.hwnd = edwin ;
      t.uId = 10000 ;
      SendMessage(p->tooltip,TTM_TRACKACTIVATE,TRUE,(LPARAM)&t) ;

   }
   semaphore = FALSE ;
}
/**********************************************************************
 * getundo creates an UNDO structure based on the user's last operation
 **********************************************************************/
UNDO *getundo(EDITDATA *p, int type)
{
   int x  ;
   UNDO *u ;
   if (p->undoing)
      return 0 ;
   if (type != UNDO_DELETESELECTION && type != UNDO_INSERTSELECTION && type != UNDO_CASECHANGE && p->undohead != p->undotail) {
      x = p->undohead-1 ;
      if (x < 0) x+= UNDO_MAX ;
      if (p->undolist[x].type == type)
         if (type != UNDO_BACKSPACE) {
            if (p->selstartcharpos == p->undolist[x].postselstart)
               return &p->undolist[x] ;
         } else {
            if (p->selstartcharpos+1 == p->undolist[x].postselstart)
               return &p->undolist[x] ;
         }
   }
   u = &p->undolist[p->undohead] ;
   if (++p->undohead >= UNDO_MAX)
      p->undohead = 0 ;
   if (p->undohead == p->undotail)
      if (++p->undotail >= UNDO_MAX)
         p ->undotail = 0 ;
   u->len = 0 ;
   u->preselstart = p->selstartcharpos ;
   u->preselend = p->selendcharpos ;
   u->modified = p->modified ;
   p->modified = TRUE ;
   u->type = type ;
   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->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) ;
   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->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 ;
   u->len++ ;
   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->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->textlen) {
      if (p->text[spos+i].ch == '\n') {
         point->y += p->txtFontHeight ;
         spos += i+1 ;
         i = 0 ;
      } else
         i++ ;
   }   
   i = 0 ;
   xcol = 0 ;
   while (spos+i < pos && spos+i < p->textlen) {
#ifdef OLD_EDIT_FORMAT
      if (p->text[spos+i].ch == '\r' || p->text[spos+i].ch == '\n')
#else
      if (p->text[spos+i].ch == '\n')
#endif
         break;
      if (p->text[spos+i].ch == '\t') {
         xcol += p->tabs ;
         xcol /= p->tabs ;
         xcol *=p->tabs ;
      } else
         xcol++ ;
      i++ ;
   }
   if (xcol >=p->leftshownindex)
      point->x = (xcol - p->leftshownindex) * p->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->txtFontHeight ;
   col = point->x / p->txtFontWidth ;
   while (row && pos < p->textlen) {
      if (p->text[pos].ch == '\n')
         row-- ;
      pos++ ;
   }

   if (pos == p->textlen)
      return pos ;
   while (pos +i < p->textlen && xcol < p->leftshownindex) {
#ifdef OLD_EDIT_FORMAT
      if (p->text[pos+i].ch == '\r' || p->text[pos+i].ch == '\n')
#else
      if (p->text[pos+i].ch == '\n')
#endif
         return pos+i-1 ;
      if (p->text[pos+i].ch == '\t') {
         xcol += p->tabs ;
         xcol /= p->tabs ;
         xcol *=p->tabs ;
      } else
         xcol++ ;
      i++ ;
   }
   pos += i ;
   i = 0 ;
   xcol = 0 ;
   while (xcol < col && pos + i < p->textlen) {
#ifdef OLD_EDIT_FORMAT
      if (p->text[pos+i].ch == '\r' || p->text[pos+i].ch == '\n')
#else
      if (p->text[pos+i].ch == '\n')
#endif
         break ;
      if (p->text[pos+i].ch == '\t') {
         xcol += p->tabs ;
         xcol /= p->tabs ;
         xcol *=p->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 ;
   }
   SetScrollPos(hwnd,SB_VERT,count1,TRUE) ;
}
/**********************************************************************
 *  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->selecting ? p->selendcharpos : p->selstartcharpos)) {
      SetCaretPos(pt.x,pt.y) ;
      ShowCaret(hwnd) ;
      p->hiddenCaret = FALSE ;
   } else {
      if (!p->hiddenCaret)
         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->txtFontHeight ;
   if (lines < 0){
      lines = - lines ;
      while(lines && pos > 0) {
         --pos ;
         if (p->text[pos].ch == '\n') {
            lines-- ;
            len-- ;
         }
      }
      while (pos) {
         --pos;
         if (p->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->txtFontHeight,&r,&r,0,&update,SW_INVALIDATE) ;
      }
      p->textshowncharpos = pos ;
   } else {
      while (lines && pos < p->textlen)  {
         if (p->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->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)
{
         POINTL pt ;
         int lines,cols,colpos=0 ;
         RECT r ;
         int pos1,pos ;
         if (!p->selecting)
            pos1 = p->selstartcharpos ;
         else
            pos1 = p->selendcharpos ;
         if (posfromchar(hwnd,p,&pt,pos1)) {
            MoveCaret(hwnd,p) ;
            return ;
         }
         ClientArea(hwnd,p,&r) ;
         lines = r.bottom / p->txtFontHeight ;
         cols = r.right/p->txtFontWidth ;
         if (pos1 > p->textshowncharpos) {
            int xlines =0;
            pos = p->textshowncharpos ;
            while (pos < pos1 && pos < p->textlen) {
               if (p->text[pos].ch == '\n')
                  xlines++ ;
               pos++ ;
            }
            if (xlines >= lines)
               scrollup(hwnd,p,xlines-lines+1) ;

         } else {
            lines = 0 ;
            if (pos1 != p->textshowncharpos) {
               pos = p->textshowncharpos ;
               while (pos >0 && pos != pos1) {
                  --pos ;
                  if (p->text[pos].ch == '\n')
                     lines++ ;
               }
               if (lines > 0)
                  scrollup(hwnd,p,-lines) ;
            }
         }
         pos = pos1 ;
         while (pos > 0 && p->text[pos-1].ch != '\n')
            pos--;
         while (pos != pos1) {
            if (p->text[pos-1].ch == '\t')
               colpos = ((colpos + p->tabs)/p->tabs) * p->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->text ;
         while (ic < p->text + p->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) ;
#ifdef OLD_EDIT_FORMAT
   if (pos && p->text[pos-1].ch != '\n' && p->text[pos-1].ch != '\r')
#else
   if (pos && p->text[pos-1].ch != '\n')
#endif
      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->text,p->selstartcharpos,p->selendcharpos) ;
               SendMessage(GetParent(hwnd),EN_LINECHANGE,SelLine(p,p->selstartcharpos),len) ;
               memcpy(p->text + p->selstartcharpos, p->text + p->selendcharpos, (p->textlen - p->selendcharpos + 1) * sizeof(INTERNAL_CHAR)) ;
               p->textlen -= i ;
            } else if (i < 0) {
               temp =  lfchars(p->text,p->selendcharpos,p->selstartcharpos) ;
               SendMessage(GetParent(hwnd),EN_LINECHANGE,SelLine(p,p->selendcharpos),-temp) ;
               VScrollPos(hwnd,-temp,FALSE) ;
               len -= temp ;
               memcpy(p->text + p->selendcharpos, p->text + p->selstartcharpos, (p->textlen - p->selstartcharpos + 1) * sizeof(INTERNAL_CHAR)) ;
               p->selstartcharpos = p->selendcharpos ;
               p->textlen += i ;
            }  
         p->selendcharpos = p->selstartcharpos + lens ;
         if (u) {
            u->postselstart = u->postselend = p->selstartcharpos ;
         }
         if (p->selendcharpos -p->selstartcharpos+ p->textlen > p->textmaxlen) {
            int adj = p->textlen + p->selendcharpos-p->selstartcharpos ;
            buf = realloc(p->text,(adj + 1) * sizeof(INTERNAL_CHAR)) ;
            if (!buf) {
               p->selendcharpos = p->selstartcharpos ;
               SendUpdate(hwnd) ;
               p->sendchangeonpaint = TRUE ;
               InvalidateRect(hwnd,0,0) ;
               return;
            }
            p->text = buf ;
            p->textmaxlen = adj ;
         }
         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->selendcharpos+p->text,p->text + p->selstartcharpos, (p->textlen - p->selstartcharpos + 1) * sizeof(INTERNAL_CHAR)) ;
         memset(p->selstartcharpos+p->text,0,(p->selendcharpos - p->selstartcharpos) * sizeof(INTERNAL_CHAR)) ;
         i = 0 ;
         while (*s && i < lens) {
            if (*s == '\n')
               len++ ;
            p->text[p->selstartcharpos+i].ch = *s++ ;
            p->text[p->selstartcharpos+i].color = p->defforeground ;
            i++ ;
            p->textlen++ ;
         }
         VScrollLen(hwnd,len,FALSE) ;
         if (!p->nocolorize)
            FormatBuffer(p->text,0,p->textlen) ;
         SendUpdate(hwnd) ;
         p->sendchangeonpaint = TRUE ;
         InvalidateRect(hwnd,0,0) ;
}
/**********************************************************************
 * 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->textlen) {
      if (p->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 ;
   if (p->selecting)
      pos = p->selendcharpos ;
   else
      pos = p->selstartcharpos ;
   ClientArea(hwnd,p,&r) ;
   r.top = GetLineOffset(hwnd,p,pos)*p->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->inserting) {
            int i = p->selendcharpos - p->selstartcharpos ;
            u = undo_deletesel(p) ;
            if (i > 0) {
               len -= lfchars(p->text,p->selstartcharpos,p->selendcharpos) ;
               SendMessage(GetParent(hwnd),EN_LINECHANGE,SelLine(p,p->selstartcharpos),len) ;
               memcpy(p->text + p->selstartcharpos, p->text + p->selendcharpos, (p->textlen - p->selendcharpos + 1) * sizeof(INTERNAL_CHAR)) ;
            } else if (i < 0) {
               temp =  lfchars(p->text,p->selendcharpos,p->selstartcharpos) ;
               SendMessage(GetParent(hwnd),EN_LINECHANGE,SelLine(p,p->selendcharpos),-temp) ;
               VScrollPos(hwnd,-temp,FALSE) ;
               len -= temp ;
               memcpy(p->text + p->selendcharpos, p->text + p->selstartcharpos, (p->textlen - p->selendcharpos + 1) * sizeof(INTERNAL_CHAR)) ;
               p->selstartcharpos = p->selendcharpos ;
            }  
            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 (2+ p->textlen >= p->textmaxlen) {
            int adj = p->textmaxlen + 64 ;
            INTERNAL_CHAR *buf = realloc(p->text,(adj + 1) * sizeof(INTERNAL_CHAR)) ;
            if (!buf) {
               SendUpdate(hwnd) ;
               p->sendchangeonpaint = TRUE ;
               InvalidateRect(hwnd,0,0) ;
               return 0 ;
            }
            p->text = buf ;
            p->textmaxlen = adj ;
         }
         u = undo_insertchar(p,ch) ;
         memmove(p->text + p->selstartcharpos +1, p->text + p->selstartcharpos, (p->textlen -p->selstartcharpos+1)*sizeof(INTERNAL_CHAR)) ;
         p->text[p->selstartcharpos].ch = ch ;
         p->textlen++ ;
         VScrollLen(hwnd,len,FALSE) ;
   } else {
      p->selendcharpos = p->selstartcharpos ;
#ifdef OLD_EDIT_FORMAT
      if (p->text[p->selstartcharpos].ch == '\r' || p->text[p->selstartcharpos].ch == '\n')
         p->selendcharpos = p->selstartcharpos++ ;
      if (p->text[p->selstartcharpos].ch == '\r' || p->text[p->selstartcharpos].ch == '\n')
         p->selendcharpos = p->selstartcharpos++ ;
#else
      if (p->text[p->selstartcharpos].ch == '\n')
         p->selendcharpos = p->selstartcharpos++ ;
#endif
      u = undo_modifychar(p) ;
      p->text[p->selstartcharpos].ch = ch ;
   }
   p->selendcharpos = ++p->selstartcharpos ;
   if (u) {
      u->postselstart = p->selstartcharpos ;
      u->postselend = p->selendcharpos ;
   }
   p->sendchangeonpaint = TRUE ;
   ScrollCaretIntoView(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 ;
   }
#ifdef OLD_EDIT_FORMAT
   insertchar(hwnd,p,'\r') ;
#endif
   insertchar(hwnd,p,'\n') ;
#ifdef OLD_EDIT_FORMAT
   if (temp < 0) {
      p->selstartcharpos = -temp+2 ;
   } else {
      p->selendcharpos = temp+2 ;
   }
#else
   if (temp < 0) {
      p->selstartcharpos = -temp+1 ;
   } else {
      p->selendcharpos = temp+1 ;
   }
#endif
//   ClientArea(hwnd,p,&r) ;
//   r.top = GetLineOffset(hwnd,p,p->selstartcharpos)*p->txtFontHeight ;
//   ScrollWindowEx(hwnd,0,1*p->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) {
         if (p->text[pos].ch == '\n') {
            pos++ ;
            break ;
         }
         pos-- ;
      }
      while (pos != p->selstartcharpos) {
         if (p->text[pos].ch == '\t') {
            col = col + p->tabs ;
            col /= p->tabs ;
            col *= p->tabs ;
         } else
            col++ ;
         pos++ ;
      }
      col2 = col +p->tabs ;
      col2 /= p->tabs ;
      col2 *= p->tabs ;
      while(col < col2) {
         insertchar(hwnd,p,' ') ;
         col++ ;
      }
   }
}
/**********************************************************************
 * go backwards to the last tab position
 **********************************************************************/
void backtab(HWND hwnd, EDITDATA *p)
{
   int pos = p->selstartcharpos, col=0,col2 ;
   if (pos) {
      if (p->text[pos-1].ch =='\t')
         p->selstartcharpos-- ;
      else {
         int sol ;
         while (pos) {
            if (p->text[pos].ch == '\n') {
               pos++ ;
               break ;
            }
            pos-- ;
         }
         sol = pos ;
         while (pos != p->selstartcharpos) {
            if (p->text[pos].ch == '\t') {
               col = col + p->tabs ;
               col /= p->tabs ;
               col *= p->tabs ;
            } else
               col++ ;
            pos++ ;
         }
         col2 = col - 1 ;
         col2 /= p->tabs ;
         col2 *= p->tabs ;
         col = 0 ;
         while (col < col2) {
            if (p->text[pos].ch == '\t') {
               col = col + p->tabs ;
               col /= p->tabs ;
               col *= p->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)
{
#ifdef OLD_EDIT_FORMAT
   int del = 1 ;
#else
   int del = 0 ;
#endif
   int sel = SelLine(p,p->selstartcharpos) ;

#ifdef OLD_EDIT_FORMAT
   if (p->text[p->selstartcharpos].ch == '\r' && p->text[p->selstartcharpos+1].ch == '\n')
#else
   if (p->text[p->selstartcharpos].ch == '\n')
#endif
      del ++,sel++;
   SendMessage(GetParent(hwnd),EN_LINECHANGE,sel,-1) ;
	undo_deletechar(p,CRLF_DUMMY,utype) ;
   memcpy(p->text + p->selstartcharpos, p->text + p->selstartcharpos + del, (p->textlen-p->selstartcharpos-del+1) * sizeof(INTERNAL_CHAR)) ;
   p->textlen -= del ;
   SendUpdate(hwnd) ;
   p->sendchangeonpaint = TRUE ;
   VScrollLen(hwnd, -1, FALSE) ;
   VScrollPos(hwnd,-1,FALSE) ;
}
/**********************************************************************
 * removechar cuts a character from the text (delete or back arrow)
 **********************************************************************/
void removechar(HWND hwnd, EDITDATA *p, int utype)
{
   if (p->inserting && p->selstartcharpos != p->selendcharpos) {
      Replace(hwnd,p,"",0) ;
      ScrollCaretIntoView(hwnd,p) ;
   } else {
      int del ;
      if (p->selstartcharpos == p->textlen)
         return ;
#ifdef OLD_EDIT_FORMAT
      if (p->text[p->selstartcharpos].ch == '\r' || p->text[p->selstartcharpos].ch == '\n') {
#else
      if (p->text[p->selstartcharpos].ch == '\n') {
#endif
         removecr(hwnd,p,utype) ;
         ScrollCaretIntoView(hwnd,p) ;
         drawline(hwnd,p,p->selstartcharpos) ;
      } else {
         undo_deletechar(p,p->text[p->selstartcharpos].ch,utype) ;
         memcpy(p->text + p->selstartcharpos, p->text + p->selstartcharpos + 1, (p->textlen-p->selstartcharpos) * sizeof(INTERNAL_CHAR)) ;
         p->textlen-- ;
         drawline(hwnd,p,p->selstartcharpos) ;
      }
   }
   p->selendcharpos = p->selstartcharpos ;
}
/**********************************************************************
 * SelToClipboard copies the current selection to the clipboard
 **********************************************************************/
void SelToClipboard(HWND hwnd, EDITDATA *p)
{
   int start,end ;
   if (p->selendcharpos-p->selstartcharpos < 0 )  {
      end = p->selstartcharpos;
      start= p->selendcharpos ;
   } else {
      end = p->selendcharpos;
      start= p->selstartcharpos ;
   }
   if (end == start)
      return ;
   if (OpenClipboard(hwnd)) {
      HGLOBAL glmem = GlobalAlloc(GMEM_DDESHARE + GMEM_MOVEABLE,end-start+1) ;
      if (glmem != NULL) {
         char *data = GlobalLock(glmem) ;
         int i ;
         for (i=start; i < end; i++)
            data[i-start] = p->text[i].ch ;
         data[i-start]= 0 ;
         GlobalUnlock(data) ;
         EmptyClipboard() ;
         SetClipboardData(CF_TEXT,glmem) ;
      }
      CloseClipboard() ;
   }
}
/**********************************************************************
 * ClipboardToSel pastes the clipboard into the text
 **********************************************************************/
void ClipboardToSel(HWND hwnd, EDITDATA *p)
{
   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 = malloc(l) ;
         if (mem) {
            memcpy(mem,data,l) ;
            mem[l-1] = 0 ;
            if (mem[0]) {
               Replace(hwnd,p,mem,strlen(mem)) ;
               ScrollCaretIntoView(hwnd,p) ;
               if (!p->nocolorize)
                  FormatBuffer(p->text,0,p->textlen) ;
               SendUpdate(hwnd) ;
               p->sendchangeonpaint = TRUE ;
               p->selendcharpos = p->selstartcharpos ;
               MoveCaret(hwnd,p) ;
               InvalidateRect(hwnd,0,0) ;
            }
            free(mem) ;
         }
      }
      GlobalUnlock(data) ;
      CloseClipboard() ;
   }
}
/**********************************************************************
 * 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 ;
#ifdef OLD_EDIT_FORMAT
   while (pos &&  text[pos].ch != '\n') {
      pos-- ;
   }
   if (text[pos].ch == '\n')
      pos++ ;
#else
   while (pos &&  text[pos-1].ch != '\n') {
      pos-- ;
   }
#endif
   while (pos < opos) {
      if (text[pos].ch == '\t') {
         rv +=p->tabs ;
         rv = ( rv /p->tabs) * p->tabs ;
      } else
         rv++ ;
      pos++ ;
   }
   return rv ;
}
/**********************************************************************
 * 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 ;
      if (p->selecting)
         pos = p->selendcharpos ;
      else
         pos = p->selstartcharpos ;
//   oldsel = pos ;
   col =curcol(p,p->text,pos) ;
   if (lines > 0) {
      while (lines && pos < p->textlen) {
         if (p->text[pos].ch == '\n')
            lines-- ;
         pos++ ;
      }
   } else {
#ifndef OLD_EDIT_FORMAT
      if (pos)
         pos-- ;
#endif
      while (lines && pos) {
         if (p->text[pos].ch == '\n')
            lines++ ;
         pos-- ;
      }
      while (pos) {
         if (p->text[pos].ch == '\n') {
            pos++ ;
            break ;
         }
         pos-- ;
      }
   }
   while (index < col && pos < p->textlen && p->text[pos].ch != '\n')  {
      if (p->text[pos].ch == '\t') {
         index += p->tabs ;
         col = (col/p->tabs) * p->tabs ;
      } else
         index++ ;
      pos ++ ;
   }
#ifdef OLD_EDIT_FORMAT
   if (pos && p->text[pos].ch == '\n')
      pos -- ;
#endif
   if (!p->selecting)
      p->selendcharpos = p->selstartcharpos = pos ;
   else
      p->selendcharpos = pos ;
   ScrollCaretIntoView(hwnd,p) ;
}
/**********************************************************************
 * FillEditBackground puts the background on those parts of a line that
 * need it
 **********************************************************************/
void FillEditBackground(HDC dc, RECT *r, HBRUSH sel, EDITDATA *p, int tabcol,int pos)
{
   RECT r1 ;
   int start,end ;
   int tabwidth ; 
   int col = 0 ;
   if (p->selstartcharpos <= p->selendcharpos) {
      start = p->selstartcharpos ;
      end = p->selendcharpos ;
   } else {
      start = p->selendcharpos ;
      end = p->selstartcharpos ;
   }
   if (start == end || pos > end) {
      FillRect(dc,r,p->hbrBackground) ;
      return ;
   }
   r1 = *r ;
   tabwidth = p->tabs * p->txtFontWidth ;
   r1.right = 0 ;
   if (pos < start) {
#ifdef OLD_EDIT_FORMAT
      while (pos < start && pos < p->textlen && p->text[pos].ch != '\r' && p->text[pos].ch != '\n') {
#else
      while (pos < start && pos < p->textlen && p->text[pos].ch != '\n') {
#endif
         if (p->text[pos].ch == '\t') {
            col = col + tabwidth ;
            col = ((col - tabcol)/tabwidth) *tabwidth + tabcol ;
         } else
           col = col + p->txtFontWidth ;
         pos++ ;
      }
      r1.right = col ;
      if (r1.left != r1.right)
         FillRect(dc,&r1,p->hbrBackground) ;
   }
   if (pos >= start && pos < end) {
      r1.left = r1.right ;
#ifdef OLD_EDIT_FORMAT
      while (pos < end && pos < p->textlen && p->text[pos].ch != '\r' && p->text[pos].ch != '\n') {
#else
      while (pos < end && pos < p->textlen && p->text[pos].ch != '\n') {
#endif
         if (p->text[pos].ch == '\t') {
            col = col + tabwidth ;
            col = ((col - tabcol)/tabwidth) *tabwidth + tabcol ;
         } else
           col = col + p->txtFontWidth ;
         pos++ ;
      }
      r1.right = col ;
      if (r1.left != r1.right) 
         FillRect(dc,&r1,sel) ;
   }
   r1.left = r1.right ;
   r1.right = r->right ;
   FillRect(dc,&r1,p->hbrBackground) ;
}
/**********************************************************************
 * 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)
{
   PAINTSTRUCT ps ;
   HDC dc ;
   RECT r,b1,client ;
   HBRUSH Sel = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT)) ;
   int lines,i,pos ;
   ClientArea(hwnd,p,&r) ;
   GetClientRect(hwnd,&client) ;
   lines = r.bottom/p->txtFontHeight ;
   dc = BeginPaint(hwnd,&ps) ;
   SetBkMode(dc,TRANSPARENT) ;
   pos = p->textshowncharpos ;
   for (i=0; i < lines; i++) {
      r.top = i * p->txtFontHeight ;
      r.bottom = r.top + p->txtFontHeight ;
      if (IntersectRect(&b1,&r,&ps.rcPaint)) {
         int col = 0,leftcol =0,tabcol,itabcol ;
         while (leftcol < p->leftshownindex) {
#ifdef OLD_EDIT_FORMAT
            if (p->text[pos].ch == '\r' || p->text[pos].ch == '\n')
#else
            if (p->text[pos].ch == '\n')
#endif
               break ;
            if (p->text[pos].ch == '\t') {
               int v = leftcol ;
               v = v + p->tabs ;
               v = (v /p->tabs) * p->tabs ;
               if (v > p->leftshownindex)
                  break ;
               leftcol = v ;
               pos++ ;
            }else {
               pos++ ;
               leftcol++ ;
            }
         }
         tabcol = p->leftshownindex % p->tabs ;
         if (tabcol == 0)
            itabcol = 0 ;
         else 
            itabcol = p->tabs - tabcol ;
         tabcol *= p->txtFontWidth ;
         itabcol *= p->txtFontWidth ;
         FillEditBackground(dc, &r, Sel, p,itabcol,pos) ;
//         FillRect(dc,&r,p->hbrBackground) ;
         while (pos < p->textlen) {
            int selection = FALSE ;
#ifdef OLD_EDIT_FORMAT
            if (p->text[pos].ch  == '\r' || p->text[pos].ch == '\n')
#else
            if (p->text[pos].ch == '\n')
#endif
               break ;
            if (p->text[pos].ch == '\t') {
               int tabwidth = p->txtFontWidth * p->tabs ;
               int newpos = col + tabwidth ;
//               HBRUSH sel,hold ;
               newpos = ((newpos-itabcol) /tabwidth) * tabwidth +itabcol;
               b1.top = r.top ;
               b1.bottom = r.bottom ;
               b1.left = col ;
               b1.right = newpos ;
               col = newpos ;
#ifdef XXXXX
               sel = CreateSolidBrush(selcolor) ;

               if (!p->nosel) {
                  if (p->selstartcharpos < p->selendcharpos)
                     if (pos >= p->selstartcharpos && pos < p->selendcharpos)
                        hold = sel ;
                     else
                        hold = p->hbrBackground ;
                  else
                     if (pos < p->selstartcharpos && pos >= p->selendcharpos)
                        hold = sel ;
                     else
                        hold = p->hbrBackground ;
               } else
                  hold = p->hbrBackground ;
               FillRect(dc,&b1,hold) ;
               DeleteObject(sel) ;
#endif
               pos++ ;
            } else {
               HFONT font ;
               int bcolor,fcolor ;
               if (p->text[pos].effect & CFE_AUTOCOLOR)
                  fcolor = p->defforeground ;
               else
                  fcolor = p->text[pos].color ;
               bcolor = p->defbackground ;
               switch(p->text[pos].effect) {
                  case CFE_BOLD:
                     font = p->hBoldFont ;
                     break ;
                  case CFE_ITALIC:
                     font = p->hItalicFont ;
                     break ;
                  case CFE_BOLD | CFE_ITALIC :
                  default:
                     font = p->hFont ;
                     break ;
               }
               SelectObject(dc,font) ;
               if (!p->nosel)
                  if (p->selstartcharpos < p->selendcharpos) {
                     if (pos >= p->selstartcharpos && pos < p->selendcharpos) {
//                        bcolor = GetSysColor(COLOR_WINDOWTEXT ) ;
                        fcolor = GetSysColor(COLOR_WINDOW) ;
                        selection = TRUE ;
                     }
                  } else
                     if (pos < p->selstartcharpos && pos >= p->selendcharpos) {
//                        bcolor = GetSysColor(COLOR_WINDOWTEXT ) ;
                        fcolor = GetSysColor(COLOR_WINDOW) ;
                        selection = TRUE ;
                     }
               SetTextColor(dc,fcolor) ;
//               SetBkColor(dc,bcolor) ;
#ifdef XXXXX
               if (selection && font == p->hItalicFont) {
                  RECT r1 = r ;
                  HBRUSH br = CreateSolidBrush(bcolor) ;
                  r1.left = col ;
                  r1.right = col + p->txtFontWidth ;
                  FillRect(dc,&r1,br);
                  DeleteObject(br) ;
               }
#endif
               TextOut(dc,col,r.top,&p->text[pos].ch,1) ;
               pos ++ ;
               col += p->txtFontWidth ;
            }
         }
      }
      while (pos < p->textlen) {
         if (p->text[pos].ch == '\n')
            break ;
         pos++ ;
      }
      if (pos >= p->textlen)
         break ;
      pos++ ;
   }
   if (r.bottom < client.bottom) {
      r.top = r.bottom ;
      r.bottom = client.bottom ;
      FillRect(dc,&r,p->hbrBackground) ;
   }
   EndPaint(hwnd,&ps) ;
   DeleteObject(Sel) ;
   if (p->sendchangeonpaint) {
      p->sendchangeonpaint = FALSE ;
      SendMessage(GetParent(hwnd),WM_COMMAND,(WPARAM)(EN_CHANGE | (GetWindowLong(hwnd,GWL_ID) << 16)),(LPARAM)hwnd) ;
   }
}
/**********************************************************************
 * eol indexes to the last character in a line
 **********************************************************************/
void eol(HWND hwnd,EDITDATA *p)
{
   int pos ;                
      if (p->selecting)
         pos = p->selendcharpos ;
      else
         pos = p->selstartcharpos ;
   while (pos < p->textlen && p->text[pos].ch != '\n') 
      pos++ ;
#ifdef OLD_EDIT_FORMAT
   if (pos && p->text[pos].ch == '\n')
      pos-- ;
#endif
   if (!p->selecting)
      p->selendcharpos = p->selstartcharpos = pos ;
   else
      p->selendcharpos = pos ;
   ScrollCaretIntoView(hwnd,p) ;
   MoveCaret(hwnd,p) ;
}
/**********************************************************************
 * sol indexes to the first character in a line
 **********************************************************************/
void sol(HWND hwnd,EDITDATA *p)
{
   int pos ;    
   int encns =FALSE;            
      if (p->selecting)
         pos = p->selendcharpos ;
      else
         pos = p->selstartcharpos ;
   if (pos && p->text[pos-1].ch == '\n') {
pastspace:
      while (p->text[pos].ch == ' ' || p->text[pos].ch == '\t')
         pos++ ;
   } else {
      while (pos && p->text[pos-1].ch != '\n') {
         if (p->text[pos-1].ch != ' ' && p->text[pos-1].ch != '\t')
            encns = TRUE ;
         pos-- ;
      }
      if (encns)
         goto pastspace ;
   }
   if (!p->selecting)
      p->selendcharpos = p->selstartcharpos = pos ;
   else
      p->selendcharpos = pos ;
   ScrollCaretIntoView(hwnd,p) ;
   MoveCaret(hwnd,p) ;


}
/**********************************************************************
 * left moves the cursor left one character
 **********************************************************************/
void left(HWND hwnd, EDITDATA *p)
{
   int pos ;    
      if (p->selecting)
         pos = p->selendcharpos ;
      else
         pos = p->selstartcharpos ;
   if (pos)
      pos-- ;
#ifdef OLD_EDIT_FORMAT
   if (pos && p->text[pos].ch == '\n')
      pos-- ;
#endif
   if (!p->selecting)
      p->selendcharpos = p->selstartcharpos = pos ;
   else
      p->selendcharpos = pos ;
   MoveCaret(hwnd,p) ;
   ScrollCaretIntoView(hwnd,p) ;
}
/**********************************************************************
 * right moves the cursor right one character
 **********************************************************************/
void right(HWND hwnd, EDITDATA *p)
{
   int pos ;    
      if (p->selecting)
         pos = p->selendcharpos ;
      else
         pos = p->selstartcharpos ;
   if (pos < p->textlen)
      pos++ ;
#ifdef OLD_EDIT_FORMAT
   if (p->text[pos].ch == '\n')
      pos++ ;
#endif
   if (!p->selecting)
      p->selendcharpos = p->selstartcharpos = pos ;
   else
      p->selendcharpos = pos ;
   MoveCaret(hwnd,p) ;
   ScrollCaretIntoView(hwnd,p) ;
}
void leftword(HWND hwnd, EDITDATA *p)
{
   int pos ;    
   int flag = 0 ;
      if (p->selecting)
         pos = p->selendcharpos ;
      else
         pos = p->selstartcharpos ;
   if (pos)
      pos-- ;
   while (pos && isspace(p->text[pos].ch))
      pos-- ;
   while (pos && (isalnum(p->text[pos].ch) || p->text[pos].ch == '_')) {
      flag = 1 ;
      pos-- ;
   }
   if (pos && flag)
      pos++ ;
   if (!p->selecting)
      p->selendcharpos = p->selstartcharpos = pos ;
   else
      p->selendcharpos = pos ;
   MoveCaret(hwnd,p) ;
   ScrollCaretIntoView(hwnd,p) ;
}
void rightword(HWND hwnd , EDITDATA *p)
{
   int pos ;    
   int flag=0 ;
      if (p->selecting)
         pos = p->selendcharpos ;
      else
         pos = p->selstartcharpos ;
   while (pos <p->textlen && (isalnum(p->text[pos].ch) || p->text[pos].ch == '_')) {
      flag = TRUE ;
      pos++ ;
   }
   while (pos < p->textlen && isspace(p->text[pos].ch)) {
      flag = TRUE ;
      pos++ ;
   }
   if (!flag && pos < p->textlen) {
      pos++ ;
      while (pos < p->textlen && isspace(p->text[pos].ch)) {
         pos++ ;
      }
   }
   if (!p->selecting)
      p->selendcharpos = p->selstartcharpos = pos ;
   else
      p->selendcharpos = pos ;
   MoveCaret(hwnd,p) ;
   ScrollCaretIntoView(hwnd,p) ;
}
/**********************************************************************
 * 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->autoscrolldir) ;
   if (p->autoscrolldir < 0) {
      p->selendcharpos = p->textshowncharpos ;
   } else {
      int lines,pos= p->textshowncharpos ;
      lines = r.bottom/p->txtFontHeight ;
      while (lines && pos < p->textlen) {
         if (p->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->autoscrolldir) {
      p->autoscrolldir = dir ;
      SetTimer(hwnd,1,100,AutoTimeProc) ;
   }
}
/**********************************************************************
 * EndAutoScroll stops the above timer
 **********************************************************************/
void EndAutoScroll(HWND hwnd, EDITDATA *p)
{
   if (p->autoscrolldir) {
      KillTimer(hwnd,1) ;
      p->autoscrolldir = 0 ;
   }
}
/**********************************************************************
 * HilightWord selects the word under the cursor
 **********************************************************************/
void HilightWord(HWND hwnd, EDITDATA *p)
{
   if (keysym(p->text[p->selstartcharpos].ch)) {
      while (p->selstartcharpos && keysym(p->text[p->selstartcharpos-1].ch))
         p->selstartcharpos-- ;
      while (p->selendcharpos < p->textlen && keysym(p->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->nocolorize)
         FormatLine(hwnd,p->text) ;
	} else {
      insertchar(hwnd,p,ch) ;
      if (!p->nocolorize) {
            if (ch == '*' || ch == '/') {
               if (!AddedComment(hwnd,p,p->text,ch))
                  FormatLine(hwnd,p->text) ;
            } else   
               FormatLine(hwnd,p->text) ;
      }
	}
}
/**********************************************************************
 * doundo is the primary routine to traverse the undo list and perform
 * an undo operation
 **********************************************************************/

void doundo(HWND hwnd, EDITDATA *p)
{
   
   if (p->undohead != p->undotail) {
      UNDO *u ;
      int oldinsert = p->inserting ;
      int start,end ;
      char *s ;
      int x = p->undohead - 1 ;
      if (x < 0)
         x += UNDO_MAX ;
      u = &p->undolist[x] ;
      p->undoing++ ;
      p->inserting = TRUE ;
      switch(u->type) {
         case UNDO_INSERT:
            start = end = u->postselstart ;
            start-- ;
            if (keysym(p->text[start].ch))
               while (end-start < u->len && keysym(p->text[start-1].ch))
                  start-- ;
            else
               if (p->text[start].ch == '\n')
                  start-- ;
            p->selstartcharpos = start ;
            p->selendcharpos = end ;
            u->len -= end-start ;
            if (u->len <= 0) {
               p->modified = u->modified ;
               p->undohead = x ;
            }
            Replace(hwnd,p,"",0) ;
            u->postselstart = u->postselend = start ;
            break ;
         case UNDO_DELETE:
            start = 0 ;
            if (keysym(u->data[0]))
               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++]) ;
#ifdef OLD_EDIT_FORMAT
               if (u->data[start] == '\r') {
                  p->selstartcharpos = p->selendcharpos = u->preselstart ;
                  undo_pchar(hwnd,p,u->data[start++]) ;
               }
#endif
            }
            if (u->len <= start) {
               p->modified = u->modified ;
               p->undohead = x ;
               if (u->max > 64) {
                  s = realloc(u->data,64) ;
                  if (s) {
                     u->data = s ;
                     u->max = 64 ;
                  }
              }
            } else {
               memcpy(u->data, u->data + start,u->len - start ) ;
               u->len -= start ;
            }
            p->selstartcharpos = p->selendcharpos = u->preselstart ;
            break ;
         case UNDO_BACKSPACE:
            p->selstartcharpos = p->selendcharpos = u->postselstart ;
            start = 0 ;
            if (keysym(u->data[0]))
               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++]) ;
               }
            }
            if (u->len <= start) {
               p->modified = u->modified ;
               p->undohead = x ;
               if (u->max > 64) {
                  s = realloc(u->data,64) ;
                  if (s) {
                     u->data = s ;
                     u->max = 64 ;
                  }
              }
            } else {
               memcpy(u->data, u->data + start,u->len - start ) ;
               u->len -= start ;
            }
            break ;
         case UNDO_MODIFY:
            p->inserting = FALSE ;
            start = 0 ;
            if (keysym(u->data[0]))
               while (keysym(u->data[start]) && u->len > start) {
                  u->postselstart-- ;
                  u->postselend-- ;
                  p->selstartcharpos = p->selendcharpos = u->postselstart ;
                  undo_pchar(hwnd,p,u->data[start++]) ;
               }
            else {
               u->postselstart-- ;
               u->postselend-- ;
               p->selstartcharpos = p->selendcharpos = u->postselstart ;
               undo_pchar(hwnd,p,u->data[start++]) ;
               if (u->data[start] == '\n') {
                  u->postselstart-- ;
                  u->postselend-- ;
                  p->selstartcharpos = p->selendcharpos = u->postselstart ;
                  undo_pchar(hwnd,p,u->data[start++]) ;
               }
            }
            if (u->len <= start) {
               p->modified = u->modified ;
               p->undohead = x ;
               if (u->max > 64) {
                  s = realloc(u->data,64) ;
                  if (s) {
                     u->data = s ;
                     u->max = 64 ;
                  }
               }
            } else {
               memcpy(u->data, u->data + start,u->len - start ) ;
               u->len -= start ;
            }
            p->selstartcharpos = p->selendcharpos = u->postselstart ;
            break ;
         case UNDO_INSERTSELECTION:
            p->selstartcharpos = u->postselstart ;
            p->selendcharpos = u->postselend ;
            Replace(hwnd,p,"",0) ;
            p->modified = u->modified ;
            p->undohead = x ;
            p->selstartcharpos = u->preselstart ;
            p->selendcharpos = u->preselend ;
            p->undohead = x ;
            break ;
         case UNDO_DELETESELECTION:
            p->selstartcharpos = u->postselstart ;
            p->selendcharpos = u->postselend ;
            Replace(hwnd,p,u->data,u->len) ;
            p->modified = u->modified ;
            p->undohead = x ;
            if (u->max > 64) {
               s = realloc(u->data,64) ;
               if (s) {
                  u->data = s ;
                  u->max = 64 ;
               }
            }
            p->selstartcharpos = u->preselstart ;
            p->selendcharpos = u->preselend ;
            p->undohead = x ;
            break ;
         case UNDO_CASECHANGE:
            p->selstartcharpos = u->postselstart ;
            p->selendcharpos = u->postselend ;
            p->modified = u->modified ;
            p->undohead = x ;
            for (start = 0; start < u->len; start++)
               p->text[p->selstartcharpos+start].ch = u->data[start] ;
            if (u->max > 64) {
               s = realloc(u->data,64) ;
               if (s) {
                  u->data = s ;
                  u->max = 64 ;
               }
            }
            p->selstartcharpos = u->preselstart ;
            p->selendcharpos = u->preselend ;
            break ;
      }
      p->inserting = oldinsert ;
      p->undoing-- ;
      p->sendchangeonpaint = TRUE;
      InvalidateRect(hwnd,0,0) ;
      ScrollCaretIntoView(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->text[i].ch = ucase ? toupper(p->text[i].ch) : tolower(p->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) ;
}
/**********************************************************************
 * exeditProc is the window proc for the edit control
 **********************************************************************/

LRESULT  CALLBACK exeditProc( HWND hwnd, UINT iMessage, WPARAM wParam,
																		LPARAM lParam)
{
   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 ;
	switch (iMessage) {                                 
      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))->inserting ;
      case WM_NOTIFY:
         lpnhead = (TOOLTIPTEXT *)lParam ;
         switch (lpnhead->hdr.code) {
            case TTN_NEEDTEXT : // tooltip
               if (selectedProject->buildFlags & BF_DEBUGTOOLTIPS) {
                  p = (EDITDATA *)GetWindowLong(hwnd,0) ;
                  x = (DWINFO *)GetWindowLong(GetParent(hwnd),0) ;
                  offset = GetBreakpointAddress(x->dwName,&p->ttlineno, TRUE) ;
                  if (FindSymbol(&dbg, &types,&syms,&offset,
                              StoppedRegs.Eip, offset, p->ttident)) {
                     var = GetVarInfo(dbg,types,syms,offset,p->ttident) ;
                     if (var) {
                        if (!var->udt) {
                           sprintf(buffer,"%s: ",p->ttident) ;
                           HintValue(types,var,buffer+ strlen(buffer)) ;
                           lpnhead->lpszText = buffer ;
                        }
                        FreeVarInfo(var) ;
                     }
                  }
               }
               break ;
         }
         break ;
      case WM_COMMAND:
         if (LOWORD(wParam) == IDM_CONTEXTHELP)
            DoHelp(hwnd) ;
         break ;   
		case WM_KEYDOWN:
			switch(wParam) {
				case VK_INSERT:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (!p->readonly) {
                  p->inserting = !p->inserting ;
                  if (p->hasfocus) {
                     SendMessage(hwnd,WM_KILLFOCUS,0,0) ;
                     SendMessage(hwnd,WM_SETFOCUS,0,0) ;
                  }
                  SendMessage(GetParent(hwnd),WM_SETCURSOR,0,0) ;
               }
				  break ;
				case VK_BACK:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (p->selstartcharpos == p->selendcharpos) {
                  if (p->selstartcharpos == 0)
                     break ;
                  left(hwnd,p) ;
               }
				case VK_DELETE:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (!p->readonly) {
                  i = CuttingComment(hwnd,p,p->text) ;
                  if (p->selstartcharpos != p->selendcharpos) {
                        Replace(hwnd,p,"",0) ;
                        ScrollCaretIntoView(hwnd,p) ;
                  } else
                     removechar(hwnd,p,wParam == VK_DELETE? UNDO_DELETE : UNDO_BACKSPACE) ;
                  CutComment(hwnd,p,p->text,wParam,i) ;
               }
               break ;
            case VK_RETURN:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (!p->readonly)
                  insertcr(hwnd,p) ;
               break ;
            case VK_TAB:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (GetKeyState(VK_SHIFT) & 0x80000000) {
                  if (!p->readonly) {
                     backtab(hwnd,p) ;
                     drawline(hwnd,p,p->selstartcharpos) ;
                  } else
                     left(hwnd,p) ;
               } else {
                  if (!p->readonly) {
                     inserttab(hwnd,p) ;
                     drawline(hwnd,p,p->selstartcharpos) ;
                  } else
                     right(hwnd,p) ;
               }
               break ;
				case VK_DOWN:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               upline(hwnd,p,1) ;
               break ;
				case VK_UP:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               upline(hwnd,p,-1) ;
               break ;
				case VK_PRIOR:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (GetKeyState(VK_CONTROL) & 0x80000000) {
                  VScrollPos(hwnd,0,TRUE) ;
                  p->textshowncharpos = p->selstartcharpos = p->selendcharpos = 0 ;
                  MoveCaret(hwnd,p) ;
                  InvalidateRect(hwnd,0,0) ;
               } else {
                  ClientArea(hwnd,p,&r) ;
                  i = r.bottom/p->txtFontHeight ;
                  upline(hwnd,p,2-i) ;
               }

               break ;
				case VK_NEXT:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               ClientArea(hwnd,p,&r) ;
               if (GetKeyState(VK_CONTROL) & 0x80000000) {
                  i = lfchars(p->text,p->textshowncharpos,p->textlen) ;
                  upline(hwnd,p,i) ;
               } else {
                  i = r.bottom/p->txtFontHeight ;
                  upline(hwnd,p,i-2) ;
               }
               break ;
				case VK_END:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (GetKeyState(VK_CONTROL) & 0x80000000) {
                  i = lfchars(p->text,p->textshowncharpos,p->textlen) ;
                  upline(hwnd,p,i) ;
               } else {
                  eol(hwnd,p) ;
               }
               break ;
				case VK_HOME:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (GetKeyState(VK_CONTROL) & 0x80000000) {
                  VScrollPos(hwnd,0,TRUE) ;
                  p->textshowncharpos = 0 ;
                  if (p->selecting)
                     p->selstartcharpos = 0 ;
                  else
                     p->selstartcharpos = p->selendcharpos = 0 ;
                  MoveCaret(hwnd,p) ;
                  SendUpdate(hwnd) ;
                  InvalidateRect(hwnd,0,0) ;
               } else {
                  sol(hwnd,p) ;
               }
               break ;
				case VK_LEFT:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (GetKeyState(VK_CONTROL) & 0x80000000)
                  leftword(hwnd,p) ;
               else
                  left(hwnd, p) ;
               break ;
				case VK_RIGHT:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (GetKeyState(VK_CONTROL) & 0x80000000)
                  rightword(hwnd,p) ;
               else 
                  right(hwnd, p) ;
               break ;
				case 'X':
                  p = (EDITDATA *)GetWindowLong(hwnd,0) ;
                  if (!p->readonly && GetKeyState(VK_CONTROL) & 0x80000000) {
                     SendMessage(hwnd,WM_CUT,0,0) ;
						}
						break ;
				case 'C': 
                  p = (EDITDATA *)GetWindowLong(hwnd,0) ;
                  if (GetKeyState(VK_CONTROL) & 0x80000000) {
                     SendMessage(hwnd,WM_COPY,0,0) ;
                  }
						break ;
				case 'V':
                  p = (EDITDATA *)GetWindowLong(hwnd,0) ;
                  if (!p->readonly && GetKeyState(VK_CONTROL) & 0x80000000) {
                     SendMessage(hwnd,WM_PASTE,0,0) ;
						}
						break ;
            case 'Z':
                  p = (EDITDATA *)GetWindowLong(hwnd,0) ;
                  if (!p->readonly && GetKeyState(VK_CONTROL) & 0x80000000) {
                     SendMessage(hwnd,WM_UNDO,0,0) ;
						}
						break ;
            case 'S':
                  p = (EDITDATA *)GetWindowLong(hwnd,0) ;
                  if (!p->readonly && GetKeyState(VK_CONTROL) & 0x80000000) {
                     if (p->modified)
                        SendMessage(GetParent(hwnd),WM_COMMAND,IDM_SAVE,0) ;
						}
                  break ;
            case VK_SHIFT:
                  p = (EDITDATA *)GetWindowLong(hwnd,0) ;
                  p->selecting = TRUE ;
                  break ;
			}
         PostMessage(GetParent(hwnd),WM_COMMAND,ID_REDRAWSTATUS,0) ;
			break ;
      case WM_KEYUP:
         if (wParam == VK_SHIFT) {
                  p = (EDITDATA *)GetWindowLong(hwnd,0) ;
                  p->selecting = FALSE ;
                  break ;
         }
         break ;
		case WM_CHAR:
			if (wParam >= ' ' && wParam < 127) {
            p = (EDITDATA *)GetWindowLong(hwnd,0) ;
            if (!p->readonly) {
               insertchar(hwnd,p,wParam) ;
               if (!p->inserting)
                  SendMessage(hwnd,WM_CLEAR,0,0) ;
               if (wParam == '*' || wParam == '/') {
                  if (!AddedComment(hwnd,p,p->text,wParam) && !p->nocolorize)
                     FormatLine(hwnd,p->text) ;
               } else   
                  if (!p->nocolorize)
                     FormatLine(hwnd,p->text) ;
               drawline(hwnd,p,p->selstartcharpos) ;
            } else
               right(hwnd,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->inserting ? 2 : p->txtFontWidth,p->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) ;
         free(p->text) ;
         p->textlen = 0 ;
         p->textmaxlen = 0 ;
         free(p->lastgottext) ;
         p->textshowncharpos = p->selstartcharpos = p->selendcharpos = 0 ;
         p->lastgottext = 0 ;
         if (!p->nocolorize)
            SendMessage(hwnd,EM_SETBKGNDCOLOR,0,backgroundColor) ;
         i = strlen((char *)lParam) ;
         if (p->text = (INTERNAL_CHAR *)calloc(i+1,sizeof(INTERNAL_CHAR))) {
            p->textlen = i ;
            p->textmaxlen = i ;
            for (i=0; i < p->textlen ; i++) {
               p->text[i].ch = ((char *)lParam)[i] ;
               p->text[i].color = p->defforeground ;
            }
            if (!p->nocolorize)
               FormatBuffer(p->text,0,p->textlen) ;
            SendMessage(hwnd,EM_GETLINECOUNT,0,0) ; // update scroll bar
            p->sendchangeonpaint = TRUE ;
            SendUpdate(hwnd) ;
            InvalidateRect(hwnd,0,0) ;
         }

         break ;
		case WM_CREATE:
         p = (EDITDATA *)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(EDITDATA)) ;
         SetWindowLong(hwnd,0, (int)p) ;
         p->inserting = TRUE ;
         p->tooltip = CreateWindowEx(WS_EX_TOPMOST,TOOLTIPS_CLASS,"",
                  WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
                  CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
                  hwnd,0,hInstance,0) ;
         SetWindowPos(p->tooltip,HWND_TOPMOST,0,0,0,0,
                  SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE) ;
         
         t1.cbSize = sizeof(TOOLINFO) ;
         t1.uFlags = TTF_TRACK | TTF_ABSOLUTE ;
         t1.hwnd = hwnd ;
         t1.uId = 10000 ;
         t1.rect.left = 0 ;
         t1.rect.right = 0 ;
         t1.rect.top = 0 ;
         t1.rect.bottom = 0 ;
         t1.hinst = hInstance ;
         t1.lpszText = LPSTR_TEXTCALLBACK ;
         SendMessage(p->tooltip,TTM_ADDTOOL,0,(LPARAM)&t1) ;

         xfont = CreateFontIndirect(&EditFont) ;
         SendMessage(hwnd,WM_SETFONT,(WPARAM)xfont,0) ;
         p->defbackground = backgroundColor ;
         p->defforeground = textColor ;
         p->hbrBackground = CreateSolidBrush(backgroundColor) ;
         p->tabs = tabs ;
         p->leftmargin = EC_LEFTMARGIN ;
         if (p->text = (INTERNAL_CHAR *)calloc(65,sizeof(INTERNAL_CHAR))) {
#ifdef OLD_EDIT_FORMAT
            p->textlen = 2 ;
#else
            p->textlen = 0 ;
#endif
            p->textmaxlen = 64 ;
            for (i=0; i < p->textlen ; i++) {
               p->text[i].color = p->defforeground ;
            }
#ifdef OLD_EDIT_FORMAT
            p->text[0].ch = '\r' ;
            p->text[1].ch = '\n' ;
#endif
         }
         SetScrollRange(hwnd,SB_HORZ,0,MAX_HSCROLL,TRUE) ;
         break ;
		case WM_SETEDITORSETTINGS:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         if (!p->nocolorize) {
            SendMessage(hwnd,EM_SETBKGNDCOLOR,0,backgroundColor) ;
            stop = p->tabs * 4 ;
            SendMessage(hwnd,EM_SETTABSTOPS,1,(LPARAM)&stop) ;
            Colorize(p->text,0,p->textlen,p->defforeground,0) ;
            FormatBuffer(p->text,0,p->textlen) ;
         }
         return 0 ;
		case WM_DESTROY:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         for (i= 0 ; i < UNDO_MAX; i++)
            free(p->undolist[i].data) ;
         DestroyWindow(p->tooltip) ;
         DeleteObject(p->hFont) ;
         DeleteObject(p->hItalicFont) ;
         DeleteObject(p->hBoldFont) ;
         DeleteObject(p->hbrBackground) ;
         free(p->text) ;
         free(p->lastgottext) ;
         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->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->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) ;
					return 0 ;
				case SB_THUMBTRACK :
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               
               TrackVScroll(hwnd,p,FALSE) ;
					return 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->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->txtFontWidth ;
               scrollleft(hwnd,p,i-1);
					break ;
            case SB_PAGELEFT:
               ClientArea(hwnd,p,&r) ;
               i = r.right/p->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_MOUSEMOVE:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         ClientArea(hwnd,p,&r) ;
         pt.x = LOWORD(lParam) ;
         pt.y = (int)(short)HIWORD(lParam) ;
         if (p->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)) ;
         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) ;
         return 0 ;
      case WM_RBUTTONUP:
      case WM_MBUTTONUP:
//         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         return 0 ;
      case WM_LBUTTONDOWN:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         if (GetFocus() != hwnd)
            SetFocus(hwnd) ;
         p->selecting = TRUE ;
         if (p->selstartcharpos != p->selendcharpos) {
            InvalidateRect(hwnd,0,0) ;
         }
         p->buttondown = TRUE ;
         pt.x = LOWORD(lParam) ;
         pt.y = HIWORD(lParam) ;
         p->selstartcharpos = p->selendcharpos = charfrompos(hwnd,p,&pt) ;
         MoveCaret(hwnd,p) ;
         SetCapture(hwnd) ;
         break ;
      case WM_LBUTTONUP:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         if (p->buttondown) {
            p->selecting = !!(GetKeyState(VK_SHIFT) & 0x80000000) ;
            p->buttondown = FALSE ;
            ReleaseCapture() ;
            EndAutoScroll(hwnd,p) ;
         }
         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:
         return GetWordFromPos(hwnd,(char *)lParam,-1,0,0,0) ;
      case EM_SETBKGNDCOLOR:
               p = (EDITDATA *)GetWindowLong(hwnd,0) ;
               if (wParam) {
                  p->defbackground = GetSysColor(COLOR_WINDOW) ;
               } else {
                  
                  p->defbackground = lParam ;
               }
               DeleteObject(p->hbrBackground) ;
               p->hbrBackground = CreateSolidBrush(lParam) ;
         break ;
      case WM_GETTEXT:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         if (!p->textlen)
            return 0 ;
         i = p->textlen ;
         if (wParam <= i)
            i = wParam - 1 ;
         {
            INTERNAL_CHAR *x = p->text ;
            while (i--)
               *((char *)lParam)++ = x++->ch ;
            *(char *)lParam = 0 ;
         }
         return i ;
      case WM_GETTEXTLENGTH:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         return p->textlen+1 ;
      case EM_SETSEL:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         if (wParam == 0 && lParam == -1) {
            p->selstartcharpos = 0 ;
            p->selendcharpos = p->textlen ;
         } else if (wParam == -1) {
            p->selendcharpos = p->selstartcharpos ;
         } else {
            p->selstartcharpos = wParam ;
            p->selendcharpos = lParam ;
         }
         if (!p->nosel) {
            ScrollCaretIntoView(hwnd,p) ;
            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->nosel = wParam ;
         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) ;
         return 0 ;
      case EM_LINEFROMCHAR:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         return LineFromChar(p,wParam) ;
      case EM_LINEINDEX:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         rv = 0 ;
         ic = p->text ;
         while (ic < p->text + p->textlen && wParam) {
            if (ic->ch == '\n')
               wParam--;
            ic++ ;
            rv++ ;
         }
         return rv ;
      case EM_GETLINE:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         rv = 0 ;
         ic = p->text ;
         while (ic < p->text + p->textlen && wParam) {
            if (ic->ch == '\n')
               wParam--;
            ic++ ;
            rv++ ;
         }
         if (ic >= p->text + p->textlen)
            return 0 ;
         l = *(short *)lParam ;
         for (i=0 ; i < l && ic < p->text +p->textlen; i++,ic++ ) {
            *(char *)(lParam + i) = ic->ch ;
#ifdef OLD_EDIT_FORMAT
            if (ic->ch == '\r'|| ic->ch == '\n')
#else
            if (ic->ch == '\n')
#endif
               break ;
         }
         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->text)
            return 0 ;
         ic = p->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->tabs = (*(int *)lParam)/4 ;
         return 0 ;
      case EM_SETMARGINS:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         p->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->modified ;
      case EM_SETMODIFY:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         p->modified = wParam ;
         return 0 ;
      case EM_GETFIRSTVISIBLELINE:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         return SendMessage(hwnd,EM_LINEFROMCHAR,p->textshowncharpos,0) ;
      case EM_GETRECT:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         ClientArea(hwnd,p,(LPRECT)lParam) ;
         ((LPRECT)lParam)->left += p->leftmargin ;
         return 0 ;
      case EM_CANUNDO:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         return p->undohead != p->undotail ;
      case WM_UNDO:
      case EM_UNDO:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         doundo(hwnd,p) ;
         return 0 ;
      case WM_CUT:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         SelToClipboard(hwnd,p) ;
         Replace(hwnd,p,"",0) ;
         ScrollCaretIntoView(hwnd,p) ;
         p->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->selecting = FALSE ;
         break ;
      case WM_SETFONT:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         DeleteObject(p->hFont) ;
         DeleteObject(p->hBoldFont) ;
         DeleteObject(p->hItalicFont) ;
         p->hFont = (HFONT)wParam ;
            dc = GetDC(hwnd) ;
            xfont = SelectObject(dc,p->hFont) ;
            GetTextMetrics(dc,&t) ;
            SelectObject(dc,xfont) ;

				ReleaseDC(hwnd,dc) ;
            p->txtFontHeight = t.tmHeight ;
            p->txtFontWidth = t.tmAveCharWidth ;
            GetObject(p->hFont,sizeof(LOGFONT),&lf) ;
            lf.lfItalic = TRUE ;
            p->hItalicFont = CreateFontIndirect(&lf) ;
            lf.lfItalic = FALSE ;
            lf.lfWeight = FW_BOLD ;
            p->hBoldFont = CreateFontIndirect(&lf) ;
         if (lParam) {
            SendUpdate(hwnd) ;
            InvalidateRect(hwnd,0,0) ;
         }
         break ;
      case EM_GETTEXTHEIGHT:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         return p->txtFontHeight ;
      case EM_NOCOLORIZE:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         p->nocolorize = TRUE ;
//         DeleteObject(p->hbrBackground) ;
//         p->defbackground = GetSysColor(COLOR_WINDOW) ;
//         p->defforeground = GetSysColor(COLOR_WINDOWTEXT) ;
//         p->hbrBackground = CreateSolidBrush(p->defbackground) ;
         break ;
      case EM_GETCOLUMN: 
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         return curcol(p,p->text,p->selstartcharpos) ;
      case EM_SETREADONLY:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         p->readonly = !!wParam ;
         return 1 ;
      case EM_GETREADONLY:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         return p->readonly ;
      case EM_TOUPPER:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         upperlowercase(hwnd,p,TRUE) ;
         break ;
      case EM_TOLOWER:
         p = (EDITDATA *)GetWindowLong(hwnd,0) ;
         upperlowercase(hwnd,p,FALSE) ;
         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_ARROW) ;
      wc.hbrBackground = 0 ;
		wc.lpszMenuName = 0 ;
		RegisterClass(&wc) ;

      backgroundColor = GetSysColor(COLOR_WINDOW) ;
      textColor = GetSysColor(COLOR_WINDOWTEXT) ;
}