/*
 * WvCompos.c
 *
 * Common window handling for post/mail windows
 * DefComposeWndProc is not used as a callback - but as a default handler for
 * messages sent to WinVnMailWndProc and WinVnPostWndProc 
 *
 * john s. cooper (jcooper@planetz.com)
 * 9/24/94
 *
 * $Id: wvcompos.c 1.14 1995/06/06 03:39:25 jcooper Exp $
 */
#include <windows.h>
#include <windowsx.h>
#include "wvglob.h"
#include "winvn.h"
#pragma hdrstop
#include <ctype.h>				// for isspace()
#include <assert.h>
#ifdef USE_3D_CONTROLS
#include "ctl3d.h"
#endif

/*
 * Forward declarations
 */
TypAttachment *PrepNewAttachment (WndEdit * compWnd);
void AskForNewAttachment (WndEdit * compWnd);
void DeleteAttachment (WndEdit * compWnd, int num);
void SetWordWrap (WndEdit * compWnd, BOOL enable);
void BrowseMail (WndEdit * editWnd, int button);
BOOL PrepareComposition (WndEdit * compWnd);
void ChangeShowHeaderControls (WndEdit * compWnd);
HWND GetNextHeaderWnd (WndEdit * compWnd, HWND hCur, BOOL next);
void ShowComposeControls (WndEdit * compWnd);
void PrepareControlMenu (WndEdit * compWnd, HWND hControl, BOOL isAttach);
int DoEditCommands (HWND hWndEdit, WPARAM wParam, LPARAM lParam);
void DoEditClose (HWND hWnd, int dirty);
BOOL IsAdditionalHeader (char *line);

long FAR PASCAL
WinVnComposeWndProc (HWND hWnd, unsigned int message, WPARAM wParam, LPARAM lParam)
{
  HMENU hMenu, hSubMenu;
  HWND hControl;
  HBRUSH hBrush;
  WndEdit *compWnd;
  RECT cRect;
  int mstatus;
  BOOL do3d, enable, down;
  register int i;

  compWnd = GetComposeWnd (hWnd);

  switch (message) {
  case WM_SYSCOMMAND:
	return (DefWindowProc (hWnd, message, wParam, lParam));

  case WM_CREATE:
	compWnd = (WndEdit *)((LPCREATESTRUCT)lParam)->lpCreateParams;
	compWnd->hWnd = hWnd;
	compWnd->headerControls = (TypHeaderControls *) NULL;
	compWnd->numAttachments = 0;
	compWnd->busy = FALSE;
	  
//      SetHandleBkBrush (hWnd, hArticleBackgroundBrush);        
	SetHandleBkBrush (hWnd, (HBRUSH) GetStockObject (LTGRAY_BRUSH));
	if (CreateComposeControls (compWnd) == FAIL)
	  break;

	hMenu = GetMenu (hWnd);
	ModifyMenu (hMenu, 0, MF_BYPOSITION | MF_STRING | MF_POPUP, (UINT) GetSubMenu (hMenu, 0),
	(LPCSTR) (compWnd->composeType == DOCTYPE_POSTING) ? "&Post" : "&Mail");
	if (compWnd->composeType == DOCTYPE_POSTING) {
	  NumPostWnds++;
	}
	else {
	  NumMailWnds++;
	}
	compWnd->dirty = DT_CLEAN;
	if (PrepareComposition (compWnd) != SUCCESS) {
	  DestroyWindow (hWnd);
	}
	else {
	  ShowWindow (hWnd, SW_SHOWNORMAL);
	}
	EnableSendComposition (compWnd, TRUE);
	break;

#ifdef _WIN32
  case WM_CTLCOLOREDIT:
  case WM_CTLCOLORLISTBOX:
  case WM_CTLCOLORSTATIC:
#else
  case WM_CTLCOLOR:			//WIN32 doesn't handle this message anymore
	// Set foreground and background colors of edit control

	if (HIWORD (lParam) == CTLCOLOR_EDIT ||
		HIWORD (lParam) == CTLCOLOR_LISTBOX ||
		HIWORD (lParam) == CTLCOLOR_STATIC)
#endif
	{
#ifdef USE_3D_CONTROLS
	  // if using 3d controls, handle the static controls the old way
	  // also, don't touch the main edit window
#ifdef _WIN32
	  do3d = (message != WM_CTLCOLORSTATIC &&
			  (HWND) lParam != compWnd->hWndEdit);
#else
	  do3d = (HIWORD (lParam) != CTLCOLOR_STATIC &&
			  (HWND) LOWORD (lParam) != compWnd->hWndEdit);
#endif
	  if (do3d) {
		hBrush = Ctl3dCtlColorEx (message, wParam, lParam);
		if (hBrush != (HBRUSH) NULL)
		  return (LRESULT) hBrush;
		else
		  return DefWindowProc (hWnd, message, wParam, lParam);
	  }
	  else
#endif
	  {
		SetTextColor ((HDC) wParam, BLACK_COLOR);
		SetBkColor ((HDC) wParam, GRAY_COLOR);
//          return (LRESULT)hArticleBackgroundBrush;
		// Return handle to background brush for the edit control
		return (LRESULT) GetStockObject (LTGRAY_BRUSH);
	  }
	}
	break;

  case WM_SIZE:   
	ResizeComposeControls (compWnd, LOWORD (lParam), HIWORD (lParam));
	break;

  case WM_ACTIVATE:
	/* Switch back to the last control which had the focus */
	if (wParam == WA_ACTIVE || wParam == WA_CLICKACTIVE) {
	  SetFocus (compWnd->hLastFocusWnd);
	}
	break;

  case WM_KEYDOWN:
	switch (wParam) {
	case VK_F6:
	  NextWindow (compWnd->hWnd, compWnd->composeType);
	  break;

	case VK_TAB:
	case VK_RETURN:
	case VK_UP:
	case VK_DOWN:
	  if (GetFocus () != compWnd->hWndEdit) {
		if (wParam == VK_TAB) {
		  down = (GetKeyState (VK_SHIFT) >= 0);
		} else 
		if (wParam == VK_UP) {
		  down = FALSE;
		}
		else {
		  down = TRUE;			// down arrow or enter
		}
		SetFocus (GetNextHeaderWnd (compWnd, GetFocus (), down));
		return 0L;
	  }
	  break;

	case VK_DELETE:
	  if (GetFocus () == compWnd->headerControls->UI[HDR_ATTACH]) {
		i = 1 + (int) SendMessage (compWnd->headerControls->UI[HDR_ATTACH],
								   LB_GETCURSEL, 0, 0L);
		if (i > 0 && i < compWnd->numAttachments) {
		  DeleteAttachment (compWnd, i);
		}
		return 0L;
	  }
	  break;
	case VK_INSERT:
	  if (GetFocus () == compWnd->headerControls->UI[HDR_ATTACH]) {
		AskForNewAttachment (compWnd);
		return 0L;
	  }
	  break;
	}
	return (DefWindowProc (hWnd, message, wParam, lParam));	/* handle it please */
	break;

  case WM_CHAR:
  case WM_KEYUP:
	switch (wParam) {
	case VK_RETURN:
	case VK_TAB:
	case VK_F6:
	case VK_UP:
	case VK_DOWN:
	  if (GetFocus () != compWnd->hWndEdit) {
		return 0L;
	  }
	  break;

	case VK_INSERT:
	case VK_DELETE:
	  if (GetFocus () == compWnd->headerControls->UI[HDR_ATTACH]) {
	  	return 0L;
	  }
      break;

	default:
		return (DefWindowProc (hWnd, message, wParam, lParam));	/* handle it please */
	}
	break;
	    
  
  case WM_INITMENU:
	// set menu for whichever control has the focus 
	hControl = GetFocus ();
	PrepareControlMenu (compWnd, hControl,
					 (hControl == compWnd->headerControls->UI[HDR_ATTACH]));

	/* disable send if comm is busy */
	hMenu = GetMenu (hWnd);
	if (compWnd->composeType == DOCTYPE_POSTING) {
	  enable = !CommBusy;
	}
	else {
	  enable = !SendingMail;
	}
	EnableMenuItem (GetSubMenu (hMenu, 0), IDM_SEND,
					enable ? ENABLE_MENU : DISABLE_MENU);
	break;

  case WM_COMMAND:
	/* Child control notifications -- edit wnd, buttons, attach listbox */
	if ((HWND) (UINT) lParam == compWnd->hWndEdit) {
	  if (GET_WM_COMMAND_CMD (wParam, lParam) == EN_CHANGE) {
		compWnd->dirty = DT_DIRTY;
	  }
	  if (GET_WM_COMMAND_CMD (wParam, lParam) == EN_ERRSPACE) {
		MessageBox (hWnd, "Not enough space in edit buffer for this operation.\nTry making an attachment instead.", "Low Memory In Edit Buffer", MB_OK | MB_ICONSTOP);
	  }
	  return (0L);
	}
	else if (compWnd->headerControls->button[BTN_TO] &&
		  (HWND) (UINT) lParam == compWnd->headerControls->button[BTN_TO]) {
	  if (GET_WM_COMMAND_CMD (wParam, lParam) == BN_CLICKED) {
		BrowseMail (compWnd, HDR_TO);
	  }
	  return (0L);
	}
	else if (compWnd->headerControls->button[BTN_CC] &&
		  (HWND) (UINT) lParam == compWnd->headerControls->button[BTN_CC]) {
	  if (GET_WM_COMMAND_CMD (wParam, lParam) == BN_CLICKED) {
		BrowseMail (compWnd, HDR_CC);
	  }
	  return (0L);
	}
	else if ((HWND) (UINT) lParam == compWnd->headerControls->UI[HDR_ATTACH]) {
	  hControl = compWnd->headerControls->UI[HDR_ATTACH];
	  if (GET_WM_COMMAND_CMD (wParam, lParam) == LBN_DBLCLK) {
		// don't forget attachment number 0 is the main art body
		i = 1 + (int) SendMessage (hControl, LB_GETCURSEL, 0, 0L);
		if (i > 0 && i < compWnd->numAttachments) {
		  if (DialogBoxParam (hInst, "WinVnAttach", compWnd->hWnd,
				   lpfnWinVnAttachDlg, (LPARAM) (compWnd->attachment[i]))) {
			SendMessage (compWnd->headerControls->UI[HDR_ATTACH],
						 LB_DELETESTRING, (WPARAM) i - 1, 0L);
			SendMessage (compWnd->headerControls->UI[HDR_ATTACH],
						 LB_INSERTSTRING, (WPARAM) i - 1,
						 (LPARAM) (LPCSTR) compWnd->attachment[i]->fileName);
		  }
		}
	  }
	}

	switch (LOWORD (wParam)) {

	case IDM_CANCEL:
	case IDV_EXIT:
	  DoEditClose (compWnd->hWnd, compWnd->dirty);
	  break;

	case IDM_PRINT:
	  PrintFile (compWnd->hWndEdit);
	  break;

	case IDM_ATTACH:
	  AskForNewAttachment (compWnd);
	  break;

	case IDM_READ_FILE:
	  {
		char fileName[MAXFILENAME];
		if (AskForExistingFileName (hWnd, fileName, "Select Text File To Read") == SUCCESS) {
		  ReadTextFileIntoEditWnd (compWnd->hWndEdit, fileName);
		  compWnd->dirty = DT_DIRTY;
		}
	  }
	  break;

	case IDM_COMPOSE_PREFS:
	  DialogBox (hInst, "WinVnComposePrefs", hWnd, lpfnWinVnComposePrefsDlg);
	  break;

	case IDM_ATTACH_PREFS:
	  DialogBox (hInst, "WinVnAttachPrefs", hWnd, lpfnWinVnAttachPrefsDlg);
	  break;

	case IDM_WORDWRAP:
	  hMenu = GetMenu (hWnd);
	  hSubMenu = GetSubMenu (hMenu, 1);
	  WordWrap = !WordWrap;
	  SetWordWrap (compWnd, WordWrap);
	  break;

	case IDM_SEND:
	  if (((compWnd->composeType == DOCTYPE_POSTING) ||
	       (compWnd->composeType == DOCTYPE_CANCEL)) &&
		  TestCommBusy (compWnd->hWnd, "Can't Send Composition")) {
		break;
	  }
 
     GetClientRect(compWnd->hWnd,&cRect);
	 if ((RectWidth (cRect) - SideSpace - 32) > (78 * CompositionCharWidth)) { 
	   mstatus =MessageBox (hWnd, "Your Screen Width is wider than the Internet \n"
                           "standard of 78 fixed point characters. This \n"
	                       "will make your post look bad on some viewers.  \n"
	                       "Select Cancel and Resize window smaller or select \n"
	                       "OK if you want to send anyway. \n",
				           "Screen Size exceeds Recommend Size",
				   MB_OKCANCEL | MB_ICONEXCLAMATION);

       if (mstatus == IDCANCEL) return 0L;
	  }

	  // destroys window if successful
	  SendComposition (compWnd);
	  break;

	default:
	  DoEditCommands (GetFocus (), wParam, lParam);
	  break;
	}
	break;

  case WM_DESTROY:
	compWnd->hWnd = (HWND) NULL;
	compWnd->hWndEdit = (HWND) NULL;

	for (i = 0; i < compWnd->numAttachments; i++) {
	  if (compWnd->attachment[i]) {
		GlobalFreePtr (compWnd->attachment[i]);
		compWnd->attachment[i] = (TypAttachment *) NULL;
	  }
	}
	compWnd->numAttachments = 0;

	if (compWnd->headerControls) {
	  GlobalFreePtr (compWnd->headerControls);
	  compWnd->headerControls = (TypHeaderControls *) NULL;
	}

	if (compWnd->extraHeaders) {
		FreeTextBlock(compWnd->extraHeaders);
		compWnd->extraHeaders = (TypTextBlock *)NULL;
	}
	if (compWnd->composeType == DOCTYPE_POSTING)
	  NumPostWnds--;
	else
	  NumMailWnds--;
	break;

  case WM_CLOSE:
	if (compWnd->busy)
	  MessageBox (hWnd,
				  "Please wait until send is complete",
				  "Cannot close compose window", MB_OK | MB_ICONSTOP);
	else
	  DoEditClose (compWnd->hWnd, compWnd->dirty);
	break;

  default:
	return (DefWindowProc (hWnd, message, wParam, lParam));
  }
  return (0L);
}


void 
BrowseMail (WndEdit * editWnd, int button)
{
  HWND control;
  char temp[MAXHEADERLINE];

  if (button == BTN_TO) {
	control = editWnd->headerControls->UI[HDR_TO];
  }
  else {
	control = editWnd->headerControls->UI[HDR_CC];
  }
  SendMessage (control, WM_GETTEXT, (WPARAM) MAXHEADERLINE, (LPARAM) (LPCSTR) temp);

  if (DialogBoxParam (hInst, "WinVnMail", editWnd->hWnd, lpfnWinVnMailDlg, (LPARAM) temp)) {
	SendMessage (control, WM_SETTEXT, 0, (LPARAM) (LPCSTR) DialogString);
  }
}

/* ------------------------------------------------------------------------
 * To maximize amount of data allowable in posting, allocate a new
 * data segment for this posting from the Global heap, and set the
 * window hInstance to this segment
 * Don't forget you can't use EM_GET/SETHANDLE on an edit buf with
 * a global mem segment
 */
BOOL
CreateEditWnd (WndEdit * compWnd)
{
  RECT cRect;
  DWORD wrapOptions = 0;
  HINSTANCE editInst;

#ifndef _WIN32
  if ((editInst = GlobalAlloc (GMEM_DDESHARE | GMEM_MOVEABLE | GMEM_ZEROINIT, 1024L)) == NULL) {
	MessageBox (compWnd->hWnd, "Memory allocation failure", "Edit Buffer", MB_OK);
	editInst = hInst;			/* use local heap instead */
  }
#else
  editInst = hInst;
#endif

  if (!WordWrap)
	wrapOptions = WS_HSCROLL | ES_AUTOHSCROLL;

  GetClientRect (compWnd->hWnd, &cRect);
  compWnd->hWndEdit = CreateWindow ((LPCSTR) "edit", (LPCSTR) NULL,
					  WS_TABSTOP | WS_CHILD | WS_VSCROLL | WS_BORDER |
					  ES_LEFT | ES_MULTILINE | ES_WANTRETURN | 
					  ES_AUTOVSCROLL | wrapOptions,
					  0, compWnd->headerControls->yEdge, RectWidth (cRect), 
					  cRect.bottom - (compWnd->headerControls->yEdge + 1),
				 compWnd->hWnd, (HMENU) EDITID, (HINSTANCE) editInst, NULL);

  if (!compWnd->hWndEdit) {
	MessageBox (compWnd->hWnd, "Window creation failure", "Edit Buffer", MB_OK);
	return (FAIL);
  }
  SendMessage (compWnd->hWndEdit, EM_LIMITTEXT, 0, 0L);
//  SetHandleBkBrush (compWnd->hWndEdit, hArticleBackgroundBrush);
  SetHandleBkBrush (compWnd->hWndEdit, (HBRUSH) GetStockObject (LTGRAY_BRUSH));
  SendMessage (compWnd->hWndEdit, WM_SETFONT, (WPARAM) hCompositionFont, FALSE);
  compWnd->dirty = DT_CLEAN;

  /* Subclass the edit window to pass acceleratprs to parent */
  SetWindowLong (compWnd->hWndEdit, GWL_WNDPROC, (LONG) lpfnWinVnControlSubclass);

  return (SUCCESS);
}

/*----------------------------------------------------------------------------
 * Create the all controls necessary for header entry
 * jsc 9/22/94
 */

#define XBASE			13
#define YBASE 			11
#define XSPACING		5
#define YSPACING		2

#define IDC_HEADER_UI 	1100

void 
ResizeComposeControls (WndEdit * compWnd, int cx, int cy)
{
  register int n;
  int x, ySize, hdrLine, b, titleWidth, editWidth, buttonWidth;
  RECT rect;
  SIZE size;
  HDC hDC;
  HFONT hOldFont;

  if (!cx && !cy) {
	GetClientRect (compWnd->hWnd, &rect);
	cx = RectWidth (rect);
	cy = RectHeight (rect);
  }
  ChangeShowHeaderControls (compWnd);

  /* floatingpt ySize yuck.  only way to avoid loss of accuracy in small font sizes */
  ySize = (int) (1.5 * (float) WinVnLineHeight);

  /* get estimated width of the longest title in the current font 
   * Use subject static control as a temporary DC in which to do work
   */
  hDC = GetDC (compWnd->headerControls->title[HDR_SUBJECT]);
  hOldFont = SelectObject (hDC, hWinVnFont);
  if (compWnd->headerControls->title[HDR_ORG]) {
	GetTextExtentPoint (hDC, " Organization: ", 15, &size);
  }
  else {
	GetTextExtentPoint (hDC, " Attachments: ", 14, &size);
  }
  titleWidth = size.cx;

  GetTextExtentPoint (hDC, " Browse ", 8, &size);
  buttonWidth = size.cx;

  SelectObject (hDC, hOldFont);
  ReleaseDC (compWnd->headerControls->title[HDR_SUBJECT], hDC);

//  titleWidth = 15 * WinVnCharWidth;   /* space for word " Organization: " */
  //  buttonWidth = 9 * WinVnCharWidth;   /* space for word " Browse " */

  x = XBASE + titleWidth + XSPACING;
  editWidth = max (buttonWidth + 50, cx - x - 15);

  for (n = 0, hdrLine = 0; n < HDR_NUM_CONTROLS; n++) {
  //   if ((n != HDR_GROUPS) || (compWnd->composeType != DOCTYPE_CANCEL)){
	  if (compWnd->headerControls->UI[n]) {
	    MoveWindow (compWnd->headerControls->title[n],
			  	    XBASE, YBASE + 3 + hdrLine * (ySize + YSPACING),
			  	    titleWidth, ySize, TRUE);

	    MoveWindow (compWnd->headerControls->UI[n],
				    x, YBASE + hdrLine * (ySize + YSPACING),
				    (n == HDR_TO || n == HDR_CC) ? editWidth - XSPACING - buttonWidth : editWidth,
				    (n == HDR_ATTACH) ? (2 * ySize) : ySize, TRUE);
	    SendMessage (compWnd->headerControls->title[n], WM_SETFONT, (WPARAM) hWinVnFont, 0);
	    SendMessage (compWnd->headerControls->UI[n], WM_SETFONT, (WPARAM) hWinVnFont, 0);

	    if (n == HDR_TO || n == HDR_CC) {
		  b = (n == HDR_TO) ? BTN_TO : BTN_CC;
		  MoveWindow (compWnd->headerControls->button[b],
					  x + editWidth - buttonWidth,
					  YBASE + hdrLine * (ySize + YSPACING),
					  buttonWidth, ySize, TRUE);
		  SendMessage (compWnd->headerControls->button[b], WM_SETFONT, (WPARAM) hWinVnFont, TRUE);
	    }

	  hdrLine++;
	 }
  //  }
  }
  compWnd->headerControls->yEdge = (hdrLine - 1) * (ySize + YSPACING) + 3 * ySize + YSPACING;
  SendMessage (compWnd->hWndEdit, WM_SETFONT, (WPARAM) hCompositionFont, TRUE);
  MoveWindow (compWnd->hWndEdit, 0, compWnd->headerControls->yEdge,
			  cx, cy - compWnd->headerControls->yEdge, TRUE);
  InvalidateRect (compWnd->hWnd, NULL, TRUE);
}

/* 
 * CreateWindow helpers
 * return true on success 
 */
BOOL 
CreateHeaderTitle (WndEdit * compWnd, int n, char *title)
{
  compWnd->headerControls->title[n] =
	CreateWindow ("STATIC", title,
				  WS_CHILD | SS_RIGHT,
				  0, 0, 0, 0,
				  compWnd->hWnd, (HMENU) NULL, hInst, NULL);

  return (compWnd->headerControls->title[n] != (HWND) NULL);
}

BOOL 
CreateHeaderEdit (WndEdit * compWnd, int n)
{

  compWnd->headerControls->UI[n] =
	CreateWindow ("EDIT", (LPCSTR) NULL,
			   WS_TABSTOP | WS_CHILD | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL,
				  0, 0, 0, 0,
				  compWnd->hWnd, (HMENU) (IDC_HEADER_UI + n), hInst, NULL);

  if (compWnd->headerControls->UI[n]) {
	SendMessage (compWnd->headerControls->UI[n], EM_LIMITTEXT, (WPARAM)MAXHEADERLINE, 0);
	/* Subclass the edit window to pass acceleratprs to parent */
	SetWindowLong (compWnd->headerControls->UI[n], GWL_WNDPROC, (LONG) lpfnWinVnControlSubclass);
#ifdef USE_3D_CONTROLS
	Ctl3dSubclassCtl (compWnd->headerControls->UI[n]);
#endif
  }
  return (compWnd->headerControls->UI[n] != (HWND) NULL);
}

BOOL 
CreateHeaderListBox (WndEdit * compWnd, int n)
{
  compWnd->headerControls->UI[n] =
	CreateWindow ("LISTBOX", (LPCSTR) NULL,
				  WS_TABSTOP | WS_CHILD | WS_BORDER | WS_VSCROLL | LBS_NOTIFY | LBS_NOINTEGRALHEIGHT,
				  0, 0, 0, 0,
				  compWnd->hWnd, (HMENU) (IDC_HEADER_UI + n), hInst, NULL);

  if (compWnd->headerControls->UI[n]) {
	SetWindowLong (compWnd->headerControls->UI[n], GWL_WNDPROC, (LONG) lpfnWinVnControlSubclass);
#ifdef USE_3D_CONTROLS
	Ctl3dSubclassCtl (compWnd->headerControls->UI[n]);
#endif
  }
  return (compWnd->headerControls->UI[n] != (HWND) NULL);
}

BOOL 
CreateButton (WndEdit * compWnd, int n)
{
  compWnd->headerControls->button[n] =
	CreateWindow ("BUTTON", (LPCSTR) "Browse",
				  WS_CHILD | BS_PUSHBUTTON, // | WS_TABSTOP 
				  0, 0, 0, 0,
				  compWnd->hWnd, (HMENU) ID_BROWSE, hInst, NULL);

  return (compWnd->headerControls->button[n] != (HWND) NULL);
}

BOOL 
CreateHeaderControls (WndEdit * compWnd, int n, char *title)
{
  return (CreateHeaderTitle (compWnd, n, title) &&
		  CreateHeaderEdit (compWnd, n));
}

void 
DestroyHeaderControls (WndEdit * compWnd, int n)
{
  if (compWnd->headerControls->UI[n]) {
	DestroyWindow (compWnd->headerControls->UI[n]);
	compWnd->headerControls->UI[n] = (HWND) NULL;
  }
  if (compWnd->headerControls->title[n]) {
	DestroyWindow (compWnd->headerControls->title[n]);
	compWnd->headerControls->title[n] = (HWND) NULL;
  }
}

/*
 * Main entry point for creating the controls 
 * returns FAIL or SUCCESS 
 */
BOOL 
CreateComposeControls (WndEdit * compWnd)
{
  RECT cRect;

  if ((compWnd->headerControls = (TypHeaderControls *)
	   GlobalAllocPtr (GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof (TypHeaderControls))) == NULL) {
	return FAIL;
  }

  if ((compWnd->composeType == DOCTYPE_POSTING) ||
      (compWnd->composeType == DOCTYPE_CANCEL)) {
	if (!CreateHeaderControls (compWnd, HDR_GROUPS, "Newsgroups:"))
	  return FAIL;
  }
  else if ((!CreateHeaderControls (compWnd, HDR_TO, "To:")) ||
		   (!CreateButton (compWnd, BTN_TO)))
	  return FAIL;

  if (!CreateHeaderControls (compWnd, HDR_SUBJECT, "Subject:"))
	return FAIL;
	
  if ((compWnd->composeType == DOCTYPE_POSTING) &&
      !CreateHeaderControls (compWnd, HDR_FOLLOWTO, "Followup-to:"))
	return FAIL;

  /* cc-by-mail when posting is only available if SMTP */
  if (compWnd->composeType != DOCTYPE_CANCEL)
    {
    if (MailCtrl.enableMail == MF_ENABLED && MailCtrl.MailType == MT_SMTP &&
	  !CreateHeaderControls (compWnd, HDR_CC, (compWnd->composeType == DOCTYPE_POSTING) ? "Cc-By-Mail:" : "Cc:"))
	return FAIL;

    if (!CreateButton (compWnd, BTN_CC))
	return FAIL;
	}

  if ((compWnd->composeType != DOCTYPE_CANCEL) &&
      (!CreateHeaderTitle (compWnd, HDR_ATTACH, "Attachments:") ||
	   !CreateHeaderListBox (compWnd, HDR_ATTACH)))
	return FAIL;

  /* all optional headers will be created by ChangeShowHeaderControls */
  if (CreateEditWnd (compWnd) == FAIL)
	return FAIL;
  
  if (compWnd->composeType != DOCTYPE_CANCEL) {
    GetClientRect (compWnd->hWnd, &cRect);
    ResizeComposeControls (compWnd, RectWidth (cRect), RectHeight (cRect));
    ShowComposeControls (compWnd);
    }
    
  if (compWnd->Doc)
	compWnd->hLastFocusWnd = compWnd->hWndEdit;
  else if (compWnd->composeType == DOCTYPE_CANCEL)
	compWnd->hLastFocusWnd = compWnd->headerControls->UI[HDR_SUBJECT];
  else if (compWnd->composeType == DOCTYPE_POSTING)
	compWnd->hLastFocusWnd = compWnd->headerControls->UI[HDR_GROUPS];
  else
	compWnd->hLastFocusWnd = compWnd->headerControls->UI[HDR_TO];
  return SUCCESS;
}

void 
ShowComposeControls (WndEdit * compWnd)
{
  register int i;
  for (i = 0; i < HDR_NUM_CONTROLS; i++) {
	  if (compWnd->headerControls->title[i])
	  	ShowWindow (compWnd->headerControls->title[i], SW_SHOWNORMAL);
	  if (compWnd->headerControls->UI[i])
	 	ShowWindow (compWnd->headerControls->UI[i], SW_SHOWNORMAL);
  }
  for (i = 0; i < HDR_NUM_BUTTONS; i++) {
	if (compWnd->headerControls->button[i])
	  ShowWindow (compWnd->headerControls->button[i], SW_SHOWNORMAL);
  }
  if (compWnd->hWndEdit)
	ShowWindow (compWnd->hWndEdit, SW_SHOWNORMAL);
}

void ChangeShowHeaderControls (WndEdit * compWnd)
{
  char temp[MAXHEADERLINE];

  if (ShowFromHdr && !(compWnd->headerControls->title[HDR_FROM])) {
    if (!CreateHeaderControls (compWnd, HDR_FROM, "From:"))
      return;
    if (GetFromAddress (temp, MAXHEADERLINE, compWnd->Doc) == TRUE) {
      SendMessage (compWnd->headerControls->UI[HDR_FROM], WM_SETTEXT,
                   0, (LPARAM) (LPCSTR) temp);
    }
  }
  if (!ShowFromHdr && compWnd->headerControls->title[HDR_FROM]) {
    DestroyHeaderControls (compWnd, HDR_FROM);
  }

  if (ShowOrgHdr && !(compWnd->headerControls->title[HDR_ORG])) {
    if (!CreateHeaderControls (compWnd, HDR_ORG, "Organization:"))
      return;
    SendMessage (compWnd->headerControls->UI[HDR_ORG], WM_SETTEXT,
                 0, (LPARAM) (LPCSTR) Organization);
  }
  if (!ShowOrgHdr && compWnd->headerControls->title[HDR_ORG]) {
    DestroyHeaderControls (compWnd, HDR_ORG);
  }

  if (ShowReplyToHdr && !(compWnd->headerControls->title[HDR_REPLYTO])) {
    if (!CreateHeaderControls (compWnd, HDR_REPLYTO, "Reply-To:"))
      return;
    SendMessage (compWnd->headerControls->UI[HDR_REPLYTO], WM_SETTEXT,
                 0, (LPARAM) (LPCSTR) ReplyTo);
  }
  if (!ShowReplyToHdr && compWnd->headerControls->title[HDR_REPLYTO]) {
    DestroyHeaderControls (compWnd, HDR_REPLYTO);
  }

  if (ShowKeywordsHdr && !(compWnd->headerControls->title[HDR_KEYWORDS])) {
    if (!CreateHeaderControls (compWnd, HDR_KEYWORDS, "Keywords:"))
      return;
  }
  if (!ShowKeywordsHdr && compWnd->headerControls->title[HDR_KEYWORDS]) {
    DestroyHeaderControls (compWnd, HDR_KEYWORDS);
  }

  if (compWnd->composeType == DOCTYPE_POSTING &&
      ShowSummaryHdr && !(compWnd->headerControls->title[HDR_SUMMARY])) {
    if (!CreateHeaderControls (compWnd, HDR_SUMMARY, "Summary:"))
      return;
  }
  if (compWnd->composeType == DOCTYPE_POSTING &&
      !ShowSummaryHdr && compWnd->headerControls->title[HDR_SUMMARY]) {
    DestroyHeaderControls (compWnd, HDR_SUMMARY);
  }

  if (compWnd->composeType == DOCTYPE_POSTING &&
      ShowDistributionHdr && !(compWnd->headerControls->title[HDR_DIST])) {
    if (!CreateHeaderControls (compWnd, HDR_DIST, "Distribution:"))
      return;
  }
  if (compWnd->composeType == DOCTYPE_POSTING &&
      !ShowDistributionHdr && compWnd->headerControls->title[HDR_DIST]) {
    DestroyHeaderControls (compWnd, HDR_DIST);
  }
  
  if (compWnd->composeType == DOCTYPE_POSTING &&
      ShowFollowupToHdr && !(compWnd->headerControls->title[HDR_FOLLOWTO])) {
    if (!CreateHeaderControls (compWnd, HDR_FOLLOWTO, "Followup-To:"))
      return;
  }
  if (compWnd->composeType == DOCTYPE_POSTING &&
      !ShowFollowupToHdr && compWnd->headerControls->title[HDR_FOLLOWTO]) {
    DestroyHeaderControls (compWnd, HDR_FOLLOWTO);
  }
  ShowComposeControls (compWnd);
  return;
}

HWND 
GetNextHeaderWnd (WndEdit * compWnd, HWND hCur, BOOL next)
{
  register int n;

  for (n = 0; n < HDR_NUM_CONTROLS; n++) {
	if (compWnd->headerControls->UI[n] == hCur)
	  break;
  }

  /* n is now set to current control */
  do {
	if (next) {
	  n++;
	  if (n == HDR_NUM_CONTROLS) {
		return compWnd->hWndEdit;
	  }
	}
	else {
	  n = n - 1;
	  if (n < 0) {
		return compWnd->hWndEdit;
	  }
	}
  } while (!compWnd->headerControls->UI[n]);
  return compWnd->headerControls->UI[n];
}

void
EnableSendComposition (WndEdit * compWnd, BOOL enable)
{
  HMENU hMenu = GetMenu (compWnd->hWnd);
  HMENU hSubMenu = GetSubMenu (hMenu, 0);

  //  Send menu is handled by WM_INITMENU
  if (compWnd->composeType == DOCTYPE_CANCEL)
    { 
    EnableMenuItem (hSubMenu, IDM_READ_FILE, DISABLE_MENU);
  	EnableMenuItem (hSubMenu, IDM_ATTACH, DISABLE_MENU);
  	EnableMenuItem (hSubMenu, IDM_COMPOSE_PREFS,DISABLE_MENU);
  	EnableMenuItem (hSubMenu, IDM_ATTACH_PREFS,DISABLE_MENU);
    }
  else {
  	EnableMenuItem (hSubMenu, IDM_READ_FILE, enable ? ENABLE_MENU : DISABLE_MENU);
  	EnableMenuItem (hSubMenu, IDM_ATTACH, enable ? ENABLE_MENU : DISABLE_MENU);
  	EnableMenuItem (hSubMenu, IDM_CANCEL, enable ? ENABLE_MENU : DISABLE_MENU);

  	hSubMenu = GetSubMenu (hMenu, 1);
  	EnableMenuItem (hSubMenu, IDM_ROT13, enable ? ENABLE_MENU : DISABLE_MENU);
  	EnableMenuItem (hSubMenu, IDM_WORDWRAP, enable ? ENABLE_MENU : DISABLE_MENU);
  }
}

/*------------ WinVnControlIntercept ---------------------------- 
 *
 * The subclassed WindowProc for the compose controls
 *
 * Capture key messages and pass them to parent
 * as may contain accelerator information
 *
 *--------------------------------------------------------------*/

BOOL FAR PASCAL
WinVnControlIntercept (HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
	WndEdit *compWnd;
	HWND hParentWnd = GetParent (hWnd);

	switch (msg) {
	case WM_SETFOCUS:
		/* remember that this control had the focus last */
		compWnd = GetComposeWnd(hParentWnd);
		if (compWnd) {
			compWnd->hLastFocusWnd = hWnd;
		}
		break;

	case WM_KILLFOCUS:
		// scroll edit text back to start when losing focus
		compWnd = GetComposeWnd (hParentWnd);
		if (hWnd != compWnd->headerControls->UI[HDR_ATTACH] && 
			hWnd != compWnd->hWndEdit) {
#ifdef _WIN32
			SendMessage (hWnd, EM_SETSEL, (WPARAM) (INT) 0, (LPARAM) (INT) 0);
			SendMessage (hWnd, EM_SCROLLCARET, (WPARAM)0, (LPARAM)0);
#else
			SendMessage (hWnd, EM_SETSEL, (WPARAM) (UINT) 0, MAKELONG (0, 0));
#endif
		}
		break;

	case WM_CHAR:
		// Process this message to avoid message beeps.
		if (wParam == VK_RETURN || wParam == VK_TAB) {
			compWnd = GetComposeWnd (hParentWnd);
			if (hWnd != compWnd->hWndEdit) {
				return 0;
			}
		}
		break;

	case WM_KEYDOWN:
	case WM_KEYUP: 
		// big kludge - arrows only seem to work if SendMsg 
		// but accelerators only seem to work if PostMsg
		switch (wParam) {
			case VK_DOWN: case VK_UP:
			case VK_LEFT: case VK_RIGHT: 
			case VK_TAB: case VK_RETURN:
				SendMessage(hParentWnd, msg, wParam, lParam);
				break;
				
			default:
				PostMessage(hParentWnd, msg, wParam, lParam);
				break;
		}
		break;

	case WM_GETDLGCODE:
		// we want to handle tabs/arrows/return 
		return DLGC_WANTALLKEYS | 
				(BOOL) CallWindowProc((WNDPROC)GetClassLong(hWnd, GCL_WNDPROC),
						hWnd, msg, (WPARAM) wParam , (LPARAM) lParam);
		break;
	}		
	/* and always let the edit control do its thing  */
	return (BOOL)CallWindowProc((WNDPROC)GetClassLong(hWnd, GCL_WNDPROC),
					hWnd, msg, wParam , lParam);
}

/* --------------- PrepareControlMenu -----------------
 * Prepare &Edit menu based on current composition control
 * 
 */
void
PrepareControlMenu (WndEdit * compWnd, HWND hControl, BOOL isAttach)
{
  DWORD dwResult;
  UINT action;
  HMENU hMenu = GetMenu (compWnd->hWnd);

  dwResult = SendMessage (hControl, EM_GETSEL, 0, 0L);

  /* cannot cut, paste, delete or rot13 unless there is a selection */
  action = (isAttach || HIWORD (dwResult) == LOWORD (dwResult)) ?
	DISABLE_MENU : ENABLE_MENU;

  EnableMenuItem (hMenu, IDM_ROT13, action);
  EnableMenuItem (hMenu, IDM_COPY, action);
  EnableMenuItem (hMenu, IDM_CUT, action);

  /* Delete and Select all are always available unless this is the 
   * attachment listbox
   */
  action = isAttach ? DISABLE_MENU : ENABLE_MENU;
  EnableMenuItem (hMenu, IDM_DEL, action);
  EnableMenuItem (hMenu, IDM_SELECT_ALL, action);

  /* if clipboard contains text, can paste */
  action = (isAttach || !IsClipboardFormatAvailable (CF_TEXT)) ?
	DISABLE_MENU : ENABLE_MENU;
  EnableMenuItem (hMenu, IDM_PASTE, action);

  /* if Undo Buffer is empty, cannot Undo changes */
  action = (isAttach || !SendMessage (hControl, EM_CANUNDO, 0, 0L)) ?
	DISABLE_MENU : ENABLE_MENU;
  EnableMenuItem (hMenu, IDM_UNDO, action);

  action = (isAttach && compWnd->numAttachments > 1) ?
	ENABLE_MENU : DISABLE_MENU;

//  EnableMenuItem(hMenu, IDM_DELETE_ATTACHMENT, action);

  CheckMenuItem (hMenu, IDM_WORDWRAP, MF_BYCOMMAND | (WordWrap ? MF_CHECKED : MF_UNCHECKED));
}

/* ---------------- function DoEditCommands -------------
 *  edit commands for composition window
 */
DoEditCommands (HWND hWndEdit, WPARAM wParam, LPARAM lParam)
{
  switch (LOWORD (wParam)) {
  case IDM_UNDO:
	SendMessage (hWndEdit, WM_UNDO, 0, 0L);
	break;

  case IDM_CUT:
	SendMessage (hWndEdit, WM_CUT, 0, 0L);
	break;

  case IDM_COPY:
	SendMessage (hWndEdit, WM_COPY, 0, 0L);
	break;

  case IDM_PASTE:
	SendMessage (hWndEdit, WM_PASTE, 0, 0L);
	break;

  case IDM_DEL:
	SendMessage (hWndEdit, WM_CLEAR, 0, 0L);
	break;

  case IDM_SELECT_ALL:
#ifdef _WIN32
	SendMessage (hWndEdit, EM_SETSEL, (WPARAM) (INT) 0, (LPARAM) (INT) - 1);
#else
	SendMessage (hWndEdit, EM_SETSEL, (WPARAM) (UINT) 0, MAKELONG (0, -1));
#endif
	break;

  case IDM_ROT13:
	{
	  DWORD dwResult;
	  WORD wStart, wEnd;
	  char *editptr;

	  dwResult = SendMessage (hWndEdit, EM_GETSEL, 0, 0L);
	  wStart = LOWORD (dwResult);
	  wEnd = HIWORD (dwResult);

	  editptr = GetEditText (hWndEdit);
	  strnROT13 (editptr + wStart, wEnd - wStart);
	  SetEditText (hWndEdit, editptr);
	  GlobalFreePtr (editptr);
	  InvalidateRect (hWndEdit, NULL, TRUE);
	}
	break;

  default:
	return (0);					// signnify no action 

  }
  return (1);					// signify actions completed 

}

/*
 *  function DoEditClose
 *  close down the edit window only if clean to close 
 *  or user agrees to lose information 
 *
 */
void 
DoEditClose (HWND hWnd, int dirty)
{
  if (dirty == DT_DIRTY) {
	if (MessageBox (hWnd, "Are you sure you want to exit?", "Unsaved Work",
					MB_ICONQUESTION | MB_OKCANCEL) == IDOK) {
	  DestroyWindow (hWnd);
	}
  }
  else {
	DestroyWindow (hWnd);
  }
}

/* -----------------------------------------------------------------
 * New and Delete attachment
 * manage list of attachments via the attach listbox
 */
TypAttachment *
PrepNewAttachment (WndEdit * compWnd)
{
  TypAttachment *thisAttach;

  if (compWnd->numAttachments + 1 > MAX_NUM_ATTACHMENTS) {
	MessageBox (compWnd->hWnd, "You have reached the maximum number of attachments.",
				"Attachment Aborted", MB_OK | MB_ICONSTOP);
	return NULL;
  }

  thisAttach = (TypAttachment *) GlobalAllocPtr (GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof (TypAttachment));
  if (thisAttach == NULL) {
	MessageBox (compWnd->hWnd, "Memory Allocation Failure",
				"Attachment Aborted", MB_OK | MB_ICONSTOP);
  }
  else {
	thisAttach->attachInNewArt = DefaultAttachInNewArt;
  }
  return thisAttach;
}

void
AskForNewAttachment (WndEdit * compWnd)
{
  TypAttachment *thisAttach;

  if ((thisAttach = PrepNewAttachment (compWnd)) == NULL)
	return;

  if (DialogBoxParam (hInst, "WinVnAttach", compWnd->hWnd, lpfnWinVnAttachDlg, (LPARAM) thisAttach)) {
	compWnd->attachment[compWnd->numAttachments] = thisAttach;
	compWnd->numAttachments++;
	SendMessage (compWnd->headerControls->UI[HDR_ATTACH],
				 LB_ADDSTRING, 0, (LPARAM) (LPCSTR) thisAttach->fileName);
  }
  else {
	GlobalFreePtr (thisAttach);
  }
}

void
DeleteAttachment (WndEdit * compWnd, int num)
{
  register int i;

  assert (num >= 0 && num < compWnd->numAttachments);

  SendMessage (compWnd->headerControls->UI[HDR_ATTACH],
			   LB_DELETESTRING, (WPARAM) (num - 1), 0);

  GlobalFreePtr (compWnd->attachment[num]);
  for (i = num; i < compWnd->numAttachments - 1; i++)
	compWnd->attachment[i] = compWnd->attachment[i + 1];

  compWnd->attachment[i] = (TypAttachment *) NULL;
  (compWnd->numAttachments)--;
}

/*------------------------------------------------------------------------
 * SetWordWrap - actually destroys the window and recreates it.
 * (I've tried messing with classword/etc.  doesn't look like you can change
 * wordwrap on the fly)
 * jsc 10/3/94
 */
void 
SetWordWrap (WndEdit * compWnd, BOOL enable)
{
  char *editBuf;
  editBuf = GetEditText (compWnd->hWndEdit);
  if (editBuf == NULL)
	return;

  DestroyWindow (compWnd->hWndEdit);
  CreateEditWnd (compWnd);
  SetEditText (compWnd->hWndEdit, editBuf);
  GlobalFreePtr (editBuf);
  ShowWindow (compWnd->hWndEdit, SW_NORMAL);
  SetFocus (compWnd->hWndEdit);
}

/*------------------------------------------------- 
 * PrepareComposition
 * fills composition controls with inital values (possibly based on a ref doc)
 * if refDoc is non-NULL, this is a doc which we are replying to
 *
 * based loosely on old CreatePostingText/ExtractPostingText
 * jsc 9/28/94
 */
BOOL
PrepareComposition (WndEdit * compWnd)
{
  char contents[MAXHEADERLINE], buf[MAXHEADERLINE];
  BOOL result = SUCCESS;
  char *editBuf;
  int SaveDT;

  /* init the 1st attachment which is the body */
  if ((compWnd->attachment[0] = PrepNewAttachment (compWnd)) == NULL) {
	return FALSE;
  }
  strcpy (compWnd->attachment[0]->encodingType, "None");
  strcpy (compWnd->attachment[0]->contentType, "Text/Plain");
  strcpy (compWnd->attachment[0]->fileName, "text");
  compWnd->attachment[0]->attachInNewArt = FALSE;
  compWnd->numAttachments = 1;

  compWnd->extraHeaders = InitTextBlock(compWnd->hWnd);

  /* leave To address blank if forwarding */
  if ((compWnd->composeType == DOCTYPE_MAIL &&
	   GetToAddress (buf, MAXHEADERLINE, compWnd->Doc, compWnd->hWnd) == TRUE)) {
	SendMessage (compWnd->headerControls->UI[HDR_TO], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  }
  
  /* Get Newsgroups line but handle special cases to use Followup-to Header
     if exists, unless we are Canceling */    
  if ((compWnd->composeType == DOCTYPE_POSTING) ||
      (compWnd->composeType == DOCTYPE_CANCEL))	{

	if (compWnd->composeType == DOCTYPE_CANCEL) {
	   SaveDT = compWnd->composeType;
	   compWnd->Doc->DocType = DOCTYPE_CANCEL;
	} 
	if (GetNewsgroups (buf, MAXHEADERLINE, compWnd->Doc) == TRUE)
	   SendMessage (compWnd->headerControls->UI[HDR_GROUPS], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
	if (compWnd->composeType == DOCTYPE_CANCEL)
	   compWnd->Doc->DocType = SaveDT; 
  }

  if (ShowFromHdr && GetFromAddress (buf, MAXHEADERLINE, compWnd->Doc) == TRUE) {
	SendMessage (compWnd->headerControls->UI[HDR_FROM], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  }
  
  if (compWnd->composeType == DOCTYPE_CANCEL) {
     GetHeaderLine (compWnd->Doc, "Message-ID:", contents, MAXHEADERLINE); 
     _snprintf (buf, MAXHEADERLINE, "Cancel %s",contents+12); 
     SendMessage (compWnd->headerControls->UI[HDR_SUBJECT], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
    }
  else if (GetSubject (buf, MAXHEADERLINE, compWnd->Doc, compWnd->composeType) == TRUE) {
	SendMessage (compWnd->headerControls->UI[HDR_SUBJECT], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  }

  if (ShowOrgHdr && GetOrganization (buf, MAXHEADERLINE, compWnd->Doc) == TRUE) {
	SendMessage (compWnd->headerControls->UI[HDR_ORG], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  }

  if (ShowReplyToHdr && GetReplyToAddress (buf, MAXHEADERLINE, compWnd->Doc) == TRUE) {
	SendMessage (compWnd->headerControls->UI[HDR_REPLYTO], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  }
  
  if (ShowFollowupToHdr && (compWnd->composeType == DOCTYPE_POSTING) &&
    GetFollowupTo (buf, MAXHEADERLINE, compWnd->Doc) == TRUE) {
	SendMessage (compWnd->headerControls->UI[HDR_FOLLOWTO], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  }

  if (compWnd->composeType == DOCTYPE_POSTING && PrefillCcAddress &&
	  (GetToAddress (buf, MAXHEADERLINE, compWnd->Doc, compWnd->hWnd) == TRUE) &&
	  (compWnd->headerControls->UI[HDR_CC] != NULL)) {
	SendMessage (compWnd->headerControls->UI[HDR_CC], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  } else	// following obtains Cc in mail replies...
  if (compWnd->composeType == DOCTYPE_MAIL &&
	  (GetCcAddress(buf, MAXHEADERLINE, compWnd->Doc) == TRUE) &&
	  (compWnd->headerControls->UI[HDR_CC] != NULL)) {
	SendMessage (compWnd->headerControls->UI[HDR_CC], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  }
   
  if (compWnd->composeType != DOCTYPE_CANCEL) {
    if (ShowKeywordsHdr && GetHeaderContents (buf, MAXHEADERLINE, compWnd->Doc, "Keywords:") == TRUE) {
	  SendMessage (compWnd->headerControls->UI[HDR_KEYWORDS], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
    }
  }

  if (compWnd->composeType == DOCTYPE_POSTING &&
	  ShowSummaryHdr && GetHeaderContents (buf, MAXHEADERLINE, compWnd->Doc, "Summary:") == TRUE) {
	SendMessage (compWnd->headerControls->UI[HDR_SUMMARY], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  }

  if (((compWnd->composeType == DOCTYPE_POSTING) ||
       (compWnd->composeType == DOCTYPE_CANCEL)) &&
	  ShowDistributionHdr && GetHeaderContents (buf, MAXHEADERLINE, compWnd->Doc, "Distribution:") == TRUE) {
	SendMessage (compWnd->headerControls->UI[HDR_DIST], WM_SETTEXT, 0, (LPARAM) (LPCSTR) buf);
  }

  /* the following are headers we need now but are not shown in the window
   * we need to capture these now, in case the refDoc gets discarded
   */
  if (((compWnd->composeType == DOCTYPE_POSTING) || 
       (compWnd->composeType == DOCTYPE_CANCEL)) &&
	  !compWnd->headerControls->UI[HDR_DIST] &&
	  GetHeaderContents (contents, MAXHEADERLINE, compWnd->Doc, "Distribution:")) {
	_snprintf (buf, MAXHEADERLINE, "Distribution: %s\r\n", contents);
	result = AddLineToTextBlock (compWnd->extraHeaders, buf);
  }
  if (result == SUCCESS && !compWnd->headerControls->UI[HDR_KEYWORDS] &&
	  GetHeaderContents (contents, MAXHEADERLINE, compWnd->Doc, "Keywords:")) {
	_snprintf (buf, MAXHEADERLINE, "Keywords: %s\r\n", contents);
	result = AddLineToTextBlock (compWnd->extraHeaders, buf);
  }
  if (result == SUCCESS && compWnd->composeType == DOCTYPE_MAIL &&
	  GetInReplyTo (contents, MAXHEADERLINE, compWnd->Doc)) {
	_snprintf (buf, MAXHEADERLINE, "In-Reply-To: %s\r\n", contents);
	result = AddLineToTextBlock (compWnd->extraHeaders, buf);
  }
  if (result == SUCCESS && GetReferences (contents, MAXHEADERLINE, compWnd->Doc)) {
	_snprintf (buf, MAXHEADERLINE, "References: %s\r\n", contents);
	result = AddLineToTextBlock (compWnd->extraHeaders, buf);
  }

  if (MakeBody (&editBuf, compWnd->Doc, compWnd->composeType, compWnd->hWnd) != SUCCESS) {
	return FAIL;
  }
  if (editBuf != (char *) NULL) {
	SetWindowText (compWnd->hWndEdit, editBuf);
	GlobalFreePtr (editBuf);
  }
  return SUCCESS;
}

/*------------------------------------------------- 
 * GenerateHeadersForSend
 * generate headers textblock based on entries in header controls
 *
 * called by SendComposition (located in wvattach.c)
 * jsc 9/28/94
 */
BOOL
GenerateHeadersForSend (WndEdit * compWnd, TypTextBlock * headers, char *saveSubject)
{
  register int n;
  unsigned long i;
  char title[MAXDIALOGSTRING], contents[MAXHEADERLINE];
  char buf[MAXHEADERLINE];
  char *dest, *src, *text, *ptr, *endPtr;
  BOOL result = SUCCESS;

  *CcAddress = '\0';

  for (n = 0; n < HDR_NUM_CONTROLS; n++) {
	if (n == HDR_ATTACH)
	  continue;

	if (compWnd->headerControls->UI[n]) {
	  SendMessage (compWnd->headerControls->title[n], WM_GETTEXT, (WPARAM) MAXDIALOGSTRING, (LPARAM) (LPCSTR) title);
	  SendMessage (compWnd->headerControls->UI[n], WM_GETTEXT, (WPARAM) MAXHEADERLINE, (LPARAM) (LPCSTR) contents);

	  /* following header lines are required  */
	  if (*contents == '\0' &&
		  (n == HDR_TO || n == HDR_GROUPS || n == HDR_FROM || n == HDR_SUBJECT)) {
		sprintf (buf, "Entry required for %s", title);
		MessageBox (compWnd->hWnd, buf, "Incomplete", MB_OK | MB_ICONSTOP);
		result = FAIL;
		break;
	  }
	  /* strip spaces in newsgroups */
	  if (n == HDR_GROUPS) {
		strcpy (buf, contents);
		for (dest = contents, src = buf; *src; src++) {
		  if (!isspace (*src))
			*dest++ = *src;
		}
		*dest = '\0';
	  }
	  if (*contents) {
		if (saveSubject && n == HDR_SUBJECT) {
		  /* save subject line if given a saveSubject buffer */
		  strcpy (saveSubject, contents);
		}
		if (n == HDR_CC && compWnd->composeType == DOCTYPE_POSTING) {
		  /* eat cc-by-mail address when posting */
		  strntcpy (CcAddress, contents, MAXHEADERLINE-1);
		}
		else if (n == HDR_REPLYTO && !strcmp (MailAddress, contents)) {
		  /* ignore reply-to if identical to mail address */
		}

		/* ignore follow-to if identical to newsgroups or we are canceling */
		else if ((n == HDR_FOLLOWTO) && 
		         ((compWnd->composeType == DOCTYPE_CANCEL) ||
		          (compWnd->composeType == DOCTYPE_POSTING) &&
		          GetNewsgroups (buf, MAXHEADERLINE, compWnd->Doc) &&
		          (!strcmp (buf, contents))))
		    { }
		else {
		  /* good header, add it to the header block */
		  _snprintf (buf, MAXHEADERLINE, "%s %s\r\n", title, contents);
		  if (AddLineToTextBlock (headers, buf) == FAIL) {
			result = FAIL;
			break;
		  }
		}
	  }
	}
  }

  /* deal with headers which are not shown, but we still want */
  if (result == SUCCESS &&
	  !compWnd->headerControls->UI[HDR_FROM] &&
	  GetFromAddress (contents, MAXHEADERLINE, compWnd->Doc)) {
	_snprintf (buf, MAXHEADERLINE, "From: %s\r\n", contents);
	result = AddLineToTextBlock (headers, buf);
  }
  if (result == SUCCESS &&
	  !compWnd->headerControls->UI[HDR_REPLYTO] && *ReplyTo) {
	_snprintf (buf, MAXHEADERLINE, "Reply-To: %s\r\n", ReplyTo);
	result = AddLineToTextBlock (headers, buf);
  }
  if (result == SUCCESS &&
	  !compWnd->headerControls->UI[HDR_ORG] && *Organization) {
	_snprintf (buf, MAXHEADERLINE, "Organization: %s\r\n", Organization);
	AddLineToTextBlock (headers, buf);
  }
					   
  if (result == SUCCESS &&	compWnd->composeType == DOCTYPE_CANCEL && 
	  GetHeaderLine (compWnd->Doc, "Message-ID:", contents, MAXHEADERLINE)) { 
     _snprintf (buf, MAXHEADERLINE, "Control: cancel %s\r\n", contents+12);
	 result = AddLineToTextBlock (headers, buf);
  }

  if (result == SUCCESS &&	compWnd->composeType != DOCTYPE_POSTING && 
	   GetDate (contents, MAXHEADERLINE)) {
	_snprintf (buf, MAXHEADERLINE, "Date: %s\r\n", contents);
	result = AddLineToTextBlock (headers, buf);
  }

  /* If authentication was required, add header so that people reading it can
   * know who really posted it.  (The From: line may be a lie.)
   */
  if (result == SUCCESS && AuthReqMail) {
	_snprintf (buf, MAXHEADERLINE, "X-Authenticated-User: %s\r\n", AuthenticatedName);
	result = AddLineToTextBlock (headers, buf);
  }

  if (result == SUCCESS) {
	if ((compWnd->composeType == DOCTYPE_POSTING) || 
	    (compWnd->composeType == DOCTYPE_CANCEL)) {
	  _snprintf (buf, MAXHEADERLINE, "X-Newsreader: %s\r\n", WINVN_VERSION
#ifdef _WIN32
" 32 bit");
#else
" 16 bit");
#endif
	}
	else {
	  _snprintf (buf, MAXHEADERLINE, "X-Mailer: %s\r\n", WINVN_VERSION
#ifdef _WIN32
" 32 bit");
#else
" 16 bit");
#endif
	}
	result = AddLineToTextBlock (headers, buf);
  }
  
  if (compWnd->composeType == DOCTYPE_CANCEL) {
	 compWnd->bodyOffset = 0L;
    }
  else {
    /* add any extra headers added in PrepareComposition */
    for (i = 0; result == SUCCESS && i < compWnd->extraHeaders->numLines; i++) {
   	  result = AddLineToTextBlock (headers, TextBlockLine(compWnd->extraHeaders, i));
    }
  
    /* now scan body text up to a blank line for additional 
     * x- and rfc822 and rfc1036 headers 
     */
    compWnd->bodyOffset = 0L;

    if ((text = GetEditText (compWnd->hWndEdit)) == NULL) {
  	  result = FAIL;
    }

    if (result == SUCCESS) {
	  ptr = text;
	  while (*ptr && result == SUCCESS) {
		if ((endPtr = strchr (ptr, '\r')) == NULL) {
		  endPtr = ptr + strlen(ptr);	/* handle no crlf on last line */
		} else {
			*endPtr = '\0';
			endPtr++;				/* move past \r */
			/* handle soft line break \r\r\n (see EM_FMTLINES) */
			if (*endPtr == '\r') {
				endPtr++;			/* move past \n */
			}
			if (*endPtr == '\n') {
				endPtr++;			/* move past extra \n (in case of soft line break extra \n */
			}
		}
		if (*ptr == '\0' ||					/* blank line */
			strchr (ptr, ':') == NULL ||	/* no colon */
			!IsAdditionalHeader(ptr)) {		/* not recognized */
			break;
		}	
		_snprintf(buf, MAXHEADERLINE, "%s\r\n", ptr);
		result = AddLineToTextBlock (headers, buf);
		ptr = endPtr;
	  }
	  if (ptr != text) {
	  	compWnd->bodyOffset = (unsigned long)(ptr - text);
	  }
	  GlobalFreePtr (text);
	}
  }
  return result;
}

/* ------------------------------------------------------------------------
 * Returns true for lines which look like standard header lines
 * A binary search through a list of words would be really smart
 * This is the lazy approach
 */
BOOL
IsAdditionalHeader(char *line)
{
	switch (tolower(line[0]))
	{
	case 'a': 
		return (!_strnicmp(line, "approved:", 9));
	case 'b': 
		return (!_strnicmp(line, "bcc:", 4));
	case 'c': 
		return (!_strnicmp(line, "cc:", 3) ||
 		 	!_strnicmp(line, "control:", 8) ||
 		 	!_strnicmp(line, "comments:", 9));
	case 'd': 
		return (!_strnicmp(line, "date:", 5) ||
 		 	!_strnicmp(line, "distribution:", 13));
	case 'e': 
		return (!_strnicmp(line, "encrypted:", 10) ||
 		 	!_strnicmp(line, "expires:", 8));
	case 'f': 
		return (!_strnicmp(line, "followup-to:", 12) ||
 		 	!_strnicmp(line, "from:", 5));
 	case 'i':
		return (!_strnicmp(line, "in-reply-to:", 12));
 	case 'k':
		return (!_strnicmp(line, "keywords:", 9));
		
	case 'l':
		return (!_strnicmp(line, "lines:", 6));
	case 'm':
		return (!_strnicmp(line, "message-id:", 11));
	case 'o':
		return (!_strnicmp(line, "organization:", 13));
	case 'p':
		return (!_strnicmp(line, "path:", 5));
	case 'r':
		return (!_strnicmp(line, "references:", 11) ||
			!_strnicmp(line, "resent-", 7) ||
			!_strnicmp(line, "return-path:", 12) ||
			!_strnicmp(line, "reply-to:", 9));
	case 's':
 		return (!_strnicmp(line, "sender:", 7) ||
 			!_strnicmp(line, "subject:", 8) ||
 			!_strnicmp(line, "summary:", 8));
	case 't':
 		return (!_strnicmp(line, "to:", 3));
	case 'x':
		return (!_strnicmp(line, "x-", 2) ||
 			!_strnicmp(line, "xref:", 5));
	default:
		return (0);
	}
}

