001: /*
002: * BrowsingCellEditor.java
003: *
004: * Copyright (C) 2002, 2003, 2004, 2005, 2006 Takis Diakoumis
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021:
022: package org.executequery.components.table;
023:
024: import java.awt.BorderLayout;
025: import java.awt.Color;
026: import java.awt.Component;
027: import java.awt.Dimension;
028: import java.awt.Font;
029: import java.awt.Graphics;
030: import java.awt.Insets;
031: import java.awt.Point;
032: import java.awt.Rectangle;
033: import java.awt.event.ActionEvent;
034: import java.awt.event.ActionListener;
035: import java.awt.event.FocusEvent;
036: import java.awt.event.FocusListener;
037: import java.awt.event.ItemEvent;
038: import java.awt.event.ItemListener;
039: import java.awt.event.KeyListener;
040: import java.awt.event.MouseEvent;
041: import java.awt.event.MouseListener;
042: import java.io.Serializable;
043: import java.util.EventObject;
044: import javax.swing.BorderFactory;
045: import javax.swing.DefaultCellEditor;
046: import javax.swing.JButton;
047: import javax.swing.JPanel;
048: import javax.swing.JTable;
049: import javax.swing.JTextField;
050: import javax.swing.UIManager;
051: import javax.swing.border.Border;
052: import javax.swing.border.LineBorder;
053: import javax.swing.event.CellEditorListener;
054: import javax.swing.event.ChangeEvent;
055: import javax.swing.event.EventListenerList;
056: import javax.swing.table.TableCellEditor;
057: import javax.swing.table.TableCellRenderer;
058: import org.executequery.Constants;
059: import org.underworldlabs.swing.table.TableCellEditorValue;
060:
061: /* ----------------------------------------------------------
062: * CVS NOTE: Changes to the CVS repository prior to the
063: * release of version 3.0.0beta1 has meant a
064: * resetting of CVS revision numbers.
065: * ----------------------------------------------------------
066: */
067:
068: /**
069: * Table cell editor with a button to the right for option
070: * selection or similar.
071: *
072: * @author Takis Diakoumis
073: * @version $Revision: 1.5 $
074: * @date $Date: 2006/07/16 15:47:56 $
075: */
076: public abstract class BrowsingCellEditor extends DefaultCellEditor
077: implements TableCellEditorValue, TableCellRenderer,
078: TableCellEditor, ActionListener, FocusListener {
079:
080: transient protected ChangeEvent changeEvent = null;
081:
082: protected EventListenerList listenerList = new EventListenerList();
083:
084: protected EditorDelegate delegate;
085:
086: /** the selection button */
087: protected BrowseButton browseButton;
088:
089: /** the editor component */
090: protected JTextField textField;
091:
092: /** the editor component insets */
093: protected static Insets textFieldInsets;
094:
095: /** the editor component focus border */
096: protected static Border textFieldFocusBorder;
097:
098: /** the editor component focus border colour */
099: protected static Color focusBorderColor;
100:
101: /** the selection button background colour */
102: protected static Color buttonBackground;
103:
104: /** the selection button icon colour */
105: protected static Color iconColor;
106:
107: /** the base panel the components are rendered onto */
108: private RendererbasePanel base;
109:
110: static {
111: Border focusBorder = UIManager
112: .getBorder("Table.focusCellHighlightBorder");
113: if (focusBorder instanceof LineBorder) {
114: focusBorderColor = ((LineBorder) focusBorder)
115: .getLineColor();
116: textFieldFocusBorder = BorderFactory.createMatteBorder(1,
117: 1, 1, 0, focusBorderColor);
118: }
119: textFieldInsets = new Insets(0, 2, 0, 0);
120: iconColor = Color.DARK_GRAY.darker();
121: }
122:
123: /** Creates a new instance of ComboBoxCellRenderer */
124: public BrowsingCellEditor() {
125: super (new CellTextField());
126:
127: // assign the editor component
128: textField = (CellTextField) editorComponent;
129:
130: //super(new BorderLayout());
131: base = new RendererbasePanel();
132:
133: textField.setBorder(null);
134: textField.setMargin(textFieldInsets);
135: textField.setDisabledTextColor(UIManager
136: .getColor("Table.foreground"));
137:
138: delegate = new EditorDelegate();
139: textField.addActionListener(delegate);
140: textField.addFocusListener(this );
141:
142: browseButton = new BrowseButton();
143: browseButton.addActionListener(this );
144:
145: base.add(textField, BorderLayout.CENTER);
146: base.add(browseButton, BorderLayout.EAST);
147: }
148:
149: /** Indicates this has focus */
150: private static boolean hasFocusOnLabel;
151:
152: public Component getTableCellEditorComponent(JTable table,
153: Object value, boolean isSelected, int row, int column) {
154:
155: //Log.debug("getTableCellEditorComponent "+row);
156:
157: hasFocusOnLabel = false;
158: textField.setFont(table.getFont());
159: delegate.setValue(value);
160: textField.setEnabled(true);
161: return base;
162: }
163:
164: public Component getTableCellRendererComponent(JTable table,
165: Object value, boolean isSelected, boolean cellHasFocus,
166: int row, int col) {
167:
168: //Log.debug("getTableCellRendererComponent " +row);
169:
170: hasFocusOnLabel = cellHasFocus;
171: textField.setEnabled(false);
172: buttonBackground = table.getBackground();
173: textField.setBackground(buttonBackground);
174: base.setBackground(buttonBackground);
175:
176: delegate.setValue(value);
177:
178: return base;
179: }
180:
181: public boolean isFocusable() {
182: return true;
183: }
184:
185: public boolean getFocusTraversalKeysEnabled() {
186: return true;
187: }
188:
189: public void setDelegateValue(Object value) {
190: delegate.setValue(value);
191: }
192:
193: /**
194: * Returns the current editor value from the component
195: * defining this object.
196: *
197: * @return the editor's value
198: */
199: public String getEditorValue() {
200: return getCellEditorValue().toString();
201: }
202:
203: public void addKeyListener(KeyListener listener) {
204: if (textField != null) {
205: textField.addKeyListener(listener);
206: }
207: }
208:
209: public void removeKeyListener(KeyListener listener) {
210: if (textField != null) {
211: textField.removeKeyListener(listener);
212: }
213: }
214:
215: /**
216: * Defines the action to be taken upon activation of the
217: * selection button.
218: *
219: * @param e - the event
220: */
221: public abstract void actionPerformed(ActionEvent e);
222:
223: public void focusGained(FocusEvent e) {
224: //Log.debug("focusGained");
225: }
226:
227: public void focusLost(FocusEvent e) {
228: fireEditingStopped();
229: }
230:
231: public void setFont(Font font) {
232: textField.setFont(font);
233: }
234:
235: /**
236: * Returns a reference to the editor component.
237: *
238: * @return the editor <code>Component</code>
239: */
240: public Component getComponent() {
241: return textField;
242: }
243:
244: // ----------------------------
245: // borrowed from javax.swing.DefaultCellEditor
246:
247: public void addCellEditorListener(CellEditorListener l) {
248: listenerList.add(CellEditorListener.class, l);
249: }
250:
251: public void removeCellEditorListener(CellEditorListener l) {
252: listenerList.remove(CellEditorListener.class, l);
253: }
254:
255: public CellEditorListener[] getCellEditorListeners() {
256: return (CellEditorListener[]) listenerList
257: .getListeners(CellEditorListener.class);
258: }
259:
260: protected void fireEditingStopped() {
261: // Guaranteed to return a non-null array
262: Object[] listeners = listenerList.getListenerList();
263: // Process the listeners last to first, notifying
264: // those that are interested in this event
265: for (int i = listeners.length - 2; i >= 0; i -= 2) {
266: if (listeners[i] == CellEditorListener.class) {
267: // Lazily create the event:
268: if (changeEvent == null)
269: changeEvent = new ChangeEvent(this );
270: ((CellEditorListener) listeners[i + 1])
271: .editingStopped(changeEvent);
272: }
273: }
274: super .fireEditingStopped();
275: }
276:
277: protected void fireEditingCanceled() {
278: // Guaranteed to return a non-null array
279: Object[] listeners = listenerList.getListenerList();
280: // Process the listeners last to first, notifying
281: // those that are interested in this event
282: for (int i = listeners.length - 2; i >= 0; i -= 2) {
283: if (listeners[i] == CellEditorListener.class) {
284: // Lazily create the event:
285: if (changeEvent == null)
286: changeEvent = new ChangeEvent(this );
287: ((CellEditorListener) listeners[i + 1])
288: .editingCanceled(changeEvent);
289: }
290: }
291: super .fireEditingCanceled();
292: }
293:
294: public Object getCellEditorValue() {
295: return delegate.getCellEditorValue();
296: }
297:
298: public boolean isCellEditable(EventObject anEvent) {
299: return delegate.isCellEditable(anEvent);
300: }
301:
302: public boolean shouldSelectCell(EventObject anEvent) {
303: return delegate.shouldSelectCell(anEvent);
304: }
305:
306: public boolean stopCellEditing() {
307: return delegate.stopCellEditing();
308: }
309:
310: public void cancelCellEditing() {
311: delegate.cancelCellEditing();
312: }
313:
314: // ----------------------------------
315:
316: protected class RendererbasePanel extends JPanel {
317:
318: public RendererbasePanel() {
319: super (new BorderLayout());
320: setBorder(BorderFactory.createEmptyBorder(0, 2, 0, 0));
321: }
322:
323: public void paintComponent(Graphics g) {
324: super .paintComponent(g);
325: // check for a selection border
326: if (hasFocusOnLabel && focusBorderColor != null) {
327: g.setColor(focusBorderColor);
328: g.drawRect(0, 0, getWidth() - 1, getHeight() - 1);
329: }
330: }
331:
332: }
333:
334: /*
335: protected static class MyBoundedRangeModel extends DefaultBoundedRangeModel {
336: public MyBoundedRangeModel() {
337: super();
338: }
339:
340: public void setExtent(int newExtent) {
341: newExtent -= 500;
342: super.setExtent(newExtent);
343: }
344:
345: public void setRangeProperties(int newValue, int newExtent, int newMin, int newMax, boolean adjusting) {
346: newExtent -= 500;
347: super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
348: Log.debug(this);
349: }
350: }
351: */
352: protected static class CellTextField extends JTextField {
353: //MyBoundedRangeModel myModel;
354: public CellTextField() {
355: /*
356: myModel = new MyBoundedRangeModel();
357: myModel.addChangeListener(new ScrollRepainter());
358: */
359: }
360:
361: /*
362: public BoundedRangeModel getHorizontalVisibility() {
363: return myModel;
364: }
365: */
366:
367: /**
368: * Gets the scroll offset, in pixels.
369: *
370: * @return the offset >= 0
371: */
372: /*
373: public int getScrollOffset() {
374: return myModel.getValue();
375: }
376: */
377:
378: /**
379: * Sets the scroll offset, in pixels.
380: *
381: * @param scrollOffset the offset >= 0
382: */
383: /*
384: public void setScrollOffset(int scrollOffset) {
385: myModel.setValue(scrollOffset);
386: }
387: **/
388:
389: /**
390: * Scrolls the field left or right.
391: *
392: * @param r the region to scroll
393: */
394: /*
395: public void scrollRectToVisible(Rectangle r) {
396: //BoundedRangeModel visibility = super.getHorizontalVisibility();
397: //visibility.setExtent(100);
398: // convert to coordinate system of the bounded range
399: BoundedRangeModel visibility = getHorizontalVisibility();
400: Insets i = getInsets();
401: int x0 = r.x + visibility.getValue() - i.left;
402: int x1 = x0 + r.width;
403: if (x0 < visibility.getValue()) {
404: // Scroll to the left
405: visibility.setValue(x0);
406: } else if(x1 > visibility.getValue() + visibility.getExtent()) {
407: // Scroll to the right
408: visibility.setValue(x1 - visibility.getExtent());
409: }
410:
411: }
412: */
413:
414: public void paintComponent(Graphics g) {
415: super .paintComponent(g);
416: // check for a selection border
417: if (hasFocusOnLabel && focusBorderColor != null) {
418: int width = getWidth() - 1;
419: int height = getHeight() - 1;
420: g.setColor(focusBorderColor);
421: g.drawLine(0, 0, width, 0); // top
422: g.drawLine(0, height, width, height); // bottom
423: //g.drawLine(0, 0, 0, height); // left
424: }
425: }
426:
427: }
428:
429: /*
430: static class ScrollRepainter implements ChangeListener, Serializable {
431:
432: public void stateChanged(ChangeEvent e) {
433: textField.repaint();
434: }
435:
436: }
437: */
438:
439: // ----------------------------------
440: private int BUTTON_WIDTH = 16;
441:
442: protected class BrowseButton extends JButton implements
443: MouseListener {
444:
445: public BrowseButton() {
446: setFocusPainted(false);
447: setBorderPainted(false);
448: setOpaque(true);
449: //addMouseListener(this);
450:
451: try {
452: setUI(new javax.swing.plaf.basic.BasicButtonUI());
453: } catch (NullPointerException nullExc) {
454: }
455:
456: }
457:
458: public void paintComponent(Graphics g) {
459: int width = getWidth();
460: int height = getHeight();
461:
462: g.setColor(buttonBackground);
463: g.fillRect(1, 1, width - 2, height - 1);
464: g.setColor(iconColor);
465: g.drawRect(2, 3, width - 5, height - 5);
466:
467: int y = height - 5;
468: int x = ((width - (width - 4)) / 2) + 4;
469:
470: g.setColor(Color.BLACK);
471: g.drawLine(x, y, x, y);
472: g.drawLine(x + 3, y, x + 3, y);
473: g.drawLine(x + 6, y, x + 6, y);
474: }
475:
476: public boolean isFocusTraversable() {
477: return false;
478: }
479:
480: public void requestFocus() {
481: };
482:
483: public int getHeight() {
484: return super .getHeight() - 2;
485: }
486:
487: public Dimension getMaximumSize() {
488: return getPreferredSize();
489: }
490:
491: public Dimension getPreferredSize() {
492: return new Dimension(BUTTON_WIDTH + 2, getHeight());
493: }
494:
495: public void mouseReleased(MouseEvent e) {
496: actionPerformed(new ActionEvent(this , -1, Constants.EMPTY));
497: }
498:
499: public void mouseClicked(MouseEvent e) {
500: }
501:
502: public void mousePressed(MouseEvent e) {
503: }
504:
505: public void mouseEntered(MouseEvent e) {
506: }
507:
508: public void mouseExited(MouseEvent e) {
509: }
510:
511: }
512:
513: // borrowed from javax.swing.DefaultCellEditor.EditorDelegate
514: private class EditorDelegate implements ActionListener,
515: ItemListener, Serializable {
516:
517: /** The value of this cell. */
518: protected Object value;
519:
520: /**
521: * Returns the value of this cell.
522: * @return the value of this cell
523: */
524: public Object getCellEditorValue() {
525: return textField.getText();
526: //return value;
527: }
528:
529: /**
530: * Sets the value of this cell.
531: * @param value the new value of this cell
532: */
533: public void setValue(Object value) {
534: this .value = value;
535: textField.setText((value != null) ? value.toString()
536: : Constants.EMPTY);
537: }
538:
539: /**
540: * Returns true if <code>anEvent</code> is <b>not</b> a
541: * <code>MouseEvent</code>. Otherwise, it returns true
542: * if the necessary number of clicks have occurred, and
543: * returns false otherwise.
544: *
545: * @param anEvent the event
546: * @return true if cell is ready for editing, false otherwise
547: * @see #setClickCountToStart
548: * @see #shouldSelectCell
549: */
550: public boolean isCellEditable(EventObject anEvent) {
551: if (anEvent instanceof MouseEvent) {
552: MouseEvent mEvent = (MouseEvent) anEvent;
553:
554: Point point = mEvent.getPoint();
555: JTable table = (JTable) mEvent.getSource();
556: Rectangle cellRect = table.getCellRect(table
557: .rowAtPoint(point), table.columnAtPoint(point),
558: true);
559:
560: if (mEvent.getX() >= (cellRect.x + cellRect.width - BUTTON_WIDTH)) {
561: return true;
562: }
563:
564: return mEvent.getClickCount() >= 2;
565: }
566: return true;
567: }
568:
569: /**
570: * Returns true to indicate that the editing cell may
571: * be selected.
572: *
573: * @param anEvent the event
574: * @return true
575: * @see #isCellEditable
576: */
577: public boolean shouldSelectCell(EventObject anEvent) {
578: return true;
579: }
580:
581: /**
582: * Returns true to indicate that editing has begun.
583: *
584: * @param anEvent the event
585: */
586: public boolean startCellEditing(EventObject anEvent) {
587: return true;
588: }
589:
590: /**
591: * Stops editing and
592: * returns true to indicate that editing has stopped.
593: * This method calls <code>fireEditingStopped</code>.
594: *
595: * @return true
596: */
597: public boolean stopCellEditing() {
598: fireEditingStopped();
599: return true;
600: }
601:
602: /**
603: * Cancels editing. This method calls <code>fireEditingCanceled</code>.
604: */
605: public void cancelCellEditing() {
606: fireEditingCanceled();
607: }
608:
609: /**
610: * When an action is performed, editing is ended.
611: * @param e the action event
612: * @see #stopCellEditing
613: */
614: public void actionPerformed(ActionEvent e) {
615: BrowsingCellEditor.this .stopCellEditing();
616: }
617:
618: /**
619: * When an item's state changes, editing is ended.
620: * @param e the action event
621: * @see #stopCellEditing
622: */
623: public void itemStateChanged(ItemEvent e) {
624: BrowsingCellEditor.this.stopCellEditing();
625: }
626: }
627:
628: }
|