Package winterwell.markdown.editors

Source Code of winterwell.markdown.editors.MarkdownContentOutlinePage$ContentProvider

/**
* Copyright winterwell Mathematics Ltd.
* @author Daniel Winterstein
* 11 Jan 2007
*/
package winterwell.markdown.editors;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.action.IToolBarManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentEvent;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentListener;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.KeyListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.views.contentoutline.ContentOutlinePage;

import winterwell.markdown.pagemodel.MarkdownPage;
import winterwell.markdown.pagemodel.MarkdownPage.Header;
import winterwell.markdown.pagemodel.MarkdownPage.KLineType;
import winterwell.utils.StrUtils;
import winterwell.utils.Utils;
import winterwell.utils.web.WebUtils;

/**
*
*
* @author Daniel Winterstein
*/
public final class MarkdownContentOutlinePage extends ContentOutlinePage {

  /**
   *
   *
   * @author Daniel Winterstein
   */
  public final class ContentProvider implements ITreeContentProvider,
      IDocumentListener {

    // protected final static String SEGMENTS= "__md_segments";
    // //$NON-NLS-1$
    // protected IPositionUpdater fPositionUpdater= new
    // DefaultPositionUpdater(SEGMENTS);
    private MarkdownPage fContent;
    // protected List fContent= new ArrayList(10);
    private MarkdownEditor fTextEditor;

    private void parse() {
      fContent = fTextEditor.getMarkdownPage();
    }

    /*
     * @see IContentProvider#inputChanged(Viewer, Object, Object)
     */
    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
      // Detach from old
      if (oldInput != null) {
        IDocument document = fDocumentProvider.getDocument(oldInput);
        if (document != null) {
          document.removeDocumentListener(this);
        }
      }
      fContent = null;
      // Attach to new
      if (newInput == null)
        return;
      IDocument document = fDocumentProvider.getDocument(newInput);
      if (document == null)
        return;
      fTextEditor = MarkdownEditor.getEditor(document);
      document.addDocumentListener(this);
      parse();
    }

    /*
     * @see IContentProvider#dispose
     */
    public void dispose() {
      fContent = null;
    }

    /*
     * @see IContentProvider#isDeleted(Object)
     */
    public boolean isDeleted(Object element) {
      return false;
    }

    /*
     * @see IStructuredContentProvider#getElements(Object)
     */
    public Object[] getElements(Object element) {
      return fContent.getHeadings(null).toArray();
    }

    /*
     * @see ITreeContentProvider#hasChildren(Object)
     */
    public boolean hasChildren(Object element) {
      if (element == fInput) {
        return true;
      }
      if (element instanceof MarkdownPage.Header) {
        MarkdownPage.Header header = (MarkdownPage.Header) element;
        return header.getSubHeaders().size() > 0;
      }
      ;
      return false;
    }

    /*
     * @see ITreeContentProvider#getParent(Object)
     */
    public Object getParent(Object element) {
      if (!(element instanceof MarkdownPage.Header))
        return null;
      return ((MarkdownPage.Header) element).getParent();
    }

    /*
     * @see ITreeContentProvider#getChildren(Object)
     */
    public Object[] getChildren(Object element) {
      if (element == fInput) {
        return fContent.getHeadings(null).toArray();
      }
      if (!(element instanceof MarkdownPage.Header))
        return null;
      return ((MarkdownPage.Header) element).getSubHeaders().toArray();
    }

    public void documentAboutToBeChanged(DocumentEvent event) {
      // nothing
    }

    public void documentChanged(DocumentEvent event) {
      parse();
      update();
    }
  }

  private Object fInput = null;
  private final IDocumentProvider fDocumentProvider;
  private final MarkdownEditor fTextEditor;
  protected boolean showWordCounts;
  private List<Header> selectedHeaders;

  /**
   * @param documentProvider
   * @param mdEditor
   */
  public MarkdownContentOutlinePage(IDocumentProvider documentProvider,
      MarkdownEditor mdEditor) {
    fDocumentProvider = documentProvider;
    fTextEditor = mdEditor;
  }

  /*
   * (non-Javadoc) Method declared on ContentOutlinePage
   */
  @Override
  public void createControl(Composite parent) {
    super.createControl(parent);
    TreeViewer viewer = getTreeViewer();
    viewer.setContentProvider(new ContentProvider());
    // Add word count annotations
    viewer.setLabelProvider(new LabelProvider() {
      @Override
      public String getText(Object element) {
        if (!(element instanceof MarkdownPage.Header))
          return super.getText(element);
        Header header = ((MarkdownPage.Header) element);
        String hText = header.toString();
        if (!showWordCounts)
          return hText;
        IRegion region = getRegion(header);
        String text;
        try {
          text = fTextEditor.getDocument().get(region.getOffset(),
              region.getLength());
          text = WebUtils.stripTags(text);
          text = text.replaceAll("#", "").trim();
          assert text.startsWith(hText);
          text = text.substring(hText.length());
          int wc = StrUtils.wordCount(text);
          return hText + " (" + wc + ":" + text.length() + ")";
        } catch (BadLocationException e) {
          return hText;
        }
      }
    });
    viewer.addSelectionChangedListener(this);

    if (fInput != null)
      viewer.setInput(fInput);

    // Buttons
    IPageSite site = getSite();
    IActionBars bars = site.getActionBars();
    IToolBarManager toolbar = bars.getToolBarManager();
    // Word count action
    Action action = new Action("123", IAction.AS_CHECK_BOX) {
      @Override
      public void run() {
        showWordCounts = isChecked();
        update();
      }
    };
    action.setToolTipText("Show/hide section word:character counts");
    toolbar.add(action);
    // +/- actions
    action = new Action("<") {
      @Override
      public void run() {
        doPromoteDemote(-1);
      }
    };
    action.setToolTipText("Promote the selected section\n -- move it up a level.");
    toolbar.add(action);
    //
    action = new Action(">") {
      @Override
      public void run() {
        doPromoteDemote(1);
      }
    };
    action.setToolTipText("Demote the selected section\n -- move it down a level.");
    toolbar.add(action);
    // up/down actions
    action = new Action("/\\") {
      @Override
      public void run() {
        try {
          doMove(-1);
        } catch (BadLocationException e) {
          throw Utils.runtime(e);
        }
      }
    };
    action.setToolTipText("Move the selected section earlier");
    toolbar.add(action);
    //
    action = new Action("\\/") {
      @Override
      public void run() {
        try {
          doMove(1);
        } catch (BadLocationException e) {
          throw Utils.runtime(e);
        }
      }
    };
    action.setToolTipText("Move the selected section later");
    toolbar.add(action);
    // Collapse
    ImageDescriptor id = ImageDescriptor.createFromFile(getClass(), "collapseall.gif");
    action = new Action("collapse", id) {
      @Override
      public void run() {
        doCollapseAll();
      }
    };
    action.setImageDescriptor(id);
    action.setToolTipText("Collapse outline tree");
    toolbar.add(action);
    // Sync
    id = ImageDescriptor.createFromFile(getClass(), "synced.gif");
    action = new Action("sync") {
      @Override
      public void run() {
        try {
          doSyncToEditor();
        } catch (BadLocationException e) {
          throw Utils.runtime(e);
        }
      }
    };
    action.setImageDescriptor(id);
    action.setToolTipText("Link with editor");
    toolbar.add(action);
    // Add edit ability
    viewer.getControl().addKeyListener(new KeyListener() {
      public void keyPressed(KeyEvent e) {
        if (e.keyCode==SWT.F2) {
          doEditHeader();
        }
      }
      public void keyReleased(KeyEvent e) {
        //
      }
    });
  }

  /**
   * @throws BadLocationException
   *
   */
  protected void doSyncToEditor() throws BadLocationException {
    TreeViewer viewer = getTreeViewer();
    if (viewer == null) return;
    // Get header
    MarkdownPage page = fTextEditor.getMarkdownPage();
    int caretOffset = fTextEditor.getViewer().getTextWidget().getCaretOffset();
    IDocument doc = fTextEditor.getDocument();
    int line = doc.getLineOfOffset(caretOffset);
    List<KLineType> lineTypes = page.getLineTypes();
    for(; line>-1; line--) {
      KLineType lt = lineTypes.get(line);
      if (lt.toString().startsWith("H")) break;
    }
    if (line<0) return;
    Header header = (Header) page.getPageObject(line);
    // Set
    IStructuredSelection selection = new StructuredSelection(header);
    viewer.setSelection(selection , true);
  }

  void doEditHeader() {
    TreeViewer viewer = getTreeViewer();
    viewer.editElement(selectedHeaders.get(0), 0);
  }

  protected void doCollapseAll() {
    TreeViewer viewer = getTreeViewer();
    if (viewer == null) return;
//    Control control = viewer.getControl();
//    if (control != null && !control.isDisposed()) {
//      control.setRedraw(false);
    viewer.collapseAll();
//      control.setRedraw(true);
//    }
  }

  /**
   * Move the selected sections up/down
   * @param i 1 or -1. 1==move later, -1=earlier
   * @throws BadLocationException
   */
  protected void doMove(int i) throws BadLocationException {
    assert i==1 || i==-1;
    if (selectedHeaders == null || selectedHeaders.size() == 0)
      return;
    // Get text region to move
    MarkdownPage.Header first = selectedHeaders.get(0);
    MarkdownPage.Header last = selectedHeaders.get(selectedHeaders.size()-1);
    int start = fTextEditor.getDocument().getLineOffset(
        first.getLineNumber());
    IRegion r = getRegion(last);
    int end = r.getOffset() + r.getLength();
    int length = end - start;
    // Get new insertion point
    int insert;
    if (i==1) {
      Header nextSection = last.getNext();
      if (nextSection==null) return;
      IRegion nr = getRegion(nextSection);
      insert = nr.getOffset()+nr.getLength();
    } else {
      Header prevSection = first.getPrevious();
      if (prevSection==null) return;
      IRegion nr = getRegion(prevSection);
      insert = nr.getOffset();
    }
    // Get text
    String text = fTextEditor.getDocument().get();
    // Move text
    String section = text.substring(start, end);
    String pre, post;
    if (i==1) {
      pre = text.substring(0, start) + text.substring(end, insert);
      post = text.substring(insert);
    } else {
      pre = text.substring(0, insert);
      post = text.substring(insert,start)+text.substring(end);
    }
    text =  pre + section + post;
    assert text.length() == fTextEditor.getDocument().get().length() :
      text.length()-fTextEditor.getDocument().get().length()+" chars gained/lost";
    // Update doc
    fTextEditor.getDocument().set(text);
  }

  /**
   * Does not support -------- / ========= underlining, only # headers
   * @param upDown 1 for demote (e.g. h2 -> h3), -1 for promote (e.g. h2 -> h1)
   */
  protected void doPromoteDemote(int upDown) {
    assert upDown==1 || upDown==-1;
    if (selectedHeaders == null || selectedHeaders.size() == 0)
      return;
    HashSet<Header> toAdjust = new HashSet<Header>(selectedHeaders);
    HashSet<Header> adjusted = new HashSet<Header>();
    // Adjust
    MarkdownPage mdPage = fTextEditor.getMarkdownPage();
    List<String> lines = new ArrayList<String>(mdPage.getText());
    while(toAdjust.size() != 0) {
      Header h = toAdjust.iterator().next();
      toAdjust.remove(h);
      adjusted.add(h);
      String line = lines.get(h.getLineNumber());
      if (upDown==-1) {
        if (h.getLevel() == 1) return; // Level 1; can't promote
        if (line.startsWith("##")) line = line.substring(1);
        else {
          return; // TODO support for ------ / ========
        }
      } else line = "#" + line;
      int ln = h.getLineNumber();
      lines.set(ln, line);
      // kids
      ArrayList<Header> kids = new ArrayList<Header>(h.getSubHeaders());
      for (Header header : kids) {
        if ( ! adjusted.contains(header)) toAdjust.add(header);
      }
    }
    // Set
    StringBuilder sb = new StringBuilder();
    for (String line : lines) {
      sb.append(line);
    }
    fTextEditor.getDocument().set(sb.toString());
  }

  /**
   * The region of text for this header. This includes the header itself.
   * @param header
   * @return
   * @throws BadLocationException
   */
  protected IRegion getRegion(Header header) {
    try {
      IDocument doc = fTextEditor.getDocument();
      // Line numbers
      int start = header.getLineNumber();
      Header next = header.getNext();
      int end;
      if (next != null) {
        end = next.getLineNumber() - 1;
      } else {
        end = doc.getNumberOfLines() - 1;
      }
      int offset = doc.getLineOffset(start);
      IRegion ei = doc.getLineInformation(end);
      int length = ei.getOffset() + ei.getLength() - offset;
      return new Region(offset, length);
    } catch (BadLocationException ex) {
      throw Utils.runtime(ex);
    }
  }

  /*
   * (non-Javadoc) Method declared on ContentOutlinePage
   */
  @Override
  public void selectionChanged(SelectionChangedEvent event) {
    super.selectionChanged(event);
    selectedHeaders = null;
    ISelection selection = event.getSelection();
    if (selection.isEmpty())
      return;
    if (!(selection instanceof IStructuredSelection))
      return;
    try {
      IStructuredSelection strucSel = (IStructuredSelection) selection;
      Object[] sections = strucSel.toArray();
      selectedHeaders = (List) Arrays.asList(sections);
      MarkdownPage.Header first = (Header) sections[0];
      MarkdownPage.Header last = (Header) sections[sections.length - 1];
      int start = fTextEditor.getDocument().getLineOffset(
          first.getLineNumber());
      int length;
      if (first == last) {
        length = fTextEditor.getDocument().getLineLength(
            first.getLineNumber());
      } else {
        IRegion r = getRegion(last);
        int end = r.getOffset() + r.getLength();
        length = end - start;
      }
      fTextEditor.setHighlightRange(start, length, true);
    } catch (Exception x) {
      System.out.println(x.getStackTrace());
      fTextEditor.resetHighlightRange();
    }
  }

  /**
   * Sets the input of the outline page
   *
   * @param input
   *            the input of this outline page
   */
  public void setInput(Object input) {
    fInput = input;
    update();
  }

  /**
   * Updates the outline page.
   */
  public void update() {
    TreeViewer viewer = getTreeViewer();

    if (viewer != null) {
      Control control = viewer.getControl();
      if (control != null && !control.isDisposed()) {
        control.setRedraw(false);
        viewer.setInput(fInput);
        viewer.expandAll();
        control.setRedraw(true);
      }
    }
  }

}
TOP

Related Classes of winterwell.markdown.editors.MarkdownContentOutlinePage$ContentProvider

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.