/* -*- 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 "fmtext.h"
#include "odctrl.h"
#include "intl_csi.h"
//	This file is dedicated to form type text edit boxes on windows and
//		their implementation as required by the XP layout library.

static const char gWndProcProperty[] = "CFormTextWndProc";

//	Construction simply clears all members.
CFormText::CFormText()
{
	//	No widget yet.
	m_pWidget = NULL;
	mRealWndProc = 0;
}

//	Destruction cleans out all members.
CFormText::~CFormText()
{
}

//	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 CFormText::SetElement(LO_FormElementStruct *pFormElement)
{
	//	Call the base.
	CFormElement::SetElement(pFormElement);

	//	Let the widget know the element.
	if(m_pWidget)	{
		m_pWidget->SetContext(GetContext() ? GetContext()->GetContext() : NULL, GetElement());
	}
}

//	Set the owning context.
//	Use this to determine what context we live in and how we should
//		represent ourself (DC or window).
void CFormText::SetContext(CAbstractCX *pCX)
{
	//	Call the base.
	CFormElement::SetContext(pCX);

	//	Let the widget know the context.
	if(m_pWidget)	{
		m_pWidget->SetContext(GetContext() ? GetContext()->GetContext() : NULL, GetElement());
	}
}

//	Display the form element given the particular context we are in.
//	Possibly only use a DC for representation, or have the
//		window move.
void CFormText::DisplayFormElement(LTRB& Rect)
{
	//	Display only has meaning if our context is a device context.
	if(GetContext() && GetContext()->IsDCContext())	{
		//	Further, need to detect how we're going to be drawing ourselves.
		if(GetContext()->IsPureDCContext())	{
			//	Only works from a DC, needs a GDI drawing representation.
			CDCCX *pDCCX = VOID2CX(GetContext(), CDCCX);
            LTRB r(Rect.left, Rect.top, Rect.right, Rect.bottom);

            //  Adjust for what we bastardized earlier in fill size info.
            r.Inflate(0, -1 * pDCCX->Pix2TwipsY(EDIT_SPACE) / 2);

            //  Do something simple.
            pDCCX->Display3DBox(r, pDCCX->ResolveLightLineColor(), pDCCX->ResolveDarkLineColor(), pDCCX->Pix2TwipsY(2));

            //  Final adjustment for below...
            r.Inflate(-1 * pDCCX->Twips2PixX(2), -1 * pDCCX->Twips2PixY(2));

            //  Is there text to output.
            if(GetElementTextData() && GetElementTextData()->current_text)  {
                char *pText = (char *)GetElementTextData()->current_text;

                //  Obtain the font for the text to be drawn.
                    //  Get the DC.
                    HDC pDC = pDCCX->GetContextDC();
                    if(pDC) {
						CyaFont	*pMyFont;
						
						pDCCX->SelectNetscapeFont( pDC, GetTextAttr(), pMyFont );
						if (pMyFont) {
                            int32 lTextLength = XP_STRLEN(pText);
                            char *pTextBuf = new char[lTextLength + 1];
                            if(pTextBuf)    {
                                //  Clear it.
                                memset(pTextBuf, 0, CASTSIZE_T(lTextLength + 1));

                                //  Decide if a password.
                                if(GetElementMinimalData()->type == FORM_TYPE_PASSWORD)  {
                                    memset(pTextBuf, '*', CASTSIZE_T(lTextLength));
                                }
                                else    {
                                    strcpy(pTextBuf, pText);
                                }

                                //  Text mode must go transparent (some printers block out drawing around text).
                                int iOldBk = ::SetBkMode(pDC, TRANSPARENT);

                                //  Decide the destination for the text rect.
                                //  We need to move the text to the right and left some, and on the Y.
                                RECT crDest;
								::SetRect(&crDest, CASTINT(r.left), CASTINT(r.top), CASTINT(r.right), CASTINT(r.bottom));
								crDest.left  += pMyFont->GetMeanWidth();
                                crDest.top += ((crDest.bottom - crDest.top) - (pMyFont->GetHeight())) / 2;
                                //  Finally draw the text.
                                //  The clipping rect is not the destination rect.
								CIntlWin::ExtTextOut(
									(GetContext()->GetContext() ?
									 	INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(GetContext()->GetContext())) :0),
									pDC, crDest.left,
                                    crDest.top, ETO_CLIPPED, CRect(CASTINT(r.left),
                                    CASTINT(r.top), CASTINT(r.right),
                                    CASTINT(r.bottom)), pTextBuf, CASTINT(lTextLength), NULL);

                                //  Restore Bk mode.
                                ::SetBkMode(pDC, iOldBk);

                                //  Get rid of the buffer.
                                delete[] pTextBuf;
//                            }

							pDCCX->ReleaseNetscapeFont( pDC, pMyFont );
						}
						else {
							m_pWidget->ReleaseDC(CDC::FromHandle(pDC));
							DestroyWidget();
						}
                        pDCCX->ReleaseContextDC(pDC);
                    }
                }
            }
		}
		else if(GetContext()->IsWindowContext())	{
			MoveWindow(m_pWidget->m_hWnd, Rect.left, Rect.top + EDIT_SPACE / 2);
		}
		else	{
			//	Is undefined....
			ASSERT(0);
		}
	}

	//	Call the base.
	CFormElement::DisplayFormElement(Rect);
}

//	Destroy the widget (window) implemenation of the form.
void CFormText::DestroyWidget()
{
	//	Get rid of the widget if around.
	if(m_pWidget)	{
		RemoveProp(m_pWidget->m_hWnd, gWndProcProperty);
		if (mRealWndProc)
			SetWindowLong(m_pWidget->m_hWnd, GWL_WNDPROC,
				(LONG)mRealWndProc);
		m_pWidget->DestroyWindow();
		delete m_pWidget;
		m_pWidget = NULL;
	}
}

//	Create the widget (window) implementation of the form
//		but DO NOT DISPLAY.
void CFormText::CreateWidget()
{
	if(GetContext() && GetElement())	{
		if(GetContext()->IsWindowContext() && VOID2CX(GetContext(), CPaneCX)->GetPane())	{
			//	Need a widget representation.
			ASSERT(m_pWidget == NULL);
			m_pWidget = new CODNetscapeEdit;
	
			if(m_pWidget == NULL)	{
				return;
			}

			//	Inform widget of context and element.
			m_pWidget->SetContext(GetContext()->GetContext(), GetElement());

#ifdef XP_WIN16
			//	On 16 bits, we need to set the segment of the widget before creation.
			HINSTANCE hSegment = GetSegment();
			if(hSegment)	{
				m_pWidget->SetInstance(hSegment);
			}
			else	{
				delete m_pWidget;
				m_pWidget = NULL;
				return;
			}
#endif

			//	The style of the widget to be created.
			DWORD dwStyle = WS_CHILD | WS_BORDER | ES_LEFT | ES_AUTOHSCROLL | WS_TABSTOP;
			if(GetElementMinimalData())	{
				switch(GetElementMinimalData()->type)	{
					case FORM_TYPE_PASSWORD:
						dwStyle |= ES_PASSWORD;
						break;
					default:
						break;
				}
			}
			else	{
				//	Must set styles.
				delete m_pWidget;
				m_pWidget = NULL;
				return;
			}

			//	Create the widget, hidden, and with a bad size.
			BOOL bCreate =
#ifdef XP_WIN32
				m_pWidget->CreateEx(WS_EX_CLIENTEDGE, 
					 _T("EDIT"),
					 NULL,
					 dwStyle, 
					 1, 1, 0, 0,
					 VOID2CX(GetContext(), CPaneCX)->GetPane(), 
					 (HMENU)GetDynamicControlID(),
					 NULL);	

#else
				m_pWidget->Create(dwStyle, CRect(1, 1, 0, 0), 
					CWnd::FromHandle(VOID2CX(GetContext(), CPaneCX)->GetPane()), 
					GetDynamicControlID());
#endif

			if(!bCreate)	{
				delete m_pWidget;
				m_pWidget = NULL;
				return;
			}

			SetProp(m_pWidget->m_hWnd, gWndProcProperty, this);
			mRealWndProc = (WNDPROC)SetWindowLong(m_pWidget->m_hWnd,
				GWL_WNDPROC, (LONG)TempWndProc);

			//	Next, need to size the widget.
			//	Obtain a font.
			//	Measure some text.
			CDC *pDC = m_pWidget->GetDC();
			CDCCX *pDCCX = VOID2CX(GetContext(), CDCCX);
			if(pDC)	{
				//	Default length is 20
				int32 lLength = 20;
				CyaFont	*pMyFont;
					
				pDCCX->SelectNetscapeFont( pDC->GetSafeHdc(), GetTextAttr(), pMyFont );
				if (pMyFont) {
					SetWidgetFont(pDC->GetSafeHdc(), m_pWidget->m_hWnd);
					GetElement()->text_attr->FE_Data = pMyFont;
					//	Now figure up the width and height we would like.
					int32 lWidgetWidth = pMyFont->GetMaxWidth();
					int32 lWidgetHeight = pMyFont->GetHeight() + (pMyFont->GetHeight() / 2);

					//	See if we can measure the default text, and/or
					//		set up the size and size limits.
					if(GetElementTextData())	{
						if(GetElementTextData()->size > 0)	{
							//	Use provided size.
							lLength = GetElementTextData()->size;

							//	Use average width.
							lWidgetWidth += lLength * pMyFont->GetMeanWidth();
						}
						else if(GetElementTextData()->default_text)	{
							//	Use length of text, exactly.
							char *pDefault = (char *)GetElementTextData()->default_text;

							lLength = XP_STRLEN(pDefault);
							SIZE csz;
							CIntlWin::GetTextExtentPointWithCyaFont(pMyFont, 
									INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(GetContext()->GetContext())),
									pDC->GetSafeHdc(), pDefault, CASTINT(lLength), &csz);
							//	Use this width.
							lWidgetWidth += csz.cx;
						}

						//	Hold to the maximum size limit, and set it.
						if(GetElementTextData()->max_size >= 0)	{
							m_pWidget->LimitText(CASTINT(GetElementTextData()->max_size));
						}
					}

					//	Restore old font selection.
					pDCCX->ReleaseNetscapeFont( pDC->GetSafeHdc(), pMyFont );
					m_pWidget->MoveWindow(1, 1, CASTINT(lWidgetWidth), CASTINT(lWidgetHeight), FALSE);
					m_pWidget->ReleaseDC(pDC);
				}
				else {
					m_pWidget->ReleaseDC(pDC);
					DestroyWidget();
					return;
				}
			}
			else	{
				//	No DC, no widget.
				DestroyWidget();
				return;
			}
		}
		else if(GetContext()->IsPureDCContext())	{
			//	Need a drawn representation.
		}
	}
}

//	Copy the current data out of the layout struct into the form
//		widget, or mark that you should represent using the current data.
void CFormText::UseCurrentData()
{
	//	Detect context type and do the right thing.
	if(GetContext())	{
		if(GetContext()->IsWindowContext())	{
			//	Need a widget.
			if(m_pWidget)	{
				//	Determine the current text to set.
				char *pCurrent = "";
				if(GetElementTextData() && GetElementTextData()->current_text)	{
					pCurrent = (char *)GetElementTextData()->current_text;
				}
				// We have to SetContext to the widget before we SetWindowText
				// Otherwise, the widget don't know what csid the text is.
				m_pWidget->SetContext(GetContext()->GetContext(), GetElement());

				m_pWidget->SetWindowText(pCurrent);
			}
		}
		else if(GetContext()->IsPureDCContext())	{
			//	Printing/metafile
            //  Whatever is in current data is current....
		}
	}
}

//	Copy the default data out of the layout struct into the form
//		widget, or mark that you should represent using the default data.
void CFormText::UseDefaultData()
{
	//	Detect context type and do the right thing.
	if(GetContext())	{
		if(GetContext()->IsWindowContext())	{
			//	Need a widget.
			if(m_pWidget)	{
				//	Determine the default text to set.
				char *pDefault = "";
				if(GetElementTextData() && GetElementTextData()->default_text)	{
					pDefault = (char *)GetElementTextData()->default_text;
				}
				// We have to SetContext to the widget before we SetWindowText
				// Otherwise, the widget don't know what csid the text is.
				m_pWidget->SetContext(GetContext()->GetContext(), GetElement());
				m_pWidget->SetWindowText(pDefault);
			}
		}
		else if(GetContext()->IsPureDCContext())	{
			//	Printing/metafile
            //  Delete any current data, attempt to use the default data if present.
            if(GetElementTextData())    {
                if(GetElementTextData()->current_text)  {
                    XP_FREE(GetElementTextData()->current_text);
                    GetElementTextData()->current_text = NULL;
                }

                if(GetElementTextData()->default_text) {
                    char *pText = (char *)GetElementTextData()->default_text;
                    int32 lSize = XP_STRLEN(pText) + 1;
                    GetElementTextData()->current_text = (XP_Block)XP_ALLOC(lSize);
                    memcpy(GetElementTextData()->current_text, pText, CASTSIZE_T(lSize));
                }
            }
		}
	}
}

//	Fill in the layout size information in the layout struct regarding
//		the dimensions of the widget.
void CFormText::FillSizeInfo()
{
	//	Detect context type and do the right thing.
	if(GetContext() && GetElement())	{
		if(GetContext()->IsWindowContext())	{
			//	Need a widget.
			if(m_pWidget)	{
				//	Determine our window position.
				CRect crRect;
				m_pWidget->GetWindowRect(crRect);

				//	Munge the coordinate a little for layout.
				//	We'll have to know how to decode this information
				//		in the display routine (by half).
				GetElement()->width = crRect.Width();
				GetElement()->height = crRect.Height() + EDIT_SPACE;
				GetElement()->border_vert_space = EDIT_SPACE/2;
                GetElement()->baseline = GetElement()->height - GetElement()->height / 4;
			}
			else	{
				//	No widget, tell layout nothing.
				GetElement()->width = 0;
				GetElement()->height = 0;
				GetElement()->baseline = 0;
			}
		}
		else if(GetContext()->IsPureDCContext())	{
            int32 lWidth = 0;
            int32 lHeight = 0;
            int32 lBaseline = 0;

            //  Need to figure the size of the widget.
            CDCCX *pDCCX = VOID2CX(GetContext(), CDCCX);
                HDC pDC = pDCCX->GetAttribDC();
                if(pDC) {
					CyaFont	*pMyFont;
                    int32 lLength = 20;
					
					pDCCX->SelectNetscapeFont( pDC, GetTextAttr(), pMyFont );
					if (pMyFont) {
						lWidth = pMyFont->GetMaxWidth();
						lHeight = pMyFont->GetHeight() + pMyFont->GetHeight() / 2 + pDCCX->Twips2PixY(EDIT_SPACE);

						//  Attempt to measure text.
						if(GetElementTextData())    {
							if(GetElementTextData()->size > 0)  {
								//  Use provided size.
								lLength = GetElementTextData()->size;

								//  Figure width using average char width.
								lWidth += lLength * pMyFont->GetMeanWidth();
							}
							else if(GetElementTextData()->default_text) {
								//  Use length of text instead.
								char *pText = (char *)GetElementTextData()->default_text;
								lLength = XP_STRLEN(pText);

								//  Use exact size information.
								SIZE csz;
								CIntlWin::GetTextExtentPointWithCyaFont(pMyFont, 
									INTL_GetCSIWinCSID(LO_GetDocumentCharacterSetInfo(GetContext()->GetContext())),
									pDC, pText, CASTINT(lLength), &csz);
								lWidth += csz.cx;
							}
						}

						//  Figure baseline.
						lBaseline = lHeight - (pMyFont->GetHeight() / 2 + pMyFont->GetDescent()) / 2;
							pDCCX->ReleaseNetscapeFont( pDC, pMyFont );
					}
					else {
						m_pWidget->ReleaseDC(CDC::FromHandle(pDC));
						DestroyWidget();
					}
                    pDCCX->ReleaseContextDC(pDC);
            }

            //  Inform layout what we now know.
            GetElement()->width = lWidth;
            GetElement()->height = lHeight;
            GetElement()->baseline = lBaseline;
		}
	}
}

//	Copy the current data out of the form element back into the
//		layout struct.
void CFormText::UpdateCurrentData(BOOL bSubmit)
{
	if(GetContext())	{
		if(GetContext()->IsWindowContext())	{
			if(m_pWidget && ::IsWindow(m_pWidget->GetSafeHwnd()) && GetElementTextData())	{
				//	Free off any current text.
				if(GetElementTextData()->current_text)	{
					XP_FREE(GetElementTextData()->current_text);
					GetElementTextData()->current_text = NULL;
				}
				//	See if we've got anything.
				int32 lLength = m_pWidget->GetWindowTextLength();
				if(lLength > 0)	{
					GetElementTextData()->current_text = (XP_Block)XP_ALLOC(lLength + 1);
					if(GetElementTextData()->current_text)	{
						m_pWidget->GetWindowText((char *)GetElementTextData()->current_text, CASTINT(lLength + 1));
					}
				}
			}
		}
		else if(GetContext()->IsPureDCContext())	{
			//	Printing/metafile
            //  If there is no current data, attempt to use the default data if present.
            if(GetElementTextData())    {
                if(GetElementTextData()->default_text) {
                    char *pText = (char *)GetElementTextData()->default_text;
                    if(GetElementTextData()->current_text == NULL)  {
                        int32 lSize = XP_STRLEN(pText) + 1;
                        GetElementTextData()->current_text = (XP_Block)XP_ALLOC(lSize);
                        memcpy(GetElementTextData()->current_text, pText, CASTSIZE_T(lSize));
                    }
                }
            }
		}
	}
}

HWND CFormText::GetRaw()
{
    return(m_pWidget ? m_pWidget->m_hWnd : NULL);
}


LRESULT CALLBACK EXPORT CFormText::TempWndProc(HWND hWnd, UINT message,
		WPARAM wParam, LPARAM lParam) {

	LRESULT ret;
	CFormText *thisObj = (CFormText *)GetProp(hWnd, gWndProcProperty);

	ASSERT(thisObj);
	if (!thisObj)
		return 0;

	/* Standard processing is fine, except for mouseactivate messages.  We explicitly
	   activate the window if the standard windproc seems to think we should.  A
	   doubleclick in an inactive window running under NT 4.0 will otherwise cause
	   the window to lose focus (and textedit fields in forms seem to be inactive
	   unless we take this step.)
	*/
	ret = CallWindowProc(thisObj->mRealWndProc, hWnd, message, wParam, lParam);
	if (message == WM_MOUSEACTIVATE && (ret == MA_ACTIVATE || ret == MA_ACTIVATEANDEAT))
			CallWindowProc(thisObj->mRealWndProc, hWnd, WM_ACTIVATE, WA_ACTIVE, NULL);
	return ret;
}
