/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo 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 Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fib.view;
import java.awt.Dimension;
import java.awt.Font;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import org.openflexo.antar.binding.TypeUtils;
import org.openflexo.fib.controller.FIBComponentDynamicModel;
import org.openflexo.fib.controller.FIBController;
import org.openflexo.fib.controller.FIBSelectable;
import org.openflexo.fib.model.FIBComponent;
import org.openflexo.localization.FlexoLocalization;
import org.openflexo.localization.Language;
import org.openflexo.localization.LocalizationListener;
public abstract class FIBView<M extends FIBComponent, J extends JComponent> implements LocalizationListener {
private static final Logger logger = Logger.getLogger(FIBView.class.getPackage().getName());
protected static final int TOP_COMPENSATING_BORDER = 3;
protected static final int BOTTOM_COMPENSATING_BORDER = TOP_COMPENSATING_BORDER;
protected static final int LEFT_COMPENSATING_BORDER = 5;
protected static final int RIGHT_COMPENSATING_BORDER = LEFT_COMPENSATING_BORDER;
private M component;
private FIBController controller;
private final FIBComponentDynamicModel dynamicModel;
protected Vector<FIBView> subViews;
private boolean visible = true;
private boolean isDeleted = false;
private JScrollPane scrolledComponent;
public FIBView(M model, FIBController controller) {
super();
this.controller = controller;
component = model;
subViews = new Vector<FIBView>();
dynamicModel = createDynamicModel();
controller.registerView(this);
}
public void delete() {
logger.fine("@@@@@@@@@ Delete view for component " + getComponent());
if (isDeleted) {
return;
}
logger.fine("Delete view for component " + getComponent());
if (subViews != null) {
for (FIBView v : subViews) {
v.delete();
}
subViews.clear();
subViews = null;
}
if (controller != null) {
controller.unregisterView(this);
}
if (dynamicModel != null) {
dynamicModel.delete();
}
isDeleted = true;
component = null;
controller = null;
}
public JComponent getJComponentForObject(FIBComponent component) {
if (getComponent() == component) {
return getJComponent();
} else {
for (FIBView v : getSubViews()) {
JComponent j = v.getJComponentForObject(component);
if (j != null) {
return j;
}
}
}
return null;
}
public JComponent geDynamicJComponentForObject(FIBComponent component) {
if (getComponent() == component) {
return getDynamicJComponent();
} else {
for (FIBView v : getSubViews()) {
JComponent j = v.geDynamicJComponentForObject(component);
if (j != null) {
return j;
}
}
}
return null;
}
public boolean isDeleted() {
return isDeleted;
}
public FIBController getController() {
return controller;
}
public final Object getDataObject() {
return getController().getDataObject();
}
public final M getComponent() {
return component;
}
public abstract void updateDataObject(Object anObject);
public abstract void updateLanguage();
/**
* Return the effective base component to be added to swing hierarchy This component may be encapsulated in a JScrollPane
*
* @return JComponent
*/
public abstract JComponent getJComponent();
/**
* Return the dynamic JComponent, ie the component on which dynamic is applied, and were actions are effective
*
* @return J
*/
public abstract J getDynamicJComponent();
/**
* Return the effective component to be added to swing hierarchy This component may be the same as the one returned by
* {@link #getJComponent()} or a encapsulation in a JScrollPane
*
* @return JComponent
*/
public JComponent getResultingJComponent() {
if (getComponent().getUseScrollBar()) {
if (scrolledComponent == null) {
scrolledComponent = new JScrollPane(getJComponent(), getComponent().getVerticalScrollbarPolicy().getPolicy(),
getComponent().getHorizontalScrollbarPolicy().getPolicy());
scrolledComponent.setOpaque(false);
scrolledComponent.getViewport().setOpaque(false);
scrolledComponent.setBorder(BorderFactory.createEmptyBorder());
}
return scrolledComponent;
} else {
return getJComponent();
}
}
/**
* This method is called to update view representing a FIBComponent.<br>
* Callers are all the components that have been updated during current update loop. If the callers contains the component itself, does
* nothing and return.
*
* @param callers
* all the components that have been previously updated during current update loop
* @return a flag indicating if component has been updated
*/
public boolean update(List<FIBComponent> callers) {
if (callers.contains(getComponent())) {
return false;
}
updateVisibility();
return true;
}
protected abstract boolean checkValidDataPath();
public final boolean isComponentVisible() {
if (getParentView() != null && !getParentView().isComponentVisible()) {
return false;
}
boolean componentVisible = true;
if (getComponent().getVisible() != null && getComponent().getVisible().isSet()) {
Object isVisible = getComponent().getVisible().getBindingValue(getController());
if (isVisible instanceof Boolean) {
componentVisible = (Boolean) isVisible;
}
}
return componentVisible;
}
public final boolean hasValue() {
return component.getData() != null && component.getData().isSet();
}
protected boolean isVisible() {
return visible;
}
protected void updateVisibility() {
if (isComponentVisible() != visible) {
visible = !visible;
getResultingJComponent().setVisible(visible);
if (getResultingJComponent().getParent() instanceof JComponent) {
((JComponent) getResultingJComponent().getParent()).revalidate();
} else if (getResultingJComponent().getParent() != null) {
getResultingJComponent().getParent().validate();
}
if (getResultingJComponent().getParent() != null) {
getResultingJComponent().getParent().repaint();
}
if (visible) {
for (FIBView<?, ?> view : subViews) {
view.updateVisibility();
}
}
if (getDynamicModel() != null) {
getDynamicModel().setVisible(visible);
}
}
}
public FIBComponentDynamicModel<?> createDynamicModel() {
if (getComponent().getDataType() != null) {
logger.fine("Create dynamic model " + this + " for " + getComponent());
return buildDynamicModel(TypeUtils.getBaseClass(getComponent().getDataType()));
}
return null;
}
private <T> FIBComponentDynamicModel<T> buildDynamicModel(Class<T> type) {
return new FIBComponentDynamicModel<T>((T) getDefaultData());
}
public Object getDefaultData() {
return null;
}
public FIBComponentDynamicModel getDynamicModel() {
return dynamicModel;
}
public void notifyDynamicModelChanged() {
// System.out.println("notifyDynamicModelChanged()");
if (getComponent() != null) {
Iterator<FIBComponent> it = getComponent().getMayAltersIterator();
while (it.hasNext()) {
FIBComponent c = it.next();
logger.fine("Because dynamic model change, now update " + c);
FIBView view = getController().viewForComponent(c);
if (view != null) {
view.updateDataObject(getDataObject());
} else {
if (logger.isLoggable(Level.FINE)) {
logger.fine("Unexpected null view when retrieving view for " + c);
}
}
}
} else {
logger.warning("Unexpected null component");
}
}
public FIBView<?, ?> getParentView() {
if (getComponent().getParent() != null) {
return getController().viewForComponent(getComponent().getParent());
}
return null;
}
public Vector<FIBView> getSubViews() {
return subViews;
}
public Font getFont() {
if (getComponent() != null) {
return getComponent().retrieveValidFont();
}
return null;
}
public abstract void updateFont();
public String getLocalized(String key) {
if (getController().getLocalizerForComponent(getComponent()) != null) {
return FlexoLocalization.localizedForKey(getController().getLocalizerForComponent(getComponent()), key);
}
return key;
}
public boolean isSelectableComponent() {
return this instanceof FIBSelectable;
}
public FIBSelectable getSelectableComponent() {
if (isSelectableComponent()) {
return (FIBSelectable) this;
}
return null;
}
@Override
public void languageChanged(Language language) {
updateLanguage();
}
public void updateGraphicalProperties() {
updatePreferredSize();
updateMaximumSize();
updateMinimumSize();
updateOpacity();
updateBackgroundColor();
updateForegroundColor();
}
protected void updateOpacity() {
if (getComponent().getOpaque() != null) {
getDynamicJComponent().setOpaque(getComponent().getOpaque());
}
}
protected void updatePreferredSize() {
if (getComponent().definePreferredDimensions()) {
Dimension preferredSize = getJComponent().getPreferredSize();
if (getComponent().getWidth() != null) {
preferredSize.width = getComponent().getWidth();
}
if (getComponent().getHeight() != null) {
preferredSize.height = getComponent().getHeight();
}
getJComponent().setPreferredSize(preferredSize);
}
}
protected void updateMinimumSize() {
if (getComponent().defineMinDimensions()) {
Dimension minSize = getJComponent().getMinimumSize();
if (getComponent().getMinWidth() != null) {
minSize.width = getComponent().getMinWidth();
}
if (getComponent().getMinHeight() != null) {
minSize.height = getComponent().getMinHeight();
}
getJComponent().setMinimumSize(minSize);
}
}
protected void updateMaximumSize() {
if (getComponent().defineMaxDimensions()) {
Dimension maxSize = getJComponent().getMaximumSize();
if (getComponent().getMaxWidth() != null) {
maxSize.width = getComponent().getMaxWidth();
}
if (getComponent().getMaxHeight() != null) {
maxSize.height = getComponent().getMaxHeight();
}
getJComponent().setMinimumSize(maxSize);
}
}
protected void updateBackgroundColor() {
getJComponent().setBackground(getComponent().getBackgroundColor());
}
protected void updateForegroundColor() {
getJComponent().setForeground(getComponent().getForegroundColor());
}
public static boolean equals(Object o1, Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null) {
return o2 == null;
} else {
return o1.equals(o2);
}
}
public static boolean notEquals(Object o1, Object o2) {
return !equals(o1, o2);
}
}