/* $Id: forms.cpp,v 1.2 1998-10-20 02:40:45 cls%seawood.org Exp $
 *
 * 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.  Portions
 * created by Warwick Allison, Kalle Dalheimer, Eirik Eng, Matthias
 * Ettrich, Arnt Gulbrandsen, Haavard Nord and Paul Olav Tvete are
 * Copyright (C) 1998 Warwick Allison, Kalle Dalheimer, Eirik Eng,
 * Matthias Ettrich, Arnt Gulbrandsen, Haavard Nord and Paul Olav
 * Tvete.  All Rights Reserved.
 */

#include <qlined.h>
#include <qmlined.h>
#include <qradiobt.h>
#include <qpushbt.h>
#include <qcombo.h>
#include <qchkbox.h>
#include <qlistbox.h>
#include <qlabel.h>
#include <qlistview.h>
#include <qkeycode.h>
#include <qdir.h>
#if defined(XP_UNIX)
#include <xpform.h>
#endif
#include <libevent.h>
#include "dirview.h"
#include "QtBrowserContext.h"


#if defined(XP_WIN)
/*
  Since xpform isn't part of the Windows distribution of Mozilla,
  we include the necessary sources here.

  Then we just don't use them, but leave them here for reference.
*/
#include "xpassert.h"

LO_FormElementData*
XP_GetFormElementData(LO_FormElementStruct *form)
{
  return form->element_data;
}

LO_TextAttr*
XP_GetFormTextAttr(LO_FormElementStruct *form)
{
  return form->text_attr;
}

uint16
XP_GetFormEleAttrmask(LO_FormElementStruct *form)
{
  return form->ele_attrmask;
}

int32
XP_FormGetType(LO_FormElementData *form)
{
  return form->type;
}

void
XP_FormSetFEData(LO_FormElementData *form,
				 void* fe_data)
{
  form->ele_minimal.FE_Data = fe_data;
}


void*
XP_FormGetFEData(LO_FormElementData *form)
{
  return form->ele_minimal.FE_Data;
}

/* Methods that work for Hidden, Submit, Reset, Button, and Readonly
   form elements. */
PA_Block
XP_FormGetValue(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_HIDDEN
			|| form->type == FORM_TYPE_SUBMIT
			|| form->type == FORM_TYPE_RESET
			|| form->type == FORM_TYPE_BUTTON
			|| form->type == FORM_TYPE_READONLY);

  return form->ele_minimal.value;
}

Bool
XP_FormGetDisabled(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_SELECT_ONE
			|| form->type == FORM_TYPE_SELECT_MULT
			|| form->type == FORM_TYPE_TEXT
			|| form->type == FORM_TYPE_TEXTAREA
			|| form->type == FORM_TYPE_RADIO
			|| form->type == FORM_TYPE_CHECKBOX
			|| form->type == FORM_TYPE_SUBMIT
			|| form->type == FORM_TYPE_RESET
			|| form->type == FORM_TYPE_BUTTON
			|| form->type == FORM_TYPE_READONLY);

  if (form->type == FORM_TYPE_SELECT_ONE
			|| form->type == FORM_TYPE_SELECT_MULT
			|| form->type == FORM_TYPE_TEXT
			|| form->type == FORM_TYPE_TEXTAREA
			|| form->type == FORM_TYPE_RADIO
			|| form->type == FORM_TYPE_CHECKBOX
			|| form->type == FORM_TYPE_SUBMIT
			|| form->type == FORM_TYPE_RESET
			|| form->type == FORM_TYPE_BUTTON
			|| form->type == FORM_TYPE_READONLY)
    return form->ele_minimal.disabled;
  else
    return FALSE;
}

/* Methods that work on text or text area form elements */
PA_Block
XP_FormGetDefaultText(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_TEXT
			|| form->type == FORM_TYPE_TEXTAREA
			|| form->type == FORM_TYPE_PASSWORD
			|| form->type == FORM_TYPE_READONLY
			|| form->type == FORM_TYPE_FILE);

  switch(form->type)
	{
	case FORM_TYPE_TEXT:
	case FORM_TYPE_PASSWORD:
	case FORM_TYPE_FILE:
	case FORM_TYPE_READONLY:
	  return form->ele_text.default_text;
	case FORM_TYPE_TEXTAREA:
	  return form->ele_textarea.default_text;
	default:
	  return NULL;
	}
}

PA_Block
XP_FormGetCurrentText(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_TEXT
			|| form->type == FORM_TYPE_TEXTAREA
			|| form->type == FORM_TYPE_PASSWORD
			|| form->type == FORM_TYPE_READONLY
			|| form->type == FORM_TYPE_FILE);

  switch(form->type)
	{
	case FORM_TYPE_TEXT:
	case FORM_TYPE_PASSWORD:
	case FORM_TYPE_FILE:
	case FORM_TYPE_READONLY:
	  return form->ele_text.current_text;
	case FORM_TYPE_TEXTAREA:
	  return form->ele_textarea.current_text;
	default:
	  return NULL;
	}
}

void
XP_FormSetCurrentText(LO_FormElementData *form,
					  PA_Block current_text)
{
  XP_ASSERT(form->type == FORM_TYPE_TEXT
			|| form->type == FORM_TYPE_TEXTAREA
			|| form->type == FORM_TYPE_PASSWORD
			|| form->type == FORM_TYPE_READONLY
			|| form->type == FORM_TYPE_FILE);

  switch(form->type)
	{
	case FORM_TYPE_TEXT:
	case FORM_TYPE_PASSWORD:
	case FORM_TYPE_FILE:
	case FORM_TYPE_READONLY:
	  form->ele_text.current_text = current_text;
	  break;
	case FORM_TYPE_TEXTAREA:
	  form->ele_textarea.current_text = current_text;
	  break;
	}
}

Bool
XP_FormGetReadonly(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_TEXT
			|| form->type == FORM_TYPE_TEXTAREA
			|| form->type == FORM_TYPE_PASSWORD
			|| form->type == FORM_TYPE_READONLY);

  switch(form->type)
	{
	case FORM_TYPE_TEXT:
	case FORM_TYPE_PASSWORD:
	case FORM_TYPE_READONLY:
	  return form->ele_text.readonly;
	case FORM_TYPE_TEXTAREA:
	  return form->ele_textarea.readonly;
	default:
	  return FALSE;
	}
}

/* text specific methods. */
int32
XP_FormTextGetSize(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_TEXT
			|| form->type == FORM_TYPE_PASSWORD
			|| form->type == FORM_TYPE_READONLY
			|| form->type == FORM_TYPE_FILE);

  return form->ele_text.size;
}

int32
XP_FormTextGetMaxSize(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_TEXT
			|| form->type == FORM_TYPE_PASSWORD
			|| form->type == FORM_TYPE_READONLY
			|| form->type == FORM_TYPE_FILE);

  return form->ele_text.max_size;
}

/* text area specific methods. */
void
XP_FormTextAreaGetDimensions(LO_FormElementData *form,
							 int32 *rows,
							 int32 *cols)
{
  XP_ASSERT(form->type == FORM_TYPE_TEXTAREA);

  if (rows)
	*rows = form->ele_textarea.rows;
  if (cols)
	*cols = form->ele_textarea.cols;
}

uint8
XP_FormTextAreaGetAutowrap(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_TEXTAREA);

  return form->ele_textarea.auto_wrap;
}

/* radio/checkbox specific methods */
Bool
XP_FormGetDefaultToggled(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_RADIO
			|| form->type == FORM_TYPE_CHECKBOX);

  return form->ele_toggle.default_toggle;
}

Bool
XP_FormGetElementToggled(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_RADIO
			|| form->type == FORM_TYPE_CHECKBOX);

  return form->ele_toggle.toggled;
}

void
XP_FormSetElementToggled(LO_FormElementData *form,
						 Bool toggled)
{
  XP_ASSERT(form->type == FORM_TYPE_RADIO
			|| form->type == FORM_TYPE_CHECKBOX);

  form->ele_toggle.toggled = toggled;
}

/* select specific methods */
int32
XP_FormSelectGetSize(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_SELECT_ONE
			|| form->type == FORM_TYPE_SELECT_MULT);

  return form->ele_select.size;
}

Bool
XP_FormSelectGetMultiple(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_SELECT_ONE
			|| form->type == FORM_TYPE_SELECT_MULT);

  return form->ele_select.multiple;
}

Bool
XP_FormSelectGetOptionsValid(LO_FormElementData *form) /* XXX ? */
{
  XP_ASSERT(form->type == FORM_TYPE_SELECT_ONE
			|| form->type == FORM_TYPE_SELECT_MULT);

  return form->ele_select.options_valid;
}

int32
XP_FormSelectGetOptionsCount(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_SELECT_ONE
			|| form->type == FORM_TYPE_SELECT_MULT);

  return form->ele_select.option_cnt;
}

lo_FormElementOptionData*
XP_FormSelectGetOption(LO_FormElementData *form,
					   int option_index)
{
  XP_ASSERT((form->type == FORM_TYPE_SELECT_ONE
			 || form->type == FORM_TYPE_SELECT_MULT)
			&& option_index >= 0
			&& option_index < form->ele_select.option_cnt);

  return &((lo_FormElementOptionData*)form->ele_select.options)[option_index];
}

/* option specific methods */
Bool
XP_FormOptionGetDefaultSelected(lo_FormElementOptionData *option_element)
{
  return option_element->def_selected;
}

Bool
XP_FormOptionGetSelected(lo_FormElementOptionData *option_element)
{
  return option_element->selected;
}

void
XP_FormOptionSetSelected(lo_FormElementOptionData *option_element,
						 Bool selected)
{
  option_element->selected = selected;
}

/* object */
LO_JavaAppStruct*
XP_FormObjectGetJavaApp(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_OBJECT);

  return form->ele_object.object;
}

void
XP_FormObjectSetJavaApp(LO_FormElementData *form,
						LO_JavaAppStruct *java_app)
{
  XP_ASSERT(form->type == FORM_TYPE_OBJECT);

  form->ele_object.object = java_app;
}

/* keygen */
PA_Block
XP_FormKeygenGetChallenge(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_KEYGEN);

  return form->ele_keygen.challenge;
}

PA_Block
XP_FormKeygenGetKeytype(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_KEYGEN);

  return form->ele_keygen.key_type;
}

PA_Block
XP_FormKeygenGetPQG(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_KEYGEN);

  return form->ele_keygen.pqg;
}

char*
XP_FormKeygenGetValueStr(LO_FormElementData *form)
{
  XP_ASSERT(form->type == FORM_TYPE_KEYGEN);

  return form->ele_keygen.value_str;
}

/* setter routines? */
#endif // XP_WIN



class QtFormElement : QObject {
    LO_FormElementStruct *form;
    QtContext* context;
protected:
    LO_FormElementData* elementData() const { return form->element_data; }

public:
    QtFormElement( LO_FormElementStruct *form_struct, QtContext* cx ) :
	form(form_struct),
	context(cx)
    {
    }
    void set(LO_FormElementStruct *form_struct, QtContext* cx)
    {
	form = form_struct;
        context = cx;
    }

    void getSize()
    {
	int bw = borderWidth();
	int w = width();
	int h = height();
	form->width = w + bw*2;
	form->height = h + bw*2;
	form->baseline = baseLine() + bw;
	widget()->resize(w,h);
    }

    virtual int borderWidth() const
    { return 5; }
    virtual int width() const
    { return widget()->sizeHint().width(); }
    virtual int height() const
    { return widget()->sizeHint().height(); }
    int baseLine() const
    {
	QFontMetrics fm = widget()->fontMetrics();
	return height() - fm.descent() - baseLineAdjust();
    }
    virtual int baseLineAdjust() const
    {
	return 6; // magic look-good value
    }

    virtual void freeStorage()
    {
    }

    virtual void getValue()
    {
    }

    virtual void reset()
    {
    }

    virtual void setToggle( bool ) { }

    virtual bool submitOnClick() const { return FALSE; }
    virtual bool submitOnEnter() const { return FALSE; }

    virtual QWidget* widget() const
    {
	return 0;
    }

    // Stuff for submission
    void isSubmit()
    {
	widget()->installEventFilter(this);
    }

protected:
    int type() const
    {
	return elementData()->type;
    }

    lo_FormElementObjectData* object() const
        { return (lo_FormElementObjectData*)elementData(); }
    lo_FormElementKeygenData* keygen() const
        { return (lo_FormElementKeygenData*)elementData(); }

private:
    bool eventFilter( QObject *, QEvent* );
};


class QtFormToggleElement : public QtFormElement {
protected:
    lo_FormElementToggleData* toggle() const
        { return (lo_FormElementToggleData*)elementData(); }
public:
    QtFormToggleElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormElement(form, cx)
    {
    }
};

class QtFormRadioElement : public QtFormToggleElement {
    QRadioButton *w;
public:
    QtFormRadioElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormToggleElement(form, cx)
    {
	w = new QRadioButton(cx->contentWidget());
    }

    virtual QWidget* widget() const
    {
	return w;
    }

    virtual void getValue()
    {
	toggle()->toggled = w->isChecked();
    }

    virtual void setToggle( bool y )
    {
	w->setChecked( y );
    }

    virtual int baseLineAdjust() const
    {
	return 3; // magic look-good value
    }
};

class QtFormCheckBoxElement : public QtFormToggleElement {
    QCheckBox *w;
public:
    QtFormCheckBoxElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormToggleElement(form, cx)
    {
	w = new QCheckBox(cx->contentWidget());
    }

    virtual QWidget* widget() const
    {
	return w;
    }

    virtual void getValue()
    {
	toggle()->toggled = w->isChecked();
    }

    virtual void setToggle( bool y )
    {
	w->setChecked( y );
    }

    virtual int baseLineAdjust() const
    {
	return 3; // magic look-good value
    }
};

class QtFormTextlikeElement : public QtFormElement {
protected:
    lo_FormElementTextData* text() const
        { return (lo_FormElementTextData*)elementData(); }

    QtFormTextlikeElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormElement(form, cx)
    {
    }

    virtual const char* currentText()
    {
	return "";
    }

    virtual void getValue()
    {
	const char* t = currentText();
	text()->current_text = PA_Block(qstrdup(t));
	if (text()->max_size > 0 && (int)strlen(t) >= text()->max_size )
	    text()->current_text[text()->max_size] = 0;
    }
};

class QtFormButtonElement : public QtFormElement {
    QPushButton *w;

    lo_FormElementMinimalData_struct* minimal() const
        { return (lo_FormElementMinimalData_struct*)elementData(); }
public:
    QtFormButtonElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormElement(form, cx)
    {
	w = new QPushButton(cx->contentWidget());
	w->setText( (char*)minimal()->value );
    }

    virtual QWidget* widget() const
    {
	return w;
    }

    virtual bool submitOnClick() const { return TRUE; }
};

class QtFormSubmitElement : public QtFormButtonElement {
public:
    QtFormSubmitElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormButtonElement(form, cx)
    {
	isSubmit();
    }
    virtual bool submitOnClick() const { return TRUE; }
};

class QtFormResetElement : public QtFormButtonElement {
public:
    QtFormResetElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormButtonElement(form, cx)
    {
    }
};

class QtJotter : public QFrame {
    QPixmap pm;
    QPoint ppos;
public:
    QtJotter( QWidget* parent ) :
	QFrame(parent)
    {
	setFrameStyle( Panel | Sunken );
    }
    void resizeEvent(QResizeEvent*)
    {
	int fw = frameWidth();
	QPixmap newpm(width()-fw*2, height()-fw*2);
	if ( !pm.isNull() )
	    bitBlt(&newpm, 0, 0, &pm);
	pm = newpm;
    }
    void paintEvent(QPaintEvent*)
    {
	int fw = frameWidth();
	bitBlt( this, fw, fw, &pm );
    }
    void mousePressEvent(QMouseEvent* e)
    {
	ppos = e->pos();
    }
    void mouseMoveEvent(QMouseEvent* e)
    {
	QPoint off(frameWidth(),frameWidth());
	QPainter p1(this);
	p1.drawLine(ppos,e->pos());
	QPainter p2(&pm);
	p2.drawLine(ppos-off,e->pos()-off);
	ppos = e->pos();
    }
};

class QtFormJotElement : public QtFormElement {
    QtJotter *w;
public:
    QtFormJotElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormElement(form, cx)
    {
	w = new QtJotter(cx->contentWidget());
    }

    virtual QWidget* widget() const
    {
	return w;
    }
};

class QtFormSelectElement : public QtFormElement {
protected:
    lo_FormElementSelectData* select() const
        { return (lo_FormElementSelectData*)elementData(); }
    lo_FormElementOptionData& option(int i) const
	{ return ((lo_FormElementOptionData*)(select()->options))[i]; }

    QtFormSelectElement(LO_FormElementStruct *form, QtContext* cx) :
        QtFormElement(form, cx)
    {
    }
};

class QtFormSelectOneElement : public QtFormSelectElement {
    QComboBox *w;
public:
    QtFormSelectOneElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormSelectElement(form, cx)
    {
	w = new QComboBox(cx->contentWidget());
	int nitems = select()->option_cnt;
	int vlines = select()->size;
	if ( vlines <= 0 ) vlines = nitems;
	for (int i=0; i < nitems; i++)
	    w->insertItem((char*)option(i).text_value);
    }

    virtual QWidget* widget() const
    {
	return w;
    }

    virtual void getValue()
    {
	for (int i=0; i < select()->option_cnt; i++)
	    option(i).selected = (w->currentItem() == i);
    }

    virtual void reset()
    {
	for (int i=0; i < select()->option_cnt; i++)
	    if ( option(i).def_selected ) {
		w->setCurrentItem(i);
		break;
	    }
    }
};

class QtFormSelectMultElement : public QtFormSelectElement {
    QListBox *w;
public:
    QtFormSelectMultElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormSelectElement(form, cx)
    {
	w = new QListBox(cx->contentWidget());
	w->setMultiSelection( select()->multiple );
	int nitems = select()->option_cnt;
	int vlines = select()->size;
	if ( vlines <= 0 ) vlines = nitems;
	for (int i=0; i < nitems; i++)
	    w->insertItem((char*)option(i).text_value);
	w->setFixedVisibleLines( vlines );
    }

    virtual QWidget* widget() const
    {
	return w;
    }

    virtual void getValue()
    {
	for (int i=0; i < select()->option_cnt; i++)
	    option(i).selected = w->isSelected(i);
    }

    virtual void reset()
    {
	for (int i=0; i < select()->option_cnt; i++)
	    w->setSelected(i, option(i).def_selected);
    }
};

class QtFormTextAreaElement : public QtFormElement {
    QMultiLineEdit *w;
    lo_FormElementTextareaData* textarea() const
        { return (lo_FormElementTextareaData*)elementData(); }
public:
    QtFormTextAreaElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormElement(form, cx)
    {
	w = new QMultiLineEdit(cx->contentWidget());
    }

    virtual QWidget* widget() const
    {
	return w;
    }

    virtual void getValue()
    {
	textarea()->current_text = PA_Block(qstrdup(w->text()));
    }

    virtual void freeStorage()
    {
	if ( textarea()->current_text != textarea()->default_text ) {
	    delete textarea()->current_text;
	    textarea()->current_text = 0;
	}
    }

    virtual void reset()
    {
	freeStorage();
	w->setText( (char*)textarea()->default_text );
	textarea()->current_text = textarea()->default_text;
    }

    virtual int width() const
    {
	QFontMetrics fm = w->fontMetrics();
	int c=textarea()->cols; if (!c) c=20;
	return c * fm.width("X");
    }

    virtual int height() const
    {
	w->setFixedVisibleLines(textarea()->rows);
	return w->height();
    }

    virtual int baseLineAdjust() const
    {
	return 5; // magic look-good value
    }
};

class QtFormPasswordElement : public QtFormTextlikeElement {
protected:
    QLineEdit *w;
public:
    QtFormPasswordElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormTextlikeElement(form, cx)
    {
	w = new QLineEdit(cx->contentWidget());
	w->setEchoMode(QLineEdit::Password);
	if ( text()->max_size > 0 )
	    w->setMaxLength(text()->max_size);
    }

    virtual QWidget* widget() const
    {
	return w;
    }

    virtual const char* currentText()
    {
	return w->text();
    }

    virtual void freeStorage()
    {
	if ( text()->current_text != text()->default_text ) {
	    delete text()->current_text;
	    text()->current_text = 0;
	}
    }

    virtual int width() const
    {
	QFontMetrics fm = w->fontMetrics();
	int c=text()->size; if (!c) c=20;
	return c * fm.width("X")
	    + (w->frame() ? 2 : 0)*2 + 3 // magic from qlineedit.cpp
	    - fm.minLeftBearing()
	    - fm.minRightBearing();
    }

    virtual void reset()
    {
	w->setText( (char*)text()->default_text );
    }


    virtual int baseLineAdjust() const
    {
	return 5; // magic look-good value
    }

    virtual bool submitOnEnter() const { return TRUE; }
};

class QtFormTextElement : public QtFormPasswordElement {
public:
    QtFormTextElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormPasswordElement(form, cx)
    {
	w->setEchoMode(QLineEdit::Normal);
    }
};

class QtFormHiddenElement : public QtFormTextElement {
public:
    QtFormHiddenElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormTextElement(form, cx)
    {
	warning("Hidden should not be created by BE");
    }
};

class QtFormIsIndexElement : public QtFormTextElement {
public:
    QtFormIsIndexElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormTextElement(form, cx)
    {
	warning("IsIndex should not be created by BE");
    }
};

class QtFormImageElement : public QtFormElement {
    QLabel *w;
public:
    QtFormImageElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormElement(form, cx)
    {
	warning("Image should not be created by BE");
    }

    virtual QWidget* widget() const
    {
	return w;
    }

    virtual bool submitOnClick() const { return TRUE; }
};

class QtFormFileElement : public QtFormTextlikeElement {

    // This is really just showing-off.  We could just use a boring
    // lineedit + browse button like other browsers usually do.

    QListView *w;
    QString current;
public:
    QtFormFileElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormTextlikeElement(form, cx)
    {
	// Just to show off, we use the Directory example program.
	w = new QListView(cx->contentWidget());
	w->addColumn( "Name" );
	w->addColumn( "Type" );
	Directory * root = new Directory( w );
	root->setOpen( TRUE );
    }

    virtual QWidget* widget() const
    {
	return w;
    }

    virtual const char* currentText()
    {
	// This is a bit ugly...
	QListViewItem * c = w->currentItem();
	current = "";
	if (c) {
	    // We know it's parent is a Directory, we put it there.
	    Directory* d = (Directory*)c->parent();
	    if (d)
		current = d->fullName() + c->text(0);
	}
//debug("FILE: \"%s\"",(const char*)current);

	return QDir::convertSeparators(current);
    }

    virtual int width() const { return 400; }
    virtual int height() const { return 300; }
};

class QtFormKeyGenElement : public QtFormElement {
    QLabel *w;
public:
    QtFormKeyGenElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormElement(form, cx)
    {
	warning("Keygen should not be created by BE");
    }

    virtual QWidget* widget() const
    {
	return w;
    }
};

class QtFormReadOnlyElement : public QtFormTextElement {
public:
    QtFormReadOnlyElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormTextElement(form, cx)
    {
	debug("What is a READONLY?");
    }
};

class QtFormObjectElement : public QtFormElement {
    QLabel *w;
public:
    QtFormObjectElement(LO_FormElementStruct *form, QtContext* cx) :
	QtFormElement(form, cx)
    {
	warning("Object should not be created by BE");
    }

    virtual QWidget* widget() const
    {
	return w;
    }
};


static
QtFormElement* element(LO_FormElementStruct *form)
{
    LO_FormElementData *form_data = form->element_data;
    if (!form_data) return 0;
    return (QtFormElement*)form_data->ele_minimal.FE_Data;
}


/* From ./forms.c: */
void QtContext::getFormElementInfo(LO_FormElementStruct *form)
{
    //    printf( "QTFE_GetFormElementInfo %p \n", form );

    LO_FormElementData *form_data = form->element_data;
    if ( !form_data ) return;

    QtFormElement *fel = (QtFormElement*)form_data->ele_minimal.FE_Data;
    if ( !fel ) {
	switch ( form_data->type ) {
	  case FORM_TYPE_NONE:
	    return;
	    break;
	  case FORM_TYPE_TEXT:
	    fel = new QtFormTextElement( form, this );
	    break;
	  case FORM_TYPE_RADIO:
	    fel = new QtFormRadioElement( form, this );
	    break;
	  case FORM_TYPE_CHECKBOX:
	    fel = new QtFormCheckBoxElement( form, this );
	    break;
	  case FORM_TYPE_HIDDEN:
	    fel = new QtFormHiddenElement( form, this );
	    break;
	  case FORM_TYPE_SUBMIT:
	    fel = new QtFormSubmitElement( form, this );
	    break;
	  case FORM_TYPE_RESET:
	    fel = new QtFormResetElement( form, this );
	    break;
	  case FORM_TYPE_PASSWORD:
	    fel = new QtFormPasswordElement( form, this );
	    break;
	  case FORM_TYPE_BUTTON:
	    fel = new QtFormButtonElement( form, this );
	    break;
	  case FORM_TYPE_JOT:
	    fel = new QtFormJotElement( form, this );
	    break;
	  case FORM_TYPE_SELECT_ONE:
	    fel = new QtFormSelectOneElement( form, this );
	    break;
	  case FORM_TYPE_SELECT_MULT:
	    fel = new QtFormSelectMultElement( form, this );
	    break;
	  case FORM_TYPE_TEXTAREA:
	    fel = new QtFormTextAreaElement( form, this );
	    break;
	  case FORM_TYPE_ISINDEX:
	    fel = new QtFormIsIndexElement( form, this );
	    break;
	  case FORM_TYPE_IMAGE:
	    fel = new QtFormImageElement( form, this );
	    break;
	  case FORM_TYPE_FILE:
	    fel = new QtFormFileElement( form, this );
	    break;
	  case FORM_TYPE_KEYGEN:
	    fel = new QtFormKeyGenElement( form, this );
	    break;
	  case FORM_TYPE_READONLY:
	    fel = new QtFormReadOnlyElement( form, this );
	    break;
	  case FORM_TYPE_OBJECT:
	    fel = new QtFormObjectElement( form, this );
	    break;
	}
	form_data->ele_minimal.FE_Data = fel;
    } else {
	fel->set(form, this);
    }

    LO_TextAttr *a = form->text_attr;
    if (a) {
	QWidget* w=fel->widget();
	w->setFont(getFont(a));
	QColor fg(a->fg.red, a->fg.green, a->fg.blue);
	QColor bg(a->bg.red, a->bg.green, a->bg.blue);

	// Qt doesn't currently work well with 100% white or black
	// backgrounds, but they are common on the web, so we adjust.
	//
	QColor topsh = bg.light();
	QColor botsh = bg.dark();
	int h,s,v;
	bg.hsv(&h,&s,&v);
	if ( v > 200 ) {
	    topsh.setHsv(h,s,200);
	} else if ( v < 90 ) {
	    topsh.setHsv(h,s,90);
	    botsh.setHsv(h,s,60);
	}

        QColorGroup g(fg,
		    bg,
		    topsh,
		    botsh,
		    bg,
		    fg,
		    contentWidget()->colorGroup().base());

	w->setPalette(QPalette(g,g,g));
    }
    fel->reset();
    fel->getSize();
}

/* From ./forms.c: */
void QtContext::getFormElementValue(LO_FormElementStruct *, bool, bool)
{
}

/* XXX - wtf does submit_p do? */
void QtBrowserContext::getFormElementValue(LO_FormElementStruct *form,
						 bool delete_p, bool submit_p)
{
    element(form)->getValue();
    if ( delete_p ) {
	// How MUCH do we delete of it?
	showSubWidget( element(form)->widget(), FALSE );
    }
}

/* From ./forms.c: */
void QtContext::resetFormElement(LO_FormElementStruct *form)
{
    element(form)->reset();
}

/* From ./forms.c: */
void QtContext::setFormElementToggle(LO_FormElementStruct *form,
			  bool state)
{
    element(form)->setToggle(state);
}

/* From ./forms.c: */
void QtContext::formTextIsSubmit(LO_FormElementStruct *form)
{
    element(form)->isSubmit();
}

/* From ./forms.c: */
void QtContext::displayFormElement(int iLocation,
					   LO_FormElementStruct *form)
{
    debug( "QTFE_DisplayFormElement %d, %p", iLocation, form);
}

/* From ./forms.c: */
void QtBrowserContext::displayFormElement(int /*iLocation*/,
					   LO_FormElementStruct *form)
{
    // The back-end calls this too early some times (eg. when laying
    // out tables).

    QtFormElement *e = element(form);
    if ( e && form->ele_attrmask & LO_ELE_INVISIBLE ) {
	QWidget * w = e->widget();
	// We are already taking document[XY]Offset() into account.
	// Mozilla puts all the logic for dealing with the window-system
	// co-ordinate range limitations of the window system.
	int x = form->x + form->x_offset + e->borderWidth() + getXOrigin();
	int y = form->y + form->y_offset + e->borderWidth() + getYOrigin();
	w->resize( form->width-e->borderWidth()*2,
		   form->height-e->borderWidth()*2 );
	moveSubWidget( w, x, y );
    }
}

/* From ./forms.c: */
void QtContext::freeFormElement(LO_FormElementData *form_data)
{
    // take it out and delete it - easier than loads of destructors.
    QtFormElement *e = (QtFormElement*)form_data->ele_minimal.FE_Data;
    if ( e ) {
	QWidget * w = e->widget();
	delete e;
	delete w;
    }
    form_data->ele_minimal.FE_Data = 0;
}



void
submitForm(MWContext *context, LO_Element *element,
                              int32 /*event*/, void *closure,
                              ETEventStatus status)
{
    QtContext *cx = QtContext::qt(context);
    if (status != EVENT_OK) return;

    LO_FormSubmitData *data;
    if ( element->type == LO_IMAGE && closure ) {
	QPoint *p = (QPoint*)closure;
	data = LO_SubmitImageForm (context, &element->lo_image, p->x(), p->y() );
    } else {
	data = LO_SubmitForm (context, (LO_FormElementStruct *) element);
    }
    if (!data) return;

    URL_Struct *url = NET_CreateURLStruct ((char *) data->action, NET_DONT_RELOAD);

    NET_AddLOSubmitDataToURLStruct(data, url);

    // Add the referer to the URL.
    History_entry *he = SHIST_GetCurrent (&context->hist);
    if (url->referer)
	free (url->referer);
    url->referer = he->address ? XP_STRDUP (he->address) : 0;

    cx->getURL(url);
    LO_FreeSubmitData (data);
}

static
void
send_submit(MWContext *context, LO_Element *element,
                              int32 /*event*/, void *closure,
                              ETEventStatus /*status*/)
{
    JSEvent *js_event = XP_NEW_ZAP(JSEvent);
    js_event->type = EVENT_SUBMIT;
    ET_SendEvent(context, element, js_event, submitForm, closure);
}



bool QtFormElement::eventFilter( QObject *o, QEvent *e )
{
    // ### This code is a bit too eager!
    ASSERT(o==widget());
    JSEvent *js_event = XP_NEW_ZAP(JSEvent);
    bool send = FALSE;
    switch (e->type()) {
    case Event_MouseButtonPress:
    case Event_MouseButtonRelease:
      {
	QMouseEvent *me = (QMouseEvent*)e;
	if (me->button() == LeftButton) js_event->which = 1;
	else if (me->button() == MidButton) js_event->which = 2;
	else if (me->button() == RightButton) js_event->which = 3;
	send = submitOnClick();
      } break;
    case Event_MouseMove:
	break;
    case Event_KeyPress:
    case Event_KeyRelease:
      {
	QKeyEvent* ke = (QKeyEvent*)e;
	js_event->which = ke->ascii();
	send = submitOnEnter() && ( ke->key() == Key_Return || ke->key() == Key_Enter );
      } break;
    default:
	break;
    }
    if ( send ) {
	// Mozilla says: "send me a click, dammit!"
	js_event->type = EVENT_CLICK;
	ET_SendEvent(context->mwContext(), (LO_Element *)form, js_event,
		      send_submit, 0);
    }
    return FALSE;
}
