/******************************************************************************* * Copyright (c) 2006, 2015 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jface.internal.databinding.provisional.swt; import org.eclipse.core.databinding.observable.ChangeEvent; import org.eclipse.core.databinding.observable.IChangeListener; import org.eclipse.core.databinding.observable.IObservable; import org.eclipse.core.databinding.observable.ObservableTracker; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.MenuEvent; import org.eclipse.swt.events.MenuListener; import org.eclipse.swt.widgets.Menu; /** * NON-API - A MenuUpdater updates an SWT menu in response to changes in the model. By * wrapping a block of code in a MenuUpdater, clients can rely on the fact that * the block of code will be re-executed whenever anything changes in the model * that might affect its behavior. * * <p> * MenuUpdaters only execute once their menus are shown. If something changes in * the model, the updater is flagged as dirty and it stops listening to the * model until the next time the menu is shown. If the menu is visible while the * model changes, it will be updated right away. * </p> * * <p> * Clients should subclass this when copying information from the model to a * menu. Typical usage: * </p> * * <ul> * <li>Override updateMenu. It should do whatever is necessary to display the * contents of the model in the menu.</li> * <li>In the constructor, attach listeners to the model. The listeners should * call markDirty whenever anything changes in the model that affects * updateMenu. Note: this step can be omitted when calling any method tagged * with "@TrackedGetter" since MenuUpdater will automatically attach a listener * to any object if a "@TrackedGetter" method is called in updateMenu.</li> * <li>(optional)Extend dispose() to remove any listeners attached in the * constructor</li> * </ul> * * @since 1.1 */ public abstract class MenuUpdater { private class PrivateInterface implements MenuListener, DisposeListener, Runnable, IChangeListener { // DisposeListener implementation @Override public void widgetDisposed(DisposeEvent e) { MenuUpdater.this.dispose(); } // Runnable implementation. This method runs at most once per repaint whenever the // value gets marked as dirty. @Override public void run() { if (theMenu != null && !theMenu.isDisposed() && theMenu.isVisible()) { updateIfNecessary(); } } // IChangeListener implementation (listening to the ComputedValue) @Override public void handleChange(ChangeEvent event) { // Whenever this updator becomes dirty, schedule the run() method makeDirty(); } @Override public void menuHidden(MenuEvent e) { // do nothing } @Override public void menuShown(MenuEvent e) { updateIfNecessary(); } } private Runnable updateRunnable = new Runnable() { @Override public void run() { updateMenu(); } }; private PrivateInterface privateInterface = new PrivateInterface(); private Menu theMenu; private IObservable[] dependencies = new IObservable[0]; private boolean dirty = false; /** * Creates an updator for the given menu. * * @param toUpdate menu to update */ public MenuUpdater(Menu toUpdate) { theMenu = toUpdate; theMenu.addDisposeListener(privateInterface); theMenu.addMenuListener(privateInterface); makeDirty(); } private void updateIfNecessary() { if (dirty) { dependencies = ObservableTracker.runAndMonitor(updateRunnable, privateInterface, null); dirty = false; } } /** * This is called automatically when the menu is disposed. It may also * be called explicitly to remove this updator from the menu. Subclasses * will normally extend this method to detach any listeners they attached * in their constructor. */ public void dispose() { theMenu.removeDisposeListener(privateInterface); theMenu.removeMenuListener(privateInterface); stopListening(); } private void stopListening() { // Stop listening for dependency changes for (IObservable observable : dependencies) { observable.removeChangeListener(privateInterface); } } /** * Updates the menu. This method will be invoked once after the * updater is created, and once for any SWT.Show event if this * updater is marked as dirty at that time. * * <p> * Subclasses should overload this method to provide any code that * udates the menu. * </p> */ protected abstract void updateMenu(); /** * Marks this updator as dirty. Causes the updateControl method to * be invoked before the next time the control is repainted. */ protected final void makeDirty() { if (!dirty) { dirty = true; stopListening(); SWTUtil.runOnce(theMenu.getDisplay(), privateInterface); } } }