/* Copyright 2003, Carnegie Mellon, All Rights Reserved */ package edu.cmu.minorthird.util.gui; import java.awt.GridBagConstraints; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JTable; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.apache.log4j.Logger; /** * * Visualize an object, and obey certain rules for propogating events. * * <p> * Specifically, a Viewer has a superview, which is also a viewer, and displays * some 'content'. The canReceive(), clearContent(), and receiveContent() * methods are used for displaying content. Viewers also send signals via the * sendSignal() method. Signals are passed up to a superview, its superview, and * so on, until a Viewer is found that canHandle() that signal, at which point * the signal is trapped, and processed. * * <p> * Viewers are intended to be used for linked display components. * * @author William cohen * */ public abstract class Viewer extends JPanel{ static final long serialVersionUID=20081125L; static private Logger log=Logger.getLogger(Viewer.class); static private final boolean DEBUG=log.isDebugEnabled(); static private final String ONLY_SUBVIEWER="*main*"; static protected final int SET_CONTENT=1; static protected final int TEXT_MESSAGE=2; static protected final int OBJECT_SELECTED=3; static protected final int OBJECT_UPDATED=4; private Viewer superView=null; private Object content="empty view"; /** Maps String 'names' of subviews to viewers. */ protected Map<String,Viewer> namedSubViews=new TreeMap<String,Viewer>(); public Viewer(){ this(null); } public Viewer(Object content){ initialize(); if(content!=null) setContent(content); } // // setters & getters // /** Declare this viewer to be the only subview of superView. */ final public void setSuperView(Viewer superView){ // if (namedSubViews.keySet().size()>1) // throw new IllegalStateException("superview already has a subview"); setSuperView(superView,ONLY_SUBVIEWER); } /** Declare this viewer to be a subview of superView. */ final public void setSuperView(Viewer superView,String title){ // if (namedSubViews.get(title)!=null) // throw new IllegalStateException("superview already has a subview named // "+title); this.superView=superView; superView.namedSubViews.put(title,this); } /** Get Viewer in which this viewer is contained. */ final public Viewer getSuperView(){ return superView; } /** Change the object being displayed by this viewer. */ final public void setContent(Object content){ setContent(content,false); } /** * Change the object being displayed by this viewer. If forceUpdate is false, * do not force the display to be changed. */ final public void setContent(Object content,boolean forceUpdate){ if(content!=this.content||forceUpdate){ this.content=content; receiveContent(content); sendSignal(SET_CONTENT,content); } } /** * Get the object being displayed, as determined by the last call to * setContent(). */ final public Object getContent(){ return content; } /** Get the object being displayed as the user sees it. */ public Object getVisibleContent(){ if(namedSubViews.size()==1&&namedSubViews.get(ONLY_SUBVIEWER)!=null){ Object result= (namedSubViews.get(ONLY_SUBVIEWER)).getVisibleContent(); return result; }else{ return content; } } public Object getSerializableContent(){ if(!(content instanceof Serializable)&& namedSubViews.get(ONLY_SUBVIEWER)!=null){ Object result= (namedSubViews.get(ONLY_SUBVIEWER)).getSerializableContent(); return result; }else return content; } // // signalling // /** Send a signal. */ final protected void sendSignal(int signal,Object argument){ if(DEBUG) log.debug("signal sent by "+this+": "+signal+","+argument); if(superView!=null){ List<Viewer> senders=new ArrayList<Viewer>(); senders.add(this); superView.hearBroadcast(signal,argument,senders); } } /** * Listen to passing signals, and either handle them, or propogare them upward * to the superViewer. */ final private void hearBroadcast(int signal,Object argument, List<Viewer> senders){ if(canHandle(signal,argument,senders)){ if(DEBUG) log.debug("signal claimed by "+this+": "+signal+","+argument+","+ senders); handle(signal,argument,senders); }else if(superView!=null){ if(DEBUG) log.debug("signal forwarded to "+superView+": "+signal+","+argument+ ","+senders); senders.add(this); superView.hearBroadcast(signal,argument,senders); }else if(superView==null){ if(DEBUG) log.debug("no superview set for "+this); } } // // abstract actions // /** Called at creation time. Initialize the layout of this view. */ abstract protected void initialize(); /** Called when new content obtained. So load it into the viewer. */ abstract public void receiveContent(Object obj); /** Called when no content should be displayed. */ abstract public void clearContent(); /** See if proposed content is displayable. */ abstract public boolean canReceive(Object obj); /** Handle a signal from a subview. */ abstract protected void handle(int signal,Object argument,List<Viewer> senders); /** Offer to handle a signal. */ abstract protected boolean canHandle(int signal,Object argument, List<Viewer> senders); /** Provide a set of subview names */ public Set<String> getSubViewNames(){ Viewer onlySubviewer=namedSubViews.get(ONLY_SUBVIEWER); if(onlySubviewer!=null) return onlySubviewer.getSubViewNames(); else return namedSubViews.keySet(); } /** Retrieve a subview by name. */ public Viewer getNamedSubView(String name){ Viewer onlySubviewer=namedSubViews.get(ONLY_SUBVIEWER); if(onlySubviewer!=null) return onlySubviewer.getNamedSubView(name); else return namedSubViews.get(name); } // // utility functions // /** * Useful default case for a GridBagConstraint. */ protected static GridBagConstraints fillerGBC(){ GridBagConstraints gbc=new GridBagConstraints(); gbc.fill=GridBagConstraints.BOTH; gbc.weightx=gbc.weighty=1.0; gbc.gridx=gbc.gridy=0; return gbc; } /** * Add a list selection listener which sends the appropriate viewer signal. * The transformer is used to re-map the selected value. */ protected void monitorSelections(final JList jlist,final Transform transformer){ jlist.addListSelectionListener(new ListSelectionListener(){ @Override public void valueChanged(ListSelectionEvent e){ int index=jlist.getSelectedIndex(); sendSignal(OBJECT_SELECTED,transformer.transform(jlist.getModel().getElementAt(index))); } }); } /** * Add a list selection listener which sends the appropriate viewer signal. */ protected void monitorSelections(final JList jlist){ monitorSelections(jlist,IDENTITY_TRANSFORM); } /** * Add a list selection listener which sends the appropriate viewer signal. * The transformer is used to re-map the selected value. */ protected void monitorSelections(final JTable jtable,final int colIndex, final Transform transformer){ jtable.addMouseListener(new MouseAdapter(){ @Override public void mouseClicked(MouseEvent e){ int rowIndex=jtable.rowAtPoint(e.getPoint()); sendSignal(OBJECT_SELECTED,transformer.transform(jtable.getModel() .getValueAt(rowIndex,colIndex))); } }); } /** * Add a list selection listener which sends the appropriate viewer signal. */ protected void monitorSelections(final JTable jtable,final int colIndex){ monitorSelections(jtable,colIndex,IDENTITY_TRANSFORM); } protected interface Transform{ public Object transform(Object o); } final private Transform IDENTITY_TRANSFORM=new Transform(){ @Override public Object transform(Object o){ return o; } }; }