/******************************************************************************* * Copyright (c) 2000, 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.ui.internal.navigator.framelist; import java.util.ArrayList; import java.util.List; import org.eclipse.core.commands.common.EventManager; import org.eclipse.core.runtime.Assert; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; /** * Supports a web-browser style of navigation by maintaining a list * of frames. Each frame holds a snapshot of a view at some point * in time. * <p> * The frame list obtains a snapshot of the current frame from a frame source * on creation, and whenever switching to a different frame. * </p> * <p> * A property change notification is sent whenever the current page changes. * </p> * @since 3.4 */ public class FrameList extends EventManager { /** Property name constant for the current frame. */ public static final String P_CURRENT_FRAME = "currentFrame"; //$NON-NLS-1$ /** Property name constant for the reset. */ public static final String P_RESET = "reset"; //$NON-NLS-1$ private IFrameSource source; private List frames; private int current; /** * Creates a new frame list with the given source. * * @param source the frame source */ public FrameList(IFrameSource source) { this.source = source; init(); } /** * Adds a property change listener. * Has no effect if an identical listener is already registered. * * @param listener a property change listener */ public void addPropertyChangeListener(IPropertyChangeListener listener) { addListenerObject(listener); } /** * Moves the frame pointer back by one. * Has no effect if there is no frame before the current one. * Fires a <code>P_CURRENT_FRAME</code> property change event. */ public void back() { if (current > 0) { setCurrent(current - 1); } } /** * Notifies any property change listeners that a property has changed. * Only listeners registered at the time this method is called are notified. * * @param event the property change event * * @see IPropertyChangeListener#propertyChange */ protected void firePropertyChange(PropertyChangeEvent event) { Object[] listeners = getListeners(); for (Object listener : listeners) { ((IPropertyChangeListener) listener).propertyChange(event); } } /** * Moves the frame pointer forward by one. * Has no effect if there is no frame after the current one. * Fires a <code>P_CURRENT_FRAME</code> property change event. */ public void forward() { if (current < frames.size() - 1) { setCurrent(current + 1); } } /** * Returns the current frame. * Returns <code>null</code> if there is no current frame. * * @return the current frame, or <code>null</code> */ public Frame getCurrentFrame() { return getFrame(current); } /** * Returns the index of the current frame. * * @return the index of the current frame */ public int getCurrentIndex() { return current; } /** * Returns the frame at the given index, or <code>null</code> * if the index is ≤ 0 or ≥ <code>size()</code>. * * @param index the index of the requested frame * @return the frame at the given index or <code>null</code> */ public Frame getFrame(int index) { if (index < 0 || index >= frames.size()) { return null; } return (Frame) frames.get(index); } /** * Returns the frame source. * @return an IFrameSource */ public IFrameSource getSource() { return source; } /** * Adds the given frame after the current frame, * and advances the pointer to the new frame. * Before doing so, updates the current frame, and removes any frames following the current frame. * Fires a <code>P_CURRENT_FRAME</code> property change event. * * @param frame the frame to add */ public void gotoFrame(Frame frame) { for (int i = frames.size(); --i > current;) { frames.remove(i); } frame.setParent(this); int index = frames.size(); frame.setIndex(index); frames.add(frame); setCurrent(index); } private void init() { Frame frame = source.getFrame(IFrameSource.CURRENT_FRAME, 0); frame.setParent(this); frame.setIndex(0); frames = new ArrayList(); frames.add(frame); current = 0; } /** * Removes a property change listener. * Has no effect if an identical listener is not registered. * * @param listener a property change listener */ public void removePropertyChangeListener(IPropertyChangeListener listener) { removeListenerObject(listener); } /** * Reset this frame list to its initial state. */ public void reset() { init(); firePropertyChange(new PropertyChangeEvent(this, P_RESET, null, getFrame(current))); } /** * Sets the current frame to the one with the given index. * Updates the old current frame, and fires a <code>P_CURRENT_FRAME</code> property change event * if the current frame changes. * * @param newCurrent the index of the frame */ void setCurrent(int newCurrent) { Assert.isTrue(newCurrent >= 0 && newCurrent < frames.size()); int oldCurrent = this.current; if (oldCurrent != newCurrent) { updateCurrentFrame(); this.current = newCurrent; firePropertyChange(new PropertyChangeEvent(this, P_CURRENT_FRAME, getFrame(oldCurrent), getFrame(newCurrent))); } } /** * Sets the current frame to the frame with the given index. * Fires a <code>P_CURRENT_FRAME</code> property change event * if the current frame changes. * @param index */ public void setCurrentIndex(int index) { if (index != -1 && index != current) { setCurrent(index); } } /** * Returns the number of frames in the frame list. * @return the number of frames. */ public int size() { return frames.size(); } /** * Replaces the current frame in this list with the current frame * from the frame source. No event is fired. */ public void updateCurrentFrame() { Assert.isTrue(current >= 0); Frame frame = source.getFrame(IFrameSource.CURRENT_FRAME, IFrameSource.FULL_CONTEXT); frame.setParent(this); frame.setIndex(current); frames.set(current, frame); } }