Java tutorial
/******************************************************************************* * Copyright (C) 2010 Lucas Madar and Peter Brewer * * This program 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 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * Lucas Madar * Peter Brewer ******************************************************************************/ /** * */ package org.tellervo.desktop.tridasv2.ui; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Font; import java.awt.GridLayout; import java.awt.Paint; import java.awt.Rectangle; import java.awt.Shape; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.io.IOException; import javax.swing.AbstractButton; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JProgressBar; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTree; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.ToolTipManager; import javax.swing.border.EtchedBorder; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreePath; import net.miginfocom.swing.MigLayout; import org.apache.commons.collections15.Transformer; import org.jdesktop.swingx.JXTable; import org.jdesktop.swingx.table.TableColumnModelExt; import org.tellervo.desktop.gui.dbbrowse.BooleanCellRenderer; import org.tellervo.desktop.gui.dbbrowse.ElementListCellRenderer; import org.tellervo.desktop.gui.dbbrowse.ElementListManager; import org.tellervo.desktop.gui.dbbrowse.ElementListPopupMenu; import org.tellervo.desktop.gui.dbbrowse.ElementListTableModel; import org.tellervo.desktop.gui.dbbrowse.ElementListTableSorter; import org.tellervo.desktop.sample.CachedElement; import org.tellervo.desktop.sample.Element; import org.tellervo.desktop.sample.ElementList; import org.tellervo.desktop.sample.Sample; import org.tellervo.desktop.sample.TellervoWSILoader; import org.tellervo.desktop.ui.I18n; import org.tellervo.desktop.util.PopupListener; import org.tellervo.desktop.wsi.ResourceEvent; import org.tellervo.desktop.wsi.ResourceEventListener; import org.tellervo.desktop.wsi.tellervo.TellervoResourceProperties; import org.tellervo.desktop.wsi.tellervo.resources.SeriesResource; import org.tellervo.schema.EntityType; import org.tellervo.schema.TellervoRequestFormat; import org.tellervo.schema.TellervoRequestType; import org.tridas.schema.TridasIdentifier; import edu.uci.ics.jung.algorithms.layout.AbstractLayout; import edu.uci.ics.jung.algorithms.layout.FRLayout; import edu.uci.ics.jung.graph.DirectedSparseMultigraph; import edu.uci.ics.jung.graph.Graph; import edu.uci.ics.jung.graph.ObservableGraph; import edu.uci.ics.jung.graph.event.GraphEvent; import edu.uci.ics.jung.graph.event.GraphEventListener; import edu.uci.ics.jung.graph.util.Graphs; import edu.uci.ics.jung.visualization.VisualizationViewer; import edu.uci.ics.jung.visualization.control.CrossoverScalingControl; import edu.uci.ics.jung.visualization.control.DefaultModalGraphMouse; import edu.uci.ics.jung.visualization.control.ModalGraphMouse; import edu.uci.ics.jung.visualization.control.ScalingControl; import edu.uci.ics.jung.visualization.decorators.ToStringLabeller; import edu.uci.ics.jung.visualization.renderers.Renderer; /** * @author Lucas Madar * */ public class ComponentViewer extends JPanel implements ResourceEventListener, ElementListManager { private static final long serialVersionUID = 1L; /** The sample we're viewing */ private Sample sample; /** True if we've loaded the comprehensive series */ private boolean loadedComprehensive; private JRadioButton btnTableView; private JRadioButton btnTreeView; /** Status bar text */ private JLabel txtStatus; /** Progress bar */ private JProgressBar pbStatus; private JPanel contentPanel, tablePanel, treePanel, tree2Panel; private JXTable table; private JTree tree; private ElementListTableSorter tableSorter; private ElementListTableModel tableModel; private DefaultTreeModel treeModel; private final static String TABLEPANEL = "Series Table View"; private final static String TREEPANEL = "Series Tree View"; private final static String TREE2PANEL = "New Tree View"; private Graph<String, String> g = null; private VisualizationViewer<String, String> vv = null; private AbstractLayout<String, String> layout = null; private JLabel lblViewAllThe; public ComponentViewer(Sample sample) { this.sample = sample; this.loadedComprehensive = false; initComponents(); updateContent(); // default to this view... btnTreeView.doClick(); } private void initComponents() { setLayout(new BorderLayout()); // create button panel JPanel topPanel = new JPanel(); btnTreeView = new JRadioButton("Tree"); btnTreeView.putClientProperty("cv.cardName", TREEPANEL); btnTableView = new JRadioButton("Table"); btnTableView.putClientProperty("cv.cardName", TABLEPANEL); ActionListener btnListener = new ActionListener() { public void actionPerformed(ActionEvent e) { // show the right layout panel String v = (String) ((AbstractButton) e.getSource()).getClientProperty("cv.cardName"); if (v != null) ((CardLayout) contentPanel.getLayout()).show(contentPanel, v); } }; btnTableView.addActionListener(btnListener); btnTreeView.addActionListener(btnListener); // connect buttons ButtonGroup group = new ButtonGroup(); group.add(btnTreeView); group.add(btnTableView); topPanel.setLayout(new MigLayout("", "[64px][55px][62px][grow]", "[][23px]")); lblViewAllThe = new JLabel( "<html>View all the component series that combine to create the current series. Series can be viewed as:"); topPanel.add(lblViewAllThe, "cell 0 0 4 1"); JRadioButton btnTree2View = new JRadioButton("Flow chart"); btnTree2View.putClientProperty("cv.cardName", TREE2PANEL); btnTree2View.addActionListener(btnListener); group.add(btnTree2View); topPanel.add(btnTree2View, "cell 0 1,alignx left,aligny center"); topPanel.add(btnTreeView, "cell 1 1,alignx left,aligny center"); topPanel.add(btnTableView, "cell 2 1,alignx left,aligny center"); topPanel.setBorder(BorderFactory.createEmptyBorder(2, 8, 8, 8)); add(topPanel, BorderLayout.NORTH); // create status bar JPanel status = new JPanel(); status.setLayout(new BoxLayout(status, BoxLayout.X_AXIS)); txtStatus = new JLabel(""); pbStatus = new JProgressBar(); pbStatus.setVisible(false); status.add(txtStatus); status.add(Box.createHorizontalStrut(8)); status.add(pbStatus); status.setBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)); add(status, BorderLayout.SOUTH); contentPanel = new JPanel(new CardLayout()); add(contentPanel, BorderLayout.CENTER); tablePanel = new JPanel(new BorderLayout()); setupTable(); tablePanel.add(new JScrollPane(table), BorderLayout.CENTER); treePanel = new JPanel(new BorderLayout()); setupTree(); treePanel.add(new JScrollPane(tree), BorderLayout.CENTER); tree2Panel = new JPanel(new BorderLayout()); setupTreeGUI(); contentPanel.add(tablePanel, TABLEPANEL); contentPanel.add(treePanel, TREEPANEL); contentPanel.add(tree2Panel, TREE2PANEL); } private void setupTree() { treeModel = new DefaultTreeModel(null); tree = new JTree(treeModel); tree.setCellRenderer(new ComponentTreeCellRenderer()); // popup menu tree.addMouseListener(new PopupListener() { @Override public void showPopup(MouseEvent e) { TreePath path = tree.getPathForLocation(e.getX(), e.getY()); DefaultMutableTreeNode node = (path == null) ? null : (DefaultMutableTreeNode) path.getLastPathComponent(); if (node == null) return; // ensure we select the node... tree.setSelectionPath(path); // get the element Element element = (Element) node.getUserObject(); // create and show the menu JPopupMenu popup = new ElementListPopupMenu(element, ComponentViewer.this); popup.show(tree, e.getX(), e.getY()); } }); ToolTipManager.sharedInstance().setInitialDelay(0); tree.setToolTipText(""); } private void setupTable() { tableModel = new ElementListTableModel(); table = new JXTable(tableModel); tableSorter = new ElementListTableSorter(tableModel, table); table.getTableHeader().addMouseListener(tableSorter); // add sorter & header renderer table.setColumnSelectionAllowed(false); table.setRowSelectionAllowed(true); // set our column widths ElementListTableModel.setupColumnWidths(table); table.setDefaultRenderer(Object.class, new ElementListCellRenderer(this, false)); table.setDefaultRenderer(Boolean.class, new BooleanCellRenderer(this, false)); // hide irrelevent columns TableColumnModelExt colmodel = (TableColumnModelExt) table.getColumnModel(); table.setColumnControlVisible(true); colmodel.getColumnExt(I18n.getText("hidden.MostRecentVersion")).setVisible(false); colmodel.getColumnExt(I18n.getText("dbbrowser.n")).setVisible(false); colmodel.getColumnExt(I18n.getText("dbbrowser.rec")).setVisible(false); colmodel.getColumnExt(I18n.getText("dbbrowser.hash")).setVisible(false); // popup menu table.addMouseListener(new PopupListener() { @Override public void showPopup(MouseEvent e) { // only clicks on tables if (!(e.getSource() instanceof JTable)) return; JTable table = (JTable) e.getSource(); ElementListTableModel model = (ElementListTableModel) table.getModel(); // get the row and sanity check int row = table.rowAtPoint(e.getPoint()); if (row < 0 || row >= model.getRowCount()) return; // select it? table.setRowSelectionInterval(row, row); // get the element Element element = model.getElementAt(row); // create and show the menu JPopupMenu popup = new ElementListPopupMenu(element, ComponentViewer.this); popup.show(table, e.getX(), e.getY()); } }); } private void updateContent() { /*ElementList elements = sample.getElements(); ElementList displayElements = new ElementList(); // if we don't have any, use an empty list instead... if(elements == null) elements = new ElementList(); // create root node DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode(new CachedElement(sample)); //recurseAddElementsToList(elements, displayElements, rootNode, sample, 0); */ PopulateComponentsTask task = new PopulateComponentsTask(sample); task.execute(); //displayElements = task.getDisplayElements(); // should be unsorted - default order is what we want //tableModel.setElements(displayElements); //treeModel.setRoot(rootNode); // expand all nodes in tree // note: tree.getRowCount() changes as we expand each node! /*for(int i = 0; i < tree.getRowCount(); i++) { tree.expandRow(i); }*/ } private void setStatus(String status, boolean inProgress) { txtStatus.setText(status); pbStatus.setVisible(inProgress); pbStatus.setIndeterminate(inProgress); } /** * Called when someone has made this panel visible */ public void notifyPanelVisible() { if (loadedComprehensive) return; loadedComprehensive = true; if (sample.getLoader() instanceof TellervoWSILoader) { TridasIdentifier identifier = ((TellervoWSILoader) sample.getLoader()).getTridasIdentifier(); // create a new resource SeriesResource resource = new SeriesResource(identifier, EntityType.MEASUREMENT_SERIES, TellervoRequestType.READ); // flag it as comprehensive resource.setProperty(TellervoResourceProperties.ENTITY_REQUEST_FORMAT, TellervoRequestFormat.COMPREHENSIVE); resource.setOwnerWindow(SwingUtilities.getWindowAncestor(this)); resource.addResourceEventListener(this); resource.query(); setStatus("Loading comprehensive series...", true); return; } setStatus("Cannot load comprehensive view for this series.", false); } /** * Called from loading a resource */ public void resourceChanged(ResourceEvent re) { switch (re.getEventType()) { case ResourceEvent.RESOURCE_DEBUG_OUT: setStatus("Loading components please wait...", true); break; case ResourceEvent.RESOURCE_DEBUG_IN: setStatus("Receiving reply...", true); break; case ResourceEvent.RESOURCE_QUERY_COMPLETE: { setStatus("Done", false); SeriesResource resource = (SeriesResource) re.getSource(); Sample s = resource.getSample(sample.getIdentifier()); if (s == null) { setStatus("Error: Series was not found.", false); return; } sample = s; updateContent(); break; } case ResourceEvent.RESOURCE_QUERY_FAILED: setStatus("Error loading view: " + re.getAttachedException().getLocalizedMessage(), false); break; } } public void deleteElement(Element e) { // this can never happen; all of our elements have dependents. } public boolean isElementDisabled(Element e) { // none of our elements are ever disabled return false; } private void setupTreeGUI() { //create a graph Graph<String, String> ig = Graphs .<String, String>synchronizedDirectedGraph(new DirectedSparseMultigraph<String, String>()); ObservableGraph<String, String> og = new ObservableGraph<String, String>(ig); og.addGraphEventListener(new GraphEventListener<String, String>() { public void handleGraphEvent(GraphEvent<String, String> evt) { System.err.println("got " + evt); } }); this.g = og; //create a graphdraw layout = new FRLayout<String, String>(g); //layout = new ISOMLayout<String, String>(g); //layout = new KKLayout<String, String>(g); //layout = new CircleLayout<String, String>(g); vv = new VisualizationViewer<String, String>(layout, null); //tree2Panel.putClientProperty("defeatSystemEventQueueCheck", Boolean.TRUE); tree2Panel.setLayout(new BorderLayout()); tree2Panel.setBackground(java.awt.Color.lightGray); tree2Panel.setFont(new Font("Serif", Font.PLAIN, 12)); vv.getModel().getRelaxer().setSleepTime(500); final DefaultModalGraphMouse graphMouse = new DefaultModalGraphMouse<Number, Number>(); vv.setGraphMouse(graphMouse); vv.getRenderer().getVertexLabelRenderer().setPosition(Renderer.VertexLabel.Position.CNTR); vv.getRenderContext().setVertexLabelTransformer(new ToStringLabeller()); vv.getRenderContext().setEdgeLabelTransformer(new ToStringLabeller()); Transformer<String, Paint> vertexColor = new Transformer<String, Paint>() { public Paint transform(String i) { if (i.equals(ComponentTreeCellRenderer.getFullTitle(sample, false))) { return Color.ORANGE; } else { return Color.PINK; } } }; Transformer<String, Paint> edgeColor = new Transformer<String, Paint>() { public Paint transform(String i) { return Color.WHITE; } }; Transformer<String, Shape> vertexShape = new Transformer<String, Shape>() { public Shape transform(String i) { if (i.equals(ComponentTreeCellRenderer.getFullTitle(sample, false))) { return new Rectangle(-150, -20, 300, 40); } else { return new Rectangle(-150, -10, 300, 20); } } }; vv.getRenderContext().setVertexFillPaintTransformer(vertexColor); vv.getRenderContext().setEdgeDrawPaintTransformer(edgeColor); vv.getRenderContext().setVertexShapeTransformer(vertexShape); ; vv.setForeground(Color.BLACK); tree2Panel.add(vv, BorderLayout.CENTER); JComboBox modeBox = graphMouse.getModeComboBox(); modeBox.setRenderer(new JUNGModeRenderer()); modeBox.addItemListener(graphMouse.getModeListener()); graphMouse.setMode(ModalGraphMouse.Mode.TRANSFORMING); final ScalingControl scaler = new CrossoverScalingControl(); JButton plus = new JButton("+"); plus.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { scaler.scale(vv, 1.1f, vv.getCenter()); } }); JButton minus = new JButton("-"); minus.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { scaler.scale(vv, 1 / 1.1f, vv.getCenter()); } }); JPanel scaleGrid = new JPanel(new GridLayout(1, 0)); scaleGrid.setBorder(BorderFactory.createTitledBorder("Zoom")); JPanel controls = new JPanel(); scaleGrid.add(plus); scaleGrid.add(minus); controls.add(scaleGrid); controls.add(modeBox); tree2Panel.add(controls, BorderLayout.NORTH); } class PopulateComponentsTask extends SwingWorker<Void, Void> { private Sample sample; private ElementList de = new ElementList(); private DefaultMutableTreeNode rootNode; public PopulateComponentsTask(Sample sample) { this.sample = sample; } @Override protected Void doInBackground() throws Exception { ElementList el = sample.getElements(); ElementList de = new ElementList(); // if we don't have any, use an empty list instead... if (el == null) el = new ElementList(); // create root node rootNode = new DefaultMutableTreeNode(new CachedElement(sample)); recurseAddElementsToList(el, rootNode, sample, 0); return null; } /* * Executed in event dispatching thread */ @Override public void done() { tableModel.setElements(de); treeModel.setRoot(rootNode); } private void recurseAddElementsToList(ElementList elements, DefaultMutableTreeNode parent, Sample parentSample, int depth) { for (Element e : elements) { if (e instanceof CachedElement) { CachedElement ce = (CachedElement) e; // need the basic... if (!ce.hasBasic()) { System.err.println("Cached, but not even basic loaded!"); continue; } // add to list de.add(ce); // add to tree DefaultMutableTreeNode node = new DefaultMutableTreeNode(ce); parent.add(node); // can't go any deeper if there's no series... if (!ce.hasFull()) continue; try { Sample s = ce.load(); layout.lock(true); //add a vertex String v1 = ComponentTreeCellRenderer.getFullTitle(s, false); //Relaxer relaxer = vv.getModel().getRelaxer(); //relaxer.pause(); g.addVertex(v1); System.err.println("added node " + v1); // wire it to some edges String verb = ""; try { verb = parentSample.getSampleType().toString(); if (verb.equals("Sum")) verb = "Summed"; if (verb.equals("Index")) verb = "Indexed"; if (verb.equals("Clean")) verb = "Cleaned"; if (verb.equals("Redate")) verb = "Redated"; if (verb.equals("Crossdate")) verb = "Crossdated"; if (verb.equals("Truncate")) verb = "Truncated"; } catch (NullPointerException ex) { } verb = verb + " [" + g.getEdgeCount() + "]"; g.addEdge(verb, ComponentTreeCellRenderer.getFullTitle(s, false), ComponentTreeCellRenderer.getFullTitle(parentSample, false)); layout.initialize(); //relaxer.resume(); layout.lock(false); ElementList sampleElements = s.getElements(); if (sampleElements != null) recurseAddElementsToList(sampleElements, node, s, depth + 1); } catch (IOException ioe) { // shouldn't happen } } else System.err.println("Non-cached element: " + e); } vv.repaint(); } } }