/*************************************************************
*
* (c) 1999, 2000 Christoph Pinkel
* This is part of the VisKProg project.
*
* You may use it under the terms of the GPL licence.
* See COPYING for more details!
*
**************************************************************/

#include "pedi.h"
#include "pedi.moc"

/*******************************************************************************
*	class propField:
*******************************************************************************/

propField::propField( QString p, QString v, QWidget *parent ) : QWidget(parent)
{
	prop = p;
	val = v;
	oldVal = v;

	pName = new eQLabel( prop, this );
	vText = new eQLabel( val, this );


	setFocusPolicy( QWidget::StrongFocus );

	myStdPal = palette();
	lStdPal = pName->palette();

	vEdit = new eQLineEdit(this);
	vEdit->hide();

	editMode = false;

	vChange = new eQPushButton( ".", this );


	allowMLCaption = false;

	connect ( vChange, SIGNAL( focusOut() ), SLOT( deActivate() ) );
	connect ( vEdit, SIGNAL( focusOut() ), SLOT( deActivate() ) );

	connect ( vChange, SIGNAL( clicked() ), SLOT( edit() ) );
	connect ( vText, SIGNAL( clicked() ), SLOT( edit() ) );
	connect ( vEdit, SIGNAL( returnPressed() ), SLOT( edit() ) );

	connect ( pName, SIGNAL( sClick() ), SLOT( Activate() ) );
	connect ( vText, SIGNAL( sClick() ), SLOT( Activate() ) );

}


void propField::rejectChange()
{
	val = oldVal;
	vText->setText(val);
}


void propField::resizeEvent( QResizeEvent * )
{
	pName->setGeometry( 1, 1, width()/2 -2, height()-2 );

	vText->setGeometry( width()/2, 1, width()/2 -PROP_FIELD_HEIGHT -1,
		height()-2 );

	vEdit->setGeometry( width()/2, 1, width()/2 -PROP_FIELD_HEIGHT -1,
		height()-2 );

	vChange->setGeometry( width() -(PROP_FIELD_HEIGHT+1), 1,
		PROP_FIELD_HEIGHT-2, PROP_FIELD_HEIGHT-2 );
}


void propField::activate( bool activ )
{
QPalette aPal;

	if ( activ )
	{
		QColorGroup aG( QColor(255,255,255), QColor(0,0,120), QColor(150,150,255),
			QColor(0,0,100), QColor(80,80,200), QColor(255,255,255), QColor(0,0,120) );

		setPalette( QPalette( aG, aG, aG ) );

		pName->setPalette( QPalette( aG, aG, aG ) );
		vText->setPalette( QPalette( aG, aG, aG ) );
	}
	else
	{
		if ( !hasFocus() && !vEdit->hasFocus() && !vChange->hasFocus() )
		{
			cancelEdit();

			setPalette( myStdPal );

			pName->setPalette( lStdPal );
			vText->setPalette( lStdPal );
		}

	}

}// activate(...)


void propField::deActivate() //SLOT
{
	activate(false);
}


void propField::Activate() //SLOT
{
	if ( !vEdit->hasFocus() && !vChange->hasFocus() ) setFocus();

	activate(true);
}


void propField::focusInEvent( QFocusEvent * )
{
	activate(true);
}


bool propField::acceptChange( QString newVal )
{
QString hStr;
int i;
bool ok;

	if ( prop == "Name" )
	{
		if( newVal.length() == 0 )
		{
			KMsgBox::message( 0L, "Wrong new value",
				"A name mustn't be an empty string." );

			return false;
		}

		if( !isalpha( newVal[0] ) )
		{
			KMsgBox::message( 0L, "Wrong new value",
				"A name must begin with an\nalphabetic character." );

			return false;
		}

		for ( i = 1; (unsigned)i < newVal.length(); i++ )
			if( !isalnum(newVal[i]) )
			{
				hStr = QString ("A name must be a string of\n")
						+("alphanumeric characters.");

				KMsgBox::message( 0L, "Wrong new value", hStr );

				return false;				
			}


		KMsgBox::message( 0L, "Warning", QString ("You _may_ run into trouble if ")
			+("you use\nupper cases in names.") );


	}


	if ( prop == "Width" )
	{
		newVal.toInt(&ok);

		if (!ok)
		{
			KMsgBox::message( 0L, "Wrong new value", "I need an integer value here." );

			return false;				
		}

		if ( newVal.toInt() < 5 )
		{
			KMsgBox::message( 0L, "Wrong new value",
				"Value must be greater than 4." );

			return false;				
		}
	}


	if ( prop == "Height" )
	{
		newVal.toInt(&ok);

		if (!ok)
		{
			KMsgBox::message( 0L, "Wrong new value", "I need an integer value here." );

			return false;				
		}

		if ( newVal.toInt() < 5 )
		{
			KMsgBox::message( 0L, "Wrong new value",
				"Value must be greater than 4." );

			return false;				
		}
	}

	if ( prop == "MinimalWidth" )
	{
		newVal.toInt(&ok);

		if (!ok)
		{
			KMsgBox::message( 0L, "Wrong new value", "I need an integer value here." );

			return false;				
		}

		if ( newVal.toInt() < 5 )
		{
			KMsgBox::message( 0L, "Wrong new value",
				"This must be something between\n5 and Width." );

			return false;				
		}

		if ( false ) // > width
		{
			KMsgBox::message( 0L, "Wrong new value",
				"This must be something between\n5 and Width." );

			return false;				
		}
	}

	if ( prop == "MinimalHeight" )
	{
		newVal.toInt(&ok);

		if (!ok)
		{
			KMsgBox::message( 0L, "Wrong new value", "I need an integer value here." );

			return false;				
		}

		if ( newVal.toInt() < 5 )
		{
			KMsgBox::message( 0L, "Wrong new value",
				"This must be something between\n5 and Height." );

			return false;				
		}

		if ( false ) // > width
		{
			KMsgBox::message( 0L, "Wrong new value",
				"This must be something between\n5 and Height." );

			return false;				
		}
	}

	if ( prop == "MaximalWidth" )
	{
		if( newVal != "(none)" )
		{
			newVal.toInt(&ok);

			if (!ok)
			{
				KMsgBox::message( 0L, "Wrong new value", "I need an integer value here\n(or \"(none)\")." );

				return false;				
			}

			if ( false ) // < width
			{
				KMsgBox::message( 0L, "Wrong new value",
					"This must greater than Width." );

				return false;				
			}
		}
	}


	if ( prop == "MaximalHeight" )
	{
		if( newVal != "(none)" )
		{
			newVal.toInt(&ok);

			if (!ok)
			{
				KMsgBox::message( 0L, "Wrong new value", "I need an integer value here\n(or \"(none)\")." );

				return false;				
			}

			if ( false ) // < width
			{
				KMsgBox::message( 0L, "Wrong new value",
					"This must greater than Height." );

				return false;				
			}
		}
	}

	if ( prop == "Steps" )
	{
		newVal.toInt(&ok);

		if (!ok)
		{
			KMsgBox::message( 0L, "Wrong new value", "I need an integer value here." );

			return false;				
		}

		if ( newVal.toInt() < 1 )
		{
			KMsgBox::message( 0L, "Wrong new value",
				"Value must be greater than 0." );

			return false;				
		}
	}

	if ( prop == "Value" )
	{
		newVal.toInt(&ok);

		if (!ok)
		{
			KMsgBox::message( 0L, "Wrong new value", "I need an integer value here." );

			return false;				
		}

		if ( newVal.toInt() < 0 )
		{
			KMsgBox::message( 0L, "Wrong new value",
				"Value must'nt be negative." );

			return false;				
		}
	}


	return true;

} //acceptChange()


void propField::edit() //SLOT
{
int i, ii;

	if ( !editMode )
	{
		activate(true);

		oldVal = val;

		if ( prop == "Caption" && allowMLCaption )
		{
			QString cStr = QString("You can set the caption\n")
				+("(i.e the text) of the widget here.");

			mld = new mlDialog( cStr, val );
			mld->show();

			val = mld->ret();

			if( acceptChange(val) )
			{
				vText->setText( val );
				emit changed( prop, val, this );
			}
			else
				val = oldVal;

		}
		else if ( prop == "Arguments" )
		{
			QString cStr = QString("You can set command line\n")
				+("arguments for the command:");

			mld = new mlDialog( cStr, val );
			mld->show();

			val = mld->ret();

			if( acceptChange(val) )
			{
				vText->setText( val );
				emit changed( prop, val, this );
			}
			else
				val = oldVal;
		}
		else if ( prop == "Running" )
		{
			QString vStr = QString ("This property controls, whether the\n")
				+("process actually is running. Set it to \"TRUE\", if\n")
				+("you want to start the process directly, when your\n")
				+("program has started.");

			if( val == "TRUE" )
				tfd = new tfDialog( vStr, true );
			else
				tfd = new tfDialog( vStr, false );

			if (tfd->exec())
			{
				if( tfd->ret() )
					val = "TRUE";
				else
					val = "FALSE";

				vText->setText( val );
				emit changed( prop, val, this );
			}
			
		}
		else if ( prop == "ReturnVal" )
		{
			KMsgBox::message( 0L, "Info", QString ("This property shows you the exit\n")
				+("code of the sub process, or -32 for \"not started, yet\",\n")
				+("-33 for \"still running\" and -34 for \"exited abnormally\".\n\n")
				+("It is read only.") );
		}
		else if ( prop == "AutoSort" )
		{
			QString vStr = QString ("In an autosorted list box, all items appear\n")
				+("in alphabetical order, otherwise they\n")
				+("appear in the order, they were inserted.");

			if( val == "TRUE" )
				tfd = new tfDialog( vStr, true );
			else
				tfd = new tfDialog( vStr, false );

			if (tfd->exec())
			{
				if( tfd->ret() )
					val = "TRUE";
				else
					val = "FALSE";

				vText->setText( val );
				emit changed( prop, val, this );
			}
			
		}
		else if ( prop == "Checked" )
		{
			QString cStr = QString ("Check or uncheck the check box here.");

			if( val == "TRUE" )
				tfd = new tfDialog( cStr, true );
			else
				tfd = new tfDialog( cStr, false );

			if (tfd->exec())
			{
				if( tfd->ret() )
					val = "TRUE";
				else
					val = "FALSE";

				vText->setText( val );
				emit changed( prop, val, this );
			}
			
		}
		else if ( prop == "CheckedExclusive" )
		{
			if ( val == "TRUE" )
			{
				KMsgBox::message( 0L, "Can't change value",
					QString ("There must be exactly one ")
						+("checked option button,\n")
						+("so please check another ")
						+("option button to uncheck\n")
						+("this one.") );
			}
			else
			{
				if( KMsgBox::yesNo( 0L, "Toggle option button ?",
					QString( "Shall I toggle this option " )
					+( "button and untoggle all other\n" )
					+( "option buttons in this (sub-) window ?" ) ) == 1 )
				{
					val = "TRUE";
					vText->setText(val);
					emit changed( prop, val, this );
				}
			}
		}
		else if ( prop == "Enabled" )
		{
			QString eStr = QString ("An enabled widget receive click events,\n")
				+("while a disabled one doesn't. The \n" )
				+("caption text of a disabled widget is gray." );

			if( val == "TRUE" )
				tfd = new tfDialog( eStr, true );
			else
				tfd = new tfDialog( eStr, false );

			if (tfd->exec())
			{
				if( tfd->ret() )
					val = "TRUE";
				else
					val = "FALSE";

				vText->setText( val );
				emit changed( prop, val, this );
			}
			
		}
		else if ( prop == "Visible" )
		{
			QString vStr = QString ("You can set the Visible property\n")
				+("to TRUE (visible) or to FALSE (hidden)." );

			if( val == "TRUE" )
				tfd = new tfDialog( vStr, true );
			else
				tfd = new tfDialog( vStr, false );

			if (tfd->exec())
			{
				if( tfd->ret() )
					val = "TRUE";
				else
					val = "FALSE";

				vText->setText( val );
				emit changed( prop, val, this );
			}

		}
		else if( prop == "BackgroundColor" )
		{

			QString r = "";
			i = 1; ii = 0;

			while( isdigit(val[i]) )
			{
				r.resize( r.length()+1 );
				r[ii] = val[i];

				i++; ii++;
			}

			r[ii] = 0L;

			QString g = "";
			i++; ii = 0;

			while( isdigit(val[i]) )
			{
				g.resize( g.length()+1 );
				g[ii] = val[i];

				i++; ii++;
			}

			g[ii] = 0L;

			QString b = "";
			i++; ii = 0;

			while( isdigit(val[i]) )
			{
				b.resize( b.length()+1 );
				b[ii] = val[i];

				i++; ii++;
			}

			b[ii] = 0L;

			KColorDialog kcdi( 0L, "selectColor", true );

			kcdi.setColor( QColor( r.toInt(), g.toInt(), b.toInt() ) );

			kcdi.show();

			if( kcdi.result() )
			{
				r.setNum( kcdi.color().red() );
				g.setNum( kcdi.color().green() );
				b.setNum( kcdi.color().blue() );
			}

			val = QString( "(" +r +"," +g +"," +b + ")" );

			vText->setText( val );

			emit changed( prop, val, this );

		}
		else if ( prop == "Picture" )
		{
			pdlg = new picDialog( val );
			pdlg->show();

			val = pdlg->ret();

			if( acceptChange(val) )
			{
				vText->setText( val );
				emit changed( prop, val, this );
			}
		}
		else
		{
			vEdit->setText( vText->text() );
			vEdit->show();
			vEdit->setFocus();

			vChange->setText( "ok" );

			editMode = true;
		}
	}
	else
	{
		if ( acceptChange( vEdit->text() ) )
		{
			vEdit->hide();

			val = vEdit->text();

			vText->setText( val );

			emit changed( prop, val, this );

			vChange->setText( "." );

			editMode = false;
		}
		else
		{
			KMsgBox::message( 0L, "", "Please enter a correct value." );
			cancelEdit();
		}

	} // else ( of: if !editMode )

} //edit()


void propField::cancelEdit()
{
	vEdit->hide();
	vChange->setText( "." );
	editMode = false;
}


void propField::focusOutEvent( QFocusEvent * )
{
	activate(false);
}


/*******************************************************************************
*	class propEditWidget:
*******************************************************************************/

propEditWidget::propEditWidget( QString props[], QString vals[],
	bool allowMLCaption, QWidget *parent )
	: QWidget( parent )
{
int i;

	for ( i = 0; i < 100; i++ ) pf[i] = 0L;

	for ( i = 0; props[i] != ""; i++ )
	{

		if ( i == 99 )
			fatal( "Too many properties to property editor (Internal error)." );

		edit[0][i] = props[i];
		edit[1][i] = vals[i];

		pf[i] = new propField( props[i], vals[i], this );

		if( props[i] == "Caption" && allowMLCaption )
			pf[i]->setML(true);

		connect( pf[i], SIGNAL( changed(QString,QString,propField*) ),
			SLOT( prChanged(QString,QString,propField*) ) );
	}

	i++; edit[0][i] = "";
	cR = false;

	resize ( width(), (i-1) * PROP_FIELD_HEIGHT );

}


void propEditWidget::resizeEvent( QResizeEvent * )
{
	for ( int i = 0; pf[i] != 0L; i++ )
		pf[i]->setGeometry( 0, i * PROP_FIELD_HEIGHT, width(), PROP_FIELD_HEIGHT );
}


bool propEditWidget::changeAccepted( QString p, QString v )
{
	if( p == "Name" )
	{
		emit checkName(v);

		if (!cR)
		{
			KMsgBox::message( 0L, "Name allready used.",
				QString ( "There's another widget with\n" )
					+( "name " ) +v +( " in this (sub)\n" )
					+( "window. Please choose another name." ) );

			return false;
		}
	}

	if( p == "Width" )
	{
		// Width >= MinimalWidth >= MaximalWidth
	}

	//if ...
	return true;
}


void propEditWidget::prChanged( QString p, QString v, propField *pcf ) //SLOT
{
int i;

	for ( i = 0; edit[0][i] != p; i++ )
	{
		if ( i == 99 )
			fatal ( "Unexpected internal error." );
	}


	if( changeAccepted(p,v) )
	{
		edit[1][i] = v;

		emit changed(p,v);
	}
	else
		pcf->rejectChange();

}


/*******************************************************************************
*	class propEditDialog:
*******************************************************************************/

propEditDialog::propEditDialog( QString props[], QString vals[], bool mlc )
	: QDialog( 0L, "", true )
{
QString weName = "(unknown widget)";
int i;

	wid = new propEditWidget( props, vals, mlc, this );

	connect( wid, SIGNAL(changed(QString,QString)), SIGNAL(changed(QString,QString)) );
	connect( wid, SIGNAL(checkName(QString)), SIGNAL(checkName(QString)) );

	bt = new QPushButton( "Close", this );

	connect( bt, SIGNAL(clicked()), SLOT(close()) );

	setMinimumSize( DIALOG_WIDTH, 150 );
	setMaximumWidth( DIALOG_WIDTH );

	resize( DIALOG_WIDTH, 300 );

	for( i = 0; props[i] != ""; i++ )
		if( props[i].lower() == "name" )
		{
			weName = vals[i];
			break;
		}

	setCaption( QString ("Properties of \"") +weName +("\"") );

	haveScrollBar = false;

}


void propEditDialog::nameOk(bool b) //SLOT
{
	wid->setCheckResult(b);
}


void propEditDialog::resizeEvent( QResizeEvent * )
{

	if (haveScrollBar)
	{
		delete scroll;
		haveScrollBar = false;
	}

	if ( wid->height() +30 > height() )
	{
		wid->setGeometry( 0, 0, width() -15, wid->height() );

		scroll = new QScrollBar( 0, wid->height()+30 -height(),
			1, 20, 0, QScrollBar::Vertical, this );

		connect( scroll, SIGNAL( valueChanged(int) ), SLOT( scrolled(int) ) );

		scroll->setGeometry( width()-15, 0, 15, height() -30 );

		scroll->show();

		haveScrollBar = true;
	}
	else
	{
		wid->setGeometry( 0, 0, width(), wid->height() );
	}

	bt->setGeometry( 0, height()-30, width(), 30 );

	bt->raise();
}

void propEditDialog::close() //SLOT
{
	accept();
}

void propEditDialog::scrolled( int newPos ) //SLOT
{
	wid->move( 0, -newPos );
}
