001: package com.xoetrope.carousel.survey;
002:
003: import java.io.Serializable;
004: import java.util.EventObject;
005:
006: import java.awt.BorderLayout;
007: import java.awt.Color;
008: import java.awt.Component;
009: import java.awt.Dimension;
010: import java.awt.Insets;
011: import java.awt.event.ActionEvent;
012: import java.awt.event.ActionListener;
013: import java.awt.event.ItemEvent;
014: import java.awt.event.ItemListener;
015: import java.awt.event.MouseEvent;
016: import java.awt.event.MouseListener;
017: import javax.swing.JButton;
018: import javax.swing.JPanel;
019: import javax.swing.JScrollPane;
020: import javax.swing.JTable;
021: import javax.swing.JTextField;
022: import javax.swing.border.EmptyBorder;
023: import javax.swing.border.LineBorder;
024: import javax.swing.event.CellEditorListener;
025: import javax.swing.event.ChangeEvent;
026: import javax.swing.event.EventListenerList;
027: import javax.swing.event.PopupMenuEvent;
028: import javax.swing.event.PopupMenuListener;
029: import javax.swing.table.DefaultTableModel;
030: import javax.swing.table.TableCellEditor;
031:
032: /**
033: * An table cell editor for editing question responses
034: *
035: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
036: * the GNU Public License (GPL), please see license.txt for more details. If
037: * you make commercial use of this software you must purchase a commercial
038: * license from Xoetrope.</p>
039: * <p> $Revision: 1.5 $</p>
040: */
041: public class XResponseSetEditor extends JPanel implements
042: TableCellEditor, ActionListener, PopupMenuListener {
043: /**
044: * An integer specifying the number of clicks needed to start editing.
045: * Even if <code>clickCountToStart</code> is defined as zero, it
046: * will not initiate until a click occurs.
047: */
048: protected int clickCountToStart = 1;
049:
050: protected EventListenerList listenerList = new EventListenerList();
051: transient protected ChangeEvent changeEvent = null;
052:
053: protected JButton expandBtn;
054: protected JTable responseTable;
055: protected JScrollPane tableScroller;
056: protected DefaultTableModel defaultModel;
057: protected JTextField questionText;
058: protected XPopupPanel popupPanel;
059: protected int defaultHeight;
060:
061: private String values[];
062:
063: public XResponseSetEditor() {
064: init("", 16);
065: }
066:
067: public XResponseSetEditor(String[] options, int defHeight) {
068: setLayout(new BorderLayout());
069: values = options;
070: init(values[0].toString(), defHeight);
071: }
072:
073: protected void init(String value, int defHeight) {
074: defaultHeight = defHeight;
075:
076: setBorder(new LineBorder(Color.black));
077:
078: setLayout(new BorderLayout());
079:
080: expandBtn = new JButton("+");
081: expandBtn.addActionListener(this );
082: expandBtn.setPreferredSize(new Dimension(16, 16));
083: expandBtn.setMargin(new Insets(0, 2, 0, 2));
084:
085: questionText = new JTextField(value);
086: questionText.setBorder(new EmptyBorder(0, 0, 0, 0));
087:
088: defaultModel = new DefaultTableModel();
089: defaultModel.addColumn("Response");
090: defaultModel.addColumn("Value");
091:
092: responseTable = new JTable(defaultModel);
093: tableScroller = new JScrollPane(responseTable);
094: tableScroller.setBorder(new EmptyBorder(0, 0, 0, 0));
095: fillTable();
096:
097: add(expandBtn, BorderLayout.WEST);
098: add(questionText, BorderLayout.CENTER);
099:
100: doLayout();
101: }
102:
103: public void addExtraMouseListener(MouseListener ml) {
104: responseTable.addMouseListener(ml);
105: questionText.addMouseListener(ml);
106: }
107:
108: private void fillTable() {
109: responseTable.getColumn("Value").setPreferredWidth(50);
110: responseTable.getColumn("Value").setMaxWidth(100);
111:
112: int numQuestions = values.length;
113: for (int i = 0; i < numQuestions; i++) {
114: String recordValues[] = new String[2];
115: recordValues[0] = values[i];
116: recordValues[1] = Integer.toString(i + 1);
117: defaultModel.addRow(recordValues);
118: }
119: }
120:
121: public void actionPerformed(ActionEvent e) {
122: if (e.getSource() == expandBtn) {
123: if (expandBtn.getText().equals("+")) {
124: expandBtn.setText("-");
125: Dimension size = getSize();
126: int height = Math.max(23 + defaultHeight
127: * responseTable.getRowCount(), responseTable
128: .getHeight()
129: + responseTable.getTableHeader().getHeight()
130: + 2);
131: popupPanel = new XPopupPanel(this );
132: popupPanel.add(tableScroller);
133: popupPanel.addPopupMenuListener(this );
134: popupPanel.setPopupSize(size.width
135: - expandBtn.getWidth(), Math.min(height, 242));
136: popupPanel.show(this , expandBtn.getWidth(), expandBtn
137: .getHeight());
138: } else {
139: expandBtn.setText("+");
140: popupPanel.close();
141: }
142: repaint();
143: }
144:
145: }
146:
147: public void popupMenuCanceled(PopupMenuEvent e) {
148: }
149:
150: public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
151: expandBtn.setText("+");
152: }
153:
154: public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
155: }
156:
157: public void addPopupActions() {
158: responseTable.setBackground(new Color(255, 247, 225));
159: }
160:
161: public void setSelectedItem(Object value) {
162: questionText.setText(value.toString());
163: }
164:
165: public Component getTableCellEditorComponent(JTable table,
166: Object value, boolean isSelected, int row, int column) {
167: if (isSelected) {
168: setForeground(table.getSelectionForeground());
169: setBackground(table.getSelectionBackground());
170: } else {
171: setForeground(table.getForeground());
172: setBackground(table.getBackground());
173: }
174: return this ;
175: }
176:
177: public void removeCellListener(CellEditorListener listener) {
178: }
179:
180: public void stopCellEditing(EventObject o) {
181: }
182:
183: public Object getCellEditorValue() {
184: return questionText.getText();
185: }
186:
187: /**
188: * Returns true.
189: * @param e an event object
190: * @return true
191: */
192: public boolean isCellEditable(EventObject e) {
193: return true;
194: }
195:
196: /**
197: * Returns true.
198: * @param e an event object
199: * @return true
200: */
201: public boolean shouldSelectCell(EventObject anEvent) {
202: return true;
203: }
204:
205: /**
206: * Calls <code>fireEditingStopped</code> and returns true.
207: * @return true
208: */
209: public boolean stopCellEditing() {
210: fireEditingStopped();
211: return true;
212: }
213:
214: /**
215: * Calls <code>fireEditingCanceled</code>.
216: */
217: public void cancelCellEditing() {
218: fireEditingCanceled();
219: }
220:
221: /**
222: * Adds a <code>CellEditorListener</code> to the listener list.
223: * @param l the new listener to be added
224: */
225: public void addCellEditorListener(CellEditorListener l) {
226: listenerList.add(CellEditorListener.class, l);
227: }
228:
229: /**
230: * Removes a <code>CellEditorListener</code> from the listener list.
231: * @param l the listener to be removed
232: */
233: public void removeCellEditorListener(CellEditorListener l) {
234: listenerList.remove(CellEditorListener.class, l);
235: }
236:
237: /**
238: * Returns an array of all the <code>CellEditorListener</code>s added
239: * to this AbstractCellEditor with addCellEditorListener().
240: *
241: * @return all of the <code>CellEditorListener</code>s added or an empty
242: * array if no listeners have been added
243: * @since 1.4
244: */
245: public CellEditorListener[] getCellEditorListeners() {
246: return (CellEditorListener[]) listenerList
247: .getListeners(CellEditorListener.class);
248: }
249:
250: /**
251: * Notifies all listeners that have registered interest for
252: * notification on this event type. The event instance
253: * is created lazily.
254: *
255: * @see EventListenerList
256: */
257: protected void fireEditingStopped() {
258: // Guaranteed to return a non-null array
259: Object[] listeners = listenerList.getListenerList();
260: // Process the listeners last to first, notifying
261: // those that are interested in this event
262: for (int i = listeners.length - 2; i >= 0; i -= 2) {
263: if (listeners[i] == CellEditorListener.class) {
264: // Lazily create the event:
265: if (changeEvent == null)
266: changeEvent = new ChangeEvent(this );
267: ((CellEditorListener) listeners[i + 1])
268: .editingStopped(changeEvent);
269: }
270: }
271: }
272:
273: /**
274: * Notifies all listeners that have registered interest for
275: * notification on this event type. The event instance
276: * is created lazily.
277: *
278: * @see EventListenerList
279: */
280: protected void fireEditingCanceled() {
281: // Guaranteed to return a non-null array
282: Object[] listeners = listenerList.getListenerList();
283: // Process the listeners last to first, notifying
284: // those that are interested in this event
285: for (int i = listeners.length - 2; i >= 0; i -= 2) {
286: if (listeners[i] == CellEditorListener.class) {
287: // Lazily create the event:
288: if (changeEvent == null)
289: changeEvent = new ChangeEvent(this );
290: ((CellEditorListener) listeners[i + 1])
291: .editingCanceled(changeEvent);
292: }
293: }
294: }
295:
296: /**
297: * The protected <code>EditorDelegate</code> class.
298: */
299: protected class EditorDelegate implements ActionListener,
300: ItemListener, Serializable {
301: /** The value of this cell. */
302: protected Object value;
303:
304: /**
305: * Returns the value of this cell.
306: * @return the value of this cell
307: */
308: public Object getCellEditorValue() {
309: return value;
310: }
311:
312: /**
313: * Sets the value of this cell.
314: * @param value the new value of this cell
315: */
316: public void setValue(Object value) {
317: this .value = value;
318: }
319:
320: /**
321: * Returns true if <code>anEvent</code> is <b>not</b> a
322: * <code>MouseEvent</code>. Otherwise, it returns true
323: * if the necessary number of clicks have occurred, and
324: * returns false otherwise.
325: *
326: * @param anEvent the event
327: * @return true if cell is ready for editing, false otherwise
328: * @see #setClickCountToStart
329: * @see #shouldSelectCell
330: */
331: public boolean isCellEditable(EventObject anEvent) {
332: if (anEvent instanceof MouseEvent)
333: return ((MouseEvent) anEvent).getClickCount() >= clickCountToStart;
334:
335: return true;
336: }
337:
338: /**
339: * Returns true to indicate that the editing cell may
340: * be selected.
341: *
342: * @param anEvent the event
343: * @return true
344: * @see #isCellEditable
345: */
346: public boolean shouldSelectCell(EventObject anEvent) {
347: return true;
348: }
349:
350: /**
351: * Returns true to indicate that editing has begun.
352: *
353: * @param anEvent the event
354: */
355: public boolean startCellEditing(EventObject anEvent) {
356: return true;
357: }
358:
359: /**
360: * Stops editing and
361: * returns true to indicate that editing has stopped.
362: * This method calls <code>fireEditingStopped</code>.
363: *
364: * @return true
365: */
366: public boolean stopCellEditing() {
367: fireEditingStopped();
368: return true;
369: }
370:
371: /**
372: * Cancels editing. This method calls <code>fireEditingCanceled</code>.
373: */
374: public void cancelCellEditing() {
375: fireEditingCanceled();
376: }
377:
378: /**
379: * When an action is performed, editing is ended.
380: * @param e the action event
381: * @see #stopCellEditing
382: */
383: public void actionPerformed(ActionEvent e) {
384: XResponseSetEditor.this .stopCellEditing();
385: }
386:
387: /**
388: * When an item's state changes, editing is ended.
389: * @param e the action event
390: * @see #stopCellEditing
391: */
392: public void itemStateChanged(ItemEvent e) {
393: XResponseSetEditor.this.stopCellEditing();
394: }
395: }
396: }
|