1 /* BamView 2 * 3 * created: 2009 4 * 5 * This file is part of Artemis 6 * 7 * Copyright(C) 2009 Genome Research Limited 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation; either version 2 12 * of the License, or(at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 22 * 23 */ 24 package uk.ac.sanger.artemis.components.alignment; 25 26 27 import java.awt.AlphaComposite; 28 import java.awt.BasicStroke; 29 import java.awt.BorderLayout; 30 import java.awt.Color; 31 import java.awt.Component; 32 import java.awt.Composite; 33 import java.awt.Cursor; 34 import java.awt.Dimension; 35 import java.awt.FlowLayout; 36 import java.awt.FontMetrics; 37 import java.awt.Graphics; 38 import java.awt.Graphics2D; 39 import java.awt.GridBagConstraints; 40 import java.awt.GridBagLayout; 41 import java.awt.Insets; 42 import java.awt.Point; 43 import java.awt.Rectangle; 44 import java.awt.Stroke; 45 import java.awt.event.ActionEvent; 46 import java.awt.event.ActionListener; 47 import java.awt.event.AdjustmentEvent; 48 import java.awt.event.AdjustmentListener; 49 import java.awt.event.ItemEvent; 50 import java.awt.event.ItemListener; 51 import java.awt.event.KeyAdapter; 52 import java.awt.event.KeyEvent; 53 import java.awt.event.MouseAdapter; 54 import java.awt.event.MouseEvent; 55 import java.awt.event.MouseMotionListener; 56 import java.awt.image.BufferedImage; 57 import java.io.BufferedReader; 58 import java.io.File; 59 import java.io.IOException; 60 import java.io.InputStream; 61 import java.io.InputStreamReader; 62 import java.lang.management.ManagementFactory; 63 import java.lang.management.MemoryMXBean; 64 import java.net.URL; 65 import java.nio.BufferOverflowException; 66 import java.nio.BufferUnderflowException; 67 import java.util.ArrayList; 68 import java.util.Collections; 69 import java.util.Enumeration; 70 import java.util.HashMap; 71 import java.util.Hashtable; 72 import java.util.List; 73 import java.util.Vector; 74 import java.util.concurrent.CountDownLatch; 75 import java.util.concurrent.ExecutorService; 76 import java.util.concurrent.Executors; 77 import java.util.concurrent.atomic.AtomicBoolean; 78 79 import javax.swing.BorderFactory; 80 import javax.swing.Box; 81 import javax.swing.BoxLayout; 82 import javax.swing.ButtonGroup; 83 import javax.swing.ImageIcon; 84 import javax.swing.JButton; 85 import javax.swing.JCheckBox; 86 import javax.swing.JCheckBoxMenuItem; 87 import javax.swing.JComponent; 88 import javax.swing.JFrame; 89 import javax.swing.JLabel; 90 import javax.swing.JMenu; 91 import javax.swing.JMenuBar; 92 import javax.swing.JMenuItem; 93 import javax.swing.JOptionPane; 94 import javax.swing.JPanel; 95 import javax.swing.JPopupMenu; 96 import javax.swing.JRadioButton; 97 import javax.swing.JScrollBar; 98 import javax.swing.JScrollPane; 99 import javax.swing.JSeparator; 100 import javax.swing.JSlider; 101 import javax.swing.JTextField; 102 import javax.swing.SwingUtilities; 103 import javax.swing.UIManager; 104 import javax.swing.border.Border; 105 import javax.swing.border.EmptyBorder; 106 import javax.swing.event.ChangeEvent; 107 import javax.swing.event.ChangeListener; 108 109 import org.apache.log4j.Level; 110 111 import htsjdk.samtools.AlignmentBlock; 112 import htsjdk.samtools.SAMException; 113 import htsjdk.samtools.SAMFileHeader; 114 import htsjdk.samtools.SamReader; 115 import htsjdk.samtools.SamReaderFactory; 116 import htsjdk.samtools.ValidationStringency; 117 import htsjdk.samtools.SamReaderFactory.Option; 118 import htsjdk.samtools.cram.ref.ReferenceSource; 119 import htsjdk.samtools.SAMReadGroupRecord; 120 import htsjdk.samtools.SAMRecord; 121 import htsjdk.samtools.SAMSequenceRecord; 122 import htsjdk.samtools.SamInputResource; 123 import htsjdk.samtools.util.CloseableIterator; 124 125 import uk.ac.sanger.artemis.Entry; 126 import uk.ac.sanger.artemis.EntryGroup; 127 import uk.ac.sanger.artemis.FeatureVector; 128 import uk.ac.sanger.artemis.Options; 129 import uk.ac.sanger.artemis.Selection; 130 import uk.ac.sanger.artemis.SelectionChangeEvent; 131 import uk.ac.sanger.artemis.SelectionChangeListener; 132 import uk.ac.sanger.artemis.circular.TextFieldInt; 133 import uk.ac.sanger.artemis.components.DisplayAdjustmentEvent; 134 import uk.ac.sanger.artemis.components.DisplayAdjustmentListener; 135 import uk.ac.sanger.artemis.components.EntryEdit; 136 import uk.ac.sanger.artemis.components.EntryFileDialog; 137 import uk.ac.sanger.artemis.components.FeatureDisplay; 138 import uk.ac.sanger.artemis.components.FileViewer; 139 import uk.ac.sanger.artemis.components.IndexReferenceEvent; 140 import uk.ac.sanger.artemis.components.MessageDialog; 141 import uk.ac.sanger.artemis.components.NonModalDialog; 142 import uk.ac.sanger.artemis.components.SequenceComboBox; 143 import uk.ac.sanger.artemis.components.SwingWorker; 144 import uk.ac.sanger.artemis.editor.MultiLineToolTipUI; 145 import uk.ac.sanger.artemis.io.EntryInformation; 146 import uk.ac.sanger.artemis.io.Range; 147 import uk.ac.sanger.artemis.sequence.Bases; 148 import uk.ac.sanger.artemis.sequence.MarkerRange; 149 import uk.ac.sanger.artemis.sequence.NoSequenceException; 150 import uk.ac.sanger.artemis.util.Document; 151 import uk.ac.sanger.artemis.util.DocumentFactory; 152 import uk.ac.sanger.artemis.util.FTPSeekableStream; 153 import uk.ac.sanger.artemis.util.OutOfRangeException; 154 155 /** 156 * Handles displaying of BAM and CRAM alignment files. 157 * If an index file is not present it will create one. 158 * Files can be read locally, by http or ftp. 159 * 160 */ 161 public class BamView extends JPanel 162 implements DisplayAdjustmentListener, SelectionChangeListener 163 { 164 private static final long serialVersionUID = 1L; 165 166 public static String BAM_SUFFIX = ".*\\.(bam|cram)$"; 167 168 /** Whether or not we re running as a standalone BamView panel. */ 169 private static boolean standaloneMode = false; 170 171 private List<BamViewRecord> readsInView; 172 private Hashtable<String, SamReader> samFileReaderHash = new Hashtable<String, SamReader>(); 173 private List<SAMReadGroupRecord> readGroups = new Vector<SAMReadGroupRecord>(); 174 175 private HashMap<String, Integer> seqLengths = new HashMap<String, Integer>(); 176 private HashMap<String, Integer> offsetLengths; 177 private Vector<String> seqNames = new Vector<String>(); 178 protected List<String> bamList; 179 protected List<Short> hideBamList = new Vector<Short>(); 180 181 private SAMRecordPredicate samRecordFlagPredicate; 182 private SAMRecordMapQPredicate samRecordMapQPredicate; 183 184 private SAMRecordFilter filterFrame; 185 186 private Bases bases; 187 private JScrollPane jspView; 188 private JScrollBar scrollBar; 189 190 private SequenceComboBox combo; 191 private boolean isOrientation = false; 192 private boolean isSingle = false; 193 private boolean isSNPs = false; 194 195 private boolean isCoverage = false; 196 private boolean isSNPplot = false; 197 198 private EntryEdit entry_edit; 199 private FeatureDisplay feature_display; 200 private Selection selection; 201 private JPanel mainPanel = new JPanel(); 202 private CoveragePanel coveragePanel; 203 private SnpPanel snpPanel; 204 205 private boolean logScale = false; 206 private Ruler ruler; 207 private int nbasesInView; 208 209 private int startBase = -1; 210 private int endBase = -1; 211 private int laststart; 212 private int lastend; 213 214 private boolean asynchronous = true; 215 private boolean showBaseAlignment = false; 216 217 private JMenu bamFilesMenu = new JMenu("BAM/CRAM files"); 218 private JCheckBoxMenuItem logMenuItem = new JCheckBoxMenuItem("Use Log Scale", logScale); 219 220 private JCheckBoxMenuItem cbStackView = new JCheckBoxMenuItem("Stack", true); 221 private JCheckBoxMenuItem cbPairedStackView = new JCheckBoxMenuItem("Paired Stack"); 222 private JCheckBoxMenuItem cbStrandStackView = new JCheckBoxMenuItem("Strand Stack"); 223 private JCheckBoxMenuItem cbIsizeStackView = new JCheckBoxMenuItem("Inferred Size", false); 224 private JCheckBoxMenuItem cbCoverageView = new JCheckBoxMenuItem("Coverage", false); 225 private JCheckBoxMenuItem cbCoverageStrandView = new JCheckBoxMenuItem("Coverage by Strand", false); 226 private JCheckBoxMenuItem cbCoverageHeatMap = new JCheckBoxMenuItem("Coverage Heat Map", false); 227 private JCheckBoxMenuItem cbLastSelected; 228 229 private ButtonGroup buttonGroup = new ButtonGroup(); 230 231 private JCheckBoxMenuItem colourByReadGrp = new JCheckBoxMenuItem("Read Group"); 232 private JCheckBoxMenuItem colourByStrandTag = new JCheckBoxMenuItem("RNASeq Strand Specific Tag (XS)"); 233 private JCheckBoxMenuItem colourByCoverageColour = new JCheckBoxMenuItem("Coverage Plot Colours"); 234 private JCheckBoxMenuItem baseQualityColour = new JCheckBoxMenuItem("Base Quality"); 235 private JCheckBoxMenuItem markInsertions = new JCheckBoxMenuItem("Mark Insertions", true); 236 237 private AlphaComposite translucent = 238 AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.6f); 239 240 private ReadGroupsFrame readGrpFrame; 241 private GroupBamFrame groupsFrame = new GroupBamFrame(this, bamFilesMenu); 242 private CoveragePanel coverageView = new CoveragePanel(); 243 244 /** Used to colour the frames. */ 245 private static Color LIGHT_GREY = new Color(200, 200, 200); 246 private static Color DARK_GREEN = new Color(0, 150, 0); 247 private static Color DARK_ORANGE = new Color(255,140,0); 248 private static Color DEEP_PINK = new Color(139,10,80); 249 250 private static Color NON_SELECTED_READ_HIGHLIGHT_COLOUR = new Color(189,103,107); 251 252 private Point lastMousePoint = null; 253 private volatile BamViewRecord mouseOverSAMRecord = null; 254 private volatile BamViewRecord highlightSAMRecord = null; 255 private String mouseOverInsertion; 256 // record of where a mouse drag starts 257 protected int dragStart = -1; 258 259 private static int MAX_BASES = 26000; 260 private int maxHeight = 800; 261 262 private boolean concatSequences = false; 263 private int ALIGNMENT_PIX_PER_BASE; 264 private int BASE_HEIGHT; 265 266 private JPopupMenu popup; 267 private PopupMessageFrame popFrame = new PopupMessageFrame(); 268 private PopupMessageFrame waitingFrame = new PopupMessageFrame("waiting..."); 269 private ExecutorService bamReadTaskExecutor; 270 private int MAX_COVERAGE = Integer.MAX_VALUE; 271 272 private float readLnHgt = 2.0f; 273 274 private int ftpSocketTimeout = BamUtils.getFtpSocketTimeout(); 275 276 /** Whether we should strictly validate upfront the input BAM/CRAM file.*/ 277 boolean doInputFileValidation = false; 278 279 /** Whether to use htsjdk file index caching.*/ 280 boolean useHtsjdkIndexCaching = false; 281 282 private volatile AtomicBoolean foundFatalErrors = new AtomicBoolean(false); 283 284 /** busy cursor */ 285 private Cursor cbusy = new Cursor(Cursor.WAIT_CURSOR); 286 /** done cursor */ 287 private Cursor cdone = new Cursor(Cursor.DEFAULT_CURSOR); 288 289 /** Reference file name for CRAM */ 290 String referenceFilename; 291 292 /**Default file validation stringency level for use by SamReader - setting this to strict could cause BAM View to shut with a fatal error for some files. */ 293 private ValidationStringency validationStringency = ValidationStringency.SILENT; 294 295 public static org.apache.log4j.Logger logger4j = 296 org.apache.log4j.Logger.getLogger(BamView.class); 297 BamView(List<String> bamList, String reference, int nbasesInView, final EntryEdit entry_edit, final FeatureDisplay feature_display, final Bases bases, final JPanel containerPanel, final JFrame frame)298 public BamView(List<String> bamList, 299 String reference, 300 int nbasesInView, 301 final EntryEdit entry_edit, 302 final FeatureDisplay feature_display, 303 final Bases bases, 304 final JPanel containerPanel, 305 final JFrame frame) 306 { 307 this(bamList, reference, nbasesInView, feature_display, bases, containerPanel, frame); 308 this.entry_edit = entry_edit; 309 } 310 BamView(List<String> bamList, String reference, int nbasesInView, final FeatureDisplay feature_display, final Bases bases, final JPanel containerPanel, final JFrame frame)311 public BamView(List<String> bamList, 312 String reference, 313 int nbasesInView, 314 final FeatureDisplay feature_display, 315 final Bases bases, 316 final JPanel containerPanel, 317 final JFrame frame) 318 { 319 super(); 320 setBackground(Color.white); 321 this.bamList = bamList; 322 this.nbasesInView = nbasesInView; 323 this.feature_display = feature_display; 324 this.bases = bases; 325 this.referenceFilename = reference; 326 327 containerPanel.setLayout(new BoxLayout(containerPanel, BoxLayout.Y_AXIS)); 328 containerPanel.add(mainPanel); 329 330 // filter out unmapped reads by default 331 setSamRecordFlagPredicate( 332 new SAMRecordFlagPredicate(SAMRecordFlagPredicate.READ_UNMAPPED_FLAG)); 333 334 if(Options.getOptions().getProperty("bam_validation_mode") != null) 335 { 336 String validationMode = Options.getOptions().getProperty("bam_validation_mode"); 337 338 logger4j.debug("BAM FILE VALIDATION STRINGENCY=" + validationMode); 339 340 if (validationMode.equalsIgnoreCase("STRICT")) 341 { 342 validationStringency = ValidationStringency.STRICT; 343 } 344 else if (validationMode.equalsIgnoreCase("LENIENT")) 345 { 346 validationStringency = ValidationStringency.LENIENT; 347 } 348 else if (validationMode.equalsIgnoreCase("SILENT")) 349 { 350 validationStringency = ValidationStringency.SILENT; 351 } 352 } 353 354 doInputFileValidation = Options.getOptions().getPropertyTruthValue("bamview_perform_detailed_validation"); 355 logger4j.debug("PERFORM UP-FRONT INPUT FILE VALIDATION=" + doInputFileValidation); 356 357 if(Options.getOptions().getIntegerProperty("bam_read_thread") != null) 358 { 359 logger4j.debug("BAM READ THREADS="+Options.getOptions().getIntegerProperty("bam_read_thread")); 360 bamReadTaskExecutor = Executors.newFixedThreadPool( 361 Options.getOptions().getIntegerProperty("bam_read_thread")); 362 } 363 else 364 bamReadTaskExecutor = Executors.newFixedThreadPool(1); 365 366 367 if(Options.getOptions().getIntegerProperty("bam_max_coverage") != null) 368 { 369 logger4j.debug("BAM MAX COVERAGE="+Options.getOptions().getIntegerProperty("bam_max_coverage")); 370 MAX_COVERAGE = Options.getOptions().getIntegerProperty("bam_max_coverage"); 371 } 372 373 useHtsjdkIndexCaching = Options.getOptions().getPropertyTruthValue("bamview_use_htsjdk_file_index_caching"); 374 logger4j.debug("USE HTSJDK BAM FILE INDEX CACHING=" + useHtsjdkIndexCaching); 375 376 try 377 { 378 readAlignmentFileHeader(); 379 } 380 catch(java.lang.UnsupportedClassVersionError err) 381 { 382 JOptionPane.showMessageDialog(null, 383 "This requires Java 1.8 or higher.", 384 "Check Java Version", JOptionPane.WARNING_MESSAGE); 385 } 386 catch (IOException ioe) 387 { 388 if (standaloneMode) 389 { 390 System.err.println(ioe.getMessage()); 391 System.exit(0); 392 } 393 else 394 { 395 // TODO Why has this exception just been ignored? 396 ioe.printStackTrace(); 397 } 398 399 } 400 catch (RuntimeException e) 401 { 402 if (standaloneMode) 403 { 404 System.err.println(e.getMessage()); 405 System.exit(0); 406 } 407 else 408 { 409 throw e; 410 } 411 } 412 413 414 final javax.swing.plaf.FontUIResource font_ui_resource = 415 Options.getOptions().getFontUIResource(); 416 417 Enumeration<Object> keys = UIManager.getDefaults().keys(); 418 while(keys.hasMoreElements()) 419 { 420 Object key = keys.nextElement(); 421 Object value = UIManager.get(key); 422 if(value instanceof javax.swing.plaf.FontUIResource) 423 UIManager.put(key, font_ui_resource); 424 } 425 426 setFont(Options.getOptions().getFont()); 427 FontMetrics fm = getFontMetrics(getFont()); 428 ALIGNMENT_PIX_PER_BASE = fm.charWidth('M'); 429 BASE_HEIGHT = fm.getMaxAscent(); 430 selection = new Selection(null); 431 432 MultiLineToolTipUI.initialize(); 433 setToolTipText(""); 434 435 buttonGroup.add(cbStackView); 436 buttonGroup.add(cbPairedStackView); 437 buttonGroup.add(cbStrandStackView); 438 buttonGroup.add(cbIsizeStackView); 439 buttonGroup.add(cbCoverageView); 440 buttonGroup.add(cbCoverageStrandView); 441 buttonGroup.add(cbCoverageHeatMap); 442 addMouseListener(new PopupListener()); 443 444 jspView = new JScrollPane(this, 445 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, 446 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); 447 448 jspView.setViewportBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.DARK_GRAY)); 449 Border empty = new EmptyBorder(0,0,0,0); 450 jspView.setBorder(empty); 451 jspView.getVerticalScrollBar().setUnitIncrement(8); 452 453 addBamToPanel(frame); 454 455 // apply command line options 456 if(System.getProperty("show_snps") != null) 457 isSNPs = true; 458 if(System.getProperty("show_snp_plot") != null) 459 { 460 isSNPplot = true; 461 snpPanel.setVisible(isSNPplot); 462 } 463 if(System.getProperty("show_cov_plot") != null) 464 { 465 isCoverage = true; 466 coveragePanel.setVisible(isCoverage); 467 } 468 } 469 getToolTipText()470 public String getToolTipText() 471 { 472 if(isCoverageView(getPixPerBaseByWidth()) && lastMousePoint != null) 473 return coverageView.getToolTipText( 474 lastMousePoint.y-getJspView().getViewport().getViewPosition().y); 475 476 if(mouseOverSAMRecord == null) 477 return null; 478 479 String msg = 480 mouseOverSAMRecord.sam.getReadName() + "\n" + 481 mouseOverSAMRecord.sam.getAlignmentStart() + ".." + 482 mouseOverSAMRecord.sam.getAlignmentEnd() + 483 (mouseOverSAMRecord.sam.getReadGroup() != null ? "\nRG="+mouseOverSAMRecord.sam.getReadGroup().getId() : "") + 484 "\nisize=" + mouseOverSAMRecord.sam.getInferredInsertSize() + 485 "\nmapq=" + mouseOverSAMRecord.sam.getMappingQuality()+ 486 "\nrname="+ mouseOverSAMRecord.sam.getReferenceName(); 487 488 if( mouseOverSAMRecord.sam.getReadPairedFlag() && 489 mouseOverSAMRecord.sam.getProperPairFlag() && 490 !mouseOverSAMRecord.sam.getMateUnmappedFlag()) 491 { 492 msg = msg + 493 "\nstrand (read/mate): "+ 494 (mouseOverSAMRecord.sam.getReadNegativeStrandFlag() ? "-" : "+")+" / "+ 495 (mouseOverSAMRecord.sam.getMateNegativeStrandFlag() ? "-" : "+"); 496 } 497 else 498 msg = msg + 499 "\nstrand (read/mate): "+ 500 (mouseOverSAMRecord.sam.getReadNegativeStrandFlag() ? "-" : "+"); 501 502 if(msg != null && mouseOverInsertion != null) 503 msg = msg + "\nInsertion at:" +mouseOverInsertion; 504 505 return msg; 506 } 507 508 /** 509 * Get the SAM file reader. 510 * @param alignmentFile String 511 * @return SamReader 512 * @throws IOException for I/O issue 513 * @throws SAMException 514 */ getSAMFileReader(final String alignmentFile)515 private SamReader getSAMFileReader(final String alignmentFile) throws IOException, SAMException 516 { 517 if(samFileReaderHash.containsKey(alignmentFile)) 518 return samFileReaderHash.get(alignmentFile); 519 520 /* 521 * Try and find the associated index file. 522 * Create one if we can't find it. 523 */ 524 File indexFile = BamUtils.getIndexFile(alignmentFile); 525 526 final SamReader samFileReader; 527 CRAMReferenceSequenceFile ref = null; 528 529 // Parsing of the header happens during SamReader construction, 530 // so need to set the default stringency (from properties). 531 // 532 final SamReaderFactory samReaderFactory = SamReaderFactory.makeDefault(); 533 if (useHtsjdkIndexCaching) 534 samReaderFactory.enable(SamReaderFactory.Option.CACHE_FILE_BASED_INDEXES); 535 else 536 samReaderFactory.disable(SamReaderFactory.Option.CACHE_FILE_BASED_INDEXES); 537 538 samReaderFactory.disable(Option.EAGERLY_DECODE); 539 samReaderFactory.validationStringency(validationStringency); 540 541 if ( BamUtils.isCramFile(alignmentFile) ) 542 { 543 htsjdk.samtools.util.Log.setGlobalLogLevel( 544 htsjdk.samtools.util.Log.LogLevel.ERROR); 545 546 if (!BamView.isStandaloneMode()) 547 { 548 // Will only be executed from Artemis, rather than standalone mode. 549 // 550 ref = new CRAMReferenceSequenceFile( 551 feature_display.getEntryGroup().getSequenceEntry(), this); 552 553 samReaderFactory.referenceSource(new ReferenceSource(ref)); 554 } 555 else 556 { 557 // BamView Standalone mode 558 // 559 if (referenceFilename != null && referenceFilename.trim().length() > 0) 560 { 561 samReaderFactory.referenceSource(new uk.ac.sanger.artemis.cramtools.ref.ReferenceSource(new File(referenceFilename))); 562 } 563 else 564 { 565 // No ref file provided so the code will search for one. 566 samReaderFactory.referenceSource(new uk.ac.sanger.artemis.cramtools.ref.ReferenceSource()); 567 } 568 } 569 } 570 571 /* 572 * Create the associated Sam File Reader 573 */ 574 if(alignmentFile.startsWith("ftp")) 575 { 576 FTPSeekableStream fss = new FTPSeekableStream(new URL(alignmentFile), ftpSocketTimeout); 577 samFileReader = samReaderFactory.open(SamInputResource.of(fss).index(indexFile) ); 578 } 579 else if(!alignmentFile.startsWith("http")) 580 { 581 File normalFile = new File(alignmentFile); 582 samFileReader = samReaderFactory.open(SamInputResource.of(normalFile).index(indexFile) ); 583 } 584 else 585 { 586 final URL urlFile = new URL(alignmentFile); 587 samFileReader = samReaderFactory.open(SamInputResource.of(urlFile).index(indexFile) ); 588 } 589 590 /* 591 * Do one-off additional up-front validation of file. 592 * May take a little while. 593 */ 594 if (doInputFileValidation) 595 BamUtils.validateSAMFile(samFileReader, ref, false); 596 597 samFileReaderHash.put(alignmentFile, samFileReader); 598 599 readGroups.addAll(samFileReader.getFileHeader().getReadGroups()); 600 601 return samFileReader; 602 } 603 readAlignmentFileHeader()604 private void readAlignmentFileHeader() throws IOException 605 { 606 final SamReader inputSam = getSAMFileReader(bamList.get(0)); 607 final SAMFileHeader header = inputSam.getFileHeader(); 608 609 for(SAMSequenceRecord seq: header.getSequenceDictionary().getSequences()) 610 { 611 seqLengths.put(seq.getSequenceName(), 612 seq.getSequenceLength()); 613 seqNames.add(seq.getSequenceName()); 614 } 615 } 616 617 class BamReadTask implements Runnable 618 { 619 private int start; 620 private int end; 621 private short bamIndex; 622 private float pixPerBase; 623 private CountDownLatch latch; 624 625 private Exception exception; 626 BamReadTask(int start, int end, short bamIndex, float pixPerBase, CountDownLatch latch)627 BamReadTask(int start, int end, short bamIndex, float pixPerBase, CountDownLatch latch) 628 { 629 this.start = start; 630 this.end = end; 631 this.bamIndex = bamIndex; 632 this.pixPerBase = pixPerBase; 633 this.latch = latch; 634 } 635 getException()636 public Exception getException() 637 { 638 return exception; 639 } 640 getBamIndex()641 public short getBamIndex() 642 { 643 return bamIndex; 644 } 645 run()646 public void run() 647 { 648 exception = null; 649 650 try 651 { 652 readFromAlignmentFile(start, end, bamIndex, pixPerBase) ; 653 } 654 catch (OutOfMemoryError ome) 655 { 656 throw ome; 657 } 658 catch(Exception e) 659 { 660 exception = e; 661 } 662 finally 663 { 664 latch.countDown(); 665 } 666 } 667 } 668 669 /** 670 * Read a BAM or CRAM file. 671 * @throws IOException 672 */ readFromAlignmentFile(int start, int end, short bamIndex, float pixPerBase)673 private void readFromAlignmentFile(int start, int end, short bamIndex, float pixPerBase) 674 throws IOException 675 { 676 // Open the input file. Automatically detects whether input is BAM or CRAM 677 // and delegates to a reader implementation for the appropriate format. 678 final String bam = bamList.get(bamIndex); 679 final SamReader inputSam = getSAMFileReader(bam); 680 681 if(isConcatSequences()) 682 { 683 for(String seq: seqNames) 684 { 685 int sLen = seqLengths.get(seq); 686 int offset = getSequenceOffset(seq); 687 int sBeg = offset+1; 688 int sEnd = sBeg+sLen-1; 689 690 if( (sBeg >= start && sBeg <= end) || 691 (sBeg <= start && sEnd >= start) ) 692 { 693 int thisStart = start - offset; 694 if(thisStart < 1) 695 thisStart = 1; 696 int thisEnd = end - offset; 697 if(thisEnd > sLen) 698 thisEnd = sLen; 699 700 iterateOverBam(inputSam, seq, thisStart, thisEnd, bamIndex, pixPerBase, bam); 701 //System.out.println("READ "+seq+" "+thisStart+".."+thisEnd+" "+start+" --- "+offset); 702 } 703 } 704 } 705 else 706 { 707 String refName = (String) combo.getSelectedItem(); 708 iterateOverBam(inputSam, refName, start, end, bamIndex, pixPerBase, bam); 709 } 710 //inputSam.close(); 711 } 712 713 /** 714 * Iterate over BAM file and load into the <code>List</code> of 715 * <code>SAMRecord</code>. 716 * @param inputSam 717 * @param refName 718 * @param start 719 * @param end 720 */ iterateOverBam(final SamReader inputSam, final String refName, final int start, final int end, final short bamIndex, final float pixPerBase, final String bam)721 private void iterateOverBam(final SamReader inputSam, 722 final String refName, final int start, final int end, 723 final short bamIndex, final float pixPerBase, 724 final String bam) 725 { 726 final MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); 727 final int checkMemAfter = 8000; 728 final int seqOffset = getSequenceOffset(refName); 729 final int offset = seqOffset- getBaseAtStartOfView(); 730 final boolean isCoverageView = isCoverageView(pixPerBase); 731 732 int cnt = 0; 733 734 int nbins = 800; 735 int binSize = ((end-start)/nbins)+1; 736 if(binSize < 1) 737 { 738 binSize = 1; 739 nbins = end-start+1; 740 } 741 int max = MAX_COVERAGE*binSize; 742 if(max < 1) 743 max = Integer.MAX_VALUE; 744 final int cov[] = new int[nbins]; 745 for(int i=0; i<nbins; i++) 746 cov[i] = 0; 747 748 final CloseableIterator<SAMRecord> it = inputSam.queryOverlapping(refName, start, end); 749 try 750 { 751 while ( it.hasNext() ) 752 { 753 try 754 { 755 cnt++; 756 SAMRecord samRecord = it.next(); 757 758 if(readGrpFrame != null && !readGrpFrame.isReadGroupVisible(samRecord.getReadGroup())) 759 continue; 760 761 if( samRecordFlagPredicate == null || 762 !samRecordFlagPredicate.testPredicate(samRecord)) 763 { 764 if(samRecordMapQPredicate == null || 765 samRecordMapQPredicate.testPredicate(samRecord)) 766 { 767 int abeg = samRecord.getAlignmentStart(); 768 int aend = samRecord.getAlignmentEnd(); 769 boolean over = false; 770 771 for(int i=abeg; i<aend; i++) 772 { 773 int bin = ((i-start)/binSize)-1; 774 if(bin < 0) 775 continue; 776 else if(bin > nbins-1) 777 bin = nbins-1; 778 cov[bin]++; 779 if(cov[bin] > max) 780 { 781 over = true; 782 break; 783 } 784 } 785 786 if(over) 787 continue; 788 789 if(isCoverageView) 790 coverageView.addRecord(samRecord, offset, bam, colourByStrandTag.isSelected()); 791 if(isCoverage) 792 coveragePanel.addRecord(samRecord, offset, bam, colourByStrandTag.isSelected()); 793 if(isSNPplot) 794 snpPanel.addRecord(samRecord, seqOffset); 795 if(!isCoverageView) 796 readsInView.add(new BamViewRecord(samRecord, bamIndex)); 797 } 798 } 799 800 if(cnt > checkMemAfter) 801 { 802 cnt = 0; 803 float heapFraction = 804 (float)((float)memory.getHeapMemoryUsage().getUsed()/ 805 (float)memory.getHeapMemoryUsage().getMax()); 806 logger4j.debug("Heap memory usage (used/max): "+heapFraction); 807 808 if(readsInView.size() > checkMemAfter*2 && !waitingFrame.isVisible()) 809 waitingFrame.showWaiting("loading...", mainPanel); 810 811 if(heapFraction > 0.90) 812 { 813 popFrame.show( 814 "Using > 90 % of the maximum memory limit:"+ 815 (memory.getHeapMemoryUsage().getMax()/1000000.f)+" Mb.\n"+ 816 "Not all reads in this range have been read in. Zoom in or\n"+ 817 "consider increasing the memory for this application.", 818 mainPanel, 819 15000); 820 break; 821 } 822 } 823 } 824 catch(Exception e) 825 { 826 System.err.println(e.getMessage()); 827 } 828 } 829 } 830 finally 831 { 832 it.close(); 833 } 834 } 835 getSequenceLength()836 private int getSequenceLength() 837 { 838 if(isConcatSequences()) 839 { 840 int len = 0; 841 for(String seq: seqNames) 842 len += seqLengths.get(seq); 843 return len; 844 } 845 else 846 return seqLengths.get((String) combo.getSelectedItem()); 847 } 848 849 /** 850 * For BAM files with multiple references sequences, calculate 851 * the offset from the start of the concatenated sequence for 852 * a given reference. 853 * @param refName 854 * @return 855 */ getSequenceOffset(String refName)856 protected int getSequenceOffset(String refName) 857 { 858 if(!isConcatSequences()) 859 return 0; 860 861 if(offsetLengths == null) 862 { 863 if(feature_display == null) 864 { 865 offsetLengths = new HashMap<String, Integer>(combo.getItemCount()); 866 int offset = 0; 867 for(int i=0; i<combo.getItemCount(); i++) 868 { 869 String thisSeqName = (String) combo.getItemAt(i); 870 offsetLengths.put(thisSeqName, offset); 871 offset += seqLengths.get(combo.getItemAt(i)); 872 } 873 return offsetLengths.get(refName); 874 } 875 876 final FeatureVector features = feature_display.getEntryGroup().getAllFeatures(); 877 final HashMap<String, Integer> lookup = new HashMap<String, Integer>(); 878 for(int i=0; i<features.size(); i++) 879 lookup.put(features.elementAt(i).getIDString(), features.elementAt(i).getFirstBase()); 880 881 offsetLengths = new HashMap<String, Integer>(seqNames.size()); 882 for(int i=0; i<seqNames.size(); i++) 883 { 884 final Integer pos = lookup.get(seqNames.get(i)); 885 if(pos != null) 886 offsetLengths.put(seqNames.get(i), pos-1); 887 /*final FeatureContigPredicate predicate = new FeatureContigPredicate(seqNames.get(i).trim()); 888 for(int j=0; j<features.size(); j++) 889 { 890 if(predicate.testPredicate(features.elementAt(j))) 891 { 892 offsetLengths.put(seqNames.get(i), features.elementAt(j).getFirstBase()-1); 893 break; 894 } 895 }*/ 896 } 897 898 if(offsetLengths.size() != seqNames.size()) 899 { 900 System.err.println("Found: "+offsetLengths.size() +" of "+ seqNames.size()); 901 SwingUtilities.invokeLater(new Runnable() 902 { 903 public void run() 904 { 905 JOptionPane.showMessageDialog(BamView.this, 906 "There is a problem matching the reference sequences\n"+ 907 "to the names in the BAM/CRAM file. This may mean the labels\n"+ 908 "on the reference features do not match those in the in\n"+ 909 "the BAM/CRAM file.", 910 "Problem Found", JOptionPane.WARNING_MESSAGE); 911 } 912 }); 913 914 //concatSequences = false; 915 int offset = 0; 916 for(int i=0; i<combo.getItemCount(); i++) 917 { 918 String thisSeqName = (String) combo.getItemAt(i); 919 offsetLengths.put(thisSeqName, offset); 920 offset += seqLengths.get(combo.getItemAt(i)); 921 } 922 //return 0; 923 } 924 } 925 return offsetLengths.get(refName); 926 } 927 928 @Override paintComponent(Graphics g)929 protected void paintComponent(Graphics g) 930 { 931 super.paintComponent(g); 932 Graphics2D g2 = (Graphics2D)g; 933 934 mouseOverSAMRecord = null; 935 final int seqLength = getSequenceLength(); 936 int start; 937 int end; 938 939 if(startBase > 0) 940 start = startBase; 941 else 942 start = getBaseAtStartOfView(); 943 944 if(endBase > 0) 945 end = endBase; 946 else 947 { 948 end = start + nbasesInView - 1; 949 if(end > seqLength) 950 end = seqLength; 951 952 if(feature_display != null && nbasesInView < feature_display.getMaxVisibleBases()) 953 nbasesInView = feature_display.getMaxVisibleBases(); 954 } 955 956 957 final float pixPerBase = getPixPerBaseByWidth(); 958 boolean changeToStackView = false; 959 MemoryMXBean memory = ManagementFactory.getMemoryMXBean(); 960 if(laststart != start || 961 lastend != end || 962 CoveragePanel.isRedraw()) 963 { 964 if(isCoverageView(pixPerBase)) 965 coverageView.init(this, pixPerBase, start, end); 966 if(isCoverage) 967 coveragePanel.init(this, pixPerBase, start, end); 968 if(isSNPplot) 969 snpPanel.init(this, pixPerBase, start, end); 970 971 BamView.this.getRootPane().setCursor(cbusy); 972 973 try 974 { 975 synchronized (this) 976 { 977 try 978 { 979 980 float heapFractionUsedBefore = (float) ((float) memory.getHeapMemoryUsage().getUsed() / 981 (float) memory.getHeapMemoryUsage().getMax()); 982 if(readsInView == null) 983 readsInView = new Vector<BamViewRecord>(); 984 else 985 readsInView.clear(); 986 987 final CountDownLatch latch = new CountDownLatch(bamList.size()-hideBamList.size()); 988 //long ms = System.currentTimeMillis(); 989 990 List<BamReadTask> tasks = new ArrayList<BamReadTask>(); 991 for(short i=0; i<bamList.size(); i++) 992 { 993 if(!hideBamList.contains(i)) 994 { 995 BamReadTask task = new BamReadTask(start, end, i, pixPerBase, latch); 996 tasks.add(task); 997 bamReadTaskExecutor.execute(task); 998 } 999 } 1000 1001 try 1002 { 1003 latch.await(); 1004 } 1005 catch (InterruptedException e) {} 1006 1007 /* 1008 * Check for errors during the bam/cram read process... 1009 */ 1010 String errorText = "Failed to load "; 1011 boolean foundErrors = false; 1012 for (BamReadTask task : tasks) 1013 { 1014 if (task.getException() != null) 1015 { 1016 foundErrors = true; 1017 1018 if (task.getException() instanceof BufferUnderflowException || task.getException() instanceof BufferOverflowException) 1019 errorText = errorText + bamList.get(task.getBamIndex()) + ".\nThis may be due to an invalid index file."; 1020 else 1021 errorText = errorText + bamList.get(task.getBamIndex()) + ".\nError: " + task.getException().getMessage(); 1022 1023 logger4j.error(errorText, task.getException()); 1024 1025 break; 1026 } 1027 } 1028 1029 if (foundErrors) 1030 { 1031 handleFatalError(errorText); 1032 } 1033 1034 //System.out.println("===== NO. THREADS="+ 1035 // ((java.util.concurrent.ThreadPoolExecutor)bamReadTaskExecutor).getPoolSize()+" TIME="+(System.currentTimeMillis()-ms)); 1036 1037 float heapFractionUsedAfter = (float) ((float) memory.getHeapMemoryUsage().getUsed() / 1038 (float) memory.getHeapMemoryUsage().getMax()); 1039 1040 // System.out.println("Heap Max : "+memory.getHeapMemoryUsage().getMax()); 1041 // System.out.println("Heap Used : "+memory.getHeapMemoryUsage().getUsed()); 1042 // System.out.println("Heap memory used "+heapFractionUsedAfter); 1043 1044 if ((heapFractionUsedAfter - heapFractionUsedBefore) > 0.06 1045 && !isStackView() && heapFractionUsedAfter > 0.8) 1046 { 1047 cbStackView.setSelected(true); 1048 changeToStackView = true; 1049 } 1050 1051 if((!isStackView() && !isStrandStackView()) || isBaseAlignmentView(pixPerBase)) 1052 { 1053 Collections.sort(readsInView, new SAMRecordComparator()); 1054 } 1055 else if( (isStackView() || isStrandStackView()) && 1056 bamList.size() > 1) 1057 { 1058 // merge multiple BAM/CRAM files 1059 Collections.sort(readsInView, new SAMRecordPositionComparator(BamView.this)); 1060 } 1061 1062 } 1063 catch (OutOfMemoryError ome) 1064 { 1065 JOptionPane.showMessageDialog(this, "Out of Memory"); 1066 readsInView.clear(); 1067 return; 1068 } 1069 catch(htsjdk.samtools.util.RuntimeIOException re) 1070 { 1071 JOptionPane.showMessageDialog(this, re.getMessage()); 1072 } 1073 } 1074 } 1075 finally 1076 { 1077 try 1078 { 1079 BamView.this.getRootPane().setCursor(cdone); 1080 } 1081 catch (NullPointerException e) 1082 { 1083 // Ignore this as could happen if the panel has been closed. 1084 } 1085 } 1086 } 1087 1088 laststart = start; 1089 lastend = end; 1090 1091 // this needs to be synchronized when cloning BAM window 1092 synchronized(this) 1093 { 1094 if(showBaseAlignment) 1095 drawBaseAlignment(g2, seqLength, pixPerBase, start, end); 1096 else 1097 { 1098 if(isCoverageView(pixPerBase)) 1099 drawCoverage(g2,start, end, pixPerBase); 1100 else if(isStackView()) 1101 drawStackView(g2, seqLength, pixPerBase, start, end); 1102 else if(isPairedStackView()) 1103 drawPairedStackView(g2, seqLength, pixPerBase, start, end); 1104 else if(isStrandStackView()) 1105 drawStrandStackView(g2, seqLength, pixPerBase, start, end); 1106 else 1107 drawLineView(g2, seqLength, pixPerBase, start, end); 1108 } 1109 } 1110 1111 if(isCoverage) 1112 coveragePanel.repaint(); 1113 if(isSNPplot) 1114 snpPanel.repaint(); 1115 1116 if(waitingFrame.isVisible()) 1117 waitingFrame.hideFrame(); 1118 if(changeToStackView) 1119 { 1120 popFrame.show( 1121 "Note :: Changed to the stack view to save memory.\n"+ 1122 "Currently this is using "+ 1123 (memory.getHeapMemoryUsage().getUsed()/1000000.f)+" Mb "+ 1124 "and the maximum\nmemory limit is "+ 1125 (memory.getHeapMemoryUsage().getMax()/1000000.f)+" Mb.", 1126 mainPanel, 1127 15000); 1128 } 1129 } 1130 repaintBamView()1131 protected void repaintBamView() 1132 { 1133 laststart = -1; 1134 1135 repaint(); 1136 } 1137 getPixPerBaseByWidth()1138 private float getPixPerBaseByWidth() 1139 { 1140 return (float)mainPanel.getWidth() / (float)nbasesInView; 1141 } 1142 1143 getMaxBasesInPanel(int seqLength)1144 private int getMaxBasesInPanel(int seqLength) 1145 { 1146 if(feature_display == null) 1147 return seqLength+nbasesInView/3; 1148 else 1149 return seqLength+nbasesInView; 1150 } 1151 1152 /** 1153 * Draw the zoomed-in base view. 1154 * @param g2 1155 * @param seqLength 1156 * @param pixPerBase 1157 * @param start 1158 * @param end 1159 */ drawBaseAlignment(Graphics2D g2, int seqLength, float pixPerBase, final int start, int end)1160 private void drawBaseAlignment(Graphics2D g2, 1161 int seqLength, 1162 float pixPerBase, 1163 final int start, 1164 int end) 1165 { 1166 ruler.start = start; 1167 ruler.end = end; 1168 1169 int ypos = 0; 1170 String refSeq = null; 1171 int refSeqStart = start; 1172 1173 end = start + ( mainPanel.getWidth() * ALIGNMENT_PIX_PER_BASE ); 1174 if(bases != null) 1175 { 1176 try 1177 { 1178 int seqEnd = end+2; 1179 if(seqEnd > bases.getLength()) 1180 seqEnd = bases.getLength(); 1181 1182 if(refSeqStart < 1) 1183 refSeqStart = 1; 1184 refSeq = 1185 bases.getSubSequence(new Range(refSeqStart, seqEnd), Bases.FORWARD).toUpperCase(); 1186 1187 ruler.refSeq = refSeq; 1188 } 1189 catch (OutOfRangeException e) 1190 { 1191 e.printStackTrace(); 1192 } 1193 } 1194 ruler.repaint(); 1195 drawSelectionRange(g2, ALIGNMENT_PIX_PER_BASE, start, end, Color.PINK); 1196 1197 g2.setStroke(new BasicStroke (2.f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND)); 1198 1199 boolean drawn[] = new boolean[readsInView.size()]; 1200 for(int i=0; i<readsInView.size(); i++) 1201 drawn[i] = false; 1202 1203 Rectangle r = jspView.getViewport().getViewRect(); 1204 int nreads = readsInView.size(); 1205 1206 for (int i = 0; i < nreads; i++) 1207 { 1208 try 1209 { 1210 if (!drawn[i]) 1211 { 1212 ypos += 11; 1213 1214 BamViewRecord thisRead = readsInView.get(i); 1215 if (ypos < r.getMaxY() || ypos > r.getMinY()) 1216 drawSequence(g2, thisRead, ypos, refSeq, refSeqStart); 1217 drawn[i] = true; 1218 1219 int thisEnd = thisRead.sam.getAlignmentEnd(); 1220 if (thisEnd == 0) 1221 thisEnd = thisRead.sam.getAlignmentStart() + thisRead.sam.getReadLength(); 1222 1223 for (int j = i + 1; j < nreads; j++) 1224 { 1225 if (!drawn[j]) 1226 { 1227 BamViewRecord nextRead = readsInView.get(j); 1228 int nextStart = nextRead.sam.getAlignmentStart(); 1229 if (nextStart > thisEnd + 1) 1230 { 1231 if (ypos < r.getMaxY() || ypos > r.getMinY()) 1232 drawSequence(g2, nextRead, ypos, refSeq, refSeqStart); 1233 1234 drawn[j] = true; 1235 thisEnd = nextRead.sam.getAlignmentEnd(); 1236 if (thisEnd == 0) 1237 thisEnd = nextStart + nextRead.sam.getReadLength(); 1238 } 1239 else if (ypos > r.getMaxY() || ypos < r.getMinY()) 1240 break; 1241 } 1242 } 1243 } 1244 } 1245 catch (ArrayIndexOutOfBoundsException ae) 1246 { 1247 System.err.println(readsInView.size()+" "+nreads); 1248 ae.printStackTrace(); 1249 } 1250 } 1251 1252 if(ypos > getHeight()) 1253 { 1254 Dimension d = getPreferredSize(); 1255 d.setSize(getPreferredSize().getWidth(), ypos); 1256 setPreferredSize(d); 1257 revalidate(); 1258 } 1259 } 1260 1261 1262 /** 1263 * Draw the query sequence 1264 * @param g2 1265 * @param read 1266 * @param pixPerBase 1267 * @param ypos 1268 */ drawSequence(final Graphics2D g2, final BamViewRecord bamViewRecord, int ypos, String refSeq, int refSeqStart)1269 private void drawSequence(final Graphics2D g2, final BamViewRecord bamViewRecord, 1270 int ypos, String refSeq, int refSeqStart) 1271 { 1272 SAMRecord samRecord = bamViewRecord.sam; 1273 if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing 1274 samRecord.getMateUnmappedFlag() ) // mate is unmapped ) // mate is unmapped 1275 g2.setColor(Color.black); 1276 else 1277 g2.setColor(Color.blue); 1278 1279 final Color col = g2.getColor(); 1280 int xpos; 1281 int len = 0; 1282 int refPos = 0; 1283 SAMRecordSequenceString readSeq = new SAMRecordSequenceString(samRecord.getReadString()); 1284 final int offset = getSequenceOffset(samRecord.getReferenceName()); 1285 1286 byte[] phredQuality = null; 1287 if(baseQualityColour.isSelected()) 1288 phredQuality = samRecord.getBaseQualities(); 1289 1290 Hashtable<Integer, String> insertions = null; 1291 List<AlignmentBlock> blocks = samRecord.getAlignmentBlocks(); 1292 for(int i=0; i<blocks.size(); i++) 1293 { 1294 AlignmentBlock block = blocks.get(i); 1295 int blockStart = block.getReadStart(); 1296 len += block.getLength(); 1297 for(int j=0; j<block.getLength(); j++) 1298 { 1299 int readPos = blockStart-1+j; 1300 xpos = block.getReferenceStart() - 1 + j + offset; 1301 refPos = xpos - refSeqStart + 1; 1302 1303 if(phredQuality != null && phredQuality.length > 0) 1304 setColourByBaseQuality(g2, phredQuality[readPos]); 1305 1306 if(isSNPs && refSeq != null && refPos > 0 && refPos < refSeq.length()) 1307 { 1308 if( 1309 (!readSeq.isSecondaryAlignment()) && 1310 (Character.toUpperCase(readSeq.charAt(readPos)) != refSeq.charAt(refPos)) 1311 ) 1312 g2.setColor(Color.red); 1313 else 1314 g2.setColor(col); 1315 } 1316 1317 g2.drawString(readSeq.substring(readPos, readPos+1), 1318 refPos*ALIGNMENT_PIX_PER_BASE, ypos); 1319 1320 if(isSNPs) 1321 g2.setColor(col); 1322 } 1323 1324 // look for insertions 1325 if(markInsertions.isSelected() && i < blocks.size()-1) 1326 { 1327 int blockEnd = blockStart+block.getLength(); 1328 int nextBlockStart = blocks.get(i+1).getReadStart(); 1329 int insertSize = nextBlockStart - blockEnd; 1330 if(insertSize > 0) 1331 { 1332 if(insertions == null) 1333 insertions = new Hashtable<Integer, String>(); 1334 1335 g2.setColor(DEEP_PINK); 1336 1337 int xscreen = (refPos+1)*ALIGNMENT_PIX_PER_BASE; 1338 insertions.put(xscreen, 1339 (refPos+refSeqStart+1)+" "+ 1340 readSeq.substring(blockEnd-1, nextBlockStart-1)); 1341 g2.drawLine(xscreen, ypos, xscreen, ypos-BASE_HEIGHT); 1342 1343 // mark on reference sequence as well 1344 if(bases != null) 1345 g2.drawLine(xscreen, 11, xscreen, 11-BASE_HEIGHT); 1346 g2.setColor(col); 1347 } 1348 } 1349 1350 if(highlightSAMRecord != null && 1351 highlightSAMRecord.sam.getReadName().equals(bamViewRecord.sam.getReadName())) 1352 { 1353 refPos = block.getReferenceStart() + offset - refSeqStart; 1354 int xstart = refPos*ALIGNMENT_PIX_PER_BASE; 1355 int width = block.getLength()*ALIGNMENT_PIX_PER_BASE; 1356 Color col1 = g2.getColor(); 1357 //g2.setColor(Color.red); 1358 1359 if (isThisBamRecordHighlighted(bamViewRecord) ) 1360 { 1361 // Selected read alignment 1362 g2.setColor(Color.black); 1363 } 1364 else 1365 { 1366 // For a read with the same name as selected one. 1367 g2.setColor(NON_SELECTED_READ_HIGHLIGHT_COLOUR); 1368 } 1369 1370 g2.drawRect(xstart, ypos-BASE_HEIGHT, width, BASE_HEIGHT); 1371 if(i < blocks.size()-1) 1372 { 1373 int nextStart = 1374 (blocks.get(i+1).getReferenceStart() + offset - refSeqStart)*ALIGNMENT_PIX_PER_BASE; 1375 g2.drawLine(xstart+width, ypos-(BASE_HEIGHT/2), nextStart, ypos-(BASE_HEIGHT/2)); 1376 } 1377 1378 g2.setColor(col1); 1379 } 1380 else if(i < blocks.size()-1) 1381 { 1382 refPos = block.getReferenceStart() + offset - refSeqStart; 1383 int xstart = refPos*ALIGNMENT_PIX_PER_BASE; 1384 int width = block.getLength()*ALIGNMENT_PIX_PER_BASE; 1385 int nextStart = 1386 (blocks.get(i+1).getReferenceStart() + offset - refSeqStart)*ALIGNMENT_PIX_PER_BASE; 1387 g2.drawLine(xstart+width, ypos-(BASE_HEIGHT/2), nextStart, ypos-(BASE_HEIGHT/2)); 1388 } 1389 } 1390 1391 if(lastMousePoint != null && blocks.size() > 0) 1392 { 1393 refPos = blocks.get(0).getReferenceStart()+offset-refSeqStart; 1394 int xstart = refPos*ALIGNMENT_PIX_PER_BASE; 1395 1396 refPos = blocks.get(blocks.size()-1).getReferenceStart()+ 1397 blocks.get(blocks.size()-1).getLength()+offset-refSeqStart; 1398 int xend = (refPos+len)*ALIGNMENT_PIX_PER_BASE; 1399 1400 if(lastMousePoint.getY() > ypos-11 && lastMousePoint.getY() < ypos) 1401 if(lastMousePoint.getX() > xstart && 1402 lastMousePoint.getX() < xend) 1403 { 1404 mouseOverSAMRecord = bamViewRecord; 1405 1406 if(insertions != null) 1407 mouseOverInsertion = insertions.get((int)lastMousePoint.getX()); 1408 } 1409 } 1410 } 1411 1412 /** 1413 * Colour bases on their mapping quality. 1414 * @param g2 1415 * @param baseQuality 1416 */ setColourByBaseQuality(Graphics2D g2, byte baseQuality)1417 private void setColourByBaseQuality(Graphics2D g2, byte baseQuality) 1418 { 1419 if (baseQuality < 10) 1420 g2.setColor(Color.blue); 1421 else if (baseQuality < 20) 1422 g2.setColor(DARK_GREEN); 1423 else if (baseQuality < 30) 1424 g2.setColor(DARK_ORANGE); 1425 else 1426 g2.setColor(Color.black); 1427 } 1428 1429 /** 1430 * Draw inferred size view. 1431 * @param g2 1432 * @param seqLength 1433 * @param pixPerBase 1434 * @param start 1435 * @param end 1436 */ drawLineView(Graphics2D g2, int seqLength, float pixPerBase, int start, int end)1437 private void drawLineView(Graphics2D g2, int seqLength, float pixPerBase, int start, int end) 1438 { 1439 drawSelectionRange(g2, pixPerBase,start, end, Color.PINK); 1440 if(isShowScale()) 1441 drawScale(g2, start, end, pixPerBase, getHeight()); 1442 1443 final Stroke stroke = 1444 new BasicStroke (readLnHgt, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND); 1445 g2.setStroke(stroke); 1446 int ydiff = (int) Math.round(1.5*readLnHgt); 1447 1448 final int scaleHeight; 1449 if(isShowScale()) 1450 scaleHeight = 15; 1451 else 1452 scaleHeight = 0; 1453 1454 int baseAtStartOfView = getBaseAtStartOfView(); 1455 Rectangle r = jspView.getViewport().getViewRect(); 1456 1457 for(int i=0; i<readsInView.size(); i++) 1458 { 1459 BamViewRecord bamViewRecord = readsInView.get(i); 1460 SAMRecord samRecord = bamViewRecord.sam; 1461 BamViewRecord bamViewNextRecord = null; 1462 SAMRecord samNextRecord = null; 1463 1464 List<Integer> snps = getSNPs(samRecord); 1465 1466 if( !samRecord.getReadPairedFlag() || // read is not paired in sequencing 1467 samRecord.getMateUnmappedFlag() ) // mate is unmapped 1468 { 1469 if(isSingle) 1470 { 1471 int ypos = getYPos(scaleHeight, samRecord.getReadString().length()); // (getHeight() - scaleHeight) - samRecord.getReadString().length(); 1472 if(ypos > r.getMaxY() || ypos < r.getMinY()) 1473 continue; 1474 1475 g2.setColor(Color.black); 1476 drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ydiff); 1477 } 1478 continue; 1479 } 1480 1481 int ypos = getYPos(scaleHeight, Math.abs(samRecord.getInferredInsertSize())); 1482 if( (ypos > r.getMaxY() || ypos < r.getMinY()) && ypos > 0 ) 1483 continue; 1484 1485 if(i < readsInView.size()-1) 1486 { 1487 bamViewNextRecord = readsInView.get(++i); 1488 samNextRecord = bamViewNextRecord.sam; 1489 1490 if(samRecord.getReadName().equals(samNextRecord.getReadName())) 1491 { 1492 // draw connection between paired reads 1493 if(samRecord.getAlignmentEnd() < samNextRecord.getAlignmentStart() && 1494 (samNextRecord.getAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f) 1495 { 1496 g2.setColor(Color.LIGHT_GRAY); 1497 1498 int offset1 = getSequenceOffset(samRecord.getReferenceName()); 1499 int end1 = samRecord.getAlignmentEnd()+offset1-baseAtStartOfView; 1500 1501 int offset2 = getSequenceOffset(samNextRecord.getReferenceName()); 1502 int start2 = samNextRecord.getAlignmentStart()+offset2-baseAtStartOfView; 1503 1504 drawTranslucentLine(g2, 1505 (int)(end1*pixPerBase), (int)(start2*pixPerBase), ypos); 1506 } 1507 1508 if(colourByCoverageColour.isSelected()) 1509 g2.setColor(getColourByCoverageColour(bamViewRecord)); 1510 else if( (samRecord.getReadNegativeStrandFlag() && // strand of the query (1 for reverse) 1511 samNextRecord.getReadNegativeStrandFlag()) || 1512 (!samRecord.getReadNegativeStrandFlag() && 1513 !samNextRecord.getReadNegativeStrandFlag())) 1514 g2.setColor(Color.red); 1515 else 1516 g2.setColor(Color.blue); 1517 1518 drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ydiff); 1519 drawRead(g2, bamViewNextRecord, pixPerBase, ypos, baseAtStartOfView, getSNPs(samNextRecord), ydiff); 1520 } 1521 else 1522 { 1523 drawLoneRead(g2, bamViewRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight, snps, ydiff); 1524 i--; 1525 } 1526 } 1527 else 1528 { 1529 drawLoneRead(g2, bamViewRecord, ypos, pixPerBase, baseAtStartOfView, scaleHeight, snps, ydiff); 1530 } 1531 } 1532 1533 drawYScale(g2, scaleHeight); 1534 } 1535 getYPos(int scaleHeight, int size)1536 private int getYPos(int scaleHeight, int size) 1537 { 1538 if(!logScale) 1539 return (getHeight() - scaleHeight) - size; 1540 else 1541 { 1542 int logInfSize = (int)( Math.log(size) * 100); 1543 return (getHeight() - scaleHeight) - logInfSize; 1544 } 1545 } 1546 1547 /** 1548 * Draw the reads as lines in vertical stacks. The reads are colour 1549 * coded as follows: 1550 * 1551 * blue - reads are unique and are paired with a mapped mate 1552 * black - reads are unique and are not paired or have an unmapped mate 1553 * green - reads are duplicates 1554 * 1555 * @param g2 1556 * @param seqLength 1557 * @param pixPerBase 1558 * @param start 1559 * @param end 1560 */ drawStackView(Graphics2D g2, final int seqLength, final float pixPerBase, final int start, final int end)1561 private void drawStackView(Graphics2D g2, 1562 final int seqLength, 1563 final float pixPerBase, 1564 final int start, 1565 final int end) 1566 { 1567 drawSelectionRange(g2, pixPerBase,start, end, Color.PINK); 1568 if(isShowScale()) 1569 drawScale(g2, start, end, pixPerBase, getHeight()); 1570 1571 final BasicStroke stroke = new BasicStroke( 1572 readLnHgt, 1573 BasicStroke.CAP_BUTT, 1574 BasicStroke.JOIN_MITER); 1575 g2.setStroke(stroke); 1576 1577 final int scaleHeight; 1578 if(isShowScale()) 1579 scaleHeight = 15; 1580 else 1581 scaleHeight = 0; 1582 1583 int ypos = (getHeight() - scaleHeight); 1584 int ydiff = (int) Math.round(1.5*readLnHgt); 1585 1586 if(isOrientation) 1587 ydiff= 2*ydiff; 1588 int maxEnd = 0; 1589 final int baseAtStartOfView = getBaseAtStartOfView(); 1590 g2.setColor(Color.blue); 1591 final Rectangle r = jspView.getViewport().getViewRect(); 1592 1593 for(BamViewRecord bamViewRecord: readsInView) 1594 { 1595 SAMRecord samRecord = bamViewRecord.sam; 1596 1597 int offset = getSequenceOffset(samRecord.getReferenceName()); 1598 1599 int recordStart = samRecord.getAlignmentStart()+offset; 1600 int recordEnd = samRecord.getAlignmentEnd()+offset; 1601 1602 List<Integer> snps = getSNPs(samRecord); 1603 1604 if(colourByStrandTag.isSelected()) 1605 { 1606 if(samRecord.getAttribute("XS") == null) 1607 g2.setColor(Color.BLACK); 1608 else if( ((Character)samRecord.getAttribute("XS")).equals('+') ) 1609 g2.setColor(Color.BLUE); 1610 else if( ((Character)samRecord.getAttribute("XS")).equals('-') ) 1611 g2.setColor(Color.RED); 1612 else 1613 g2.setColor(Color.BLACK); 1614 } 1615 else if(colourByCoverageColour.isSelected()) 1616 g2.setColor(getColourByCoverageColour(bamViewRecord)); 1617 else if(colourByReadGrp.isSelected()) 1618 g2.setColor(getReadGroupFrame().getReadGroupColour(readGroups, samRecord.getReadGroup())); 1619 else if (samRecord.getDuplicateReadFlag()) 1620 g2.setColor(DARK_GREEN); // Duplicate 1621 else if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing 1622 samRecord.getMateUnmappedFlag() ) // mate is unmapped ) // mate is unmapped 1623 g2.setColor(Color.black); 1624 else 1625 g2.setColor(Color.blue); 1626 1627 if(maxEnd < recordStart || ypos < 0) 1628 { 1629 ypos = (getHeight() - scaleHeight)-ydiff; 1630 maxEnd = recordEnd+2; 1631 } 1632 else 1633 ypos = ypos-ydiff; 1634 1635 if(ypos > r.getMaxY() || ypos < r.getMinY()) 1636 continue; 1637 1638 drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ydiff); 1639 } 1640 } 1641 1642 /** 1643 * Draw the reads as lines in vertical stacks. The reads are colour 1644 * coded as follows: 1645 * 1646 * blue - reads are unique and are paired with a mapped mate 1647 * black - reads are unique and are not paired or have an unmapped mate 1648 * green - reads are duplicates 1649 * 1650 * @param g2 1651 * @param seqLength 1652 * @param pixPerBase 1653 * @param start 1654 * @param end 1655 */ drawStrandStackView(Graphics2D g2, int seqLength, float pixPerBase, int start, int end)1656 private void drawStrandStackView(Graphics2D g2, 1657 int seqLength, 1658 float pixPerBase, 1659 int start, 1660 int end) 1661 { 1662 drawSelectionRange(g2, pixPerBase,start, end, Color.PINK); 1663 final BasicStroke stroke = new BasicStroke( 1664 readLnHgt, 1665 BasicStroke.CAP_BUTT, 1666 BasicStroke.JOIN_MITER); 1667 1668 final int scaleHeight = 15; 1669 drawScale(g2, start, end, pixPerBase, ((getHeight()+scaleHeight)/2)); 1670 1671 int ymid = (getHeight()/ 2); 1672 int ydiff = (int) Math.round(1.5*readLnHgt); 1673 if(isOrientation) 1674 ydiff= 2*ydiff; 1675 1676 g2.setStroke(stroke); 1677 // positive strand 1678 drawStrand(g2, false, scaleHeight, ymid-(scaleHeight/2), -ydiff, pixPerBase); 1679 1680 // negative strand 1681 drawStrand(g2, true, scaleHeight, ymid+(scaleHeight/2), ydiff, pixPerBase); 1682 } 1683 1684 drawStrand(Graphics2D g2, boolean isStrandNegative, int scaleHeight, int ymid, int ystep, float pixPerBase)1685 private void drawStrand(Graphics2D g2, 1686 boolean isStrandNegative, 1687 int scaleHeight, 1688 int ymid, 1689 int ystep, 1690 float pixPerBase) 1691 { 1692 int hgt = getHeight(); 1693 int ypos = (hgt - scaleHeight); 1694 int maxEnd = 0; 1695 int baseAtStartOfView = getBaseAtStartOfView(); 1696 g2.setColor(Color.blue); 1697 Rectangle r = jspView.getViewport().getViewRect(); 1698 1699 for(BamViewRecord bamViewRecord: readsInView) 1700 { 1701 SAMRecord samRecord = bamViewRecord.sam; 1702 1703 if( isNegativeStrand(samRecord, colourByStrandTag.isSelected()) == isStrandNegative ) 1704 { 1705 final int offset = getSequenceOffset(samRecord.getReferenceName()); 1706 final int recordStart = samRecord.getAlignmentStart()+offset; 1707 final int recordEnd = samRecord.getAlignmentEnd()+offset; 1708 List<Integer> snps = getSNPs(samRecord); 1709 1710 if(colourByStrandTag.isSelected()) 1711 { 1712 if(samRecord.getAttribute("XS") == null) 1713 g2.setColor(Color.BLACK); 1714 else if( ((Character)samRecord.getAttribute("XS")).equals('+') ) 1715 g2.setColor(Color.BLUE); 1716 else if( ((Character)samRecord.getAttribute("XS")).equals('-') ) 1717 g2.setColor(Color.RED); 1718 else 1719 g2.setColor(Color.BLACK); 1720 } 1721 else if(colourByCoverageColour.isSelected()) 1722 g2.setColor(getColourByCoverageColour(bamViewRecord)); 1723 else if(colourByReadGrp.isSelected()) 1724 g2.setColor(getReadGroupFrame().getReadGroupColour(readGroups, samRecord.getReadGroup())); 1725 else if (samRecord.getDuplicateReadFlag()) 1726 g2.setColor(DARK_GREEN); // Duplicate 1727 else if (!samRecord.getReadPairedFlag() || // read is not paired in sequencing 1728 samRecord.getMateUnmappedFlag() ) // mate is unmapped 1729 g2.setColor(Color.black); 1730 else 1731 g2.setColor(Color.blue); 1732 1733 if(maxEnd < recordStart || ypos < 0 || ypos > hgt) 1734 { 1735 ypos = ymid + ystep; 1736 maxEnd = recordEnd+2; 1737 } 1738 else 1739 ypos = ypos + ystep; 1740 1741 if(ypos > r.getMaxY() || ypos < r.getMinY()) 1742 continue; 1743 1744 drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ystep); 1745 } 1746 } 1747 } 1748 1749 /** 1750 * Draw paired reads as lines in a vertical stacks. 1751 * @param g2 1752 * @param seqLength 1753 * @param pixPerBase 1754 * @param start 1755 * @param end 1756 */ drawPairedStackView(Graphics2D g2, final int seqLength, final float pixPerBase, final int start, final int end)1757 private void drawPairedStackView(Graphics2D g2, 1758 final int seqLength, 1759 final float pixPerBase, 1760 final int start, 1761 final int end) 1762 { 1763 drawSelectionRange(g2, pixPerBase,start, end, Color.PINK); 1764 if(isShowScale()) 1765 drawScale(g2, start, end, pixPerBase, getHeight()); 1766 1767 final Vector<PairedRead> pairedReads = new Vector<PairedRead>(); 1768 for(int i=0; i<readsInView.size(); i++) 1769 { 1770 BamViewRecord bamViewRecord = readsInView.get(i); 1771 SAMRecord samRecord = bamViewRecord.sam; 1772 if( !samRecord.getReadPairedFlag() || // read is not paired in sequencing 1773 samRecord.getMateUnmappedFlag() ) // mate is unmapped 1774 continue; 1775 1776 BamViewRecord bamViewNextRecord = null; 1777 if(i < readsInView.size()-1) 1778 { 1779 bamViewNextRecord = readsInView.get(++i); 1780 SAMRecord samNextRecord = bamViewNextRecord.sam; 1781 1782 final PairedRead pr = new PairedRead(); 1783 if(samRecord.getReadName().equals(samNextRecord.getReadName()) && 1784 isFromSameBamFile(bamViewRecord, bamViewNextRecord, bamList)) 1785 { 1786 if(samRecord.getAlignmentStart() < samNextRecord.getAlignmentStart()) 1787 { 1788 pr.sam1 = bamViewRecord; 1789 pr.sam2 = bamViewNextRecord; 1790 } 1791 else 1792 { 1793 pr.sam2 = bamViewRecord; 1794 pr.sam1 = bamViewNextRecord; 1795 } 1796 } 1797 else 1798 { 1799 --i; 1800 pr.sam1 = bamViewRecord; 1801 pr.sam2 = null; 1802 } 1803 1804 pairedReads.add(pr); 1805 } 1806 } 1807 Collections.sort(pairedReads, new PairedReadComparator()); 1808 1809 Stroke originalStroke = new BasicStroke (readLnHgt, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND); 1810 1811 g2.setStroke( new BasicStroke (1.3f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND)); 1812 1813 final int scaleHeight; 1814 if(isShowScale()) 1815 scaleHeight = 15; 1816 else 1817 scaleHeight = 0; 1818 1819 int ydiff = (int) Math.round(2.3*readLnHgt); 1820 if(isOrientation) 1821 ydiff= 2*ydiff; 1822 int ypos = getHeight() - scaleHeight - ydiff; 1823 int lastEnd = 0; 1824 int baseAtStartOfView = getBaseAtStartOfView(); 1825 Rectangle r = jspView.getViewport().getViewRect(); 1826 1827 for(PairedRead pr: pairedReads) 1828 { 1829 if(pr.sam1.sam.getAlignmentStart() > lastEnd) 1830 { 1831 ypos = getHeight() - scaleHeight - ydiff; 1832 if(pr.sam2 != null) 1833 lastEnd = pr.sam2.sam.getAlignmentEnd(); 1834 else 1835 lastEnd = pr.sam1.sam.getAlignmentEnd(); 1836 } 1837 else 1838 ypos = ypos - ydiff; 1839 1840 if(ypos > r.getMaxY() || ypos < r.getMinY()) 1841 continue; 1842 1843 g2.setStroke(originalStroke); 1844 1845 if( highlightSAMRecord != null && 1846 highlightSAMRecord.sam.getReadName().equals(pr.sam1.sam.getReadName()) ) 1847 g2.setColor(Color.black); 1848 else 1849 g2.setColor(Color.gray); 1850 1851 if(pr.sam2 != null) 1852 { 1853 if(!readsOverlap(pr.sam1.sam, pr.sam2.sam)) 1854 { 1855 int offset1 = getSequenceOffset(pr.sam1.sam.getReferenceName()); 1856 int offset2 = getSequenceOffset(pr.sam2.sam.getReferenceName()); 1857 drawTranslucentJointedLine(g2, 1858 (int)((pr.sam1.sam.getAlignmentEnd()+offset1-getBaseAtStartOfView())*pixPerBase), 1859 (int)((pr.sam2.sam.getAlignmentStart()+offset2-getBaseAtStartOfView())*pixPerBase), ypos); 1860 } 1861 } 1862 else if(!pr.sam1.sam.getMateUnmappedFlag() && 1863 pr.sam1.sam.getProperPairFlag() && 1864 pr.sam1.sam.getMateReferenceName().equals(pr.sam1.sam.getReferenceName())) 1865 { 1866 final int prStart, prEnd; 1867 if(pr.sam1.sam.getAlignmentStart() > pr.sam1.sam.getMateAlignmentStart()) 1868 { 1869 prStart = pr.sam1.sam.getMateAlignmentStart(); 1870 prEnd = pr.sam1.sam.getAlignmentStart(); 1871 } 1872 else 1873 { 1874 prStart = pr.sam1.sam.getAlignmentEnd(); 1875 prEnd = pr.sam1.sam.getMateAlignmentStart(); 1876 } 1877 1878 int offset = getSequenceOffset(pr.sam1.sam.getReferenceName()); 1879 drawTranslucentJointedLine(g2, 1880 (int)( (prStart+offset-getBaseAtStartOfView())*pixPerBase), 1881 (int)( (prEnd +offset-getBaseAtStartOfView())*pixPerBase), ypos); 1882 } 1883 1884 if(colourByCoverageColour.isSelected()) 1885 g2.setColor(getColourByCoverageColour(pr.sam1)); 1886 else if(colourByStrandTag.isSelected()) 1887 { 1888 if( ((Character)pr.sam1.sam.getAttribute("XS")).equals('+') ) 1889 g2.setColor(Color.BLUE); 1890 else if( ((Character)pr.sam1.sam.getAttribute("XS")).equals('-') ) 1891 g2.setColor(Color.RED); 1892 else 1893 g2.setColor(Color.BLACK); 1894 } 1895 else if(colourByReadGrp.isSelected()) 1896 g2.setColor(getReadGroupFrame().getReadGroupColour(readGroups, pr.sam1.sam.getReadGroup())); 1897 else if( pr.sam2 != null && 1898 !( pr.sam1.sam.getReadNegativeStrandFlag() ^ pr.sam2.sam.getReadNegativeStrandFlag() ) ) 1899 g2.setColor(Color.red); 1900 else 1901 g2.setColor(Color.blue); 1902 1903 Color c = g2.getColor(); 1904 drawRead(g2, pr.sam1, pixPerBase, ypos, baseAtStartOfView, getSNPs(pr.sam1.sam), ydiff); 1905 if(pr.sam2 != null) 1906 { 1907 g2.setColor(c); 1908 drawRead(g2, pr.sam2, pixPerBase, ypos, baseAtStartOfView, getSNPs(pr.sam2.sam), ydiff); 1909 } 1910 } 1911 } 1912 1913 /** 1914 * Check if a record is on the negative strand. If the RNA strand specific 1915 * checkbox is set then use the RNA strand. 1916 * @param samRecord 1917 * @param useStrandTag - strand specific tag 1918 * @return 1919 */ isNegativeStrand(final SAMRecord samRecord, final boolean useStrandTag)1920 protected static boolean isNegativeStrand(final SAMRecord samRecord, 1921 final boolean useStrandTag) 1922 { 1923 if(useStrandTag) 1924 { 1925 if(samRecord.getAttribute("XS") == null) 1926 return samRecord.getReadNegativeStrandFlag(); 1927 if( ((Character)samRecord.getAttribute("XS")).equals('+') ) 1928 return false; 1929 else 1930 return true; 1931 } 1932 return samRecord.getReadNegativeStrandFlag(); 1933 } 1934 1935 /** 1936 * Check if two records are from the same BAM file 1937 * @param sam1 1938 * @param sam2 1939 * @param bamList 1940 * @return 1941 */ isFromSameBamFile(final BamViewRecord sam1, final BamViewRecord sam2, final List<String> bamList)1942 private boolean isFromSameBamFile(final BamViewRecord sam1, 1943 final BamViewRecord sam2, 1944 final List<String> bamList) 1945 { 1946 if(bamList == null || bamList.size()<2) 1947 return true; 1948 1949 final short o1 = sam1.bamIndex; 1950 final short o2 = sam2.bamIndex; 1951 if(o1 != -1 && o2 != -1) 1952 if( o1 != o2 ) 1953 return false; 1954 1955 return true; 1956 } 1957 1958 1959 /** 1960 * Check if two records overlap 1961 * @param s1 1962 * @param s2 1963 * @return true id the two reads overlap 1964 */ readsOverlap(final SAMRecord s1, final SAMRecord s2)1965 private boolean readsOverlap(final SAMRecord s1, 1966 final SAMRecord s2) 1967 { 1968 if( (s2.getAlignmentStart() >= s1.getAlignmentStart() && 1969 s2.getAlignmentStart() <= s1.getAlignmentEnd()) || 1970 (s2.getAlignmentEnd() >= s1.getAlignmentStart() && 1971 s2.getAlignmentEnd() <= s1.getAlignmentEnd()) ) 1972 return true; 1973 1974 if( (s1.getAlignmentStart() >= s2.getAlignmentStart() && 1975 s1.getAlignmentStart() <= s2.getAlignmentEnd()) || 1976 (s1.getAlignmentEnd() >= s2.getAlignmentStart() && 1977 s1.getAlignmentEnd() <= s2.getAlignmentEnd()) ) 1978 return true; 1979 return false; 1980 } 1981 1982 /** 1983 * Draw the read coverage. 1984 * @param g2 1985 * @param start 1986 * @param end 1987 * @param pixPerBase 1988 */ drawCoverage(Graphics2D g2, int start, int end, float pixPerBase)1989 private void drawCoverage(Graphics2D g2, int start, int end, float pixPerBase) 1990 { 1991 int scaleHeight = 0; 1992 if(isShowScale()) 1993 { 1994 drawScale(g2, start, end, pixPerBase, getHeight()); 1995 scaleHeight = 15; 1996 } 1997 1998 int hgt = jspView.getVisibleRect().height-scaleHeight; 1999 if(!cbCoverageStrandView.isSelected() && !coverageView.isPlotHeatMap()) 2000 { 2001 try 2002 { 2003 int y = getHeight()-6-( (hgt* MAX_COVERAGE)/(coverageView.max/coverageView.windowSize) ); 2004 g2.setColor(Color.YELLOW); 2005 // draw the threshold for the coverage max read cut-off 2006 g2.fillRect(0, y, getWidth(), 8); 2007 } 2008 catch(Exception e){} 2009 } 2010 2011 g2.translate(0, getJspView().getViewport().getViewPosition().y); 2012 2013 coverageView.drawSelectionRange(g2, pixPerBase, start, end, getHeight(), Color.PINK); 2014 coverageView.draw(g2, getWidth(), hgt, hideBamList); 2015 if(!coverageView.isPlotHeatMap()) 2016 coverageView.drawMax(g2, coverageView.getMaxCoverage()); 2017 } 2018 2019 /** 2020 * Draw a read that apparently has a read mate that is not in view. 2021 * @param g2 2022 * @param thisRead 2023 * @param ypos 2024 * @param pixPerBase 2025 * @param originalStroke 2026 * @param stroke 2027 */ drawLoneRead(Graphics2D g2, BamViewRecord bamViewRecord, int ypos, float pixPerBase, int baseAtStartOfView, int scaleHeight, List<Integer> snps, int ydiff)2028 private void drawLoneRead(Graphics2D g2, BamViewRecord bamViewRecord, int ypos, 2029 float pixPerBase, int baseAtStartOfView, int scaleHeight, List<Integer> snps, int ydiff) 2030 { 2031 SAMRecord samRecord = bamViewRecord.sam; 2032 boolean offTheTop = false; 2033 int offset = getSequenceOffset(samRecord.getReferenceName()); 2034 int thisStart = samRecord.getAlignmentStart()+offset; 2035 int thisEnd = thisStart + samRecord.getReadString().length() -1; 2036 2037 if(ypos <= 0) 2038 { 2039 offTheTop = true; 2040 ypos = samRecord.getReadString().length(); 2041 } 2042 2043 if(samRecord.getInferredInsertSize() == 0) 2044 { 2045 offTheTop = true; 2046 ypos = getHeight() - scaleHeight - 5; 2047 } 2048 2049 if(samRecord.getInferredInsertSize() != 0 && 2050 Math.abs(samRecord.getMateAlignmentStart()-samRecord.getAlignmentEnd())*pixPerBase > 2.f) 2051 { 2052 g2.setColor(Color.LIGHT_GRAY); 2053 2054 if(samRecord.getAlignmentEnd() < samRecord.getMateAlignmentStart()) 2055 { 2056 int nextStart = 2057 (int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase); 2058 drawTranslucentLine(g2, 2059 (int)((thisEnd-getBaseAtStartOfView())*pixPerBase), nextStart, ypos); 2060 } 2061 else 2062 { 2063 int nextStart = 2064 (int)((samRecord.getMateAlignmentStart()-getBaseAtStartOfView()+offset)*pixPerBase); 2065 drawTranslucentLine(g2, 2066 (int)((thisStart-getBaseAtStartOfView())*pixPerBase), nextStart, ypos); 2067 } 2068 } 2069 2070 if(colourByCoverageColour.isSelected()) 2071 g2.setColor(getColourByCoverageColour(bamViewRecord)); 2072 else if(offTheTop) 2073 g2.setColor(DARK_ORANGE); 2074 else if(samRecord.getReadNegativeStrandFlag() && 2075 samRecord.getMateNegativeStrandFlag()) // strand of the query (1 for reverse) 2076 g2.setColor(Color.red); 2077 else 2078 g2.setColor(Color.blue); 2079 2080 drawRead(g2, bamViewRecord, pixPerBase, ypos, baseAtStartOfView, snps, ydiff); 2081 2082 /*if (isSNPs) 2083 showSNPsOnReads(g2, samRecord, pixPerBase, ypos, offset);*/ 2084 } 2085 2086 drawScale(Graphics2D g2, int start, int end, float pixPerBase, int ypos)2087 private void drawScale(Graphics2D g2, int start, int end, float pixPerBase, int ypos) 2088 { 2089 g2.setColor(Color.black); 2090 g2.drawLine( 0, ypos-14, 2091 (int)((end - getBaseAtStartOfView())*pixPerBase), ypos-14); 2092 int interval = end-start; 2093 2094 if(interval > 256000) 2095 drawTicks(g2, start, end, pixPerBase, 512000, ypos); 2096 else if(interval > 64000) 2097 drawTicks(g2, start, end, pixPerBase, 12800, ypos); 2098 else if(interval > 16000) 2099 drawTicks(g2, start, end, pixPerBase, 3200, ypos); 2100 else if(interval > 4000) 2101 drawTicks(g2, start, end, pixPerBase, 800, ypos); 2102 else if(interval > 1000) 2103 drawTicks(g2, start, end, pixPerBase, 400, ypos); 2104 else 2105 drawTicks(g2, start, end, pixPerBase, 100, ypos); 2106 } 2107 drawTicks(Graphics2D g2, int start, int end, float pixPerBase, int division, int ypos)2108 private void drawTicks(Graphics2D g2, int start, int end, float pixPerBase, int division, int ypos) 2109 { 2110 int markStart = (Math.round(start/division)*division); 2111 2112 if(markStart < 1) 2113 markStart = 1; 2114 2115 int sm = markStart-(division/2); 2116 float x; 2117 if(sm > start) 2118 { 2119 x = (sm-getBaseAtStartOfView())*pixPerBase; 2120 g2.drawLine((int)x, ypos-14,(int)x, ypos-12); 2121 } 2122 2123 for(int m=markStart; m<end; m+=division) 2124 { 2125 x = (m-getBaseAtStartOfView())*pixPerBase; 2126 g2.drawString(Integer.toString(m), x, ypos-1); 2127 g2.drawLine((int)x, ypos-14,(int)x, ypos-11); 2128 2129 sm = m+(division/2); 2130 2131 if(sm < end) 2132 { 2133 x = (sm-getBaseAtStartOfView())*pixPerBase; 2134 g2.drawLine((int)x, ypos-14,(int)x, ypos-12); 2135 } 2136 2137 if(m == 1) 2138 m = 0; 2139 } 2140 } 2141 2142 /** 2143 * Draw a y-scale for inferred size (isize) of reads. 2144 * @param g2 2145 * @param xScaleHeight 2146 */ drawYScale(Graphics2D g2, int xScaleHeight)2147 private void drawYScale(Graphics2D g2, int xScaleHeight) 2148 { 2149 g2.setColor(Color.black); 2150 int maxY = getPreferredSize().height-xScaleHeight; 2151 2152 if(logScale) 2153 { 2154 int start = 10; 2155 int count = 0; 2156 int ypos = getYPos(xScaleHeight, start); 2157 2158 while(ypos > 0 && count < 15 && start > 1) 2159 { 2160 g2.drawLine(0, ypos, 2, ypos); 2161 g2.drawString(Integer.toString(start), 3, ypos); 2162 start = start*5; 2163 ypos = getYPos(xScaleHeight, start); 2164 count++; 2165 } 2166 return; 2167 } 2168 2169 for(int i=100; i<maxY; i+=100) 2170 { 2171 int ypos = getHeight()-i-xScaleHeight; 2172 g2.drawLine(0, ypos, 2, ypos); 2173 g2.drawString(Integer.toString(i), 3, ypos); 2174 } 2175 } 2176 2177 /** 2178 * Draw a given read. 2179 * @param g2 2180 * @param thisRead 2181 * @param pixPerBase 2182 * @param ypos 2183 * @param baseAtStartOfView 2184 * @param snps 2185 */ drawRead(Graphics2D g2, final BamViewRecord bamViewRecord, final float pixPerBase, final int ypos, final int baseAtStartOfView, final List<Integer> snps, final int ydiff)2186 private void drawRead(Graphics2D g2, 2187 final BamViewRecord bamViewRecord, 2188 final float pixPerBase, 2189 final int ypos, 2190 final int baseAtStartOfView, 2191 final List<Integer> snps, 2192 final int ydiff) 2193 { 2194 SAMRecord thisRead = bamViewRecord.sam; 2195 int offset = getSequenceOffset(thisRead.getReferenceName()); 2196 2197 int thisStart = thisRead.getAlignmentStart()+offset-baseAtStartOfView; 2198 int thisEnd = thisRead.getAlignmentEnd()+offset-baseAtStartOfView; 2199 2200 if(highlightSAMRecord != null && 2201 highlightSAMRecord.sam.getReadName().equals(thisRead.getReadName())) 2202 { 2203 Stroke originalStroke = g2.getStroke(); 2204 Stroke stroke = 2205 new BasicStroke (readLnHgt*1.6f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND); 2206 g2.setStroke(stroke); 2207 Color c = g2.getColor(); 2208 2209 if (isThisBamRecordHighlighted(bamViewRecord) ) 2210 { 2211 // Selected read alignment 2212 g2.setColor(Color.black); 2213 } 2214 else 2215 { 2216 // For a read with the same name as selected one. 2217 g2.setColor(NON_SELECTED_READ_HIGHLIGHT_COLOUR); 2218 } 2219 2220 g2.drawLine((int)( thisStart * pixPerBase), ypos, 2221 (int)( thisEnd * pixPerBase), ypos); 2222 g2.setColor(c); 2223 g2.setStroke(originalStroke); 2224 } 2225 2226 2227 if(thisRead.getCigar().getCigarElements().size() == 1) 2228 g2.drawLine((int)( thisStart * pixPerBase), ypos, 2229 (int)( thisEnd * pixPerBase), ypos); 2230 else 2231 { 2232 List<AlignmentBlock> blocks = thisRead.getAlignmentBlocks(); 2233 Color c = g2.getColor(); 2234 int lastEnd = 0; 2235 for(int i=0; i<blocks.size(); i++) 2236 { 2237 AlignmentBlock block = blocks.get(i); 2238 int blockStart = block.getReferenceStart()+offset-baseAtStartOfView; 2239 int blockEnd = blockStart + block.getLength() - 1; 2240 2241 g2.drawLine((int)( blockStart * pixPerBase), ypos, 2242 (int)( blockEnd * pixPerBase), ypos); 2243 if(i > 0 && blockStart != lastEnd) 2244 { 2245 g2.setColor(Color.gray); 2246 g2.drawLine((int)( blockStart * pixPerBase), ypos, 2247 (int)( lastEnd * pixPerBase), ypos); 2248 g2.setColor(c); 2249 } 2250 lastEnd = blockEnd; 2251 } 2252 } 2253 2254 if(isOrientation) 2255 drawArrow(g2, thisRead, thisStart, thisEnd, pixPerBase, ypos, ydiff); 2256 2257 // test if the mouse is over this read 2258 if(lastMousePoint != null) 2259 { 2260 if(lastMousePoint.getY() > ypos-(Math.abs(ydiff)/2) && lastMousePoint.getY() < ypos+(Math.abs(ydiff)/2)) 2261 { 2262 if(lastMousePoint.getX() > thisStart * pixPerBase && 2263 lastMousePoint.getX() < thisEnd * pixPerBase) 2264 { 2265 mouseOverSAMRecord = bamViewRecord; 2266 } 2267 } 2268 } 2269 2270 if (isSNPs && snps != null && snps.size() > 0) 2271 showSNPsOnReads(snps, g2, pixPerBase, ypos); 2272 } 2273 2274 /** 2275 * Draw arrow on the read to indicate orientation. 2276 * @param g2 2277 * @param thisRead 2278 * @param thisStart 2279 * @param thisEnd 2280 * @param pixPerBase 2281 * @param ypos 2282 */ drawArrow(final Graphics2D g2, final SAMRecord thisRead, final int thisStart, final int thisEnd, final float pixPerBase, int ypos, int ydiff)2283 private void drawArrow(final Graphics2D g2, 2284 final SAMRecord thisRead, 2285 final int thisStart, 2286 final int thisEnd, 2287 final float pixPerBase, 2288 int ypos, 2289 int ydiff) 2290 { 2291 if(ydiff < 0) 2292 ydiff = -ydiff; 2293 2294 if(thisRead.getReadNegativeStrandFlag()) 2295 { 2296 ypos-=readLnHgt/2; 2297 int apos = ypos + ydiff - 1; 2298 g2.drawLine((int)( (thisStart+5) * pixPerBase), apos, 2299 (int)( thisStart * pixPerBase), ypos); 2300 } 2301 else 2302 { 2303 ypos+=readLnHgt/2; 2304 int apos = ypos - ydiff + 1; 2305 g2.drawLine((int)( (thisEnd-5) * pixPerBase), apos, 2306 (int)( thisEnd * pixPerBase), ypos); 2307 } 2308 } 2309 2310 /** 2311 * Highlight a selected range 2312 * @param g2 2313 * @param pixPerBase 2314 * @param start 2315 * @param end 2316 */ drawSelectionRange(Graphics2D g2, float pixPerBase, int start, int end, Color c)2317 private void drawSelectionRange(Graphics2D g2, float pixPerBase, int start, int end, Color c) 2318 { 2319 if(getSelection() != null) 2320 { 2321 Range selectedRange = getSelection().getSelectionRange(); 2322 2323 if(selectedRange != null) 2324 { 2325 int rangeStart = selectedRange.getStart(); 2326 int rangeEnd = selectedRange.getEnd(); 2327 2328 if(end < rangeStart || start > rangeEnd) 2329 return; 2330 2331 int x = (int) (pixPerBase*(rangeStart-getBaseAtStartOfView())); 2332 int width = (int) (pixPerBase*(rangeEnd-rangeStart+1)); 2333 2334 g2.setColor(c); 2335 g2.fillRect(x, 0, width, getHeight()); 2336 } 2337 } 2338 } 2339 2340 /** 2341 * Draw a translucent line 2342 * @param g2 2343 * @param start 2344 * @param end 2345 * @param ypos 2346 */ drawTranslucentLine(Graphics2D g2, int start, int end, int ypos)2347 private void drawTranslucentLine(Graphics2D g2, int start, int end, int ypos) 2348 { 2349 Composite origComposite = g2.getComposite(); 2350 g2.setComposite(translucent); 2351 g2.drawLine(start, ypos, end, ypos); 2352 g2.setComposite(origComposite); 2353 } 2354 2355 /** 2356 * Draw a translucent line 2357 * @param g2 2358 * @param start 2359 * @param end 2360 * @param ypos 2361 */ drawTranslucentJointedLine(Graphics2D g2, int start, int end, int ypos)2362 private void drawTranslucentJointedLine(Graphics2D g2, int start, int end, int ypos) 2363 { 2364 Composite origComposite = g2.getComposite(); 2365 g2.setComposite(translucent); 2366 2367 int mid = (int) ((end-start)/2.f)+start; 2368 //g2.drawLine(start, ypos, end, ypos); 2369 g2.drawLine(start, ypos, mid, ypos-5); 2370 g2.drawLine(mid, ypos-5, end, ypos); 2371 g2.setComposite(origComposite); 2372 } 2373 2374 /** 2375 * Display the SNPs for the given read. 2376 * @param snps 2377 * @param g2 2378 * @param pixPerBase 2379 * @param ypos 2380 */ showSNPsOnReads(final List<Integer> snps, final Graphics2D g2, float pixPerBase, final int ypos)2381 private void showSNPsOnReads(final List<Integer> snps, 2382 final Graphics2D g2, 2383 float pixPerBase, final int ypos) 2384 { 2385 final Stroke originalStroke = g2.getStroke(); 2386 final BasicStroke stroke = new BasicStroke( 2387 1.3f, 2388 BasicStroke.CAP_BUTT, 2389 BasicStroke.JOIN_MITER); 2390 g2.setStroke(stroke); 2391 2392 g2.setColor(Color.red); 2393 for(int pos: snps) 2394 g2.drawLine((int) (pos * pixPerBase), ypos + 2, 2395 (int) (pos * pixPerBase), ypos - 2); 2396 g2.setStroke(originalStroke); 2397 } 2398 2399 2400 /** 2401 * Get the SNP positions 2402 * @param samRecord 2403 */ getSNPs(final SAMRecord samRecord)2404 private List<Integer> getSNPs(final SAMRecord samRecord) 2405 { 2406 if(!isSNPs) // return null if not displaying SNPs 2407 return null; 2408 int rbeg = samRecord.getAlignmentStart(); 2409 int rend = samRecord.getAlignmentEnd(); 2410 int offset = getSequenceOffset(samRecord.getReferenceName()); 2411 ArrayList<Integer> snps = null; 2412 2413 // use alignment blocks of the contiguous alignment of 2414 // subsets of read bases to a reference sequence 2415 try 2416 { 2417 final char[] refSeq = bases.getSubSequenceC( 2418 new Range(rbeg+offset, rend+offset), Bases.FORWARD); 2419 final byte[] readSeq = samRecord.getReadBases(); 2420 2421 if (readSeq == null || readSeq.length == 0) 2422 { 2423 // Can occur for secondary alignments 2424 return null; 2425 } 2426 2427 offset = offset - getBaseAtStartOfView(); 2428 final List<AlignmentBlock> blocks = samRecord.getAlignmentBlocks(); 2429 for(AlignmentBlock block: blocks) 2430 { 2431 int readStart = block.getReadStart(); 2432 int refStart = block.getReferenceStart(); 2433 int len = block.getLength(); 2434 for(int j=0; j<len; j++) 2435 { 2436 int readPos = readStart-1+j; 2437 int refPos = refStart+j; 2438 if (Character.toUpperCase(refSeq[refPos-rbeg]) != Character.toUpperCase( (char)readSeq[readPos]) ) 2439 { 2440 if(snps == null) 2441 snps = new ArrayList<Integer>(); 2442 snps.add(refPos+offset); 2443 } 2444 } 2445 } 2446 } 2447 catch (OutOfRangeException e) 2448 { 2449 System.err.println(samRecord.getReadName()+" "+e.getMessage()); 2450 } 2451 return snps; 2452 } 2453 2454 2455 /** 2456 * Add the alignment view to the supplied <code>JPanel</code> in 2457 * a <code>JScrollPane</code>. 2458 * @param mainPanel panel to add the alignment to 2459 * @param frame 2460 * @param autohide automatically hide the top panel containing the buttons 2461 * @param feature_display 2462 */ addBamToPanel(final JFrame frame)2463 private void addBamToPanel(final JFrame frame) 2464 { 2465 final JComponent topPanel = bamTopPanel(frame); 2466 mainPanel.setPreferredSize(new Dimension(900, 400)); 2467 2468 setDisplay(1, nbasesInView, null); 2469 mainPanel.setLayout(new BorderLayout()); 2470 2471 if(topPanel instanceof JPanel) 2472 mainPanel.add(topPanel, BorderLayout.NORTH); 2473 mainPanel.add(jspView, BorderLayout.CENTER); 2474 2475 JPanel bottomPanel = new JPanel(new BorderLayout()); 2476 coveragePanel = new CoveragePanel(this); 2477 bottomPanel.add(coveragePanel, BorderLayout.CENTER); 2478 2479 // 2480 snpPanel = new SnpPanel(this, bases); 2481 bottomPanel.add(snpPanel, BorderLayout.NORTH); 2482 2483 if(feature_display == null) 2484 { 2485 scrollBar = new JScrollBar(JScrollBar.HORIZONTAL, 1, nbasesInView, 1, 2486 getMaxBasesInPanel(getSequenceLength())); 2487 scrollBar.setUnitIncrement(nbasesInView/20); 2488 scrollBar.addAdjustmentListener(new AdjustmentListener() 2489 { 2490 public void adjustmentValueChanged(AdjustmentEvent e) 2491 { 2492 repaint(); 2493 2494 if(isSNPplot) 2495 snpPanel.repaint(); 2496 if(isCoverage) 2497 coveragePanel.repaint(); 2498 } 2499 }); 2500 bottomPanel.add(scrollBar, BorderLayout.SOUTH); 2501 } 2502 else 2503 { 2504 if(!isConcatSequences()) 2505 { 2506 int seqLen = seqLengths.get((String) combo.getSelectedItem()); 2507 int artemisSeqLen = feature_display.getSequenceLength(); 2508 if(seqLen != artemisSeqLen) 2509 { 2510 int newIndex = -1; 2511 for(int i=0; i<seqNames.size(); i++) 2512 { 2513 if(seqLengths.get(seqNames.get(i)) == artemisSeqLen) 2514 { 2515 // this looks like the correct sequence 2516 combo.setSelectedIndex(i); 2517 newIndex = i; 2518 } 2519 } 2520 2521 if(!Options.isBlackBeltMode()) 2522 { 2523 String label[] = { 2524 "The length of the sequence loaded does not match the length of", 2525 "the default reference sequence in the BAM/CRAM ("+seqNames.get(0)+").", 2526 (newIndex == -1 ? "" : "The length does match the reference "+ 2527 seqNames.get(newIndex)+" so this has been set as the default.") 2528 }; 2529 new NonModalDialog(frame, label); 2530 } 2531 } 2532 } 2533 } 2534 2535 mainPanel.add(bottomPanel, BorderLayout.SOUTH); 2536 coveragePanel.setPreferredSize(new Dimension(900, 100)); 2537 coveragePanel.setVisible(false); 2538 snpPanel.setPreferredSize(new Dimension(900, 100)); 2539 snpPanel.setVisible(false); 2540 2541 mainPanel.revalidate(); 2542 jspView.getVerticalScrollBar().setValue( 2543 jspView.getVerticalScrollBar().getMaximum()); 2544 } 2545 addToViewMenu(final short thisBamIndex)2546 private void addToViewMenu(final short thisBamIndex) 2547 { 2548 final File f = new File(bamList.get(thisBamIndex)); 2549 final JCheckBoxMenuItem cbBam = new JCheckBoxMenuItem( 2550 f.getName(), 2551 getImageIcon(getColourByCoverageColour(thisBamIndex)), 2552 true); 2553 bamFilesMenu.add(cbBam); 2554 cbBam.addItemListener(new ItemListener() { 2555 public void itemStateChanged(ItemEvent e) { 2556 if(cbBam.isSelected()) 2557 hideBamList.remove(new Short(thisBamIndex)); 2558 else 2559 hideBamList.add(new Short(thisBamIndex)); 2560 laststart = -1; 2561 repaint(); 2562 } 2563 }); 2564 } 2565 2566 /** 2567 * Refresh the colour of the icons used to identify the 2568 * BAM/CRAM files. 2569 */ refreshColourOfBamMenu()2570 protected void refreshColourOfBamMenu() 2571 { 2572 final Component cs[] = bamFilesMenu.getMenuComponents(); 2573 for(Component c : cs) 2574 { 2575 if(c instanceof JCheckBoxMenuItem) 2576 { 2577 final JCheckBoxMenuItem cbBam = (JCheckBoxMenuItem) c; 2578 final Color col = getColorByJCheckBoxMenuItem(cbBam); 2579 if(col != null) 2580 cbBam.setIcon(getImageIcon(col)); 2581 } 2582 } 2583 } 2584 getColorByJCheckBoxMenuItem(JCheckBoxMenuItem cbBam)2585 protected Color getColorByJCheckBoxMenuItem(JCheckBoxMenuItem cbBam) 2586 { 2587 final String bam = cbBam.getText(); 2588 for(short i=0; i<bamList.size(); i++) 2589 { 2590 final File f = new File(bamList.get(i)); 2591 if(f.getName().equals(bam)) 2592 return getColourByCoverageColour(i); 2593 } 2594 return null; 2595 } 2596 2597 /** 2598 * Create an icon of a box using the given colour. 2599 * @param c 2600 * @return 2601 */ getImageIcon(Color c)2602 ImageIcon getImageIcon(Color c) 2603 { 2604 BufferedImage image = (BufferedImage)this.createImage(10, 10); 2605 Graphics2D g2 = image.createGraphics(); 2606 g2.setColor(c); 2607 g2.fillRect(0, 0, 10, 10); 2608 return new ImageIcon(image); 2609 } 2610 createMenus(JComponent menu)2611 private void createMenus(JComponent menu) 2612 { 2613 final JMenuItem addBam = new JMenuItem("Add BAM/CRAM ..."); 2614 menu.add(addBam); 2615 addBam.addActionListener(new ActionListener() 2616 { 2617 public void actionPerformed(ActionEvent e) 2618 { 2619 FileSelectionDialog bamFileSelection = new FileSelectionDialog( 2620 null, false, "BamView", "BAM/CRAM"); 2621 List<String> bamFiles = bamFileSelection.getFiles(BAM_SUFFIX); 2622 short count = (short) bamList.size(); 2623 2624 bamList.addAll(bamFileSelection.getFiles(BAM_SUFFIX)); 2625 2626 for(short i=0; i<bamFiles.size(); i++) 2627 addToViewMenu((short) (i+count)); 2628 laststart = -1; 2629 repaint(); 2630 } 2631 }); 2632 2633 bamFilesMenu.setFont(addBam.getFont()); 2634 2635 final JMenuItem groupBams = new JMenuItem("Group BAMs/CRAMs ..."); 2636 groupBams.addActionListener(new ActionListener(){ 2637 public void actionPerformed(ActionEvent arg0) 2638 { 2639 groupsFrame.updateAndDisplay(); 2640 } 2641 }); 2642 bamFilesMenu.add(groupBams); 2643 bamFilesMenu.addSeparator(); 2644 menu.add(bamFilesMenu); 2645 2646 2647 final JMenu analyse = new JMenu("Analyse"); 2648 menu.add(analyse); 2649 final JMenuItem readCount = new JMenuItem("Read count of selected features ..."); 2650 analyse.add(readCount); 2651 if(feature_display == null) 2652 readCount.setEnabled(false); 2653 readCount.addActionListener(new ActionListener() 2654 { 2655 public void actionPerformed(ActionEvent e) 2656 { 2657 FeatureVector features = feature_display.getSelection().getAllFeatures(); 2658 2659 JCheckBox overlap = new JCheckBox("Include all overlapping reads", true); 2660 overlap.setToolTipText("Include reads that partially overlap the feature"); 2661 JCheckBox spliced = new JCheckBox("Introns included", true); 2662 Box yBox = Box.createVerticalBox(); 2663 yBox.add(overlap); 2664 yBox.add(spliced); 2665 2666 final ReadCountDialog opts = new ReadCountDialog(new JFrame(), 2667 "Read Count Options", feature_display, yBox); 2668 if(opts.getStatus() == -1) 2669 return; 2670 //JOptionPane.showMessageDialog(null, yBox, "Read Count Option", JOptionPane.INFORMATION_MESSAGE); 2671 2672 new MappedReads(BamView.this, features, 2673 !overlap.isSelected(), spliced.isSelected(), colourByStrandTag.isSelected()); 2674 } 2675 }); 2676 2677 final JMenuItem rpkm = new JMenuItem("RPKM value of selected features ..."); 2678 analyse.add(rpkm); 2679 if(feature_display == null) 2680 rpkm.setEnabled(false); 2681 rpkm.addActionListener(new ActionListener() 2682 { 2683 public void actionPerformed(ActionEvent e) 2684 { 2685 final FeatureVector features = feature_display.getSelection().getAllFeatures(); 2686 2687 JCheckBox overlap = new JCheckBox("Include all overlapping reads", true); 2688 overlap.setToolTipText("Include reads that partially overlap the feature"); 2689 JCheckBox spliced = new JCheckBox("Introns included", true); 2690 JCheckBox allRefSeqs = new JCheckBox("Use reads mapped to all reference sequences", false); 2691 2692 Box yBox = Box.createVerticalBox(); 2693 yBox.add(overlap); 2694 yBox.add(spliced); 2695 2696 if(seqLengths.size() > 1) 2697 yBox.add(allRefSeqs); 2698 2699 final ReadCountDialog opts = new ReadCountDialog(new JFrame(), 2700 "RPKM Options", feature_display, yBox); 2701 if(opts.getStatus() == -1) 2702 return; 2703 2704 int seqlen = 0; 2705 if(feature_display != null) 2706 seqlen = feature_display.getSequenceLength(); 2707 else if(bases != null) 2708 seqlen = bases.getLength(); 2709 2710 new MappedReads(BamView.this, features, seqlen, 2711 !overlap.isSelected(), spliced.isSelected(), allRefSeqs.isSelected(), 2712 colourByStrandTag.isSelected()); 2713 } 2714 }); 2715 2716 final JMenuItem createFeatures = new JMenuItem("Create features from coverage peaks ..."); 2717 analyse.add(createFeatures); 2718 if(feature_display == null) 2719 createFeatures.setEnabled(false); 2720 createFeatures.addActionListener(new ActionListener() 2721 { 2722 public void actionPerformed(ActionEvent e) 2723 { 2724 if(feature_display == null) 2725 return; 2726 new CreateFeatures(groupsFrame); 2727 } 2728 }); 2729 2730 for(short i=0; i<bamList.size(); i++) 2731 addToViewMenu(i); 2732 2733 menu.add(new JSeparator()); 2734 2735 JMenu viewMenu = new JMenu("Views"); 2736 cbStackView.setFont(viewMenu.getFont()); 2737 cbIsizeStackView.setFont(viewMenu.getFont()); 2738 cbPairedStackView.setFont(viewMenu.getFont()); 2739 cbStrandStackView.setFont(viewMenu.getFont()); 2740 cbCoverageView.setFont(viewMenu.getFont()); 2741 cbCoverageStrandView.setFont(viewMenu.getFont()); 2742 cbCoverageHeatMap.setFont(viewMenu.getFont()); 2743 2744 baseQualityColour.setFont(viewMenu.getFont()); 2745 colourByReadGrp.setFont(viewMenu.getFont()); 2746 colourByCoverageColour.setFont(viewMenu.getFont()); 2747 colourByStrandTag.setFont(viewMenu.getFont()); 2748 markInsertions.setFont(viewMenu.getFont()); 2749 2750 cbIsizeStackView.addActionListener(new ActionListener() 2751 { 2752 public void actionPerformed(ActionEvent e) 2753 { 2754 laststart = -1; 2755 logMenuItem.setEnabled(isIsizeStackView()); 2756 getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 2757 repaint(); 2758 } 2759 }); 2760 viewMenu.add(cbIsizeStackView); 2761 2762 2763 cbStackView.addActionListener(new ActionListener() 2764 { 2765 public void actionPerformed(ActionEvent e) 2766 { 2767 laststart = -1; 2768 if(cbStackView.isSelected()) 2769 logMenuItem.setEnabled(false); 2770 getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 2771 setViewportBtm(); 2772 repaint(); 2773 } 2774 }); 2775 viewMenu.add(cbStackView); 2776 2777 2778 cbPairedStackView.addActionListener(new ActionListener() 2779 { 2780 public void actionPerformed(ActionEvent e) 2781 { 2782 laststart = -1; 2783 if(cbPairedStackView.isSelected()) 2784 logMenuItem.setEnabled(false); 2785 getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 2786 repaint(); 2787 } 2788 }); 2789 viewMenu.add(cbPairedStackView); 2790 2791 cbStrandStackView.addActionListener(new ActionListener() 2792 { 2793 public void actionPerformed(ActionEvent e) 2794 { 2795 laststart = -1; 2796 if(isStrandStackView()) 2797 setViewportMidPoint(); 2798 2799 if(cbStrandStackView.isSelected()) 2800 logMenuItem.setEnabled(false); 2801 getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); 2802 repaint(); 2803 } 2804 }); 2805 viewMenu.add(cbStrandStackView); 2806 2807 cbCoverageView.addActionListener(new ActionListener() 2808 { 2809 public void actionPerformed(ActionEvent e) 2810 { 2811 laststart = -1; 2812 if(cbCoverageView.isSelected()) 2813 { 2814 logMenuItem.setEnabled(false); 2815 coverageView.setPlotHeatMap(false); 2816 coverageView.setPlotByStrand(false); 2817 setViewportBtm(); 2818 getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 2819 } 2820 repaint(); 2821 } 2822 }); 2823 viewMenu.add(cbCoverageView); 2824 2825 cbCoverageStrandView.addActionListener(new ActionListener() 2826 { 2827 public void actionPerformed(ActionEvent e) 2828 { 2829 laststart = -1; 2830 if(cbCoverageStrandView.isSelected()) 2831 { 2832 logMenuItem.setEnabled(true); 2833 coverageView.setPlotHeatMap(false); 2834 coverageView.setPlotByStrand(true); 2835 setViewportBtm(); 2836 getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 2837 } 2838 2839 repaint(); 2840 } 2841 }); 2842 viewMenu.add(cbCoverageStrandView); 2843 2844 2845 cbCoverageHeatMap.addActionListener(new ActionListener() 2846 { 2847 public void actionPerformed(ActionEvent e) 2848 { 2849 laststart = -1; 2850 if(cbCoverageHeatMap.isSelected()) 2851 { 2852 logMenuItem.setEnabled(true); 2853 coverageView.setPlotHeatMap(true); 2854 setViewportBtm(); 2855 getJspView().setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 2856 } 2857 2858 repaint(); 2859 } 2860 }); 2861 viewMenu.add(cbCoverageHeatMap); 2862 2863 menu.add(viewMenu); 2864 2865 final JCheckBoxMenuItem checkBoxSNPs = new JCheckBoxMenuItem("SNP marks", isSNPs); 2866 // 2867 final JMenu colourMenu = new JMenu("Colour By"); 2868 2869 final JCheckBoxMenuItem colourDefault = new JCheckBoxMenuItem ("Default", true); 2870 final ButtonGroup grp = new ButtonGroup(); 2871 grp.add(colourByReadGrp); 2872 grp.add(colourByCoverageColour); 2873 grp.add(colourByStrandTag); 2874 grp.add(colourDefault); 2875 2876 colourMenu.add(colourDefault); 2877 colourDefault.addActionListener(new ActionListener() 2878 { 2879 public void actionPerformed(ActionEvent e) 2880 { 2881 repaintBamView(); 2882 } 2883 }); 2884 2885 colourMenu.add(colourByReadGrp); 2886 colourByReadGrp.addActionListener(new ActionListener() 2887 { 2888 public void actionPerformed(ActionEvent e) 2889 { 2890 repaintBamView(); 2891 } 2892 }); 2893 2894 colourMenu.add(colourByCoverageColour); 2895 colourByCoverageColour.addActionListener(new ActionListener() 2896 { 2897 public void actionPerformed(ActionEvent e) 2898 { 2899 repaintBamView(); 2900 } 2901 }); 2902 2903 colourMenu.add(colourByStrandTag); 2904 colourByStrandTag.addActionListener(new ActionListener() 2905 { 2906 public void actionPerformed(ActionEvent e) 2907 { 2908 repaintBamView(); 2909 } 2910 }); 2911 2912 2913 baseQualityColour.addActionListener(new ActionListener() 2914 { 2915 public void actionPerformed(ActionEvent e) 2916 { 2917 if(baseQualityColour.isSelected()) 2918 { 2919 checkBoxSNPs.setSelected(false); 2920 isSNPs = false; 2921 } 2922 repaint(); 2923 } 2924 }); 2925 colourMenu.addSeparator(); 2926 colourMenu.add(baseQualityColour); 2927 menu.add(colourMenu); 2928 2929 // ============= 2930 // Show Menu 2931 // ============= 2932 2933 JMenu showMenu = new JMenu("Show"); 2934 2935 JCheckBoxMenuItem checkBoxOrientation = new JCheckBoxMenuItem("Orientation"); 2936 checkBoxOrientation.addActionListener(new ActionListener() 2937 { 2938 public void actionPerformed(ActionEvent e) 2939 { 2940 isOrientation = !isOrientation; 2941 repaint(); 2942 } 2943 }); 2944 showMenu.add(checkBoxOrientation); 2945 2946 JCheckBoxMenuItem checkBoxSingle = new JCheckBoxMenuItem("Single Reads"); 2947 checkBoxSingle.addActionListener(new ActionListener() 2948 { 2949 public void actionPerformed(ActionEvent e) 2950 { 2951 repaint(); 2952 isSingle = !isSingle; 2953 } 2954 }); 2955 showMenu.add(checkBoxSingle); 2956 2957 checkBoxSNPs.addActionListener(new ActionListener() 2958 { 2959 public void actionPerformed(ActionEvent e) 2960 { 2961 if (isSNPs && bases == null) 2962 { 2963 JOptionPane.showMessageDialog(null, 2964 "No reference sequence supplied to identify SNPs.", "SNPs", 2965 JOptionPane.INFORMATION_MESSAGE); 2966 } 2967 isSNPs = !isSNPs; 2968 2969 if(isSNPs) 2970 baseQualityColour.setSelected(false); 2971 repaint(); 2972 } 2973 }); 2974 showMenu.add(checkBoxSNPs); 2975 2976 markInsertions.addActionListener(new ActionListener() 2977 { 2978 public void actionPerformed(ActionEvent e) 2979 { 2980 repaint(); 2981 } 2982 }); 2983 showMenu.add(markInsertions); 2984 menu.add(showMenu); 2985 2986 // 2987 JMenu graphMenu = new JMenu("Graph"); 2988 JCheckBoxMenuItem checkBoxCoverage = new JCheckBoxMenuItem("Coverage", isCoverage); 2989 checkBoxCoverage.addActionListener(new ActionListener() 2990 { 2991 public void actionPerformed(ActionEvent e) 2992 { 2993 isCoverage = !isCoverage; 2994 coveragePanel.setVisible(isCoverage); 2995 2996 if( isCoverage && 2997 !cbCoverageView.isSelected() && 2998 !cbCoverageStrandView.isSelected() && 2999 !cbCoverageHeatMap.isSelected()) 3000 laststart = -1; 3001 repaint(); 3002 } 3003 }); 3004 graphMenu.add(checkBoxCoverage); 3005 3006 JCheckBoxMenuItem checkBoxSNP = new JCheckBoxMenuItem("SNP", isSNPplot); 3007 checkBoxSNP.addActionListener(new ActionListener() 3008 { 3009 public void actionPerformed(ActionEvent e) 3010 { 3011 isSNPplot = !isSNPplot; 3012 snpPanel.setVisible(isSNPplot); 3013 laststart = -1; 3014 repaint(); 3015 } 3016 }); 3017 graphMenu.add(checkBoxSNP); 3018 menu.add(graphMenu); 3019 3020 3021 if(feature_display != null) 3022 { 3023 final JCheckBoxMenuItem checkBoxSync = 3024 new JCheckBoxMenuItem("Asynchronous", asynchronous); 3025 checkBoxSync.addActionListener(new ActionListener() 3026 { 3027 public void actionPerformed(ActionEvent e) 3028 { 3029 asynchronous = checkBoxSync.isSelected(); 3030 } 3031 }); 3032 menu.add(checkBoxSync); 3033 } 3034 3035 menu.add(new JSeparator()); 3036 3037 JMenu maxHeightMenu = new JMenu("BamView Height"); 3038 menu.add(maxHeightMenu); 3039 3040 final String hgts[] = 3041 {"500", "800", "1000", "1500", "2500", "5000", "50000"}; 3042 3043 ButtonGroup bgroup = new ButtonGroup(); 3044 for(int i=0; i<hgts.length; i++) 3045 { 3046 final String hgt = hgts[i]; 3047 final JCheckBoxMenuItem maxHeightMenuItem = new JCheckBoxMenuItem(hgt); 3048 bgroup.add(maxHeightMenuItem); 3049 maxHeightMenuItem.setSelected(hgts[i].equals(Integer.toString(maxHeight))); 3050 maxHeightMenu.add(maxHeightMenuItem); 3051 maxHeightMenuItem.addActionListener(new ActionListener() 3052 { 3053 public void actionPerformed(ActionEvent e) 3054 { 3055 if(maxHeightMenuItem.isSelected()) 3056 maxHeight = Integer.parseInt(hgt); 3057 int start = getBaseAtStartOfView(); 3058 setDisplay(start, nbasesInView+start, null); 3059 } 3060 }); 3061 } 3062 3063 menu.add(new JSeparator()); 3064 logMenuItem.setFont(menu.getFont()); 3065 menu.add(logMenuItem); 3066 logMenuItem.setEnabled(isIsizeStackView()); 3067 3068 logMenuItem.addActionListener(new ActionListener() 3069 { 3070 public void actionPerformed(ActionEvent e) 3071 { 3072 logScale = logMenuItem.isSelected(); 3073 repaint(); 3074 } 3075 }); 3076 3077 final JMenuItem readGroupsMenu = new JMenuItem("Read Groups ..."); 3078 readGroupsMenu.addActionListener(new ActionListener(){ 3079 public void actionPerformed(ActionEvent arg0) 3080 { 3081 ReadGroupsFrame f = getReadGroupFrame(); 3082 f.setVisible(true); 3083 } 3084 }); 3085 menu.add(readGroupsMenu); 3086 3087 JMenuItem filter = new JMenuItem("Filter Reads ..."); 3088 menu.add(filter); 3089 filter.addActionListener(new ActionListener() 3090 { 3091 public void actionPerformed(ActionEvent e) 3092 { 3093 if(filterFrame == null) 3094 filterFrame = new SAMRecordFilter(BamView.this); 3095 else 3096 filterFrame.setVisible(true); 3097 } 3098 }); 3099 3100 JMenuItem maxReadCoverage = new JMenuItem("Read Coverage Threshold ..."); 3101 menu.add(maxReadCoverage); 3102 maxReadCoverage.addActionListener(new ActionListener() 3103 { 3104 public void actionPerformed(ActionEvent e) 3105 { 3106 final TextFieldInt maxRead = new TextFieldInt(); 3107 maxRead.setValue(MAX_COVERAGE); 3108 int status = JOptionPane.showConfirmDialog(null, maxRead, 3109 "Read Coverage Threshold", JOptionPane.OK_CANCEL_OPTION); 3110 if(status == JOptionPane.OK_OPTION && 3111 maxRead.getValue() != MAX_COVERAGE) 3112 { 3113 MAX_COVERAGE = maxRead.getValue(); 3114 if(MAX_COVERAGE < 1) 3115 MAX_COVERAGE = Integer.MAX_VALUE; 3116 laststart = -1; 3117 repaint(); 3118 } 3119 } 3120 }); 3121 3122 JMenuItem readList = new JMenuItem("List Reads ..."); 3123 menu.add(readList); 3124 readList.addActionListener(new ActionListener() 3125 { 3126 public void actionPerformed(ActionEvent e) 3127 { 3128 new SAMRecordList(BamView.this); 3129 } 3130 }); 3131 3132 final JMenuItem bamSplitter = new JMenuItem("Clone window"); 3133 bamSplitter.addActionListener(new ActionListener() 3134 { 3135 public void actionPerformed(ActionEvent e) 3136 { 3137 openBamView(new Vector<String>(bamList)); 3138 } 3139 }); 3140 menu.add(new JSeparator()); 3141 menu.add(bamSplitter); 3142 3143 // 3144 JMenu coverageMenu = new JMenu("Coverage Options"); 3145 coverageView.init(this, 0.f, 0, 0); 3146 coverageView.createMenus(coverageMenu); 3147 viewMenu.add(new JSeparator()); 3148 viewMenu.add(coverageMenu); 3149 } 3150 getReadGroupFrame()3151 private ReadGroupsFrame getReadGroupFrame() 3152 { 3153 if(readGrpFrame == null) 3154 readGrpFrame = new ReadGroupsFrame(readGroups, BamView.this); 3155 return readGrpFrame; 3156 } 3157 bamTopPanel(final JFrame frame)3158 private JComponent bamTopPanel(final JFrame frame) 3159 { 3160 final JComponent topPanel; 3161 if(frame == null) 3162 { 3163 topPanel = new JPanel(new FlowLayout(FlowLayout.LEADING, 0, 0)); 3164 if(feature_display != null) 3165 this.selection = feature_display.getSelection(); 3166 } 3167 else 3168 { 3169 topPanel = new JMenuBar(); 3170 frame.setJMenuBar((JMenuBar)topPanel); 3171 3172 JMenu fileMenu = new JMenu("File"); 3173 topPanel.add(fileMenu); 3174 3175 JMenuItem readBam = new JMenuItem("Open new BamView ..."); 3176 fileMenu.add(readBam); 3177 readBam.addActionListener(new ActionListener() 3178 { 3179 public void actionPerformed(ActionEvent e) 3180 { 3181 String[] s = { "NEW-BAMVIEW" }; 3182 BamView.main(s); 3183 } 3184 }); 3185 3186 3187 JMenuItem saveAs = new JMenuItem("Save As Image File (png/jpeg/svg) ..."); 3188 fileMenu.add(saveAs); 3189 saveAs.addActionListener(new ActionListener() 3190 { 3191 public void actionPerformed(ActionEvent e) 3192 { 3193 PrintBamView.print((JPanel)mainPanel.getParent()); 3194 } 3195 }); 3196 3197 3198 JMenuItem close = new JMenuItem("Close"); 3199 fileMenu.add(close); 3200 close.addActionListener(new ActionListener() 3201 { 3202 public void actionPerformed(ActionEvent e) 3203 { 3204 close(); 3205 } 3206 }); 3207 3208 JMenuItem exit = new JMenuItem("Exit"); 3209 fileMenu.add(new JSeparator()); 3210 fileMenu.add(exit); 3211 exit.addActionListener(new ActionListener() 3212 { 3213 public void actionPerformed(ActionEvent e) 3214 { 3215 int status = JOptionPane.showConfirmDialog(BamView.this, 3216 "Exit BamView?", "Exit", 3217 JOptionPane.OK_CANCEL_OPTION); 3218 if(status != JOptionPane.OK_OPTION) 3219 return; 3220 System.exit(0); 3221 } 3222 }); 3223 3224 addKeyListener(new KeyAdapter() 3225 { 3226 public void keyPressed(final KeyEvent event) 3227 { 3228 switch (event.getKeyCode()) 3229 { 3230 case KeyEvent.VK_UP: 3231 setZoomLevel((int) (BamView.this.nbasesInView * 1.1)); 3232 break; 3233 case KeyEvent.VK_DOWN: 3234 if (showBaseAlignment) 3235 break; 3236 setZoomLevel((int) (BamView.this.nbasesInView * .9)); 3237 break; 3238 default: 3239 break; 3240 } 3241 } 3242 }); 3243 } 3244 3245 if(seqNames.size() > 1) 3246 { 3247 int len = 0; 3248 for(int i=0; i<seqNames.size(); i++) 3249 len += seqLengths.get(seqNames.get(i)); 3250 3251 if(feature_display != null && 3252 len == feature_display.getSequenceLength()) 3253 concatSequences = true; 3254 else if(bases != null && 3255 len == bases.getLength() ) 3256 concatSequences = true; 3257 } 3258 3259 // auto hide top panel 3260 final JCheckBox buttonAutoHide = new JCheckBox("Hide", (frame == null)); 3261 buttonAutoHide.setToolTipText("Auto-Hide"); 3262 final MouseMotionListener mouseMotionListener = new MouseMotionListener() 3263 { 3264 public void mouseDragged(MouseEvent event) 3265 { 3266 handleCanvasMouseDrag(event); 3267 } 3268 3269 public void mouseMoved(MouseEvent e) 3270 { 3271 lastMousePoint = e.getPoint(); 3272 int thisHgt = HEIGHT-2; 3273 if (thisHgt < 5) 3274 thisHgt = 15; 3275 3276 int y = (int) (e.getY() - jspView.getViewport().getViewRect().getY()); 3277 if (y < thisHgt) 3278 topPanel.setVisible(true); 3279 else 3280 { 3281 if (buttonAutoHide.isSelected() && topPanel.isVisible()) 3282 topPanel.setVisible(false); 3283 } 3284 3285 // KJP - added these to make selecting work properly. 3286 mainPanel.repaint(); 3287 mainPanel.revalidate(); 3288 } 3289 }; 3290 addMouseMotionListener(mouseMotionListener); 3291 3292 3293 combo = new SequenceComboBox(seqNames){ 3294 private static final long serialVersionUID = 1L; 3295 public void update(IndexReferenceEvent event) 3296 { 3297 laststart = -1; 3298 if(feature_display != null) 3299 { 3300 setZoomLevel(feature_display.getMaxVisibleBases()); 3301 } 3302 else 3303 { 3304 /* 3305 * Reset the scroll bar to the left hand side when we change 3306 * the combo selection (for bamview). If this is not done 3307 * then we can go to the end of one [long] contig, select a new one that's 3308 * shorter and we will end up off the end of it in no-man's land 3309 * - this eventually leads to an array negative index exception in 3310 * iterateOverBam 3311 */ 3312 startBase = -1; 3313 endBase = -1; 3314 scrollBar.setValues(1, BamView.this.nbasesInView, 1, 3315 getMaxBasesInPanel(getSequenceLength())); 3316 setZoomLevel(BamView.this.nbasesInView); 3317 repaint(); 3318 } 3319 } 3320 }; 3321 topPanel.add(combo); 3322 3323 if(feature_display == null) 3324 { 3325 final JTextField baseText = new JTextField(8); 3326 JButton goTo = new JButton("GoTo:"); 3327 goTo.setToolTipText("Go to base position"); 3328 goTo.addActionListener(new ActionListener() 3329 { 3330 public void actionPerformed(ActionEvent e) 3331 { 3332 try 3333 { 3334 int basePosition = Integer.parseInt(baseText.getText()); 3335 scrollBar.setValue(basePosition); 3336 } 3337 catch (NumberFormatException nfe) 3338 { 3339 JOptionPane.showMessageDialog(BamView.this, 3340 "Expecting a base number!", "Number Format", 3341 JOptionPane.WARNING_MESSAGE); 3342 } 3343 } 3344 }); 3345 topPanel.add(goTo); 3346 topPanel.add(baseText); 3347 3348 JButton zoomIn = new JButton("-"); 3349 zoomIn.setToolTipText("Zoom out (up arrow)"); 3350 Insets ins = new Insets(1,1,1,1); 3351 zoomIn.setMargin(ins); 3352 zoomIn.addActionListener(new ActionListener() 3353 { 3354 public void actionPerformed(ActionEvent e) 3355 { 3356 setZoomLevel((int) (BamView.this.nbasesInView * 1.1)); 3357 } 3358 }); 3359 topPanel.add(zoomIn); 3360 3361 JButton zoomOut = new JButton("+"); 3362 zoomOut.setToolTipText("Zoom in (down arrow)"); 3363 zoomOut.setMargin(ins); 3364 zoomOut.addActionListener(new ActionListener() 3365 { 3366 public void actionPerformed(ActionEvent e) 3367 { 3368 if (showBaseAlignment) 3369 return; 3370 setZoomLevel((int) (BamView.this.nbasesInView * .9)); 3371 } 3372 }); 3373 topPanel.add(zoomOut); 3374 } 3375 3376 topPanel.add(buttonAutoHide); 3377 3378 3379 final JSlider slider = new JSlider(13, 52, (int) (readLnHgt*10)); 3380 slider.addChangeListener(new ChangeListener(){ 3381 public void stateChanged(ChangeEvent arg0) 3382 { 3383 readLnHgt = (slider.getValue()/10.f); 3384 repaintBamView(); 3385 } 3386 }); 3387 topPanel.add(new JLabel(" Read Height:")); 3388 topPanel.add(slider); 3389 3390 if(feature_display != null) 3391 { 3392 JButton close = new JButton("Close"); 3393 topPanel.add(close); 3394 close.addActionListener(new ActionListener() 3395 { 3396 public void actionPerformed(ActionEvent e) 3397 { 3398 int status = JOptionPane.showConfirmDialog(frame, 3399 "Close the BAM panel?", "Close", 3400 JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); 3401 if(status == JOptionPane.CANCEL_OPTION) 3402 return; 3403 3404 closeBamPanel(); 3405 } 3406 }); 3407 } 3408 return topPanel; 3409 } 3410 setVisible(boolean visible)3411 public void setVisible(boolean visible) 3412 { 3413 super.setVisible(visible); 3414 mainPanel.setVisible(visible); 3415 } 3416 setViewportMidPoint()3417 private void setViewportMidPoint() 3418 { 3419 Point p = jspView.getViewport().getLocation(); 3420 p.y = (getHeight() - jspView.getViewport().getViewRect().height)/2; 3421 jspView.getViewport().setViewPosition(p); 3422 } 3423 setViewportBtm()3424 private void setViewportBtm() 3425 { 3426 jspView.getVerticalScrollBar().setValue( 3427 jspView.getVerticalScrollBar().getMaximum()); 3428 } 3429 getBaseAtStartOfView()3430 protected int getBaseAtStartOfView() 3431 { 3432 if(feature_display != null) 3433 return feature_display.getForwardBaseAtLeftEdge(); 3434 else 3435 return scrollBar.getValue(); 3436 } 3437 3438 /** 3439 * Set the panel size based on the number of bases visible 3440 * and repaint. 3441 * @param nbasesInView 3442 */ setZoomLevel(final int nbasesInView)3443 private void setZoomLevel(final int nbasesInView) 3444 { 3445 int startValue = getBaseAtStartOfView(); 3446 this.nbasesInView = nbasesInView; 3447 float pixPerBase = getPixPerBaseByWidth(); 3448 3449 if(isBaseAlignmentView(pixPerBase)) 3450 { 3451 pixPerBase = ALIGNMENT_PIX_PER_BASE; 3452 this.nbasesInView = (int)(mainPanel.getWidth()/pixPerBase); 3453 jspView.getVerticalScrollBar().setValue(0); 3454 3455 if(ruler == null) 3456 ruler = new Ruler(); 3457 jspView.setColumnHeaderView(ruler); 3458 showBaseAlignment = true; 3459 baseQualityColour.setEnabled(true); 3460 markInsertions.setEnabled(true); 3461 } 3462 else if(jspView != null) 3463 { 3464 if(!isCoverageView(pixPerBase) && nbasesInView >= MAX_BASES) 3465 { 3466 cbLastSelected = getSelectedCheckBoxMenuItem(); 3467 cbCoverageView.setSelected(true); 3468 coverageView.setPlotByStrand(false); 3469 } 3470 else if(isCoverageView(pixPerBase) && nbasesInView < MAX_BASES && cbLastSelected != null) 3471 { 3472 cbLastSelected.setSelected(true); 3473 cbLastSelected = null; 3474 } 3475 3476 jspView.setColumnHeaderView(null); 3477 if(isCoverageView(pixPerBase)) 3478 setViewportBtm(); 3479 else if (isStrandStackView()) 3480 setViewportMidPoint(); 3481 showBaseAlignment = false; 3482 baseQualityColour.setEnabled(false); 3483 markInsertions.setEnabled(false); 3484 } 3485 3486 if(scrollBar != null) 3487 { 3488 scrollBar.setValues(startValue, nbasesInView, 1, 3489 getMaxBasesInPanel(getSequenceLength())); 3490 scrollBar.setUnitIncrement(nbasesInView/20); 3491 scrollBar.setBlockIncrement(nbasesInView); 3492 } 3493 } 3494 3495 3496 /** 3497 * Set the start and end base positions to display. 3498 * @param start 3499 * @param end 3500 * @param event 3501 */ setDisplay(int start, int end, DisplayAdjustmentEvent event)3502 public void setDisplay(int start, 3503 int end, 3504 DisplayAdjustmentEvent event) 3505 { 3506 this.startBase = start; 3507 this.endBase = end; 3508 this.nbasesInView = end-start+1; 3509 lastMousePoint = null; 3510 3511 float pixPerBase; 3512 if(jspView.getViewport().getViewRect().width > 0) 3513 pixPerBase = getPixPerBaseByWidth(); 3514 else 3515 { 3516 if(feature_display == null) 3517 pixPerBase = 1000.f/(float)(end-start+1); 3518 else 3519 pixPerBase = feature_display.getWidth()/(float)(end-start+1); 3520 } 3521 3522 Dimension d = new Dimension(); 3523 d.setSize(nbasesInView*pixPerBase, maxHeight); 3524 setPreferredSize(d); 3525 3526 if(event == null) 3527 { 3528 this.startBase = -1; 3529 this.endBase = -1; 3530 } 3531 } 3532 3533 /** 3534 * Return an Artemis entry from a file 3535 * @param entryFileName 3536 * @param entryGroup 3537 * @return 3538 * @throws NoSequenceException 3539 */ getEntry(final String entryFileName, final EntryGroup entryGroup)3540 private Entry getEntry(final String entryFileName, final EntryGroup entryGroup) 3541 throws NoSequenceException 3542 { 3543 final Document entry_document = DocumentFactory.makeDocument(entryFileName); 3544 final EntryInformation artemis_entry_information = 3545 Options.getArtemisEntryInformation(); 3546 3547 //System.out.println(entryFileName); 3548 final uk.ac.sanger.artemis.io.Entry new_embl_entry = 3549 EntryFileDialog.getEntryFromFile(null, entry_document, 3550 artemis_entry_information, 3551 false); 3552 3553 if(new_embl_entry == null) // the read failed 3554 return null; 3555 3556 Entry entry = null; 3557 try 3558 { 3559 if(entryGroup.getSequenceEntry() != null) 3560 bases = entryGroup.getSequenceEntry().getBases(); 3561 3562 if(bases == null) 3563 { 3564 entry = new Entry(new_embl_entry); 3565 bases = entry.getBases(); 3566 } 3567 else 3568 entry = new Entry(bases,new_embl_entry); 3569 3570 entryGroup.add(entry); 3571 } 3572 catch(OutOfRangeException e) 3573 { 3574 new MessageDialog(null, "read failed: one of the features in " + 3575 entryFileName + " has an out of range " + 3576 "location: " + e.getMessage()); 3577 } 3578 return entry; 3579 } 3580 isShowScale()3581 private boolean isShowScale() 3582 { 3583 return (feature_display == null ? true : false); 3584 } 3585 getJspView()3586 public JScrollPane getJspView() 3587 { 3588 return jspView; 3589 } 3590 3591 /** 3592 * Handle a mouse drag event on the drawing canvas. 3593 **/ handleCanvasMouseDrag(final MouseEvent event)3594 private void handleCanvasMouseDrag(final MouseEvent event) 3595 { 3596 if(event.getButton() == MouseEvent.BUTTON3 || bases == null) 3597 return; 3598 3599 // KJP: Removed this as it causes selection issues and 3600 // doesn't seem strictly necessary. 3601 // highlightSAMRecord = null; 3602 3603 if(event.getClickCount() > 1) 3604 { 3605 getSelection().clear(); 3606 repaint(); 3607 return; 3608 } 3609 3610 highlightRange(event, 3611 MouseEvent.BUTTON1_DOWN_MASK & MouseEvent.BUTTON2_DOWN_MASK); 3612 } 3613 3614 /** 3615 * 3616 * @param event 3617 * @param onmask 3618 */ highlightRange(MouseEvent event, int onmask)3619 protected void highlightRange(MouseEvent event, int onmask) 3620 { 3621 int seqLength = getSequenceLength(); 3622 float pixPerBase = getPixPerBaseByWidth(); 3623 int start = (int) ( ( (event.getPoint().getX())/pixPerBase) + getBaseAtStartOfView() ); 3624 3625 if(start < 1) 3626 start = 1; 3627 if(start > seqLength) 3628 start = seqLength; 3629 3630 if (dragStart < 0 && (event.getModifiersEx() & onmask) == onmask) 3631 dragStart = start; 3632 else if((event.getModifiersEx() & onmask) != onmask) 3633 dragStart = -1; 3634 3635 MarkerRange drag_range; 3636 try 3637 { 3638 if(dragStart < 0) 3639 drag_range = new MarkerRange (bases.getForwardStrand(), start, start); 3640 else 3641 drag_range = new MarkerRange (bases.getForwardStrand(), dragStart, start); 3642 getSelection().setMarkerRange(drag_range); 3643 repaint(); 3644 } 3645 catch (OutOfRangeException e) 3646 { 3647 e.printStackTrace(); 3648 } 3649 } 3650 3651 /** 3652 * Get the colour for the given read given to it by the coverage plot. 3653 * @param samRecord 3654 * @return 3655 */ getColourByCoverageColour(BamViewRecord samRecord)3656 private Color getColourByCoverageColour(BamViewRecord samRecord) 3657 { 3658 short fileIndex = 0; 3659 if(bamList.size()>1) 3660 fileIndex = samRecord.bamIndex; 3661 return getColourByCoverageColour(fileIndex); 3662 } 3663 getColourByCoverageColour(final short fileIndex)3664 private Color getColourByCoverageColour(final short fileIndex) 3665 { 3666 LineAttributes lines[] = CoveragePanel.getLineAttributes(bamList.size()); 3667 return lines[fileIndex].getLineColour(); 3668 } 3669 3670 getMaxBases()3671 protected int getMaxBases() 3672 { 3673 return MAX_BASES; 3674 } 3675 setMaxBases(int max)3676 protected void setMaxBases(int max) 3677 { 3678 MAX_BASES = max; 3679 } 3680 isStackView()3681 private boolean isStackView() 3682 { 3683 return cbStackView.isSelected(); 3684 } 3685 isPairedStackView()3686 private boolean isPairedStackView() 3687 { 3688 return cbPairedStackView.isSelected(); 3689 } 3690 isStrandStackView()3691 private boolean isStrandStackView() 3692 { 3693 return cbStrandStackView.isSelected(); 3694 } 3695 isCoverageView(float pixPerBase)3696 private boolean isCoverageView(float pixPerBase) 3697 { 3698 if(isBaseAlignmentView(pixPerBase)) 3699 return false; 3700 return cbCoverageView.isSelected() || cbCoverageStrandView.isSelected() || cbCoverageHeatMap.isSelected(); 3701 } 3702 isIsizeStackView()3703 private boolean isIsizeStackView() 3704 { 3705 return cbIsizeStackView.isSelected(); 3706 } 3707 isBaseAlignmentView(float pixPerBase)3708 private boolean isBaseAlignmentView(float pixPerBase) 3709 { 3710 if(pixPerBase*1.08f >= ALIGNMENT_PIX_PER_BASE) 3711 return true; 3712 return false; 3713 } 3714 getSelectedCheckBoxMenuItem()3715 private JCheckBoxMenuItem getSelectedCheckBoxMenuItem() 3716 { 3717 if(isStackView()) 3718 return cbStackView; 3719 if(isPairedStackView()) 3720 return cbPairedStackView; 3721 if(isStrandStackView()) 3722 return cbStrandStackView; 3723 if(isIsizeStackView()) 3724 return cbIsizeStackView; 3725 if(cbCoverageView.isSelected()) 3726 return cbCoverageView; 3727 if(cbCoverageHeatMap.isSelected()) 3728 return cbCoverageHeatMap; 3729 return cbCoverageStrandView; 3730 } 3731 getSelection()3732 protected Selection getSelection() 3733 { 3734 return selection; 3735 } 3736 getReadsInView()3737 protected List<BamViewRecord> getReadsInView() 3738 { 3739 return readsInView; 3740 } 3741 getBasesInView()3742 protected int getBasesInView() 3743 { 3744 return nbasesInView; 3745 } 3746 setHighlightSAMRecord(BamViewRecord highlightSAMRecord)3747 protected void setHighlightSAMRecord(BamViewRecord highlightSAMRecord) 3748 { 3749 this.highlightSAMRecord = highlightSAMRecord; 3750 } 3751 getHighlightSAMRecord()3752 protected BamViewRecord getHighlightSAMRecord() 3753 { 3754 return highlightSAMRecord; 3755 } 3756 getFeatureDisplay()3757 protected FeatureDisplay getFeatureDisplay() 3758 { 3759 return feature_display; 3760 } 3761 3762 /** 3763 * @return the combo 3764 */ getCombo()3765 public SequenceComboBox getCombo() 3766 { 3767 return combo; 3768 } 3769 getSamFileReaderHash()3770 protected Hashtable<String, SamReader> getSamFileReaderHash() 3771 { 3772 return samFileReaderHash; 3773 } 3774 getSeqNames()3775 protected Vector<String> getSeqNames() 3776 { 3777 return seqNames; 3778 } 3779 getSeqLengths()3780 protected HashMap<String, Integer> getSeqLengths() 3781 { 3782 return seqLengths; 3783 } 3784 getOffsetLengths()3785 protected HashMap<String, Integer> getOffsetLengths() 3786 { 3787 return offsetLengths; 3788 } 3789 getVersion()3790 private String getVersion() 3791 { 3792 final ClassLoader cl = this.getClass().getClassLoader(); 3793 try 3794 { 3795 String line; 3796 InputStream in = cl.getResourceAsStream("etc/versions"); 3797 BufferedReader reader = new BufferedReader(new InputStreamReader(in)); 3798 while((line = reader.readLine()) != null) 3799 { 3800 if(line.startsWith("BamView")) 3801 return line.substring( "BamView".length() ).trim(); 3802 } 3803 reader.close(); 3804 in.close(); 3805 } 3806 catch (Exception ex) 3807 { 3808 } 3809 return null; 3810 } 3811 3812 /** 3813 * Open another BamView window 3814 */ openBamView(final List<String> bamsList)3815 public void openBamView(final List<String> bamsList) 3816 { 3817 BamView bamView = new BamView(bamsList, 3818 null, nbasesInView, entry_edit, 3819 feature_display, bases, (JPanel) mainPanel.getParent(), null); 3820 bamView.getJspView().getVerticalScrollBar().setValue( 3821 bamView.getJspView().getVerticalScrollBar().getMaximum()); 3822 getJspView().getVerticalScrollBar().setValue( 3823 bamView.getJspView().getVerticalScrollBar().getMaximum()); 3824 3825 int start = getBaseAtStartOfView(); 3826 setDisplay(start, nbasesInView+start, null); 3827 if(feature_display != null) 3828 { 3829 feature_display.addDisplayAdjustmentListener(bamView); 3830 feature_display.getSelection().addSelectionChangeListener(bamView); 3831 3832 if(entry_edit != null) 3833 entry_edit.getOneLinePerEntryDisplay().addDisplayAdjustmentListener(bamView); 3834 if(feature_display.getEntryGroup().getSequenceEntry().getEMBLEntry().getSequence() 3835 instanceof uk.ac.sanger.artemis.io.IndexFastaStream) 3836 { 3837 if(entry_edit != null) 3838 { 3839 // add reference sequence selection listeners 3840 entry_edit.getEntryGroupDisplay().getIndexFastaCombo().addIndexReferenceListener(bamView.getCombo()); 3841 bamView.getCombo().addIndexReferenceListener(entry_edit.getEntryGroupDisplay().getIndexFastaCombo()); 3842 } 3843 } 3844 } 3845 } 3846 3847 /** 3848 * Artemis event notification 3849 */ displayAdjustmentValueChanged(final DisplayAdjustmentEvent event)3850 public void displayAdjustmentValueChanged(final DisplayAdjustmentEvent event) 3851 { 3852 if(event.getType() == DisplayAdjustmentEvent.REV_COMP_EVENT && 3853 event.isRevCompDisplay()) 3854 JOptionPane.showMessageDialog(this, 3855 "Flipping the display is not supported by BamView.", "Warning", 3856 JOptionPane.WARNING_MESSAGE); 3857 3858 if(!asynchronous) 3859 { 3860 // if not asynchronous 3861 displayAdjustmentWork(event); 3862 return; 3863 } 3864 3865 SwingWorker worker = new SwingWorker() 3866 { 3867 public Object construct() 3868 { 3869 try 3870 { 3871 Thread.sleep(500); 3872 } 3873 catch (InterruptedException e) 3874 { 3875 e.printStackTrace(); 3876 } 3877 3878 if(event.getStart() != ((FeatureDisplay)event.getSource()).getForwardBaseAtLeftEdge()) 3879 { 3880 waitingFrame.showWaiting("waiting...", mainPanel); 3881 return null; 3882 } 3883 3884 displayAdjustmentWork(event); 3885 waitingFrame.setVisible(false); 3886 return null; 3887 } 3888 }; 3889 worker.start(); 3890 } 3891 3892 /** 3893 * Carry out the display agjustment event action. 3894 * @param event 3895 */ displayAdjustmentWork(final DisplayAdjustmentEvent event)3896 private void displayAdjustmentWork(final DisplayAdjustmentEvent event) 3897 { 3898 if(event.getType() == DisplayAdjustmentEvent.SCALE_ADJUST_EVENT) 3899 { 3900 laststart = -1; 3901 3902 BamView.this.startBase = event.getStart(); 3903 BamView.this.endBase = event.getEnd(); 3904 3905 int width = feature_display.getMaxVisibleBases(); 3906 setZoomLevel(width); 3907 repaint(); 3908 } 3909 else 3910 { 3911 setDisplay(event.getStart(), 3912 event.getStart()+feature_display.getMaxVisibleBases(), event); 3913 repaint(); 3914 } 3915 } 3916 selectionChanged(SelectionChangeEvent event)3917 public void selectionChanged(SelectionChangeEvent event) 3918 { 3919 repaint(); 3920 } 3921 3922 private class Ruler extends JPanel 3923 { 3924 private static final long serialVersionUID = 1L; 3925 protected int start; 3926 protected int end; 3927 protected String refSeq; 3928 Ruler()3929 public Ruler() 3930 { 3931 super(); 3932 setPreferredSize(new Dimension(mainPanel.getWidth(), 26)); 3933 setBackground(Color.white); 3934 } 3935 paintComponent(Graphics g)3936 public void paintComponent(Graphics g) 3937 { 3938 super.paintComponent(g); 3939 Graphics2D g2 = (Graphics2D)g; 3940 drawBaseScale(g2, start, end, 12); 3941 } 3942 drawBaseScale(Graphics2D g2, int start, int end, int ypos)3943 private void drawBaseScale(Graphics2D g2, int start, int end, int ypos) 3944 { 3945 int startMark = (((int)(start/10))*10)+1; 3946 if(end > getSequenceLength()) 3947 end = getSequenceLength(); 3948 3949 for(int i=startMark; i<end; i+=20) 3950 { 3951 int xpos = (i-start)*ALIGNMENT_PIX_PER_BASE; 3952 g2.drawString(Integer.toString(i), xpos, ypos); 3953 } 3954 3955 for(int i=startMark; i<end; i+=10) 3956 { 3957 int xpos = (i-start)*ALIGNMENT_PIX_PER_BASE; 3958 xpos+=(ALIGNMENT_PIX_PER_BASE/2); 3959 g2.drawLine(xpos, ypos+1, xpos, ypos+5); 3960 } 3961 3962 if(refSeq != null) 3963 { 3964 ypos+=15; 3965 g2.setColor(LIGHT_GREY); 3966 g2.fillRect(0, ypos-11, getWidth(), 11); 3967 3968 g2.translate(0, 16); 3969 drawSelectionRange(g2, ALIGNMENT_PIX_PER_BASE, start, end, Color.yellow); 3970 g2.translate(0, -16); 3971 g2.setColor(Color.black); 3972 g2.drawString(refSeq, 0, ypos-2); 3973 } 3974 } 3975 } 3976 3977 /** 3978 * Popup menu listener 3979 */ 3980 class PopupListener extends MouseAdapter 3981 { 3982 private JMenuItem gotoMateMenuItem; 3983 private JMenuItem showDetails; 3984 private JMenu coverageMenu; 3985 private JMenuItem createGroup; 3986 mouseClicked(MouseEvent e)3987 public void mouseClicked(MouseEvent e) 3988 { 3989 if(e.isPopupTrigger() || 3990 e.getButton() == MouseEvent.BUTTON3) 3991 return; 3992 3993 BamView.this.requestFocus(); 3994 3995 if(e.getClickCount() > 1) 3996 getSelection().clear(); 3997 else if(e.getButton() == MouseEvent.BUTTON1) 3998 { 3999 if(isCoverageView(getPixPerBaseByWidth())) 4000 coverageView.singleClick(e.isShiftDown(), 4001 e.getPoint().y-getJspView().getViewport().getViewPosition().y); 4002 } 4003 else 4004 highlightRange(e, MouseEvent.BUTTON2_DOWN_MASK); 4005 } 4006 mousePressed(final MouseEvent e)4007 public void mousePressed(final MouseEvent e) 4008 { 4009 highlightSAMRecord = mouseOverSAMRecord; 4010 4011 repaint(); 4012 4013 javax.swing.SwingUtilities.invokeLater(new Runnable() { 4014 public void run() { 4015 maybeShowPopup(e); 4016 } 4017 }); 4018 4019 } 4020 mouseReleased(MouseEvent e)4021 public void mouseReleased(MouseEvent e) 4022 { 4023 dragStart = -1; 4024 maybeShowPopup(e); 4025 } 4026 maybeShowPopup(MouseEvent e)4027 private void maybeShowPopup(MouseEvent e) 4028 { 4029 if(e.isPopupTrigger()) 4030 { 4031 // 4032 // main menu options 4033 if(popup == null) 4034 { 4035 popup = new JPopupMenu(); 4036 createMenus(popup); 4037 } 4038 4039 // 4040 // coverage heatmap menu options 4041 if(coverageMenu != null) 4042 popup.remove(coverageMenu); 4043 if(isCoverageView(getPixPerBaseByWidth()) && coverageView.isPlotHeatMap()) 4044 { 4045 if(coverageMenu == null) 4046 { 4047 coverageMenu = new JMenu("Coverage HeatMap"); 4048 coverageView.createMenus(coverageMenu); 4049 4050 final JCheckBoxMenuItem coverageGrid = new JCheckBoxMenuItem("Show heatmap grid", false); 4051 coverageGrid.addActionListener(new ActionListener() 4052 { 4053 public void actionPerformed(ActionEvent e) 4054 { 4055 coverageView.showLabels(coverageGrid.isSelected()); 4056 } 4057 }); 4058 coverageMenu.add(coverageGrid); 4059 4060 createGroup = new JMenuItem("Create group from selected BAMs/CRAMs"); 4061 createGroup.addActionListener(new ActionListener() 4062 { 4063 private int n = 1; 4064 public void actionPerformed(ActionEvent e) 4065 { 4066 String groupName = "group_"+n; 4067 groupsFrame.addGroup(groupName); 4068 final List<String> selected = coverageView.getSelected(); 4069 for(String sel: selected) 4070 groupsFrame.addToGroup((new File(sel)).getName(), groupName); 4071 groupsFrame.updateAndDisplay(); 4072 n++; 4073 } 4074 }); 4075 coverageMenu.add(createGroup); 4076 } 4077 createGroup.setEnabled(coverageView.hasSelectedBams()); 4078 popup.add(coverageMenu); 4079 } 4080 4081 if(gotoMateMenuItem != null) 4082 popup.remove(gotoMateMenuItem); 4083 if(showDetails != null) 4084 popup.remove(showDetails); 4085 4086 if( mouseOverSAMRecord != null && 4087 mouseOverSAMRecord.sam.getReadPairedFlag() && 4088 !mouseOverSAMRecord.sam.getMateUnmappedFlag() ) 4089 { 4090 highlightSAMRecord = mouseOverSAMRecord; 4091 4092 final BamViewRecord thisSAMRecord = mouseOverSAMRecord; 4093 gotoMateMenuItem = new JMenuItem("Go to mate of : "+ 4094 thisSAMRecord.sam.getReadName()); 4095 gotoMateMenuItem.addActionListener(new ActionListener() 4096 { 4097 public void actionPerformed(ActionEvent e) 4098 { 4099 String name = thisSAMRecord.sam.getMateReferenceName(); 4100 if(name.equals("=")) 4101 name = thisSAMRecord.sam.getReferenceName(); 4102 int offset = getSequenceOffset(name); 4103 if(feature_display != null) 4104 feature_display.makeBaseVisible( 4105 thisSAMRecord.sam.getMateAlignmentStart()+offset); 4106 else 4107 scrollBar.setValue( 4108 thisSAMRecord.sam.getMateAlignmentStart()+offset- 4109 (nbasesInView/2)); 4110 4111 highlightSAMRecord = thisSAMRecord; 4112 } 4113 }); 4114 popup.add(gotoMateMenuItem); 4115 } 4116 4117 if( mouseOverSAMRecord != null) 4118 { 4119 highlightSAMRecord = mouseOverSAMRecord; 4120 4121 final BamViewRecord thisSAMRecord = mouseOverSAMRecord; 4122 showDetails = new JMenuItem("Show details of : "+ 4123 thisSAMRecord.sam.getReadName()); 4124 showDetails.addActionListener(new ActionListener() 4125 { 4126 public void actionPerformed(ActionEvent e) 4127 { 4128 BamView.this.getRootPane().setCursor(cbusy); 4129 BamView.this.getRootPane().repaint(); 4130 BamView.this.getRootPane().revalidate(); 4131 4132 SwingUtilities.invokeLater( 4133 new Runnable() 4134 { 4135 public void run() 4136 { 4137 try 4138 { 4139 openFileViewer(thisSAMRecord.sam, getMate(thisSAMRecord), bamList); 4140 } 4141 finally 4142 { 4143 BamView.this.getRootPane().setCursor(cdone); 4144 } 4145 } 4146 } 4147 ); 4148 4149 } 4150 }); 4151 popup.add(showDetails); 4152 } 4153 popup.show(e.getComponent(), 4154 e.getX(), e.getY()); 4155 } 4156 } 4157 } 4158 openFileViewer(SAMRecord readRecord, SAMRecord mateRecord, List<String> bamList)4159 protected static void openFileViewer(SAMRecord readRecord, SAMRecord mateRecord, List<String> bamList) 4160 { 4161 FileViewer viewDetail = new FileViewer(readRecord.getReadName(), true, false, false); 4162 appendToDetailView(readRecord, mateRecord, viewDetail, bamList); 4163 } 4164 appendToDetailView(final SAMRecord sam, final SAMRecord mate, final FileViewer viewer, final List<String> bamList)4165 private static void appendToDetailView(final SAMRecord sam, 4166 final SAMRecord mate, 4167 final FileViewer viewer, 4168 final List<String> bamList) 4169 { 4170 if(bamList.size() > 1 && sam.getAttribute("FL") != null) 4171 { 4172 int bamIdx = (Integer)sam.getAttribute("FL"); 4173 if(bamIdx < bamList.size()) 4174 viewer.appendString("File "+bamList.get(bamIdx)+"\n\n", Level.INFO); 4175 } 4176 4177 viewer.appendString("Read Name "+sam.getReadName()+"\n", Level.INFO); 4178 viewer.appendString("Coordinates "+sam.getAlignmentStart()+".."+ 4179 sam.getAlignmentEnd()+"\n", Level.DEBUG); 4180 if(sam.getReadGroup() != null) 4181 viewer.appendString("Read Group "+sam.getReadGroup().getId()+"\n", Level.DEBUG); 4182 viewer.appendString("Length "+sam.getReadLength()+"\n", Level.DEBUG); 4183 viewer.appendString("Reference Name "+sam.getReferenceName()+"\n", Level.DEBUG); 4184 viewer.appendString("Inferred Size "+sam.getInferredInsertSize()+"\n", Level.DEBUG); 4185 viewer.appendString("Mapping Quality "+sam.getMappingQuality()+"\n", Level.DEBUG); 4186 viewer.appendString("Cigar String "+sam.getCigarString()+"\n", Level.DEBUG); 4187 viewer.appendString("Strand "+ 4188 (sam.getReadNegativeStrandFlag() ? "-\n\n" : "+\n\n"), Level.DEBUG); 4189 4190 if(sam.getReadPairedFlag()) 4191 { 4192 if(mate != null) 4193 { 4194 viewer.appendString("Mate Coordinates "+mate.getAlignmentStart()+".."+ 4195 mate.getAlignmentEnd()+"\n", Level.DEBUG); 4196 viewer.appendString("Mate Length "+mate.getReadLength()+"\n", Level.DEBUG); 4197 viewer.appendString("Mate Reference Name "+mate.getReferenceName()+"\n", Level.DEBUG); 4198 viewer.appendString("Mate Inferred Size "+mate.getInferredInsertSize()+"\n", Level.DEBUG); 4199 viewer.appendString("Mate Mapping Quality "+mate.getMappingQuality()+"\n", Level.DEBUG); 4200 viewer.appendString("Mate Cigar String "+mate.getCigarString()+"\n", Level.DEBUG); 4201 } 4202 else 4203 { 4204 viewer.appendString("Mate Start Coordinate "+sam.getMateAlignmentStart()+"\n", Level.DEBUG); 4205 viewer.appendString("Mate Reference Name "+sam.getMateReferenceName()+"\n", Level.DEBUG); 4206 } 4207 viewer.appendString("Mate Strand "+ 4208 (sam.getMateNegativeStrandFlag() ? "-" : "+"), Level.DEBUG); 4209 } 4210 4211 viewer.appendString("\n\nFlags:", Level.INFO); 4212 viewer.appendString("\nDuplicate Read "+ 4213 (sam.getDuplicateReadFlag() ? "yes" : "no"), Level.DEBUG); 4214 viewer.appendString("\nSecondary Alignment "+ 4215 (sam.isSecondaryAlignment() ? "yes" : "no"), Level.DEBUG); 4216 viewer.appendString("\nSupplementary Alignment "+ 4217 (sam.getSupplementaryAlignmentFlag() ? "yes" : "no"), Level.DEBUG); 4218 4219 viewer.appendString("\nRead Paired "+ 4220 (sam.getReadPairedFlag() ? "yes" : "no"), Level.DEBUG); 4221 if(sam.getReadPairedFlag()) 4222 { 4223 viewer.appendString("\nFirst of Pair "+ 4224 (sam.getFirstOfPairFlag() ? "yes" : "no"), Level.DEBUG); 4225 viewer.appendString("\nMate Unmapped "+ 4226 (sam.getMateUnmappedFlag() ? "yes" : "no"), Level.DEBUG); 4227 viewer.appendString("\nProper Pair "+ 4228 (sam.getProperPairFlag() ? "yes" : "no"), Level.DEBUG); 4229 } 4230 viewer.appendString("\nRead Fails Vendor\nQuality Check "+ 4231 (sam.getReadFailsVendorQualityCheckFlag() ? "yes" : "no"), Level.DEBUG); 4232 viewer.appendString("\nRead Unmapped "+ 4233 (sam.getReadUnmappedFlag() ? "yes" : "no"), Level.DEBUG); 4234 4235 if(sam.getReadPairedFlag()) 4236 viewer.appendString("\nSecond Of Pair "+ 4237 (sam.getSecondOfPairFlag() ? "yes" : "no"), Level.DEBUG); 4238 4239 viewer.appendString("\n\nRead Bases:\n", Level.INFO); 4240 wrapReadBases(sam, viewer); 4241 4242 if(sam.getReadPairedFlag() && mate != null) 4243 { 4244 viewer.appendString("\nMate Read Bases:\n", Level.INFO); 4245 wrapReadBases(mate, viewer); 4246 } 4247 } 4248 wrapReadBases(final SAMRecord sam, final FileViewer viewer)4249 private static void wrapReadBases(final SAMRecord sam, 4250 final FileViewer viewer) 4251 { 4252 final String seq = new String(sam.getReadBases()); 4253 final int MAX_SEQ_LINE_LENGTH = 100; 4254 for(int i=0; i<=seq.length(); i+=MAX_SEQ_LINE_LENGTH) 4255 { 4256 int iend = i+MAX_SEQ_LINE_LENGTH; 4257 if(iend > seq.length()) 4258 iend = seq.length(); 4259 viewer.appendString(seq.substring(i, iend)+"\n", Level.DEBUG); 4260 } 4261 } 4262 4263 /** 4264 * Query for the mate of a read 4265 * @param mate 4266 * @return 4267 */ getMate(BamViewRecord thisSAMRecord)4268 protected SAMRecord getMate(BamViewRecord thisSAMRecord) 4269 { 4270 if(!thisSAMRecord.sam.getReadPairedFlag()) // read is not paired in sequencing 4271 return null; 4272 4273 SAMRecord mate = null; 4274 try 4275 { 4276 short fileIndex = 0; 4277 if(bamList.size()>1 && thisSAMRecord.bamIndex > 0) 4278 fileIndex = thisSAMRecord.bamIndex; 4279 String bam = bamList.get(fileIndex); 4280 final SamReader inputSam = getSAMFileReader(bam); 4281 mate = inputSam.queryMate(thisSAMRecord.sam); 4282 } 4283 catch (Exception e) 4284 { 4285 logger4j.warn("WARN: BamView.getMate() :: "+e.getMessage()); 4286 e.printStackTrace(); 4287 } 4288 return mate; 4289 } 4290 getSamRecordFlagPredicate()4291 protected SAMRecordPredicate getSamRecordFlagPredicate() 4292 { 4293 return samRecordFlagPredicate; 4294 } 4295 setSamRecordFlagPredicate( SAMRecordPredicate samRecordFlagPredicate)4296 protected void setSamRecordFlagPredicate( 4297 SAMRecordPredicate samRecordFlagPredicate) 4298 { 4299 laststart = -1; 4300 lastend = -1; 4301 this.samRecordFlagPredicate = samRecordFlagPredicate; 4302 } 4303 getSamRecordMapQPredicate()4304 protected SAMRecordMapQPredicate getSamRecordMapQPredicate() 4305 { 4306 return samRecordMapQPredicate; 4307 } 4308 setSamRecordMapQPredicate( SAMRecordMapQPredicate samRecordMapQPredicate)4309 protected void setSamRecordMapQPredicate( 4310 SAMRecordMapQPredicate samRecordMapQPredicate) 4311 { 4312 laststart = -1; 4313 lastend = -1; 4314 this.samRecordMapQPredicate = samRecordMapQPredicate; 4315 } 4316 4317 /** 4318 * @return the concatSequences 4319 */ isConcatSequences()4320 protected boolean isConcatSequences() 4321 { 4322 return concatSequences; 4323 } 4324 4325 /** 4326 * Check whether the given BAM record is highlighted currently. 4327 * @param bamRec BamViewRecord 4328 * @return boolean - true if highlighted 4329 */ isThisBamRecordHighlighted(BamViewRecord bamRec)4330 protected boolean isThisBamRecordHighlighted(BamViewRecord bamRec) 4331 { 4332 if (highlightSAMRecord == null) 4333 { 4334 return false; 4335 } 4336 4337 return BamUtils.samRecordEqualityCheck(highlightSAMRecord.sam, bamRec.sam); 4338 } 4339 4340 class PairedRead 4341 { 4342 BamViewRecord sam1; 4343 BamViewRecord sam2; 4344 } 4345 4346 class CreateFeatures 4347 { CreateFeatures(final GroupBamFrame groupsFrame)4348 CreateFeatures(final GroupBamFrame groupsFrame) 4349 { 4350 final TextFieldInt threshold = new TextFieldInt(); 4351 final TextFieldInt minSize = new TextFieldInt(); 4352 final TextFieldInt minBams = new TextFieldInt(); 4353 4354 threshold.setValue(6); 4355 minSize.setValue(6); 4356 minBams.setValue( (groupsFrame.getNumberOfGroups() == 1 ? 4357 bamList.size() : groupsFrame.getMaximumBamsInGroup()) ); 4358 4359 final JPanel gridPanel = new JPanel(new GridBagLayout()); 4360 GridBagConstraints c = new GridBagConstraints(); 4361 c.anchor = GridBagConstraints.WEST; 4362 c.fill = GridBagConstraints.HORIZONTAL; 4363 c.gridx = 0; 4364 c.gridy = 0; 4365 4366 gridPanel.add(new JLabel("Minimum number of reads:"), c); 4367 c.gridy++; 4368 gridPanel.add(threshold, c); 4369 4370 c.gridy++; 4371 gridPanel.add(new JSeparator(), c); 4372 c.gridy++; 4373 gridPanel.add(new JLabel("Minimum number of BAMs/CRAMs for reads to be present in:"), c); 4374 c.gridy++; 4375 gridPanel.add(minBams, c); 4376 4377 JRadioButton useAllBams = new JRadioButton("out of all BAMs/CRAMs", (groupsFrame.getNumberOfGroups() == 1)); 4378 JRadioButton useGroup = new JRadioButton("within a group", (groupsFrame.getNumberOfGroups() != 1)); 4379 4380 if(groupsFrame.getNumberOfGroups() == 1) 4381 useGroup.setEnabled(false); 4382 4383 final ButtonGroup group = new ButtonGroup(); 4384 group.add(useAllBams); 4385 group.add(useGroup); 4386 4387 final Box xBox = Box.createHorizontalBox(); 4388 xBox.add(useAllBams); 4389 xBox.add(useGroup); 4390 xBox.add(Box.createHorizontalGlue()); 4391 c.gridy++; 4392 gridPanel.add(xBox, c); 4393 4394 c.gridy++; 4395 gridPanel.add(new JSeparator(), c); 4396 c.gridy++; 4397 gridPanel.add(new JLabel("Minimum feature size:"), c); 4398 c.gridy++; 4399 gridPanel.add(minSize, c); 4400 4401 final JCheckBox cbOpposite = new JCheckBox("Assume reads on opposite strand", false); 4402 cbOpposite.setToolTipText("for cDNA experiments when the reads are on the opposite strand"); 4403 c.gridy++; 4404 gridPanel.add(cbOpposite, c); 4405 4406 int status = 4407 JOptionPane.showConfirmDialog(feature_display, gridPanel, 4408 "Options", JOptionPane.OK_CANCEL_OPTION); 4409 if(status == JOptionPane.CANCEL_OPTION) 4410 return; 4411 4412 if(!useGroup.isSelected() && minBams.getValue() > bamList.size()) 4413 { 4414 status = 4415 JOptionPane.showConfirmDialog(feature_display, 4416 "The minimum number of BAMs setting can not be\n"+ 4417 "greater than the total number of BAM files.\n"+ 4418 "Set this to the number of BAMs (i.e. "+bamList.size()+").", 4419 "Options", JOptionPane.OK_CANCEL_OPTION); 4420 if(status == JOptionPane.CANCEL_OPTION) 4421 return; 4422 minBams.setValue(bamList.size()); 4423 } 4424 else if(useGroup.isSelected() && minBams.getValue() > groupsFrame.getMaximumBamsInGroup()) 4425 { 4426 status = 4427 JOptionPane.showConfirmDialog(feature_display, 4428 "Minimum number of BAMs setting can not be greater than\n"+ 4429 "the total number of BAM files found in any of the groups.\n"+ 4430 "Set this to the greatest number of BAM files in any\n"+ 4431 "group (i.e. "+groupsFrame.getMaximumBamsInGroup()+").", 4432 "Options", JOptionPane.OK_CANCEL_OPTION); 4433 if(status == JOptionPane.CANCEL_OPTION) 4434 return; 4435 minBams.setValue(groupsFrame.getMaximumBamsInGroup()); 4436 } 4437 4438 new MappedReads(BamView.this, 4439 (useGroup.isSelected() ? groupsFrame : null), threshold.getValue(), 4440 minSize.getValue(), minBams.getValue(), cbOpposite.isSelected(), true); 4441 } 4442 } 4443 4444 /** 4445 * Is BamView being used as a standalone application. 4446 * @return boolean 4447 */ isStandaloneMode()4448 public static boolean isStandaloneMode() 4449 { 4450 return standaloneMode; 4451 } 4452 4453 /** 4454 * Set whether or not BamView is being used as a standalone application. 4455 * @param inStandaloneMode boolean 4456 */ setStandaloneMode(boolean inStandaloneMode)4457 public static void setStandaloneMode(boolean inStandaloneMode) 4458 { 4459 standaloneMode = inStandaloneMode; 4460 } 4461 4462 /** 4463 * Close the application. 4464 */ close()4465 public void close() 4466 { 4467 BamView.this.setVisible(false); 4468 Component comp = BamView.this; 4469 4470 while( !(comp instanceof JFrame) ) 4471 comp = comp.getParent(); 4472 4473 ((JFrame)comp).dispose(); 4474 } 4475 4476 /** 4477 * Close the BAM panel. 4478 */ closeBamPanel()4479 public void closeBamPanel() 4480 { 4481 BamView.this.getRootPane().setCursor(cdone); 4482 4483 final JPanel containerPanel = (JPanel) mainPanel.getParent(); 4484 if (feature_display != null) 4485 { 4486 feature_display.removeDisplayAdjustmentListener(BamView.this); 4487 feature_display.getSelection().removeSelectionChangeListener(BamView.this); 4488 } 4489 containerPanel.remove(mainPanel); 4490 4491 if(containerPanel.getComponentCount() > 0) 4492 containerPanel.revalidate(); 4493 else 4494 { 4495 if(entry_edit != null) 4496 entry_edit.setNGDivider(); 4497 else 4498 containerPanel.setVisible(false); 4499 } 4500 } 4501 4502 /** 4503 * Called when we encounter a major error while loading a BAM or CRAM file. 4504 * It will display the errors in an option pane and then close the panel, 4505 * exiting completely of BamView is in standalone mode. 4506 * @param errorText String - the error message to display. 4507 */ handleFatalError(final String errorText)4508 protected void handleFatalError(final String errorText) 4509 { 4510 if (!foundFatalErrors.get()) 4511 { 4512 foundFatalErrors.set(true); 4513 4514 logger4j.error("BamView errors: " + errorText); 4515 4516 SwingUtilities.invokeLater(new Runnable() { 4517 4518 public void run() { 4519 4520 JOptionPane.showMessageDialog(null, errorText); 4521 readsInView.clear(); 4522 closeBamPanel(); 4523 4524 if (BamView.isStandaloneMode()) 4525 { 4526 System.exit(0); 4527 } 4528 } 4529 }); 4530 } 4531 } 4532 4533 /** 4534 * Class main method. 4535 * @param args 4536 */ main(String[] args)4537 public static void main(String[] args) 4538 { 4539 BamFrame frame = new BamFrame(); 4540 if(args.length == 0 && BamFrame.isMac()) 4541 { 4542 try 4543 { 4544 Thread.sleep(1000); 4545 } 4546 catch (InterruptedException e1) {} 4547 if(frame.getBamFile() != null) 4548 args = new String[]{ frame.getBamFile() }; 4549 } 4550 4551 List<String> alignmentFileList = new Vector<String>(); 4552 String reference = null; 4553 if(args.length == 0 || args[0].equals("NEW-BAMVIEW")) 4554 { 4555 System.setProperty("default_directory", System.getProperty("user.dir")); 4556 FileSelectionDialog fileSelection = new FileSelectionDialog( 4557 null, true, "BamView", "BAM/CRAM"); 4558 alignmentFileList = fileSelection.getFiles(BAM_SUFFIX); 4559 reference = fileSelection.getReferenceFile(); 4560 if(reference == null || reference.trim().equals("")) 4561 reference = null; 4562 4563 if(alignmentFileList == null || alignmentFileList.size() < 1) 4564 { 4565 if(args.length > 0 && args[0].equals("NEW-BAMVIEW")) 4566 return; 4567 System.err.println("No files found."); 4568 System.exit(0); 4569 } 4570 4571 } 4572 else if(!args[0].startsWith("-")) 4573 { 4574 for(int i=0; i< args.length; i++) 4575 alignmentFileList.add(args[i]); 4576 } 4577 4578 int nbasesInView = 2000; 4579 String chr = null; 4580 String vw = null; 4581 boolean orientation = false; 4582 boolean covPlot = false; 4583 boolean snpPlot = false; 4584 int base = 0; 4585 4586 for(int i=0;i<args.length; i++) 4587 { 4588 if(args[i].equals("-a")) 4589 { 4590 while(i < args.length-1 && !args[++i].startsWith("-")) 4591 { 4592 String filename = args[i]; 4593 if(FileSelectionDialog.isListOfFiles(filename)) 4594 alignmentFileList.addAll(FileSelectionDialog.getListOfFiles(filename)); 4595 else 4596 alignmentFileList.add(filename); 4597 } 4598 --i; 4599 } 4600 else if(args[i].equals("-r")) 4601 reference = args[++i]; 4602 else if(args[i].equals("-n")) 4603 nbasesInView = Integer.parseInt(args[++i]); 4604 else if(args[i].equals("-s")) 4605 System.setProperty("samtoolDir", args[++i]); 4606 else if(args[i].equals("-c")) 4607 chr = args[++i].trim(); 4608 else if(args[i].equals("-b")) 4609 base = Integer.parseInt(args[++i].trim()); 4610 else if(args[i].equals("-v")) 4611 vw = args[++i].trim(); 4612 else if(args[i].equals("-o")) 4613 orientation = true; 4614 else if(args[i].equals("-pc")) 4615 covPlot = true; 4616 else if(args[i].equals("-ps")) 4617 snpPlot = true; 4618 else if(args[i].startsWith("-h")) 4619 { 4620 System.out.println("-h\t show help"); 4621 4622 System.out.println("-a\t BAM/CRAM file to display"); 4623 System.out.println("-r\t reference file (optional)"); 4624 System.out.println("-n\t number of bases to display in the view (optional)"); 4625 System.out.println("-c\t chromosome name (optional)"); 4626 System.out.println("-v\t view (optional - IS (inferred size), S (stack, default), PS (paired stack), ST (strand), C (coverage))"); 4627 System.out.println("-b\t base position (optional)"); 4628 System.out.println("-o\t show orientation (optional)"); 4629 System.out.println("-pc\t plot coverage (optional)"); 4630 System.out.println("-ps\t plot SNP (optional and only with -r)"); 4631 System.exit(0); 4632 } 4633 } 4634 4635 BamView.setStandaloneMode(true); 4636 4637 /* 4638 * If a reference was specified then make sure that it's a valid file. 4639 */ 4640 if (reference != null) 4641 { 4642 File tmpRef = new File(reference); 4643 if (!tmpRef.exists()) { 4644 System.err.println("The specified reference file does not exist."); 4645 System.exit(0); 4646 } 4647 } 4648 4649 final BamView view = new BamView(alignmentFileList, reference, nbasesInView, null, null, 4650 (JPanel)frame.getContentPane(), frame); 4651 frame.setTitle("BamView v"+view.getVersion()); 4652 4653 if(chr != null) 4654 view.combo.setSelectedItem(chr); 4655 if(vw != null) 4656 { 4657 if(vw.equalsIgnoreCase("IS")) 4658 view.cbIsizeStackView.setSelected(true); 4659 if(vw.equalsIgnoreCase("PS")) 4660 view.cbPairedStackView.setSelected(true); 4661 if(vw.equalsIgnoreCase("ST")) 4662 view.cbStrandStackView.setSelected(true); 4663 if(vw.equalsIgnoreCase("C")) 4664 view.cbCoverageView.setSelected(true); 4665 } 4666 if(base > 0) 4667 view.scrollBar.setValue(base); 4668 if(orientation) 4669 view.isOrientation = true; 4670 if(covPlot) 4671 { 4672 view.isCoverage = true; 4673 view.coveragePanel.setVisible(true); 4674 } 4675 if(snpPlot) 4676 { 4677 view.isSNPplot = true; 4678 view.snpPanel.setVisible(true); 4679 } 4680 4681 // translucent 4682 //frame.getRootPane().putClientProperty("Window.alpha", new Float(0.9f)); 4683 /*frame.addWindowFocusListener(new WindowFocusListener() 4684 { 4685 public void windowGainedFocus(WindowEvent e) 4686 { 4687 view.requestFocus(); 4688 } 4689 public void windowLostFocus(WindowEvent e){} 4690 });*/ 4691 4692 frame.pack(); 4693 4694 view.jspView.getVerticalScrollBar().setValue( 4695 view.jspView.getVerticalScrollBar().getMaximum()); 4696 frame.setVisible(true); 4697 } 4698 } 4699