Java tutorial
/* * Copyright (c) 2008-2016 Haulmont. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.haulmont.cuba.desktop.gui.components; import com.haulmont.chile.core.model.Instance; import com.haulmont.chile.core.model.MetaClass; import com.haulmont.chile.core.model.MetaProperty; import com.haulmont.chile.core.model.MetaPropertyPath; import com.haulmont.chile.core.model.utils.InstanceUtils; import com.haulmont.cuba.client.ClientConfig; import com.haulmont.cuba.core.entity.Entity; import com.haulmont.cuba.core.global.*; import com.haulmont.cuba.desktop.gui.executors.impl.DesktopBackgroundWorker; import com.haulmont.cuba.desktop.sys.DesktopToolTipManager; import com.haulmont.cuba.desktop.sys.vcl.Picker; import com.haulmont.cuba.gui.components.Action; import com.haulmont.cuba.gui.components.*; import com.haulmont.cuba.gui.data.Datasource; import com.haulmont.cuba.gui.data.impl.WeakItemChangeListener; import com.haulmont.cuba.gui.data.impl.WeakItemPropertyChangeListener; import org.apache.commons.lang.StringUtils; import javax.annotation.Nullable; import javax.swing.AbstractAction; import javax.swing.*; import javax.swing.text.JTextComponent; import java.awt.event.*; import java.util.*; import static com.haulmont.bali.util.Preconditions.checkNotNullArgument; import static com.haulmont.cuba.gui.ComponentsHelper.findActionById; import static com.haulmont.cuba.gui.ComponentsHelper.handleFilteredAttributes; public class DesktopPickerField extends DesktopAbstractField<Picker> implements PickerField, Component.SecuredActionsHolder { protected CaptionMode captionMode = CaptionMode.ITEM; protected String captionProperty; protected Datasource<Entity> datasource; protected MetaClass metaClass; protected Object prevValue; protected String prevTextValue; protected java.util.List<Action> actionsOrder = new LinkedList<>(); protected java.util.Set<DesktopButton> buttons = new HashSet<>(); protected int modifiersMask; protected Map<Action, List<KeyStroke>> keyStrokesMap = new HashMap<>(); protected boolean updatingInstance; protected Metadata metadata = AppBeans.get(Metadata.NAME); protected MetadataTools metadataTools = AppBeans.get(MetadataTools.NAME); protected final ActionsPermissions actionsPermissions = new ActionsPermissions(this); protected Datasource.ItemChangeListener itemChangeListener; protected Datasource.ItemChangeListener securityItemChangeListener; protected Datasource.ItemPropertyChangeListener itemPropertyChangeListener; public DesktopPickerField() { impl = new Picker(); initModifiersMask(); } public DesktopPickerField(Picker picker) { impl = picker; initModifiersMask(); } @Override public CaptionMode getCaptionMode() { return captionMode; } @Override public void setCaptionMode(CaptionMode captionMode) { this.captionMode = captionMode; } @Override public String getCaptionProperty() { return captionProperty; } @Override public void setCaptionProperty(String captionProperty) { this.captionProperty = captionProperty; } @Override public MetaClass getMetaClass() { Datasource ds = getDatasource(); if (ds != null) { return metaProperty.getRange().asClass(); } else { return metaClass; } } @Override public void setMetaClass(MetaClass metaClass) { Datasource ds = getDatasource(); if (ds != null) throw new IllegalStateException("Datasource is not null"); this.metaClass = metaClass; } @Override public LookupAction addLookupAction() { LookupAction action = LookupAction.create(this); addAction(action); return action; } @Override public ClearAction addClearAction() { ClearAction action = ClearAction.create(this); addAction(action); return action; } @Override public OpenAction addOpenAction() { OpenAction action = OpenAction.create(this); addAction(action); return action; } @Override public void addFieldListener(final FieldListener listener) { final JTextField field = (JTextField) impl.getEditor(); field.addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { fireFieldListener(listener, field.getText()); } }); field.addKeyListener(new KeyAdapter() { protected static final int ENTER_CODE = 10; @Override public void keyPressed(KeyEvent e) { if (ENTER_CODE == e.getKeyCode()) { fireFieldListener(listener, field.getText()); } } }); } protected void fireFieldListener(FieldListener listener, String fieldText) { if (!(Objects.equals(prevTextValue, fieldText))) { prevTextValue = fieldText; listener.actionPerformed(fieldText, getValue()); } } protected void initModifiersMask() { Configuration configuration = AppBeans.get(Configuration.NAME); ClientConfig config = configuration.getConfig(ClientConfig.class); String[] strModifiers = StringUtils.split(config.getPickerShortcutModifiers().toUpperCase(), "-"); for (String strModifier : strModifiers) { KeyCombination.Modifier modifier = KeyCombination.Modifier.valueOf(strModifier); modifiersMask = modifiersMask | DesktopComponentsHelper.convertModifier(modifier); } } @Override public void setFieldEditable(boolean editable) { if (isEditable()) ((JTextField) impl.getEditor()).setEditable(editable); } @Override public <T> T getValue() { if ((datasource != null) && (metaPropertyPath != null) && (datasource.getState() == Datasource.State.VALID) && (datasource.getItem() != null)) { return datasource.getItem().getValueEx(metaPropertyPath.toString()); } else { //noinspection unchecked return (T) prevValue; } } @Override public void setValue(Object value) { DesktopBackgroundWorker.checkSwingUIAccess(); if (datasource == null && metaClass == null) { throw new IllegalStateException("Datasource or metaclass must be set for field"); } if (value != null) { Class fieldClass = getMetaClass().getJavaClass(); Class<?> valueClass = value.getClass(); //noinspection unchecked if (!fieldClass.isAssignableFrom(valueClass)) { throw new IllegalArgumentException( String.format("Could not set value with class %s to field with class %s", fieldClass.getCanonicalName(), valueClass.getCanonicalName())); } } if (!InstanceUtils.propertyValueEquals(prevValue, value)) { updateInstance(value); updateComponent(value); fireChangeListeners(value); } else { updateComponent(prevValue); } } private void updateInstance(Object value) { if (updatingInstance) return; if (InstanceUtils.propertyValueEquals(prevValue, value)) return; updatingInstance = true; try { if (datasource != null && metaProperty != null && datasource.getState() == Datasource.State.VALID && datasource.getItem() != null) InstanceUtils.setValueEx(datasource.getItem(), metaPropertyPath.getPath(), value); } finally { updatingInstance = false; } } @Override public Datasource getDatasource() { return datasource; } @Override public MetaProperty getMetaProperty() { return metaProperty; } @Override public MetaPropertyPath getMetaPropertyPath() { return metaPropertyPath; } public void checkDatasourceProperty(Datasource datasource, String property) { checkNotNullArgument(datasource); checkNotNullArgument(property); MetaPropertyPath metaPropertyPath = getResolvedMetaPropertyPath(datasource.getMetaClass(), property); if (!metaPropertyPath.getRange().isClass()) { throw new DevelopmentException(String.format("property '%s.%s' should have Entity type", datasource.getMetaClass().getName(), property)); } } @Override public void setDatasource(Datasource datasource, String property) { checkDatasourceProperty(datasource, property); this.datasource = datasource; // null datasource is not supported for this class /*if (datasource == null) { setValue(null); return; }*/ resolveMetaPropertyPath(datasource.getMetaClass(), property); itemChangeListener = e -> { if (updatingInstance) { return; } Object value = InstanceUtils.getValueEx(e.getItem(), metaPropertyPath.getPath()); updateComponent(value); fireChangeListeners(value); }; //noinspection unchecked datasource.addItemChangeListener(new WeakItemChangeListener(datasource, itemChangeListener)); itemPropertyChangeListener = e -> { if (updatingInstance) { return; } if (e.getProperty().equals(metaProperty.getName())) { updateComponent(e.getValue()); fireChangeListeners(e.getValue()); } }; //noinspection unchecked datasource.addItemPropertyChangeListener( new WeakItemPropertyChangeListener(datasource, itemPropertyChangeListener)); if ((datasource.getState() == Datasource.State.VALID) && (datasource.getItem() != null)) { Object newValue = InstanceUtils.getValueEx(datasource.getItem(), metaPropertyPath.getPath()); updateComponent(newValue); fireChangeListeners(newValue); } initRequired(metaPropertyPath); if (metaProperty.isReadOnly()) { setEditable(false); } handleFilteredAttributes(this, this.datasource, metaPropertyPath); securityItemChangeListener = e -> handleFilteredAttributes(this, this.datasource, metaPropertyPath); // noinspection unchecked this.datasource .addItemChangeListener(new WeakItemChangeListener(this.datasource, securityItemChangeListener)); initBeanValidator(); } protected void fireChangeListeners(Object newValue) { if (!InstanceUtils.propertyValueEquals(prevValue, newValue)) { Object oldValue = prevValue; prevValue = newValue; fireValueChanged(oldValue, newValue); } } protected void updateComponent(Object value) { String text; if (value == null) { text = ""; } else { if (value instanceof Instance) { if (captionMode.equals(CaptionMode.ITEM)) { text = ((Instance) value).getInstanceName(); } else { Object propertyValue = ((Instance) value).getValue(captionProperty); MetaClass valueClass = metadata.getClassNN(value.getClass()); MetaProperty property = valueClass.getProperty(captionProperty); text = metadataTools.format(propertyValue, property); } } else { text = value.toString(); } } impl.setValue(text); prevTextValue = text; updateMissingValueState(); } @Override protected void setCaptionToComponent(String caption) { super.setCaptionToComponent(caption); requestContainerUpdate(); } @Override public String getDescription() { return impl.getEditor().getToolTipText(); } @Override public void setDescription(String description) { if (!Objects.equals(this.getDescription(), description)) { impl.getEditor().setToolTipText(description); DesktopToolTipManager.getInstance().registerTooltip(impl.getEditor()); requestContainerUpdate(); } } @Override protected void setEditableToComponent(boolean editable) { for (Action action : actionsOrder) { if (action instanceof StandardAction) { ((StandardAction) action).setEditable(editable); } } if (!editable && impl.getEditor() instanceof JTextComponent) { JTextComponent editor = (JTextComponent) impl.getEditor(); editor.setEditable(false); } updateMissingValueState(); } @Override public void updateEnabled() { super.updateEnabled(); boolean resultEnabled = isEnabledWithParent(); for (DesktopButton button : buttons) { button.setParentEnabled(resultEnabled); } if (impl.getEditor() instanceof JTextComponent) { JTextComponent editor = (JTextComponent) impl.getEditor(); editor.setFocusable(resultEnabled); } } @Override public void addAction(Action action) { int index = findActionById(actionsOrder, action.getId()); if (index < 0) { index = actionsOrder.size(); } addAction(action, index); } @Override public void addAction(Action action, int index) { checkNotNullArgument(action, "action must be non null"); int oldIndex = findActionById(actionsOrder, action.getId()); if (oldIndex >= 0) { removeAction(actionsOrder.get(oldIndex)); if (index > oldIndex) { index--; } } actionsOrder.add(index, action); DesktopButton dButton = new DesktopButton(); dButton.setParentEnabled(isEnabledWithParent()); dButton.setShouldBeFocused(false); dButton.setAction(action); dButton.getImpl().setFocusable(false); dButton.getImpl().setText(""); impl.addButton(dButton.getImpl(), index); buttons.add(dButton); // apply Editable after action owner is set if (action instanceof StandardAction) { ((StandardAction) action).setEditable(isEditable()); } updateOrderedShortcuts(); ActionMap actionMap = getImpl().getInputField().getActionMap(); actionMap.put(action.getId(), new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { action.actionPerform(dButton); } }); if (action.getShortcutCombination() != null) { InputMap inputMap = getImpl().getInputField().getInputMap(JComponent.WHEN_FOCUSED); KeyStroke shortcutKeyStroke = DesktopComponentsHelper .convertKeyCombination(action.getShortcutCombination()); inputMap.put(shortcutKeyStroke, action.getId()); } actionsPermissions.apply(action); } @Override public void removeAction(@Nullable Action action) { if (action != null) { if (actionsOrder.remove(action)) { if (action.getOwner() != null && action.getOwner() instanceof DesktopButton) { JButton button = ((DesktopButton) action.getOwner()).getImpl(); impl.removeButton(button); } InputMap inputMap = getImpl().getInputField().getInputMap(JComponent.WHEN_FOCUSED); ActionMap actionMap = getImpl().getInputField().getActionMap(); List<KeyStroke> keyStrokes = keyStrokesMap.get(action); if (keyStrokes != null) { for (KeyStroke keyStroke : keyStrokes) { inputMap.remove(keyStroke); } actionMap.remove(action.getId()); } updateOrderedShortcuts(); } } } protected void updateOrderedShortcuts() { InputMap inputMap = getImpl().getInputField().getInputMap(JComponent.WHEN_FOCUSED); for (int i = 0; i < 9; i++) { KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_1 + i, modifiersMask, false); inputMap.remove(keyStroke); } int index = 0; for (Action action : actionsOrder) { KeyStroke keyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_1 + index, modifiersMask, false); List<KeyStroke> keyStrokes = new LinkedList<>(); keyStrokes.add(keyStroke); keyStrokesMap.put(action, keyStrokes); inputMap.put(keyStroke, action.getId()); index++; } } @Override public void removeAction(@Nullable String id) { Action action = getAction(id); if (action != null) { removeAction(action); } } @Override public void removeAllActions() { for (Action action : new ArrayList<>(actionsOrder)) { removeAction(action); } } @Override public Collection<Action> getActions() { return Collections.unmodifiableCollection(actionsOrder); } @Override @Nullable public Action getAction(String id) { for (Action action : getActions()) { if (Objects.equals(action.getId(), id)) { return action; } } return null; } @Override public void updateMissingValueState() { if (!(impl.getEditor() instanceof JTextComponent)) { return; } JTextComponent editor = (JTextComponent) impl.getEditor(); boolean value = required && isEditableWithParent() && StringUtils.isBlank(editor.getText()); decorateMissingValue(impl.getEditor(), value); } @Override public ActionsPermissions getActionsPermissions() { return actionsPermissions; } @Override public void setLookupSelectHandler(Runnable selectHandler) { // do nothing } @Override public Collection getLookupSelectedItems() { return Collections.singleton(getValue()); } @Override public void commit() { // do nothing } @Override public void discard() { // do nothing } @Override public boolean isBuffered() { // do nothing return false; } @Override public void setBuffered(boolean buffered) { // do nothing } @Override public boolean isModified() { // do nothing return false; } }