diff --git a/pom.xml b/pom.xml index d1787bf..35fb2b6 100644 --- a/pom.xml +++ b/pom.xml @@ -2,7 +2,7 @@ 4.0.0 us.deathmarine luyten - 0.5.4 + 0.7.0 com.fifesoft diff --git a/src/us/deathmarine/luyten/FindAllBox.java b/src/us/deathmarine/luyten/FindAllBox.java index 92bb1e3..bf35849 100644 --- a/src/us/deathmarine/luyten/FindAllBox.java +++ b/src/us/deathmarine/luyten/FindAllBox.java @@ -65,11 +65,14 @@ public class FindAllBox extends JDialog { private Thread tmp_thread; + private MainWindow mainWindow; + public FindAllBox(final MainWindow mainWindow) { this.setDefaultCloseOperation(HIDE_ON_CLOSE); this.setHideOnEscapeButton(); progressBar = new JProgressBar(0, 100); + this.mainWindow = mainWindow; JLabel label = new JLabel("Find What:"); textField = new JTextField(); @@ -99,7 +102,7 @@ public class FindAllBox extends JDialog { String internalName = StringUtilities.removeRight(entryName, ".class"); TypeReference type = Model.metadataSystem.lookupType(internalName); try { - mainWindow.getModel().extractClassToTextPane(type, array[array.length - 1], entryName, + mainWindow.getSelectedModel().extractClassToTextPane(type, array[array.length - 1], entryName, null); } catch (Exception e) { Luyten.showExceptionDialog("Exception!", e); @@ -107,8 +110,8 @@ public class FindAllBox extends JDialog { } else { try { - JarFile jfile = new JarFile(MainWindow.model.getOpenedFile()); - mainWindow.getModel().extractSimpleFileEntryToTextPane( + JarFile jfile = new JarFile(mainWindow.getSelectedModel().getOpenedFile()); + mainWindow.getSelectedModel().extractSimpleFileEntryToTextPane( jfile.getInputStream(jfile.getEntry(entryName)), array[array.length - 1], entryName); jfile.close(); @@ -196,7 +199,7 @@ public class FindAllBox extends JDialog { classesList.clear(); ConfigSaver configSaver = ConfigSaver.getLoadedInstance(); DecompilerSettings settings = configSaver.getDecompilerSettings(); - File inFile = MainWindow.model.getOpenedFile(); + File inFile = mainWindow.getSelectedModel().getOpenedFile(); boolean filter = ConfigSaver.getLoadedInstance().getLuytenPreferences() .isFilterOutInnerClassEntries(); try { diff --git a/src/us/deathmarine/luyten/FindBox.java b/src/us/deathmarine/luyten/FindBox.java index d43dc8f..0c14256 100644 --- a/src/us/deathmarine/luyten/FindBox.java +++ b/src/us/deathmarine/luyten/FindBox.java @@ -54,7 +54,7 @@ public class FindBox extends JDialog { JLabel label = new JLabel("Find What:"); textField = new JTextField(); - RSyntaxTextArea pane = mainWindow.getModel().getCurrentTextArea(); + RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); if (pane != null) { textField.setText(pane.getSelectedText()); } @@ -132,7 +132,7 @@ public class FindBox extends JDialog { if (textField.getText().length() == 0) return; - RSyntaxTextArea pane = mainWindow.getModel().getCurrentTextArea(); + RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); if (pane == null) return; @@ -207,7 +207,7 @@ public class FindBox extends JDialog { public void actionPerformed(ActionEvent e) { if (textField.getText().length() == 0) return; - RSyntaxTextArea pane = mainWindow.getModel().getCurrentTextArea(); + RSyntaxTextArea pane = mainWindow.getSelectedModel().getCurrentTextArea(); if (pane == null) return; SearchContext context = new SearchContext(); diff --git a/src/us/deathmarine/luyten/Luyten.java b/src/us/deathmarine/luyten/Luyten.java index d8139d7..6c5bd7b 100644 --- a/src/us/deathmarine/luyten/Luyten.java +++ b/src/us/deathmarine/luyten/Luyten.java @@ -7,13 +7,11 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.PrintWriter; -import java.io.StringWriter; +import java.io.*; +import java.net.ServerSocket; +import java.net.Socket; import java.net.URI; +import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicReference; import java.util.List; import java.util.ArrayList; @@ -40,8 +38,20 @@ public class Luyten { private static final AtomicReference mainWindowRef = new AtomicReference<>(); private static final List pendingFiles = new ArrayList<>(); + private static ServerSocket lockSocket = null; - public static void main(String[] args) { + public static void main(final String[] args) { + Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { + @Override + public void run() { + try { + if (lockSocket != null) { + lockSocket.close(); + } + } catch (IOException e) { + } + } + })); try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); @@ -55,12 +65,41 @@ public class Luyten { // .zip or .jar) final File fileFromCommandLine = getFileFromCommandLine(args); + try { + launchMainInstance(fileFromCommandLine); + } catch (Exception e) { + // Instance already exists. Open new file in running instance + try { + Socket socket = new Socket("localhost", 3456); + DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); + dos.writeUTF(args[0]); + dos.flush(); + dos.close(); + socket.close(); + } catch (IOException ex) { + showExceptionDialog("Exception", e); + } + } + } + + private static void launchMainInstance(final File fileFromCommandLine) throws IOException { + lockSocket = new ServerSocket(3456); + launchSession(fileFromCommandLine); + new Thread(new Runnable() { + @Override + public void run() { + launchServer(); + } + }).start(); + } + + private static void launchSession(final File fileFromCommandLine) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { if (!mainWindowRef.compareAndSet(null, new MainWindow(fileFromCommandLine))) { // Already set - so add the files to open - openFileInInstance(fileFromCommandLine); + addToPendingFiles(fileFromCommandLine); } processPendingFiles(); mainWindowRef.get().setVisible(true); @@ -68,14 +107,29 @@ public class Luyten { }); } + private static void launchServer() { + try { // Server + while (true) { + Socket socket = lockSocket.accept(); + DataInputStream dis = new DataInputStream(socket.getInputStream()); + addToPendingFiles(getFileFromCommandLine(dis.readUTF())); + processPendingFiles(); + dis.close(); + socket.close(); + } + } catch (IOException e) { // Client + showExceptionDialog("Exception", e); + } + } + // Private function which processes all pending files - synchronized on the // list of pending files - private static void processPendingFiles() { + public static void processPendingFiles() { final MainWindow mainWindow = mainWindowRef.get(); if (mainWindow != null) { synchronized (pendingFiles) { for (File f : pendingFiles) { - mainWindow.getModel().loadFile(f); + mainWindow.loadNewFile(f); } pendingFiles.clear(); } @@ -84,13 +138,12 @@ public class Luyten { // Function which opens the given file in the instance, if it's running - // and if not, it processes the files - public static void openFileInInstance(File fileToOpen) { + public static void addToPendingFiles(File fileToOpen) { synchronized (pendingFiles) { if (fileToOpen != null) { pendingFiles.add(fileToOpen); } } - processPendingFiles(); } // Function which exits the application if it's running @@ -101,7 +154,7 @@ public class Luyten { } } - public static File getFileFromCommandLine(String[] args) { + public static File getFileFromCommandLine(String... args) { File fileFromCommandLine = null; try { if (args.length > 0) { diff --git a/src/us/deathmarine/luyten/LuytenOsx.java b/src/us/deathmarine/luyten/LuytenOsx.java index 46670ae..3ba653d 100644 --- a/src/us/deathmarine/luyten/LuytenOsx.java +++ b/src/us/deathmarine/luyten/LuytenOsx.java @@ -16,7 +16,8 @@ public class LuytenOsx extends Luyten { Application app = new Application(); app.addApplicationListener(new ApplicationAdapter() { public void handleOpenFile(ApplicationEvent e) { - Luyten.openFileInInstance(new File(e.getFilename())); + Luyten.addToPendingFiles(new File(e.getFilename())); + Luyten.processPendingFiles(); } public void handleQuit(ApplicationEvent e) { diff --git a/src/us/deathmarine/luyten/MainMenuBar.java b/src/us/deathmarine/luyten/MainMenuBar.java index b9af8ba..bd108c7 100644 --- a/src/us/deathmarine/luyten/MainMenuBar.java +++ b/src/us/deathmarine/luyten/MainMenuBar.java @@ -167,7 +167,7 @@ public class MainMenuBar extends JMenuBar { menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - mainWindow.getModel().loadFile(file); + mainWindow.loadNewFile(file); } }); recentFiles.add(menuItem); @@ -196,12 +196,12 @@ public class MainMenuBar extends JMenuBar { menuItem.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - JTabbedPane house = mainWindow.getModel().house; + JTabbedPane house = mainWindow.getSelectedModel().house; if (e.getModifiers() != 2 || house.getTabCount() == 0) mainWindow.onCloseFileMenu(); else { - mainWindow.getModel().closeOpenTab(house.getSelectedIndex()); + mainWindow.getSelectedModel().closeOpenTab(house.getSelectedIndex()); } } }); diff --git a/src/us/deathmarine/luyten/MainWindow.java b/src/us/deathmarine/luyten/MainWindow.java index 68d27dc..ab89c04 100644 --- a/src/us/deathmarine/luyten/MainWindow.java +++ b/src/us/deathmarine/luyten/MainWindow.java @@ -15,20 +15,12 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; -import java.util.Iterator; -import java.util.Vector; +import java.util.*; +import java.util.concurrent.Callable; -import javax.swing.AbstractAction; -import javax.swing.Action; -import javax.swing.ImageIcon; -import javax.swing.JComponent; -import javax.swing.JFrame; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JProgressBar; -import javax.swing.JSplitPane; -import javax.swing.KeyStroke; +import javax.swing.*; import javax.swing.border.BevelBorder; +import javax.swing.plaf.basic.BasicTabbedPaneUI; import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea; @@ -39,8 +31,8 @@ public class MainWindow extends JFrame { private static final long serialVersionUID = 5265556630724988013L; private static final String TITLE = "Luyten"; + private static final String DEFAULT_TAB = "#DEFAULT"; - public static Model model; private JProgressBar bar; private JLabel label; FindBox findBox; @@ -50,13 +42,16 @@ public class MainWindow extends JFrame { private LuytenPreferences luytenPrefs; private FileDialog fileDialog; private FileSaver fileSaver; + private JTabbedPane jarsTabbedPane; + private Map jarModels; public MainMenuBar mainMenuBar; public MainWindow(File fileFromCommandLine) { configSaver = ConfigSaver.getLoadedInstance(); windowPosition = configSaver.getMainWindowPosition(); luytenPrefs = configSaver.getLuytenPreferences(); - + + jarModels = new HashMap(); mainMenuBar = new MainMenuBar(this); this.setJMenuBar(mainMenuBar); @@ -84,8 +79,18 @@ public class MainWindow extends JFrame { panel2.setPreferredSize(new Dimension(this.getWidth() / 3, 20)); panel2.add(bar); - model = new Model(this); - this.getContentPane().add(model); + jarsTabbedPane = new JTabbedPane(SwingConstants.TOP, JTabbedPane.SCROLL_TAB_LAYOUT); + jarsTabbedPane.setUI(new BasicTabbedPaneUI() { + @Override + protected int calculateTabAreaHeight(int tab_placement, int run_count, int max_tab_height) { + if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) == -1) + return super.calculateTabAreaHeight(tab_placement, run_count, max_tab_height); + else + return 0; + } + }); + jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); + this.getContentPane().add(jarsTabbedPane); JSplitPane spt = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, panel1, panel2) { private static final long serialVersionUID = 2189946972124687305L; @@ -108,8 +113,9 @@ public class MainWindow extends JFrame { spt.setBorder(new BevelBorder(BevelBorder.LOWERED)); spt.setPreferredSize(new Dimension(this.getWidth(), 24)); this.add(spt, BorderLayout.SOUTH); + Model jarModel = null; if (fileFromCommandLine != null) { - model.loadFile(fileFromCommandLine); + jarModel = loadNewFile(fileFromCommandLine); } try { @@ -123,34 +129,80 @@ public class MainWindow extends JFrame { fileDialog = new FileDialog(this); fileSaver = new FileSaver(bar, label); - this.setExitOnEscWhenEnabled(model); + if (jarModel != null) { + this.setExitOnEscWhenEnabled(jarModel); + } - if (fileFromCommandLine == null || fileFromCommandLine.getName().toLowerCase().endsWith(".jar") - || fileFromCommandLine.getName().toLowerCase().endsWith(".zip")) { - model.startWarmUpThread(); + if (jarModel != null && (fileFromCommandLine.getName().toLowerCase().endsWith(".jar") + || fileFromCommandLine.getName().toLowerCase().endsWith(".zip"))) { + jarModel.startWarmUpThread(); } if(RecentFiles.load() > 0) mainMenuBar.updateRecentFiles(); } + private void createDefaultTab() { + jarsTabbedPane.addTab(DEFAULT_TAB, new Model(this)); + } + + private void removeDefaultTab() { + jarsTabbedPane.remove(jarsTabbedPane.indexOfTab(DEFAULT_TAB)); + } + public void onOpenFileMenu() { File selectedFile = fileDialog.doOpenDialog(); if (selectedFile != null) { System.out.println("[Open]: Opening " + selectedFile.getAbsolutePath()); - - this.getModel().loadFile(selectedFile); + this.loadNewFile(selectedFile); } } + + public Model loadNewFile(final File file) { + // In case we open the same file again + // we remove the old entry to force a refresh + if (jarModels.containsKey(file.getAbsolutePath())) { + jarModels.remove(file.getAbsolutePath()); + int index = jarsTabbedPane.indexOfTab(file.getName()); + jarsTabbedPane.remove(index); + } + + Model jarModel = new Model(this); + jarModel.loadFile(file); + jarModels.put(file.getAbsolutePath(), jarModel); + jarsTabbedPane.addTab(file.getName(), jarModel); + jarsTabbedPane.setSelectedComponent(jarModel); + + final String tabName = file.getName(); + int index = jarsTabbedPane.indexOfTab(tabName); + Model.Tab tabUI = new Model.Tab(tabName, new Callable() { + @Override + public Void call() { + int index = jarsTabbedPane.indexOfTab(tabName); + jarModels.remove(file.getAbsolutePath()); + jarsTabbedPane.remove(index); + if (jarsTabbedPane.getTabCount() == 0) { + createDefaultTab(); + } + return null; + } + }); + jarsTabbedPane.setTabComponentAt(index, tabUI); + if (jarsTabbedPane.indexOfTab(DEFAULT_TAB) != -1 && jarsTabbedPane.getTabCount() > 1) { + removeDefaultTab(); + } + return jarModel; + } public void onCloseFileMenu() { - this.getModel().closeFile(); + this.getSelectedModel().closeFile(); + jarModels.remove(getSelectedModel()); } public void onSaveAsMenu() { - RSyntaxTextArea pane = this.getModel().getCurrentTextArea(); + RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); if (pane == null) return; - String tabTitle = this.getModel().getCurrentTabTitle(); + String tabTitle = this.getSelectedModel().getCurrentTabTitle(); if (tabTitle == null) return; @@ -162,7 +214,7 @@ public class MainWindow extends JFrame { } public void onSaveAllMenu() { - File openedFile = this.getModel().getOpenedFile(); + File openedFile = this.getSelectedModel().getOpenedFile(); if (openedFile == null) return; @@ -187,7 +239,7 @@ public class MainWindow extends JFrame { public void onSelectAllMenu() { try { - RSyntaxTextArea pane = this.getModel().getCurrentTextArea(); + RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); if (pane != null) { pane.requestFocusInWindow(); pane.setSelectionStart(0); @@ -200,7 +252,7 @@ public class MainWindow extends JFrame { public void onFindMenu() { try { - RSyntaxTextArea pane = this.getModel().getCurrentTextArea(); + RSyntaxTextArea pane = this.getSelectedModel().getCurrentTextArea(); if (pane != null) { if (findBox == null) findBox = new FindBox(this); @@ -229,7 +281,7 @@ public class MainWindow extends JFrame { bar.setVisible(true); bar.setIndeterminate(true); String legalStr = getLegalStr(); - MainWindow.this.getModel().showLegal(legalStr); + getSelectedModel().showLegal(legalStr); } finally { bar.setIndeterminate(false); bar.setVisible(false); @@ -251,7 +303,7 @@ public class MainWindow extends JFrame { } myCL = myCL.getParent(); } - MainWindow.this.getModel().show("Debug", sb.toString()); + this.getSelectedModel().show("Debug", sb.toString()); } finally { bar.setIndeterminate(false); bar.setVisible(false); @@ -295,21 +347,27 @@ public class MainWindow extends JFrame { } public void onThemesChanged() { - this.getModel().changeTheme(luytenPrefs.getThemeXml()); - luytenPrefs.setFont_size(this.getModel().getTheme().baseFont.getSize()); + for (Model jarModel : jarModels.values()) { + jarModel.changeTheme(luytenPrefs.getThemeXml()); + luytenPrefs.setFont_size(jarModel.getTheme().baseFont.getSize()); + } } public void onSettingsChanged() { - this.getModel().updateOpenClasses(); + for (Model jarModel : jarModels.values()) { + jarModel.updateOpenClasses(); + } } public void onTreeSettingsChanged() { - this.getModel().updateTree(); + for (Model jarModel : jarModels.values()) { + jarModel.updateTree(); + } } public void onFileDropped(File file) { if (file != null) { - this.getModel().loadFile(file); + this.loadNewFile(file); } } @@ -326,7 +384,7 @@ public class MainWindow extends JFrame { } public void onNavigationRequest(String uniqueStr) { - this.getModel().navigateTo(uniqueStr); + this.getSelectedModel().navigateTo(uniqueStr); } private void adjustWindowPositionBySavedState() { @@ -426,8 +484,8 @@ public class MainWindow extends JFrame { mainComponent.getActionMap().put("ESCAPE", escapeAction); } - public Model getModel() { - return model; + public Model getSelectedModel() { + return (Model) jarsTabbedPane.getSelectedComponent(); } public JProgressBar getBar() { diff --git a/src/us/deathmarine/luyten/Model.java b/src/us/deathmarine/luyten/Model.java index 2d0b802..2ecfb8e 100644 --- a/src/us/deathmarine/luyten/Model.java +++ b/src/us/deathmarine/luyten/Model.java @@ -5,12 +5,7 @@ import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.Toolkit; -import java.awt.event.ActionEvent; -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.*; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; @@ -28,6 +23,7 @@ import java.util.List; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; +import java.util.concurrent.Callable; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -213,8 +209,14 @@ public class Model extends JSplitPane { house.addTab(title, rTextScrollPane); index = house.indexOfComponent(rTextScrollPane); house.setSelectedIndex(index); - Tab ct = new Tab(title); - ct.getButton().addMouseListener(new CloseTab(title)); + Tab ct = new Tab(title, new Callable() { + @Override + public Void call() throws Exception { + int index = house.indexOfTab(title); + closeOpenTab(index); + return null; + } + }); house.setTabComponentAt(index, ct); } else { house.setSelectedIndex(index); @@ -586,25 +588,28 @@ public class Model extends JSplitPane { } } - private class Tab extends JPanel { - private static final long serialVersionUID = -514663009333644974L; + public static class Tab extends JPanel { + private JLabel tabTitle; private JLabel closeButton = new JLabel(new ImageIcon( Toolkit.getDefaultToolkit().getImage(this.getClass().getResource("/resources/icon_close.png")))); - private JLabel tabTitle = new JLabel(); - private String title = ""; - public Tab(String t) { + public Tab(String title, final Callable onCloseTabAction) { super(new GridBagLayout()); this.setOpaque(false); - - this.title = t; this.tabTitle = new JLabel(title); - this.createTab(); - } - - public JLabel getButton() { - return this.closeButton; + closeButton.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + try { + if (onCloseTabAction != null) { + onCloseTabAction.call(); + } + } catch (Exception ex) { + Luyten.showExceptionDialog("Exception!", ex); + } + } + }); } public void createTab() {