Initial release of Maemo 5 port of gnuplot
[gnuplot] / src / win / wtext.c
diff --git a/src/win/wtext.c b/src/win/wtext.c
new file mode 100644 (file)
index 0000000..bdd1dc9
--- /dev/null
@@ -0,0 +1,1973 @@
+#ifndef lint
+static char *RCSid() { return RCSid("$Id: wtext.c,v 1.15.2.2 2008/05/29 20:01:25 sfeam Exp $"); }
+#endif
+
+/* GNUPLOT - win/wtext.c */
+/*[
+ * Copyright 1992, 1993, 1998, 2004   Russell Lang
+ *
+ * Permission to use, copy, and distribute this software and its
+ * documentation for any purpose with or without fee is hereby granted,
+ * provided that the above copyright notice appear in all copies and
+ * that both that copyright notice and this permission notice appear
+ * in supporting documentation.
+ *
+ * Permission to modify the software is granted, but not the right to
+ * distribute the complete modified source code.  Modifications are to
+ * be distributed as patches to the released version.  Permission to
+ * distribute binaries produced by compiling modified sources is granted,
+ * provided you
+ *   1. distribute the corresponding source modifications from the
+ *    released version in the form of a patch file along with the binaries,
+ *   2. add special version identification to distinguish your version
+ *    in addition to the base release version number,
+ *   3. provide your name and address as the primary contact for the
+ *    support of your modified version, and
+ *   4. retain our contact information in regard to use of the base
+ *    software.
+ * Permission to distribute the released version of the source code along
+ * with corresponding source modifications in the form of a patch file is
+ * granted with same provisions 2 through 4 for binary distributions.
+ *
+ * This software is provided "as is" without express or implied warranty
+ * to the extent permitted by applicable law.
+]*/
+
+/*
+ * AUTHORS
+ *
+ *   Russell Lang
+ */
+
+/* WARNING: Do not write to stdout/stderr with functions not listed
+   in win/wtext.h */
+
+#define STRICT
+
+#include <string.h>    /* use only far items */
+#include <stdlib.h>
+#include <ctype.h>
+#include <dos.h>
+#ifndef __MSC__
+# include <mem.h>
+#endif
+
+#ifdef WIN32
+/* needed for mouse scroll wheel support */
+#define _WIN32_WINNT 0x0400
+#endif
+
+#include <windows.h>
+#include <windowsx.h>
+#if WINVER >= 0x030a
+# include <commdlg.h>
+#endif
+
+#include "wgnuplib.h"
+#include "wresourc.h"
+#include "wcommon.h"
+
+/* font stuff */
+#define TEXTFONTSIZE 9
+#define TEXTFONTNAME "Terminal"
+
+#ifndef EOF /* HBB 980809: for MinGW32 */
+#define EOF -1         /* instead of using <stdio.h> */
+#endif
+/* limits */
+#define MAXSTR 256
+POINT ScreenMinSize = {16,4};
+
+LRESULT CALLBACK WINEXPORT WndParentProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+LRESULT CALLBACK WINEXPORT WndTextProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+void ReadTextIni(LPTW lptw);
+void LimitMark(LPTW lptw, POINT FAR *lppt);
+
+char szNoMemory[] = "out of memory";
+COLORREF TextColorTable[16] = {
+       RGB(0,0,0),                     /* black */
+       RGB(0,0,128),           /* dark blue */
+       RGB(0,128,0),           /* dark green */
+       RGB(0,128,128),         /* dark cyan */
+       RGB(128,0,0),           /* dark red */
+       RGB(128,0,128),         /* dark magenta */
+       RGB(128,128,0),         /* dark yellow */
+       RGB(128,128,128),       /* dark grey */
+       RGB(192,192,192),       /* light grey */
+       RGB(0,0,255),           /* blue */
+       RGB(0,255,0),           /* green */
+       RGB(0,255,255),         /* cyan */
+       RGB(255,0,0),           /* red */
+       RGB(255,0,255),         /* magenta */
+       RGB(255,255,0),         /* yellow */
+       RGB(255,255,255),       /* white */
+};
+#define NOTEXT 0xF0
+#define MARKFORE RGB(255,255,255)
+#define MARKBACK RGB(0,0,128)
+#define TextFore(attr) TextColorTable[(attr) & 15]
+#define TextBack(attr) TextColorTable[(attr>>4) & 15]
+
+
+void WDPROC
+TextMessage()
+{
+    MSG msg;
+
+    while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) {
+#if 1 /* HBB 19990505: Petzold says we should check this: */
+        if (msg.message == WM_QUIT)
+            return;
+#endif
+        TranslateMessage(&msg);
+        DispatchMessage(&msg);
+    }
+}
+
+
+
+void
+CreateTextClass(LPTW lptw)
+{
+    WNDCLASS wndclass;
+
+#ifdef WIN32
+    hdllInstance = lptw->hInstance;    /* not using a DLL */
+#endif
+    wndclass.style = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc = WndTextProc;
+    wndclass.cbClsExtra = 0;
+    wndclass.cbWndExtra = 2 * sizeof(void FAR *);
+    wndclass.hInstance = lptw->hInstance;
+    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wndclass.hbrBackground = NULL;
+    lptw->hbrBackground = CreateSolidBrush(lptw->bSysColors ?
+                                          GetSysColor(COLOR_WINDOW) : RGB(0,0,0));
+    wndclass.lpszMenuName = NULL;
+    wndclass.lpszClassName = szTextClass;
+    RegisterClass(&wndclass);
+
+    wndclass.style = CS_HREDRAW | CS_VREDRAW;
+    wndclass.lpfnWndProc = WndParentProc;
+    wndclass.cbClsExtra = 0;
+    wndclass.cbWndExtra = 2 * sizeof(void FAR *);
+    wndclass.hInstance = lptw->hInstance;
+    if (lptw->hIcon)
+       wndclass.hIcon = lptw->hIcon;
+    else
+       wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+    wndclass.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH);
+    wndclass.lpszMenuName = NULL;
+    wndclass.lpszClassName = szParentClass;
+    RegisterClass(&wndclass);
+}
+
+
+/* make text window */
+int WDPROC
+TextInit(LPTW lptw)
+{
+    RECT rect;
+    HMENU sysmenu;
+    HGLOBAL hglobal;
+
+    ReadTextIni(lptw);
+
+    if (!lptw->hPrevInstance)
+       CreateTextClass(lptw);
+
+    if (lptw->KeyBufSize == 0)
+       lptw->KeyBufSize = 256;
+
+    if (lptw->ScreenSize.x < ScreenMinSize.x)
+       lptw->ScreenSize.x = ScreenMinSize.x;
+    if (lptw->ScreenSize.y < ScreenMinSize.y)
+       lptw->ScreenSize.y = ScreenMinSize.y;
+
+    lptw->CursorPos.x = lptw->CursorPos.y = 0;
+    lptw->bFocus = FALSE;
+    lptw->bGetCh = FALSE;
+    lptw->CaretHeight = 0;
+    if (!lptw->nCmdShow)
+       lptw->nCmdShow = SW_SHOWNORMAL;
+    if (!lptw->Attr)
+       lptw->Attr = 0xf0;      /* black on white */
+
+    hglobal = GlobalAlloc(GHND, lptw->ScreenSize.x * lptw->ScreenSize.y);
+    lptw->ScreenBuffer = (BYTE FAR *)GlobalLock(hglobal);
+    if (lptw->ScreenBuffer == (BYTE FAR *)NULL) {
+       MessageBox((HWND)NULL,szNoMemory,(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
+       return(1);
+    }
+    _fmemset(lptw->ScreenBuffer, ' ', lptw->ScreenSize.x * lptw->ScreenSize.y);
+    hglobal = GlobalAlloc(GHND, lptw->ScreenSize.x * lptw->ScreenSize.y);
+    lptw->AttrBuffer = (BYTE FAR *)GlobalLock(hglobal);
+    if (lptw->AttrBuffer == (BYTE FAR *)NULL) {
+       MessageBox((HWND)NULL,szNoMemory,(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
+       return(1);
+    }
+    _fmemset(lptw->AttrBuffer, NOTEXT, lptw->ScreenSize.x * lptw->ScreenSize.y);
+    hglobal = GlobalAlloc(LHND, lptw->KeyBufSize);
+    lptw->KeyBuf = (BYTE FAR *)GlobalLock(hglobal);
+    if (lptw->KeyBuf == (BYTE FAR *)NULL) {
+       MessageBox((HWND)NULL,szNoMemory,(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
+       return(1);
+    }
+    lptw->KeyBufIn = lptw->KeyBufOut = lptw->KeyBuf;
+
+    lptw->hWndParent = CreateWindow(szParentClass, lptw->Title,
+                                   WS_OVERLAPPEDWINDOW,
+                                   lptw->Origin.x, lptw->Origin.y,
+                                   lptw->Size.x, lptw->Size.y,
+                                   NULL, NULL, lptw->hInstance, lptw);
+    if (lptw->hWndParent == (HWND)NULL) {
+       MessageBox((HWND)NULL,"Couldn't open parent text window",(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
+       return(1);
+    }
+    GetClientRect(lptw->hWndParent, &rect);
+
+    lptw->hWndText = CreateWindow(szTextClass, lptw->Title,
+                                 WS_CHILD | WS_VSCROLL | WS_HSCROLL,
+                                 0, lptw->ButtonHeight,
+                                 rect.right, rect.bottom-lptw->ButtonHeight,
+                                 lptw->hWndParent, NULL, lptw->hInstance, lptw);
+    if (lptw->hWndText == (HWND)NULL) {
+       MessageBox((HWND)NULL,"Couldn't open text window",(LPSTR)NULL, MB_ICONHAND | MB_SYSTEMMODAL);
+       return(1);
+    }
+
+    lptw->hPopMenu = CreatePopupMenu();
+    AppendMenu(lptw->hPopMenu, MF_STRING, M_COPY_CLIP, "&Copy to Clipboard\tCtrl-Ins");
+    AppendMenu(lptw->hPopMenu, MF_STRING, M_PASTE, "&Paste\tShift-Ins");
+    AppendMenu(lptw->hPopMenu, MF_SEPARATOR, 0, NULL);
+#if WINVER >= 0x030a
+    AppendMenu(lptw->hPopMenu, MF_STRING, M_CHOOSE_FONT, "Choose &Font...");
+#endif
+    AppendMenu(lptw->hPopMenu, MF_STRING | (lptw->bSysColors ? MF_CHECKED : MF_UNCHECKED),
+              M_SYSCOLORS, "&System Colors");
+    if (lptw->IniFile != (LPSTR)NULL) {
+       char buf[MAX_PATH+80];
+       wsprintf(buf,"&Update %s", lptw->IniFile);
+       AppendMenu(lptw->hPopMenu, MF_STRING, M_WRITEINI, (LPSTR)buf);
+    }
+
+    sysmenu = GetSystemMenu(lptw->hWndParent,0);       /* get the sysmenu */
+    AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
+    AppendMenu(sysmenu, MF_POPUP, (UINT)lptw->hPopMenu, "&Options");
+    AppendMenu(sysmenu, MF_STRING, M_ABOUT, "&About");
+
+    if (lptw->lpmw)
+       LoadMacros(lptw);
+
+    ShowWindow(lptw->hWndText, SW_SHOWNORMAL);
+    BringWindowToTop(lptw->hWndText);
+    SetFocus(lptw->hWndText);
+    TextMessage();
+    return(0);
+}
+
+/* close a text window */
+void WDPROC
+TextClose(LPTW lptw)
+{
+    HGLOBAL hglobal;
+
+    /* close window */
+    if (lptw->hWndParent)
+       DestroyWindow(lptw->hWndParent);
+    TextMessage();
+
+    hglobal = (HGLOBAL)GlobalHandle( SELECTOROF(lptw->ScreenBuffer) );
+    if (hglobal) {
+       GlobalUnlock(hglobal);
+       GlobalFree(hglobal);
+    }
+    hglobal = (HGLOBAL)GlobalHandle( SELECTOROF(lptw->AttrBuffer) );
+    if (hglobal) {
+       GlobalUnlock(hglobal);
+       GlobalFree(hglobal);
+    }
+    hglobal = (HGLOBAL)GlobalHandle( SELECTOROF(lptw->KeyBuf) );
+    if (hglobal) {
+       GlobalUnlock(hglobal);
+       GlobalFree(hglobal);
+    }
+
+    if (lptw->lpmw)
+       CloseMacros(lptw);
+    lptw->hWndParent = (HWND)NULL;
+}
+
+void
+WriteTextIni(LPTW lptw)
+{
+    RECT rect;
+    LPSTR file = lptw->IniFile;
+    LPSTR section = lptw->IniSection;
+    char profile[80];
+    int iconic;
+
+
+    if ((file == (LPSTR)NULL) || (section == (LPSTR)NULL))
+       return;
+
+    iconic = IsIconic(lptw->hWndParent);
+    if (iconic)
+       ShowWindow(lptw->hWndParent, SW_SHOWNORMAL);
+    GetWindowRect(lptw->hWndParent,&rect);
+    wsprintf(profile, "%d %d", rect.left, rect.top);
+    WritePrivateProfileString(section, "TextOrigin", profile, file);
+    wsprintf(profile, "%d %d", rect.right-rect.left, rect.bottom-rect.top);
+    WritePrivateProfileString(section, "TextSize", profile, file);
+    wsprintf(profile, "%d", iconic);
+    WritePrivateProfileString(section, "TextMinimized", profile, file);
+    wsprintf(profile, "%s,%d", lptw->fontname, lptw->fontsize);
+    WritePrivateProfileString(section, "TextFont", profile, file);
+    wsprintf(profile, "%d", lptw->bSysColors);
+    WritePrivateProfileString(section, "SysColors", profile, file);
+    if (iconic)
+       ShowWindow(lptw->hWndParent, SW_SHOWMINIMIZED);
+    return;
+}
+
+void
+ReadTextIni(LPTW lptw)
+{
+    LPSTR file = lptw->IniFile;
+    LPSTR section = lptw->IniSection;
+    char profile[81];
+    LPSTR p;
+    BOOL bOKINI;
+
+    bOKINI = (file != (LPSTR)NULL) && (section != (LPSTR)NULL);
+    profile[0] = '\0';
+
+    if (bOKINI)
+       GetPrivateProfileString(section, "TextOrigin", "", profile, 80, file);
+    if ( (p = GetInt(profile, (LPINT)&lptw->Origin.x)) == NULL)
+       lptw->Origin.x = CW_USEDEFAULT;
+    if ( (p = GetInt(p, (LPINT)&lptw->Origin.y)) == NULL)
+       lptw->Origin.y = CW_USEDEFAULT;
+    if ( (file != (LPSTR)NULL) && (section != (LPSTR)NULL) )
+       GetPrivateProfileString(section, "TextSize", "", profile, 80, file);
+    if ( (p = GetInt(profile, (LPINT)&lptw->Size.x)) == NULL)
+       lptw->Size.x = CW_USEDEFAULT;
+    if ( (p = GetInt(p, (LPINT)&lptw->Size.y)) == NULL)
+       lptw->Size.y = CW_USEDEFAULT;
+
+    if (bOKINI)
+       GetPrivateProfileString(section, "TextFont", "", profile, 80, file);
+    {
+       char FAR *size;
+       size = _fstrchr(profile,',');
+       if (size) {
+           *size++ = '\0';
+           if ( (p = GetInt(size, &lptw->fontsize)) == NULL)
+               lptw->fontsize = TEXTFONTSIZE;
+       }
+       _fstrcpy(lptw->fontname, profile);
+       if (lptw->fontsize == 0)
+           lptw->fontsize = TEXTFONTSIZE;
+       if (!(*lptw->fontname))
+           _fstrcpy(lptw->fontname,TEXTFONTNAME);
+    }
+
+    if (bOKINI) {
+       int iconic;
+       GetPrivateProfileString(section, "TextMinimized", "", profile, 80, file);
+       if ((p = GetInt(profile, &iconic)) == NULL)
+           iconic = 0;
+       if (iconic)
+           lptw->nCmdShow = SW_SHOWMINIMIZED;
+    }
+    lptw->bSysColors = FALSE;
+    GetPrivateProfileString(section, "SysColors", "", profile, 80, file);
+    if ((p = GetInt(profile, &lptw->bSysColors)) == NULL)
+       lptw->bSysColors = 0;
+}
+
+
+/* Bring Cursor into text window */
+void WDPROC
+TextToCursor(LPTW lptw)
+{
+    int nXinc=0;
+    int nYinc=0;
+    int cxCursor;
+    int cyCursor;
+
+    cyCursor = lptw->CursorPos.y * lptw->CharSize.y;
+    if ( (cyCursor + lptw->CharSize.y > lptw->ScrollPos.y + lptw->ClientSize.y)
+        || (cyCursor < lptw->ScrollPos.y) ) {
+       nYinc = max(0, cyCursor + lptw->CharSize.y - lptw->ClientSize.y) - lptw->ScrollPos.y;
+       nYinc = min(nYinc, lptw->ScrollMax.y - lptw->ScrollPos.y);
+    }
+    cxCursor = lptw->CursorPos.x * lptw->CharSize.x;
+    if ( (cxCursor + lptw->CharSize.x > lptw->ScrollPos.x + lptw->ClientSize.x)
+        || (cxCursor < lptw->ScrollPos.x) ) {
+       nXinc = max(0, cxCursor + lptw->CharSize.x - lptw->ClientSize.x/2) - lptw->ScrollPos.x;
+       nXinc = min(nXinc, lptw->ScrollMax.x - lptw->ScrollPos.x);
+    }
+    if (nYinc || nXinc) {
+       lptw->ScrollPos.y += nYinc;
+       lptw->ScrollPos.x += nXinc;
+       ScrollWindow(lptw->hWndText,-nXinc,-nYinc,NULL,NULL);
+       SetScrollPos(lptw->hWndText,SB_VERT,lptw->ScrollPos.y,TRUE);
+       SetScrollPos(lptw->hWndText,SB_HORZ,lptw->ScrollPos.x,TRUE);
+       UpdateWindow(lptw->hWndText);
+    }
+}
+
+void
+NewLine(LPTW lptw)
+{
+    lptw->CursorPos.x = 0;
+    lptw->CursorPos.y++;
+    if (lptw->CursorPos.y >= lptw->ScreenSize.y) {
+       int i =  lptw->ScreenSize.x * (lptw->ScreenSize.y - 1);
+       _fmemmove(lptw->ScreenBuffer, lptw->ScreenBuffer+lptw->ScreenSize.x, i);
+       _fmemset(lptw->ScreenBuffer + i, ' ', lptw->ScreenSize.x);
+       _fmemmove(lptw->AttrBuffer, lptw->AttrBuffer+lptw->ScreenSize.x, i);
+       _fmemset(lptw->AttrBuffer + i, NOTEXT, lptw->ScreenSize.x);
+       lptw->CursorPos.y--;
+       ScrollWindow(lptw->hWndText,0,-lptw->CharSize.y,NULL,NULL);
+       lptw->MarkBegin.y--;
+       lptw->MarkEnd.y--;
+       LimitMark(lptw, &lptw->MarkBegin);
+       LimitMark(lptw, &lptw->MarkEnd);
+       UpdateWindow(lptw->hWndText);
+    }
+    if (lptw->CursorFlag)
+       TextToCursor(lptw);
+    TextMessage();
+}
+
+/* Update count characters in window at cursor position */
+/* Updates cursor position */
+void
+UpdateText(LPTW lptw, int count)
+{
+    HDC hdc;
+    int xpos, ypos;
+
+    xpos = lptw->CursorPos.x*lptw->CharSize.x - lptw->ScrollPos.x;
+    ypos = lptw->CursorPos.y*lptw->CharSize.y - lptw->ScrollPos.y;
+    hdc = GetDC(lptw->hWndText);
+    if (lptw->bSysColors) {
+       SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
+       SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
+    } else {
+       SetTextColor(hdc, TextFore(lptw->Attr));
+       SetBkColor(hdc, TextBack(lptw->Attr));
+    }
+    SelectObject(hdc, lptw->hfont);
+    TextOut(hdc,xpos,ypos,
+           (LPSTR)(lptw->ScreenBuffer + lptw->CursorPos.y*lptw->ScreenSize.x +
+                   lptw->CursorPos.x), count);
+    ReleaseDC(lptw->hWndText,hdc);
+    lptw->CursorPos.x += count;
+    if (lptw->CursorPos.x >= lptw->ScreenSize.x)
+       NewLine(lptw);
+}
+
+int WDPROC
+TextPutCh(LPTW lptw, BYTE ch)
+{
+int pos;
+       switch(ch) {
+               case '\r':
+                       lptw->CursorPos.x = 0;
+                       if (lptw->CursorFlag)
+                               TextToCursor(lptw);
+                       break;
+               case '\n':
+                       NewLine(lptw);
+                       break;
+               case 7:
+                       MessageBeep(0xFFFFFFFF);
+                       if (lptw->CursorFlag)
+                               TextToCursor(lptw);
+                       break;
+               case '\t':
+                       {
+                       int n;
+                               for ( n = 8 - (lptw->CursorPos.x % 8); n>0; n-- )
+                                       TextPutCh(lptw, ' ');
+                       }
+                       break;
+               case 0x08:
+               case 0x7f:
+                       lptw->CursorPos.x--;
+                       if (lptw->CursorPos.x < 0) {
+                               lptw->CursorPos.x = lptw->ScreenSize.x - 1;
+                               lptw->CursorPos.y--;
+                       }
+                       if (lptw->CursorPos.y < 0)
+                               lptw->CursorPos.y = 0;
+                       break;
+               default:
+                       pos = lptw->CursorPos.y*lptw->ScreenSize.x + lptw->CursorPos.x;
+                       lptw->ScreenBuffer[pos] = ch;
+                       lptw->AttrBuffer[pos] = lptw->Attr;
+                       UpdateText(lptw, 1);
+       }
+       return ch;
+}
+
+void
+TextPutStr(LPTW lptw, LPSTR str)
+{
+    BYTE FAR *p, FAR *pa;
+    int count, limit;
+
+    while (*str) {
+       p = lptw->ScreenBuffer + lptw->CursorPos.y*lptw->ScreenSize.x + lptw->CursorPos.x;
+       pa = lptw->AttrBuffer + lptw->CursorPos.y*lptw->ScreenSize.x + lptw->CursorPos.x;
+       limit = lptw->ScreenSize.x - lptw->CursorPos.x;
+       for (count=0; (count < limit) && *str && (isprint(*str) || *str=='\t'); count++) {
+           if (*str=='\t') {
+               int n;
+
+               for ( n = 8 - ((lptw->CursorPos.x+count) % 8); (count < limit) & (n>0); n--, count++ ) {
+                   *p++ = ' ';
+                   *pa++ = lptw->Attr;
+               }
+               str++;
+               count--;
+           } else {
+               *p++ = *str++;
+               *pa++ = lptw->Attr;
+           }
+       }
+       if (count>0) {
+           UpdateText(lptw, count);
+       }
+       if (*str=='\n') {
+           NewLine(lptw);
+           str++;
+       } else if (*str && !isprint(*str) && *str!='\t') {
+           TextPutCh(lptw, *str++);
+       }
+    }
+}
+
+
+void
+LimitMark(LPTW lptw, POINT FAR *lppt)
+{
+    if (lppt->x < 0)
+       lppt->x = 0;
+    if (lppt->y < 0) {
+       lppt->x = 0;
+       lppt->y = 0;
+    }
+    if (lppt->x > lptw->ScreenSize.x)
+       lppt->x = lptw->ScreenSize.x;
+    if (lppt->y >= lptw->ScreenSize.y) {
+       lppt->x = 0;
+       lppt->y = lptw->ScreenSize.y;
+    }
+}
+
+void
+ClearMark(LPTW lptw, POINT pt)
+{
+    RECT rect1, rect2, rect3;
+    int tmp;
+
+    if ((lptw->MarkBegin.x != lptw->MarkEnd.x) ||
+       (lptw->MarkBegin.y != lptw->MarkEnd.y) ) {
+       if (lptw->MarkBegin.x > lptw->MarkEnd.x) {
+           tmp = lptw->MarkBegin.x;
+           lptw->MarkBegin.x = lptw->MarkEnd.x;
+           lptw->MarkEnd.x = tmp;
+       }
+       if (lptw->MarkBegin.y > lptw->MarkEnd.y) {
+           tmp = lptw->MarkBegin.y;
+           lptw->MarkBegin.y = lptw->MarkEnd.y;
+           lptw->MarkEnd.y = tmp;
+       }
+       /* calculate bounding rectangle in character coordinates */
+       if (lptw->MarkBegin.y != lptw->MarkEnd.y) {
+           rect1.left = 0;
+           rect1.right = lptw->ScreenSize.x;
+       } else {
+           rect1.left = lptw->MarkBegin.x;
+           rect1.right = lptw->MarkEnd.x + 1;
+       }
+       rect1.top = lptw->MarkBegin.y;
+       rect1.bottom = lptw->MarkEnd.y + 1;
+       /* now convert to client coordinates */
+       rect1.left   = rect1.left   * lptw->CharSize.x - lptw->ScrollPos.x;
+       rect1.right  = rect1.right  * lptw->CharSize.x - lptw->ScrollPos.x;
+       rect1.top    = rect1.top    * lptw->CharSize.y - lptw->ScrollPos.y;
+       rect1.bottom = rect1.bottom * lptw->CharSize.y - lptw->ScrollPos.y;
+       /* get client rect and calculate intersection */
+       GetClientRect(lptw->hWndText, &rect2);
+       IntersectRect(&rect3,  &rect1, &rect2);
+       /* update window if necessary */
+       if (!IsRectEmpty(&rect3)) {
+           InvalidateRect(lptw->hWndText, &rect3, TRUE);
+       }
+    }
+    LimitMark(lptw, &pt);
+    lptw->MarkBegin.x = lptw->MarkEnd.x = pt.x;
+    lptw->MarkBegin.y = lptw->MarkEnd.y = pt.y;
+    UpdateWindow(lptw->hWndText);
+}
+
+
+/* output a line including attribute changes as needed */
+void
+DoLine(LPTW lptw, HDC hdc, int xpos, int ypos, int offset, int count)
+{
+    BYTE FAR *pa, attr;
+    int idx, num;
+
+    pa = lptw->AttrBuffer + offset;
+    if ((offset < 0) || (offset >= lptw->ScreenSize.x*lptw->ScreenSize.y))
+       MessageBox((HWND)NULL, "panic", "panic", MB_OK | MB_ICONEXCLAMATION);
+    idx = 0;
+    num = count;
+    while (num > 0) {
+       attr = *pa;
+       while ((num > 0) && (attr == *pa)) {
+           /* skip over bytes with same attribute */
+           num--;
+           pa++;
+       }
+       if (lptw->bSysColors) {
+           SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
+           SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
+       } else {
+           SetTextColor(hdc, TextFore(attr));
+           SetBkColor(hdc, TextBack(attr));
+       }
+       TextOut(hdc,xpos,ypos, (LPSTR)(lptw->ScreenBuffer + offset + idx),
+               count-num-idx);
+       xpos += lptw->CharSize.x * (count-num-idx);
+       idx = count-num;
+    }
+}
+
+void
+DoMark(LPTW lptw, POINT pt, POINT end, BOOL mark)
+{
+    int xpos, ypos;
+    HDC hdc;
+    int count;
+    int offset;
+
+    offset = lptw->ScreenSize.x * pt.y + pt.x;
+    hdc = GetDC(lptw->hWndText);
+    SelectObject(hdc, lptw->hfont);
+    if (lptw->bSysColors) {
+       SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+       SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
+    } else {
+       SetTextColor(hdc, MARKFORE);
+       SetBkColor(hdc, MARKBACK);
+    }
+    while (pt.y < end.y) {
+       /* multiple lines */
+       xpos = pt.x*lptw->CharSize.x - lptw->ScrollPos.x;
+       ypos = pt.y*lptw->CharSize.y - lptw->ScrollPos.y;
+       count = lptw->ScreenSize.x - pt.x;
+       if (mark)
+           TextOut(hdc,xpos,ypos, (LPSTR)(lptw->ScreenBuffer + offset), count);
+       else {
+           DoLine(lptw, hdc, xpos, ypos, offset, count);
+           if (lptw->bSysColors) {
+               SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+               SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
+           } else {
+               SetTextColor(hdc, MARKFORE);
+               SetBkColor(hdc, MARKBACK);
+           }
+       }
+       offset += count;
+       pt.y++;
+       pt.x=0;
+    }
+    /* partial line */
+    xpos = pt.x*lptw->CharSize.x - lptw->ScrollPos.x;
+    ypos = pt.y*lptw->CharSize.y - lptw->ScrollPos.y;
+    count = end.x - pt.x;
+    if (end.y != lptw->ScreenSize.y) {
+       if (mark)
+           TextOut(hdc,xpos,ypos, (LPSTR)(lptw->ScreenBuffer + offset), count);
+       else
+           DoLine(lptw, hdc, xpos, ypos, offset, count);
+    }
+    ReleaseDC(lptw->hWndText,hdc);
+}
+
+void
+UpdateMark(LPTW lptw, POINT pt)
+{
+    int begin, point, end;
+
+    LimitMark(lptw, &pt);
+    begin = lptw->ScreenSize.x*lptw->MarkBegin.y + lptw->MarkBegin.x;
+    point = lptw->ScreenSize.x*pt.y + pt.x;
+    end   = lptw->ScreenSize.x*lptw->MarkEnd.y + lptw->MarkEnd.x;
+
+    if (begin <= end) {
+       /* forward mark */
+       if (point >= end) {
+           /* extend marked area */
+           DoMark(lptw, lptw->MarkEnd, pt, TRUE);
+       } else if (point >= begin) {
+           /* retract marked area */
+           DoMark(lptw, pt, lptw->MarkEnd, FALSE);
+       } else {        /* retract and reverse */
+           DoMark(lptw, lptw->MarkBegin, lptw->MarkEnd, FALSE);
+           DoMark(lptw, pt, lptw->MarkBegin, TRUE);
+       }
+    } else {
+       /* reverse mark */
+       if (point <= end) {
+           /* extend marked area */
+           DoMark(lptw, pt, lptw->MarkEnd, TRUE);
+       } else if (point <= begin) {
+           /* retract marked area */
+           DoMark(lptw, lptw->MarkEnd, pt, FALSE);
+       } else {        /* retract and reverse */
+           DoMark(lptw, lptw->MarkEnd, lptw->MarkBegin, FALSE);
+           DoMark(lptw, lptw->MarkBegin, pt, TRUE);
+       }
+    }
+    lptw->MarkEnd.x = pt.x;
+    lptw->MarkEnd.y = pt.y;
+}
+
+
+#if WINVER >= 0x030a
+/* Windows 3.1 drag-drop feature */
+
+void
+DragFunc(LPTW lptw, HDROP hdrop)
+{
+    int i, cFiles;
+    LPSTR p;
+
+    if ((lptw->DragPre==(LPSTR)NULL) || (lptw->DragPost==(LPSTR)NULL))
+       return;
+    cFiles = DragQueryFile(hdrop, (UINT) -1, (LPSTR)NULL, 0);
+    for (i=0; i<cFiles; i++) {
+       char szFile[MAX_PATH];
+
+       DragQueryFile(hdrop, i, szFile, MAX_PATH);
+       for (p=lptw->DragPre; *p; p++)
+           SendMessage(lptw->hWndText,WM_CHAR,*p,1L);
+       for (p=szFile; *p; p++)
+           SendMessage(lptw->hWndText,WM_CHAR,*p,1L);
+       for (p=lptw->DragPost; *p; p++)
+           SendMessage(lptw->hWndText,WM_CHAR,*p,1L);
+    }
+    DragFinish(hdrop);
+}
+#endif
+
+
+void
+TextCopyClip(LPTW lptw)
+{
+    int size, count;
+    HGLOBAL hGMem;
+    LPSTR cbuf, cp;
+    POINT pt, end;
+    TEXTMETRIC tm;
+    UINT type;
+    HDC hdc;
+
+    if ((lptw->MarkBegin.x == lptw->MarkEnd.x) &&
+       (lptw->MarkBegin.y == lptw->MarkEnd.y) ) {
+       /* copy user text */
+       return;
+    }
+
+    size = (lptw->MarkEnd.y - lptw->MarkBegin.y + 1)
+       * (lptw->ScreenSize.x + 2) + 1;
+    hGMem = GlobalAlloc(GMEM_MOVEABLE, (DWORD)size);
+    cbuf = cp = (LPSTR)GlobalLock(hGMem);
+    if (cp == (LPSTR)NULL)
+       return;
+
+    pt.x = lptw->MarkBegin.x;
+    pt.y = lptw->MarkBegin.y;
+    end.x   = lptw->MarkEnd.x;
+    end.y   = lptw->MarkEnd.y;
+
+    while (pt.y < end.y) {
+       /* copy to global buffer */
+       count = lptw->ScreenSize.x - pt.x;
+       _fmemcpy(cp,
+                lptw->ScreenBuffer + lptw->ScreenSize.x * pt.y + pt.x,
+                count);
+       /* remove trailing spaces */
+       for (count=count-1; count>=0; count--) {
+           if (cp[count]!=' ')
+               break;
+           cp[count] = '\0';
+       }
+       cp[++count] = '\r';
+       cp[++count] = '\n';
+       cp[++count] = '\0';
+       cp += count;
+       pt.y++;
+       pt.x=0;
+    }
+    /* partial line */
+    count = end.x - pt.x;
+    if (end.y != lptw->ScreenSize.y) {
+       _fmemcpy(cp, lptw->ScreenBuffer + lptw->ScreenSize.x*pt.y+pt.x, count);
+       cp[count] = '\0';
+    }
+    size = _fstrlen(cbuf) + 1;
+    GlobalUnlock(hGMem);
+    hGMem = GlobalReAlloc(hGMem, (DWORD)size, GMEM_MOVEABLE);
+    /* find out what type to put into clipboard */
+    hdc = GetDC(lptw->hWndText);
+    SelectObject(hdc, lptw->hfont);
+    GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
+    if (tm.tmCharSet == OEM_CHARSET)
+       type = CF_OEMTEXT;
+    else
+       type = CF_TEXT;
+    ReleaseDC(lptw->hWndText, hdc);
+    /* give buffer to clipboard */
+    OpenClipboard(lptw->hWndParent);
+    EmptyClipboard();
+    SetClipboardData(type, hGMem);
+    CloseClipboard();
+}
+
+void
+TextMakeFont(LPTW lptw)
+{
+    LOGFONT lf;
+    TEXTMETRIC tm;
+    LPSTR p;
+    HDC hdc;
+
+    hdc = GetDC(lptw->hWndText);
+    _fmemset(&lf, 0, sizeof(LOGFONT));
+    _fstrncpy(lf.lfFaceName,lptw->fontname,LF_FACESIZE);
+    lf.lfHeight = -MulDiv(lptw->fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+    lf.lfPitchAndFamily = FIXED_PITCH;
+    lf.lfCharSet = DEFAULT_CHARSET;
+    if ( (p = _fstrstr(lptw->fontname," Italic")) != (LPSTR)NULL ) {
+       lf.lfFaceName[ (unsigned int)(p-lptw->fontname) ] = '\0';
+       lf.lfItalic = TRUE;
+    }
+    if ( (p = _fstrstr(lptw->fontname," Bold")) != (LPSTR)NULL ) {
+       lf.lfFaceName[ (unsigned int)(p-lptw->fontname) ] = '\0';
+       lf.lfWeight = FW_BOLD;
+    }
+    if (lptw->hfont != 0)
+       DeleteObject(lptw->hfont);
+    lptw->hfont = CreateFontIndirect((LOGFONT FAR *)&lf);
+    /* get text size */
+    SelectObject(hdc, lptw->hfont);
+    GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
+    lptw->CharSize.y = tm.tmHeight;
+    lptw->CharSize.x = tm.tmAveCharWidth;
+    lptw->CharAscent = tm.tmAscent;
+    if (lptw->bFocus)
+       CreateCaret(lptw->hWndText, 0, lptw->CharSize.x, 2+lptw->CaretHeight);
+    ReleaseDC(lptw->hWndText, hdc);
+    return;
+}
+
+void
+TextSelectFont(LPTW lptw) {
+#if WINVER >= 0x030a
+    LOGFONT lf;
+    CHOOSEFONT cf;
+    HDC hdc;
+    char lpszStyle[LF_FACESIZE];
+    LPSTR p;
+
+    /* Set all structure fields to zero. */
+    _fmemset(&cf, 0, sizeof(CHOOSEFONT));
+    _fmemset(&lf, 0, sizeof(LOGFONT));
+    cf.lStructSize = sizeof(CHOOSEFONT);
+    cf.hwndOwner = lptw->hWndParent;
+    _fstrncpy(lf.lfFaceName,lptw->fontname,LF_FACESIZE);
+    if ( (p = _fstrstr(lptw->fontname," Bold")) != (LPSTR)NULL ) {
+       _fstrncpy(lpszStyle,p+1,LF_FACESIZE);
+       lf.lfFaceName[ (unsigned int)(p-lptw->fontname) ] = '\0';
+    }
+    else if ( (p = _fstrstr(lptw->fontname," Italic")) != (LPSTR)NULL ) {
+       _fstrncpy(lpszStyle,p+1,LF_FACESIZE);
+       lf.lfFaceName[ (unsigned int)(p-lptw->fontname) ] = '\0';
+    } else
+       _fstrcpy(lpszStyle,"Regular");
+    cf.lpszStyle = lpszStyle;
+    hdc = GetDC(lptw->hWndText);
+    lf.lfHeight = -MulDiv(lptw->fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
+    ReleaseDC(lptw->hWndText, hdc);
+    lf.lfPitchAndFamily = FIXED_PITCH;
+    cf.lpLogFont = &lf;
+    cf.nFontType = SCREEN_FONTTYPE;
+    cf.Flags = CF_SCREENFONTS | CF_FIXEDPITCHONLY | CF_INITTOLOGFONTSTRUCT | CF_USESTYLE;
+    if (ChooseFont(&cf)) {
+       RECT rect;
+       _fstrcpy(lptw->fontname,lf.lfFaceName);
+       lptw->fontsize = cf.iPointSize / 10;
+       if (cf.nFontType & BOLD_FONTTYPE)
+           lstrcat(lptw->fontname," Bold");
+       if (cf.nFontType & ITALIC_FONTTYPE)
+           lstrcat(lptw->fontname," Italic");
+       TextMakeFont(lptw);
+       /* force a window update */
+       GetClientRect(lptw->hWndText, (LPRECT) &rect);
+       SendMessage(lptw->hWndText, WM_SIZE, SIZE_RESTORED,
+                   MAKELPARAM(rect.right-rect.left, rect.bottom-rect.top));
+       GetClientRect(lptw->hWndText, (LPRECT) &rect);
+       InvalidateRect(lptw->hWndText, (LPRECT) &rect, 1);
+       UpdateWindow(lptw->hWndText);
+    }
+#endif
+}
+
+
+/* parent overlapped window */
+LRESULT CALLBACK WINEXPORT
+WndParentProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    HDC hdc;
+    PAINTSTRUCT ps;
+    RECT rect;
+    LPTW lptw;
+
+    lptw = (LPTW)GetWindowLong(hwnd, 0);
+
+    switch(message) {
+    case WM_SYSCOMMAND:
+       switch(LOWORD(wParam)) {
+       case M_COPY_CLIP:
+       case M_PASTE:
+       case M_CHOOSE_FONT:
+       case M_SYSCOLORS:
+       case M_WRITEINI:
+       case M_ABOUT:
+           SendMessage(lptw->hWndText, WM_COMMAND, wParam, lParam);
+       }
+       break;
+    case WM_SETFOCUS:
+       if (IsWindow(lptw->hWndText)) {
+           SetFocus(lptw->hWndText);
+           return(0);
+       }
+       break;
+    case WM_GETMINMAXINFO:
+    {
+       POINT FAR * MMinfo = (POINT FAR *)lParam;
+       TEXTMETRIC tm;
+
+       hdc = GetDC(hwnd);
+       SelectObject(hdc, GetStockObject(OEM_FIXED_FONT));
+       GetTextMetrics(hdc,(LPTEXTMETRIC)&tm);
+       ReleaseDC(hwnd,hdc);
+       /* minimum size */
+       MMinfo[3].x = ScreenMinSize.x*tm.tmAveCharWidth
+           + GetSystemMetrics(SM_CXVSCROLL) + 2*GetSystemMetrics(SM_CXFRAME);
+       MMinfo[3].y = ScreenMinSize.y*tm.tmHeight
+           + GetSystemMetrics(SM_CYHSCROLL) + 2*GetSystemMetrics(SM_CYFRAME)
+           + GetSystemMetrics(SM_CYCAPTION);
+       return(0);
+    }
+    case WM_SIZE:
+       SetWindowPos(lptw->hWndText, (HWND)NULL, 0, lptw->ButtonHeight,
+                    LOWORD(lParam), HIWORD(lParam)-lptw->ButtonHeight,
+                    SWP_NOZORDER | SWP_NOACTIVATE);
+       return(0);
+    case WM_COMMAND:
+       if (IsWindow(lptw->hWndText))
+           SetFocus(lptw->hWndText);
+       SendMessage(lptw->hWndText, message, wParam, lParam); /* pass on menu commands */
+       return(0);
+    case WM_PAINT:
+       hdc = BeginPaint(hwnd, &ps);
+       if (lptw->ButtonHeight) {
+           HBRUSH hbrush;
+           GetClientRect(hwnd, &rect);
+           hbrush = CreateSolidBrush(GetSysColor(COLOR_BTNSHADOW));
+           rect.bottom = lptw->ButtonHeight-1;
+           FillRect(hdc, &rect, hbrush);
+           DeleteObject(hbrush);
+           SelectObject(hdc, GetStockObject(BLACK_PEN));
+           MoveTo(hdc, rect.left, lptw->ButtonHeight-1);
+           LineTo(hdc, rect.right, lptw->ButtonHeight-1);
+       }
+       EndPaint(hwnd, &ps);
+       return 0;
+
+#if WINVER >= 0x030a
+    case WM_DROPFILES:
+    {
+       WORD version = LOWORD(GetVersion());
+
+       if ((LOBYTE(version)*100 + HIBYTE(version)) >= 310)
+           DragFunc(lptw, (HDROP)wParam);
+    }
+    break;
+#endif
+
+    case WM_CREATE:
+    {
+       RECT crect, wrect;
+       TEXTMETRIC tm;
+
+       lptw = ((CREATESTRUCT FAR *)lParam)->lpCreateParams;
+       SetWindowLong(hwnd, 0, (LONG)lptw);
+       lptw->hWndParent = hwnd;
+       /* get character size */
+       TextMakeFont(lptw);
+       hdc = GetDC(hwnd);
+       SelectObject(hdc, lptw->hfont);
+       GetTextMetrics(hdc,(LPTEXTMETRIC)&tm);
+       lptw->CharSize.y = tm.tmHeight;
+       lptw->CharSize.x = tm.tmAveCharWidth;
+       lptw->CharAscent = tm.tmAscent;
+       ReleaseDC(hwnd,hdc);
+       GetClientRect(hwnd, &crect);
+       if ( lptw->CharSize.x*lptw->ScreenSize.x < crect.right ) {
+           /* shrink x size */
+           GetWindowRect(lptw->hWndParent,&wrect);
+           MoveWindow(lptw->hWndParent, wrect.left, wrect.top,
+                      wrect.right-wrect.left + (lptw->CharSize.x*lptw->ScreenSize.x - crect.right),
+                      wrect.bottom-wrect.top,
+                      TRUE);
+       }
+       if ( lptw->CharSize.y*lptw->ScreenSize.y < crect.bottom ) {
+           /* shrink y size */
+           GetWindowRect(lptw->hWndParent,&wrect);
+           MoveWindow(lptw->hWndParent, wrect.left, wrect.top,
+                      wrect.right-wrect.left,
+                      wrect.bottom-wrect.top + (lptw->CharSize.y*lptw->ScreenSize.y+lptw->ButtonHeight - crect.bottom),
+                      TRUE);
+       }
+    }
+
+#if WINVER >= 0x030a
+    {
+       WORD version = LOWORD(GetVersion());
+
+       if ((LOBYTE(version)*100 + HIBYTE(version)) >= 310)
+           if ( (lptw->DragPre!=(LPSTR)NULL) && (lptw->DragPost!=(LPSTR)NULL) )
+               DragAcceptFiles(hwnd, TRUE);
+    }
+#endif
+    break;
+
+    case WM_DESTROY:
+#if WINVER >= 0x030a
+    {
+       WORD version = LOWORD(GetVersion());
+
+       if ((LOBYTE(version)*100 + HIBYTE(version)) >= 310)
+           DragAcceptFiles(hwnd, FALSE);
+    }
+#endif
+
+    DeleteObject(lptw->hfont);
+    lptw->hfont = 0;
+    break;
+
+    case WM_CLOSE:
+       if (lptw->shutdown) {
+           FARPROC lpShutDown = lptw->shutdown;
+           (*lpShutDown)();
+       }
+       break;
+    } /* switch() */
+
+    return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+
+/* PM 20011218: Reallocate larger keyboard buffer */
+int
+ReallocateKeyBuf(LPTW lptw)
+{
+    int newbufsize = lptw->KeyBufSize + 16*1024; /* new buffer size */
+    HGLOBAL h_old = (HGLOBAL)GlobalHandle( SELECTOROF(lptw->KeyBuf) );
+    HGLOBAL h = GlobalAlloc(LHND, newbufsize);
+    int pos_in = lptw->KeyBufIn - lptw->KeyBuf;
+    int pos_out = lptw->KeyBufOut - lptw->KeyBuf;
+    BYTE FAR *NewKeyBuf = (BYTE FAR *)GlobalLock(h);
+
+    if (NewKeyBuf == (BYTE FAR *)NULL) {
+       MessageBox((HWND)NULL, szNoMemory, (LPSTR)NULL,
+                  MB_ICONHAND | MB_SYSTEMMODAL);
+       return 1;
+    }
+    if (lptw->KeyBufIn > lptw->KeyBufOut) {
+       /*  | Buf ... Out ... In | */
+       _fmemcpy(NewKeyBuf, lptw->KeyBufOut,
+                 lptw->KeyBufIn - lptw->KeyBufOut);
+       lptw->KeyBufIn = NewKeyBuf + (pos_in - pos_out);
+    } else {
+       /*  | Buf ... In ... Out ... | */
+       _fmemcpy(NewKeyBuf, lptw->KeyBufOut, lptw->KeyBufSize - pos_out );
+       _fmemcpy(NewKeyBuf, lptw->KeyBuf, pos_in );
+       lptw->KeyBufIn = NewKeyBuf + (lptw->KeyBufSize - pos_out + pos_in);
+    }
+    if (h_old) {
+       GlobalUnlock(h_old);
+       GlobalFree(h_old);
+    }
+    lptw->KeyBufSize = newbufsize;
+    lptw->KeyBufOut = lptw->KeyBuf = NewKeyBuf;
+    return 0;
+}
+
+
+/* child text window */
+LRESULT CALLBACK WINEXPORT
+WndTextProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+    HDC hdc;
+    PAINTSTRUCT ps;
+    RECT rect;
+    int nYinc, nXinc;
+    LPTW lptw;
+
+    lptw = (LPTW)GetWindowLong(hwnd, 0);
+
+    switch(message) {
+    case WM_SETFOCUS:
+       lptw->bFocus = TRUE;
+       CreateCaret(hwnd, 0, lptw->CharSize.x, 2+lptw->CaretHeight);
+       SetCaretPos(lptw->CursorPos.x*lptw->CharSize.x - lptw->ScrollPos.x,
+                   lptw->CursorPos.y*lptw->CharSize.y + lptw->CharAscent
+                   - lptw->CaretHeight - lptw->ScrollPos.y);
+       if (lptw->bGetCh)
+           ShowCaret(hwnd);
+       break;
+    case WM_KILLFOCUS:
+       DestroyCaret();
+       lptw->bFocus = FALSE;
+       break;
+    case WM_SIZE:
+       lptw->ClientSize.y = HIWORD(lParam);
+       lptw->ClientSize.x = LOWORD(lParam);
+
+       lptw->ScrollMax.y = max(0, lptw->CharSize.y*lptw->ScreenSize.y - lptw->ClientSize.y);
+       lptw->ScrollPos.y = min(lptw->ScrollPos.y, lptw->ScrollMax.y);
+
+       SetScrollRange(hwnd, SB_VERT, 0, lptw->ScrollMax.y, FALSE);
+       SetScrollPos(hwnd, SB_VERT, lptw->ScrollPos.y, TRUE);
+
+       lptw->ScrollMax.x = max(0, lptw->CharSize.x*lptw->ScreenSize.x - lptw->ClientSize.x);
+       lptw->ScrollPos.x = min(lptw->ScrollPos.x, lptw->ScrollMax.x);
+
+       SetScrollRange(hwnd, SB_HORZ, 0, lptw->ScrollMax.x, FALSE);
+       SetScrollPos(hwnd, SB_HORZ, lptw->ScrollPos.x, TRUE);
+
+       if (lptw->bFocus && lptw->bGetCh) {
+           SetCaretPos(lptw->CursorPos.x*lptw->CharSize.x - lptw->ScrollPos.x,
+                       lptw->CursorPos.y*lptw->CharSize.y + lptw->CharAscent
+                       - lptw->CaretHeight - lptw->ScrollPos.y);
+           ShowCaret(hwnd);
+       }
+       return(0);
+    case WM_VSCROLL:
+       switch(LOWORD(wParam)) {
+       case SB_TOP:
+           nYinc = -lptw->ScrollPos.y;
+           break;
+       case SB_BOTTOM:
+           nYinc = lptw->ScrollMax.y - lptw->ScrollPos.y;
+           break;
+       case SB_LINEUP:
+           nYinc = -lptw->CharSize.y;
+           break;
+       case SB_LINEDOWN:
+           nYinc = lptw->CharSize.y;
+           break;
+       case SB_PAGEUP:
+           nYinc = min(-1,-lptw->ClientSize.y);
+           break;
+       case SB_PAGEDOWN:
+           nYinc = max(1,lptw->ClientSize.y);
+           break;
+       case SB_THUMBPOSITION:
+           nYinc = HIWORD(wParam) - lptw->ScrollPos.y;
+           break;
+       default:
+           nYinc = 0;
+       } /* switch(loword(wparam)) */
+
+       if ( (nYinc = max(-lptw->ScrollPos.y,
+                         min(nYinc, lptw->ScrollMax.y - lptw->ScrollPos.y)))
+            != 0 ) {
+           lptw->ScrollPos.y += nYinc;
+           ScrollWindow(hwnd,0,-nYinc,NULL,NULL);
+           SetScrollPos(hwnd,SB_VERT,lptw->ScrollPos.y,TRUE);
+           UpdateWindow(hwnd);
+       }
+       return(0);
+    case WM_HSCROLL:
+       switch(LOWORD(wParam)) {
+       case SB_LINEUP:
+           nXinc = -lptw->CharSize.x;
+           break;
+       case SB_LINEDOWN:
+           nXinc = lptw->CharSize.x;
+           break;
+       case SB_PAGEUP:
+           nXinc = min(-1,-lptw->ClientSize.x);
+           break;
+       case SB_PAGEDOWN:
+           nXinc = max(1,lptw->ClientSize.x);
+           break;
+       case SB_THUMBPOSITION:
+           nXinc = HIWORD(wParam) - lptw->ScrollPos.x;
+           break;
+       default:
+           nXinc = 0;
+       } /* switch(loword(wparam)) */
+
+       if ( (nXinc = max(-lptw->ScrollPos.x,
+                         min(nXinc, lptw->ScrollMax.x - lptw->ScrollPos.x)))
+            != 0 ) {
+           lptw->ScrollPos.x += nXinc;
+           ScrollWindow(hwnd,-nXinc,0,NULL,NULL);
+           SetScrollPos(hwnd,SB_HORZ,lptw->ScrollPos.x,TRUE);
+           UpdateWindow(hwnd);
+       }
+       return(0);
+    case WM_KEYDOWN:
+       if (GetKeyState(VK_SHIFT) < 0) {
+           switch(wParam) {
+           case VK_HOME:
+               SendMessage(hwnd, WM_VSCROLL, SB_TOP, (LPARAM)0);
+               break;
+           case VK_END:
+               SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, (LPARAM)0);
+               break;
+           case VK_PRIOR:
+               SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, (LPARAM)0);
+               break;
+           case VK_NEXT:
+               SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, (LPARAM)0);
+               break;
+           case VK_UP:
+               SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, (LPARAM)0);
+               break;
+           case VK_DOWN:
+               SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, (LPARAM)0);
+               break;
+           case VK_LEFT:
+               SendMessage(hwnd, WM_HSCROLL, SB_LINEUP, (LPARAM)0);
+               break;
+           case VK_RIGHT:
+               SendMessage(hwnd, WM_HSCROLL, SB_LINEDOWN, (LPARAM)0);
+               break;
+           } /* switch(wparam) */
+       } else {                /* if(shift) */
+           switch(wParam) {
+           case VK_HOME:
+           case VK_END:
+           case VK_PRIOR:
+           case VK_NEXT:
+           case VK_UP:
+           case VK_DOWN:
+           case VK_LEFT:
+           case VK_RIGHT:
+           case VK_DELETE: { /* store key in circular buffer */
+               long count = lptw->KeyBufIn - lptw->KeyBufOut;
+
+               if (count < 0)
+                   count += lptw->KeyBufSize;
+               if (count < lptw->KeyBufSize-2) {
+                   *lptw->KeyBufIn++ = 0;
+                   if (lptw->KeyBufIn - lptw->KeyBuf >= lptw->KeyBufSize)
+                       lptw->KeyBufIn = lptw->KeyBuf;  /* wrap around */
+                   *lptw->KeyBufIn++ = HIWORD(lParam) & 0xff;
+                   if (lptw->KeyBufIn - lptw->KeyBuf >= lptw->KeyBufSize)
+                       lptw->KeyBufIn = lptw->KeyBuf;  /* wrap around */
+               }
+           }
+           } /* switch(wparam) */
+       } /* else(shift) */
+       break;
+    case WM_KEYUP:
+       if (GetKeyState(VK_SHIFT) < 0) {
+           switch(wParam) {
+           case VK_INSERT:
+               /* Shift-Insert: paste clipboard */
+               SendMessage(lptw->hWndText, WM_COMMAND, M_PASTE, (LPARAM)0);
+               break;
+           }
+       } /* if(shift) */
+       if (GetKeyState(VK_CONTROL) < 0) {
+           switch(wParam) {
+           case VK_INSERT:
+               /* Ctrl-Insert: copy to clipboard */
+               SendMessage(lptw->hWndText, WM_COMMAND, M_COPY_CLIP, (LPARAM)0);
+               break;
+           case 'V':
+               /* Ctrl-V: paste clipboard */
+               SendMessage(lptw->hWndText, WM_COMMAND, M_PASTE, (LPARAM)0);
+               break;
+           } /* switch(wparam) */
+       } /* if(Ctrl) */
+       break;
+
+    case WM_RBUTTONDOWN:
+    {
+       POINT pt;
+
+       pt.x = LOWORD(lParam);
+       pt.y = HIWORD(lParam);
+       ClientToScreen(hwnd,&pt);
+       TrackPopupMenu(lptw->hPopMenu, TPM_LEFTALIGN,
+                      pt.x, pt.y, 0, hwnd, NULL);
+       return(0);
+    }
+    case WM_LBUTTONDOWN:
+    { /* start marking text */
+       POINT pt;
+
+       pt.x = LOWORD(lParam);
+       pt.y = HIWORD(lParam);
+       pt.x = (pt.x + lptw->ScrollPos.x)/lptw->CharSize.x;
+       pt.y = (pt.y + lptw->ScrollPos.y)/lptw->CharSize.y;
+       ClearMark(lptw, pt);
+       SetCapture(hwnd);       /* track the mouse */
+       lptw->Marking = TRUE;
+       break;
+    }
+    case WM_LBUTTONUP:
+    { /* finish marking text */
+       /* ensure begin mark is before end mark */
+       ReleaseCapture();
+       lptw->Marking = FALSE;
+       if ((lptw->ScreenSize.x*lptw->MarkBegin.y + lptw->MarkBegin.x) >
+           (lptw->ScreenSize.x*lptw->MarkEnd.y   + lptw->MarkEnd.x)) {
+           POINT tmp;
+           tmp.x = lptw->MarkBegin.x;
+           tmp.y = lptw->MarkBegin.y;
+           lptw->MarkBegin.x = lptw->MarkEnd.x;
+           lptw->MarkBegin.y = lptw->MarkEnd.y;
+           lptw->MarkEnd.x   = tmp.x;
+           lptw->MarkEnd.y   = tmp.y;
+       }
+       break;
+    }
+    case WM_MOUSEMOVE:
+       if ((wParam & MK_LBUTTON) && lptw->Marking) {
+           RECT rect;
+           POINT pt;
+
+           pt.x = LOWORD(lParam);
+           pt.y = HIWORD(lParam);
+           GetClientRect(hwnd, &rect);
+           if (PtInRect(&rect, pt)) {
+               pt.x = (pt.x + lptw->ScrollPos.x)/lptw->CharSize.x;
+               pt.y = (pt.y + lptw->ScrollPos.y)/lptw->CharSize.y;
+               UpdateMark(lptw, pt);
+           } else {
+               int nXinc, nYinc;
+
+               do {
+                   nXinc = 0;
+                   nYinc = 0;
+                   if (pt.x > rect.right) {
+                       nXinc = lptw->CharSize.x * 4;
+                       pt.x = (rect.right + lptw->ScrollPos.x)
+                           / lptw->CharSize.x + 2;
+                   } else if (pt.x < rect.left) {
+                       nXinc = -lptw->CharSize.x * 4;
+                       pt.x = (rect.left + lptw->ScrollPos.x)
+                           / lptw->CharSize.x - 2;
+                   } else
+                       pt.x = (pt.x + lptw->ScrollPos.x)
+                           /lptw->CharSize.x;
+                   if (pt.y > rect.bottom) {
+                       nYinc = lptw->CharSize.y;
+                       pt.y = (rect.bottom + lptw->ScrollPos.y)
+                           / lptw->CharSize.y + 1;
+                   } else if (pt.y < rect.top) {
+                       nYinc = -lptw->CharSize.y;
+                       pt.y = (rect.top + lptw->ScrollPos.y)
+                           / lptw->CharSize.y - 1;
+                   } else
+                       pt.y = (pt.y + lptw->ScrollPos.y)
+                           / lptw->CharSize.y;
+
+                   LimitMark(lptw, &pt);
+                   nXinc = max(nXinc, -lptw->ScrollPos.x);
+                   nYinc = max(nYinc, -lptw->ScrollPos.y);
+                   nYinc = min(nYinc, lptw->ScrollMax.y - lptw->ScrollPos.y);
+                   nXinc = min(nXinc, lptw->ScrollMax.x - lptw->ScrollPos.x);
+                   if (nYinc || nXinc) {
+                       lptw->ScrollPos.y += nYinc;
+                       lptw->ScrollPos.x += nXinc;
+                       ScrollWindow(lptw->hWndText, -nXinc, -nYinc,
+                                    NULL, NULL);
+                       SetScrollPos(lptw->hWndText, SB_VERT,
+                                    lptw->ScrollPos.y, TRUE);
+                       SetScrollPos(lptw->hWndText, SB_HORZ,
+                                    lptw->ScrollPos.x, TRUE);
+                       UpdateWindow(lptw->hWndText);
+                   }
+                   UpdateMark(lptw, pt);
+                   GetCursorPos(&pt);
+                   ScreenToClient(hwnd, &pt);
+               }
+               while((nYinc || nXinc) && !PtInRect(&rect, pt)
+                     && (GetAsyncKeyState(VK_LBUTTON) < 0));
+           } /* moved inside viewport */
+       } /* if(dragging) */
+       break;
+#if _WIN32_WINNT >= 0x0400
+    case WM_MOUSEWHEEL: {
+           WORD fwKeys;
+           short int zDelta;
+
+           fwKeys = LOWORD(wParam);
+           zDelta = HIWORD(wParam);
+           switch (fwKeys) {
+           case 0:
+               if (zDelta < 0)
+                   SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, (LPARAM)0);
+               else
+                   SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, (LPARAM)0);
+               return 0;
+           case MK_SHIFT:
+               if (zDelta < 0)
+                   SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, (LPARAM)0);
+               else
+                   SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, (LPARAM)0);
+               return 0;
+           case MK_CONTROL:
+               if (zDelta < 0)
+                   SendMessage(hwnd, WM_CHAR, 0x0e, (LPARAM)0); // CTRL-N
+               else
+                   SendMessage(hwnd, WM_CHAR, 0x10, (LPARAM)0); // CTRL-P
+               return 0;
+           }
+       }
+       break;
+#endif
+    case WM_CHAR: {
+       /* store key in circular buffer */
+       long count = lptw->KeyBufIn - lptw->KeyBufOut;
+
+       if (count < 0)
+           count += lptw->KeyBufSize;
+       if (count == lptw->KeyBufSize-1) {
+           /* PM 20011218: Keyboard buffer is full, thus reallocate
+            * larger one.  (Up to now: forthcoming characters were
+            * silently ignored.)
+            */
+           if ( ReallocateKeyBuf(lptw) )
+               return 0; /* not enough memory */
+       }
+       if (count < lptw->KeyBufSize-1) {
+           *lptw->KeyBufIn++ = wParam;
+           if (lptw->KeyBufIn - lptw->KeyBuf >= lptw->KeyBufSize)
+               lptw->KeyBufIn = lptw->KeyBuf;  /* wrap around */
+       }
+       return 0;
+    }
+    case WM_COMMAND:
+       if (LOWORD(wParam) < NUMMENU)
+           SendMacro(lptw, LOWORD(wParam));
+       else
+           switch(LOWORD(wParam)) {
+           case M_COPY_CLIP:
+               TextCopyClip(lptw);
+               return 0;
+           case M_PASTE:
+           {
+               HGLOBAL hGMem;
+               BYTE FAR *cbuf;
+               TEXTMETRIC tm;
+               UINT type;
+
+               /* find out what type to get from clipboard */
+               hdc = GetDC(hwnd);
+               SelectObject(hdc, lptw->hfont);
+               GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
+               if (tm.tmCharSet == OEM_CHARSET)
+                   type = CF_OEMTEXT;
+               else
+                   type = CF_TEXT;
+               ReleaseDC(lptw->hWndText, hdc);
+               /* now get it from clipboard */
+               OpenClipboard(hwnd);
+               hGMem = GetClipboardData(type);
+               if (hGMem) {
+                   cbuf = (BYTE FAR *) GlobalLock(hGMem);
+                   while (*cbuf) {
+                       if (*cbuf != '\n')
+                           SendMessage(lptw->hWndText,WM_CHAR,*cbuf,1L);
+                       cbuf++;
+                   }
+                   GlobalUnlock(hGMem);
+               } /* if(hGmem) */
+               CloseClipboard();
+               return 0;
+           }
+           case M_CHOOSE_FONT:
+               TextSelectFont(lptw);
+               return 0;
+           case M_SYSCOLORS:
+               lptw->bSysColors = !lptw->bSysColors;
+               if (lptw->bSysColors)
+                   CheckMenuItem(lptw->hPopMenu, M_SYSCOLORS, MF_BYCOMMAND | MF_CHECKED);
+               else
+                   CheckMenuItem(lptw->hPopMenu, M_SYSCOLORS, MF_BYCOMMAND | MF_UNCHECKED);
+               SendMessage(hwnd, WM_SYSCOLORCHANGE, (WPARAM)0, (LPARAM)0);
+               InvalidateRect(hwnd, (LPRECT)NULL, 1);
+               UpdateWindow(hwnd);
+               return 0;
+           case M_WRITEINI:
+               WriteTextIni(lptw);
+               return 0;
+           case M_ABOUT:
+               AboutBox(hwnd,lptw->AboutText);
+               return 0;
+           } /* switch(loword(wparam)) */
+       return(0);
+
+    case WM_SYSCOLORCHANGE:
+       DeleteObject(lptw->hbrBackground);
+       lptw->hbrBackground = CreateSolidBrush(lptw->bSysColors ?
+                                              GetSysColor(COLOR_WINDOW) : RGB(0,0,0));
+       return(0);
+    case WM_ERASEBKGND:
+       return(1);      /* we will erase it ourselves */
+    case WM_PAINT:
+    {
+       POINT source, width, dest;
+       POINT MarkBegin, MarkEnd;
+
+       hdc = BeginPaint(hwnd, &ps);
+       if (ps.fErase)
+           FillRect(hdc, &ps.rcPaint, lptw->hbrBackground);
+       SelectObject(hdc, lptw->hfont);
+       SetMapMode(hdc, MM_TEXT);
+       SetBkMode(hdc,OPAQUE);
+       GetClientRect(hwnd, &rect);
+       /* source */
+       source.x = (rect.left + lptw->ScrollPos.x) / lptw->CharSize.x;
+       source.y = (rect.top + lptw->ScrollPos.y) / lptw->CharSize.y;
+       /* destination */
+       dest.x = source.x * lptw->CharSize.x - lptw->ScrollPos.x;
+       dest.y = source.y * lptw->CharSize.y - lptw->ScrollPos.y;
+       width.x = ((rect.right  + lptw->ScrollPos.x + lptw->CharSize.x - 1)
+                  / lptw->CharSize.x) - source.x;
+       width.y = ((rect.bottom + lptw->ScrollPos.y + lptw->CharSize.y - 1)
+                  / lptw->CharSize.y) - source.y;
+       if (source.x < 0)
+           source.x = 0;
+       if (source.y < 0)
+           source.y = 0;
+       if (source.x+width.x > lptw->ScreenSize.x)
+           width.x = lptw->ScreenSize.x - source.x;
+       if (source.y+width.y > lptw->ScreenSize.y)
+           width.y = lptw->ScreenSize.y - source.y;
+       /* ensure begin mark is before end mark */
+       if ((lptw->ScreenSize.x*lptw->MarkBegin.y + lptw->MarkBegin.x) >
+           (lptw->ScreenSize.x*lptw->MarkEnd.y   + lptw->MarkEnd.x)) {
+           MarkBegin.x = lptw->MarkEnd.x;
+           MarkBegin.y = lptw->MarkEnd.y;
+           MarkEnd.x   = lptw->MarkBegin.x;
+           MarkEnd.y   = lptw->MarkBegin.y;
+       } else {
+           MarkBegin.x = lptw->MarkBegin.x;
+           MarkBegin.y = lptw->MarkBegin.y;
+           MarkEnd.x   = lptw->MarkEnd.x;
+           MarkEnd.y   = lptw->MarkEnd.y;
+       }
+
+       /* for each line */
+       while (width.y>0) {
+           if ((source.y >= MarkBegin.y) && (source.y <= MarkEnd.y)) {
+               int start, end;
+               int count, offset;
+
+               if (source.y == MarkBegin.y)
+                   start = MarkBegin.x;
+               else
+                   start = 0;
+               if (source.y == MarkEnd.y)
+                   end = MarkEnd.x;
+               else
+                   end = lptw->ScreenSize.x;
+               /* do stuff before marked text */
+               offset = 0;
+               count = start - source.x;
+               if (count > 0)
+                   DoLine(lptw, hdc, dest.x, dest.y,
+                          source.y*lptw->ScreenSize.x + source.x, count);
+               /* then the marked text */
+               offset += count;
+               count = end - start;
+               if ((count > 0) && (offset < width.x)){
+                   if (lptw->bSysColors) {
+                       SetTextColor(hdc, GetSysColor(COLOR_HIGHLIGHTTEXT));
+                       SetBkColor(hdc, GetSysColor(COLOR_HIGHLIGHT));
+                   } else {
+                       SetTextColor(hdc, MARKFORE);
+                       SetBkColor(hdc, MARKBACK);
+                   }
+                   TextOut(hdc, dest.x + lptw->CharSize.x*offset, dest.y,
+                           (LPSTR)(lptw->ScreenBuffer
+                                   + source.y * lptw->ScreenSize.x
+                                   + source.x + offset),
+                           count);
+               }
+               /* then stuff after marked text */
+               offset += count;
+               count = width.x + source.x - end;
+               if ((count > 0) && (offset < width.x))
+                   DoLine(lptw, hdc, dest.x + lptw->CharSize.x*offset, dest.y,
+                          source.y * lptw->ScreenSize.x + source.x + offset,
+                          count);
+           } else {
+               DoLine(lptw, hdc, dest.x, dest.y,
+                      source.y*lptw->ScreenSize.x + source.x, width.x);
+           }
+           dest.y += lptw->CharSize.y;
+           source.y++;
+           width.y--;
+       }
+       EndPaint(hwnd, &ps);
+       return 0;
+    }
+    case WM_CREATE:
+       lptw = ((CREATESTRUCT FAR *)lParam)->lpCreateParams;
+       SetWindowLong(hwnd, 0, (LONG)lptw);
+       lptw->hWndText = hwnd;
+       break;
+    case WM_DESTROY:
+       DeleteObject(lptw->hbrBackground);
+       break;
+    } /* switch(message) */
+    return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+
+/* ================================== */
+/* replacement stdio routines */
+
+/* TRUE if key hit, FALSE if no key */
+int WDPROC
+TextKBHit(LPTW lptw)
+{
+    return (lptw->KeyBufIn != lptw->KeyBufOut);
+}
+
+/* get character from keyboard, no echo */
+/* need to add extended codes */
+int WDPROC
+TextGetCh(LPTW lptw)
+{
+    int ch;
+    TextToCursor(lptw);
+    lptw->bGetCh = TRUE;
+    if (lptw->bFocus) {
+       SetCaretPos(lptw->CursorPos.x * lptw->CharSize.x - lptw->ScrollPos.x,
+                   lptw->CursorPos.y * lptw->CharSize.y + lptw->CharAscent
+                   - lptw->CaretHeight - lptw->ScrollPos.y);
+       ShowCaret(lptw->hWndText);
+    }
+    while (!TextKBHit(lptw)) {
+       /* CMW: can't use TextMessage here as it does not idle properly */
+       MSG msg;
+       GetMessage(&msg, 0, 0, 0);
+       TranslateMessage(&msg);
+       DispatchMessage(&msg);
+    }
+    ch = *lptw->KeyBufOut++;
+    if (ch=='\r')
+       ch = '\n';
+    if (lptw->KeyBufOut - lptw->KeyBuf >= lptw->KeyBufSize)
+       lptw->KeyBufOut = lptw->KeyBuf; /* wrap around */
+    if (lptw->bFocus)
+       HideCaret(lptw->hWndText);
+    lptw->bGetCh = FALSE;
+    return ch;
+}
+
+/* get character from keyboard, with echo */
+int WDPROC
+TextGetChE(LPTW lptw)
+{
+    int ch;
+
+    ch = TextGetCh(lptw);
+    TextPutCh(lptw, (BYTE)ch);
+    return ch;
+}
+
+LPSTR WDPROC
+TextGetS(LPTW lptw, LPSTR str, unsigned int size)
+{
+    LPSTR next = str;
+
+    while (--size>0) {
+       switch(*next = TextGetChE(lptw)) {
+       case EOF:
+           *next = 0;
+           if (next == str)
+               return (LPSTR) NULL;
+           return str;
+       case '\n':
+           *(next+1) = 0;
+           return str;
+       case 0x08:
+       case 0x7f:
+           if (next > str)
+               --next;
+           break;
+       default:
+           ++next;
+       }
+    }
+    *next = 0;
+    return str;
+}
+
+int WDPROC
+TextPutS(LPTW lptw, LPSTR str)
+{
+    TextPutStr(lptw, str);
+    return str[_fstrlen(str)-1];
+}
+
+/* ================================== */
+/* routines added for elvis */
+
+void WDPROC
+TextGotoXY(LPTW lptw, int x, int y)
+{
+    lptw->CursorPos.x = x;
+    lptw->CursorPos.y = y;
+}
+
+int  WDPROC
+TextWhereX(LPTW lptw)
+{
+    return lptw->CursorPos.x;
+}
+
+int  WDPROC
+TextWhereY(LPTW lptw)
+{
+    return lptw->CursorPos.y;
+}
+
+void WDPROC
+TextCursorHeight(LPTW lptw, int height)
+{
+    lptw->CaretHeight = height;
+    if (lptw->bFocus)
+       CreateCaret(lptw->hWndText, 0, lptw->CharSize.x, 2+lptw->CaretHeight);
+}
+
+void WDPROC
+TextClearEOL(LPTW lptw)
+{
+    HDC hdc;
+    int xpos, ypos;
+    int from, len;
+    POINT pt;
+
+    pt.x = pt.y = 0;
+    ClearMark(lptw, pt);
+    from = lptw->CursorPos.y * lptw->ScreenSize.x + lptw->CursorPos.x;
+    len = lptw->ScreenSize.x-lptw->CursorPos.x;
+    _fmemset(lptw->ScreenBuffer + from, ' ', len);
+    _fmemset(lptw->AttrBuffer + from, NOTEXT, len);
+    xpos = lptw->CursorPos.x * lptw->CharSize.x - lptw->ScrollPos.x;
+    ypos = lptw->CursorPos.y * lptw->CharSize.y - lptw->ScrollPos.y;
+    hdc = GetDC(lptw->hWndText);
+    if (lptw->bSysColors) {
+       SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
+       SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
+    } else {
+       SetTextColor(hdc, TextFore(lptw->Attr));
+       SetBkColor(hdc, TextBack(lptw->Attr));
+    }
+    SelectObject(hdc, (lptw->hfont));
+    TextOut(hdc,xpos,ypos,
+           (LPSTR)(lptw->ScreenBuffer
+                   + lptw->CursorPos.y * lptw->ScreenSize.x
+                   + lptw->CursorPos.x),
+           lptw->ScreenSize.x - lptw->CursorPos.x);
+    ReleaseDC(lptw->hWndText,hdc);
+}
+
+void WDPROC
+TextClearEOS(LPTW lptw)
+{
+    RECT rect;
+    int from, len;
+    POINT pt;
+
+    pt.x = pt.y = 0;
+    ClearMark(lptw, pt);
+    from = lptw->CursorPos.y*lptw->ScreenSize.x + lptw->CursorPos.x;
+    len = lptw->ScreenSize.x-lptw->CursorPos.x
+       + (lptw->ScreenSize.y - lptw->CursorPos.y - 1) * lptw->ScreenSize.x;
+    _fmemset(lptw->ScreenBuffer + from, ' ', len);
+    _fmemset(lptw->AttrBuffer + from, NOTEXT, len);
+    GetClientRect(lptw->hWndText, (LPRECT) &rect);
+    InvalidateRect(lptw->hWndText, (LPRECT) &rect, 1);
+    UpdateWindow(lptw->hWndText);
+}
+
+void WDPROC
+TextInsertLine(LPTW lptw)
+{
+    RECT rect;
+    int from, to, len;
+    POINT pt;
+
+    pt.x = pt.y = 0;
+    ClearMark(lptw, pt);
+    from = lptw->CursorPos.y * lptw->ScreenSize.x;
+    to = (lptw->CursorPos.y + 1) * lptw->ScreenSize.x;
+    len = (lptw->ScreenSize.y - lptw->CursorPos.y - 1) * lptw->ScreenSize.x;
+    _fmemmove(lptw->ScreenBuffer + to, lptw->ScreenBuffer + from, len);
+    _fmemmove(lptw->AttrBuffer + to, lptw->AttrBuffer + from, len);
+    _fmemset(lptw->ScreenBuffer + from, ' ', lptw->ScreenSize.x);
+    _fmemset(lptw->AttrBuffer + from, NOTEXT, lptw->ScreenSize.x);
+    GetClientRect(lptw->hWndText, (LPRECT) &rect);
+    InvalidateRect(lptw->hWndText, (LPRECT) &rect, 1);
+    UpdateWindow(lptw->hWndText);
+    if (lptw->CursorFlag)
+       TextToCursor(lptw);
+}
+
+void WDPROC
+TextDeleteLine(LPTW lptw)
+{
+    RECT rect;
+    int from, to, len;
+    POINT pt;
+
+    pt.x = pt.y = 0;
+    ClearMark(lptw, pt);
+    to = lptw->CursorPos.y * lptw->ScreenSize.x;
+    from = (lptw->CursorPos.y + 1) * lptw->ScreenSize.x;
+    len = (lptw->ScreenSize.y - lptw->CursorPos.y - 1) * lptw->ScreenSize.x;
+    _fmemmove(lptw->ScreenBuffer + to, lptw->ScreenBuffer + from, len);
+    _fmemmove(lptw->AttrBuffer + to, lptw->AttrBuffer + from, len);
+    from = lptw->ScreenSize.x * (lptw->ScreenSize.y -1);
+    _fmemset(lptw->ScreenBuffer + from, ' ', lptw->ScreenSize.x);
+    _fmemset(lptw->AttrBuffer + from, NOTEXT, lptw->ScreenSize.x);
+    GetClientRect(lptw->hWndText, (LPRECT) &rect);
+    InvalidateRect(lptw->hWndText, (LPRECT) &rect, 1);
+    UpdateWindow(lptw->hWndText);
+    if (lptw->CursorFlag)
+       TextToCursor(lptw);
+}
+
+void WDPROC
+TextScrollReverse(LPTW lptw)
+{
+    RECT rect;
+    int len = lptw->ScreenSize.x * (lptw->ScreenSize.y - 1);
+
+    _fmemmove(lptw->ScreenBuffer+lptw->ScreenSize.x, lptw->ScreenBuffer, len);
+    _fmemset(lptw->ScreenBuffer, ' ', lptw->ScreenSize.x);
+    _fmemmove(lptw->AttrBuffer+lptw->ScreenSize.x, lptw->AttrBuffer, len);
+    _fmemset(lptw->AttrBuffer, NOTEXT, lptw->ScreenSize.x);
+    if (lptw->CursorPos.y)
+       lptw->CursorPos.y--;
+    ScrollWindow(lptw->hWndText, 0, lptw->CharSize.y, NULL, NULL);
+    GetClientRect(lptw->hWndText, (LPRECT) &rect);
+    rect.top = lptw->ScreenSize.y * lptw->CharSize.y;
+    if (rect.top < rect.bottom)
+       InvalidateRect(lptw->hWndText, (LPRECT) &rect, 1);
+    lptw->MarkBegin.y++;
+    lptw->MarkEnd.y++;
+    LimitMark(lptw, &lptw->MarkBegin);
+    LimitMark(lptw, &lptw->MarkEnd);
+    UpdateWindow(lptw->hWndText);
+}
+
+void WDPROC
+TextAttr(LPTW lptw, BYTE attr)
+{
+    lptw->Attr = attr;
+}
+
+/* About Box */
+BOOL CALLBACK WINEXPORT
+AboutDlgProc(HWND hDlg, UINT wMsg, WPARAM wParam, LPARAM lParam)
+{
+    switch (wMsg) {
+    case WM_INITDIALOG:
+    {
+       char buf[80];
+
+       GetWindowText(GetParent(hDlg),buf,80);
+       SetDlgItemText(hDlg, AB_TEXT1, buf);
+       SetDlgItemText(hDlg, AB_TEXT2, (LPSTR)lParam);
+#ifdef __DLL__
+       wsprintf(buf,"WGNUPLOT.DLL Version %s",(LPSTR)WGNUPLOTVERSION);
+       SetDlgItemText(hDlg, AB_TEXT3, buf);
+#endif
+       return TRUE;
+    }
+    case WM_DRAWITEM:
+    {
+       LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lParam;
+#ifdef WIN32
+       DrawIcon(lpdis->hDC, 0, 0, (HICON)GetClassLong(GetParent(hDlg), GCL_HICON));
+#else
+       DrawIcon(lpdis->hDC, 0, 0, (HICON)GetClassWord(GetParent(hDlg), GCW_HICON));
+#endif
+       return FALSE;
+    }
+    case WM_COMMAND:
+       switch (LOWORD(wParam)) {
+       case IDCANCEL:
+       case IDOK:
+           EndDialog(hDlg, LOWORD(wParam));
+           return TRUE;
+       }
+       break;
+    } /* switch(message) */
+    return FALSE;
+}
+
+
+void WDPROC
+AboutBox(HWND hwnd, LPSTR str)
+{
+#ifdef WIN32
+    DialogBoxParam(hdllInstance, "AboutDlgBox", hwnd,
+                  AboutDlgProc, (LPARAM)str);
+#else
+    DLGPROC lpfnAboutDlgProc;
+
+# ifdef __DLL__
+    lpfnAboutDlgProc = (DLGPROC)GetProcAddress(hdllInstance, "AboutDlgProc");
+# else
+    lpfnAboutDlgProc = (DLGPROC)MakeProcInstance((FARPROC)AboutDlgProc,
+                                                hdllInstance);
+# endif /* __DLL__ */
+    DialogBoxParam(hdllInstance,"AboutDlgBox", hwnd, lpfnAboutDlgProc,
+                  (LPARAM)str);
+    EnableWindow(hwnd,TRUE);
+# ifndef __DLL__
+    FreeProcInstance((FARPROC)lpfnAboutDlgProc);
+# endif /* __DLL__ */
+#endif /* WIN32 */
+}