net.sf.eclipsensis.console.NSISConsoleViewer.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.eclipsensis.console.NSISConsoleViewer.java

Source

/*******************************************************************************
 * Copyright (c) 2004-2010 Sunil Kamath (IcemanK).
 * All rights reserved.
 * This program is made available under the terms of the Common Public License
 * v1.0 which is available at http://www.eclipse.org/legal/cpl-v10.html
 *
 * Contributors:
 *     Sunil Kamath (IcemanK) - initial API and implementation
 *
 * Based upon org.eclipse.ui.internal.console.IOConsoleViewer
 *
 *******************************************************************************/
package net.sf.eclipsensis.console;

import java.util.*;
import java.util.List;

import net.sf.eclipsensis.EclipseNSISPlugin;
import net.sf.eclipsensis.editor.NSISEditorUtilities;
import net.sf.eclipsensis.settings.INSISPreferenceConstants;

import org.eclipse.core.runtime.*;
import org.eclipse.jface.preference.JFacePreferences;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.*;
import org.eclipse.jface.text.source.*;
import org.eclipse.jface.util.*;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.*;
import org.eclipse.swt.widgets.*;
import org.eclipse.ui.console.*;
import org.eclipse.ui.internal.console.ConsoleHyperlinkPosition;
import org.eclipse.ui.progress.WorkbenchJob;
import org.eclipse.ui.texteditor.DefaultMarkerAnnotationAccess;

@SuppressWarnings("restriction")
public class NSISConsoleViewer extends TextConsoleViewer {
    private static final int REVEAL_SCHEDULE_DELAY = 50;
    private static final int VERTICAL_RULER_WIDTH = 12;
    private static final int VERTICAL_RULER_GAP = 1;

    private boolean mAutoScroll = true;
    private IDocumentListener mDocumentListener;
    private CompositeRuler mRuler;
    private Composite mComposite;
    private AnnotationModel mAnnotationModel;
    private Cursor mHitDetectionCursor;
    private WorkbenchJob mRevealJob;
    private IPropertyChangeListener mPropertyChangeListener = new IPropertyChangeListener() {
        public void propertyChange(PropertyChangeEvent event) {
            String property = event.getProperty();
            if (property.equals(INSISPreferenceConstants.CONSOLE_ERROR_COLOR)
                    || property.equals(INSISPreferenceConstants.CONSOLE_WARNING_COLOR)
                    || property.equals(INSISPreferenceConstants.CONSOLE_INFO_COLOR)) {
                StyledText textWidget = getTextWidget();
                if (textWidget != null) {
                    textWidget.redraw();
                }
            } else if (property.equals(INSISPreferenceConstants.CONSOLE_FONT)) {
                if (mRuler != null) {
                    mRuler.getControl().redraw();
                }
            }
        }
    };

    public NSISConsoleViewer(Composite parent, NSISConsole console) {
        super(parent, console);
        mAnnotationModel = console.getAnnotationModel();
        mRuler.setModel(mAnnotationModel);
        mAnnotationModel.connect(getDocument());
        StyledText text = getTextWidget();
        if (text != null) {
            text.setEditable(false);
        }
        JFaceResources.getColorRegistry().addListener(mPropertyChangeListener);
        JFaceResources.getFontRegistry().addListener(mPropertyChangeListener);
    }

    private WorkbenchJob getRevealJob() {
        if (mRevealJob == null) {
            mRevealJob = new WorkbenchJob(EclipseNSISPlugin.getResourceString("console.reveal.job.name")) { //$NON-NLS-1$
                @Override
                public IStatus runInUIThread(IProgressMonitor monitor) {
                    StyledText textWidget = getTextWidget();
                    if (textWidget != null) {
                        int lineCount = textWidget.getLineCount();
                        setTopIndex(lineCount - 1);
                    }
                    return Status.OK_STATUS;
                }
            };
            mRevealJob.setSystem(true);
        }
        return mRevealJob;
    }

    @Override
    protected void revealEndOfDocument() {
        getRevealJob().schedule(REVEAL_SCHEDULE_DELAY);
    }

    @Override
    protected void createControl(Composite parent, int styles) {
        mHitDetectionCursor = parent.getDisplay().getSystemCursor(SWT.CURSOR_HAND);
        mRuler = new CompositeRuler(VERTICAL_RULER_WIDTH);
        AnnotationRulerColumn rulerColumn = new AnnotationRulerColumn(VERTICAL_RULER_WIDTH,
                new DefaultMarkerAnnotationAccess()) {
            @Override
            protected void doPaint(GC gc) {
                try {
                    super.doPaint(gc);
                } catch (Exception ex) {
                }
            }

            @Override
            protected void doPaint1(GC gc) {
                try {
                    super.doPaint1(gc);
                } catch (Exception ex) {
                }
            }

        };
        rulerColumn.addAnnotationType(NSISConsoleAnnotation.TYPE);
        //rulerColumn
        mRuler.addDecorator(0, rulerColumn);
        int styles2 = (styles & ~SWT.BORDER);
        mComposite = new Canvas(parent, SWT.NONE);
        mComposite.setLayout(new RulerLayout(VERTICAL_RULER_GAP));
        Composite parent2 = mComposite;
        super.createControl(parent2, styles2);
        mRuler.createControl(mComposite, this);
        mRuler.getControl().setBackground(getTextWidget().getBackground());
        rulerColumn.getControl().setBackground(getTextWidget().getBackground());
        mRuler.getControl().addMouseListener(new MouseAdapter() {
            @Override
            public void mouseUp(MouseEvent e) {
                NSISConsoleAnnotation a = getAnnotation(e.y);
                if (a != null) {
                    NSISEditorUtilities.gotoConsoleLineProblem(a.getLine());
                }
            }
        });
        mRuler.getControl().addMouseMoveListener(new MouseMoveListener() {
            public void mouseMove(MouseEvent e) {
                if (hasAnnotation(e.y)) {
                    mRuler.getControl().setCursor(mHitDetectionCursor);
                } else {
                    mRuler.getControl().setCursor(null);
                }

            }
        });
    }

    private NSISConsoleAnnotation getAnnotation(int y) {
        try {
            IDocument document = getDocument();
            int lineNumber = mRuler.toDocumentLineNumber(y);
            IRegion info = document.getLineInformation(lineNumber);

            if (mAnnotationModel != null) {
                for (Iterator<?> iter = mAnnotationModel.getAnnotationIterator(); iter.hasNext();) {
                    Annotation a = (Annotation) iter.next();
                    Position p = mAnnotationModel.getPosition(a);
                    if (p != null && p.overlapsWith(info.getOffset(), info.getLength())) {
                        if (a instanceof NSISConsoleAnnotation) {
                            return (NSISConsoleAnnotation) a;
                        }
                    }
                }
            }
        } catch (Exception ex) {
        }
        return null;
    }

    private boolean hasAnnotation(int y) {
        return getAnnotation(y) != null;
    }

    public boolean isAutoScroll() {
        return mAutoScroll;
    }

    public void setAutoScroll(boolean scroll) {
        mAutoScroll = scroll;
    }

    @Override
    public Control getControl() {
        return mComposite;
    }

    @Override
    public void setDocument(IDocument document) {
        IDocument oldDocument = getDocument();

        if (oldDocument != null) {
            oldDocument.removeDocumentListener(getDocumentListener());
        }

        super.setDocument(document);

        if (oldDocument != null && mAnnotationModel != null) {
            mAnnotationModel.disconnect(oldDocument);
        }

        if (document != null) {
            document.addDocumentListener(getDocumentListener());
            if (mAnnotationModel != null) {
                mAnnotationModel.connect(document);
            }
            revealEndOfDocument();
        }
    }

    @Override
    protected void handleDispose() {
        JFaceResources.getColorRegistry().removeListener(mPropertyChangeListener);
        JFaceResources.getFontRegistry().removeListener(mPropertyChangeListener);
        if (getDocument() != null) {
            mAnnotationModel.disconnect(getDocument());
        }
        mAnnotationModel = null;
        mRuler = null;
        mComposite = null;
        super.handleDispose();
    }

    private IDocumentListener getDocumentListener() {
        if (mDocumentListener == null) {
            mDocumentListener = new IDocumentListener() {
                public void documentAboutToBeChanged(DocumentEvent event) {
                }

                public void documentChanged(DocumentEvent event) {
                    if (mAutoScroll) {
                        revealEndOfDocument();
                    }
                }
            };
        }
        return mDocumentListener;
    }

    //Need to replace the following 3 methods because I want my own link colors.
    @Override
    public void lineGetStyle(LineStyleEvent event) {
        IDocument document = getDocument();
        if (document != null && document.getLength() > 0) {
            List<StyleRange> ranges = new ArrayList<StyleRange>();
            int offset = event.lineOffset;
            int length = event.lineText.length();

            StyleRange[] partitionerStyles = ((IConsoleDocumentPartitioner) document.getDocumentPartitioner())
                    .getStyleRanges(event.lineOffset, event.lineText.length());
            if (partitionerStyles != null) {
                for (int i = 0; i < partitionerStyles.length; i++) {
                    ranges.add(partitionerStyles[i]);
                }
            }

            try {
                Position[] positions = getDocument().getPositions(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY);
                Position[] overlap = findPosition(offset, length, positions);
                if (overlap != null) {
                    Color color = JFaceResources.getColorRegistry().get(JFacePreferences.HYPERLINK_COLOR);
                    for (int i = 0; i < overlap.length; i++) {
                        Position position = overlap[i];
                        StyleRange linkRange = new StyleRange(position.offset, position.length, color, null);
                        linkRange.underline = true;
                        override(ranges, linkRange);
                    }
                }
            } catch (BadPositionCategoryException e) {
            }

            if (ranges.size() > 0) {
                event.styles = ranges.toArray(new StyleRange[ranges.size()]);
            }
        }
    }

    private void override(List<StyleRange> ranges, StyleRange newRange) {
        if (ranges.isEmpty()) {
            ranges.add(newRange);
            return;
        }

        List<StyleRange> newRanges = new ArrayList<StyleRange>();
        newRanges.add(newRange);
        for (ListIterator<StyleRange> iter = ranges.listIterator(); iter.hasNext();) {
            if (newRanges.size() > 0) {
                StyleRange existingRange = iter.next();
                for (ListIterator<StyleRange> iter2 = newRanges.listIterator(); iter2.hasNext();) {
                    StyleRange newRange2 = iter2.next();
                    int offset1 = existingRange.start;
                    int offset2 = newRange2.start;
                    int end1 = offset1 + existingRange.length - 1;
                    int end2 = offset2 + newRange2.length - 1;
                    if (offset1 >= 0 && end1 >= offset1 && offset2 >= 0 && end2 >= offset2) {
                        if (offset1 == offset2 && end1 == end2) {
                            newRange2.foreground = existingRange.foreground;
                            iter2.remove();
                            iter.set(newRange2);
                            continue;
                        }
                        if (offset1 < offset2) {
                            if (end1 >= offset2) {
                                existingRange.length = offset2 - offset1;
                                if (end1 >= end2) {
                                    iter2.remove();
                                    if (end1 > end2) {
                                        StyleRange range2 = (StyleRange) existingRange.clone();
                                        range2.start = end2 + 1;
                                        range2.length = end1 - end2;
                                        iter.add(range2);
                                    }
                                } else {
                                    StyleRange range2 = (StyleRange) newRange2.clone();
                                    range2.start = end1 + 1;
                                    range2.length = end2 - end1;
                                    iter2.set(range2);
                                    newRange2.length = end1 - offset2 + 1;
                                }
                                newRange2.foreground = existingRange.foreground;
                                iter.add(newRange2);
                            }
                        } else if (offset1 == offset2) {
                            if (end1 < end2) {
                                StyleRange range2 = (StyleRange) newRange2.clone();
                                newRange2.length = end1 - offset1 + 1;
                                newRange2.foreground = existingRange.foreground;
                                iter.set(newRange2);
                                range2.start = end1 + 1;
                                range2.length = end2 - end1;
                                iter2.set(range2);
                            } else {
                                iter2.remove();
                                newRange2.foreground = existingRange.foreground;
                                iter.set(newRange2);
                                existingRange.start = end2 + 1;
                                existingRange.length = end1 - end2;
                                iter.add(existingRange);
                            }
                        } else {
                            if (offset1 <= end2) {
                                newRange2.length = offset1 - offset2 + 1;
                                if (end2 < end1) {
                                    StyleRange range2 = (StyleRange) newRange2.clone();
                                    range2.start = offset1;
                                    range2.length = end2 - offset1 + 1;
                                    range2.foreground = existingRange.foreground;
                                    iter.add(range2);
                                    existingRange.start = end2 + 1;
                                    existingRange.length = end1 - end2;
                                } else {
                                    StyleRange range2 = (StyleRange) newRange2.clone();
                                    range2.start = offset1;
                                    range2.length = end1 - offset1 + 1;
                                    range2.foreground = existingRange.foreground;
                                    iter.set(range2);
                                    if (end2 > end1) {
                                        range2 = (StyleRange) newRange2.clone();
                                        range2.start = end1 + 1;
                                        range2.length = end2 - end1;
                                        iter2.add(range2);
                                    }
                                }
                            }
                        }

                    }
                }
            } else {
                break;
            }
        }
        ranges.addAll(newRanges);
    }

    private Position[] findPosition(int offset, int length, Position[] positions) {
        if (positions.length == 0) {
            return null;
        }

        int rangeEnd = offset + length;
        int left = 0;
        int right = positions.length - 1;
        int mid = 0;
        Position position = null;

        while (left < right) {

            mid = (left + right) / 2;

            position = positions[mid];
            if (rangeEnd < position.getOffset()) {
                if (left == mid) {
                    right = left;
                } else {
                    right = mid - 1;
                }
            } else if (offset > (position.getOffset() + position.getLength() - 1)) {
                if (right == mid) {
                    left = right;
                } else {
                    left = mid + 1;
                }
            } else {
                left = right = mid;
            }
        }

        List<Position> list = new ArrayList<Position>();
        int index = left - 1;
        if (index >= 0) {
            position = positions[index];
            while (index >= 0 && (position.getOffset() + position.getLength()) > offset) {
                index--;
                if (index > 0) {
                    position = positions[index];
                }
            }
        }
        index++;
        position = positions[index];
        while (index < positions.length && (position.getOffset() < rangeEnd)) {
            list.add(position);
            index++;
            if (index < positions.length) {
                position = positions[index];
            }
        }

        if (list.isEmpty()) {
            return null;
        }
        return list.toArray(new Position[list.size()]);
    }

    protected class RulerLayout extends Layout {
        protected int mGap;

        public RulerLayout(int gap) {
            mGap = gap;
        }

        @Override
        protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
            Control[] children = composite.getChildren();
            Point s = children[children.length - 1].computeSize(SWT.DEFAULT, SWT.DEFAULT, flushCache);
            if (mRuler != null) {
                s.x += mRuler.getWidth() + mGap;
            }
            return s;
        }

        @Override
        protected void layout(Composite composite, boolean flushCache) {
            Rectangle clArea = composite.getClientArea();
            Rectangle trim = getTextWidget().computeTrim(0, 0, 0, 0);
            int topTrim = -trim.y;
            int scrollbarHeight = trim.height - topTrim; // scrollbar is only under the client area

            int x = clArea.x;
            int width = clArea.width;

            if (mRuler != null) {
                int verticalRulerWidth = mRuler.getWidth();
                mRuler.getControl().setBounds(clArea.x, clArea.y + topTrim, verticalRulerWidth,
                        clArea.height - scrollbarHeight - topTrim);

                x += verticalRulerWidth + mGap;
                width -= verticalRulerWidth + mGap;
            }

            getTextWidget().setBounds(x, clArea.y, width, clArea.height);
        }
    }
}