
com.globalmentor.swing.ModelViewablePanel Maven / Gradle / Ivy
Show all versions of globalmentor-swing Show documentation
/*
* Copyright © 1996-2009 GlobalMentor, Inc.
*
* 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.globalmentor.swing;
import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.io.IOException;
import com.globalmentor.java.Arrays;
import com.globalmentor.model.ModelViewable;
/**
* Panel that allows multiple views of a data model to be displayed.
*
* An implementing class may consider the data in each view to be loaded and stored separately, in which case {@link ModelViewablePanel#loadModel(int)} and
* {@link ModelViewablePanel#saveModel(int)} should be used for the appropriate views. The implementation may instead decide that all data is saved and loaded
* together, in which case {@link ModelViewablePanel#loadModel()} and {@link ModelViewablePanel#saveModel()} should be used to load or save all data in all
* views at the same time. An implementation may use a hybrid of these paradigms, for example storing views of all the data in some views using the
* view-specific loading and saving methods, and using the generic loading and save methods for views that contain portions of the data.
*
*
* Bound properties:
*
*
* - {@link ModelViewable#MODEL_VIEW_PROPERTY} ({@link Integer})
* - Indicates the data view has been changed.
*
* @author Garret Wilson
*/
public abstract class ModelViewablePanel extends ModelPanel implements ModelViewable {
/** The default model views supported by this panel. */
private final int[] DEFAULT_SUPPORTED_MODEL_VIEWS = new int[] {};
/** The default default model view of this panel. */
private final int DEFAULT_DEFAULT_MODEL_VIEW = NO_MODEL_VIEW;
/** The model views supported by the panel. */
private int[] supportedModelViews;
/**
* Determines whether this object supports the given data view.
* @param modelView A model view such as SUMMARY_MODEL_VIEW
.
* @return true
if and only if this object supports the indicated model view.
*/
public boolean isModelViewSupported(final int modelView) {
return Arrays.indexOf(getSupportedModelViews(), modelView) >= 0; //see whether this model view is in our array of supported views
}
/** @return The supported model views. */
public int[] getSupportedModelViews() {
return supportedModelViews;
}
/**
* Sets the model views supported by this panel.
* @param modelViews The supported model views.
*/
protected void setSupportedModelViews(final int[] modelViews) {
supportedModelViews = modelViews; //update the supported model views
}
/**
* Adds support for a model view.
* @param modelView The supported model view to add.
*/
protected void addSupportedModelView(final int modelView) {
if(!isModelViewSupported(modelView)) { //if this model view isn't already supported
setSupportedModelViews(Arrays.append(getSupportedModelViews(), modelView)); //append the new model view to our supported model views
}
}
/**
* Adds support for model views.
* @param modelViews The supported model views to add.
*/
protected void addSupportedModelViews(final int[] modelViews) {
//TODO make sure the given views aren't already supported
setSupportedModelViews(Arrays.append(getSupportedModelViews(), modelViews)); //append the new model views to our supported model views
}
/** The default data view of this panel. */
private int defaultDataView;
/** @return The default view of the data, such as SUMMARY_MODEL_VIEW
. */
public int getDefaultModelView() {
return defaultDataView;
}
/**
* Sets the default data view.
* @param dataView The default view of the data, such as SUMMARY_MODEL_VIEW
.
*/
public void setDefaultDataView(final int dataView) {
defaultDataView = dataView;
}
/** The view of the data, such as SUMMARY_MODEL_VIEW
. */
private int modelView;
/** @return The view of the data, such as SUMMARY_MODEL_VIEW
. */
public int getModelView() {
return modelView;
}
/**
* Sets the view of the data.
* @param newView The view of the data, such as SUMMARY_MODEL_VIEW
.
* @throws IllegalArgumentException Thrown if the given view is not supported.
*/
public void setModelView(final int newView) {
final int oldView = modelView; //get the old value
if(oldView != newView) { //if the value is really changing
if(!isModelViewSupported(newView)) { //if the new data view isn't supported
cancelModelViewChange(oldView, newView); //cancel the change
throw new IllegalArgumentException("Unsupported model view " + newView);
}
if(canChangeModelView(oldView, newView)) { //if we can change the view
modelView = newView; //update the value
try {
loadModel(newView); //load the model into the new view
} catch(IOException ioException) { //if there were any problems saving the model
AbstractSwingApplication.displayApplicationError(this, ioException); //display the error
}
onModelViewChange(oldView, newView); //show this class that the model is changing
firePropertyChange(MODEL_VIEW_PROPERTY, new Integer(oldView), new Integer(newView)); //show that the property has changed
} else { //if we shouldn't change the view
cancelModelViewChange(oldView, newView); //cancel the change
}
}
}
/**
* Model constructor.
* @param model The data model for which this component provides a view.
*/
public ModelViewablePanel(final M model) {
this(model, true); //initialize the panel
}
/**
* Constructor with optional initialization that uses a FlowLayout
.
* @param model The data model for which this component provides a view.
* @param initialize true
if the panel should initialize itself by calling the initialization methods.
* @see #FlowLayout
*/
public ModelViewablePanel(final M model, final boolean initialize) {
this(new FlowLayout(), model, initialize); //construct the panel with a flow layout by default
}
/**
* Layout constructor.
* @param layout The layout manager to use.
* @param model The data model for which this component provides a view.
*/
public ModelViewablePanel(final LayoutManager layout, final M model) {
this(layout, model, true); //construct the class with the layout, initializing the panel
}
/**
* Layout constructor with optional initialization.
* @param layout The layout manager to use.
* @param model The data model for which this component provides a view.
* @param initialize true
if the panel should initialize itself by calling the initialization methods.
*/
public ModelViewablePanel(final LayoutManager layout, final M model, final boolean initialize) {
super(layout, model, false); //construct the parent but don't initialize the panel
supportedModelViews = DEFAULT_SUPPORTED_MODEL_VIEWS; //set the model views we support (in this case, none)
defaultDataView = DEFAULT_DEFAULT_MODEL_VIEW; //set the default model view (in this case, no model view)
modelView = NO_MODEL_VIEW; //default to no valid view
if(initialize) //if we should initialize
initialize(); //initialize the panel
}
/**
* Initialize the user interface.
*
* This version sets the data view to the default.
*
*/
protected void initializeUI() {
assert isModelViewSupported(getDefaultModelView()) : "Specified default model view is not supported";
modelView = getDefaultModelView(); //default to the model view
super.initializeUI(); //do the default UI initialization, which will load the model into our new view
}
/**
* Loads the data from the model to the view, if necessary.
*
* This implementation loads the model normally and then loads the model for the current view.
*
* @throws IOException Thrown if there was an error loading the model.
* @see #loadModel(int)
*/
public void loadModel() throws IOException {
super.loadModel(); //do the default loading
loadModel(getModelView()); //load the model into the current view
}
/**
* Loads the data from the model to the specified view, if necessary.
* @param modelView The view of the data, such as SUMMARY_MODEL_VIEW
.
* @throws IOException Thrown if there was an error loading the model.
*/
protected void loadModel(final int modelView) throws IOException {
}
/**
* Stores the current data being edited to the model, if necessary.
*
* This implementation first saves the model for the current view and then saves the model normally.
*
* @throws IOException Thrown if there was an error saving the model.
* @see #saveModel(int)
*/
public void saveModel() throws IOException {
saveModel(getModelView()); //save the current view
super.saveModel(); //do the default saving
}
/**
* Stores the current data being edited to the model, if necessary. If no model is being edited or there is no valid view, no action occurs.
* @param modelView The view of the data, such as SUMMARY_MODEL_VIEW
.
* @throws IOException Thrown if there was an error saving the model.
*/
protected void saveModel(final int modelView) throws IOException {
}
/**
* Determines whether it is appropriate under the circumstances to change the model view. This method may display any necessary dialog boxes in response to
* any error conditions before returning.
*
* This version verifies the panel before allowing the change.
*
* @param oldView The view before the change.
* @param newView The new view of the data
* @return true
if the model view change should be allowed, else false
.
*/
protected boolean canChangeModelView(final int oldView, final int newView) {
return verify(); //we can change views if the panel verifies TODO this needs to be changed so that only the old view is saved; see TabbedViewPanel.canChangeModelView()
}
/**
* Called when the change in model view should be canceled.
* @param oldView The view before the change.
* @param newView The new view of the data
*/
protected void cancelModelViewChange(final int oldView, final int newView) {
}
/**
* Indicates that the view of the data has changed.
* @param oldView The view before the change.
* @param newView The new view of the data
*/
protected void onModelViewChange(final int oldView, final int newView) {
}
}