package com.framsticks.gui; import com.framsticks.communication.File; import com.framsticks.communication.queries.NeedFile; import com.framsticks.communication.queries.NeedFileAcceptor; import com.framsticks.gui.console.Console; import com.framsticks.gui.console.DirectConsole; import com.framsticks.gui.console.ManagedConsole; import com.framsticks.gui.console.TrackConsole; import com.framsticks.gui.table.ColumnsConfig; import com.framsticks.gui.table.ListPanelProvider; import com.framsticks.params.annotations.AutoAppendAnnotation; import com.framsticks.params.annotations.FramsClassAnnotation; import com.framsticks.params.annotations.ParamAnnotation; import com.framsticks.parsers.FileSource; import com.framsticks.remote.RemoteTree; import com.framsticks.structure.Path; import com.framsticks.structure.Tree; import com.framsticks.util.ExceptionHandler; import com.framsticks.util.FramsticksException; import com.framsticks.util.dispatching.AbstractJoinable; import com.framsticks.util.dispatching.Dispatcher; import com.framsticks.util.dispatching.Dispatching; import com.framsticks.util.dispatching.FutureHandler; import com.framsticks.util.dispatching.Joinable; import com.framsticks.util.dispatching.JoinableCollection; import com.framsticks.util.dispatching.JoinableParent; import com.framsticks.util.dispatching.JoinableState; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import java.awt.Dimension; import java.awt.Toolkit; import java.awt.datatransfer.StringSelection; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.IOException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.framsticks.util.dispatching.RunAt; import com.framsticks.util.lang.Strings; /** * @author Piotr Sniegowski */ @FramsClassAnnotation public class Browser extends AbstractJoinable implements Dispatcher, JoinableParent, ExceptionHandler { private static final Logger log = LogManager.getLogger(Browser.class); protected final JoinableCollection frames = new JoinableCollection(JoinableCollection.FinishPolicy.OnAll).setObservableName("frames"); protected final JoinableCollection trees = new JoinableCollection(JoinableCollection.FinishPolicy.Never).setObservableName("trees"); protected final JoinableCollection consoles = new JoinableCollection(JoinableCollection.FinishPolicy.Never).setObservableName("consoles"); protected final List popupMenuEntryProviders = new LinkedList<>(); // protected final SwingDispatcher protected final MainFrame mainFrame; protected final List panelProviders = new ArrayList(); protected Dimension defaultFrameDimension; String name; public void addFrame(Frame frame) { frames.add(frame); } protected final StandardPanelProvider standardPanelProvider; protected final ListPanelProvider listPanelProvider; public Browser() { setName("browser"); JPopupMenu.setDefaultLightWeightPopupEnabled(false); addPanelProvider(standardPanelProvider = new StandardPanelProvider()); addPanelProvider(listPanelProvider = new ListPanelProvider()); mainFrame = new MainFrame(Browser.this); // mainFrame.getStatusBar().setExceptionHandler(ThrowExceptionHandler.getInstance()); addFrame(mainFrame); addPopupMenuEntryProvider(new PopupMenuEntryProvider() { @Override public void provide(JPopupMenu menu, Path path) { menu.add(new JMenuItem(path.getFullTextual())); menu.addSeparator(); } }); addPopupMenuEntryProvider(new PopupMenuEntryProvider() { @SuppressWarnings("serial") @Override public void provide(JPopupMenu menu, final Path path) { menu.add(new AbstractAction("Copy path to clipboard") { @Override public void actionPerformed(ActionEvent e) { Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(path.getFullTextual()), null); } }); } }); addPopupMenuEntryProvider(new PopupMenuEntryProvider() { @SuppressWarnings("serial") @Override public void provide(JPopupMenu menu, final Path path) { if (!(path.getTree() instanceof RemoteTree)) { return; } final RemoteTree remoteTree = (RemoteTree) path.getTree(); menu.add(new AbstractAction("Open tracking console") { @Override public void actionPerformed(ActionEvent e) { consoles.add(new TrackConsole().setConnection(remoteTree.getConnection())); } }); menu.add(new AbstractAction("Open managed console") { @Override public void actionPerformed(ActionEvent e) { consoles.add(new ManagedConsole().setTree(remoteTree)); } }); menu.add(new AbstractAction("Open direct console") { @Override public void actionPerformed(ActionEvent e) { consoles.add(new DirectConsole().setAddress(remoteTree.getAddress())); } }); } }); } @AutoAppendAnnotation public void addPanelProvider(PanelProvider panelProvider) { log.debug("added panel provider of type: {}", panelProvider.getClass().getCanonicalName()); panelProviders.add(panelProvider); } @AutoAppendAnnotation public void addColumnsConfig(ColumnsConfig columnsConfig) { listPanelProvider.addColumnsConfig(columnsConfig); } @AutoAppendAnnotation public void addPopupMenuEntryProvider(PopupMenuEntryProvider popupMenuEntryProvider) { popupMenuEntryProviders.add(popupMenuEntryProvider); } protected static final Pattern extensionFilterPattern = Pattern.compile("\\*\\.(\\S+)"); @AutoAppendAnnotation public void addTree(final Tree tree) { log.debug("adding tree: {}", tree); tree.setDispatcher(new SwingDispatcher()); tree.setExceptionHandler(this); trees.add(tree); final NeedFileAcceptor acceptor = new NeedFileAcceptor() { protected boolean done = false; @Override public boolean acceptNeed(final NeedFile needFile) { final JFileChooser chooser = new JFileChooser(); final JFrame frame = new JFrame(); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { if (!done) { needFile.getFuture().handle(new FramsticksException().msg("user closed the window")); } frame.setVisible(false); frame.dispose(); } }); frame.setTitle(Strings.toStringEmptyProof(needFile.getDescription(), "Choose file")); chooser.setMultiSelectionEnabled(false); Matcher matcher = extensionFilterPattern.matcher(needFile.getSuggestedName()); if (matcher.matches()) { chooser.setFileFilter(new FileNameExtensionFilter(Strings.toStringEmptyProof(needFile.getDescription(), "file"), Strings.takeGroup(needFile.getSuggestedName(), matcher, 1).toString())); } frame.getContentPane().add(chooser); chooser.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent event) { if (event.getActionCommand().equals("CancelSelection")) { needFile.getFuture().handle(new FramsticksException().msg("user cancelled choose")); frame.setVisible(false); frame.dispose(); } if (event.getActionCommand().equals("ApproveSelection")) { File file = null; String filename = chooser.getSelectedFile().getAbsolutePath(); try { file = new File("", new FileSource(filename)); } catch (IOException e) { needFile.getFuture().handle(new FramsticksException().msg("failed to open chosen file").arg("filename", filename).cause(e)); } if (file != null) { done = true; needFile.getFuture().pass(file); } frame.setVisible(false); frame.dispose(); } } }); frame.setVisible(true); return true; } }; tree.dispatch(new RunAt(this) { @Override protected void runAt() { log.debug("adding need file acceptor: {}", acceptor); tree.addNeedFileAcceptor(Integer.MAX_VALUE, acceptor); } }); } public void autoResolvePath(final String path, final FutureHandler future) { // final Tree i = trees.get("localhost"); // i.dispatch(new RunAt(future) { // @Override // protected void runAt() { // TreeOperations.tryGet(i, path, new FutureHandler(future) { // @Override // protected void result(final Path p) { // future.pass(p); // mainFrame.dispatch(new RunAt(future) { // @Override // protected void runAt() { // mainFrame.goTo(p); // } // }); // } // }); // } // }); } public void clear() { assert isActive(); for (Frame f : frames) { f.clear(); } } @Override protected void joinableStart() { Dispatching.use(frames, this); Dispatching.use(trees, this); Dispatching.use(consoles, this); dispatch(new RunAt(this) { @Override protected void runAt() { for (final Tree tree : trees) { tree.dispatch(new RunAt(this) { @Override protected void runAt() { final Path p = Path.to(tree, "/"); log.debug("adding path: {}", p); dispatch(new RunAt(this) { @Override protected void runAt() { mainFrame.addRootPath(p); } }); } }); } } }); } /** * @return the tree */ public JoinableCollection getTrees() { return trees; } /** * @return the mainFrame */ public MainFrame getMainFrame() { return mainFrame; } /** * @return the name */ @ParamAnnotation public String getName() { return name; } /** * @param name the name to set */ @ParamAnnotation public void setName(String name) { this.name = name; } @Override public boolean isActive() { return SwingDispatcher.getInstance().isActive(); } @Override public void dispatch(RunAt runnable) { SwingDispatcher.getInstance().dispatch(runnable); } @Override protected void joinableJoin() throws InterruptedException { Dispatching.join(frames); Dispatching.join(trees); Dispatching.join(consoles); // super.join(); } @Override protected void joinableInterrupt() { Dispatching.drop(consoles, this); Dispatching.drop(frames, this); Dispatching.drop(trees, this); } @Override public void childChangedState(Joinable joinable, JoinableState state) { if (joinable == frames) { proceedToState(state); } } @Override protected void joinableFinish() { } @Override public String toString() { return getName(); } @Override public void handle(FramsticksException exception) { mainFrame.handle(exception); } }