All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.nuiton.jaxx.application.swing.AbstractApplicationUIHandler Maven / Gradle / Ivy

There is a newer version: 3.0-alpha-1
Show newest version
package org.nuiton.jaxx.application.swing;

/*
 * #%L
 * JAXX :: Application Swing
 * %%
 * Copyright (C) 2008 - 2014 Code Lutin, Tony Chemit
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as 
 * published by the Free Software Foundation, either version 3 of the 
 * License, or (at your option) any later version.
 * 
 * This program 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 General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public 
 * License along with this program.  If not, see
 * .
 * #L%
 */

import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import jaxx.runtime.JAXXUtil;
import jaxx.runtime.SwingUtil;
import jaxx.runtime.spi.UIHandler;
import jaxx.runtime.swing.JAXXWidgetUtil;
import jaxx.runtime.swing.editor.FileEditor;
import jaxx.runtime.swing.editor.NumberEditor;
import jaxx.runtime.swing.editor.SimpleTimeEditor;
import jaxx.runtime.swing.editor.bean.BeanComboBox;
import jaxx.runtime.swing.editor.bean.BeanDoubleList;
import jaxx.runtime.swing.editor.bean.BeanFilterableComboBox;
import jaxx.runtime.swing.editor.bean.BeanUIUtil;
import jaxx.runtime.swing.editor.cell.NumberCellEditor;
import jaxx.runtime.swing.editor.gis.DmdCoordinateEditor;
import jaxx.runtime.swing.editor.gis.DmsCoordinateEditor;
import jaxx.runtime.swing.renderer.DecoratorListCellRenderer;
import jaxx.runtime.swing.renderer.DecoratorTableCellRenderer;
import jaxx.runtime.validator.swing.SwingValidator;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdesktop.swingx.JXDatePicker;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor;
import org.jdesktop.swingx.autocomplete.ObjectToStringConverter;
import org.jdesktop.swingx.table.TableColumnExt;
import org.nuiton.decorator.Decorator;
import org.nuiton.decorator.JXPathDecorator;
import org.nuiton.jaxx.application.ApplicationConfiguration;
import org.nuiton.jaxx.application.bean.JavaBeanObjectUtil;
import org.nuiton.jaxx.application.swing.action.AbstractApplicationAction;
import org.nuiton.jaxx.application.swing.action.ApplicationActionUI;
import org.nuiton.jaxx.application.swing.action.ApplicationUIAction;
import org.nuiton.jaxx.application.swing.table.ColumnIdentifier;
import org.nuiton.jaxx.application.swing.util.ApplicationUIUtil;
import org.nuiton.jaxx.application.swing.util.Cancelable;
import org.nuiton.jaxx.application.type.ApplicationProgressionModel;
import org.nuiton.validator.bean.AbstractValidator;
import org.nuiton.validator.bean.list.BeanListValidator;
import org.nuiton.validator.bean.simple.SimpleBeanValidator;

import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Action;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JRootPane;
import javax.swing.JScrollPane;
import javax.swing.JSpinner;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JViewport;
import javax.swing.KeyStroke;
import javax.swing.ListCellRenderer;
import javax.swing.ListSelectionModel;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.border.LineBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumnModel;
import javax.swing.text.JTextComponent;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dialog;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.ItemEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.io.Serializable;
import java.text.ParseException;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;

import static org.nuiton.i18n.I18n.t;

/**
 * Handler of any ui.
 *
 * Created on 11/23/13.
 *
 * @author Tony Chemit - [email protected]
 * @since 2.8
 */
public abstract class AbstractApplicationUIHandler> implements UIHandler {

    /** Logger. */
    private static final Log log =
            LogFactory.getLog(AbstractApplicationUIHandler.class);

    public abstract void onCloseUI();

    /**
     * @return a single validator :(
     * @deprecated since 2.10, prefer use now the {@link #getSimpleValidators()} and {@link #getListValidators()} initialized in {@link #initUI(ApplicationUI)} method.
     */
    @Deprecated
    public abstract SwingValidator getValidator();

    public abstract Component getTopestUI();

    public abstract  Decorator getDecorator(Class beanType, String decoratorContext);

    /**
     * Hook to prepare popup just before showing it.
     *
     * The right place to update actions accessibility; a quite better design
     * than trying to update each time something change in the table...
     *
     * @param modelRowIndex    selected row index (or lowest selected one)
     * @param modelColumnIndex selected column index
     * @since 2.6
     */
    protected void beforeOpenPopup(int modelRowIndex, int modelColumnIndex) {
        // by default do nothing
    }

    /**
     * Global application context.
     */
    private ApplicationUIContext context;

    /**
     * UI handled.
     */
    protected UI ui;

    /**
     * Simple validators detected in {@link #initUI(ApplicationUI)} method.
     *
     * @since 2.10
     */
    private Set simpleValidators;

    /**
     * List validators detected in {@link #initUI(ApplicationUI)} method.
     *
     * @since 2.10
     */
    private Set listValidators;

    //------------------------------------------------------------------------//
    //-- Public methods                                                     --//
    //------------------------------------------------------------------------//

    /**
     * @deprecated since 2.10, should move this to {@link SwingUtil#newComboModel(Object...)}
     */
    @Deprecated
    public DefaultComboBoxModel newComboModel(Object... items) {
        return SwingUtil.newComboModel(items);
    }

    public final M getModel() {
        return ui.getModel();
    }

    public final UI getUI() {
        return ui;
    }

    public Set getSimpleValidators() {
        return simpleValidators;
    }

    public Set getListValidators() {
        return listValidators;
    }

    @Override
    public void beforeInit(UI ui) {
        this.ui = ui;
    }

    public ApplicationUIContext getContext() {
        if (context == null) {
            context = ApplicationUIUtil.getApplicationContext(ui);
        }
        return context;
    }

    public void setText(KeyEvent event, String property) {
        JTextComponent field = (JTextComponent) event.getSource();
        String value = field.getText();
        JavaBeanObjectUtil.setProperty(getModel(), property, value);
    }

    public void setFile(ActionEvent event, String property) {
        FileEditor field = (FileEditor) event.getSource();
        File value = field.getSelectedFile();
        JavaBeanObjectUtil.setProperty(getModel(), property, value);
    }

    public void setBoolean(ItemEvent event, String property) {
        boolean value = event.getStateChange() == ItemEvent.SELECTED;
        JavaBeanObjectUtil.setProperty(getModel(), property, value);
    }

    public void setDate(ActionEvent event, String property) {
        JXDatePicker field = (JXDatePicker) event.getSource();
        Date value = field.getDate();
        JavaBeanObjectUtil.setProperty(getModel(), property, value);
    }

    public void selectListData(ListSelectionEvent event, String property) {
        if (!event.getValueIsAdjusting()) {
            JList list = (JList) event.getSource();
            ListSelectionModel selectionModel = list.getSelectionModel();

            selectionModel.setValueIsAdjusting(true);
            try {
                List selectedList = Lists.newLinkedList();

                for (int index : list.getSelectedIndices()) {
                    Object o = list.getModel().getElementAt(index);
                    selectedList.add(o);
                }
                JavaBeanObjectUtil.setProperty(getModel(), property, selectedList);
            } finally {
                selectionModel.setValueIsAdjusting(false);
            }
        }
    }

    public void openDialog(ApplicationUI dialogContent,
                           String title, Dimension dim) {
        Component topestUI = getTopestUI();

        JDialog result;
        if (topestUI instanceof Frame) {
            result = new JDialog((Frame) topestUI, title, true);
        } else {
            result = new JDialog((Dialog) topestUI, title, true);
        }

        result.add((Component) dialogContent);
        result.setResizable(true);

        result.setSize(dim);

        final AbstractApplicationUIHandler handler = dialogContent.getHandler();

        if (handler instanceof Cancelable) {

            // add a auto-close action
            JRootPane rootPane = result.getRootPane();

            KeyStroke shortcutClosePopup = getContext().getConfiguration().getShortcutClosePopup();

            rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
                    shortcutClosePopup, "close");
            rootPane.getActionMap().put("close", new AbstractAction() {
                private static final long serialVersionUID = 1L;

                @Override
                public void actionPerformed(ActionEvent e) {
                    ((Cancelable) handler).cancel();
                }
            });
        }

        result.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosed(WindowEvent e) {
                Component ui = (Component) e.getSource();
                if (log.isDebugEnabled()) {
                    log.debug("Destroy ui " + ui);
                }
                JAXXUtil.destroy(ui);
            }
        });
        SwingUtil.center(getContext().getMainUI(), result);
        result.setVisible(true);
    }

    public void closeDialog() {
        getParentContainer(JDialog.class).setVisible(false);
    }

    public  U getParentContainer(Class uiType) {
        return SwingUtil.getParentContainer(ui, uiType);
    }

    public static final String CONFIRMATION_FORMAT = "%s

%s"; public int askSaveBeforeLeaving(String message) { String htmlMessage = String.format( CONFIRMATION_FORMAT, message, t("jaxx.application.common.askSaveBeforeLeaving.help")); return JOptionPane.showConfirmDialog( getTopestUI(), htmlMessage, t("jaxx.application.common.askSaveBeforeLeaving.title"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); } public boolean askCancelEditBeforeLeaving(String message) { String htmlMessage = String.format( CONFIRMATION_FORMAT, message, t("jaxx.application.common.askCancelEditBeforeLeaving.help")); int i = JOptionPane.showConfirmDialog( getTopestUI(), htmlMessage, t("jaxx.application.common.askCancelEditBeforeLeaving.title"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); return i == JOptionPane.OK_OPTION; } public void showSuccessMessage(String title, String message) { Component topestUI = getTopestUI(); boolean alwaysOnTop = false; if (topestUI instanceof JDialog) { alwaysOnTop = ((JDialog) topestUI).isAlwaysOnTop(); } if (alwaysOnTop) { ((JDialog) topestUI).setAlwaysOnTop(false); } try { JOptionPane.showMessageDialog( topestUI, message, title, JOptionPane.INFORMATION_MESSAGE, UIManager.getIcon("info") ); } finally { if (alwaysOnTop) { ((JDialog) topestUI).setAlwaysOnTop(true); } } } protected void addHighlighters(final JXTable table) { } public void autoSelectRowInTable(MouseEvent e, JPopupMenu popup) { boolean rightClick = SwingUtilities.isRightMouseButton(e); if (rightClick || SwingUtilities.isLeftMouseButton(e)) { // get the coordinates of the mouse click Point p = e.getPoint(); JXTable source = (JXTable) e.getSource(); int[] selectedRows = source.getSelectedRows(); int[] selectedColumns = source.getSelectedColumns(); // get the row index at this point int rowIndex = source.rowAtPoint(p); // get the column index at this point int columnIndex = source.columnAtPoint(p); if (log.isDebugEnabled()) { log.debug("At point [" + p + "] found Row " + rowIndex + ", Column " + columnIndex); } boolean canContinue = true; if (source.isEditing()) { // stop editing boolean stopEdit = source.getCellEditor().stopCellEditing(); if (!stopEdit) { if (log.isWarnEnabled()) { log.warn("Could not stop edit cell..."); } canContinue = false; } } if (canContinue) { // select row (could empty selection) if (rowIndex == -1) { source.clearSelection(); } else if (!ArrayUtils.contains(selectedRows, rowIndex)) { if (ListSelectionModel.MULTIPLE_INTERVAL_SELECTION == source.getSelectionMode()) { // add to selection source.addRowSelectionInterval(rowIndex, rowIndex); } else { // set selection source.setRowSelectionInterval(rowIndex, rowIndex); } } // select column (could empty selection) if (columnIndex == -1) { source.clearSelection(); } else if (!ArrayUtils.contains(selectedColumns, columnIndex)) { source.setColumnSelectionInterval(columnIndex, columnIndex); } if (rightClick) { // use now model coordinate int modelRowIndex = rowIndex == -1 ? -1 : source.convertRowIndexToModel(rowIndex); int modelColumnIndex = columnIndex == -1 ? -1 : source.convertColumnIndexToModel(columnIndex); beforeOpenPopup(modelRowIndex, modelColumnIndex); // on right click show popup popup.show(source, e.getX(), e.getY()); } } } } public void openRowMenu(KeyEvent e, JPopupMenu popup) { if (e.getKeyCode() == KeyEvent.VK_CONTEXT_MENU) { JXTable source = (JXTable) e.getSource(); // get the lowest selected row int[] selectedRows = source.getSelectedRows(); int lowestRow = -1; for (int row : selectedRows) { lowestRow = Math.max(lowestRow, row); } // get the selected column int selectedColumn = source.getSelectedColumn(); Rectangle r = source.getCellRect(lowestRow, selectedColumn, true); // get the point in the middle lower of the cell Point p = new Point(r.x + r.width / 2, r.y + r.height); if (log.isDebugEnabled()) { log.debug("Row " + lowestRow + " found t point [" + p + "]"); } boolean canContinue = true; if (source.isEditing()) { // stop editing boolean stopEdit = source.getCellEditor().stopCellEditing(); if (!stopEdit) { if (log.isWarnEnabled()) { log.warn("Could not stop edit cell..."); } canContinue = false; } } if (canContinue) { // use now model coordinate int rowIndex = source.convertRowIndexToModel(lowestRow); int columnIndex = source.convertColumnIndexToModel(selectedColumn); beforeOpenPopup(rowIndex, columnIndex); popup.show(source, p.x, p.y); } } } //------------------------------------------------------------------------// //-- Init methods --// //------------------------------------------------------------------------// protected void initUIComponent(Object component) { if (component instanceof NumberEditor) { initNumberEditor((NumberEditor) component); } else if (component instanceof JXDatePicker) { initDatePicker((JXDatePicker) component); } else if (component instanceof SimpleTimeEditor) { initTimeEditor((SimpleTimeEditor) component); } else if (component instanceof JLabel) { initLabel((JLabel) component); } else if (component instanceof JTextField) { initTextField((JTextField) component); } else if (component instanceof AbstractButton) { initButton((AbstractButton) component); } else if (component instanceof JScrollPane) { initScrollPane((JScrollPane) component); } else if (component instanceof DmsCoordinateEditor) { initCoordinateDMSEditor((DmsCoordinateEditor) component); } else if (component instanceof DmdCoordinateEditor) { initCoordinateDMDEditor((DmdCoordinateEditor) component); } else if (component instanceof SimpleBeanValidator) { simpleValidators.add((SimpleBeanValidator) component); } else if (component instanceof BeanListValidator) { listValidators.add((BeanListValidator) component); } } protected void initUI(UI ui) { simpleValidators = Sets.newHashSet(); listValidators = Sets.newHashSet(); for (Map.Entry entry : ui.get$objectMap().entrySet()) { Object component = entry.getValue(); initUIComponent(component); } simpleValidators = Collections.unmodifiableSet(simpleValidators); listValidators = Collections.unmodifiableSet(listValidators); initValidators(simpleValidators, listValidators); //FIXME This does not work if there is more than one ui initComponentToFocus((Component) ui); } protected void initComponentToFocus(Component ui) { ui.addHierarchyListener(new HierarchyListener() { @Override public void hierarchyChanged(HierarchyEvent e) { JComponent component = getComponentToFocus(); if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) > 0 && e.getChanged().isShowing() && component != null) { component.requestFocus(); } } }); } protected abstract JComponent getComponentToFocus(); protected void initTextField(JTextField jTextField) { if (isAutoSelectOnFocus(jTextField)) { addAutoSelectOnFocus(jTextField); } } protected void initLabel(JLabel jLabel) { // by default do nothing more } protected void initButton(AbstractButton abstractButton) { Class actionName = (Class) abstractButton.getClientProperty("applicationAction"); if (actionName != null) { Action action = getContext().getActionFactory().createUIAction(this, abstractButton, actionName); abstractButton.setAction(action); } if (abstractButton.getAction() != null) { // get the _applicationActionKey property String key = (String) abstractButton.getClientProperty("applicationActionKey"); if (key != null) { affectKeyStroke(abstractButton, key); } } } protected void affectKeyStroke(AbstractButton abstractButton, String key) { if (abstractButton.getAction() == null) { log.warn("the buton '" + abstractButton.getName() + "' has no action. can't affect key stroke."); return; } KeyStroke keyStroke = KeyStroke.getKeyStroke(key); if (keyStroke == null) { if (log.isWarnEnabled()) { log.warn("Could not transform keyStroke: " + key); } } else { // affect this key stroke to the button's action abstractButton.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, key); abstractButton.getActionMap().put(key, abstractButton.getAction()); boolean modifyText = true; boolean modifyTip = false; ApplicationConfiguration configuration = context.getConfiguration(); if (configuration instanceof ApplicationUIConfiguration) { ApplicationUIConfiguration applicationUIConfiguration = (ApplicationUIConfiguration) configuration; modifyText = applicationUIConfiguration.isShowKeyStrokeOnText(); modifyTip = applicationUIConfiguration.isShowKeyStrokeOnTip(); } // modify text String text = abstractButton.getText(); if (modifyText && !StringUtils.isBlank(text)) { text += " (" + key.toUpperCase() + ")"; abstractButton.setText(text); } // modify tip String tip = abstractButton.getToolTipText(); if (modifyTip && !StringUtils.isBlank(tip)) { tip += " (" + key.toUpperCase() + ")"; abstractButton.setToolTipText(tip); } } } /** * Prépare un component de choix d'entités pour un type d'entité donné et * pour un service de persistance donné. * * @param comboBox le component graphique à initialiser */ protected void initBeanFilterableComboBox( BeanFilterableComboBox comboBox, List data, E selectedData) { initBeanFilterableComboBox(comboBox, data, selectedData, null); } protected void initBeanFilterableComboBox( BeanFilterableComboBox comboBox, List data, E selectedData, String decoratorContext) { Preconditions.checkNotNull(comboBox, "No comboBox!"); Class beanType = comboBox.getBeanType(); Preconditions.checkNotNull(beanType, "No beanType on the combobox!"); Decorator decorator = getDecorator(beanType, decoratorContext); if (data == null) { data = Lists.newArrayList(); } if (log.isDebugEnabled()) { log.debug("entity comboBox list [" + beanType.getName() + "] : " + data.size()); } comboBox.setI18nPrefix(getContext().getI18nPrefix()); // add data list to combo box comboBox.init((JXPathDecorator) decorator, data); comboBox.setSelectedItem(selectedData); if (log.isDebugEnabled()) { log.debug("combo [" + beanType.getName() + "] : " + comboBox.getData().size()); } } protected void initBeanComboBox(BeanComboBox comboBox, List data, E selectedData) { initBeanComboBox(comboBox, data, selectedData, null); } protected void initBeanComboBox(BeanComboBox comboBox, List data, E selectedData, String decoratorContext) { Preconditions.checkNotNull(comboBox, "No comboBox!"); Class beanType = comboBox.getBeanType(); Preconditions.checkNotNull(beanType, "No beanType on the combobox!"); Decorator decorator = getDecorator(beanType, decoratorContext); if (data == null) { data = Lists.newArrayList(); } if (log.isDebugEnabled()) { log.debug("entity comboBox list [" + beanType.getName() + "] : " + data.size()); } comboBox.setI18nPrefix(getContext().getI18nPrefix()); // add data list to combo box comboBox.init((JXPathDecorator) decorator, data); comboBox.setSelectedItem(selectedData); if (log.isDebugEnabled()) { log.debug("combo [" + beanType.getName() + "] : " + comboBox.getData().size()); } } /** * Prépare un component de choix d'entités pour un type d'entité donné et * pour un service de persistance donné. * * @param list le component graphique à initialiser * @param data la liste des données à mettre dans la liste de gauche * @param selectedData la liste des données à mettre dans la liste de droite */ protected void initBeanList(BeanDoubleList list, List data, List selectedData, Decorator selectedDecorator) { Preconditions.checkNotNull(list, "No list!"); Class beanType = list.getBeanType(); Preconditions.checkNotNull(beanType, "No beanType on the double list!"); Decorator decorator = getDecorator(beanType, null); if (log.isDebugEnabled()) { log.debug("entity list [" + beanType.getName() + "] : " + (data == null ? 0 : data.size())); } list.setI18nPrefix(getContext().getI18nPrefix()); // add data list to combo box list.init((JXPathDecorator) decorator, (JXPathDecorator) selectedDecorator, data, selectedData); if (log.isDebugEnabled()) { log.debug("Jlist [" + beanType.getName() + "] : " + list.getUniverseList().getModel().getSize()); } } /** * Prépare un component de choix d'entités pour un type d'entité donné et * pour un service de persistance donné. * * @param list le component graphique à initialiser * @param data la liste des données à mettre dans la liste de gauche * @param selectedData la liste des données à mettre dans la liste de droite */ protected void initBeanList(BeanDoubleList list, List data, List selectedData) { initBeanList(list, data, selectedData, null); } protected void initNumberEditor(NumberEditor editor) { if (log.isDebugEnabled()) { log.debug("init number editor " + editor.getName()); } editor.init(); // Force binding if value is already in model Number model = editor.getModel(); if (model != null) { editor.setModel(null); editor.setModel(model); } if (isAutoSelectOnFocus(editor)) { addAutoSelectOnFocus(editor.getTextField()); } } protected void initTimeEditor(SimpleTimeEditor editor) { if (log.isDebugEnabled()) { log.debug("init time editor " + editor.getName() + " for property " + editor.getModel().getProperty()); } editor.init(); if (isAutoSelectOnFocus(editor)) { addAutoSelectOnFocus(((JSpinner.DefaultEditor) editor.getHour().getEditor()).getTextField()); addAutoSelectOnFocus(((JSpinner.DefaultEditor) editor.getMinute().getEditor()).getTextField()); } } protected void initCoordinateDMSEditor(DmsCoordinateEditor editor) { Object property = editor.getClientProperty("longitudeEditor"); boolean longitudeEditor = property != null && Boolean.valueOf(property.toString()); editor.init(longitudeEditor); } protected void initCoordinateDMDEditor(DmdCoordinateEditor editor) { Object property = editor.getClientProperty("longitudeEditor"); boolean longitudeEditor = property != null && Boolean.valueOf(property.toString()); editor.init(longitudeEditor); } protected void initDatePicker(final JXDatePicker picker) { if (log.isDebugEnabled()) { log.debug("disable JXDatePicker editor" + picker.getName()); } String dateFormat = getContext().getDateFormat(); picker.setFormats(dateFormat); picker.setToolTipText(t("jaxx.application.common.datefield.tip", dateFormat)); picker.getEditor().addFocusListener(new FocusAdapter() { @Override public void focusLost(FocusEvent e) { try { picker.commitEdit(); } catch (ParseException ex) { if (log.isDebugEnabled()) { log.debug("format error", ex); } picker.requestFocus(); } } }); if (isAutoSelectOnFocus(picker)) { addAutoSelectOnFocus(picker.getEditor()); } } protected void initScrollPane(JScrollPane scrollPane) { Boolean onlyVerticalScrollable = (Boolean) scrollPane.getClientProperty("onlyVerticalScrollable"); if (onlyVerticalScrollable != null && onlyVerticalScrollable) { scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); final JViewport viewport = scrollPane.getViewport(); viewport.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { Dimension newDimension = new Dimension(viewport.getExtentSize().width, viewport.getViewSize().height); viewport.setViewSize(newDimension); } }); } } protected void initValidators(Set simpleValidators, Set listValidators) { M model = getModel(); if (useSimpleValidators() || useListValidators()) { Set validators = Sets.newHashSet(); validators.addAll(simpleValidators); validators.addAll(listValidators); ValidatorsValidPropertyChangeListener listener = new ValidatorsValidPropertyChangeListener((AbstractApplicationFormUIModel) model, validators); for (AbstractValidator validator : validators) { if (log.isInfoEnabled()) { log.info("Listen valid state of validator: " + validator); } validator.addPropertyChangeListener(SimpleBeanValidator.VALID_PROPERTY, listener); } } } protected boolean useListValidators() { return getModel() instanceof AbstractApplicationFormUIModel && !listValidators.isEmpty(); } protected boolean useSimpleValidators() { return getModel() instanceof AbstractApplicationFormUIModel && !simpleValidators.isEmpty(); } protected boolean isAutoSelectOnFocus(JComponent comp) { Object selectOnFocus = comp.getClientProperty("selectOnFocus"); return selectOnFocus != null && Boolean.valueOf(selectOnFocus.toString()); } protected void addAutoSelectOnFocus(JTextField jTextField) { jTextField.addFocusListener(new FocusAdapter() { @Override public void focusGained(final FocusEvent e) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { JTextField source = (JTextField) e.getSource(); source.selectAll(); } }); } }); } /** * @deprecated since 2.11, use now {@link #quitScreen(boolean, boolean, String, String, Action, boolean)} */ @Deprecated protected boolean quitScreen(boolean modelIsValid, boolean modelIsModify, String askGiveUpMessage, String askSaveMessage, Action saveAction) { return quitScreen(modelIsValid, modelIsModify, askGiveUpMessage, askSaveMessage, saveAction, false); } /** * @deprecated since 2.11, use now {@link #quitScreen(boolean, boolean, String, String, Action, boolean)} */ @Deprecated protected boolean quitInternalScreen(boolean modelIsValid, boolean modelIsModify, String askGiveUpMessage, String askSaveMessage, Action saveAction) { return quitScreen(modelIsValid, modelIsModify, askGiveUpMessage, askSaveMessage, saveAction, true); } protected boolean quitScreen(boolean modelIsValid, boolean modelIsModify, String askGiveUpMessage, String askSaveMessage, Action saveAction, boolean runInternal) { boolean result; if (!modelIsValid) { // model is not valid // ask user to qui or not result = askCancelEditBeforeLeaving(askGiveUpMessage); } else if (modelIsModify) { // something is modify ask user what to do int answer = askSaveBeforeLeaving(askSaveMessage); switch (answer) { case JOptionPane.YES_OPTION: // ok save if (runInternal) { ApplicationUIAction uiAction = (ApplicationUIAction) saveAction; getContext().getActionEngine().runFullInternalAction(uiAction.getLogicAction()); } else { saveAction.actionPerformed(null); } result = true; break; case JOptionPane.NO_OPTION: // do not save but can still quit the screen (so nothing to do) result = true; break; default: // do not save and stay here (so nothing to do) result = false; } } else { // model is valid and not modify, can safely quit screen result = true; } return result; } //------------------------------------------------------------------------// //-- Decorator API --// //------------------------------------------------------------------------// protected String decorate(Serializable object) { return decorate(object, null); } protected String decorate(Serializable object, String context) { String result = ""; if (object != null) { result = getDecorator(object.getClass(), context).toString(object); } return result; } //------------------------------------------------------------------------// //-- List API --// //------------------------------------------------------------------------// protected ListCellRenderer newListCellRender(Class type) { return newListCellRender(type, null); } protected ListCellRenderer newListCellRender(Class type, String name) { Decorator decorator = getDecorator(type, name); return newListCellRender(decorator); } protected ListCellRenderer newListCellRender(Decorator decorator) { Preconditions.checkNotNull(decorator); return new DecoratorListCellRenderer(decorator); } //FIXME Move this in JAXX protected void changeValidatorContext(String newContext, SwingValidator validator) { B bean = validator.getBean(); validator.setContext(newContext); validator.setBean(bean); } //------------------------------------------------------------------------// //-- Table API --// //------------------------------------------------------------------------// protected TableColumnExt addColumnToModel(TableColumnModel model, TableCellEditor editor, TableCellRenderer renderer, ColumnIdentifier identifier) { TableColumnExt col = new TableColumnExt(model.getColumnCount()); col.setCellEditor(editor); col.setCellRenderer(renderer); String label = t(identifier.getHeaderI18nKey()); col.setHeaderValue(label); String tip = t(identifier.getHeaderTipI18nKey()); col.setToolTipText(tip); col.setIdentifier(identifier); model.addColumn(col); // by default no column is sortable, must specify it col.setSortable(false); return col; } protected TableColumnExt addColumnToModel(TableColumnModel model, ColumnIdentifier identifier) { return addColumnToModel(model, null, null, identifier); } protected TableColumnExt addFloatColumnToModel(TableColumnModel model, ColumnIdentifier identifier, String numberPattern, boolean sign, JTable table) { NumberCellEditor editor = JAXXWidgetUtil.newNumberTableCellEditor(Float.class, sign); editor.getNumberEditor().setSelectAllTextOnError(true); editor.getNumberEditor().getTextField().setBorder(new LineBorder(Color.GRAY, 2)); editor.getNumberEditor().setNumberPattern(numberPattern); TableCellRenderer renderer = newNumberCellRenderer(table.getDefaultRenderer(Number.class)); return addColumnToModel(model, editor, renderer, identifier); } protected TableColumnExt addFloatColumnToModel(TableColumnModel model, ColumnIdentifier identifier, String numberPattern, JTable table) { return addFloatColumnToModel(model, identifier, numberPattern, false, table); } protected TableColumnExt addIntegerColumnToModel(TableColumnModel model, ColumnIdentifier identifier, String numberPattern, boolean sign, JTable table) { NumberCellEditor editor = JAXXWidgetUtil.newNumberTableCellEditor(Integer.class, sign); editor.getNumberEditor().setSelectAllTextOnError(true); editor.getNumberEditor().getTextField().setBorder(new LineBorder(Color.GRAY, 2)); editor.getNumberEditor().setNumberPattern(numberPattern); TableCellRenderer renderer = newNumberCellRenderer(table.getDefaultRenderer(Number.class)); return addColumnToModel(model, editor, renderer, identifier); } protected TableColumnExt addIntegerColumnToModel(TableColumnModel model, ColumnIdentifier identifier, String numberPattern, JTable table) { return addIntegerColumnToModel(model, identifier, numberPattern, false, table); } protected TableCellRenderer newNumberCellRenderer(final TableCellRenderer defaultRenderer) { return new TableCellRenderer() { @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component result1 = defaultRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (result1 instanceof JLabel) { JLabel jLabel = (JLabel) result1; jLabel.setHorizontalTextPosition(SwingConstants.RIGHT); } return result1; } }; } protected TableColumnExt addBooleanColumnToModel(TableColumnModel model, ColumnIdentifier identifier, JTable table) { return addColumnToModel(model, table.getDefaultEditor(Boolean.class), table.getDefaultRenderer(Boolean.class), identifier); } protected TableColumnExt addComboDataColumnToModel(TableColumnModel model, ColumnIdentifier identifier, Decorator decorator, List data) { JComboBox comboBox = new JComboBox(); comboBox.setRenderer(newListCellRender(decorator)); List dataToList = Lists.newArrayList(data); // add a null value at first position if (!dataToList.isEmpty() && dataToList.get(0) != null) { dataToList.add(0, null); } SwingUtil.fillComboBox(comboBox, dataToList, null); ObjectToStringConverter converter = BeanUIUtil.newDecoratedObjectToStringConverter(decorator); BeanUIUtil.decorate(comboBox, converter); ComboBoxCellEditor editor = new ComboBoxCellEditor(comboBox); return addColumnToModel(model, editor, newTableCellRender(decorator), identifier); } protected TableCellRenderer newTableCellRender(Class type) { return newTableCellRender(type, null); } protected TableCellRenderer newTableCellRender(Class type, String name) { Decorator decorator = getDecorator(type, name); return newTableCellRender(decorator); } protected TableCellRenderer newTableCellRender(Decorator decorator) { Preconditions.checkNotNull(decorator); return new DecoratorTableCellRenderer(decorator, true); } protected void incrementsMessage(String message) { ApplicationActionUI actionUI = getContext().getActionUI(); if (actionUI != null) { ApplicationProgressionModel progressionModel = actionUI.getModel().getProgressionModel(); if (progressionModel != null) progressionModel.increments(message); } } /** * */ private static class ValidatorsValidPropertyChangeListener implements PropertyChangeListener { private final AbstractApplicationFormUIModel model; private final Set validators; public ValidatorsValidPropertyChangeListener(AbstractApplicationFormUIModel model, Set validators) { this.model = model; this.validators = validators; } @Override public void propertyChange(PropertyChangeEvent evt) { boolean valid = true; for (AbstractValidator validator : validators) { if (!validator.isValid()) { valid = false; break; } } model.setValid(valid); } } protected static class RunFullInternalActionListener implements PropertyChangeListener { private final ApplicationUIContext context; private final AbstractApplicationAction uiAction; public RunFullInternalActionListener(ApplicationUIContext context, AbstractApplicationUIHandler handler, Class actionName) { this.context = context; this.uiAction = context.getActionFactory().createLogicAction(handler, actionName); } protected boolean doPropertyChange(PropertyChangeEvent evt) { return true; } @Override public void propertyChange(PropertyChangeEvent evt) { boolean doPropertyChange = doPropertyChange(evt); if (doPropertyChange) { if (log.isInfoEnabled()) { log.info("Execute action start: " + uiAction); } context.getActionEngine().runFullInternalAction(uiAction); if (log.isInfoEnabled()) { log.info("Execute action end: " + uiAction); } } } } }