From 33505ff308c83dd3970ec7fd0b4185d8d92b15e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jindra=20Pet=F8=EDk?= Date: Sun, 19 May 2013 21:06:40 +0200 Subject: [PATCH] Issue #85 - AS3 Text search --- .../decompiler/flash/SWFInputStream.java | 8 +- .../decompiler/flash/abc/gui/ABCPanel.java | 41 ++- .../flash/abc/gui/DecompiledEditorPane.java | 118 ++++++--- .../jpexs/decompiler/flash/action/Action.java | 4 +- .../flash/action/gui/ActionPanel.java | 243 ++++++++++++++++-- .../jpexs/decompiler/flash/gui/MainFrame.java | 52 ++-- .../decompiler/flash/gui/SearchDialog.java | 103 ++++++++ .../decompiler/flash/gui/TagTreeModel.java | 38 +++ 8 files changed, 510 insertions(+), 97 deletions(-) create mode 100644 trunk/src/com/jpexs/decompiler/flash/gui/SearchDialog.java diff --git a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java index d06b29fe1..fedbea697 100644 --- a/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java +++ b/trunk/src/com/jpexs/decompiler/flash/SWFInputStream.java @@ -536,8 +536,8 @@ public class SWFInputStream extends InputStream { if (visited.contains(ip)) { break; } - for (DisassemblyListener listener : listeners) { - listener.progress("constantpool", ip + 1, code.size()); + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).progress("constantpool", ip + 1, code.size()); } lastIp = ip; GraphSourceItem ins = code.get(ip); @@ -806,8 +806,8 @@ public class SWFInputStream extends InputStream { Scanner sc = new Scanner(System.in); int prevIp = ip; while (((endip == -1) || (endip > ip)) && (a = sis.readAction(rri)) != null) { - for (DisassemblyListener listener : listeners) { - listener.progress("Reading", rri.getCount(), rri.length()); + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).progress("Reading", rri.getCount(), rri.length()); } if ((ip < ret.size()) && (!(ret.get(ip) instanceof ActionNop))) { a = ret.get(ip); diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/gui/ABCPanel.java b/trunk/src/com/jpexs/decompiler/flash/abc/gui/ABCPanel.java index 60bcaa40d..4e1858ad2 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/gui/ABCPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/gui/ABCPanel.java @@ -20,6 +20,7 @@ import com.jpexs.decompiler.flash.Main; import com.jpexs.decompiler.flash.abc.ABC; import com.jpexs.decompiler.flash.abc.ScriptPack; import com.jpexs.decompiler.flash.abc.gui.tablemodels.*; +import com.jpexs.decompiler.flash.gui.TagTreeModel; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import java.awt.BorderLayout; @@ -33,13 +34,16 @@ import java.util.HashMap; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import java.util.regex.Pattern; import javax.swing.*; import javax.swing.border.BevelBorder; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.table.*; +import javax.swing.tree.TreePath; import jsyntaxpane.DefaultSyntaxKit; import jsyntaxpane.actions.DocumentSearchData; +import jsyntaxpane.actions.gui.QuickFindDialog; public class ABCPanel extends JPanel implements ItemListener, ActionListener { @@ -68,15 +72,29 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener { private int foundPos = 0; private JLabel searchForLabel; private String searchFor; + private boolean searchIgnoreCase; + private boolean searchRegexp; - public boolean search(String txt) { + public boolean search(String txt, boolean ignoreCase, boolean regexp) { if ((txt != null) && (!txt.equals(""))) { + searchIgnoreCase = ignoreCase; + searchRegexp = regexp; ClassesListTreeModel clModel = (ClassesListTreeModel) classTree.getModel(); HashMap allpacks = clModel.getList(); + found = new ArrayList(); + Pattern pat = null; + if (regexp) { + pat = Pattern.compile(txt, ignoreCase ? Pattern.CASE_INSENSITIVE : 0); + } else { + pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? Pattern.CASE_INSENSITIVE : 0); + } for (ScriptPack p : allpacks.values()) { decompiledTextArea.cacheScriptPack(p, list); + if (pat.matcher(decompiledTextArea.getCachedText(p)).find()) { + found.add(p); + } } - found = decompiledTextArea.searchCache(txt); + //found = decompiledTextArea.searchCache(txt, ignoreCase, regexp); if (found.isEmpty()) { searchPanel.setVisible(false); return false; @@ -324,9 +342,6 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener { searchPanel.add(searchPos); searchPanel.add(nextSearchButton); searchPanel.add(cancelSearchButton); - - - searchPanel.setVisible(false); JLabel picLabel = new JLabel(View.getIcon("search16")); filterPanel.add(picLabel, BorderLayout.EAST); @@ -429,10 +444,22 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener { public void updateSearchPos() { searchPos.setText((foundPos + 1) + "/" + found.size()); decompiledTextArea.setScript(found.get(foundPos), list); + TagTreeModel ttm = (TagTreeModel) Main.mainFrame.tagTree.getModel(); + TreePath tp = ttm.getTagPath(found.get(foundPos)); + Main.mainFrame.tagTree.setSelectionPath(tp); + Main.mainFrame.tagTree.scrollPathToVisible(tp); decompiledTextArea.setCaretPosition(0); + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + Logger.getLogger(ABCPanel.class.getName()).log(Level.SEVERE, null, ex); + } DocumentSearchData dsd = DocumentSearchData.getFromEditor(decompiledTextArea); - dsd.setPattern(searchFor, false, false); - dsd.showQuickFindDialog(decompiledTextArea); + dsd.setPattern(searchFor, searchRegexp, searchIgnoreCase); + QuickFindDialog quickFindDlg = new QuickFindDialog(decompiledTextArea, dsd); + quickFindDlg.setRegularExpression(searchRegexp); + quickFindDlg.setIgnoreCase(searchIgnoreCase); + quickFindDlg.showFor(decompiledTextArea); } @Override diff --git a/trunk/src/com/jpexs/decompiler/flash/abc/gui/DecompiledEditorPane.java b/trunk/src/com/jpexs/decompiler/flash/abc/gui/DecompiledEditorPane.java index f2e6cf35a..0b5b3ce16 100644 --- a/trunk/src/com/jpexs/decompiler/flash/abc/gui/DecompiledEditorPane.java +++ b/trunk/src/com/jpexs/decompiler/flash/abc/gui/DecompiledEditorPane.java @@ -25,9 +25,13 @@ import com.jpexs.decompiler.flash.helpers.Highlighting; import com.jpexs.decompiler.flash.tags.ABCContainerTag; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; +import java.util.Set; import java.util.Timer; import java.util.TimerTask; +import java.util.regex.Pattern; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; @@ -215,20 +219,59 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL private class CachedDecompilation { public String text; - public List highlights; - public List traitHighlights; - public List methodHighlights; - public List classHighlights; + public String hilightedText; - public CachedDecompilation(String text, List highlights, List traitHighlights, List methodHighlights, List classHighlights) { - this.text = text; - this.highlights = highlights; - this.traitHighlights = traitHighlights; - this.methodHighlights = methodHighlights; - this.classHighlights = classHighlights; + public List getHighlights() { + return Highlighting.getInstrHighlights(hilightedText); + } + + public List getTraitHighlights() { + return Highlighting.getTraitHighlights(hilightedText); + } + + public List getMethodHighlights() { + return Highlighting.getMethodHighlights(hilightedText); + } + + public List getClassHighlights() { + return Highlighting.getClassHighlights(hilightedText); + } + + public CachedDecompilation(String hilightedText) { + this.hilightedText = hilightedText; + this.text = Highlighting.stripHilights(hilightedText); } } - private HashMap decompilationCache = new HashMap(); + private List cacheKeys = new LinkedList(); + private List cacheValues = new LinkedList(); + + private void uncache(ScriptPack pack) { + for (int i = 0; i < cacheKeys.size(); i++) { + if (cacheKeys.get(i) == pack) { + cacheValues.remove(i); + cacheKeys.remove(i); + break; + } + } + } + + private CachedDecompilation getCached(ScriptPack pack) { + for (int i = 0; i < cacheKeys.size(); i++) { + if (cacheKeys.get(i) == pack) { + return cacheValues.get(i); + } + } + return null; + } + + public String getCachedText(ScriptPack pack) { + for (int i = 0; i < cacheKeys.size(); i++) { + if (cacheKeys.get(i) == pack) { + return cacheValues.get(i).text; + } + } + return null; + } public void gotoLastTrait() { gotoTrait(lastTraitIndex); @@ -283,21 +326,12 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL private List abcList; public void clearScriptCache() { - decompilationCache.clear(); - } - - public List searchCache(String str) { - List found = new ArrayList(); - for (ScriptPack pack : decompilationCache.keySet()) { - CachedDecompilation cs = decompilationCache.get(pack); - if (cs.text.contains(str)) { - found.add(pack); - } - } - return found; + cacheKeys.clear(); + cacheValues.clear(); } public void cacheScriptPack(ScriptPack scriptLeaf, List abcList) { + int maxCacheSize = 50; int scriptIndex = scriptLeaf.scriptIndex; StringBuilder hilightedCodeBuf = new StringBuilder(); String hilightedCode = ""; @@ -306,17 +340,24 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL if (scriptIndex > -1) { script = abc.script_info[scriptIndex]; } - if (!decompilationCache.containsKey(scriptLeaf)) { + boolean found = false; + for (int i = 0; i < cacheKeys.size(); i++) { + if (cacheKeys.get(i) == scriptLeaf) { + found = true; + break; + } + } + if (!found) { + cacheKeys.add(scriptLeaf); for (int scriptTraitIndex : scriptLeaf.traitIndices) { hilightedCodeBuf.append(script.traits.traits[scriptTraitIndex].convertPackaged("", abcList, abc, false, false, scriptIndex, -1, true, new ArrayList())); } hilightedCode = hilightedCodeBuf.toString(); - List highlights = Highlighting.getInstrHighlights(hilightedCode); - List traitHighlights = Highlighting.getTraitHighlights(hilightedCode); - List methodHighlights = Highlighting.getMethodHighlights(hilightedCode); - List classHighlights = Highlighting.getClassHighlights(hilightedCode); - hilightedCode = Highlighting.stripHilights(hilightedCode); - decompilationCache.put(scriptLeaf, new CachedDecompilation(hilightedCode, highlights, traitHighlights, methodHighlights, classHighlights)); + cacheValues.add(new CachedDecompilation(hilightedCode)); + } + if (cacheKeys.size() > maxCacheSize) { + cacheKeys.remove(0); + cacheValues.remove(0); } } @@ -338,12 +379,12 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL String hilightedCode = ""; cacheScriptPack(scriptLeaf, abcList); - CachedDecompilation cd = decompilationCache.get(scriptLeaf); + CachedDecompilation cd = getCached(scriptLeaf); hilightedCode = cd.text; - highlights = cd.highlights; - traitHighlights = cd.traitHighlights; - methodHighlights = cd.methodHighlights; - classHighlights = cd.classHighlights; + highlights = cd.getHighlights(); + traitHighlights = cd.getTraitHighlights(); + methodHighlights = cd.getMethodHighlights(); + classHighlights = cd.getClassHighlights(); this.abc = abc; this.abcList = abcList; this.script = scriptLeaf; @@ -352,9 +393,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL public void reloadClass() { int ci = classIndex; - if (decompilationCache.containsKey(script)) { - decompilationCache.remove(script); - } + uncache(script); if ((script != null) && (abc != null)) { setScript(script, abcList); } @@ -368,7 +407,8 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL public void setABC(ABC abc) { this.abc = abc; - decompilationCache.clear(); + cacheKeys.clear(); + cacheValues.clear(); setText(""); } } diff --git a/trunk/src/com/jpexs/decompiler/flash/action/Action.java b/trunk/src/com/jpexs/decompiler/flash/action/Action.java index 391d55bbb..9698a0cda 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/Action.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/Action.java @@ -402,8 +402,8 @@ public class Action implements GraphSourceItem { boolean lastPush = false; StringBuilder ret = new StringBuilder(); for (GraphSourceItem s : srcList) { - for (DisassemblyListener l : listeners) { - l.progress("toString", pos + 2, srcList.size()); + for (int i = 0; i < listeners.size(); i++) { + listeners.get(i).progress("toString", pos + 2, srcList.size()); } Action a = null; if (s instanceof Action) { diff --git a/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java b/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java index 979475508..9b0dc2da3 100644 --- a/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java +++ b/trunk/src/com/jpexs/decompiler/flash/action/gui/ActionPanel.java @@ -19,13 +19,18 @@ package com.jpexs.decompiler.flash.action.gui; import com.jpexs.decompiler.flash.DisassemblyListener; import com.jpexs.decompiler.flash.Main; import com.jpexs.decompiler.flash.SWF; +import com.jpexs.decompiler.flash.abc.ScriptPack; +import com.jpexs.decompiler.flash.abc.gui.DecompiledEditorPane; import com.jpexs.decompiler.flash.abc.gui.LineMarkedEditorPane; +import com.jpexs.decompiler.flash.action.Action; import com.jpexs.decompiler.flash.action.ActionGraph; import com.jpexs.decompiler.flash.action.parser.ParseException; import com.jpexs.decompiler.flash.action.parser.pcode.ASMParser; import com.jpexs.decompiler.flash.action.parser.script.ActionScriptParser; import com.jpexs.decompiler.flash.graph.GraphTargetItem; import com.jpexs.decompiler.flash.gui.GraphFrame; +import com.jpexs.decompiler.flash.gui.TagNode; +import com.jpexs.decompiler.flash.gui.TagTreeModel; import com.jpexs.decompiler.flash.gui.View; import com.jpexs.decompiler.flash.helpers.Helper; import com.jpexs.decompiler.flash.helpers.Highlighting; @@ -41,14 +46,27 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; -import javax.swing.*; +import java.util.regex.Pattern; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.JToggleButton; +import javax.swing.SwingConstants; import javax.swing.border.BevelBorder; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; +import javax.swing.tree.TreePath; import jsyntaxpane.DefaultSyntaxKit; +import jsyntaxpane.actions.DocumentSearchData; +import jsyntaxpane.actions.gui.QuickFindDialog; public class ActionPanel extends JPanel implements ActionListener { @@ -83,6 +101,108 @@ public class ActionPanel extends JPanel implements ActionListener { private String srcWithHex; private String srcNoHex; private String lastDecompiled = ""; + public JPanel searchPanel; + public JLabel searchPos; + private List found = new ArrayList(); + private int foundPos = 0; + private JLabel searchForLabel; + private String searchFor; + private List cacheKeys = new LinkedList(); + private List cacheValues = new LinkedList(); + //private HashMap cache = new HashMap(); + private boolean searchIgnoreCase; + private boolean searchRegexp; + + private CachedScript getCached(ASMSource pack) { + for (int i = 0; i < cacheKeys.size(); i++) { + if (cacheKeys.get(i) == pack) { + return cacheValues.get(i); + } + } + return null; + } + + private void cacheScript(ASMSource src) { + int maxCacheSize = 20; + for (int i = 0; i < cacheKeys.size(); i++) { + if (cacheKeys.get(i) == src) { + return; + } + } + List as = src.getActions(SWF.DEFAULT_VERSION); + String s = com.jpexs.decompiler.flash.action.Action.actionsToSource(as, SWF.DEFAULT_VERSION); + List hilights = Highlighting.getInstrHighlights(s); + String srcNoHex = Highlighting.stripHilights(s); + cacheKeys.add(src); + cacheValues.add(new CachedScript(srcNoHex, hilights, as)); + if (cacheKeys.size() > maxCacheSize) { + cacheKeys.remove(0); + cacheValues.remove(0); + } + + } + + private static class CachedScript { + + public String text; + List hilights; + List actions; + + public CachedScript(String text, List hilights, List actions) { + this.text = text; + this.hilights = hilights; + this.actions = actions; + } + } + + private List getASMs(List nodes) { + List ret = new ArrayList(); + for (TagNode n : nodes) { + if (n.tag instanceof ASMSource) { + //cacheScript((ASMSource) n.tag); + ret.add((ASMSource) n.tag); + } + ret.addAll(getASMs(n.subItems)); + } + return ret; + } + + public boolean search(String txt, boolean ignoreCase, boolean regexp) { + if ((txt != null) && (!txt.equals(""))) { + searchIgnoreCase = ignoreCase; + searchRegexp = regexp; + List tags = new ArrayList(Main.swf.tags); + List list = Main.swf.createASTagList(tags, null); + List asms = getASMs(list); + found = new ArrayList(); + Pattern pat = null; + if (regexp) { + pat = Pattern.compile(txt, ignoreCase ? Pattern.CASE_INSENSITIVE : 0); + } else { + pat = Pattern.compile(Pattern.quote(txt), ignoreCase ? Pattern.CASE_INSENSITIVE : 0); + } + for (ASMSource s : asms) { + cacheScript(s); + if (pat.matcher(getCached(s).text).find()) { + found.add(s); + } + } + + if (found.isEmpty()) { + searchPanel.setVisible(false); + return false; + } else { + foundPos = 0; + setSource(found.get(foundPos), true); + searchPanel.setVisible(true); + searchFor = txt; + updateSearchPos(); + searchForLabel.setText("Search for \"" + txt + "\" : "); + } + return true; + } + return false; + } public void setText(String text) { int pos = editor.getCaretPosition(); @@ -116,7 +236,7 @@ public class ActionPanel extends JPanel implements ActionListener { setText(hex ? srcWithHex : srcNoHex); } - public void setSource(ASMSource src) { + public void setSource(ASMSource src, final boolean useCache) { this.src = src; Main.startWork("Decompiling..."); final ASMSource asm = (ASMSource) src; @@ -154,32 +274,21 @@ public class ActionPanel extends JPanel implements ActionListener { setHex(hexButton.isSelected()); if (Main.DO_DECOMPILE) { decompiledEditor.setText("//Decompiling..."); - List as = asm.getActions(SWF.DEFAULT_VERSION); - lastCode = as; - //com.jpexs.decompiler.flash.action.Action.setActionsAddresses(as, 0, SWF.DEFAULT_VERSION); - String s = com.jpexs.decompiler.flash.action.Action.actionsToSource(as, SWF.DEFAULT_VERSION); - decompiledHilights = Highlighting.getInstrHighlights(s); - String stripped = Highlighting.stripHilights(s); - /*try { - ActionScriptParser.parse(stripped); - } catch (ParseException ex) { - JOptionPane.showMessageDialog(null, ex.getMessage()); - Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); - } catch (IOException ex) { - Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); - }*/ - lastDecompiled = stripped; - /*if(lastDecompiled.length()>30000){ - decompiledEditor.setContentType("text/plain"); - }else{ - decompiledEditor.setContentType("text/actionscript"); - }*/ + String stripped = ""; + if (!useCache) { + uncache(asm); + } + cacheScript(asm); + CachedScript sc = getCached(asm); + lastCode = sc.actions; + decompiledHilights = sc.hilights; + lastDecompiled = sc.text; + stripped = lastDecompiled; decompiledEditor.setText(lastDecompiled); if (debugRecompile) { try { ActionScriptParser ps = new ActionScriptParser(); - recompiledEditor.setText(Highlighting.stripHilights(com.jpexs.decompiler.flash.action.Action.actionsToString(new ArrayList(), 0, ps.parse(stripped), null, SWF.DEFAULT_VERSION, false, 0))); } catch (ParseException ex) { Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); @@ -199,7 +308,6 @@ public class ActionPanel extends JPanel implements ActionListener { } public ActionPanel() { - this.list = list; DefaultSyntaxKit.initKit(); editor = new LineMarkedEditorPane(); editor.setEditable(false); @@ -209,6 +317,30 @@ public class ActionPanel extends JPanel implements ActionListener { recompiledEditor = new LineMarkedEditorPane(); recompiledEditor.setEditable(false); + searchPanel = new JPanel(new FlowLayout()); + + JButton prevSearchButton = new JButton(View.getIcon("prev16")); + prevSearchButton.setMargin(new Insets(3, 3, 3, 3)); + prevSearchButton.addActionListener(this); + prevSearchButton.setActionCommand("SEARCHPREV"); + JButton nextSearchButton = new JButton(View.getIcon("next16")); + nextSearchButton.setMargin(new Insets(3, 3, 3, 3)); + nextSearchButton.addActionListener(this); + nextSearchButton.setActionCommand("SEARCHNEXT"); + JButton cancelSearchButton = new JButton(View.getIcon("cancel16")); + cancelSearchButton.setMargin(new Insets(3, 3, 3, 3)); + cancelSearchButton.addActionListener(this); + cancelSearchButton.setActionCommand("SEARCHCANCEL"); + searchPos = new JLabel("0/0"); + searchForLabel = new JLabel("Search for \"\": "); + searchPanel.add(searchForLabel); + searchPanel.add(prevSearchButton); + searchPanel.add(new JLabel("Script ")); + searchPanel.add(searchPos); + searchPanel.add(nextSearchButton); + searchPanel.add(cancelSearchButton); + + JButton graphButton = new JButton(View.getIcon("graph16")); graphButton.setActionCommand("GRAPH"); graphButton.addActionListener(this); @@ -289,9 +421,14 @@ public class ActionPanel extends JPanel implements ActionListener { saveDecompiledButton.setVisible(false); cancelDecompiledButton.setVisible(false); + JPanel decPanel = new JPanel(new BorderLayout()); + decPanel.add(new JScrollPane(decompiledEditor), BorderLayout.CENTER); + decPanel.add(searchPanel, BorderLayout.NORTH); + + searchPanel.setVisible(false); JPanel panA = new JPanel(); panA.setLayout(new BorderLayout()); - panA.add(new JScrollPane(decompiledEditor), BorderLayout.CENTER); + panA.add(decPanel, BorderLayout.CENTER); panA.add(decLabel, BorderLayout.NORTH); panA.add(decButtonsPan, BorderLayout.SOUTH); decLabel.setHorizontalAlignment(SwingConstants.CENTER); @@ -366,7 +503,9 @@ public class ActionPanel extends JPanel implements ActionListener { for (Highlighting h2 : disassembledHilights) { if (h2.offset == h.offset) { ignoreCarret = true; - editor.setCaretPosition(h2.startPos); + if (h2.startPos > 0 && h2.startPos < editor.getText().length()) { + editor.setCaretPosition(h2.startPos); + } editor.getCaret().setVisible(true); ignoreCarret = false; break; @@ -437,6 +576,23 @@ public class ActionPanel extends JPanel implements ActionListener { @Override public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals("SEARCHCANCEL")) { + foundPos = 0; + searchPanel.setVisible(false); + found = new ArrayList(); + searchFor = null; + } + if (e.getActionCommand().equals("SEARCHPREV")) { + foundPos--; + if (foundPos < 0) { + foundPos += found.size(); + } + updateSearchPos(); + } + if (e.getActionCommand().equals("SEARCHNEXT")) { + foundPos = (foundPos + 1) % found.size(); + updateSearchPos(); + } if (e.getActionCommand().equals("GRAPH")) { if (lastCode != null) { GraphFrame gf = new GraphFrame(new ActionGraph(lastCode, new HashMap(), new HashMap(), new HashMap(), SWF.DEFAULT_VERSION), ""); @@ -452,7 +608,7 @@ public class ActionPanel extends JPanel implements ActionListener { } else if (e.getActionCommand().equals("SAVEACTION")) { try { src.setActions(ASMParser.parse(0, src.getPos(), true, new ByteArrayInputStream(editor.getText().getBytes()), SWF.DEFAULT_VERSION), SWF.DEFAULT_VERSION); - setSource(this.src); + setSource(this.src, false); JOptionPane.showMessageDialog(this, "Code successfully saved"); saveButton.setVisible(false); cancelButton.setVisible(false); @@ -472,7 +628,7 @@ public class ActionPanel extends JPanel implements ActionListener { try { ActionScriptParser par = new ActionScriptParser(); src.setActions(par.parse(decompiledEditor.getText()), SWF.DEFAULT_VERSION); - setSource(this.src); + setSource(this.src, false); JOptionPane.showMessageDialog(this, "Code successfully saved"); saveDecompiledButton.setVisible(false); cancelDecompiledButton.setVisible(false); @@ -487,4 +643,35 @@ public class ActionPanel extends JPanel implements ActionListener { } } + + public void updateSearchPos() { + searchPos.setText((foundPos + 1) + "/" + found.size()); + setSource(found.get(foundPos), true); + TagTreeModel ttm = (TagTreeModel) Main.mainFrame.tagTree.getModel(); + TreePath tp = ttm.getTagPath(found.get(foundPos)); + Main.mainFrame.tagTree.setSelectionPath(tp); + Main.mainFrame.tagTree.scrollPathToVisible(tp); + decompiledEditor.setCaretPosition(0); + try { + Thread.sleep(100); + } catch (InterruptedException ex) { + Logger.getLogger(ActionPanel.class.getName()).log(Level.SEVERE, null, ex); + } + DocumentSearchData dsd = DocumentSearchData.getFromEditor(decompiledEditor); + dsd.setPattern(searchFor, searchRegexp, searchIgnoreCase); + QuickFindDialog quickFindDlg = new QuickFindDialog(decompiledEditor, dsd); + quickFindDlg.setRegularExpression(searchRegexp); + quickFindDlg.setIgnoreCase(searchIgnoreCase); + quickFindDlg.showFor(decompiledEditor); + } + + private void uncache(ASMSource pack) { + for (int i = 0; i < cacheKeys.size(); i++) { + if (cacheKeys.get(i) == pack) { + cacheValues.remove(i); + cacheKeys.remove(i); + break; + } + } + } } diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java index 97b905506..bbd922a94 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/MainFrame.java @@ -355,7 +355,7 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi miProxy.setIcon(View.getIcon("proxy16")); miProxy.addActionListener(this); - JMenuItem miSearchScript = new JMenuItem("Search ActionScript..."); + JMenuItem miSearchScript = new JMenuItem("Search All ActionScript..."); miSearchScript.addActionListener(this); miSearchScript.setActionCommand("SEARCHAS"); miSearchScript.setIcon(View.getIcon("search16")); @@ -1132,28 +1132,46 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi public boolean confirmExperimental() { return JOptionPane.showConfirmDialog(null, "Following procedure can damage SWF file which can be then unplayable.\r\nUSE IT ON YOUR OWN RISK. Do you want to continue?", "Warning", JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION; } + private SearchDialog searchDialog; @Override public void actionPerformed(ActionEvent e) { if (e.getActionCommand().equals("SEARCHAS")) { - if (abcPanel != null) { - final String txt = JOptionPane.showInputDialog("Search text:"); - if (txt != null) { + if (searchDialog == null) { + searchDialog = new SearchDialog(); + } + searchDialog.setVisible(true); + if (searchDialog.result) { + final String txt = searchDialog.searchField.getText(); + if (!txt.equals("")) { Main.startWork("Searching \"" + txt + "\"..."); - (new Thread() { - @Override - public void run() { - if (abcPanel.search(txt)) { - showDetail(DETAILCARDAS3NAVIGATOR); - showCard(CARDACTIONSCRIPTPANEL); - } else { - JOptionPane.showMessageDialog(null, "String \"" + txt + "\" not found.", "Not found", JOptionPane.INFORMATION_MESSAGE); + if (abcPanel != null) { + (new Thread() { + @Override + public void run() { + if (abcPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) { + showDetail(DETAILCARDAS3NAVIGATOR); + showCard(CARDACTIONSCRIPTPANEL); + } else { + JOptionPane.showMessageDialog(null, "String \"" + txt + "\" not found.", "Not found", JOptionPane.INFORMATION_MESSAGE); + } + Main.stopWork(); } - Main.stopWork(); - } - }).start(); - + }).start(); + } else { + (new Thread() { + @Override + public void run() { + if (actionPanel.search(txt, searchDialog.ignoreCaseCheckBox.isSelected(), searchDialog.regexpCheckBox.isSelected())) { + showCard(CARDACTIONSCRIPTPANEL); + } else { + JOptionPane.showMessageDialog(null, "String \"" + txt + "\" not found.", "Not found", JOptionPane.INFORMATION_MESSAGE); + } + Main.stopWork(); + } + }).start(); + } } } } @@ -1787,7 +1805,7 @@ public class MainFrame extends JFrame implements ActionListener, TreeSelectionLi showCard(CARDEMPTYPANEL); } else if (tagObj instanceof ASMSource) { showCard(CARDACTIONSCRIPTPANEL); - actionPanel.setSource((ASMSource) tagObj); + actionPanel.setSource((ASMSource) tagObj, true); } else if (tagObj instanceof ImageTag) { imageButtonsPanel.setVisible(((ImageTag) tagObj).importSupported()); showCard(CARDIMAGEPANEL); diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/SearchDialog.java b/trunk/src/com/jpexs/decompiler/flash/gui/SearchDialog.java new file mode 100644 index 000000000..fea22b4a3 --- /dev/null +++ b/trunk/src/com/jpexs/decompiler/flash/gui/SearchDialog.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2013 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 . + */ +package com.jpexs.decompiler.flash.gui; + +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JDialog; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JTextField; + +/** + * + * @author JPEXS + */ +public class SearchDialog extends JDialog implements ActionListener { + + public JTextField searchField = new JTextField(); + public JCheckBox ignoreCaseCheckBox = new JCheckBox("Ignore case"); + public JCheckBox regexpCheckBox = new JCheckBox("Regular expression"); + public boolean result = false; + + public SearchDialog() { + setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE); + Container cnt = getContentPane(); + setSize(400, 150); + cnt.setLayout(new BoxLayout(cnt, BoxLayout.PAGE_AXIS)); + JPanel panButtons = new JPanel(new FlowLayout()); + JButton okButton = new JButton("OK"); + okButton.setActionCommand("OK"); + okButton.addActionListener(this); + JButton cancelButton = new JButton("Cancel"); + cancelButton.setActionCommand("CANCEL"); + cancelButton.addActionListener(this); + panButtons.add(okButton); + panButtons.add(cancelButton); + JPanel panField = new JPanel(new FlowLayout()); + searchField.setPreferredSize(new Dimension(250, 25)); + panField.add(new JLabel("Search text:")); + panField.add(searchField); + cnt.add(panField); + JPanel checkPanel = new JPanel(new FlowLayout()); + checkPanel.add(ignoreCaseCheckBox); + checkPanel.add(regexpCheckBox); + cnt.add(checkPanel); + cnt.add(panButtons); + getRootPane().setDefaultButton(okButton); + View.centerScreen(this); + View.setWindowIcon(this); + setTitle("ActionScript search"); + setModalityType(ModalityType.APPLICATION_MODAL); + } + + @Override + public void setVisible(boolean b) { + if (b) { + result = false; + searchField.requestFocusInWindow(); + } + super.setVisible(b); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getActionCommand().equals("OK")) { + if (regexpCheckBox.isSelected()) { + try { + Pattern pat = Pattern.compile(searchField.getText()); + } catch (PatternSyntaxException ex) { + JOptionPane.showMessageDialog(null, "Invalid pattern", "Error", JOptionPane.ERROR_MESSAGE); + return; + } + } + result = true; + } else { + result = false; + } + setVisible(false); + } +} diff --git a/trunk/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java b/trunk/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java index 301cf9e8d..fdd80a85a 100644 --- a/trunk/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java +++ b/trunk/src/com/jpexs/decompiler/flash/gui/TagTreeModel.java @@ -34,6 +34,44 @@ public class TagTreeModel implements TreeModel { this.list = list; } + private List searchTag(Object obj, Object parent, List path) { + List ret = null; + int cnt = getChildCount(parent); + for (int i = 0; i < cnt; i++) { + Object n = getChild(parent, i); + List newPath = new ArrayList(); + newPath.addAll(path); + newPath.add(n); + + if (n instanceof TreeElement) { + TreeElement te = (TreeElement) n; + Object it = te.getItem(); + if (obj == it) { + return newPath; + } + } + if (n instanceof TagNode) { + TagNode nd = (TagNode) n; + if (nd.tag == obj) { + return newPath; + } + } + ret = searchTag(obj, n, newPath); + if (ret != null) { + return ret; + } + } + return ret; + } + + public TreePath getTagPath(Object obj) { + List path = new ArrayList(); + path.add(getRoot()); + path = searchTag(obj, getRoot(), path); + TreePath tp = new TreePath(path.toArray(new Object[path.size()])); + return tp; + } + public List getNodeList() { return list; }