blob: 56ff2e84a07bf0f0a99fd71b0f5ecfe0fc45d7f6 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* OutputEditorKit.java
*
* Created on May 9, 2004, 4:34 PM
*/
package org.netbeans.core.output2;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
import javax.swing.JEditorPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.Document;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.TextAction;
import org.openide.util.Exceptions;
/**
* A simple editor kit which provides instances of ExtPlainView/ExtWrappedPlainView as its views.
*
* @author Tim Boudreau
*/
final class OutputEditorKit extends DefaultEditorKit implements javax.swing.text.ViewFactory, ChangeListener {
private final boolean wrapped;
private final JTextComponent comp;
private static final Action[] actions = prepareActions();
private final PropertyChangeListener propertyChangeListener;
/** Creates a new instance of OutputEditorKit */
OutputEditorKit(boolean wrapped, JTextComponent comp,
PropertyChangeListener propertyChangeListener) {
this.comp = comp;
this.wrapped = wrapped;
this.propertyChangeListener = propertyChangeListener;
}
@Override
public Action[] getActions() {
return actions;
}
public WrappedTextView view() {
return lastWrappedView;
}
private WrappedTextView lastWrappedView = null;
public javax.swing.text.View create(Element element) {
javax.swing.text.View result = wrapped
? new WrappedTextView(element, comp, propertyChangeListener)
: new ExtPlainView(element);
lastWrappedView = wrapped ? (WrappedTextView) result : null;
return result;
}
@Override
public javax.swing.text.ViewFactory getViewFactory() {
return this;
}
public boolean isWrapped() {
return wrapped;
}
@Override
public void install(JEditorPane c) {
super.install(c);
if (wrapped) {
c.getCaret().addChangeListener(this);
}
}
@Override
public void deinstall(JEditorPane c) {
super.deinstall(c);
if (wrapped) {
c.getCaret().removeChangeListener(this);
}
}
private int lastMark = -1;
private int lastDot = -1;
private static final Rectangle scratch = new Rectangle();
/**
* Manages repainting when the selection changes
*/
public void stateChanged (ChangeEvent ce) {
int mark = comp.getSelectionStart();
int dot = comp.getSelectionEnd();
boolean hasSelection = mark != dot;
boolean hadSelection = lastMark != lastDot;
// System.err.println("Change: " + mark + " : " + dot + "/" + lastMark + ":" + lastDot + " hadSelection " + hadSelection + " hasSelection " + hasSelection);
if (lastMark != mark || lastDot != dot) {
int begin = Math.min (mark, dot);
int end = Math.max (mark, dot);
int oldBegin = Math.min (lastMark, lastDot);
int oldEnd = Math.max (lastMark, lastDot);
if (hadSelection && hasSelection) {
if (begin != oldBegin) {
int startChar = Math.min (begin, oldBegin);
int endChar = Math.max (begin, oldBegin);
repaintRange (startChar, endChar);
} else {
int startChar = Math.min (end, oldEnd);
int endChar = Math.max (end, oldEnd);
repaintRange (startChar, endChar);
}
} else if (hadSelection && !hasSelection) {
repaintRange (oldBegin, oldEnd);
}
}
lastMark = mark;
lastDot = dot;
}
private void repaintRange (int start, int end) {
try {
Rectangle r = (Rectangle) view().modelToView(end, scratch, Position.Bias.Forward);
int y1 = r.y + r.height;
r = (Rectangle) view().modelToView(start, scratch, Position.Bias.Forward);
r.x = 0;
r.width = comp.getWidth();
r.height = y1 - r.y;
// System.err.println("RepaintRange " + start + " to " + end + ": " + r);
comp.repaint (r);
} catch (BadLocationException e) {
comp.repaint();
Exceptions.printStackTrace(e);
}
}
/*
* Replaces DefaultEditorKit actions which uses Utilities.getRowEnd()/getRowStart()
* which are very expensive for long lines in unwrapped mode
*/
static Action[] prepareActions() {
DefaultEditorKit dek = new DefaultEditorKit();
Action[] defActions = dek.getActions();
Action[] newActions = new Action[defActions.length];
for (int i = 0; i < defActions.length; i++) {
Object actionName = defActions[i].getValue(Action.NAME);
if (actionName.equals(beginLineAction)) {
newActions[i] = new OutputBeginLineAction(beginLineAction, false);
} else if (actionName.equals(selectionBeginLineAction)) {
newActions[i] = new OutputBeginLineAction(selectionBeginLineAction, true);
} else if (actionName.equals(endLineAction)) {
newActions[i] = new OutputEndLineAction(endLineAction, false);
} else if (actionName.equals(selectionEndLineAction)) {
newActions[i] = new OutputEndLineAction(selectionEndLineAction, true);
} else {
newActions[i] = defActions[i];
}
}
return newActions;
}
/*
* Position the caret to the beginning of the line.
* @see DefaultEditorKit#beginLineAction
* @see DefaultEditorKit#selectBeginLineAction
* @see DefaultEditorKit#getActions
*/
static class OutputBeginLineAction extends TextAction {
/**
* Create this action with the appropriate identifier.
* @param nm the name of the action, Action.NAME.
* @param select whether to extend the selection when
* changing the caret position.
*/
OutputBeginLineAction(String nm, boolean select) {
super(nm);
this.select = select;
}
/** The operation to perform when this action is triggered. */
public void actionPerformed(ActionEvent e) {
JTextComponent target = getTextComponent(e);
if (target != null) {
Document doc = target.getDocument();
Element map = doc.getDefaultRootElement();
int offs = target.getCaretPosition();
int lineIndex = map.getElementIndex(offs);
int lineStart = map.getElement(lineIndex).getStartOffset();
if (select) {
target.moveCaretPosition(lineStart);
} else {
target.setCaretPosition(lineStart);
}
}
}
private boolean select;
}
/*
* Position the caret to the end of the line.
* @see DefaultEditorKit#endLineAction
* @see DefaultEditorKit#selectEndLineAction
* @see DefaultEditorKit#getActions
*/
static class OutputEndLineAction extends TextAction {
/**
* Create this action with the appropriate identifier.
* @param nm the name of the action, Action.NAME.
* @param select whether to extend the selection when
* changing the caret position.
*/
OutputEndLineAction(String nm, boolean select) {
super(nm);
this.select = select;
}
/** The operation to perform when this action is triggered. */
public void actionPerformed(ActionEvent e) {
JTextComponent target = getTextComponent(e);
if (target != null) {
Document doc = target.getDocument();
Element map = doc.getDefaultRootElement();
int offs = target.getCaretPosition();
int lineIndex = map.getElementIndex(offs);
int lineEnd = map.getElement(lineIndex).getEndOffset() - 1;
if (select) {
target.moveCaretPosition(lineEnd);
} else {
target.setCaretPosition(lineEnd);
}
}
}
private boolean select;
}
}