content: /**
   *  Copyright (c) 2013-2016 Angelo ZERR.
   *  All rights reserved. This program and the accompanying materials
   *  are made available under the terms of the Eclipse Public License v1.0
   *  which accompanies this distribution, and is available at
   *  http://www.eclipse.org/legal/epl-v10.html
   *
   *  Contributors:
   *  Angelo Zerr <angelo.zerr@gmail.com> - initial API and implementation
   */
  package tern.eclipse.ide.ui.views;
  
  import org.eclipse.core.resources.IFile;
  import org.eclipse.core.resources.IProject;
  import org.eclipse.jface.text.IDocument;
  import org.eclipse.ui.IEditorPart;
  import org.eclipse.ui.IMemento;
  import org.eclipse.ui.IViewSite;
  import org.eclipse.ui.IWorkbenchPart;
  import org.eclipse.ui.PartInitException;
  import org.eclipse.ui.navigator.CommonViewer;
  import org.eclipse.ui.part.IPage;
  import org.eclipse.ui.part.IPageBookViewPage;
  import org.eclipse.ui.texteditor.ITextEditor;
  import org.eclipse.ui.views.contentoutline.ContentOutline;
  import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
  
  import tern.eclipse.ide.ui.utils.EditorUtils;
  import tern.server.protocol.outline.IJSNode;
  import tern.server.protocol.outline.TernOutlineCollector;
  
  /**
   * Abstract class for tern outline view.
   *
   */
  public abstract class AbstractTernOutlineView extends ContentOutline {
  
  	public static final int IS_LINKING_ENABLED_PROPERTY = 0;
  
  	private ITextEditor textEditor;
  
  	protected IMemento memento;
  
  	private String LINKING_ENABLED = "AbstractTernOutlineView.LINKING_ENABLED"; //$NON-NLS-1$
  	private boolean linkingEnabled = false;
  
  	boolean ignoreEditorActivation;
  	boolean ignoreSelectionChanged;
  
  	// ----------- Jobs
  
  	// Job which is executed when the treeview of the tern outline is clicked to
  	// select the well content of the active JavaScript Editor.
  	private ActivateEditorJob activateEditorJob = new ActivateEditorJob(this);
  	// Job which is executed when there is a selection inside JavaScript Editor
  	// to select the well node in the tern outline.
  	private final UpdateSelectionJob updateSelectionJob = new UpdateSelectionJob(this);
  	private final RefreshOutlineJob refreshJob = new RefreshOutlineJob(this);
  
  	@Override
  	protected PageRec doCreatePage(IWorkbenchPart part) {
  		IFile file = getFile(part);
  		if (file == null) {
  			// The opened file in the given editor cannot be displayed in the
  			// outline
  			return null;
  		}
  
  		// Try to get the shared outline for the given project (ex : for angular
  		// explorer)
  		IProject project = file.getProject();
  		IContentOutlinePage page = getOutlinePage(project);
  		if (page == null) {
  			// Create a new instance of the outline page
  			page = createOutlinePage(file.getProject());
  			if (page != null) {
  				if (page instanceof IPageBookViewPage) {
  					initPage((IPageBookViewPage) page);
  				}
  				page.createControl(getPageBook());
  				if (page instanceof AbstractTernContentOutlinePage) {
  					updateCurrentFile(page, part, file);
  				}
  			}
  		} else {
  			if (page instanceof AbstractTernContentOutlinePage) {
  				updateCurrentFile(page, part, file);
  			}
  		}
  
  		if (page != null) {
  			return new PageRec(part, page);
  		}
  
  		// There is no content outline
  		return null;
  	}
  
  	@Override
  	protected void showPageRec(PageRec pageRec) {
  		IPage page = pageRec.page;
  		IWorkbenchPart part = pageRec.part;
  		if (page instanceof AbstractTernContentOutlinePage) {
  			IFile file = getFile(part);
  			if (file != null) {
  				updateCurrentFile(page, part, file);
  			}
  		}
  		super.showPageRec(pageRec);
  	}
  
  	/**
  	 * Returns the shared outline page for the given project or null if a page
  	 * must be created for each file.
  	 * 
  	 * @param project
  	 * @return the shared outline page for the given project or null if a page
  	 *         must be created for each file.
  	 */
  	protected IContentOutlinePage getOutlinePage(IProject project) {
  		return null;
  	}
  
  	protected IFile getFile(IWorkbenchPart part) {
  		if (part != null && part instanceof IEditorPart) {
  			IFile file = EditorUtils.getFile((IEditorPart) part);
  			if (file != null && isAdaptFor(file)) {
  				return file;
  			}
  		}
  		return null;
  	}
  
  	@Override
  	public void init(IViewSite aSite, IMemento aMemento) throws PartInitException {
  		super.init(aSite, aMemento);
  		memento = aMemento;
  		if (memento != null) {
  			Integer linkingEnabledInteger = memento.getInteger(LINKING_ENABLED);
  			setLinkingEnabled(((linkingEnabledInteger != null) ? linkingEnabledInteger.intValue() == 1 : false));
  		}
  
  	}
  
  	@Override
  	public void saveState(IMemento aMemento) {
  		aMemento.putInteger(LINKING_ENABLED, (linkingEnabled) ? 1 : 0);
  		super.saveState(aMemento);
  	}
  
  	public boolean isLinkingEnabled() {
  		return linkingEnabled;
  	}
  
  	public void setLinkingEnabled(boolean linkingEnabled) {
  		this.linkingEnabled = linkingEnabled;
  	}
  
  	private void updateCurrentFile(IPage page, IWorkbenchPart part, IFile file) {
  		if (isEditorChanged(part)) {
  			((AbstractTernContentOutlinePage) page).setCurrentFile(file);
  			this.setCurrentPart(part);
  		}
  	}
  
  	protected boolean isEditorChanged(IWorkbenchPart part) {
  		return this.textEditor != part;
  	}
  
  	private void setCurrentPart(IWorkbenchPart part) {
  		if (part instanceof ITextEditor) {
  			this.textEditor = (ITextEditor) part;
  		} else {
  			this.textEditor = null;
  		}
  		updateSelectionJob.setCurrentPart(part);
  	}
  
  	public void openInEditor(IJSNode node, boolean force) {
  		activateEditorJob.openInEditor(node, force);
  	}
  
  	/**
  	 * Returns the current tern outline page and null otherwise.
  	 * 
  	 * @return the current tern outline page and null otherwise.
  	 */
  	AbstractTernContentOutlinePage getCurrentTernPage() {
  		IPage p = getCurrentPage();
  		if (p == null || !(p instanceof AbstractTernContentOutlinePage)) {
  			return null;
  		}
  		AbstractTernContentOutlinePage page = (AbstractTernContentOutlinePage) p;
  		return page;
  	}
  
  	/**
  	 * Returns the viewer of the current tern outline page and null otherwise.
  	 * 
  	 * @return the viewer of the current tern outline page and null otherwise.
  	 */
  	CommonViewer getCurrentViewer() {
  		AbstractTernContentOutlinePage page = getCurrentTernPage();
  		return page != null ? page.getViewer() : null;
  	}
  
  	@Override
  	public void dispose() {
  		super.dispose();
  		activateEditorJob.cancel();
  		updateSelectionJob.dispose();
  		refreshJob.cancel();
  	}
  
  	/**
  	 * Refresh the outline tree in a job.
  	 */
  	public void refreshOutline() {
  		refreshJob.refreshOutline();
  	}
  
  	/**
  	 * Returns true if the outline view is adapted for the given file and false
  	 * otherwise.
  	 * 
  	 * @param file
  	 * @return true if the outline view is adapted for the given file and false
  	 *         otherwise.
  	 */
  	protected abstract boolean isAdaptFor(IFile file);
  
  	/**
  	 * Create the outline view for the given project. It can have one page per
  	 * file (see tern outline) or one page per project (see angular outline).
  	 * 
  	 * @param project
  	 * @return an instance of outline page for the given project.
  	 */
  	protected abstract IContentOutlinePage createOutlinePage(IProject project);
  
  	public abstract TernOutlineCollector loadOutline(IFile file, IDocument document) throws Exception;
  
  	public abstract boolean isOutlineAvailable(IFile file);
  
  }
  