blob: 5ea988de7c95c16423afafe79a3c70940249163f [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2010 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.common.ui.internal.utility.swt;
import org.eclipse.jpt.common.ui.internal.listeners.SWTPropertyChangeListenerWrapper;
import org.eclipse.jpt.common.utility.internal.StringTools;
import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent;
import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener;
import org.eclipse.jpt.common.utility.model.value.PropertyValueModel;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Control;
/**
* This controller enables a boolean model to control either the
* <em>enabled</em> or <em>visible</em> properties of SWT controls; i.e. the
* controls' properties are kept in synch with the boolean model,
* but <em>not</em> vice-versa.
* <p>
* Subclasses must manage the listeners; i.e. the engaging and disengaging of
* the boolean model and the control(s).
*
* @see PropertyValueModel
* @see Control#setEnabled(boolean)
* @see Control#setVisible(boolean)
*/
abstract class BooleanStateController {
/**
* The controlling boolean model.
*/
private final PropertyValueModel<Boolean> booleanModel;
/**
* A listener that allows us to synchronize the control states with
* changes in the value of the boolean model.
*/
private final PropertyChangeListener booleanChangeListener;
/**
* A listener that allows us to stop listening to stuff when all the
* controls are disposed. (Critical for preventing memory leaks.)
*/
private final DisposeListener controlDisposeListener;
/**
* The default setting for the state; for when the underlying boolean model is
* <code>null</code>. The default [default value] is <code>false<code>.
*/
private final boolean defaultValue;
/**
* The adapter determines whether the 'enabled' or 'visible' property is
* controlled.
*/
private final Adapter adapter;
// ********** constructor **********
/**
* Constructor - the boolean model and the adapter are required.
*/
BooleanStateController(PropertyValueModel<Boolean> booleanModel, boolean defaultValue, Adapter adapter) {
super();
if ((booleanModel == null) || (adapter == null)) {
throw new NullPointerException();
}
this.booleanModel = booleanModel;
this.defaultValue = defaultValue;
this.adapter = adapter;
this.booleanChangeListener = this.buildBooleanChangeListener();
this.controlDisposeListener = this.buildControlDisposeListener();
}
// ********** initialization **********
private PropertyChangeListener buildBooleanChangeListener() {
return new SWTPropertyChangeListenerWrapper(this.buildBooleanChangeListener_());
}
private PropertyChangeListener buildBooleanChangeListener_() {
return new PropertyChangeListener() {
public void propertyChanged(PropertyChangeEvent event) {
BooleanStateController.this.booleanChanged(event);
}
@Override
public String toString() {
return "boolean listener"; //$NON-NLS-1$
}
};
}
private DisposeListener buildControlDisposeListener() {
return new DisposeListener() {
public void widgetDisposed(DisposeEvent event) {
// the control is not yet "disposed" when we receive this event
// so we can still remove our listener
BooleanStateController.this.controlDisposed((Control) event.widget);
}
@Override
public String toString() {
return "control dispose listener"; //$NON-NLS-1$
}
};
}
// ********** boolean model **********
void engageBooleanModel() {
this.booleanModel.addPropertyChangeListener(PropertyValueModel.VALUE, this.booleanChangeListener);
}
void disengageBooleanModel() {
this.booleanModel.removePropertyChangeListener(PropertyValueModel.VALUE, this.booleanChangeListener);
}
/**
* The boolean model has changed - synchronize the controls.
* If the new boolean model value is <code>null</code>, use the controller's
* default value (which is typically false).
*/
/* CU private */ void booleanChanged(PropertyChangeEvent event) {
this.setControlState((Boolean) event.getNewValue());
}
boolean getBooleanValue() {
return this.booleanValue(this.booleanModel.getValue());
}
private boolean booleanValue(Boolean b) {
return (b != null) ? b.booleanValue() : this.defaultValue;
}
// ********** control **********
void engageControl(Control control) {
control.addDisposeListener(this.controlDisposeListener);
}
void disengageControl(Control control) {
control.removeDisposeListener(this.controlDisposeListener);
}
private void setControlState(Boolean b) {
this.setControlState(this.booleanValue(b));
}
abstract void setControlState(boolean b);
void setControlState(Control control, boolean b) {
if ( ! control.isDisposed()) {
this.adapter.setState(control, b);
}
}
void controlDisposed(Control control) {
this.disengageControl(control);
}
// ********** standard methods **********
@Override
public String toString() {
return StringTools.buildToStringFor(this, this.booleanModel);
}
// ********** adapter interface **********
interface Adapter {
void setState(Control control, boolean b);
}
}