Issue #302 Find declaration (Ctrl+click, Ctrl+B), Find usages (Ctrl+U)

This commit is contained in:
Jindra Petřík
2014-06-01 17:57:34 +02:00
parent 303935cf4e
commit 95746fd3d7
26 changed files with 389 additions and 157 deletions
@@ -43,16 +43,21 @@ import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JEditorPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRootPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
@@ -62,6 +67,7 @@ import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.plaf.FontUIResource;
import javax.swing.plaf.basic.BasicColorChooserUI;
import javax.swing.text.JTextComponent;
import javax.swing.tree.TreeModel;
import javax.swing.tree.TreePath;
import org.pushingpixels.flamingo.api.common.icon.ImageWrapperResizableIcon;
@@ -498,4 +504,20 @@ public class View {
tree.collapsePath(parent);
}
}
public static void addEditorAction(JEditorPane editor,AbstractAction a,String key, String name, String keyStroke){
KeyStroke ks = KeyStroke.getKeyStroke(keyStroke);
a.putValue(Action.ACCELERATOR_KEY, ks);
a.putValue(Action.NAME, name);
String actionName = key;
ActionMap amap = editor.getActionMap();
InputMap imap = editor.getInputMap(JTextComponent.WHEN_FOCUSED);
imap.put(ks, actionName);
amap.put(actionName, a);
JPopupMenu pmenu = editor.getComponentPopupMenu();
JMenuItem findUsagesMenu = new JMenuItem(a);
pmenu.add(findUsagesMenu);
}
}
@@ -38,6 +38,7 @@ import com.jpexs.decompiler.flash.abc.types.traits.Trait;
import com.jpexs.decompiler.flash.abc.types.traits.TraitMethodGetterSetter;
import com.jpexs.decompiler.flash.abc.types.traits.TraitSlotConst;
import com.jpexs.decompiler.flash.abc.types.traits.Traits;
import com.jpexs.decompiler.flash.abc.usages.MultinameUsage;
import com.jpexs.decompiler.flash.configuration.Configuration;
import com.jpexs.decompiler.flash.gui.HeaderLabel;
import com.jpexs.decompiler.flash.gui.Main;
@@ -67,6 +68,7 @@ import com.jpexs.helpers.CancellableWorker;
import com.jpexs.helpers.Helper;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.Insets;
@@ -74,8 +76,12 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
@@ -87,17 +93,25 @@ import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ActionMap;
import javax.swing.BoxLayout;
import javax.swing.InputMap;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;
import javax.swing.JTable;
import javax.swing.JToggleButton;
import javax.swing.KeyStroke;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.BevelBorder;
@@ -107,10 +121,13 @@ import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.JTextComponent;
import javax.swing.tree.TreePath;
import jsyntaxpane.DefaultSyntaxKit;
import jsyntaxpane.actions.DocumentSearchData;
import jsyntaxpane.actions.QuickFindAction;
import jsyntaxpane.actions.SyntaxAction;
public class ABCPanel extends JPanel implements ItemListener, ActionListener, SearchListener<ABCPanelSearchResult>, Freed {
@@ -449,7 +466,32 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se
});
decompiledTextArea.setContentType("text/actionscript");
decompiledTextArea.setFont(new Font("Monospaced", Font.PLAIN, decompiledTextArea.getFont().getSize()));
View.addEditorAction(decompiledTextArea, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
int multinameIndex = decompiledTextArea.getMultinameUnderCursor();
if(multinameIndex>-1){
UsageFrame usageFrame = new UsageFrame(swf.abcList, abc, multinameIndex, ABCPanel.this,false);
usageFrame.setVisible(true);
}
}
}, "find-usages", AppStrings.translate("abc.action.find-usages"), "control U");
View.addEditorAction(decompiledTextArea, new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
gotoDeclaration();
}
}, "find-declaration", AppStrings.translate("abc.action.find-declaration"), "control B");
CtrlClickHandler cch = new CtrlClickHandler();
decompiledTextArea.addKeyListener(cch);
decompiledTextArea.addMouseListener(cch);
decompiledTextArea.addMouseMotionListener(cch);
JPanel pan2 = new JPanel();
pan2.setLayout(new BorderLayout());
pan2.add((abcComboBox = new JComboBox<>(new ABCComboBoxModel(new ArrayList<ABCContainerTag>()))), BorderLayout.NORTH);
@@ -473,32 +515,10 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se
});
Main.startWork(AppStrings.translate("work.buildingscripttree") + "...");
/* splitPaneTreeVSNavigator = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
treePanel,
navPanel);
splitPaneTreeVSNavigator.setResizeWeight(0.5);
splitPaneTreeVSNavigator.setContinuousLayout(true);*/
tabbedPane = new JTabbedPane();
tabbedPane.addTab(AppStrings.translate("traits"), navPanel);
//tabbedPane.setTabPlacement(JTabbedPane.BOTTOM);
//pan2.add(tabbedPane, BorderLayout.CENTER);
abcComboBox.addItemListener(this);
/*
splitPaneTreeNavVSDecompiledDetail = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,
pan2,
splitPaneDecompiledVSDetail);
splitPaneTreeNavVSDecompiledDetail.setResizeWeight(0);
splitPaneTreeNavVSDecompiledDetail.setContinuousLayout(true);
//pan2.setPreferredSize(new Dimension(300, 200));
add(splitPaneTreeNavVSDecompiledDetail, BorderLayout.CENTER);*/
add(splitPane, BorderLayout.CENTER);
JPanel panConstants = new JPanel();
@@ -523,7 +543,7 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se
}
int multinameIndex = constantTable.convertRowIndexToModel(rowIndex);
if (multinameIndex > 0) {
UsageFrame usageFrame = new UsageFrame(t.swf.abcList, abc, multinameIndex, t);
UsageFrame usageFrame = new UsageFrame(t.swf.abcList, abc, multinameIndex, t, false);
usageFrame.setVisible(true);
}
}
@@ -535,6 +555,99 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se
panConstants.add(new JScrollPane(constantTable), BorderLayout.CENTER);
tabbedPane.addTab(AppStrings.translate("constants"), panConstants);
}
private void gotoDeclaration(){
int multinameIndex = decompiledTextArea.getMultinameUnderCursor();
if(multinameIndex>-1){
List<MultinameUsage> usages = abc.findMultinameDefinition(swf.abcList,multinameIndex);
Multiname m = abc.constants.constant_multiname.get(multinameIndex);
//search other ABC tags if this is not private multiname
if(m.namespace_index>0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE)
{
for(ABCContainerTag at:swf.abcList){
ABC a = at.getABC();
if(a == abc){
continue;
}
int mid=a.constants.getMultinameId(m,false);
if(mid>0){
usages.addAll(a.findMultinameDefinition(swf.abcList, mid));
}
}
}
//more than one? display list
if(usages.size()>1){
UsageFrame usageFrame = new UsageFrame(swf.abcList, abc, multinameIndex, ABCPanel.this,true);
usageFrame.setVisible(true);
}else if(!usages.isEmpty()){ //one
UsageFrame.gotoUsage(ABCPanel.this, usages.get(0));
}
}
}
private class CtrlClickHandler extends KeyAdapter implements MouseListener, MouseMotionListener {
private boolean ctrlDown = false;
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == 17 && !decompiledTextArea.isEditable()){
ctrlDown = true;
decompiledTextArea.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
}
}
@Override
public void keyReleased(KeyEvent e) {
if(e.getKeyCode() == 17){
ctrlDown = false;
decompiledTextArea.setCursor(Cursor.getDefaultCursor());
}
}
@Override
public void mouseClicked(MouseEvent e) {
if(ctrlDown && e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1 && !decompiledTextArea.isEditable()){
ctrlDown = false;
decompiledTextArea.setCursor(Cursor.getDefaultCursor());
gotoDeclaration();
}
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
ctrlDown = false;
decompiledTextArea.setCursor(Cursor.getDefaultCursor());
}
@Override
public void mouseDragged(MouseEvent e) {
}
@Override
public void mouseMoved(MouseEvent e) {
if(ctrlDown && decompiledTextArea.isEditable()){
ctrlDown = false;
decompiledTextArea.setCursor(Cursor.getDefaultCursor());
}
}
}
public void reload() {
switchAbc(listIndex);
@@ -732,12 +845,10 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se
//reload();
} catch (ParseException ex) {
abc.script_info.get(oldIndex).delete(abc, false);
ex.printStackTrace();
View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
decompiledTextArea.gotoLine((int) ex.line);
} catch (CompilationException ex) {
abc.script_info.get(oldIndex).delete(abc, false);
ex.printStackTrace();
View.showMessageDialog(this, AppStrings.translate("error.action.save").replace("%error%", ex.text).replace("%line%", "" + ex.line), AppStrings.translate("error"), JOptionPane.ERROR_MESSAGE);
decompiledTextArea.gotoLine((int) ex.line);
} catch (IOException | InterruptedException ex) {
@@ -847,4 +958,6 @@ public class ABCPanel extends JPanel implements ItemListener, ActionListener, Se
break;
}
}
}
@@ -61,7 +61,23 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL
private boolean isStatic = false;
private final Cache<CachedDecompilation> cache = Cache.getInstance(true);
private Trait currentTrait = null;
private List<Runnable> scriptListeners = new ArrayList<Runnable>();
public void addScriptListener(Runnable l){
scriptListeners.add(l);
}
public void removeScriptListener(Runnable l){
scriptListeners.remove(l);
}
public void fireScript(){
for(int i=0;i<scriptListeners.size();i++){
scriptListeners.get(i).run();
}
}
public Trait getCurrentTrait() {
return currentTrait;
}
@@ -143,7 +159,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL
}
}
public void setClassIndex(int classIndex) {
public synchronized void setClassIndex(int classIndex) {
this.classIndex = classIndex;
}
@@ -491,6 +507,7 @@ public class DecompiledEditorPane extends LineMarkedEditorPane implements CaretL
setContentType("text/actionscript");
setText(hilightedCode);
}
fireScript();
}
public void reloadClass() {
@@ -17,6 +17,8 @@
package com.jpexs.decompiler.flash.gui.abc;
import com.jpexs.decompiler.flash.abc.ABC;
import com.jpexs.decompiler.flash.abc.types.Multiname;
import com.jpexs.decompiler.flash.abc.types.Namespace;
import com.jpexs.decompiler.flash.abc.usages.InsideClassMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.MethodMultinameUsage;
import com.jpexs.decompiler.flash.abc.usages.MultinameUsage;
@@ -51,14 +53,26 @@ public class UsageFrame extends AppFrame implements ActionListener, MouseListene
private final JButton cancelButton = new JButton(translate("button.cancel"));
private final JList usageList;
private final UsageListModel usageListModel;
private final ABC abc;
private final ABCPanel abcPanel;
public UsageFrame(List<ABCContainerTag> abcTags, ABC abc, int multinameIndex, ABCPanel abcPanel) {
public UsageFrame(List<ABCContainerTag> abcTags, ABC abc, int multinameIndex, ABCPanel abcPanel, boolean definitions) {
this.abcPanel = abcPanel;
List<MultinameUsage> usages = abc.findMultinameUsage(multinameIndex);
this.abc = abc;
usageListModel = new UsageListModel(abcTags, abc);
List<MultinameUsage> usages = definitions?abc.findMultinameDefinition(abcTags,multinameIndex):abc.findMultinameUsage(abcTags,multinameIndex);
Multiname m = abc.constants.constant_multiname.get(multinameIndex);
if(m.namespace_index>0 && abc.constants.constant_namespace.get(m.namespace_index).kind != Namespace.KIND_PRIVATE)
{
for(ABCContainerTag at:abcTags){
ABC a = at.getABC();
if(a == abc){
continue;
}
int mid=a.constants.getMultinameId(m,false);
if(mid>0){
usages.addAll(definitions?a.findMultinameDefinition(abcTags, mid):a.findMultinameUsage(abcTags, mid));
}
}
}
usageListModel = new UsageListModel();
for (MultinameUsage u : usages) {
usageListModel.addElement(u);
}
@@ -79,40 +93,57 @@ public class UsageFrame extends AppFrame implements ActionListener, MouseListene
cont.add(new JScrollPane(usageList), BorderLayout.CENTER);
cont.add(buttonsPanel, BorderLayout.SOUTH);
setSize(400, 300);
setTitle(translate("dialog.title") + abc.constants.getMultiname(multinameIndex).getNameWithNamespace(abc.constants));
setTitle((definitions?translate("dialog.title.declaration"):translate("dialog.title")) + abc.constants.getMultiname(multinameIndex).getNameWithNamespace(abc.constants));
View.centerScreen(this);
View.setWindowIcon(this);
}
public static void gotoUsage(final ABCPanel abcPanel,final MultinameUsage usage){
if (usage instanceof InsideClassMultinameUsage) {
final InsideClassMultinameUsage icu = (InsideClassMultinameUsage) usage;
Runnable settrait = new Runnable() {
@Override
public void run() {
abcPanel.decompiledTextArea.removeScriptListener(this);
abcPanel.decompiledTextArea.setClassIndex(icu.classIndex);
if (usage instanceof TraitMultinameUsage) {
TraitMultinameUsage tmu = (TraitMultinameUsage) usage;
int traitIndex;
if (tmu.parentTraitIndex > -1) {
traitIndex = tmu.parentTraitIndex;
} else {
traitIndex = tmu.traitIndex;
}
if (!tmu.isStatic) {
traitIndex += abcPanel.abc.class_info.get(tmu.classIndex).static_traits.traits.size();
}
if (tmu instanceof MethodMultinameUsage) {
MethodMultinameUsage mmu = (MethodMultinameUsage) usage;
if (mmu.isInitializer == true) {
traitIndex = abcPanel.abc.class_info.get(mmu.classIndex).static_traits.traits.size() + abcPanel.abc.instance_info.get(mmu.classIndex).instance_traits.traits.size() + (mmu.isStatic ? 1 : 0);
}
}
abcPanel.decompiledTextArea.gotoTrait(traitIndex);
}
}
};
if(abcPanel.decompiledTextArea.getClassIndex() == icu.classIndex && abcPanel.abc == icu.abc){
settrait.run();
}else{
abcPanel.decompiledTextArea.addScriptListener(settrait);
abcPanel.hilightScript(abcPanel.swf, abcPanel.abc.instance_info.get(icu.classIndex).getName(abcPanel.abc.constants).getNameWithNamespace(abcPanel.abc.constants));
}
}
}
private void gotoUsage() {
if (usageList.getSelectedIndex() != -1) {
MultinameUsage usage = usageListModel.getUsage(usageList.getSelectedIndex());
if (usage instanceof InsideClassMultinameUsage) {
InsideClassMultinameUsage icu = (InsideClassMultinameUsage) usage;
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
}
if (usage instanceof TraitMultinameUsage) {
TraitMultinameUsage tmu = (TraitMultinameUsage) usage;
int traitIndex;
if (tmu.parentTraitIndex > -1) {
traitIndex = tmu.parentTraitIndex;
} else {
traitIndex = tmu.traitIndex;
}
if (!tmu.isStatic) {
traitIndex += abc.class_info.get(tmu.classIndex).static_traits.traits.size();
}
if (tmu instanceof MethodMultinameUsage) {
MethodMultinameUsage mmu = (MethodMultinameUsage) usage;
if (mmu.isInitializer == true) {
traitIndex = abc.class_info.get(mmu.classIndex).static_traits.traits.size() + abc.instance_info.get(mmu.classIndex).instance_traits.traits.size() + (mmu.isStatic ? 1 : 0);
}
}
abcPanel.decompiledTextArea.gotoTrait(traitIndex);
}
}
gotoUsage(abcPanel,usage);
}
}
@@ -30,32 +30,14 @@ import javax.swing.DefaultListModel;
*/
public class UsageListModel extends DefaultListModel<Object> {
private final ABC abc;
private final List<ABCContainerTag> abcTags;
public UsageListModel(List<ABCContainerTag> abcTags, ABC abc) {
this.abc = abc;
this.abcTags = abcTags;
}
@Override
public Object get(int index) {
try {
return ((MultinameUsage) super.get(index)).toString(abcTags, abc);
} catch (InterruptedException ex) {
Logger.getLogger(UsageListModel.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
return ((MultinameUsage) super.get(index)).toString();
}
@Override
public Object getElementAt(int index) {
try {
return ((MultinameUsage) super.getElementAt(index)).toString(abcTags, abc);
} catch (InterruptedException ex) {
Logger.getLogger(UsageListModel.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
return ((MultinameUsage) super.getElementAt(index)).toString();
}
public MultinameUsage getUsage(int index) {
@@ -478,3 +478,6 @@ import.select.directory = Select directory to import
error.text.import = Error during text import. Do you want to continue?
contextmenu.removeWithDependencies = Remove with dependencies
abc.action.find-usages = Find usages
abc.action.find-declaration = Find declaration
@@ -466,4 +466,7 @@ message.action.playerglobal.needed = Pro p\u0159\u00edmou editaci ActionScriptu
message.action.playerglobal.place = St\u00e1hn\u011bte knihovnu nazvanou PlayerGlobal(.swc), a um\u00edst\u011bte j\u00ed do adres\u00e1\u0159e\r\n%libpath%\r\n Stiskn\u011bte OK pro pokra\u010dov\u00e1n\u00ed.
message.confirm.experimental.function = Tato funkce je EXPERIMENT\u00c1LN\u00cd. To znamen\u00e1, \u017ee byste nem\u011bli v\u011b\u0159it jej\u00edm v\u00fdsledk\u016fm a SWF soubor m\u016f\u017ee po ulo\u017een\u00ed p\u0159estat fungovat.
message.confirm.donotshowagain = P\u0159\u00ed\u0161t\u011b nezobrazovat
message.confirm.donotshowagain = P\u0159\u00ed\u0161t\u011b nezobrazovat
abc.action.find-usages = Naj\u00edt pou\u017eit\u00ed
abc.action.find-declaration = Naj\u00edt deklaraci
@@ -16,3 +16,4 @@
button.goto = Go to
button.cancel = Cancel
dialog.title = Usages:
dialog.title.declaration = Declaration:
@@ -16,3 +16,4 @@
button.goto = P\u0159ej\u00edt na
button.cancel = Storno
dialog.title = Pou\u017eit\u00ed:
dialog.title.declaration = Deklarace: