/*************************************************** * * cismet GmbH, Saarbruecken, Germany * * ... and it just works. * ****************************************************/ package de.cismet.cids.editors; import Sirius.navigator.tools.MetaObjectCache; import Sirius.server.localserver.attribute.MemberAttributeInfo; import Sirius.server.middleware.types.MetaClass; import Sirius.server.middleware.types.MetaClassStore; import Sirius.server.middleware.types.MetaObject; import org.apache.log4j.Logger; import org.jdesktop.beansbinding.Converter; import org.jdesktop.beansbinding.Validator; import java.awt.Color; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import javax.swing.JCheckBox; import javax.swing.JPanel; import javax.swing.SwingWorker; import javax.swing.border.EmptyBorder; import de.cismet.cids.navigator.utils.ClassCacheMultiple; import de.cismet.tools.CismetThreadPool; /** * DOCUMENT ME! * * @author therter * @version $Revision$, $Date$ */ public class DefaultBindableCheckboxField extends JPanel implements Bindable, MetaClassStore, ActionListener { //~ Static fields/initializers --------------------------------------------- private static final Logger LOG = Logger.getLogger(DefaultBindableCheckboxField.class); //~ Instance fields -------------------------------------------------------- private PropertyChangeSupport propertyChangeSupport; private List selectedElements = null; private MetaClass mc = null; private Map<JCheckBox, MetaObject> boxToObjectMapping = new HashMap<JCheckBox, MetaObject>(); private volatile boolean threadRunning = false; private Color backgroundSelected = null; private Color backgroundUnselected = null; private boolean readOnly = false; private Comparator<MetaObject> comparator; //~ Constructors ----------------------------------------------------------- /** * Creates a new CustomReferencedCheckboxField object. */ public DefaultBindableCheckboxField() { } /** * Creates a new CustomReferencedCheckboxField object. * * @param comparator DOCUMENT ME! */ public DefaultBindableCheckboxField(final Comparator<MetaObject> comparator) { this.comparator = comparator; } //~ Methods ---------------------------------------------------------------- /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public PropertyChangeSupport getPropertyChangeSupport() { if (propertyChangeSupport == null) { propertyChangeSupport = new PropertyChangeSupport(this); } return propertyChangeSupport; } /** * Add PropertyChangeListener. * * @param listener DOCUMENT ME! */ @Override public void addPropertyChangeListener(final PropertyChangeListener listener) { if (listener != null) { getPropertyChangeSupport().addPropertyChangeListener(listener); } } /** * Remove PropertyChangeListener. * * @param listener DOCUMENT ME! */ @Override public void removePropertyChangeListener(final PropertyChangeListener listener) { getPropertyChangeSupport().removePropertyChangeListener(listener); } /** * DOCUMENT ME! * * @param elements DOCUMENT ME! */ public void setSelectedElements(final Object elements) { if (elements instanceof List) { this.selectedElements = (List)elements; } activateSelectedObjects(); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ public Object getSelectedElements() { return selectedElements; } @Override public String getBindingProperty() { return "selectedElements"; } @Override public Validator getValidator() { return null; } @Override public Converter getConverter() { return null; } @Override public Object getNullSourceValue() { return null; } @Override public Object getErrorSourceValue() { return null; } @Override public MetaClass getMetaClass() { return this.mc; } @Override public void setMetaClass(final MetaClass metaClass) { if (LOG.isDebugEnabled()) { LOG.debug("set meta class " + ((metaClass != null) ? metaClass.getName() : "null")); } CismetThreadPool.execute(new SwingWorker<MetaObject[], Void>() { @Override protected MetaObject[] doInBackground() throws Exception { Thread.currentThread().setName("DefaultBindableCheckboxField setMetaClass()"); while (!setThreadRunning()) { try { Thread.sleep(20); } catch (InterruptedException ex) { } } mc = metaClass; boxToObjectMapping = new HashMap<JCheckBox, MetaObject>(); if (mc != null) { final MetaClass foreignClass = getReferencedClass(mc); final String query = "select " + foreignClass.getID() + ", " + foreignClass.getPrimaryKey() + " from " + foreignClass.getTableName(); return MetaObjectCache.getInstance().getMetaObjectsByQuery(query, mc.getDomain()); } else { LOG.error("The meta class was not set.", new Throwable()); } return null; } @Override protected void done() { try { final MetaObject[] metaObjects = get(); DefaultBindableCheckboxField.this.removeAll(); if (metaObjects != null) { if (comparator != null) { Arrays.sort(metaObjects, comparator); } JCheckBox box = null; DefaultBindableCheckboxField.this.setLayout(new GridLayout(metaObjects.length, 1)); if (LOG.isDebugEnabled()) { LOG.debug(metaObjects.length + "objects found."); } for (final MetaObject tmpMc : metaObjects) { box = new JCheckBox(tmpMc.getBean().toString()); box.addActionListener(DefaultBindableCheckboxField.this); box.setOpaque(false); box.setContentAreaFilled(false); box.setFocusPainted(false); box.setBorder(new EmptyBorder(2, 2, 0, 2)); add(box); boxToObjectMapping.put(box, tmpMc); } activateSelectedObjects(); setReadOnly(readOnly); } } catch (Exception e) { LOG.error("Error while filling a checkbox field.", e); // NOI18N } finally { threadRunning = false; } } }); } /** * DOCUMENT ME! */ private void activateSelectedObjects() { if (selectedElements != null) { final Iterator<JCheckBox> it = boxToObjectMapping.keySet().iterator(); while (it.hasNext()) { final JCheckBox tmp = it.next(); final MetaObject mo = boxToObjectMapping.get(tmp); if ((mo != null) && selectedElements.contains(mo.getBean())) { tmp.setSelected(true); if (backgroundSelected != null) { tmp.setOpaque(true); tmp.setContentAreaFilled(true); tmp.setBackground(backgroundSelected); } } else { tmp.setSelected(false); if (backgroundUnselected != null) { tmp.setOpaque(true); tmp.setContentAreaFilled(true); tmp.setBackground(backgroundUnselected); } } } } } /** * DOCUMENT ME! * * @param mclass DOCUMENT ME! * * @return DOCUMENT ME! */ private MetaClass getReferencedClass(final MetaClass mclass) { MetaClass result = mclass; if (mclass.isArrayElementLink()) { final HashMap hm = mc.getMemberAttributeInfos(); final Iterator it = hm.values().iterator(); while (it.hasNext()) { final Object tmp = it.next(); if (tmp instanceof MemberAttributeInfo) { if (((MemberAttributeInfo)tmp).isForeignKey()) { final int classId = ((MemberAttributeInfo)tmp).getForeignKeyClassId(); result = ClassCacheMultiple.getMetaClass(mclass.getDomain(), classId); } } } } else { LOG.error("The given class " + mclass.getName() + " is no array element link."); } return result; } /** * DOCUMENT ME! * * @param decider DOCUMENT ME! * @param removeSelectedElements DOCUMENT ME! */ public void refreshCheckboxState(final FieldStateDecider decider, final boolean removeSelectedElements) { refreshCheckboxState(decider, false, removeSelectedElements); } /** * DOCUMENT ME! * * @param decider DOCUMENT ME! * @param hideElements Elements which are not accepted by the decider should be shown as unenabled, if * hideElements is false. Otherwise, the elements should not be shown, if they are * not accepted by the decider. * @param removeSelectedElements DOCUMENT ME! */ public void refreshCheckboxState(final FieldStateDecider decider, final boolean hideElements, final boolean removeSelectedElements) { CismetThreadPool.execute(new SwingWorker<Void, Void>() { @Override protected Void doInBackground() throws Exception { Thread.currentThread().setName("DefaultBindableCheckBoxField refreshCheckboxState()"); while (!setThreadRunning()) { try { Thread.sleep(20); } catch (InterruptedException ex) { } } return null; } @Override protected void done() { try { final JCheckBox[] boxes = boxToObjectMapping.keySet() .toArray(new JCheckBox[boxToObjectMapping.keySet().size()]); if (comparator != null) { Arrays.sort(boxes, new Comparator<JCheckBox>() { @Override public int compare(final JCheckBox o1, final JCheckBox o2) { final MetaObject m1 = boxToObjectMapping.get(o1); final MetaObject m2 = boxToObjectMapping.get(o2); return comparator.compare(m1, m2); } }); } if (removeSelectedElements) { selectedElements.clear(); } if (hideElements) { removeAll(); } for (final JCheckBox box : boxes) { if (!hideElements) { box.setEnabled( !readOnly && decider.isCheckboxForClassActive(boxToObjectMapping.get(box))); } else { if (decider.isCheckboxForClassActive(boxToObjectMapping.get(box))) { add(box); } } box.setSelected(false); if (Thread.currentThread().isInterrupted()) { return; } } activateSelectedObjects(); } finally { threadRunning = false; } } }); } /** * DOCUMENT ME! */ public void dispose() { } @Override public void actionPerformed(final ActionEvent ae) { final MetaObject mo = boxToObjectMapping.get((JCheckBox)ae.getSource()); final List old = new ArrayList(selectedElements); if (selectedElements.contains(mo.getBean())) { selectedElements.remove(mo.getBean()); } else { selectedElements.add(mo.getBean()); } final JCheckBox box = (JCheckBox)ae.getSource(); box.setOpaque(false); box.setContentAreaFilled(false); if (box.isSelected()) { if (backgroundSelected != null) { box.setOpaque(true); box.setContentAreaFilled(true); box.setBackground(backgroundSelected); } } else { if (backgroundUnselected != null) { box.setOpaque(true); box.setContentAreaFilled(true); box.setBackground(backgroundUnselected); } } propertyChangeSupport.firePropertyChange("selectedElements", old, selectedElements); propertyChangeSupport.firePropertyChange("selectedElements", null, mo.getBean()); } /** * DOCUMENT ME! * * @return DOCUMENT ME! */ private synchronized boolean setThreadRunning() { if (threadRunning) { return false; } else { threadRunning = true; return true; } } /** * DOCUMENT ME! * * @return the backgroundSelected */ public Color getBackgroundSelected() { return backgroundSelected; } /** * DOCUMENT ME! * * @param backgroundSelected the backgroundSelected to set */ public void setBackgroundSelected(final Color backgroundSelected) { this.backgroundSelected = backgroundSelected; } /** * DOCUMENT ME! * * @return the backgroundUnselected */ public Color getBackgroundUnselected() { return backgroundUnselected; } /** * DOCUMENT ME! * * @param backgroundUnselected the backgroundUnselected to set */ public void setBackgroundUnselected(final Color backgroundUnselected) { this.backgroundUnselected = backgroundUnselected; } /** * DOCUMENT ME! * * @param readOnly DOCUMENT ME! */ public void setReadOnly(final boolean readOnly) { this.readOnly = readOnly; final Iterator<JCheckBox> it = boxToObjectMapping.keySet().iterator(); while (it.hasNext()) { final JCheckBox box = it.next(); box.setEnabled(!readOnly && box.isEnabled()); } } }