/** * * @author GroG (at) myrobotlab.org * * This file is part of MyRobotLab (http://myrobotlab.org). * * MyRobotLab 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 2 of the License, or * (at your option) any later version (subject to the "Classpath" exception * as provided in the LICENSE.txt file that accompanied this code). * * MyRobotLab is distributed in the hope that it will be useful or fun, * 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. * * All libraries in thirdParty bundle are subject to their own license * requirements - please refer to http://myrobotlab.org/libraries for * details. * * Enjoy ! * * */ package org.myrobotlab.control; import java.awt.Component; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Image; import java.awt.Point; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.net.URL; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import org.myrobotlab.framework.MRLListener; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.net.BareBonesBrowserLaunch; import org.myrobotlab.service.GUIService; import org.myrobotlab.service.Runtime; import org.myrobotlab.service.interfaces.ServiceInterface; import org.slf4j.Logger; /** * @author GroG ServiceGUI - is owned immediately or through a routing map to * ultimately a Service it has the capability of undocking and docking * itself * */ // if need an interface make TabAdapter extend WindowAdapter and contain it public abstract class ServiceGUI extends WindowAdapter implements TabControlEventHandler { public final static Logger log = LoggerFactory.getLogger(ServiceGUI.class); public final String boundServiceName; public final GUIService myService; public GridBagConstraints gc = new GridBagConstraints(); public JPanel display = new JPanel(); // undocked information -- begin -- public int x; public int y; public int width = 600; public int height = 600; transient private JFrame undocked; // undocked information -- end -- // tab -- begin -- TabControl2 tabControl; JTabbedPane tabs; // the tabbed pane this tab control belongs to // tab -- end -- protected ServiceGUI self; boolean isHidden = false; // flipping visible on and off will ruin tab // panels i think public ServiceGUI(final String boundServiceName, final GUIService myService, JTabbedPane tabs) { self = this; this.boundServiceName = boundServiceName; this.myService = myService; this.tabs = tabs; this.tabControl = new TabControl2(this, myService.tabs, display, boundServiceName); tabs.addTab(boundServiceName, display); tabs.setTabComponentAt(tabs.getTabCount() - 1, tabControl); gc.anchor = GridBagConstraints.FIRST_LINE_END; // place menu gc.gridx = 0; gc.gridy = 0; display.setLayout(new GridBagLayout()); // gc.fill = GridBagConstraints.HORIZONTAL; gc.anchor = GridBagConstraints.FIRST_LINE_START; } @Override public void actionPerformed(ActionEvent e, String tabName) { String cmd = e.getActionCommand(); // parent.getSelectedComponent() String label = tabName; if (label.equals(tabName)) { // Service Frame ServiceInterface sw = Runtime.getService(tabName); if ("info".equals(cmd)) { BareBonesBrowserLaunch.openURL("http://myrobotlab.org/service/" + sw.getSimpleName()); } else if ("undock".equals(cmd)) { undockPanel(); } else if ("release".equals(cmd)) { myService.send(Runtime.getInstance().getName(), "releaseService", label); } else if ("prevent export".equals(cmd)) { myService.send(label, "allowExport", false); } else if ("allow export".equals(cmd)) { myService.send(label, "allowExport", true); } else if ("hide".equals(cmd)) { // myService.send(label, "hide", true); // myService.hidePanel(label); hidePanel(); } } else { // Sub Tabbed sub pane ServiceInterface sw = Runtime.getService(label); if ("info".equals(cmd)) { BareBonesBrowserLaunch.openURL("http://myrobotlab.org/service/" + sw.getSimpleName() + "#" + tabName); } else if ("undock".equals(cmd)) { undockPanel(); } } } public abstract void attachGUI(); public abstract void detachGUI(); // -- TabControlEventHandler -- begin @Override /** * closes window and puts the panel back into the tabbed pane */ synchronized public void dockPanel() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { // setting tabcontrol String label = tabControl.getText(); display.setVisible(true); tabs.add(display); log.debug("here tabs count {}", tabs.getTabCount()); tabs.setTabComponentAt(tabs.getTabCount() - 1, tabControl); savePosition(); log.debug("{}", tabs.indexOfTab(label)); if (undocked != null) { undocked.dispose(); undocked = null; } // FIXME - necessary ? or just this panel invalidate? myService.getFrame().pack(); myService.save(); tabs.setSelectedComponent(display); } }); } public JPanel getDisplay() { return display; } public Component getTabControl() { return tabControl; } public void hidePanel() { if (isHidden) { log.info("{} panel is already hidden", tabControl.getText()); return; } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { log.info("hidePanel"); if (undocked != null) { undocked.setVisible(false); } else { // YAY! - the way to do it ! int index = tabs.indexOfComponent(display); // int index = tabs.indexOfTab(tabControl.getText()); if (index != -1) { tabs.remove(index); } else { log.error("{} - has -1 index", tabControl.getText()); } } isHidden = true; } }); } public abstract void init(); public boolean isDocked() { return undocked == null; } public boolean isHidden() { return isHidden; } /** * hook for GUIService framework to query each panel before release checking * if any panel needs user input before shutdown * * @return */ public boolean isReadyForRelease() { return true; } public void makeReadyForRelease() { } @Override public void mouseClicked(MouseEvent event, String tabName) { if (myService != null) { myService.lastTabVisited = tabName; } } public void remove() { detachGUI(); hidePanel(); if (undocked != null) { undocked.dispose(); } } public void savePosition() { if (undocked != null) { Point point = undocked.getLocation(); x = point.x; y = point.y; width = undocked.getWidth(); height = undocked.getHeight(); } } public void send(String method) { send(method, (Object[]) null); } public void send(String method, Object... params) { myService.send(boundServiceName, method, params); } /* * Service functions */ public void subscribe(String inOutMethod) { subscribe(inOutMethod, inOutMethod, (Class<?>[]) null); } public void subscribe(String inMethod, String outMethod) { subscribe(inMethod, outMethod, (Class<?>[]) null); } public void subscribe(String outMethod, String inMethod, Class<?>... parameterType) { MRLListener listener = null; if (parameterType != null) { listener = new MRLListener(outMethod, myService.getName(), inMethod); } else { listener = new MRLListener(outMethod, myService.getName(), inMethod); } myService.send(boundServiceName, "addListener", listener); } public int test(int i, double d) { int x = 0; return x; } @Override /** * undocks a tabbed panel into a JFrame FIXME - NORMALIZE - there are similar * methods in GUIService FIXME - there needs to be clear pattern replacement - * this is a decorator - I think... (also it will always be Swing) * */ // can't return JFrame referrence since its in a invokeLater.. public void undockPanel() { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { tabs.remove(display); String label = tabControl.getText(); if (undocked != null) { log.warn("{} undocked already created", label); } undocked = new JFrame(label); undocked.getContentPane().add(display); // icon URL url = getClass().getResource("/resource/mrl_logo_36_36.png"); Toolkit kit = Toolkit.getDefaultToolkit(); Image img = kit.createImage(url); undocked.setIconImage(img); if (x != 0 || y != 0) { undocked.setLocation(x, y); } if (width != 0 || height != 0) { undocked.setSize(width, height); } undocked.addWindowListener(self); undocked.setVisible(true); undocked.pack(); } }); } public void unhidePanel() { if (!isHidden) { log.info("{} panel is already un-hidden", tabControl.getText()); return; } SwingUtilities.invokeLater(new Runnable() { @Override public void run() { log.info("unhidePanel {}", tabControl.getText()); if (undocked != null) { undocked.setVisible(true); } else { display.setVisible(true); // tabs.addTab(tabControl.getText(), tabControl); tabs.add(tabControl.getText(), display); tabs.setTabComponentAt(tabs.getTabCount() - 1, tabControl); } // FIXME don't know what i'm doing... display.revalidate(); // getFrame().revalidate(); // getFrame().pack(); isHidden = false; } }); } // TODO - more closely model java event system with addNotification or // addListener public void unsubscribe(String inOutMethod) { unsubscribe(inOutMethod, inOutMethod, (Class<?>[]) null); } public void unsubscribe(String inMethod, String outMethod) { unsubscribe(inMethod, outMethod, (Class<?>[]) null); } public void unsubscribe(String outMethod, String inMethod, Class<?>... parameterType) { MRLListener listener = null; if (parameterType != null) { listener = new MRLListener(outMethod, myService.getName(), inMethod); } else { listener = new MRLListener(outMethod, myService.getName(), inMethod); } myService.send(boundServiceName, "removeListener", listener); } // -- TabControlEventHandler -- end @Override public void windowClosing(WindowEvent winEvt) { dockPanel(); } }