mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/jpexs-decompiler.git
synced 2026-09-24 21:51:32 +00:00
AS1/2/3 - highlight variables and errors on panel next to vertical scrollbar
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (C) 2025 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.gui.editor;
|
||||
|
||||
import com.jpexs.decompiler.flash.gui.AppStrings;
|
||||
import java.awt.Color;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.plaf.ScrollBarUI;
|
||||
import javax.swing.text.BadLocationException;
|
||||
import javax.swing.text.Highlighter;
|
||||
import jsyntaxpane.actions.ActionUtils;
|
||||
import jsyntaxpane.components.Markers;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class HighlightsPanel extends JPanel {
|
||||
|
||||
private final LineMarkedEditorPane editorPane;
|
||||
private Cursor HAND_CURSOR = Cursor.getPredefinedCursor(Cursor.HAND_CURSOR);
|
||||
private Cursor DEFAULT_CURSOR = Cursor.getDefaultCursor();
|
||||
private Map<Integer, String> errors = new LinkedHashMap<>();
|
||||
private int scrollBarButtonSize = 0;
|
||||
private JScrollPane scrollPane;
|
||||
|
||||
public HighlightsPanel(LineMarkedEditorPane editorPane) {
|
||||
this.editorPane = editorPane;
|
||||
|
||||
scrollPane = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, editorPane);
|
||||
|
||||
JScrollBar bar = new JScrollBar(JScrollBar.VERTICAL, 0, 1, 0, 100);
|
||||
scrollBarButtonSize = bar.getPreferredSize().width;
|
||||
setPreferredSize(new Dimension(16, Integer.MAX_VALUE));
|
||||
addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (!SwingUtilities.isLeftMouseButton(e)) {
|
||||
return;
|
||||
}
|
||||
int totalLineCount = ActionUtils.getLineCount(editorPane);
|
||||
Rectangle r = getScrollbarTrackRect();
|
||||
int line = (e.getY() - r.y) * totalLineCount / r.height;
|
||||
int linesCountPer1Px = totalLineCount / r.height;
|
||||
if (!scrollPane.getVerticalScrollBar().isVisible()) {
|
||||
linesCountPer1Px = 0;
|
||||
}
|
||||
|
||||
for (Highlighter.Highlight highlight : editorPane.getHighlighter().getHighlights()) {
|
||||
Highlighter.HighlightPainter painter = highlight.getPainter();
|
||||
if (painter instanceof Markers.SimpleMarker) {
|
||||
try {
|
||||
int lineNum = ActionUtils.getLineNumber(editorPane, highlight.getStartOffset());
|
||||
if (lineNum >= line - linesCountPer1Px && lineNum <= line + linesCountPer1Px) {
|
||||
editorPane.setCaretPosition(highlight.getStartOffset());
|
||||
return;
|
||||
}
|
||||
} catch (BadLocationException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
addMouseMotionListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
int totalLineCount = ActionUtils.getLineCount(editorPane);
|
||||
Rectangle r = getScrollbarTrackRect();
|
||||
int line = (e.getY() - r.y) * totalLineCount / r.height;
|
||||
|
||||
int linesCountPer1Px = totalLineCount / r.height;
|
||||
if (!scrollPane.getVerticalScrollBar().isVisible()) {
|
||||
linesCountPer1Px = 0;
|
||||
}
|
||||
|
||||
int lineStart = line * r.height / totalLineCount;
|
||||
int lineEnd = (line + 1) * r.height / totalLineCount;
|
||||
if (e.getY() - r.y < lineStart - 1 || e.getY() - r.y > lineStart + 2) {
|
||||
if (getCursor() != DEFAULT_CURSOR) {
|
||||
setCursor(DEFAULT_CURSOR);
|
||||
}
|
||||
|
||||
setToolTipText(null);
|
||||
return;
|
||||
}
|
||||
|
||||
int currentLine = editorPane.getLine();
|
||||
|
||||
for (Highlighter.Highlight highlight : editorPane.getHighlighter().getHighlights()) {
|
||||
Highlighter.HighlightPainter painter = highlight.getPainter();
|
||||
if (painter instanceof Markers.SimpleMarker) {
|
||||
try {
|
||||
int lineNum = ActionUtils.getLineNumber(editorPane, highlight.getStartOffset());
|
||||
if (lineNum >= line - linesCountPer1Px && lineNum <= line + linesCountPer1Px) {
|
||||
if (line != currentLine) {
|
||||
if (getCursor() != HAND_CURSOR) {
|
||||
setCursor(HAND_CURSOR);
|
||||
}
|
||||
}
|
||||
if (errors.containsKey(highlight.getStartOffset())) {
|
||||
setToolTipText(AppStrings.translate("highlighter.error").replace("%error%", errors.get(highlight.getStartOffset())));
|
||||
return;
|
||||
}
|
||||
if (line != currentLine && painter instanceof OccurencesMarker) {
|
||||
setToolTipText(AppStrings.translate("highlighter.occurences"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (line == currentLine) {
|
||||
setToolTipText(AppStrings.translate("highlighter.currentLine"));
|
||||
}
|
||||
return;
|
||||
}
|
||||
} catch (BadLocationException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (getCursor() != DEFAULT_CURSOR) {
|
||||
setCursor(DEFAULT_CURSOR);
|
||||
}
|
||||
|
||||
setToolTipText(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void setErrors(Map<Integer, String> errors) {
|
||||
this.errors = errors;
|
||||
}
|
||||
|
||||
private Rectangle getScrollbarTrackRect() {
|
||||
return new Rectangle(0, scrollBarButtonSize, 16, getHeight() + - 2 * scrollBarButtonSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void paintComponent(Graphics g) {
|
||||
super.paintComponent(g);
|
||||
Rectangle r = getScrollbarTrackRect();
|
||||
|
||||
int totalLineCount = ActionUtils.getLineCount(editorPane);
|
||||
int h = 3;
|
||||
Highlighter.Highlight[] highlights = editorPane.getHighlighter().getHighlights();
|
||||
Set<Integer> ignoredLines = new HashSet<>();
|
||||
int currentLine = editorPane.getLine();
|
||||
|
||||
for (Highlighter.Highlight highlight : highlights) {
|
||||
Highlighter.HighlightPainter painter = highlight.getPainter();
|
||||
if (painter instanceof Markers.SimpleMarker) {
|
||||
Markers.SimpleMarker simpleMarker = (Markers.SimpleMarker) painter;
|
||||
g.setColor(simpleMarker.getColor());
|
||||
|
||||
try {
|
||||
int line = ActionUtils.getLineNumber(editorPane, highlight.getStartOffset());
|
||||
|
||||
//prefer error lines
|
||||
if (painter instanceof WavyUnderLinePainter) {
|
||||
ignoredLines.add(line);
|
||||
} else if (ignoredLines.contains(line)) {
|
||||
continue;
|
||||
}
|
||||
float ratio = (float) line / totalLineCount;
|
||||
int y = r.y + (int) (ratio * r.height);
|
||||
g.fillRect(0, y, getWidth(), h);
|
||||
} catch (BadLocationException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (currentLine >= 0) {
|
||||
float ratio = (float) currentLine / totalLineCount;
|
||||
int y = r.y + (int) (ratio * r.height) + 1;
|
||||
g.setColor(Color.gray);
|
||||
g.fillRect(0, y, getWidth(), 1);
|
||||
g.fillOval(16 / 2 - 2, y - 1, 3, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2025 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.gui.editor;
|
||||
|
||||
import java.awt.Color;
|
||||
import jsyntaxpane.components.Markers;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class OccurencesMarker extends Markers.SimpleMarker {
|
||||
|
||||
public OccurencesMarker(Color color) {
|
||||
super(color);
|
||||
}
|
||||
}
|
||||
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2025 JPEXS
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.jpexs.decompiler.flash.gui.editor;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.swing.JComponent;
|
||||
import javax.swing.JLayer;
|
||||
import javax.swing.JScrollBar;
|
||||
import javax.swing.plaf.LayerUI;
|
||||
import javax.swing.plaf.ScrollBarUI;
|
||||
import jsyntaxpane.actions.ActionUtils;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author JPEXS
|
||||
*/
|
||||
public class ScrollbarOverlay extends LayerUI<JScrollBar> {
|
||||
|
||||
private final LineMarkedEditorPane editorPane;
|
||||
private final Map<Integer, Color> markedLines = new LinkedHashMap<>();
|
||||
|
||||
public ScrollbarOverlay(LineMarkedEditorPane editorPane) {
|
||||
this.editorPane = editorPane;
|
||||
}
|
||||
|
||||
public void removeMarkers(Color color) {
|
||||
Set<Integer> lines = new HashSet<>(markedLines.keySet());
|
||||
for (int key : lines) {
|
||||
if (markedLines.get(key).equals(color)) {
|
||||
markedLines.remove(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void clearMarkers() {
|
||||
markedLines.clear();
|
||||
}
|
||||
|
||||
public void addMarker(int line, Color color) {
|
||||
markedLines.put(line, color);
|
||||
}
|
||||
|
||||
public void setMarkedLines(Map<Integer, Color> markedLines) {
|
||||
this.markedLines.clear();
|
||||
this.markedLines.putAll(markedLines);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paint(Graphics g, JComponent c) {
|
||||
super.paint(g, c);
|
||||
JScrollBar bar = (JScrollBar) ((JLayer<?>) c).getView();
|
||||
ScrollBarUI ui = bar.getUI();
|
||||
Rectangle r;
|
||||
if (ui instanceof TrackRectSubstanceScrollbarUI) {
|
||||
r = ((TrackRectSubstanceScrollbarUI) ui).getTrackBounds();
|
||||
} else {
|
||||
r = new Rectangle(0, 16, 16, bar.getHeight() - 32);
|
||||
}
|
||||
|
||||
int totalLineCount = ActionUtils.getLineCount(editorPane);
|
||||
for (Map.Entry<Integer, Color> entry : markedLines.entrySet()) {
|
||||
g.setColor(entry.getValue());
|
||||
|
||||
float ratio = (float) entry.getKey() / totalLineCount;
|
||||
int y = r.y + (int) (ratio * r.height);
|
||||
g.drawLine(0, y, bar.getWidth(), y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
private static final Color SCROLLBAR_ERROR_COLOR = new Color(0xff0000);
|
||||
private JEditorPane pane;
|
||||
private final Set<TokenType> tokenTypes = new HashSet<>();
|
||||
private Markers.SimpleMarker marker;
|
||||
private OccurencesMarker marker;
|
||||
private Markers.SimpleMarker errorMarker;
|
||||
private Status status;
|
||||
private Map<Integer, String> errors = new LinkedHashMap<>();
|
||||
@@ -87,9 +87,7 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
|
||||
private MouseMotionAdapter mouseMotionAdapter;
|
||||
|
||||
private JLayer<JScrollBar> layer;
|
||||
|
||||
private ScrollbarOverlay scrollbarOverlay;
|
||||
private HighlightsPanel highlightsPanel;
|
||||
|
||||
private ScrollPaneUI originalScrollPaneUI;
|
||||
|
||||
@@ -120,7 +118,7 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
if (token != null && tokenTypes.contains(token.type)) {
|
||||
addMarkers(token);
|
||||
}
|
||||
layer.repaint();
|
||||
highlightsPanel.repaint();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,12 +127,10 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
*/
|
||||
public void removeMarkers() {
|
||||
Markers.removeMarkers(pane, marker);
|
||||
scrollbarOverlay.removeMarkers(SCROLLBAR_VARIABLE_COLOR);
|
||||
}
|
||||
|
||||
public void removeErrorMarkers() {
|
||||
Markers.removeMarkers(pane, errorMarker);
|
||||
scrollbarOverlay.removeMarkers(SCROLLBAR_ERROR_COLOR);
|
||||
}
|
||||
|
||||
private Token getIdentifierTokenAt(SyntaxDocument sDoc, int pos) {
|
||||
@@ -188,6 +184,8 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
}
|
||||
|
||||
void addErrorMarkers() {
|
||||
highlightsPanel.setErrors(errors);
|
||||
|
||||
SyntaxDocument doc = ActionUtils.getSyntaxDocument(pane);
|
||||
if (doc == null) {
|
||||
return;
|
||||
@@ -197,10 +195,9 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
Token token = getNearestTokenAt(doc, position);
|
||||
if (token != null) {
|
||||
Markers.markToken(pane, token, errorMarker);
|
||||
markPositionOnScrollbar(position, SCROLLBAR_ERROR_COLOR);
|
||||
}
|
||||
}
|
||||
layer.repaint();
|
||||
highlightsPanel.repaint();
|
||||
doc.readUnlock();
|
||||
}
|
||||
|
||||
@@ -220,12 +217,10 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
if (definitionToken != null) {
|
||||
if (definitionPosToReferences.containsKey(definitionPos)) {
|
||||
Markers.markToken(pane, definitionToken, marker);
|
||||
markPositionOnScrollbar(definitionToken.start, SCROLLBAR_VARIABLE_COLOR);
|
||||
for (int i : definitionPosToReferences.get(definitionPos)) {
|
||||
Token referenceToken = getIdentifierTokenAt(sDoc, i);
|
||||
if (referenceToken != null) {
|
||||
Markers.markToken(pane, referenceToken, marker);
|
||||
markPositionOnScrollbar(referenceToken.start, SCROLLBAR_VARIABLE_COLOR);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,19 +228,11 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
sDoc.readUnlock();
|
||||
}
|
||||
|
||||
private void markPositionOnScrollbar(int position, Color color) {
|
||||
try {
|
||||
scrollbarOverlay.addMarker(ActionUtils.getLineNumber(pane, position), color);
|
||||
} catch (BadLocationException ex) {
|
||||
//ignore
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void config(Configuration config) {
|
||||
Color markerColor = config.getColor(PROPERTY_COLOR, DEFAULT_COLOR);
|
||||
Color errorColor = config.getColor(PROPERTY_ERRORCOLOR, DEFAULT_ERRORCOLOR);
|
||||
this.marker = new Markers.SimpleMarker(markerColor);
|
||||
this.marker = new OccurencesMarker(markerColor);
|
||||
this.errorMarker = new WavyUnderLinePainter(errorColor); //Markers.SimpleMarker(errorColor);
|
||||
String types = config.getString(
|
||||
PROPERTY_TOKENTYPES, DEFAULT_TOKENTYPES);
|
||||
@@ -290,13 +277,10 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
verticalScrollBar.setUI(new TrackRectSubstanceScrollbarUI(verticalScrollBar));
|
||||
}
|
||||
|
||||
scrollbarOverlay = new ScrollbarOverlay((LineMarkedEditorPane) pane);
|
||||
|
||||
layer = new JLayer<>(verticalScrollBar, scrollbarOverlay);
|
||||
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
|
||||
|
||||
highlightsPanel = new HighlightsPanel((LineMarkedEditorPane) pane);
|
||||
//scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
|
||||
JPanel panel = (JPanel) SwingUtilities.getAncestorOfClass(JPanel.class, scrollPane);
|
||||
panel.add(layer, BorderLayout.EAST);
|
||||
panel.add(highlightsPanel, BorderLayout.EAST);
|
||||
|
||||
documentUpdated();
|
||||
markTokenAt(editor.getCaretPosition());
|
||||
@@ -335,8 +319,8 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
}
|
||||
JScrollPane scrollPane = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, editor);
|
||||
JPanel panel = (JPanel) SwingUtilities.getAncestorOfClass(JPanel.class, scrollPane);
|
||||
panel.remove(layer);
|
||||
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
|
||||
panel.remove(highlightsPanel);
|
||||
//scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
|
||||
panel.revalidate();
|
||||
panel.repaint();
|
||||
scrollPane.setUI(originalScrollPaneUI);
|
||||
@@ -362,7 +346,7 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
private void documentUpdated() {
|
||||
errors.clear();
|
||||
removeErrorMarkers();
|
||||
layer.repaint();
|
||||
highlightsPanel.repaint();
|
||||
try {
|
||||
SyntaxDocument sDoc = (SyntaxDocument) pane.getDocument();
|
||||
|
||||
@@ -392,8 +376,7 @@ public class VariableMarker implements SyntaxComponent, CaretListener, PropertyC
|
||||
} catch (SimpleParseException ex) {
|
||||
definitionPosToReferences.clear();
|
||||
referenceToDefinition.clear();
|
||||
errors.put((int) ex.position, ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
errors.put((int) ex.position, ex.getMessage());
|
||||
}
|
||||
Timer tim = errorsTimer;
|
||||
if (tim != null) {
|
||||
|
||||
@@ -1057,4 +1057,8 @@ work.debugging.start = Starting Flash content debugger
|
||||
|
||||
menu.file.view.alwaysOnTop = Always on top
|
||||
|
||||
contextmenu.showDetail = Show detail
|
||||
contextmenu.showDetail = Show detail
|
||||
|
||||
highlighter.occurences = Mark occurences
|
||||
highlighter.currentLine = Current line
|
||||
highlighter.error = Error: %error%
|
||||
Reference in New Issue
Block a user