Java tutorial
//Copyright (C) 2010 Novabit Informationssysteme GmbH // //This file is part of Nuclos. // //Nuclos is free software: you can redistribute it and/or modify //it under the terms of the GNU Affero General Public License as published by //the Free Software Foundation, either version 3 of the License, or //(at your option) any later version. // //Nuclos 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 Affero General Public License for more details. // //You should have received a copy of the GNU Affero General Public License //along with Nuclos. If not, see <http://www.gnu.org/licenses/>. package org.nuclos.client.ui.collect.component; import info.clearthought.layout.TableLayout; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Font; import javax.swing.AbstractCellEditor; import javax.swing.InputVerifier; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.table.TableCellEditor; import org.apache.commons.lang.NullArgumentException; import org.apache.log4j.Logger; import org.nuclos.client.ui.Errors; import org.nuclos.client.ui.collect.DynamicRowHeightChangeProvider; import org.nuclos.client.ui.collect.SubForm; import org.nuclos.client.ui.collect.component.model.CollectableComponentModelEvent; import org.nuclos.client.ui.collect.component.model.CollectableComponentModelHelper; import org.nuclos.client.ui.collect.component.model.CollectableComponentModelListener; import org.nuclos.client.ui.collect.component.model.DetailsComponentModelEvent; import org.nuclos.client.ui.collect.component.model.SearchComponentModelEvent; import org.nuclos.client.ui.collect.component.verifier.DateInputVerifier; import org.nuclos.client.ui.collect.component.verifier.FloatAndDoubleInputVerifier; import org.nuclos.client.ui.collect.component.verifier.TrueInputVerifier; import org.nuclos.client.ui.labeled.LabeledComponent; import org.nuclos.common.collect.collectable.CollectableEntityField; import org.nuclos.common.collect.collectable.CollectableField; import org.nuclos.common.collect.collectable.searchcondition.CollectableSearchCondition; import org.nuclos.common.collect.exception.CollectableFieldFormatException; import org.nuclos.common2.StringUtils; import org.nuclos.common2.exception.CommonValidationException; /** * <code>TableCellEditor</code> for a <code>CollectableComponent</code>. This makes it possible to edit * any <code>CollectableField</code> in a <code>JTable</code>. * <br> * <br>Created by Novabit Informationssysteme GmbH * <br>Please visit <a href="http://www.novabit.de">www.novabit.de</a> * * @author <a href="mailto:Christoph.Radig@novabit.de">Christoph.Radig</a> * @version 01.00.00 */ public class CollectableComponentTableCellEditor extends AbstractCellEditor implements TableCellEditor, CollectableComponentModelListener { private static final Logger log = Logger.getLogger(CollectableComponentTableCellEditor.class); private CollectableComponent clctcomp; private final CollectableEntityField clctef; private final CollectableComponentModelHelper clctcompmodelhelper = new CollectableComponentModelHelper(); private final boolean bSearchable; private CollectableSearchCondition clctcond; private CollectableField clctfValue; private int editingRow = -1; /** * creates a <code>TableCellEditor</code> that lazily creates a suitable <code>CollectableComponent</code> * according to <code>clctef</code>. * @param clctef * @postcondition !this.isSearchable() */ public CollectableComponentTableCellEditor(CollectableEntityField clctef) { if (clctef == null) { throw new NullArgumentException("clctef"); } this.clctef = clctef; this.bSearchable = false; this.editorDateInputVerifier = new DateInputVerifier(clctef); this.floatAndDoubleInputVerifier = new FloatAndDoubleInputVerifier(clctef); this.trueInputVerifier = new TrueInputVerifier(); assert !this.isSearchable(); } /** * creates a <code>TableCellEditor</code> that uses the given <code>CollectableComponent</code>. * @param clctcomp * @postcondition !this.isSearchable() * @deprecated This for backwards compatibility with LindaCollectableComponentTableCellEditor only. */ @Deprecated public CollectableComponentTableCellEditor(CollectableComponent clctcomp) { this(clctcomp, false); assert !this.isSearchable(); } /** * creates a <code>TableCellEditor</code> that uses the given <code>CollectableComponent</code>. * @param clctcomp * @param bSearchable * @postcondition this.isSearchable() <--> bSearchable */ public CollectableComponentTableCellEditor(CollectableComponent clctcomp, boolean bSearchable) { if (clctcomp == null) { throw new NullArgumentException("clctcomp"); } this.clctcomp = clctcomp; this.clctcomp.getModel().addCollectableComponentModelListener(this); this.clctef = clctcomp.getEntityField(); this.bSearchable = bSearchable; this.editorDateInputVerifier = new DateInputVerifier(clctef); this.floatAndDoubleInputVerifier = new FloatAndDoubleInputVerifier(clctef); this.trueInputVerifier = new TrueInputVerifier(); assert this.isSearchable() == bSearchable; } public boolean isSearchable() { return this.bSearchable; } public CollectableComponent getCollectableComponent() { if (this.clctcomp == null) { // create the component lazily: this.clctcomp = CollectableComponentFactory.getInstance().newCollectableComponent(this.clctef, null, false); /** @todo add preferences to clctcomp */ this.clctcomp.getModel().addCollectableComponentModelListener(this); } // don't show the component's label: this.clctcomp.setLabelText(null); return this.clctcomp; } @Override public void collectableFieldChangedInModel(CollectableComponentModelEvent ev) { /** @todo ChangeListener would be cleaner here. */ if (ev.collectableFieldHasChanged()) { // pass the event on: this.fireCollectableComponentModelChanged(ev); } } @Override public void searchConditionChangedInModel(SearchComponentModelEvent ev) { this.fireSearchableCollectableComponentModelChanged(ev); } @Override public void valueToBeChanged(DetailsComponentModelEvent ev) { /** @todo Is it right to do nothing here? (Probably: "yes, because this is never called") */ // do nothing } public void addCollectableComponentModelListener(CollectableComponentModelListener listener) { clctcompmodelhelper.addCollectableComponentModelListener(listener); } public void removeCollectableComponentModelListener(CollectableComponentModelListener listener) { clctcompmodelhelper.removeCollectableComponentModelListener(listener); } private void fireCollectableComponentModelChanged(CollectableComponentModelEvent ev) { clctcompmodelhelper.fireCollectableFieldChanged(ev.getCollectableComponentModel(), ev.getOldValue(), ev.getNewValue()); } private void fireSearchableCollectableComponentModelChanged(SearchComponentModelEvent ev) { clctcompmodelhelper.fireSearchConditionChanged(ev.getSearchComponentModel()); } @Override public Object getCellEditorValue() { return this.isSearchable() ? (Object) this.clctcond : (Object) this.clctfValue; } @Override public boolean stopCellEditing() { //noinspection LocalCanBeFinal boolean result; try { this.setValue(); result = true; this.fireEditingStopped(); } catch (CollectableFieldFormatException ex) { log.debug("stopCellEditing: Invalid value in cell.", ex); result = false; /** @todo returning false doesn't seem to work for BasicTableUI. */ } return result; } @Override public void cancelCellEditing() { this.resetValue(); this.fireEditingCanceled(); } private void setValue() throws CollectableFieldFormatException { if (this.isSearchable()) { this.clctcond = this.getCollectableComponent().getSearchCondition(); } else { try { this.getCollectableComponent().makeConsistent(); } catch (CollectableFieldFormatException ex) { log.warn("validation failed", ex); Errors.getInstance().showExceptionDialog(clctcomp.getJComponent(), new CommonValidationException( StringUtils.getParameterizedExceptionMessage("field.invalid.value", clctef.getLabel())));//"Das Feld \"" + clctef.getLabel() + "\" hat keinen g\u00fcltigen Wert.")); } this.clctfValue = this.getCollectableComponent().getField(); } } private void resetValue() { if (this.isSearchable()) { this.clctcond = null; } else { this.clctfValue = null; } } @Override public Component getTableCellEditorComponent(final JTable tbl, Object oValue, boolean bSelected, final int iRow, final int iColumn) { final CollectableComponent clctcomp = this.getCollectableComponent(); log.debug("getTableCellEditorComponent - row: " + iRow + " - column: " + iColumn + " - component name: " + clctcomp.getFieldName() + " - selected: " + bSelected); this.editingRow = iRow; clctcomp.getModel().removeCollectableComponentModelListener(this); if (this.isSearchable()) { clctcomp.getSearchModel().setSearchCondition((CollectableSearchCondition) oValue); } else { clctcomp.getModel().setField((CollectableField) oValue); } clctcomp.getModel().addCollectableComponentModelListener(this); boolean subformMinRowHeight = false; // a datefield gets a inputverifier /** @todo this needs to be called only once for the component - move to getCollectableComponent/ctor! */ /** @todo no input verifier for searchable components? */ if (!this.isSearchable() && clctcomp instanceof CollectableDateChooser) { final CollectableDateChooser clctdatechooser = (CollectableDateChooser) clctcomp; clctdatechooser.getDateChooser().getJTextField().setInputVerifier(editorDateInputVerifier); } if (!this.isSearchable() && clctcomp instanceof CollectableTextField) { final CollectableTextField clcttextfield = (CollectableTextField) clctcomp; clcttextfield.getJTextComponent().setInputVerifier(floatAndDoubleInputVerifier); } if (!this.isSearchable() && clctcomp instanceof CollectableComboBox) { final CollectableComboBox clctcombobox = (CollectableComboBox) clctcomp; ((JTextField) clctcombobox.getJComboBox().getEditor().getEditorComponent()) .setInputVerifier(trueInputVerifier); //subformMinRowHeight = true; } if (!this.isSearchable() && clctcomp instanceof CollectableListOfValues) { final CollectableListOfValues clctlov = (CollectableListOfValues) clctcomp; clctlov.getJTextField().setInputVerifier(trueInputVerifier); } JComponent result = this.clctcomp.getJComponent(); if (result instanceof LabeledComponent) { /** @todo find a better solution */ result = ((LabeledComponent) result).getControlComponent(); } else if (result instanceof JCheckBox) { final JCheckBox chkbx = (JCheckBox) result; chkbx.setHorizontalAlignment(JCheckBox.CENTER); /** @todo setting the colors here doesn't work as bSelected is false when clicking in a nonselected row. */ // result.setBackground(bSelected ? tbl.getSelectionBackground() : tbl.getBackground()); // result.setForeground(bSelected ? tbl.getSelectionForeground() : tbl.getForeground()); } if (clctcomp instanceof DynamicRowHeightChangeProvider) { return result; } else { return new AlignTop(result, subformMinRowHeight); } } protected class AlignTop extends JPanel { final JComponent editor; public AlignTop(JComponent editor, boolean subformMinRowHeight) { super(subformMinRowHeight ? new TableLayout(new double[] { TableLayout.FILL }, new double[] { SubForm.MIN_ROWHEIGHT }) : new BorderLayout()); this.editor = editor; setOpaque(false); if (subformMinRowHeight) { add(editor, "0,0"); } else { add(editor, BorderLayout.CENTER); } } @Override public Font getFont() { if (this.editor != null) return this.editor.getFont(); return super.getFont(); } @Override public void setFont(Font font) { super.setFont(font); if (this.editor != null) this.editor.setFont(font); } @Override public boolean requestFocusInWindow() { return editor.requestFocusInWindow(); } } public int getLastEditingRow() { return editingRow; } /** * Verifies the input of the datechooser * It has two states: checkstate = true or false * In this way it is possible to show a ExceptionDialog * (the ExceptionDialog needs the focus to show it self) */ private final InputVerifier editorDateInputVerifier; /** * Verifies the input of the text field with integer or double values * It has two states: checkstate = true or false * In this way it is possible to show a ExceptionDialog * (the ExceptionDialog needs the focus to show it self) */ private final InputVerifier floatAndDoubleInputVerifier; /** * Verifies the input of the text field * It has two states: checkstate = true or false * In this way it is possible to show a ExceptionDialog * (the ExceptionDialog needs the focus to show it self) */ private final InputVerifier trueInputVerifier; } // class CollectableComponentTableCellEditor