1632 lines
44 KiB
C
1632 lines
44 KiB
C
/*
|
|
* WPF.C
|
|
*
|
|
* WPF line output/input windows. Taken from the wpf.dll library,
|
|
* originally written by ToddLa.
|
|
*
|
|
* History:
|
|
* 10/02/86 Todd Laney Created
|
|
* 04/14/87 toddla Added new function CreateDebugWin
|
|
* 07/08/87 brianc added iMaxLines parm to CreateDebugWin[dow]
|
|
* 2/90 russellw moved into Wincom and cleaned up.
|
|
* 10/1/92 robinsp moved to NT
|
|
*
|
|
* NT conversion
|
|
* remove the tricks on dereferencing and passing a pointer to
|
|
* the stack in wvsprintf. This means we can't do the printf
|
|
* thing so have to use a macro, sprintf etc which means wpfprintf
|
|
* must be coded :
|
|
* wpfprintf(hwnd, lpszFormat, (args))
|
|
*
|
|
*/
|
|
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
#include "wincom.h"
|
|
#include "sbtest.h"
|
|
#include <stdarg.h>
|
|
|
|
/*
|
|
* This code appears to make use of the fact that a local handle can be
|
|
* double de-referenced to get the actual pointer that the local handle
|
|
* refers to. This is a somewhat shady practice.. maybe eliminate it?
|
|
*
|
|
*/
|
|
|
|
|
|
/*--------------------------------------------------------------------------*\
|
|
| |
|
|
| g e n e r a l c o n s t a n t s |
|
|
| |
|
|
\*--------------------------------------------------------------------------*/
|
|
|
|
#define MAXBUFLEN 200 /* Maximum string length for wprintf */
|
|
|
|
/* Macros to manipulate the printf window array of lines. This array
|
|
* wraps around, thus the need for the modulo arithmetic
|
|
*/
|
|
#define FIRST(pTxt) ((pTxt)->iFirst)
|
|
#define TOP(pTxt) (((pTxt)->iFirst + (pTxt)->iTop) % (pTxt)->iMaxLines)
|
|
#define LAST(pTxt) (((pTxt)->iFirst + (pTxt)->iCount-1) % (pTxt)->iMaxLines)
|
|
#define INC(pTxt,x) ((x) = ++(x) % (pTxt)->iMaxLines)
|
|
#define DEC(pTxt,x) ((x) = --(x) % (pTxt)->iMaxLines)
|
|
|
|
#define HWinInfo(hwnd) ((HTXT)GetWindowLong((hwnd),0))
|
|
#define LockWinInfo(hwnd) ((PTXT)LocalLock((HANDLE)HWinInfo(hwnd)))
|
|
#define UnlockWinInfo(hwnd) ((PTXT)LocalUnlock((HANDLE)HWinInfo(hwnd)))
|
|
|
|
#define VK(vk) ((vk) | 0x0100)
|
|
|
|
/* The pad values used between the edge of the window and the text.
|
|
* x = 1/2 ave char width, y = 1 pixel
|
|
*/
|
|
#define OFFSETX (pTxt->Tdx/2)
|
|
#define OFFSETY 1
|
|
#define VARSIZE 1
|
|
|
|
|
|
#define BOUND(x,min,max) ((x) < (min) ? (min) : ((x) > (max) ? (max) : (x)))
|
|
|
|
|
|
|
|
#define SEEK_CUR 1
|
|
#define SEEK_END 2
|
|
#define SEEK_SET 0
|
|
|
|
/*--------------------------------------------------------------------------*\
|
|
| |
|
|
| g l o b a l v a r i a b l e s |
|
|
| |
|
|
\*--------------------------------------------------------------------------*/
|
|
#define QUESIZE 128
|
|
|
|
/*
|
|
* QUEUE STRUCTURE: Support queuing of input characters.
|
|
*
|
|
*/
|
|
typedef struct {
|
|
int iLen;
|
|
char ach[QUESIZE];
|
|
} QUE;
|
|
typedef QUE *PQUE; /* pointer to a char que */
|
|
typedef PQUE *HQUE; /* handle (**) to a char que */
|
|
|
|
|
|
/*
|
|
* WPF WINDOW INSTANCE DATA STRUCTURE
|
|
*
|
|
*/
|
|
typedef struct {
|
|
int iLen;
|
|
char **hText;
|
|
} LINE;
|
|
|
|
struct TEXT_STRUCT {
|
|
HWND hwnd; // Window displaying the text
|
|
WORD wID; // window ID code, for WM_COMMAND messages
|
|
BOOL fScrollSemaphore;
|
|
WORD wOutputLocation;
|
|
int iFile;
|
|
int iFirst; // First line in que
|
|
int iCount; // Number of lines in que
|
|
int iTop; // Line at top of window
|
|
int iLeft; // X offset of the window
|
|
int MaxLen; // length of longest string currently stored.
|
|
int iMaxLines; // max number of LINEs
|
|
int iRangeH;
|
|
int iRangeV;
|
|
HFONT hFont; // Font to draw with
|
|
int Tdx,Tdy; // Font Size
|
|
HQUE hQue;
|
|
int nTabs;
|
|
PINT pTabs;
|
|
LINE arLines[VARSIZE]; // array of iMaxLines LINEs
|
|
};
|
|
typedef struct TEXT_STRUCT *PTXT; /* pointer to a text struct */
|
|
typedef PTXT *HTXT; /* Handle (**) to a text struct */
|
|
|
|
static int iSem=0;
|
|
static BOOL gbRedraw=TRUE;
|
|
|
|
static HWND hwndLast = NULL;
|
|
|
|
/* External buffer for scratch space */
|
|
char bufTmp[MAXBUFLEN]; /* intermediate buffer */
|
|
|
|
static char szClass[] = "WPFWIN";
|
|
static BOOL fInit = FALSE;
|
|
|
|
/*--------------------------------------------------------------------------*\
|
|
| |
|
|
| f u n c t i o n d e f i n i t i o n s |
|
|
| |
|
|
\*--------------------------------------------------------------------------*/
|
|
|
|
LONG FAR PASCAL PrintfWndProc(HWND, unsigned, UINT, LONG);
|
|
|
|
void NEAR PASCAL WpfSetFont(HWND hWnd, HFONT hFont);
|
|
void NEAR PASCAL WpfClear(HWND hWnd);
|
|
void NEAR PASCAL WpfSetTabs(HWND hwnd, int nTabs, LPINT pTabs);
|
|
BOOL NEAR PASCAL WpfGetTabs(HWND hwnd, LPINT pTabs);
|
|
void NEAR PASCAL WpfPaint(HWND hwnd, HDC hdc);
|
|
|
|
void NEAR PASCAL WpfVScroll(HWND hWnd, PTXT pTxt, int n);
|
|
void NEAR PASCAL WpfHScroll(HWND hWnd, PTXT pTxt, int n);
|
|
int NEAR PASCAL LinesInWpfWindow(HWND hWnd);
|
|
int NEAR PASCAL CharsInWpfWindow(HWND hWnd);
|
|
void NEAR PASCAL WpfMaxLen(PTXT pTxt);
|
|
void NEAR PASCAL WpfSetScrollRange(HWND hWnd, BOOL bRedraw);
|
|
|
|
void NEAR PASCAL NewLine(PTXT pTxt);
|
|
int NEAR PASCAL ChangeLine(PTXT pTxt, int iLine, LPSTR lpch);
|
|
int NEAR PASCAL InsertString(PTXT pTxt, LPSTR lpstr);
|
|
|
|
BOOL NEAR PASCAL EnQueChar(HTXT hTxt, WORD vk);
|
|
void NEAR PASCAL UpdateCursorPos(PTXT pTxt);
|
|
|
|
WORD NEAR PASCAL SetOutput(HWND hwnd, UINT wParam, LONG lParam);
|
|
WORD NEAR PASCAL GetOutput(HWND hwnd);
|
|
|
|
BOOL NEAR PASCAL wpfWrtTTY(HWND hWnd, LPSTR sz);
|
|
|
|
// BOOL FAR PASCAL DbgDestroy(HWND hwnd);
|
|
|
|
void wpfWrtFile(int fh, LPSTR sz);
|
|
|
|
/*
|
|
* fSuccess = WpfInit(hInst)
|
|
*
|
|
* Register the WinPrintf window class.
|
|
*
|
|
*/
|
|
BOOL FAR PASCAL WpfInit(HANDLE hInstance);
|
|
|
|
#pragma alloc_text(init, WpfInit)
|
|
|
|
BOOL FAR PASCAL WpfInit(HANDLE hInstance)
|
|
{
|
|
WNDCLASS rClass;
|
|
|
|
if (!fInit) {
|
|
rClass.hCursor = LoadCursor(NULL,IDC_ARROW);
|
|
rClass.hIcon = (HICON)NULL;
|
|
rClass.lpszMenuName = (LPSTR)NULL;
|
|
rClass.lpszClassName = szClass;
|
|
rClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
|
rClass.hInstance = hInstance;
|
|
rClass.style = CS_GLOBALCLASS;
|
|
rClass.lpfnWndProc = PrintfWndProc;
|
|
rClass.cbWndExtra = sizeof (HTXT);
|
|
rClass.cbClsExtra = 0;
|
|
|
|
if (!RegisterClass(&rClass))
|
|
return FALSE;
|
|
|
|
fInit++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
* @doc EXTERNAL WINCOM WPFWINDOW
|
|
*
|
|
* @api HWND | wpfCreateWindow | This function creates a
|
|
* text output window. WPF windows allow <f printf> style output
|
|
* and line oriented input. WPF windows also remember a fixed number of
|
|
* lines of previous output for scrolling back.
|
|
*
|
|
* @parm HWND | hwndParent | Specifies the parent window.
|
|
*
|
|
* @parm HANDLE | hInst | Specifies the module instance handle of the
|
|
* DLL owner. If the parameter is NULL, the module instance handle of
|
|
* the WINCOM DLL is used.
|
|
*
|
|
* @parm LPSTR | lpszTitle | Points to the window title. This
|
|
* information is ignored when the style specified by <p dwStyle> does
|
|
* not create a title bar for the window.
|
|
*
|
|
* @parm DWORD | dwStyle | Specifies the window style flags. All
|
|
* standard window style flags are valid. The WPF window class also defines
|
|
* the following additional flags:
|
|
*
|
|
* @flag WPF_CHARINPUT | The WPF window allows the user to input
|
|
* characters and sends its parent <m WPF_NCHAR> and <m WPF_NTEXT> messages.
|
|
*
|
|
* @parm WORD | x | Specifies the x position of the window.
|
|
* @parm WORD | y | Specifies the y position of the window.
|
|
* @parm WORD | dx | Specifies the width of the window.
|
|
* @parm WORD | dy | Specifies the height of the window.
|
|
*
|
|
* @parm int | iMaxLines | Specifies the maximum number of lines that
|
|
* the WPF window remembers for scrolling purposes. If this
|
|
* parameter is zero, a default value of 100 is supplied.
|
|
*
|
|
* @parm WORD | wID | Specifies the window ID of the WPF window. This
|
|
* code is used in WM_COMMAND message to notify the owner of the WPF
|
|
* window when key events have occurred.
|
|
*
|
|
* @rdesc Returns the window handle of the new WPF window, or NULL if
|
|
* an error occurs. The returned window handle may be used with the
|
|
* normal Windows window-management APIs.
|
|
*
|
|
* @comm A WPF window behaves like a partial Windows control. The
|
|
* owner may change the parameters of a WPF window by sending control
|
|
* messages (including WM_SETFONT, WM_GETFONT, and the WPF messages
|
|
* documented with the WINCOM DLL). The WPF window notifies its owner
|
|
* of state changes by sending the owner WM_COMMAND messages with a
|
|
* control ID of <p wID>. WPF windows are not full controls, however,
|
|
* as they cannot be used in dialog boxes. WPF windows also do not
|
|
* respond to WM_GETTEXT and WM_SETTEXT messages.
|
|
*
|
|
*/
|
|
HWND FAR PASCAL wpfCreateWindow(HWND hwndParent, HANDLE hInst,LPSTR lpszTitle,
|
|
DWORD dwStyle, WORD x, WORD y,
|
|
WORD dx, WORD dy, int iMaxLines, WORD wID)
|
|
{
|
|
HWND hWnd;
|
|
|
|
if (!fInit)
|
|
if (!WpfInit(ghInst))
|
|
/* Return NULL if the class could not be registered */
|
|
return NULL;
|
|
|
|
if (iMaxLines == 0)
|
|
iMaxLines = 100;
|
|
|
|
if (hInst == NULL)
|
|
hInst = ghInst;
|
|
|
|
hWnd = CreateWindow((LPSTR)szClass,
|
|
(LPSTR)lpszTitle,
|
|
dwStyle,
|
|
x,y,
|
|
dx,dy,
|
|
(HWND) hwndParent,
|
|
(HMENU) NULL,
|
|
(HANDLE) hInst,
|
|
(LPSTR) MAKELONG(iMaxLines, wID)
|
|
);
|
|
|
|
return hWnd;
|
|
}
|
|
|
|
|
|
/*****************************************************
|
|
*
|
|
* UTILITY PROCEDURES
|
|
*
|
|
*****************************************************/
|
|
|
|
/*
|
|
* WpfSetFont(hwnd, hfont)
|
|
*
|
|
* Changes the font of a winprintf window to be the specified handle.
|
|
* Rebuilds the internal character size measurements, and causes the
|
|
* window to repaint.
|
|
*
|
|
* Is there a problem with scroll ranges changing here?
|
|
*
|
|
*/
|
|
void NEAR PASCAL WpfSetFont(HWND hWnd, HFONT hFont)
|
|
{
|
|
PTXT pTxt;
|
|
HDC hDC;
|
|
TEXTMETRIC tm;
|
|
|
|
pTxt = LockWinInfo(hWnd);
|
|
|
|
pTxt->hFont = hFont;
|
|
|
|
/* Find out the size of a Char in the font */
|
|
hDC = GetDC(hWnd);
|
|
SelectObject(hDC, hFont);
|
|
GetTextMetrics(hDC, (LPTEXTMETRIC) &tm);
|
|
pTxt->Tdy = tm.tmHeight;
|
|
pTxt->Tdx = tm.tmAveCharWidth;
|
|
ReleaseDC (hWnd, hDC);
|
|
InvalidateRect(hWnd, NULL, TRUE);
|
|
UnlockWinInfo(hWnd);
|
|
}
|
|
|
|
|
|
/*
|
|
* WpfClear(hwnd)
|
|
*
|
|
* Clears all text from the window. Frees all allocated memory. The
|
|
* current queue is not modified?
|
|
*
|
|
*/
|
|
void NEAR PASCAL WpfClear(HWND hWnd)
|
|
{
|
|
int i,iQue;
|
|
PTXT pTxt;
|
|
|
|
pTxt = LockWinInfo(hWnd);
|
|
|
|
iQue = FIRST(pTxt);
|
|
for (i=0; i < pTxt->iCount; i++, INC(pTxt,iQue))
|
|
if (pTxt->arLines[iQue].hText != NULL)
|
|
LocalFree((HANDLE) pTxt->arLines[iQue].hText);
|
|
|
|
pTxt->iFirst = 0; /* Set the que up to have 1 NULL line */
|
|
pTxt->iCount = 1;
|
|
pTxt->iTop = 0;
|
|
pTxt->iLeft = 0;
|
|
pTxt->MaxLen = 0;
|
|
pTxt->arLines[0].hText = NULL;
|
|
pTxt->arLines[0].iLen = 0;
|
|
|
|
UnlockWinInfo(hWnd);
|
|
|
|
InvalidateRect(hWnd,NULL,TRUE);
|
|
WpfSetScrollRange(hWnd,TRUE);
|
|
UpdateWindow(hWnd);
|
|
}
|
|
|
|
/*
|
|
* WpfSetTabs(hwnd, nTabs, pTabs)
|
|
*
|
|
* Sets up hwnd to use the tabs stops specified by pTabs. Copies these
|
|
* tabs into a local-alloc'ed buffer. Any pre-existing tab stops are
|
|
* deallocated.
|
|
*
|
|
*/
|
|
void NEAR PASCAL WpfSetTabs(HWND hwnd, int nTabs, LPINT pTabs)
|
|
{
|
|
PTXT pTxt;
|
|
int i;
|
|
|
|
pTxt = LockWinInfo(hwnd);
|
|
|
|
/* Discard old tabs, allocate space for new tab settings */
|
|
if (pTxt->pTabs)
|
|
LocalFree((HANDLE)pTxt->pTabs);
|
|
|
|
if (pTabs == NULL || nTabs == 0) {
|
|
pTxt->pTabs = NULL;
|
|
pTxt->nTabs = 0;
|
|
}
|
|
else {
|
|
pTxt->pTabs = (PINT)LocalAlloc(LPTR, nTabs * sizeof(int));
|
|
pTxt->nTabs = nTabs;
|
|
|
|
/* Copy caller's tab settings into the current tab table */
|
|
if (pTxt->pTabs) {
|
|
for (i=0; i < nTabs; i++)
|
|
pTxt->pTabs[i] = *pTabs++;
|
|
}
|
|
}
|
|
|
|
InvalidateRect(hwnd,NULL,TRUE);
|
|
UnlockWinInfo(hwnd);
|
|
}
|
|
|
|
/*
|
|
* fIsTabs = WpfGetTabs(hwnd, pTabs)
|
|
*
|
|
* Responds to a WPF_GETTABSTOPS message by filling in the supplied
|
|
* buffer with the current tab settings. Returns TRUE if there are tabs
|
|
* stops, or FALSE if there aren't any tab stops in use.
|
|
*
|
|
*/
|
|
BOOL NEAR PASCAL WpfGetTabs(HWND hwnd, LPINT pTabs)
|
|
{
|
|
PTXT pTxt;
|
|
int i;
|
|
|
|
pTxt = LockWinInfo(hwnd);
|
|
|
|
/* If there are no current tabs, return FALSE */
|
|
if (pTxt->nTabs == 0 || pTxt->pTabs == NULL) {
|
|
UnlockWinInfo(hwnd);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Otherwise, copy my tabs into the caller's buffer. Assume
|
|
* that the caller's buffer is large enough.
|
|
*/
|
|
for (i=0; i < pTxt->nTabs; i++) {
|
|
*pTabs++ = pTxt->pTabs[i];
|
|
}
|
|
|
|
UnlockWinInfo(hwnd);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************
|
|
*
|
|
* WINDOW PROCEDURE
|
|
*
|
|
***********************************/
|
|
|
|
/*--------------------------------------------------------------------------*\
|
|
| WpfPaint(hWnd, hDC ) |
|
|
| |
|
|
| Description: |
|
|
| The paint function. |
|
|
| |
|
|
| Arguments: |
|
|
| hWnd Window to paint to. |
|
|
| hDC handle to update region's display context |
|
|
| |
|
|
| Returns: |
|
|
| nothing |
|
|
| |
|
|
\*--------------------------------------------------------------------------*/
|
|
void NEAR PASCAL WpfPaint(HWND hwnd, HDC hdc)
|
|
{
|
|
PTXT pTxt;
|
|
int i;
|
|
int iQue;
|
|
int xco;
|
|
int yco;
|
|
int iLast;
|
|
RECT rc;
|
|
RECT rcClip;
|
|
HFONT hfontOld;
|
|
|
|
//LockData(0); /* need from spy DS not locked! */
|
|
|
|
pTxt = LockWinInfo(hwnd);
|
|
|
|
GetClientRect(hwnd, &rc);
|
|
rc.left += OFFSETX;
|
|
rc.top += OFFSETY;
|
|
IntersectClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
|
|
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
|
|
SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
|
|
|
|
/* If a font (other than the system font) has been specified, use it */
|
|
if (pTxt->hFont)
|
|
hfontOld = SelectObject(hdc, pTxt->hFont);
|
|
|
|
/* Setup counters as appropriate. Get indexes of first and last
|
|
* lines visible within the window.
|
|
*/
|
|
iLast = LAST(pTxt);
|
|
iQue = TOP(pTxt);
|
|
/* The x and y initial points for the text line.
|
|
* xco is shifted left to account for any horizonal scrolling
|
|
* that may be going on
|
|
*/
|
|
xco = OFFSETX - pTxt->iLeft * pTxt->Tdx; // shifted for h-scrolling
|
|
yco = OFFSETY; // starting y pix value
|
|
|
|
/* RC is the bounding rect for the current line.
|
|
*
|
|
* Calc initial line bounding rect.. top = top of window (padded),
|
|
* bottom = top + height of one line.
|
|
*/
|
|
rc.left = xco;
|
|
rc.top = yco;
|
|
rc.bottom = yco + pTxt->Tdy;
|
|
|
|
/* Get the clipping rectangle */
|
|
GetClipBox(hdc, &rcClip);
|
|
|
|
/* Iter over all lines that are visible - if the bounding rect
|
|
* for the current line intersects the clip rect, draw the line.
|
|
*/
|
|
for (;;) {
|
|
if (rc.bottom >= rcClip.top) {
|
|
/* If we're using tabs, then tab out the text.
|
|
*/
|
|
char *pStr;
|
|
pStr = LocalLock(pTxt->arLines[iQue].hText);
|
|
if (pTxt->nTabs > 0) {
|
|
/* Erase the background */
|
|
ExtTextOut(hdc, 0, 0, ETO_OPAQUE, &rc, NULL, 0, NULL);
|
|
|
|
/* Using *pTxt->arLines[iQue].hText returns the local
|
|
* string that is refered to by local handle hText.
|
|
*/
|
|
TabbedTextOut(hdc, xco, yco,
|
|
(LPSTR)pStr,
|
|
pTxt->arLines[iQue].iLen,
|
|
pTxt->nTabs, pTxt->pTabs, xco);
|
|
}
|
|
else {
|
|
/* Otherwise, blow it out using ExtTextOut */
|
|
ExtTextOut(hdc, xco, yco, ETO_OPAQUE, &rc,
|
|
(LPSTR)pStr,
|
|
pTxt->arLines[iQue].iLen, NULL);
|
|
}
|
|
LocalUnlock(pTxt->arLines[iQue].hText);
|
|
}
|
|
|
|
/* Bail out when finished printing window contents */
|
|
if (iQue == iLast)
|
|
break;
|
|
|
|
INC(pTxt, iQue);
|
|
/* Advance the boundry rect & char positions down one line */
|
|
yco = rc.top = rc.bottom;
|
|
rc.bottom += pTxt->Tdy;
|
|
|
|
if (yco > rcClip.bottom)
|
|
break;
|
|
}
|
|
|
|
/* Restore the old font */
|
|
if (hfontOld)
|
|
SelectObject(hdc, hfontOld);
|
|
|
|
// UnlockData(0);
|
|
}
|
|
|
|
|
|
|
|
|
|
LONG FAR PASCAL PrintfWndProc(HWND hWnd, unsigned uiMessage,
|
|
UINT wParam, LONG lParam)
|
|
{
|
|
PAINTSTRUCT rPS;
|
|
PTXT pTxt;
|
|
HTXT hTxt;
|
|
int i;
|
|
int iQue;
|
|
DWORD rc = 0L;
|
|
|
|
hTxt = (HTXT)GetWindowLong(hWnd,0);
|
|
if (hTxt) pTxt = LocalLock(hTxt);
|
|
|
|
#define lpCreate ((LPCREATESTRUCT)lParam)
|
|
|
|
switch (uiMessage) {
|
|
case WM_CREATE:
|
|
i = LOWORD((DWORD)lpCreate->lpCreateParams);
|
|
|
|
/* Allocate and initialize the window instance structure
|
|
* The storage for the current lines is placed at the end
|
|
* end of the instance data structure. allocate room for it.
|
|
*/
|
|
hTxt = (HTXT) LocalAlloc(LHND, sizeof(struct TEXT_STRUCT) +
|
|
(i - VARSIZE) * sizeof(LINE));
|
|
|
|
if (!hTxt)
|
|
return -1L;
|
|
|
|
pTxt = (PTXT)LocalLock((HANDLE)hTxt);
|
|
|
|
pTxt->hwnd = hWnd;
|
|
pTxt->wID = HIWORD(lpCreate->lpCreateParams);
|
|
pTxt->iFile = -1;
|
|
pTxt->wOutputLocation = WPFOUT_WINDOW;
|
|
pTxt->iFirst = 0; // initially 1 null line
|
|
pTxt->iCount = 1;
|
|
pTxt->iTop = 0;
|
|
pTxt->iLeft = 0; // no initial hscroll offset
|
|
pTxt->MaxLen = 0;
|
|
pTxt->iMaxLines = i;
|
|
pTxt->nTabs = 0;
|
|
/* If user specified character input, allocate a buffer */
|
|
if (lpCreate->style & WPF_CHARINPUT)
|
|
pTxt->hQue = (HQUE) LocalAlloc(LHND | LMEM_ZEROINIT,
|
|
sizeof(QUE));
|
|
else
|
|
pTxt->hQue = NULL;
|
|
|
|
/* Null initial first line */
|
|
pTxt->arLines[0].hText = NULL;
|
|
pTxt->arLines[0].iLen = 0;
|
|
|
|
/* Store the structure pointer onto the window */
|
|
SetWindowLong(hWnd, 0, (LONG) hTxt);
|
|
|
|
/* Setup to use the system font by default */
|
|
WpfSetFont(hWnd, GetStockObject(SYSTEM_FONT));
|
|
|
|
LocalUnlock((HANDLE) hTxt);
|
|
return 0L;
|
|
|
|
case WM_DESTROY:
|
|
// DbgDestroy(hWnd);
|
|
|
|
/* Flush any files in use by the window */
|
|
SetOutput(hWnd, WPFOUT_DISABLED, 0L);
|
|
|
|
/* Blow away all lines held by the window */
|
|
iQue = FIRST(pTxt);
|
|
for (i=0; i < pTxt->iCount; i++, INC(pTxt,iQue))
|
|
if (pTxt->arLines[iQue].hText != NULL)
|
|
LocalFree((HANDLE) pTxt->arLines[iQue].hText);
|
|
|
|
/* And kill char input and tab stop storage */
|
|
if (pTxt->hQue)
|
|
LocalFree ((HANDLE) pTxt->hQue);
|
|
if (pTxt->pTabs)
|
|
LocalFree ((HANDLE) pTxt->pTabs);
|
|
|
|
LocalUnlock(hTxt);
|
|
LocalFree((HANDLE)hTxt);
|
|
hTxt = NULL;
|
|
break;
|
|
|
|
// case WPF_SETNLINES:
|
|
// return 0L;
|
|
|
|
case WPF_GETNLINES:
|
|
rc = pTxt->iMaxLines;
|
|
break;
|
|
|
|
case WM_GETFONT:
|
|
rc = pTxt->hFont;
|
|
break;
|
|
|
|
case WM_SETFONT:
|
|
WpfSetFont(hWnd, wParam);
|
|
break;
|
|
|
|
/* Tab stop stuff */
|
|
case WPF_SETTABSTOPS:
|
|
WpfSetTabs(hWnd, wParam, (LPINT) lParam);
|
|
break;
|
|
|
|
case WPF_GETNUMTABS:
|
|
rc = pTxt->pTabs ? pTxt->nTabs : 0;
|
|
break;
|
|
|
|
case WPF_GETTABSTOPS:
|
|
rc = (LONG) WpfGetTabs(hWnd, (LPINT) lParam);
|
|
break;
|
|
|
|
case WPF_SETOUTPUT:
|
|
rc = (LONG) SetOutput(hWnd, wParam, lParam);
|
|
break;
|
|
|
|
case WPF_GETOUTPUT:
|
|
rc = (LONG) GetOutput(hWnd);
|
|
break;
|
|
|
|
case WPF_CLEARWINDOW:
|
|
WpfClear(hWnd);
|
|
break;
|
|
|
|
case WM_SIZE:
|
|
/* It is possible to get WM_SIZEs as a result of
|
|
* dicking with scrollbars.. Avoid race conditions
|
|
*/
|
|
if (!pTxt->fScrollSemaphore) {
|
|
pTxt->fScrollSemaphore++;
|
|
WpfSetScrollRange(hWnd, TRUE);
|
|
UpdateCursorPos(pTxt);
|
|
pTxt->fScrollSemaphore--;
|
|
}
|
|
break;
|
|
|
|
case WM_VSCROLL:
|
|
switch (wParam) {
|
|
case SB_LINEDOWN:
|
|
WpfVScroll (hWnd,pTxt,1);
|
|
break;
|
|
case SB_LINEUP:
|
|
WpfVScroll (hWnd,pTxt,-1);
|
|
break;
|
|
case SB_PAGEUP:
|
|
WpfVScroll (hWnd,pTxt,-LinesInWpfWindow(hWnd));
|
|
break;
|
|
case SB_PAGEDOWN:
|
|
WpfVScroll (hWnd,pTxt,LinesInWpfWindow(hWnd));
|
|
break;
|
|
case SB_THUMBTRACK:
|
|
WpfVScroll (hWnd,pTxt,LOWORD(lParam)-pTxt->iTop);
|
|
break;
|
|
|
|
case SB_THUMBPOSITION:
|
|
WpfVScroll (hWnd,pTxt,LOWORD(lParam)-pTxt->iTop);
|
|
/* Fall through */
|
|
|
|
case SB_ENDSCROLL:
|
|
WpfSetScrollRange(hWnd,TRUE);
|
|
UpdateCursorPos(pTxt);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_HSCROLL:
|
|
switch (wParam) {
|
|
case SB_LINEDOWN:
|
|
WpfHScroll (hWnd, pTxt, 1);
|
|
break;
|
|
case SB_LINEUP:
|
|
WpfHScroll (hWnd, pTxt, -1);
|
|
break;
|
|
case SB_PAGEUP:
|
|
WpfHScroll (hWnd, pTxt, -CharsInWpfWindow(hWnd));
|
|
break;
|
|
case SB_PAGEDOWN:
|
|
WpfHScroll (hWnd, pTxt, CharsInWpfWindow(hWnd));
|
|
break;
|
|
case SB_THUMBTRACK:
|
|
WpfHScroll (hWnd, pTxt, LOWORD(lParam) - pTxt->iLeft);
|
|
break;
|
|
|
|
case SB_THUMBPOSITION:
|
|
WpfHScroll (hWnd, pTxt, LOWORD(lParam) - pTxt->iLeft);
|
|
/* Fall through */
|
|
|
|
case SB_ENDSCROLL:
|
|
WpfSetScrollRange(hWnd,TRUE);
|
|
UpdateCursorPos(pTxt);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_PAINT:
|
|
BeginPaint(hWnd,&rPS);
|
|
WpfPaint (hWnd,rPS.hdc);
|
|
EndPaint(hWnd,&rPS);
|
|
break;
|
|
|
|
/* Allow keyboard scrolling */
|
|
case WM_KEYDOWN:
|
|
switch (wParam) {
|
|
case VK_UP:
|
|
PostMessage (hWnd,WM_VSCROLL,SB_LINEUP,0L); break;
|
|
case VK_DOWN:
|
|
PostMessage (hWnd,WM_VSCROLL,SB_LINEDOWN,0L); break;
|
|
case VK_PRIOR:
|
|
PostMessage (hWnd,WM_VSCROLL,SB_PAGEUP,0L); break;
|
|
case VK_NEXT:
|
|
PostMessage (hWnd,WM_VSCROLL,SB_PAGEDOWN,0L); break;
|
|
|
|
case VK_HOME:
|
|
PostMessage (hWnd,WM_HSCROLL,SB_PAGEUP,0L); break;
|
|
case VK_END:
|
|
PostMessage (hWnd,WM_HSCROLL,SB_PAGEDOWN,0L); break;
|
|
case VK_LEFT:
|
|
PostMessage (hWnd,WM_HSCROLL,SB_LINEUP,0L); break;
|
|
case VK_RIGHT:
|
|
PostMessage (hWnd,WM_HSCROLL,SB_LINEDOWN,0L); break;
|
|
}
|
|
break;
|
|
|
|
/* Handle focus messages to hide and show the caret
|
|
* if the WPF window allows for character input.
|
|
*/
|
|
case WM_SETFOCUS:
|
|
if (pTxt->hQue) {
|
|
CreateCaret(hWnd,0,1,pTxt->Tdy);
|
|
UpdateCursorPos(pTxt);
|
|
ShowCaret(hWnd);
|
|
}
|
|
break;
|
|
|
|
case WM_KILLFOCUS:
|
|
if (pTxt->hQue)
|
|
DestroyCaret();
|
|
break;
|
|
|
|
case WM_CHAR:
|
|
EnQueChar(hTxt,wParam);
|
|
break;
|
|
|
|
case WM_KEYUP:
|
|
switch (wParam) {
|
|
case VK_F3:
|
|
EnQueChar(hTxt,VK(wParam));
|
|
break;
|
|
|
|
/* Send endscroll when the key goes up - allows for
|
|
* type-a-matic action.
|
|
*/
|
|
case VK_UP:
|
|
case VK_DOWN:
|
|
case VK_PRIOR:
|
|
case VK_NEXT:
|
|
PostMessage (hWnd,WM_VSCROLL,SB_ENDSCROLL,0L);
|
|
break;
|
|
|
|
case VK_HOME:
|
|
case VK_END:
|
|
case VK_LEFT:
|
|
case VK_RIGHT:
|
|
PostMessage (hWnd,WM_HSCROLL,SB_ENDSCROLL,0L);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return DefWindowProc(hWnd,uiMessage,wParam,lParam);
|
|
}
|
|
|
|
if (hTxt) LocalUnlock(hTxt);
|
|
return rc;
|
|
}
|
|
|
|
|
|
/***********************************************
|
|
*
|
|
* SCROLLING STUFF
|
|
*
|
|
***********************************************/
|
|
|
|
/*
|
|
* WpfVScroll(hwnd, pTxt, n)
|
|
*
|
|
* Vertical scroll the window by n number of lines.
|
|
*
|
|
*/
|
|
void NEAR PASCAL WpfVScroll(HWND hWnd, PTXT pTxt, int n)
|
|
{
|
|
RECT rect;
|
|
int iRange;
|
|
|
|
/* GetScrollRange (hWnd,SB_VERT,&iMinPos,&iMaxPos); */
|
|
iRange = pTxt->iRangeV; // where did this come from?
|
|
GetClientRect(hWnd, &rect);
|
|
rect.left += OFFSETX; // adjust for pad boundry
|
|
rect.top += OFFSETY;
|
|
|
|
n = BOUND(pTxt->iTop + n, 0, iRange) - pTxt->iTop;
|
|
pTxt->iTop += n;
|
|
ScrollWindow(hWnd, 0, -n * pTxt->Tdy, &rect, &rect);
|
|
SetScrollPos(hWnd, SB_VERT, pTxt->iTop, gbRedraw);
|
|
UpdateWindow(hWnd);
|
|
}
|
|
|
|
|
|
/*
|
|
* WpfHScroll(hwnd, ptxt, n)
|
|
*
|
|
* Horizontally scrolls the window by n number of character widths.
|
|
*
|
|
*/
|
|
void NEAR PASCAL WpfHScroll(HWND hWnd, PTXT pTxt, int n)
|
|
{
|
|
RECT rect;
|
|
int iRange;
|
|
|
|
/* GetScrollRange (hWnd,SB_HORZ,&iMinPos,&iMaxPos); */
|
|
iRange = pTxt->iRangeH;
|
|
GetClientRect (hWnd,&rect);
|
|
rect.left += OFFSETX;
|
|
rect.top += OFFSETY;
|
|
|
|
n = BOUND(pTxt->iLeft + n, 0, iRange) - pTxt->iLeft;
|
|
pTxt->iLeft += n;
|
|
ScrollWindow(hWnd, -n * pTxt->Tdx, 0, &rect, &rect);
|
|
SetScrollPos(hWnd, SB_HORZ, pTxt->iLeft, gbRedraw);
|
|
UpdateWindow(hWnd);
|
|
}
|
|
|
|
|
|
/*
|
|
* nLines = LinesInWpfWindow(hwnd)
|
|
*
|
|
* Returns the height in lines of the window.
|
|
*
|
|
*/
|
|
int NEAR PASCAL LinesInWpfWindow(HWND hWnd)
|
|
{
|
|
RECT rRect;
|
|
PTXT pTxt;
|
|
int iLines;
|
|
|
|
pTxt = *(HTXT) GetWindowLong(hWnd, 0);
|
|
GetClientRect(hWnd, &rRect);
|
|
iLines = 0;
|
|
if (pTxt) {
|
|
iLines = (rRect.bottom - rRect.top - OFFSETY) / pTxt->Tdy;
|
|
iLines = min(iLines, pTxt->iMaxLines);
|
|
}
|
|
return iLines;
|
|
}
|
|
|
|
|
|
/*
|
|
* nChars = CharsInWpfWindow(hwnd)
|
|
*
|
|
* Returns the width in characters of the window.
|
|
*
|
|
*/
|
|
int NEAR PASCAL CharsInWpfWindow(HWND hWnd)
|
|
{
|
|
RECT rRect;
|
|
PTXT pTxt;
|
|
|
|
pTxt = *(HTXT)GetWindowLong (hWnd,0);
|
|
GetClientRect(hWnd,&rRect);
|
|
return pTxt ? (rRect.right - rRect.left - OFFSETX) / pTxt->Tdx : 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* WpfMaxLen(pTxt)
|
|
*
|
|
* This function sets the pTxt->MaxLen field to be the length in
|
|
* characters of the longest string currently being stored by the WPF
|
|
* window.
|
|
*
|
|
*/
|
|
void NEAR PASCAL WpfMaxLen(PTXT pTxt)
|
|
{
|
|
int iQue;
|
|
int iLast;
|
|
int iLen;
|
|
SIZE size;
|
|
// DWORD dwLen;
|
|
// HDC hdc;
|
|
|
|
#if 0
|
|
hdc = GetDC(NULL);
|
|
|
|
if (pTxt->hFont)
|
|
SelectObject(hdc, pTxt->hFont);
|
|
#endif
|
|
|
|
iLast = LAST(pTxt);
|
|
iQue = TOP(pTxt);
|
|
pTxt->MaxLen = 0;
|
|
for (;;) {
|
|
iLen = pTxt->arLines[iQue].iLen;
|
|
|
|
#if 0
|
|
if (pTxt->nTabs)
|
|
dwLen = GetTabbedTextExtent(hdc, (LPSTR) *pTxt->arLines[iQue].hText,
|
|
iLen, pTxt->nTabs, (LPINT)pTxt->pTabs);
|
|
else
|
|
GetTextExtent(hdc, (LPSTR) *pTxt->arLines[iQue].hText,iLen,&size);
|
|
|
|
iLen = size.cx // pTxt->Tdx + 1;
|
|
#endif
|
|
|
|
if (iLen > pTxt->MaxLen)
|
|
pTxt->MaxLen = iLen;
|
|
if (iQue == iLast) break;
|
|
INC(pTxt,iQue);
|
|
}
|
|
|
|
// ReleaseDC(NULL,hdc);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* WpfSetScrollRange(hwnd, bRedraw)
|
|
*
|
|
* This function sets the scrollbar ranges according to the current
|
|
* character/line contents of the window. Both the horizontal and
|
|
* vertical scrollbars are adjusted.
|
|
*
|
|
* This function then calls WpfVScroll/WpfHScroll to adjust the
|
|
* scrollbar position accordingly.
|
|
*
|
|
*/
|
|
void NEAR PASCAL WpfSetScrollRange(HWND hWnd, BOOL bRedraw)
|
|
{
|
|
PTXT pTxt;
|
|
int iRange;
|
|
|
|
if (pTxt = *(HTXT) GetWindowLong(hWnd, 0)) {
|
|
gbRedraw = bRedraw;
|
|
/* Update the scroll bars */
|
|
|
|
iRange = pTxt->iCount - LinesInWpfWindow(hWnd) + 1;
|
|
|
|
/* Adjust for blank last line? */
|
|
if (pTxt->arLines[LAST(pTxt)].iLen == 0);
|
|
iRange -= 1;
|
|
|
|
if (iRange < 0) iRange = 0;
|
|
|
|
/* Set the scrollbar range to that calculated */
|
|
pTxt->iRangeV = iRange;
|
|
SetScrollRange(hWnd, SB_VERT, 0, iRange, FALSE);
|
|
WpfVScroll(hWnd, pTxt, 0);
|
|
|
|
/* Setup the horizontal scrollbar range */
|
|
WpfMaxLen(pTxt);
|
|
iRange = pTxt->MaxLen - CharsInWpfWindow(hWnd) + 1;
|
|
if (iRange < 0) iRange = 0;
|
|
pTxt->iRangeH = iRange;
|
|
SetScrollRange(hWnd, SB_HORZ, 0, iRange, FALSE);
|
|
WpfHScroll(hWnd, pTxt, 0);
|
|
|
|
gbRedraw = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/***********************************************************
|
|
*
|
|
* STUFF TO ADD NEW TEXT LINES
|
|
*
|
|
***********************************************************/
|
|
|
|
/*
|
|
* NewLine(pTxt)
|
|
*
|
|
* Adjusts a WPF window when adding a line to the circular array.
|
|
* iCount is the count of valid lines in the array. If we
|
|
* haven't yet filled up the array, the count is merely increased.
|
|
* Otherwise, if the array is full and we're about to wrap around, fixup
|
|
* the wrap-around.
|
|
*
|
|
*/
|
|
void NEAR PASCAL NewLine(PTXT pTxt)
|
|
{
|
|
int iLast = LAST(pTxt);
|
|
int iLine,cLine;
|
|
RECT rect;
|
|
|
|
if (pTxt->iCount == pTxt->iMaxLines) {
|
|
/* If the array is full, check for wrap-around */
|
|
LocalFree ((HANDLE)pTxt->arLines[pTxt->iFirst].hText);
|
|
pTxt->arLines[pTxt->iFirst].hText = NULL;
|
|
|
|
INC(pTxt, pTxt->iFirst);
|
|
|
|
if (pTxt->iTop > 0)
|
|
pTxt->iTop--;
|
|
else {
|
|
GetClientRect (pTxt->hwnd,&rect);
|
|
rect.left += OFFSETX;
|
|
rect.top += OFFSETY;
|
|
ScrollWindow (pTxt->hwnd, 0, -pTxt->Tdy, &rect, &rect);
|
|
}
|
|
}
|
|
else {
|
|
pTxt->iCount++;
|
|
}
|
|
iLast = LAST(pTxt);
|
|
pTxt->arLines[iLast].hText = NULL;
|
|
pTxt->arLines[iLast].iLen = 0;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* fSuccess = ChangeLine(pTxt, iLine, lpsz)
|
|
*
|
|
* Changes line number <iLine> to be the string pointed to by lpsz.
|
|
* Frees any line currently occupying index <iLine>, and then alloc and
|
|
* stores text lpsz.
|
|
*
|
|
*/
|
|
int NEAR PASCAL ChangeLine(PTXT pTxt, int iLine, LPSTR lpch)
|
|
{
|
|
int iLen;
|
|
LPSTR pData;
|
|
|
|
if (pTxt->arLines[iLine].hText != NULL)
|
|
LocalFree((HANDLE)pTxt->arLines[iLine].hText);
|
|
|
|
iLen = lstrlen(lpch);
|
|
if ((pTxt->arLines[iLine].hText = (char**)LocalAlloc(LHND,iLen+1))== NULL)
|
|
return FALSE;
|
|
|
|
pTxt->arLines[iLine].iLen = iLen;
|
|
pData = LocalLock(pTxt->arLines[iLine].hText);
|
|
lstrcpy(pData, lpch);
|
|
LocalUnlock(pTxt->arLines[iLine].hText);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
*/
|
|
int NEAR PASCAL InsertString(PTXT pTxt, LPSTR lpstr)
|
|
{
|
|
int iBuf;
|
|
int iLast = LAST(pTxt);
|
|
int cLine = 0;
|
|
char buf[MAXBUFLEN];
|
|
buf[0] = '\0';
|
|
|
|
/*
|
|
* copy the string already there
|
|
*/
|
|
{
|
|
PSTR pch;
|
|
HANDLE hText;
|
|
|
|
hText = pTxt->arLines[iLast].hText;
|
|
if (hText) {
|
|
pch = LocalLock(hText); // (LocalLock eqiv)
|
|
iBuf = lstrlen(pch);
|
|
lstrcpy(buf, pch); // why?
|
|
LocalUnlock(pTxt->arLines[iLast].hText);
|
|
} else {
|
|
iBuf = 0;
|
|
}
|
|
}
|
|
|
|
while (*lpstr != '\0') {
|
|
while (*lpstr != '\n' && *lpstr != '\0' && iBuf < MAXBUFLEN-2)
|
|
switch (*lpstr) {
|
|
|
|
case '\b':
|
|
/* Backspace, blow away one character */
|
|
iBuf--;
|
|
lpstr++;
|
|
break;
|
|
|
|
case '\r':
|
|
/* Carriage return, go back to beginning of line */
|
|
iBuf = 0;
|
|
lpstr++;
|
|
break;
|
|
|
|
default:
|
|
/* Otherwise, add this char to line */
|
|
buf[iBuf++] = *lpstr++;
|
|
break;
|
|
}
|
|
buf[iBuf++] = 0;
|
|
|
|
/* Presto chango add the line */
|
|
ChangeLine(pTxt, iLast, buf); /* buf must be a asciiz string */
|
|
|
|
if (*lpstr == '\n') { /* Now do the next string after the \n */
|
|
lpstr++;
|
|
iBuf = 0;
|
|
cLine++;
|
|
NewLine(pTxt);
|
|
INC(pTxt, iLast);
|
|
}
|
|
}
|
|
return cLine; /* the number of new lines added to list */
|
|
}
|
|
|
|
|
|
/**********************************************************
|
|
*
|
|
* CHARACTER INPUT STUFF
|
|
*
|
|
**********************************************************/
|
|
|
|
|
|
BOOL NEAR PASCAL EnQueChar(HTXT hTxt, WORD vk)
|
|
{
|
|
PTXT pTxt;
|
|
PQUE pQue;
|
|
int i;
|
|
HWND hwndP;
|
|
|
|
pTxt = (PTXT)LocalLock((HANDLE)hTxt);
|
|
|
|
if (!pTxt->hQue)
|
|
goto noque;
|
|
|
|
pQue = (PQUE)LocalLock((HANDLE)pTxt->hQue);
|
|
|
|
i = pQue->iLen;
|
|
|
|
switch (vk)
|
|
{
|
|
case '\b':
|
|
if (i > 0)
|
|
{
|
|
--i;
|
|
wpfOut(pTxt->hwnd, "\b");
|
|
}
|
|
break;
|
|
|
|
case VK(VK_F3):
|
|
wpfOut(pTxt->hwnd, pQue->ach + i);
|
|
i += lstrlen(pQue->ach + i);
|
|
break;
|
|
|
|
case '\r':
|
|
case '\n':
|
|
if (GetKeyState(VK_CONTROL) < 0)
|
|
{
|
|
wpfOut(pTxt->hwnd,"\\\n");
|
|
}
|
|
else
|
|
{
|
|
wpfOut(pTxt->hwnd, "\n");
|
|
pQue->ach[i] = '\0';
|
|
if (hwndP = GetParent(pTxt->hwnd))
|
|
SendMessage(hwndP, WPF_NTEXT, pTxt->wID,
|
|
(LONG)(LPSTR)pQue->ach);
|
|
i = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (i < QUESIZE)
|
|
{
|
|
pQue->ach[i] = (char)vk;
|
|
sprintf(ach, ("%c", vk));
|
|
wpfOut(pTxt->hwnd, ach);
|
|
if (hwndP = GetParent(pTxt->hwnd))
|
|
SendMessage(hwndP, WPF_NCHAR, pTxt->wID, (LONG) vk);
|
|
i++;
|
|
}
|
|
else
|
|
{
|
|
/* Input que is full, beep to notify */
|
|
MessageBeep(0);
|
|
}
|
|
break;
|
|
}
|
|
|
|
pQue->iLen = i;
|
|
LocalUnlock((HANDLE)pTxt->hQue);
|
|
|
|
noque:
|
|
LocalUnlock((HANDLE)hTxt);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
void NEAR PASCAL UpdateCursorPos(PTXT pTxt)
|
|
{
|
|
int iLine;
|
|
int y,x;
|
|
int iLen;
|
|
DWORD dw;
|
|
HDC hdc;
|
|
SIZE size;
|
|
char **h;
|
|
char *ptxt;
|
|
|
|
/* If I don't do char input, or don't have the focus, forget it */
|
|
if (!pTxt->hQue || GetFocus() != pTxt->hwnd)
|
|
return;
|
|
|
|
hdc = GetDC(NULL);
|
|
SelectObject(hdc, pTxt->hFont);
|
|
iLen = pTxt->arLines[LAST(pTxt)].iLen - pTxt->iLeft;
|
|
h = pTxt->arLines[LAST(pTxt)].hText;
|
|
|
|
// HACK HACK Need to account for tabs?
|
|
ptxt = LocalLock(h);
|
|
dw = GetTextExtentPoint(hdc, (LPSTR) ptxt + pTxt->iLeft, iLen, &size);
|
|
LocalUnlock(h);
|
|
iLine = pTxt->iCount - pTxt->iTop;
|
|
ReleaseDC(NULL,hdc);
|
|
|
|
y = OFFSETY + (iLine - 1) * pTxt->Tdy;
|
|
x = OFFSETX + size.cx;
|
|
SetCaretPos(x,y);
|
|
}
|
|
|
|
|
|
/*************************************************
|
|
*
|
|
* OUTPUT APIS
|
|
*
|
|
*************************************************/
|
|
|
|
|
|
|
|
/*
|
|
* fSuccess = SetOutput(hwnd, wCommand, lpszFile)
|
|
*
|
|
* Changes the output location of the window to be the location
|
|
* designated by wParam, one of the WPFOUT_ codes. If this specifies a
|
|
* file, lParam points to the filename.
|
|
*
|
|
* If the new output location cannot be opened/used, the previous output
|
|
* location is not altered and FALSE is returned. Otherwise, the
|
|
* previous output location is closed (for files) and TRUE is returned.
|
|
*
|
|
*/
|
|
WORD NEAR PASCAL SetOutput(HWND hwnd, UINT wParam, LONG lParam)
|
|
{
|
|
PTXT pTxt;
|
|
int i;
|
|
HANDLE h;
|
|
int fhOld = -1;
|
|
|
|
#define COM1_FH (3) // stdaux
|
|
|
|
/* Check for invalid command code */
|
|
if (!(wParam == WPFOUT_WINDOW || wParam == WPFOUT_COM1 ||
|
|
wParam == WPFOUT_NEWFILE || wParam == WPFOUT_APPENDFILE ||
|
|
wParam == WPFOUT_DISABLED)) {
|
|
return FALSE;
|
|
}
|
|
|
|
h = GetWindowLong(hwnd, 0);
|
|
pTxt = (PTXT)LocalLock(h);
|
|
|
|
/* Save the old file handle */
|
|
fhOld = pTxt->iFile;
|
|
|
|
/* If I'm using a file output type, setup the file handle */
|
|
switch (wParam) {
|
|
case WPFOUT_COM1:
|
|
pTxt->iFile = COM1_FH;
|
|
break;
|
|
|
|
case WPFOUT_APPENDFILE:
|
|
/* Open file to see if it is there, then seek to end */
|
|
i = _lopen((LPSTR) lParam, OF_READWRITE);
|
|
if (i == -1) {
|
|
/* File didn't exist, just create it */
|
|
i = _lcreat((LPSTR) lParam, 0);
|
|
if (i == -1) {
|
|
/* Couldn't open, just return FALSE */
|
|
LocalUnlock(h);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else {
|
|
/* Seek to the end of existing file */
|
|
_llseek(i, 0L, 2);
|
|
}
|
|
|
|
pTxt->iFile = i;
|
|
break;
|
|
|
|
case WPFOUT_NEWFILE:
|
|
i = _lcreat((LPSTR) lParam, 0);
|
|
if (i == -1) {
|
|
LocalUnlock(h);
|
|
return FALSE;
|
|
}
|
|
pTxt->iFile = i;
|
|
break;
|
|
|
|
case WPFOUT_DISABLED:
|
|
case WPFOUT_WINDOW:
|
|
pTxt->iFile = -1;
|
|
break;
|
|
|
|
}
|
|
|
|
/* Clear any existing open file handle by closing it */
|
|
if (fhOld != -1 && fhOld != COM1_FH) {
|
|
/* Close the file */
|
|
_lclose(fhOld);
|
|
}
|
|
|
|
pTxt->wOutputLocation = wParam;
|
|
LocalUnlock(h);
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* wOutput = GetOutput(hwnd)
|
|
*
|
|
* Returns the output location for window hwnd (one of the WPFOUT_ codes)
|
|
*
|
|
*/
|
|
WORD NEAR PASCAL GetOutput(HWND hwnd)
|
|
{
|
|
PTXT pTxt;
|
|
WORD w;
|
|
HANDLE h;
|
|
|
|
h = GetWindowLong(hwnd, 0);
|
|
pTxt = (PTXT) LocalLock(h);
|
|
|
|
w = pTxt->wOutputLocation;
|
|
|
|
LocalUnlock(h);
|
|
return w;
|
|
}
|
|
|
|
/*
|
|
* @doc EXTERNAL WINCOM WPFWINDOW
|
|
*
|
|
* @api int | wpfPrintf | This function prints a string to a WPF window
|
|
* (or redirected output device) using <f printf> style formatting
|
|
* codes. The output is placed at the end of the specified WPF window,
|
|
* which is scrolled as required. This function does not yield.
|
|
*
|
|
* @parm HWND | hwnd | Specifies the WPF window. Output to the window
|
|
* may be redirected to a file or COM1 by sending a WPF_SETOUTPUT window
|
|
* message to <p hwnd>. If output has been redirected, this parameter
|
|
* is still required as the current output location is stored in the WPF
|
|
* window instance data.
|
|
*
|
|
* @parm LPSTR | lpszFormat | Points to the output string format
|
|
* specification. This string uses the same formatting codes as the
|
|
* Windows <f wsprintf> function.
|
|
*
|
|
* @parm argument | [ arguments, ...] | Extra parameters
|
|
* as required by the
|
|
* formatting string. Note that these parameters are in the form
|
|
* required by <p wsprintf>, so that all string arguments must be far
|
|
* pointers (LPSTR) or be cast to be far pointers.
|
|
*
|
|
* @rdesc Returns the number of characters output. If output to
|
|
* the WPF window is disabled, zero is returned. The returned count of
|
|
* characters output does not include the translation of newline
|
|
* characters into carriage return newline sequences.
|
|
*
|
|
* @xref wpfVprintf
|
|
*
|
|
*/
|
|
//int FAR cdecl wpfPrintf(HWND hwnd, LPSTR lpszFormat, ...)
|
|
//{
|
|
// return wpfVprintf(hwnd, lpszFormat, (LPSTR)(&lpszFormat + 1));
|
|
//}
|
|
|
|
|
|
/* wpfWrtTTY(hWnd, sz)
|
|
*
|
|
* Print <sz> to wprintf window <hWnd>.
|
|
*
|
|
*/
|
|
BOOL NEAR PASCAL wpfWrtTTY(HWND hWnd, LPSTR sz)
|
|
{
|
|
RECT rect;
|
|
int iFree;
|
|
int iLine;
|
|
PTXT pTxt;
|
|
HTXT hTxt;
|
|
MSG rMsg;
|
|
POINT rPoint;
|
|
|
|
if (!hWnd) hWnd = hwndLast;
|
|
|
|
if (!hWnd || !IsWindow(hWnd))
|
|
return FALSE; /* fail if bad window handle */
|
|
|
|
hwndLast = hWnd;
|
|
|
|
hTxt = (HTXT)GetWindowLong (hWnd,0);
|
|
pTxt = (PTXT)LocalLock((HANDLE)hTxt);
|
|
|
|
iLine = pTxt->iCount - pTxt->iTop;
|
|
/*
|
|
* invalidate the last line to the bottom of window so
|
|
* new text will be painted.
|
|
*/
|
|
GetClientRect(hWnd,&rect);
|
|
rect.top += (iLine-1) * pTxt->Tdy;
|
|
InvalidateRect (hWnd,&rect,FALSE);
|
|
|
|
InsertString (pTxt, sz); /* Insert text in the que */
|
|
iLine = (pTxt->iCount - pTxt->iTop) - iLine;
|
|
|
|
if (iLine > 0) {
|
|
WpfSetScrollRange (hWnd,FALSE);
|
|
WpfVScroll (hWnd,pTxt,pTxt->iCount);/* scroll all the way to bottom */
|
|
}
|
|
#if 0
|
|
else {
|
|
WpfSetScrollRange (hWnd,TRUE);
|
|
}
|
|
#endif
|
|
UpdateCursorPos(pTxt);
|
|
LocalUnlock((HANDLE)hTxt);
|
|
UpdateWindow (hWnd);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* @doc EXTERNAL WINCOM WPFWINDOW
|
|
*
|
|
* @api int | wpfVprintf | This function prints a string to a WPF window
|
|
* (or redirected output device) using <f printf> style formatting
|
|
* codes. This function is the same as the <f wpfOut> function, except
|
|
* that arguments to the format string are placed in an array of WORDs
|
|
* or DWORDs.
|
|
*
|
|
* @parm HWND | hwnd | Specifies the WPF window. Output to the window
|
|
* may be redirected to a file or COM1 by sending a WPF_SETOUTPUT window
|
|
* message to <p hwnd>. If output has been redirected, this parameter
|
|
* is still required as the current output location is stored in the WPF
|
|
* window instance data.
|
|
*
|
|
* @parm LPSTR | lpszFormat | Points to the output string format
|
|
* specification. This string uses the same formatting codes as the
|
|
* Windows <f wsprintf> function.
|
|
*
|
|
* @parm LPSTR | pargs | Points to an array of words, each of which
|
|
* specifies an argument for the format string <p lspzFormat>. The
|
|
* number, type, and interpretation of the arguments depend on the
|
|
* corresponding format control sequences in <p lpszFormat>.
|
|
*
|
|
* @rdesc Returns the number of characters output. If output to the
|
|
* WPF window is disabled, zero is returned. The returned count of
|
|
* characters output does not include the translation of newline
|
|
* characters into carriage return newline sequences.
|
|
*
|
|
* @xref wpfPrintf
|
|
*
|
|
*/
|
|
//int FAR cdecl wpfVprintf(HWND hwnd, LPSTR lpszFormat, LPSTR pargs)
|
|
//{
|
|
// int i;
|
|
//
|
|
//
|
|
// i = wvsprintf(bufTmp, lpszFormat, pargs);
|
|
// wpfOut(hwnd, bufTmp);
|
|
//
|
|
// return i;
|
|
//}
|
|
|
|
/*
|
|
* @doc WINCOM EXTERNAL WPFWINDOW
|
|
*
|
|
* @api void | wpfOut | This function prints a string to a WPF window
|
|
* or redirected output device. No formatting is carried out upon the
|
|
* string, it is printed verbatim.
|
|
*
|
|
* @parm HWND | hwnd | Specifies the WPF window. Output to the window
|
|
* may be redirected to a file or COM1 by sending a WPF_SETOUTPUT window
|
|
* message to <p hwnd>. If output has been redirected, this parameter
|
|
* is still required as the current output location is stored in the WPF
|
|
* window instance data.
|
|
*
|
|
* @parm LPSTR | lpsz | Points to the string to be output.
|
|
*
|
|
* @rdesc None.
|
|
*
|
|
* @xref wpfPrintf
|
|
*
|
|
*/
|
|
void FAR PASCAL wpfOut(HWND hwnd, LPSTR lpsz)
|
|
{
|
|
PTXT pTxt;
|
|
HTXT hTxt;
|
|
|
|
if (!IsWindow(hwnd))
|
|
return;
|
|
|
|
hTxt = (HTXT) GetWindowLong(hwnd, 0);
|
|
pTxt = (PTXT) LocalLock((HANDLE) hTxt);
|
|
|
|
if (pTxt->wOutputLocation != WPFOUT_DISABLED) {
|
|
if (pTxt->wOutputLocation == WPFOUT_WINDOW) {
|
|
wpfWrtTTY(hwnd, lpsz);
|
|
}
|
|
else {
|
|
wpfWrtFile(pTxt->iFile, lpsz);
|
|
}
|
|
}
|
|
|
|
LocalUnlock((HANDLE) hTxt);
|
|
}
|
|
|
|
|
|
void wpfWrtFile(int fh, LPSTR sz)
|
|
{
|
|
LPSTR p, q;
|
|
char save;
|
|
|
|
if (fh == -1)
|
|
return;
|
|
|
|
/* output to <fh>, but must convert \n's to \n\r's;
|
|
* code below is designed to minimize calls to write()
|
|
*/
|
|
for (p = q = sz; *p != 0; p++) {
|
|
/* e.g. bufTmp="hello\nabc", q->'h', p->'\n' */
|
|
if (*p == '\n') {
|
|
/* hack: temporarily replace next char by \r */
|
|
/* won't work if string is READ-ONLY!!! */
|
|
save = *++p; /* remember it */
|
|
p[0] = '\n'; /* replace by \r */
|
|
p[-1]= '\r'; /* replace by \r */
|
|
_lwrite(fh, q, p - q + 1);
|
|
q = p; /* for next write() */
|
|
*p-- = save; /* un-hack */
|
|
*p = '\n';
|
|
}
|
|
}
|
|
if (p > q) /* any part of <bufTmp> left to write */
|
|
_lwrite(fh, q, p - q);
|
|
|
|
//
|
|
// flush the file, by closing a copy of the file
|
|
//
|
|
FlushFileBuffers(fh);
|
|
}
|