Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/port80/eclipse/util/NavigateHistory.java


1   package com.port80.eclipse.util;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import org.eclipse.jface.action.Action;
7   import org.eclipse.jface.action.IMenuManager;
8   import org.eclipse.jface.action.IToolBarManager;
9   
10  /**
11   * @author chrisl
12   *
13   * Implements a simple web style navigation metaphor over a list of objects for a viewer.  
14   * Back, and forward functions are supported. Each time a new object is added
15   * to the list at an existing index, the rest of the list is truncated.
16   * 
17   */
18  public class NavigateHistory {
19  
20    ////////////////////////////////////////////////////////////////////////
21  
22    public static final String NAME = "NavigateHistory";
23    public static final String ID = "com.port80.eclipse.util.NavigateHistory";
24    private static final int DEF_HISTORY_SIZE = 8;
25    private static final int DEF_VISITED_SIZE = 20;
26  
27    ////////////////////////////////////////////////////////////////////////
28  
29    /** History, in acess time order, oldest first.*/
30    private Object[] fHistory;
31    /** Unique objects visited, unordered.*/
32    private Object[] fVisited;
33    private List fListeners;
34    private int fPos;
35    private int fLast; // Last valid position in the history.
36    private int fVisitedSize;
37  
38    private Action backAction;
39    private Action forwardAction;
40    private boolean fCanGoBack;
41    private boolean fCanGoForward;
42  
43    ////////////////////////////////////////////////////////////////////////
44  
45    public NavigateHistory() {
46      this(DEF_HISTORY_SIZE, DEF_VISITED_SIZE);
47    }
48  
49    public NavigateHistory(int size) {
50      this(size, size);
51    }
52  
53    public NavigateHistory(int historysize, int visitedsize) {
54      this.fHistory = new Object[historysize];
55      this.fVisited = new Object[visitedsize];
56      this.fListeners = new ArrayList();
57      this.fPos = -1;
58      this.fLast = -1;
59      this.fVisitedSize = 0;
60      fCanGoBack = canGoBack();
61      fCanGoForward = canGoForward();
62    }
63  
64    ////////////////////////////////////////////////////////////////////////
65  
66    public void addHistoryListener(IHistoryListener l) {
67      fListeners.add(l);
68      fireUpdateStatusEvent();
69    }
70    public void removeHistoryListener(IHistoryListener l) {
71      fListeners.remove(l);
72    }
73  
74    ////////////////////////////////////////////////////////////////////////
75  
76    /**
77     * Add an object to the history.  Adding object now do not fire any GotoEvent.
78     * 
79     * Typical event sequence is:
80     * Trigger (doubleclick,actions in views, eg. History forward/backward/goto action->GotoHistoryEvent)
81     * -> Editor open
82     * -> PartActivationEvent
83     * -> Add to history, select in views ... etc.
84     * 
85     * When a new object is added when visiting an object in the middle of the history,
86     * the new object is inserted to the history after the current position in the history.
87     */
88    public void add(Object a) {
89      // Add to history.
90      if (a == null)
91        return;
92      if (fPos >= 0 && a.equals(fHistory[fPos])) {
93        return;
94      }
95      if (fPos < fLast && a.equals(fHistory[fPos + 1])) {
96        ++fPos;
97        updateButtonStatus();
98        updateVisited(fHistory[fPos]);
99        return;
100     }
101     if (fPos == fHistory.length - 1) {
102       System.arraycopy(fHistory, 1, fHistory, 0, fHistory.length - 1);
103     } else {
104       // Insert into history.
105       ++fPos;
106       for (int i = fHistory.length - 1; i > fPos; --i)
107         fHistory[i] = fHistory[i - 1];
108       if (fLast < fHistory.length - 1)
109         ++fLast;
110     }
111     fHistory[fPos] = a;
112     updateButtonStatus();
113     updateVisited(a);
114   }
115 
116   ////////////////////////////////////////////////////////////////////////
117 
118   public int historySize() {
119     return fLast + 1;
120   }
121 
122   public int historyPos() {
123     return fPos;
124   }
125 
126   /**
127    * @return History object at the given index.
128    */
129   public Object getHistory(int n) {
130     if (n < 0 || n > fLast)
131       return null;
132     return fHistory[n];
133   }
134 
135   public Object[] getHistory() {
136     Object[] ret = new Object[historySize()];
137     System.arraycopy(fHistory, 0, ret, 0, historySize());
138     return ret;
139   }
140 
141   /**
142    * Goto the specified position.
143    */
144   public void gotoHistory(int n) {
145     if (n != fPos && n >= 0 && n <= fLast) {
146       fPos = n;
147       updateButtonStatus();
148       updateVisited(fHistory[fPos]);
149       fireGotoEvent(fHistory[fPos]);
150     }
151   }
152 
153   ////////////////////////////////////////////////////////////////////////
154 
155   public int visitedSize() {
156     return fVisitedSize;
157   }
158 
159   public Object getVisited(int n) {
160     return fVisited[n];
161   }
162 
163   public Object[] getVisited() {
164     Object[] ret = new Object[fVisitedSize];
165     System.arraycopy(fVisited, 0, ret, 0, fVisitedSize);
166     return ret;
167   }
168 
169   public void gotoVisited(int n) {
170     if (n >= 0 && n < fVisitedSize) {
171       Object ret = fVisited[n];
172       add(fVisited[n]);
173       fireGotoEvent(ret);
174     }
175   }
176 
177   /** Added object to visited set. This is automatically invoked before firing a GotoEvent. */
178   public void updateVisited(Object a) {
179     if (a == null)
180       return;
181     int i = 0;
182     for (; i < fVisitedSize; ++i) {
183       if (a.equals(fVisited[i])) {
184         break;
185       }
186     }
187     if (i == fVisitedSize) {
188       // Not found.
189       if (fVisitedSize >= fVisited.length) {
190         System.arraycopy(fVisited, 1, fVisited, 0, fVisited.length - 1);
191       } else {
192         ++fVisitedSize;
193       }
194     } else if (i < fVisitedSize - 1) {
195       // Found before last entry, move the entry to the front (most recent used).
196       // Otherwise we can just replace it.
197       System.arraycopy(fVisited, i + 1, fVisited, i, fVisitedSize - 1 - i);
198     }
199     fVisited[fVisitedSize - 1] = a;
200   }
201 
202   ////////////////////////////////////////////////////////////////////////
203 
204   /**
205    * @return <code>true</code> if "go back" is possible; <code>false</code> otherwise
206    */
207   public boolean canGoBack() {
208     return fPos > 0;
209   }
210 
211   /**
212    * @return <code>true</code> if "go into" is possible; <code>false</code> otherwise
213    */
214   public boolean canGoForward() {
215     return fPos < fLast;
216   }
217 
218   /**
219    * Go back.
220    */
221   public boolean goBack() {
222     if (fPos >= 0) {
223       --fPos;
224       updateButtonStatus();
225     }
226     if (fPos < 0)
227       return false;
228     Object input = fHistory[fPos];
229     updateVisited(input);
230     fireGotoEvent(input);
231     return canGoBack();
232   }
233 
234   /**
235    * Go forward.
236    */
237   public boolean goForward() {
238     if (fPos >= fLast)
239       return false;
240     Object input = fHistory[++fPos];
241     updateButtonStatus();
242     updateVisited(input);
243     fireGotoEvent(input);
244     return canGoForward();
245   }
246 
247   /**
248    * Resets the history.
249    */
250   public void reset() {
251     for (int i = 0; i <= fLast; ++i)
252       fHistory[i] = null;
253     fPos = -1;
254     fLast = -1;
255     for (int i = 0; i < fVisitedSize; ++i)
256       fVisited[i] = null;
257     fVisitedSize = 0;
258     updateButtonStatus();
259     //fireGotoEvent(null);
260   }
261 
262   /**
263    * Updates the enabled state for each navigation button.  
264    */
265   protected void updateButtonStatus() {
266     boolean needupdate = ((fCanGoBack != canGoBack()) || (fCanGoForward != canGoForward()));
267     if (needupdate) {
268       fCanGoBack = canGoBack();
269       fCanGoForward = canGoForward();
270       if (backAction != null)
271         backAction.setEnabled(fCanGoBack);
272       if (forwardAction != null)
273         forwardAction.setEnabled(fCanGoForward);
274       fireUpdateStatusEvent();
275     }
276   }
277 
278   ////////////////////////////////////////////////////////////////////////
279 
280   private void fireUpdateStatusEvent() {
281     for (int i = 0; i < fListeners.size(); ++i)
282       ((IHistoryListener) fListeners.get(i)).updateHistoryStatus(
283         canGoBack(),
284         canGoForward(),
285         getHistory(fPos - 1),
286         getHistory(fPos + 1));
287   }
288 
289   private void fireGotoEvent(Object a) {
290     for (int i = 0; i < fListeners.size(); ++i)
291        ((IHistoryListener) fListeners.get(i)).gotoObject(a);
292   }
293 
294   ////////////////////////////////////////////////////////////////////////
295 
296   /**
297    * Adds actions for "go back", "go home", and "go into" to a menu manager.
298    *
299    * @param manager is the target manager to update
300    */
301   public void addNavigationActions(IMenuManager manager) {
302     createActions();
303     manager.add(backAction);
304     manager.add(forwardAction);
305     updateButtonStatus();
306   }
307 
308   /**
309    * Adds actions for "go back", "go home", and "go into" to a tool bar manager.
310    *
311    * @param manager is the target manager to update
312    */
313   public void addNavigationActions(IToolBarManager toolBar) {
314     createActions();
315     toolBar.add(backAction);
316     toolBar.add(forwardAction);
317     updateButtonStatus();
318   }
319 
320   /**
321    * Create the actions for navigation.
322    */
323   private void createActions() {
324     // Only do this once
325     if (backAction != null)
326       return;
327     backAction = new Action(Messages.getString("GoBack.text")) {
328       public void run() {
329         goBack();
330       }
331     };
332     backAction.setToolTipText(Messages.getString("GoBack.toolTip")); //$NON-NLS-1$
333     UtilPluginImages.setLocalImageDescriptors(backAction, UtilPluginImages.IMG_BACKWARD);
334 
335     // Forward.
336     forwardAction = new Action(Messages.getString("GoForward.text")) {
337       public void run() {
338         goForward();
339       }
340     };
341     forwardAction.setToolTipText(Messages.getString("GoFoward.toolTip")); //$NON-NLS-1$
342     UtilPluginImages.setLocalImageDescriptors(forwardAction, UtilPluginImages.IMG_FORWARD);
343 
344     // Update the buttons when a selection change occurs.
345     updateButtonStatus();
346   }
347 
348   ////////////////////////////////////////////////////////////////////////
349 
350 }