package com.xoetrope.carousel.survey;
import java.io.Serializable;
import java.util.EventObject;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.event.CellEditorListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.EventListenerList;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
/**
* An table cell editor for editing question responses
*
* <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
* the GNU Public License (GPL), please see license.txt for more details. If
* you make commercial use of this software you must purchase a commercial
* license from Xoetrope.</p>
* <p> $Revision: 1.5 $</p>
*/
public class XResponseSetEditor extends JPanel implements TableCellEditor, ActionListener, PopupMenuListener
{
/**
* An integer specifying the number of clicks needed to start editing.
* Even if <code>clickCountToStart</code> is defined as zero, it
* will not initiate until a click occurs.
*/
protected int clickCountToStart = 1;
protected EventListenerList listenerList = new EventListenerList();
transient protected ChangeEvent changeEvent = null;
protected JButton expandBtn;
protected JTable responseTable;
protected JScrollPane tableScroller;
protected DefaultTableModel defaultModel;
protected JTextField questionText;
protected XPopupPanel popupPanel;
protected int defaultHeight;
private String values[];
public XResponseSetEditor()
{
init( "", 16 );
}
public XResponseSetEditor( String[] options, int defHeight )
{
setLayout( new BorderLayout());
values = options;
init( values[ 0 ].toString(), defHeight );
}
protected void init( String value, int defHeight )
{
defaultHeight = defHeight;
setBorder( new LineBorder( Color.black ));
setLayout( new BorderLayout());
expandBtn = new JButton( "+" );
expandBtn.addActionListener( this );
expandBtn.setPreferredSize( new Dimension( 16, 16 ));
expandBtn.setMargin( new Insets( 0, 2, 0, 2 ));
questionText = new JTextField( value );
questionText.setBorder( new EmptyBorder( 0,0,0,0 ));
defaultModel = new DefaultTableModel();
defaultModel.addColumn( "Response" );
defaultModel.addColumn( "Value" );
responseTable = new JTable( defaultModel );
tableScroller = new JScrollPane( responseTable );
tableScroller.setBorder( new EmptyBorder( 0, 0, 0, 0 ));
fillTable();
add( expandBtn, BorderLayout.WEST );
add( questionText, BorderLayout.CENTER );
doLayout();
}
public void addExtraMouseListener( MouseListener ml )
{
responseTable.addMouseListener( ml );
questionText.addMouseListener( ml );
}
private void fillTable()
{
responseTable.getColumn( "Value" ).setPreferredWidth( 50 );
responseTable.getColumn( "Value" ).setMaxWidth( 100 );
int numQuestions = values.length;
for ( int i = 0; i < numQuestions; i++ ) {
String recordValues[] = new String[ 2 ];
recordValues[ 0 ] = values[ i ];
recordValues[ 1 ] = Integer.toString( i+1 );
defaultModel.addRow( recordValues );
}
}
public void actionPerformed( ActionEvent e )
{
if ( e.getSource() == expandBtn ) {
if ( expandBtn.getText().equals( "+" )) {
expandBtn.setText( "-" );
Dimension size = getSize();
int height = Math.max( 23 + defaultHeight * responseTable.getRowCount(), responseTable.getHeight() + responseTable.getTableHeader().getHeight() + 2 );
popupPanel = new XPopupPanel( this );
popupPanel.add( tableScroller );
popupPanel.addPopupMenuListener( this );
popupPanel.setPopupSize( size.width - expandBtn.getWidth(), Math.min( height, 242 ));
popupPanel.show( this, expandBtn.getWidth(), expandBtn.getHeight());
}
else {
expandBtn.setText( "+" );
popupPanel.close();
}
repaint();
}
}
public void popupMenuCanceled(PopupMenuEvent e) {}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e)
{
expandBtn.setText( "+" );
}
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {}
public void addPopupActions()
{
responseTable.setBackground( new Color( 255, 247, 225 ));
}
public void setSelectedItem( Object value )
{
questionText.setText( value.toString());
}
public Component getTableCellEditorComponent( JTable table, Object value, boolean isSelected, int row, int column )
{
if ( isSelected ) {
setForeground( table.getSelectionForeground() );
setBackground( table.getSelectionBackground() );
}
else {
setForeground( table.getForeground() );
setBackground( table.getBackground() );
}
return this;
}
public void removeCellListener( CellEditorListener listener )
{}
public void stopCellEditing( EventObject o )
{}
public Object getCellEditorValue()
{
return questionText.getText();
}
/**
* Returns true.
* @param e an event object
* @return true
*/
public boolean isCellEditable( EventObject e )
{
return true;
}
/**
* Returns true.
* @param e an event object
* @return true
*/
public boolean shouldSelectCell( EventObject anEvent )
{
return true;
}
/**
* Calls <code>fireEditingStopped</code> and returns true.
* @return true
*/
public boolean stopCellEditing()
{
fireEditingStopped();
return true;
}
/**
* Calls <code>fireEditingCanceled</code>.
*/
public void cancelCellEditing()
{
fireEditingCanceled();
}
/**
* Adds a <code>CellEditorListener</code> to the listener list.
* @param l the new listener to be added
*/
public void addCellEditorListener( CellEditorListener l )
{
listenerList.add( CellEditorListener.class, l );
}
/**
* Removes a <code>CellEditorListener</code> from the listener list.
* @param l the listener to be removed
*/
public void removeCellEditorListener( CellEditorListener l )
{
listenerList.remove( CellEditorListener.class, l );
}
/**
* Returns an array of all the <code>CellEditorListener</code>s added
* to this AbstractCellEditor with addCellEditorListener().
*
* @return all of the <code>CellEditorListener</code>s added or an empty
* array if no listeners have been added
* @since 1.4
*/
public CellEditorListener[] getCellEditorListeners()
{
return (CellEditorListener[])listenerList.getListeners(
CellEditorListener.class );
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance
* is created lazily.
*
* @see EventListenerList
*/
protected void fireEditingStopped()
{
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for ( int i = listeners.length - 2; i >= 0; i -= 2 ) {
if ( listeners[i]==CellEditorListener.class ) {
// Lazily create the event:
if ( changeEvent == null )
changeEvent = new ChangeEvent( this );
((CellEditorListener)listeners[ i + 1 ]).editingStopped( changeEvent );
}
}
}
/**
* Notifies all listeners that have registered interest for
* notification on this event type. The event instance
* is created lazily.
*
* @see EventListenerList
*/
protected void fireEditingCanceled()
{
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for ( int i = listeners.length - 2; i >= 0; i -= 2 ) {
if ( listeners[ i ] == CellEditorListener.class ) {
// Lazily create the event:
if ( changeEvent == null )
changeEvent = new ChangeEvent( this );
((CellEditorListener)listeners[ i + 1 ] ).editingCanceled( changeEvent );
}
}
}
/**
* The protected <code>EditorDelegate</code> class.
*/
protected class EditorDelegate implements ActionListener, ItemListener, Serializable
{
/** The value of this cell. */
protected Object value;
/**
* Returns the value of this cell.
* @return the value of this cell
*/
public Object getCellEditorValue()
{
return value;
}
/**
* Sets the value of this cell.
* @param value the new value of this cell
*/
public void setValue(Object value)
{
this.value = value;
}
/**
* Returns true if <code>anEvent</code> is <b>not</b> a
* <code>MouseEvent</code>. Otherwise, it returns true
* if the necessary number of clicks have occurred, and
* returns false otherwise.
*
* @param anEvent the event
* @return true if cell is ready for editing, false otherwise
* @see #setClickCountToStart
* @see #shouldSelectCell
*/
public boolean isCellEditable( EventObject anEvent )
{
if ( anEvent instanceof MouseEvent )
return ((MouseEvent)anEvent).getClickCount() >= clickCountToStart;
return true;
}
/**
* Returns true to indicate that the editing cell may
* be selected.
*
* @param anEvent the event
* @return true
* @see #isCellEditable
*/
public boolean shouldSelectCell( EventObject anEvent )
{
return true;
}
/**
* Returns true to indicate that editing has begun.
*
* @param anEvent the event
*/
public boolean startCellEditing( EventObject anEvent )
{
return true;
}
/**
* Stops editing and
* returns true to indicate that editing has stopped.
* This method calls <code>fireEditingStopped</code>.
*
* @return true
*/
public boolean stopCellEditing()
{
fireEditingStopped();
return true;
}
/**
* Cancels editing. This method calls <code>fireEditingCanceled</code>.
*/
public void cancelCellEditing()
{
fireEditingCanceled();
}
/**
* When an action is performed, editing is ended.
* @param e the action event
* @see #stopCellEditing
*/
public void actionPerformed( ActionEvent e )
{
XResponseSetEditor.this.stopCellEditing();
}
/**
* When an item's state changes, editing is ended.
* @param e the action event
* @see #stopCellEditing
*/
public void itemStateChanged( ItemEvent e )
{
XResponseSetEditor.this.stopCellEditing();
}
}
}
|