/* * 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 #include #include "wincom.h" #include "sbtest.h" #include /* * 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 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

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 and 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

. 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 to be the string pointed to by lpsz. * Frees any line currently occupying index , 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 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

. 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 function. * * @parm argument | [ arguments, ...] | Extra parameters * as required by the * formatting string. Note that these parameters are in the form * required by

, 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 to wprintf window . * */ 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 style formatting * codes. This function is the same as the 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

. 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 function. * * @parm LPSTR | pargs | Points to an array of words, each of which * specifies an argument for the format string

. The * number, type, and interpretation of the arguments depend on the * corresponding format control sequences in

. * * @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

. 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 , 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 left to write */ _lwrite(fh, q, p - q); // // flush the file, by closing a copy of the file // FlushFileBuffers(fh); }