LISTSERV mailing list manager LISTSERV 16.5

Help for HPS-SVN Archives


HPS-SVN Archives

HPS-SVN Archives


HPS-SVN@LISTSERV.SLAC.STANFORD.EDU


View:

Message:

[

First

|

Previous

|

Next

|

Last

]

By Topic:

[

First

|

Previous

|

Next

|

Last

]

By Author:

[

First

|

Previous

|

Next

|

Last

]

Font:

Proportional Font

LISTSERV Archives

LISTSERV Archives

HPS-SVN Home

HPS-SVN Home

HPS-SVN  November 2014

HPS-SVN November 2014

Subject:

r1384 - in /java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay: ui/ util/

From:

[log in to unmask]

Reply-To:

Notification of commits to the hps svn repository <[log in to unmask]>

Date:

Sat, 1 Nov 2014 09:29:59 -0000

Content-Type:

text/plain

Parts/Attachments:

Parts/Attachments

text/plain (3873 lines)

Author: mccaky
Date: Sat Nov  1 02:29:56 2014
New Revision: 1384

Log:
Added updated event display GUI and ability to display ecal hardware settings on status panel.

Added:
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/DataFileViewer.java   (with props)
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PDataEventViewer.java   (with props)
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ResizableFieldPanel.java   (with props)
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/CrystalDataSet.java   (with props)
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/EcalWiringManager.java   (with props)
Modified:
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ActiveViewer.java
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ClusterViewer.java
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/FileViewer.java
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/OccupancyViewer.java
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PEventViewer.java
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/POccupancyViewer.java
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PassiveViewer.java
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/StatusPanel.java
    java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/Viewer.java

Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ActiveViewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ActiveViewer.java	(original)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ActiveViewer.java	Sat Nov  1 02:29:56 2014
@@ -19,50 +19,43 @@
  * @author Kyle McCarty
  */
 public abstract class ActiveViewer extends Viewer {
-	private static final long serialVersionUID = -6107646224627009923L;
-	// Stores whether the background color is set or not.
-	private boolean background = false;
-	// Gets events from some file.
-	protected final EventManager em;
-	
-	/**
-     * Creates an active-type <code>Viewer</code> window which draws
-     * events from the indicated data source.
-	 * @param em - The data source event manager.
-	 */
-	public ActiveViewer(EventManager em) { this(em, new String[0]); }
-	
-	/**
+    private static final long serialVersionUID = -6107646224627009923L;
+    // Stores whether the background color is set or not.
+    private boolean background = false;
+    // Gets events from some file.
+    protected final EventManager em;
+    
+    /**
      * Creates an active-type <code>Viewer</code> window which draws
      * events from the indicated data source with additional status
      * fields defined by the <code>fieldNames</code> argument.
-	 * @param em - The data source event manager.
-	 * @param fieldNames - An array of additional status fields
-	 * that should be displayed.
-	 */
-	public ActiveViewer(EventManager em, String... fieldNames) {
-		// Pass any additional field values to the super class.
-		super(fieldNames);
-		
-		// Set the data source.
-		this.em = em;
-		
+     * @param em - The data source event manager.
+     * @param fieldNames - An array of additional status fields
+     * that should be displayed.
+     */
+    public ActiveViewer(EventManager em) {
+        // Pass any additional field values to the super class.
+        super();
+        
+        // Set the data source.
+        this.em = em;
+        
         // Make a key listener to change events.
         addKeyListener(new EcalKeyListener());
-	}
-	
+    }
+    
     /**
      * Feeds the calorimeter panel the data from the next event.
      * @throws IOException Occurs when there is an issue with reading the data file.
      **/
-	public abstract void displayNextEvent() throws IOException;
-	
+    public abstract void displayNextEvent() throws IOException;
+    
     /**
      * Feeds the calorimeter panel the data from the previous event.
      * @throws IOException Occurs when there is an issue with reading the data file.
      **/
-	public abstract void displayPreviousEvent() throws IOException;
-	
+    public abstract void displayPreviousEvent() throws IOException;
+    
     /**
      * The <code>EcalListener</code> class binds keys to actions.
      * Bound actions include:
@@ -71,7 +64,7 @@
      * b             :: Toggle color-mapping for 0 energy crystals
      * h             :: Toggle selected crystal highlighting
      * l             :: Toggle logarithmic versus linear scaling
-     * s			 :: Saves the current display to a file
+     * s             :: Saves the current display to a file
      **/
     private class EcalKeyListener implements KeyListener {
         public void keyPressed(KeyEvent e) { }
@@ -97,9 +90,9 @@
             
             // 'b' toggles the default white background.
             else if(e.getKeyCode() == 66) {
-            	if(background) { ecalPanel.setDefaultCrystalColor(null); }
-            	else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
-            	background = !background;
+                if(background) { ecalPanel.setDefaultCrystalColor(null); }
+                else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
+                background = !background;
             }
             
             // 'h' toggles highlighting the crystal under the cursor.
@@ -107,45 +100,45 @@
             
             // 'l' toggles linear or logarithmic scaling.
             else if(e.getKeyCode() == 76) {
-            	if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
-            	else { ecalPanel.setScalingLinear(); }
+                if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
+                else { ecalPanel.setScalingLinear(); }
             }
             
             // 'x' toggles x-axis mirroring.
             else if(e.getKeyCode() == 88) {
-            	ecalPanel.setMirrorX(!ecalPanel.isMirroredX());
-            	updateStatusPanel();
+                ecalPanel.setMirrorX(!ecalPanel.isMirroredX());
+                updateStatusPanel();
             }
             
             // 'y' toggles y-axis mirroring.
             else if(e.getKeyCode() == 89) {
-            	ecalPanel.setMirrorY(!ecalPanel.isMirroredY());
-            	updateStatusPanel();
+                ecalPanel.setMirrorY(!ecalPanel.isMirroredY());
+                updateStatusPanel();
             }
             
             // 's' saves the panel to a file.
             else if(e.getKeyCode() == 83) {
-            	// Make a new buffered image on which to draw the content pane.
-            	BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
-            			getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
-            	
-            	// Paint the content pane to image.
-            	getContentPane().paint(screenshot.getGraphics());
-            	
-            	// Get the lowest available file name.
-            	int fileNum = 0;
-            	File imageFile = new File("screenshot_" + fileNum + ".png");
-            	while(imageFile.exists()) {
-            		fileNum++;
-            		imageFile = new File("screenshot_" + fileNum + ".png");
-            	}
-            	
-            	// Save the image to a PNG file.
-            	try { ImageIO.write(screenshot, "PNG", imageFile); }
-            	catch(IOException ioe) {
-            		System.err.println("Error saving file \"screenshot.png\".");
-            	}
-            	System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
+                // Make a new buffered image on which to draw the content pane.
+                BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
+                        getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
+                
+                // Paint the content pane to image.
+                getContentPane().paint(screenshot.getGraphics());
+                
+                // Get the lowest available file name.
+                int fileNum = 0;
+                File imageFile = new File("screenshot_" + fileNum + ".png");
+                while(imageFile.exists()) {
+                    fileNum++;
+                    imageFile = new File("screenshot_" + fileNum + ".png");
+                }
+                
+                // Save the image to a PNG file.
+                try { ImageIO.write(screenshot, "PNG", imageFile); }
+                catch(IOException ioe) {
+                    System.err.println("Error saving file \"screenshot.png\".");
+                }
+                System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
             }
             
             // Otherwise, print out the key code for the pressed key.

Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ClusterViewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ClusterViewer.java	(original)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ClusterViewer.java	Sat Nov  1 02:29:56 2014
@@ -31,10 +31,11 @@
  * 
  * @author Kyle McCarty
  */
+@Deprecated
 public class ClusterViewer extends ActiveViewer {
-	private static final long serialVersionUID = 17058336873349781L;
-	// Stores whether the background color is set or not.
-	private boolean background = false;
+    private static final long serialVersionUID = 17058336873349781L;
+    // Stores whether the background color is set or not.
+    private boolean background = false;
     // Store the index in the buffer of the displayed event.
     private int bufferIndex;
     // Map cluster location to a cluster object.
@@ -50,197 +51,205 @@
     // Additional status display field names for this data type.
     private static final String[] fieldNames = { "Shared Hits", "Component Hits", "Cluster Energy", "Buffer Index" };
     
-	/**
-	 * <b>ClusterViewer</b><br/><br/>
+    /**
+     * <b>ClusterViewer</b><br/><br/>
      * <code>public <b>ClusterViewer</b>()</code><br/><br/>
      * Constructs a new <code>Viewer</code> for displaying data read
      * from a file.
-	 * @param dataSource - The <code>EventManager</code> responsible
-	 * for reading data from a file.
-	 * @throws NullPointerException Occurs if the event manager is
-	 * <code>null</code>.
-	 */
-	public ClusterViewer(EventManager dataSource, int eventWindow) throws NullPointerException {
-		// Pass any additional fields required by the event manager
-		// to the underlying Viewer object to be added to the status
-		// display panel.
-		super(dataSource, fieldNames);
-		
-		// Define the event window and initialize the event data.
-		this.eventWindow = eventWindow;
-		eventEnergyBuffer = new LinkedList<Double[][]>();
-		eventHitBuffer = new LinkedList<List<EcalHit>>();
-		
-		// Prepare the event buffer to display the first event.
-		try {
-			// Make an empty array. At the start, there are no previous
-			// events to load.
-			Double[][] emptyArray = new Double[46][11];
-			for(int x = 0; x < 46; x++) {
-				for(int y = 0; y < 11; y++) { emptyArray[x][y] = new Double(0.0); }
-			}
-			
-			// Populate the eventWindow before section of the buffer
-			// with the empty events.
-			for(int i = 0; i <= eventWindow; i++) {
-				eventEnergyBuffer.addFirst(emptyArray);
-				eventHitBuffer.addFirst(new ArrayList<EcalHit>());
-			}
-			
-			// Fill the rest of the array with future events.
-			for(int i = 0; i < eventWindow; i++) {
-				em.nextEvent();
-				eventEnergyBuffer.addFirst(toEnergyArray(em.getHits()));
-				eventHitBuffer.addFirst(em.getHits());
-			}
-		}
-		catch(java.io.IOException e) { System.exit(1); }
-		
+     * @param dataSource - The <code>EventManager</code> responsible
+     * for reading data from a file.
+     * @throws NullPointerException Occurs if the event manager is
+     * <code>null</code>.
+     */
+    public ClusterViewer(EventManager dataSource, int eventWindow) throws NullPointerException {
+        // Initialize the superclass.
+        super(dataSource);
+        
+        // Add the additional fields.
+        for(String field : fieldNames) {
+            addStatusField(field);
+        }
+        
+        // Define the event window and initialize the event data.
+        this.eventWindow = eventWindow;
+        eventEnergyBuffer = new LinkedList<Double[][]>();
+        eventHitBuffer = new LinkedList<List<EcalHit>>();
+        
+        // Prepare the event buffer to display the first event.
+        try {
+            // Make an empty array. At the start, there are no previous
+            // events to load.
+            Double[][] emptyArray = new Double[46][11];
+            for(int x = 0; x < 46; x++) {
+                for(int y = 0; y < 11; y++) { emptyArray[x][y] = new Double(0.0); }
+            }
+            
+            // Populate the eventWindow before section of the buffer
+            // with the empty events.
+            for(int i = 0; i <= eventWindow; i++) {
+                eventEnergyBuffer.addFirst(emptyArray);
+                eventHitBuffer.addFirst(new ArrayList<EcalHit>());
+            }
+            
+            // Fill the rest of the array with future events.
+            for(int i = 0; i < eventWindow; i++) {
+                em.nextEvent();
+                eventEnergyBuffer.addFirst(toEnergyArray(em.getHits()));
+                eventHitBuffer.addFirst(em.getHits());
+            }
+        }
+        catch(java.io.IOException e) { System.exit(1); }
+        
         // Make a key listener to change events.
         addKeyListener(new EcalKeyListener());
-	}
-    
-    /**
-     * <b>displayNextEvent</b><br/><br/>
-     * <code>public void <b>displayNextEvent</b>()</code><br/><br/>
+    }
+    
+    /**
      * Feeds the calorimeter panel the data from the next event.
      * @throws IOException Occurs when there is an issue with reading the data file.
      **/
+    @Override
     public void displayNextEvent() throws IOException { getEvent(true); }
     
     /**
-     * <b>displayPreviousEvent</b><br/><br/>
-     * <code>public void <b>displayPreviousEvent</b>()</code><br/><br/>
      * Feeds the calorimeter panel the data from the previous event.
      * @throws IOException Occurs when there is an issue with reading the data file.
      **/
+    @Override
     public void displayPreviousEvent() throws IOException { getEvent(false); }
-	
-	public List<Cluster> getClusters() {
-		// Get the set of hits in the middle of the buffer. This is
-		// the "current" event.
-		List<EcalHit> activeEvent = eventHitBuffer.get(eventWindow);
-		
-		// Store clusters.
-		ArrayList<Cluster> clusterList = new ArrayList<Cluster>();
-		
-		// For each hit, check if it meets the criteria for a cluster.
-		for(EcalHit hit : activeEvent) {
-			// Track whether this hit is a cluster.
-			boolean isCluster = true;
-			
-			// Track the current hit's cluster energy.
-			double clusterEnergy = 0.0;
-			
-			// Convert the current hit to the proper coordinates.
-			Point hitLoc = toPanelPoint(hit.getLocation());
-			
-			// Track which crystals are part of the cluster.
-			HashSet<Point> componentSet = new HashSet<Point>();
-			
-			// Get the set of the current hit's neighbors.
-			Set<Point> neighbors = ecalPanel.getNeighbors(hitLoc);
-			
-			// Loop through the buffer and perform comparisons.
-			for(Double[][] event : eventEnergyBuffer) {
-				// Increment the cluster energy by the hit's energy at
-				// the current time in the buffer.
-				clusterEnergy += event[hitLoc.x][hitLoc.y];
-				
-				// A hit must be larger than itself at all other times
-				// stored in the buffer.
-				if(event[hitLoc.x][hitLoc.y] > hit.getEnergy()) {
-					isCluster = false;
-					break;
-				}
-				
-				// A hit must be larger than its immediate neighbors
-				// at all times in the buffer as well.
-				for(Point neighbor : neighbors) {
-					// Increment the cluster energy by the neighbor's
-					// energy at the current time in the buffer.
-					clusterEnergy += event[neighbor.x][neighbor.y];
-					
-					// Check that the neighbor's energy is not higher
-					// than the present hit's.
-					if(event[neighbor.x][neighbor.y] > hit.getEnergy()) {
-						isCluster = false;
-						break;
-					}
-					
-					// If this neighbor has a non-zero energy, it is
-					// a component of the potential cluster.
-					if(event[neighbor.x][neighbor.y] != 0) { componentSet.add(neighbor); }
-				}
-			}
-			
-			// If the current hit did not fail any of the preceding
-			// checks, then it is a cluster and should be added to
-			// the cluster list.
-			if(isCluster) {
-				Cluster cluster = new Cluster(hit.getLocation(), clusterEnergy);
-				for(Point neighbor : componentSet) { cluster.addComponentHit(toEcalPoint(neighbor)); }
-				clusterList.add(cluster);
-			}
-		}
-		
-		// Return the list of clusters.
-		return clusterList;
-	}
-    
+    
+    /**
+     * Generates a list of clusters from the list of hits in the event.
+     * This was used as a debugging method for the current clustering
+     * algorithm.
+     * @return Returns a generated list of clusters.
+     */
+    public List<Cluster> getClusters() {
+        // Get the set of hits in the middle of the buffer. This is
+        // the "current" event.
+        List<EcalHit> activeEvent = eventHitBuffer.get(eventWindow);
+        
+        // Store clusters.
+        ArrayList<Cluster> clusterList = new ArrayList<Cluster>();
+        
+        // For each hit, check if it meets the criteria for a cluster.
+        for(EcalHit hit : activeEvent) {
+            // Track whether this hit is a cluster.
+            boolean isCluster = true;
+            
+            // Track the current hit's cluster energy.
+            double clusterEnergy = 0.0;
+            
+            // Convert the current hit to the proper coordinates.
+            Point hitLoc = toPanelPoint(hit.getLocation());
+            
+            // Track which crystals are part of the cluster.
+            HashSet<Point> componentSet = new HashSet<Point>();
+            
+            // Get the set of the current hit's neighbors.
+            Set<Point> neighbors = ecalPanel.getNeighbors(hitLoc);
+            
+            // Loop through the buffer and perform comparisons.
+            for(Double[][] event : eventEnergyBuffer) {
+                // Increment the cluster energy by the hit's energy at
+                // the current time in the buffer.
+                clusterEnergy += event[hitLoc.x][hitLoc.y];
+                
+                // A hit must be larger than itself at all other times
+                // stored in the buffer.
+                if(event[hitLoc.x][hitLoc.y] > hit.getEnergy()) {
+                    isCluster = false;
+                    break;
+                }
+                
+                // A hit must be larger than its immediate neighbors
+                // at all times in the buffer as well.
+                for(Point neighbor : neighbors) {
+                    // Increment the cluster energy by the neighbor's
+                    // energy at the current time in the buffer.
+                    clusterEnergy += event[neighbor.x][neighbor.y];
+                    
+                    // Check that the neighbor's energy is not higher
+                    // than the present hit's.
+                    if(event[neighbor.x][neighbor.y] > hit.getEnergy()) {
+                        isCluster = false;
+                        break;
+                    }
+                    
+                    // If this neighbor has a non-zero energy, it is
+                    // a component of the potential cluster.
+                    if(event[neighbor.x][neighbor.y] != 0) { componentSet.add(neighbor); }
+                }
+            }
+            
+            // If the current hit did not fail any of the preceding
+            // checks, then it is a cluster and should be added to
+            // the cluster list.
+            if(isCluster) {
+                Cluster cluster = new Cluster(hit.getLocation(), clusterEnergy);
+                for(Point neighbor : componentSet) { cluster.addComponentHit(toEcalPoint(neighbor)); }
+                clusterList.add(cluster);
+            }
+        }
+        
+        // Return the list of clusters.
+        return clusterList;
+    }
+    
+    @Override
     protected void updateStatusPanel() {
-    	super.updateStatusPanel();
-    	
-		// Get the currently selected crystal.
-		Point crystal = ecalPanel.getSelectedCrystal();
-    	
-		// If the active crystal is not null, see if it is a cluster.
-		if(crystal != null) {
-			// Get the cluster associated with this point.
-			Cluster activeCluster = clusterMap.get(crystal);
-			
-			// If the cluster is null, we set everything to undefined.
-			if(activeCluster == null) {
-				for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); }
-			}
-			
-			// Otherwise, define the fields based on the cluster.
-			else {
-				// Get the shared and component hit counts.
-				setStatusField(fieldNames[0], Integer.toString(activeCluster.getSharedHitCount()));
-				setStatusField(fieldNames[1], Integer.toString(activeCluster.getComponentHitCount()));
-				
-				// Format the cluster energy, or account for it if it
-				// doesn't exist.
-				String energy;
-				if(activeCluster.getClusterEnergy() != Double.NaN) {
-					DecimalFormat formatter = new DecimalFormat("0.####E0");
-					energy = formatter.format(activeCluster.getClusterEnergy());
-				}
-				else { energy = "---"; }
-				setStatusField(fieldNames[2], energy);
-			}
-		}
-		// Otherwise, clear the field values.
-		else { for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); } }
-		
-		// Write the current buffer index.
-		
-		setStatusField(fieldNames[3], Integer.toString(eventWindow - bufferIndex));
-    }
-    
-	/**
-	 * <b>displayEvent</b><br/><br/>
-	 * <code>private void <b>displayEvent</b></code><br/><br/>
-	 * Displays the given lists of hits and clusters on the calorimeter
-	 * panel.
-	 * @param hitList - A list of hits for the current event.
-	 * @param clusterList  - A list of clusters for the current event.
-	 */
-	private void displayEvent(List<EcalHit> hitList, List<Cluster> clusterList) {
-		// Suppress the calorimeter panel.
-		ecalPanel.setSuppressRedraw(true);
-		
+        super.updateStatusPanel();
+        
+        // Get the currently selected crystal.
+        Point crystal = ecalPanel.getSelectedCrystal();
+        
+        // If the active crystal is not null, see if it is a cluster.
+        if(crystal != null) {
+            // Get the cluster associated with this point.
+            Cluster activeCluster = clusterMap.get(crystal);
+            
+            // If the cluster is null, we set everything to undefined.
+            if(activeCluster == null) {
+                for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); }
+            }
+            
+            // Otherwise, define the fields based on the cluster.
+            else {
+                // Get the shared and component hit counts.
+                setStatusField(fieldNames[0], Integer.toString(activeCluster.getSharedHitCount()));
+                setStatusField(fieldNames[1], Integer.toString(activeCluster.getComponentHitCount()));
+                
+                // Format the cluster energy, or account for it if it
+                // doesn't exist.
+                String energy;
+                if(activeCluster.getClusterEnergy() != Double.NaN) {
+                    DecimalFormat formatter = new DecimalFormat("0.####E0");
+                    energy = formatter.format(activeCluster.getClusterEnergy());
+                }
+                else { energy = "---"; }
+                setStatusField(fieldNames[2], energy);
+            }
+        }
+        // Otherwise, clear the field values.
+        else { for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); } }
+        
+        // Write the current buffer index.
+        
+        setStatusField(fieldNames[3], Integer.toString(eventWindow - bufferIndex));
+    }
+    
+    /**
+     * <b>displayEvent</b><br/><br/>
+     * <code>private void <b>displayEvent</b></code><br/><br/>
+     * Displays the given lists of hits and clusters on the calorimeter
+     * panel.
+     * @param hitList - A list of hits for the current event.
+     * @param clusterList  - A list of clusters for the current event.
+     */
+    private void displayEvent(List<EcalHit> hitList, List<Cluster> clusterList) {
+        // Suppress the calorimeter panel.
+        ecalPanel.setSuppressRedraw(true);
+        
         // Display the hits.
         for (EcalHit h : hitList) {
             int ix = toPanelX(h.getX());
@@ -250,28 +259,28 @@
         
         // Display the clusters.
         for(Cluster cluster : clusterList) {
-        	Point rawCluster = cluster.getClusterCenter();
-        	Point clusterCenter = toPanelPoint(rawCluster);
+            Point rawCluster = cluster.getClusterCenter();
+            Point clusterCenter = toPanelPoint(rawCluster);
             ecalPanel.setCrystalCluster(clusterCenter.x, clusterCenter.y, true);
             
-        	// Add component hits to the calorimeter panel.
-        	for(Point ch : cluster.getComponentHits()) {
-        		ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(ch), HIGHLIGHT_CLUSTER_COMPONENT));
-        	}
-        	
-        	// Add shared hits to the calorimeter panel.
-        	for(Point sh : cluster.getSharedHits()) {
-        		ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(sh), HIGHLIGHT_CLUSTER_SHARED));
-        	}
-        }
-        
-		// Stop suppressing the panel and order it to redraw.
-		ecalPanel.setSuppressRedraw(false);
-		ecalPanel.repaint();
+            // Add component hits to the calorimeter panel.
+            for(Point ch : cluster.getComponentHits()) {
+                ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(ch), HIGHLIGHT_CLUSTER_COMPONENT));
+            }
+            
+            // Add shared hits to the calorimeter panel.
+            for(Point sh : cluster.getSharedHits()) {
+                ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(sh), HIGHLIGHT_CLUSTER_SHARED));
+            }
+        }
+        
+        // Stop suppressing the panel and order it to redraw.
+        ecalPanel.setSuppressRedraw(false);
+        ecalPanel.repaint();
         
         // Update the status panel to account for the new event.
         updateStatusPanel();
-	}
+    }
     
     /**
      * <b>getEvent</b><br/><br/>
@@ -294,16 +303,16 @@
         
         // Remove the last buffer event and add the new one.
         if(forward) {
-        	eventEnergyBuffer.removeLast();
-        	eventHitBuffer.removeLast();
-        	eventEnergyBuffer.addFirst(toEnergyArray(em.getHits()));
-        	eventHitBuffer.addFirst(em.getHits());
+            eventEnergyBuffer.removeLast();
+            eventHitBuffer.removeLast();
+            eventEnergyBuffer.addFirst(toEnergyArray(em.getHits()));
+            eventHitBuffer.addFirst(em.getHits());
         }
         else {
-        	eventEnergyBuffer.removeFirst();
-        	eventHitBuffer.removeFirst();
-        	eventEnergyBuffer.addLast(toEnergyArray(em.getHits()));
-        	eventHitBuffer.addLast(em.getHits());
+            eventEnergyBuffer.removeFirst();
+            eventHitBuffer.removeFirst();
+            eventEnergyBuffer.addLast(toEnergyArray(em.getHits()));
+            eventHitBuffer.addLast(em.getHits());
         }
         
         // Determine if any of the hits in the active event are
@@ -321,29 +330,36 @@
         // Display it.
         displayEvent(eventHitBuffer.get(eventWindow), eventClusters);
     }
-	
-	private Double[][] toEnergyArray(List<EcalHit> hits) {
-		// Define the energy array.
-		Double[][] energy = new Double[46][11];
-		for(int x = 0; x < energy.length; x++) {
-			for(int y = 0; y < energy[x].length; y++) {
-				energy[x][y] = new Double(0);
-			}
-		}
-		
-		// For each hit, place its energy in the array.
-		for(EcalHit hit : hits) {
-			// Get the converted crystal index.
-			Point panelLoc = toPanelPoint(hit.getLocation());
-			
-			// Add the energy to the array.
-			energy[panelLoc.x][panelLoc.y] += hit.getEnergy(); 
-		}
-		
-		// Return the resulting array.
-		return energy;
-	}
-	
+    
+    /**
+     * Gets the energy that should be stored in each crystal of the
+     * calorimeter.
+     * @param hits - The list of hits for the event.
+     * @return Returns the energy of each crystal as an array of <code>
+     * Double</code> objects.
+     */
+    private Double[][] toEnergyArray(List<EcalHit> hits) {
+        // Define the energy array.
+        Double[][] energy = new Double[46][11];
+        for(int x = 0; x < energy.length; x++) {
+            for(int y = 0; y < energy[x].length; y++) {
+                energy[x][y] = new Double(0);
+            }
+        }
+        
+        // For each hit, place its energy in the array.
+        for(EcalHit hit : hits) {
+            // Get the converted crystal index.
+            Point panelLoc = toPanelPoint(hit.getLocation());
+            
+            // Add the energy to the array.
+            energy[panelLoc.x][panelLoc.y] += hit.getEnergy(); 
+        }
+        
+        // Return the resulting array.
+        return energy;
+    }
+    
     /**
      * The <code>EcalListener</code> class binds keys to actions.
      * Bound actions include:
@@ -352,11 +368,13 @@
      * b             :: Toggle color-mapping for 0 energy crystals
      * h             :: Toggle selected crystal highlighting
      * l             :: Toggle logarithmic versus linear scaling
-     * s			 :: Saves the current display to a file
+     * s             :: Saves the current display to a file
      **/
     private class EcalKeyListener implements KeyListener {
+        @Override
         public void keyPressed(KeyEvent e) { }
         
+        @Override
         public void keyReleased(KeyEvent e) {
             // If right-arrow was pressed, go to the next event.
             if (e.getKeyCode() == 39) {
@@ -379,30 +397,30 @@
             // If the down-arrow was pressed, move down a time step in
             // the buffer and display it.
             else if(e.getKeyCode() == 40) {
-            	if(bufferIndex == eventHitBuffer.size() - 1) { return; }
-            	else {
-                	bufferIndex++;
-            		ecalPanel.clearCrystals();
-            		displayEvent(eventHitBuffer.get(bufferIndex), eventClusters);
-            	}
+                if(bufferIndex == eventHitBuffer.size() - 1) { return; }
+                else {
+                    bufferIndex++;
+                    ecalPanel.clearCrystals();
+                    displayEvent(eventHitBuffer.get(bufferIndex), eventClusters);
+                }
             }
             
             // If the up-arrow was pressed, move up a time step in
             // the buffer and display it.
             else if(e.getKeyCode() == 38) {
-            	if(bufferIndex == 0) { return; }
-            	else {
-            		bufferIndex--;
-            		ecalPanel.clearCrystals();
-            		displayEvent(eventHitBuffer.get(bufferIndex), eventClusters);
-            	}
+                if(bufferIndex == 0) { return; }
+                else {
+                    bufferIndex--;
+                    ecalPanel.clearCrystals();
+                    displayEvent(eventHitBuffer.get(bufferIndex), eventClusters);
+                }
             }
             
             // 'b' toggles the default white background.
             else if(e.getKeyCode() == 66) {
-            	if(background) { ecalPanel.setDefaultCrystalColor(null); }
-            	else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
-            	background = !background;
+                if(background) { ecalPanel.setDefaultCrystalColor(null); }
+                else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
+                background = !background;
             }
             
             // 'h' toggles highlighting the crystal under the cursor.
@@ -410,39 +428,40 @@
             
             // 'l' toggles linear or logarithmic scaling.
             else if(e.getKeyCode() == 76) {
-            	if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
-            	else { ecalPanel.setScalingLinear(); }
+                if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
+                else { ecalPanel.setScalingLinear(); }
             }
             
             // 's' saves the panel to a file.
             else if(e.getKeyCode() == 83) {
-            	// Make a new buffered image on which to draw the content pane.
-            	BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
-            			getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
-            	
-            	// Paint the content pane to image.
-            	getContentPane().paint(screenshot.getGraphics());
-            	
-            	// Get the lowest available file name.
-            	int fileNum = 0;
-            	File imageFile = new File("screenshot_" + fileNum + ".png");
-            	while(imageFile.exists()) {
-            		fileNum++;
-            		imageFile = new File("screenshot_" + fileNum + ".png");
-            	}
-            	
-            	// Save the image to a PNG file.
-            	try { ImageIO.write(screenshot, "PNG", imageFile); }
-            	catch(IOException ioe) {
-            		System.err.println("Error saving file \"screenshot.png\".");
-            	}
-            	System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
+                // Make a new buffered image on which to draw the content pane.
+                BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
+                        getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
+                
+                // Paint the content pane to image.
+                getContentPane().paint(screenshot.getGraphics());
+                
+                // Get the lowest available file name.
+                int fileNum = 0;
+                File imageFile = new File("screenshot_" + fileNum + ".png");
+                while(imageFile.exists()) {
+                    fileNum++;
+                    imageFile = new File("screenshot_" + fileNum + ".png");
+                }
+                
+                // Save the image to a PNG file.
+                try { ImageIO.write(screenshot, "PNG", imageFile); }
+                catch(IOException ioe) {
+                    System.err.println("Error saving file \"screenshot.png\".");
+                }
+                System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
             }
             
             // Otherwise, print out the key code for the pressed key.
             else { System.out.printf("Key Code: %d%n", e.getKeyCode()); }
         }
         
+        @Override
         public void keyTyped(KeyEvent e) { }
     }
 }

Added: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/DataFileViewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/DataFileViewer.java	(added)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/DataFileViewer.java	Sat Nov  1 02:29:56 2014
@@ -0,0 +1,99 @@
+package org.hps.monitoring.ecal.eventdisplay.ui;
+
+import java.awt.Point;
+import java.io.IOException;
+
+import org.hps.monitoring.ecal.eventdisplay.io.EventManager;
+import org.hps.monitoring.ecal.eventdisplay.util.CrystalDataSet;
+import org.hps.monitoring.ecal.eventdisplay.util.EcalWiringManager;
+
+/**
+ * Class <code>DataFileViewer</code> is the active variant of a data
+ * viewer. It displays crystal hardware information read from a given
+ * data file and displays it along with crystal energy and index data.
+ * 
+ * @author Kyle McCarty
+ */
+public class DataFileViewer extends FileViewer {
+    // Local variables.
+    private static final long serialVersionUID = 1L;
+    private final EcalWiringManager ewm;
+    
+    // Hardware display fields.
+    private static final String[] fieldNames = {
+        "APD Number", "Preamp Number", "LED Channel", "LED Driver",
+        "FADC Slot", "FADC Channel", "Splitter Number", "HV Group",
+        "Jout", "MB", "Channel", "Gain"
+    };
+    
+    // Hardware display field indices.
+    private static final int FIELD_APD = 0;
+    private static final int FIELD_PREAMP = 1;
+    private static final int FIELD_LED_CHANNEL = 2;
+    private static final int FIELD_LED_DRIVER = 3;
+    private static final int FIELD_FADC_SLOT = 4;
+    private static final int FIELD_FADC_CHANNEL = 5;
+    private static final int FIELD_SPLITTER = 6;
+    private static final int FIELD_HV_GROUP = 7;
+    private static final int FIELD_JOUT = 8;
+    private static final int FIELD_MB = 9;
+    private static final int FIELD_CHANNEL = 10;
+    private static final int FIELD_GAIN = 11;
+    
+    /**
+     * Initializes a new <code>DataFileViewer</code> that reads from
+     * the given event manager for event data and the given hardware
+     * data file for crystal hardware data readout.
+     * @param dataSource - The manager for event data.
+     * @param crystalDataFilePath - The data file for crystal hardware
+     * information.
+     * @throws IOException Occurs if there is an error reading from
+     * either data source.
+     */
+    public DataFileViewer(EventManager dataSource, String crystalDataFilePath) throws IOException {
+        // Initialize the super class file.
+        super(dataSource);
+        
+        // Load the crystal data mapping.
+        ewm = new EcalWiringManager(crystalDataFilePath);
+        
+        // Add the crystal data fields.
+        for(String fieldName : fieldNames) {
+            addStatusField(fieldName);
+        }
+    }
+    
+    @Override
+    protected void updateStatusPanel() {
+        // Run the superclass method.
+        super.updateStatusPanel();
+        
+        // Get the selected crystal.
+        Point crystal = ecalPanel.getSelectedCrystal();
+        
+        // If a crystal is selected, display its data set.
+        if(crystal != null) {
+            // Get the LCSim coordinate system version of the crystal.
+            Point lcsimCrystal = Viewer.toEcalPoint(crystal);
+            
+            // Get the hardware data set associated with the crystal.
+            CrystalDataSet cds = ewm.getCrystalData(lcsimCrystal);
+            
+            // If the data set exists, update the all the fields.
+            if(cds != null) {
+                setStatusField(fieldNames[FIELD_APD], "" + cds.getAPDNumber());
+                setStatusField(fieldNames[FIELD_PREAMP], cds.getPreamplifierNumber().toString());
+                setStatusField(fieldNames[FIELD_LED_CHANNEL], "" + cds.getLEDChannel());
+                setStatusField(fieldNames[FIELD_LED_DRIVER], "" + cds.getLEDDriver());
+                setStatusField(fieldNames[FIELD_FADC_SLOT], "" + cds.getFADCSlot());
+                setStatusField(fieldNames[FIELD_FADC_CHANNEL], "" + cds.getFADCChannel());
+                setStatusField(fieldNames[FIELD_SPLITTER], "" + cds.getSplitterNumber());
+                setStatusField(fieldNames[FIELD_HV_GROUP], "" + cds.getHighVoltageGroup());
+                setStatusField(fieldNames[FIELD_JOUT], "" + cds.getJout());
+                setStatusField(fieldNames[FIELD_MB], "" + cds.getMB());
+                setStatusField(fieldNames[FIELD_CHANNEL], "" + cds.getChannel());
+                setStatusField(fieldNames[FIELD_GAIN], "" + cds.getGain());
+            }
+        }
+    }
+}

Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/FileViewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/FileViewer.java	(original)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/FileViewer.java	Sat Nov  1 02:29:56 2014
@@ -21,7 +21,7 @@
  * @author Kyle McCarty
  */
 public class FileViewer extends ActiveViewer {
-	private static final long serialVersionUID = 17058336873349781L;
+    private static final long serialVersionUID = 17058336873349781L;
     // Map cluster location to a cluster object.
     private HashMap<Point, Cluster> clusterMap = new HashMap<Point, Cluster>();
     // Additional status display field names for this data type.
@@ -32,80 +32,87 @@
     private static final int COMPONENT_HITS = 2;
     private static final int CLUSTER_ENERGY = 3;
     
-	/**
-	 * <b>FileViewer</b><br/><br/>
+    /**
+     * <b>FileViewer</b><br/><br/>
      * <code>public <b>FileViewer</b>()</code><br/><br/>
      * Constructs a new <code>Viewer</code> for displaying data read
      * from a file.
-	 * @param dataSource - The <code>EventManager</code> responsible
-	 * for reading data from a file.
-	 * @throws NullPointerException Occurs if the event manager is
-	 * <code>null</code>.
-	 */
-	public FileViewer(EventManager dataSource) throws NullPointerException {
-		// Pass any additional fields required by the event manager
-		// to the underlying Viewer object to be added to the status
-		// display panel.
-		super(dataSource, fieldNames);
-	}
+     * @param dataSource - The <code>EventManager</code> responsible
+     * for reading data from a file.
+     * @throws NullPointerException Occurs if the event manager is
+     * <code>null</code>.
+     */
+    public FileViewer(EventManager dataSource) throws NullPointerException {
+        // Initialize the superclass viewer.
+        super(dataSource);
+        
+        // Add additional fields.
+        insertStatusField(0, fieldNames[0]);
+        for(int index = 1; index < fieldNames.length; index++) {
+            addStatusField(fieldNames[index]);
+        }
+    }
     
+    @Override
     public void displayNextEvent() throws IOException { getEvent(true); }
     
+    @Override
     public void displayPreviousEvent() throws IOException { getEvent(false); }
     
+    @Override
     protected void updateStatusPanel() {
-    	// Update the superclass status fields.
-    	super.updateStatusPanel();
-    	
-		// Get the currently selected crystal.
-		Point crystal = ecalPanel.getSelectedCrystal();
-    	
-		// If the active crystal is not null, see if it is a cluster.
-		if(crystal != null) {
-			// Get the cluster associated with this point.
-			Cluster activeCluster = clusterMap.get(crystal);
-			
-			// If the cluster is null, we set everything to undefined.
-			if(activeCluster == null) {
-				for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); }
-			}
-			
-			// Otherwise, define the fields based on the cluster.
-			else {
-				// Get the shared and component hit counts.
-				setStatusField(fieldNames[SHARED_HITS], Integer.toString(activeCluster.getSharedHitCount()));
-				setStatusField(fieldNames[COMPONENT_HITS], Integer.toString(activeCluster.getComponentHitCount()));
-				
-				// Format the cluster energy, or account for it if it
-				// doesn't exist.
-				String energy;
-				if(activeCluster.getClusterEnergy() != Double.NaN) {
-					DecimalFormat formatter = new DecimalFormat("0.####E0");
-					energy = formatter.format(activeCluster.getClusterEnergy());
-				}
-				else { energy = "---"; }
-				setStatusField(fieldNames[CLUSTER_ENERGY], energy);
-			}
-		}
-		// Otherwise, clear the field values.
-		else { for(String field : fieldNames) { setStatusField(field, StatusPanel.NULL_VALUE); } }
-    	
-    	// Set the event number.
-    	setStatusField(fieldNames[EVENT_NUMBER], Integer.toString(em.getEventNumber()));
+        // Update the superclass status fields.
+        super.updateStatusPanel();
+        
+        // Get the currently selected crystal.
+        Point crystal = ecalPanel.getSelectedCrystal();
+        
+        // If the active crystal is not null, see if it is a cluster.
+        if(crystal != null) {
+            // Get the cluster associated with this point.
+            Cluster activeCluster = clusterMap.get(crystal);
+            
+            // If the cluster is null, we set everything to undefined.
+            if(activeCluster == null) {
+                for(String field : fieldNames) { setStatusField(field, ResizableFieldPanel.NULL_VALUE); }
+            }
+            
+            // Otherwise, define the fields based on the cluster.
+            else {
+                // Get the shared and component hit counts.
+                setStatusField(fieldNames[SHARED_HITS], Integer.toString(activeCluster.getSharedHitCount()));
+                setStatusField(fieldNames[COMPONENT_HITS], Integer.toString(activeCluster.getComponentHitCount()));
+                
+                // Format the cluster energy, or account for it if it
+                // doesn't exist.
+                String energy;
+                if(activeCluster.getClusterEnergy() != Double.NaN) {
+                    DecimalFormat formatter = new DecimalFormat("0.####E0");
+                    energy = formatter.format(activeCluster.getClusterEnergy());
+                }
+                else { energy = "---"; }
+                setStatusField(fieldNames[CLUSTER_ENERGY], energy);
+            }
+        }
+        // Otherwise, clear the field values.
+        else { for(String field : fieldNames) { setStatusField(field, ResizableFieldPanel.NULL_VALUE); } }
+        
+        // Set the event number.
+        setStatusField(fieldNames[EVENT_NUMBER], Integer.toString(em.getEventNumber()));
     }
     
-	/**
-	 * <b>displayEvent</b><br/><br/>
-	 * <code>private void <b>displayEvent</b>(List<EcalHit> hitList, List<Cluster> clusterList)</code><br/><br/>
-	 * Displays the given lists of hits and clusters on the calorimeter
-	 * panel.
-	 * @param hitList - A list of hits for the current event.
-	 * @param clusterList  - A list of clusters for the current event.
-	 */
-	private void displayEvent(List<EcalHit> hitList, List<Cluster> clusterList) {
-		// Suppress the calorimeter panel's redrawing.
-		ecalPanel.setSuppressRedraw(true);
-		
+    /**
+     * <b>displayEvent</b><br/><br/>
+     * <code>private void <b>displayEvent</b>(List<EcalHit> hitList, List<Cluster> clusterList)</code><br/><br/>
+     * Displays the given lists of hits and clusters on the calorimeter
+     * panel.
+     * @param hitList - A list of hits for the current event.
+     * @param clusterList  - A list of clusters for the current event.
+     */
+    private void displayEvent(List<EcalHit> hitList, List<Cluster> clusterList) {
+        // Suppress the calorimeter panel's redrawing.
+        ecalPanel.setSuppressRedraw(true);
+        
         // Display the hits.
         for (EcalHit h : hitList) {
             int ix = toPanelX(h.getX());
@@ -115,19 +122,19 @@
         
         // Display the clusters.
         for(Cluster cluster : clusterList) {
-        	Point rawCluster = cluster.getClusterCenter();
-        	Point clusterCenter = toPanelPoint(rawCluster);
+            Point rawCluster = cluster.getClusterCenter();
+            Point clusterCenter = toPanelPoint(rawCluster);
             ecalPanel.setCrystalCluster(clusterCenter.x, clusterCenter.y, true);
             
-        	// Add component hits to the calorimeter panel.
-        	for(Point ch : cluster.getComponentHits()) {
-        		ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(ch), HIGHLIGHT_CLUSTER_COMPONENT));
-        	}
-        	
-        	// Add shared hits to the calorimeter panel.
-        	for(Point sh : cluster.getSharedHits()) {
-        		ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(sh), HIGHLIGHT_CLUSTER_SHARED));
-        	}
+            // Add component hits to the calorimeter panel.
+            for(Point ch : cluster.getComponentHits()) {
+                ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(ch), HIGHLIGHT_CLUSTER_COMPONENT));
+            }
+            
+            // Add shared hits to the calorimeter panel.
+            for(Point sh : cluster.getSharedHits()) {
+                ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(sh), HIGHLIGHT_CLUSTER_SHARED));
+            }
         }
         
         // Stop suppressing the redraw and order the panel to update.
@@ -136,7 +143,7 @@
         
         // Update the status panel to account for the new event.
         updateStatusPanel();
-	}
+    }
     
     /**
      * <b>getEvent</b><br/><br/>

Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/OccupancyViewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/OccupancyViewer.java	(original)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/OccupancyViewer.java	Sat Nov  1 02:29:56 2014
@@ -17,63 +17,59 @@
  * @author Kyle McCarty
  */
 public class OccupancyViewer extends ActiveViewer {
-	private static final long serialVersionUID = 3712604287904215617L;
-	// The number of events that have been read so far.
-	private long events = 0;
-	// The total number of hits for each crystal position.
-	private long[][] hits;
-	
-	/**
-	 * <b>OccupancyViewer</b><br/><br/>
-     * <code>public <b>OccupancyViewer</b>(EventManager em)</code><br/><br/>
+    private static final long serialVersionUID = 3712604287904215617L;
+    // The number of events that have been read so far.
+    private long events = 0;
+    // The total number of hits for each crystal position.
+    private long[][] hits;
+    
+    /**
      * Creates a new occupancy display that draws event data from the
      * indicated data source.
-	 * @param em - The data source from which to draw events.
-	 */
-	public OccupancyViewer(EventManager em) {
-		// Initialize the super class.
-		super(em);
-		
-		// Set the title and scale.
-		setTitle("HPS Calorimeter Occupancies");
-		ecalPanel.setScaleMaximum(1.0);
-		
-		// Initialize the hit counts array.
-		Dimension ecalSize = ecalPanel.getCrystalBounds();
-		hits = new long[ecalSize.width][ecalSize.height];
-	}
+     * @param em - The data source from which to draw events.
+     */
+    public OccupancyViewer(EventManager em) {
+        // Initialize the super class.
+        super(em);
+        
+        // Set the title and scale.
+        setTitle("HPS Calorimeter Occupancies");
+        ecalPanel.setScaleMaximum(1.0);
+        
+        // Initialize the hit counts array.
+        Dimension ecalSize = ecalPanel.getCrystalBounds();
+        hits = new long[ecalSize.width][ecalSize.height];
+    }
     
+    @Override
     public void displayNextEvent() throws IOException { getEvent(true); }
     
+    @Override
     public void displayPreviousEvent() throws IOException { getEvent(false); }
     
     /**
-     * <b>resetOccupancies</b><br/><br/>
-     * <code>public void <b>resetOccupancies</b>()</code><br/><br/>
      * Clears the current occupancy data.
      */
     public void resetOccupancies() {
-    	// Clear the crystal hit counts.
-    	for(int x = 0; x < hits.length; x++) {
-    		for(int y = 0; y < hits[0].length; y++) {
-    			hits[x][y] = 0;
-    		}
-    	}
-    	
-    	// Clear the number of events.
-    	events = 0;
+        // Clear the crystal hit counts.
+        for(int x = 0; x < hits.length; x++) {
+            for(int y = 0; y < hits[0].length; y++) {
+                hits[x][y] = 0;
+            }
+        }
+        
+        // Clear the number of events.
+        events = 0;
     }
     
-	/**
-	 * <b>displayEvent</b><br/><br/>
-	 * <code>private void <b>displayEvent</b>(List<EcalHit> hitList)</code><br/><br/>
-	 * Displays the given lists of hits on the calorimeter panel.
-	 * @param hitList - A list of hits for the current event.
-	 */
-	private void displayEvent(List<EcalHit> hitList) {
-		// Suppress the calorimeter panel's redrawing.
-		ecalPanel.setSuppressRedraw(true);
-		
+    /**
+     * Displays the given lists of hits on the calorimeter panel.
+     * @param hitList - A list of hits for the current event.
+     */
+    private void displayEvent(List<EcalHit> hitList) {
+        // Suppress the calorimeter panel's redrawing.
+        ecalPanel.setSuppressRedraw(true);
+        
         // Display the hits.
         for (EcalHit h : hitList) {
             ecalPanel.addCrystalEnergy(h.getX(), h.getY(), h.getEnergy());
@@ -85,11 +81,9 @@
         
         // Update the status panel to account for the new event.
         updateStatusPanel();
-	}
+    }
     
     /**
-     * <b>getEvent</b><br/><br/>
-     * <code>private void <b>getEvent</b>(boolean forward)</code><br/><br/>
      * Reads either the next or the previous event from the event manager.
      * @param forward - Whether the event data should be read forward
      * or backward.
@@ -104,44 +98,44 @@
         
         // Get the next event.
         if(forward) {
-        	// Get the next event.
-        	em.nextEvent();
-        	
-        	// Increment the event count.
-        	events++;
-        	
-        	// For each hit, increment the hit count for the relevant
-        	// crystal by one.
-        	for(EcalHit hit : em.getHits()) {
-        		hits[toPanelX(hit.getX())][toPanelY(hit.getY())]++;
-        	}
+            // Get the next event.
+            em.nextEvent();
+            
+            // Increment the event count.
+            events++;
+            
+            // For each hit, increment the hit count for the relevant
+            // crystal by one.
+            for(EcalHit hit : em.getHits()) {
+                hits[toPanelX(hit.getX())][toPanelY(hit.getY())]++;
+            }
         }
         else {
-        	// Get the previous event.
-        	em.previousEvent();
-        	
-        	// Decrement the event count.
-        	events--;
-        	
-        	// For each hit, decrement the hit count for the relevant
-        	// crystal by one.
-        	for(EcalHit hit : em.getHits()) {
-        		hits[toPanelX(hit.getX())][toPanelY(hit.getY())]--;
-        	}
+            // Get the previous event.
+            em.previousEvent();
+            
+            // Decrement the event count.
+            events--;
+            
+            // For each hit, decrement the hit count for the relevant
+            // crystal by one.
+            for(EcalHit hit : em.getHits()) {
+                hits[toPanelX(hit.getX())][toPanelY(hit.getY())]--;
+            }
         }
         
         // Build a "hit list" from the occupancies.
         ArrayList<EcalHit> occupancyList = new ArrayList<EcalHit>();
         for(int x = 0; x < hits.length; x++) {
-        	for(int y = 0; y < hits[0].length; y++) {
-        		if(hits[x][y] != 0) {
-        			// Define the crystal ID and "energy."
-        			Point cid = new Point(x, y);
-        			double occupancy = ((double) hits[x][y]) / events;
-        			EcalHit occupancyHit = new EcalHit(cid, occupancy);
-        			occupancyList.add(occupancyHit);
-        		}
-        	}
+            for(int y = 0; y < hits[0].length; y++) {
+                if(hits[x][y] != 0) {
+                    // Define the crystal ID and "energy."
+                    Point cid = new Point(x, y);
+                    double occupancy = ((double) hits[x][y]) / events;
+                    EcalHit occupancyHit = new EcalHit(cid, occupancy);
+                    occupancyList.add(occupancyHit);
+                }
+            }
         }
         
         // Display it the occupancies.

Added: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PDataEventViewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PDataEventViewer.java	(added)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PDataEventViewer.java	Sat Nov  1 02:29:56 2014
@@ -0,0 +1,98 @@
+package org.hps.monitoring.ecal.eventdisplay.ui;
+
+import java.awt.Point;
+import java.io.IOException;
+
+import org.hps.monitoring.ecal.eventdisplay.util.CrystalDataSet;
+import org.hps.monitoring.ecal.eventdisplay.util.EcalWiringManager;
+
+/**
+ * Class <code>PDataEventViewer</code> is the passive variant of a data
+ * viewer. It displays crystal hardware information read from a given
+ * data file and displays it along with crystal energy and index data.
+ * 
+ * @author Kyle McCarty
+ */
+public class PDataEventViewer extends PEventViewer {
+    // Local variables.
+    private static final long serialVersionUID = 1L;
+    private final EcalWiringManager ewm;
+    
+    // Hardware display fields.
+    private static final String[] fieldNames = {
+        "APD Number", "Preamp Number", "LED Channel", "LED Driver",
+        "FADC Slot", "FADC Channel", "Splitter Number", "HV Group",
+        "Jout", "MB", "Channel", "Gain"
+    };
+    
+    // Hardware display field indices.
+    private static final int FIELD_APD = 0;
+    private static final int FIELD_PREAMP = 1;
+    private static final int FIELD_LED_CHANNEL = 2;
+    private static final int FIELD_LED_DRIVER = 3;
+    private static final int FIELD_FADC_SLOT = 4;
+    private static final int FIELD_FADC_CHANNEL = 5;
+    private static final int FIELD_SPLITTER = 6;
+    private static final int FIELD_HV_GROUP = 7;
+    private static final int FIELD_JOUT = 8;
+    private static final int FIELD_MB = 9;
+    private static final int FIELD_CHANNEL = 10;
+    private static final int FIELD_GAIN = 11;
+    
+    /**
+     * Initializes a new <code>DataFileViewer</code> that reads from
+     * the given event manager for event data and the given hardware
+     * data file for crystal hardware data readout.
+     * @param dataSource - The manager for event data.
+     * @param crystalDataFilePath - The data file for crystal hardware
+     * information.
+     * @throws IOException Occurs if there is an error reading from
+     * either data source.
+     */
+    public PDataEventViewer(String crystalDataFilePath) throws IOException {
+        // Initialize the super class file.
+        super();
+        
+        // Load the crystal data mapping.
+        ewm = new EcalWiringManager(crystalDataFilePath);
+        
+        // Add the crystal data fields.
+        for(String fieldName : fieldNames) {
+            addStatusField(fieldName);
+        }
+    }
+    
+    @Override
+    protected void updateStatusPanel() {
+        // Run the superclass method.
+        super.updateStatusPanel();
+        
+        // Get the selected crystal.
+        Point crystal = ecalPanel.getSelectedCrystal();
+        
+        // If a crystal is selected, display its data set.
+        if(crystal != null) {
+            // Get the LCSim coordinate system version of the crystal.
+            Point lcsimCrystal = Viewer.toEcalPoint(crystal);
+            
+            // Get the hardware data set associated with the crystal.
+            CrystalDataSet cds = ewm.getCrystalData(lcsimCrystal);
+            
+            // If the data set exists, update the all the fields.
+            if(cds != null) {
+                setStatusField(fieldNames[FIELD_APD], "" + cds.getAPDNumber());
+                setStatusField(fieldNames[FIELD_PREAMP], cds.getPreamplifierNumber().toString());
+                setStatusField(fieldNames[FIELD_LED_CHANNEL], "" + cds.getLEDChannel());
+                setStatusField(fieldNames[FIELD_LED_DRIVER], "" + cds.getLEDDriver());
+                setStatusField(fieldNames[FIELD_FADC_SLOT], "" + cds.getFADCSlot());
+                setStatusField(fieldNames[FIELD_FADC_CHANNEL], "" + cds.getFADCChannel());
+                setStatusField(fieldNames[FIELD_SPLITTER], "" + cds.getSplitterNumber());
+                setStatusField(fieldNames[FIELD_HV_GROUP], "" + cds.getHighVoltageGroup());
+                setStatusField(fieldNames[FIELD_JOUT], "" + cds.getJout());
+                setStatusField(fieldNames[FIELD_MB], "" + cds.getMB());
+                setStatusField(fieldNames[FIELD_CHANNEL], "" + cds.getChannel());
+                setStatusField(fieldNames[FIELD_GAIN], "" + cds.getGain());
+            }
+        }
+    }
+}

Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PEventViewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PEventViewer.java	(original)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PEventViewer.java	Sat Nov  1 02:29:56 2014
@@ -22,60 +22,58 @@
  * @author Kyle McCarty
  */
 public class PEventViewer extends PassiveViewer {
-	private static final long serialVersionUID = -7479125553259270894L;
-	// Stores whether the background color is set or not.
-	private boolean background = false;
-	// Stores cluster objects.
-	protected ArrayList<Cluster> clusterList = new ArrayList<Cluster>();
-	// Stores hit objects.
-	protected ArrayList<EcalHit> hitList = new ArrayList<EcalHit>();
-	
-	/**
-	 * <b>PEventViewer</b><br/><br/>
-	 * <code>public <b>PEventViewer</b>(String... fieldValues)</code><br/><br/>
-	 * Creates a passive viewer for displaying hits and clusters in
-	 * an event.
-	 * @param fieldValues - Any additional status fields to display.
-	 */
-	public PEventViewer(String... fieldValues) {
-		// Pass the field values to the superclass.
-		super(fieldValues);
-		
-		// Set the key bindings.
-		addKeyListener(new EcalKeyListener());
-	}
-	
-	public void addHit(EcalHit hit) { hitList.add(hit); }
-	
-	public void addCluster(Cluster cluster) { clusterList.add(cluster); }
-	
-	/**
-	 * <b>clearHits</b><br/><br/>
-	 * <code>public void <b>clearHits</b>()</code><br/><br/>
-	 * Removes all of the hit data from the viewer.
-	 */
-	public void clearHits() { hitList.clear(); }
-	
-	/**
-	 * <b>clearClusters</b><br/><br/>
-	 * <code>public void <b>clearClusters</b>()</code><br/><br/>
-	 * Removes all of the cluster data from the viewer.
-	 */
-	public void clearClusters() { hitList.clear(); }
-	
-	public void resetDisplay() {
-		// Reset the hit and cluster lists.
-		hitList.clear();
-		clusterList.clear();
-	}
-	
-	public void updateDisplay() {
-		// Suppress the calorimeter panel's redrawing.
-		ecalPanel.setSuppressRedraw(true);
-		
-		// Clear the panel data.
-		ecalPanel.clearCrystals();
-		
+    private static final long serialVersionUID = -7479125553259270894L;
+    // Stores whether the background color is set or not.
+    private boolean background = false;
+    // Stores cluster objects.
+    protected ArrayList<Cluster> clusterList = new ArrayList<Cluster>();
+    // Stores hit objects.
+    protected ArrayList<EcalHit> hitList = new ArrayList<EcalHit>();
+    
+    /**
+     * Creates a passive viewer for displaying hits and clusters in
+     * an event.
+     * @param fieldValues - Any additional status fields to display.
+     */
+    public PEventViewer() {
+        // Initialize the superclass.
+        super();
+        
+        // Set the key bindings.
+        addKeyListener(new EcalKeyListener());
+    }
+    
+    @Override
+    public void addHit(EcalHit hit) { hitList.add(hit); }
+    
+    @Override
+    public void addCluster(Cluster cluster) { clusterList.add(cluster); }
+    
+    /**
+     * Removes all of the hit data from the viewer.
+     */
+    public void clearHits() { hitList.clear(); }
+    
+    /**
+     * Removes all of the cluster data from the viewer.
+     */
+    public void clearClusters() { hitList.clear(); }
+    
+    @Override
+    public void resetDisplay() {
+        // Reset the hit and cluster lists.
+        hitList.clear();
+        clusterList.clear();
+    }
+    
+    @Override
+    public void updateDisplay() {
+        // Suppress the calorimeter panel's redrawing.
+        ecalPanel.setSuppressRedraw(true);
+        
+        // Clear the panel data.
+        ecalPanel.clearCrystals();
+        
         // Display the hits.
         for (EcalHit h : hitList) {
             int ix = toPanelX(h.getX());
@@ -85,19 +83,19 @@
         
         // Display the clusters.
         for(Cluster cluster : clusterList) {
-        	Point rawCluster = cluster.getClusterCenter();
-        	Point clusterCenter = toPanelPoint(rawCluster);
+            Point rawCluster = cluster.getClusterCenter();
+            Point clusterCenter = toPanelPoint(rawCluster);
             ecalPanel.setCrystalCluster(clusterCenter.x, clusterCenter.y, true);
             
-        	// Add component hits to the calorimeter panel.
-        	for(Point ch : cluster.getComponentHits()) {
-        		ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(ch), HIGHLIGHT_CLUSTER_COMPONENT));
-        	}
-        	
-        	// Add shared hits to the calorimeter panel.
-        	for(Point sh : cluster.getSharedHits()) {
-        		ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(sh), HIGHLIGHT_CLUSTER_SHARED));
-        	}
+            // Add component hits to the calorimeter panel.
+            for(Point ch : cluster.getComponentHits()) {
+                ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(ch), HIGHLIGHT_CLUSTER_COMPONENT));
+            }
+            
+            // Add shared hits to the calorimeter panel.
+            for(Point sh : cluster.getSharedHits()) {
+                ecalPanel.addAssociation(new Association(clusterCenter, toPanelPoint(sh), HIGHLIGHT_CLUSTER_SHARED));
+            }
         }
         
         // Stop suppressing the redraw and order the panel to update.
@@ -106,25 +104,27 @@
         
         // Update the status panel to account for the new event.
         updateStatusPanel();
-	}
-	
+    }
+    
     /**
      * The <code>EcalListener</code> class binds keys to actions.
      * Bound actions include:
      * b             :: Toggle color-mapping for 0 energy crystals
      * h             :: Toggle selected crystal highlighting
      * l             :: Toggle logarithmic versus linear scaling
-     * s			 :: Saves the current display to a file
+     * s             :: Saves the current display to a file
      **/
     private class EcalKeyListener implements KeyListener {
+        @Override
         public void keyPressed(KeyEvent e) { }
         
+        @Override
         public void keyReleased(KeyEvent e) {
             // 'b' toggles the default white background.
             if(e.getKeyCode() == 66) {
-            	if(background) { ecalPanel.setDefaultCrystalColor(null); }
-            	else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
-            	background = !background;
+                if(background) { ecalPanel.setDefaultCrystalColor(null); }
+                else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
+                background = !background;
             }
             
             // 'h' toggles highlighting the crystal under the cursor.
@@ -132,39 +132,40 @@
             
             // 'l' toggles linear or logarithmic scaling.
             else if(e.getKeyCode() == 76) {
-            	if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
-            	else { ecalPanel.setScalingLinear(); }
+                if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
+                else { ecalPanel.setScalingLinear(); }
             }
             
             // 's' saves the panel to a file.
             else if(e.getKeyCode() == 83) {
-            	// Make a new buffered image on which to draw the content pane.
-            	BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
-            			getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
-            	
-            	// Paint the content pane to image.
-            	getContentPane().paint(screenshot.getGraphics());
-            	
-            	// Get the lowest available file name.
-            	int fileNum = 0;
-            	File imageFile = new File("screenshot_" + fileNum + ".png");
-            	while(imageFile.exists()) {
-            		fileNum++;
-            		imageFile = new File("screenshot_" + fileNum + ".png");
-            	}
-            	
-            	// Save the image to a PNG file.
-            	try { ImageIO.write(screenshot, "PNG", imageFile); }
-            	catch(IOException ioe) {
-            		System.err.println("Error saving file \"screenshot.png\".");
-            	}
-            	System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
+                // Make a new buffered image on which to draw the content pane.
+                BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
+                        getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
+                
+                // Paint the content pane to image.
+                getContentPane().paint(screenshot.getGraphics());
+                
+                // Get the lowest available file name.
+                int fileNum = 0;
+                File imageFile = new File("screenshot_" + fileNum + ".png");
+                while(imageFile.exists()) {
+                    fileNum++;
+                    imageFile = new File("screenshot_" + fileNum + ".png");
+                }
+                
+                // Save the image to a PNG file.
+                try { ImageIO.write(screenshot, "PNG", imageFile); }
+                catch(IOException ioe) {
+                    System.err.println("Error saving file \"screenshot.png\".");
+                }
+                System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
             }
             
             // Otherwise, print out the key code for the pressed key.
             else { System.out.printf("Key Code: %d%n", e.getKeyCode()); }
         }
         
+        @Override
         public void keyTyped(KeyEvent e) { }
     }
 }

Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/POccupancyViewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/POccupancyViewer.java	(original)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/POccupancyViewer.java	Sat Nov  1 02:29:56 2014
@@ -16,100 +16,93 @@
  * @author Kyle McCarty
  */
 public class POccupancyViewer extends PassiveViewer {
-	private static final long serialVersionUID = 3712604287904215617L;
-	// Store the number of hits for each crystal.
-	private long[][] hits;
-	// Store the total number of events read.
-	private long events = 0;
-	// Stores hit objects.
-	protected ArrayList<EcalHit> hitList = new ArrayList<EcalHit>();
-	
-	/**
-	 * <b>POccupancyViewer</b><br/><br/>
-     * <code>public <b>POccupancyViewer</b>(int updateRate, boolean resetAtUpdate)</code><br/><br/>
+    private static final long serialVersionUID = 3712604287904215617L;
+    // Store the number of hits for each crystal.
+    private long[][] hits;
+    // Store the total number of events read.
+    private long events = 0;
+    // Stores hit objects.
+    protected ArrayList<EcalHit> hitList = new ArrayList<EcalHit>();
+    
+    /**
      * Initializes a <code>Viewer</code> window that displays will
      * occupancies from a data stream.
-	 */
-	public POccupancyViewer() {
-		// Set the title and scale.
-		setTitle("HPS Calorimeter Occupancies");
-		ecalPanel.setScaleMaximum(1.0);
-		
-		// Initialize the hit counts array.
-		Dimension ecalSize = ecalPanel.getCrystalBounds();
-		hits = new long[ecalSize.width][ecalSize.height];
-	}
+     */
+    public POccupancyViewer() {
+        // Set the title and scale.
+        setTitle("HPS Calorimeter Occupancies");
+        ecalPanel.setScaleMaximum(1.0);
+        
+        // Initialize the hit counts array.
+        Dimension ecalSize = ecalPanel.getCrystalBounds();
+        hits = new long[ecalSize.width][ecalSize.height];
+    }
     
-	public void addHit(EcalHit hit) {
-		// Get the panel coordinates of the hit.
-		int ix = toPanelX(hit.getX());
-		int iy = toPanelY(hit.getY());
-		
-		// Increment the hit count at the indicated location.
-		hits[ix][iy]++;
-	}
-	
-	/**
-	 * <b>addCluster</b><br/><br/>
-	 * <code>public void <b>addCluster</b>(Cluster cluster)</code><br/><br/>
-	 * Adds a new cluster to the display.<br/><br/>
-	 * <b>Note:</b> This operation is not supported for occupancies.
-	 */
-	public void addCluster(Cluster cluster) { }
-	
-	/**
-	 * <b>removeHit</b><br/><br/>
-	 * <code>public void <b>removeHit</b>(EcalHit hit)</code><br/><br/>
-	 * Removes a hit from the display.
-	 * @param hit - The hit to be removed.
-	 */
-	public void removeHit(EcalHit hit) {
-		// Get the panel coordinates of the hit.
-		int ix = toPanelX(hit.getX());
-		int iy = toPanelY(hit.getY());
-		
-		// Decrement the hit count at the indicated location.
-		hits[ix][iy]--;
-	}
-	
-	public void resetDisplay() { hitList.clear(); }
-	
-	/**
-	 * <b>incrementEventCount</b><br/><br/>
-	 * <code>public void <b>incrementEventCount</b>(int amount)</code><br/><br/>
-	 * Increments the number of events represented by the current data
-	 * set by the indicated amount. Note that this may be negative to
-	 * reduce the number of events.
-	 * @param amount - The number of events to add.
-	 */
-	public void incrementEventCount(int amount) { events += amount; }
-	
-	/**
-	 * <b>updateDisplay</b><br/><br/>
-	 * <code>public void <b>updateDisplay</b>()</code><br/><br/>
-	 * Displays the hits and clusters added by the <code>addHit</code>
-	 * and <code>addCluster</code> methods.
-	 */
-	public void updateDisplay() { 
+    @Override
+    public void addHit(EcalHit hit) {
+        // Get the panel coordinates of the hit.
+        int ix = toPanelX(hit.getX());
+        int iy = toPanelY(hit.getY());
+        
+        // Increment the hit count at the indicated location.
+        hits[ix][iy]++;
+    }
+    
+    /**
+     * Adds a new cluster to the display.<br/><br/>
+     * <b>Note:</b> This operation is not supported for occupancies.
+     */
+    public void addCluster(Cluster cluster) { }
+    
+    /**
+     * Removes a hit from the display.
+     * @param hit - The hit to be removed.
+     */
+    public void removeHit(EcalHit hit) {
+        // Get the panel coordinates of the hit.
+        int ix = toPanelX(hit.getX());
+        int iy = toPanelY(hit.getY());
+        
+        // Decrement the hit count at the indicated location.
+        hits[ix][iy]--;
+    }
+    
+    @Override
+    public void resetDisplay() { hitList.clear(); }
+    
+    /**
+     * Increments the number of events represented by the current data
+     * set by the indicated amount. Note that this may be negative to
+     * reduce the number of events.
+     * @param amount - The number of events to add.
+     */
+    public void incrementEventCount(int amount) { events += amount; }
+    
+    /**
+     * Displays the hits and clusters added by the <code>addHit</code>
+     * and <code>addCluster</code> methods.
+     */
+    @Override
+    public void updateDisplay() { 
         // Build a "hit list" from the occupancies.
         for(int x = 0; x < hits.length; x++) {
-        	for(int y = 0; y < hits[0].length; y++) {
-        		// Don't bother performing calculations or building
-        		// any objects if there are zero hits.
-        		if(hits[x][y] != 0) {
-        			// Define the crystal ID and "energy."
-        			Point cid = new Point(x, y);
-        			double occupancy = ((double) hits[x][y]) / events;
-        			
-        			// Add a "hit" formed from these values.
-        			hitList.add(new EcalHit(cid, occupancy));
-        		}
-        	}
+            for(int y = 0; y < hits[0].length; y++) {
+                // Don't bother performing calculations or building
+                // any objects if there are zero hits.
+                if(hits[x][y] != 0) {
+                    // Define the crystal ID and "energy."
+                    Point cid = new Point(x, y);
+                    double occupancy = ((double) hits[x][y]) / events;
+                    
+                    // Add a "hit" formed from these values.
+                    hitList.add(new EcalHit(cid, occupancy));
+                }
+            }
         }
         
-		// Suppress the calorimeter panel's redrawing.
-		ecalPanel.setSuppressRedraw(true);
-		
+        // Suppress the calorimeter panel's redrawing.
+        ecalPanel.setSuppressRedraw(true);
+        
         // Display the hits.
         for (EcalHit h : hitList) {
             int ix = toPanelX(h.getX());
@@ -123,5 +116,5 @@
         
         // Update the status panel to account for the new event.
         updateStatusPanel();
-	}
+    }
 }

Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PassiveViewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PassiveViewer.java	(original)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/PassiveViewer.java	Sat Nov  1 02:29:56 2014
@@ -20,94 +20,94 @@
  * @author Kyle McCarty
  */
 public abstract class PassiveViewer extends Viewer {
-	private static final long serialVersionUID = -7479125553259270894L;
-	// Stores whether the background color is set or not.
-	private boolean background = false;
-	
-	/**
-	 * <b>PassiveViewer</b><br/><br/>
-	 * <code>public <b>PassiveViewer</b>(String... fieldValues)</code><br/><br/>
-	 * @param fieldValues
-	 */
-	public PassiveViewer(String... fieldValues) {
-		// Pass the field values to the superclass.
-		super(fieldValues);
-		
-		// Set the key bindings.
-		addKeyListener(new EcalKeyListener());
-	}
-	
-	/**
-	 * <b>addHit</b><br/><br/>
-	 * <code>public void <b>addHit</b>(EcalHit hit)</code><br/><br/>
-	 * Adds a new hit to the display.
-	 * @param hit - The hit to be added.
-	 */
-	public abstract void addHit(EcalHit hit);
-	
-	/**
-	 * <b>addCluster</b><br/><br/>
-	 * <code>public void <b>addCluster</b>(Cluster cluster)</code><br/><br/>
-	 * Adds a new cluster to the display.
-	 * @param cluster - The cluster to be added.
-	 */
-	public abstract void addCluster(Cluster cluster);
-	
-	/**
-	 * <b>resetDisplay</b><br/><br/>
-	 * <code>public void <b>resetDisplay</b>()</code><br/><br/>
-	 * Clears any hits or clusters that have been added to the viewer.
-	 * Note that this does not automatically update the displayed panel.
-	 * <code>updateDisplay</code> must be called separately.
-	 */
-	public abstract void resetDisplay();
-	
-	/**
-	 * <b>setScale</b><br/><br/>
-	 * <code>public void <b>setScale</b>(int min, int max)</code><br/><br/>
-	 * Sets the upper and lower bounds of for the calorimeter display's
-	 * color mapping scale.
-	 * @param min - The lower bound.
-	 * @param max - The upper bound.
-	 */
-	public void setScale(int min, int max) {
-		ecalPanel.setScaleMinimum(min);
-		ecalPanel.setScaleMaximum(max);
-	}
-	
-	/**
-	 * <b>setScaleMaximum</b><br/><br/>
-	 * <code>public void <b>setScaleMaximum</b>(int max)</code><br/><br/>
-	 * Sets the upper bound for the calorimeter display's color mapping
-	 * scale.
-	 * @param max - The upper bound.
-	 */
-	public void setScaleMaximum(int max) { ecalPanel.setScaleMaximum(max); }
-	
-	/**
-	 * <b>setScaleMinimum</b><br/><br/>
-	 * <code>public void <b>setScaleMinimum</b>(int min)</code><br/><br/>
-	 * Sets the lower bound for the calorimeter display's color mapping
-	 * scale.
-	 * @param min - The lower bound.
-	 */
-	public void setScaleMinimum(int min) { ecalPanel.setScaleMinimum(min); }
-	
-	/**
-	 * <b>updateDisplay</b><br/><br/>
-	 * <code>public void <b>updateDisplay</b>()</code><br/><br/>
-	 * Displays the hits and clusters added by the <code>addHit</code>
-	 * and <code>addCluster</code> methods.
-	 */
-	public abstract void updateDisplay();
-	
+    private static final long serialVersionUID = -7479125553259270894L;
+    // Stores whether the background color is set or not.
+    private boolean background = false;
+    
+    /**
+     * <b>PassiveViewer</b><br/><br/>
+     * <code>public <b>PassiveViewer</b>(String... fieldValues)</code><br/><br/>
+     * @param fieldValues
+     */
+    public PassiveViewer() {
+        // Initialize the superclass.
+        super();
+        
+        // Set the key bindings.
+        addKeyListener(new EcalKeyListener());
+    }
+    
+    /**
+     * <b>addHit</b><br/><br/>
+     * <code>public void <b>addHit</b>(EcalHit hit)</code><br/><br/>
+     * Adds a new hit to the display.
+     * @param hit - The hit to be added.
+     */
+    public abstract void addHit(EcalHit hit);
+    
+    /**
+     * <b>addCluster</b><br/><br/>
+     * <code>public void <b>addCluster</b>(Cluster cluster)</code><br/><br/>
+     * Adds a new cluster to the display.
+     * @param cluster - The cluster to be added.
+     */
+    public abstract void addCluster(Cluster cluster);
+    
+    /**
+     * <b>resetDisplay</b><br/><br/>
+     * <code>public void <b>resetDisplay</b>()</code><br/><br/>
+     * Clears any hits or clusters that have been added to the viewer.
+     * Note that this does not automatically update the displayed panel.
+     * <code>updateDisplay</code> must be called separately.
+     */
+    public abstract void resetDisplay();
+    
+    /**
+     * <b>setScale</b><br/><br/>
+     * <code>public void <b>setScale</b>(int min, int max)</code><br/><br/>
+     * Sets the upper and lower bounds of for the calorimeter display's
+     * color mapping scale.
+     * @param min - The lower bound.
+     * @param max - The upper bound.
+     */
+    public void setScale(int min, int max) {
+        ecalPanel.setScaleMinimum(min);
+        ecalPanel.setScaleMaximum(max);
+    }
+    
+    /**
+     * <b>setScaleMaximum</b><br/><br/>
+     * <code>public void <b>setScaleMaximum</b>(int max)</code><br/><br/>
+     * Sets the upper bound for the calorimeter display's color mapping
+     * scale.
+     * @param max - The upper bound.
+     */
+    public void setScaleMaximum(int max) { ecalPanel.setScaleMaximum(max); }
+    
+    /**
+     * <b>setScaleMinimum</b><br/><br/>
+     * <code>public void <b>setScaleMinimum</b>(int min)</code><br/><br/>
+     * Sets the lower bound for the calorimeter display's color mapping
+     * scale.
+     * @param min - The lower bound.
+     */
+    public void setScaleMinimum(int min) { ecalPanel.setScaleMinimum(min); }
+    
+    /**
+     * <b>updateDisplay</b><br/><br/>
+     * <code>public void <b>updateDisplay</b>()</code><br/><br/>
+     * Displays the hits and clusters added by the <code>addHit</code>
+     * and <code>addCluster</code> methods.
+     */
+    public abstract void updateDisplay();
+    
     /**
      * The <code>EcalListener</code> class binds keys to actions.
      * Bound actions include:
      * b             :: Toggle color-mapping for 0 energy crystals
      * h             :: Toggle selected crystal highlighting
      * l             :: Toggle logarithmic versus linear scaling
-     * s			 :: Saves the current display to a file
+     * s             :: Saves the current display to a file
      **/
     private class EcalKeyListener implements KeyListener {
         public void keyPressed(KeyEvent e) { }
@@ -115,9 +115,9 @@
         public void keyReleased(KeyEvent e) {
             // 'b' toggles the default white background.
             if(e.getKeyCode() == 66) {
-            	if(background) { ecalPanel.setDefaultCrystalColor(null); }
-            	else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
-            	background = !background;
+                if(background) { ecalPanel.setDefaultCrystalColor(null); }
+                else { ecalPanel.setDefaultCrystalColor(Color.GRAY); }
+                background = !background;
             }
             
             // 'h' toggles highlighting the crystal under the cursor.
@@ -125,33 +125,33 @@
             
             // 'l' toggles linear or logarithmic scaling.
             else if(e.getKeyCode() == 76) {
-            	if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
-            	else { ecalPanel.setScalingLinear(); }
+                if(ecalPanel.isScalingLinear()) { ecalPanel.setScalingLogarithmic(); }
+                else { ecalPanel.setScalingLinear(); }
             }
             
             // 's' saves the panel to a file.
             else if(e.getKeyCode() == 83) {
-            	// Make a new buffered image on which to draw the content pane.
-            	BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
-            			getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
-            	
-            	// Paint the content pane to image.
-            	getContentPane().paint(screenshot.getGraphics());
-            	
-            	// Get the lowest available file name.
-            	int fileNum = 0;
-            	File imageFile = new File("screenshot_" + fileNum + ".png");
-            	while(imageFile.exists()) {
-            		fileNum++;
-            		imageFile = new File("screenshot_" + fileNum + ".png");
-            	}
-            	
-            	// Save the image to a PNG file.
-            	try { ImageIO.write(screenshot, "PNG", imageFile); }
-            	catch(IOException ioe) {
-            		System.err.println("Error saving file \"screenshot.png\".");
-            	}
-            	System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
+                // Make a new buffered image on which to draw the content pane.
+                BufferedImage screenshot = new BufferedImage(getContentPane().getWidth(),
+                        getContentPane().getHeight(), BufferedImage.TYPE_INT_ARGB);
+                
+                // Paint the content pane to image.
+                getContentPane().paint(screenshot.getGraphics());
+                
+                // Get the lowest available file name.
+                int fileNum = 0;
+                File imageFile = new File("screenshot_" + fileNum + ".png");
+                while(imageFile.exists()) {
+                    fileNum++;
+                    imageFile = new File("screenshot_" + fileNum + ".png");
+                }
+                
+                // Save the image to a PNG file.
+                try { ImageIO.write(screenshot, "PNG", imageFile); }
+                catch(IOException ioe) {
+                    System.err.println("Error saving file \"screenshot.png\".");
+                }
+                System.out.println("Screenshot saved to: " + imageFile.getAbsolutePath());
             }
             
             // Otherwise, print out the key code for the pressed key.

Added: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ResizableFieldPanel.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ResizableFieldPanel.java	(added)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/ResizableFieldPanel.java	Sat Nov  1 02:29:56 2014
@@ -0,0 +1,515 @@
+package org.hps.monitoring.ecal.eventdisplay.ui;
+
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.LayoutManager;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+/**
+ * Class <code>ResizableFieldPanel</code> is an extension of <code>
+ * JPanel</code> that contains a series of "fields." Each field is
+ * composed of a header label that displays its name and a value
+ * label that displays its value. Field names are constant once set
+ * but values may be changed.<br/>
+ * <br/>
+ * <code>ResizableFieldPanel</code> automatically handles the layout
+ * of its fields, displaying the header label next to the value label
+ * and inserting as many in a row as possible before wrapping to the
+ * next row. <code>ResizableFieldPanel</code> ensures that each field
+ * in a column is the same size as the other fields in the same column
+ * and that all fields in the same column are aligned. Individual
+ * columns may be different widths.<br/>
+ * <br/>
+ * The preferred width of a column is set by the preferred size of the
+ * largest field in that column or a minimum row size, if it is set.
+ * Fields that exceed the minimum row size are still granted their full
+ * preferred size.
+ * 
+ * @author Kyle McCarty
+ * @see JPanel
+ */
+public class ResizableFieldPanel extends JPanel {
+    // Local variables.
+    private static final long serialVersionUID = 1L;
+    private Font boldFont = getFont().deriveFont(Font.BOLD);
+    private static final int horizontal = 10;
+    private static final int vertical = 10;
+    private Dimension oldSize = new Dimension(0, 0);
+    private int oldFieldCount = 0;
+    private int minDisplayWidth = 0;
+    private Dimension userPreferred = null;
+    private Dimension actualPreferred = new Dimension(0, 0);
+    
+    // Constituent components.
+    private List<JLabel> headerList = new ArrayList<JLabel>();
+    private List<JLabel> displayList = new ArrayList<JLabel>();
+    
+    // Class variables.
+    /** The display text for a field with no value. */
+    static final String NULL_VALUE = "---";
+    
+    /**
+     * Instantiates a new <code>ResizableFieldPanel</code> with no
+     * minimum column width.
+     */
+    public ResizableFieldPanel() { this(0); }
+    
+    /**
+     * Instantiates a new <code>ResizableFieldPanel</code> with the
+     * indicated minimum column width.
+     * @param minWidth - The minimum column width.
+     */
+    public ResizableFieldPanel(int minWidth) {
+        // Component handles constituent component placement manually.
+        super.setLayout(null);
+        
+        // Set the minimum component width.
+        minDisplayWidth = minWidth >= 0 ? minWidth : 0;
+        
+        // Reset the constituent component placement whenever the base
+        // component changes size.
+        addComponentListener(new ComponentAdapter() {
+            @Override
+            public void componentResized(ComponentEvent e) {
+                // Issue an order to reset the component layout.
+                resetLayout();
+            }
+        });
+    }
+    
+    /**
+     * Adds a new field to the panel.
+     * @param fieldName - The name of the field.
+     * @throws NullPointerException Occurs if the field name is <code>
+     * null</code>.
+     */
+    public void addField(String fieldName) throws NullPointerException {
+        addField(fieldName, null);
+    }
+    
+    /**
+     * Adds a new field to the panel.
+     * @param fieldName - The name of the field.
+     * @param fieldValue - The initial value of the field.
+     * @throws NullPointerException Occurs if the field name is <code>
+     * null</code>.
+     */
+    public void addField(String fieldName, String fieldValue) throws NullPointerException {
+        // Require that the field have a non-null name.
+        if(fieldName == null) { throw new NullPointerException("Field names can not be null."); }
+        
+        // Null values default the default value.
+        if(fieldValue == null) { fieldValue = NULL_VALUE; }
+        
+        // Create new labels to populate the component.
+        JLabel header = new JLabel(fieldName + ":");
+        header.setFont(boldFont);
+        header.setHorizontalAlignment(JLabel.RIGHT);
+        JLabel display = new JLabel(fieldValue);
+        display.setFont(getFont());
+        
+        // Add the labels to the list.
+        headerList.add(header);
+        displayList.add(display);
+        
+        // Add the components to the base panel.
+        add(header);
+        add(display);
+        
+        // Reset the label layout.
+        resetLayout();
+    }
+    
+    /**
+     * Sets the values of all fields to <code>NULL_VALUE</code>.
+     */
+    public void clearFields() {
+        for(JLabel display : displayList) {
+            display.setText(NULL_VALUE);
+        }
+    }
+    
+    /**
+     * Gets the number of fields contained in the component.
+     * @return Returns the number of fields as an <code>int</code>
+     * primitive.
+     */
+    public int getFieldCount() { return headerList.size(); }
+    
+    /**
+     * Generates a map that links field name to field index.
+     * @return Returns a <code>Map</code> object linking the <code>
+     * String</code> field name to the <code>int</code> field index.
+     */
+    public Map<String, Integer> getFieldNameIndexMap() {
+        // Get the number of fields.
+        int fields = headerList.size();
+        
+        // Create the map.
+        Map<String, Integer> fieldMap = new HashMap<String, Integer>(fields);
+        
+        // Populate the map.
+        for(int index = 0; index < fields; index++) {
+            // Get the header name and remove the colon at the end.
+            String header = headerList.get(index).getText();
+            header = header.substring(0, header.length() - 1);
+            
+            // Add it to the map.
+            fieldMap.put(header, new Integer(index));
+        }
+        
+        // Return the map.
+        return fieldMap;
+    }
+    
+    /**
+     * Gets the list of all field names in order of field index.
+     * @return Returns the set of field names as an array of <code>
+     * String</code> objects.
+     */
+    public String[] getFieldNames() {
+        // Create an array for the field names.
+        String[] fieldNames = new String[headerList.size()];
+        
+        // Populate the array.
+        for(int index = 0; index < headerList.size(); index++) {
+            String fieldName = headerList.get(index).getText();
+            fieldNames[index] = fieldName.substring(0, fieldName.length() - 2);
+        }
+        
+        // Return the result.
+        return fieldNames;
+    }
+    
+    /**
+     * Gets the value for the field at the indicated index.
+     * @param fieldIndex - The index of the field.
+     * @return Returns the field value as a <code>String</code> object.
+     * @throws IndexOutOfBoundsException Occurs if an invalid field index
+     * is given.
+     */
+    public String getFieldValue(int fieldIndex) throws IndexOutOfBoundsException {
+        // Validate the index.
+        validateFieldIndex(fieldIndex);
+        
+        // Return the value of the requested field.
+        return displayList.get(fieldIndex).getText();
+    }
+    
+    @Override
+    public Dimension getPreferredSize() {
+        if(userPreferred == null) { return actualPreferred; }
+        else { return userPreferred; }
+    }
+    
+    /**
+     * Inserts a field at the indicated index, if possible.
+     * @param index - The index at which to insert the field.
+     * @param fieldName - The name of the field.
+     * @throws IndexOutOfBoundsException Occurs if the insertion index
+     * is not a valid index within the component.
+     * @throws NullPointerException Occurs if the field name is <code>
+     * null</code>.
+     */
+    public void insertField(int index, String fieldName) throws IndexOutOfBoundsException, NullPointerException {
+        insertField(index, fieldName, "");
+    }
+    
+    /**
+     * Inserts a field at the indicated index, if possible.
+     * @param index - The index at which to insert the field.
+     * @param fieldName - The name of the field.
+     * @param fieldValue - The initial value of the field.
+     * @throws IndexOutOfBoundsException Occurs if the insertion index
+     * is not a valid index within the component.
+     * @throws NullPointerException Occurs if the field name is <code>
+     * null</code>.
+     */
+    public void insertField(int index, String fieldName, String fieldValue) throws IndexOutOfBoundsException, NullPointerException {
+        // Require that the field have a non-null name.
+        if(fieldName == null) { throw new NullPointerException("Field names can not be null."); }
+        
+        // Null values default the default value.
+        if(fieldValue == null) { fieldValue = NULL_VALUE; }
+        
+        // Create new labels to populate the component.
+        JLabel header = new JLabel(fieldName + ":");
+        header.setFont(boldFont);
+        header.setHorizontalAlignment(JLabel.RIGHT);
+        JLabel display = new JLabel(fieldValue);
+        display.setFont(getFont());
+        
+        // Add the labels to the list.
+        headerList.add(index, header);
+        displayList.add(index, display);
+        
+        // Add the components to the base panel.
+        add(header);
+        add(display);
+        
+        // Reset the label layout.
+        resetLayout();
+    }
+    
+    /**
+     * Removes the field at the indicated index.
+     * @param fieldIndex - The index of the field.
+     * @throws IndexOutOfBoundsException Occurs if an invalid field index
+     * is given.
+     */
+    public void removeField(int fieldIndex) throws IndexOutOfBoundsException {
+        // Validate the index.
+        validateFieldIndex(fieldIndex);
+        
+        // Remove the requested field.
+        remove(headerList.get(fieldIndex));
+        remove(displayList.get(fieldIndex));
+        headerList.remove(fieldIndex);
+        displayList.remove(fieldIndex);
+        
+        // Reset the layout.
+        resetLayout();
+    }
+    
+    /**
+     * Sets the value of the field at the indicated index.
+     * @param fieldIndex - The index of the field.
+     * @param fieldValue - The new value of the field.
+     * @throws IndexOutOfBoundsException Occurs if an invalid field index
+     * is given.
+     */
+    public void setFieldValue(int fieldIndex, String fieldValue) throws IndexOutOfBoundsException {
+        // Validate the index.
+        validateFieldIndex(fieldIndex);
+        
+        // Set the value of the requested field.
+        displayList.get(fieldIndex).setText(fieldValue);
+    }
+    
+    @Override
+    public void setFont(Font font) {
+        // If the superclass font is null, the component is still
+        // being initialized and needs to just run the superclass
+        // method.
+        if(getFont() == null) { super.setFont(font); }
+        
+        // Otherwise, set any constituent components to the correct
+        // font as well.
+        else {
+            // If the font is being set to null, use the default
+            // system font.
+            if(font == null) { font = new Font(Font.DIALOG, Font.PLAIN, 11); }
+            
+            // Set the base panel's font to the indicated value.
+            super.setFont(font);
+            
+            // Create a new bold font.
+            boldFont = super.getFont().deriveFont(Font.BOLD);
+            
+            // Set each display label to the indicated font.
+            for(JLabel display : displayList) { display.setFont(font); }
+            
+            // Set each header label to the bold font.
+            for(JLabel header : headerList) { header.setFont(boldFont); }
+        }
+    }
+    
+    @Override
+    public void setLayout(LayoutManager mgr) {
+        // ResizableDisplayManager handles its own layout, so do nothing.
+        return;
+    }
+    
+    @Override
+    public void setPreferredSize(Dimension preferredSize) {
+        userPreferred = preferredSize;
+    }
+    
+    /**
+     * Repositions the fields to the appropriate positions given the
+     * current width of the component.
+     */
+    private void resetLayout() {
+        // If the neither the size of the component nor the number of
+        // fields have changed, there is nothing to do here.
+        if(headerList.size() == oldFieldCount && oldSize.width == getSize().width
+                && oldSize.height == getSize().height) {
+            return;
+        }
+        
+        // Set the last width and field counts.
+        oldSize = getSize();
+        oldFieldCount = headerList.size();
+        
+        // Otherwise, determine the ideal maximum number of fields that
+        // can be displayed in a single row.
+        int availableWidth = getWidth() - horizontal;
+        int max = 0;
+        idealLoop:
+        for(int index = 0; index < headerList.size(); index++) {
+            // Subtract the necessary width for the header label.
+            availableWidth -= headerList.get(index).getPreferredSize().width;
+            availableWidth -= horizontal;
+            
+            // Subtract the required width for the display label.
+            int displayWidth = displayList.get(index).getPreferredSize().width;
+            if(displayWidth < minDisplayWidth) { displayWidth = minDisplayWidth; }
+            availableWidth -= displayWidth;
+            availableWidth -= horizontal;
+            
+            // Increment the maximum number of displayable headers.
+            max++;
+            
+            // If the available width is depleted, exit the loop.
+            if(availableWidth <= 0) { break idealLoop; }
+        }
+        
+        // If the maximum displayable number is either 1 or equal to
+        // actual number, the fields should be displayed with their
+        // preferred widths.
+        if(max == 1 || max == headerList.size()) {
+            // Track the current position on the component.
+            int curX = horizontal;
+            int curY = vertical;
+            
+            // Add each field.
+            for(int index = 0; index < headerList.size(); index++) {
+                // Get the current header and display label.
+                JLabel header = headerList.get(index);
+                JLabel display = displayList.get(index);
+                
+                // Set the label sizes to the preferred size.
+                header.setSize(header.getPreferredSize());
+                display.setSize(display.getPreferredSize());
+                
+                // Set the label locations to the current correct place.
+                header.setLocation(curX, curY);
+                header.setSize(header.getPreferredSize());
+                curX += header.getSize().width + horizontal;
+                display.setLocation(curX, curY);
+                display.setSize(header.getPreferredSize());
+                curX += display.getWidth() + horizontal;
+            }
+            
+            // The process is finished, so return.
+            return;
+        }
+        
+        // Otherwise, the labels in subsequent rows must be analyzed
+        // to determine what the actual maximum number of labels per
+        // row can be used. Try the ideal maximum first and work down
+        // from there to the minimum of one per row.
+        else {
+            // Track the number of columns needed and the proper widths
+            // of each column.
+            int columns = 1;
+            int[] columnWidth = { 0 };
+            actualLoop:
+            for(int actual = max; actual > 0; actual--) {
+                // Store the actual necessary width for each component in
+                // each column for the current row size.
+                int[] actualColWidth = new int[actual + actual];
+                
+                // Track the current column.
+                int curCol = 0;
+                
+                // Iterate over the labels and find the largest necessary
+                // width for each column.
+                for(int index = 0; index < headerList.size(); index++) {
+                    // Get the widths needed for the current header and
+                    // display labels.
+                    int headerWidth = headerList.get(index).getPreferredSize().width;
+                    int displayWidth = displayList.get(index).getPreferredSize().width;
+                    if(displayWidth < minDisplayWidth) { displayWidth = minDisplayWidth; }
+                    
+                    // If the needed widths exceed the current maximum, they
+                    // should replace it.
+                    if(actualColWidth[2 * curCol] < headerWidth) { actualColWidth[2 * curCol] = headerWidth; }
+                    if(actualColWidth[(2 * curCol) + 1] < displayWidth) { actualColWidth[(2 * curCol) + 1] = displayWidth; }
+                    
+                    // Increment the current column.
+                    curCol++;
+                    if(curCol == actual) { curCol = 0; }
+                }
+                
+                // Check if the actual needed width is less than the width
+                // available for the labels.
+                availableWidth = getWidth() - horizontal;
+                for(int width : actualColWidth) {
+                    availableWidth -= width;
+                    availableWidth -= horizontal;
+                }
+                
+                // Store the results for this number of columns.
+                columns = actual;
+                columnWidth = actualColWidth;
+                
+                // If the result is either zero of positive, then there is
+                // enough space available and the current number of columns
+                // may be employed. Otherwise, continue to the next smaller
+                // number of columns.
+                if(availableWidth >= 0) { break actualLoop; }
+            }
+            
+            // The necessary width for each column and the number of columns
+            // should now be calculated. Set the constituent component
+            // positions and sizes to match it.
+            int curX = horizontal;
+            int curY = vertical;
+            int curCol = 0;
+            for(int index = 0; index < headerList.size(); index++) {
+                // Get the current header and display label.
+                JLabel header = headerList.get(index);
+                JLabel display = displayList.get(index);
+                
+                // Set the label sizes to the preferred size.
+                header.setSize(header.getPreferredSize());
+                display.setSize(display.getPreferredSize());
+                
+                // Set the label locations to the current correct place.
+                header.setLocation(curX, curY);
+                header.setSize(columnWidth[2 * curCol], header.getPreferredSize().height);
+                curX += header.getSize().width + horizontal;
+                display.setLocation(curX, curY);
+                display.setSize(columnWidth[(2 * curCol) + 1], display.getPreferredSize().height);
+                curX += display.getWidth() + horizontal;
+                
+                // Increment the current column index.
+                curCol++;
+                if(curCol == columns) {
+                    curCol = 0;
+                    curY += header.getPreferredSize().height + vertical;
+                    curX = horizontal;
+                }
+            }
+        }
+        
+        // Update the preferred size such that the preferred height will
+        // encompass all of the rows.
+        JLabel lastHeader = headerList.get(headerList.size() - 1);
+        int preferredHeight = lastHeader.getY() + lastHeader.getHeight() + vertical;
+        actualPreferred = new Dimension(getSize().width, preferredHeight);
+    }
+    
+    /**
+     * Throws an <code>IndexOutOfBoundsException</code> if the argument
+     * index is not a valid field index. This is used to validate the
+     * index given in methods requiring one.
+     * @param index - The index to validate.
+     * @throws IndexOutOfBoundsException Occurs if the index fails
+     * to validate.
+     */
+    private void validateFieldIndex(int index) throws IndexOutOfBoundsException {
+        if(index < 0 || index >= headerList.size()) {
+            throw new IndexOutOfBoundsException(String.format("Index %d is not a valid field index.", index));
+        }
+    }
+}

Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/StatusPanel.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/StatusPanel.java	(original)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/StatusPanel.java	Sat Nov  1 02:29:56 2014
@@ -9,188 +9,180 @@
 
 /**
  * Class <code>StatusPanel</code> displays text in a set of fields.
+ * This class is being phased out in favor of <code>ResizableFieldPanel
+ * </code> and should no longer be used. It will be removed next update.
  *
  * @author Kyle McCarty
  */
+@Deprecated
 public class StatusPanel extends JPanel {
-	private static final long serialVersionUID = -8353479383875379010L;
-	// The panel that displays behind the status field.
-	private BackPanel background = new BackPanel();
-	// The status fields. The first array index represents which status
-	// field and the second is always of size two, with index 0 mapping
-	// to the label that displays the field name and index 1 mapping to
-	// the label displaying the field value.
-	private JLabel[][] field;
-	// Spacing variables for panel layout.
-	private int leftBuffer = 10;
-	private int upperBuffer = 10;
-	
-	/**
-	 * <b>NULL_VALUE</b><br/><br/>
-	 * <code><b>static final String <b>NULL_VALUE</b></code><br/><br/>
-	 * A <code>String</code> representing the default value to be
-	 * displayed on the status panel whenever there is no value for
-	 * that field.
-	 */
-	static final String NULL_VALUE = "---";
-	
-	/**
-	 * <b>StatusPanel</b><br/><br/>
-	 * <code>public <b>StatusPanel</b>(String... fieldName)</code><br/><br/>
-	 * Creates a new status panel with display fields with the indicated
-	 * names. They will be assigned a field index in the order that they
-	 * are given starting with zero.
-	 * @param fieldName - The names of the fields to display.
-	 */
-	public StatusPanel(String... fieldName) {
-		// Initialize the component.
-		super();
-		
-		// Set the layout manager to manual.
-		setLayout(null);
-		
-		// Build the text fields.
-		int curZ = 0;
-		field = new JLabel[fieldName.length][2];
-		for(int i = 0; i < field.length; i++) {
-			for(int j = 0; j < field[i].length; j++) {
-				field[i][j] = new JLabel();
-				field[i][j].setOpaque(true);
-				field[i][j].setBackground(Color.WHITE);
-				add(field[i][j]);
-				setComponentZOrder(field[i][j], curZ);
-				curZ++;
-			}
-			field[i][0].setText(fieldName[i] + ":   ");
-			field[i][0].setHorizontalAlignment(JLabel.RIGHT);
-		}
-		
-		// Start the fields as null by default.
-		clearValues();
-		
-		// Build the background panel.
-		add(background);
-		setComponentZOrder(background, curZ);
-	}
-	
-	/**
-	 * <b>clearValues</b><br/><br/>
-	 * <code>public void <b>clearValues</b>()</code><br/><br/>
-	 * Sets all of the fields on the status display to the null value.
-	 */
-	public void clearValues() {
-		for(int i = 0; i < field.length; i++) {
-			field[i][1].setText(NULL_VALUE);
-		}
-	}
-	
-	/**
-	 * <b>setFieldValue</b><br/><br/>
-	 * Sets the value of the indicated field.
-	 * @param index - The field's index.
-	 * @param value - The new value to display.
-	 * @throws IndexOutOfBoundsException Occurs when the field index
-	 * is neither more than the existing number of fields or is negative.
-	 */
-	public void setFieldValue(int index, String value) throws IndexOutOfBoundsException {
-		if(index >= 0 && index < field.length) {
-			if(value == null) { field[index][1].setText(NULL_VALUE); }
-			else  { field[index][1].setText(value); }
-		}
-		else { throw new IndexOutOfBoundsException("Invalid field index."); }
-	}
-	
-	public void setSize(int width, int height) {
-		super.setSize(width, height);
-		resize();
-	}
-	
-	public void setSize(Dimension d) {
-		super.setSize(d);
-		resize();
-	}
-	
-	/**
-	 *<b>getNextX</b><br/><br/>
-	 * <code>private int <b>getNextX</b>(Component c)</code><br/><br/>
-	 * Finds the x-coordinate immediately after the component.
-	 * @param c - The component of which to find the end.
-	 * @return Returns the x-coordinate at the end of the component. 
-	 */
-	private final static int getNextX(Component c) { return getNextX(c, 0); }
-	
-	/**
-	/**
-	 *<b>getNextX</b><br/><br/>
-	 * <code>private int <b>getNextX</b>(Component c, int buffer)</code><br/><br/>
-	 * Finds the x-coordinate after the component with a given buffer.
-	 * @param c - The component of which to find the end.
-	 * @param buffer - The extra space after the component to be included.
-	 * @return Returns the x-coordinate at the end of the component,
-	 * with a buffer length.
-	 */
-	private final static int getNextX(Component c, int buffer) {
-		return c.getX() + c.getWidth() + buffer;
-	}
-	
-	/**
-	 * <b>resize</b><br/><br/>
-	 * <code>private void <b>resize</b>()</code><br/><br/>
-	 * Updates the layout of the component to the panel's current size.
-	 */
-	private void resize() {
-		// Define the width an height as convenience variables.
-		int width = getWidth();
-		int height = getHeight();
-		
-		// Size the background panel.
-		background.setBounds(0, 0, width, height);
-		
-		// Size and place the text labels.
-		if(field.length != 0) {
-			int labelHeight = (height - (int)(upperBuffer + 5)) / 3;
-			int labelRem = (height - upperBuffer - 8) % field.length;
-			int curX = leftBuffer;
-			int curY = (int)(upperBuffer + 2);
-			for(int i = 0; i < field.length; i++) {
-				// Determine the appropriate field height.
-				int thisHeight = labelHeight;
-				if(labelRem > 0) {
-					thisHeight++;
-					labelRem--;
-				}
-				
-				// Place the field.
-				field[i][0].setBounds(curX, curY, 130, thisHeight);
-				field[i][1].setBounds(getNextX(field[i][0]), curY, 75, thisHeight);
-				
-				// If we have written three labels, then start a new column.
-				if(i % 3 == 2) {
-					curX = getNextX(field[i][1], 10);
-					curY = (int)(upperBuffer + 2);
-				}
-				
-				// Otherwise just increment the current height.
-				else { curY += thisHeight; }
-			}
-		}
-	}
-	
-	/**
-	 * Class <code>BackPanel</code> simply renders the background panel
-	 * for the status panel.
-	 */
-	private class BackPanel extends JPanel {
-		private static final long serialVersionUID = 4997805650267243080L;
+    private static final long serialVersionUID = -8353479383875379010L;
+    // The panel that displays behind the status field.
+    private BackPanel background = new BackPanel();
+    // The status fields. The first array index represents which status
+    // field and the second is always of size two, with index 0 mapping
+    // to the label that displays the field name and index 1 mapping to
+    // the label displaying the field value.
+    private JLabel[][] field;
+    // Spacing variables for panel layout.
+    private int leftBuffer = 10;
+    private int upperBuffer = 10;
+    
+    /**
+     * A <code>String</code> representing the default value to be
+     * displayed on the status panel whenever there is no value for
+     * that field.
+     */
+    static final String NULL_VALUE = "---";
+    
+    /**
+     * Creates a new status panel with display fields with the indicated
+     * names. They will be assigned a field index in the order that they
+     * are given starting with zero.
+     * @param fieldName - The names of the fields to display.
+     */
+    public StatusPanel(String... fieldName) {
+        // Initialize the component.
+        super();
+        
+        // Set the layout manager to manual.
+        setLayout(null);
+        
+        // Build the text fields.
+        int curZ = 0;
+        field = new JLabel[fieldName.length][2];
+        for(int i = 0; i < field.length; i++) {
+            for(int j = 0; j < field[i].length; j++) {
+                field[i][j] = new JLabel();
+                field[i][j].setOpaque(true);
+                field[i][j].setBackground(Color.WHITE);
+                add(field[i][j]);
+                setComponentZOrder(field[i][j], curZ);
+                curZ++;
+            }
+            field[i][0].setText(fieldName[i] + ":   ");
+            field[i][0].setHorizontalAlignment(JLabel.RIGHT);
+        }
+        
+        // Start the fields as null by default.
+        clearValues();
+        
+        // Build the background panel.
+        add(background);
+        setComponentZOrder(background, curZ);
+    }
+    
+    /**
+     * Sets all of the fields on the status display to the null value.
+     */
+    public void clearValues() {
+        for(int i = 0; i < field.length; i++) {
+            field[i][1].setText(NULL_VALUE);
+        }
+    }
+    
+    /**
+     * Sets the value of the indicated field.
+     * @param index - The field's index.
+     * @param value - The new value to display.
+     * @throws IndexOutOfBoundsException Occurs when the field index
+     * is neither more than the existing number of fields or is negative.
+     */
+    public void setFieldValue(int index, String value) throws IndexOutOfBoundsException {
+        if(index >= 0 && index < field.length) {
+            if(value == null) { field[index][1].setText(NULL_VALUE); }
+            else  { field[index][1].setText(value); }
+        }
+        else { throw new IndexOutOfBoundsException("Invalid field index."); }
+    }
+    
+    @Override
+    public void setSize(int width, int height) {
+        super.setSize(width, height);
+        resize();
+    }
+    
+    @Override
+    public void setSize(Dimension d) {
+        super.setSize(d);
+        resize();
+    }
+    
+    /**
+     * Finds the x-coordinate immediately after the component.
+     * @param c - The component of which to find the end.
+     * @return Returns the x-coordinate at the end of the component. 
+     */
+    private final static int getNextX(Component c) { return getNextX(c, 0); }
+    
+    /**
+    /**
+     * Finds the x-coordinate after the component with a given buffer.
+     * @param c - The component of which to find the end.
+     * @param buffer - The extra space after the component to be included.
+     * @return Returns the x-coordinate at the end of the component,
+     * with a buffer length.
+     */
+    private final static int getNextX(Component c, int buffer) {
+        return c.getX() + c.getWidth() + buffer;
+    }
+    
+    /**
+     * Updates the layout of the component to the panel's current size.
+     */
+    private void resize() {
+        // Define the width an height as convenience variables.
+        int width = getWidth();
+        int height = getHeight();
+        
+        // Size the background panel.
+        background.setBounds(0, 0, width, height);
+        
+        // Size and place the text labels.
+        if(field.length != 0) {
+            int labelHeight = (height - (int)(upperBuffer + 5)) / 3;
+            int labelRem = (height - upperBuffer - 8) % field.length;
+            int curX = leftBuffer;
+            int curY = (int)(upperBuffer + 2);
+            for(int i = 0; i < field.length; i++) {
+                // Determine the appropriate field height.
+                int thisHeight = labelHeight;
+                if(labelRem > 0) {
+                    thisHeight++;
+                    labelRem--;
+                }
+                
+                // Place the field.
+                field[i][0].setBounds(curX, curY, 130, thisHeight);
+                field[i][1].setBounds(getNextX(field[i][0]), curY, 75, thisHeight);
+                
+                // If we have written three labels, then start a new column.
+                if(i % 3 == 2) {
+                    curX = getNextX(field[i][1], 10);
+                    curY = (int)(upperBuffer + 2);
+                }
+                
+                // Otherwise just increment the current height.
+                else { curY += thisHeight; }
+            }
+        }
+    }
+    
+    /**
+     * Class <code>BackPanel</code> simply renders the background panel
+     * for the status panel.
+     */
+    private class BackPanel extends JPanel {
+        private static final long serialVersionUID = 4997805650267243080L;
 
-		public void paint(Graphics g) {
-			// Render the panel background.
-			g.setColor(Color.WHITE);
-			g.fillRect(0, upperBuffer, getWidth(), getHeight() - upperBuffer);
-			g.setColor(Color.GRAY);
-			g.drawRect(0, upperBuffer, getWidth() - 1, getHeight() - upperBuffer - 1);
-			g.setColor(Color.LIGHT_GRAY);
-			g.drawRect(1, upperBuffer + 1, getWidth() - 3, getHeight() - upperBuffer - 3);
-		}
-	}
+        public void paint(Graphics g) {
+            // Render the panel background.
+            g.setColor(Color.WHITE);
+            g.fillRect(0, upperBuffer, getWidth(), getHeight() - upperBuffer);
+            g.setColor(Color.GRAY);
+            g.drawRect(0, upperBuffer, getWidth() - 1, getHeight() - upperBuffer - 1);
+            g.setColor(Color.LIGHT_GRAY);
+            g.drawRect(1, upperBuffer + 1, getWidth() - 3, getHeight() - upperBuffer - 3);
+        }
+    }
 }

Modified: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/Viewer.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/Viewer.java	(original)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/ui/Viewer.java	Sat Nov  1 02:29:56 2014
@@ -11,6 +11,7 @@
 import java.text.DecimalFormat;
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.Map;
 import java.util.NoSuchElementException;
 
 import javax.swing.JFrame;
@@ -30,7 +31,7 @@
     // Java-suggested variable.
     private static final long serialVersionUID = -2022819652687941812L;
     // A map of field names to field indices.
-    private final HashMap<String, Integer> fieldMap = new HashMap<String, Integer>();
+    private Map<String, Integer> fieldMap = new HashMap<String, Integer>();
     // A list of crystal listeners attached to the viewer.
     private ArrayList<CrystalListener> listenerList = new ArrayList<CrystalListener>();
     // The default field names.
@@ -41,33 +42,25 @@
     private static final int CELL_VALUE = 2;
     
     /**
-     * <b><statusPanel/b><br/><br/>
-     * <code>protected final StatusPanel <b>statusPanel</b></code><br/><br/>
      * The component responsible for displaying status information 
      * about the currently selected crystal.
      */
-    protected final StatusPanel statusPanel;
-    
-    /**
-     * <b>ecalPanel</b><br/><br/>
-     * <code>protected final CalorimeterPanel <b>ecalPanel</b></code><br/><br/>
+    protected final ResizableFieldPanel statusPanel;
+    
+    /**
      * The panel displaying the calorimeter crystals and scale.
      */
     protected final CalorimeterPanel ecalPanel = new CalorimeterPanel(46, 11);
-	
-    /**
-     * <b>HIGHLIGHT_CLUSTER_COMPONENT</b><br/><br/>
-     * <code>public static final Color <b>HIGHLIGHT_CLUSTER_COMPONENT</b></code><br/><br/>
+    
+    /**
      * The default color for highlighting cluster components.
      */
-	public static final Color HIGHLIGHT_CLUSTER_COMPONENT = Color.RED;
-	
-	/**
-	 * <b>HIGHLIGHT_CLUSTER_SHARED</b><br/><br/>
-     * <code>public static final Color <b>HIGHLIGHT_CLUSTER_SHARED</b></code><br/><br/>
+    public static final Color HIGHLIGHT_CLUSTER_COMPONENT = Color.RED;
+    
+    /**
      * The default color for highlighting cluster shared hits.
-	 */
-	public static final Color HIGHLIGHT_CLUSTER_SHARED = Color.YELLOW;
+     */
+    public static final Color HIGHLIGHT_CLUSTER_SHARED = Color.YELLOW;
     
     /**
      * Initializes the viewer window and calorimeter panel.
@@ -76,29 +69,20 @@
      * @throws NullPointerException Occurs if any of the additional field
      * arguments are <code>null</code>.
      **/
-    public Viewer(String... statusFields) throws NullPointerException {
+    public Viewer() throws NullPointerException {
         // Initialize the underlying JPanel.
         super();
         
-        // Define the status panel fields and map them to indices.
-        String[] fields = new String[statusFields.length + defaultFields.length];
-        for(int i = 0; i < defaultFields.length; i++) {
-        	fields[i] = defaultFields[i];
-        	fieldMap.put(defaultFields[i], i);
-        }
-        for(int i = 0; i < statusFields.length; i++) {
-        	int index = i + defaultFields.length;
-        	fields[index] = statusFields[i];
-        	fieldMap.put(statusFields[i], index);
-        }
-        
         // Generate the status panel.
-        statusPanel = new StatusPanel(fields);
+        statusPanel = new ResizableFieldPanel(100);
+        statusPanel.setBackground(Color.WHITE);
+        
+        // Add the default fields.
+        for(String field : defaultFields) { addStatusField(field); }
         
         // Set the scaling settings.
-        ecalPanel.setScaleMinimum(0.00001);
-        ecalPanel.setScaleMaximum(3);
-       // ecalPanel.setScalingLogarithmic();
+        ecalPanel.setScaleMinimum(0.001);
+        ecalPanel.setScaleMaximum(3.0);
         ecalPanel.setScalingLinear();
         
         // Disable the crystals in the calorimeter panel along the beam gap.
@@ -137,7 +121,28 @@
      * @param cl - The listener to add.
      */
     public void addCrystalListener(CrystalListener cl) {
-    	if(cl != null) { listenerList.add(cl); }
+        if(cl != null) { listenerList.add(cl); }
+    }
+    
+    /**
+     * Adds a new field to the status panel.
+     * @param fieldName - The name to display for the field and that
+     * links to the field when calling <code>setStatusField</code>.
+     */
+    protected void addStatusField(String fieldName) {
+        fieldMap.put(fieldName, statusPanel.getFieldCount());
+        statusPanel.addField(fieldName);
+    }
+    
+    /**
+     * Inserts the field at the indicated location on the status panel.
+     * @param index - The index at which to insert the field.
+     * @param fieldName - The name to display for the field and that
+     * links to the field when calling <code>setStatusField</code>.
+     */
+    protected void insertStatusField(int index, String fieldName) {
+        statusPanel.insertField(index, fieldName);
+        fieldMap = statusPanel.getFieldNameIndexMap();
     }
     
     /**
@@ -147,14 +152,14 @@
      * @return Returns the coordinate pair in LCSim's coordinate system
      * as an <code>int</code>.
      **/
-	public static final Point toEcalPoint(Point panelPoint) {
-		// Convert the point coordinates.
-		int ix = toEcalX(panelPoint.x);
-		int iy = toEcalY(panelPoint.y);
-		
-		// Return the new point.
-		return new Point(ix, iy);
-	}
+    public static final Point toEcalPoint(Point panelPoint) {
+        // Convert the point coordinates.
+        int ix = toEcalX(panelPoint.x);
+        int iy = toEcalY(panelPoint.y);
+        
+        // Return the new point.
+        return new Point(ix, iy);
+    }
     
     /**
      * Converts the panel x-coordinate to the calorimeter's
@@ -164,8 +169,8 @@
      * coordinate system as an <code>int</code>.
      */
     public static final int toEcalX(int panelX) {
-    	if(panelX > 22) { return panelX - 22; }
-    	else { return panelX - 23; }
+        if(panelX > 22) { return panelX - 22; }
+        else { return panelX - 23; }
     }
     
     /**
@@ -184,14 +189,14 @@
      * @return Returns the coordinate pair in the calorimeter panel's
      * coordinate system as an <code>int</code>.
      **/
-	public static final Point toPanelPoint(Point ecalPoint) {
-		// Convert the point coordinates.
-		int ix = toPanelX(ecalPoint.x);
-		int iy = toPanelY(ecalPoint.y);
-		
-		// Return the new point.
-		return new Point(ix, iy);
-	}
+    public static final Point toPanelPoint(Point ecalPoint) {
+        // Convert the point coordinates.
+        int ix = toPanelX(ecalPoint.x);
+        int iy = toPanelY(ecalPoint.y);
+        
+        // Return the new point.
+        return new Point(ix, iy);
+    }
     
     /**
      * Converts the LCSim x-coordinate to the calorimeter panel's
@@ -238,7 +243,7 @@
      * @param cl - The listener to remove.
      */
     public void removeCrystalListener(CrystalListener cl) {
-    	if(cl != null) { listenerList.remove(cl); }
+        if(cl != null) { listenerList.remove(cl); }
     }
     
     public void setSize(int width, int height) {
@@ -259,44 +264,46 @@
      * is provided for argument <code>fieldName</code>.
      */
     public final void setStatusField(String fieldName, String value) throws NoSuchElementException {
-    	// Get the index for the indicated field.
-    	Integer index = fieldMap.get(fieldName);
-    	
-    	// If it is null, the field does not exist.
-    	if(index == null) { throw new NoSuchElementException("Field \"" + fieldName + "\" does not exist."); }
-    	
-    	// Otherwise, set the field.
-    	else { statusPanel.setFieldValue(index, value); }
-    }
-    
-	/**
-	 * Updates the information on the status panel to match that of
-	 * the calorimeter panel's currently selected crystal.
-	 */
-	protected void updateStatusPanel() {
-		// Get the currently selected crystal.
-		Point crystal = ecalPanel.getSelectedCrystal();
-		
-		// If the crystal is null, there is no selection.
-		if(crystal == null || ecalPanel.isCrystalDisabled(crystal.x, crystal.y)) { statusPanel.clearValues(); }
-		
-		// Otherwise, write the crystal's data to the panel.
-		else {
-			setStatusField(defaultFields[X_INDEX], String.valueOf(toEcalX(crystal.x)));
-			setStatusField(defaultFields[Y_INDEX], String.valueOf(toEcalY(crystal.y)));
-			DecimalFormat formatter = new DecimalFormat("0.####E0");
-			String energy = formatter.format(ecalPanel.getCrystalEnergy(crystal.x, crystal.y));
-			setStatusField(defaultFields[CELL_VALUE], energy);
-		}
-	}
+        // Get the index for the indicated field.
+        Integer index = fieldMap.get(fieldName);
+        
+        // If it is null, the field does not exist.
+        if(index == null) { throw new NoSuchElementException("Field \"" + fieldName + "\" does not exist."); }
+        
+        // Otherwise, set the field.
+        else { statusPanel.setFieldValue(index, value); }
+    }
+    
+    /**
+     * Updates the information on the status panel to match that of
+     * the calorimeter panel's currently selected crystal.
+     */
+    protected void updateStatusPanel() {
+        // Get the currently selected crystal.
+        Point crystal = ecalPanel.getSelectedCrystal();
+        
+        // If the crystal is null, there is no selection.
+        if(crystal == null || ecalPanel.isCrystalDisabled(crystal.x, crystal.y)) {
+            statusPanel.clearFields();
+        }
+        
+        // Otherwise, write the crystal's data to the panel.
+        else {
+            setStatusField(defaultFields[X_INDEX], String.valueOf(toEcalX(crystal.x)));
+            setStatusField(defaultFields[Y_INDEX], String.valueOf(toEcalY(crystal.y)));
+            DecimalFormat formatter = new DecimalFormat("0.####E0");
+            String energy = formatter.format(ecalPanel.getCrystalEnergy(crystal.x, crystal.y));
+            setStatusField(defaultFields[CELL_VALUE], energy);
+        }
+    }
     
     /**
      * Handles proper resizing of the window and its components.
      **/
     private void resize() {
-    	// Define the size constants.
-    	int statusHeight = 125;
-    	
+        // Define the size constants.
+        int statusHeight = 125;
+        
         // Size and position the calorimeter display.
         ecalPanel.setLocation(0, 0);
         ecalPanel.setSize(getContentPane().getWidth(), getContentPane().getHeight() - statusHeight);
@@ -312,30 +319,30 @@
      * It also triggers crystal click events.
      */
     private class EcalMouseListener implements MouseListener {
-		public void mouseClicked(MouseEvent e) {
-			// If there is a selected crystal, trigger a crystal click event.
-			if(ecalPanel.getSelectedCrystal() != null) {
-				// Get the selected crystal.
-				Point crystal = ecalPanel.getSelectedCrystal();
-				
-				// Construct a crystal event.
-				CrystalEvent ce = new CrystalEvent(Viewer.this, crystal);
-				
-				// Loop through all the crystal listeners and trigger them.
-				for(CrystalListener cl : listenerList) { cl.crystalClicked(ce); }
-			}
-		}
-		
-		public void mouseEntered(MouseEvent e) { }
-		
-		public void mouseExited(MouseEvent e) {
-			ecalPanel.clearSelectedCrystal();
-			statusPanel.clearValues();
-		}
-		
-		public void mousePressed(MouseEvent e) { }
-		
-		public void mouseReleased(MouseEvent e) { }
+        public void mouseClicked(MouseEvent e) {
+            // If there is a selected crystal, trigger a crystal click event.
+            if(ecalPanel.getSelectedCrystal() != null) {
+                // Get the selected crystal.
+                Point crystal = ecalPanel.getSelectedCrystal();
+                
+                // Construct a crystal event.
+                CrystalEvent ce = new CrystalEvent(Viewer.this, crystal);
+                
+                // Loop through all the crystal listeners and trigger them.
+                for(CrystalListener cl : listenerList) { cl.crystalClicked(ce); }
+            }
+        }
+        
+        public void mouseEntered(MouseEvent e) { }
+        
+        public void mouseExited(MouseEvent e) {
+            ecalPanel.clearSelectedCrystal();
+            statusPanel.clearFields();
+        }
+        
+        public void mousePressed(MouseEvent e) { }
+        
+        public void mouseReleased(MouseEvent e) { }
     }
     
     /**
@@ -344,80 +351,80 @@
      * mouse moves over the window. Additionally triggers crystal
      * activation and deactivation events.
      */
-    private class EcalMouseMotionListener implements MouseMotionListener {    	
-		public void mouseDragged(MouseEvent arg0) { }
-		
-		public void mouseMoved(MouseEvent e) {
-			// Get the panel coordinates.
-			int x = e.getX();
-			int y = e.getY();
-			
-			// Get the crystal index for these coordinates.
-			Point crystal = ecalPanel.getCrystalID(x, y);
-			
-			// If either of the crystal indices are negative, then
-			// the mouse is not in a crystal and the selection should
-			// be cleared.
-			boolean validCrystal = (crystal != null);
-			
-			// Get the currently selected calorimeter crystal.
-			Point curCrystal = ecalPanel.getSelectedCrystal();
-			
-			// Perform event comparison checks.
-			boolean[] nullCrystal = { !validCrystal, curCrystal == null };
-			boolean[] disabledCrystal = { true, true };
-			if(!nullCrystal[0]) { disabledCrystal[0] = ecalPanel.isCrystalDisabled(crystal); }
-			if(!nullCrystal[1]) { disabledCrystal[1] = ecalPanel.isCrystalDisabled(curCrystal); }
-			boolean sameCrystal = true;
-			if(validCrystal) { sameCrystal = crystal.equals(curCrystal); }
-			
-			// If the crystals are the same, there are no events to throw.
-			if(!sameCrystal) {
-				// If the new crystal is non-null and enabled, throw an event.
-				if(!nullCrystal[0] && !disabledCrystal[0]) { throwActivationEvent(crystal); }
-				
-				// If the old crystal is non-null and enabled, throw an event.
-				if(!nullCrystal[1] && !disabledCrystal[1]) { throwDeactivationEvent(curCrystal); }
-			}
-			
-			// If the crystal is valid, then set the selected crystal
-			// to the current one.
-			if(validCrystal) { ecalPanel.setSelectedCrystal(crystal); }
-			
-			// Otherwise, clear the selection.
-			else { ecalPanel.clearSelectedCrystal(); }
-			
-			// Update the status panel.
-			updateStatusPanel();
-		}
-		
-		/**
-		 * Triggers crystal activation events on all listeners for
-		 * this component.
-		 * @param activatedCrystal - The panel coordinates for the
-		 * activated crystal.
-		 */
-		private void throwActivationEvent(Point activatedCrystal) {
-			// Create a crystal event.
-			CrystalEvent ce = new CrystalEvent(Viewer.this, activatedCrystal);
-			
-			// Throw the event with every listener.
-			for(CrystalListener cl : listenerList) { cl.crystalActivated(ce); }
-		}
-		
-		/**
-		 * Triggers crystal deactivation events on all listeners for
-		 * this component.
-		 * @param deactivatedCrystal - The panel coordinates for the
-		 * deactivated crystal.
-		 */
-		private void throwDeactivationEvent(Point deactivatedCrystal) {
-			// Create a crystal event.
-			CrystalEvent ce = new CrystalEvent(Viewer.this, deactivatedCrystal);
-			
-			// Throw the event with every listener.
-			for(CrystalListener cl : listenerList) { cl.crystalDeactivated(ce); }
-		}
+    private class EcalMouseMotionListener implements MouseMotionListener {        
+        public void mouseDragged(MouseEvent arg0) { }
+        
+        public void mouseMoved(MouseEvent e) {
+            // Get the panel coordinates.
+            int x = e.getX();
+            int y = e.getY();
+            
+            // Get the crystal index for these coordinates.
+            Point crystal = ecalPanel.getCrystalID(x, y);
+            
+            // If either of the crystal indices are negative, then
+            // the mouse is not in a crystal and the selection should
+            // be cleared.
+            boolean validCrystal = (crystal != null);
+            
+            // Get the currently selected calorimeter crystal.
+            Point curCrystal = ecalPanel.getSelectedCrystal();
+            
+            // Perform event comparison checks.
+            boolean[] nullCrystal = { !validCrystal, curCrystal == null };
+            boolean[] disabledCrystal = { true, true };
+            if(!nullCrystal[0]) { disabledCrystal[0] = ecalPanel.isCrystalDisabled(crystal); }
+            if(!nullCrystal[1]) { disabledCrystal[1] = ecalPanel.isCrystalDisabled(curCrystal); }
+            boolean sameCrystal = true;
+            if(validCrystal) { sameCrystal = crystal.equals(curCrystal); }
+            
+            // If the crystals are the same, there are no events to throw.
+            if(!sameCrystal) {
+                // If the new crystal is non-null and enabled, throw an event.
+                if(!nullCrystal[0] && !disabledCrystal[0]) { throwActivationEvent(crystal); }
+                
+                // If the old crystal is non-null and enabled, throw an event.
+                if(!nullCrystal[1] && !disabledCrystal[1]) { throwDeactivationEvent(curCrystal); }
+            }
+            
+            // If the crystal is valid, then set the selected crystal
+            // to the current one.
+            if(validCrystal) { ecalPanel.setSelectedCrystal(crystal); }
+            
+            // Otherwise, clear the selection.
+            else { ecalPanel.clearSelectedCrystal(); }
+            
+            // Update the status panel.
+            updateStatusPanel();
+        }
+        
+        /**
+         * Triggers crystal activation events on all listeners for
+         * this component.
+         * @param activatedCrystal - The panel coordinates for the
+         * activated crystal.
+         */
+        private void throwActivationEvent(Point activatedCrystal) {
+            // Create a crystal event.
+            CrystalEvent ce = new CrystalEvent(Viewer.this, activatedCrystal);
+            
+            // Throw the event with every listener.
+            for(CrystalListener cl : listenerList) { cl.crystalActivated(ce); }
+        }
+        
+        /**
+         * Triggers crystal deactivation events on all listeners for
+         * this component.
+         * @param deactivatedCrystal - The panel coordinates for the
+         * deactivated crystal.
+         */
+        private void throwDeactivationEvent(Point deactivatedCrystal) {
+            // Create a crystal event.
+            CrystalEvent ce = new CrystalEvent(Viewer.this, deactivatedCrystal);
+            
+            // Throw the event with every listener.
+            for(CrystalListener cl : listenerList) { cl.crystalDeactivated(ce); }
+        }
     }
     
     /**

Added: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/CrystalDataSet.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/CrystalDataSet.java	(added)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/CrystalDataSet.java	Sat Nov  1 02:29:56 2014
@@ -0,0 +1,213 @@
+package org.hps.monitoring.ecal.eventdisplay.util;
+
+import java.awt.Point;
+
+/**
+ * Class <code>CrystalDataSet</code> contains all of the hardware data
+ * for a single calorimeter crystal as defined in the crystal hardware
+ * reference sheet.
+ * 
+ * @author Kyle McCarty
+ */
+public class CrystalDataSet {
+    // Data points.
+    private final Point crystalIndex;
+    private final short apd;
+    private final PreamplifierNumber preamp;
+    private final short ledChannel;
+    private final byte[] ledDriver;
+    private final byte fadcSlot;
+    private final byte fadcChannel;
+    private final byte splitterNum;
+    private final byte hvGroup;
+    private final byte jout;
+    private final String mb;
+    private final short channel;
+    private final int gain;
+    
+    /**
+     * Defines the data set.
+     * @param ix - The crystal's x-index in the LCSimcoordinate system.
+     * @param iy - The crystal's y-index in the LCSimcoordinate system.
+     * @param apd - The number of the APD attached to the crystal.
+     * @param preamp - The number of the crystal's premaplifier. This
+     * may also include a reference wire color.
+     * @param ledChannel - The channel number for the crystal's LED.
+     * @param ledDriver
+     * @param fadcSlot - The FADC slot the crystal occupies.
+     * @param fadcChannel - The channel number through which the crystal
+     * communicates with the FADC.
+     * @param splitter
+     * @param hvGroup - The high voltage group for which the crystal
+     * is a member.
+     * @param jout
+     * @param mb
+     * @param channel
+     * @param gain - The crystal's gain.
+     */
+    public CrystalDataSet(int ix, int iy, int apd, String preamp, int ledChannel,
+            double ledDriver, int fadcSlot, int fadcChannel, int splitter, int hvGroup,
+            int jout, String mb, int channel, int gain) {
+        // Define crystal indices.
+        crystalIndex = new Point(ix, iy);
+        
+        // Define the general properties.
+        this.apd = (short) apd;
+        this.ledChannel = (short) ledChannel;
+        this.fadcSlot = (byte) fadcSlot;
+        this.fadcChannel = (byte) fadcChannel;
+        this.splitterNum = (byte) splitter;
+        this.hvGroup = (byte) hvGroup;
+        this.jout = (byte) jout;
+        this.channel = (short) channel;
+        this.gain = gain;
+        this.mb = mb;
+        
+        // Define the LED driver.
+        this.ledDriver = new byte[2];
+        this.ledDriver[0] = (byte) Math.floor(ledDriver);
+        this.ledDriver[1] = (byte) ((ledDriver - this.ledDriver[0]) * 10);
+        
+        // Handle the preamplifier number.
+        StringBuffer num = new StringBuffer();
+        StringBuffer col = new StringBuffer();
+        for(char c : preamp.toCharArray()) {
+            if(Character.isDigit(c)) { num.append(c); }
+            else { col.append(c); }
+        }
+        int number = Integer.parseInt(num.toString());
+        String color = null;
+        if(col.length() != 0) { color = col.toString(); }
+        this.preamp = new PreamplifierNumber(number, color);
+    }
+    
+    /**
+     * Gets the crystal's positional indices in the LCSim coordinate
+     * system.
+     * @return Returns the crystal's positional indices as a <code>
+     * Point</code> object.
+     */
+    public Point getCrystalIndex() { return crystalIndex; }
+    
+    /**
+     * Gets the crystal's x-index in the LCSim coordinate system.
+     * @return Returns the crystal's x-index as an <code>int</code>
+     * primitive.
+     */
+    public int getCrystalXIndex() { return crystalIndex.x; }
+    
+    /**
+     * Gets the crystal's y-index in the LCSim coordinate system.
+     * @return Returns the crystal's y-index as an <code>int</code>
+     * primitive.
+     */
+    public int getCrystalYIndex() { return crystalIndex.y; }
+    
+    /**
+     * Gets the number of the APD attached to the crystal.
+     * @return Returns the crystal's APD number as an <code>int</code>
+     * primitive.
+     */
+    public int getAPDNumber() { return apd; }
+    
+    /**
+     * Gets the crystal's preamplifier reference data.
+     * @return Returns the preamplifier reference as a <code>
+     * PreamplifierNumber</code> object.
+     */
+    public PreamplifierNumber getPreamplifierNumber() { return preamp; }
+    
+    /**
+     * Gets the crystal's LED channel.
+     * @return Returns the LED channel as an <code>int</code> primitive.
+     */
+    public int getLEDChannel() { return ledChannel; }
+    
+    public double getLEDDriver() {
+        return ((double) ledDriver[0]) + ((double) ledDriver[1] / 10);
+    }
+    
+    /**
+     * Gets the crystal's FADC slot.
+     * @return Returns the FADC slot as an <code>int</code> primitive.
+     */
+    public int getFADCSlot() { return fadcSlot; }
+    
+    /**
+     * Gets the crystal's FADC channel.
+     * @return Returns the FADC channel as an <code>int</code> primitive.
+     */
+    public int getFADCChannel() { return fadcChannel; }
+    
+    public int getSplitterNumber() { return splitterNum; }
+    
+    /**
+     * Gets the crystal's high voltage group.
+     * @return Returns the high voltage group number as an <code>int
+     * </code> primitive.
+     */
+    public int getHighVoltageGroup() { return hvGroup; }
+    
+    public int getJout() { return jout; }
+    
+    public String getMB() { return mb; }
+    
+    public int getChannel() { return channel; }
+    
+    /**
+     * Gets the crystal's gain.
+     * @return Returns the gain as an <code>int</code> primitive.
+     */
+    public int getGain() { return gain; }
+    
+    /**
+     * Class <code>PreamplifierNumber</code> represents the number
+     * of a crystal's preamplifier. It can also contain a reference
+     * wire color if necessary.
+     * 
+     * @author Kyle McCarty
+     */
+    public class PreamplifierNumber {
+        private final short number;
+        private final String color;
+        
+        /**
+         * Initializes a preamplifier number with no reference wire
+         * color.
+         * @param number - The preamplifier's number.
+         */
+        public PreamplifierNumber(int number) { this(number, null); }
+        
+        /**
+         * Initializes a preamplifier number with the specified reference
+         * wire color.
+         * @param number - The preamplifier's number.
+         * @param color - The reference wire color.
+         */
+        public PreamplifierNumber(int number, String color) {
+            this.number = (short) number;
+            this.color = color;
+        }
+        
+        /**
+         * Gets the number of the preamplifier.
+         * @return Returns the preamplifier number as an <code>int
+         * </code> primitive.
+         */
+        public int getNumber() { return number; }
+        
+        /**
+         * Gets the reference wire color associated with the crystal's
+         * preamplifier if it exists.
+         * @return Returns the reference wire color as a <code>String
+         * </code> object or <code>null</code> if it does not exist.
+         */
+        public String getColor() { return color; }
+        
+        @Override
+        public String toString() {
+            if(color == null) { return "" + number; }
+            else { return number + " (" + color + ")"; }
+        }
+    }
+}

Added: java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/EcalWiringManager.java
 =============================================================================
--- java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/EcalWiringManager.java	(added)
+++ java/trunk/ecal-event-display/src/main/java/org/hps/monitoring/ecal/eventdisplay/util/EcalWiringManager.java	Sat Nov  1 02:29:56 2014
@@ -0,0 +1,112 @@
+package org.hps.monitoring.ecal.eventdisplay.util;
+
+import java.awt.Point;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Scanner;
+
+/**
+ * Class <code>EcalWiringManager</code> reads in the crystal hardware
+ * data sheet for the calorimeter and stores the data in <code>
+ * CrystalDataSet</code> objects for access and reference by the <code>
+ * Viewer</code> classes. Crystal LCSim indices are mapped to the data
+ * set that corresponds to that crystal.
+ * 
+ * @author Kyle McCarty
+ */
+public class EcalWiringManager {
+    // Delimiter class statics.
+    public static final int SPACE_DELIMITED = 1;
+    public static final int TAB_DELIMITED = 2;
+    public static final int COMMA_DELIMITED = 3;
+    
+    // Internal variables.
+    private Map<Point, CrystalDataSet> crystalMap = new HashMap<Point, CrystalDataSet>(442);
+    
+    /**
+     * Initializes an <code>EcalWiringManager</code> database with
+     * hardware information loaded from the indicated file. The data
+     * file is assumed to be comma delimited and the first line is
+     * assumed to be a header line and to not contain data.
+     * @param dataFile - The path to the data file.
+     * @throws IOException Occurs if there is an error opening or parsing
+     * the data file.
+     */
+    public EcalWiringManager(String dataFile) throws IOException {
+        this(dataFile, COMMA_DELIMITED, true);
+    }
+    
+    /**
+     * Initializes an <code>EcalWiringManager</code> database with
+     * hardware information loaded from the indicated file.
+     * @param dataFile - The path to the data file.
+     * @param delimiter - The delimiter used by the data file.
+     * @param skipFirstLine - Whether the first line should be skipped.
+     * @throws IOException Occurs if there is an error opening or parsing
+     * the data file.
+     */
+    public EcalWiringManager(String dataFile, int delimiter, boolean skipFirstLine) throws IOException {
+        // Create a file reader.
+        FileReader baseReader = new FileReader(dataFile);
+        BufferedReader reader = new BufferedReader(baseReader);
+        
+        // If the first line should be skipped, do that.
+        if(skipFirstLine) { reader.readLine(); }
+        
+        // Iterate over the lines of the data file.
+        String curLine = null;
+        readLoop:
+        while((curLine = reader.readLine()) != null) {
+            // If the current line is empty, skip it.
+            if(curLine.isEmpty()) { continue readLoop; }
+            
+            // Create a line scanner.
+            Scanner lineScan = new Scanner(curLine);
+            if(delimiter == COMMA_DELIMITED) { lineScan.useDelimiter(","); }
+            else if(delimiter == SPACE_DELIMITED) { lineScan.useDelimiter(" *"); }
+            else if(delimiter == TAB_DELIMITED) { lineScan.useDelimiter("\t"); }
+            
+            // Get the crystal data values.
+            int ix = lineScan.nextInt();
+            int iy = lineScan.nextInt();
+            int apd = lineScan.nextInt();
+            String preamp = lineScan.next();
+            int ledChan = lineScan.nextInt();
+            double ledDriver = lineScan.nextDouble();
+            int fadcSlot = lineScan.nextInt();
+            int fadcChan = lineScan.nextInt();
+            int splitter = lineScan.nextInt();
+            int hvGroup = lineScan.nextInt();
+            int jout = lineScan.nextInt();
+            String mb = lineScan.next();
+            int channel = lineScan.nextInt();
+            int gain = lineScan.nextInt();
+            lineScan.close();
+            
+            // Create a crystal data set in which to store the data.
+            CrystalDataSet cds = new CrystalDataSet(ix, iy, apd, preamp, ledChan, ledDriver, fadcSlot,
+                    fadcChan, splitter, hvGroup, jout, mb, channel, gain);
+            
+            // Map the crystal index to the crystal data set.
+            crystalMap.put(cds.getCrystalIndex(), cds);
+        }
+        
+        // Close the readers.
+        reader.close();
+        baseReader.close();
+    }
+    
+    /**
+     * Gets the set of calorimeter hardware data associated with the
+     * crystal at the given index.
+     * @param crystalIndex - The index of the crystal for which to obtain
+     * the hardware information. This should be in LCSim coordinates.
+     * @return Returns the hardware information for the crystal as a
+     * <code>CrystalDataSet</code> object or returns <code>null</code>
+     * if the crystal index is invalid.
+     */
+    public CrystalDataSet getCrystalData(Point crystalIndex) { return crystalMap.get(crystalIndex); }
+}

Top of Message | Previous Page | Permalink

Advanced Options


Options

Log In

Log In

Get Password

Get Password


Search Archives

Search Archives


Subscribe or Unsubscribe

Subscribe or Unsubscribe


Archives

November 2017
August 2017
July 2017
January 2017
December 2016
November 2016
October 2016
September 2016
August 2016
July 2016
June 2016
May 2016
April 2016
March 2016
February 2016
January 2016
December 2015
November 2015
October 2015
September 2015
August 2015
July 2015
June 2015
May 2015
April 2015
March 2015
February 2015
January 2015
December 2014
November 2014
October 2014
September 2014
August 2014
July 2014
June 2014
May 2014
April 2014
March 2014
February 2014
January 2014
December 2013
November 2013

ATOM RSS1 RSS2



LISTSERV.SLAC.STANFORD.EDU

Secured by F-Secure Anti-Virus CataList Email List Search Powered by the LISTSERV Email List Manager

Privacy Notice, Security Notice and Terms of Use