/* * Created on Aug 28, 2007 */ package org.seqcode.projects.seqview.components; import java.util.*; import java.util.regex.*; import java.util.logging.*; import java.io.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.*; import javax.swing.border.*; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.text.*; import org.seqcode.genome.Genome; import org.seqcode.genome.location.Region; import org.seqcode.genome.sequence.SequenceGenerator; import org.seqcode.gseutils.NotFoundException; /** * @author tdanford */ public class SequenceViewer extends JFrame implements CaretListener { public static void main(String[] args) { SequenceViewer sv = new SequenceViewer(); } private static Pattern regionPattern = Pattern.compile("([\\w\\d]+):([\\w\\d]+):(\\d+)-(\\d+)"); private Logger logger; private Level logLevel; private SequenceGenerator seqgen; private Region currentRegion; private String currentSequence; private boolean currentStrand; private JTextPane sequenceArea; private StyledDocument seqDoc; private JTextField locField, matchField; private JButton retrieveSequence, matchSequence; private JLabel coordinateLabel; private JRadioButton forwardStrand, reverseStrand; private ButtonGroup strandGroup; public SequenceViewer() { super("SequenceViewer"); init(""); } public SequenceViewer(String initialLocation) { super("SequenceViewer"); init(initialLocation); fetchSequence(initialLocation); } public SequenceViewer(Region initialLocation) { super("SequenceViewer"); String initial = getRegionString(initialLocation); init(initial); fetchSequence(initial); } private void init(String location) { logger = Logger.getLogger("org.seqcode.genome.sequence.SequenceViewer"); logLevel = Level.INFO; logger.log(logLevel, "Starting SequenceViewer.."); seqgen = new SequenceGenerator(); currentRegion = null; currentSequence = null; currentStrand = true; sequenceArea = new JTextPane(); sequenceArea.addCaretListener(this); seqDoc = sequenceArea.getStyledDocument(); initStyles(); locField = new JTextField(location); retrieveSequence = new JButton("Fetch"); matchField = new JTextField(); matchSequence = new JButton("Match"); coordinateLabel = new JLabel(" "); forwardStrand = new JRadioButton("Forward"); reverseStrand = new JRadioButton("Reverse"); strandGroup = new ButtonGroup(); strandGroup.add(forwardStrand); strandGroup.add(reverseStrand); forwardStrand.setSelected(true); JPanel strandPanel = new JPanel(); strandPanel.setLayout(new BorderLayout()); strandPanel.add(forwardStrand, BorderLayout.NORTH); strandPanel.add(reverseStrand, BorderLayout.SOUTH); JPanel locPanel = new JPanel(); locPanel.setLayout(new BorderLayout()); locPanel.add(locField, BorderLayout.CENTER); locPanel.add(retrieveSequence, BorderLayout.EAST); JPanel matchPanel = new JPanel(); matchPanel.setLayout(new BorderLayout()); matchPanel.add(matchField, BorderLayout.CENTER); matchPanel.add(matchSequence, BorderLayout.EAST); JPanel inputPanel = new JPanel(); inputPanel.setLayout(new BorderLayout()); inputPanel.add(strandPanel, BorderLayout.NORTH); inputPanel.add(locPanel, BorderLayout.SOUTH); JPanel seqPanel = new JPanel(); seqPanel.setLayout(new BorderLayout()); seqPanel.add(new JScrollPane(sequenceArea), BorderLayout.CENTER); seqPanel.setBorder(new TitledBorder("Chromosomal Sequence")); seqPanel.add(matchPanel, BorderLayout.NORTH); seqPanel.add(coordinateLabel, BorderLayout.SOUTH); Container c = (Container)getContentPane(); c.setLayout(new BorderLayout()); c.add(seqPanel, BorderLayout.CENTER); c.add(inputPanel, BorderLayout.SOUTH); setJMenuBar(createMenubar()); retrieveSequence.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { fetchSequence(locField.getText()); } }); matchSequence.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { findAndMark(matchField.getText()); } }); forwardStrand.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setStrand(true); } }); reverseStrand.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { setStrand(false); } }); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setSize(400, 600); setVisible(true); } public void setStrand(boolean value) { if(value != currentStrand) { currentSequence = reverseComplement(currentSequence); currentStrand = value; updateTextArea(); } } private JMenuBar createMenubar() { JMenuBar bar = new JMenuBar(); JMenu menu = null; JMenuItem item = null; final JFrame thisframe = this; bar.add(menu = new JMenu("File")); menu.add(item = new JMenuItem("Close")); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { thisframe.dispose(); } }); menu.add(item = new JMenuItem("Exit")); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { System.exit(0); } }); bar.add(menu = new JMenu("Edit")); menu.add(item = new JMenuItem("Clear")); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { clearMarkings(); } }); return bar; } private void recordCoordinates(int dot, int mark) { int start = Math.min(dot, mark), end = Math.max(dot, mark); start += currentRegion.getStart(); end += currentRegion.getStart(); String msg = start + "," + end; if(start == end) { msg = String.valueOf(start); } SwingUtilities.invokeLater(new CoordinateSetter(msg)); } public void caretUpdate(CaretEvent evt) { recordCoordinates(evt.getDot(), evt.getMark()); } private void initStyles() { //Initialize some styles. Style def = StyleContext.getDefaultStyleContext(). getStyle(StyleContext.DEFAULT_STYLE); Style regular = seqDoc.addStyle("regular", def); StyleConstants.setFontFamily(def, "Courier"); StyleConstants.setFontSize(def, 14); Style s = seqDoc.addStyle("italic", regular); StyleConstants.setItalic(s, true); s = seqDoc.addStyle("bold", regular); StyleConstants.setBold(s, true); s = seqDoc.addStyle("red", regular); StyleConstants.setForeground(s, Color.red); } public String getRegionString(Region r) { return String.format("%s:%s:%d-%d", r.getGenome().getVersion(), r.getChrom(), r.getStart(), r.getEnd()); } public void fetchSequence(String seqstring) { logger.log(logLevel, "Retrieving sequence for " + seqstring); Matcher m = regionPattern.matcher(seqstring); if(m.matches()) { String genomeName = m.group(1); String chromName = m.group(2); int start = Integer.parseInt(m.group(3)); int end = Integer.parseInt(m.group(4)); try { Genome genome = Genome.findGenome(genomeName); Region r = new Region(genome, chromName, start, end); String seq = seqgen.execute(r); seq = seq.toUpperCase(); logger.log(logLevel, "Sequence: [" + seq + "]"); currentRegion = r; currentSequence = seq; currentStrand = true; updateTextArea(); } catch (NotFoundException e) { e.printStackTrace(); logger.log(Level.WARNING, "Couldn't find genome \"" + genomeName + "\""); } } else { logger.log(Level.WARNING, "Couldn't parse \"" + seqstring + "\""); } } public void findAndMark(String substr) { Pattern p = Pattern.compile(substr); Matcher m = p.matcher(currentSequence); while(m.find()) { int st = m.start(); int end = m.end(); SwingUtilities.invokeLater(new TextMarker(st, end, "red", false)); } } private String reverseComplement(String str) { StringBuffer sb = new StringBuffer(); for(int i = str.length()-1; i >= 0; i--) { sb.append(complement(str.charAt(i))); } return sb.toString(); } private char complement(char c) { switch(c) { case 'A': return 'T'; case 'a': return 't'; case 'T': return 'A'; case 't': return 'a'; case 'G': return 'C'; case 'g': return 'c'; case 'C': return 'G'; case 'c': return 'g'; default: return c; } } public void clearMarkings() { SwingUtilities.invokeLater(new TextMarker(0, currentSequence.length(), "regular", true)); } private void updateTextArea() { SwingUtilities.invokeLater(new Updater()); } private class Updater implements Runnable { public void run() { try { seqDoc.remove(0, seqDoc.getLength()); seqDoc.insertString(0, currentSequence, seqDoc.getStyle("regular")); } catch (BadLocationException e) { e.printStackTrace(); } } } private class CoordinateSetter implements Runnable { private String lbl; public CoordinateSetter(String l) { lbl = l; } public void run() { coordinateLabel.setText(lbl); } } private class TextMarker implements Runnable { private String stylename; private int start, end; private boolean rep; public TextMarker(int st, int ed, String s, boolean r) { start = st; end = ed; stylename = s; rep = r; } public void run() { int length = end-start; logger.log(logLevel, "Marking " + start + "-" + end + " as " + stylename); seqDoc.setCharacterAttributes(start, end-start, seqDoc.getStyle(stylename), rep); } } }