blob: 2c4a1349162bd1302f43cfed4da9cdda701a957c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014, 2015 Florian Thienel and others.
* 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:
* Florian Thienel - initial API and implementation
*******************************************************************************/
package org.eclipse.vex.ui.boxview;
import static org.eclipse.vex.core.internal.core.TextUtils.ANY_LINE_BREAKS;
import static org.eclipse.vex.core.internal.core.TextUtils.CURRENCY_SIGN;
import static org.eclipse.vex.core.internal.core.TextUtils.PARAGRAPH_SIGN;
import static org.eclipse.vex.core.internal.core.TextUtils.RAQUO;
import static org.eclipse.vex.core.internal.io.UniversalTestDocument.ANCHOR;
import static org.eclipse.vex.core.internal.io.UniversalTestDocument.B;
import static org.eclipse.vex.core.internal.io.UniversalTestDocument.I;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.IViewSite;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.vex.core.internal.css.StyleSheet;
import org.eclipse.vex.core.internal.css.StyleSheetReader;
import org.eclipse.vex.core.internal.io.UniversalTestDocument;
import org.eclipse.vex.core.internal.visualization.CssBasedBoxModelBuilder;
import org.eclipse.vex.core.internal.widget.swt.BoxWidget;
import org.eclipse.vex.core.internal.widget.swt.IVexSelection;
import org.eclipse.vex.core.provisional.dom.ContentRange;
import org.eclipse.vex.core.provisional.dom.IContent;
import org.eclipse.vex.core.provisional.dom.IDocument;
import org.w3c.css.sac.CSSException;
import org.w3c.css.sac.InputSource;
/**
* This is a viewer for the new box model - just to do visual experiments.
*
* @author Florian Thienel
*/
public class BoxDemoView extends ViewPart {
private static final int CONTEXT_WINDOW = 5;
private static final int SAMPLE_COUNT = 25;
private static final IPath CSS_WORKSPACE_FILE = new Path("/test/box-demo.css");
private Composite boxWidgetParent;
private BoxWidget boxWidget;
private IDocument document;
private Label offsetLabel;
private Label contextLabel;
private final ISelectionChangedListener selectionChangedListener = new ISelectionChangedListener() {
@Override
public void selectionChanged(final SelectionChangedEvent event) {
updateInfoPanel(event.getSelection());
}
};
private final IResourceChangeListener resourceChangeListener = new IResourceChangeListener() {
@Override
public void resourceChanged(final IResourceChangeEvent event) {
try {
event.getDelta().accept(new IResourceDeltaVisitor() {
@Override
public boolean visit(final IResourceDelta delta) throws CoreException {
if (!delta.getFullPath().equals(CSS_WORKSPACE_FILE)) {
return true;
}
reloadStyleSheet();
return false;
}
});
} catch (final CoreException e) {
e.printStackTrace();
}
}
};
@Override
public void init(final IViewSite site) throws PartInitException {
super.init(site);
ResourcesPlugin.getWorkspace().addResourceChangeListener(resourceChangeListener, IResourceChangeEvent.POST_CHANGE);
}
@Override
public void createPartControl(final Composite parent) {
final Composite root = new Composite(parent, SWT.NONE);
root.setLayout(new GridLayout());
boxWidgetParent = new Composite(root, SWT.NONE);
boxWidgetParent.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, true));
boxWidgetParent.setLayout(new FillLayout());
final Composite infoPanel = new Composite(root, SWT.NONE);
infoPanel.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, false));
infoPanel.setLayout(new RowLayout());
new Label(infoPanel, SWT.NONE).setText("Caret Position:");
offsetLabel = new Label(infoPanel, SWT.LEFT);
offsetLabel.setLayoutData(new RowData(40, SWT.DEFAULT));
new Label(infoPanel, SWT.NONE).setText("Caret Context:");
contextLabel = new Label(infoPanel, SWT.LEFT);
contextLabel.setLayoutData(new RowData(SWT.DEFAULT, SWT.DEFAULT));
recreateBoxWidget();
}
@Override
public void dispose() {
super.dispose();
boxWidget = null;
ResourcesPlugin.getWorkspace().removeResourceChangeListener(resourceChangeListener);
}
@Override
public void setFocus() {
boxWidget.setFocus();
}
public void recreateBoxWidget() {
if (boxWidget != null) {
boxWidget.removeSelectionChangedListener(selectionChangedListener);
boxWidget.dispose();
boxWidget = null;
cleanStaleReferenceInShell();
}
boxWidget = new BoxWidget(boxWidgetParent, SWT.V_SCROLL);
document = UniversalTestDocument.createTestDocumentWithAllFeatures(SAMPLE_COUNT);
boxWidget.setDocument(document);
boxWidget.setBoxModelBuilder(new CssBasedBoxModelBuilder(readStyleSheet()));
boxWidgetParent.layout();
boxWidget.addSelectionChangedListener(selectionChangedListener);
updateInfoPanel(boxWidget.getSelection());
}
private void cleanStaleReferenceInShell() {
/*
* Shell keeps a reference to the boxWidget in Shell.savedFocus. parent.setFocus() forces Shell to store a
* reference to parent instead.
*/
boxWidgetParent.setFocus();
}
private void updateInfoPanel(final ISelection selection) {
final int caretPosition = caretPosition(selection);
offsetLabel.setText(Integer.toString(caretPosition));
contextLabel.setText(caretContext(caretPosition, document.getContent()));
contextLabel.getParent().layout();
}
private static int caretPosition(final ISelection selection) {
return ((IVexSelection) selection).getCaretOffset();
}
private static String caretContext(final int caretPosition, final IContent content) {
final ContentRange contextRange = ContentRange.window(caretPosition, CONTEXT_WINDOW).limitTo(content.getRange());
final String rawContext = content.getRawText(contextRange);
final int caretIndexInText = caretPosition - contextRange.getStartOffset();
final String caretContext = (rawContext.substring(0, caretIndexInText) + "|" + rawContext.substring(caretIndexInText))
.replaceAll(ANY_LINE_BREAKS.pattern(), Character.toString(PARAGRAPH_SIGN))
.replaceAll("\0", Character.toString(CURRENCY_SIGN))
.replaceAll("\t", Character.toString(RAQUO));
return caretContext;
}
private void reloadStyleSheet() {
boxWidget.setBoxModelBuilder(new CssBasedBoxModelBuilder(readStyleSheet()));
rebuildBoxModel();
}
private StyleSheet readStyleSheet() {
try {
final InputStream styleSheetInputStream;
final IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(CSS_WORKSPACE_FILE);
if (file.exists()) {
styleSheetInputStream = file.getContents();
} else {
styleSheetInputStream = getClass().getResourceAsStream("box-demo.css");
}
final InputSource inputSource = new InputSource(new InputStreamReader(styleSheetInputStream, "utf-8"));
return new StyleSheetReader().read(inputSource, null);
} catch (final UnsupportedEncodingException e) {
e.printStackTrace();
} catch (final CSSException e) {
e.printStackTrace();
} catch (final IOException e) {
e.printStackTrace();
} catch (final CoreException e) {
e.printStackTrace();
}
return null;
}
public void rebuildBoxModel() {
boxWidget.refresh();
}
public void insertBold() {
boxWidget.insertElement(B);
}
public void insertItalic() {
boxWidget.insertElement(I);
}
public void insertAnchor() {
boxWidget.insertElement(ANCHOR);
}
public void insertComment() {
boxWidget.insertComment();
}
public void insertProcessingInstruction(final String target) {
boxWidget.insertProcessingInstruction(target);
}
}