package nl.siegmann.epublib.browsersupport; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import nl.siegmann.epublib.domain.Book; import nl.siegmann.epublib.domain.Resource; /** * A helper class for epub browser applications. * * It helps moving from one resource to the other, from one resource to the other and keeping other * elements of the application up-to-date by calling the NavigationEventListeners. * * @author paul * */ public class Navigator implements Serializable { /** * */ private static final long serialVersionUID = 1076126986424925474L; private Book book; private int currentSpinePos; private Resource currentResource; private int currentPagePos; private String currentFragmentId; private List<NavigationEventListener> eventListeners = new ArrayList<>(); public Navigator() { this(null); } public Navigator(Book book) { this.book = book; this.currentSpinePos = 0; if (book != null) { this.currentResource = book.getCoverPage(); } this.currentPagePos = 0; } private synchronized void handleEventListeners(NavigationEvent navigationEvent) { for (int i = 0; i < eventListeners.size(); i++) { NavigationEventListener navigationEventListener = eventListeners.get(i); navigationEventListener.navigationPerformed(navigationEvent); } } public boolean addNavigationEventListener(NavigationEventListener navigationEventListener) { return this.eventListeners.add(navigationEventListener); } public boolean removeNavigationEventListener(NavigationEventListener navigationEventListener) { return this.eventListeners.remove(navigationEventListener); } public int gotoFirstSpineSection(Object source) { return gotoSpineSection(0, source); } public int gotoPreviousSpineSection(Object source) { return gotoPreviousSpineSection(0, source); } public int gotoPreviousSpineSection(int pagePos, Object source) { if (currentSpinePos < 0) { return gotoSpineSection(0, pagePos, source); } else { return gotoSpineSection(currentSpinePos - 1, pagePos, source); } } public boolean hasNextSpineSection() { return (currentSpinePos < (book.getSpine().size() - 1)); } public boolean hasPreviousSpineSection() { return (currentSpinePos > 0); } public int gotoNextSpineSection(Object source) { if (currentSpinePos < 0) { return gotoSpineSection(0, source); } else { return gotoSpineSection(currentSpinePos + 1, source); } } public int gotoResource(String resourceHref, Object source) { Resource resource = book.getResources().getByHref(resourceHref); return gotoResource(resource, source); } public int gotoResource(Resource resource, Object source) { return gotoResource(resource, 0, null, source); } public int gotoResource(Resource resource, String fragmentId, Object source) { return gotoResource(resource, 0, fragmentId, source); } public int gotoResource(Resource resource, int pagePos, Object source) { return gotoResource(resource, pagePos, null, source); } public int gotoResource(Resource resource, int pagePos, String fragmentId, Object source) { if (resource == null) { return -1; } NavigationEvent navigationEvent = new NavigationEvent(source, this); this.currentResource = resource; this.currentSpinePos = book.getSpine().getResourceIndex(currentResource); this.currentPagePos = pagePos; this.currentFragmentId = fragmentId; handleEventListeners(navigationEvent); return currentSpinePos; } public int gotoResourceId(String resourceId, Object source) { return gotoSpineSection(book.getSpine().findFirstResourceById(resourceId), source); } public int gotoSpineSection(int newSpinePos, Object source) { return gotoSpineSection(newSpinePos, 0, source); } /** * Go to a specific section. * Illegal spine positions are silently ignored. * * @param newSpinePos * @param source * @return */ public int gotoSpineSection(int newSpinePos, int newPagePos, Object source) { if (newSpinePos == currentSpinePos) { return currentSpinePos; } if (newSpinePos < 0 || newSpinePos >= book.getSpine().size()) { return currentSpinePos; } NavigationEvent navigationEvent = new NavigationEvent(source, this); currentSpinePos = newSpinePos; currentPagePos = newPagePos; currentResource = book.getSpine().getResource(currentSpinePos); handleEventListeners(navigationEvent); return currentSpinePos; } public int gotoLastSpineSection(Object source) { return gotoSpineSection(book.getSpine().size() - 1, source); } public void gotoBook(Book book, Object source) { NavigationEvent navigationEvent = new NavigationEvent(source, this); this.book = book; this.currentFragmentId = null; this.currentPagePos = 0; this.currentResource = null; this.currentSpinePos = book.getSpine().getResourceIndex(currentResource); handleEventListeners(navigationEvent); } /** * The current position within the spine. * * @return something < 0 if the current position is not within the spine. */ public int getCurrentSpinePos() { return currentSpinePos; } public Resource getCurrentResource() { return currentResource; } /** * Sets the current index and resource without calling the eventlisteners. * * If you want the eventListeners called use gotoSection(index); * * @param currentIndex */ public void setCurrentSpinePos(int currentIndex) { this.currentSpinePos = currentIndex; this.currentResource = book.getSpine().getResource(currentIndex); } public Book getBook() { return book; } /** * Sets the current index and resource without calling the eventlisteners. * * If you want the eventListeners called use gotoSection(index); * * @param currentSpinePos */ public int setCurrentResource(Resource currentResource) { this.currentSpinePos = book.getSpine().getResourceIndex(currentResource); this.currentResource = currentResource; return currentSpinePos; } public String getCurrentFragmentId() { return currentFragmentId; } public int getCurrentSectionPos() { return currentPagePos; } }