| /******************************************************************************* |
| * Copyright (c) 2000, 2015 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.ui.internal; |
| |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.Map; |
| import org.eclipse.core.runtime.Assert; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IEditorInput; |
| import org.eclipse.ui.IEditorPart; |
| import org.eclipse.ui.IMemento; |
| import org.eclipse.ui.INavigationHistory; |
| import org.eclipse.ui.INavigationLocation; |
| import org.eclipse.ui.INavigationLocationProvider; |
| import org.eclipse.ui.IPartListener2; |
| import org.eclipse.ui.IWorkbenchPage; |
| import org.eclipse.ui.IWorkbenchPartReference; |
| import org.eclipse.ui.IWorkbenchPartSite; |
| import org.eclipse.ui.internal.StartupThreading.StartupRunnable; |
| |
| /** |
| * Implementation of the back and forward actions. |
| */ |
| public class NavigationHistory implements INavigationHistory { |
| |
| private static final boolean DEBUG = false; |
| |
| private static final int CAPACITY = 50; |
| |
| private NavigationHistoryAction backwardAction; |
| |
| private NavigationHistoryAction forwardAction; |
| |
| private int ignoreEntries; |
| |
| private ArrayList<NavigationHistoryEntry> history = new ArrayList<>(CAPACITY); |
| |
| Map<Object, PerTabHistory> perTabHistoryMap = new HashMap<>(); |
| |
| private ArrayList<NavigationHistoryEditorInfo> editors = new ArrayList<>(CAPACITY); |
| |
| private IWorkbenchPage page; |
| |
| private int activeEntry = 0; |
| |
| /** |
| * Creates a new NavigationHistory to keep the NavigationLocation entries of the |
| * specified page. |
| * |
| * @param page the workbench page |
| */ |
| public NavigationHistory(final IWorkbenchPage page) { |
| this.page = page; |
| page.addPartListener(new IPartListener2() { |
| @Override |
| public void partActivated(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partBroughtToTop(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partDeactivated(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partOpened(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partHidden(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partVisible(IWorkbenchPartReference partRef) { |
| } |
| |
| @Override |
| public void partClosed(IWorkbenchPartReference partRef) { |
| if (isPerTabHistoryEnabled() && partRef instanceof EditorReference) { |
| if (!((EditorReference) partRef).isDisposed()) { |
| Object editorTabCookie = ((EditorReference) partRef).getModel().getWidget(); |
| disposeHistoryForTab(editorTabCookie); |
| updateActions(); |
| } |
| } |
| updateNavigationHistory(partRef, true); |
| } |
| |
| @Override |
| public void partInputChanged(IWorkbenchPartReference partRef) { |
| updateNavigationHistory(partRef, false); |
| } |
| |
| private void updateNavigationHistory(IWorkbenchPartReference partRef, boolean partClosed) { |
| if (partRef != null && partRef.getPart(false) instanceof IEditorPart) { |
| IEditorPart editor = (IEditorPart) partRef.getPart(false); |
| IEditorInput input = editor.getEditorInput(); |
| String id = editor.getSite().getId(); |
| Iterator<NavigationHistoryEditorInfo> e = editors.iterator(); |
| NavigationHistoryEditorInfo info = null; |
| NavigationHistoryEditorInfo currentInfo = null; |
| NavigationHistoryEntry current = getEntry(activeEntry); |
| if (current != null) { |
| currentInfo = current.editorInfo; |
| } |
| while (e.hasNext()) { |
| info = e.next(); |
| if (id.equals(info.editorID) && input.equals(info.editorInput)) { |
| if (partClosed && info != currentInfo) { |
| info.handlePartClosed(); |
| } |
| break; |
| } |
| info = null; |
| } |
| if (info == null) { |
| return; |
| } |
| boolean isEntryDisposed = false; |
| Iterator<NavigationHistoryEntry> it = history.iterator(); |
| int i = 0; |
| while (it.hasNext()) { |
| NavigationHistoryEntry entry = it.next(); |
| if (entry.editorInfo == info) { |
| if (!entry.handlePartClosed()) { |
| // update the active entry since we are removing an item |
| if (i < activeEntry) { |
| activeEntry--; |
| } else if (i == activeEntry) { |
| if (i != 0) { |
| activeEntry--; |
| } |
| } else { |
| // activeEntry is before item we deleted |
| i++; |
| } |
| isEntryDisposed = true; |
| it.remove(); |
| disposeEntry(entry); |
| } else { |
| i++; |
| } |
| } |
| } |
| |
| /* |
| * Promote the entry of the last closed editor to be the active one, see: |
| * https://bugs.eclipse.org/bugs/show_bug.cgi?id=154431 |
| */ |
| if (!isEntryDisposed && page.getActiveEditor() == null && activeEntry < history.size()) |
| activeEntry++; |
| |
| updateActions(); |
| } |
| } |
| }); |
| } |
| |
| private Display getDisplay() { |
| return Workbench.getInstance().getDisplay(); |
| } |
| |
| private boolean isPerTabHistoryEnabled() { |
| return false; |
| } |
| |
| /* |
| * Adds an editor to the editor history without getting its location. |
| */ |
| public void markEditor(final IEditorPart part) { |
| if (ignoreEntries > 0 || part == null) { |
| return; |
| } |
| /* |
| * Ignore all entries until the async exec runs. Workaround to avoid extra entry |
| * when using Open Declaration (F3) that opens another editor. |
| */ |
| ignoreEntries++; |
| getDisplay().asyncExec(() -> { |
| if (--ignoreEntries == 0) { |
| if (part.getEditorSite() instanceof EditorSite) { |
| EditorSite site = (EditorSite) part.getEditorSite(); |
| Control c = (Control) site.getModel().getWidget(); |
| if (c == null || c.isDisposed()) { |
| return; |
| } |
| NavigationHistoryEntry e = getEntry(activeEntry); |
| if (e != null && part.getEditorInput() != e.editorInfo.editorInput) { |
| updateEntry(e); |
| } |
| addEntry(part); |
| } |
| } |
| }); |
| } |
| |
| @Override |
| public void markLocation(IEditorPart part) { |
| addEntry(part); |
| } |
| |
| /* |
| * Return the backward history entries. Return in restore order (i.e., the first |
| * entry is the entry that would become active if the "Backward" action was |
| * executed). <p> (Called by NavigationHistoryAction) </p> |
| */ |
| NavigationHistoryEntry[] getBackwardEntries() { |
| if (isPerTabHistoryEnabled()) { |
| return getEntriesForTab(false); |
| } |
| int length = activeEntry; |
| NavigationHistoryEntry[] entries = new NavigationHistoryEntry[length]; |
| for (int i = 0; i < activeEntry; i++) { |
| entries[activeEntry - 1 - i] = getEntry(i); |
| } |
| return entries; |
| } |
| |
| /* |
| * Return the forward history entries. Return in restore order (i.e., the first |
| * entry is the entry that would become active if the "Forward" action was |
| * executed). <p> (Called by NavigationHistoryAction) </p> |
| */ |
| NavigationHistoryEntry[] getForwardEntries() { |
| if (isPerTabHistoryEnabled()) { |
| return getEntriesForTab(true); |
| } |
| int length = history.size() - activeEntry - 1; |
| length = Math.max(0, length); |
| NavigationHistoryEntry[] entries = new NavigationHistoryEntry[length]; |
| for (int i = activeEntry + 1; i < history.size(); i++) { |
| entries[i - activeEntry - 1] = getEntry(i); |
| } |
| return entries; |
| } |
| |
| @Override |
| public INavigationLocation[] getLocations() { |
| INavigationLocation result[] = new INavigationLocation[history.size()]; |
| for (int i = 0; i < result.length; i++) { |
| NavigationHistoryEntry e = history.get(i); |
| result[i] = e.location; |
| } |
| return result; |
| } |
| |
| @Override |
| public INavigationLocation getCurrentLocation() { |
| NavigationHistoryEntry entry = getEntry(activeEntry); |
| return entry == null ? null : entry.location; |
| } |
| |
| /** |
| * Disposes this NavigationHistory and all entries. |
| */ |
| public void dispose() { |
| disposeHistoryForTabs(); |
| Iterator<NavigationHistoryEntry> e = history.iterator(); |
| while (e.hasNext()) { |
| NavigationHistoryEntry entry = e.next(); |
| disposeEntry(entry); |
| } |
| } |
| |
| /** |
| * Keeps a reference to the forward action to update its state whenever needed. |
| * <p> |
| * (Called by NavigationHistoryAction) |
| * </p> |
| */ |
| void setForwardAction(NavigationHistoryAction action) { |
| forwardAction = action; |
| updateActions(); |
| } |
| |
| /** |
| * Keeps a reference to the backward action to update its state whenever needed. |
| * <p> |
| * (Called by NavigationHistoryAction) |
| * </p> |
| */ |
| void setBackwardAction(NavigationHistoryAction action) { |
| backwardAction = action; |
| updateActions(); |
| } |
| |
| /* |
| * Returns the history entry indexed by <code>index</code> |
| */ |
| private NavigationHistoryEntry getEntry(int index) { |
| if (0 <= index && index < history.size()) { |
| return history.get(index); |
| } |
| return null; |
| } |
| |
| /* |
| * Adds the specified entry to the history. |
| */ |
| private void add(NavigationHistoryEntry entry) { |
| removeForwardEntries(); |
| if (history.size() == CAPACITY) { |
| NavigationHistoryEntry e = history.remove(0); |
| disposeEntry(e); |
| } |
| history.add(entry); |
| activeEntry = history.size() - 1; |
| } |
| |
| /* |
| * Remove all entries after the active entry. |
| */ |
| private void removeForwardEntries() { |
| int length = history.size(); |
| for (int i = activeEntry + 1; i < length; i++) { |
| NavigationHistoryEntry e = history.remove(activeEntry + 1); |
| disposeEntry(e); |
| } |
| } |
| |
| /* |
| * Adds a location to the history. |
| */ |
| private void addEntry(IEditorPart part) { |
| if (ignoreEntries > 0 || part == null) { |
| return; |
| } |
| |
| if (isPerTabHistoryEnabled()) { |
| markLocationForTab(part); |
| } |
| INavigationLocation location = null; |
| if (part instanceof INavigationLocationProvider) { |
| location = ((INavigationLocationProvider) part).createNavigationLocation(); |
| } |
| |
| NavigationHistoryEntry current = getEntry(activeEntry); |
| if (current != null && current.editorInfo.memento != null) { |
| current.editorInfo.restoreEditor(); |
| checkDuplicates(current.editorInfo); |
| } |
| NavigationHistoryEntry e = createEntry(page, part, location); |
| if (current == null) { |
| add(e); |
| } else { |
| if (e.mergeInto(current)) { |
| disposeEntry(e); |
| removeForwardEntries(); |
| } else { |
| add(e); |
| } |
| } |
| printEntries("added entry"); //$NON-NLS-1$ |
| updateActions(); |
| } |
| |
| /* |
| * Prints all the entries in the console. For debug only. |
| */ |
| private void printEntries(String label) { |
| if (DEBUG) { |
| System.out.println("+++++ " + label + "+++++ "); //$NON-NLS-1$ //$NON-NLS-2$ |
| int size = history.size(); |
| for (int i = 0; i < size; i++) { |
| String append = activeEntry == i ? ">>" : ""; //$NON-NLS-1$ //$NON-NLS-2$ |
| System.out.println(append + "Index: " + i + " " + history.get(i)); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| |
| /* |
| * Returns true if the forward action can be performed otherwise returns false. |
| * <p> (Called by NavigationHistoryAction) </p> |
| */ |
| /* package */boolean canForward() { |
| if (isPerTabHistoryEnabled()) { |
| return hasEntriesForTab(true); |
| } |
| return (0 <= activeEntry + 1) && (activeEntry + 1 < history.size()); |
| } |
| |
| /* |
| * Returns true if the backward action can be performed otherwise returns false. |
| * <p> (Called by NavigationHistoryAction) </p> |
| */ |
| /* package */boolean canBackward() { |
| if (isPerTabHistoryEnabled()) { |
| return hasEntriesForTab(false); |
| } |
| return (0 <= activeEntry - 1) && (activeEntry - 1 < history.size()); |
| } |
| |
| /* |
| * Update the actions enable/disable and tooltip state. |
| */ |
| private void updateActions() { |
| if (backwardAction != null) { |
| backwardAction.update(); |
| } |
| if (forwardAction != null) { |
| forwardAction.update(); |
| } |
| } |
| |
| /* |
| * Restore the specified entry |
| */ |
| private void gotoEntry(NavigationHistoryEntry entry) { |
| if (entry == null) { |
| return; |
| } |
| try { |
| ignoreEntries++; |
| if (entry.editorInfo.memento != null) { |
| entry.editorInfo.restoreEditor(); |
| checkDuplicates(entry.editorInfo); |
| } |
| entry.restoreLocation(); |
| updateActions(); |
| printEntries("goto entry"); //$NON-NLS-1$ |
| } finally { |
| ignoreEntries--; |
| } |
| } |
| |
| /* |
| * update the active entry |
| */ |
| private void updateEntry(NavigationHistoryEntry activeEntry) { |
| if (activeEntry == null || activeEntry.location == null) { |
| return; |
| } |
| activeEntry.location.update(); |
| printEntries("updateEntry"); //$NON-NLS-1$ |
| } |
| |
| /* |
| * Perform the forward action by getting the next location and restoring its |
| * context. <p> (Called by NavigationHistoryAction) </p> |
| */ |
| void forward() { |
| if (isPerTabHistoryEnabled()) { |
| forwardForTab(); |
| return; |
| } |
| if (canForward()) { |
| shiftEntry(true); |
| } |
| } |
| |
| /* |
| * Perform the backward action by getting the previous location and restoring |
| * its context. <p> (Called by NavigationHistoryAction) </p> |
| */ |
| void backward() { |
| if (isPerTabHistoryEnabled()) { |
| backwardForTab(); |
| return; |
| } |
| if (canBackward()) { |
| shiftEntry(false); |
| } |
| } |
| |
| /* |
| * Shift the history back or forward |
| */ |
| private void shiftEntry(boolean forward) { |
| updateEntry(getEntry(activeEntry)); |
| if (forward) { |
| activeEntry++; |
| } else { |
| activeEntry--; |
| } |
| NavigationHistoryEntry entry = getEntry(activeEntry); |
| if (entry != null) { |
| gotoEntry(entry); |
| } |
| } |
| |
| /* |
| * Shift the history to the given entry. <p> (Called by NavigationHistoryAction) |
| * </p> |
| */ |
| void shiftCurrentEntry(NavigationHistoryEntry entry, boolean forward) { |
| if (isPerTabHistoryEnabled()) { |
| gotoEntryForTab(entry, forward); |
| return; |
| } |
| updateEntry(getEntry(activeEntry)); |
| activeEntry = history.indexOf(entry); |
| gotoEntry(entry); |
| } |
| |
| /** |
| * Save the state of this history into the memento. |
| */ |
| void saveState(IMemento memento) { |
| NavigationHistoryEntry cEntry = getEntry(activeEntry); |
| if (cEntry == null || !cEntry.editorInfo.isPersistable()) { |
| return; |
| } |
| |
| ArrayList<NavigationHistoryEditorInfo> editors = new ArrayList<>(this.editors); |
| for (Iterator<NavigationHistoryEditorInfo> iter = editors.iterator(); iter.hasNext();) { |
| NavigationHistoryEditorInfo info = iter.next(); |
| if (!info.isPersistable()) { |
| iter.remove(); |
| } |
| } |
| IMemento editorsMem = memento.createChild(IWorkbenchConstants.TAG_EDITORS); |
| for (NavigationHistoryEditorInfo info : editors) { |
| info.saveState(editorsMem.createChild(IWorkbenchConstants.TAG_EDITOR)); |
| } |
| |
| ArrayList<NavigationHistoryEntry> list = new ArrayList<>(history.size()); |
| int size = history.size(); |
| for (int i = 0; i < size; i++) { |
| NavigationHistoryEntry entry = history.get(i); |
| if (entry.editorInfo.isPersistable()) { |
| list.add(entry); |
| } |
| } |
| size = list.size(); |
| for (int i = 0; i < size; i++) { |
| NavigationHistoryEntry entry = list.get(i); |
| IMemento childMem = memento.createChild(IWorkbenchConstants.TAG_ITEM); |
| if (entry == cEntry) { |
| childMem.putString(IWorkbenchConstants.TAG_ACTIVE, "true"); //$NON-NLS-1$ |
| } |
| entry.saveState(childMem, list); |
| childMem.putInteger(IWorkbenchConstants.TAG_INDEX, editors.indexOf(entry.editorInfo)); |
| } |
| } |
| |
| /** |
| * Restore the state of this history from the memento. |
| */ |
| void restoreState(IMemento memento) { |
| IMemento editorsMem = memento.getChild(IWorkbenchConstants.TAG_EDITORS); |
| IMemento items[] = memento.getChildren(IWorkbenchConstants.TAG_ITEM); |
| if (items.length == 0 || editorsMem == null) { |
| if (page.getActiveEditor() != null) { |
| markLocation(page.getActiveEditor()); |
| } |
| return; |
| } |
| |
| IMemento children[] = editorsMem.getChildren(IWorkbenchConstants.TAG_EDITOR); |
| NavigationHistoryEditorInfo editorsInfo[] = new NavigationHistoryEditorInfo[children.length]; |
| for (int i = 0; i < editorsInfo.length; i++) { |
| editorsInfo[i] = new NavigationHistoryEditorInfo(children[i]); |
| editors.add(editorsInfo[i]); |
| } |
| |
| for (int i = 0; i < items.length; i++) { |
| IMemento item = items[i]; |
| int index = item.getInteger(IWorkbenchConstants.TAG_INDEX).intValue(); |
| NavigationHistoryEditorInfo info = editorsInfo[index]; |
| info.refCount++; |
| NavigationHistoryEntry entry = new NavigationHistoryEntry(info, page, null, null); |
| history.add(entry); |
| entry.restoreState(item); |
| if (item.getString(IWorkbenchConstants.TAG_ACTIVE) != null) { |
| activeEntry = i; |
| } |
| } |
| |
| final NavigationHistoryEntry entry = getEntry(activeEntry); |
| if (entry != null && entry.editorInfo.editorInput != null) { |
| if (page.getActiveEditor() == page.findEditor(entry.editorInfo.editorInput)) { |
| StartupThreading.runWithoutExceptions(new StartupRunnable() { |
| |
| @Override |
| public void runWithException() throws Throwable { |
| gotoEntry(entry); |
| } |
| }); |
| } |
| } |
| } |
| |
| public NavigationHistoryEntry createEntry(IWorkbenchPage page, IEditorPart part, INavigationLocation location) { |
| String editorID = part.getSite().getId(); |
| IEditorInput editorInput = part.getEditorInput(); |
| NavigationHistoryEditorInfo info = null; |
| for (Iterator<NavigationHistoryEditorInfo> iter = editors.iterator(); iter.hasNext();) { |
| info = iter.next(); |
| if (editorID.equals(info.editorID) && editorInput.equals(info.editorInput)) { |
| info.refCount++; |
| break; |
| } |
| info = null; |
| } |
| if (info == null) { |
| info = new NavigationHistoryEditorInfo(part); |
| info.refCount++; |
| editors.add(info); |
| } |
| return new NavigationHistoryEntry(info, page, part, location); |
| } |
| |
| public void disposeEntry(NavigationHistoryEntry entry) { |
| if (entry.editorInfo == null) { |
| return; |
| } |
| entry.editorInfo.refCount--; |
| if (entry.editorInfo.refCount == 0) { |
| editors.remove(entry.editorInfo); |
| } |
| entry.dispose(); |
| } |
| |
| void checkDuplicates(NavigationHistoryEditorInfo info) { |
| NavigationHistoryEditorInfo dup = null; |
| if (info.editorInput == null) { |
| return; |
| } |
| for (Iterator<NavigationHistoryEditorInfo> iter = editors.iterator(); iter.hasNext();) { |
| dup = iter.next(); |
| if (info != dup && info.editorID.equals(dup.editorID) && info.editorInput.equals(dup.editorInput)) { |
| break; |
| } |
| dup = null; |
| } |
| if (dup == null) { |
| return; |
| } |
| for (NavigationHistoryEntry entry : history) { |
| if (entry.editorInfo == dup) { |
| entry.editorInfo = info; |
| info.refCount++; |
| } |
| } |
| editors.remove(dup); |
| } |
| |
| /*********************************************************/ |
| /*** new per-tab history code ***/ |
| /*********************************************************/ |
| |
| private static class PerTabHistory { |
| LinkedList<NavigationHistoryEntry> backwardEntries = new LinkedList<>(); |
| NavigationHistoryEntry currentEntry = null; |
| LinkedList<NavigationHistoryEntry> forwardEntries = new LinkedList<>(); |
| } |
| |
| private void setNewCurrentEntryForTab(PerTabHistory perTabHistory, NavigationHistoryEntry entry) { |
| if (perTabHistory.currentEntry != null) { |
| perTabHistory.backwardEntries.addFirst(perTabHistory.currentEntry); |
| } |
| perTabHistory.currentEntry = entry; |
| removeEntriesForTab(perTabHistory.forwardEntries); |
| } |
| |
| private Object getCookieForTab(IEditorPart part) { |
| if (part != null) { |
| IWorkbenchPartSite site = part.getSite(); |
| if (site instanceof PartSite) { |
| PartSite partSite = (PartSite) site; |
| WorkbenchPartReference ref = (WorkbenchPartReference) partSite.getPartReference(); |
| if (!ref.isDisposed()) { |
| return partSite.getModel().getWidget(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| private void markLocationForTab(IEditorPart part) { |
| if (part instanceof ErrorEditorPart) { |
| updateActions(); |
| return; |
| } |
| Object tabCookie = getCookieForTab(part); |
| if (tabCookie != null) { |
| INavigationLocation location = null; |
| if (part instanceof INavigationLocationProvider) { |
| location = ((INavigationLocationProvider) part).createNavigationLocation(); |
| } |
| PerTabHistory perTabHistory = perTabHistoryMap.get(tabCookie); |
| if (perTabHistory == null) { |
| perTabHistory = new PerTabHistory(); |
| perTabHistoryMap.put(tabCookie, perTabHistory); |
| } |
| NavigationHistoryEntry current = perTabHistory.currentEntry; |
| if (current != null && current.editorInfo.memento != null) { |
| current.editorInfo.restoreEditor(); |
| checkDuplicates(current.editorInfo); |
| } |
| NavigationHistoryEntry entry = createEntry(page, part, location); |
| if (current != null && entry.mergeInto(current)) { |
| disposeEntry(entry); |
| removeEntriesForTab(perTabHistory.forwardEntries); |
| } else { |
| setNewCurrentEntryForTab(perTabHistory, entry); |
| } |
| } |
| updateActions(); |
| } |
| |
| public void updateCookieForTab(Object oldCookie, Object newCookie) { |
| if (newCookie.equals(oldCookie)) { |
| return; |
| } |
| PerTabHistory perTabHistory = perTabHistoryMap.remove(oldCookie); |
| if (perTabHistory != null) { |
| perTabHistoryMap.put(newCookie, perTabHistory); |
| } |
| } |
| |
| private void gotoEntryForTab(NavigationHistoryEntry target, boolean forward) { |
| Object editorTabCookie = getCookieForTab(page.getActiveEditor()); |
| if (editorTabCookie != null) { |
| PerTabHistory perTabHistory = perTabHistoryMap.get(editorTabCookie); |
| if (perTabHistory != null) { |
| LinkedList<NavigationHistoryEntry> source = forward ? perTabHistory.forwardEntries |
| : perTabHistory.backwardEntries; |
| LinkedList<NavigationHistoryEntry> destination = forward ? perTabHistory.backwardEntries |
| : perTabHistory.forwardEntries; |
| if (perTabHistory.currentEntry != null) { |
| if (perTabHistory.currentEntry.location != null) { |
| perTabHistory.currentEntry.location.update(); |
| } |
| destination.addFirst(perTabHistory.currentEntry); |
| } |
| NavigationHistoryEntry newCurrent = null; |
| while (!source.isEmpty() && newCurrent == null) { |
| NavigationHistoryEntry entry = source.removeFirst(); |
| if (entry.equals(target)) { |
| newCurrent = entry; |
| } else { |
| destination.addFirst(entry); |
| } |
| } |
| Assert.isTrue(newCurrent != null); |
| perTabHistory.currentEntry = newCurrent; |
| try { |
| ignoreEntries++; |
| if (newCurrent.editorInfo.memento != null) { |
| newCurrent.editorInfo.restoreEditor(); |
| checkDuplicates(newCurrent.editorInfo); |
| } |
| newCurrent.restoreLocation(); |
| updateActions(); |
| } finally { |
| ignoreEntries--; |
| } |
| } |
| } |
| } |
| |
| private void forwardForTab() { |
| Object editorTabCookie = getCookieForTab(page.getActiveEditor()); |
| if (editorTabCookie != null) { |
| PerTabHistory perTabHistory = perTabHistoryMap.get(editorTabCookie); |
| if (perTabHistory != null && !perTabHistory.forwardEntries.isEmpty()) { |
| NavigationHistoryEntry newCurrent = perTabHistory.forwardEntries.removeFirst(); |
| if (perTabHistory.currentEntry != null) { |
| final INavigationLocation location = perTabHistory.currentEntry.location; |
| if (location != null) { |
| location.update(); |
| } |
| perTabHistory.backwardEntries.addFirst(perTabHistory.currentEntry); |
| } |
| perTabHistory.currentEntry = newCurrent; |
| try { |
| ignoreEntries++; |
| if (newCurrent.editorInfo.memento != null) { |
| newCurrent.editorInfo.restoreEditor(); |
| checkDuplicates(newCurrent.editorInfo); |
| } |
| newCurrent.restoreLocation(); |
| updateActions(); |
| } finally { |
| ignoreEntries--; |
| } |
| } |
| } |
| } |
| |
| private void backwardForTab() { |
| Object editorTabCookie = getCookieForTab(page.getActiveEditor()); |
| if (editorTabCookie != null) { |
| PerTabHistory perTabHistory = perTabHistoryMap.get(editorTabCookie); |
| if (perTabHistory != null && !perTabHistory.backwardEntries.isEmpty()) { |
| NavigationHistoryEntry newCurrent = perTabHistory.backwardEntries |
| .removeFirst(); |
| if (perTabHistory.currentEntry != null) { |
| perTabHistory.currentEntry.location.update(); |
| perTabHistory.forwardEntries.addFirst(perTabHistory.currentEntry); |
| } |
| perTabHistory.currentEntry = newCurrent; |
| try { |
| ignoreEntries++; |
| if (newCurrent.editorInfo.memento != null) { |
| newCurrent.editorInfo.restoreEditor(); |
| checkDuplicates(newCurrent.editorInfo); |
| } |
| newCurrent.restoreLocation(); |
| updateActions(); |
| } finally { |
| ignoreEntries--; |
| } |
| } |
| } |
| } |
| |
| private boolean hasEntriesForTab(boolean forward) { |
| Object editorTabCookie = getCookieForTab(page.getActiveEditor()); |
| if (editorTabCookie != null) { |
| PerTabHistory perTabHistory = perTabHistoryMap.get(editorTabCookie); |
| if (perTabHistory != null) { |
| LinkedList<NavigationHistoryEntry> entries = forward ? perTabHistory.forwardEntries |
| : perTabHistory.backwardEntries; |
| return !entries.isEmpty(); |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Returns entries in restore order. |
| * |
| * @param forward <code>true</code> for forward and <code>false</code> for |
| * backward history |
| * @return the navigation history entries |
| */ |
| private NavigationHistoryEntry[] getEntriesForTab(boolean forward) { |
| Object editorTabCookie = getCookieForTab(page.getActiveEditor()); |
| if (editorTabCookie != null) { |
| PerTabHistory perTabHistory = perTabHistoryMap.get(editorTabCookie); |
| if (perTabHistory != null) { |
| LinkedList<NavigationHistoryEntry> entries = forward ? perTabHistory.forwardEntries |
| : perTabHistory.backwardEntries; |
| return entries.toArray(new NavigationHistoryEntry[entries.size()]); |
| } |
| } |
| return new NavigationHistoryEntry[0]; |
| } |
| |
| private void disposeHistoryForTabs() { |
| Object[] keys = perTabHistoryMap.keySet().toArray(); |
| for (Object key : keys) { |
| disposeHistoryForTab(key); |
| } |
| } |
| |
| void disposeHistoryForTab(Object editorTabCookie) { |
| PerTabHistory perTabHistory = perTabHistoryMap.remove(editorTabCookie); |
| if (perTabHistory != null) { |
| if (perTabHistory.currentEntry != null) { |
| disposeEntry(perTabHistory.currentEntry); |
| perTabHistory.currentEntry = null; |
| } |
| removeEntriesForTab(perTabHistory.backwardEntries); |
| removeEntriesForTab(perTabHistory.forwardEntries); |
| } |
| } |
| |
| private void removeEntriesForTab(LinkedList<NavigationHistoryEntry> entries) { |
| for (Iterator<NavigationHistoryEntry> it = entries.iterator(); it.hasNext();) { |
| NavigationHistoryEntry entry = it.next(); |
| disposeEntry(entry); |
| it.remove(); |
| } |
| } |
| |
| public boolean updateActive(IEditorPart editor) { |
| NavigationHistoryEntry e = getEntry(activeEntry); |
| if (e == null) |
| return false; |
| // 1) check if editor ID matches |
| IWorkbenchPartSite site = editor.getSite(); |
| if (site == null) // might happen if site has not being initialized yet |
| return false; |
| String editorID = site.getId(); |
| if (editorID == null) // should not happen for an editor |
| return false; |
| if (!editorID.equals(e.editorInfo.editorID)) |
| return false; |
| // 2) check that input matches |
| IEditorInput input = editor.getEditorInput(); |
| if (input == null) |
| return false; |
| if (!input.equals(e.editorInfo.editorInput)) |
| return false; |
| updateEntry(e); |
| return true; |
| } |
| } |