/* Author: Jeff Dalton * Updated: Thu Jun 7 16:22:40 2001 by Jeff Dalton * Copyright: (c) 2001, AIAI, University of Edinburgh */ package ix.ileed; import javax.swing.*; import javax.swing.table.*; import java.awt.Font; import java.awt.Dimension; import java.awt.event.*; import java.util.*; import ix.ideel.IssueViewingTable; import ix.iface.util.GridColumn; import ix.icore.process.StatusValues; import ix.util.*; import ix.util.lisp.*; /** * A simple process / product viewer */ public class SimpleProcessViewer extends JPanel implements ProcessViewer { // JPanel layout manager defaults to FlowLayout. ProcessModel model; ProcessTable processTable; ProductTable productTable; LList processExpansionTree; Vector products; public SimpleProcessViewer() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); } public void setProcessModel(ProcessModel model) { // We can't set things up in any interesting way until after // there's a process model we can ask for the relevant information. this.model = model; setUpTables(); } public synchronized void statusUpdate(final ProcessStatusUpdate up) { if (SwingUtilities.isEventDispatchThread()) { // Happens when there's a "reset" invoked via the interface. Debug.noteln("Process statusUpdate in event dispatch thread"); } SwingUtilities.invokeLater(new Runnable() { public void run() { processTable.statusUpdate(up); productTable.statusUpdate(up); } }); } public synchronized void reset() { processTable.reset(); productTable.reset(); } void setUpTables() { processExpansionTree = model.staticExpansionTree(); products = model.getAllProducts(); processTable = new ProcessTable(processExpansionTree); productTable = new ProductTable(products); // add(processTable); JPanel processPanel = new JPanel(); processPanel.add(processTable); JScrollPane processScroll = new JScrollPane(processPanel); processScroll.setBorder (BorderFactory.createTitledBorder("Process status")); add(processScroll); // add(productTable); JPanel productPanel = new JPanel(); productPanel.setBorder (BorderFactory.createTitledBorder("Product status")); productPanel.add(productTable); add(productPanel); } static class ProcessTable extends StatusTable { JLabel actionLabel = new JLabel("Activity"); JLabel statusLabel = new JLabel("Status"); List rows = new ArrayList(); ProcessTable(LList expansionTree) { super(); vectorizeExpansionTree(expansionTree, 0); fillTable(); } // The expansion tree shows the action nesting present in the plan. // The tree is a list of entries of the form (pattern subtree). void vectorizeExpansionTree(LList tree, int depth) { for (LList at = tree; at != Lisp.NIL; at = at.cdr()) { LList e = (LList)at.car(); Object pattern = (Object)e.elementAt(0); LList subtree = (LList)e.elementAt(1); String name = pattern.toString(); JTextField tf = new JTextField (Util.repeatString(" ", depth) + name, 40); // Bold text if there's a subtree. if (subtree != Lisp.NIL) tf.setFont(tf.getFont().deriveFont(Font.BOLD)); JTextField sf = makeStatusField(name); Row row = new Row(tf, sf); row.subtree = subtree; row.depth = depth; row.open = true; // subtree == Lisp.NIL; tf.addMouseListener(makeMouseListener(row)); rows.add(row); vectorizeExpansionTree(subtree, depth+1); } } void clearTable() { nameCol.removeAll(); statusCol.removeAll(); } void fillTable() { nameCol.add(actionLabel); statusCol.add(statusLabel); for (ListIterator i = rows.listIterator(); i.hasNext();) { Row row = (Row)i.next(); nameCol.add(row.text); statusCol.add(row.status); if (!row.open) { while(i.hasNext()) { Row below = (Row)i.next(); if (below.depth <= row.depth) { i.previous(); break; } } } } } static class Row { LList subtree; int depth; boolean open; JTextField text; JTextField status; Row(JTextField text, JTextField status) { this.text = text; this.status = status; } } /** * Returns a listener than can be called when the user clicks * in the text of an action description. */ MouseListener makeMouseListener(final Row row) { return new MouseAdapter() { public void mouseClicked(MouseEvent e) { JTextField clicked = (JTextField)e.getComponent(); String selectedIssueText = clicked.getText(); Debug.noteln("User clicked in", selectedIssueText); if (row.subtree != Lisp.NIL) { row.open = !row.open; clearTable(); fillTable(); invalidate(); SwingUtilities.getRoot(ProcessTable.this).validate(); } } }; } void statusUpdate(ProcessStatusUpdate up) { // Node status changes. for (Enumeration ce = up.statusChanges.elements(); ce.hasMoreElements();) { StatusChange change = (StatusChange)ce.nextElement(); setStatus(change.action, change.status); } } } static class ProductTable extends StatusTable { ProductTable(Vector products) { super(); nameCol.add(new JLabel("Product")); statusCol.add(new JLabel("Status")); for (Iterator pi = products.iterator(); pi.hasNext();) { Product p = (Product)pi.next(); nameCol.add(new JTextField(p.name, 20)); addStatus(p.name); } } void statusUpdate(ProcessStatusUpdate up) { for (Iterator pi = up.products.iterator(); pi.hasNext();) { Product p = (Product)pi.next(); setStatus(p.name, p.status); } } } static class StatusTable extends JPanel { GridColumn nameCol = new GridColumn(); GridColumn statusCol = new GridColumn(); HashMap statMap = new HashMap(); StatusTable() { super(); setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); add(nameCol); add(statusCol); } void addStatus(Object item) { statusCol.add(makeStatusField(item)); } JTextField makeStatusField(Object item) { JTextField statField = new JTextField(" ", 5); statMap.put(item, statField); return statField; } void setStatus(Object item, int status) { JTextField tf = (JTextField)statMap.get(item); Debug.assert(tf != null, "Can't find item for status"); tf.setBackground(ViewColor.statusColor[status]); } void reset() { for (Iterator ki = statMap.keySet().iterator(); ki.hasNext();) { Object key = ki.next(); setStatus(key, StatusValues.STATUS_BLANK); } } } } // Issues: // * Should it extend JPanel or contain one?