From 9b9e653876f55b963aa5363ceb1c5eeea6adf3ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=C5=99=C3=ADk?= Date: Sun, 27 Mar 2016 09:33:51 +0200 Subject: [PATCH] Display AS3 p-code docs in GUI, unfinished = stub, needs UI enhancements Must be turned on in settings --- .../flash/configuration/Configuration.java | 4 + .../locales/docs/pcode/AS3Generator.java | 134 -------------- .../flash/locales/docs/pcode/As3Docs.java | 171 ++++++++++++++++++ .../decompiler/flash/gui/DocsWindow.java | 79 ++++++++ .../flash/gui/abc/ASMSourceEditorPane.java | 68 +++++++ .../flash/gui/abc/DocsListener.java | 30 +++ .../flash/gui/abc/MethodCodePanel.java | 14 +- .../gui/editor/LineMarkedEditorPane.java | 43 ++++- 8 files changed, 400 insertions(+), 143 deletions(-) delete mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/locales/docs/pcode/AS3Generator.java create mode 100644 libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/locales/docs/pcode/As3Docs.java create mode 100644 src/com/jpexs/decompiler/flash/gui/DocsWindow.java create mode 100644 src/com/jpexs/decompiler/flash/gui/abc/DocsListener.java diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java index a641695f0..380b92ba7 100644 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/configuration/Configuration.java @@ -598,6 +598,10 @@ public class Configuration { @ConfigurationInternal public static final ConfigurationItem hwAcceleratedGraphics = null; + @ConfigurationDefaultBoolean(false) + @ConfigurationInternal + public static final ConfigurationItem as3pcodeDocWindow = null; + private enum OSId { WINDOWS, OSX, UNIX diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/locales/docs/pcode/AS3Generator.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/locales/docs/pcode/AS3Generator.java deleted file mode 100644 index 1fad19744..000000000 --- a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/locales/docs/pcode/AS3Generator.java +++ /dev/null @@ -1,134 +0,0 @@ -package com.jpexs.decompiler.flash.locales.docs.pcode; - -import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; -import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2InstructionFlag; -import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; -import java.io.IOException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; - -/** - * Generator for AVM2 instruction set documentation. TODO: use this somehow in - * GUI - * - * @author JPEXS - */ -public class AS3Generator { - - private static String makeIdent(String name) { - StringBuilder identName = new StringBuilder(); - boolean cap = false; - for (int i = 0; i < name.length(); i++) { - char c = name.charAt(i); - if (c == '_') { - cap = true; - continue; - } - if (cap) { - identName.append(c); - cap = false; - } else { - identName.append(Character.toLowerCase(c)); - } - } - return identName.toString(); - } - - public static void main(String[] args) throws IOException { - Properties prop = new Properties(); - prop.load(AS3Generator.class.getClassLoader().getResourceAsStream("com/jpexs/decompiler/flash/locales/docs/pcode/AS3.properties")); - - Map flagDescriptions = new HashMap<>(); - - for (AVM2InstructionFlag flg : AVM2InstructionFlag.values()) { - String flagIdent = makeIdent(flg.toString()); - String flagDescription = prop.getProperty("instructionFlag." + flagIdent); - flagDescriptions.put(flg, flagDescription); - } - - for (InstructionDefinition def : AVM2Code.allInstructionSet) { - if (def == null) { - continue; - } - System.out.println("========================="); - String insName = def.instructionName; - - String insShortDescription = prop.getProperty("instruction." + insName + ".shortDescription"); - String insDescription = prop.getProperty("instruction." + insName + ".description"); - String stackBefore = def.hasFlag(AVM2InstructionFlag.UNKNOWN_STACK) ? "???" : prop.getProperty("instruction." + insName + ".stackBefore"); - String stackAfter = def.hasFlag(AVM2InstructionFlag.UNKNOWN_STACK) ? "???" : prop.getProperty("instruction." + insName + ".stackAfter"); - String operandsDoc = def.hasFlag(AVM2InstructionFlag.UNKNOWN_OPERANDS) ? "???" : prop.getProperty("instruction." + insName + ".operands"); - - System.out.println(String.format("0x%02X", def.instructionCode) + " " + insName + ": " + insShortDescription); - - if (!insDescription.trim().isEmpty()) { - System.out.println("Description: " + insDescription); - } - System.out.println("Stack before: " + stackBefore); - System.out.println("Stack after: " + stackAfter); - boolean flagsPrinted = false; - - System.out.print("Operands: "); - - if (def.hasFlag(AVM2InstructionFlag.UNKNOWN_OPERANDS)) { - System.out.println("???"); - } else { - String[] operandsDocs = operandsDoc.split(", ?"); - boolean first = true; - for (int i = 0; i < def.operands.length; i++) { - int op = def.operands[i]; - String opDoc = operandsDocs[i]; - String operandTypeRaw = AVM2Code.operandTypeToString(op, false); - String operandTypeCombined = AVM2Code.operandTypeToString(op, true); - if (operandTypeCombined.contains(", ")) { - String operandTypesCombined[] = operandTypeCombined.split(", ?"); - for (int j = 0; j < operandTypesCombined.length; j++) { - if (!first) { - System.out.print(", "); - } else { - first = false; - } - opDoc = operandsDocs[i + j]; - operandTypeCombined = operandTypesCombined[j]; - if (opDoc.equals("...")) { - System.out.print("..."); - } else { - System.out.print(opDoc + ":" + operandTypeCombined); - } - } - } else { - if (!first) { - System.out.print(", "); - } else { - first = false; - } - if (opDoc.equals(operandTypeRaw)) { - System.out.print(operandTypeCombined); - } else { - System.out.print(opDoc + ":" + operandTypeCombined); - } - } - } - if (def.operands.length == 0) { - //System.out.print(""); - } - System.out.println(""); - } - - AVM2InstructionFlag flags[] = def.flags.clone(); - Arrays.sort(flags, Enum::compareTo); - - for (AVM2InstructionFlag fl : flags) { - if (fl != AVM2InstructionFlag.UNKNOWN_OPERANDS && fl != AVM2InstructionFlag.UNKNOWN_STACK) { - if (!flagsPrinted) { - flagsPrinted = true; - System.out.println("Flags:"); - } - System.out.println(" - " + flagDescriptions.get(fl)); - } - } - } - } -} diff --git a/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/locales/docs/pcode/As3Docs.java b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/locales/docs/pcode/As3Docs.java new file mode 100644 index 000000000..16c766e74 --- /dev/null +++ b/libsrc/ffdec_lib/src/com/jpexs/decompiler/flash/locales/docs/pcode/As3Docs.java @@ -0,0 +1,171 @@ +package com.jpexs.decompiler.flash.locales.docs.pcode; + +import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; +import com.jpexs.decompiler.flash.abc.avm2.instructions.AVM2InstructionFlag; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; +import com.jpexs.helpers.Cache; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +/** + * Generator for AVM2 instruction set documentation. TODO: use this somehow in + * GUI + * + * @author JPEXS + */ +public class As3Docs { + + private static Properties prop; + private static Map flagDescriptions = new HashMap<>(); + + private static Cache docsCache = Cache.getInstance(false, true, "as3DocsCache"); + private static Map nameToDef = new HashMap<>(); + + static { + prop = new Properties(); + try { + prop.load(As3Docs.class.getClassLoader().getResourceAsStream("com/jpexs/decompiler/flash/locales/docs/pcode/AS3.properties")); + } catch (IOException e) { + //ignore + } + + for (InstructionDefinition def : AVM2Code.allInstructionSet) { + if (def == null) { + continue; + } + nameToDef.put(def.instructionName, def); + } + + for (AVM2InstructionFlag flg : AVM2InstructionFlag.values()) { + String flagIdent = makeIdent(flg.toString()); + String flagDescription = prop.getProperty("instructionFlag." + flagIdent); + flagDescriptions.put(flg, flagDescription); + } + } + + private static String makeIdent(String name) { + StringBuilder identName = new StringBuilder(); + boolean cap = false; + for (int i = 0; i < name.length(); i++) { + char c = name.charAt(i); + if (c == '_') { + cap = true; + continue; + } + if (cap) { + identName.append(c); + cap = false; + } else { + identName.append(Character.toLowerCase(c)); + } + } + return identName.toString(); + } + + public static String getDocsForIns(String insName) { + if (!nameToDef.containsKey(insName)) { + return null; + } + return getDocsForIns(nameToDef.get(insName)); + } + + public static String getDocsForIns(InstructionDefinition def) { + String v = docsCache.get(def.instructionName); + if (v != null) { + return v; + } + + StringBuilder sb = new StringBuilder(); + String insName = def.instructionName; + final String NEWLINE = "\r\n"; + + String insShortDescription = prop.getProperty("instruction." + insName + ".shortDescription"); + String insDescription = prop.getProperty("instruction." + insName + ".description"); + String stackBefore = def.hasFlag(AVM2InstructionFlag.UNKNOWN_STACK) ? "???" : prop.getProperty("instruction." + insName + ".stackBefore"); + String stackAfter = def.hasFlag(AVM2InstructionFlag.UNKNOWN_STACK) ? "???" : prop.getProperty("instruction." + insName + ".stackAfter"); + String operandsDoc = def.hasFlag(AVM2InstructionFlag.UNKNOWN_OPERANDS) ? "???" : prop.getProperty("instruction." + insName + ".operands"); + + sb.append(String.format("0x%02X", def.instructionCode)).append(" ").append(insName).append(": ").append(insShortDescription).append(NEWLINE); + + if (!insDescription.trim().isEmpty()) { + sb.append("Description: ").append(insDescription).append(NEWLINE); + } + sb.append("Stack before: ").append(stackBefore).append(NEWLINE); + sb.append("Stack after: ").append(stackAfter).append(NEWLINE); + boolean flagsPrinted = false; + + sb.append("Operands: "); + + if (def.hasFlag(AVM2InstructionFlag.UNKNOWN_OPERANDS)) { + sb.append("???").append(NEWLINE); + } else { + String[] operandsDocs = operandsDoc.split(", ?"); + boolean first = true; + for (int i = 0; i < def.operands.length; i++) { + int op = def.operands[i]; + String opDoc = operandsDocs[i]; + String operandTypeRaw = AVM2Code.operandTypeToString(op, false); + String operandTypeCombined = AVM2Code.operandTypeToString(op, true); + if (operandTypeCombined.contains(", ")) { + String operandTypesCombined[] = operandTypeCombined.split(", ?"); + for (int j = 0; j < operandTypesCombined.length; j++) { + if (!first) { + sb.append(", "); + } else { + first = false; + } + opDoc = operandsDocs[i + j]; + operandTypeCombined = operandTypesCombined[j]; + if (opDoc.equals("...")) { + sb.append("..."); + } else { + sb.append(opDoc).append(":").append(operandTypeCombined); + } + } + } else { + if (!first) { + sb.append(", "); + } else { + first = false; + } + if (opDoc.equals(operandTypeRaw)) { + sb.append(operandTypeCombined); + } else { + sb.append(opDoc).append(":").append(operandTypeCombined); + } + } + } + sb.append(NEWLINE); + } + + AVM2InstructionFlag flags[] = def.flags.clone(); + Arrays.sort(flags, Enum::compareTo); + + for (AVM2InstructionFlag fl : flags) { + if (fl != AVM2InstructionFlag.UNKNOWN_OPERANDS && fl != AVM2InstructionFlag.UNKNOWN_STACK) { + if (!flagsPrinted) { + flagsPrinted = true; + sb.append("Flags:").append(NEWLINE); + } + sb.append(" - ").append(flagDescriptions.get(fl)).append(NEWLINE); + } + } + String r = sb.toString(); + docsCache.put(def.instructionName, r); + return r; + } + + public static void main(String[] args) throws IOException { + + for (InstructionDefinition def : AVM2Code.allInstructionSet) { + if (def == null) { + continue; + } + System.out.println("========================="); + System.out.print(getDocsForIns(def)); + } + } +} diff --git a/src/com/jpexs/decompiler/flash/gui/DocsWindow.java b/src/com/jpexs/decompiler/flash/gui/DocsWindow.java new file mode 100644 index 000000000..b9f24c4d7 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/DocsWindow.java @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2016 Jindra + * + * 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 . + */ +package com.jpexs.decompiler.flash.gui; + +import com.jpexs.decompiler.flash.gui.abc.DocsListener; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.HeadlessException; +import java.awt.Point; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.swing.JEditorPane; +import javax.swing.JFrame; +import javax.swing.JRootPane; +import javax.swing.JScrollPane; + +/** + * + * @author JPEXS + */ +public class DocsWindow extends JFrame implements DocsListener { + + private JEditorPane textDisplay = new JEditorPane(); + + public DocsWindow() { + setAlwaysOnTop(true); + setSize(500, 250); + setTitle("-"); + getContentPane().setLayout(new BorderLayout()); + getContentPane().add(new JScrollPane(textDisplay), BorderLayout.CENTER); + textDisplay.setContentType("text/html"); + setAutoRequestFocus(false); + textDisplay.setFocusable(false); + View.setWindowIcon(this); + this.getRootPane().setWindowDecorationStyle(JRootPane.PLAIN_DIALOG); + textDisplay.setBackground(Color.white); + } + + @Override + public void docs(String identifier, String docs, Point screenLocation) { + setTitle(identifier); + textDisplay.setText(docs.replace("\r\n", "
")); + if (screenLocation != null) { + setLocation(screenLocation); + } + + setFocusableWindowState(false); + setVisible(true); + View.execInEventDispatchLater(new Runnable() { + @Override + public void run() { + setFocusableWindowState(true); + } + }); + + } + + @Override + public void noDocs() { + setVisible(false); + setTitle("-"); + textDisplay.setText(""); + } + +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java b/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java index a84e6e978..d79622b58 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/ASMSourceEditorPane.java @@ -20,9 +20,11 @@ import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.avm2.AVM2Code; import com.jpexs.decompiler.flash.abc.avm2.exceptions.AVM2ExecutionException; import com.jpexs.decompiler.flash.abc.avm2.graph.AVM2Graph; +import com.jpexs.decompiler.flash.abc.avm2.instructions.InstructionDefinition; import com.jpexs.decompiler.flash.abc.avm2.parser.AVM2ParseException; import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.ASM3Parser; import com.jpexs.decompiler.flash.abc.avm2.parser.pcode.MissingSymbolHandler; +import com.jpexs.decompiler.flash.abc.avm2.parser.script.Reference; import com.jpexs.decompiler.flash.abc.types.Decimal; import com.jpexs.decompiler.flash.abc.types.Float4; import com.jpexs.decompiler.flash.abc.types.MethodBody; @@ -36,19 +38,24 @@ import com.jpexs.decompiler.flash.helpers.HighlightedText; import com.jpexs.decompiler.flash.helpers.HighlightedTextWriter; import com.jpexs.decompiler.flash.helpers.hilight.HighlightSpecialType; import com.jpexs.decompiler.flash.helpers.hilight.Highlighting; +import com.jpexs.decompiler.flash.locales.docs.pcode.As3Docs; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import com.jpexs.decompiler.flash.tags.Tag; import com.jpexs.decompiler.graph.ScopeStack; import com.jpexs.helpers.Helper; +import java.awt.Point; import java.io.IOException; import java.io.StringReader; import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.SwingUtilities; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; +import jsyntaxpane.SyntaxDocument; /** * @@ -70,6 +77,8 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi private List specialHilights = new ArrayList<>(); + private List docsListeners = new ArrayList<>(); + private final DecompiledEditorPane decompiledEditor; private boolean ignoreCarret = false; @@ -88,6 +97,16 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi private int firstInstrLine = -1; + private Map insNameToDef = new HashMap<>(); + + public void addDocsListener(DocsListener l) { + docsListeners.add(l); + } + + public void removeDocsListener(DocsListener l) { + docsListeners.remove(l); + } + public ABCPanel getAbcPanel() { return decompiledEditor.getAbcPanel(); } @@ -139,6 +158,11 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi public ASMSourceEditorPane(DecompiledEditorPane decompiledEditor) { this.decompiledEditor = decompiledEditor; addCaretListener(this); + for (InstructionDefinition def : AVM2Code.instructionSet) { + if (def != null) { + insNameToDef.put(def.instructionName, def); + } + } } public void hilighSpecial(HighlightSpecialType type, String specialValue) { @@ -383,9 +407,53 @@ public class ASMSourceEditorPane extends DebuggableEditorPane implements CaretLi return lastH == null ? 0 : lastH.getProperties().offset; } + private void fireDocs(String identifier, String value, Point screenLocation) { + for (DocsListener l : docsListeners) { + l.docs(identifier, value, screenLocation); + } + } + + private void fireNoDocs() { + for (DocsListener l : docsListeners) { + l.noDocs(); + } + } + + public void caretUpdateEdit(CaretEvent e) { + String curLine = getCurrentLineText(); + + if (curLine == null) { + return; + } + //strip labels, e.g. ofs123:pushint 25 + if (curLine.matches("^\\p{L}+:")) { + curLine = curLine.substring(curLine.indexOf(":") + 1).trim(); + } + + //strip instruction arguments, we want only its name + if (curLine.contains(" ")) { + curLine = curLine.substring(0, curLine.indexOf(" ")); + } + //strip comments, e.g. pushnull;comment + if (curLine.contains(";")) { + curLine = curLine.substring(0, curLine.indexOf(";")); + } + String insName = curLine.toLowerCase(); + if (insNameToDef.containsKey(insName)) { + Point loc = getLineLocation(getLine() + 1); + if (loc != null) { + SwingUtilities.convertPointToScreen(loc, this); + } + fireDocs(insName, As3Docs.getDocsForIns(insName), loc); + } else { + fireNoDocs(); + } + } + @Override public void caretUpdate(CaretEvent e) { if (isEditable()) { + caretUpdateEdit(e); return; } if (ignoreCarret) { diff --git a/src/com/jpexs/decompiler/flash/gui/abc/DocsListener.java b/src/com/jpexs/decompiler/flash/gui/abc/DocsListener.java new file mode 100644 index 000000000..50227cb06 --- /dev/null +++ b/src/com/jpexs/decompiler/flash/gui/abc/DocsListener.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2016 Jindra + * + * 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 . + */ +package com.jpexs.decompiler.flash.gui.abc; + +import java.awt.Point; + +/** + * + * @author JPEXS + */ +public interface DocsListener { + + public void docs(String identifier, String docs, Point screenLocation); + + public void noDocs(); +} diff --git a/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java b/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java index 97a228bf4..84188b925 100644 --- a/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java +++ b/src/com/jpexs/decompiler/flash/gui/abc/MethodCodePanel.java @@ -18,8 +18,10 @@ package com.jpexs.decompiler.flash.gui.abc; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.types.traits.Trait; +import com.jpexs.decompiler.flash.configuration.Configuration; import com.jpexs.decompiler.flash.exporters.modes.ScriptExportMode; import com.jpexs.decompiler.flash.gui.AppStrings; +import com.jpexs.decompiler.flash.gui.DocsWindow; import com.jpexs.decompiler.flash.gui.Main; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.gui.controls.NoneSelectedButtonGroup; @@ -49,6 +51,7 @@ public class MethodCodePanel extends JPanel { private final JToggleButton hexButton; private final JToggleButton hexOnlyButton; + private final DocsWindow docsWindow; public void refreshMarkers() { sourceTextArea.refreshMarkers(); @@ -143,6 +146,11 @@ public class MethodCodePanel extends JPanel { buttonsPanel.add(new JPanel()); add(buttonsPanel, BorderLayout.NORTH); + docsWindow = new DocsWindow(); + + if (Configuration.as3pcodeDocWindow.get()) { + sourceTextArea.addDocsListener(docsWindow); + } } private void graphButtonActionPerformed(ActionEvent evt) { @@ -183,10 +191,8 @@ public class MethodCodePanel extends JPanel { ScriptExportMode exportMode = getExportMode(); if (val) { sourceTextArea.setHex(exportMode == ScriptExportMode.HEX ? ScriptExportMode.HEX : ScriptExportMode.PCODE, false); - } else { - if (exportMode != ScriptExportMode.PCODE) { - sourceTextArea.setHex(exportMode, false); - } + } else if (exportMode != ScriptExportMode.PCODE) { + sourceTextArea.setHex(exportMode, false); } sourceTextArea.setEditable(val); diff --git a/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java b/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java index 1b66104df..e8e5ddf51 100644 --- a/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java +++ b/src/com/jpexs/decompiler/flash/gui/editor/LineMarkedEditorPane.java @@ -39,6 +39,8 @@ import java.util.Map; import java.util.Objects; import java.util.SortedSet; import java.util.TreeSet; +import java.util.logging.Level; +import java.util.logging.Logger; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.plaf.TextUI; @@ -204,7 +206,10 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan } public int getLine() { - return lastLine; + int caretPosition = getCaretPosition(); + Element root = getDocument().getDefaultRootElement(); + int currentLine = root.getElementIndex(caretPosition); + return currentLine; } public void markError() { @@ -212,7 +217,20 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan } public void gotoLine(int line) { - setCaretPosition(ActionUtils.getDocumentPosition(this, line, 0)); + setCaretPosition(ActionUtils.getDocumentPosition(this, line + 1, 0)); + } + + public Point getLineLocation(int line) { + int pos = ActionUtils.getDocumentPosition(this, line + 1, 0); + if (pos < 0) { + return null; + } + try { + Rectangle r = modelToView(pos); + return new Point(r.x, r.y); + } catch (BadLocationException ex) { + return null; + } } private void getLineBounds(int line, Reference lineStart, Reference lineEnd) { @@ -223,7 +241,7 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan } catch (BadLocationException ex) { //ignore } - int lineCnt = 1; + int lineCnt = 0; int lineStartVal = 0; int lineEndVal = -1; for (int i = 0; i < text.length(); i++) { @@ -237,9 +255,9 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan } } } - if (lineCnt == 1) { + if (lineCnt == 0) { lineEndVal = text.length() - 1; - if (line > 1) { + if (line > 0) { lineStartVal = text.length() - 1; } } @@ -256,6 +274,21 @@ public class LineMarkedEditorPane extends UndoFixedEditorPane implements LinkHan requestFocus(); } + public String getCurrentLineText() { + return getLineText(getLine()); + } + + public String getLineText(int line) { + Reference lineStart = new Reference<>(0); + Reference lineEnd = new Reference<>(0); + getLineBounds(line, lineStart, lineEnd); + try { + return getDocument().getText(lineStart.getVal(), lineEnd.getVal() - lineStart.getVal()); + } catch (BadLocationException ex) { + return null; + } + } + public LineMarkedEditorPane() { setOpaque(false); addCaretListener(new CaretListener() {