package semorg.gui;

import java.sql.SQLException;
import java.sql.Timestamp;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.Text;

import semorg.gui.list.LecturerListWindow;
import semorg.gui.util.AssociationTabControl;
import semorg.gui.util.CalendarControl;
import semorg.gui.util.ItemListener;
import semorg.gui.util.Messages;
import semorg.gui.util.CalendarControl.SWTCalendarEvent;
import semorg.gui.util.CalendarControl.SWTCalendarListener;
import semorg.sql.tables.AbstractTable;
import semorg.sql.tables.Enumeration;
import semorg.sql.tables.Lecturer;
import semorg.sql.tables.SimpleIDKey;
import semorg.sql.util.Utility;

/** The window for editing new or existing lecturer records. */
public class LecturerWindow {

    /**
     * Inner class which provides a listener reacting on changes of the input in
     * this window.
     */
    class InputChangedListener implements ModifyListener, SelectionListener,
	    SWTCalendarListener, ItemListener {
	/**
	 * Appends to the title of the window an asterisk (*) if the input has
	 * changed. Furthermore it sets the flag
	 * {@link semorg.gui.LecturerWindow#inputChanged} to <tt>true</tt>.
	 */
	private void inputChanged() {
	    if (!settingInput && !inputChanged) {
		inputChanged = true;
		shell.setText(shell.getText() + "*");
	    }
	}

	/** Invokes {@link #inputChanged()} if a text has been changed. */
	public void modifyText(ModifyEvent e) {
	    inputChanged();
	}

	/** Invokes {@link #inputChanged()} if a widget has been selected. */
	public void widgetSelected(SelectionEvent e) {
	    inputChanged();
	}

	/** Invokes {@link #inputChanged()} if a doubleclick has occured. */
	public void widgetDefaultSelected(SelectionEvent e) {
	    inputChanged();
	}

	/** Invokes {@link #inputChanged()} if a date has been changed. */
	public void dateChanged(SWTCalendarEvent event) {
	    inputChanged();
	}

	/**
	 * Invokes {@link #inputChanged()} if a item in the
	 * AssociationTabControl has changed.
	 */
	public void itemChanged() {
	    inputChanged();
	}
    }

    /**
     * Holds the data of the actual displayed lecturer record. If a new record
     * is entered it is set to <tt>null</tt> until the changes have been
     * committed.
     */
    private Lecturer input;

    /**
     * Is set to <tt>true</tt> if the data from the database (or corresponding
     * from {@link #input}) is to be set into the window form, otherwise
     * <tt>false</tt>. <br>
     * <br>
     * 
     * That is needed to prevent the ModifyListener from firing when the data is
     * load into the window form by using the .setText() methods of the gui
     * elements.
     */
    private boolean settingInput = false;

    /**
     * Is set to <tt>true</tt> if the input has been changed and not committed
     * yet, otherwise it is set to <tt>false</tt>.
     */
    private boolean inputChanged;

    /**
     * <tt>true</tt> if the editing of the input (i.e. the elements of the
     * form) has been enabled, otherwise it is <tt>false</tt>. <br>
     * <br>
     * 
     * This flag indicates whether the actual displayed record has been locked
     * by the actual editing window or not. This avoids the releasing of records
     * which are locked by other editing windows.
     */
    private boolean inputEditingEnabled;

    /** Listener instance added to the most elements of the window. */
    private InputChangedListener inputChangedListener = new InputChangedListener();

    /* ********** GUI PROPERTIES ********* */
    /** The editing window. */
    private Shell shell;

    /** The height of the title bar. */
    private int titleBarHeight;

    /* Text- and Combofields, CalendarControls and Buttons */
    private final Label numberLabel;
    private final Text numberText;
    private final Combo salutationCombo;
    private final Combo titleCombo;
    private final Text firstNameText;
    /** Text field for the lastname of the lecurer. */
    private final Text nameText;
    private final Text streetText;
    private final Text zipCodeText;
    private final Text cityText;
    private final Combo countryCombo;
    private final Combo annexCombo;
    private final Text phoneText;
    private final Text mobileText;
    private final Text faxText;
    private final Text emailText;
    private final CalendarControl birthDayCalendar;
    private final CalendarControl firstContactCalendar;
    /** The text field which shows the biography of the lecturer record. */
    private final Text bioText;
    private final Text dailyFeeText;
    private final Text hourlyFeeText;
    private final Text shortMessageText;
    private final Text memoText;

    /** The control widget to manage the association supervisor. */
    private AssociationTabControl supervisorControl;
    /** The control widget to manage the association instructor. */
    private AssociationTabControl instructorControl;
    /** The control widget to manage the association conduct. */
    private AssociationTabControl conductControl;

    private final Button okButton;
    private final Button applyButton;
    private final Button newButton;
    private final Button abortButton;
    /** The triangle-button with the apex to the left. */
    private final Button prevButton;
    /** The triangle-button with the apex to the right. */
    private final Button nextButton;
    private final Button addressLabelButton;

    /**
     * Creates a LecturerWindow instance with the given shell as the parent
     * shell. <br>
     * <br>
     * 
     * It also adds a listener which reacts on closing events. It checks if
     * changes have been made and if so it opens a dialog which informs the user
     * and asks for a decision (confirm/discard). Finally it releases the locked
     * record (if one is displayed and it is locked by this editing window).
     * 
     * @param parent
     *                Shell instance used the as parent shell of the lecturer
     *                window.
     * @param caller
     *                {@link AssociationTabControl} instance which called the
     *                editing window. If editing window is called by a menu this
     *                parameter is <tt>null</tt>.
     */
    private LecturerWindow(Shell parent, final AssociationTabControl caller) {
	// corresponds to a function createContents()
	shell = new Shell(parent, SWT.CLOSE);

	shell.addListener(SWT.Close, new Listener() {
	    public void handleEvent(Event event) {
		if (inputChanged) {
		    if (confirmClose()) {
			// is a record displayed and did we locked it?
			if (inputEditingEnabled && input != null)
			    Lecturer.lockedIds
				    .remove(new Integer(input.getId()));
		    } else
			event.doit = false; // cancel this operation
		}
		// is a record displayed and did we locked it?
		else if (input != null && inputEditingEnabled)
		    Lecturer.lockedIds.remove(new Integer(input.getId()));
	    }

	});

	shell.setLayout(new GridLayout(1, false));
	shell.setSize(730, 550);
	shell.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "LecturerWindow");

	GridData mgd = new GridData();
	mgd.horizontalAlignment = GridData.FILL;
	mgd.verticalAlignment = GridData.FILL;
	mgd.grabExcessHorizontalSpace = true;
	mgd.grabExcessVerticalSpace = true;

	final Group mainForm = new Group(shell, SWT.NULL);
	mainForm.setLayoutData(mgd);
	mainForm.setLayout(new GridLayout(2, false));

	final TabFolder tabs = new TabFolder(shell, SWT.FLAT);
	tabs.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "Tabs");
	GridData tgd = new GridData();
	tgd.heightHint = 130;
	tgd.horizontalAlignment = GridData.FILL;
	tgd.grabExcessHorizontalSpace = true;
	tabs.setLayoutData(tgd);

	final Composite leftColumn = new Composite(mainForm, SWT.NULL);
	leftColumn.setLayout(new GridLayout(1, false));
	GridData lcgd = new GridData();
	lcgd.widthHint = 380;// 350
	lcgd.horizontalAlignment = GridData.FILL;
	lcgd.grabExcessHorizontalSpace = true;
	lcgd.verticalAlignment = GridData.BEGINNING;
	leftColumn.setLayoutData(lcgd);

	final Composite rightColumn = new Composite(mainForm, SWT.NULL);
	rightColumn.setLayout(new GridLayout(1, false));
	GridData rightColumnGridData = new GridData();
	rightColumnGridData.horizontalAlignment = GridData.FILL;
	rightColumnGridData.grabExcessHorizontalSpace = true;
	rightColumn.setLayoutData(rightColumnGridData);

	Composite numberComp = new Composite(leftColumn, SWT.NULL);
	GridLayout numGridLayout = new GridLayout(2, false);
	numGridLayout.verticalSpacing = 0;
	numGridLayout.marginTop = 0;
	numGridLayout.marginBottom = 0;
	numberComp.setLayout(numGridLayout);

	numberLabel = new Label(numberComp, SWT.READ_ONLY);
	numberLabel.setText(Messages.getString("GUIText.NumberText"));
	numberLabel.setAlignment(SWT.RIGHT);
	GridData numberLabelGridData = new GridData();
	numberLabelGridData.widthHint = 58;
	numberLabel.setLayoutData(numberLabelGridData);

	numberText = new Text(numberComp, SWT.SINGLE | SWT.BORDER
		| SWT.READ_ONLY);
	numberText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "NumberText");
	Utility.setLength(numberText, 10);
	numberText.addModifyListener(inputChangedListener);
	numberText.setToolTipText(Messages.getString("GUIText.NumberToolTip"));

	final Group nameField = new Group(leftColumn, SWT.NULL);
	nameField.setText(Messages.getString("GUIText.NameFieldText"));
	GridLayout nameGridLayout = new GridLayout(2, false);
	nameGridLayout.marginTop = 0;
	nameField.setLayout(nameGridLayout);
	GridData nameFieldGridData = new GridData();
	nameFieldGridData.horizontalAlignment = GridData.FILL;
	nameFieldGridData.grabExcessHorizontalSpace = true;
	nameField.setLayoutData(nameFieldGridData);

	final Group contactField = new Group(rightColumn, SWT.NULL);
	contactField.setText(Messages.getString("GUIText.ContactFieldText"));
	GridLayout contactGridLayout = new GridLayout(2, false);
	contactGridLayout.marginHeight = 11;

	contactField.setLayout(contactGridLayout);
	GridData contactFieldGridData = new GridData();
	contactFieldGridData.horizontalAlignment = GridData.FILL;
	contactFieldGridData.grabExcessHorizontalSpace = true;
	contactField.setLayoutData(contactFieldGridData);

	final Label salutationLabel = new Label(nameField, SWT.READ_ONLY);
	salutationLabel.setText(Messages.getString("GUIText.SalutationText"));
	salutationLabel.setAlignment(SWT.RIGHT);
	GridData salutationLabelGridData = new GridData();
	salutationLabelGridData.widthHint = 55;
	salutationLabel.setLayoutData(salutationLabelGridData);

	Composite nameRowCompletion = new Composite(nameField, SWT.NULL);
	GridLayout rcgl = new GridLayout(3, false);
	rcgl.marginWidth = 0;
	rcgl.marginHeight = 0;
	nameRowCompletion.setLayout(rcgl);

	salutationCombo = new Combo(nameRowCompletion, SWT.DROP_DOWN
		| SWT.BORDER);
	Enumeration.addSalutationEnums(salutationCombo);
	salutationCombo.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"SalutationCombo");
	salutationCombo.setBackground(Utility.LIGHT_YELLOW);
	salutationCombo.addFocusListener(Utility.checkEmptyListener);
	salutationCombo.setTextLimit(13);
	GridData scgd = new GridData();
	scgd.widthHint = 80;
	salutationCombo.setLayoutData(scgd);
	salutationCombo.setToolTipText(Messages
		.getString("GUIText.SalutationToolTip"));
	salutationCombo.addModifyListener(inputChangedListener);

	final Label titleLabel = new Label(nameRowCompletion, SWT.READ_ONLY);
	titleLabel.setText(Messages.getString("GUIText.TitelText"));

	titleCombo = new Combo(nameRowCompletion, SWT.DROP_DOWN | SWT.BORDER);
	titleCombo.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "TitleCombo");
	Enumeration.addTitleEnums(titleCombo);
	Utility.setLength(titleCombo, 13);
	titleCombo.setToolTipText(Messages.getString("GUIText.TitelToolTip"));
	titleCombo.addModifyListener(inputChangedListener);

	final Label firstNameLabel = new Label(nameField, SWT.READ_ONLY);
	firstNameLabel.setText(Messages.getString("GUIText.FirstNameText"));
	Utility.alignRight(firstNameLabel);

	firstNameText = new Text(nameField, SWT.CHECK | SWT.SINGLE | SWT.BORDER);
	firstNameText
		.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "FirstNameText");
	Utility.setLength(firstNameText, 30);
	firstNameText.setToolTipText(Messages
		.getString("GUIText.FirstNameToolTip"));
	firstNameText.addModifyListener(inputChangedListener);

	final Label nameLabel = new Label(nameField, SWT.READ_ONLY);
	nameLabel.setText(Messages.getString("GUIText.LastNameText"));
	Utility.alignRight(nameLabel);

	nameText = new Text(nameField, SWT.SINGLE | SWT.BORDER);
	nameText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "NameText");
	Utility.setLength(nameText, 30);
	nameText.setToolTipText(Messages.getString("GUIText.LastNameToolTip"));
	nameText.setBackground(Utility.LIGHT_YELLOW);
	nameText.addFocusListener(Utility.checkEmptyListener);
	nameText.addModifyListener(inputChangedListener);

	final Group addressField = new Group(leftColumn, SWT.NULL);
	addressField.setText(Messages.getString("GUIText.AdressFieldText"));
	addressField.setLayout(new GridLayout(2, false));
	GridData addressFieldGridData = new GridData();
	addressFieldGridData.horizontalAlignment = GridData.FILL;
	addressFieldGridData.grabExcessHorizontalSpace = true;
	addressField.setLayoutData(addressFieldGridData);

	final Label streetLabel = new Label(addressField, SWT.READ_ONLY);
	streetLabel.setText(Messages.getString("GUIText.StreetText"));
	streetLabel.setAlignment(SWT.RIGHT);
	GridData streetLabelGridData = new GridData();
	streetLabelGridData.widthHint = 55;
	streetLabel.setLayoutData(streetLabelGridData);

	streetText = new Text(addressField, SWT.SINGLE | SWT.BORDER);
	streetText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "StreetText");
	Utility.setLength(streetText, 50);
	streetText.setToolTipText(Messages.getString("GUIText.StreetToolTip"));
	streetText.addModifyListener(inputChangedListener);

	final Label cityLabel = new Label(addressField, SWT.READ_ONLY);
	cityLabel.setText(Messages.getString("GUIText.CityZipCodeText"));
	Utility.alignRight(cityLabel);

	Composite cityRowCompletion = new Composite(addressField, SWT.NULL);
	cityRowCompletion.setLayout(rcgl);

	zipCodeText = new Text(cityRowCompletion, SWT.SINGLE | SWT.BORDER);
	zipCodeText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "ZipCodeText");
	Utility.setLength(zipCodeText, 5);
	zipCodeText
		.setToolTipText(Messages.getString("GUIText.ZipCodeToolTip"));
	zipCodeText.addModifyListener(inputChangedListener);

	cityText = new Text(cityRowCompletion, SWT.SINGLE | SWT.BORDER);
	cityText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "CityText");
	Utility.setLength(cityText, 30);
	cityText.setToolTipText(Messages.getString("GUIText.CityToolTip"));
	cityText.addModifyListener(inputChangedListener);

	final Label countryLabel = new Label(addressField, SWT.READ_ONLY);
	countryLabel.setText(Messages.getString("GUIText.CountryText"));
	Utility.alignRight(countryLabel);

	Composite countryRowCompletion = new Composite(addressField, SWT.NULL);
	countryRowCompletion.setLayout(rcgl);

	countryCombo = new Combo(countryRowCompletion, SWT.DROP_DOWN
		| SWT.BORDER);
	countryCombo.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "CountryCombo");
	Enumeration.addCountryEnums(countryCombo);
	Utility.setLength(countryCombo, 13);
	countryCombo.addModifyListener(inputChangedListener);
	countryCombo.setToolTipText(Messages
		.getString("GUIText.CountryToolTip"));

	final Label annexLabel = new Label(countryRowCompletion, SWT.READ_ONLY);
	annexLabel.setText(Messages.getString("GUIText.AnnexText"));

	annexCombo = new Combo(countryRowCompletion, SWT.DROP_DOWN | SWT.BORDER);
	annexCombo.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "AnnexCombo");
	Enumeration.addAnnexEnums(annexCombo);
	Utility.setLength(annexCombo, 13);
	annexCombo.setToolTipText(Messages.getString("GUIText.AnnexToolTip"));
	annexCombo.addModifyListener(inputChangedListener);

	final Label phoneLabel = new Label(contactField, SWT.READ_ONLY);
	phoneLabel.setText(Messages.getString("GUIText.PhoneText"));
	phoneLabel.setAlignment(SWT.RIGHT);
	GridData phoneLabelGridData = new GridData();
	phoneLabelGridData.widthHint = 100;
	phoneLabel.setLayoutData(phoneLabelGridData);

	phoneText = new Text(contactField, SWT.SINGLE | SWT.BORDER);
	phoneText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "PhoneText");
	Utility.setLength(phoneText, 20);
	phoneText.setToolTipText(Messages.getString("GUIText.PhoneToolTip"));
	phoneText.addModifyListener(inputChangedListener);

	final Label mobileLabel = new Label(contactField, SWT.READ_ONLY);
	mobileLabel.setText(Messages.getString("GUIText.MobileText"));
	Utility.alignRight(mobileLabel);

	mobileText = new Text(contactField, SWT.SINGLE | SWT.BORDER);
	mobileText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "MobileText");
	Utility.setLength(mobileText, 20);
	mobileText.setToolTipText(Messages.getString("GUIText.MobileToolTip"));
	mobileText.addModifyListener(inputChangedListener);

	final Label faxLabel = new Label(contactField, SWT.READ_ONLY);
	faxLabel.setText(Messages.getString("GUIText.FaxText"));
	Utility.alignRight(faxLabel);

	faxText = new Text(contactField, SWT.SINGLE | SWT.BORDER);
	faxText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "FaxText");
	Utility.setLength(faxText, 20);
	faxText.setToolTipText(Messages.getString("GUIText.FaxToolTip"));
	faxText.addModifyListener(inputChangedListener);

	final Label emailLabel = new Label(contactField, SWT.READ_ONLY);
	emailLabel.setText(Messages.getString("GUIText.EmailText"));
	Utility.alignRight(emailLabel);

	emailText = new Text(contactField, SWT.SINGLE | SWT.BORDER);
	emailText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "EmailText");
	Utility.setLength(emailText, 256, 20);
	emailText.setToolTipText(Messages.getString("GUIText.EmailToolTip"));
	emailText.addModifyListener(inputChangedListener);

	Composite additionalComponents = new Composite(rightColumn, SWT.NULL);
	additionalComponents.setLayout(new GridLayout(2, false));

	Label birthDayLabel = new Label(additionalComponents, SWT.READ_ONLY);
	birthDayLabel.setText(Messages.getString("GUIText.BirthdayText"));
	birthDayLabel.setAlignment(SWT.RIGHT);
	GridData birthDayLabelGridData = new GridData();
	birthDayLabelGridData.widthHint = 103;
	birthDayLabel.setLayoutData(birthDayLabelGridData);

	birthDayCalendar = new CalendarControl(additionalComponents,
		titleBarHeight);
	birthDayCalendar.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"BirthDayCalendar");
	birthDayCalendar.addSWTCalendarlistener(inputChangedListener);
	birthDayCalendar.setToolTipText(Messages
		.getString("GUIText.BirthdayToolTip"));

	Label firstContactLabel = new Label(additionalComponents, SWT.READ_ONLY);
	firstContactLabel.setText(Messages
		.getString("GUIText.FirstContactText"));
	Utility.alignRight(firstContactLabel);

	firstContactCalendar = new CalendarControl(additionalComponents,
		titleBarHeight);
	firstContactCalendar.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"FirstContactCalendar");
	firstContactCalendar.addSWTCalendarlistener(inputChangedListener);
	firstContactCalendar.setToolTipText(Messages
		.getString("GUIText.FirstContactToolTip"));

	final Group feeGroup = new Group(rightColumn, SWT.NULL);
	feeGroup.setText(Messages.getString("GUIText.LecturerFeeFieldText"));
	feeGroup.setLayout(new GridLayout(2, false));
	GridData feeGroupGridData = new GridData();
	feeGroupGridData.horizontalAlignment = GridData.FILL;
	feeGroupGridData.grabExcessHorizontalSpace = true;
	feeGroup.setLayoutData(feeGroupGridData);

	Label dailyFeeLabel = new Label(feeGroup, SWT.NULL);
	dailyFeeLabel.setText(Messages
		.getString("GUIText.LecturerDailyFeeText"));
	Utility.alignRight(dailyFeeLabel);

	dailyFeeText = new Text(feeGroup, SWT.SINGLE | SWT.BORDER);
	dailyFeeText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "DailyFeeText");
	Utility.setLength(dailyFeeText, 10);
	dailyFeeText.addModifyListener(inputChangedListener);
	dailyFeeText.addFocusListener(Utility.checkCorrectFloatListener);
	dailyFeeText.setToolTipText(Messages
		.getString("GUIText.LecturerDailyFeeToolTip"));

	Label hourlyFeeLabel = new Label(feeGroup, SWT.NULL);
	hourlyFeeLabel.setText(Messages
		.getString("GUIText.LecturerHourlyFeeText"));
	Utility.alignRight(hourlyFeeLabel);

	hourlyFeeText = new Text(feeGroup, SWT.SINGLE | SWT.BORDER);
	hourlyFeeText
		.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "HourlyFeeText");
	Utility.setLength(hourlyFeeText, 10);
	hourlyFeeText.addModifyListener(inputChangedListener);
	hourlyFeeText.addFocusListener(Utility.checkCorrectFloatListener);
	hourlyFeeText.setToolTipText(Messages
		.getString("GUIText.LecturerHourlyFeeToolTip"));

	bioText = Utility.createTextTab(tabs, Messages
		.getString("GUIText.LecturerBiographyText"), null, 400);
	bioText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "BioText");
	bioText.addModifyListener(inputChangedListener);

	shortMessageText = Utility.createTextTab(tabs, Messages
		.getString("GUIText.ShortMessageText"), null, 200);
	shortMessageText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"ShortMessageText");
	shortMessageText.addModifyListener(inputChangedListener);
	memoText = Utility.createTextTab(tabs, Messages
		.getString("GUIText.MemoText"), null, 200);
	memoText.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "MemoText");
	memoText.addModifyListener(inputChangedListener);

	TabItem supervisorItem = new TabItem(tabs, SWT.NULL);
	supervisorItem.setText(Messages.getString("GUIText.SupervisorText"));
	supervisorControl = new AssociationTabControl(tabs,
		AssociationTabControl.TYPE_PRESENTATION_SUPERVISOR, shell);
	supervisorControl.addItemListener(inputChangedListener);
	supervisorControl.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"SupervisorControl");
	supervisorItem.setControl(supervisorControl);

	TabItem instructorItem = new TabItem(tabs, SWT.NULL);
	instructorItem.setText(Messages.getString("GUIText.InstructorText"));
	instructorControl = new AssociationTabControl(tabs,
		AssociationTabControl.TYPE_PRESENTATION_INSTRUCTOR, shell);
	instructorControl.addItemListener(inputChangedListener);
	instructorControl.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"InstructorControl");
	instructorItem.setControl(instructorControl);

	TabItem conductItem = new TabItem(tabs, SWT.NULL);
	conductItem.setText(Messages.getString("GUIText.ConductText")); // tab
	conductControl = new AssociationTabControl(tabs, // contents of tab
		AssociationTabControl.TYPE_SEMINARTYPE_CONDUCT, shell);
	conductControl.addItemListener(inputChangedListener);
	conductControl.setData(Utility.ATOSJ_COMPONENT_NAME_KEY,
		"ConductControl");
	conductItem.setControl(conductControl);

	final Composite buttons = new Composite(shell, shell.getStyle());
	buttons.setLayout(new GridLayout(7, false));

	okButton = new Button(buttons, SWT.PUSH);
	okButton.setText(Messages.getString("GUIText.OkButtonText"));
	okButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	applyButton = new Button(buttons, SWT.PUSH);
	applyButton.setText(Messages.getString("GUIText.ApplyButtonText"));
	applyButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	newButton = new Button(buttons, SWT.PUSH);
	newButton.setText(Messages.getString("GUIText.NewButtonText"));
	newButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	abortButton = new Button(buttons, SWT.PUSH);
	abortButton.setText(Messages.getString("GUIText.AbortButtonText"));
	abortButton.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
		| GridData.GRAB_VERTICAL));

	GridData arrowButtonGridData = new GridData();
	arrowButtonGridData.verticalAlignment = GridData.FILL;
	arrowButtonGridData.grabExcessVerticalSpace = true;
	arrowButtonGridData.widthHint = 23;

	prevButton = new Button(buttons, SWT.ARROW | SWT.LEFT);
	prevButton.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "Previous");
	prevButton.setLayoutData(arrowButtonGridData);

	nextButton = new Button(buttons, SWT.ARROW | SWT.RIGHT);
	nextButton.setData(Utility.ATOSJ_COMPONENT_NAME_KEY, "Next");
	nextButton.setLayoutData(arrowButtonGridData);

	addressLabelButton = new Button(buttons, SWT.PUSH);
	addressLabelButton.setText(Messages
		.getString("GUIText.AddressButtonText"));
	addressLabelButton.setEnabled(true);
	addressLabelButton.setLayoutData(new GridData(
		GridData.VERTICAL_ALIGN_FILL | GridData.GRAB_VERTICAL));

	createButtonListener(buttons, caller);
    }

    /**
     * Creates an instance of this class, sets the input and finally opens it.
     * 
     * @param parent
     *                the parent shell to which this window belongs
     * @param input
     *                the lecturer data to be displayed in the window
     */
    public LecturerWindow(Shell parent, Lecturer input,
	    final AssociationTabControl caller) {
	this(parent, caller);

	setInput(input);
	shell.open();
    }

    /**
     * Loads a given lecturer record into the window form of initialize the
     * elements of the form the empty string/null if no data is given.
     * 
     * @param lecturer
     *                Lecturer instance which contains the data to be displayed
     *                or <tt>null</tt> if a completely new record is to be
     *                edited.
     */
    private void setInput(Lecturer lecturer) {
	/* STEP 1: switch off the listeners */
	settingInput = true;

	/* STEP 2: unlock the actually displayed data record (if exists) */
	if (this.input != null && inputEditingEnabled)
	    Lecturer.lockedIds.remove(new Integer(this.input.getId()));

	/*
	 * STEP 3: loading the given (new) lecturer instance into the window
	 * form (if not null else initialize the elements)
	 */
	if (lecturer != null) {
	    salutationCombo.setFocus();
	    numberText.setText(new Integer(lecturer.getId()).toString());
	    numberLabel.setEnabled(true);

	    if (lecturer.getSalutation() != null)
		salutationCombo.setText(lecturer.getSalutation());
	    else
		salutationCombo.setText("");
	    if (lecturer.getTitle() != null)
		titleCombo.setText(lecturer.getTitle());
	    else
		titleCombo.setText("");
	    if (lecturer.getFirstname() != null)
		firstNameText.setText(lecturer.getFirstname());
	    else
		firstNameText.setText("");
	    if (lecturer.getName() != null)
		nameText.setText(lecturer.getName());
	    else
		nameText.setText("");
	    if (lecturer.getStreet() != null)
		streetText.setText(lecturer.getStreet());
	    else
		streetText.setText("");
	    if (lecturer.getZipCode() != null)
		zipCodeText.setText(lecturer.getZipCode());
	    else
		zipCodeText.setText("");
	    if (lecturer.getCity() != null)
		cityText.setText(lecturer.getCity());
	    else
		cityText.setText("");
	    if (lecturer.getCountry() != null)
		countryCombo.setText(lecturer.getCountry());
	    else
		countryCombo.setText("");
	    if (lecturer.getAnnex() != null)
		annexCombo.setText(lecturer.getAnnex());
	    else
		annexCombo.setText("");
	    if (lecturer.getPhone() != null)
		phoneText.setText(lecturer.getPhone());
	    else
		phoneText.setText("");
	    if (lecturer.getMobile() != null)
		mobileText.setText(lecturer.getMobile());
	    else
		mobileText.setText("");
	    if (lecturer.getFax() != null)
		faxText.setText(lecturer.getFax());
	    else
		faxText.setText("");
	    if (lecturer.getEmail() != null)
		emailText.setText(lecturer.getEmail());
	    else
		emailText.setText("");

	    birthDayCalendar.setDate(lecturer.getBirthday());
	    firstContactCalendar.setDate(lecturer.getFirstContact());

	    if (lecturer.getBio() != null)
		bioText.setText(lecturer.getBio());
	    else
		bioText.setText("");

	    if (lecturer.getDailyFee() != null)
		dailyFeeText.setText(Utility.createFloatText(lecturer
			.getDailyFee()));
	    else
		dailyFeeText.setText("");
	    if (lecturer.getHourlyFee() != null)
		hourlyFeeText.setText(Utility.createFloatText(lecturer
			.getHourlyFee()));
	    else
		hourlyFeeText.setText("");

	    if (lecturer.getShortinfo() != null)
		shortMessageText.setText(lecturer.getShortinfo());
	    else
		shortMessageText.setText("");
	    if (lecturer.getNotices() != null)
		memoText.setText(lecturer.getNotices());
	    else
		memoText.setText("");

	    try {
		supervisorControl.setInput(lecturer.getId());
		instructorControl.setInput(lecturer.getId());
		conductControl.setInput(lecturer.getId());
	    } catch (SQLException e) {/* TODO */
	    }
	} else {
	    // no data given (lecturer == null): initialize elements
	    numberLabel.setEnabled(false);
	    numberText.setEnabled(false);
	    numberText.setText("");
	    salutationCombo.setText("");
	    salutationCombo.setFocus();
	    titleCombo.setText("");
	    firstNameText.setText("");
	    nameText.setText("");
	    streetText.setText("");
	    zipCodeText.setText("");
	    cityText.setText("");
	    countryCombo.setText("");
	    annexCombo.setText("");
	    phoneText.setText("");
	    mobileText.setText("");
	    faxText.setText("");
	    emailText.setText("");
	    birthDayCalendar.setDate(null);
	    firstContactCalendar.setDate(null);
	    bioText.setText("");
	    dailyFeeText.setText("");
	    hourlyFeeText.setText("");
	    shortMessageText.setText("");
	    memoText.setText("");

	    try {
		supervisorControl.setInput(AbstractTable.NULL_ID);
		instructorControl.setInput(AbstractTable.NULL_ID);
		conductControl.setInput(AbstractTable.NULL_ID);
	    } catch (SQLException e) {/* TODO */
	    }
	}

	/*
	 * STEP 4: Check if the given record lecturer is already locked, i.e. it
	 * is already edited (if lecturer is not null, else enable the
	 * elements). -> yes: disable the form elements -> no: enable them and
	 * lock the new record
	 */
	if (lecturer != null) {
	    boolean alreadyEditing = Lecturer.lockedIds.contains(new Integer(
		    lecturer.getId()));
	    setInputComponentsEnabled(!alreadyEditing);

	    if (!alreadyEditing) {
		Lecturer.lockedIds.add(new Integer(lecturer.getId()));
	    }
	} else
	    // completely new data record
	    setInputComponentsEnabled(true);

	/*
	 * STEP 5: set input to the new lecturer, inputChanged to true and
	 * enable next/prev buttons (if lecturer != null else...)
	 */
	this.input = lecturer;
	inputChanged = (lecturer == null);

	prevButton.setEnabled(this.input != null);
	nextButton.setEnabled(this.input != null);

	/* STEP 6: set the window title */
	String shellText;
	if (input == null)
	    shellText = Messages.getString("GUIText.NewItemWindowTitleText");
	else
	    shellText = input.getId() + ", " + input.getSalutation() + ", "
		    + input.getName() + ", ";

	shellText += Messages.getString("GUIText.LecturerWindowTitleText");
	if (input == null)
	    shellText += "*";
	shell.setText(shellText);

	/* STEP 7: switch on the listeners */
	settingInput = false;
    }

    /**
     * According to the value of the given parameter (<tt>true</tt>/<tt>false</tt>)
     * this method enables / disables the widgets of the window form.<br>
     * <br>
     * 
     * <i>Note:</i> It also sets the member {@link #inputEditingEnabled} to the
     * value of the parameter to signal that editing of the input has been
     * enabled or not.
     * 
     * @param enabled
     *                if equals <tt>true</tt> the widgets are enabled
     *                otherwise the widgets get disabled.
     */
    private void setInputComponentsEnabled(boolean enabled) {
	numberText.setEnabled(enabled);
	salutationCombo.setEnabled(enabled);
	titleCombo.setEnabled(enabled);
	firstNameText.setEnabled(enabled);
	nameText.setEnabled(enabled);
	streetText.setEnabled(enabled);
	zipCodeText.setEnabled(enabled);
	cityText.setEnabled(enabled);
	countryCombo.setEnabled(enabled);
	annexCombo.setEnabled(enabled);
	phoneText.setEnabled(enabled);
	mobileText.setEnabled(enabled);
	faxText.setEnabled(enabled);
	emailText.setEnabled(enabled);
	birthDayCalendar.setEnabled(enabled);
	firstContactCalendar.setEnabled(enabled);
	bioText.setEnabled(enabled);
	dailyFeeText.setEnabled(enabled);
	hourlyFeeText.setEnabled(enabled);

	shortMessageText.setEnabled(enabled);
	memoText.setEnabled(enabled);
	supervisorControl.setEnabled(enabled);
	instructorControl.setEnabled(enabled);
	conductControl.setEnabled(enabled);

	okButton.setEnabled(enabled);
	applyButton.setEnabled(enabled);
	newButton.setEnabled(enabled);
	abortButton.setEnabled(enabled);
	prevButton.setEnabled(enabled);
	nextButton.setEnabled(enabled);
	// addressLabelButton.setEnabled(enabled);

	inputEditingEnabled = enabled;
    }

    /**
     * Adds a listener to several buttons of the window form.<br>
     * <br>
     * 
     * This involves at the moment the buttons {@link #okButton},
     * {@link #abortButton}, {@link #applyButton}, {@link #newButton},
     * {@link #prevButton}, {@link #nextButton}.
     */
    private void createButtonListener(Composite buttons,
	    final AssociationTabControl caller) {

	if (caller == null) { // normal mode, no caller

	    okButton.addListener(SWT.Selection, new Listener() {
		public void handleEvent(Event event) {
		    if (onSave())
			shell.close();
		}
	    });
	    applyButton.addListener(SWT.Selection, new Listener() {
		public void handleEvent(Event arg0) {
		    onSave();
		}
	    });
	} else {// caller mode

	    okButton.addListener(SWT.Selection, new Listener() {
		public void handleEvent(Event event) {
		    if (onSave()) {
			caller.addSelectedIds(new SimpleIDKey[] { input },
				false);
			shell.close();
		    }
		}
	    });
	    applyButton.addListener(SWT.Selection, new Listener() {
		public void handleEvent(Event arg0) {
		    if (onSave()) {
			caller.addSelectedIds(new SimpleIDKey[] { input },
				false);
		    }
		}
	    });
	}

	newButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		if (onSave())
		    setInput(null);
	    }
	});

	abortButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		shell.close();
	    }
	});

	prevButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		if (inputChanged) {
		    if (confirmClose())
			setInput(Lecturer.getPrevious(input.getId()));
		} else
		    setInput(Lecturer.getPrevious(input.getId()));
	    }
	});

	nextButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		if (inputChanged) {
		    if (confirmClose())
			setInput(Lecturer.getNext(input.getId()));
		} else
		    setInput(Lecturer.getNext(input.getId()));
	    }
	});

	addressLabelButton.addListener(SWT.Selection, new Listener() {
	    public void handleEvent(Event arg0) {
		MessageDialog
			.openInformation(
				shell,
				Messages
					.getString("GUIText.NotImplementedMessageDialogWindowTitle"),
				Messages
					.getString("GUIText.NotImplementedMessageDialogHintText"));
	    }
	});
    }

    /**
     * Checks if all obligatory attributes (salutation, lastname) of a lecturer
     * record are entered into the window form and if the entered fees are
     * correct float numbers.
     * 
     * @return <tt>true</tt> if all mandatory fields are set and the fee
     *         values are valid float numbers (if entered), otherwise
     *         <tt>false</tt>.
     * 
     * @see semorg.sql.util.Utility#isCorrectFloat(String)
     */
    protected boolean checkInput() {
	boolean inputOK = true;
	String errorMessage = "";
	if (salutationCombo.getText().equals("")) {
	    errorMessage += Messages
		    .getString("GUIText.ErrorMessageDialogMandatoryFieldSalutationText");
	    inputOK = false;
	}
	if (nameText.getText().equals("")) {
	    errorMessage += Messages
		    .getString("GUIText.ErrorMessageDialogMandatoryFieldLastNameText");
	    inputOK = false;
	}
	if (!Utility.isCorrectFloat(dailyFeeText.getText())) {
	    errorMessage += Messages
		    .getString("GUIText.ErrorMessageDialogIncorrectFloatDailyFee");
	    inputOK = false;
	}
	if (!Utility.isCorrectFloat(hourlyFeeText.getText())) {
	    errorMessage += Messages
		    .getString("GUIText.ErrorMessageDialogIncorrectFloatDailyFee");
	    inputOK = false;
	}

	if (!inputOK) {
	    MessageDialog
		    .openInformation(
			    shell,
			    Messages
				    .getString("GUIText.ErrorMessageDialogMandatoryFieldWindowTitle"),
			    errorMessage);
	}

	return inputOK;
    }

    /**
     * Reads the data from the window form elements, sets the values to a
     * Lecturer instance and commits the changes / the new record into the
     * database.
     * 
     * Furthermore the member {@link #input} is set to the new values.
     * 
     */
    protected void commitInputToDB() {

	if (input == null) {
	    // new record into the db
	    Lecturer newInput = new Lecturer(Lecturer.NULL_ID, salutationCombo
		    .getText(), titleCombo.getText(), firstNameText.getText(),
		    nameText.getText(), streetText.getText(), zipCodeText
			    .getText(), cityText.getText(), countryCombo
			    .getText(), annexCombo.getText(), phoneText
			    .getText(), mobileText.getText(),
		    faxText.getText(), emailText.getText(), birthDayCalendar
			    .getDate(), firstContactCalendar.getDate(), bioText
			    .getText(), Utility.parseFloat(dailyFeeText
			    .getText()), Utility.parseFloat(hourlyFeeText
			    .getText()), shortMessageText.getText(), memoText
			    .getText(), new Timestamp(System
			    .currentTimeMillis()), new Timestamp(System
			    .currentTimeMillis()));
	    input = newInput;
	    try {
		int id = input.insertIntoDB();
		numberText.setText(new Integer(id).toString());
	    } catch (SQLException e) {
		e.printStackTrace();
	    }
	} else {
	    // update an existing record
	    input.setSalutation(salutationCombo.getText());
	    input.setTitle(titleCombo.getText());
	    input.setFirstname(firstNameText.getText());
	    input.setName(nameText.getText());
	    input.setStreet(streetText.getText());
	    input.setZipCode(zipCodeText.getText());
	    input.setCity(cityText.getText());
	    input.setCountry(countryCombo.getText());
	    input.setAnnex(annexCombo.getText());
	    input.setPhone(phoneText.getText());
	    input.setMobile(mobileText.getText());
	    input.setFax(faxText.getText());
	    input.setEmail(emailText.getText());
	    input.setBirthday(birthDayCalendar.getDate());
	    input.setFirstContact(firstContactCalendar.getDate());
	    input.setBio(bioText.getText());
	    input.setDailyFee(Utility.parseFloat(dailyFeeText.getText()));
	    input.setHourlyFee(Utility.parseFloat(hourlyFeeText.getText()));

	    input.setShortinfo(shortMessageText.getText());
	    input.setNotices(memoText.getText());

	    try {
		input.updateDB();
	    } catch (SQLException e) {
		e.printStackTrace();
	    }
	}

	try {
	    conductControl.commitIntoDB(input.getId());
	} catch (SQLException e) {
	    e.printStackTrace();
	}

	try {
	    instructorControl.commitIntoDB(input.getId());
	} catch (SQLException e) {
	    e.printStackTrace();
	}

	try {
	    supervisorControl.commitIntoDB(input.getId());
	} catch (SQLException e) {
	    e.printStackTrace();
	}

	LecturerListWindow.instance.update();
    }

    /**
     * Creates a message dialog if the data in the window form has been changed
     * (or was entered newly) and these changes haven't been confirmed (e.g. by
     * clicking the ok button) or discarded so far. It also receives the
     * decision of the user (confirm/discard changes or abort the window
     * closing) and handels it.
     * 
     * In the positive case (confirmation of the changes) it invokes the method
     * {@link #onSave()}.
     * 
     * @return <tt>true</tt> if the shell can be closed and <tt>false</tt>
     *         otherwise.
     */
    private boolean confirmClose() {
	// has no visual representation until .open() is called
	MessageDialog dlg;

	if (input == null) {
	    dlg = new MessageDialog(
		    shell,
		    Messages
			    .getString("GUIText.SaveNewItemMessageDialogWindowTitle"),
		    null,
		    Messages
			    .getString("GUIText.SaveNewItemMessageDialogQuestionText"),
		    MessageDialog.QUESTION,
		    new String[] {
			    Messages
				    .getString("GUIText.SaveNewItemMessageDialogYesButtonText"),
			    Messages
				    .getString("GUIText.SaveNewItemMessageDialogNoButtonText"),
			    Messages
				    .getString("GUIText.SaveNewItemMessageDialogAbortButtonText") },
		    0);
	} else {
	    dlg = new MessageDialog(
		    shell,
		    Messages
			    .getString("GUIText.SaveChangesMessageDialogWindowTitle"),
		    null,
		    Messages
			    .getString("GUIText.SaveChangesMessageDialogQuestionText"),
		    MessageDialog.QUESTION,
		    new String[] {
			    Messages
				    .getString("GUIText.SaveChangesMessageDialogYesButtonText"),
			    Messages
				    .getString("GUIText.SaveChangesMessageDialogNoButtonText"),
			    Messages
				    .getString("GUIText.SaveChangesMessageDialogAbortButtonText") },
		    0);
	}
	switch (dlg.open()) {
	case 0: // "yes" selected
	    return onSave();
	case 1: // "no" selected -> close immediatly
	    return true;
	case 2: // "abort" selected -> don't close the shell
	    return false;
	default:
	    return true; // "x" button clicked -> close immediatly
	}
    }

    /**
     * Checks whether the input has been changed (i.e. inputChanged equals
     * <tt>true</tt>) or not. If changes have been done, it firstly checks if
     * the changes are valid ({@link #checkInput()}), secondly invokes the
     * method {@link #commitInputToDB()} to commit the changes and thirdly
     * updates the GUI by invoking the method {@link #setInput(Lecturer)}.
     * 
     * @return <tt>true</tt> if nothing has changed or the commit of the
     *         changes to the database was successful, <tt>false</tt>
     *         otherwise.
     */
    private boolean onSave() {
	if (!inputChanged)
	    return true;

	if (checkInput()) {
	    commitInputToDB();
	    setInput(input);
	    return true;
	} else
	    return false;
    }
}
