ca.phon.app.query.report.ResultListingSectionPanel.java Source code

Java tutorial

Introduction

Here is the source code for ca.phon.app.query.report.ResultListingSectionPanel.java

Source

/*
 * Phon - An open source tool for research in phonology.
 * Copyright (C) 2005 - 2015, Gregory Hedlund <ghedlund@mun.ca> and Yvan Rose <yrose@mun.ca>
 * Dept of Linguistics, Memorial University <https://phon.ca>
 * 
 * This program 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 3 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package ca.phon.app.query.report;

import static ca.phon.query.report.util.ResultListingFieldBuilder.ALIGNMENT_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.METADATA_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.RECORD_NUMBER_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.RECORD_TIER_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.RESULT_EXCLUDED_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.RESULT_FORMAT_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.RESULT_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.SESSION_DATE_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.SESSION_MEDIA_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.SESSION_NAME_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.SPEAKER_AGE_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.SPEAKER_GENDER_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.SPEAKER_NAME_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.SYLLABIFICATION_SCRIPT;
import static ca.phon.query.report.util.ResultListingFieldBuilder.createEmptyField;
import static ca.phon.query.report.util.ResultListingFieldBuilder.getDefaultFields;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.script.ScriptEngineFactory;
import javax.swing.ActionMap;
import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.DefaultListCellRenderer;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButton;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.KeyStroke;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import org.jdesktop.swingx.JXList;

import ca.phon.app.query.ScriptPanel;
import ca.phon.query.report.io.ObjectFactory;
import ca.phon.query.report.io.ResultListing;
import ca.phon.query.report.io.ResultListingField;
import ca.phon.query.report.io.ResultListingFormatType;
import ca.phon.query.report.io.ScriptParameter;
import ca.phon.query.script.QueryScript;
import ca.phon.query.script.QueryScriptContext;
import ca.phon.script.PhonScriptException;
import ca.phon.script.params.EnumScriptParam;
import ca.phon.script.params.ScriptParam;
import ca.phon.script.params.ScriptParameters;
import ca.phon.session.SystemTierType;
import ca.phon.ui.action.PhonActionEvent;
import ca.phon.ui.action.PhonUIAction;
import ca.phon.util.icons.IconManager;
import ca.phon.util.icons.IconSize;

import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;

/**
 * Section panel for customizable result listings.
 *
 */
public class ResultListingSectionPanel extends SectionPanel<ResultListing> {

    private final static Logger LOGGER = Logger.getLogger(ResultListingSectionPanel.class.getName());

    /**
     * Help text
     */
    private final static String INFO_TEXT = "<html><body>" + "<i>Result List</i>"
            + "<p>Outputs data for each result in a search.  Data can be printed as a table or listed with one field per line.<p>"
            + "<p>To add/remove fields, use the <img width='12' height='12' src='${field_add_img}' alt='+'/>"
            + "and <img width='12' height='12' src='${field_remove_img}' alt='-'/> buttons in the field outline.</p>"
            + "</body></html>";

    /*
     * UI
     * 
     */
    /*
     * List
     */
    private JXList fieldList;

    private JButton addFieldButton;

    private JButton delFieldButton;

    private JButton upFieldButton;

    private JButton downFieldButton;

    /*
     * Form controls
     */
    private JTextField nameField;

    private JPanel namePanel;

    /*
     * Panel for field options 
     */
    private JPanel fieldOptionsPanel;

    /*
     * Query scripts associated with fields
     * The editor will work with the query script
     * objects while editing and update field values
     * when report is saved.
     */
    private Map<ResultListingField, ScriptPanel> scriptPanels = Collections
            .synchronizedMap(new HashMap<ResultListingField, ScriptPanel>());

    /*
     * Table or list?
     * 
     */
    private JRadioButton tableOptBox;

    private JRadioButton listOptBox;

    /*
     * Include excluded?
     */
    private JCheckBox includeExcludedBox;

    private JPanel fieldPanel;

    public ResultListingSectionPanel(ResultListing listing) {
        super(listing);

        if (getSection().getField().size() == 0) {
            // setup default list
            for (ResultListingField field : getDefaultFields()) {
                getSection().getField().add(field);
            }
        }

        init();
    }

    private void init() {
        // get absolute locations of icons
        String addImgRelPath = "data" + File.separator + "icons" + File.separator + "16x16" + File.separator
                + "actions" + File.separator + "list-add.png";
        File addImgFile = new File(addImgRelPath);
        String addImgURI = addImgFile.toURI().toASCIIString();

        String removeImgRelPath = "data" + File.separator + "icons" + File.separator + "16x16" + File.separator
                + "actions" + File.separator + "list-remove.png";
        File remImgFile = new File(removeImgRelPath);
        String remImgURI = remImgFile.toURI().toASCIIString();

        String infoTxt = INFO_TEXT.replaceAll("\\$\\{field_add_img\\}", addImgURI)
                .replaceAll("\\$\\{field_remove_img\\}", remImgURI);

        super.setInformationText(getClass().getName() + ".info", infoTxt);

        // list panel
        FormLayout listLayout = new FormLayout("fill:pref:grow, pref", "pref, fill:pref:grow");
        JPanel listPanel = new JPanel(listLayout);
        CellConstraints cc = new CellConstraints();

        ResultListing resList = getSection();
        fieldList = new JXList(resList.getField().toArray(new ResultListingField[0]));
        fieldList.setCellRenderer(new FieldListCellRenderer());
        fieldList.addListSelectionListener(new FieldListSelectionListener());
        fieldList.setMinimumSize(new Dimension(200, 0));

        ActionMap fieldActionMap = fieldList.getActionMap();
        InputMap fieldInputMap = fieldList.getInputMap(JComponent.WHEN_FOCUSED);

        PhonUIAction showListAction = new PhonUIAction(this, "onShowPopup");
        ImageIcon addIcn = IconManager.getInstance().getIcon("actions/list-add", IconSize.XSMALL);
        showListAction.putValue(PhonUIAction.SMALL_ICON, addIcn);
        showListAction.putValue(PhonUIAction.SHORT_DESCRIPTION, "Add field...");
        addFieldButton = new JButton(showListAction);

        PhonUIAction removeFieldAction = new PhonUIAction(this, "onDelField");
        ImageIcon delIcn = IconManager.getInstance().getIcon("actions/list-remove", IconSize.XSMALL);
        removeFieldAction.putValue(PhonUIAction.SMALL_ICON, delIcn);
        removeFieldAction.putValue(PhonUIAction.SHORT_DESCRIPTION, "Remove selected field");
        delFieldButton = new JButton(removeFieldAction);
        String delActID = "_remove_field_";
        fieldActionMap.put(delActID, removeFieldAction);
        KeyStroke delKs1 = KeyStroke.getKeyStroke(KeyEvent.VK_BACK_SPACE, 0);
        KeyStroke delKs2 = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
        fieldInputMap.put(delKs1, delActID);
        fieldInputMap.put(delKs2, delActID);

        PhonUIAction moveFieldUpAction = new PhonUIAction(this, "onMoveFieldUp");
        ImageIcon upIcn = IconManager.getInstance().getIcon("actions/go-up", IconSize.XSMALL);
        moveFieldUpAction.putValue(PhonUIAction.SMALL_ICON, upIcn);
        moveFieldUpAction.putValue(PhonUIAction.SHORT_DESCRIPTION, "Move selected field up");
        upFieldButton = new JButton(moveFieldUpAction);

        PhonUIAction moveFieldDownAction = new PhonUIAction(this, "onMoveFieldDown");
        ImageIcon downIcn = IconManager.getInstance().getIcon("actions/go-down", IconSize.XSMALL);
        moveFieldDownAction.putValue(PhonUIAction.SMALL_ICON, downIcn);
        moveFieldDownAction.putValue(PhonUIAction.SHORT_DESCRIPTION, "Move selected field down");
        downFieldButton = new JButton(moveFieldDownAction);

        FormLayout topBtnLayout = new FormLayout(
                "fill:pref:grow, right:pref, right:pref, " + (upFieldButton.getPreferredSize().width) + "px",
                "pref");
        JPanel topBtnPanel = new JPanel(topBtnLayout);

        FormLayout sideBtnLayout = new FormLayout("pref", "pref, 3dlu, pref, fill:pref:grow");
        JPanel sideBtnPanel = new JPanel(sideBtnLayout);

        JScrollPane listScroller = new JScrollPane(fieldList);

        topBtnPanel.add(addFieldButton, cc.xy(2, 1));
        topBtnPanel.add(delFieldButton, cc.xy(3, 1));

        sideBtnPanel.add(upFieldButton, cc.xy(1, 1));
        sideBtnPanel.add(downFieldButton, cc.xy(1, 3));

        listPanel.add(topBtnPanel, cc.xyw(1, 1, 2));
        listPanel.add(sideBtnPanel, cc.xywh(2, 2, 1, 1));
        listPanel.add(listScroller, cc.xy(1, 2));

        // field form
        fieldOptionsPanel = new JPanel(new BorderLayout());
        fieldOptionsPanel.setBorder(BorderFactory.createTitledBorder("Options"));

        namePanel = new JPanel(new FormLayout("left:pref, 3dlu, fill:pref:grow", "pref"));
        nameField = new JTextField();
        nameField.getDocument().addDocumentListener(new NameFieldListener());
        namePanel.add(new JLabel("Field name:"), cc.xy(1, 1));
        namePanel.add(nameField, cc.xy(3, 1));

        fieldOptionsPanel.add(namePanel, BorderLayout.NORTH);

        // format selection
        tableOptBox = new JRadioButton("Table");
        listOptBox = new JRadioButton("List");

        ButtonGroup btnGroup = new ButtonGroup();

        FormatHandler formatHandler = new FormatHandler();
        tableOptBox.setSelected(resList.getFormat() == ResultListingFormatType.TABLE);
        tableOptBox.addActionListener(formatHandler);
        listOptBox.setSelected(!tableOptBox.isSelected());
        listOptBox.addActionListener(formatHandler);

        includeExcludedBox = new JCheckBox("Include excluded results");
        includeExcludedBox.setSelected(getSection().isIncludeExcluded());
        includeExcludedBox.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent arg0) {
                getSection().setIncludeExcluded(includeExcludedBox.isSelected());
            }
        });

        btnGroup.add(tableOptBox);
        btnGroup.add(listOptBox);

        FormLayout splitLayout = new FormLayout("200px:nogrow, fill:default:grow", "fill:default:grow");
        fieldPanel = new JPanel(splitLayout);
        fieldPanel.add(listPanel, cc.xy(1, 1));
        fieldPanel.add(fieldOptionsPanel, cc.xy(2, 1));
        fieldPanel.setBorder(BorderFactory.createTitledBorder("Field Outline"));

        JPanel formatPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        formatPanel.add(new JLabel("Display data as:"));
        formatPanel.add(tableOptBox);
        formatPanel.add(listOptBox);
        formatPanel.add(includeExcludedBox);
        formatPanel.setBorder(BorderFactory.createTitledBorder("Options"));

        JPanel cPanel = new JPanel(new BorderLayout());
        cPanel.add(formatPanel, BorderLayout.NORTH);
        cPanel.add(fieldPanel, BorderLayout.CENTER);
        add(cPanel, BorderLayout.CENTER);

        nameField.setEnabled(false);
    }

    public JPanel getFieldPanel() {
        return this.fieldPanel;
    }

    /**
     * Get a list of pre-defined fields.
     * 
     * @return list of fields
     */
    private ResultListingField[] getPredefinedFields() {
        List<ResultListingField> retVal = new ArrayList<ResultListingField>();

        // query info

        // session info
        // session name
        ResultListingField sNameField = createEmptyField();
        sNameField.setTitle("Session Name");
        sNameField.getFieldValue().setLang("Javascript");
        sNameField.getFieldValue().setScript(SESSION_NAME_SCRIPT);
        retVal.add(sNameField);

        ResultListingField sDateField = createEmptyField();
        sDateField.setTitle("Session Date");
        sDateField.getFieldValue().setLang("Javascript");
        sDateField.getFieldValue().setScript(SESSION_DATE_SCRIPT);
        retVal.add(sDateField);

        ResultListingField sMediaField = createEmptyField();
        sMediaField.setTitle("Session Media");
        sMediaField.getFieldValue().setLang("Javascript");
        sMediaField.getFieldValue().setScript(SESSION_MEDIA_SCRIPT);
        retVal.add(sMediaField);

        // record info
        ResultListingField rNumField = createEmptyField();
        rNumField.setTitle("Record #");
        rNumField.getFieldValue().setLang("Javascript");
        rNumField.getFieldValue().setScript(RECORD_NUMBER_SCRIPT);
        retVal.add(rNumField);

        ResultListingField rSpeakerField = createEmptyField();
        rSpeakerField.setTitle("Speaker");
        rSpeakerField.getFieldValue().setLang("Javascript");
        rSpeakerField.getFieldValue().setScript(SPEAKER_NAME_SCRIPT);
        retVal.add(rSpeakerField);

        ResultListingField rSpeakerAgeField = createEmptyField();
        rSpeakerAgeField.setTitle("Speaker Age");
        rSpeakerAgeField.getFieldValue().setLang("Javascript");
        rSpeakerAgeField.getFieldValue().setScript(SPEAKER_AGE_SCRIPT);
        retVal.add(rSpeakerAgeField);

        ResultListingField rSpeakerSexField = createEmptyField();
        rSpeakerSexField.setTitle("Speaker Gender");
        rSpeakerSexField.getFieldValue().setLang("Javascript");
        rSpeakerSexField.getFieldValue().setScript(SPEAKER_GENDER_SCRIPT);
        retVal.add(rSpeakerSexField);

        // system tiers
        for (SystemTierType stt : SystemTierType.values()) {
            ResultListingField sttField = createEmptyField();
            sttField.setTitle(stt.getName());
            sttField.getFieldValue().setLang("Javascript");

            if (stt == SystemTierType.TargetSyllables) {
                sttField.getFieldValue().setScript(SYLLABIFICATION_SCRIPT.replaceAll("\\$\\{TIER\\}", "0"));
            } else if (stt == SystemTierType.ActualSyllables) {
                sttField.getFieldValue().setScript(SYLLABIFICATION_SCRIPT.replaceAll("\\$\\{TIER\\}", "1"));
            } else if (stt == SystemTierType.SyllableAlignment) {
                sttField.getFieldValue().setScript(ALIGNMENT_SCRIPT);
            } else {
                sttField.getFieldValue().setScript(RECORD_TIER_SCRIPT.replaceAll("\\$\\{TIER\\}", stt.getName()));
            }

            retVal.add(sttField);
        }

        // alignment

        // result info
        ResultListingField resValField = createEmptyField();
        resValField.setTitle("Result");
        resValField.getFieldValue().setLang("Javascript");
        resValField.getFieldValue().setScript(RESULT_SCRIPT);
        retVal.add(resValField);

        ResultListingField resFormatField = createEmptyField();
        resFormatField.setTitle("Result Format");
        resFormatField.getFieldValue().setLang("Javascript");
        resFormatField.getFieldValue().setScript(RESULT_FORMAT_SCRIPT);
        retVal.add(resFormatField);

        ResultListingField exludedField = createEmptyField();
        exludedField.setTitle("Result Excluded");
        exludedField.getFieldValue().setLang("Javascript");
        exludedField.getFieldValue().setScript(RESULT_EXCLUDED_SCRIPT);
        retVal.add(exludedField);

        ResultListingField resMetaField = createEmptyField();
        resMetaField.setTitle("Result Metadata");
        resMetaField.getFieldValue().setLang("Javascript");
        resMetaField.getFieldValue().setScript(METADATA_SCRIPT);
        retVal.add(resMetaField);

        return retVal.toArray(new ResultListingField[0]);
    }

    /*
     * Update form components with given field data
     * 
     * @param field
     * 
     */
    private void updateForm(ResultListingField field) {
        nameField.setText(field.getTitle());

        fieldOptionsPanel.removeAll();
        fieldOptionsPanel.add(namePanel, BorderLayout.NORTH);

        ScriptPanel scriptPanel = scriptPanels.get(field);
        if (scriptPanel == null) {
            QueryScript qs = new QueryScript(field.getFieldValue().getScript());
            QueryScriptContext ctx = qs.getQueryContext();
            ScriptParameters params = new ScriptParameters();
            try {
                params = ctx.getScriptParameters(ctx.getEvaluatedScope());
            } catch (PhonScriptException e) {
                LOGGER.log(Level.SEVERE, e.getLocalizedMessage(), e);
            }
            // setup script parameters
            for (ScriptParam param : params) {
                for (String paramId : param.getParamIds()) {
                    ScriptParameter savedParam = null;
                    for (ScriptParameter sp : field.getFieldValue().getParam()) {
                        if (sp.getName().equals(paramId)) {
                            savedParam = sp;
                            break;
                        }
                    }

                    if (savedParam != null) {
                        Object paramVal = null;
                        try {
                            if (param.getParamType().equals("bool")) {
                                paramVal = Boolean.parseBoolean(savedParam.getContent());
                            } else if (param.getParamType().equals("enum")) {
                                EnumScriptParam esp = (EnumScriptParam) param;
                                EnumScriptParam.ReturnValue rVal = null;
                                for (EnumScriptParam.ReturnValue v : esp.getChoices()) {
                                    if (v.toString().equals(savedParam.getContent())) {
                                        rVal = v;
                                        break;
                                    }
                                }
                                if (rVal != null)
                                    paramVal = rVal;
                                else
                                    paramVal = esp.getDefaultValue(paramId);
                            } else {
                                paramVal = savedParam.getContent();
                            }
                            param.setValue(paramId, paramVal);
                        } catch (Exception e) {
                            LOGGER.log(Level.WARNING, e.getLocalizedMessage(), e);
                        }
                    }
                }
            }
            scriptPanel = new ScriptPanel(qs);
            scriptPanels.put(field, scriptPanel);
            scriptPanel.addPropertyChangeListener(new ScriptPanelListener(field));
        }
        fieldOptionsPanel.add(scriptPanel, BorderLayout.CENTER);
        fieldOptionsPanel.revalidate();
        fieldOptionsPanel.repaint();
    }

    /*
     * UI Actions
     */
    public void onShowPopup(PhonActionEvent pae) {
        // display a popup menu for selecting what field to add
        JPopupMenu menu = new JPopupMenu();

        ResultListingField[] defFields = getPredefinedFields();
        for (ResultListingField field : defFields) {
            PhonUIAction fieldAct = new PhonUIAction(this, "onAddField", field);
            fieldAct.putValue(PhonUIAction.NAME, field.getTitle());
            fieldAct.putValue(PhonUIAction.SHORT_DESCRIPTION, field.getFieldValue().getLang());
            fieldAct.putValue(PhonUIAction.LONG_DESCRIPTION, field.getFieldValue().getScript());
            JMenuItem fieldItem = new JMenuItem(fieldAct);
            menu.add(fieldItem);
        }
        menu.addSeparator();

        ResultListingField userTierField = createEmptyField();
        userTierField.setTitle("User Defined Tier");
        userTierField.getFieldValue().setLang("Javascript");
        userTierField.getFieldValue().setScript(RECORD_TIER_SCRIPT.replaceAll("\\$\\{TIER\\}", ""));
        PhonUIAction userFieldAct = new PhonUIAction(this, "onAddField", userTierField);
        userFieldAct.putValue(PhonUIAction.NAME, userTierField.getTitle());
        JMenuItem userFieldItem = new JMenuItem(userFieldAct);
        menu.add(userFieldItem);

        ResultListingField emptyField = createEmptyField();
        emptyField.setTitle("Untitled");
        PhonUIAction customFieldAct = new PhonUIAction(this, "onAddField", emptyField);
        customFieldAct.putValue(PhonUIAction.NAME, "Custom");
        customFieldAct.putValue(PhonUIAction.SHORT_DESCRIPTION, "Create a custom field");
        JMenuItem customFieldItem = new JMenuItem(customFieldAct);
        menu.add(customFieldItem);

        menu.show(addFieldButton, addFieldButton.getWidth(), addFieldButton.getHeight());
    }

    public void onAddField(PhonActionEvent pae) {
        if (pae.getData() == null)
            throw new IllegalArgumentException("field cannot be null");
        if (!(pae.getData() instanceof ResultListingField))
            throw new IllegalArgumentException("not a field!");

        ResultListingField field = (ResultListingField) pae.getData();
        ResultListing resList = getSection();

        resList.getField().add(field);

        // update list
        fieldList.setListData(resList.getField().toArray(new ResultListingField[0]));

        fieldList.setSelectedValue(field, true);

    }

    public void onDelField(PhonActionEvent pae) {
        int selectedField = fieldList.getSelectedIndex();

        ResultListing resList = getSection();
        if (selectedField >= 0 && selectedField < resList.getField().size()) {
            ResultListingField removed = resList.getField().remove(selectedField);

            // update list
            fieldList.setListData(resList.getField().toArray(new ResultListingField[0]));
            scriptPanels.remove(removed);
        }
    }

    public void onMoveFieldUp(PhonActionEvent pae) {
        int selectedField = fieldList.getSelectedIndex();

        ResultListing resList = getSection();
        if (selectedField > 0 && selectedField < resList.getField().size()) {
            ResultListingField element = resList.getField().remove(selectedField);
            resList.getField().add(selectedField - 1, element);

            // update list
            fieldList.setListData(resList.getField().toArray(new ResultListingField[0]));
            fieldList.setSelectedValue(element, true);
        }
    }

    public void onMoveFieldDown(PhonActionEvent pae) {
        int selectedField = fieldList.getSelectedIndex();

        ResultListing resList = getSection();
        if (selectedField >= 0 && selectedField < resList.getField().size() - 1) {
            ResultListingField element = resList.getField().remove(selectedField);
            resList.getField().add(selectedField + 1, element);

            // update list
            fieldList.setListData(resList.getField().toArray(new ResultListingField[0]));
            fieldList.setSelectedValue(element, true);
        }
    }

    /* 
     * Return the editor mimetype for the given script engine
     */
    private String getEditorMimetypeForScriptEngineFactory(ScriptEngineFactory factory) {
        String retVal = null;

        // look for the first listed mimetype starting with 'text/'
        for (String mimetype : factory.getMimeTypes()) {
            if (mimetype.startsWith("text/")) {
                retVal = mimetype;
                break;
            }
        }
        // didn't find one - use 'text/<lang>'
        if (retVal == null) {
            retVal = "text/" + factory.getLanguageName().toLowerCase();
        }

        return retVal;
    }

    /**
     * Property changes for result listing fields
     */
    private class ScriptPanelListener implements PropertyChangeListener {

        private ResultListingField field;

        public ScriptPanelListener(ResultListingField field) {
            this.field = field;
        }

        @Override
        public void propertyChange(PropertyChangeEvent evt) {
            String propName = evt.getPropertyName();
            //         String value = (evt.getNewValue() != null ? evt.getNewValue().toString() : "");
            if (propName.equals(ScriptPanel.SCRIPT_PROP)) {
                ScriptPanel panel = (ScriptPanel) evt.getSource();
                field.getFieldValue().setScript(panel.getScript().getScript());
            } else if (propName.startsWith(ScriptPanel.PARAM_PREFIX)) {
                String paramid = propName.substring(ScriptPanel.PARAM_PREFIX.length() + 1);
                String value = (evt.getNewValue() != null ? evt.getNewValue().toString() : "");

                ScriptParameter sp = null;
                for (ScriptParameter savedParam : field.getFieldValue().getParam()) {
                    if (savedParam.getName().equals(paramid)) {
                        sp = savedParam;
                    }
                }
                if (sp == null) {
                    sp = (new ObjectFactory()).createScriptParameter();
                    sp.setName(paramid);
                    field.getFieldValue().getParam().add(sp);
                }
                sp.setContent(value);
            }
        }

    }

    /**
     * List selection listener
     */
    private class FieldListSelectionListener implements ListSelectionListener {

        @Override
        public void valueChanged(ListSelectionEvent e) {
            int selectedIndex = fieldList.getSelectedIndex();
            if (selectedIndex >= 0) {
                nameField.setEnabled(true);

                ResultListing resList = getSection();
                ResultListingField field = resList.getField().get(selectedIndex);

                updateForm(field);
            } else {
                nameField.setEnabled(false);
            }
        }

    }

    /*
     * Methods for updating the currently selected field
     */
    private void updateCurrentFieldName() {
        int selectedIdx = fieldList.getSelectedIndex();
        if (selectedIdx >= 0) {
            ResultListing resList = getSection();
            ResultListingField selectedField = resList.getField().get(selectedIdx);

            selectedField.setTitle(nameField.getText());

            // update cell in table
            fieldList.repaint();
        }
    }

    /**
     * Entity object for scripting language selection
     */
    private class ScriptEngineInfo {

        private ScriptEngineFactory factory;

        private String mimeType;

        private String title;

        public ScriptEngineInfo(ScriptEngineFactory factory, String mimetype, String title) {
            this.factory = factory;
            this.mimeType = mimetype;
            this.title = title;
        }

        public ScriptEngineFactory getFactory() {
            return factory;
        }

        public void setFactory(ScriptEngineFactory factory) {
            this.factory = factory;
        }

        public String getMimeType() {
            return mimeType;
        }

        public void setMimeType(String mimeType) {
            this.mimeType = mimeType;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }
    }

    /**
     * List cell renderer
     */
    private class FieldListCellRenderer extends DefaultListCellRenderer {

        @Override
        public Component getListCellRendererComponent(JList arg0, Object arg1, int arg2, boolean arg3,
                boolean arg4) {
            JLabel retVal = (JLabel) super.getListCellRendererComponent(arg0, arg1, arg2, arg3, arg4);

            ResultListingField f = (ResultListingField) arg1;

            if (f.getTitle().length() == 0)
                retVal.setText("<html><i>No title</i></html>");
            else
                retVal.setText(f.getTitle());

            return retVal;
        }

    }

    /**
     * Name field handler
     */
    private class NameFieldListener implements DocumentListener {

        @Override
        public void changedUpdate(DocumentEvent e) {
        }

        @Override
        public void insertUpdate(DocumentEvent e) {
            updateCurrentFieldName();
        }

        @Override
        public void removeUpdate(DocumentEvent e) {
            updateCurrentFieldName();
        }

    }

    /**
     * Format listener
     * 
     */
    private class FormatHandler implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent arg0) {
            ResultListingFormatType format = ResultListingFormatType.TABLE;

            if (listOptBox.isSelected())
                format = ResultListingFormatType.LIST;

            ResultListing resList = getSection();
            resList.setFormat(format);
        }

    }
}