/*
 * Electric(tm) VLSI Design Systems
 *
 * File: graphqtdlg.cpp
 * Dialogs implementation on Qt
 * Written by: Dmitry Nadezhin, Instutute for Design Problems in Microelectronics, Russian Academy of Sciences
 *
 * Copyright (c) 2001 Static Free Software.
 *
 * Electric(tm) is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Electric(tm) is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Electric(tm); see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, Mass 02111-1307, USA.
 *
 * Static Free Software
 * 4119 Alpine Road
 * Portola Valley, California 94028
 * info@staticfreesoft.com
 */

#include "graphqtdlg.h"
#include "usr.h"

#include <qapplication.h>
#include <qbitmap.h>
#include <qclipboard.h>
#include <qcombobox.h>
#include <qcursor.h>
#include <qlineedit.h>
#include <qpainter.h>
#include <qprogressbar.h>
#include <qpushbutton.h>
#include <qradiobutton.h>
#include <qsignalmapper.h>
#include <qtimer.h>
#include <qwmatrix.h>

#define GTRACE          0       /* etrace flag of usual graphics trace */

/****** the dialogs ******/

#define MAXSCROLLMULTISELECT 1000

#define MAXDIALOGS            3   /* maximum nested dialogs */
#define DIALOGNUM            16
#define DIALOGDEN            12
#define MAXICONS             20

/* maximal time (in ms) to wait for next event */
#define MAXEVENTTIME         100

static EDialog	       *gra_dialogs[MAXDIALOGS];
static EDialog	       *gra_curdialog;
static INTBIG		gra_curdialogindex = -1;
static INTBIG		gra_dialoghit;
static INTBIG		gra_dialoghitchar;


static INTBIG gra_dialogstringpos;
static int gra_stringposascending(const void *e1, const void *e2);



/****************************** QT DIALOGS ******************************/

/*
 * Routine to initialize a dialog described by "dialog".
 * Returns true if dialog cannot be initialized.
 */
BOOLEAN DiaInitDialog(DIALOG *dialog)
{
	QWidget *base;

#ifdef ETRACE
	etrace(GTRACE, "{ DiaInitDialog: %s\n",
	       dialog->movable != NULL ? dialog->movable : "");
#endif
	/* get the current dialog structure */
#if QT_VERSION >= 300
	if (gra_curdialogindex >= 0) base = gra_curdialog; else
#endif
	if (el_curwindowpart == NOWINDOWPART) base = qApp->mainWidget(); else
		base = (QWidget*)el_curwindowpart->frame->qt;

	gra_curdialog = new EDialog( base, dialog );
	gra_curdialogindex++;
	gra_dialogs[gra_curdialogindex] = gra_curdialog;
	gra_curdialog->showMe();
#ifdef ETRACE
	etrace(GTRACE, "} DiaInitDialog\n");
#endif
	return(FALSE);
}

/*
 * Routine to handle actions and return the next item hit.
 */
INTBIG DiaNextHit(void)
{
	return(gra_curdialog->nextHit());
}

/*
 * Routine to parse the next input event and return the next character typed.
 * If the routine returns -1, nothing has happened.  If the
 * routine returns -2, an item has been hit (and is in "itemHit").
 */
INTSML DiaGetNextCharacter(INTBIG *itemHit)
{
	return(gra_curdialog->getNextCharacter(itemHit));
}

void DiaDoneDialog(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{ DiaDoneDialog\n");
#endif
	delete gra_curdialog;
	gra_curdialogindex--;
	if (gra_curdialogindex >= 0) {
	    gra_curdialog = gra_dialogs[gra_curdialogindex];
	} else {
	    gra_curdialog = 0;
	}
#ifdef ETRACE
	etrace(GTRACE, "} DiaDoneDialog\n");
#endif
}

/*
 * Routine to set the text in item "item" to "msg"
 */
void DiaSetText(INTBIG item, char *msg)
{
	gra_curdialog->setText(item, msg);
}

/*
 * Routine to return the text in item "item"
 */
char *DiaGetText(INTBIG item)
{
	return(gra_curdialog->getText(item));
}

/*
 * Routine to set the value in item "item" to "value"
 */
void DiaSetControl(INTBIG item, INTBIG value)
{
	gra_curdialog->setControl(item, value);
}

/*
 * Routine to return the value in item "item"
 */
INTBIG DiaGetControl(INTBIG item)
{
	return(gra_curdialog->getControl(item));
}

/*
 * Routine to check item "item" to make sure that there is
 * text in it.  If so, it returns true.  Otherwise it beeps and returns false.
 */
BOOLEAN DiaValidEntry(INTBIG item)
{
	return(gra_curdialog->validEntry(item));
}

/*
 * Routine to dim item "item"
 */
void DiaDimItem(INTBIG item)
{
	gra_curdialog->dimItem(item);
}

/*
 * Routine to un-dim item "item"
 */
void DiaUnDimItem(INTBIG item)
{
	gra_curdialog->unDimItem(item);
}

/*
 * Routine to change item "item" to be a message rather
 * than editable text
 */
void DiaNoEditControl(INTBIG item)
{
	gra_curdialog->noEditControl(item);
}

/*
 * Routine to change item "item" to be editable text rather
 * than a message
 */
void DiaEditControl(INTBIG item)
{
	gra_curdialog->editControl(item);
}

void DiaOpaqueEdit(INTBIG item)
{
	gra_curdialog->opaqueEdit(item);
}

/*
 * Routine to cause item "item" to report character hits
 */
void DiaCharacterEdit(INTBIG item)
{
	gra_curdialog->characterEdit(item);
}

/*
 * Routine to cause item "item" to be the default button
 */
void DiaDefaultButton(INTBIG item)
{
	gra_curdialog->defaultButton(item);
}

/*
 * Routine to change item "item" into a popup with "count" entries
 * in "names".
 */
void DiaSetPopup(INTBIG item, INTBIG count, char **names)
{
	gra_curdialog->setPopup(item, count, names);
}

/*
 * Routine to change popup item "item" so that the current entry is "entry".
 */
void DiaSetPopupEntry(INTBIG item, INTBIG entry)
{
	gra_curdialog->setPopupEntry(item, entry);
}

/*
 * Routine to return the current item in popup menu item "item".
 */
INTBIG DiaGetPopupEntry(INTBIG item)
{
	return(gra_curdialog->getPopupEntry(item));
}

void DiaInitTextDialog(INTBIG item, BOOLEAN (*toplist)(char **), char *(*nextinlist)(void),
	void (*donelist)(void), INTBIG sortpos, INTBIG flags)
{
	gra_curdialog->initTextDialog(item, toplist, nextinlist, donelist, sortpos, flags);
}

void DiaLoadTextDialog(INTBIG item, BOOLEAN (*toplist)(char **), char *(*nextinlist)(void),
	void (*donelist)(void), INTBIG sortpos)
{
	gra_curdialog->loadTextDialog(item, toplist, nextinlist, donelist, sortpos);
}

/*
 * Routine to stuff line "line" at the end of the edit buffer.
 */
void DiaStuffLine(INTBIG item, char *line)
{
	gra_curdialog->stuffLine(item, line);
}

/*
 * Routine to select line "line" of scroll item "item".
 */
void DiaSelectLine(INTBIG item, INTBIG line)
{
	gra_curdialog->selectLine(item, line);
}

/*
 * Routine to select "count" lines in "lines" of scroll item "item".
 */
void DiaSelectLines(INTBIG item, INTBIG count, INTBIG *lines)
{
	gra_curdialog->selectLines(item, count, lines);
}

/*
 * Returns the currently selected line in the scroll list "item".
 */
INTBIG DiaGetCurLine(INTBIG item)
{
	return(gra_curdialog->getCurLine(item));
}

/*
 * Returns the currently selected lines in the scroll list "item".  The returned
 * array is terminated with -1.
 */
INTBIG *DiaGetCurLines(INTBIG item)
{
	return(gra_curdialog->getCurLines(item));
}

char *DiaGetScrollLine(INTBIG item, INTBIG line)
{
	return(gra_curdialog->getScrollLine(item, line));
}

void DiaSetScrollLine(INTBIG item, INTBIG line, char *msg)
{
	gra_curdialog->setScrollLine(item, line, msg);
}

void DiaItemRect(INTBIG item, RECTAREA *rect)
{
	gra_curdialog->itemRect(item, rect);
}

void DiaPercent(INTBIG item, INTBIG percent)
{
	gra_curdialog->percent(item, percent);
}

void DiaRedispRoutine(INTBIG item, void (*routine)(RECTAREA*))
{
	gra_curdialog->redispRoutine(item, routine);
}

void DiaInvertRect(INTBIG item, RECTAREA *r)
{
	gra_curdialog->invertRect(item, r);
}

void DiaFrameRect(INTBIG item, RECTAREA *r)
{
	gra_curdialog->frameRect(item, r);
}

void DiaDrawLine(INTBIG item, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty, INTBIG mode)
{
	gra_curdialog->drawLine(item, fx, fy, tx, ty, mode);
}

void DiaFillPoly(INTBIG item, INTBIG *xv, INTBIG *yv, INTBIG count, INTBIG r, INTBIG g, INTBIG b)
{
	gra_curdialog->fillPoly(item, xv, yv, count, r, g, b);
}

void DiaAllowUserDoubleClick(void)
{
	gra_curdialog->allowUserDoubleClick();
}

void DiaDrawRect(INTBIG item, RECTAREA *rect, INTBIG r, INTBIG g, INTBIG b)
{
	gra_curdialog->drawRect(item, rect, r, g, b);
}

void DiaPutText(INTBIG item, char *msg, INTBIG x, INTBIG y)
{
	gra_curdialog->putText(item, msg, x, y);
}

void DiaSetTextSize(INTBIG size)
{
	gra_curdialog->setTextSize(size);
}

void DiaGetTextInfo(char *msg, INTBIG *wid, INTBIG *hei)
{
	gra_curdialog->getTextInfo(msg, wid, hei);
}

void DiaTrackCursor(void (*eachdown)(INTBIG x, INTBIG y))
{
	gra_curdialog->trackCursor(eachdown);
}

void DiaGetMouse(INTBIG *x, INTBIG *y)
{
	gra_curdialog->getMouse(x, y);
}

BOOLEAN DiaNullDlogList(char **c) { return(FALSE); }

char *DiaNullDlogItem(void) { return(0); }

void DiaNullDlogDone(void) {}


/****************************** EDialog class ******************************/

EDialog::EDialog(QWidget *parent, DIALOG *dialog)
  : QDialog(parent, dialog->movable, TRUE)
{
	INTBIG i, itemtype, x, y, wid, hei;

	/* be sure the dialog is translated */
	DiaTranslate(dialog);

	itemdesc = dialog;
	editline = -1;
	lastedittext = QString::null;
	getdialogcoordinates(&dialog->windowRect, &x, &y, &wid, &hei);
	setFixedSize( wid, hei );
	move( x, y );
	setCaption( QString::fromLocal8Bit( dialog->movable ) );

	/* load the items */
	mapper = new QSignalMapper( this, "mapper");
        connect(mapper,SIGNAL(mapped(int)),this,SLOT(action(int)));
	QWidget *focus = 0;
	QPushButton *defbutton = 0;
	QPushButton *buttonzero = 0;
	for(i=0; i<dialog->items; i++)
	{
		getdialogcoordinates(&dialog->list[i].r, &x, &y, &wid, &hei);
		itemtype = dialog->list[i].type;
		QString unimsg = QString::fromLocal8Bit( dialog->list[i].msg );
		QWidget *widget = 0;
		QPushButton *button;
		ECheckField *check;
		QRadioButton *radio;
		QProgressBar *progress;
		QLineEdit *lineEdit;
		QLabel *label;
		QComboBox *comboBox;
		EUserDrawnField *user;

		switch (itemtype&ITEMTYPE)
		{
			case BUTTON:
			case DEFBUTTON:
			        widget = button = new QPushButton(unimsg, this);
				connect(button,SIGNAL(clicked()),mapper,SLOT(map()));
				if (i == 0) buttonzero = button;
				if ((itemtype&ITEMTYPE) == DEFBUTTON) defbutton = button;
				break;
			case CHECK:
				widget = check = new ECheckField(unimsg, this );
				connect(check,SIGNAL(clicked()),mapper,SLOT(map()));
				break;
			case RADIO:
#if 0
				/* radio buttons seem too low, so raise them */
				y -= 5;
#endif
				widget = radio = new QRadioButton(unimsg, this );
				connect(radio,SIGNAL(clicked()),mapper,SLOT(map()));
				break;
			case EDITTEXT:
#if 0
				/* raise text and force it to minimum height */
				y -= 4;
				if (hei < 30) hei = 30;
#endif
				widget = lineEdit = new QLineEdit( this );
				connect(lineEdit,SIGNAL(textChanged(const QString&)),mapper,SLOT(map()));
				if (focus == 0) focus = lineEdit;
				break;
			case MESSAGE:
				widget = label = new QLabel( unimsg, this );
#if QT_VERSION >= 300
				label->setAlignment( AlignAuto | AlignTop | ExpandTabs | WordBreak | BreakAnywhere);
#else
				label->setAlignment( AlignLeft | AlignTop | ExpandTabs | WordBreak );
#endif
				break;
			case PROGRESS:
				widget = progress = new QProgressBar( 100, this );
				break;
			case DIVIDELINE:
				widget = new QWidget( this );
				widget->setBackgroundMode( PaletteForeground );
				break;
			case USERDRAWN:
				widget = user = new EUserDrawnField ( this, &dialog->list[i] );
				connect(user,SIGNAL(clicked()),mapper,SLOT(map()));
				break;
			case POPUP:
#if 0
				/* popups seem too low, so raise them */
				y -= 7;
				hei += 7;
#endif
				widget = comboBox = new QComboBox( this );
				connect(comboBox,SIGNAL(activated(int)),mapper,SLOT(map()));
				break;
			case ICON: {
			        EIconField *icon = new EIconField( this, &dialog->list[i] );
				widget = icon;
				if ((itemtype&INACTIVE) == 0) {
					connect(icon,SIGNAL(clicked()),mapper,SLOT(map()));
				}
			}
			break;
			case SCROLL:
		        case SCROLLMULTI: {
#if 0
			        /* shrink to make room for vertical scrollbar */
				wid -= 20;
#endif
			        EScrollField *scroll;
				widget = scroll = new EScrollField( this );
				connect(scroll,SIGNAL(selectionChanged()),mapper,SLOT(map()));
				if ((itemtype&ITEMTYPE) == SCROLLMULTI)
					scroll->setSelectionMode( EScrollField::Extended );
			}
			break;
			default:
				break;
		}
		items[i] = widget;
		if (widget != 0) {
			widget->setFixedSize( wid, hei );
			widget->move( x, y );
			mapper->setMapping( widget, i );
		}
	}
	if (defbutton == 0 && buttonzero != 0) defbutton = buttonzero;
	if (defbutton != 0) defbutton->setDefault(TRUE);
	if (focus == 0) focus = defbutton;
	if (focus == 0) focus = items[0];
	if (focus != 0) focus->setFocus();
}

EDialog::~EDialog()
{
    /* adjust the dialog position if it was moved */
    QPoint d = (pos() - iniPos) * DIALOGDEN / DIALOGNUM;
    itemdesc->windowRect.left += d.x();
    itemdesc->windowRect.right += d.x();
    itemdesc->windowRect.top += d.y();
    itemdesc->windowRect.bottom += d.y();
}

void EDialog::showMe()
{
    QWidget::show();
    iniPos = pos();
    gra_dialoghit = -1;
}

char *EDialog::localize (QString qstr)
{
    static QCString str;

    str = qstr.local8Bit();
    const char *s = str;
    return((char*)s);
}

/*
 * Routine to handle actions and return the next item hit.
 */
INTBIG EDialog::nextHit(void)
{
#ifdef ETRACE
	etrace(GTRACE, "{ DiaNextHit\n");
#endif
	/* flush the screen */
	flushscreen();

	while(gra_dialoghit == -1)
	{
		QTimer timer( this );
		timer.start( MAXEVENTTIME, TRUE );
		qApp->processOneEvent();
	}
	INTBIG item = gra_dialoghit;
	gra_dialoghit = -1;
#ifdef ETRACE
	etrace(GTRACE, "} DiaNextHit: item=%d\n", item);
#endif
	return(item);
}

/*
 * Routine to parse the next input event and return the next character typed.
 * If the routine returns -1, nothing has happened.  If the
 * routine returns -2, an item has been hit (and is in "itemHit").
 */
INTSML EDialog::getNextCharacter(INTBIG *itemHit)
{
	INTBIG chr;

#ifdef ETRACE
	etrace(GTRACE, "{ DiaGetNextCharacter\n");
#endif
	*itemHit = nextHit();
	if (gra_dialoghitchar == 0) {
		etrace(GTRACE, "} DiaGetNextCharacter: result=%d itemHit=%d\n", -2, *itemHit);
		return(-2);
	}
	chr = gra_dialoghitchar;
	gra_dialoghitchar = 0;
#ifdef ETRACE
	etrace(GTRACE, "} DiaGetNextCharacter: result=%d itemHit=%d\n", chr, *itemHit);
#endif
	return(chr);
}

/*
 * Routine to set the text in item "item" to "msg"
 */
void EDialog::setText(INTBIG item, char *msg)
{
	bool highlight = FALSE;
	if (item < 0)
	{
		item = -item;
		highlight = TRUE;
	}
	item--;
	INTBIG type = itemdesc->list[item].type;
	QString qmsg = QString::fromLocal8Bit( msg );
	switch (type&ITEMTYPE) {
	case EDITTEXT: {
	    QLineEdit *lineEdit = (QLineEdit*)items[item];
	    QObject::disconnect(lineEdit,SIGNAL(textChanged(const QString&)),mapper,SLOT(map()));
	    lineEdit->setText ( qmsg );
	    if (highlight != 0) lineEdit->selectAll(); else lineEdit->deselect();
	    lineEdit->setCursorPosition( lineEdit->text().length() );
	    QObject::connect(lineEdit,SIGNAL(textChanged(const QString&)),mapper,SLOT(map()));
	    if (item == editline)
	        lastedittext = qmsg;
	}
	break;
	case MESSAGE: {
	  extern DIALOG us_eprogressdialog, us_progressdialog;
	  QLabel *label = (QLabel*)items[item];
	  label->setText( qmsg );
	  if (itemdesc == &us_eprogressdialog || itemdesc == &us_progressdialog) label->repaint();
	}
	break;
	case BUTTON:
	case DEFBUTTON:
	case CHECK:
	case RADIO: {
	  QButton *button = (QButton*)items[item];
	  button->setText( qmsg );
	}
	break;
	default:
	  qDebug("Bad itemtype in DiaSetText %x", type);
	}
}

/*
 * Routine to return the text in item "item"
 */
char *EDialog::getText(INTBIG item)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case EDITTEXT: {
	    QLineEdit *lineEdit = (QLineEdit*)items[item];
	    QString qstr = (item == editline ? lastedittext : lineEdit->text() );
	    return localize( qstr );
	}
	break;
	case MESSAGE: {
	    QLabel *label = (QLabel*)items[item];
	    return localize( label->text() );
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaGetText %x", type);
	}
	return("");
}

/*
 * Routine to set the value in item "item" to "value"
 */
void EDialog::setControl(INTBIG item, INTBIG value)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case RADIO: {
	    QRadioButton *radio = (QRadioButton*)items[item];
	    radio->setChecked( value );
	}
	break;
	case CHECK: {
	    ECheckField *check = (ECheckField*)items[item];
	    check->setChecked( value );
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaSetControl %x", type);
	}
}

/*
 * Routine to return the value in item "item"
 */
INTBIG EDialog::getControl(INTBIG item)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case RADIO: {
	    QRadioButton *radio = (QRadioButton*)items[item];
	    return(radio->isChecked());
	}
	break;
	case CHECK: {
	    ECheckField *check = (ECheckField*)items[item];
	    return(check->isChecked());
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaGetControl %x", type);
	}
}

/*
 * Routine to check item "item" to make sure that there is
 * text in it.  If so, it returns true.  Otherwise it beeps and returns false.
 */
BOOLEAN EDialog::validEntry(INTBIG item)
{
	char *msg = DiaGetText(item);
	while (*msg == ' ') msg++;
	if (*msg != 0) return(TRUE);
	ttybeep(1);
	return(FALSE);
}

/*
 * Routine to dim item "item"
 */
void EDialog::dimItem(INTBIG item)
{
	item--;
	QWidget *widget = items[item];
	widget->setEnabled( FALSE );
}

/*
 * Routine to un-dim item "item"
 */
void EDialog::unDimItem(INTBIG item)
{
	item--;
	QWidget *widget = items[item];
	widget->setEnabled( TRUE );
}

/*
 * Routine to change item "item" to be a message rather
 * than editable text
 */
void EDialog::noEditControl(INTBIG item)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case EDITTEXT: {
	    QLineEdit *lineEdit = (QLineEdit*)items[item];
	    lineEdit->setReadOnly( TRUE );
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaNoEditControl %x", type);
	}
}

/*
 * Routine to change item "item" to be editable text rather
 * than a message
 */
void EDialog::editControl(INTBIG item)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case EDITTEXT: {
	    QLineEdit *lineEdit = (QLineEdit*)items[item];
	    lineEdit->setReadOnly( FALSE );
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaEditControl %x", type);
	}
}

void EDialog::opaqueEdit(INTBIG item)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case EDITTEXT: {
	    QLineEdit *lineEdit = (QLineEdit*)items[item];
	    lineEdit->setEchoMode( QLineEdit::Password );
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaOpaqueText %x", type);
	}
}

/*
 * Routine to cause item "item" to report character hits
 */
void EDialog::characterEdit(INTBIG item)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case EDITTEXT: {
	    editline = item;
	    QLineEdit *lineEdit = (QLineEdit*)items[item];
	    lineEdit->setText( lastedittext );
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaCharacter edit %x", type);
	}
}

/*
 * Routine to cause item "item" to be the default button
 */
void EDialog::defaultButton(INTBIG item)
{
  //	qDebug("DiaDefaultButton %d", item);
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case BUTTON:
	case DEFBUTTON: {
	    QPushButton *button = (QPushButton*)items[item];
	    button->setDefault( TRUE );
	    //	    button->setFocus();
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaDefaultButton %x", type);
	}
}

/*
 * Routine to change item "item" into a popup with "count" entries
 * in "names".
 */
void EDialog::setPopup(INTBIG item, INTBIG count, char **names)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case POPUP: {
	    QComboBox *comboBox = (QComboBox*)items[item];
	    for (int i = 0; i < count; i++) {
	        comboBox->insertItem( QString::fromLocal8Bit( names[i] ), i );
	    }
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaSetPopup %x", type);
	}
}

/*
 * Routine to change popup item "item" so that the current entry is "entry".
 */
void EDialog::setPopupEntry(INTBIG item, INTBIG entry)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case POPUP: {
	    QComboBox *comboBox = (QComboBox*)items[item];
	    comboBox->setCurrentItem( entry );
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaSetPopupEntry %x", type);
	}
}

/*
 * Routine to return the current item in popup menu item "item".
 */
INTBIG EDialog::getPopupEntry(INTBIG item)
{
	item--;
	INTBIG type = itemdesc->list[item].type;
	switch (type&ITEMTYPE) {
	case POPUP: {
	    QComboBox *comboBox = (QComboBox*)items[item];
	    return comboBox->currentItem();
	}
	break;
	default:
	    qDebug("Bad itemtype in DiaSetPopup %x", type);
	}
	return(0);
}

void EDialog::initTextDialog(INTBIG item, BOOLEAN (*toplist)(char **), char *(*nextinlist)(void),
	void (*donelist)(void), INTBIG sortpos, INTBIG flags)
{
	item--;
	EScrollField *scroll = (EScrollField*)items[item];
	if ((flags&SCDOUBLEQUIT) != 0)
	{
		INTBIG type0 = itemdesc->list[0].type&ITEMTYPE;
		Q_ASSERT( type0 == DEFBUTTON || type0 == BUTTON );
		QPushButton *button0 = (QPushButton*)items[0];
		connect(scroll,SIGNAL(doubleClicked(QListBoxItem*)),button0,SLOT(animateClick()));
	}
	if ((flags&SCFIXEDWIDTH) != 0) {
	    QFont fixedfont( QString::null );
	    fixedfont.setStyleHint( QFont::TypeWriter );
	    fixedfont.setFixedPitch( TRUE );
	    scroll->setFont( fixedfont );
	}
	DiaLoadTextDialog(item+1, toplist, nextinlist, donelist, sortpos);
}

void EDialog::loadTextDialog(INTBIG item, BOOLEAN (*toplist)(char **), char *(*nextinlist)(void),
	void (*donelist)(void), INTBIG sortpos)
{
	INTBIG i, it;
	char *next, **list, line[256];
	QString qstr;

	item--;
	EScrollField *scroll = (EScrollField*)items[item];

	scroll->blockSignals( TRUE );

	/* clear the list */
	scroll->clear();

	if (sortpos < 0)
	{
		/* unsorted: load the list directly */
		line[0] = 0;
		next = line;
		(void)(*toplist)(&next);
		for(it=0; ; it++)
		{
			next = (*nextinlist)();
			if (next == 0) break;
			qstr = QString::fromLocal8Bit( next );
			scroll->insertItem( qstr );
		}
		(*donelist)();
	} else
	{
		/* count the number of items to be put in the text editor */
		line[0] = 0;
		next = line;
		(void)(*toplist)(&next);
		for(it=0; ; it++) if ((*nextinlist)() == 0) break;
		(*donelist)();

		/* allocate space for the strings */
		if (it > 0)
		{
			list = (char **)emalloc(it * (sizeof (char *)), el_tempcluster);
			if (list == 0) return;
		}

		/* get the list */
		line[0] = 0;
		next = line;
		(void)(*toplist)(&next);
		for(i=0; i<it; i++)
		{
			next = (*nextinlist)();
			if (next == 0) next = "???";
			list[i] = (char *)emalloc(strlen(next)+1, el_tempcluster);
			if (list[i] == 0) return;
			strcpy(list[i], next);
		}
		(*donelist)();

		/* sort the list */
		gra_dialogstringpos = sortpos;
		esort(list, it, sizeof (char *), gra_stringposascending);

		/* stuff the list into the text editor */
		for(i=0; i<it; i++)
		{
			qstr = QString::fromLocal8Bit( list[i] );
			scroll->insertItem( qstr );
		}

		/* deallocate the list */
		if (it > 0)
		{
			for(i=0; i<it; i++) efree((char *)list[i]);
			efree((char *)(char *)list);
		}
	}
	if (it > 0) scroll->setSelected( 0, TRUE );

	scroll->blockSignals( FALSE );
}

/*
 * Routine to stuff line "line" at the end of the edit buffer.
 */
void EDialog::stuffLine(INTBIG item, char *line)
{
	item--;
	EScrollField *scroll = (EScrollField*)items[item];
	QString qstr = QString::fromLocal8Bit( line );
	scroll->blockSignals( TRUE );
	scroll->insertItem( qstr );
	scroll->blockSignals( FALSE );
}

/*
 * Routine to select line "line" of scroll item "item".
 */
void EDialog::selectLine(INTBIG item, INTBIG line)
{
	item--;
	EScrollField *scroll = (EScrollField*)items[item];
	scroll->blockSignals( TRUE );
	if (line < 0) scroll->clearSelection(); else scroll->setSelected( line, TRUE );
	scroll->blockSignals( FALSE );

	/* make sure lines are visible */
	int first = scroll->topItem();
	int visible = scroll->numItemsVisible();
	if (line < first || line >= first+visible) {
		first = QMAX( line - visible/2 , 0);
		scroll->setTopItem( first );
	}
}

/*
 * Routine to select "count" lines in "lines" of scroll item "item".
 */
void EDialog::selectLines(INTBIG item, INTBIG count, INTBIG *lines)
{
	item--;
	EScrollField *scroll = (EScrollField*)items[item];
	scroll->blockSignals( TRUE );
	scroll->clearSelection();
	int low, high;
	for(int i=0; i<count; i++)
	{
		int line = lines[i];
		scroll->setSelected( i, TRUE );
		if (i == 0) low = high = line; else
		{
			if (line < low) low = line;
			if (line > high) high = line;
		}
	}
	scroll->blockSignals( FALSE );

	/* make sure lines are visible */
	int first = scroll->topItem();
	int visible = scroll->numItemsVisible();
	if (high < first || low >= first+visible) scroll->setTopItem( low );
}

/*
 * Returns the currently selected line in the scroll list "item".
 */
INTBIG EDialog::getCurLine(INTBIG item)
{
	item--;
	EScrollField *scroll = (EScrollField*)items[item];
	int selected = scroll->currentItem();
	return(selected);
}

/*
 * Returns the currently selected lines in the scroll list "item".  The returned
 * array is terminated with -1.
 */
INTBIG *EDialog::getCurLines(INTBIG item)
{
	static INTBIG selected[MAXSCROLLMULTISELECT];
	int i;

	item--;
	EScrollField *scroll = (EScrollField*)items[item];
	int count = 0;
	for (i = 0; i < scroll->count(); i++) {
	  if (scroll->isSelected( i ) && count < MAXSCROLLMULTISELECT - 1) {
	    selected[count] = i;
	    count++;
	  }
	}
	selected[count] = -1;
	return(selected);
}

char *EDialog::getScrollLine(INTBIG item, INTBIG line)
{
	item--;
	EScrollField *scroll = (EScrollField*)items[item];
	return localize( scroll->text( line ) );
}

void EDialog::setScrollLine(INTBIG item, INTBIG line, char *msg)
{
	item--;
	EScrollField *scroll = (EScrollField*)items[item];
	QString qmsg = QString::fromLocal8Bit( msg );
	scroll->blockSignals( TRUE );
	scroll->changeItem( qmsg, line );
	scroll->blockSignals( FALSE );
}

void EDialog::itemRect(INTBIG item, RECTAREA *rect)
{
	item--;
	if (item < 0 || item >= itemdesc->items) return;
	rect->left = itemdesc->list[item].r.left+1;
	rect->right = itemdesc->list[item].r.right-1;
	rect->top = itemdesc->list[item].r.top+1;
	rect->bottom = itemdesc->list[item].r.bottom-1;
}

void EDialog::percent(INTBIG item, INTBIG percent)
{
	item--;
	QProgressBar *progress = (QProgressBar*)items[item];
	progress->setProgress( percent );
}

void EDialog::redispRoutine(INTBIG item, void (*routine)(RECTAREA*))
{
    item--;
    Q_ASSERT( (itemdesc->list[item].type&ITEMTYPE) == USERDRAWN );
    EUserDrawnField *user = (EUserDrawnField*)items[item];
    user->redispRoutine = routine;
}

void EDialog::invertRect(INTBIG item, RECTAREA *r)
{
    item--;
    Q_ASSERT( (itemdesc->list[item].type&ITEMTYPE) == USERDRAWN );
    int xoff = itemdesc->list[item].r.left;
    int yoff = itemdesc->list[item].r.top;
    QPainter p( items[item] );
    int x1 = scaledialogcoordinate(r->left-xoff);
    int y1 = scaledialogcoordinate(r->top-yoff);
    int x2 = scaledialogcoordinate(r->right-xoff);
    int y2 = scaledialogcoordinate(r->bottom-yoff);
    p.setRasterOp( NotROP );
    p.drawRect( x1, y1, x2 - x1, y2 - y1 );
}

void EDialog::frameRect(INTBIG item, RECTAREA *r)
{
    item--;
    Q_ASSERT( (itemdesc->list[item].type&ITEMTYPE) == USERDRAWN );
    int xoff = itemdesc->list[item].r.left;
    int yoff = itemdesc->list[item].r.top;
    QPainter p( items[item] );
    int x1 = scaledialogcoordinate(r->left-xoff);
    int y1 = scaledialogcoordinate(r->top-yoff);
    int x2 = scaledialogcoordinate(r->right-xoff);
    int y2 = scaledialogcoordinate(r->bottom-yoff);
    p.setPen( black );
    p.setBrush( white );
    p.drawRect( x1, y1, x2 - x1, y2 - y1 );
}

void EDialog::drawLine(INTBIG item, INTBIG fx, INTBIG fy, INTBIG tx, INTBIG ty, INTBIG mode)
{
    item--;
    Q_ASSERT( (itemdesc->list[item].type&ITEMTYPE) == USERDRAWN );
    int xoff = itemdesc->list[item].r.left;
    int yoff = itemdesc->list[item].r.top;
    QPainter p( items[item] );
    int x1 = scaledialogcoordinate(fx-xoff);
    int y1 = scaledialogcoordinate(fy-yoff);
    int x2 = scaledialogcoordinate(tx-xoff);
    int y2 = scaledialogcoordinate(ty-yoff);
    switch( mode ) {
    case DLMODEON:
        p.setPen( black );
	break;
    case DLMODEOFF:
        p.setPen( white );
	break;
    case DLMODEINVERT:
        p.setRasterOp( NotROP );
	break;
    }
    p.drawLine( x1, y1, x2, y2 );
}

void EDialog::fillPoly(INTBIG item, INTBIG *xv, INTBIG *yv, INTBIG count, INTBIG r, INTBIG g, INTBIG b)
{
    item--;
    Q_ASSERT( (itemdesc->list[item].type&ITEMTYPE) == USERDRAWN );
    int xoff = itemdesc->list[item].r.left;
    int yoff = itemdesc->list[item].r.top;
    QPainter p( items[item] );
    QPointArray pointlist( count );
    for( int i=0; i<count; i++ ) {
        pointlist.setPoint( i, scaledialogcoordinate(xv[i]-xoff) ,
			       scaledialogcoordinate(yv[i]-yoff) );
    }
    p.setPen( NoPen );
    p.setBrush( QColor( r, g, b ) );
    p.drawPolygon( pointlist );
}

void EDialog::allowUserDoubleClick(void)
{
}

void EDialog::drawRect(INTBIG item, RECTAREA *rect, INTBIG r, INTBIG g, INTBIG b)
{
    item--;
    Q_ASSERT( (itemdesc->list[item].type&ITEMTYPE) == USERDRAWN );
    int xoff = itemdesc->list[item].r.left;
    int yoff = itemdesc->list[item].r.top;
    QPainter p( items[item] );
    int x1 = scaledialogcoordinate(rect->left-xoff);
    int y1 = scaledialogcoordinate(rect->top-yoff);
    int x2 = scaledialogcoordinate(rect->right-xoff);
    int y2 = scaledialogcoordinate(rect->bottom-yoff);
    p.setPen( NoPen );
    p.setBrush( QColor( r, g, b ) );
    p.drawRect( x1, y1, x2 - x1, y2 - y1 );
}

void EDialog::putText(INTBIG item, char *msg, INTBIG x, INTBIG y)
{
#if 1
    item--;
    Q_ASSERT( (itemdesc->list[item].type&ITEMTYPE) == USERDRAWN );
    int xoff = itemdesc->list[item].r.left;
    int yoff = itemdesc->list[item].r.top;
    QPainter p( items[item] );
    int x1 = scaledialogcoordinate(x-xoff);
    int y1 = scaledialogcoordinate(y-yoff);
    p.setPen( black );
    // p.setFont(???);
    p.drawText( x1, y1 + p.fontMetrics().ascent(), QString::fromLocal8Bit( msg ) );
#else
	INTBIG fontnumber, xoff, yoff, xp, yp;
	XFontStruct *font;
	Widget w;
	Display *dpy;
	Window win;
	XGCValues gcv;
	GC gc;

	item--;
	w = gra_curdialog->items[item];
	xoff = gra_curdialog->itemdesc->list[item].r.left;
	yoff = gra_curdialog->itemdesc->list[item].r.top;
	dpy = XtDisplay(w);
	win = XtWindow(w);

	fontnumber = 2;   /* used to be TXT8P */
	font = gra_font[fontnumber].font;
	gcv.foreground = BlackPixelOfScreen(XtScreen(w));
	gc = XtGetGC(w, GCForeground, &gcv);
	XSetFont(dpy, gc, font->fid);
	xp = gra_scaledialogcoordinate(x-xoff);
	yp = gra_scaledialogcoordinate(y-yoff);
	XDrawString(dpy, win, gc, xp, yp+font->ascent+font->descent, msg,
		strlen(msg));
	XtReleaseGC(w, gc);
#endif
}

void EDialog::setTextSize(INTBIG size)
{
}

void EDialog::getTextInfo(char *msg, INTBIG *wid, INTBIG *hei)
{
    QPainter p( this );
    // p.setFont(???);
    QSize size = p.fontMetrics().boundingRect( QString::fromLocal8Bit( msg ) ).size();
    size = size * DIALOGDEN / DIALOGNUM;
    *wid = size.width();
    *hei = size.height();
}

void EDialog::trackCursor(void (*eachdown)(INTBIG x, INTBIG y))
{
    /* find user defined field which grab mouse */
    EUserDrawnField *user = 0;
    for( int i=0; i<itemdesc->items && user == 0; i++ ) {
        if ((itemdesc->list[i].type&ITEMTYPE) != USERDRAWN) continue;
	user = (EUserDrawnField*)items[i];
	if (!user->buttonPressed) user = 0;
    }
    if (user) {
        user->eachdown = eachdown;
        qApp->enter_loop();
        Q_ASSERT( user->eachdown == 0 );
    }
}

void EDialog::getMouse(INTBIG *x, INTBIG *y)
{
    QPoint pos = mapFromGlobal( QCursor::pos() ) * DIALOGDEN / DIALOGNUM;
    *x = pos.x();
    *y = pos.y();
}

/*
 * Routine to convert dialog coordinates
 */
void EDialog::getdialogcoordinates(RECTAREA *rect, INTBIG *x, INTBIG *y, INTBIG *wid, INTBIG *hei)
{
	*x = scaledialogcoordinate(rect->left);
	*y = scaledialogcoordinate(rect->top);
	*wid = scaledialogcoordinate(rect->right - rect->left);
	*hei = scaledialogcoordinate(rect->bottom - rect->top);
}

INTBIG EDialog::scaledialogcoordinate(INTBIG x)
{
	return((x * DIALOGNUM + DIALOGDEN/2) / DIALOGDEN);
}

void EDialog::action(int id)
{
    INTBIG item, itemtype, value, count, selected, myselected[1], *selectedlist, i;
    XEvent *event;

    item = ((int)id) & 0xFFFF;
    itemtype = itemdesc->list[item].type;
    if (gra_dialoghit != -1) ttyputerr(_("dailog hit lost"));
    gra_dialoghit = item+1;

    switch (itemtype&ITEMTYPE) {
    case EDITTEXT: {
      QLineEdit *lineEdit = (QLineEdit*)items[item];
      if (item == editline) {
          /* determine which character was typed (hack!!!) */
	  if (lineEdit->text().length() > lastedittext.length()) {
	      const char* lastchar = localize( lineEdit->text().right( 1 ) );
	      gra_dialoghitchar = lastchar[0];
	  } else if (lineEdit->text().length() < lastedittext.length()) {
	      gra_dialoghitchar = us_erasech;
	  } else if (lastedittext.length() != 0) {
	      qDebug ("Character edit: new length = old length");
	  }

      }
    }
    break;
    }
}

void EDialog::closeEvent( QCloseEvent *e )
{
    e->ignore();
}

void EDialog::keyPressEvent( QKeyEvent *e )
{
    /* ignore Escape because we should terminate by doneDialog only */
    if( e->key() == Key_Escape ) return;
    QDialog::keyPressEvent( e );
}

/******************************** Check Field ********************************/

ECheckField::ECheckField( QString &text, QWidget *parent )
    : QCheckBox( text, parent )
{
    checked = FALSE;
}

ECheckField::~ECheckField()
{
    if (isChecked() != QCheckBox::isChecked())
        ttyputerr(_("CheckBox field appearance differs from value"));
}

bool ECheckField::isChecked()
{
    return checked;
}

void ECheckField::setChecked( bool value )
{
    checked = value;
    QCheckBox::setChecked( value );
}

/******************************** Scroll Field ********************************/

EScrollField::EScrollField( QWidget *parent )
    : QListBox( parent )
{
}

void EScrollField::keyPressEvent( QKeyEvent *e )
{
    if ( e->key() == Key_C && (e->state() & ControlButton) ||
	 e->key() == Key_F16) { // Copy key on Sun keyboards
        qDebug( "Copy list");
        QStringList strList;
        for( int i=0; i < count(); i++) strList.append( text( i ) );
	qApp->clipboard()->setText( strList.join( "\n" ) );
	qDebug( qApp->clipboard()->text() );
        return;
    }
    QListBox::keyPressEvent( e );
}

/****************************** Icon Field ******************************/

EIconField::EIconField( QWidget *parent, DIALOGITEM *item )
    : QLabel( parent )
{
    QBitmap bitmap(32, 32, (uchar*)item->msg, FALSE);
    QWMatrix m;
    double scale = (double)DIALOGNUM / double(DIALOGDEN);
    m.scale( scale, scale );
    QPixmap pixmap = bitmap.xForm( m );
    setPixmap( pixmap );
}

void EIconField::mousePressEvent( QMouseEvent *e )
{
    emit clicked();
};

/****************************** User Drawn Field ******************************/

EUserDrawnField::EUserDrawnField(QWidget *parent, DIALOGITEM *item)
    : QWidget(parent), it(item), redispRoutine(0), buttonPressed(FALSE), eachdown(0)
{
}

void EUserDrawnField::mouseMoveEvent( QMouseEvent *e )
{
    if (eachdown == 0) return;
    QPoint pos = mapToParent( e->pos() ) * DIALOGDEN / DIALOGNUM;
    (*eachdown)( pos.x(), pos.y() );
}

void EUserDrawnField::mousePressEvent( QMouseEvent *e )
{
    buttonPressed = TRUE;
    emit clicked();
}

void EUserDrawnField::mouseReleaseEvent( QMouseEvent *e )
{
    buttonPressed = FALSE;
    if (eachdown != 0) {
        eachdown = 0;
        qApp->exit_loop();
    }
}

void EUserDrawnField::paintEvent( QPaintEvent *e )
{
    RECTAREA rect;
    rect.left = it->r.left+1;
    rect.right = it->r.right-1;
    rect.top = it->r.top+1;
    rect.bottom = it->r.bottom-1;
    if ( redispRoutine )
        (*redispRoutine)(&rect);
}



/****************************** DIALOG SUPPORT ******************************/

/*
 * Helper routine for "DiaLoadTextDialog" that makes strings go in ascending order.
 */
int gra_stringposascending(const void *e1, const void *e2)
{
	REGISTER char *c1, *c2;

	c1 = *((char **)e1);
	c2 = *((char **)e2);
	return(namesame(&c1[gra_dialogstringpos], &c2[gra_dialogstringpos]));
}

