![JAR search and dependency download from the Maven repository](/logo.png)
org.netbeans.editor.GlyphGutter Maven / Gradle / Ivy
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.editor;
import javax.swing.JComponent;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Font;
import java.awt.Image;
import java.net.URL;
import java.net.MalformedURLException;
import java.awt.Toolkit;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.event.DocumentListener;
import javax.swing.event.DocumentEvent;
import javax.swing.text.BadLocationException;
import java.awt.FontMetrics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Shape;
import java.awt.image.ImageObserver;
import java.awt.event.InputEvent;
import javax.swing.JPopupMenu;
import org.netbeans.editor.Utilities;
import javax.swing.event.PopupMenuListener;
import javax.swing.event.PopupMenuEvent;
import org.netbeans.editor.FontMetricsCache;
import org.netbeans.editor.LocaleSupport;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeEvent;
import java.awt.event.*;
import java.lang.ref.WeakReference;
import java.util.Map;
import javax.swing.Action;
import javax.accessibility.*;
import javax.swing.SwingUtilities;
import javax.swing.text.AbstractDocument;
import javax.swing.text.BoxView;
import javax.swing.text.Element;
import javax.swing.text.JTextComponent;
import javax.swing.text.Position;
import javax.swing.text.View;
import org.netbeans.api.editor.fold.FoldHierarchy;
import org.netbeans.api.editor.fold.FoldHierarchyEvent;
import org.netbeans.api.editor.fold.FoldHierarchyListener;
import org.openide.ErrorManager;
/** GlyphGutter is component for displaying line numbers and annotation
* glyph icons. Component also allow to "cycle" through the annotations. It
* means that if there is more than one annotation on the line, only one of them
* might be visible. And clicking the special cycling button in the gutter the user
* can cycle through the annotations.
*
* @author David Konecny
* @since 07/2001
*/
public class GlyphGutter extends JComponent implements Annotations.AnnotationsListener, Accessible, SettingsChangeListener, SideBarFactory {
/** EditorUI which part this gutter is */
private EditorUI editorUI;
/** Document to which this gutter is attached*/
private BaseDocument doc;
/** Annotations manager responsible for annotations for this line */
private Annotations annos;
/** Cycling button image */
private Image gutterButton;
/** Backroung color of the gutter */
private Color backgroundColor;
/** Foreground color of the gutter. Used for drawing line numbers. */
private Color foreColor;
/** Font used for drawing line numbers */
private Font font;
/** Height of the line as it was calculated in EditorUI. */
private int lineHeight;
/** Flag whther the gutter was initialized or not. The painting is disabled till the
* gutter is not initialized */
private boolean init;
/** Width of the column used for drawing line numbers. The value contains
* also line number margins. */
private int numberWidth;
/** Predefined width of the glyph icons */
private final static int glyphWidth = 16;
/** Preddefined width of the cycling button */
private final static int glyphButtonWidth = 9;
/** Whether the line numbers are shown or not */
private boolean showLineNumbers = true;
/** Image observer used for glyph icons */
private ImageObserver imgObserver = null;
/** The gutter height is enlarged by number of lines which specifies this constant */
private static final int ENLARGE_GUTTER_HEIGHT = 300;
/** The hightest line number. This value is used for calculating width of the gutter */
private int highestLineNumber = 0;
/** Whether the annotation glyph can be drawn over the line numbers */
private boolean drawOverLineNumbers = false;
/* These two variables are used for caching of count of line annos
* on the line over which is the mouse caret. Just for sake of optimalization. */
private int cachedCountOfAnnos = -1;
private int cachedCountOfAnnosForLine = -1;
/** Property change listener on AnnotationTypes changes */
private PropertyChangeListener annoTypesListener;
private PropertyChangeListener editorUIListener;
private GlyphGutter.GlyphGutterFoldHierarchyListener glyphGutterFoldHierarchyListener;
private GutterMouseListener gutterMouseListener;
private FoldHierarchy foldHierarchy;
private Map renderingHints;
public GlyphGutter(){}
public GlyphGutter(EditorUI editorUI) {
super();
this.editorUI = editorUI;
init = false;
doc = editorUI.getDocument();
annos = doc.getAnnotations();
// Annotations class is model for this view, so the listener on changes in
// Annotations must be added here
annos.addAnnotationsListener(this);
// do initialization
init();
update();
Settings.addSettingsChangeListener(this);
setMaximumSize(new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE));
foldHierarchy = FoldHierarchy.get(editorUI.getComponent());
glyphGutterFoldHierarchyListener = new GlyphGutterFoldHierarchyListener();
foldHierarchy.addFoldHierarchyListener(glyphGutterFoldHierarchyListener);
editorUIListener = new EditorUIListener();
editorUI.addPropertyChangeListener(editorUIListener);
updateRenderingHints();
setOpaque (true);
}
private void updateRenderingHints(){
JTextComponent comp = editorUI.getComponent();
if (comp == null) return;
Object value = Settings.getValue(Utilities.getKitClass(comp), SettingsNames.RENDERING_HINTS);
renderingHints = (value instanceof Map) ? (java.util.Map)value : null;
}
public void settingsChange(SettingsChangeEvent evt) {
if (editorUI == null) // no long er active
return;
JTextComponent component = editorUI.getComponent();
if (evt == null || component == null) return;
String settingName = evt.getSettingName();
if (settingName == null || SettingsNames.RENDERING_HINTS.equals(settingName)) {
updateRenderingHints();
}
Class kitClass = evt.getKitClass();
if (Utilities.getKitClass(component) != kitClass){
Rectangle rect = component.getVisibleRect();
if (rect!=null && rect.width == 0){
if (SwingUtilities.isEventDispatchThread()) {
resize();
} else {
SwingUtilities.invokeLater(
new Runnable() {
public void run() {
resize();
}
}
);
}
}
}
}
/* Read accessible context
* @return - accessible context
*/
public AccessibleContext getAccessibleContext () {
if (accessibleContext == null) {
accessibleContext = new AccessibleJComponent() {
public AccessibleRole getAccessibleRole() {
return AccessibleRole.PANEL;
}
};
}
return accessibleContext;
}
/** Do initialization of the glyph gutter*/
protected void init() {
if (editorUI == null)
return ;
URL imageURL = null;
try {
// cycling button
imageURL = new URL("nbresloc:/org/netbeans/editor/resources/glyphbutton.gif"); // NOI18N
} catch (MalformedURLException ex) {
Utilities.annotateLoggable(ex);
return;
}
if (imageURL != null)
gutterButton = Toolkit.getDefaultToolkit().getImage(imageURL);
setToolTipText ("");
getAccessibleContext().setAccessibleName(LocaleSupport.getString("ACSN_Glyph_Gutter")); // NOI18N
getAccessibleContext().setAccessibleDescription(LocaleSupport.getString("ACSD_Glyph_Gutter")); // NOI18N
// add mouse listener for cycling button
// TODO: clicking the line number should select whole line
// TODO: clicking the line number abd dragging the mouse should select block of lines
gutterMouseListener = new GutterMouseListener ();
addMouseListener (gutterMouseListener);
addMouseMotionListener (gutterMouseListener);
// after the glyph icons are loaded it is necessary to repaint the gutter
imgObserver = new Observer(this);
AnnotationTypes.getTypes().addPropertyChangeListener( annoTypesListener = new PropertyChangeListener() {
public void propertyChange (PropertyChangeEvent evt) {
if (evt.getPropertyName() == AnnotationTypes.PROP_GLYPHS_OVER_LINE_NUMBERS ||
evt.getPropertyName() == AnnotationTypes.PROP_SHOW_GLYPH_GUTTER) {
update();
}
}
});
}
/** Update colors, fonts, sizes and invalidate itself. This method is
* called from EditorUI.update() */
public void update() {
if (editorUI == null)
return ;
Coloring lineColoring = (Coloring)editorUI.getColoringMap().get(SettingsNames.LINE_NUMBER_COLORING);
Coloring defaultColoring = (Coloring)editorUI.getDefaultColoring();
// fix for issue #16940
// the real cause of this problem is that closed document is not garbage collected,
// because of *some* references (see #16072) and so any change in AnnotationTypes.PROP_*
// properties is fired which must update this component although it is not visible anymore
if (lineColoring == null)
return;
if (lineColoring.getBackColor() != null)
backgroundColor = lineColoring.getBackColor();
else
backgroundColor = defaultColoring.getBackColor();
if (lineColoring.getForeColor() != null)
foreColor = lineColoring.getForeColor();
else
foreColor = defaultColoring.getForeColor();
if (lineColoring.getFont() != null)
font = lineColoring.getFont();
else
font = defaultColoring.getFont();
lineHeight = editorUI.getLineHeight();
showLineNumbers = editorUI.lineNumberVisibleSetting;
drawOverLineNumbers = AnnotationTypes.getTypes().isGlyphsOverLineNumbers().booleanValue();
init = true;
// initialize the value with current number of lines
highestLineNumber = getLineCount();
repaint();
resize();
}
protected void resize() {
Dimension dim = new Dimension();
dim.width = getWidthDimension();
dim.height = getHeightDimension();
// enlarge the gutter so that inserting new lines into
// document does not cause resizing too often
dim.height += ENLARGE_GUTTER_HEIGHT * lineHeight;
numberWidth = getLineNumberWidth();
if (!showLineNumbers)
numberWidth = 0;
setPreferredSize(dim);
revalidate();
}
/** Return number of lines in the document */
protected int getLineCount() {
int lineCnt;
try {
if (doc != null) {
lineCnt = Utilities.getLineOffset(doc, doc.getLength()) + 1;
} else { // deactivated
lineCnt = 1;
}
} catch (BadLocationException e) {
lineCnt = 1;
}
return lineCnt;
}
/** Gets number of digits in the number */
protected int getDigitCount(int number) {
return Integer.toString(number).length();
}
protected int getLineNumberWidth() {
int newWidth = 0;
if (editorUI != null) {
Insets insets = editorUI.getLineNumberMargin();
if (insets != null) {
newWidth += insets.left + insets.right;
}
newWidth += getDigitCount(highestLineNumber) * editorUI.getLineNumberDigitWidth();
}
return newWidth;
}
protected int getWidthDimension() {
int newWidth = 0;
if (editorUI != null) {
if (annos.isGlyphColumn() || AnnotationTypes.getTypes().isShowGlyphGutter().booleanValue())
newWidth += glyphWidth;
if (annos.isGlyphButtonColumn())
newWidth += glyphButtonWidth;
if (showLineNumbers) {
int lineNumberWidth = getLineNumberWidth();
if (drawOverLineNumbers) {
if (lineNumberWidth > newWidth)
newWidth = lineNumberWidth;
} else
newWidth += lineNumberWidth;
}
}
return newWidth;
}
protected int getHeightDimension() {
if (editorUI == null)
return 0;
JComponent comp = editorUI.getComponent();
if (comp == null)
return 0;
return highestLineNumber * lineHeight + (int)comp.getSize().getHeight();
}
void paintGutterForView(Graphics g, View view, int y){
if (editorUI == null)
return ;
Rectangle clip = g.getClipBounds();
JTextComponent component = editorUI.getComponent();
if (component == null) return;
BaseTextUI textUI = (BaseTextUI)component.getUI();
Rectangle rec = new Rectangle(0, y, 0, editorUI.getLineHeight());
// Should already be filled by back color in paintComponent()
// g.setColor(backgroundColor);
// Rectangle fillRect = new Rectangle(clip.x, rec.y, getSize().width, rec.height);
// g.fillRect(0, rec.y, getSize().width, rec.height);
g.setFont(font);
g.setColor(foreColor);
FontMetrics fm = FontMetricsCache.getFontMetrics(font, this);
int rightMargin = 0;
Insets margin = editorUI.getLineNumberMargin();
if (margin != null) rightMargin = margin.right;
Element rootElem = textUI.getRootView(component).getElement();
int line = rootElem.getElementIndex(view.getStartOffset());
// find the nearest visible line with an annotation
int lineWithAnno = annos.getNextLineWithAnnotation(line);
int lineNumberWidth = fm.stringWidth(String.valueOf(line + 1));
if (showLineNumbers && ( (!drawOverLineNumbers) || (drawOverLineNumbers && line != lineWithAnno) ) ) {
g.drawString(String.valueOf(line + 1), numberWidth-lineNumberWidth-rightMargin, y + editorUI.getLineAscent());
}
// draw anotation if we get to the line with some annotation
if (line == lineWithAnno) {
int count = annos.getNumberOfAnnotations(line);
AnnotationDesc anno = annos.getActiveAnnotation(line);
int xPos = numberWidth;
if (drawOverLineNumbers) {
xPos = getWidth() - glyphWidth;
if (count > 1)
xPos -= glyphButtonWidth;
}
if (anno != null) {
// draw the glyph only when the annotation type has its own icon (no the default one)
// or in case there is more than one annotations on the line
if ( ! (count == 1 && anno.isDefaultGlyph()) ) {
if (anno.getGlyph() != null && prepareImage(anno.getGlyph(), imgObserver))
g.drawImage(anno.getGlyph(), xPos, y + (lineHeight-anno.getGlyph().getHeight(null)) / 2 + 1, null);
}
}
// draw cycling button if there is more than one annotations on the line
if (count > 1)
if (anno.getGlyph() != null && prepareImage(gutterButton, imgObserver) && prepareImage(anno.getGlyph(), imgObserver))
g.drawImage(gutterButton, xPos+glyphWidth, y + (lineHeight-anno.getGlyph().getHeight(null)) / 2, null);
// update the value with next line with some anntoation
lineWithAnno = annos.getNextLineWithAnnotation(line+1);
}
}
/** Paint the gutter itself */
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (editorUI == null)
return ;
// Possibly apply the rendering hints
if (renderingHints != null) {
((java.awt.Graphics2D)g).setRenderingHints(renderingHints);
}
// if the gutter was not initialized yet, skip the painting
if (!init) return;
Rectangle clip = g.getClipBounds();
JTextComponent component = editorUI.getComponent();
if (component == null) return;
BaseTextUI textUI = (BaseTextUI)component.getUI();
Element rootElem = textUI.getRootView(component).getElement();
View rootView = Utilities.getDocumentView(component);
if (rootView == null) return;
g.setColor(backgroundColor);
g.fillRect(clip.x, clip.y, clip.width, clip.height);
AbstractDocument doc = (AbstractDocument)component.getDocument();
doc.readLock();
try{
foldHierarchy.lock();
try{
int startPos = textUI.getPosFromY(clip.y);
int startViewIndex = rootView.getViewIndex(startPos,Position.Bias.Forward);
int rootViewCount = rootView.getViewCount();
if (startViewIndex >= 0 && startViewIndex < rootViewCount) {
// find the nearest visible line with an annotation
int lineWithAnno = annos.getNextLineWithAnnotation(rootElem.getElementIndex(startPos));
Rectangle rec = textUI.modelToView(component, rootView.getView(startViewIndex).getStartOffset());
int y = (rec == null) ? 0 : rec.y;
int clipEndY = clip.y + clip.height;
for (int i = startViewIndex; i < rootViewCount; i++){
View view = rootView.getView(i);
paintGutterForView(g, view, y);
y += editorUI.getLineHeight();
if (y >= clipEndY) {
break;
}
}
}
}finally{
foldHierarchy.unlock();
}
}catch(BadLocationException ble){
ErrorManager.getDefault().notify(ble);
}finally{
doc.readUnlock();
}
}
/** Data for the line has changed and the line must be redraw. */
public void changedLine(int line) {
if (!init || editorUI == null)
return;
// reset cache if there was some change
cachedCountOfAnnos = -1;
// redraw also lines around - three lines will be redrawn
if (line > 0)
line--;
JTextComponent component = editorUI.getComponent();
if (component!=null){
BaseTextUI textUI = (BaseTextUI)component.getUI();
try{
Element rootElem = component.getDocument().getDefaultRootElement();
if (line >= rootElem.getElementCount()) { // #42504
return;
}
Element lineElem = rootElem.getElement(line);
if (lineElem == null) return;
int lineOffset = lineElem.getStartOffset();
Rectangle mtvRect = textUI.modelToView(component, lineOffset);
if (mtvRect == null) return;
repaint(0, mtvRect.y, (int)getSize().getWidth(), 3*lineHeight);
checkSize();
}catch(BadLocationException ble){
ErrorManager.getDefault().notify(ble);
}
}
}
/** Repaint whole gutter.*/
public void changedAll() {
if (!init || editorUI == null)
return;
// reset cache if there was some change
cachedCountOfAnnos = -1;
int lineCnt;
try {
lineCnt = Utilities.getLineOffset(doc, doc.getLength()) + 1;
} catch (BadLocationException e) {
lineCnt = 1;
}
repaint();
checkSize();
}
/** Check whether it is not necessary to resize the gutter */
protected void checkSize() {
int count = getLineCount();
if (count > highestLineNumber) {
highestLineNumber = count;
}
Dimension dim = getPreferredSize();
if (getWidthDimension() > dim.width ||
getHeightDimension() > dim.height) {
resize();
}
}
/** Get tooltip text for the mouse position */
// TODO: does not work for asynchronous tooltip texts
public String getToolTipText (MouseEvent e) {
if (editorUI == null)
return null;
int line = getLineFromMouseEvent(e);
if (annos.getNumberOfAnnotations(line) == 0)
return null;
if (isMouseOverCycleButton(e) && annos.getNumberOfAnnotations(line) > 1) {
return java.text.MessageFormat.format (
LocaleSupport.getString ("cycling-glyph_tooltip"), //NOI18N
new Object[] { new Integer (annos.getNumberOfAnnotations(line)) });
}
else if (isMouseOverGlyph(e)) {
return annos.getActiveAnnotation(line).getShortDescription();
}
else
return null;
}
/** Count the X position of the glyph on the line. */
private int getXPosOfGlyph(int line) {
if (editorUI == null)
return 0;
int xPos = numberWidth;
if (drawOverLineNumbers) {
xPos = getWidth() - glyphWidth;
if (cachedCountOfAnnos == -1 || cachedCountOfAnnosForLine != line) {
cachedCountOfAnnos = annos.getNumberOfAnnotations(line);
cachedCountOfAnnosForLine = line;
}
if (cachedCountOfAnnos > 1)
xPos -= glyphButtonWidth;
}
return xPos;
}
/** Check whether the mouse is over some glyph icon or not */
private boolean isMouseOverGlyph(MouseEvent e) {
int line = getLineFromMouseEvent(e);
if (e.getX() >= getXPosOfGlyph(line) && e.getX() <= getXPosOfGlyph(line)+glyphWidth)
return true;
else
return false;
}
/** Check whether the mouse is over the cycling button or not */
private boolean isMouseOverCycleButton(MouseEvent e) {
int line = getLineFromMouseEvent(e);
if (e.getX() >= getXPosOfGlyph(line)+glyphWidth && e.getX() <= getXPosOfGlyph(line)+glyphWidth+glyphButtonWidth)
return true;
else
return false;
}
public JComponent createSideBar(JTextComponent target) {
EditorUI eui = Utilities.getEditorUI(target);
GlyphGutter glyph = new GlyphGutter(eui);
eui.setGlyphGutter(glyph);
return glyph;
}
private int getLineFromMouseEvent(MouseEvent e){
int line = -1;
if (editorUI != null) {
try{
JTextComponent component = editorUI.getComponent();
BaseTextUI textUI = (BaseTextUI)component.getUI();
int clickOffset = textUI.viewToModel(component, new Point(0, e.getY()));
line = Utilities.getLineOffset(doc, clickOffset);
}catch (BadLocationException ble){
}
}
return line;
}
class GutterMouseListener extends MouseAdapter implements MouseMotionListener {
/** start line of the dragging. */
private int dragStartLine;
/** end line of the dragging. */
private int dragEndLine;
/** end line of last selection. */
private int currentEndLine;
/** If true, the selection goes forwards. */
private boolean selectForward;
public void mouseClicked(MouseEvent e) {
if (editorUI==null)
return;
// cycling button was clicked by left mouse button
if (e.getModifiers() == InputEvent.BUTTON1_MASK) {
if (isMouseOverCycleButton(e)) {
int line = getLineFromMouseEvent(e);
annos.activateNextAnnotation(line);
} else {
Action actions[] = ImplementationProvider.getDefault().getGlyphGutterActions(editorUI.getComponent());
if (actions != null && actions.length >0) {
Action a = actions[0]; //TODO - create GUI chooser
if (a!=null && a.isEnabled()){
int currentLine = -1;
int line = getLineFromMouseEvent(e);
if (line == -1) return;
try {
currentLine = Utilities.getLineOffset(doc, editorUI.getComponent().getCaret().getDot());
} catch (BadLocationException ex) {
return;
}
if (line != currentLine) {
int offset = Utilities.getRowStartFromLineOffset(doc, line);
JumpList.checkAddEntry();
editorUI.getComponent().getCaret().setDot(offset);
}
a.actionPerformed(new ActionEvent(editorUI.getComponent(), 0, ""));
repaint();
}
} else {
Toolkit.getDefaultToolkit().beep();
}
}
}
}
private void showPopup(MouseEvent e) {
if (editorUI == null)
return;
// annotation glyph was clicked by right mouse button
if (e.isPopupTrigger()) {
int line = getLineFromMouseEvent(e);
int offset;
if (annos.getActiveAnnotation(line) != null)
offset = annos.getActiveAnnotation(line).getOffset();
else
offset = Utilities.getRowStartFromLineOffset(doc, line);
if (editorUI.getComponent().getCaret().getDot() != offset)
JumpList.checkAddEntry();
editorUI.getComponent().getCaret().setDot(offset);
JPopupMenu pm = annos.createPopupMenu(Utilities.getKit(editorUI.getComponent()), line);
if (pm != null) {
pm.show(GlyphGutter.this, e.getX(), e.getY());
}
pm.addPopupMenuListener( new PopupMenuListener() {
public void popupMenuCanceled(PopupMenuEvent e2) {
editorUI.getComponent().requestFocus();
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent e2) {
editorUI.getComponent().requestFocus();
}
public void popupMenuWillBecomeVisible(PopupMenuEvent e2) {
}
});
}
}
public void mouseReleased(MouseEvent e) {
showPopup(e);
}
public void mousePressed (MouseEvent e) {
showPopup(e);
// "click gutter selects line" functionality was disabled
// // only react when it is not a cycling button
// if ((e.getModifiers() & InputEvent.BUTTON1_MASK) == InputEvent.BUTTON1_MASK) {
// if (! isMouseOverCycleButton(e)) {
// dragStartLine = (int)( (float)e.getY() / (float)lineHeight );
// updateSelection (true);
// }
// }
}
public void mouseDragged(MouseEvent e) {
// "click gutter selects line" functionality was disabled
// dragEndLine = (int)( (float)e.getY() / (float)lineHeight );
// updateSelection (false);
}
public void mouseMoved(MouseEvent e) {}
/** Updates the selection */
private void updateSelection (boolean newSelection) {
if (editorUI == null)
return ;
javax.swing.text.JTextComponent comp = Utilities.getLastActiveComponent ();
try {
if (newSelection) {
selectForward = true;
// try to get the startOffset. In case of -1 it is most
// likely the end of the document
int rowStart = Utilities.getRowStartFromLineOffset (doc, dragStartLine);
if (rowStart < 0) {
rowStart = Utilities.getRowStart (doc, doc.getLength ());
dragStartLine = Utilities.getLineOffset (doc, rowStart);
}
comp.setCaretPosition (rowStart);
int offSet = Utilities.getRowEnd (doc, rowStart);
if (offSet < doc.getLength()) {
offSet = offSet + 1;
}
comp.moveCaretPosition (offSet);
currentEndLine = dragEndLine = dragStartLine;
} else {
if (currentEndLine == dragEndLine) return;
// select backwards
if (dragEndLine < dragStartLine) {
if (selectForward) {
// selection start should be at start of (dragLine + 1)
int offSet = Utilities.getRowStartFromLineOffset (doc, dragStartLine + 1);
if (offSet < 0) {
offSet = Utilities.getRowEnd (doc, Utilities.getRowStartFromLineOffset (doc, dragStartLine));
}
comp.setCaretPosition (offSet);
selectForward = false;
}
int rowStart = Utilities.getRowStartFromLineOffset (doc, dragEndLine);
if (rowStart < 0) rowStart = 0;
comp.moveCaretPosition (rowStart);
}
// select forwards
else {
if (! selectForward) {
// select start should be at dragStartLine
comp.setCaretPosition (Utilities.getRowStartFromLineOffset (doc, dragStartLine));
selectForward = true;
}
// try to get the begin of (endLine + 1)
int offSet = Utilities.getRowStartFromLineOffset (doc, dragEndLine + 1);;
// for last line or more -1 is returned, so set to docLength...
if (offSet < 0) {
offSet = doc.getLength ();
}
comp.moveCaretPosition (offSet);
}
}
currentEndLine = dragEndLine;
} catch (BadLocationException ble) {
ErrorManager.getDefault().notify(ble);
}
}
}
class GlyphGutterFoldHierarchyListener implements FoldHierarchyListener{
public GlyphGutterFoldHierarchyListener(){
}
public void foldHierarchyChanged(FoldHierarchyEvent evt) {
repaint();
}
}
/** Listening to EditorUI to properly deinstall attached listeners */
class EditorUIListener implements PropertyChangeListener{
public void propertyChange (PropertyChangeEvent evt) {
if (evt!=null && EditorUI.COMPONENT_PROPERTY.equals(evt.getPropertyName())) {
if (evt.getNewValue() == null){
// component deinstalled, lets uninstall all isteners
editorUI.removePropertyChangeListener(editorUIListener);
annos.removeAnnotationsListener(GlyphGutter.this);
foldHierarchy.removeFoldHierarchyListener(glyphGutterFoldHierarchyListener);
if (gutterMouseListener!=null){
removeMouseListener(gutterMouseListener);
removeMouseMotionListener(gutterMouseListener);
}
if (annoTypesListener !=null){
AnnotationTypes.getTypes().removePropertyChangeListener(annoTypesListener);
}
foldHierarchy.removeFoldHierarchyListener(glyphGutterFoldHierarchyListener);
foldHierarchy = null;
// Release document reference
doc = null;
editorUI.removePropertyChangeListener(this);
editorUI = null;
annos = null;
}
}
}
}
private static class Observer implements ImageObserver {
WeakReference glyphGutter;
private Observer(GlyphGutter gutter) {
glyphGutter = new WeakReference(gutter);
}
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
if ((infoflags & ImageObserver.ALLBITS) == ImageObserver.ALLBITS) {
GlyphGutter obj = (GlyphGutter)glyphGutter.get();
if (obj != null)
obj.repaint();
}
return true;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy