/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "stdafx.h"

#include "fmabstra.h"
#include "fmbutton.h"
#include "fmfile.h"
#include "fmradio.h"
#include "fmselmul.h"
#include "fmselone.h"
#include "fmtext.h"
#include "fmtxarea.h"
#include "fmrdonly.h"

#include "windowsx.h"

//	Class to define common base functionality of all form elements
//		in the front end in an object oriented manner.

//	Static control ID unique for each instance of this class.
UINT CFormElement::m_uNextControlID = 0;

//	Function to create and/or retrieve a CFormElement from
//		a layout form struct.
CFormElement *CFormElement::GetFormElement(CAbstractCX *pCX, LO_FormElementStruct *pFormElement)
{
	CFormElement *pRetval = NULL;

	//	Ensure we have enough information with which to continue.
	if(pCX == NULL)	{
		return(pRetval);
	}
	else if(pFormElement == NULL)	{
		return(pRetval);
	}

	pRetval = GetFormElement(pCX, pFormElement->element_data);

	//	Look at the form element structure to see if one already exists.
	if(pRetval == NULL && pFormElement->element_data != NULL)	{
		//	We need to create a new form class.
		TRY	{
			switch(pFormElement->element_data->type)	{
				case FORM_TYPE_TEXT:
				case FORM_TYPE_PASSWORD:
					pRetval = (CFormElement *)new CFormText();
					break;
				case FORM_TYPE_CHECKBOX:
				case FORM_TYPE_RADIO:
					pRetval = (CFormElement *)new CFormRadio();
					break;
				case FORM_TYPE_BUTTON:
				case FORM_TYPE_RESET:
				case FORM_TYPE_SUBMIT:
					pRetval = (CFormElement *)new CFormButton();
					break;
				case FORM_TYPE_SELECT_ONE:
					pRetval = (CFormElement *)new CFormSelectOne();
					break;
				case FORM_TYPE_SELECT_MULT:
					pRetval = (CFormElement *)new CFormSelectMult();
					break;
				case FORM_TYPE_TEXTAREA:
					pRetval = (CFormElement *)new CFormTextarea();
					break;
				case FORM_TYPE_FILE:
					pRetval = (CFormElement *)new CFormFile();
					break;
				case FORM_TYPE_READONLY:
					pRetval = (CFormElement *)new CFormReadOnly();
					break;
				default:
					//	What exactly are we doing here?
					TRACE("Unimplemented form type (%d) requested.\n", (int)pFormElement->element_data->type);
					pRetval = NULL;
					break;
			}
		}
		CATCH(CException, e)	{
			pRetval = NULL;
		}
		END_CATCH
	}

	//	Set up and/or update the relevant information in the class.
	if(pRetval != NULL)	{
		pRetval->SetContext(pCX);
		pRetval->SetElement(pFormElement);
	}

	return(pRetval);
}

//	Only retrieve the form element class, do not create.
CFormElement *CFormElement::GetFormElement(CAbstractCX *pCX, LO_FormElementData *pFormData)
{
    CFormElement *pRetval = NULL;

    //	Ensure we have enough information with which to continue.
    if(pFormData == NULL) {
	    return(pRetval);
    }
    else if(pFormData->type == FORM_TYPE_KEYGEN)    {
        //  Doesn't have minimal data.
        return(pRetval);
    }

    //	Look at the form element structure to see if one already exists.
    if(pFormData->ele_minimal.FE_Data != NULL)	{
	    //	Already there, return it.
	    pRetval = (CFormElement *)pFormData->ele_minimal.FE_Data;
    }

    //	Set up and/or update the relevant information in the class.
    if(pRetval != NULL)	{
	    pRetval->SetContext(pCX);
    }

    return(pRetval);
}


//	Construction simply clears all members.
CFormElement::CFormElement()
{
	//	No LO form element.
	SetElement(NULL);

	//	No context.
	SetContext(NULL);

	//	By default, we have no widget allocated, and we are using default data.
	m_bUseCurrentData = FALSE;
	m_bWidgetPresent = FALSE;
}

//	Destruction cleans out all members.
CFormElement::~CFormElement()
{
    //	No LO form anymore.
    //	FreeFormElement must be used to delete the class.
    //	This assert knows that FreeFormElement calls SetElement(NULL).
    ASSERT(GetElement() == NULL);

	//	No context anymore.
	SetContext(NULL);
}

//	How to set the LO form element.
//	This may change during the lifetime of an instance, so use this to update all referencing values.
void CFormElement::SetElement(LO_FormElementStruct *pFormElement)
{
	//	Simply copy.
	m_pFormElement = pFormElement;
	
	//	Ensure its FE_Data points to us.
	if(GetElementMinimalData())	{
		GetElementMinimalData()->FE_Data = (void *)this;
	}
}

//	Return the owning LO element.
LO_FormElementStruct *CFormElement::GetElement() const
{
	return(m_pFormElement);
}

LO_FormElementData *CFormElement::GetElementData() const
{
	LO_FormElementData *pRetval = NULL;
	if(GetElement())	{
		pRetval = GetElement()->element_data;
	}

	return(pRetval);
}

//	Careful when using, may return incorrect structure if not correct
//		form type.
lo_FormElementTextData *CFormElement::GetElementTextData() const
{
	lo_FormElementTextData *pRetval = NULL;
	if(GetElementData())	{
		ASSERT(
			GetElementData()->type == FORM_TYPE_TEXT ||
			GetElementData()->type == FORM_TYPE_PASSWORD ||
			GetElementData()->type == FORM_TYPE_FILE
			);
		pRetval = &(GetElementData()->ele_text);
	}

	return(pRetval);
}

//	Careful when using, may return incorrect structure if not correct
//		form type.
lo_FormElementTextareaData *CFormElement::GetElementTextareaData() const
{
	lo_FormElementTextareaData *pRetval = NULL;
	if(GetElementData())	{
		ASSERT(
			GetElementData()->type == FORM_TYPE_TEXTAREA
			);
		pRetval = &(GetElementData()->ele_textarea);
	}

	return(pRetval);
}


//	Always safe to use.
lo_FormElementMinimalData *CFormElement::GetElementMinimalData() const
{
	lo_FormElementMinimalData *pRetval = NULL;
	if(GetElementData())	{
		pRetval = &(GetElementData()->ele_minimal);
	}

	return(pRetval);
}

//	Careful when using, may return incorrect structure if not correct
//		form type.
lo_FormElementToggleData *CFormElement::GetElementToggleData() const
{
	lo_FormElementToggleData *pRetval = NULL;
	if(GetElementData())	{
		ASSERT(
			GetElementData()->type == FORM_TYPE_RADIO ||
			GetElementData()->type == FORM_TYPE_CHECKBOX
			);
		pRetval = &(GetElementData()->ele_toggle);
	}

	return(pRetval);
}

//	Careful when using, may return incorrect structure if not correct
//		form type.
lo_FormElementSelectData *CFormElement::GetElementSelectData() const
{
	lo_FormElementSelectData *pRetval = NULL;
	if(GetElementData())	{
		ASSERT(
			GetElementData()->type == FORM_TYPE_SELECT_ONE ||
			GetElementData()->type == FORM_TYPE_SELECT_MULT
			);
		pRetval = &(GetElementData()->ele_select);
	}

	return(pRetval);
}

void *CFormElement::GetElementFEData() const
{
	void *pRetval = NULL;
	if(GetElementMinimalData())	{
		pRetval = GetElementMinimalData()->FE_Data;
	}

	return(pRetval);
}

//	Set the owning context.
void CFormElement::SetContext(CAbstractCX *pCX)
{
	//	Simply copy.
	m_pCX = pCX;

    //  If we're printing, we know (or assume), that
    //      any form data has been set to the last context's current
    //      state, so we don't want to just throw it all away by
    //      calling UseDefaultData in GetFormElementInfo.
    //  This does not apply to metafiles until they serialize the form data also.
    if(GetContext() && GetContext()->IsPrintContext())  {
        m_bUseCurrentData = TRUE;
    }
}

//	Return the owning context.
CAbstractCX *CFormElement::GetContext() const
{
	return(m_pCX);
}

//	Update the coordinates of display from the layout
//		element and display if need be.
void CFormElement::DisplayFormElement(LTRB& Rect)
{
    //	Class which need to handle this should know how in the derivation.
    //	Call the base class after your implementation.
    
    //	In any event, make sure that the widget is visible.
    //	Again, call the base class after your implementation.
    HWND hRaw = GetRaw();
    if(hRaw)	{
        BOOL bVisible = ::IsWindowVisible(hRaw);
        
        /* If window is visible, but should be invisible, then make it disappear */
        if (m_pFormElement->ele_attrmask & LO_ELE_INVISIBLE) {
            if (bVisible) {
                ::ShowWindow(hRaw, SW_HIDE);
            }
        }
        /* If window is invisible, but should be visible, then draw it */
        else {
            if (!bVisible) {
                ::ShowWindow(hRaw, SW_SHOWNA);
            }
        }
    }
}

//	This is called by LAYOUT when a form element should interpret a newline as
//		hitting the submit button in a form.  This is done mainly on single line
//		edit fields.
void CFormElement::FormTextIsSubmit()
{
    HWND hRaw = GetRaw();
    if(hRaw) {
        CNetscapeEdit *pEdit = (CNetscapeEdit *)CEdit::FromHandlePermanent(hRaw);
        pEdit->SetSubmit(TRUE);
    }
}

//	Free the form element.
//	The form data is passed in, because layout has already
//		disassociated it with the form struct.
//	No need to override really....
void CFormElement::FreeFormElement(LO_FormElementData *pFormData)
{
	//	Cause all widgets to be deallocated if not already.
	DestroyWidget();

	//	NOTE:	m_pFormElement at this point can not be guaranteed to be valid.
	//			Do not use it or members which access it.
	SetElement(NULL);

	//	We're done.
    //  Set the FE data to be null, since we're deleting it.
	if(pFormData)	{
		pFormData->ele_minimal.FE_Data = NULL;
	}
	delete this;
}

//	Layout calls this to determine the size of a form element.
void CFormElement::GetFormElementInfo()
{
	//	Avoid creating lots of widgets, see if we already have one.
	if(IsWidgetPresent() == FALSE)	{
		//	Not present, we have to create it on our own.
		//	Be sure to check ShouldUseCurrentData to see if you should use the
		//		current or default data from the Layout struct when
		//		filling in the information (however, the actual size
		//		calculation of the widget should be done with default
		//		data always).
		CreateWidget();

		if(ShouldUseCurrentData())	{
			UseCurrentData();
		}
		else	{
			UseDefaultData();

			//	First time creations should by default fill in the
			//		current data with the default data, so that
			//		MOCHA has some data to play with.
			UpdateCurrentData();
		}
	}

	//	A widget should be present, so that we can check cached
	//		size values in the future.
	m_bWidgetPresent = TRUE;

	//	Get widget information into the layout structure.
	FillSizeInfo();
}

//	Retrieve the data from the form element, and possibly
//		delete the form element widget.
void CFormElement::GetFormElementValue(BOOL bTurnOff)
{
	//	Collect data from the widgets.
	UpdateCurrentData();

	if(bTurnOff)	{
		//	If we have to turn off, then mark that we should use current data in the future
		//		when we have to recreate ourselves, and mark that our widget is gone.
		m_bUseCurrentData = TRUE;
		m_bWidgetPresent = FALSE;

		//	Get rid of the widgets.
		DestroyWidget();
	}
}

//	Reset the form element to its default value.
void CFormElement::ResetFormElement()
{
	//	Just use the default data in the LAYOUT struct.
	UseDefaultData();

	//	Update (write over) any modified current (now old) data.
	UpdateCurrentData();
}

//	Toggle the form element's state.
void CFormElement::SetFormElementToggle(BOOL bState)
{
    HWND hRaw = GetRaw();
    if(hRaw) {
        //  Set state if not already reflected.
        if(Button_GetCheck(hRaw) != bState) {
            Button_SetCheck(hRaw, bState);
        }
        UpdateCurrentData();
    }
}

//	Give focus to the form element.
void CFormElement::FocusInputElement()
{
    HWND hRaw = GetRaw();
    if(hRaw && GetContext() && GetElement()) {
        FEU_MakeElementVisible(GetContext()->GetContext(), (LO_Any *)GetElement());
        ::SetFocus(hRaw);
    }
}

//	Do not give focus to the form element.
void CFormElement::BlurInputElement()
{
    HWND hRaw = GetRaw();
    if(hRaw) {
        // Only blur focus if we already have it and we can give it to our parent.
        if(hRaw == ::GetFocus() && ::GetParent(hRaw)) {
            ::SetFocus(::GetParent(hRaw));
        }
    }
}

//	Select the form element's data.
void CFormElement::SelectInputElement()
{
    HWND hRaw = GetRaw();
    if(hRaw) {
        Edit_SetSel(hRaw, 0, -1);
    }
}

//	Change the value of the form element.
void CFormElement::ChangeInputElement()
{
    //	All we really need to do is update ourselves so that we reflect the
	//		current data.
	UseCurrentData();

	if(GetElementMinimalData())	{
		if(GetElementMinimalData()->type == FORM_TYPE_RADIO)	{
			if(GetContext() && GetContext()->GetContext())	{
                lo_FormElementToggleData * tog_data;
                tog_data = (lo_FormElementToggleData *) GetElement()->element_data;
		        if(tog_data->toggled) {
                    // If we are supposed to be on, turn off everyone else
    				LO_FormRadioSet(GetContext()->GetDocumentContext(), GetElement());  
		        }
			}
		}
	}
}

//  Enable the form element (Editable to the user).
void CFormElement::EnableInputElement()
{
    HWND hRaw = GetRaw();
    if(hRaw) {
        ::EnableWindow(hRaw, TRUE);
    }
}

//  Disable the form element (Read only to the user).
void CFormElement::DisableInputElement()
{
    HWND hRaw = GetRaw();
    if(hRaw) {
        ::EnableWindow(hRaw, FALSE);
    }
}

//	Return a unique control ID each time called.
UINT CFormElement::GetDynamicControlID()
{
	//	Handle wrap.
	if(m_uNextControlID == 0)	{
		m_uNextControlID = DYNAMIC_RESOURCE;
	}

	return(m_uNextControlID++);
}

#ifdef XP_WIN16
//	Call this to get the segment for edit fields under 16 bit windows.
//	This is so they are taken out of the precious DGROUP.
HINSTANCE CFormElement::GetSegment()
{
	HINSTANCE hRetval = NULL;

	//	Only if we have a context, and it has widgets.
	if(GetContext() && GetContext()->IsWindowContext())	{
		CPaneCX *pPaneCX = VOID2CX(GetContext(), CPaneCX);
		hRetval = pPaneCX->GetSegment();
	}

	return(hRetval);
}
#endif

extern "C" void FE_RaiseWindow(MWContext *pContext)    {
    //  Make sure this is possible.
    if(pContext && ABSTRACTCX(pContext) && !ABSTRACTCX(pContext)->IsDestroyed()
        && ABSTRACTCX(pContext)->IsFrameContext() && PANECX(pContext)->GetPane()
        && WINCX(pContext)->GetFrame() && WINCX(pContext)->GetFrame()->GetFrameWnd()) {
        CWinCX *pWinCX = WINCX(pContext);
        CFrameWnd *pFrameWnd = pWinCX->GetFrame()->GetFrameWnd();

        //  Bring the frame to the top first.
		if(pFrameWnd->IsIconic())	{
			pFrameWnd->ShowWindow(SW_RESTORE);
		}
        ::SetWindowPos(pFrameWnd->GetSafeHwnd(), HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
#ifdef XP_WIN32
        pFrameWnd->SetForegroundWindow();
#endif

        //  Now, set focus to the view being raised,
        //      possibly a frame cell.
        pWinCX->
            GetFrame()->
            GetFrameWnd()->
            SetActiveView(pWinCX->GetView(), TRUE);
    }
}

extern "C" void FE_LowerWindow(MWContext *pContext)    {
    //  Make sure this is possible.
    if(pContext && ABSTRACTCX(pContext) && !ABSTRACTCX(pContext)->IsDestroyed()
        && ABSTRACTCX(pContext)->IsFrameContext() && PANECX(pContext)->GetPane()
        && WINCX(pContext)->GetFrame() && WINCX(pContext)->GetFrame()->GetFrameWnd()) {
        CWinCX *pWinCX = WINCX(pContext);
        CFrameWnd *pFrameWnd = pWinCX->GetFrame()->GetFrameWnd();

	MWContext *pTravContext = NULL;
	CWinCX *pTravWinCX = NULL;
	CFrameWnd *pTravFrameWnd = NULL;
	HWND pTopBottommostHwnd = NULL;
	HWND pTravHwnd;
	XP_Bool LowerWindow;

	// We don't have a good way to do this.  Look through the global
	// context list for always lowered windows.
	XP_List *pTraverse = XP_GetGlobalContextList();
	while (pTravContext = (MWContext *)XP_ListNextObject(pTraverse)) {
	    if(pTravContext && ABSTRACTCX(pTravContext) && 
		    !ABSTRACTCX(pTravContext)->IsDestroyed() &&
		    ABSTRACTCX(pTravContext)->IsFrameContext() &&
		    WINCX(pTravContext)->GetFrame() && 
		    WINCX(pTravContext)->GetFrame()->GetFrameWnd())	{

		// Find each always lowered window.
		pTravWinCX = WINCX(pTravContext);
		pTravFrameWnd = pTravWinCX->GetFrame()->GetFrameWnd();
		if (((CGenericFrame*)pTravFrameWnd)->IsBottommost()) {
		    if (!pTopBottommostHwnd)
			pTopBottommostHwnd = pTravFrameWnd->GetSafeHwnd();
		    else {
			//Ugly!  See if any of the other bottommost windows are above
			//this one.  If so, use them.
			LowerWindow = FALSE;
			pTravHwnd = pTopBottommostHwnd;
			while (pTravHwnd = GetNextWindow(pTravHwnd, GW_HWNDPREV)) {
			    if (pTravHwnd == pTravFrameWnd->GetSafeHwnd())
				LowerWindow = TRUE;
			}
			if (LowerWindow)
			    pTopBottommostHwnd = pTravFrameWnd->GetSafeHwnd();
		    }
		}
	    }
	}
	
	if (pTopBottommostHwnd)
	    //  Place the frame above the top bottommost window..
	    ::SetWindowPos(pFrameWnd->GetSafeHwnd(), GetNextWindow(pTopBottommostHwnd, GW_HWNDPREV), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
	else
	    //  Push the frame to the bottom.
	    ::SetWindowPos(pFrameWnd->GetSafeHwnd(), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

        //  Remove focus.
        ::SetFocus(NULL);
    }
}

//
// Mocha-betties demand attention --- give them the focus
//
extern "C" void FE_FocusInputElement(MWContext * pContext, LO_Element * pElement)
{
    //  They may intend the actual context.
    if(pElement == NULL)    {
        TRACE("Focusing context\n");
        FE_RaiseWindow(pContext);
        return;
    }
	else if(pElement->type == LO_FORM_ELE)	{
		CFormElement *pFormClass = CFormElement::GetFormElement(ABSTRACTCX(pContext), &(pElement->lo_form));
		if(pFormClass != NULL)	{
			pFormClass->FocusInputElement();
		}
	}
}

//
// Mocha-betties are sulking and don't want anyone looking at them --- just give
//    focus to our parent (if we have the focus to begin with)
//
extern "C" void FE_BlurInputElement(MWContext * pContext, LO_Element * pElement)
{
    //  They may intend the actual context.
    if(pElement == NULL)    {
        TRACE("Blurring context\n");
        FE_LowerWindow(pContext);
        return;
    }
	else if(pElement->type == LO_FORM_ELE)	{
		CFormElement *pFormClass = CFormElement::GetFormElement(ABSTRACTCX(pContext), &(pElement->lo_form));
		if(pFormClass != NULL)	{
			pFormClass->BlurInputElement();
		}
	}
}

//
// Mocha-betties want to be the ones that are selected
//
extern "C" void FE_SelectInputElement(MWContext * pContext, LO_Element * pElement)
{
	//	Get our front end form element, and have it do its thang.
	ASSERT(pElement && pElement->type == LO_FORM_ELE);
	if(pElement && pElement->type == LO_FORM_ELE)	{
		CFormElement *pFormClass = CFormElement::GetFormElement(ABSTRACTCX(pContext), &(pElement->lo_form));
		if(pFormClass != NULL)	{
			pFormClass->SelectInputElement();
		}
	}
}

// Mocha-betties have decided to change the value of a form element
//   deal with it
extern "C" void FE_ChangeInputElement(MWContext * pContext, LO_Element * pElement)
{
	//	Get our front end form element, and have it do its thang.
	ASSERT(pElement && pElement->type == LO_FORM_ELE);
	if(pElement && pElement->type == LO_FORM_ELE)	{
		CFormElement *pFormClass = CFormElement::GetFormElement(ABSTRACTCX(pContext), &(pElement->lo_form));
		if(pFormClass != NULL)	{
			pFormClass->ChangeInputElement();
		}
	}
}

// FE_ClickAnyElement() is based on FE_ClickInputElement(), and should replace the latter.
// If haveXY != 0, the xx, yy will be used for click. otherwise click the center of element.
//
// xx, yy are element coordinates, This function take care of :
// lo_any.x_offset, pWinCX->GetOriginX(), ClientToScreen(), and  ScreenToClient()
extern "C" int FE_ClickAnyElement(MWContext * pContext,  LO_Element * pElement, int haveXY, int32 xx, int32 yy )
{
    if(pElement && ABSTRACTCX(pContext) && ABSTRACTCX(pContext)->IsWindowContext())    {
        HWND hRaw = NULL;
        CPaneCX *pPaneCX = PANECX(pContext);
    	//	Get our front end form element, and have it do its thang.
        //  This differs only in that they are child windows and therefore, the destination window
        //      for the events differ.
	    if(pElement->type == LO_FORM_ELE)	{
		    CFormElement *pFormClass = CFormElement::GetFormElement(ABSTRACTCX(pContext), &(pElement->lo_form));
		    if(pFormClass != NULL)	{
                hRaw = pFormClass->GetRaw();
		    }
	    }
        else    {
            //  The destination for the click is the view.
            hRaw = pPaneCX->GetPane();
        }

        if(hRaw) {

			if( ! haveXY ) {
				//  Emulate a mouse click in the center of the element.
				xx  = pElement->lo_any.x + pElement->lo_any.width / 2;
				yy  = pElement->lo_any.y + pElement->lo_any.height / 2;
			}
			
			//  Mouse coordinates are relative to the widget, not absolute to the screen.
            //  We may have some problems if the destination is not viewable.
            
            //  Figure the coordinates of the element according to context (view).
            xx += pElement->lo_any.x_offset - pPaneCX->GetOriginX();
	        yy += pElement->lo_any.y_offset - pPaneCX->GetOriginY();
 
            //  Adjust for coords to the widget.
            CPoint pCoords(CASTINT(xx), CASTINT(yy));
            ::ClientToScreen(pPaneCX->GetPane(), &pCoords);
            ::ScreenToClient(hRaw, &pCoords);
            //  Fire.
            ::SendMessage(hRaw, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(pCoords.x, pCoords.y));
            ::SendMessage(hRaw, WM_LBUTTONUP, 0, MAKELPARAM(pCoords.x, pCoords.y));
			return( 1 );
        }
    }	// if(pElement && ABSTRACTCX(pCon
	return( 0 );
}	// FE_ClickAnyElement()

extern "C" void FE_ClickInputElement(MWContext * pContext, LO_Element * pElement)
{
    if(pElement && ABSTRACTCX(pContext) && ABSTRACTCX(pContext)->IsWindowContext())    {
        HWND hRaw = NULL;
        CPaneCX *pPaneCX = PANECX(pContext);
    	//	Get our front end form element, and have it do its thang.
        //  This differs only in that they are child windows and therefore, the destination window
        //      for the events differ.
	    if(pElement->type == LO_FORM_ELE)	{
		    CFormElement *pFormClass = CFormElement::GetFormElement(ABSTRACTCX(pContext), &(pElement->lo_form));
		    if(pFormClass != NULL)	{
                hRaw = pFormClass->GetRaw();
		    }
	    }
        else    {
            //  The destination for the click is the view.
            hRaw = pPaneCX->GetPane();
        }

        if(hRaw) {
            //  Emulate a mouse click in the center of the element.
            //  Mouse coordinates are relative to the widget, not absolute to the screen.
            //  We may have some problems if the destination is not viewable.
            
            //  Figure the coordinates of the element according to context (view).
            LTRB Rect;
	        Rect.left = pElement->lo_any.x + pElement->lo_any.x_offset - pPaneCX->GetOriginX();
	        Rect.top = pElement->lo_any.y + pElement->lo_any.y_offset - pPaneCX->GetOriginY();
	        Rect.right = Rect.left + pElement->lo_any.width;
	        Rect.bottom = Rect.top + pElement->lo_any.height;
            //  Adjust for coords to the widget.
            CPoint pCoords(CASTINT(Rect.left), CASTINT(Rect.top));
            ::ClientToScreen(pPaneCX->GetPane(), &pCoords);
            ::ScreenToClient(hRaw, &pCoords);
            //  Center target.
            pCoords.x += CASTINT(Rect.Width() / 2);
            pCoords.y += CASTINT(Rect.Height() / 2);
            //  Fire.
            ::SendMessage(hRaw, WM_LBUTTONDOWN, MK_LBUTTON, MAKELPARAM(pCoords.x, pCoords.y));
            ::SendMessage(hRaw, WM_LBUTTONUP, 0, MAKELPARAM(pCoords.x, pCoords.y));
        }
    }
}

extern "C" void FE_EnableInputElement(MWContext *pContext, LO_Element *pElement)
{
	//	Get our front end form element, and have it do its thang.
	ASSERT(pElement && pElement->type == LO_FORM_ELE);
	if(pElement && pElement->type == LO_FORM_ELE)	{
		CFormElement *pFormClass = CFormElement::GetFormElement(ABSTRACTCX(pContext), &(pElement->lo_form));
		if(pFormClass != NULL)	{
			pFormClass->EnableInputElement();
		}
	}
}

extern "C" void FE_DisableInputElement(MWContext *pContext, LO_Element *pElement)
{
	//	Get our front end form element, and have it do its thang.
	ASSERT(pElement && pElement->type == LO_FORM_ELE);
	if(pElement && pElement->type == LO_FORM_ELE)	{
		CFormElement *pFormClass = CFormElement::GetFormElement(ABSTRACTCX(pContext), &(pElement->lo_form));
		if(pFormClass != NULL)	{
			pFormClass->DisableInputElement();
		}
	}
}

void CFormElement::MoveWindow(HWND hWnd, int32 lX, int32 lY)
{
    if(hWnd && ::GetParent(hWnd))	{
        //	Is a window, needs a widget/window representation.
        //	All we need to do is to make sure that the widget is
        //		at the correct location as requested by layout.
        //	Figure the XY layout wants.

        //	Determine widget offset into the client window.
        RECT crWidget;
        ::GetWindowRect(hWnd, &crWidget);
        
        POINT ptLT;
        ptLT.x = crWidget.left;
        ptLT.y = crWidget.top;
        ::ScreenToClient(::GetParent(hWnd), &ptLT);

        //  Only move the widget if not already there.
        //  No need to cause the widget to redraw.
        if(ptLT.x != lX || ptLT.y != lY) {
            ::MoveWindow(hWnd, CASTINT(lX), CASTINT(lY),
                crWidget.right - crWidget.left,
                crWidget.bottom - crWidget.top,
                TRUE);
        }
    }
}


void CFormElement::SetWidgetFont(HDC hdc, HWND hWidget)
{
	// this is just a trick to get the HFONT from the dc.
	if (hWidget) {
		HFONT hFont = (HFONT)::SelectObject(hdc, 
						(HFONT)	GetStockObject(SYSTEM_FONT));
		// now select the correct font into the DC.		
		::SelectObject(hdc, hFont);
		
#ifdef XP_WIN32
		//  Win95 systems need to keep their previous margins, or things get changed (margins)
		//      upon switching fonts.
		//  MS Knowledge Base article ID: Q138419
		DWORD dwMargins = 0;
		if(sysInfo.IsWin4_32()) {
			dwMargins = ::SendMessage(hWidget, EM_GETMARGINS, 0L, 0L);
		}
#endif
		::SendMessage(hWidget, WM_SETFONT, (WPARAM)hFont, TRUE);
#ifdef XP_WIN32
		if(sysInfo.IsWin4_32()) {
			::SendMessage(
			    hWidget,
			    EM_SETMARGINS,
			    EC_LEFTMARGIN | EC_RIGHTMARGIN,
			    MAKELPARAM(LOWORD(dwMargins), HIWORD(dwMargins)));
		}
#endif
	}
}
