Package cuchaz.enigma.gui

Source Code of cuchaz.enigma.gui.Gui

/*******************************************************************************
* Copyright (c) 2014 Jeff Martin.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the GNU Public License v3.0
* which accompanies this distribution, and is available at
* http://www.gnu.org/licenses/gpl.html
*
* Contributors:
*     Jeff Martin - initial API and implementation
******************************************************************************/
package cuchaz.enigma.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Vector;

import javax.swing.BorderFactory;
import javax.swing.JEditorPane;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
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.JTextField;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Highlighter;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;

import jsyntaxpane.DefaultSyntaxKit;

import com.google.common.collect.Lists;

import cuchaz.enigma.Constants;
import cuchaz.enigma.analysis.BehaviorReferenceTreeNode;
import cuchaz.enigma.analysis.ClassImplementationsTreeNode;
import cuchaz.enigma.analysis.ClassInheritanceTreeNode;
import cuchaz.enigma.analysis.EntryReference;
import cuchaz.enigma.analysis.FieldReferenceTreeNode;
import cuchaz.enigma.analysis.MethodImplementationsTreeNode;
import cuchaz.enigma.analysis.MethodInheritanceTreeNode;
import cuchaz.enigma.analysis.ReferenceTreeNode;
import cuchaz.enigma.analysis.Token;
import cuchaz.enigma.gui.ClassSelector.ClassSelectionListener;
import cuchaz.enigma.mapping.ArgumentEntry;
import cuchaz.enigma.mapping.ClassEntry;
import cuchaz.enigma.mapping.ConstructorEntry;
import cuchaz.enigma.mapping.Entry;
import cuchaz.enigma.mapping.FieldEntry;
import cuchaz.enigma.mapping.IllegalNameException;
import cuchaz.enigma.mapping.MappingParseException;
import cuchaz.enigma.mapping.MethodEntry;

public class Gui
{
  private GuiController m_controller;
 
  // controls
  private JFrame m_frame;
  private ClassSelector m_obfClasses;
  private ClassSelector m_deobfClasses;
  private JEditorPane m_editor;
  private JPanel m_classesPanel;
  private JSplitPane m_splitClasses;
  private JPanel m_infoPanel;
  private ObfuscatedHighlightPainter m_obfuscatedHighlightPainter;
  private DeobfuscatedHighlightPainter m_deobfuscatedHighlightPainter;
  private OtherHighlightPainter m_otherHighlightPainter;
  private SelectionHighlightPainter m_selectionHighlightPainter;
  private JTree m_inheritanceTree;
  private JTree m_implementationsTree;
  private JTree m_callsTree;
  private JList<Token> m_tokens;
  private JTabbedPane m_tabs;
 
  // dynamic menu items
  private JMenuItem m_closeJarMenu;
  private JMenuItem m_openMappingsMenu;
  private JMenuItem m_saveMappingsMenu;
  private JMenuItem m_saveMappingsAsMenu;
  private JMenuItem m_closeMappingsMenu;
  private JMenuItem m_renameMenu;
  private JMenuItem m_showInheritanceMenu;
  private JMenuItem m_openEntryMenu;
  private JMenuItem m_openPreviousMenu;
  private JMenuItem m_showCallsMenu;
  private JMenuItem m_showImplementationsMenu;
  private JMenuItem m_toggleMappingMenu;
  private JMenuItem m_exportSourceMenu;
  private JMenuItem m_exportJarMenu;
 
  // state
  private EntryReference<Entry,Entry> m_reference;
  private JFileChooser m_jarFileChooser;
  private JFileChooser m_mappingsFileChooser;
  private JFileChooser m_exportSourceFileChooser;
  private JFileChooser m_exportJarFileChooser;
 
  public Gui( )
  {
    // init frame
    m_frame = new JFrame( Constants.Name );
    final Container pane = m_frame.getContentPane();
    pane.setLayout( new BorderLayout() );
   
    if( Boolean.parseBoolean( System.getProperty( "enigma.catchExceptions", "true" ) ) )
    {
      // install a global exception handler to the event thread
      CrashDialog.init( m_frame );
      Thread.setDefaultUncaughtExceptionHandler( new UncaughtExceptionHandler( )
      {
        @Override
        public void uncaughtException( Thread thread, Throwable ex )
        {
          ex.printStackTrace( System.err );
          CrashDialog.show( ex );
        }
      } );
    }
   
    m_controller = new GuiController( this );
   
    // init file choosers
    m_jarFileChooser = new JFileChooser();
    m_mappingsFileChooser = new JFileChooser();
    m_exportSourceFileChooser = new JFileChooser();
    m_exportSourceFileChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY );
    m_exportJarFileChooser = new JFileChooser();
   
    // init obfuscated classes list
    m_obfClasses = new ClassSelector( ClassSelector.ObfuscatedClassEntryComparator );
    m_obfClasses.setListener( new ClassSelectionListener( )
    {
      @Override
      public void onSelectClass( ClassEntry classEntry )
      {
        navigateTo( classEntry );
      }
    } );
    JScrollPane obfScroller = new JScrollPane( m_obfClasses );
    JPanel obfPanel = new JPanel();
    obfPanel.setLayout( new BorderLayout() );
    obfPanel.add( new JLabel( "Obfuscated Classes" ), BorderLayout.NORTH );
    obfPanel.add( obfScroller, BorderLayout.CENTER );
   
    // init deobfuscated classes list
    m_deobfClasses = new ClassSelector( ClassSelector.DeobfuscatedClassEntryComparator );
    m_deobfClasses.setListener( new ClassSelectionListener( )
    {
      @Override
      public void onSelectClass( ClassEntry classEntry )
      {
        navigateTo( classEntry );
      }
    } );
    JScrollPane deobfScroller = new JScrollPane( m_deobfClasses );
    JPanel deobfPanel = new JPanel();
    deobfPanel.setLayout( new BorderLayout() );
    deobfPanel.add( new JLabel( "De-obfuscated Classes" ), BorderLayout.NORTH );
    deobfPanel.add( deobfScroller, BorderLayout.CENTER );
   
    // set up classes panel (don't add the splitter yet)
    m_splitClasses = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, obfPanel, deobfPanel );
    m_splitClasses.setResizeWeight( 0.3 );
    m_classesPanel = new JPanel();
    m_classesPanel.setLayout( new BorderLayout() );
    m_classesPanel.setPreferredSize( new Dimension( 250, 0 ) );
   
    // init info panel
    m_infoPanel = new JPanel();
    m_infoPanel.setLayout( new GridLayout( 4, 1, 0, 0 ) );
    m_infoPanel.setPreferredSize( new Dimension( 0, 100 ) );
    m_infoPanel.setBorder( BorderFactory.createTitledBorder( "Identifier Info" ) );
    clearReference();
   
    // init editor
    DefaultSyntaxKit.initKit();
    m_obfuscatedHighlightPainter = new ObfuscatedHighlightPainter();
    m_deobfuscatedHighlightPainter = new DeobfuscatedHighlightPainter();
    m_otherHighlightPainter = new OtherHighlightPainter();
    m_selectionHighlightPainter = new SelectionHighlightPainter();
    m_editor = new JEditorPane();
    m_editor.setEditable( false );
    m_editor.setCaret( new BrowserCaret() );
    JScrollPane sourceScroller = new JScrollPane( m_editor );
    m_editor.setContentType( "text/java" );
    m_editor.addCaretListener( new CaretListener( )
    {
      @Override
      public void caretUpdate( CaretEvent event )
      {
        onCaretMove( event.getDot() );
      }
    } );
    m_editor.addKeyListener( new KeyAdapter( )
    {
      @Override
      public void keyPressed( KeyEvent event )
      {
        switch( event.getKeyCode() )
        {
          case KeyEvent.VK_R:
            m_renameMenu.doClick();
          break;
         
          case KeyEvent.VK_I:
            m_showInheritanceMenu.doClick();
          break;
         
          case KeyEvent.VK_M:
            m_showImplementationsMenu.doClick();
          break;
         
          case KeyEvent.VK_N:
            m_openEntryMenu.doClick();
          break;
         
          case KeyEvent.VK_P:
            m_openPreviousMenu.doClick();
          break;
         
          case KeyEvent.VK_C:
            m_showCallsMenu.doClick();
          break;
         
          case KeyEvent.VK_T:
            m_toggleMappingMenu.doClick();
          break;
        }
      }
    } );
   
    // turn off token highlighting (it's wrong most of the time anyway...)
    DefaultSyntaxKit kit = (DefaultSyntaxKit)m_editor.getEditorKit();
    kit.toggleComponent( m_editor, "jsyntaxpane.components.TokenMarker" );
   
    // init editor popup menu
    JPopupMenu popupMenu = new JPopupMenu();
    m_editor.setComponentPopupMenu( popupMenu );
    {
      JMenuItem menu = new JMenuItem( "Rename" );
      menu.addActionListener( new ActionListener( )
      {
        @Override
        public void actionPerformed( ActionEvent event )
        {
          startRename();
        }
      } );
      menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_R, 0 ) );
      menu.setEnabled( false );
      popupMenu.add( menu );
      m_renameMenu = menu;
    }
    {
      JMenuItem menu = new JMenuItem( "Show Inheritance" );
      menu.addActionListener( new ActionListener( )
      {
        @Override
        public void actionPerformed( ActionEvent event )
        {
          showInheritance();
        }
      } );
      menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_I, 0 ) );
      menu.setEnabled( false );
      popupMenu.add( menu );
      m_showInheritanceMenu = menu;
    }
    {
      JMenuItem menu = new JMenuItem( "Show Implementations" );
      menu.addActionListener( new ActionListener( )
      {
        @Override
        public void actionPerformed( ActionEvent event )
        {
          showImplementations();
        }
      } );
      menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_M, 0 ) );
      menu.setEnabled( false );
      popupMenu.add( menu );
      m_showImplementationsMenu = menu;
    }
    {
      JMenuItem menu = new JMenuItem( "Show Calls" );
      menu.addActionListener( new ActionListener( )
      {
        @Override
        public void actionPerformed( ActionEvent event )
        {
          showCalls();
        }
      } );
      menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_C, 0 ) );
      menu.setEnabled( false );
      popupMenu.add( menu );
      m_showCallsMenu = menu;
    }
    {
      JMenuItem menu = new JMenuItem( "Go to Declaration" );
      menu.addActionListener( new ActionListener( )
      {
        @Override
        public void actionPerformed( ActionEvent event )
        {
          navigateTo( m_reference.entry );
        }
      } );
      menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_N, 0 ) );
      menu.setEnabled( false );
      popupMenu.add( menu );
      m_openEntryMenu = menu;
    }
    {
      JMenuItem menu = new JMenuItem( "Go to previous" );
      menu.addActionListener( new ActionListener( )
      {
        @Override
        public void actionPerformed( ActionEvent event )
        {
          m_controller.openPreviousReference();
        }
      } );
      menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_P, 0 ) );
      menu.setEnabled( false );
      popupMenu.add( menu );
      m_openPreviousMenu = menu;
    }
    {
      JMenuItem menu = new JMenuItem( "Mark as deobfuscated" );
      menu.addActionListener( new ActionListener( )
      {
        @Override
        public void actionPerformed( ActionEvent event )
        {
          toggleMapping();
        }
      } );
      menu.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_T, 0 ) );
      menu.setEnabled( false );
      popupMenu.add( menu );
      m_toggleMappingMenu = menu;
    }
   
    // init inheritance panel
    m_inheritanceTree = new JTree();
    m_inheritanceTree.setModel( null );
    m_inheritanceTree.addMouseListener( new MouseAdapter( )
    {
      @Override
      public void mouseClicked( MouseEvent event )
      {
        if( event.getClickCount() == 2 )
        {
          // get the selected node
          TreePath path = m_inheritanceTree.getSelectionPath();
          if( path == null )
          {
            return;
          }
         
          Object node = path.getLastPathComponent();
          if( node instanceof ClassInheritanceTreeNode )
          {
            ClassInheritanceTreeNode classNode = (ClassInheritanceTreeNode)node;
            navigateTo( new ClassEntry( classNode.getObfClassName() ) );
          }
          else if( node instanceof MethodInheritanceTreeNode )
          {
            MethodInheritanceTreeNode methodNode = (MethodInheritanceTreeNode)node;
            if( methodNode.isImplemented() )
            {
              navigateTo( methodNode.getMethodEntry() );
            }
          }
        }
      }
    } );
    JPanel inheritancePanel = new JPanel();
    inheritancePanel.setLayout( new BorderLayout() );
    inheritancePanel.add( new JScrollPane( m_inheritanceTree ) );
   
    // init implementations panel
    m_implementationsTree = new JTree();
    m_implementationsTree.setModel( null );
    m_implementationsTree.addMouseListener( new MouseAdapter( )
    {
      @Override
      public void mouseClicked( MouseEvent event )
      {
        if( event.getClickCount() == 2 )
        {
          // get the selected node
          TreePath path = m_implementationsTree.getSelectionPath();
          if( path == null )
          {
            return;
          }
         
          Object node = path.getLastPathComponent();
          if( node instanceof ClassImplementationsTreeNode )
          {
            ClassImplementationsTreeNode classNode = (ClassImplementationsTreeNode)node;
            navigateTo( classNode.getClassEntry() );
          }
          else if( node instanceof MethodImplementationsTreeNode )
          {
            MethodImplementationsTreeNode methodNode = (MethodImplementationsTreeNode)node;
            navigateTo( methodNode.getMethodEntry() );
          }
        }
      }
    } );
    JPanel implementationsPanel = new JPanel();
    implementationsPanel.setLayout( new BorderLayout() );
    implementationsPanel.add( new JScrollPane( m_implementationsTree ) );
   
    // init call panel
    m_callsTree = new JTree();
    m_callsTree.setModel( null );
    m_callsTree.addMouseListener( new MouseAdapter( )
    {
      @SuppressWarnings( "unchecked" )
      @Override
      public void mouseClicked( MouseEvent event )
      {
        if( event.getClickCount() == 2 )
        {
          // get the selected node
          TreePath path = m_callsTree.getSelectionPath();
          if( path == null )
          {
            return;
          }
         
          Object node = path.getLastPathComponent();
          if( node instanceof ReferenceTreeNode )
          {
            ReferenceTreeNode<Entry,Entry> referenceNode = ((ReferenceTreeNode<Entry,Entry>)node);
            if( referenceNode.getReference() != null )
            {
              navigateTo( referenceNode.getReference() );
            }
            else
            {
              navigateTo( referenceNode.getEntry() );
            }
          }
        }
      }
    } );
    m_tokens = new JList<Token>();
    m_tokens.setCellRenderer( new TokenListCellRenderer( m_controller ) );
    m_tokens.setSelectionMode( ListSelectionModel.SINGLE_SELECTION );
    m_tokens.setLayoutOrientation( JList.VERTICAL );
    m_tokens.addMouseListener( new MouseAdapter()
    {
      @Override
      public void mouseClicked( MouseEvent event )
      {
        if( event.getClickCount() == 2 )
        {
          Token selected = m_tokens.getSelectedValue();
          if( selected != null )
          {
            showToken( selected );
          }
        }
      }
    } );
    m_tokens.setPreferredSize( new Dimension( 0, 200 ) );
    m_tokens.setMinimumSize( new Dimension( 0, 200 ) );
    JSplitPane callPanel = new JSplitPane( JSplitPane.VERTICAL_SPLIT, true, new JScrollPane( m_callsTree ), new JScrollPane( m_tokens ) );
    callPanel.setResizeWeight( 1 ); // let the top side take all the slack
    callPanel.resetToPreferredSizes();
   
    // layout controls
    JPanel centerPanel = new JPanel();
    centerPanel.setLayout( new BorderLayout() );
    centerPanel.add( m_infoPanel, BorderLayout.NORTH );
    centerPanel.add( sourceScroller, BorderLayout.CENTER );
    m_tabs = new JTabbedPane();
    m_tabs.setPreferredSize( new Dimension( 250, 0 ) );
    m_tabs.addTab( "Inheritance", inheritancePanel );
    m_tabs.addTab( "Implementations", implementationsPanel );
    m_tabs.addTab( "Call Graph", callPanel );
    JSplitPane splitRight = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, centerPanel, m_tabs );
    splitRight.setResizeWeight( 1 ); // let the left side take all the slack
    splitRight.resetToPreferredSizes();
    JSplitPane splitCenter = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, m_classesPanel, splitRight );
    splitCenter.setResizeWeight( 0 ); // let the right side take all the slack
    pane.add( splitCenter, BorderLayout.CENTER );
   
    // init menus
    JMenuBar menuBar = new JMenuBar();
    m_frame.setJMenuBar( menuBar );
    {
      JMenu menu = new JMenu( "File" );
      menuBar.add( menu );
      {
        JMenuItem item = new JMenuItem( "Open Jar..." );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            if( m_jarFileChooser.showOpenDialog( m_frame ) == JFileChooser.APPROVE_OPTION )
            {
              // load the jar in a separate thread
              new Thread( )
              {
                @Override
                public void run( )
                {
                  try
                  {
                    m_controller.openJar( m_jarFileChooser.getSelectedFile() );
                  }
                  catch( IOException ex )
                  {
                    throw new Error( ex );
                  }
                }
              }.start();
            }
          }
        } );
      }
      {
        JMenuItem item = new JMenuItem( "Close Jar" );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            m_controller.closeJar();
          }
        } );
        m_closeJarMenu = item;
      }
      menu.addSeparator();
      {
        JMenuItem item = new JMenuItem( "Open Mappings..." );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            if( m_mappingsFileChooser.showOpenDialog( m_frame ) == JFileChooser.APPROVE_OPTION )
            {
              try
              {
                m_controller.openMappings( m_mappingsFileChooser.getSelectedFile() );
              }
              catch( IOException ex )
              {
                throw new Error( ex );
              }
              catch( MappingParseException ex )
              {
                JOptionPane.showMessageDialog( m_frame, ex.getMessage() );
              }
            }
          }
        } );
        m_openMappingsMenu = item;
      }
      {
        JMenuItem item = new JMenuItem( "Save Mappings" );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            try
            {
              m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() );
            }
            catch( IOException ex )
            {
              throw new Error( ex );
            }
          }
        } );
        item.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK ) );
        m_saveMappingsMenu = item;
      }
      {
        JMenuItem item = new JMenuItem( "Save Mappings As..." );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            if( m_mappingsFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION )
            {
              try
              {
                m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() );
                m_saveMappingsMenu.setEnabled( true );
              }
              catch( IOException ex )
              {
                throw new Error( ex );
              }
            }
          }
        } );
        item.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK ) );
        m_saveMappingsAsMenu = item;
      }
      {
        JMenuItem item = new JMenuItem( "Close Mappings" );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            m_controller.closeMappings();
          }
        } );
        m_closeMappingsMenu = item;
      }
      menu.addSeparator();
      {
        JMenuItem item = new JMenuItem( "Export Source..." );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            if( m_exportSourceFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION )
            {
              m_controller.exportSource( m_exportSourceFileChooser.getSelectedFile() );
            }
          }
        } );
        m_exportSourceMenu = item;
      }
      {
        JMenuItem item = new JMenuItem( "Export Jar..." );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            if( m_exportJarFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION )
            {
              m_controller.exportJar( m_exportJarFileChooser.getSelectedFile() );
            }
          }
        } );
        m_exportJarMenu = item;
      }
      menu.addSeparator();
      {
        JMenuItem item = new JMenuItem( "Exit" );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            close();
          }
        } );
      }
    }
    {
      JMenu menu = new JMenu( "Help" );
      menuBar.add( menu );
      {
        JMenuItem item = new JMenuItem( "About" );
        menu.add( item );
        item.addActionListener( new ActionListener( )
        {
          @Override
          public void actionPerformed( ActionEvent event )
          {
            AboutDialog.show( m_frame );
          }
        } );
      }
    }
   
    // init state
    onCloseJar();
   
    m_frame.addWindowListener( new WindowAdapter( )
    {
      @Override
      public void windowClosing( WindowEvent event )
      {
        close();
      }
    } );
   
    // show the frame
    pane.doLayout();
    m_frame.setSize( 1024, 576 );
    m_frame.setMinimumSize( new Dimension( 640, 480 ) );
    m_frame.setVisible( true );
    m_frame.setDefaultCloseOperation( WindowConstants.DO_NOTHING_ON_CLOSE );
  }
 
  public JFrame getFrame( )
  {
    return m_frame;
  }
 
  public GuiController getController( )
  {
    return m_controller;
  }
 
  public void onStartOpenJar( )
  {
    m_classesPanel.removeAll();
    JPanel panel = new JPanel();
    panel.setLayout( new FlowLayout() );
    panel.add( new JLabel( "Loading..." ) );
    m_classesPanel.add( panel );
    redraw();
  }
 
  public void onFinishOpenJar( String jarName )
  {
    // update gui
    m_frame.setTitle( Constants.Name + " - " + jarName );
    m_classesPanel.removeAll();
    m_classesPanel.add( m_splitClasses );
    setSource( null );
   
    // update menu
    m_closeJarMenu.setEnabled( true );
    m_openMappingsMenu.setEnabled( true );
    m_saveMappingsMenu.setEnabled( false );
    m_saveMappingsAsMenu.setEnabled( true );
    m_closeMappingsMenu.setEnabled( true );
    m_exportSourceMenu.setEnabled( true );
    m_exportJarMenu.setEnabled( true );
   
    redraw();
  }
 
  public void onCloseJar( )
  {
    // update gui
    m_frame.setTitle( Constants.Name );
    setObfClasses( null );
    setDeobfClasses( null );
    setSource( null );
    m_classesPanel.removeAll();
   
    // update menu
    m_closeJarMenu.setEnabled( false );
    m_openMappingsMenu.setEnabled( false );
    m_saveMappingsMenu.setEnabled( false );
    m_saveMappingsAsMenu.setEnabled( false );
    m_closeMappingsMenu.setEnabled( false );
    m_exportSourceMenu.setEnabled( false );
    m_exportJarMenu.setEnabled( false );
   
    redraw();
  }
 
  public void setObfClasses( Collection<ClassEntry> obfClasses )
  {
    m_obfClasses.setClasses( obfClasses );
  }
 
  public void setDeobfClasses( Collection<ClassEntry> deobfClasses )
  {
    m_deobfClasses.setClasses( deobfClasses );
  }
 
  public void setMappingsFile( File file )
  {
    m_mappingsFileChooser.setSelectedFile( file );
    m_saveMappingsMenu.setEnabled( file != null );
  }
 
  public void setSource( String source )
  {
    m_editor.getHighlighter().removeAllHighlights();
    m_editor.setText( source );
  }
 
  public void showToken( final Token token )
  {
    if( token == null )
    {
      throw new IllegalArgumentException( "Token cannot be null!" );
    }
   
    // set the caret position to the token
    m_editor.setCaretPosition( token.start );
    m_editor.grabFocus();
   
    try
    {
      // make sure the token is visible in the scroll window
      Rectangle start = m_editor.modelToView( token.start );
      Rectangle end = m_editor.modelToView( token.end );
      final Rectangle show = start.union( end );
      show.grow( start.width*10, start.height*6 );
      SwingUtilities.invokeLater( new Runnable( )
      {
        @Override
        public void run( )
        {
          m_editor.scrollRectToVisible( show );
        }
      } );
    }
    catch( BadLocationException ex )
    {
      throw new Error( ex );
    }
   
    // highlight the token momentarily
    final Timer timer = new Timer( 200, new ActionListener( )
    {
      private int m_counter = 0;
      private Object m_highlight = null;
     
      @Override
      public void actionPerformed( ActionEvent event )
      {
        if( m_counter % 2 == 0 )
        {
          try
          {
            m_highlight = m_editor.getHighlighter().addHighlight( token.start, token.end, m_selectionHighlightPainter );
          }
          catch( BadLocationException ex )
          {
            // don't care
          }
        }
        else if( m_highlight != null )
        {
          m_editor.getHighlighter().removeHighlight( m_highlight );
        }
       
        if( m_counter++ > 6 )
        {
          Timer timer = (Timer)event.getSource();
          timer.stop();
        }
      }
    } );
    timer.start();
   
    redraw();
  }
 
  public void showTokens( Collection<Token> tokens )
  {
    Vector<Token> sortedTokens = new Vector<Token>( tokens );
    Collections.sort( sortedTokens );
    if( sortedTokens.size() > 1 )
    {
      // sort the tokens and update the tokens panel
      m_tokens.setListData( sortedTokens );
      m_tokens.setSelectedIndex( 0 );
    }
    else
    {
      m_tokens.setListData( new Vector<Token>() );
    }
   
    // show the first token
    showToken( sortedTokens.get( 0 ) );
  }
 
  public void setHighlightedTokens( Iterable<Token> obfuscatedTokens, Iterable<Token> deobfuscatedTokens, Iterable<Token> otherTokens )
  {
    // remove any old highlighters
    m_editor.getHighlighter().removeAllHighlights();
   
    // color things based on the index
    if( obfuscatedTokens != null )
    {
      setHighlightedTokens( obfuscatedTokens, m_obfuscatedHighlightPainter );
    }
    if( deobfuscatedTokens != null )
    {
      setHighlightedTokens( deobfuscatedTokens, m_deobfuscatedHighlightPainter );
    }
    if( otherTokens != null )
    {
      setHighlightedTokens( otherTokens, m_otherHighlightPainter );
    }
   
    redraw();
  }
 
  private void setHighlightedTokens( Iterable<Token> tokens, Highlighter.HighlightPainter painter )
  {
    for( Token token : tokens )
    {
      try
      {
        m_editor.getHighlighter().addHighlight( token.start, token.end, painter );
      }
      catch( BadLocationException ex )
      {
        throw new IllegalArgumentException( ex );
      }
    }
  }
 
  private void clearReference( )
  {
    m_infoPanel.removeAll();
    JLabel label = new JLabel( "No identifier selected" );
    GuiTricks.unboldLabel( label );
    label.setHorizontalAlignment( JLabel.CENTER );
    m_infoPanel.add( label );
   
    redraw();
  }
 
  private void showReference( EntryReference<Entry,Entry> reference )
  {
    if( reference == null )
    {
      clearReference();
      return;
    }
   
    m_reference = reference;
   
    m_infoPanel.removeAll();
    if( reference.entry instanceof ClassEntry )
    {
      showClassEntry( (ClassEntry)m_reference.entry );
    }
    else if( m_reference.entry instanceof FieldEntry )
    {
      showFieldEntry( (FieldEntry)m_reference.entry );
    }
    else if( m_reference.entry instanceof MethodEntry )
    {
      showMethodEntry( (MethodEntry)m_reference.entry );
    }
    else if( m_reference.entry instanceof ConstructorEntry )
    {
      showConstructorEntry( (ConstructorEntry)m_reference.entry );
    }
    else if( m_reference.entry instanceof ArgumentEntry )
    {
      showArgumentEntry( (ArgumentEntry)m_reference.entry );
    }
    else
    {
      throw new Error( "Unknown entry type: " + m_reference.entry.getClass().getName() );
    }
   
    redraw();
  }
 
  private void showClassEntry( ClassEntry entry )
  {
    addNameValue( m_infoPanel, "Class", entry.getName() );
  }
 
  private void showFieldEntry( FieldEntry entry )
  {
    addNameValue( m_infoPanel, "Field", entry.getName() );
    addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() );
  }
 
  private void showMethodEntry( MethodEntry entry )
  {
    addNameValue( m_infoPanel, "Method", entry.getName() );
    addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() );
    addNameValue( m_infoPanel, "Signature", entry.getSignature() );
  }
 
  private void showConstructorEntry( ConstructorEntry entry )
  {
    addNameValue( m_infoPanel, "Constructor", entry.getClassEntry().getName() );
    addNameValue( m_infoPanel, "Signature", entry.getSignature() );
  }
 
  private void showArgumentEntry( ArgumentEntry entry )
  {
    addNameValue( m_infoPanel, "Argument", entry.getName() );
    addNameValue( m_infoPanel, "Class", entry.getClassEntry().getName() );
    addNameValue( m_infoPanel, "Method", entry.getBehaviorEntry().getName() );
    addNameValue( m_infoPanel, "Index", Integer.toString( entry.getIndex() ) );
  }
 
  private void addNameValue( JPanel container, String name, String value )
  {
    JPanel panel = new JPanel();
    panel.setLayout( new FlowLayout( FlowLayout.LEFT, 6, 0 ) );
    container.add( panel );
   
    JLabel label = new JLabel( name + ":", JLabel.RIGHT );
    label.setPreferredSize( new Dimension( 100, label.getPreferredSize().height ) );
    panel.add( label );
   
    panel.add( GuiTricks.unboldLabel( new JLabel( value, JLabel.LEFT ) ) );
  }
 
  private void onCaretMove( int pos )
  {
    Token token = m_controller.getToken( pos );
    boolean isToken = token != null;
   
    m_reference = m_controller.getDeobfReference( token );
    boolean isClassEntry = isToken && m_reference.entry instanceof ClassEntry;
    boolean isFieldEntry = isToken && m_reference.entry instanceof FieldEntry;
    boolean isMethodEntry = isToken && m_reference.entry instanceof MethodEntry;
    boolean isConstructorEntry = isToken && m_reference.entry instanceof ConstructorEntry;
    boolean isInJar = isToken && m_controller.entryIsInJar( m_reference.entry );
    boolean isRenameable = isToken && m_controller.referenceIsRenameable( m_reference );
   
    if( isToken )
    {
      showReference( m_reference );
    }
    else
    {
      clearReference();
    }
   
    m_renameMenu.setEnabled( isRenameable && isToken );
    m_showInheritanceMenu.setEnabled( isClassEntry || isMethodEntry || isConstructorEntry );
    m_showImplementationsMenu.setEnabled( isClassEntry || isMethodEntry );
    m_showCallsMenu.setEnabled( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry );
    m_openEntryMenu.setEnabled( isInJar && ( isClassEntry || isFieldEntry || isMethodEntry || isConstructorEntry ) );
    m_openPreviousMenu.setEnabled( m_controller.hasPreviousLocation() );
    m_toggleMappingMenu.setEnabled( isRenameable && isToken );
   
    if( isToken && m_controller.entryHasDeobfuscatedName( m_reference.entry ) )
    {
      m_toggleMappingMenu.setText( "Reset to obfuscated" );
    }
    else
    {
      m_toggleMappingMenu.setText( "Mark as deobfuscated" );
    }
  }
 
  private void navigateTo( Entry entry )
  {
    if( !m_controller.entryIsInJar( entry ) )
    {
      // entry is not in the jar. Ignore it
      return;
    }
    if( m_reference != null )
    {
      m_controller.savePreviousReference( m_reference );
    }
    m_controller.openDeclaration( entry );
  }
 
  private void navigateTo( EntryReference<Entry,Entry> reference )
  {
    if( !m_controller.entryIsInJar( reference.getLocationClassEntry() ) )
    {
      // reference is not in the jar. Ignore it
      return;
    }
    if( m_reference != null )
    {
      m_controller.savePreviousReference( m_reference );
    }
    m_controller.openReference( reference );
  }
 
  private void startRename( )
  {
    // init the text box
    final JTextField text = new JTextField();
    text.setText( m_reference.getNamableName() );
    text.setPreferredSize( new Dimension( 360, text.getPreferredSize().height ) );
    text.addKeyListener( new KeyAdapter( )
    {
      @Override
      public void keyPressed( KeyEvent event )
      {
        switch( event.getKeyCode() )
        {
          case KeyEvent.VK_ENTER:
            finishRename( text, true );
          break;
         
          case KeyEvent.VK_ESCAPE:
            finishRename( text, false );
          break;
        }
      }
    } );
   
    // find the label with the name and replace it with the text box
    JPanel panel = (JPanel)m_infoPanel.getComponent( 0 );
    panel.remove( panel.getComponentCount() - 1 );
    panel.add( text );
    text.grabFocus();
    text.selectAll();
   
    redraw();
  }
 
  private void finishRename( JTextField text, boolean saveName )
  {
    String newName = text.getText();
    if( saveName && newName != null && newName.length() > 0 )
    {
      try
      {
        m_controller.rename( m_reference, newName );
      }
      catch( IllegalNameException ex )
      {
        text.setBorder( BorderFactory.createLineBorder( Color.red, 1 ) );
        text.setToolTipText( ex.getReason() );
        GuiTricks.showToolTipNow( text );
      }
      return;
    }
   
    // abort the rename
    JPanel panel = (JPanel)m_infoPanel.getComponent( 0 );
    panel.remove( panel.getComponentCount() - 1 );
    panel.add( GuiTricks.unboldLabel( new JLabel( m_reference.getNamableName(), JLabel.LEFT ) ) );
   
    m_editor.grabFocus();
   
    redraw();
  }
 
  private void showInheritance( )
  {
    if( m_reference == null )
    {
      return;
    }
   
    m_inheritanceTree.setModel( null );
   
    if( m_reference.entry instanceof ClassEntry )
    {
      // get the class inheritance
      ClassInheritanceTreeNode classNode = m_controller.getClassInheritance( (ClassEntry)m_reference.entry );
     
      // show the tree at the root
      TreePath path = getPathToRoot( classNode );
      m_inheritanceTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) );
      m_inheritanceTree.expandPath( path );
      m_inheritanceTree.setSelectionRow( m_inheritanceTree.getRowForPath( path ) );
    }
    else if( m_reference.entry instanceof MethodEntry )
    {
      // get the method inheritance
      MethodInheritanceTreeNode classNode = m_controller.getMethodInheritance( (MethodEntry)m_reference.entry );
     
      // show the tree at the root
      TreePath path = getPathToRoot( classNode );
      m_inheritanceTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) );
      m_inheritanceTree.expandPath( path );
      m_inheritanceTree.setSelectionRow( m_inheritanceTree.getRowForPath( path ) );
    }
   
    m_tabs.setSelectedIndex( 0 );
    redraw();
  }
 
  private void showImplementations( )
  {
    if( m_reference == null )
    {
      return;
    }
   
    m_implementationsTree.setModel( null );
   
    if( m_reference.entry instanceof ClassEntry )
    {
      // get the class implementations
      ClassImplementationsTreeNode node = m_controller.getClassImplementations( (ClassEntry)m_reference.entry );
      if( node != null )
      {
        // show the tree at the root
        TreePath path = getPathToRoot( node );
        m_implementationsTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) );
        m_implementationsTree.expandPath( path );
        m_implementationsTree.setSelectionRow( m_implementationsTree.getRowForPath( path ) );
      }
    }
    else if( m_reference.entry instanceof MethodEntry )
    {
      // get the method implementations
      MethodImplementationsTreeNode node = m_controller.getMethodImplementations( (MethodEntry)m_reference.entry );
      if( node != null )
      {
        // show the tree at the root
        TreePath path = getPathToRoot( node );
        m_implementationsTree.setModel( new DefaultTreeModel( (TreeNode)path.getPathComponent( 0 ) ) );
        m_implementationsTree.expandPath( path );
        m_implementationsTree.setSelectionRow( m_implementationsTree.getRowForPath( path ) );
      }
    }
   
    m_tabs.setSelectedIndex( 1 );
    redraw();
  }
 
  private void showCalls( )
  {
    if( m_reference == null )
    {
      return;
    }
   
    if( m_reference.entry instanceof ClassEntry )
    {
      // look for calls to the default constructor
      // TODO: get a list of all the constructors and find calls to all of them
      BehaviorReferenceTreeNode node = m_controller.getMethodReferences( new ConstructorEntry( (ClassEntry)m_reference.entry, "()V" ) );
      m_callsTree.setModel( new DefaultTreeModel( node ) );
    }
    else if( m_reference.entry instanceof FieldEntry )
    {
      FieldReferenceTreeNode node = m_controller.getFieldReferences( (FieldEntry)m_reference.entry );
      m_callsTree.setModel( new DefaultTreeModel( node ) );
    }
    else if( m_reference.entry instanceof MethodEntry )
    {
      BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (MethodEntry)m_reference.entry );
      m_callsTree.setModel( new DefaultTreeModel( node ) );
    }
    else if( m_reference.entry instanceof ConstructorEntry )
    {
      BehaviorReferenceTreeNode node = m_controller.getMethodReferences( (ConstructorEntry)m_reference.entry );
      m_callsTree.setModel( new DefaultTreeModel( node ) );
    }
   
    m_tabs.setSelectedIndex( 2 );
    redraw();
  }
 
  private void toggleMapping()
  {
    if( m_controller.entryHasDeobfuscatedName( m_reference.entry ) )
    {
      m_controller.removeMapping( m_reference );
    }
    else
    {
      m_controller.markAsDeobfuscated( m_reference );
    }
  }
 
  private TreePath getPathToRoot( TreeNode node )
  {
    List<TreeNode> nodes = Lists.newArrayList();
    TreeNode n = node;
    do
    {
      nodes.add( n );
      n = n.getParent();
    }
    while( n != null );
    Collections.reverse( nodes );
    return new TreePath( nodes.toArray() );
  }
 
  private void close( )
  {
    if( !m_controller.isDirty() )
    {
      // everything is saved, we can exit safely
      m_frame.dispose();
    }
    else
    {
      // ask to save before closing
      String[] options = {
        "Save and exit",
        "Discard changes",
        "Cancel"
      };
      int response = JOptionPane.showOptionDialog(
        m_frame,
        "Your mappings have not been saved yet. Do you want to save?",
        "Save your changes?",
        JOptionPane.YES_NO_CANCEL_OPTION,
        JOptionPane.QUESTION_MESSAGE,
        null,
        options,
        options[2]
      );
      switch( response )
      {
        case JOptionPane.YES_OPTION: // save and exit
          if( m_mappingsFileChooser.getSelectedFile() != null || m_mappingsFileChooser.showSaveDialog( m_frame ) == JFileChooser.APPROVE_OPTION )
          {
            try
            {
              m_controller.saveMappings( m_mappingsFileChooser.getSelectedFile() );
              m_frame.dispose();
            }
            catch( IOException ex )
            {
              throw new Error( ex );
            }
          }
        break;
       
        case JOptionPane.NO_OPTION:
          // don't save, exit
          m_frame.dispose();
        break;
       
        // cancel means do nothing
      }
    }
  }

  private void redraw( )
  {
    m_frame.validate();
    m_frame.repaint();
  }
}
TOP

Related Classes of cuchaz.enigma.gui.Gui

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.