001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal;
011:
012: import java.util.ArrayList;
013: import java.util.HashMap;
014: import java.util.Iterator;
015: import java.util.LinkedList;
016: import java.util.Map;
017:
018: import org.eclipse.core.runtime.Assert;
019: import org.eclipse.swt.widgets.Control;
020: import org.eclipse.swt.widgets.Display;
021: import org.eclipse.ui.IEditorInput;
022: import org.eclipse.ui.IEditorPart;
023: import org.eclipse.ui.IMemento;
024: import org.eclipse.ui.INavigationHistory;
025: import org.eclipse.ui.INavigationLocation;
026: import org.eclipse.ui.INavigationLocationProvider;
027: import org.eclipse.ui.IPartListener2;
028: import org.eclipse.ui.IWorkbenchPage;
029: import org.eclipse.ui.IWorkbenchPartReference;
030: import org.eclipse.ui.IWorkbenchPartSite;
031: import org.eclipse.ui.internal.StartupThreading.StartupRunnable;
032: import org.eclipse.ui.internal.tweaklets.TabBehaviour;
033: import org.eclipse.ui.internal.tweaklets.Tweaklets;
034:
035: /**
036: * Implementation of the back and forward actions.
037: */
038: public class NavigationHistory implements INavigationHistory {
039:
040: private static final int CAPACITY = 50;
041:
042: private NavigationHistoryAction backwardAction;
043:
044: private NavigationHistoryAction forwardAction;
045:
046: private int ignoreEntries;
047:
048: private ArrayList history = new ArrayList(CAPACITY);
049:
050: Map perTabHistoryMap = new HashMap();
051:
052: private ArrayList editors = new ArrayList(CAPACITY);
053:
054: private WorkbenchPage page;
055:
056: private int activeEntry = 0;
057:
058: /**
059: * Creates a new NavigationHistory to keep the NavigationLocation
060: * entries of the specified page.
061: */
062: public NavigationHistory(final WorkbenchPage page) {
063: this .page = page;
064: page.addPartListener(new IPartListener2() {
065: public void partActivated(IWorkbenchPartReference partRef) {
066: }
067:
068: public void partBroughtToTop(IWorkbenchPartReference partRef) {
069: }
070:
071: public void partDeactivated(IWorkbenchPartReference partRef) {
072: }
073:
074: public void partOpened(IWorkbenchPartReference partRef) {
075: }
076:
077: public void partHidden(IWorkbenchPartReference partRef) {
078: }
079:
080: public void partVisible(IWorkbenchPartReference partRef) {
081: }
082:
083: public void partClosed(IWorkbenchPartReference partRef) {
084: if (isPerTabHistoryEnabled()
085: && partRef instanceof EditorReference) {
086: if (!((EditorReference) partRef).isDisposed()) {
087: Object editorTabCookie = ((EditorReference) partRef)
088: .getPane();
089: disposeHistoryForTab(editorTabCookie);
090: updateActions();
091: }
092: }
093: updateNavigationHistory(partRef, true);
094: }
095:
096: public void partInputChanged(IWorkbenchPartReference partRef) {
097: updateNavigationHistory(partRef, false);
098: }
099:
100: private void updateNavigationHistory(
101: IWorkbenchPartReference partRef, boolean partClosed) {
102: if (partRef != null
103: && partRef.getPart(false) instanceof IEditorPart) {
104: IEditorPart editor = (IEditorPart) partRef
105: .getPart(false);
106: IEditorInput input = editor.getEditorInput();
107: String id = editor.getSite().getId();
108: Iterator e = editors.iterator();
109: NavigationHistoryEditorInfo info = null;
110: NavigationHistoryEditorInfo currentInfo = null;
111: NavigationHistoryEntry current = getEntry(activeEntry);
112: if (current != null) {
113: currentInfo = current.editorInfo;
114: }
115: while (e.hasNext()) {
116: info = (NavigationHistoryEditorInfo) e.next();
117: if (id.equals(info.editorID)
118: && input.equals(info.editorInput)) {
119: if (partClosed && info != currentInfo) {
120: info.handlePartClosed();
121: }
122: break;
123: }
124: info = null;
125: }
126: if (info == null) {
127: return;
128: }
129: boolean isEntryDisposed = false;
130: e = history.iterator();
131: int i = 0;
132: while (e.hasNext()) {
133: NavigationHistoryEntry entry = (NavigationHistoryEntry) e
134: .next();
135: if (entry.editorInfo == info) {
136: if (!entry.handlePartClosed()) {
137: // update the active entry since we are removing an item
138: if (i < activeEntry) {
139: activeEntry--;
140: } else if (i == activeEntry) {
141: if (i != 0) {
142: activeEntry--;
143: }
144: } else {
145: // activeEntry is before item we deleted
146: i++;
147: }
148: isEntryDisposed = true;
149: e.remove();
150: disposeEntry(entry);
151: } else {
152: i++;
153: }
154: }
155: }
156:
157: /*
158: * Promote the entry of the last closed editor to be the active
159: * one, see: https://bugs.eclipse.org/bugs/show_bug.cgi?id=154431
160: */
161: if (!isEntryDisposed
162: && page.getActiveEditor() == null
163: && activeEntry < history.size())
164: activeEntry++;
165:
166: updateActions();
167: }
168: }
169: });
170: }
171:
172: private Display getDisplay() {
173: return page.getWorkbenchWindow().getShell().getDisplay();
174: }
175:
176: private boolean isPerTabHistoryEnabled() {
177: return ((TabBehaviour) Tweaklets.get(TabBehaviour.KEY))
178: .isPerTabHistoryEnabled();
179: }
180:
181: /*
182: * Adds an editor to the editor history without getting its location.
183: */
184: public void markEditor(final IEditorPart part) {
185: if (ignoreEntries > 0 || part == null) {
186: return;
187: }
188: /* Ignore all entries until the async exec runs. Workaround to avoid
189: * extra entry when using Open Declaration (F3) that opens another editor. */
190: ignoreEntries++;
191: getDisplay().asyncExec(new Runnable() {
192: public void run() {
193: if (--ignoreEntries == 0) {
194: if (part.getEditorSite() instanceof EditorSite) {
195: EditorSite site = (EditorSite) part
196: .getEditorSite();
197: Control c = site.getPane().getControl();
198: if (c == null || c.isDisposed()) {
199: return;
200: }
201: NavigationHistoryEntry e = getEntry(activeEntry);
202: if (e != null
203: && part.getEditorInput() != e.editorInfo.editorInput) {
204: updateEntry(e);
205: }
206: addEntry(part);
207: }
208: }
209: }
210: });
211: }
212:
213: /*
214: * (non-Javadoc)
215: * Method declared on INavigationHistory.
216: */
217: public void markLocation(IEditorPart part) {
218: addEntry(part);
219: }
220:
221: /*
222: * Return the backward history entries. Return in restore order (i.e., the
223: * first entry is the entry that would become active if the "Backward" action
224: * was executed).
225: * <p>
226: * (Called by NavigationHistoryAction)
227: * </p>
228: */
229: NavigationHistoryEntry[] getBackwardEntries() {
230: if (isPerTabHistoryEnabled()) {
231: return getEntriesForTab(false);
232: }
233: int length = activeEntry;
234: NavigationHistoryEntry[] entries = new NavigationHistoryEntry[length];
235: for (int i = 0; i < activeEntry; i++) {
236: entries[activeEntry - 1 - i] = getEntry(i);
237: }
238: return entries;
239: }
240:
241: /*
242: * Return the forward history entries. Return in restore order (i.e., the first
243: * entry is the entry that would become active if the "Forward" action was
244: * executed).
245: * <p>
246: * (Called by NavigationHistoryAction)
247: * </p>
248: */
249: NavigationHistoryEntry[] getForwardEntries() {
250: if (isPerTabHistoryEnabled()) {
251: return getEntriesForTab(true);
252: }
253: int length = history.size() - activeEntry - 1;
254: length = Math.max(0, length);
255: NavigationHistoryEntry[] entries = new NavigationHistoryEntry[length];
256: for (int i = activeEntry + 1; i < history.size(); i++) {
257: entries[i - activeEntry - 1] = getEntry(i);
258: }
259: return entries;
260: }
261:
262: /*
263: * (non-Javadoc)
264: * Method declared on INavigationHistory.
265: */
266: public INavigationLocation[] getLocations() {
267: INavigationLocation result[] = new INavigationLocation[history
268: .size()];
269: for (int i = 0; i < result.length; i++) {
270: NavigationHistoryEntry e = (NavigationHistoryEntry) history
271: .get(i);
272: result[i] = e.location;
273: }
274: return result;
275: }
276:
277: /*
278: * (non-Javadoc)
279: * Method declared on INavigationHistory.
280: */
281: public INavigationLocation getCurrentLocation() {
282: NavigationHistoryEntry entry = getEntry(activeEntry);
283: return entry == null ? null : entry.location;
284: }
285:
286: /**
287: * Disposes this NavigationHistory and all entries.
288: */
289: public void dispose() {
290: disposeHistoryForTabs();
291: Iterator e = history.iterator();
292: while (e.hasNext()) {
293: NavigationHistoryEntry entry = (NavigationHistoryEntry) e
294: .next();
295: disposeEntry(entry);
296: }
297: }
298:
299: /**
300: * Keeps a reference to the forward action to update its state
301: * whenever needed.
302: * <p>
303: * (Called by NavigationHistoryAction)
304: * </p>
305: */
306: void setForwardAction(NavigationHistoryAction action) {
307: forwardAction = action;
308: updateActions();
309: }
310:
311: /**
312: * Keeps a reference to the backward action to update its state
313: * whenever needed.
314: * <p>
315: * (Called by NavigationHistoryAction)
316: * </p>
317: */
318: void setBackwardAction(NavigationHistoryAction action) {
319: backwardAction = action;
320: updateActions();
321: }
322:
323: /*
324: * Returns the history entry indexed by <code>index</code>
325: */
326: private NavigationHistoryEntry getEntry(int index) {
327: if (0 <= index && index < history.size()) {
328: return (NavigationHistoryEntry) history.get(index);
329: }
330: return null;
331: }
332:
333: /*
334: * Adds the specified entry to the history.
335: */
336: private void add(NavigationHistoryEntry entry) {
337: removeForwardEntries();
338: if (history.size() == CAPACITY) {
339: NavigationHistoryEntry e = (NavigationHistoryEntry) history
340: .remove(0);
341: disposeEntry(e);
342: }
343: history.add(entry);
344: activeEntry = history.size() - 1;
345: }
346:
347: /*
348: * Remove all entries after the active entry.
349: */
350: private void removeForwardEntries() {
351: int length = history.size();
352: for (int i = activeEntry + 1; i < length; i++) {
353: NavigationHistoryEntry e = (NavigationHistoryEntry) history
354: .remove(activeEntry + 1);
355: disposeEntry(e);
356: }
357: }
358:
359: /*
360: * Adds a location to the history.
361: */
362: private void addEntry(IEditorPart part) {
363: if (ignoreEntries > 0 || part == null) {
364: return;
365: }
366:
367: if (isPerTabHistoryEnabled()) {
368: markLocationForTab(part);
369: }
370: INavigationLocation location = null;
371: if (part instanceof INavigationLocationProvider) {
372: location = ((INavigationLocationProvider) part)
373: .createNavigationLocation();
374: }
375:
376: NavigationHistoryEntry current = getEntry(activeEntry);
377: if (current != null && current.editorInfo.memento != null) {
378: current.editorInfo.restoreEditor();
379: checkDuplicates(current.editorInfo);
380: }
381: NavigationHistoryEntry e = createEntry(page, part, location);
382: if (current == null) {
383: add(e);
384: } else {
385: if (e.mergeInto(current)) {
386: disposeEntry(e);
387: removeForwardEntries();
388: } else {
389: add(e);
390: }
391: }
392: printEntries("added entry"); //$NON-NLS-1$
393: updateActions();
394: }
395:
396: /*
397: * Prints all the entries in the console. For debug only.
398: */
399: private void printEntries(String label) {
400: if (false) {
401: System.out.println("+++++ " + label + "+++++ "); //$NON-NLS-1$ //$NON-NLS-2$
402: int size = history.size();
403: for (int i = 0; i < size; i++) {
404: String append = activeEntry == i ? ">>" : ""; //$NON-NLS-1$ //$NON-NLS-2$
405: System.out.println(append
406: + "Index: " + i + " " + history.get(i)); //$NON-NLS-1$ //$NON-NLS-2$
407: }
408: }
409: }
410:
411: /*
412: * Returns true if the forward action can be performed otherwise returns false.
413: * <p>
414: * (Called by NavigationHistoryAction)
415: * </p>
416: */
417: /* package */boolean canForward() {
418: if (isPerTabHistoryEnabled()) {
419: return hasEntriesForTab(true);
420: }
421: return (0 <= activeEntry + 1)
422: && (activeEntry + 1 < history.size());
423: }
424:
425: /*
426: * Returns true if the backward action can be performed otherwise returns false.
427: * <p>
428: * (Called by NavigationHistoryAction)
429: * </p>
430: */
431: /* package */boolean canBackward() {
432: if (isPerTabHistoryEnabled()) {
433: return hasEntriesForTab(false);
434: }
435: return (0 <= activeEntry - 1)
436: && (activeEntry - 1 < history.size());
437: }
438:
439: /*
440: * Update the actions enable/disable and tooltip state.
441: */
442: private void updateActions() {
443: if (backwardAction != null) {
444: backwardAction.update();
445: }
446: if (forwardAction != null) {
447: forwardAction.update();
448: }
449: }
450:
451: /*
452: * Restore the specified entry
453: */
454: private void gotoEntry(NavigationHistoryEntry entry) {
455: if (entry == null) {
456: return;
457: }
458: try {
459: ignoreEntries++;
460: if (entry.editorInfo.memento != null) {
461: entry.editorInfo.restoreEditor();
462: checkDuplicates(entry.editorInfo);
463: }
464: entry.restoreLocation();
465: updateActions();
466: printEntries("goto entry"); //$NON-NLS-1$
467: } finally {
468: ignoreEntries--;
469: }
470: }
471:
472: /*
473: * update the active entry
474: */
475: private void updateEntry(NavigationHistoryEntry activeEntry) {
476: if (activeEntry == null || activeEntry.location == null) {
477: return;
478: }
479: activeEntry.location.update();
480: printEntries("updateEntry"); //$NON-NLS-1$
481: }
482:
483: /*
484: * Perform the forward action by getting the next location and restoring
485: * its context.
486: * <p>
487: * (Called by NavigationHistoryAction)
488: * </p>
489: */
490: void forward() {
491: if (isPerTabHistoryEnabled()) {
492: forwardForTab();
493: return;
494: }
495: if (canForward()) {
496: shiftEntry(true);
497: }
498: }
499:
500: /*
501: * Perform the backward action by getting the previous location and restoring
502: * its context.
503: * <p>
504: * (Called by NavigationHistoryAction)
505: * </p>
506: */
507: void backward() {
508: if (isPerTabHistoryEnabled()) {
509: backwardForTab();
510: return;
511: }
512: if (canBackward()) {
513: shiftEntry(false);
514: }
515: }
516:
517: /*
518: * Shift the history back or forward
519: */
520: private void shiftEntry(boolean forward) {
521: updateEntry(getEntry(activeEntry));
522: if (forward) {
523: activeEntry++;
524: } else {
525: activeEntry--;
526: }
527: NavigationHistoryEntry entry = getEntry(activeEntry);
528: if (entry != null) {
529: gotoEntry(entry);
530: }
531: }
532:
533: /*
534: * Shift the history to the given entry.
535: * <p>
536: * (Called by NavigationHistoryAction)
537: * </p>
538: */
539: void shiftCurrentEntry(NavigationHistoryEntry entry, boolean forward) {
540: if (isPerTabHistoryEnabled()) {
541: gotoEntryForTab(entry, forward);
542: return;
543: }
544: updateEntry(getEntry(activeEntry));
545: activeEntry = history.indexOf(entry);
546: gotoEntry(entry);
547: }
548:
549: /**
550: * Save the state of this history into the memento.
551: */
552: void saveState(IMemento memento) {
553: NavigationHistoryEntry cEntry = getEntry(activeEntry);
554: if (cEntry == null || !cEntry.editorInfo.isPersistable()) {
555: return;
556: }
557:
558: ArrayList editors = (ArrayList) this .editors.clone();
559: for (Iterator iter = editors.iterator(); iter.hasNext();) {
560: NavigationHistoryEditorInfo info = (NavigationHistoryEditorInfo) iter
561: .next();
562: if (!info.isPersistable()) {
563: iter.remove();
564: }
565: }
566: IMemento editorsMem = memento
567: .createChild(IWorkbenchConstants.TAG_EDITORS);
568: for (Iterator iter = editors.iterator(); iter.hasNext();) {
569: NavigationHistoryEditorInfo info = (NavigationHistoryEditorInfo) iter
570: .next();
571: info.saveState(editorsMem
572: .createChild(IWorkbenchConstants.TAG_EDITOR));
573: }
574:
575: ArrayList list = new ArrayList(history.size());
576: int size = history.size();
577: for (int i = 0; i < size; i++) {
578: NavigationHistoryEntry entry = (NavigationHistoryEntry) history
579: .get(i);
580: if (entry.editorInfo.isPersistable()) {
581: list.add(entry);
582: }
583: }
584: size = list.size();
585: for (int i = 0; i < size; i++) {
586: NavigationHistoryEntry entry = (NavigationHistoryEntry) list
587: .get(i);
588: IMemento childMem = memento
589: .createChild(IWorkbenchConstants.TAG_ITEM);
590: if (entry == cEntry) {
591: childMem.putString(IWorkbenchConstants.TAG_ACTIVE,
592: "true"); //$NON-NLS-1$
593: }
594: entry.saveState(childMem, list);
595: childMem.putInteger(IWorkbenchConstants.TAG_INDEX, editors
596: .indexOf(entry.editorInfo));
597: }
598: }
599:
600: /**
601: * Restore the state of this history from the memento.
602: */
603: void restoreState(IMemento memento) {
604: IMemento editorsMem = memento
605: .getChild(IWorkbenchConstants.TAG_EDITORS);
606: IMemento items[] = memento
607: .getChildren(IWorkbenchConstants.TAG_ITEM);
608: if (items.length == 0 || editorsMem == null) {
609: if (page.getActiveEditor() != null) {
610: markLocation(page.getActiveEditor());
611: }
612: return;
613: }
614:
615: IMemento children[] = editorsMem
616: .getChildren(IWorkbenchConstants.TAG_EDITOR);
617: NavigationHistoryEditorInfo editorsInfo[] = new NavigationHistoryEditorInfo[children.length];
618: for (int i = 0; i < editorsInfo.length; i++) {
619: editorsInfo[i] = new NavigationHistoryEditorInfo(
620: children[i]);
621: editors.add(editorsInfo[i]);
622: }
623:
624: for (int i = 0; i < items.length; i++) {
625: IMemento item = items[i];
626: int index = item.getInteger(IWorkbenchConstants.TAG_INDEX)
627: .intValue();
628: NavigationHistoryEditorInfo info = editorsInfo[index];
629: info.refCount++;
630: NavigationHistoryEntry entry = new NavigationHistoryEntry(
631: info, page, null, null);
632: history.add(entry);
633: entry.restoreState(item);
634: if (item.getString(IWorkbenchConstants.TAG_ACTIVE) != null) {
635: activeEntry = i;
636: }
637: }
638:
639: final NavigationHistoryEntry entry = getEntry(activeEntry);
640: if (entry != null && entry.editorInfo.editorInput != null) {
641: if (page.getActiveEditor() == page
642: .findEditor(entry.editorInfo.editorInput)) {
643: StartupThreading
644: .runWithoutExceptions(new StartupRunnable() {
645:
646: public void runWithException()
647: throws Throwable {
648: gotoEntry(entry);
649: }
650: });
651: }
652: }
653: }
654:
655: public NavigationHistoryEntry createEntry(IWorkbenchPage page,
656: IEditorPart part, INavigationLocation location) {
657: String editorID = part.getSite().getId();
658: IEditorInput editorInput = part.getEditorInput();
659: NavigationHistoryEditorInfo info = null;
660: for (Iterator iter = editors.iterator(); iter.hasNext();) {
661: info = (NavigationHistoryEditorInfo) iter.next();
662: if (editorID.equals(info.editorID)
663: && editorInput.equals(info.editorInput)) {
664: info.refCount++;
665: break;
666: } else {
667: info = null;
668: }
669: }
670: if (info == null) {
671: info = new NavigationHistoryEditorInfo(part);
672: info.refCount++;
673: editors.add(info);
674: }
675: return new NavigationHistoryEntry(info, page, part, location);
676: }
677:
678: public void disposeEntry(NavigationHistoryEntry entry) {
679: if (entry.editorInfo == null) {
680: return;
681: }
682: entry.editorInfo.refCount--;
683: if (entry.editorInfo.refCount == 0) {
684: editors.remove(entry.editorInfo);
685: }
686: entry.dispose();
687: }
688:
689: void checkDuplicates(NavigationHistoryEditorInfo info) {
690: NavigationHistoryEditorInfo dup = null;
691: if (info.editorInput == null) {
692: return;
693: }
694: for (Iterator iter = editors.iterator(); iter.hasNext();) {
695: dup = (NavigationHistoryEditorInfo) iter.next();
696: if (info != dup && info.editorID.equals(dup.editorID)
697: && info.editorInput.equals(dup.editorInput)) {
698: break;
699: } else {
700: dup = null;
701: }
702: }
703: if (dup == null) {
704: return;
705: }
706: for (Iterator iter = history.iterator(); iter.hasNext();) {
707: NavigationHistoryEntry entry = (NavigationHistoryEntry) iter
708: .next();
709: if (entry.editorInfo == dup) {
710: entry.editorInfo = info;
711: info.refCount++;
712: }
713: }
714: editors.remove(dup);
715: }
716:
717: /*********************************************************/
718: /*** new per-tab history code ***/
719: /*********************************************************/
720:
721: private static class PerTabHistory {
722: LinkedList backwardEntries = new LinkedList();
723: NavigationHistoryEntry currentEntry = null;
724: LinkedList forwardEntries = new LinkedList();
725: }
726:
727: private void setNewCurrentEntryForTab(PerTabHistory perTabHistory,
728: NavigationHistoryEntry entry) {
729: if (perTabHistory.currentEntry != null) {
730: perTabHistory.backwardEntries
731: .addFirst(perTabHistory.currentEntry);
732: }
733: perTabHistory.currentEntry = entry;
734: removeEntriesForTab(perTabHistory.forwardEntries);
735: }
736:
737: private Object getCookieForTab(IEditorPart part) {
738: if (part != null) {
739: IWorkbenchPartSite site = part.getSite();
740: if (site instanceof PartSite) {
741: PartSite partSite = (PartSite) site;
742: WorkbenchPartReference ref = (WorkbenchPartReference) partSite
743: .getPartReference();
744: if (!ref.isDisposed()) {
745: return partSite.getPane();
746: }
747: }
748: }
749: return null;
750: }
751:
752: private void markLocationForTab(IEditorPart part) {
753: if (part instanceof ErrorEditorPart) {
754: updateActions();
755: return;
756: }
757: Object tabCookie = getCookieForTab(part);
758: if (tabCookie != null) {
759: INavigationLocation location = null;
760: if (part instanceof INavigationLocationProvider) {
761: location = ((INavigationLocationProvider) part)
762: .createNavigationLocation();
763: }
764: PerTabHistory perTabHistory = (PerTabHistory) perTabHistoryMap
765: .get(tabCookie);
766: if (perTabHistory == null) {
767: perTabHistory = new PerTabHistory();
768: perTabHistoryMap.put(tabCookie, perTabHistory);
769: }
770: NavigationHistoryEntry current = perTabHistory.currentEntry;
771: if (current != null && current.editorInfo.memento != null) {
772: current.editorInfo.restoreEditor();
773: checkDuplicates(current.editorInfo);
774: }
775: NavigationHistoryEntry entry = createEntry(page, part,
776: location);
777: if (current != null && entry.mergeInto(current)) {
778: disposeEntry(entry);
779: removeEntriesForTab(perTabHistory.forwardEntries);
780: } else {
781: setNewCurrentEntryForTab(perTabHistory, entry);
782: }
783: }
784: updateActions();
785: }
786:
787: public void updateCookieForTab(Object oldCookie, Object newCookie) {
788: if (newCookie.equals(oldCookie)) {
789: return;
790: }
791: PerTabHistory perTabHistory = (PerTabHistory) perTabHistoryMap
792: .remove(oldCookie);
793: if (perTabHistory != null) {
794: perTabHistoryMap.put(newCookie, perTabHistory);
795: }
796: }
797:
798: private void gotoEntryForTab(NavigationHistoryEntry target,
799: boolean forward) {
800: Object editorTabCookie = getCookieForTab(page.getActiveEditor());
801: if (editorTabCookie != null) {
802: PerTabHistory perTabHistory = (PerTabHistory) perTabHistoryMap
803: .get(editorTabCookie);
804: if (perTabHistory != null) {
805: LinkedList source = forward ? perTabHistory.forwardEntries
806: : perTabHistory.backwardEntries;
807: LinkedList destination = forward ? perTabHistory.backwardEntries
808: : perTabHistory.forwardEntries;
809: if (perTabHistory.currentEntry != null) {
810: if (perTabHistory.currentEntry.location != null) {
811: perTabHistory.currentEntry.location.update();
812: }
813: destination.addFirst(perTabHistory.currentEntry);
814: }
815: NavigationHistoryEntry newCurrent = null;
816: while (!source.isEmpty() && newCurrent == null) {
817: NavigationHistoryEntry entry = (NavigationHistoryEntry) source
818: .removeFirst();
819: if (entry.equals(target)) {
820: newCurrent = entry;
821: } else {
822: destination.addFirst(entry);
823: }
824: }
825: Assert.isTrue(newCurrent != null);
826: perTabHistory.currentEntry = newCurrent;
827: try {
828: ignoreEntries++;
829: if (newCurrent.editorInfo.memento != null) {
830: newCurrent.editorInfo.restoreEditor();
831: checkDuplicates(newCurrent.editorInfo);
832: }
833: newCurrent.restoreLocation();
834: updateActions();
835: } finally {
836: ignoreEntries--;
837: }
838: }
839: }
840: }
841:
842: private void forwardForTab() {
843: Object editorTabCookie = getCookieForTab(page.getActiveEditor());
844: if (editorTabCookie != null) {
845: PerTabHistory perTabHistory = (PerTabHistory) perTabHistoryMap
846: .get(editorTabCookie);
847: if (perTabHistory != null
848: && !perTabHistory.forwardEntries.isEmpty()) {
849: NavigationHistoryEntry newCurrent = (NavigationHistoryEntry) perTabHistory.forwardEntries
850: .removeFirst();
851: if (perTabHistory.currentEntry != null) {
852: final INavigationLocation location = perTabHistory.currentEntry.location;
853: if (location != null) {
854: location.update();
855: }
856: perTabHistory.backwardEntries
857: .addFirst(perTabHistory.currentEntry);
858: }
859: perTabHistory.currentEntry = newCurrent;
860: try {
861: ignoreEntries++;
862: if (newCurrent.editorInfo.memento != null) {
863: newCurrent.editorInfo.restoreEditor();
864: checkDuplicates(newCurrent.editorInfo);
865: }
866: newCurrent.restoreLocation();
867: updateActions();
868: } finally {
869: ignoreEntries--;
870: }
871: }
872: }
873: }
874:
875: private void backwardForTab() {
876: Object editorTabCookie = getCookieForTab(page.getActiveEditor());
877: if (editorTabCookie != null) {
878: PerTabHistory perTabHistory = (PerTabHistory) perTabHistoryMap
879: .get(editorTabCookie);
880: if (perTabHistory != null
881: && !perTabHistory.backwardEntries.isEmpty()) {
882: NavigationHistoryEntry newCurrent = (NavigationHistoryEntry) perTabHistory.backwardEntries
883: .removeFirst();
884: if (perTabHistory.currentEntry != null) {
885: perTabHistory.currentEntry.location.update();
886: perTabHistory.forwardEntries
887: .addFirst(perTabHistory.currentEntry);
888: }
889: perTabHistory.currentEntry = newCurrent;
890: try {
891: ignoreEntries++;
892: if (newCurrent.editorInfo.memento != null) {
893: newCurrent.editorInfo.restoreEditor();
894: checkDuplicates(newCurrent.editorInfo);
895: }
896: newCurrent.restoreLocation();
897: updateActions();
898: } finally {
899: ignoreEntries--;
900: }
901: }
902: }
903: }
904:
905: private boolean hasEntriesForTab(boolean forward) {
906: Object editorTabCookie = getCookieForTab(page.getActiveEditor());
907: if (editorTabCookie != null) {
908: PerTabHistory perTabHistory = (PerTabHistory) perTabHistoryMap
909: .get(editorTabCookie);
910: if (perTabHistory != null) {
911: LinkedList entries = forward ? perTabHistory.forwardEntries
912: : perTabHistory.backwardEntries;
913: return !entries.isEmpty();
914: }
915: }
916: return false;
917: }
918:
919: /**
920: * Returns entries in restore order.
921: * @param editorTabCookie
922: * @param forward
923: * @return
924: */
925: private NavigationHistoryEntry[] getEntriesForTab(boolean forward) {
926: Object editorTabCookie = getCookieForTab(page.getActiveEditor());
927: if (editorTabCookie != null) {
928: PerTabHistory perTabHistory = (PerTabHistory) perTabHistoryMap
929: .get(editorTabCookie);
930: if (perTabHistory != null) {
931: LinkedList entries = forward ? perTabHistory.forwardEntries
932: : perTabHistory.backwardEntries;
933: return (NavigationHistoryEntry[]) entries
934: .toArray(new NavigationHistoryEntry[entries
935: .size()]);
936: }
937: }
938: return new NavigationHistoryEntry[0];
939: }
940:
941: private void disposeHistoryForTabs() {
942: Object[] keys = perTabHistoryMap.keySet().toArray();
943: for (int i = 0; i < keys.length; i++) {
944: disposeHistoryForTab(keys[i]);
945: }
946: }
947:
948: void disposeHistoryForTab(Object editorTabCookie) {
949: PerTabHistory perTabHistory = (PerTabHistory) perTabHistoryMap
950: .remove(editorTabCookie);
951: if (perTabHistory != null) {
952: if (perTabHistory.currentEntry != null) {
953: disposeEntry(perTabHistory.currentEntry);
954: perTabHistory.currentEntry = null;
955: }
956: removeEntriesForTab(perTabHistory.backwardEntries);
957: removeEntriesForTab(perTabHistory.forwardEntries);
958: }
959: }
960:
961: private void removeEntriesForTab(LinkedList entries) {
962: for (Iterator it = entries.iterator(); it.hasNext();) {
963: NavigationHistoryEntry entry = (NavigationHistoryEntry) it
964: .next();
965: disposeEntry(entry);
966: it.remove();
967: }
968: }
969: }
|