00001 package org.gel.mauve.gui.sequence;
00002
00003 import java.awt.BasicStroke;
00004 import java.awt.BorderLayout;
00005 import java.awt.Color;
00006 import java.awt.Dimension;
00007 import java.awt.event.ActionEvent;
00008 import java.io.IOException;
00009 import java.util.EventListener;
00010 import java.util.Iterator;
00011 import java.util.StringTokenizer;
00012 import java.util.Vector;
00013
00014 import javax.swing.AbstractAction;
00015 import javax.swing.JDialog;
00016 import javax.swing.JFrame;
00017 import javax.swing.JMenuItem;
00018 import javax.swing.JOptionPane;
00019 import javax.swing.JPopupMenu;
00020 import javax.swing.JTabbedPane;
00021 import javax.swing.ToolTipManager;
00022 import javax.swing.event.EventListenerList;
00023
00024 import org.biojava.bio.gui.sequence.AbstractBeadRenderer;
00025 import org.biojava.bio.gui.sequence.FeatureBlockSequenceRenderer;
00026 import org.biojava.bio.gui.sequence.FilteringRenderer;
00027 import org.biojava.bio.gui.sequence.MultiLineRenderer;
00028 import org.biojava.bio.gui.sequence.OverlayRendererWrapper;
00029 import org.biojava.bio.gui.sequence.RectangularBeadRenderer;
00030 import org.biojava.bio.gui.sequence.SequenceRenderer;
00031 import org.biojava.bio.gui.sequence.SequenceViewerEvent;
00032 import org.biojava.bio.gui.sequence.SequenceViewerListener;
00033 import org.biojava.bio.gui.sequence.SequenceViewerMotionListener;
00034 import org.biojava.bio.gui.sequence.TranslatedSequencePanel;
00035 import org.biojava.bio.seq.Feature;
00036 import org.biojava.bio.seq.FeatureFilter;
00037 import org.biojava.bio.seq.FeatureHolder;
00038 import org.biojava.bio.seq.Sequence;
00039 import org.biojava.bio.seq.StrandedFeature;
00040 import org.biojava.bio.symbol.Location;
00041 import org.biojava.bio.symbol.LocationTools;
00042 import org.biojava.utils.ChangeVetoException;
00043 import org.gel.mauve.BrowserLauncher;
00044 import org.gel.mauve.DbXrefFactory;
00045 import org.gel.mauve.FilterCacheSpec;
00046 import org.gel.mauve.Genome;
00047 import org.gel.mauve.GenomeBuilder;
00048 import org.gel.mauve.MauveConstants;
00049 import org.gel.mauve.ModelEvent;
00050 import org.gel.mauve.BaseViewerModel;
00051 import org.gel.mauve.gui.MySymbolSequenceRenderer;
00052 import org.gel.mauve.gui.QualifierPanel;
00053
00061 public class FeaturePanel extends AbstractSequencePanel
00062 {
00063 public static final int DEFAULT_WIDTH = 10000;
00064 public static final int DEFAULT_HEIGHT = 40;
00065 public static final int MAX_FEATURE_DISPLAY_RANGE = 500000;
00066 private TranslatedSequencePanel trans;
00067 private Sequence seq;
00068 private final GenbankMenuItemBuilder gmib = new GenbankMenuItemBuilder();
00069 private final DbXrefMenuItemBuilder dmib = new DbXrefMenuItemBuilder();
00070 private final FeaturePopupMenuBuilder fpmb = new FeaturePopupMenuBuilder();
00071
00072 public FeaturePanel(Genome genome, BaseViewerModel model)
00073 {
00074 super(model, genome);
00075 setLayout(new BorderLayout());
00076 init();
00077 }
00078
00079
00080
00081 public void setBounds(int arg0, int arg1, int arg2, int arg3)
00082 {
00083 super.setBounds(arg0, arg1, arg2, arg3);
00084
00085 if (trans != null)
00086 adjustScaleAndTranslation();
00087 }
00088
00089 private void clearTransPanel()
00090 {
00091 if (trans != null)
00092 {
00093 remove(trans);
00094 trans = null;
00095 }
00096 }
00097
00098 private void init()
00099 {
00100 seq = getGenome().getAnnotationSequence();
00101
00102 if (seq == null)
00103 {
00104 clearTransPanel();
00105 return;
00106 }
00107
00108 if (trans != null)
00109 {
00110 remove(trans);
00111 }
00112
00113 trans = new TranslatedSequencePanel();
00114 add(trans, BorderLayout.CENTER);
00115
00116 try
00117 {
00118 trans.setSequence(seq);
00119 trans.setDirection(TranslatedSequencePanel.HORIZONTAL);
00120
00121 trans.addSequenceViewerMotionListener(new ToolTipMotionListener());
00122 trans.addSequenceViewerListener(new ClickListener());
00123
00124 MultiLineRenderer multi = new MultiLineRenderer();
00125 FilterCacheSpec[] specs = getGenome().getAnnotationFormat().getFilterCacheSpecs();
00126 FeatureFilterer filterer = FeatureFilterer.getFilterer (model);
00127 MySymbolSequenceRenderer my_symbol = new MySymbolSequenceRenderer();
00128 filterer.addMultiRenderer (multi, my_symbol);
00129
00130 for (int i = 0; i < specs.length; i++)
00131 {
00132 FilterCacheSpec spec = specs[i];
00133 if (spec.getFeatureRenderer() != null)
00134 {
00135 makeRenderer (model, multi, spec);
00136 }
00137 }
00138
00139 multi.addRenderer(my_symbol);
00140 trans.setRenderer(multi);
00141
00142
00143 Dimension my_size = new Dimension();
00144 my_size.height = DEFAULT_HEIGHT;
00145 my_size.width = DEFAULT_WIDTH;
00146 setNewSize (my_size);
00147 }
00148 catch (ChangeVetoException e)
00149 {
00150 JOptionPane.showMessageDialog(this, "Could not render pane", "Rendering error", JOptionPane.ERROR_MESSAGE);
00151 clearTransPanel();
00152 }
00153
00154
00155 fpmb.addMenuItemBuilder(gmib);
00156 fpmb.addMenuItemBuilder(dmib);
00157 }
00158
00168 protected static void makeRenderer (BaseViewerModel model,
00169 MultiLineRenderer multi, FilterCacheSpec spec)
00170 throws ChangeVetoException {
00171 FeatureBlockSequenceRenderer fbr = new FeatureBlockSequenceRenderer ();
00172 fbr.setFeatureRenderer (spec.getFeatureRenderer ());
00173 fbr.setCollapsing (false);
00174 OverlayRendererWrapper over = new OverlayRendererWrapper (
00175 new FilteringRenderer (fbr, spec.getFilter (), true));
00176 FeatureFilterer.getFilterer (model).addOverlayRenderer (multi, over);
00177 multi.addRenderer (over);
00178 }
00179
00180 public void resizeForMoreFeatures () {
00181 Dimension my_size = getSize ();
00182 my_size.height += MauveConstants.FEATURE_HEIGHT;
00183 setNewSize (my_size);
00184 }
00185
00186 private void setNewSize (Dimension my_size) {
00187 setSize (my_size);
00188 setPreferredSize (my_size);
00189 setMaximumSize (my_size);
00190 setMinimumSize (my_size);
00191 trans.setSize (my_size);
00192 trans.setPreferredSize (my_size);
00193 trans.setMaximumSize (my_size);
00194 trans.setMinimumSize (my_size);
00195 }
00196
00197 public FeaturePopupMenuBuilder getFeaturePopupMenuBuilder(){ return fpmb; }
00198 public DbXrefMenuItemBuilder getDbXrefMenuItemBuilder(){ return dmib; }
00199 public GenbankMenuItemBuilder getGenbankMenuItemBuilder(){ return gmib; }
00200
00201
00202 private SequenceRenderer barRenderer(String type, Color innerColor, double depth, StrandedFeature.Strand strand) throws ChangeVetoException
00203 {
00204 FeatureFilter filter = new FeatureFilter.And(new FeatureFilter.ByType(type),new FeatureFilter.StrandFilter(strand));
00205 FeatureBlockSequenceRenderer fbr = new FeatureBlockSequenceRenderer();
00206 double offset = strand == StrandedFeature.POSITIVE ? 0 : 5;
00207 if(strand==StrandedFeature.NEGATIVE)
00208 offset = 10;
00209 RectangularBeadRenderer renderer = new RectangularBeadRenderer(depth, offset, Color.BLACK, innerColor, new BasicStroke());
00210 renderer.setHeightScaling(false);
00211 fbr.setFeatureRenderer(renderer);
00212 fbr.setCollapsing(false);
00213 return new OverlayRendererWrapper(new FilteringRenderer(fbr, filter, true));
00214 }
00215
00216 private void adjustScaleAndTranslation()
00217 {
00218 if (getSize().width != 0)
00219 {
00220 int width = this.getSize().width;
00221 double scale = (double) width / (double) getGenome().getViewLength();
00222
00223 if (getGenome().getViewStart() >= seq.length() ||
00224 getGenome().getViewLength() >= MAX_FEATURE_DISPLAY_RANGE)
00225 {
00226
00227
00228
00229 trans.setVisible(false);
00230 }
00231 else
00232 {
00233 trans.setScale(scale);
00234 trans.setSymbolTranslation((int) getGenome().getViewStart());
00235 trans.setVisible(true);
00236 }
00237 }
00238 }
00239
00240
00241 private final class DbXrefMenuAction extends AbstractAction
00242 {
00243 protected String url;
00244 protected String db_name;
00245
00246 DbXrefMenuAction( String url, String db_name, String feature_name ){
00247 super("View " + feature_name + " in " + db_name);
00248 this.url = url;
00249 this.db_name = db_name;
00250 }
00251 public void actionPerformed( ActionEvent e ){
00252 try{
00253 BrowserLauncher.openURL(url);
00254 }catch(IOException ioe){}
00255 }
00256 }
00257
00258 private final class GenbankMenuAction extends AbstractAction
00259 {
00260 protected int seq_index;
00261
00262 GenbankMenuAction( int seq_index ){
00263 super("View GenBank annotation for features at " + seq_index);
00264 this.seq_index = seq_index;
00265 }
00266
00267 public void actionPerformed( ActionEvent e ){
00268
00269 Location loc = LocationTools.makeLocation(seq_index, seq_index);
00270 System.err.println("Starting with " + seq.countFeatures() + " features");
00271 FeatureHolder fh = seq.filter(new FeatureFilter.And(new FeatureFilter.OverlapsLocation(loc), new FeatureFilter.Not(new FeatureFilter.ByType(GenomeBuilder.MAUVE_AGGREGATE))));
00272 System.err.println("Filtering leaves " + fh.countFeatures() + " features.");
00273 if (fh.countFeatures() == 0)
00274 return;
00275
00276 JDialog dialog = new JDialog((JFrame) FeaturePanel.this.getTopLevelAncestor(), "Feature Detail", true);
00277
00278 JTabbedPane tabs = new JTabbedPane();
00279 dialog.getContentPane().add(tabs, BorderLayout.CENTER);
00280
00281 for (Iterator fi = fh.features(); fi.hasNext();)
00282 {
00283 Feature f = (Feature) fi.next();
00284 tabs.add(new QualifierPanel(f));
00285 }
00286 dialog.setSize(800, 800);
00287 dialog.pack();
00288 dialog.setVisible(true);
00289 }
00290 }
00291
00292 public interface FeatureMenuItemBuilder extends EventListener
00293 {
00294 public JMenuItem[] getItem(SequenceViewerEvent sve, Genome g, BaseViewerModel model);
00295 }
00296
00297 public class FeaturePopupMenuBuilder
00298 {
00299 protected EventListenerList builders = new EventListenerList();
00300 public void addMenuItemBuilder(FeatureMenuItemBuilder fmib)
00301 {
00302 builders.add(FeatureMenuItemBuilder.class, fmib);
00303 }
00304 public JPopupMenu build(SequenceViewerEvent sve, Genome g, BaseViewerModel model)
00305 {
00306 Object[] listeners = builders.getListenerList();
00307 JPopupMenu leMenu = new JPopupMenu();
00308 for (int i = listeners.length-2; i>=0; i-=2) {
00309 if (listeners[i]==FeatureMenuItemBuilder.class) {
00310 JMenuItem[] items = ((FeatureMenuItemBuilder)listeners[i+1]).getItem(sve, g, model);
00311 for(int j = 0; j < items.length; j++)
00312 leMenu.add(items[j]);
00313 }
00314 }
00315 return leMenu;
00316 }
00317 public void removeMenuItemBuilder(FeatureMenuItemBuilder fmib)
00318 {
00319 builders.remove(FeatureMenuItemBuilder.class, fmib);
00320 }
00321 }
00322
00323
00324 private class GenbankMenuItemBuilder implements FeatureMenuItemBuilder
00325 {
00326 public JMenuItem[] getItem(SequenceViewerEvent sve, Genome g, BaseViewerModel model)
00327 {
00328 JMenuItem gbk_item = new JMenuItem();
00329 gbk_item.setAction( new GenbankMenuAction(sve.getPos()) );
00330 return new JMenuItem[]{gbk_item};
00331 }
00332 }
00333 private class DbXrefMenuItemBuilder implements FeatureMenuItemBuilder
00334 {
00335 public JMenuItem[] getItem(SequenceViewerEvent sve, Genome g, BaseViewerModel model)
00336 {
00337 Vector items = new Vector();
00338 Object t = sve.getTarget();
00339
00340 if (t instanceof FeatureHolder)
00341 {
00342
00343
00344
00345 FeatureHolder fh = (FeatureHolder) t;
00346 for (Iterator fi = fh.features(); fi.hasNext();)
00347 {
00348 Feature f = (Feature) fi.next();
00349
00350 if (f.getAnnotation().containsProperty("db_xref"))
00351 {
00352 String feature_name = f.getType() + " ";
00353 if( f.getAnnotation().containsProperty("gene") )
00354 feature_name += f.getAnnotation().getProperty("gene");
00355 else if( f.getAnnotation().containsProperty("locus_tag") )
00356 feature_name += f.getAnnotation().getProperty("locus_tag");
00357 else
00358 feature_name += f.getLocation();
00359 String db_xref = f.getAnnotation().getProperty("db_xref").toString();
00360 if( db_xref.charAt(0) == '[' )
00361 db_xref = db_xref.substring(1, db_xref.length() - 1 );
00362
00363
00364 StringTokenizer comma_tok = new StringTokenizer(db_xref, ",");
00365
00366
00367 while(comma_tok.hasMoreTokens()){
00368 String cur_xref = comma_tok.nextToken();
00369 cur_xref = cur_xref.trim();
00370
00371 try{
00372 DbXrefFactory dxuf = DbXrefFactory.getInstance();
00373 String db_url = dxuf.getDbURL(cur_xref);
00374 String db_name = dxuf.getDbName(cur_xref);
00375 JMenuItem xref_item = new JMenuItem();
00376 xref_item.setAction( new DbXrefMenuAction(db_url,db_name,feature_name) );
00377 items.add(xref_item);
00378 }catch(DbXrefFactory.UnknownDatabaseException ude)
00379 {
00380 System.err.println(ude.getMessage());
00381 }
00382 }
00383 }
00384 }
00385 }
00386 JMenuItem[] items_array = new JMenuItem[items.size()];
00387 items.toArray(items_array);
00388 return items_array;
00389 }
00390 }
00391
00392 private final class ClickListener implements SequenceViewerListener
00393 {
00394 public void mouseClicked(SequenceViewerEvent sve)
00395 {
00396 JPopupMenu jpm = fpmb.build(sve, getGenome(), model);
00397 jpm.show(sve.getMouseEvent().getComponent(), sve.getMouseEvent().getX(), sve.getMouseEvent().getY());
00398
00399 }
00400
00401 public void mousePressed(SequenceViewerEvent sve)
00402 {
00403
00404 }
00405
00406 public void mouseReleased(SequenceViewerEvent sve)
00407 {
00408
00409 }
00410 }
00411
00412 private final class ToolTipMotionListener implements SequenceViewerMotionListener
00413 {
00414 public void mouseDragged(SequenceViewerEvent sve)
00415 {
00416
00417 }
00418
00419 public void mouseMoved(SequenceViewerEvent sve)
00420 {
00421 Object t = sve.getTarget();
00422
00423 if (t != null && t instanceof FeatureHolder &&
00424 ((FeatureHolder) t).countFeatures() > 0) {
00425 FeatureHolder fh = (FeatureHolder) t;
00426 ToolTipManager.sharedInstance().registerComponent (trans);
00427 StringBuffer msg = new StringBuffer("<HTML>");
00428 for (Iterator fi = fh.features(); fi.hasNext();)
00429 {
00430 Feature f = (Feature) fi.next();
00431 if(f.getAnnotation() == null)
00432 continue;
00433 msg.append(f.getType() + " ");
00434 msg.append(f.getLocation());
00435 msg.append("<br>");
00436
00437 if (f.getAnnotation().containsProperty("gene"))
00438 {
00439 msg.append(" <b>");
00440 msg.append(f.getAnnotation().getProperty("gene"));
00441 msg.append("</b>");
00442 }
00443 else if (f.getAnnotation().containsProperty("locus_tag"))
00444 {
00445 msg.append(" <b>");
00446 msg.append(f.getAnnotation().getProperty("locus_tag"));
00447 msg.append("</b>");
00448 }
00449
00450 if (f.getAnnotation().containsProperty("product"))
00451 {
00452 msg.append(" ");
00453 msg.append(f.getAnnotation().getProperty("product"));
00454 }
00455
00456 if (fi.hasNext())
00457 {
00458 msg.append("<BR>");
00459 }
00460 }
00461 trans.setToolTipText(msg.toString());
00462 }
00463 else
00464 {
00465 trans.setToolTipText(null);
00466 ToolTipManager.sharedInstance().unregisterComponent (trans);
00467 }
00468 }
00469 }
00470
00471 public void viewableRangeChanged(ModelEvent event)
00472 {
00473 if (trans != null)
00474 {
00475 adjustScaleAndTranslation();
00476 }
00477 }
00478
00479 public void genomesReordered(ModelEvent event)
00480 {
00481
00482 }
00483 }