EDITCTLS.C

// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// Copyright 1995 - 1998 Microsoft Corporation. All Rights Reserved.
//
// MODULE: EditCtls.c
//
// PURPOSE: Handles the UI Edit controls to get data from user and display
// data recieved from the COMM port.
//
// EXPORTED FUNCTIONS: These functions are for use by other modules.
// InitEditCtls - Initialize the edit controls.
// SizeEditCtls - Size and resize the edit controls.
// SetFocusEditCtls - Set the focus to the correct edit control.
// WriteToDisplayCtl - Write a string to the Display edit control.
// PostWriteToDisplayCtl - Posts a message to the UI thread
// with data to write.
//
// INTERNAL FUNCTIONS: These functions are for this module only.
// SubclassInputEditProc - Subclass proc to catch the 'enter' keystroke.
//
// COMMENTS:
// This sample uses exceptionally simple terminal emulation: none.
// There are two edit controls involved. One for the user to type the
// outgoing strings (called the InputCtl) and another to display both
// transmitted and received strings (called the DisplayCtl). There is
// absolutely *no* translation of incoming strings, and the only
// modifications to outgoing strings is to append "/r/n" to it.
//
// This sample only emulates line mode transmission. The string is only
// read from the InputCtl when the 'Enter' key is typed.
//

#include <windows.h>
#include <string.h>
#include "globals.h"
#include "EditCtls.h"
#include "CommCode.h"
#include "toolbar.h"
#include "statbar.h"

// Maximum size of Edit control buffers

#define MAXDISPLAYSIZE 0xF000 // Almost 64K
#define MAXINPUTSIZE 0x400 // 1K

#define PWM_WRITESTRING WM_USER + 741 // Arbitrary number

HWND hWndInputCtl; // Edit control to get input from user
HWND hWndDisplayCtl; // Edit control to display all output
HWND hWndParent; // Parent window of edit controls

WNDPROC lpfnInputEdit; // Storage for subclassed edit control function


// Prototype for the subclassed procedure.
LRESULT CALLBACK SubclassInputEditProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);


//
// FUNCTION: InitEditCtls(HWND)
//
// PURPOSE: Initialize the UI edit controls.
//
// PARAMETERS:
// hWndMain - The window that is to contain the edit controls.
//
// RETURN VALUE:
// TRUE if the edit controls are successfully initialized.
//
// COMMENTS:
// This function should only need to be called once, when
// creating the main window. The only reason it could fail
// is if the edit controls couldn't be created.
//

BOOL InitEditCtls(HWND hWndMain)
{
hWndParent = hWndMain;

if (!hWndMain)
{
OutputDebugString("Invalid parent hWnd for EditCtls!\n");
return FALSE;
}

hWndDisplayCtl = CreateWindowEx(
WS_EX_CLIENTEDGE,
"EDIT","",
WS_CHILD | WS_HSCROLL | WS_VSCROLL | ES_MULTILINE
| ES_OEMCONVERT | WS_VISIBLE | WS_BORDER,
0, 0, 0, 0, hWndParent,
NULL, hInst, NULL);

hWndInputCtl = CreateWindowEx(
WS_EX_CLIENTEDGE,
"EDIT","",
WS_CHILD | ES_AUTOHSCROLL | WS_VISIBLE | WS_BORDER,
0, 0, 0, 0, hWndParent,
NULL, hInst, NULL);

if (!hWndDisplayCtl || !hWndInputCtl)
{
OutputDebugString("Unable to create EditCtls.\n");
return FALSE;
}

SendMessage(hWndInputCtl, EM_LIMITTEXT, MAXINPUTSIZE, 0);
SendMessage(hWndDisplayCtl, EM_LIMITTEXT, MAXDISPLAYSIZE, 0);

// This is where we subclass the input edit control
// so we can catch the 'enter' keystroke
lpfnInputEdit = (WNDPROC)
SetWindowLong(hWndInputCtl, GWL_WNDPROC, (long) SubclassInputEditProc);

return TRUE;
}


//
// FUNCTION: SizeEditCtls
//
// PURPOSE: Sizes, resizes and positions the edit controls.
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
// Anytime the parent window is resized, it needs to signal
// the edit controls to also resize. The size of the edit
// controls assumes that the edit controls are going to
// contain the full client area of the parent, allowing for
// the toolbar and statusbar.
//

void SizeEditCtls()
{
RECT rectParent, rectTemp;
LONG HeightToolbar, HeightStatusbar;
LONG HeightDisplayCtl, HeightInputCtl;
LONG WidthCtls;
LONG yposTopInputCtl, yposTopDisplayCtl;
HDC hdc;
TEXTMETRIC tm;

// Get the client area of the parent.
if (!GetClientRect(hWndParent, &rectParent))
{
OutputDebugString("GetClientRect on EditCtls parent failed\n");
return;
}

// leave a pixel on each side
WidthCtls = rectParent.right - 2;

// Allow for the toolbar at the top.
GetWindowRect(hWndToolbar, &rectTemp);
HeightToolbar = rectTemp.bottom - rectTemp.top;

// Allow for the statusbar at the bottom.
GetWindowRect(hWndStatusbar, &rectTemp);
HeightStatusbar = rectTemp.bottom - rectTemp.top;

// How high should the input control be?
hdc = GetDC(hWndInputCtl);
GetTextMetrics(hdc, &tm);
ReleaseDC(hWndInputCtl, hdc);
HeightInputCtl = tm.tmHeight + tm.tmExternalLeading + 6;
// 6 == 3 extra pixels between text and edit control vertical borders.

// Position Input control.
yposTopInputCtl = rectParent.bottom - HeightStatusbar - HeightInputCtl - 1;

// Position Display control.
yposTopDisplayCtl = rectParent.top + HeightToolbar + 2;
HeightDisplayCtl = yposTopInputCtl - yposTopDisplayCtl - 1;

// Move them both.
MoveWindow(
hWndInputCtl,
1, yposTopInputCtl,
WidthCtls, HeightInputCtl,
TRUE);

MoveWindow(
hWndDisplayCtl,
1, yposTopDisplayCtl,
WidthCtls, HeightDisplayCtl,
TRUE);
}


//
// FUNCTION: SetFocusEditCtls
//
// PURPOSE: Sets the focus to the correct edit control.
//
// PARAMETERS:
// none
//
// RETURN VALUE:
// none
//
// COMMENTS:
// Everytime the parent window gets focus, it needs to signal
// the edit controls to set focus also. This very simple
// algorythm always sets focus to the Input control.
//

void SetFocusEditCtls()
{
DefWindowProc(hWndParent, WM_SETFOCUS, (WPARAM) NULL, (LPARAM) NULL);

if (GetFocus() == hWndParent)
SetFocus(hWndInputCtl);

return;
}


//
// FUNCTION: SubclassInputEditProc
//
// PURPOSE: Subclass proceedure of Input control.
//
// PARAMETERS:
// Standard WNDPROC parameters.
//
// RETURN VALUE:
// Standard WNDPROC return value.
//
// COMMENTS:
// We subclass the Input edit control so that we can catch the 'Enter'
// keystroke (VK_RETURN). This is how we know when the user wants to
// transmit something to the COMM port. This indeed means that only
// line based terminal emulation is supported by this sample. However,
// it makes editing the outgoing text much easier.
//
// WriteCommString is the call that writes a string to the COMM port.
// However, the string that is written *must* be allocated with
// LocalAlloc, and if WriteCommString succeeds, then it will also
// LocalFree the string. If WriteCommString fails, then it refused
// the string and its our job to free it.
//

LRESULT CALLBACK SubclassInputEditProc(
HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_CHAR:

// Found a RETURN keystroke!
if ((TCHAR) wParam == VK_RETURN)
{

LPSTR pszString;
long lSizeofString;

// Get the size of the string to send.
lSizeofString =
SendMessage(hWndInputCtl, WM_GETTEXTLENGTH, 0, 0);

// Allocate enough space for the string and "\r\0"
pszString = (LPSTR) LocalAlloc(LPTR, lSizeofString +2);

// Get the string
SendMessage(hWndInputCtl, WM_GETTEXT,
lSizeofString + 1, (LPARAM) pszString);

// Terminate it.
strcat(pszString, "\r");
lSizeofString += 1;

// If the write to Comm failed, its probably because
// a connection isn't made yet.
if (WriteCommString(pszString, lSizeofString))
{
// This is what we'd do to local echo user input
// Unnecessary usually.
//WriteToOutputEdit(pszString, lSizeofString);
;
}
else
{
WriteToDisplayCtl("Not yet connected\r", 20);

// The comm code refused the string, so we must free it.
LocalFree(pszString);
}

// Clear the Input control.
SendMessage(hWndInputCtl, WM_SETTEXT, 0, (LPARAM) "");

// Don't let default processing handle the RETURN key.
return 0;
}

break;

case PWM_WRITESTRING:
WriteToDisplayCtl((LPSTR) lParam, (DWORD) wParam);
LocalFree((LPSTR) lParam);
break;

default:
break;
}
return CallWindowProc(lpfnInputEdit, hWnd, message, wParam, lParam);
}


//
// FUNCTION: WriteToDisplayCtl(LPSTR, DWORD)
//
// PURPOSE: Writes a string to the Display control.
//
// PARAMETERS:
// lpNewString - The string to display.
// dwSizeofNewString - The length of the string.
//
// RETURN VALUE:
// none.
//
// COMMENTS:
//

void WriteToDisplayCtl(LPSTR lpNewString, DWORD dwSizeofNewString)
{
DWORD dwSizeofEdit;

dwSizeofEdit = SendMessage(hWndDisplayCtl, WM_GETTEXTLENGTH, 0, 0);

if ((dwSizeofEdit + dwSizeofNewString) > MAXDISPLAYSIZE)
{
// Suggestion for future additions:
// Handle edit control overflow by deleting from top of edit control
}

// Actually place the string into the Display control.
SendMessage(hWndDisplayCtl, EM_SETSEL, dwSizeofEdit, dwSizeofEdit);
SendMessage(hWndDisplayCtl, EM_REPLACESEL, 0, (LPARAM) lpNewString);
SendMessage(hWndDisplayCtl, EM_SETSEL,
dwSizeofEdit+dwSizeofNewString, dwSizeofEdit+dwSizeofNewString);
}


//
// FUNCTION: PostWriteToDisplayCtl(LPSTR, DWORD)
//
// PURPOSE: Writes a string to the Display control.
//
// PARAMETERS:
// lpNewString - The string to display.
// dwSizeofNewString - The length of the string.
//
// RETURN VALUE:
// none.
//
// COMMENTS:
//
// Its very important that when data is recieved from the comm port,
// the thread reading the comm port doesn't have to wait for the
// data to be interpreted. Instead, it posts the data to the main UI
// thread to be handled when it is convenient. This is the API to
// wrap up the PostMessage.
//
// Note that the string posted must be LocalAlloc()d, and its the job
// of the window recieving the message to LocalFree it.
//

BOOL PostWriteToDisplayCtl(LPSTR lpNewString, DWORD dwSizeofNewString)
{
return PostMessage(hWndInputCtl, PWM_WRITESTRING,
(WPARAM)dwSizeofNewString, (LPARAM) lpNewString);
}