blob: 672424d62c68f463e07306b16387089f4c2649fc [file] [log] [blame]
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* 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.intellij.ui.navigation;
import com.intellij.openapi.Disposable;
import com.intellij.openapi.actionSystem.DataKey;
import com.intellij.openapi.util.ActionCallback;
import com.intellij.openapi.util.Disposer;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArraySet;
public final class History {
public static DataKey<History> KEY = DataKey.create("History");
private final List<Place> myHistory = new ArrayList<Place>();
private int myCurrentPos;
private final Place.Navigator myRoot;
private boolean myNavigatedNow;
private final CopyOnWriteArraySet<HistoryListener> myListeners = new CopyOnWriteArraySet<HistoryListener>();
public History(@NotNull Place.Navigator root) {
myRoot = root;
}
public void pushQueryPlace() {
if (isNavigatingNow()) return;
final Place place = query();
if (place != null) {
pushPlace(query());
}
}
public void pushPlace(@NotNull Place place) {
while (myCurrentPos > 0 && myHistory.size() > 0 && myCurrentPos < myHistory.size() - 1) {
myHistory.remove(myHistory.size() - 1);
}
if (myHistory.size() > 0) {
final Place prev = myHistory.get(myHistory.size() - 1);
if (prev.equals(place)) return;
if (prev.isMoreGeneralFor(place)) {
myHistory.remove(prev);
}
}
addPlace(place);
}
private void addPlace(Place place) {
myHistory.add(place);
myCurrentPos = myHistory.size() - 1;
}
public void pushPlaceForElement(String name, Object value) {
if (!canNavigateFor(name)) return;
final Place checkPlace = getCheckPlace(name);
if (checkPlace == null) return;
pushPlace(checkPlace.cloneForElement(name, value));
}
public Place getPlaceForElement(String name, String value) {
final Place checkPlace = getCheckPlace(name);
if (checkPlace == null) return new Place();
return checkPlace.cloneForElement(name, value);
}
public void navigateTo(Place place) {
myRoot.navigateTo(place, false);
}
public void back() {
assert canGoBack();
goThere(myCurrentPos - 1);
}
private void goThere(final int nextPos) {
myNavigatedNow = true;
final Place next = myHistory.get(nextPos);
final Place from = getCurrent();
fireStarted(from, next);
try {
final ActionCallback callback = myRoot.navigateTo(next, false);
callback.doWhenDone(new Runnable() {
@Override
public void run() {
myCurrentPos = nextPos;
}
}).doWhenProcessed(new Runnable() {
@Override
public void run() {
myNavigatedNow = false;
fireFinished(from, next);
}
});
}
catch (Throwable e) {
myNavigatedNow = false;
throw new RuntimeException(e);
}
}
public boolean isNavigatingNow() {
return myNavigatedNow;
}
public boolean canGoBack() {
return myHistory.size() > 1 && myCurrentPos > 0;
}
public void forward() {
assert canGoForward();
goThere(myCurrentPos + 1);
}
public boolean canGoForward() {
return myHistory.size() > 1 && myCurrentPos < myHistory.size() - 1;
}
public void clear() {
myHistory.clear();
myCurrentPos = -1;
}
public Place query() {
final Place result = new Place();
myRoot.queryPlace(result);
return result;
}
private Place getCurrent() {
if (myCurrentPos >= 0 && myCurrentPos < myHistory.size()) {
return myHistory.get(myCurrentPos);
} else {
return null;
}
}
private boolean canNavigateFor(String pathElement) {
if (isNavigatingNow()) return false;
Place checkPlace = getCheckPlace(pathElement);
return checkPlace != null && checkPlace.getPath(pathElement) != null;
}
@Nullable
private Place getCheckPlace(String pathElement) {
Place checkPlace = getCurrent();
if (checkPlace == null || checkPlace.getPath(pathElement) == null) {
checkPlace = query();
}
return checkPlace != null && checkPlace.getPath(pathElement) != null ? checkPlace : null;
}
public void addListener(final HistoryListener listener, Disposable parent) {
myListeners.add(listener);
Disposer.register(parent, new Disposable() {
@Override
public void dispose() {
myListeners.remove(listener);
}
});
}
private void fireStarted(Place from, Place to) {
for (HistoryListener each : myListeners) {
each.navigationStarted(from, to);
}
}
private void fireFinished(Place from, Place to) {
for (HistoryListener each : myListeners) {
each.navigationFinished(from, to);
}
}
}